diff options
Diffstat (limited to 'drivers')
561 files changed, 12691 insertions, 10528 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 8f8da9f92090..eac4f3b02df9 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -847,6 +847,8 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) if (ghes_read_estatus(ghes, 1)) { ghes_clear_estatus(ghes); continue; + } else { + ret = NMI_HANDLED; } sev = ghes_severity(ghes->estatus->error_severity); @@ -858,12 +860,11 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) __process_error(ghes); ghes_clear_estatus(ghes); - - ret = NMI_HANDLED; } #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG - irq_work_queue(&ghes_proc_irq_work); + if (ret == NMI_HANDLED) + irq_work_queue(&ghes_proc_irq_work); #endif atomic_dec(&ghes_in_nmi); return ret; diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index c097f477c74c..14c2a07c9f3f 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -965,7 +965,7 @@ static size_t sizeof_nfit_set_info(int num_mappings) + num_mappings * sizeof(struct nfit_set_info_map); } -static int cmp_map(const void *m0, const void *m1) +static int cmp_map_compat(const void *m0, const void *m1) { const struct nfit_set_info_map *map0 = m0; const struct nfit_set_info_map *map1 = m1; @@ -974,6 +974,14 @@ static int cmp_map(const void *m0, const void *m1) sizeof(u64)); } +static int cmp_map(const void *m0, const void *m1) +{ + const struct nfit_set_info_map *map0 = m0; + const struct nfit_set_info_map *map1 = m1; + + return map0->region_offset - map1->region_offset; +} + /* Retrieve the nth entry referencing this spa */ static struct acpi_nfit_memory_map *memdev_from_spa( struct acpi_nfit_desc *acpi_desc, u16 range_index, int n) @@ -1029,6 +1037,12 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc, sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map), cmp_map, NULL); nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0); + + /* support namespaces created with the wrong sort order */ + sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map), + cmp_map_compat, NULL); + nd_set->altcookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0); + ndr_desc->nd_set = nd_set; devm_kfree(dev, info); diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 80e55cb0827b..b48ecbfc4498 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -271,6 +271,26 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"), }, }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1123661 */ + .callback = video_detect_force_native, + .ident = "Dell XPS 17 L702X", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L702X"), + }, + }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1204476 */ + /* https://bugs.launchpad.net/ubuntu/+source/linux-lts-trusty/+bug/1416940 */ + .callback = video_detect_force_native, + .ident = "HP Pavilion dv6", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv6 Notebook PC"), + }, + }, + { }, }; diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 0f6591a72cf8..d0334e50f2f9 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2221,9 +2221,10 @@ static void binder_transaction(struct binder_proc *proc, return_error = BR_FAILED_REPLY; goto err_bad_offset; } - if (copy_from_user(sg_bufp, - (const void __user *)(uintptr_t) - bp->buffer, bp->length)) { + if (copy_from_user_preempt_disabled( + sg_bufp, + (const void __user *)(uintptr_t) + bp->buffer, bp->length)) { binder_user_error("%d:%d got transaction with invalid offsets ptr\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; @@ -2511,7 +2512,8 @@ static int binder_thread_write(struct binder_proc *proc, case BC_REPLY_SG: { struct binder_transaction_data_sg tr; - if (copy_from_user(&tr, ptr, sizeof(tr))) + if (copy_from_user_preempt_disabled(&tr, ptr, + sizeof(tr))) return -EFAULT; ptr += sizeof(tr); binder_transaction(proc, thread, &tr.transaction_data, diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index bd370c98f77d..b0b77b61c40c 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4139,10 +4139,10 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "ST380013AS", "3.20", ATA_HORKAGE_MAX_SEC_1024 }, /* - * Device times out with higher max sects. + * These devices time out with higher max sects. * https://bugzilla.kernel.org/show_bug.cgi?id=121671 */ - { "LITEON CX1-JB256-HP", NULL, ATA_HORKAGE_MAX_SEC_1024 }, + { "LITEON CX1-JB*-HP", NULL, ATA_HORKAGE_MAX_SEC_1024 }, /* Devices we expect to fail diagnostics */ diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index bd74ee555278..729f26322095 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -4121,6 +4121,9 @@ static int mv_platform_probe(struct platform_device *pdev) host->iomap = NULL; hpriv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!hpriv->base) + return -ENOMEM; + hpriv->base -= SATAHC0_REG_BASE; hpriv->clk = clk_get(&pdev->dev, NULL); diff --git a/drivers/base/core.c b/drivers/base/core.c index bbe8e2efc677..3fa9096b27c2 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1116,7 +1116,13 @@ int device_add(struct device *dev) error = dpm_sysfs_add(dev); if (error) goto DPMError; - device_pm_add(dev); + if ((dev->pm_domain) || (dev->type && dev->type->pm) + || (dev->class && (dev->class->pm || dev->class->resume)) + || (dev->bus && (dev->bus->pm || dev->bus->resume)) || + (dev->driver && dev->driver->pm)) { + device_pm_add(dev); + } + if (MAJOR(dev->devt)) { error = device_create_file(dev, &dev_attr_dev); diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 1c6e4da01e69..f8112c356bc5 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -1108,13 +1108,14 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, timeout = MAX_JIFFY_OFFSET; } - retval = wait_for_completion_interruptible_timeout(&buf->completion, + timeout = wait_for_completion_interruptible_timeout(&buf->completion, timeout); - if (retval == -ERESTARTSYS || !retval) { + if (timeout == -ERESTARTSYS || !timeout) { + retval = timeout; mutex_lock(&fw_lock); fw_load_abort(fw_priv); mutex_unlock(&fw_lock); - } else if (retval > 0) { + } else if (timeout > 0) { retval = 0; } diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 25425d3f2575..48c0a1d0dd3a 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -388,30 +388,29 @@ static ssize_t show_valid_zones(struct device *dev, { struct memory_block *mem = to_memory_block(dev); unsigned long start_pfn, end_pfn; + unsigned long valid_start, valid_end; unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; - struct page *first_page; struct zone *zone; start_pfn = section_nr_to_pfn(mem->start_section_nr); end_pfn = start_pfn + nr_pages; - first_page = pfn_to_page(start_pfn); /* The block contains more than one zone can not be offlined. */ - if (!test_pages_in_a_zone(start_pfn, end_pfn)) + if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end)) return sprintf(buf, "none\n"); - zone = page_zone(first_page); + zone = page_zone(pfn_to_page(valid_start)); if (zone_idx(zone) == ZONE_MOVABLE - 1) { /*The mem block is the last memoryblock of this zone.*/ - if (end_pfn == zone_end_pfn(zone)) + if (valid_end == zone_end_pfn(zone)) return sprintf(buf, "%s %s\n", zone->name, (zone + 1)->name); } if (zone_idx(zone) == ZONE_MOVABLE) { /*The mem block is the first memoryblock of ZONE_MOVABLE.*/ - if (start_pfn == zone->zone_start_pfn) + if (valid_start == zone->zone_start_pfn) return sprintf(buf, "%s %s\n", zone->name, (zone - 1)->name); } diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 6c5bc3fadfcf..a88590bb0b10 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -162,6 +162,12 @@ void device_pm_move_before(struct device *deva, struct device *devb) pr_debug("PM: Moving %s:%s before %s:%s\n", deva->bus ? deva->bus->name : "No Bus", dev_name(deva), devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); + if (!((devb->pm_domain) || (devb->type && devb->type->pm) + || (devb->class && (devb->class->pm || devb->class->resume)) + || (devb->bus && (devb->bus->pm || devb->bus->resume)) || + (devb->driver && devb->driver->pm))) { + device_pm_add(devb); + } /* Delete deva from dpm_list and reinsert before devb. */ list_move_tail(&deva->power.entry, &devb->power.entry); } @@ -176,6 +182,12 @@ void device_pm_move_after(struct device *deva, struct device *devb) pr_debug("PM: Moving %s:%s after %s:%s\n", deva->bus ? deva->bus->name : "No Bus", dev_name(deva), devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); + if (!((devb->pm_domain) || (devb->type && devb->type->pm) + || (devb->class && (devb->class->pm || devb->class->resume)) + || (devb->bus && (devb->bus->pm || devb->bus->resume)) || + (devb->driver && devb->driver->pm))) { + device_pm_add(devb); + } /* Delete deva from dpm_list and reinsert after devb. */ list_move(&deva->power.entry, &devb->power.entry); } diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 297beae64314..297aa5cf393e 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -20,14 +20,22 @@ static inline void pm_runtime_early_init(struct device *dev) extern void pm_runtime_init(struct device *dev); extern void pm_runtime_remove(struct device *dev); +#define WAKE_IRQ_DEDICATED_ALLOCATED BIT(0) +#define WAKE_IRQ_DEDICATED_MANAGED BIT(1) +#define WAKE_IRQ_DEDICATED_MASK (WAKE_IRQ_DEDICATED_ALLOCATED | \ + WAKE_IRQ_DEDICATED_MANAGED) + struct wake_irq { struct device *dev; + unsigned int status; int irq; - bool dedicated_irq:1; }; extern void dev_pm_arm_wake_irq(struct wake_irq *wirq); extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq); +extern void dev_pm_enable_wake_irq_check(struct device *dev, + bool can_change_status); +extern void dev_pm_disable_wake_irq_check(struct device *dev); #ifdef CONFIG_PM_SLEEP @@ -102,6 +110,15 @@ static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq) { } +static inline void dev_pm_enable_wake_irq_check(struct device *dev, + bool can_change_status) +{ +} + +static inline void dev_pm_disable_wake_irq_check(struct device *dev) +{ +} + #endif #ifdef CONFIG_PM_SLEEP diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 9796a1a15ef6..3252429f96af 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -515,7 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) callback = RPM_GET_CALLBACK(dev, runtime_suspend); - dev_pm_enable_wake_irq(dev); + dev_pm_enable_wake_irq_check(dev, true); retval = rpm_callback(callback, dev); if (retval) goto fail; @@ -554,7 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) return retval; fail: - dev_pm_disable_wake_irq(dev); + dev_pm_disable_wake_irq_check(dev); __update_runtime_status(dev, RPM_ACTIVE); dev->power.deferred_resume = false; wake_up_all(&dev->power.wait_queue); @@ -737,12 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags) callback = RPM_GET_CALLBACK(dev, runtime_resume); - dev_pm_disable_wake_irq(dev); + dev_pm_disable_wake_irq_check(dev); retval = rpm_callback(callback, dev); if (retval) { __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_cancel_pending(dev); - dev_pm_enable_wake_irq(dev); + dev_pm_enable_wake_irq_check(dev, false); } else { no_callback: __update_runtime_status(dev, RPM_ACTIVE); diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c index 0d77cd6fd8d1..404d94c6c8bc 100644 --- a/drivers/base/power/wakeirq.c +++ b/drivers/base/power/wakeirq.c @@ -110,8 +110,10 @@ void dev_pm_clear_wake_irq(struct device *dev) dev->power.wakeirq = NULL; spin_unlock_irqrestore(&dev->power.lock, flags); - if (wirq->dedicated_irq) + if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED) { free_irq(wirq->irq, wirq); + wirq->status &= ~WAKE_IRQ_DEDICATED_MASK; + } kfree(wirq); } EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq); @@ -179,7 +181,6 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) wirq->dev = dev; wirq->irq = irq; - wirq->dedicated_irq = true; irq_set_status_flags(irq, IRQ_NOAUTOEN); /* @@ -195,6 +196,8 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) if (err) goto err_free_irq; + wirq->status = WAKE_IRQ_DEDICATED_ALLOCATED; + return err; err_free_irq: @@ -210,9 +213,9 @@ EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq); * dev_pm_enable_wake_irq - Enable device wake-up interrupt * @dev: Device * - * Called from the bus code or the device driver for - * runtime_suspend() to enable the wake-up interrupt while - * the device is running. + * Optionally called from the bus code or the device driver for + * runtime_resume() to override the PM runtime core managed wake-up + * interrupt handling to enable the wake-up interrupt. * * Note that for runtime_suspend()) the wake-up interrupts * should be unconditionally enabled unlike for suspend() @@ -222,7 +225,7 @@ void dev_pm_enable_wake_irq(struct device *dev) { struct wake_irq *wirq = dev->power.wakeirq; - if (wirq && wirq->dedicated_irq) + if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED)) enable_irq(wirq->irq); } EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq); @@ -231,20 +234,73 @@ EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq); * dev_pm_disable_wake_irq - Disable device wake-up interrupt * @dev: Device * - * Called from the bus code or the device driver for - * runtime_resume() to disable the wake-up interrupt while - * the device is running. + * Optionally called from the bus code or the device driver for + * runtime_suspend() to override the PM runtime core managed wake-up + * interrupt handling to disable the wake-up interrupt. */ void dev_pm_disable_wake_irq(struct device *dev) { struct wake_irq *wirq = dev->power.wakeirq; - if (wirq && wirq->dedicated_irq) + if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED)) disable_irq_nosync(wirq->irq); } EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq); /** + * dev_pm_enable_wake_irq_check - Checks and enables wake-up interrupt + * @dev: Device + * @can_change_status: Can change wake-up interrupt status + * + * Enables wakeirq conditionally. We need to enable wake-up interrupt + * lazily on the first rpm_suspend(). This is needed as the consumer device + * starts in RPM_SUSPENDED state, and the the first pm_runtime_get() would + * otherwise try to disable already disabled wakeirq. The wake-up interrupt + * starts disabled with IRQ_NOAUTOEN set. + * + * Should be only called from rpm_suspend() and rpm_resume() path. + * Caller must hold &dev->power.lock to change wirq->status + */ +void dev_pm_enable_wake_irq_check(struct device *dev, + bool can_change_status) +{ + struct wake_irq *wirq = dev->power.wakeirq; + + if (!wirq || !((wirq->status & WAKE_IRQ_DEDICATED_MASK))) + return; + + if (likely(wirq->status & WAKE_IRQ_DEDICATED_MANAGED)) { + goto enable; + } else if (can_change_status) { + wirq->status |= WAKE_IRQ_DEDICATED_MANAGED; + goto enable; + } + + return; + +enable: + enable_irq(wirq->irq); +} + +/** + * dev_pm_disable_wake_irq_check - Checks and disables wake-up interrupt + * @dev: Device + * + * Disables wake-up interrupt conditionally based on status. + * Should be only called from rpm_suspend() and rpm_resume() path. + */ +void dev_pm_disable_wake_irq_check(struct device *dev) +{ + struct wake_irq *wirq = dev->power.wakeirq; + + if (!wirq || !((wirq->status & WAKE_IRQ_DEDICATED_MASK))) + return; + + if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED) + disable_irq_nosync(wirq->irq); +} + +/** * dev_pm_arm_wake_irq - Arm device wake-up * @wirq: Device wake-up interrupt * diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 59d8d0d14824..327f9e374b44 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -640,8 +640,11 @@ static int bcma_device_probe(struct device *dev) drv); int err = 0; + get_device(dev); if (adrv->probe) err = adrv->probe(core); + if (err) + put_device(dev); return err; } @@ -654,6 +657,7 @@ static int bcma_device_remove(struct device *dev) if (adrv->remove) adrv->remove(core); + put_device(dev); return 0; } diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 80cf8add46ff..cec36d5c24f5 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1108,9 +1108,12 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) if ((unsigned int) info->lo_encrypt_key_size > LO_KEY_SIZE) return -EINVAL; + /* I/O need to be drained during transfer transition */ + blk_mq_freeze_queue(lo->lo_queue); + err = loop_release_xfer(lo); if (err) - return err; + goto exit; if (info->lo_encrypt_type) { unsigned int type = info->lo_encrypt_type; @@ -1125,12 +1128,14 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) err = loop_init_xfer(lo, xfer, info); if (err) - return err; + goto exit; if (lo->lo_offset != info->lo_offset || lo->lo_sizelimit != info->lo_sizelimit) - if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) - return -EFBIG; + if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) { + err = -EFBIG; + goto exit; + } loop_config_discard(lo); @@ -1148,13 +1153,6 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) (info->lo_flags & LO_FLAGS_AUTOCLEAR)) lo->lo_flags ^= LO_FLAGS_AUTOCLEAR; - if ((info->lo_flags & LO_FLAGS_PARTSCAN) && - !(lo->lo_flags & LO_FLAGS_PARTSCAN)) { - lo->lo_flags |= LO_FLAGS_PARTSCAN; - lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN; - loop_reread_partitions(lo, lo->lo_device); - } - lo->lo_encrypt_key_size = info->lo_encrypt_key_size; lo->lo_init[0] = info->lo_init[0]; lo->lo_init[1] = info->lo_init[1]; @@ -1167,7 +1165,17 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) /* update dio if lo_offset or transfer is changed */ __loop_update_dio(lo, lo->use_dio); - return 0; + exit: + blk_mq_unfreeze_queue(lo->lo_queue); + + if (!err && (info->lo_flags & LO_FLAGS_PARTSCAN) && + !(lo->lo_flags & LO_FLAGS_PARTSCAN)) { + lo->lo_flags |= LO_FLAGS_PARTSCAN; + lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN; + loop_reread_partitions(lo, lo->lo_device); + } + + return err; } static int @@ -1657,7 +1665,7 @@ static int loop_queue_rq(struct blk_mq_hw_ctx *hctx, blk_mq_start_request(bd->rq); if (lo->lo_state != Lo_bound) - return -EIO; + return BLK_MQ_RQ_QUEUE_ERROR; if (lo->use_dio && !(cmd->rq->cmd_flags & (REQ_FLUSH | REQ_DISCARD))) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index eada042b6eab..76ac3179f25c 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -1391,8 +1391,14 @@ static ssize_t hot_remove_store(struct class *class, return ret ? ret : count; } +/* + * NOTE: hot_add attribute is not the usual read-only sysfs attribute. In a + * sense that reading from this file does alter the state of your system -- it + * creates a new un-initialized zram device and returns back this device's + * device_id (or an error code if it fails to create a new device). + */ static struct class_attribute zram_control_class_attrs[] = { - __ATTR_RO(hot_add), + __ATTR(hot_add, 0400, hot_add_show, NULL), __ATTR_WO(hot_remove), __ATTR_NULL, }; diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 0beaa52df66b..5df8e1234505 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -94,6 +94,7 @@ static const struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x04CA, 0x300f) }, { USB_DEVICE(0x04CA, 0x3010) }, { USB_DEVICE(0x04CA, 0x3014) }, + { USB_DEVICE(0x04CA, 0x3018) }, { USB_DEVICE(0x0930, 0x0219) }, { USB_DEVICE(0x0930, 0x021c) }, { USB_DEVICE(0x0930, 0x0220) }, @@ -160,6 +161,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3018), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c index 061177173ab8..1ed366fd3d71 100644 --- a/drivers/bluetooth/btfm_slim_codec.c +++ b/drivers/bluetooth/btfm_slim_codec.c @@ -92,6 +92,9 @@ static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream, return; } + if (dai->id == BTFM_FM_SLIM_TX) + goto out; + /* Search for dai->id matched port handler */ for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) && @@ -105,6 +108,7 @@ static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream, } btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan); +out: btfm_slim_hw_deinit(btfmslim); } @@ -167,6 +171,61 @@ int btfm_slim_dai_prepare(struct snd_pcm_substream *substream, return ret; } +static int btfm_slim_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = -EINVAL, i; + struct btfmslim *btfmslim = dai->dev->platform_data; + struct btfmslim_ch *ch; + uint8_t rxport, grp = false, nchan = 1; + + BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name, + dai->id, dai->rate); + + switch (dai->id) { + case BTFM_FM_SLIM_TX: + grp = true; nchan = 2; + ch = btfmslim->tx_chs; + rxport = 0; + break; + case BTFM_BT_SCO_SLIM_TX: + ch = btfmslim->tx_chs; + rxport = 0; + break; + case BTFM_BT_SCO_A2DP_SLIM_RX: + case BTFM_BT_SPLIT_A2DP_SLIM_RX: + ch = btfmslim->rx_chs; + rxport = 1; + break; + case BTFM_SLIM_NUM_CODEC_DAIS: + default: + BTFMSLIM_ERR("dai->id is invalid:%d", dai->id); + goto out; + } + + if (dai->id != BTFM_FM_SLIM_TX) { + ret = 0; + goto out; + } + + /* Search for dai->id matched port handler */ + for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && + (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) && + (ch->id != dai->id); ch++, i++) + ; + + if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) || + (ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) { + BTFMSLIM_ERR("ch is invalid!!"); + goto out; + } + + btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan); + +out: + return ret; +} + /* This function will be called once during boot up */ static int btfm_slim_dai_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, unsigned int *tx_slot, @@ -306,6 +365,7 @@ static struct snd_soc_dai_ops btfmslim_dai_ops = { .shutdown = btfm_slim_dai_shutdown, .hw_params = btfm_slim_dai_hw_params, .prepare = btfm_slim_dai_prepare, + .hw_free = btfm_slim_dai_hw_free, .set_channel_map = btfm_slim_dai_set_channel_map, .get_channel_map = btfm_slim_dai_get_channel_map, }; diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c index 72e28da4bd3b..d61454ac6e84 100644 --- a/drivers/bluetooth/btfm_slim_wcn3990.c +++ b/drivers/bluetooth/btfm_slim_wcn3990.c @@ -39,6 +39,7 @@ int btfm_slim_chrk_hw_init(struct btfmslim *btfmslim) { int ret = 0; uint8_t reg_val; + uint16_t reg; BTFMSLIM_DBG(""); @@ -46,20 +47,20 @@ int btfm_slim_chrk_hw_init(struct btfmslim *btfmslim) return -EINVAL; /* Get SB_SLAVE_HW_REV_MSB value*/ - ret = btfm_slim_read(btfmslim, CHRK_SB_SLAVE_HW_REV_MSB, 1, - ®_val, IFD); + reg = CHRK_SB_SLAVE_HW_REV_MSB; + ret = btfm_slim_read(btfmslim, reg, 1, ®_val, IFD); if (ret) { - BTFMSLIM_ERR("failed to read (%d)", ret); + BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg); goto error; } BTFMSLIM_DBG("Major Rev: 0x%x, Minor Rev: 0x%x", (reg_val & 0xF0) >> 4, (reg_val & 0x0F)); /* Get SB_SLAVE_HW_REV_LSB value*/ - ret = btfm_slim_read(btfmslim, CHRK_SB_SLAVE_HW_REV_LSB, 1, - ®_val, IFD); + reg = CHRK_SB_SLAVE_HW_REV_LSB; + ret = btfm_slim_read(btfmslim, reg, 1, ®_val, IFD); if (ret) { - BTFMSLIM_ERR("failed to read (%d)", ret); + BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg); goto error; } BTFMSLIM_DBG("Step Rev: 0x%x", reg_val); @@ -80,41 +81,41 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, if (rxport) { /* Port enable */ reg = CHRK_SB_PGD_PORT_RX_CFGN(port_num - 0x10); - } else { /* txport */ - /* Multiple Channel Setting - only FM Tx will be multiple - * channel - */ - if (enable && (port_num == CHRK_SB_PGD_PORT_TX1_FM || - port_num == CHRK_SB_PGD_PORT_TX2_FM)) { - - reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) | - (0x1 << CHRK_SB_PGD_PORT_TX2_FM); - reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); - ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); - if (ret) { - BTFMSLIM_ERR("failed to write (%d)", ret); - goto error; - } - } + goto enable_disable_rxport; + } + /* txport */ + if (!enable) + goto enable_disable_txport; - /* Enable Tx port hw auto recovery for underrun or - * overrun error - */ - reg_val = (enable) ? (CHRK_ENABLE_OVERRUN_AUTO_RECOVERY | - CHRK_ENABLE_UNDERRUN_AUTO_RECOVERY) : 0x0; + /* Multiple Channel Setting - only for FM Tx */ + if (port_num == CHRK_SB_PGD_PORT_TX1_FM || + port_num == CHRK_SB_PGD_PORT_TX2_FM) { - ret = btfm_slim_write(btfmslim, - CHRK_SB_PGD_PORT_TX_OR_UR_CFGN(port_num), 1, - ®_val, IFD); + reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) | + (0x1 << CHRK_SB_PGD_PORT_TX2_FM); + reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); if (ret) { - BTFMSLIM_ERR("failed to write (%d)", ret); + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); goto error; } + } - /* Port enable */ - reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num); + /* Enable Tx port hw auto recovery for underrun or overrun error */ + reg_val = (CHRK_ENABLE_OVERRUN_AUTO_RECOVERY | + CHRK_ENABLE_UNDERRUN_AUTO_RECOVERY); + reg = CHRK_SB_PGD_PORT_TX_OR_UR_CFGN(port_num); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); + goto error; } +enable_disable_txport: + /* Port enable */ + reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num); + +enable_disable_rxport: if (enable) /* Set water mark to 1 and enable the port */ reg_val = CHRK_SB_PGD_PORT_ENABLE | CHRK_SB_PGD_PORT_WM_LB; @@ -123,7 +124,7 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); if (ret) - BTFMSLIM_ERR("failed to write (%d)", ret); + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); error: return ret; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index c306b483de60..cd6b141b9825 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -208,6 +208,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3018), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bus/vexpress-config.c b/drivers/bus/vexpress-config.c index 6575c0fe6a4e..27ea64fa4f9b 100644 --- a/drivers/bus/vexpress-config.c +++ b/drivers/bus/vexpress-config.c @@ -171,6 +171,7 @@ static int vexpress_config_populate(struct device_node *node) { struct device_node *bridge; struct device *parent; + int ret; bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0); if (!bridge) @@ -181,7 +182,11 @@ static int vexpress_config_populate(struct device_node *node) if (WARN_ON(!parent)) return -ENODEV; - return of_platform_populate(node, NULL, NULL, parent); + ret = of_platform_populate(node, NULL, NULL, parent); + + put_device(parent); + + return ret; } static int __init vexpress_config_init(void) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 7820f5625266..e0106a7e31fa 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1435,6 +1435,12 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode, int err = 0; struct timespec invoket; + VERIFY(err, fl->sctx); + if (err) + goto bail; + VERIFY(err, fl->cid >= 0 && fl->cid < NUM_CHANNELS); + if (err) + goto bail; if (fl->profile) getnstimeofday(&invoket); if (!kernel) { diff --git a/drivers/char/diag/diagfwd_mhi.c b/drivers/char/diag/diagfwd_mhi.c index df26e2522baf..8b0e1f32bdc5 100644 --- a/drivers/char/diag/diagfwd_mhi.c +++ b/drivers/char/diag/diagfwd_mhi.c @@ -665,8 +665,7 @@ static int diag_mhi_register_ch(int id, struct diag_mhi_ch_t *ch) atomic_set(&(ch->opened), 0); ctxt = SET_CH_CTXT(id, ch->type); ch->client_info.mhi_client_cb = mhi_notifier; - return mhi_register_channel(&ch->hdl, ch->chan, 0, &ch->client_info, - (void *)(uintptr_t)ctxt); + return mhi_register_channel(&ch->hdl, NULL); } int diag_mhi_init() diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c index 3f9f7a999fdf..b04008c8fec3 100644 --- a/drivers/char/diag/diagfwd_peripheral.c +++ b/drivers/char/diag/diagfwd_peripheral.c @@ -855,7 +855,7 @@ int diagfwd_channel_open(struct diagfwd_info *fwd_info) __func__, fwd_info->peripheral, fwd_info->type); return 0; } - + mutex_lock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]); fwd_info->ch_open = 1; diagfwd_buffers_init(fwd_info); diagfwd_write_buffers_init(fwd_info); @@ -873,7 +873,7 @@ int diagfwd_channel_open(struct diagfwd_info *fwd_info) if (fwd_info->p_ops && fwd_info->p_ops->open) fwd_info->p_ops->open(fwd_info->ctxt); } - + mutex_unlock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]); return 0; } @@ -883,6 +883,7 @@ int diagfwd_channel_close(struct diagfwd_info *fwd_info) if (!fwd_info) return -EIO; + mutex_lock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]); fwd_info->ch_open = 0; if (fwd_info && fwd_info->c_ops && fwd_info->c_ops->close) fwd_info->c_ops->close(fwd_info); @@ -898,7 +899,7 @@ int diagfwd_channel_close(struct diagfwd_info *fwd_info) } DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "p: %d t: %d considered closed\n", fwd_info->peripheral, fwd_info->type); - + mutex_unlock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]); return 0; } diff --git a/drivers/char/hw_random/msm-rng.c b/drivers/char/hw_random/msm-rng.c index 96fb986402eb..296b23960815 100644 --- a/drivers/char/hw_random/msm-rng.c +++ b/drivers/char/hw_random/msm-rng.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2013,2015,2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -156,6 +156,7 @@ static int msm_rng_probe(struct platform_device *pdev) rng->hwrng.init = msm_rng_init, rng->hwrng.cleanup = msm_rng_cleanup, rng->hwrng.read = msm_rng_read, + rng->hwrng.quality = 700; ret = devm_hwrng_register(&pdev->dev, &rng->hwrng); if (ret) { diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index 3111f2778079..849f2e29c243 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -305,7 +305,6 @@ static int tpmfront_probe(struct xenbus_device *dev, rv = setup_ring(dev, priv); if (rv) { chip = dev_get_drvdata(&dev->dev); - tpm_chip_unregister(chip); ring_free(priv); return rv; } diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 6029313aa995..35ab89fe9d7b 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -1082,7 +1082,9 @@ static void bcm2835_pll_divider_off(struct clk_hw *hw) cprman_write(cprman, data->cm_reg, (cprman_read(cprman, data->cm_reg) & ~data->load_mask) | data->hold_mask); - cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE); + cprman_write(cprman, data->a2w_reg, + cprman_read(cprman, data->a2w_reg) | + A2W_PLL_CHANNEL_DISABLE); spin_unlock(&cprman->regs_lock); } diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 43f9d15255f4..763aed2de893 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -247,7 +247,7 @@ static int wm831x_clkout_is_prepared(struct clk_hw *hw) if (ret < 0) { dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n", ret); - return true; + return false; } return (ret & WM831X_CLKOUT_ENA) != 0; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index e8e48015d3af..4a9e034f939f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2874,8 +2874,6 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) goto err_out; } - clk_debug_measure_add(core->hw, core->dentry); - ret = 0; goto out; @@ -3005,10 +3003,8 @@ static int __init clk_debug_init(void) return -ENOMEM; mutex_lock(&clk_debug_lock); - hlist_for_each_entry(core, &clk_debug_list, debug_node) { - clk_register_debug(core->hw, core->dentry); + hlist_for_each_entry(core, &clk_debug_list, debug_node) clk_debug_create_one(core, rootdir); - } inited = 1; mutex_unlock(&clk_debug_lock); diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h index 25bf4bc85f5c..87af7af7aac2 100644 --- a/drivers/clk/clk.h +++ b/drivers/clk/clk.h @@ -23,8 +23,6 @@ void __clk_free_clk(struct clk *clk); /* Debugfs API to print the enabled clocks */ void clock_debug_print_enabled(void); -int clk_register_debug(struct clk_hw *hw, struct dentry *dentry); -void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry); void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f); #else diff --git a/drivers/clk/imx/clk-imx31.c b/drivers/clk/imx/clk-imx31.c index 6a964144a5b5..6a49ba2b9671 100644 --- a/drivers/clk/imx/clk-imx31.c +++ b/drivers/clk/imx/clk-imx31.c @@ -157,10 +157,8 @@ static void __init _mx31_clocks_init(unsigned long fref) } } -int __init mx31_clocks_init(void) +int __init mx31_clocks_init(unsigned long fref) { - u32 fref = 26000000; /* default */ - _mx31_clocks_init(fref); clk_register_clkdev(clk[gpt_gate], "per", "imx-gpt.0"); diff --git a/drivers/clk/msm/clock-alpha-pll.c b/drivers/clk/msm/clock-alpha-pll.c index b9a1167a790d..834315aa4993 100644 --- a/drivers/clk/msm/clock-alpha-pll.c +++ b/drivers/clk/msm/clock-alpha-pll.c @@ -612,13 +612,17 @@ static int alpha_pll_set_rate(struct clk *c, unsigned long rate) return -EINVAL; } + if (pll->no_irq_dis) + spin_lock(&c->lock); + else + spin_lock_irqsave(&c->lock, flags); + /* * For PLLs that do not support dynamic programming (dynamic_update * is not set), ensure PLL is off before changing rate. For * optimization reasons, assume no downstream clock is actively * using it. */ - spin_lock_irqsave(&c->lock, flags); if (c->count && !pll->dynamic_update) c->ops->disable(c); @@ -644,7 +648,10 @@ static int alpha_pll_set_rate(struct clk *c, unsigned long rate) if (c->count && !pll->dynamic_update) c->ops->enable(c); - spin_unlock_irqrestore(&c->lock, flags); + if (pll->no_irq_dis) + spin_unlock(&c->lock); + else + spin_unlock_irqrestore(&c->lock, flags); return 0; } diff --git a/drivers/clk/msm/clock-cpu-8996.c b/drivers/clk/msm/clock-cpu-8996.c index 7e03a599fccc..bcda6f31d6f5 100644 --- a/drivers/clk/msm/clock-cpu-8996.c +++ b/drivers/clk/msm/clock-cpu-8996.c @@ -238,6 +238,7 @@ static struct alpha_pll_clk perfcl_alt_pll = { .post_div_config = 0x100, /* Div-2 */ .config_ctl_val = 0x4001051B, .offline_bit_workaround = true, + .no_irq_dis = true, .c = { .always_on = true, .parent = &alpha_xo_ao.c, @@ -300,6 +301,7 @@ static struct alpha_pll_clk pwrcl_alt_pll = { .post_div_config = 0x100, /* Div-2 */ .config_ctl_val = 0x4001051B, .offline_bit_workaround = true, + .no_irq_dis = true, .c = { .always_on = true, .dbg_name = "pwrcl_alt_pll", diff --git a/drivers/clk/msm/clock-gcc-8996.c b/drivers/clk/msm/clock-gcc-8996.c index a9e0b53c3b22..e93e9c494023 100644 --- a/drivers/clk/msm/clock-gcc-8996.c +++ b/drivers/clk/msm/clock-gcc-8996.c @@ -105,6 +105,8 @@ DEFINE_CLK_DUMMY(gcc_ce1_axi_m_clk, 0); DEFINE_CLK_DUMMY(measure_only_bimc_hmss_axi_clk, 0); DEFINE_CLK_RPM_SMD_XO_BUFFER(ln_bb_clk, ln_bb_a_clk, LN_BB_CLK_ID); +DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(ln_bb_clk_pin, ln_bb_a_clk_pin, + LN_BB_CLK_PIN_ID); static DEFINE_CLK_VOTER(mcd_ce1_clk, &ce1_clk.c, 85710000); static DEFINE_CLK_VOTER(pnoc_keepalive_a_clk, &pnoc_a_clk.c, LONG_MAX); static DEFINE_CLK_VOTER(pnoc_msmbus_clk, &pnoc_clk.c, LONG_MAX); @@ -1340,9 +1342,11 @@ static struct rcg_clk pdm2_clk_src = { }, }; -/* Frequency table might change later */ static struct clk_freq_tbl ftbl_qspi_ser_clk_src[] = { - F( 192000000, gpll4_out_main, 2, 0, 0), + F( 75000000, gpll0_out_main, 8, 0, 0), + F( 150000000, gpll0_out_main, 4, 0, 0), + F( 256000000, gpll4_out_main, 1.5, 0, 0), + F( 300000000, gpll0_out_main, 2, 0, 0), F_END }; @@ -3387,6 +3391,8 @@ static struct clk_lookup msm_clocks_rpm_8996[] = { CLK_LIST(ipa_clk), CLK_LIST(ln_bb_clk), CLK_LIST(ln_bb_a_clk), + CLK_LIST(ln_bb_clk_pin), + CLK_LIST(ln_bb_a_clk_pin), CLK_LIST(mcd_ce1_clk), CLK_LIST(pnoc_keepalive_a_clk), CLK_LIST(pnoc_msmbus_clk), diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 317091a8003d..4c18181c047c 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -12,7 +12,7 @@ clk-qcom-y += clk-regmap-mux.o clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o clk-qcom-y += clk-hfpll.o clk-qcom-y += reset.o clk-voter.o -clk-qcom-y += clk-dummy.o +clk-qcom-y += clk-dummy.o clk-debug.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o gdsc-regulator.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index ca6010db8d78..3e9cd9909b86 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -21,6 +21,7 @@ #include <linux/regmap.h> #include "clk-branch.h" +#include "clk-debug.h" #include "clk-regmap.h" #include "common.h" @@ -250,6 +251,7 @@ const struct clk_ops clk_branch2_ops = { .is_enabled = clk_is_enabled_regmap, .set_flags = clk_branch_set_flags, .list_registers = clk_branch2_list_registers, + .debug_init = clk_debug_measure_add, }; EXPORT_SYMBOL_GPL(clk_branch2_ops); @@ -384,6 +386,7 @@ const struct clk_ops clk_gate2_ops = { .is_enabled = clk_is_enabled_regmap, .list_registers = clk_gate2_list_registers, .set_flags = clk_gate2_set_flags, + .debug_init = clk_debug_measure_add, }; EXPORT_SYMBOL_GPL(clk_gate2_ops); diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c index 6b00bee337a1..f82ddc3b008b 100644 --- a/drivers/clk/qcom/clk-cpu-osm.c +++ b/drivers/clk/qcom/clk-cpu-osm.c @@ -39,6 +39,7 @@ #include "common.h" #include "clk-regmap.h" #include "clk-rcg.h" +#include "clk-debug.h" enum { LMH_LITE_CLK_SRC, @@ -757,6 +758,7 @@ static struct clk_ops clk_ops_cpu_osm = { .round_rate = clk_osm_round_rate, .list_rate = clk_osm_list_rate, .recalc_rate = clk_osm_recalc_rate, + .debug_init = clk_debug_measure_add, }; static const struct parent_map gcc_parent_map_1[] = { diff --git a/drivers/clk/qcom/clk-debug.c b/drivers/clk/qcom/clk-debug.c new file mode 100644 index 000000000000..50d0d01188ed --- /dev/null +++ b/drivers/clk/qcom/clk-debug.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2013-2014, 2016-2017, + * + * The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/export.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/of.h> + +#include "clk-regmap.h" +#include "clk-debug.h" +#include "common.h" + +static struct clk_hw *measure; + +static DEFINE_SPINLOCK(clk_reg_lock); +static DEFINE_MUTEX(clk_debug_lock); + +#define TCXO_DIV_4_HZ 4800000 +#define SAMPLE_TICKS_1_MS 0x1000 +#define SAMPLE_TICKS_14_MS 0x10000 + +#define XO_DIV4_CNT_DONE BIT(25) +#define CNT_EN BIT(20) +#define MEASURE_CNT BM(24, 0) + +/* Sample clock for 'ticks' reference clock ticks. */ +static u32 run_measurement(unsigned ticks, struct regmap *regmap, + u32 ctl_reg, u32 status_reg) +{ + u32 regval; + + /* Stop counters and set the XO4 counter start value. */ + regmap_write(regmap, ctl_reg, ticks); + + regmap_read(regmap, status_reg, ®val); + + /* Wait for timer to become ready. */ + while ((regval & XO_DIV4_CNT_DONE) != 0) { + cpu_relax(); + regmap_read(regmap, status_reg, ®val); + } + + /* Run measurement and wait for completion. */ + regmap_write(regmap, ctl_reg, (CNT_EN|ticks)); + + regmap_read(regmap, status_reg, ®val); + + while ((regval & XO_DIV4_CNT_DONE) == 0) { + cpu_relax(); + regmap_read(regmap, status_reg, ®val); + } + + /* Return measured ticks. */ + regmap_read(regmap, status_reg, ®val); + regval &= MEASURE_CNT; + + return regval; +} + +/* + * Perform a hardware rate measurement for a given clock. + * FOR DEBUG USE ONLY: Measurements take ~15 ms! + */ +static unsigned long clk_debug_mux_measure_rate(struct clk_hw *hw) +{ + unsigned long flags, ret = 0; + u32 gcc_xo4_reg, multiplier; + u64 raw_count_short, raw_count_full; + struct clk_debug_mux *meas = to_clk_measure(hw); + struct measure_clk_data *data = meas->priv; + + clk_prepare_enable(data->cxo); + + spin_lock_irqsave(&clk_reg_lock, flags); + + multiplier = meas->multiplier + 1; + + /* Enable CXO/4 and RINGOSC branch. */ + regmap_read(meas->regmap[GCC], data->xo_div4_cbcr, &gcc_xo4_reg); + gcc_xo4_reg |= BIT(0); + regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg); + + /* + * The ring oscillator counter will not reset if the measured clock + * is not running. To detect this, run a short measurement before + * the full measurement. If the raw results of the two are the same + * then the clock must be off. + */ + + /* Run a short measurement. (~1 ms) */ + raw_count_short = run_measurement(SAMPLE_TICKS_1_MS, meas->regmap[GCC], + data->ctl_reg, data->status_reg); + + /* Run a full measurement. (~14 ms) */ + raw_count_full = run_measurement(SAMPLE_TICKS_14_MS, meas->regmap[GCC], + data->ctl_reg, data->status_reg); + + gcc_xo4_reg &= ~BIT(0); + regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg); + + /* Return 0 if the clock is off. */ + if (raw_count_full == raw_count_short) + ret = 0; + else { + /* Compute rate in Hz. */ + raw_count_full = ((raw_count_full * 10) + 15) * TCXO_DIV_4_HZ; + do_div(raw_count_full, ((SAMPLE_TICKS_14_MS * 10) + 35)); + ret = (raw_count_full * multiplier); + } + + spin_unlock_irqrestore(&clk_reg_lock, flags); + + clk_disable_unprepare(data->cxo); + + return ret; +} + +static u8 clk_debug_mux_get_parent(struct clk_hw *hw) +{ + struct clk_debug_mux *meas = to_clk_measure(hw); + int i, num_parents = clk_hw_get_num_parents(hw); + + for (i = 0; i < num_parents; i++) { + if (!strcmp(meas->parent[i].parents, + hw->init->parent_names[i])) { + pr_debug("%s: Clock name %s index %d\n", __func__, + hw->init->name, i); + return i; + } + } + + return 0; +} + +static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_debug_mux *meas = to_clk_measure(hw); + unsigned long lsb = 0; + u32 regval = 0; + int dbg_cc = 0; + + dbg_cc = meas->parent[index].dbg_cc; + + if (dbg_cc != GCC) { + regmap_read(meas->regmap[dbg_cc], 0x0, ®val); + + /* Clear & Set post divider bits */ + if (meas->parent[index].post_div_mask) { + regval &= ~meas->parent[index].post_div_mask; + lsb = find_first_bit((unsigned long *) + &meas->parent[index].post_div_mask, 32); + regval |= (meas->parent[index].post_div_val << lsb) & + meas->parent[index].post_div_mask; + meas->multiplier = meas->parent[index].post_div_val; + } + + if (meas->parent[index].mask) + regval &= ~meas->parent[index].mask << + meas->parent[index].shift; + else + regval &= ~meas->mask; + + regval |= (meas->parent[index].next_sel & meas->mask); + + if (meas->parent[index].en_mask == 0xFF) + /* Skip en_mask */ + regval = regval; + else if (meas->parent[index].en_mask) + regval |= meas->parent[index].en_mask; + else + regval |= meas->en_mask; + + regmap_write(meas->regmap[dbg_cc], 0x0, regval); + } + + /* update the debug sel for GCC */ + regmap_read(meas->regmap[GCC], meas->debug_offset, ®val); + + /* clear post divider bits */ + regval &= ~BM(15, 12); + lsb = find_first_bit((unsigned long *) + &meas->parent[index].post_div_mask, 32); + regval |= (meas->parent[index].post_div_val << lsb) & + meas->parent[index].post_div_mask; + meas->multiplier = meas->parent[index].post_div_val; + regval &= ~meas->mask; + regval |= (meas->parent[index].sel & meas->mask); + regval |= meas->en_mask; + + regmap_write(meas->regmap[GCC], meas->debug_offset, regval); + + return 0; +} + +const struct clk_ops clk_debug_mux_ops = { + .get_parent = clk_debug_mux_get_parent, + .set_parent = clk_debug_mux_set_parent, +}; +EXPORT_SYMBOL(clk_debug_mux_ops); + +static int clk_debug_measure_get(void *data, u64 *val) +{ + struct clk_hw *hw = data, *par; + int ret = 0; + unsigned long meas_rate, sw_rate; + + mutex_lock(&clk_debug_lock); + + ret = clk_set_parent(measure->clk, hw->clk); + if (!ret) { + par = measure; + while (par && par != hw) { + if (par->init->ops->enable) + par->init->ops->enable(par); + par = clk_hw_get_parent(par); + } + *val = clk_debug_mux_measure_rate(measure); + } + + meas_rate = clk_get_rate(hw->clk); + sw_rate = clk_get_rate(clk_hw_get_parent(measure)->clk); + if (sw_rate && meas_rate >= (sw_rate * 2)) + *val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate); + + mutex_unlock(&clk_debug_lock); + + return ret; +} + +DEFINE_SIMPLE_ATTRIBUTE(clk_measure_fops, clk_debug_measure_get, + NULL, "%lld\n"); + +int clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry) +{ + if (IS_ERR_OR_NULL(measure)) { + pr_err_once("Please check if `measure` clk is registered!!!\n"); + return 0; + } + + if (clk_set_parent(measure->clk, hw->clk)) + return 0; + + debugfs_create_file("clk_measure", S_IRUGO, dentry, hw, + &clk_measure_fops); + return 0; +} +EXPORT_SYMBOL(clk_debug_measure_add); + +int clk_register_debug(struct clk_hw *hw) +{ + if (IS_ERR_OR_NULL(measure)) { + if (hw->init->flags & CLK_IS_MEASURE) { + measure = hw; + return 0; + } + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(clk_register_debug); + diff --git a/drivers/clk/qcom/clk-debug.h b/drivers/clk/qcom/clk-debug.h new file mode 100644 index 000000000000..40e8730a9466 --- /dev/null +++ b/drivers/clk/qcom/clk-debug.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2014, 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __QCOM_CLK_DEBUG_H__ +#define __QCOM_CLK_DEBUG_H__ + +#include "../clk.h" + +/* Debugfs Measure Clocks */ + +/** + * struct measure_clk_data - Structure of clk measure + * + * @cxo: XO clock. + * @xo_div4_cbcr: offset of debug XO/4 div register. + * @ctl_reg: offset of debug control register. + * @status_reg: offset of debug status register. + * + */ +struct measure_clk_data { + struct clk *cxo; + u32 xo_div4_cbcr; + u32 ctl_reg; + u32 status_reg; +}; + +/** + * List of Debug clock controllers. + */ +enum debug_cc { + GCC, + MMCC, + GPU, + CPU, +}; + +/** + * struct clk_src - Struture of clock source for debug mux + * + * @parents: clock name to be used as parent for debug mux. + * @sel: debug mux index at global clock controller. + * @dbg_cc: indicates the clock controller for recursive debug clock + * controllers. + * @next_sel: indicates the debug mux index at recursive debug mux. + * @mask: indicates the mask required at recursive debug mux. + * @shift: indicates the shift required at recursive debug mux. + * @en_mask: indicates the enable bit mask at recursive debug mux. + * Incase the recursive debug mux does not have a enable bit, + * 0xFF should be used to indicate the same, otherwise global + * enable bit would be used. + * @post_div_mask: indicates the post div mask to be used at debug/recursive + * debug mux. + * @post_div_val: indicates the post div value to be used at debug/recursive + * debug mux. + */ +struct clk_src { + const char *parents; + int sel; + enum debug_cc dbg_cc; + int next_sel; + u32 mask; + u32 shift; + u32 en_mask; + u32 post_div_mask; + u32 post_div_val; +}; + +#define MUX_SRC_LIST(...) \ + .parent = (struct clk_src[]){__VA_ARGS__}, \ + .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__})) + +/** + * struct clk_debug_mux - Struture of clock debug mux + * + * @parent: structure of clk_src + * @num_parents: number of parents + * @regmap: regmaps of debug mux + * @num_parent_regmap: number of regmap of debug mux + * @priv: private measure_clk_data to be used by debug mux + * @en_mask: indicates the enable bit mask at global clock + * controller debug mux. + * @mask: indicates the mask to be used at global clock + * controller debug mux. + * @debug_offset: debug mux offset. + * @hw: handle between common and hardware-specific interfaces. + * @multiplier: internally used by debug mux as post div multiplier. + */ +struct clk_debug_mux { + struct clk_src *parent; + int num_parents; + struct regmap **regmap; + int num_parent_regmap; + void *priv; + u32 en_mask; + u32 mask; + u32 debug_offset; + struct clk_hw hw; + + /* internal */ + u32 multiplier; +}; + +#define to_clk_measure(_hw) container_of((_hw), struct clk_debug_mux, hw) + +extern const struct clk_ops clk_debug_mux_ops; + +int clk_register_debug(struct clk_hw *hw); +int clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry); + +#endif diff --git a/drivers/clk/qcom/clk-dummy.c b/drivers/clk/qcom/clk-dummy.c index e06a829e508c..c08136563e31 100644 --- a/drivers/clk/qcom/clk-dummy.c +++ b/drivers/clk/qcom/clk-dummy.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,6 +17,8 @@ #include <linux/slab.h> #include <linux/reset-controller.h> +#include "clk-debug.h" + struct clk_dummy { struct clk_hw hw; struct reset_controller_dev reset; @@ -65,6 +67,7 @@ struct clk_ops clk_dummy_ops = { .round_rate = dummy_clk_round_rate, .recalc_rate = dummy_clk_recalc_rate, .set_flags = dummy_clk_set_flags, + .debug_init = clk_debug_measure_add, }; EXPORT_SYMBOL_GPL(clk_dummy_ops); diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index 58fbd07e6f15..0bcada9aac5d 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013, 2016-2017, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -192,5 +192,6 @@ extern const struct clk_ops clk_pixel_ops; extern const struct clk_ops clk_gfx3d_ops; extern const struct clk_ops clk_gfx3d_src_ops; extern const struct clk_ops clk_dp_ops; +extern const struct clk_ops clk_esc_ops; #endif diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 632d0f4ac9c1..ff0c8327fabe 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -1349,3 +1349,73 @@ const struct clk_ops clk_gfx3d_src_ops = { .list_registers = clk_rcg2_list_registers, }; EXPORT_SYMBOL_GPL(clk_gfx3d_src_ops); + +static int clk_esc_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + unsigned long parent_rate, div; + u32 mask = BIT(rcg->hid_width) - 1; + struct clk_hw *p; + unsigned long rate = req->rate; + + if (rate == 0) + return -EINVAL; + + p = req->best_parent_hw; + req->best_parent_rate = parent_rate = clk_hw_round_rate(p, rate); + + div = ((2 * parent_rate) / rate) - 1; + div = min_t(u32, div, mask); + + req->rate = calc_rate(parent_rate, 0, 0, 0, div); + + return 0; +} + +static int clk_esc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + struct freq_tbl f = { 0 }; + unsigned long div; + int i, num_parents = clk_hw_get_num_parents(hw); + u32 mask = BIT(rcg->hid_width) - 1; + u32 cfg; + + div = ((2 * parent_rate) / rate) - 1; + div = min_t(u32, div, mask); + + f.pre_div = div; + + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); + cfg &= CFG_SRC_SEL_MASK; + cfg >>= CFG_SRC_SEL_SHIFT; + + for (i = 0; i < num_parents; i++) { + if (cfg == rcg->parent_map[i].cfg) { + f.src = rcg->parent_map[i].src; + return clk_rcg2_configure(rcg, &f); + } + } + + return -EINVAL; +} + +static int clk_esc_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate, u8 index) +{ + return clk_esc_set_rate(hw, rate, parent_rate); +} + +const struct clk_ops clk_esc_ops = { + .is_enabled = clk_rcg2_is_enabled, + .get_parent = clk_rcg2_get_parent, + .set_parent = clk_rcg2_set_parent, + .recalc_rate = clk_rcg2_recalc_rate, + .determine_rate = clk_esc_determine_rate, + .set_rate = clk_esc_set_rate, + .set_rate_and_parent = clk_esc_set_rate_and_parent, + .list_registers = clk_rcg2_list_registers, +}; +EXPORT_SYMBOL(clk_esc_ops); diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 5345e9086627..8dcdf2929ca6 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -30,6 +30,7 @@ #include <dt-bindings/mfd/qcom-rpm.h> #include "clk-voter.h" +#include "clk-debug.h" #define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773 #define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370 @@ -463,6 +464,7 @@ static const struct clk_ops clk_smd_rpm_ops = { .round_rate = clk_smd_rpm_round_rate, .recalc_rate = clk_smd_rpm_recalc_rate, .is_enabled = clk_smd_rpm_is_enabled, + .debug_init = clk_debug_measure_add, }; static const struct clk_ops clk_smd_rpm_branch_ops = { @@ -471,6 +473,7 @@ static const struct clk_ops clk_smd_rpm_branch_ops = { .round_rate = clk_smd_rpm_round_rate, .recalc_rate = clk_smd_rpm_recalc_rate, .is_enabled = clk_smd_rpm_is_enabled, + .debug_init = clk_debug_measure_add, }; /* msm8916 */ diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index f6ce3106ab6f..8ecb207a97e5 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -1,5 +1,7 @@ /* - * Copyright (c) 2013-2014, 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2014, 2016-2017, + * + * The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -287,239 +289,4 @@ int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc) } EXPORT_SYMBOL_GPL(qcom_cc_probe); -/* Debugfs Support */ -static struct clk_hw *measure; - -DEFINE_SPINLOCK(clk_reg_lock); - -/* Sample clock for 'ticks' reference clock ticks. */ -static u32 run_measurement(unsigned ticks, struct regmap *regmap, - u32 ctl_reg, u32 status_reg) -{ - u32 regval; - - /* Stop counters and set the XO4 counter start value. */ - regmap_write(regmap, ctl_reg, ticks); - - regmap_read(regmap, status_reg, ®val); - - /* Wait for timer to become ready. */ - while ((regval & BIT(25)) != 0) { - cpu_relax(); - regmap_read(regmap, status_reg, ®val); - } - - /* Run measurement and wait for completion. */ - regmap_write(regmap, ctl_reg, (BIT(20)|ticks)); - regmap_read(regmap, ctl_reg, ®val); - - regmap_read(regmap, status_reg, ®val); - - while ((regval & BIT(25)) == 0) { - cpu_relax(); - regmap_read(regmap, status_reg, ®val); - } - - /* Return measured ticks. */ - regmap_read(regmap, status_reg, ®val); - regval &= BM(24, 0); - - return regval; -} - -/* - * Perform a hardware rate measurement for a given clock. - * FOR DEBUG USE ONLY: Measurements take ~15 ms! - */ -static unsigned long clk_debug_mux_measure_rate(struct clk_hw *hw) -{ - unsigned long flags, ret = 0; - u32 gcc_xo4_reg, sample_ticks = 0x10000, multiplier; - u64 raw_count_short, raw_count_full; - struct clk_debug_mux *meas = to_clk_measure(hw); - struct measure_clk_data *data = meas->priv; - - clk_prepare_enable(data->cxo); - - spin_lock_irqsave(&clk_reg_lock, flags); - - multiplier = meas->multiplier + 1; - - /* Enable CXO/4 and RINGOSC branch. */ - regmap_read(meas->regmap[GCC], data->xo_div4_cbcr, &gcc_xo4_reg); - gcc_xo4_reg |= BIT(0); - regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg); - - /* - * The ring oscillator counter will not reset if the measured clock - * is not running. To detect this, run a short measurement before - * the full measurement. If the raw results of the two are the same - * then the clock must be off. - */ - - /* Run a short measurement. (~1 ms) */ - raw_count_short = run_measurement(0x1000, meas->regmap[GCC], - data->ctl_reg, data->status_reg); - - /* Run a full measurement. (~14 ms) */ - raw_count_full = run_measurement(sample_ticks, meas->regmap[GCC], - data->ctl_reg, data->status_reg); - - gcc_xo4_reg &= ~BIT(0); - regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg); - - /* Return 0 if the clock is off. */ - if (raw_count_full == raw_count_short) - ret = 0; - else { - /* Compute rate in Hz. */ - raw_count_full = ((raw_count_full * 10) + 15) * 4800000; - do_div(raw_count_full, ((sample_ticks * 10) + 35)); - ret = (raw_count_full * multiplier); - } - - spin_unlock_irqrestore(&clk_reg_lock, flags); - - clk_disable_unprepare(data->cxo); - - return ret; -} - -static u8 clk_debug_mux_get_parent(struct clk_hw *hw) -{ - struct clk_debug_mux *meas = to_clk_measure(hw); - int i, num_parents = clk_hw_get_num_parents(hw); - - for (i = 0; i < num_parents; i++) { - if (!strcmp(meas->parent[i].parents, - hw->init->parent_names[i])) { - pr_debug("%s :Clock name %s index %d\n", __func__, - hw->init->name, i); - return i; - } - } - - return 0; -} - -static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index) -{ - struct clk_debug_mux *meas = to_clk_measure(hw); - unsigned long lsb = 0; - u32 regval = 0; - int dbg_cc = 0; - - dbg_cc = meas->parent[index].dbg_cc; - - if (dbg_cc != GCC) { - regmap_read(meas->regmap[dbg_cc], 0x0, ®val); - - /* Clear & Set post divider bits */ - if (meas->parent[index].post_div_mask) { - regval &= ~meas->parent[index].post_div_mask; - lsb = find_first_bit((unsigned long *) - &meas->parent[index].post_div_mask, 32); - regval |= (meas->parent[index].post_div_val << lsb) & - meas->parent[index].post_div_mask; - meas->multiplier = meas->parent[index].post_div_val; - } - - if (meas->parent[index].mask) - regval &= ~meas->parent[index].mask << - meas->parent[index].shift; - else - regval &= ~meas->mask; - - regval |= (meas->parent[index].next_sel & meas->mask); - - if (meas->parent[index].en_mask == 0xFF) - /* Skip en_mask */ - regval = regval; - else if (meas->parent[index].en_mask) - regval |= meas->parent[index].en_mask; - else - regval |= meas->en_mask; - - regmap_write(meas->regmap[dbg_cc], 0x0, regval); - } - - /* update the debug sel for GCC */ - regmap_read(meas->regmap[GCC], meas->debug_offset, ®val); - - /* clear post divider bits */ - regval &= ~BM(15, 12); - lsb = find_first_bit((unsigned long *) - &meas->parent[index].post_div_mask, 32); - regval |= (meas->parent[index].post_div_val << lsb) & - meas->parent[index].post_div_mask; - meas->multiplier = meas->parent[index].post_div_val; - regval &= ~meas->mask; - regval |= (meas->parent[index].sel & meas->mask); - regval |= meas->en_mask; - - regmap_write(meas->regmap[GCC], meas->debug_offset, regval); - - return 0; -} - -const struct clk_ops clk_debug_mux_ops = { - .get_parent = clk_debug_mux_get_parent, - .set_parent = clk_debug_mux_set_parent, -}; -EXPORT_SYMBOL_GPL(clk_debug_mux_ops); - -static int clk_debug_measure_get(void *data, u64 *val) -{ - struct clk_hw *hw = data, *par; - int ret = 0; - unsigned long meas_rate, sw_rate; - - ret = clk_set_parent(measure->clk, hw->clk); - if (!ret) { - par = measure; - while (par && par != hw) { - if (par->init->ops->enable) - par->init->ops->enable(par); - par = clk_hw_get_parent(par); - } - *val = clk_debug_mux_measure_rate(measure); - } - - meas_rate = clk_get_rate(hw->clk); - sw_rate = clk_get_rate(clk_hw_get_parent(measure)->clk); - if (sw_rate && meas_rate >= (sw_rate * 2)) - *val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate); - - return ret; -} - -DEFINE_SIMPLE_ATTRIBUTE(clk_measure_fops, clk_debug_measure_get, - NULL, "%lld\n"); - -void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry) -{ - if (IS_ERR_OR_NULL(measure)) - return; - - if (clk_set_parent(measure->clk, hw->clk)) - return; - - debugfs_create_file("measure", S_IRUGO, dentry, hw, - &clk_measure_fops); -} -EXPORT_SYMBOL_GPL(clk_debug_measure_add); - -int clk_register_debug(struct clk_hw *hw, struct dentry *dentry) -{ - if (IS_ERR_OR_NULL(measure)) { - if (hw->init->flags & CLK_IS_MEASURE) - measure = hw; - if (!IS_ERR_OR_NULL(measure)) - clk_debug_measure_add(hw, dentry); - } - - return 0; -} -EXPORT_SYMBOL_GPL(clk_register_debug); - MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 95408a47fef0..c532444a0d40 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -13,7 +13,7 @@ #ifndef __QCOM_CLK_COMMON_H__ #define __QCOM_CLK_COMMON_H__ -#include "../clk.h" +#include "clk-debug.h" struct platform_device; struct regmap_config; @@ -54,107 +54,9 @@ extern int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc); extern struct clk_ops clk_dummy_ops; -/* Debugfs Measure Clocks */ - -/** - * struct measure_clk_data - Structure of clk measure - * - * @cxo: XO clock. - * @xo_div4_cbcr: offset of debug XO/4 div register. - * @ctl_reg: offset of debug control register. - * @status_reg: offset of debug status register. - * - */ -struct measure_clk_data { - struct clk *cxo; - u32 xo_div4_cbcr; - u32 ctl_reg; - u32 status_reg; -}; - -/** - * List of Debug clock controllers. - */ -enum debug_cc { - GCC, - MMCC, - GPU, - CPU, -}; - -/** - * struct clk_src - Struture of clock source for debug mux - * - * @parents: clock name to be used as parent for debug mux. - * @sel: debug mux index at global clock controller. - * @dbg_cc: indicates the clock controller for recursive debug clock - * controllers. - * @next_sel: indicates the debug mux index at recursive debug mux. - * @mask: indicates the mask required at recursive debug mux. - * @shift: indicates the shift required at recursive debug mux. - * @en_mask: indicates the enable bit mask at recursive debug mux. - * Incase the recursive debug mux does not have a enable bit, - * 0xFF should be used to indicate the same, otherwise global - * enable bit would be used. - * @post_div_mask: indicates the post div mask to be used at debug/recursive - * debug mux. - * @post_div_val: indicates the post div value to be used at debug/recursive - * debug mux. - */ -struct clk_src { - const char *parents; - int sel; - enum debug_cc dbg_cc; - int next_sel; - u32 mask; - u32 shift; - u32 en_mask; - u32 post_div_mask; - u32 post_div_val; -}; - -#define MUX_SRC_LIST(...) \ - .parent = (struct clk_src[]){__VA_ARGS__}, \ - .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__})) - -/** - * struct clk_debug_mux - Struture of clock debug mux - * - * @parent: structure of clk_src - * @num_parents: number of parents - * @regmap: regmaps of debug mux - * @num_parent_regmap: number of regmap of debug mux - * @priv: private measure_clk_data to be used by debug mux - * @en_mask: indicates the enable bit mask at global clock - * controller debug mux. - * @mask: indicates the mask to be used at global clock - * controller debug mux. - * @debug_offset: Start of debug mux offset. - * @hw: handle between common and hardware-specific interfaces. - * @multiplier: internally used by debug mux as post div multiplier. - */ -struct clk_debug_mux { - struct clk_src *parent; - int num_parents; - struct regmap **regmap; - int num_parent_regmap; - void *priv; - u32 en_mask; - u32 mask; - u32 debug_offset; - struct clk_hw hw; - - /* internal */ - u32 multiplier; -}; - #define BM(msb, lsb) (((((uint32_t)-1) << (31-msb)) >> (31-msb+lsb)) << lsb) #define BVAL(msb, lsb, val) (((val) << lsb) & BM(msb, lsb)) -#define to_clk_measure(_hw) container_of((_hw), struct clk_debug_mux, hw) - -extern const struct clk_ops clk_debug_mux_ops; - #define WARN_CLK(core, name, cond, fmt, ...) do { \ clk_debug_print_hw(core, NULL); \ WARN(cond, "%s: " fmt, name, ##__VA_ARGS__); \ diff --git a/drivers/clk/qcom/gcc-sdm660.c b/drivers/clk/qcom/gcc-sdm660.c index c282253da584..b55310e091af 100644 --- a/drivers/clk/qcom/gcc-sdm660.c +++ b/drivers/clk/qcom/gcc-sdm660.c @@ -27,6 +27,7 @@ #include "clk-alpha-pll.h" #include "clk-branch.h" +#include "clk-debug.h" #include "common.h" #include "clk-pll.h" #include "clk-regmap.h" @@ -3328,7 +3329,11 @@ static int clk_debug_660_probe(struct platform_device *pdev) return PTR_ERR(clk); } - dev_info(&pdev->dev, "Registered debug mux successfully\n"); + ret = clk_register_debug(&gcc_debug_mux.hw); + if (ret) + dev_err(&pdev->dev, "Could not register Measure clock\n"); + else + dev_info(&pdev->dev, "Registered debug mux successfully\n"); return ret; } diff --git a/drivers/clk/qcom/mmcc-sdm660.c b/drivers/clk/qcom/mmcc-sdm660.c index 0bf7ef05ed06..910c36c65b6a 100644 --- a/drivers/clk/qcom/mmcc-sdm660.c +++ b/drivers/clk/qcom/mmcc-sdm660.c @@ -978,12 +978,11 @@ static struct clk_rcg2 esc0_clk_src = { .mnd_width = 0, .hid_width = 5, .parent_map = mmcc_parent_map_1, - .freq_tbl = ftbl_dp_aux_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "esc0_clk_src", .parent_names = mmcc_parent_names_1, .num_parents = 4, - .ops = &clk_rcg2_ops, + .ops = &clk_esc_ops, VDD_DIG_FMAX_MAP1( LOWER, 19200000), }, @@ -994,12 +993,11 @@ static struct clk_rcg2 esc1_clk_src = { .mnd_width = 0, .hid_width = 5, .parent_map = mmcc_parent_map_1, - .freq_tbl = ftbl_dp_aux_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "esc1_clk_src", .parent_names = mmcc_parent_names_1, .num_parents = 4, - .ops = &clk_rcg2_ops, + .ops = &clk_esc_ops, VDD_DIG_FMAX_MAP1( LOWER, 19200000), }, diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c index 8831e1a05367..11d8aa3ec186 100644 --- a/drivers/clk/ti/clk-3xxx.c +++ b/drivers/clk/ti/clk-3xxx.c @@ -22,13 +22,6 @@ #include "clock.h" -/* - * DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks - * that are sourced by DPLL5, and both of these require this clock - * to be at 120 MHz for proper operation. - */ -#define DPLL5_FREQ_FOR_USBHOST 120000000 - #define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1 #define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5 #define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8 @@ -546,14 +539,21 @@ void __init omap3_clk_lock_dpll5(void) struct clk *dpll5_clk; struct clk *dpll5_m2_clk; + /* + * Errata sprz319f advisory 2.1 documents a USB host clock drift issue + * that can be worked around using specially crafted dpll5 settings + * with a dpll5_m2 divider set to 8. Set the dpll5 rate to 8x the USB + * host clock rate, its .set_rate handler() will detect that frequency + * and use the errata settings. + */ dpll5_clk = clk_get(NULL, "dpll5_ck"); - clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST); + clk_set_rate(dpll5_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST * 8); clk_prepare_enable(dpll5_clk); - /* Program dpll5_m2_clk divider for no division */ + /* Program dpll5_m2_clk divider */ dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck"); clk_prepare_enable(dpll5_m2_clk); - clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST); + clk_set_rate(dpll5_m2_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST); clk_disable_unprepare(dpll5_m2_clk); clk_disable_unprepare(dpll5_clk); diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index 90f3f472ae1c..13c37f48d9d6 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -257,11 +257,20 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw, unsigned long parent_rate); +/* + * OMAP3_DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks + * that are sourced by DPLL5, and both of these require this clock + * to be at 120 MHz for proper operation. + */ +#define OMAP3_DPLL5_FREQ_FOR_USBHOST 120000000 + unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate); int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate, unsigned long parent_rate); int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index); +int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); void omap3_clk_lock_dpll5(void); unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 5519b386edc0..f9a5089ddc79 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -114,6 +114,18 @@ static const struct clk_ops omap3_dpll_ck_ops = { .round_rate = &omap2_dpll_round_rate, }; +static const struct clk_ops omap3_dpll5_ck_ops = { + .enable = &omap3_noncore_dpll_enable, + .disable = &omap3_noncore_dpll_disable, + .get_parent = &omap2_init_dpll_parent, + .recalc_rate = &omap3_dpll_recalc, + .set_rate = &omap3_dpll5_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap3_noncore_dpll_determine_rate, + .round_rate = &omap2_dpll_round_rate, +}; + static const struct clk_ops omap3_dpll_per_ck_ops = { .enable = &omap3_noncore_dpll_enable, .disable = &omap3_noncore_dpll_disable, @@ -461,7 +473,12 @@ static void __init of_ti_omap3_dpll_setup(struct device_node *node) .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED), }; - of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd); + if ((of_machine_is_compatible("ti,omap3630") || + of_machine_is_compatible("ti,omap36xx")) && + !strcmp(node->name, "dpll5_ck")) + of_ti_dpll_setup(node, &omap3_dpll5_ck_ops, &dd); + else + of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd); } CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock", of_ti_omap3_dpll_setup); diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c index f4dec00fb684..0e9119fae760 100644 --- a/drivers/clk/ti/dpll3xxx.c +++ b/drivers/clk/ti/dpll3xxx.c @@ -815,3 +815,70 @@ int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate, index); } + +/* Apply DM3730 errata sprz319 advisory 2.1. */ +static bool omap3_dpll5_apply_errata(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct omap3_dpll5_settings { + unsigned int rate, m, n; + }; + + static const struct omap3_dpll5_settings precomputed[] = { + /* + * From DM3730 errata advisory 2.1, table 35 and 36. + * The N value is increased by 1 compared to the tables as the + * errata lists register values while last_rounded_field is the + * real divider value. + */ + { 12000000, 80, 0 + 1 }, + { 13000000, 443, 5 + 1 }, + { 19200000, 50, 0 + 1 }, + { 26000000, 443, 11 + 1 }, + { 38400000, 25, 0 + 1 } + }; + + const struct omap3_dpll5_settings *d; + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + struct dpll_data *dd; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(precomputed); ++i) { + if (parent_rate == precomputed[i].rate) + break; + } + + if (i == ARRAY_SIZE(precomputed)) + return false; + + d = &precomputed[i]; + + /* Update the M, N and rounded rate values and program the DPLL. */ + dd = clk->dpll_data; + dd->last_rounded_m = d->m; + dd->last_rounded_n = d->n; + dd->last_rounded_rate = div_u64((u64)parent_rate * d->m, d->n); + omap3_noncore_dpll_program(clk, 0); + + return true; +} + +/** + * omap3_dpll5_set_rate - set rate for omap3 dpll5 + * @hw: clock to change + * @rate: target rate for clock + * @parent_rate: rate of the parent clock + * + * Set rate for the DPLL5 clock. Apply the sprz319 advisory 2.1 on OMAP36xx if + * the DPLL is used for USB host (detected through the requested rate). + */ +int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + if (rate == OMAP3_DPLL5_FREQ_FOR_USBHOST * 8) { + if (omap3_dpll5_apply_errata(hw, parent_rate)) + return 0; + } + + return omap3_noncore_dpll_set_rate(hw, rate, parent_rate); +} diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index ff44082a0827..47f8aafe3344 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -482,6 +482,7 @@ static void exynos4_local_timer_stop(struct mct_clock_event_device *mevt) if (mct_int_type == MCT_INT_SPI) { if (evt->irq != -1) disable_irq_nosync(evt->irq); + exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET); } else { disable_percpu_irq(mct_irqs[MCT_L0_IRQ]); } diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index cb501386eb6e..c4b0ef65988c 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c @@ -373,8 +373,14 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy, if (unlikely(rebooting) && new_index != get_nominal_index()) return 0; - if (!throttled) + if (!throttled) { + /* we don't want to be preempted while + * checking if the CPU frequency has been throttled + */ + preempt_disable(); powernv_cpufreq_throttle_check(NULL); + preempt_enable(); + } freq_data.pstate_id = powernv_freqs[new_index].driver_data; diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 2cde3796cb82..f3307fc38e79 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -702,7 +702,9 @@ copy_iv: /* Will read cryptlen */ append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ); - aead_append_src_dst(desc, FIFOLD_TYPE_MSG1OUT2); + append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH | KEY_VLF | + FIFOLD_TYPE_MSG1OUT2 | FIFOLD_TYPE_LASTBOTH); + append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF); /* Write ICV */ append_seq_store(desc, ctx->authsize, LDST_CLASS_2_CCB | diff --git a/drivers/devfreq/governor_spdm_bw_hyp.c b/drivers/devfreq/governor_spdm_bw_hyp.c index cd068a3097ce..f18b72af5fc4 100644 --- a/drivers/devfreq/governor_spdm_bw_hyp.c +++ b/drivers/devfreq/governor_spdm_bw_hyp.c @@ -1,5 +1,5 @@ /* -*Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +*Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. * *This program is free software; you can redistribute it and/or modify *it under the terms of the GNU General Public License version 2 and @@ -84,6 +84,10 @@ static irqreturn_t threaded_isr(int irq, void *dev_id) (int)desc.arg[0], ext_status); mutex_lock(&devfreqs_lock); list_for_each_entry(data, &devfreqs, list) { + if (data == NULL || data->devfreq == NULL) { + pr_err("Spurious interrupts\n"); + break; + } if (data->spdm_client == desc.ret[0]) { devfreq_monitor_suspend(data->devfreq); mutex_lock(&data->devfreq->lock); diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c index dd184b50e5b4..284627806b88 100644 --- a/drivers/dma/ipu/ipu_irq.c +++ b/drivers/dma/ipu/ipu_irq.c @@ -272,7 +272,7 @@ static void ipu_irq_handler(struct irq_desc *desc) u32 status; int i, line; - for (i = IPU_IRQ_NR_FN_BANKS; i < IPU_IRQ_NR_BANKS; i++) { + for (i = 0; i < IPU_IRQ_NR_BANKS; i++) { struct ipu_irq_bank *bank = irq_bank + i; raw_spin_lock(&bank_lock); diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 17ee758b419f..8250950aab8b 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -445,6 +445,9 @@ struct dma_pl330_chan { /* for cyclic capability */ bool cyclic; + + /* for runtime pm tracking */ + bool active; }; struct pl330_dmac { @@ -1994,6 +1997,7 @@ static void pl330_tasklet(unsigned long data) _stop(pch->thread); spin_unlock(&pch->thread->dmac->lock); power_down = true; + pch->active = false; } else { /* Make sure the PL330 Channel thread is active */ spin_lock(&pch->thread->dmac->lock); @@ -2015,6 +2019,7 @@ static void pl330_tasklet(unsigned long data) desc->status = PREP; list_move_tail(&desc->node, &pch->work_list); if (power_down) { + pch->active = true; spin_lock(&pch->thread->dmac->lock); _start(pch->thread); spin_unlock(&pch->thread->dmac->lock); @@ -2129,6 +2134,7 @@ static int pl330_terminate_all(struct dma_chan *chan) unsigned long flags; struct pl330_dmac *pl330 = pch->dmac; LIST_HEAD(list); + bool power_down = false; pm_runtime_get_sync(pl330->ddma.dev); spin_lock_irqsave(&pch->lock, flags); @@ -2139,6 +2145,8 @@ static int pl330_terminate_all(struct dma_chan *chan) pch->thread->req[0].desc = NULL; pch->thread->req[1].desc = NULL; pch->thread->req_running = -1; + power_down = pch->active; + pch->active = false; /* Mark all desc done */ list_for_each_entry(desc, &pch->submitted_list, node) { @@ -2156,6 +2164,8 @@ static int pl330_terminate_all(struct dma_chan *chan) list_splice_tail_init(&pch->completed_list, &pl330->desc_pool); spin_unlock_irqrestore(&pch->lock, flags); pm_runtime_mark_last_busy(pl330->ddma.dev); + if (power_down) + pm_runtime_put_autosuspend(pl330->ddma.dev); pm_runtime_put_autosuspend(pl330->ddma.dev); return 0; @@ -2302,6 +2312,7 @@ static void pl330_issue_pending(struct dma_chan *chan) * updated on work_list emptiness status. */ WARN_ON(list_empty(&pch->submitted_list)); + pch->active = true; pm_runtime_get_sync(pch->dmac->ddma.dev); } list_splice_tail_init(&pch->submitted_list, &pch->work_list); diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c index 7275a99873b8..1e5f35d8422d 100644 --- a/drivers/esoc/esoc-mdm-4x.c +++ b/drivers/esoc/esoc-mdm-4x.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -434,11 +434,12 @@ static irqreturn_t mdm_status_change(int irq, void *dev_id) { int value; struct esoc_clink *esoc; + struct device *dev; struct mdm_ctrl *mdm = (struct mdm_ctrl *)dev_id; - struct device *dev = mdm->dev; if (!mdm) return IRQ_HANDLED; + dev = mdm->dev; esoc = mdm->esoc; value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)); if (value == 0 && mdm->ready) { @@ -499,7 +500,7 @@ static void mdm_configure_debug(struct mdm_ctrl *mdm) struct device_node *node = mdm->dev->of_node; addr = of_iomap(node, 0); - if (IS_ERR(addr)) { + if (IS_ERR_OR_NULL(addr)) { dev_err(mdm->dev, "failed to get debug base addres\n"); return; } @@ -508,7 +509,7 @@ static void mdm_configure_debug(struct mdm_ctrl *mdm) if (val == MDM_DBG_MODE) { mdm->dbg_mode = true; mdm->cti = coresight_cti_get(MDM_CTI_NAME); - if (IS_ERR(mdm->cti)) { + if (IS_ERR_OR_NULL(mdm->cti)) { dev_err(mdm->dev, "unable to get cti handle\n"); goto cti_get_err; } @@ -743,7 +744,7 @@ static int mdm9x25_setup_hw(struct mdm_ctrl *mdm, mdm->dev = &pdev->dev; mdm->pon_ops = pon_ops; esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); - if (IS_ERR(esoc)) { + if (IS_ERR_OR_NULL(esoc)) { dev_err(mdm->dev, "cannot allocate esoc device\n"); return PTR_ERR(esoc); } @@ -813,7 +814,7 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm, mdm->pon_ops = pon_ops; node = pdev->dev.of_node; esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); - if (IS_ERR(esoc)) { + if (IS_ERR_OR_NULL(esoc)) { dev_err(mdm->dev, "cannot allocate esoc device\n"); return PTR_ERR(esoc); } @@ -901,7 +902,7 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm, mdm->pon_ops = pon_ops; node = pdev->dev.of_node; esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); - if (IS_ERR(esoc)) { + if (IS_ERR_OR_NULL(esoc)) { dev_err(mdm->dev, "cannot allocate esoc device\n"); return PTR_ERR(esoc); } @@ -936,6 +937,10 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm, mdm->dual_interface = of_property_read_bool(node, "qcom,mdm-dual-link"); esoc->link_name = MDM9x55_PCIE; + ret = of_property_read_string(node, "qcom,mdm-link-info", + &esoc->link_info); + if (ret) + dev_info(mdm->dev, "esoc link info missing\n"); esoc->clink_ops = clink_ops; esoc->parent = mdm->dev; esoc->owner = THIS_MODULE; @@ -1001,11 +1006,11 @@ static int mdm_probe(struct platform_device *pdev) struct mdm_ctrl *mdm; match = of_match_node(mdm_dt_match, node); - if (IS_ERR(match)) + if (IS_ERR_OR_NULL(match)) return PTR_ERR(match); mdm_ops = match->data; mdm = devm_kzalloc(&pdev->dev, sizeof(*mdm), GFP_KERNEL); - if (IS_ERR(mdm)) + if (IS_ERR_OR_NULL(mdm)) return PTR_ERR(mdm); return mdm_ops->config_hw(mdm, mdm_ops, pdev); } diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c index 9abe125f28b5..8697428eceb2 100644 --- a/drivers/esoc/esoc-mdm-drv.c +++ b/drivers/esoc/esoc-mdm-drv.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -230,7 +230,7 @@ int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv) struct esoc_eng *esoc_eng; mdm_drv = devm_kzalloc(&esoc_clink->dev, sizeof(*mdm_drv), GFP_KERNEL); - if (IS_ERR(mdm_drv)) + if (IS_ERR_OR_NULL(mdm_drv)) return PTR_ERR(mdm_drv); esoc_eng = &mdm_drv->cmd_eng; esoc_eng->handle_clink_evt = mdm_handle_clink_evt; diff --git a/drivers/esoc/esoc.h b/drivers/esoc/esoc.h index efa2a1d5e5a6..755fb24bd60a 100644 --- a/drivers/esoc/esoc.h +++ b/drivers/esoc/esoc.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -46,6 +46,7 @@ struct esoc_eng { * struct esoc_clink: Representation of external esoc device * @name: Name of the external esoc. * @link_name: name of the physical link. + * @link_info: additional info about the physical link. * @parent: parent device. * @dev: device for userspace interface. * @id: id of the external device. @@ -62,6 +63,7 @@ struct esoc_eng { struct esoc_clink { const char *name; const char *link_name; + const char *link_info; struct device *parent; struct device dev; unsigned int id; diff --git a/drivers/esoc/esoc_bus.c b/drivers/esoc/esoc_bus.c index 110a5dabed10..f925607511ba 100644 --- a/drivers/esoc/esoc_bus.c +++ b/drivers/esoc/esoc_bus.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -32,10 +32,19 @@ esoc_link_show(struct device *dev, struct device_attribute *attr, to_esoc_clink(dev)->link_name); } +static ssize_t +esoc_link_info_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, ESOC_LINK_LEN, "%s", + to_esoc_clink(dev)->link_info); +} + static struct device_attribute esoc_clink_attrs[] = { __ATTR_RO(esoc_name), __ATTR_RO(esoc_link), + __ATTR_RO(esoc_link_info), __ATTR_NULL, }; @@ -129,7 +138,7 @@ struct esoc_clink *get_esoc_clink(int id) struct device *dev; dev = bus_find_device(&esoc_bus_type, NULL, &id, esoc_clink_match_id); - if (IS_ERR(dev)) + if (IS_ERR_OR_NULL(dev)) return NULL; esoc_clink = to_esoc_clink(dev); return esoc_clink; @@ -143,7 +152,7 @@ struct esoc_clink *get_esoc_clink_by_node(struct device_node *node) dev = bus_find_device(&esoc_bus_type, NULL, node, esoc_clink_match_node); - if (IS_ERR(dev)) + if (IS_ERR_OR_NULL(dev)) return NULL; esoc_clink = to_esoc_clink(dev); return esoc_clink; @@ -175,14 +184,14 @@ int esoc_clink_register_ssr(struct esoc_clink *esoc_clink) len = strlen("esoc") + sizeof(esoc_clink->id); subsys_name = kzalloc(len, GFP_KERNEL); - if (IS_ERR(subsys_name)) + if (IS_ERR_OR_NULL(subsys_name)) return PTR_ERR(subsys_name); snprintf(subsys_name, len, "esoc%d", esoc_clink->id); esoc_clink->subsys.name = subsys_name; esoc_clink->dev.of_node = esoc_clink->np; esoc_clink->subsys.dev = &esoc_clink->dev; esoc_clink->subsys_dev = subsys_register(&esoc_clink->subsys); - if (IS_ERR(esoc_clink->subsys_dev)) { + if (IS_ERR_OR_NULL(esoc_clink->subsys_dev)) { dev_err(&esoc_clink->dev, "failed to register ssr node\n"); ret = PTR_ERR(esoc_clink->subsys_dev); goto subsys_err; diff --git a/drivers/esoc/esoc_client.c b/drivers/esoc/esoc_client.c index e9932ea3e964..0fe7a83cc3c2 100644 --- a/drivers/esoc/esoc_client.c +++ b/drivers/esoc/esoc_client.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -43,7 +43,7 @@ struct esoc_desc *devm_register_esoc_client(struct device *dev, struct device_node *np = dev->of_node; struct esoc_clink *esoc_clink; struct esoc_desc *desc; - char *esoc_name, *esoc_link; + char *esoc_name, *esoc_link, *esoc_link_info; for (index = 0;; index++) { esoc_prop = kasprintf(GFP_KERNEL, "esoc-%d", index); @@ -85,16 +85,26 @@ struct esoc_desc *devm_register_esoc_client(struct device *dev, kfree(esoc_name); return ERR_PTR(-ENOMEM); } + esoc_link_info = kasprintf(GFP_KERNEL, "%s", + esoc_clink->link_info); + if (IS_ERR_OR_NULL(esoc_link_info)) { + dev_err(dev, "unable to alloc link info name\n"); + kfree(esoc_name); + kfree(esoc_link); + return ERR_PTR(-ENOMEM); + } desc = devres_alloc(devm_esoc_desc_release, sizeof(*desc), GFP_KERNEL); if (IS_ERR_OR_NULL(desc)) { kfree(esoc_name); kfree(esoc_link); + kfree(esoc_link_info); dev_err(dev, "unable to allocate esoc descriptor\n"); return ERR_PTR(-ENOMEM); } desc->name = esoc_name; desc->link = esoc_link; + desc->link_info = esoc_link_info; desc->priv = esoc_clink; devres_add(dev, desc); return desc; diff --git a/drivers/esoc/esoc_dev.c b/drivers/esoc/esoc_dev.c index 56b767b29093..26b8d0fe512b 100644 --- a/drivers/esoc/esoc_dev.c +++ b/drivers/esoc/esoc_dev.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -260,7 +260,16 @@ static int esoc_dev_open(struct inode *inode, struct file *file) unsigned int minor = iminor(inode); esoc_udev = esoc_udev_get_by_minor(minor); + if (!esoc_udev) { + pr_err("failed to get udev\n"); + return -ENOMEM; + } + esoc_clink = get_esoc_clink(esoc_udev->clink->id); + if (!esoc_clink) { + pr_err("failed to get clink\n"); + return -ENOMEM; + } uhandle = kzalloc(sizeof(*uhandle), GFP_KERNEL); if (!uhandle) { @@ -306,12 +315,12 @@ int esoc_clink_add_device(struct device *dev, void *dummy) struct esoc_clink *esoc_clink = to_esoc_clink(dev); esoc_udev = get_free_esoc_udev(esoc_clink); - if (IS_ERR(esoc_udev)) + if (IS_ERR_OR_NULL(esoc_udev)) return PTR_ERR(esoc_udev); esoc_udev->dev = device_create(esoc_class, &esoc_clink->dev, MKDEV(esoc_major, esoc_clink->id), esoc_clink, "esoc-%d", esoc_clink->id); - if (IS_ERR(esoc_udev->dev)) { + if (IS_ERR_OR_NULL(esoc_udev->dev)) { pr_err("failed to create user device\n"); goto dev_err; } @@ -358,7 +367,7 @@ int __init esoc_dev_init(void) { int ret = 0; esoc_class = class_create(THIS_MODULE, "esoc-dev"); - if (IS_ERR(esoc_class)) { + if (IS_ERR_OR_NULL(esoc_class)) { pr_err("coudn't create class"); return PTR_ERR(esoc_class); } diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index 9e15d571b53c..a76c35fc0b92 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -203,7 +203,19 @@ void __init efi_init(void) reserve_regions(); early_memunmap(memmap.map, params.mmap_size); - memblock_mark_nomap(params.mmap & PAGE_MASK, - PAGE_ALIGN(params.mmap_size + - (params.mmap & ~PAGE_MASK))); + + if (IS_ENABLED(CONFIG_ARM)) { + /* + * ARM currently does not allow ioremap_cache() to be called on + * memory regions that are covered by struct page. So remove the + * UEFI memory map from the linear mapping. + */ + memblock_mark_nomap(params.mmap & PAGE_MASK, + PAGE_ALIGN(params.mmap_size + + (params.mmap & ~PAGE_MASK))); + } else { + memblock_reserve(params.mmap & PAGE_MASK, + PAGE_ALIGN(params.mmap_size + + (params.mmap & ~PAGE_MASK))); + } } diff --git a/drivers/firmware/qcom/tz_log.c b/drivers/firmware/qcom/tz_log.c index bf3a24b3eb01..fa90330db93b 100644 --- a/drivers/firmware/qcom/tz_log.c +++ b/drivers/firmware/qcom/tz_log.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2015,2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -58,6 +58,15 @@ */ #define QSEE_VERSION_TZ_3_X 0x800000 /* + * TZ 4.X version info + */ +#define QSEE_VERSION_TZ_4_X 0x1000000 + +#define TZBSP_AES_256_ENCRYPTED_KEY_SIZE 256 +#define TZBSP_NONCE_LEN 12 +#define TZBSP_TAG_LEN 16 + +/* * VMID Table */ struct tzdbg_vmid_t { @@ -126,6 +135,26 @@ struct tzdbg_int_t { }; /* + * Interrupt Info Table used in tz version >=4.X + */ +struct tzdbg_int_t_tz40 { + uint16_t int_info; + uint8_t avail; + uint8_t spare; + uint32_t int_num; + uint8_t int_desc[TZBSP_MAX_INT_DESC]; + uint32_t int_count[TZBSP_MAX_CPU_COUNT]; /* uint32_t in TZ ver >= 4.x*/ +}; + +/* warm boot reason for cores */ +struct tzbsp_diag_wakeup_info_t { + /* Wake source info : APCS_GICC_HPPIR */ + uint32_t HPPIR; + /* Wake source info : APCS_GICC_AHPPIR */ + uint32_t AHPPIR; +}; + +/* * Log ring buffer position */ struct tzdbg_log_pos_t { @@ -179,6 +208,10 @@ struct tzdbg_t { * Ring Buffer Length */ uint32_t ring_len; + + /* Offset for Wakeup info */ + uint32_t wakeup_info_off; + /* * VMID to EE Mapping */ @@ -193,6 +226,16 @@ struct tzdbg_t { struct tzdbg_reset_info_t reset_info[TZBSP_MAX_CPU_COUNT]; uint32_t num_interrupts; struct tzdbg_int_t int_info[TZBSP_DIAG_INT_NUM]; + + /* Wake up info */ + struct tzbsp_diag_wakeup_info_t wakeup_info[TZBSP_MAX_CPU_COUNT]; + + uint8_t key[TZBSP_AES_256_ENCRYPTED_KEY_SIZE]; + + uint8_t nonce[TZBSP_NONCE_LEN]; + + uint8_t tag[TZBSP_TAG_LEN]; + /* * We need at least 2K for the ring buffer */ @@ -264,6 +307,7 @@ struct tzdbg { struct tzdbg_stat stat[TZDBG_STATS_MAX]; uint32_t hyp_debug_rw_buf_size; bool is_hyplog_enabled; + uint32_t tz_version; }; static struct tzdbg tzdbg = { @@ -337,31 +381,9 @@ static int _disp_tz_boot_stats(void) int len = 0; struct tzdbg_boot_info_t *ptr = NULL; struct tzdbg_boot_info64_t *ptr_64 = NULL; - int ret = 0; - uint32_t smc_id = 0; - uint32_t feature = 10; - struct qseecom_command_scm_resp resp = {}; - struct scm_desc desc = {0}; - - if (!is_scm_armv8()) { - ret = scm_call(SCM_SVC_INFO, SCM_SVC_UTIL, &feature, - sizeof(feature), &resp, sizeof(resp)); - } else { - smc_id = TZ_INFO_GET_FEATURE_VERSION_ID; - desc.arginfo = TZ_INFO_GET_FEATURE_VERSION_ID_PARAM_ID; - desc.args[0] = feature; - ret = scm_call2(smc_id, &desc); - resp.result = desc.ret[0]; - } - if (ret) { - pr_err("%s: scm_call to register log buffer failed\n", - __func__); - return 0; - } - pr_info("qsee_version = 0x%x\n", resp.result); - - if (resp.result >= QSEE_VERSION_TZ_3_X) { + pr_info("qsee_version = 0x%x\n", tzdbg.tz_version); + if (tzdbg.tz_version >= QSEE_VERSION_TZ_3_X) { ptr_64 = (struct tzdbg_boot_info64_t *)((unsigned char *) tzdbg.diag_buf + tzdbg.diag_buf->boot_info_off); } else { @@ -370,7 +392,7 @@ static int _disp_tz_boot_stats(void) } for (i = 0; i < tzdbg.diag_buf->cpu_count; i++) { - if (resp.result >= QSEE_VERSION_TZ_3_X) { + if (tzdbg.tz_version >= QSEE_VERSION_TZ_3_X) { len += snprintf(tzdbg.disp_buf + len, (debug_rw_buf_size - 1) - len, " CPU #: %d\n" @@ -460,6 +482,7 @@ static int _disp_tz_interrupt_stats(void) int *num_int; unsigned char *ptr; struct tzdbg_int_t *tzdbg_ptr; + struct tzdbg_int_t_tz40 *tzdbg_ptr_tz40; num_int = (uint32_t *)((unsigned char *)tzdbg.diag_buf + (tzdbg.diag_buf->int_info_off - sizeof(uint32_t))); @@ -468,9 +491,12 @@ static int _disp_tz_interrupt_stats(void) int_info_size = ((tzdbg.diag_buf->ring_off - tzdbg.diag_buf->int_info_off)/(*num_int)); - for (i = 0; i < (*num_int); i++) { - tzdbg_ptr = (struct tzdbg_int_t *)ptr; - len += snprintf(tzdbg.disp_buf + len, + pr_info("qsee_version = 0x%x\n", tzdbg.tz_version); + + if (tzdbg.tz_version < QSEE_VERSION_TZ_4_X) { + for (i = 0; i < (*num_int); i++) { + tzdbg_ptr = (struct tzdbg_int_t *)ptr; + len += snprintf(tzdbg.disp_buf + len, (debug_rw_buf_size - 1) - len, " Interrupt Number : 0x%x\n" " Type of Interrupt : 0x%x\n" @@ -478,24 +504,53 @@ static int _disp_tz_interrupt_stats(void) tzdbg_ptr->int_num, (uint32_t)tzdbg_ptr->int_info, (uint8_t *)tzdbg_ptr->int_desc); - for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) { - len += snprintf(tzdbg.disp_buf + len, + for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) { + len += snprintf(tzdbg.disp_buf + len, (debug_rw_buf_size - 1) - len, " int_count on CPU # %d : %u\n", (uint32_t)j, (uint32_t)tzdbg_ptr->int_count[j]); - } - len += snprintf(tzdbg.disp_buf + len, debug_rw_buf_size - 1, - "\n"); + } + len += snprintf(tzdbg.disp_buf + len, + debug_rw_buf_size - 1, "\n"); - if (len > (debug_rw_buf_size - 1)) { - pr_warn("%s: Cannot fit all info into the buffer\n", + if (len > (debug_rw_buf_size - 1)) { + pr_warn("%s: Cannot fit all info into buf\n", __func__); - break; + break; + } + ptr += int_info_size; } + } else { + for (i = 0; i < (*num_int); i++) { + tzdbg_ptr_tz40 = (struct tzdbg_int_t_tz40 *)ptr; + len += snprintf(tzdbg.disp_buf + len, + (debug_rw_buf_size - 1) - len, + " Interrupt Number : 0x%x\n" + " Type of Interrupt : 0x%x\n" + " Description of interrupt : %s\n", + tzdbg_ptr_tz40->int_num, + (uint32_t)tzdbg_ptr_tz40->int_info, + (uint8_t *)tzdbg_ptr_tz40->int_desc); + for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) { + len += snprintf(tzdbg.disp_buf + len, + (debug_rw_buf_size - 1) - len, + " int_count on CPU # %d : %u\n", + (uint32_t)j, + (uint32_t)tzdbg_ptr_tz40->int_count[j]); + } + len += snprintf(tzdbg.disp_buf + len, + debug_rw_buf_size - 1, "\n"); - ptr += int_info_size; + if (len > (debug_rw_buf_size - 1)) { + pr_warn("%s: Cannot fit all info into buf\n", + __func__); + break; + } + ptr += int_info_size; + } } + tzdbg.stat[TZDBG_INTERRUPT].data = tzdbg.disp_buf; return len; } @@ -731,10 +786,16 @@ static ssize_t tzdbgfs_read(struct file *file, char __user *buf, int len = 0; int *tz_id = file->private_data; - memcpy_fromio((void *)tzdbg.diag_buf, tzdbg.virt_iobase, + if (*tz_id == TZDBG_BOOT || *tz_id == TZDBG_RESET || + *tz_id == TZDBG_INTERRUPT || *tz_id == TZDBG_GENERAL || + *tz_id == TZDBG_VMID || *tz_id == TZDBG_LOG) + memcpy_fromio((void *)tzdbg.diag_buf, tzdbg.virt_iobase, debug_rw_buf_size); - memcpy_fromio((void *)tzdbg.hyp_diag_buf, tzdbg.hyp_virt_iobase, + + if (*tz_id == TZDBG_HYP_GENERAL || *tz_id == TZDBG_HYP_LOG) + memcpy_fromio((void *)tzdbg.hyp_diag_buf, tzdbg.hyp_virt_iobase, tzdbg.hyp_debug_rw_buf_size); + switch (*tz_id) { case TZDBG_BOOT: len = _disp_tz_boot_stats(); @@ -982,6 +1043,33 @@ static int __update_hypdbg_base(struct platform_device *pdev, return 0; } +static void tzdbg_get_tz_version(void) +{ + uint32_t smc_id = 0; + uint32_t feature = 10; + struct qseecom_command_scm_resp resp = {0}; + struct scm_desc desc = {0}; + int ret = 0; + + if (!is_scm_armv8()) { + ret = scm_call(SCM_SVC_INFO, SCM_SVC_UTIL, &feature, + sizeof(feature), &resp, sizeof(resp)); + } else { + smc_id = TZ_INFO_GET_FEATURE_VERSION_ID; + desc.arginfo = TZ_INFO_GET_FEATURE_VERSION_ID_PARAM_ID; + desc.args[0] = feature; + ret = scm_call2(smc_id, &desc); + resp.result = desc.ret[0]; + } + + if (ret) + pr_err("%s: scm_call to get tz version failed\n", + __func__); + else + tzdbg.tz_version = resp.result; + +} + /* * Driver functions */ @@ -1070,6 +1158,9 @@ static int tz_log_probe(struct platform_device *pdev) goto err; tzdbg_register_qsee_log_buf(); + + tzdbg_get_tz_version(); + return 0; err: kfree(tzdbg.diag_buf); @@ -1100,6 +1191,7 @@ static struct platform_driver tz_log_driver = { .name = "tz_log", .owner = THIS_MODULE, .of_match_table = tzlog_match, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index c161eeda417b..267749a94c5a 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -3704,9 +3704,15 @@ static void dce_v11_0_encoder_add(struct amdgpu_device *adev, default: encoder->possible_crtcs = 0x3; break; + case 3: + encoder->possible_crtcs = 0x7; + break; case 4: encoder->possible_crtcs = 0xf; break; + case 5: + encoder->possible_crtcs = 0x1f; + break; case 6: encoder->possible_crtcs = 0x3f; break; diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index e0b4586a26fd..9b8f0b975ca6 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -223,7 +223,8 @@ static int ast_get_dram_info(struct drm_device *dev) ast_write32(ast, 0x10000, 0xfc600309); do { - ; + if (pci_channel_offline(dev->pdev)) + return -EIO; } while (ast_read32(ast, 0x10000) != 0x01); data = ast_read32(ast, 0x10004); @@ -429,7 +430,9 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags) ast_detect_chip(dev, &need_post); if (ast->chip != AST1180) { - ast_get_dram_info(dev); + ret = ast_get_dram_info(dev); + if (ret) + goto out_free; ast->vram_size = ast_get_vram_info(dev); DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size); } diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index 810c51d92b99..30672a3df8a9 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -58,13 +58,9 @@ bool ast_is_vga_enabled(struct drm_device *dev) /* TODO 1180 */ } else { ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT); - if (ch) { - ast_open_key(ast); - ch = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff); - return ch & 0x04; - } + return !!(ch & 0x01); } - return 0; + return false; } static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff }; @@ -375,8 +371,8 @@ void ast_post_gpu(struct drm_device *dev) pci_write_config_dword(ast->dev->pdev, 0x04, reg); ast_enable_vga(dev); - ast_enable_mmio(dev); ast_open_key(ast); + ast_enable_mmio(dev); ast_set_def_ext_reg(dev); if (ast->chip == AST2300 || ast->chip == AST2400) @@ -1630,12 +1626,44 @@ static void ast_init_dram_2300(struct drm_device *dev) temp |= 0x73; ast_write32(ast, 0x12008, temp); + param.dram_freq = 396; param.dram_type = AST_DDR3; + temp = ast_mindwm(ast, 0x1e6e2070); if (temp & 0x01000000) param.dram_type = AST_DDR2; - param.dram_chipid = ast->dram_type; - param.dram_freq = ast->mclk; - param.vram_size = ast->vram_size; + switch (temp & 0x18000000) { + case 0: + param.dram_chipid = AST_DRAM_512Mx16; + break; + default: + case 0x08000000: + param.dram_chipid = AST_DRAM_1Gx16; + break; + case 0x10000000: + param.dram_chipid = AST_DRAM_2Gx16; + break; + case 0x18000000: + param.dram_chipid = AST_DRAM_4Gx16; + break; + } + switch (temp & 0x0c) { + default: + case 0x00: + param.vram_size = AST_VIDMEM_SIZE_8M; + break; + + case 0x04: + param.vram_size = AST_VIDMEM_SIZE_16M; + break; + + case 0x08: + param.vram_size = AST_VIDMEM_SIZE_32M; + break; + + case 0x0c: + param.vram_size = AST_VIDMEM_SIZE_64M; + break; + } if (param.dram_type == AST_DDR3) { get_ddr3_info(ast, ¶m); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 1ac29d703c12..ea443fafb934 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -265,7 +265,7 @@ mode_fixup(struct drm_atomic_state *state) struct drm_connector *connector; struct drm_connector_state *conn_state; int i; - bool ret; + int ret; for_each_crtc_in_state(state, crtc, crtc_state, i) { if (!crtc_state->mode_changed && diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 7cb2815e815e..a3b96d691ac9 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1812,7 +1812,7 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) mgr->payloads[i].num_slots = req_payload.num_slots; } else if (mgr->payloads[i].num_slots) { mgr->payloads[i].num_slots = 0; - drm_dp_destroy_payload_step1(mgr, port, port->vcpi.vcpi, &mgr->payloads[i]); + drm_dp_destroy_payload_step1(mgr, port, mgr->payloads[i].vcpi, &mgr->payloads[i]); req_payload.payload_state = mgr->payloads[i].payload_state; mgr->payloads[i].start_slot = 0; } diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 8c9ac021608f..cc1e16fd7e76 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -144,6 +144,9 @@ static struct edid_quirk { /* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */ { "SEC", 0xd033, EDID_QUIRK_FORCE_8BPC }, + + /* Rotel RSX-1058 forwards sink's EDID but only does HDMI 1.1*/ + { "ETR", 13896, EDID_QUIRK_FORCE_8BPC }, }; /* diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 39e30abddf08..71a10f08522e 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1401,6 +1401,13 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev, return NULL; mode->type |= DRM_MODE_TYPE_USERDEF; + /* fix up 1368x768: GFT/CVT can't express 1366 width due to alignment */ + if (cmd->xres == 1366 && mode->hdisplay == 1368) { + mode->hdisplay = 1366; + mode->hsync_start--; + mode->hsync_end--; + drm_mode_set_name(mode); + } drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); return mode; } diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 92e7e5795398..db98ab5cde3d 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -484,6 +484,9 @@ static const struct file_operations psb_gem_fops = { .open = drm_open, .release = drm_release, .unlocked_ioctl = psb_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif .mmap = drm_gem_mmap, .poll = drm_poll, .read = drm_read, diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 97d1ed20418b..63fea6a2869c 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -445,6 +445,7 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector) struct drm_i915_private *dev_priv = crt->base.base.dev->dev_private; struct edid *edid; struct i2c_adapter *i2c; + bool ret = false; BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG); @@ -461,17 +462,17 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector) */ if (!is_digital) { DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n"); - return true; + ret = true; + } else { + DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n"); } - - DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n"); } else { DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [no valid EDID found]\n"); } kfree(edid); - return false; + return ret; } static enum drm_connector_status diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 909d1d71d130..4f5d07bb3511 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3948,10 +3948,10 @@ static void page_flip_completed(struct intel_crtc *intel_crtc) drm_crtc_vblank_put(&intel_crtc->base); wake_up_all(&dev_priv->pending_flip_queue); - queue_work(dev_priv->wq, &work->work); - trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj); + + queue_work(dev_priv->wq, &work->work); } void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 3f802163f7d4..e7c18519274a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6803,7 +6803,18 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) static void vlv_init_display_clock_gating(struct drm_i915_private *dev_priv) { - I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); + u32 val; + + /* + * On driver load, a pipe may be active and driving a DSI display. + * Preserve DPOUNIT_CLOCK_GATE_DISABLE to avoid the pipe getting stuck + * (and never recovering) in this case. intel_dsi_post_disable() will + * clear it when we turn off the display. + */ + val = I915_READ(DSPCLK_GATE_D); + val &= DPOUNIT_CLOCK_GATE_DISABLE; + val |= VRHUNIT_CLOCK_GATE_DISABLE; + I915_WRITE(DSPCLK_GATE_D, val); /* * Disable trickle feed and enable pnd deadline calculation diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index f821a81c53a6..532ff8677259 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -1683,7 +1683,7 @@ static struct drm_driver msm_driver = { .debugfs_cleanup = msm_debugfs_cleanup, #endif .ioctls = msm_ioctls, - .num_ioctls = DRM_MSM_NUM_IOCTLS, + .num_ioctls = ARRAY_SIZE(msm_ioctls), .fops = &fops, .name = "msm_drm", .desc = "MSM Snapdragon DRM", diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 63128d11767e..d1455fbc980e 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -750,7 +750,10 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, size = PAGE_ALIGN(size); + mutex_lock(&dev->struct_mutex); ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj); + mutex_unlock(&dev->struct_mutex); + if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index 7ca96831a9b3..a227f1ba0573 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -17,7 +17,7 @@ #include "msm_drv.h" #include "msm_gem.h" -#include "msm_mmu.h" +#include "msm_iommu.h" static void msm_gem_address_space_destroy(struct kref *kref) @@ -149,6 +149,9 @@ static int iommu_aspace_map_vma(struct msm_gem_address_space *aspace, if (flags & MSM_BO_PRIVILEGED) iommu_flags |= IOMMU_PRIV; + if ((flags & MSM_BO_CACHED) && msm_iommu_coherent(aspace->mmu)) + iommu_flags |= IOMMU_CACHE; + if (WARN_ON(drm_mm_node_allocated(&vma->node))) return 0; diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 3176f301e7a8..14c0cfc58270 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -576,8 +576,7 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) if (submit->bos[i].flags & MSM_SUBMIT_BO_READ) msm_gem_move_to_active(&msm_obj->base, gpu, false, submit->fence); - - if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE) + else if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE) msm_gem_move_to_active(&msm_obj->base, gpu, true, submit->fence); } diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 3c16222b8890..3af24646f4f1 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -16,6 +16,7 @@ */ #include <linux/of_platform.h> +#include <linux/of_address.h> #include "msm_drv.h" #include "msm_iommu.h" @@ -126,6 +127,9 @@ static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt) if (ret) iommu->allow_dynamic = false; + /* Mark the GPU as I/O coherent if it is supported */ + iommu->is_coherent = of_dma_is_coherent(mmu->dev->of_node); + /* Attach the device to the domain */ ret = _attach_iommu_device(mmu, iommu->domain, names, cnt); if (ret) @@ -312,6 +316,7 @@ struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *base) struct iommu_domain *domain; struct msm_mmu *mmu; int ret, val = 1; + struct msm_iommu *child_iommu; /* Don't continue if the base domain didn't have the support we need */ if (!base || base_iommu->allow_dynamic == false) @@ -339,5 +344,9 @@ struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *base) iommu_domain_set_attr(domain, DOMAIN_ATTR_CONTEXT_BANK, &base_iommu->cb); + /* Mark the dynamic domain as I/O coherent if the base domain is */ + child_iommu = to_msm_iommu(mmu); + child_iommu->is_coherent = base_iommu->is_coherent; + return mmu; } diff --git a/drivers/gpu/drm/msm/msm_iommu.h b/drivers/gpu/drm/msm/msm_iommu.h index d005cfb9758f..3a67b60ad81d 100644 --- a/drivers/gpu/drm/msm/msm_iommu.h +++ b/drivers/gpu/drm/msm/msm_iommu.h @@ -25,6 +25,8 @@ struct msm_iommu { struct clk *clocks[5]; int nr_clocks; + + bool is_coherent; }; #define to_msm_iommu(x) container_of(x, struct msm_iommu, base) @@ -34,4 +36,11 @@ static inline bool msm_iommu_allow_dynamic(struct msm_mmu *mmu) return iommu->allow_dynamic; } + +static inline bool msm_iommu_coherent(struct msm_mmu *mmu) +{ + struct msm_iommu *iommu = to_msm_iommu(mmu); + + return iommu->is_coherent; +} #endif diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.c b/drivers/gpu/drm/nouveau/dispnv04/hw.c index 956a833b8200..57c7389feee4 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/hw.c +++ b/drivers/gpu/drm/nouveau/dispnv04/hw.c @@ -222,6 +222,7 @@ nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype) uint32_t mpllP; pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP); + mpllP = (mpllP >> 8) & 0xf; if (!mpllP) mpllP = 4; @@ -232,7 +233,7 @@ nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype) uint32_t clock; pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock); - return clock; + return clock / 1000; } ret = nouveau_hw_get_pllvals(dev, plltype, &pllvals); diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 4dca65a63b92..af224fafa21f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -333,6 +333,9 @@ get_fp_strap(struct drm_device *dev, struct nvbios *bios) if (bios->major_version < 5 && bios->data[0x48] & 0x4) return NVReadVgaCrtc5758(dev, 0, 0xf) & 0xf; + if (drm->device.info.family >= NV_DEVICE_INFO_V0_MAXWELL) + return nvif_rd32(device, 0x001800) & 0x0000000f; + else if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) return (nvif_rd32(device, NV_PEXTDEV_BOOT_0) >> 24) & 0xf; else diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index bbc9824af6e0..ece9f4102c0e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -1833,7 +1833,7 @@ nvf1_chipset = { .fb = gk104_fb_new, .fuse = gf100_fuse_new, .gpio = gk104_gpio_new, - .i2c = gf119_i2c_new, + .i2c = gk104_i2c_new, .ibus = gk104_ibus_new, .imem = nv50_instmem_new, .ltc = gk104_ltc_new, @@ -1941,7 +1941,7 @@ nv117_chipset = { .fb = gm107_fb_new, .fuse = gm107_fuse_new, .gpio = gk104_gpio_new, - .i2c = gf119_i2c_new, + .i2c = gk104_i2c_new, .ibus = gk104_ibus_new, .imem = nv50_instmem_new, .ltc = gm107_ltc_new, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c index c1590b746f13..eb58cd7bfbc9 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c @@ -59,7 +59,7 @@ gt215_hda_eld(NV50_DISP_MTHD_V1) ); } for (i = 0; i < size; i++) - nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[0]); + nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[i]); for (; i < 0x60; i++) nvkm_wr32(device, 0x61c440 + soff, (i << 8)); nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000003); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c index e7cbc139c1d4..89976ff4b305 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c @@ -59,6 +59,7 @@ gf100_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *base, struct nvkm_gpuobj *inst = chan->base.inst; int ret = 0; + mutex_lock(&subdev->mutex); nvkm_wr32(device, 0x002634, chan->base.chid); if (nvkm_msec(device, 2000, if (nvkm_rd32(device, 0x002634) == chan->base.chid) @@ -66,10 +67,12 @@ gf100_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *base, ) < 0) { nvkm_error(subdev, "channel %d [%s] kick timeout\n", chan->base.chid, chan->base.object.client->name); - ret = -EBUSY; - if (suspend) - return ret; + ret = -ETIMEDOUT; } + mutex_unlock(&subdev->mutex); + + if (ret && suspend) + return ret; if (offset) { nvkm_kmap(inst); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c index 0b817540a9e4..aa1692e5669f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c @@ -39,7 +39,9 @@ gk104_fifo_gpfifo_kick(struct gk104_fifo_chan *chan) struct nvkm_subdev *subdev = &fifo->base.engine.subdev; struct nvkm_device *device = subdev->device; struct nvkm_client *client = chan->base.object.client; + int ret = 0; + mutex_lock(&subdev->mutex); nvkm_wr32(device, 0x002634, chan->base.chid); if (nvkm_msec(device, 2000, if (!(nvkm_rd32(device, 0x002634) & 0x00100000)) @@ -47,10 +49,10 @@ gk104_fifo_gpfifo_kick(struct gk104_fifo_chan *chan) ) < 0) { nvkm_error(subdev, "channel %d [%s] kick timeout\n", chan->base.chid, client->name); - return -EBUSY; + ret = -ETIMEDOUT; } - - return 0; + mutex_unlock(&subdev->mutex); + return ret; } static u32 diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h index 212800ecdce9..7d1d3c6b4b72 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h @@ -12,6 +12,7 @@ struct nvbios_source { bool rw; bool ignore_checksum; bool no_pcir; + bool require_checksum; }; int nvbios_extend(struct nvkm_bios *, u32 length); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c index b2557e87afdd..7deb81b6dbac 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c @@ -86,9 +86,12 @@ shadow_image(struct nvkm_bios *bios, int idx, u32 offset, struct shadow *mthd) nvbios_checksum(&bios->data[image.base], image.size)) { nvkm_debug(subdev, "%08x: checksum failed\n", image.base); - if (mthd->func->rw) + if (!mthd->func->require_checksum) { + if (mthd->func->rw) + score += 1; score += 1; - score += 1; + } else + return 0; } else { score += 3; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c index 8fecb5ff22a0..06572f8ce914 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c @@ -99,6 +99,7 @@ nvbios_acpi_fast = { .init = acpi_init, .read = acpi_read_fast, .rw = false, + .require_checksum = true, }; const struct nvbios_source diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c index 85b1464c0194..587c52f08d3f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c @@ -47,8 +47,10 @@ nvkm_ltc_tags_clear(struct nvkm_ltc *ltc, u32 first, u32 count) BUG_ON((first > limit) || (limit >= ltc->num_tags)); + mutex_lock(<c->subdev.mutex); ltc->func->cbc_clear(ltc, first, limit); ltc->func->cbc_wait(ltc); + mutex_unlock(<c->subdev.mutex); } int diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index afaf346bd50e..8901228b5d5d 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -90,6 +90,9 @@ static void radeon_show_cursor(struct drm_crtc *crtc) struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct radeon_device *rdev = crtc->dev->dev_private; + if (radeon_crtc->cursor_out_of_bounds) + return; + if (ASIC_IS_DCE4(rdev)) { WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, upper_32_bits(radeon_crtc->cursor_addr)); @@ -143,21 +146,25 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y) int xorigin = 0, yorigin = 0; int w = radeon_crtc->cursor_width; + radeon_crtc->cursor_x = x; + radeon_crtc->cursor_y = y; + if (ASIC_IS_AVIVO(rdev)) { /* avivo cursor are offset into the total surface */ x += crtc->x; y += crtc->y; } - DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y); - if (x < 0) { + if (x < 0) xorigin = min(-x, radeon_crtc->max_cursor_width - 1); - x = 0; - } - if (y < 0) { + if (y < 0) yorigin = min(-y, radeon_crtc->max_cursor_height - 1); - y = 0; + + if (!ASIC_IS_AVIVO(rdev)) { + x += crtc->x; + y += crtc->y; } + DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y); /* fixed on DCE6 and newer */ if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE6(rdev)) { @@ -180,27 +187,31 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y) if (i > 1) { int cursor_end, frame_end; - cursor_end = x - xorigin + w; + cursor_end = x + w; frame_end = crtc->x + crtc->mode.crtc_hdisplay; if (cursor_end >= frame_end) { w = w - (cursor_end - frame_end); if (!(frame_end & 0x7f)) w--; - } else { - if (!(cursor_end & 0x7f)) - w--; + } else if (cursor_end <= 0) { + goto out_of_bounds; + } else if (!(cursor_end & 0x7f)) { + w--; } if (w <= 0) { - w = 1; - cursor_end = x - xorigin + w; - if (!(cursor_end & 0x7f)) { - x--; - WARN_ON_ONCE(x < 0); - } + goto out_of_bounds; } } } + if (x <= (crtc->x - w) || y <= (crtc->y - radeon_crtc->cursor_height) || + x >= (crtc->x + crtc->mode.hdisplay) || + y >= (crtc->y + crtc->mode.vdisplay)) + goto out_of_bounds; + + x += xorigin; + y += yorigin; + if (ASIC_IS_DCE4(rdev)) { WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y); WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin); @@ -212,6 +223,9 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y) WREG32(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset, ((w - 1) << 16) | (radeon_crtc->cursor_height - 1)); } else { + x -= crtc->x; + y -= crtc->y; + if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN) y *= 2; @@ -229,10 +243,20 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y) yorigin * 256); } - radeon_crtc->cursor_x = x; - radeon_crtc->cursor_y = y; + if (radeon_crtc->cursor_out_of_bounds) { + radeon_crtc->cursor_out_of_bounds = false; + if (radeon_crtc->cursor_bo) + radeon_show_cursor(crtc); + } return 0; + + out_of_bounds: + if (!radeon_crtc->cursor_out_of_bounds) { + radeon_hide_cursor(crtc); + radeon_crtc->cursor_out_of_bounds = true; + } + return 0; } int radeon_crtc_cursor_move(struct drm_crtc *crtc, @@ -297,22 +321,23 @@ int radeon_crtc_cursor_set2(struct drm_crtc *crtc, return ret; } - radeon_crtc->cursor_width = width; - radeon_crtc->cursor_height = height; - radeon_lock_cursor(crtc, true); - if (hot_x != radeon_crtc->cursor_hot_x || + if (width != radeon_crtc->cursor_width || + height != radeon_crtc->cursor_height || + hot_x != radeon_crtc->cursor_hot_x || hot_y != radeon_crtc->cursor_hot_y) { int x, y; x = radeon_crtc->cursor_x + radeon_crtc->cursor_hot_x - hot_x; y = radeon_crtc->cursor_y + radeon_crtc->cursor_hot_y - hot_y; - radeon_cursor_move_locked(crtc, x, y); - + radeon_crtc->cursor_width = width; + radeon_crtc->cursor_height = height; radeon_crtc->cursor_hot_x = hot_x; radeon_crtc->cursor_hot_y = hot_y; + + radeon_cursor_move_locked(crtc, x, y); } radeon_show_cursor(crtc); diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 7a0666ac4e23..d8f8be608c19 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -330,6 +330,7 @@ struct radeon_crtc { u16 lut_r[256], lut_g[256], lut_b[256]; bool enabled; bool can_tile; + bool cursor_out_of_bounds; uint32_t crtc_offset; struct drm_gem_object *cursor_bo; uint64_t cursor_addr; diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 10191b935937..b6f16804e73b 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -3008,24 +3008,12 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, (rdev->pdev->device == 0x6817) || (rdev->pdev->device == 0x6806)) max_mclk = 120000; - } else if (rdev->family == CHIP_VERDE) { - if ((rdev->pdev->revision == 0x81) || - (rdev->pdev->revision == 0x83) || - (rdev->pdev->revision == 0x87) || - (rdev->pdev->device == 0x6820) || - (rdev->pdev->device == 0x6821) || - (rdev->pdev->device == 0x6822) || - (rdev->pdev->device == 0x6823) || - (rdev->pdev->device == 0x682A) || - (rdev->pdev->device == 0x682B)) { - max_sclk = 75000; - max_mclk = 80000; - } } else if (rdev->family == CHIP_OLAND) { if ((rdev->pdev->revision == 0xC7) || (rdev->pdev->revision == 0x80) || (rdev->pdev->revision == 0x81) || (rdev->pdev->revision == 0x83) || + (rdev->pdev->revision == 0x87) || (rdev->pdev->device == 0x6604) || (rdev->pdev->device == 0x6605)) { max_sclk = 75000; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 4ae8b56b1847..037c38bb5333 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1621,7 +1621,6 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) struct ttm_buffer_object *bo; int ret = -EBUSY; int put_count; - uint32_t swap_placement = (TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM); spin_lock(&glob->lru_lock); list_for_each_entry(bo, &glob->swap_lru, swap) { @@ -1657,7 +1656,8 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) if (unlikely(ret != 0)) goto out; - if ((bo->mem.placement & swap_placement) != swap_placement) { + if (bo->mem.mem_type != TTM_PL_SYSTEM || + bo->ttm->caching_state != tt_cached) { struct ttm_mem_reg evict_mem; evict_mem = bo->mem; diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index b852330d5671..3faa5aaf9d03 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1720,6 +1720,30 @@ static int adreno_getproperty(struct kgsl_device *device, status = 0; } break; + case KGSL_PROP_DEVICE_QTIMER: + { + struct kgsl_qtimer_prop qtimerprop = {0}; + struct kgsl_memdesc *qtimer_desc = + kgsl_mmu_get_qtimer_global_entry(device); + + if (sizebytes != sizeof(qtimerprop)) { + status = -EINVAL; + break; + } + + if (qtimer_desc) { + qtimerprop.gpuaddr = qtimer_desc->gpuaddr; + qtimerprop.size = qtimer_desc->size; + } + + if (copy_to_user(value, &qtimerprop, + sizeof(qtimerprop))) { + status = -EFAULT; + break; + } + status = 0; + } + break; case KGSL_PROP_MMU_ENABLE: { /* Report MMU only if we can handle paged memory */ diff --git a/drivers/gpu/msm/adreno_compat.c b/drivers/gpu/msm/adreno_compat.c index d86a0c60f0b4..5a8d587d4536 100644 --- a/drivers/gpu/msm/adreno_compat.c +++ b/drivers/gpu/msm/adreno_compat.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -113,6 +113,30 @@ int adreno_getproperty_compat(struct kgsl_device *device, status = 0; } break; + case KGSL_PROP_DEVICE_QTIMER: + { + struct kgsl_qtimer_prop qtimerprop = {0}; + struct kgsl_memdesc *qtimer_desc = + kgsl_mmu_get_qtimer_global_entry(device); + + if (sizebytes != sizeof(qtimerprop)) { + status = -EINVAL; + break; + } + + if (qtimer_desc) { + qtimerprop.gpuaddr = qtimer_desc->gpuaddr; + qtimerprop.size = qtimer_desc->size; + } + + if (copy_to_user(value, &qtimerprop, + sizeof(qtimerprop))) { + status = -EFAULT; + break; + } + status = 0; + } + break; default: /* * Call the adreno_getproperty to check if the property type diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index 6c667cb62896..af9fc1c15236 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -106,6 +106,7 @@ static struct kgsl_memdesc *kgsl_global_secure_pt_entry; static int global_pt_count; uint64_t global_pt_alloc; static struct kgsl_memdesc gpu_qdss_desc; +static struct kgsl_memdesc gpu_qtimer_desc; void kgsl_print_global_pt_entries(struct seq_file *s) { @@ -261,6 +262,50 @@ static inline void kgsl_cleanup_qdss_desc(struct kgsl_mmu *mmu) kgsl_sharedmem_free(&gpu_qdss_desc); } +struct kgsl_memdesc *kgsl_iommu_get_qtimer_global_entry(void) +{ + return &gpu_qtimer_desc; +} + +static void kgsl_setup_qtimer_desc(struct kgsl_device *device) +{ + int result = 0; + uint32_t gpu_qtimer_entry[2]; + + if (!of_find_property(device->pdev->dev.of_node, + "qcom,gpu-qtimer", NULL)) + return; + + if (of_property_read_u32_array(device->pdev->dev.of_node, + "qcom,gpu-qtimer", gpu_qtimer_entry, 2)) { + KGSL_CORE_ERR("Failed to read gpu qtimer dts entry\n"); + return; + } + + gpu_qtimer_desc.flags = 0; + gpu_qtimer_desc.priv = 0; + gpu_qtimer_desc.physaddr = gpu_qtimer_entry[0]; + gpu_qtimer_desc.size = gpu_qtimer_entry[1]; + gpu_qtimer_desc.pagetable = NULL; + gpu_qtimer_desc.ops = NULL; + gpu_qtimer_desc.dev = device->dev->parent; + gpu_qtimer_desc.hostptr = NULL; + + result = memdesc_sg_dma(&gpu_qtimer_desc, gpu_qtimer_desc.physaddr, + gpu_qtimer_desc.size); + if (result) { + KGSL_CORE_ERR("memdesc_sg_dma failed: %d\n", result); + return; + } + + kgsl_mmu_add_global(device, &gpu_qtimer_desc, "gpu-qtimer"); +} + +static inline void kgsl_cleanup_qtimer_desc(struct kgsl_mmu *mmu) +{ + kgsl_iommu_remove_global(mmu, &gpu_qtimer_desc); + kgsl_sharedmem_free(&gpu_qtimer_desc); +} static inline void _iommu_sync_mmu_pc(bool lock) { @@ -1403,6 +1448,7 @@ static void kgsl_iommu_close(struct kgsl_mmu *mmu) kgsl_iommu_remove_global(mmu, &iommu->setstate); kgsl_sharedmem_free(&iommu->setstate); kgsl_cleanup_qdss_desc(mmu); + kgsl_cleanup_qtimer_desc(mmu); } static int _setstate_alloc(struct kgsl_device *device, @@ -1474,6 +1520,7 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu) kgsl_iommu_add_global(mmu, &iommu->setstate, "setstate"); kgsl_setup_qdss_desc(device); + kgsl_setup_qtimer_desc(device); done: if (status) @@ -2616,6 +2663,7 @@ struct kgsl_mmu_ops kgsl_iommu_ops = { .mmu_remove_global = kgsl_iommu_remove_global, .mmu_getpagetable = kgsl_iommu_getpagetable, .mmu_get_qdss_global_entry = kgsl_iommu_get_qdss_global_entry, + .mmu_get_qtimer_global_entry = kgsl_iommu_get_qtimer_global_entry, .probe = kgsl_iommu_probe, }; diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index 46bb6f4656fb..aa7157e882ac 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -619,6 +619,18 @@ struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device) } EXPORT_SYMBOL(kgsl_mmu_get_qdss_global_entry); +struct kgsl_memdesc *kgsl_mmu_get_qtimer_global_entry( + struct kgsl_device *device) +{ + struct kgsl_mmu *mmu = &device->mmu; + + if (MMU_OP_VALID(mmu, mmu_get_qtimer_global_entry)) + return mmu->mmu_ops->mmu_get_qtimer_global_entry(); + + return NULL; +} +EXPORT_SYMBOL(kgsl_mmu_get_qtimer_global_entry); + /* * NOMMU defintions - NOMMU really just means that the MMU is kept in pass * through and the GPU directly accesses physical memory. Used in debug mode and diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h index bc448d424ccb..505fe591a53e 100644 --- a/drivers/gpu/msm/kgsl_mmu.h +++ b/drivers/gpu/msm/kgsl_mmu.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -81,6 +81,7 @@ struct kgsl_mmu_ops { struct kgsl_pagetable * (*mmu_getpagetable)(struct kgsl_mmu *mmu, unsigned long name); struct kgsl_memdesc* (*mmu_get_qdss_global_entry)(void); + struct kgsl_memdesc* (*mmu_get_qtimer_global_entry)(void); }; struct kgsl_mmu_pt_ops { @@ -231,6 +232,9 @@ int kgsl_mmu_unmap_offset(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device); +struct kgsl_memdesc *kgsl_mmu_get_qtimer_global_entry( + struct kgsl_device *device); + int kgsl_mmu_sparse_dummy_map(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc, uint64_t offset, uint64_t size); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 577183bea07c..34ea83d067af 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2011,7 +2011,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) }, diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c index bcefb9ebb026..88be56321610 100644 --- a/drivers/hid/hid-corsair.c +++ b/drivers/hid/hid-corsair.c @@ -148,26 +148,36 @@ static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev) struct usb_interface *usbif = to_usb_interface(dev->parent); struct usb_device *usbdev = interface_to_usbdev(usbif); int brightness; - char data[8]; + char *data; + + data = kmalloc(8, GFP_KERNEL); + if (!data) + return -ENOMEM; ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), K90_REQUEST_STATUS, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, data, 8, USB_CTRL_SET_TIMEOUT); - if (ret < 0) { + if (ret < 5) { dev_warn(dev, "Failed to get K90 initial state (error %d).\n", ret); - return -EIO; + ret = -EIO; + goto out; } brightness = data[4]; if (brightness < 0 || brightness > 3) { dev_warn(dev, "Read invalid backlight brightness: %02hhx.\n", data[4]); - return -EIO; + ret = -EIO; + goto out; } - return brightness; + ret = brightness; +out: + kfree(data); + + return ret; } static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev) @@ -253,17 +263,22 @@ static ssize_t k90_show_macro_mode(struct device *dev, struct usb_interface *usbif = to_usb_interface(dev->parent); struct usb_device *usbdev = interface_to_usbdev(usbif); const char *macro_mode; - char data[8]; + char *data; + + data = kmalloc(2, GFP_KERNEL); + if (!data) + return -ENOMEM; ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), K90_REQUEST_GET_MODE, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, data, 2, USB_CTRL_SET_TIMEOUT); - if (ret < 0) { + if (ret < 1) { dev_warn(dev, "Failed to get K90 initial mode (error %d).\n", ret); - return -EIO; + ret = -EIO; + goto out; } switch (data[0]) { @@ -277,10 +292,15 @@ static ssize_t k90_show_macro_mode(struct device *dev, default: dev_warn(dev, "K90 in unknown mode: %02hhx.\n", data[0]); - return -EIO; + ret = -EIO; + goto out; } - return snprintf(buf, PAGE_SIZE, "%s\n", macro_mode); + ret = snprintf(buf, PAGE_SIZE, "%s\n", macro_mode); +out: + kfree(data); + + return ret; } static ssize_t k90_store_macro_mode(struct device *dev, @@ -320,26 +340,36 @@ static ssize_t k90_show_current_profile(struct device *dev, struct usb_interface *usbif = to_usb_interface(dev->parent); struct usb_device *usbdev = interface_to_usbdev(usbif); int current_profile; - char data[8]; + char *data; + + data = kmalloc(8, GFP_KERNEL); + if (!data) + return -ENOMEM; ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), K90_REQUEST_STATUS, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, data, 8, USB_CTRL_SET_TIMEOUT); - if (ret < 0) { + if (ret < 8) { dev_warn(dev, "Failed to get K90 initial state (error %d).\n", ret); - return -EIO; + ret = -EIO; + goto out; } current_profile = data[7]; if (current_profile < 1 || current_profile > 3) { dev_warn(dev, "Read invalid current profile: %02hhx.\n", data[7]); - return -EIO; + ret = -EIO; + goto out; } - return snprintf(buf, PAGE_SIZE, "%d\n", current_profile); + ret = snprintf(buf, PAGE_SIZE, "%d\n", current_profile); +out: + kfree(data); + + return ret; } static ssize_t k90_store_current_profile(struct device *dev, diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c index 1b764d1745f3..1689568b597d 100644 --- a/drivers/hid/hid-cypress.c +++ b/drivers/hid/hid-cypress.c @@ -39,6 +39,9 @@ static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX)) return rdesc; + if (*rsize < 4) + return rdesc; + for (i = 0; i < *rsize - 4; i++) if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) { rdesc[i] = 0x19; diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 5c02d7bbc7f2..35e3fd9fadf6 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -148,19 +148,21 @@ static int wacom_pl_irq(struct wacom_wac *wacom) wacom->id[0] = STYLUS_DEVICE_ID; } - pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1)); - if (features->pressure_max > 255) - pressure = (pressure << 1) | ((data[4] >> 6) & 1); - pressure += (features->pressure_max + 1) / 2; - - input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); - input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); - input_report_abs(input, ABS_PRESSURE, pressure); - - input_report_key(input, BTN_TOUCH, data[4] & 0x08); - input_report_key(input, BTN_STYLUS, data[4] & 0x10); - /* Only allow the stylus2 button to be reported for the pen tool. */ - input_report_key(input, BTN_STYLUS2, (wacom->tool[0] == BTN_TOOL_PEN) && (data[4] & 0x20)); + if (prox) { + pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1)); + if (features->pressure_max > 255) + pressure = (pressure << 1) | ((data[4] >> 6) & 1); + pressure += (features->pressure_max + 1) / 2; + + input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); + input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); + input_report_abs(input, ABS_PRESSURE, pressure); + + input_report_key(input, BTN_TOUCH, data[4] & 0x08); + input_report_key(input, BTN_STYLUS, data[4] & 0x10); + /* Only allow the stylus2 button to be reported for the pen tool. */ + input_report_key(input, BTN_STYLUS2, (wacom->tool[0] == BTN_TOOL_PEN) && (data[4] & 0x20)); + } if (!prox) wacom->id[0] = 0; diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 63194a9a7189..57c191798699 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -219,7 +219,7 @@ int hv_init(void) /* See if the hypercall page is already set */ rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC); + virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_RX); if (!virtaddr) goto cleanup; @@ -422,7 +422,7 @@ int hv_synic_alloc(void) goto err; } - for_each_online_cpu(cpu) { + for_each_present_cpu(cpu) { hv_context.event_dpc[cpu] = kmalloc(size, GFP_ATOMIC); if (hv_context.event_dpc[cpu] == NULL) { pr_err("Unable to allocate event dpc\n"); @@ -461,6 +461,8 @@ int hv_synic_alloc(void) pr_err("Unable to allocate post msg page\n"); goto err; } + + INIT_LIST_HEAD(&hv_context.percpu_list[cpu]); } return 0; @@ -485,7 +487,7 @@ void hv_synic_free(void) int cpu; kfree(hv_context.hv_numa_map); - for_each_online_cpu(cpu) + for_each_present_cpu(cpu) hv_synic_free_cpu(cpu); } @@ -555,8 +557,6 @@ void hv_synic_init(void *arg) rdmsrl(HV_X64_MSR_VP_INDEX, vp_index); hv_context.vp_index[cpu] = (u32)vp_index; - INIT_LIST_HEAD(&hv_context.percpu_list[cpu]); - /* * Register the per-cpu clockevent source. */ diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c index c37a71e13de0..1fb02dcbc500 100644 --- a/drivers/hv/hv_fcopy.c +++ b/drivers/hv/hv_fcopy.c @@ -61,6 +61,7 @@ static DECLARE_WORK(fcopy_send_work, fcopy_send_data); static const char fcopy_devname[] = "vmbus/hv_fcopy"; static u8 *recv_buffer; static struct hvutil_transport *hvt; +static struct completion release_event; /* * This state maintains the version number registered by the daemon. */ @@ -312,12 +313,14 @@ static void fcopy_on_reset(void) if (cancel_delayed_work_sync(&fcopy_timeout_work)) fcopy_respond_to_host(HV_E_FAIL); + complete(&release_event); } int hv_fcopy_init(struct hv_util_service *srv) { recv_buffer = srv->recv_buffer; + init_completion(&release_event); /* * When this driver loads, the user level daemon that * processes the host requests may not yet be running. @@ -339,4 +342,5 @@ void hv_fcopy_deinit(void) fcopy_transaction.state = HVUTIL_DEVICE_DYING; cancel_delayed_work_sync(&fcopy_timeout_work); hvutil_transport_destroy(hvt); + wait_for_completion(&release_event); } diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 2a3420c4ca59..ce4d3a935491 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -86,6 +86,7 @@ static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); static const char kvp_devname[] = "vmbus/hv_kvp"; static u8 *recv_buffer; static struct hvutil_transport *hvt; +static struct completion release_event; /* * Register the kernel component with the user-level daemon. * As part of this registration, pass the LIC version number. @@ -682,6 +683,7 @@ static void kvp_on_reset(void) if (cancel_delayed_work_sync(&kvp_timeout_work)) kvp_respond_to_host(NULL, HV_E_FAIL); kvp_transaction.state = HVUTIL_DEVICE_INIT; + complete(&release_event); } int @@ -689,6 +691,7 @@ hv_kvp_init(struct hv_util_service *srv) { recv_buffer = srv->recv_buffer; + init_completion(&release_event); /* * When this driver loads, the user level daemon that * processes the host requests may not yet be running. @@ -711,4 +714,5 @@ void hv_kvp_deinit(void) cancel_delayed_work_sync(&kvp_timeout_work); cancel_work_sync(&kvp_sendkey_work); hvutil_transport_destroy(hvt); + wait_for_completion(&release_event); } diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index 81882d4848bd..faad79ae318a 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -66,6 +66,7 @@ static int dm_reg_value; static const char vss_devname[] = "vmbus/hv_vss"; static __u8 *recv_buffer; static struct hvutil_transport *hvt; +static struct completion release_event; static void vss_send_op(struct work_struct *dummy); static void vss_timeout_func(struct work_struct *dummy); @@ -326,11 +327,13 @@ static void vss_on_reset(void) if (cancel_delayed_work_sync(&vss_timeout_work)) vss_respond_to_host(HV_E_FAIL); vss_transaction.state = HVUTIL_DEVICE_INIT; + complete(&release_event); } int hv_vss_init(struct hv_util_service *srv) { + init_completion(&release_event); if (vmbus_proto_version < VERSION_WIN8_1) { pr_warn("Integration service 'Backup (volume snapshot)'" " not supported on this host version.\n"); @@ -360,4 +363,5 @@ void hv_vss_deinit(void) cancel_delayed_work_sync(&vss_timeout_work); cancel_work_sync(&vss_send_op_work); hvutil_transport_destroy(hvt); + wait_for_completion(&release_event); } diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 12e851a5af48..46b4e35fd555 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -188,8 +188,8 @@ static struct amc6821_data *amc6821_update_device(struct device *dev) !data->valid) { for (i = 0; i < TEMP_IDX_LEN; i++) - data->temp[i] = i2c_smbus_read_byte_data(client, - temp_reg[i]); + data->temp[i] = (int8_t)i2c_smbus_read_byte_data( + client, temp_reg[i]); data->stat1 = i2c_smbus_read_byte_data(client, AMC6821_REG_STAT1); diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c index edf550fc4eef..0043a4c02b85 100644 --- a/drivers/hwmon/ds620.c +++ b/drivers/hwmon/ds620.c @@ -166,7 +166,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, if (res) return res; - val = (val * 10 / 625) * 8; + val = (clamp_val(val, -128000, 128000) * 10 / 625) * 8; mutex_lock(&data->update_lock); data->temp[attr->index] = val; diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index b96a2a9e4df7..628be9c95ff9 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -193,14 +193,17 @@ static inline unsigned int rpm_from_cnt(u8 cnt, u32 clk_freq, u16 p, * Convert fan RPM value from sysfs into count value for fan controller * register (FAN_SET_CNT). */ -static inline unsigned char cnt_from_rpm(u32 rpm, u32 clk_freq, u16 p, +static inline unsigned char cnt_from_rpm(unsigned long rpm, u32 clk_freq, u16 p, u8 clk_div, u8 gear_mult) { - if (!rpm) /* to stop the fan, set cnt to 255 */ + unsigned long f1 = clk_freq * 30 * gear_mult; + unsigned long f2 = p * clk_div; + + if (!rpm) /* to stop the fan, set cnt to 255 */ return 0xff; - return clamp_val(((clk_freq * 30 * gear_mult) / (rpm * p * clk_div)), - 0, 255); + rpm = clamp_val(rpm, f1 / (255 * f2), ULONG_MAX / f2); + return DIV_ROUND_CLOSEST(f1, rpm * f2); } /* helper to grab and cache data, at most one time per second */ diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index 3ce33d244cc0..12b94b094c0d 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -259,13 +259,15 @@ static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, ret = 0; else if (ret) ret = DIV_ROUND_CLOSEST(1350000U, ret); + else + ret = 1350000U; abort: mutex_unlock(&data->access_lock); return ret; } static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low, - u8 reg_fan_high, unsigned int limit) + u8 reg_fan_high, unsigned long limit) { int err; @@ -326,8 +328,8 @@ static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index, int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; int err; + voltage = clamp_val(voltage, 0, 0x3ff * nct7802_vmul[nr]); voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]); - voltage = clamp_val(voltage, 0, 0x3ff); mutex_lock(&data->access_lock); err = regmap_write(data->regmap, @@ -402,7 +404,7 @@ static ssize_t store_temp(struct device *dev, struct device_attribute *attr, if (err < 0) return err; - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); err = regmap_write(data->regmap, nr, val & 0xff); return err ? : count; diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c index 7e20567bc369..6827169c82d4 100644 --- a/drivers/hwmon/scpi-hwmon.c +++ b/drivers/hwmon/scpi-hwmon.c @@ -272,6 +272,7 @@ static const struct of_device_id scpi_of_match[] = { {.compatible = "arm,scpi-sensors"}, {}, }; +MODULE_DEVICE_TABLE(of, scpi_of_match); static struct platform_driver scpi_hwmon_platdrv = { .driver = { diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c index bf2a1dd7cf15..7f98d9f527b9 100644 --- a/drivers/i2c/busses/i2c-msm-v2.c +++ b/drivers/i2c/busses/i2c-msm-v2.c @@ -1151,12 +1151,20 @@ static void i2c_msm_dma_xfer_unprepare(struct i2c_msm_ctrl *ctrl) buf_itr->dma_dir); } -static void i2c_msm_dma_callback_xfer_complete(void *dma_async_param) +static void i2c_msm_dma_callback_tx_complete(void *dma_async_param) { struct i2c_msm_ctrl *ctrl = dma_async_param; + complete(&ctrl->xfer.complete); } +static void i2c_msm_dma_callback_rx_complete(void *dma_async_param) +{ + struct i2c_msm_ctrl *ctrl = dma_async_param; + + complete(&ctrl->xfer.rx_complete); +} + /* * i2c_msm_dma_xfer_process: Queue transfers to DMA * @pre 1)QUP is in run state. 2) i2c_msm_dma_xfer_prepare() was called. @@ -1269,14 +1277,16 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl) } /* callback defined for tx dma desc */ - dma_desc_tx->callback = i2c_msm_dma_callback_xfer_complete; + dma_desc_tx->callback = i2c_msm_dma_callback_tx_complete; dma_desc_tx->callback_param = ctrl; dmaengine_submit(dma_desc_tx); dma_async_issue_pending(tx->dma_chan); /* queue the rx dma desc */ dma_desc_rx = dmaengine_prep_slave_sg(rx->dma_chan, sg_rx, - sg_rx_itr - sg_rx, rx->dir, 0); + sg_rx_itr - sg_rx, rx->dir, + (SPS_IOVEC_FLAG_EOT | + SPS_IOVEC_FLAG_NWD)); if (dma_desc_rx < 0) { dev_err(ctrl->dev, "error dmaengine_prep_slave_sg rx:%ld\n", @@ -1285,6 +1295,8 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl) goto dma_xfer_end; } + dma_desc_rx->callback = i2c_msm_dma_callback_rx_complete; + dma_desc_rx->callback_param = ctrl; dmaengine_submit(dma_desc_rx); dma_async_issue_pending(rx->dma_chan); @@ -1297,6 +1309,8 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl) } ret = i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.complete); + if (!ret && ctrl->xfer.rx_cnt) + i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.rx_complete); dma_xfer_end: /* free scatter-gather lists */ @@ -2054,13 +2068,14 @@ static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl, long time_left; int ret = 0; - time_left = wait_for_completion_timeout(complete, xfer->timeout); + time_left = wait_for_completion_timeout(complete, + xfer->timeout); if (!time_left) { xfer->err = I2C_MSM_ERR_TIMEOUT; i2c_msm_dbg_dump_diag(ctrl, false, 0, 0); ret = -EIO; i2c_msm_prof_evnt_add(ctrl, MSM_ERR, I2C_MSM_COMPLT_FL, - xfer->timeout, time_left, 0); + xfer->timeout, time_left, 0); } else { /* return an error if one detected by ISR */ if (xfer->err) @@ -2327,6 +2342,8 @@ i2c_msm_frmwrk_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) xfer->tx_ovrhd_cnt = 0; atomic_set(&xfer->event_cnt, 0); init_completion(&xfer->complete); + init_completion(&xfer->rx_complete); + xfer->cur_buf.is_init = false; xfer->cur_buf.msg_idx = 0; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index d625167357cc..e4587411b447 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1400,7 +1400,7 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, if (i2c_check_addr_validity(addr, info.flags)) { dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n", - info.addr, node->full_name); + addr, node->full_name); return ERR_PTR(-EINVAL); } diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 2413ec9f8207..94c837046786 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -329,7 +329,7 @@ static noinline int i2cdev_ioctl_smbus(struct i2c_client *client, unsigned long arg) { struct i2c_smbus_ioctl_data data_arg; - union i2c_smbus_data temp; + union i2c_smbus_data temp = {}; int datasize, res; if (copy_from_user(&data_arg, diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c index 57145ea72e90..537cca877f66 100644 --- a/drivers/iio/adc/qcom-rradc.c +++ b/drivers/iio/adc/qcom-rradc.c @@ -38,6 +38,7 @@ #define FG_ADC_RR_FAKE_BATT_HIGH_MSB 0x5B #define FG_ADC_RR_BATT_ID_CTRL 0x60 +#define FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV BIT(0) #define FG_ADC_RR_BATT_ID_TRIGGER 0x61 #define FG_ADC_RR_BATT_ID_TRIGGER_CTL BIT(0) #define FG_ADC_RR_BATT_ID_STS 0x62 @@ -163,11 +164,6 @@ #define FG_ADC_RR_DIE_TEMP_SLOPE 2 #define FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC 25000 -#define FAB_ID_GF 0x30 -#define FAB_ID_SMIC 0x11 -#define FAB_ID_660_GF 0x0 -#define FAB_ID_660_TSMC 0x2 -#define FAB_ID_660_MX 0x3 #define FG_ADC_RR_CHG_TEMP_GF_OFFSET_UV 1303168 #define FG_ADC_RR_CHG_TEMP_GF_SLOPE_UV_PER_C 3784 #define FG_ADC_RR_CHG_TEMP_SMIC_OFFSET_UV 1338433 @@ -401,11 +397,11 @@ static int rradc_get_660_fab_coeff(struct rradc_chip *chip, int64_t *offset, int64_t *slope) { switch (chip->pmic_fab_id->fab_id) { - case FAB_ID_660_GF: + case PM660_FAB_ID_GF: *offset = FG_ADC_RR_CHG_TEMP_660_GF_OFFSET_UV; *slope = FG_RR_CHG_TEMP_660_GF_SLOPE_UV_PER_C; break; - case FAB_ID_660_TSMC: + case PM660_FAB_ID_TSMC: *offset = FG_ADC_RR_CHG_TEMP_660_SMIC_OFFSET_UV; *slope = FG_RR_CHG_TEMP_660_SMIC_SLOPE_UV_PER_C; break; @@ -421,11 +417,11 @@ static int rradc_get_8998_fab_coeff(struct rradc_chip *chip, int64_t *offset, int64_t *slope) { switch (chip->pmic_fab_id->fab_id) { - case FAB_ID_GF: + case PMI8998_FAB_ID_GF: *offset = FG_ADC_RR_CHG_TEMP_GF_OFFSET_UV; *slope = FG_ADC_RR_CHG_TEMP_GF_SLOPE_UV_PER_C; break; - case FAB_ID_SMIC: + case PMI8998_FAB_ID_SMIC: *offset = FG_ADC_RR_CHG_TEMP_SMIC_OFFSET_UV; *slope = FG_ADC_RR_CHG_TEMP_SMIC_SLOPE_UV_PER_C; break; @@ -753,6 +749,75 @@ static int rradc_read_channel_with_continuous_mode(struct rradc_chip *chip, return rc; } +static int rradc_enable_batt_id_channel(struct rradc_chip *chip, bool enable) +{ + int rc = 0; + + if (enable) { + rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL, + FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV, + FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV); + if (rc < 0) { + pr_err("Enabling BATT ID channel failed:%d\n", rc); + return rc; + } + } else { + rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL, + FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV, 0); + if (rc < 0) { + pr_err("Disabling BATT ID channel failed:%d\n", rc); + return rc; + } + } + + return rc; +} + +static int rradc_do_batt_id_conversion(struct rradc_chip *chip, + struct rradc_chan_prop *prop, u16 *data, u8 *buf) +{ + int rc = 0, ret = 0; + + rc = rradc_enable_batt_id_channel(chip, true); + if (rc < 0) { + pr_err("Enabling BATT ID channel failed:%d\n", rc); + return rc; + } + + rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER, + FG_ADC_RR_BATT_ID_TRIGGER_CTL, + FG_ADC_RR_BATT_ID_TRIGGER_CTL); + if (rc < 0) { + pr_err("BATT_ID trigger set failed:%d\n", rc); + ret = rc; + rc = rradc_enable_batt_id_channel(chip, false); + if (rc < 0) + pr_err("Disabling BATT ID channel failed:%d\n", rc); + return ret; + } + + rc = rradc_read_channel_with_continuous_mode(chip, prop, buf); + if (rc < 0) { + pr_err("Error reading in continuous mode:%d\n", rc); + ret = rc; + } + + rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER, + FG_ADC_RR_BATT_ID_TRIGGER_CTL, 0); + if (rc < 0) { + pr_err("BATT_ID trigger re-set failed:%d\n", rc); + ret = rc; + } + + rc = rradc_enable_batt_id_channel(chip, false); + if (rc < 0) { + pr_err("Disabling BATT ID channel failed:%d\n", rc); + ret = rc; + } + + return ret; +} + static int rradc_do_conversion(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 *data) { @@ -765,24 +830,9 @@ static int rradc_do_conversion(struct rradc_chip *chip, switch (prop->channel) { case RR_ADC_BATT_ID: - rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER, - FG_ADC_RR_BATT_ID_TRIGGER_CTL, - FG_ADC_RR_BATT_ID_TRIGGER_CTL); - if (rc < 0) { - pr_err("BATT_ID trigger set failed:%d\n", rc); - goto fail; - } - - rc = rradc_read_channel_with_continuous_mode(chip, prop, buf); - if (rc < 0) { - pr_err("Error reading in continuous mode:%d\n", rc); - goto fail; - } - - rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER, - FG_ADC_RR_BATT_ID_TRIGGER_CTL, 0); + rc = rradc_do_batt_id_conversion(chip, prop, data, buf); if (rc < 0) { - pr_err("BATT_ID trigger re-set failed:%d\n", rc); + pr_err("Battery ID conversion failed:%d\n", rc); goto fail; } break; diff --git a/drivers/iio/adc/qcom-tadc.c b/drivers/iio/adc/qcom-tadc.c index 9241288c1d43..054dfcc8556a 100644 --- a/drivers/iio/adc/qcom-tadc.c +++ b/drivers/iio/adc/qcom-tadc.c @@ -18,7 +18,12 @@ #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <linux/power_supply.h> +#include <linux/pmic-voter.h> +#define USB_PRESENT_VOTER "USB_PRESENT_VOTER" +#define SLEEP_VOTER "SLEEP_VOTER" +#define SHUTDOWN_VOTER "SHUTDOWN_VOTER" #define TADC_REVISION1_REG 0x00 #define TADC_REVISION2_REG 0x01 #define TADC_REVISION3_REG 0x02 @@ -54,6 +59,7 @@ #define TADC_CH7_ADC_HI_REG(chip) (chip->tadc_base + 0x73) #define TADC_CH8_ADC_LO_REG(chip) (chip->tadc_base + 0x74) #define TADC_CH8_ADC_HI_REG(chip) (chip->tadc_base + 0x75) +#define TADC_ADC_DIRECT_TST(chip) (chip->tadc_base + 0xE7) /* TADC_CMP register definitions */ #define TADC_CMP_THR1_CMP_REG(chip) (chip->tadc_cmp_base + 0x51) @@ -217,6 +223,11 @@ struct tadc_chip { struct tadc_chan_data chans[TADC_NUM_CH]; struct completion eoc_complete; struct mutex write_lock; + struct mutex conv_lock; + struct power_supply *usb_psy; + struct votable *tadc_disable_votable; + struct work_struct status_change_work; + struct notifier_block nb; }; struct tadc_pt { @@ -274,7 +285,7 @@ static bool tadc_is_reg_locked(struct tadc_chip *chip, u16 reg) if ((reg & 0xFF00) == chip->tadc_cmp_base) return true; - if (reg == TADC_HWTRIG_CONV_CH_EN_REG(chip)) + if (reg >= TADC_HWTRIG_CONV_CH_EN_REG(chip)) return true; return false; @@ -480,12 +491,22 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc) { unsigned long timeout, timeleft; u8 val[TADC_NUM_CH * 2]; - int rc, i; + int rc = 0, i; + mutex_lock(&chip->conv_lock); rc = tadc_read(chip, TADC_MBG_ERR_REG(chip), val, 1); if (rc < 0) { pr_err("Couldn't read mbg error status rc=%d\n", rc); - return rc; + goto unlock; + } + + reinit_completion(&chip->eoc_complete); + + if (get_effective_result(chip->tadc_disable_votable)) { + /* leave it back in completed state */ + complete_all(&chip->eoc_complete); + rc = -ENODATA; + goto unlock; } if (val[0] != 0) { @@ -496,7 +517,7 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc) rc = tadc_write(chip, TADC_CONV_REQ_REG(chip), channels); if (rc < 0) { pr_err("Couldn't write conversion request rc=%d\n", rc); - return rc; + goto unlock; } timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS); @@ -506,25 +527,34 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc) rc = tadc_read(chip, TADC_SW_CH_CONV_REG(chip), val, 1); if (rc < 0) { pr_err("Couldn't read conversion status rc=%d\n", rc); - return rc; + goto unlock; } + /* + * check one last time if the channel we are requesting + * has completed conversion + */ if (val[0] != channels) { - pr_err("Conversion timed out\n"); - return -ETIMEDOUT; + rc = -ETIMEDOUT; + goto unlock; } } rc = tadc_read(chip, TADC_CH1_ADC_LO_REG(chip), val, ARRAY_SIZE(val)); if (rc < 0) { pr_err("Couldn't read adc channels rc=%d\n", rc); - return rc; + goto unlock; } for (i = 0; i < TADC_NUM_CH; i++) adc[i] = (s16)(val[i * 2] | (u16)val[i * 2 + 1] << 8); - return jiffies_to_msecs(timeout - timeleft); + pr_debug("Conversion time for channels 0x%x = %dms\n", channels, + jiffies_to_msecs(timeout - timeleft)); + +unlock: + mutex_unlock(&chip->conv_lock); + return rc; } static int tadc_read_raw(struct iio_dev *indio_dev, @@ -593,12 +623,17 @@ static int tadc_read_raw(struct iio_dev *indio_dev, break; default: rc = tadc_do_conversion(chip, BIT(chan->channel), adc); - if (rc >= 0) - *val = adc[chan->channel]; + if (rc < 0) { + if (rc != -ENODATA) + pr_err("Couldn't read battery current and voltage channels rc=%d\n", + rc); + return rc; + } + *val = adc[chan->channel]; break; } - if (rc < 0) { + if (rc < 0 && rc != -ENODATA) { pr_err("Couldn't read channel %d\n", chan->channel); return rc; } @@ -630,7 +665,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev, case TADC_BATT_P: rc = tadc_do_conversion(chip, BIT(TADC_BATT_I) | BIT(TADC_BATT_V), adc); - if (rc < 0) { + if (rc < 0 && rc != -ENODATA) { pr_err("Couldn't read battery current and voltage channels rc=%d\n", rc); return rc; @@ -641,7 +676,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev, case TADC_INPUT_P: rc = tadc_do_conversion(chip, BIT(TADC_INPUT_I) | BIT(TADC_INPUT_V), adc); - if (rc < 0) { + if (rc < 0 && rc != -ENODATA) { pr_err("Couldn't read input current and voltage channels rc=%d\n", rc); return rc; @@ -683,6 +718,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev, case TADC_DIE_TEMP: case TADC_DIE_TEMP_THR1: case TADC_DIE_TEMP_THR2: + case TADC_DIE_TEMP_THR3: *val = chan_data->scale; return IIO_VAL_INT; case TADC_BATT_I: @@ -821,15 +857,130 @@ static int tadc_write_raw(struct iio_dev *indio_dev, return 0; } - static irqreturn_t handle_eoc(int irq, void *dev_id) { struct tadc_chip *chip = dev_id; - complete(&chip->eoc_complete); + complete_all(&chip->eoc_complete); return IRQ_HANDLED; } +static int tadc_disable_vote_callback(struct votable *votable, + void *data, int disable, const char *client) +{ + struct tadc_chip *chip = data; + int rc; + int timeout; + unsigned long timeleft; + + if (disable) { + timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS); + timeleft = wait_for_completion_timeout(&chip->eoc_complete, + timeout); + if (timeleft == 0) + pr_err("Timed out waiting for eoc, disabling hw conversions regardless\n"); + + rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), 0x00); + if (rc < 0) { + pr_err("Couldn't disable hw conversions rc=%d\n", rc); + return rc; + } + rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x80); + if (rc < 0) { + pr_err("Couldn't enable direct test mode rc=%d\n", rc); + return rc; + } + } else { + rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x00); + if (rc < 0) { + pr_err("Couldn't disable direct test mode rc=%d\n", rc); + return rc; + } + rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), 0x07); + if (rc < 0) { + pr_err("Couldn't enable hw conversions rc=%d\n", rc); + return rc; + } + } + + pr_debug("client: %s disable: %d\n", client, disable); + return 0; +} + +static void status_change_work(struct work_struct *work) +{ + struct tadc_chip *chip = container_of(work, + struct tadc_chip, status_change_work); + union power_supply_propval pval = {0, }; + int rc; + + if (!chip->usb_psy) + chip->usb_psy = power_supply_get_by_name("usb"); + + if (!chip->usb_psy) { + /* treat usb is not present */ + vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0); + return; + } + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + if (rc < 0) { + pr_err("Couldn't get present status rc=%d\n", rc); + /* treat usb is not present */ + vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0); + return; + } + + /* disable if usb is not present */ + vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, !pval.intval, 0); +} + +static int tadc_notifier_call(struct notifier_block *nb, + unsigned long ev, void *v) +{ + struct power_supply *psy = v; + struct tadc_chip *chip = container_of(nb, struct tadc_chip, nb); + + if (ev != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if ((strcmp(psy->desc->name, "usb") == 0)) + schedule_work(&chip->status_change_work); + + return NOTIFY_OK; +} + +static int tadc_register_notifier(struct tadc_chip *chip) +{ + int rc; + + chip->nb.notifier_call = tadc_notifier_call; + rc = power_supply_reg_notifier(&chip->nb); + if (rc < 0) { + pr_err("Couldn't register psy notifier rc = %d\n", rc); + return rc; + } + + return 0; +} + +static int tadc_suspend(struct device *dev) +{ + struct tadc_chip *chip = dev_get_drvdata(dev); + + vote(chip->tadc_disable_votable, SLEEP_VOTER, true, 0); + return 0; +} + +static int tadc_resume(struct device *dev) +{ + struct tadc_chip *chip = dev_get_drvdata(dev); + + vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0); + return 0; +} + static int tadc_set_therm_table(struct tadc_chan_data *chan_data, u32 beta, u32 rtherm) { @@ -1009,6 +1160,12 @@ static int tadc_probe(struct platform_device *pdev) chip->dev = &pdev->dev; init_completion(&chip->eoc_complete); + /* + * set the completion in "completed" state so disable of the tadc + * can progress + */ + complete_all(&chip->eoc_complete); + rc = of_property_read_u32(node, "reg", &chip->tadc_base); if (rc < 0) { pr_err("Couldn't read base address rc=%d\n", rc); @@ -1017,6 +1174,8 @@ static int tadc_probe(struct platform_device *pdev) chip->tadc_cmp_base = chip->tadc_base + 0x100; mutex_init(&chip->write_lock); + mutex_init(&chip->conv_lock); + INIT_WORK(&chip->status_change_work, status_change_work); chip->regmap = dev_get_regmap(chip->dev->parent, NULL); if (!chip->regmap) { pr_err("Couldn't get regmap\n"); @@ -1035,17 +1194,36 @@ static int tadc_probe(struct platform_device *pdev) return rc; } + chip->tadc_disable_votable = create_votable("SMB_TADC_DISABLE", + VOTE_SET_ANY, + tadc_disable_vote_callback, + chip); + if (IS_ERR(chip->tadc_disable_votable)) { + rc = PTR_ERR(chip->tadc_disable_votable); + return rc; + } + /* assume usb is not present */ + vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0); + vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, false, 0); + vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0); + + rc = tadc_register_notifier(chip); + if (rc < 0) { + pr_err("Couldn't register notifier=%d\n", rc); + goto destroy_votable; + } + irq = of_irq_get_byname(node, "eoc"); if (irq < 0) { pr_err("Couldn't get eoc irq rc=%d\n", irq); - return irq; + goto destroy_votable; } rc = devm_request_threaded_irq(chip->dev, irq, NULL, handle_eoc, IRQF_ONESHOT, "eoc", chip); if (rc < 0) { pr_err("Couldn't request irq %d rc=%d\n", irq, rc); - return rc; + goto destroy_votable; } indio_dev->dev.parent = chip->dev; @@ -1058,17 +1236,37 @@ static int tadc_probe(struct platform_device *pdev) rc = devm_iio_device_register(chip->dev, indio_dev); if (rc < 0) { pr_err("Couldn't register IIO device rc=%d\n", rc); - return rc; + goto destroy_votable; } + platform_set_drvdata(pdev, chip); return 0; + +destroy_votable: + destroy_votable(chip->tadc_disable_votable); + return rc; } static int tadc_remove(struct platform_device *pdev) { + struct tadc_chip *chip = platform_get_drvdata(pdev); + + destroy_votable(chip->tadc_disable_votable); return 0; } +static void tadc_shutdown(struct platform_device *pdev) +{ + struct tadc_chip *chip = platform_get_drvdata(pdev); + + vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, true, 0); +} + +static const struct dev_pm_ops tadc_pm_ops = { + .resume = tadc_resume, + .suspend = tadc_suspend, +}; + static const struct of_device_id tadc_match_table[] = { { .compatible = "qcom,tadc" }, { } @@ -1076,12 +1274,14 @@ static const struct of_device_id tadc_match_table[] = { MODULE_DEVICE_TABLE(of, tadc_match_table); static struct platform_driver tadc_driver = { - .driver = { + .driver = { .name = "qcom-tadc", .of_match_table = tadc_match_table, + .pm = &tadc_pm_ops, }, - .probe = tadc_probe, - .remove = tadc_remove, + .probe = tadc_probe, + .remove = tadc_remove, + .shutdown = tadc_shutdown, }; module_platform_driver(tadc_driver); diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c index a0d7deeac62f..3f90985d545e 100644 --- a/drivers/iio/pressure/mpl115.c +++ b/drivers/iio/pressure/mpl115.c @@ -136,6 +136,7 @@ static const struct iio_chan_spec mpl115_channels[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), }, }; diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c index 01b2e0b18878..0f5b8767ec2e 100644 --- a/drivers/iio/pressure/mpl3115.c +++ b/drivers/iio/pressure/mpl3115.c @@ -182,7 +182,7 @@ static const struct iio_chan_spec mpl3115_channels[] = { { .type = IIO_PRESSURE, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), .scan_index = 0, .scan_type = { .sign = 'u', @@ -195,7 +195,7 @@ static const struct iio_chan_spec mpl3115_channels[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), .scan_index = 1, .scan_type = { .sign = 's', diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 17a15c56028c..3f5741a3e728 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -2578,7 +2578,8 @@ static int cma_bind_addr(struct rdma_cm_id *id, struct sockaddr *src_addr, if (!src_addr || !src_addr->sa_family) { src_addr = (struct sockaddr *) &id->route.addr.src_addr; src_addr->sa_family = dst_addr->sa_family; - if (dst_addr->sa_family == AF_INET6) { + if (IS_ENABLED(CONFIG_IPV6) && + dst_addr->sa_family == AF_INET6) { struct sockaddr_in6 *src_addr6 = (struct sockaddr_in6 *) src_addr; struct sockaddr_in6 *dst_addr6 = (struct sockaddr_in6 *) dst_addr; src_addr6->sin6_scope_id = dst_addr6->sin6_scope_id; @@ -3348,6 +3349,9 @@ static int cma_accept_iw(struct rdma_id_private *id_priv, struct iw_cm_conn_param iw_param; int ret; + if (!conn_param) + return -EINVAL; + ret = cma_modify_qp_rtr(id_priv, conn_param); if (ret) return ret; diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index 2281de122038..8d84c563ba75 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -1745,7 +1745,7 @@ find_mad_agent(struct ib_mad_port_private *port_priv, if (!class) goto out; if (convert_mgmt_class(mad_hdr->mgmt_class) >= - IB_MGMT_MAX_METHODS) + ARRAY_SIZE(class->method_table)) goto out; method = class->method_table[convert_mgmt_class( mad_hdr->mgmt_class)]; diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c index 6aa648cb5381..2cd97977b988 100644 --- a/drivers/infiniband/core/multicast.c +++ b/drivers/infiniband/core/multicast.c @@ -517,8 +517,11 @@ static void join_handler(int status, struct ib_sa_mcmember_rec *rec, process_join_error(group, status); else { int mgids_changed, is_mgid0; - ib_find_pkey(group->port->dev->device, group->port->port_num, - be16_to_cpu(rec->pkey), &pkey_index); + + if (ib_find_pkey(group->port->dev->device, + group->port->port_num, be16_to_cpu(rec->pkey), + &pkey_index)) + pkey_index = MCAST_INVALID_PKEY_INDEX; spin_lock_irq(&group->port->lock); if (group->state == MCAST_BUSY && diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 04f3c0db9126..0ae337bec4f2 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -134,6 +134,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_MW_BIND)); if (access & IB_ACCESS_ON_DEMAND) { + put_pid(umem->pid); ret = ib_umem_odp_get(context, umem); if (ret) { kfree(umem); @@ -149,6 +150,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, page_list = (struct page **) __get_free_page(GFP_KERNEL); if (!page_list) { + put_pid(umem->pid); kfree(umem); return ERR_PTR(-ENOMEM); } diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c index c007c766c61e..fc21bdbb8b32 100644 --- a/drivers/infiniband/hw/mlx4/ah.c +++ b/drivers/infiniband/hw/mlx4/ah.c @@ -113,7 +113,9 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr !(1 << ah->av.eth.stat_rate & dev->caps.stat_rate_support)) --ah->av.eth.stat_rate; } - + ah->av.eth.sl_tclass_flowlabel |= + cpu_to_be32((ah_attr->grh.traffic_class << 20) | + ah_attr->grh.flow_label); /* * HW requires multicast LID so we just choose one. */ @@ -121,7 +123,7 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr ah->av.ib.dlid = cpu_to_be16(0xc000); memcpy(ah->av.eth.dgid, ah_attr->grh.dgid.raw, 16); - ah->av.eth.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 29); + ah->av.eth.sl_tclass_flowlabel |= cpu_to_be32(ah_attr->sl << 29); return &ah->ibah; } diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 97d6878f9938..77ddf2fa8625 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -630,9 +630,11 @@ static int eth_link_query_port(struct ib_device *ibdev, u8 port, if (err) goto out; - props->active_width = (((u8 *)mailbox->buf)[5] == 0x40) ? - IB_WIDTH_4X : IB_WIDTH_1X; - props->active_speed = IB_SPEED_QDR; + props->active_width = (((u8 *)mailbox->buf)[5] == 0x40) || + (((u8 *)mailbox->buf)[5] == 0x20 /*56Gb*/) ? + IB_WIDTH_4X : IB_WIDTH_1X; + props->active_speed = (((u8 *)mailbox->buf)[5] == 0x20 /*56Gb*/) ? + IB_SPEED_FDR : IB_SPEED_QDR; props->port_cap_flags = IB_PORT_CM_SUP | IB_PORT_IP_BASED_GIDS; props->gid_tbl_len = mdev->dev->caps.gid_table_len[port]; props->max_msg_sz = mdev->dev->caps.max_msg_sz; @@ -2401,14 +2403,19 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) goto err_steer_qp_release; } - bitmap_zero(ibdev->ib_uc_qpns_bitmap, ibdev->steer_qpn_count); - - err = mlx4_FLOW_STEERING_IB_UC_QP_RANGE( - dev, ibdev->steer_qpn_base, - ibdev->steer_qpn_base + - ibdev->steer_qpn_count - 1); - if (err) - goto err_steer_free_bitmap; + if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_DMFS_IPOIB) { + bitmap_zero(ibdev->ib_uc_qpns_bitmap, + ibdev->steer_qpn_count); + err = mlx4_FLOW_STEERING_IB_UC_QP_RANGE( + dev, ibdev->steer_qpn_base, + ibdev->steer_qpn_base + + ibdev->steer_qpn_count - 1); + if (err) + goto err_steer_free_bitmap; + } else { + bitmap_fill(ibdev->ib_uc_qpns_bitmap, + ibdev->steer_qpn_count); + } } for (j = 1; j <= ibdev->dev->caps.num_ports; j++) diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index f350f2d61c15..1c8b7c22c822 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -1207,7 +1207,8 @@ int mlx4_ib_destroy_qp(struct ib_qp *qp) if (is_qp0(dev, mqp)) mlx4_CLOSE_PORT(dev->dev, mqp->port); - if (dev->qp1_proxy[mqp->port - 1] == mqp) { + if (mqp->mlx4_ib_qp_type == MLX4_IB_QPT_PROXY_GSI && + dev->qp1_proxy[mqp->port - 1] == mqp) { mutex_lock(&dev->qp1_proxy_lock[mqp->port - 1]); dev->qp1_proxy[mqp->port - 1] = NULL; mutex_unlock(&dev->qp1_proxy_lock[mqp->port - 1]); diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 6000f7aeede9..3399271c235b 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -614,6 +614,33 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev) return 0; } +static void wait_for_async_commands(struct mlx5_ib_dev *dev) +{ + struct mlx5_mr_cache *cache = &dev->cache; + struct mlx5_cache_ent *ent; + int total = 0; + int i; + int j; + + for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) { + ent = &cache->ent[i]; + for (j = 0 ; j < 1000; j++) { + if (!ent->pending) + break; + msleep(50); + } + } + for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) { + ent = &cache->ent[i]; + total += ent->pending; + } + + if (total) + mlx5_ib_warn(dev, "aborted while there are %d pending mr requests\n", total); + else + mlx5_ib_warn(dev, "done with all pending requests\n"); +} + int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev) { int i; @@ -627,6 +654,7 @@ int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev) clean_keys(dev, i); destroy_workqueue(dev->cache.wq); + wait_for_async_commands(dev); del_timer_sync(&dev->delay_timer); return 0; diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index 69a151ae8261..07cfcc326863 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -63,6 +63,8 @@ enum ipoib_flush_level { enum { IPOIB_ENCAP_LEN = 4, + IPOIB_PSEUDO_LEN = 20, + IPOIB_HARD_LEN = IPOIB_ENCAP_LEN + IPOIB_PSEUDO_LEN, IPOIB_UD_HEAD_SIZE = IB_GRH_BYTES + IPOIB_ENCAP_LEN, IPOIB_UD_RX_SG = 2, /* max buffer needed for 4K mtu */ @@ -131,15 +133,21 @@ struct ipoib_header { u16 reserved; }; -struct ipoib_cb { - struct qdisc_skb_cb qdisc_cb; - u8 hwaddr[INFINIBAND_ALEN]; +struct ipoib_pseudo_header { + u8 hwaddr[INFINIBAND_ALEN]; }; -static inline struct ipoib_cb *ipoib_skb_cb(const struct sk_buff *skb) +static inline void skb_add_pseudo_hdr(struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct ipoib_cb)); - return (struct ipoib_cb *)skb->cb; + char *data = skb_push(skb, IPOIB_PSEUDO_LEN); + + /* + * only the ipoib header is present now, make room for a dummy + * pseudo header and set skb field accordingly + */ + memset(data, 0, IPOIB_PSEUDO_LEN); + skb_reset_mac_header(skb); + skb_pull(skb, IPOIB_HARD_LEN); } /* Used for all multicast joins (broadcast, IPv4 mcast and IPv6 mcast) */ diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 8ca75af0e6d1..2018d24344de 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -63,6 +63,8 @@ MODULE_PARM_DESC(cm_data_debug_level, #define IPOIB_CM_RX_DELAY (3 * 256 * HZ) #define IPOIB_CM_RX_UPDATE_MASK (0x3) +#define IPOIB_CM_RX_RESERVE (ALIGN(IPOIB_HARD_LEN, 16) - IPOIB_ENCAP_LEN) + static struct ib_qp_attr ipoib_cm_err_attr = { .qp_state = IB_QPS_ERR }; @@ -147,15 +149,15 @@ static struct sk_buff *ipoib_cm_alloc_rx_skb(struct net_device *dev, struct sk_buff *skb; int i; - skb = dev_alloc_skb(IPOIB_CM_HEAD_SIZE + 12); + skb = dev_alloc_skb(ALIGN(IPOIB_CM_HEAD_SIZE + IPOIB_PSEUDO_LEN, 16)); if (unlikely(!skb)) return NULL; /* - * IPoIB adds a 4 byte header. So we need 12 more bytes to align the + * IPoIB adds a IPOIB_ENCAP_LEN byte header, this will align the * IP header to a multiple of 16. */ - skb_reserve(skb, 12); + skb_reserve(skb, IPOIB_CM_RX_RESERVE); mapping[0] = ib_dma_map_single(priv->ca, skb->data, IPOIB_CM_HEAD_SIZE, DMA_FROM_DEVICE); @@ -624,9 +626,9 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) if (wc->byte_len < IPOIB_CM_COPYBREAK) { int dlen = wc->byte_len; - small_skb = dev_alloc_skb(dlen + 12); + small_skb = dev_alloc_skb(dlen + IPOIB_CM_RX_RESERVE); if (small_skb) { - skb_reserve(small_skb, 12); + skb_reserve(small_skb, IPOIB_CM_RX_RESERVE); ib_dma_sync_single_for_cpu(priv->ca, rx_ring[wr_id].mapping[0], dlen, DMA_FROM_DEVICE); skb_copy_from_linear_data(skb, small_skb->data, dlen); @@ -663,8 +665,7 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) copied: skb->protocol = ((struct ipoib_header *) skb->data)->proto; - skb_reset_mac_header(skb); - skb_pull(skb, IPOIB_ENCAP_LEN); + skb_add_pseudo_hdr(skb); ++dev->stats.rx_packets; dev->stats.rx_bytes += skb->len; @@ -1035,8 +1036,6 @@ static struct ib_qp *ipoib_cm_create_tx_qp(struct net_device *dev, struct ipoib_ tx_qp = ib_create_qp(priv->pd, &attr); if (PTR_ERR(tx_qp) == -EINVAL) { - ipoib_warn(priv, "can't use GFP_NOIO for QPs on device %s, using GFP_KERNEL\n", - priv->ca->name); attr.create_flags &= ~IB_QP_CREATE_USE_GFP_NOIO; tx_qp = ib_create_qp(priv->pd, &attr); } @@ -1489,12 +1488,14 @@ static ssize_t set_mode(struct device *d, struct device_attribute *attr, ret = ipoib_set_mode(dev, buf); - rtnl_unlock(); - - if (!ret) - return count; + /* The assumption is that the function ipoib_set_mode returned + * with the rtnl held by it, if not the value -EBUSY returned, + * then no need to rtnl_unlock + */ + if (ret != -EBUSY) + rtnl_unlock(); - return ret; + return (!ret || ret == -EBUSY) ? count : ret; } static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, show_mode, set_mode); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index 85de078fb0ce..8f8c3af9f4e8 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -130,16 +130,15 @@ static struct sk_buff *ipoib_alloc_rx_skb(struct net_device *dev, int id) buf_size = IPOIB_UD_BUF_SIZE(priv->max_ib_mtu); - skb = dev_alloc_skb(buf_size + IPOIB_ENCAP_LEN); + skb = dev_alloc_skb(buf_size + IPOIB_HARD_LEN); if (unlikely(!skb)) return NULL; /* - * IB will leave a 40 byte gap for a GRH and IPoIB adds a 4 byte - * header. So we need 4 more bytes to get to 48 and align the - * IP header to a multiple of 16. + * the IP header will be at IPOIP_HARD_LEN + IB_GRH_BYTES, that is + * 64 bytes aligned */ - skb_reserve(skb, 4); + skb_reserve(skb, sizeof(struct ipoib_pseudo_header)); mapping = priv->rx_ring[id].mapping; mapping[0] = ib_dma_map_single(priv->ca, skb->data, buf_size, @@ -242,8 +241,7 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) skb_pull(skb, IB_GRH_BYTES); skb->protocol = ((struct ipoib_header *) skb->data)->proto; - skb_reset_mac_header(skb); - skb_pull(skb, IPOIB_ENCAP_LEN); + skb_add_pseudo_hdr(skb); ++dev->stats.rx_packets; dev->stats.rx_bytes += skb->len; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 5f7681b975d0..8efcff1beb8f 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -464,8 +464,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf) priv->tx_wr.wr.send_flags &= ~IB_SEND_IP_CSUM; ipoib_flush_paths(dev); - rtnl_lock(); - return 0; + return (!rtnl_trylock()) ? -EBUSY : 0; } if (!strcmp(buf, "datagram\n")) { @@ -474,8 +473,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf) dev_set_mtu(dev, min(priv->mcast_mtu, dev->mtu)); rtnl_unlock(); ipoib_flush_paths(dev); - rtnl_lock(); - return 0; + return (!rtnl_trylock()) ? -EBUSY : 0; } return -EINVAL; @@ -628,6 +626,14 @@ void ipoib_mark_paths_invalid(struct net_device *dev) spin_unlock_irq(&priv->lock); } +static void push_pseudo_header(struct sk_buff *skb, const char *daddr) +{ + struct ipoib_pseudo_header *phdr; + + phdr = (struct ipoib_pseudo_header *)skb_push(skb, sizeof(*phdr)); + memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN); +} + void ipoib_flush_paths(struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); @@ -850,9 +856,11 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, ipoib_neigh_free(neigh); goto err_drop; } - if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) + if (skb_queue_len(&neigh->queue) < + IPOIB_MAX_PATH_REC_QUEUE) { + push_pseudo_header(skb, neigh->daddr); __skb_queue_tail(&neigh->queue, skb); - else { + } else { ipoib_warn(priv, "queue length limit %d. Packet drop.\n", skb_queue_len(&neigh->queue)); goto err_drop; @@ -868,10 +876,12 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, if (!path->query && path_rec_start(dev, path)) goto err_path; - if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) + if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { + push_pseudo_header(skb, neigh->daddr); __skb_queue_tail(&neigh->queue, skb); - else + } else { goto err_drop; + } } spin_unlock_irqrestore(&priv->lock, flags); @@ -889,7 +899,7 @@ err_drop: } static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, - struct ipoib_cb *cb) + struct ipoib_pseudo_header *phdr) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_path *path; @@ -897,16 +907,17 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, spin_lock_irqsave(&priv->lock, flags); - path = __path_find(dev, cb->hwaddr + 4); + path = __path_find(dev, phdr->hwaddr + 4); if (!path || !path->valid) { int new_path = 0; if (!path) { - path = path_rec_create(dev, cb->hwaddr + 4); + path = path_rec_create(dev, phdr->hwaddr + 4); new_path = 1; } if (path) { if (skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) { + push_pseudo_header(skb, phdr->hwaddr); __skb_queue_tail(&path->queue, skb); } else { ++dev->stats.tx_dropped; @@ -934,10 +945,11 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, be16_to_cpu(path->pathrec.dlid)); spin_unlock_irqrestore(&priv->lock, flags); - ipoib_send(dev, skb, path->ah, IPOIB_QPN(cb->hwaddr)); + ipoib_send(dev, skb, path->ah, IPOIB_QPN(phdr->hwaddr)); return; } else if ((path->query || !path_rec_start(dev, path)) && skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) { + push_pseudo_header(skb, phdr->hwaddr); __skb_queue_tail(&path->queue, skb); } else { ++dev->stats.tx_dropped; @@ -951,13 +963,15 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_neigh *neigh; - struct ipoib_cb *cb = ipoib_skb_cb(skb); + struct ipoib_pseudo_header *phdr; struct ipoib_header *header; unsigned long flags; + phdr = (struct ipoib_pseudo_header *) skb->data; + skb_pull(skb, sizeof(*phdr)); header = (struct ipoib_header *) skb->data; - if (unlikely(cb->hwaddr[4] == 0xff)) { + if (unlikely(phdr->hwaddr[4] == 0xff)) { /* multicast, arrange "if" according to probability */ if ((header->proto != htons(ETH_P_IP)) && (header->proto != htons(ETH_P_IPV6)) && @@ -970,13 +984,13 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } /* Add in the P_Key for multicast*/ - cb->hwaddr[8] = (priv->pkey >> 8) & 0xff; - cb->hwaddr[9] = priv->pkey & 0xff; + phdr->hwaddr[8] = (priv->pkey >> 8) & 0xff; + phdr->hwaddr[9] = priv->pkey & 0xff; - neigh = ipoib_neigh_get(dev, cb->hwaddr); + neigh = ipoib_neigh_get(dev, phdr->hwaddr); if (likely(neigh)) goto send_using_neigh; - ipoib_mcast_send(dev, cb->hwaddr, skb); + ipoib_mcast_send(dev, phdr->hwaddr, skb); return NETDEV_TX_OK; } @@ -985,16 +999,16 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) case htons(ETH_P_IP): case htons(ETH_P_IPV6): case htons(ETH_P_TIPC): - neigh = ipoib_neigh_get(dev, cb->hwaddr); + neigh = ipoib_neigh_get(dev, phdr->hwaddr); if (unlikely(!neigh)) { - neigh_add_path(skb, cb->hwaddr, dev); + neigh_add_path(skb, phdr->hwaddr, dev); return NETDEV_TX_OK; } break; case htons(ETH_P_ARP): case htons(ETH_P_RARP): /* for unicast ARP and RARP should always perform path find */ - unicast_arp_send(skb, dev, cb); + unicast_arp_send(skb, dev, phdr); return NETDEV_TX_OK; default: /* ethertype not supported by IPoIB */ @@ -1011,11 +1025,12 @@ send_using_neigh: goto unref; } } else if (neigh->ah) { - ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(cb->hwaddr)); + ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(phdr->hwaddr)); goto unref; } if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { + push_pseudo_header(skb, phdr->hwaddr); spin_lock_irqsave(&priv->lock, flags); __skb_queue_tail(&neigh->queue, skb); spin_unlock_irqrestore(&priv->lock, flags); @@ -1048,7 +1063,6 @@ static int ipoib_hard_header(struct sk_buff *skb, const void *daddr, const void *saddr, unsigned len) { struct ipoib_header *header; - struct ipoib_cb *cb = ipoib_skb_cb(skb); header = (struct ipoib_header *) skb_push(skb, sizeof *header); @@ -1057,12 +1071,12 @@ static int ipoib_hard_header(struct sk_buff *skb, /* * we don't rely on dst_entry structure, always stuff the - * destination address into skb->cb so we can figure out where + * destination address into skb hard header so we can figure out where * to send the packet later. */ - memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN); + push_pseudo_header(skb, daddr); - return sizeof *header; + return IPOIB_HARD_LEN; } static void ipoib_set_mcast_list(struct net_device *dev) @@ -1638,7 +1652,7 @@ void ipoib_setup(struct net_device *dev) dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - dev->hard_header_len = IPOIB_ENCAP_LEN; + dev->hard_header_len = IPOIB_HARD_LEN; dev->addr_len = INFINIBAND_ALEN; dev->type = ARPHRD_INFINIBAND; dev->tx_queue_len = ipoib_sendq_size * 2; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 87799de90a1d..5580ab0b5781 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -563,8 +563,11 @@ void ipoib_mcast_join_task(struct work_struct *work) if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) return; - if (ib_query_port(priv->ca, priv->port, &port_attr) || - port_attr.state != IB_PORT_ACTIVE) { + if (ib_query_port(priv->ca, priv->port, &port_attr)) { + ipoib_dbg(priv, "ib_query_port() failed\n"); + return; + } + if (port_attr.state != IB_PORT_ACTIVE) { ipoib_dbg(priv, "port state is not ACTIVE (state = %d) suspending join task\n", port_attr.state); return; @@ -753,9 +756,11 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb) __ipoib_mcast_add(dev, mcast); list_add_tail(&mcast->list, &priv->multicast_list); } - if (skb_queue_len(&mcast->pkt_queue) < IPOIB_MAX_MCAST_QUEUE) + if (skb_queue_len(&mcast->pkt_queue) < IPOIB_MAX_MCAST_QUEUE) { + /* put pseudoheader back on for next time */ + skb_push(skb, sizeof(struct ipoib_pseudo_header)); skb_queue_tail(&mcast->pkt_queue, skb); - else { + } else { ++dev->stats.tx_dropped; dev_kfree_skb_any(skb); } diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 5f0f4fc58f43..e397f1b0af09 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1787,17 +1787,24 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) if (unlikely(rsp->tag & SRP_TAG_TSK_MGMT)) { spin_lock_irqsave(&ch->lock, flags); ch->req_lim += be32_to_cpu(rsp->req_lim_delta); + if (rsp->tag == ch->tsk_mgmt_tag) { + ch->tsk_mgmt_status = -1; + if (be32_to_cpu(rsp->resp_data_len) >= 4) + ch->tsk_mgmt_status = rsp->data[3]; + complete(&ch->tsk_mgmt_done); + } else { + shost_printk(KERN_ERR, target->scsi_host, + "Received tsk mgmt response too late for tag %#llx\n", + rsp->tag); + } spin_unlock_irqrestore(&ch->lock, flags); - - ch->tsk_mgmt_status = -1; - if (be32_to_cpu(rsp->resp_data_len) >= 4) - ch->tsk_mgmt_status = rsp->data[3]; - complete(&ch->tsk_mgmt_done); } else { scmnd = scsi_host_find_tag(target->scsi_host, rsp->tag); - if (scmnd) { + if (scmnd && scmnd->host_scribble) { req = (void *)scmnd->host_scribble; scmnd = srp_claim_req(ch, req, NULL, scmnd); + } else { + scmnd = NULL; } if (!scmnd) { shost_printk(KERN_ERR, target->scsi_host, @@ -2469,19 +2476,18 @@ srp_change_queue_depth(struct scsi_device *sdev, int qdepth) } static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun, - u8 func) + u8 func, u8 *status) { struct srp_target_port *target = ch->target; struct srp_rport *rport = target->rport; struct ib_device *dev = target->srp_host->srp_dev->dev; struct srp_iu *iu; struct srp_tsk_mgmt *tsk_mgmt; + int res; if (!ch->connected || target->qp_in_error) return -1; - init_completion(&ch->tsk_mgmt_done); - /* * Lock the rport mutex to avoid that srp_create_ch_ib() is * invoked while a task management function is being sent. @@ -2504,10 +2510,16 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun, tsk_mgmt->opcode = SRP_TSK_MGMT; int_to_scsilun(lun, &tsk_mgmt->lun); - tsk_mgmt->tag = req_tag | SRP_TAG_TSK_MGMT; tsk_mgmt->tsk_mgmt_func = func; tsk_mgmt->task_tag = req_tag; + spin_lock_irq(&ch->lock); + ch->tsk_mgmt_tag = (ch->tsk_mgmt_tag + 1) | SRP_TAG_TSK_MGMT; + tsk_mgmt->tag = ch->tsk_mgmt_tag; + spin_unlock_irq(&ch->lock); + + init_completion(&ch->tsk_mgmt_done); + ib_dma_sync_single_for_device(dev, iu->dma, sizeof *tsk_mgmt, DMA_TO_DEVICE); if (srp_post_send(ch, iu, sizeof(*tsk_mgmt))) { @@ -2516,13 +2528,15 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun, return -1; } + res = wait_for_completion_timeout(&ch->tsk_mgmt_done, + msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS)); + if (res > 0 && status) + *status = ch->tsk_mgmt_status; mutex_unlock(&rport->mutex); - if (!wait_for_completion_timeout(&ch->tsk_mgmt_done, - msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS))) - return -1; + WARN_ON_ONCE(res < 0); - return 0; + return res > 0 ? 0 : -1; } static int srp_abort(struct scsi_cmnd *scmnd) @@ -2548,7 +2562,7 @@ static int srp_abort(struct scsi_cmnd *scmnd) shost_printk(KERN_ERR, target->scsi_host, "Sending SRP abort for tag %#x\n", tag); if (srp_send_tsk_mgmt(ch, tag, scmnd->device->lun, - SRP_TSK_ABORT_TASK) == 0) + SRP_TSK_ABORT_TASK, NULL) == 0) ret = SUCCESS; else if (target->rport->state == SRP_RPORT_LOST) ret = FAST_IO_FAIL; @@ -2566,14 +2580,15 @@ static int srp_reset_device(struct scsi_cmnd *scmnd) struct srp_target_port *target = host_to_target(scmnd->device->host); struct srp_rdma_ch *ch; int i; + u8 status; shost_printk(KERN_ERR, target->scsi_host, "SRP reset_device called\n"); ch = &target->ch[0]; if (srp_send_tsk_mgmt(ch, SRP_TAG_NO_REQ, scmnd->device->lun, - SRP_TSK_LUN_RESET)) + SRP_TSK_LUN_RESET, &status)) return FAILED; - if (ch->tsk_mgmt_status) + if (status) return FAILED; for (i = 0; i < target->ch_count; i++) { diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h index f6af531f9f32..109eea94d0f9 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.h +++ b/drivers/infiniband/ulp/srp/ib_srp.h @@ -168,6 +168,7 @@ struct srp_rdma_ch { int max_ti_iu_len; int comp_vector; + u64 tsk_mgmt_tag; struct completion tsk_mgmt_done; u8 tsk_mgmt_status; bool connected; diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index aff42d5e2296..16f000a76de5 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -1238,6 +1238,12 @@ static int xpad_init_input(struct usb_xpad *xpad) input_dev->name = xpad->name; input_dev->phys = xpad->phys; usb_to_input_id(xpad->udev, &input_dev->id); + + if (xpad->xtype == XTYPE_XBOX360W) { + /* x360w controllers and the receiver have different ids */ + input_dev->id.product = 0x02a1; + } + input_dev->dev.parent = &xpad->intf->dev; input_set_drvdata(input_dev, xpad); diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index cf29f2756b84..c93dd193a496 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -3,6 +3,7 @@ * * Copyright 2005 Phil Blundell * Copyright 2010, 2011 David Jander <david@protonic.nl> + * Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -32,6 +33,7 @@ #include <linux/of_irq.h> #include <linux/spinlock.h> #include <linux/pinctrl/consumer.h> +#include <linux/syscore_ops.h> struct gpio_button_data { const struct gpio_keys_button *button; @@ -57,6 +59,11 @@ struct gpio_keys_drvdata { struct gpio_button_data data[0]; }; +static struct device *global_dev; +static struct syscore_ops gpio_keys_syscore_pm_ops; + +static void gpio_keys_syscore_resume(void); + /* * SYSFS interface for enabling/disabling keys and switches: * @@ -343,14 +350,14 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; - int state = gpio_get_value_cansleep(button->gpio); + int state; + state = (__gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; if (state < 0) { dev_err(input->dev.parent, "failed to get gpio state\n"); return; } - state = (state ? 1 : 0) ^ button->active_low; if (type == EV_ABS) { if (state) input_event(input, type, button->code, button->value); @@ -664,6 +671,8 @@ gpio_keys_get_devtree_pdata(struct device *dev) pdata->nbuttons = nbuttons; pdata->rep = !!of_get_property(node, "autorepeat", NULL); + pdata->name = of_get_property(node, "input-name", NULL); + pdata->use_syscore = of_property_read_bool(node, "use-syscore"); i = 0; for_each_child_of_node(node, pp) { @@ -710,7 +719,7 @@ gpio_keys_get_devtree_pdata(struct device *dev) button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL); if (of_property_read_u32(pp, "debounce-interval", - &button->debounce_interval)) + &button->debounce_interval)) button->debounce_interval = 5; } @@ -767,6 +776,7 @@ static int gpio_keys_probe(struct platform_device *pdev) return -ENOMEM; } + global_dev = dev; ddata->pdata = pdata; ddata->input = input; mutex_init(&ddata->disable_lock); @@ -835,6 +845,11 @@ static int gpio_keys_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, wakeup); + if (pdata->use_syscore) + gpio_keys_syscore_pm_ops.resume = gpio_keys_syscore_resume; + + register_syscore_ops(&gpio_keys_syscore_pm_ops); + return 0; err_remove_group: @@ -857,6 +872,7 @@ err_setup_key: static int gpio_keys_remove(struct platform_device *pdev) { sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); + unregister_syscore_ops(&gpio_keys_syscore_pm_ops); device_init_wakeup(&pdev->dev, 0); @@ -864,6 +880,41 @@ static int gpio_keys_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP +static void gpio_keys_syscore_resume(void) +{ + struct gpio_keys_drvdata *ddata = dev_get_drvdata(global_dev); + struct input_dev *input = ddata->input; + struct gpio_button_data *bdata = NULL; + int error = 0; + int i; + + if (ddata->key_pinctrl) { + error = gpio_keys_pinctrl_configure(ddata, true); + if (error) { + dev_err(global_dev, "failed to put the pin in resume state\n"); + return; + } + } + + if (device_may_wakeup(global_dev)) { + for (i = 0; i < ddata->pdata->nbuttons; i++) { + bdata = &ddata->data[i]; + if (bdata->button->wakeup) + disable_irq_wake(bdata->irq); + } + } else { + mutex_lock(&input->mutex); + if (input->users) + error = gpio_keys_open(input); + mutex_unlock(&input->mutex); + } + + if (error) + return; + + gpio_keys_report_state(ddata); +} + static int gpio_keys_suspend(struct device *dev) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); @@ -901,6 +952,11 @@ static int gpio_keys_resume(struct device *dev) int error = 0; int i; + if (ddata->pdata->use_syscore == true) { + dev_dbg(global_dev, "Using syscore resume, no need of this resume.\n"); + return 0; + } + if (ddata->key_pinctrl) { error = gpio_keys_pinctrl_configure(ddata, true); if (error) { @@ -928,6 +984,21 @@ static int gpio_keys_resume(struct device *dev) gpio_keys_report_state(ddata); return 0; } + +#else + +static void gpio_keys_syscore_resume(void){} + +static int gpio_keys_suspend(struct device *dev) +{ + return 0; +} + +static int gpio_keys_resume(struct device *dev) +{ + return 0; +} + #endif static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 5cfa1848e37c..dd98d8c8fd1f 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -834,4 +834,14 @@ config INPUT_DRV2667_HAPTICS source "drivers/input/misc/ots_pat9125/Kconfig" +config INPUT_STMVL53L0 + tristate "STM VL53L0 Proximity support" + depends on INPUT && I2C + help + Say Y here if you want to use STMicroelectronics's proximity sensor + through I2C interface. + + To compile this driver as a module, choose M here: the + module will be called stmvl53l0. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index a5ab4b762d31..44c026abcb6f 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -80,3 +80,4 @@ obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += ots_pat9125/ +obj-$(CONFIG_INPUT_STMVL53L0) += vl53L0/ diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 2adfd86c869a..930424e55439 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -592,7 +592,6 @@ static int drv260x_probe(struct i2c_client *client, } haptics->input_dev->name = "drv260x:haptics"; - haptics->input_dev->dev.parent = client->dev.parent; haptics->input_dev->close = drv260x_close; input_set_drvdata(haptics->input_dev, haptics); input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE); diff --git a/drivers/input/misc/vl53L0/Makefile b/drivers/input/misc/vl53L0/Makefile index 4a6be55094b6..f105e1c3c60f 100644 --- a/drivers/input/misc/vl53L0/Makefile +++ b/drivers/input/misc/vl53L0/Makefile @@ -9,12 +9,12 @@ FEATURE_USE_CCI := true ifeq ($(FEATURE_USE_CCI), true) ccflags-y += -Idrivers/input/misc/vl53L0/inc -DCAMERA_CCI else -ccflags-y += -Idrivers/input/misc/vl53L0/inc +ccflags-y += -Idrivers/input/misc/vl53L0/inc endif ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io ccflags-y += -Idrivers/media/platform/msm/camera_v2 ccflags-y += -Idrivers/media/platform/msm/camera_v2/common ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci -obj-$(CONFIG_STMVL53L0) += stmvl53l0.o +obj-$(CONFIG_INPUT_STMVL53L0) += stmvl53l0.o stmvl53l0-objs := stmvl53l0_module.o stmvl53l0_module-i2c.o stmvl53l0_module-cci.o src/vl53l0_api_calibration.o src/vl53l0_api_core.o src/vl53l0_api_histogram.o src/vl53l0_api_ranging.o src/vl53l0_api_strings.o src/vl53l0_api.o src/vl53l0_platform.o src/vl53l0_i2c_platform.o src/vl53l0_port_i2c.o src/vl53l010_api.o src/vl53l010_tuning.o diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index d15b33813021..ed1935f300a7 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1232,6 +1232,7 @@ static const struct acpi_device_id elan_acpi_id[] = { { "ELAN0000", 0 }, { "ELAN0100", 0 }, { "ELAN0600", 0 }, + { "ELAN0605", 0 }, { "ELAN1000", 0 }, { } }; diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 073246c7d163..0cdd95801a25 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -211,6 +211,12 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"), }, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"), + DMI_MATCH(DMI_PRODUCT_NAME, "C15B"), + }, + }, { } }; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 99de4002275e..1d4e8a4ce206 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1128,6 +1128,16 @@ config TOUCHSCREEN_FT5X06_GESTURE If unsure, say N. +config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + bool "Synaptics DSX firmware update extra sysfs attributes" + depends on TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE + help + Say Y here to enable support for extra sysfs attributes + supporting firmware update in a development environment. + This does not affect the core or other subsystem attributes. + + If unsure, say N. + config TOUCHSCREEN_ROHM_BU21023 tristate "ROHM BU21023/24 Dual touch support resistive touchscreens" depends on I2C @@ -1206,18 +1216,6 @@ config TOUCHSCREEN_IT7260_I2C To compile this driver as a module, choose M here: the module will be called it7258_ts_i2c. -config TOUCHSCREEN_GT9XX - bool "Goodix touchpanel GT9xx series" - depends on I2C - help - Say Y here if you have a Goodix GT9xx touchscreen. - Gt9xx controllers are multi touch controllers which can - report 5 touches at a time. - - If unsure, say N. - -source "drivers/input/touchscreen/gt9xx/Kconfig" - config TOUCHSCREEN_ST bool "STMicroelectronics Touchscreen Driver" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index a32132cffe92..2e0161cf95bc 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -98,5 +98,4 @@ obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o -obj-$(CONFIG_TOUCHSCREEN_GT9XX) += gt9xx/ obj-$(CONFIG_TOUCHSCREEN_ST) += st/ diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index ac09855fa435..486f8fe242da 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -905,9 +905,9 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev) case QUEUE_HEADER_NORMAL: report_count = ts->buf[FW_HDR_COUNT]; - if (report_count > 3) { + if (report_count == 0 || report_count > 3) { dev_err(&client->dev, - "too large report count: %*ph\n", + "bad report count: %*ph\n", HEADER_SIZE, ts->buf); break; } diff --git a/drivers/input/touchscreen/gt9xx/Kconfig b/drivers/input/touchscreen/gt9xx/Kconfig deleted file mode 100644 index 2e1b5ba567a0..000000000000 --- a/drivers/input/touchscreen/gt9xx/Kconfig +++ /dev/null @@ -1,51 +0,0 @@ -# -# Goodix GT9xx Touchscreen driver -# - -config GT9XX_TOUCHPANEL_DRIVER - tristate "Goodix GT9xx touchpanel driver" - depends on TOUCHSCREEN_GT9XX - default n - help - This is the main file for touchpanel driver for Goodix GT9xx - touchscreens. - - Say Y here if you have a Goodix GT9xx touchscreen connected - to your system. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called gt9xx. - -config GT9XX_TOUCHPANEL_UPDATE - tristate "Goodix GT9xx touchpanel auto update support" - depends on GT9XX_TOUCHPANEL_DRIVER - default n - help - This enables support for firmware update for Goodix GT9xx - touchscreens. - - Say Y here if you have a Goodix GT9xx touchscreen connected - to your system. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called gt9xx_update. - -config GT9XX_TOUCHPANEL_DEBUG - tristate "Goodix GT9xx Tools for debuging" - depends on GT9XX_TOUCHPANEL_DRIVER - default n - help - This is application debug interface support for Goodix GT9xx - touchscreens. - - Say Y here if you want to have a Android app debug interface - to your system. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called gt9xx_tool. diff --git a/drivers/input/touchscreen/gt9xx/Makefile b/drivers/input/touchscreen/gt9xx/Makefile deleted file mode 100644 index 482d869a2d37..000000000000 --- a/drivers/input/touchscreen/gt9xx/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -#gt915 touchpanel driver - - -obj-$(CONFIG_GT9XX_TOUCHPANEL_DRIVER) += gt9xx.o -#gt915 update file -obj-$(CONFIG_GT9XX_TOUCHPANEL_UPDATE) += gt9xx_update.o -#debug tool -obj-$(CONFIG_GT9XX_TOUCHPANEL_DEBUG) += goodix_tool.o diff --git a/drivers/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c deleted file mode 100644 index 02fa0cbbe392..000000000000 --- a/drivers/input/touchscreen/gt9xx/goodix_tool.c +++ /dev/null @@ -1,600 +0,0 @@ -/* drivers/input/touchscreen/goodix_tool.c - * - * 2010 - 2012 Goodix Technology. - * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be a reference - * to you, when you are integrating the GOODiX's CTP IC into your system, - * 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. - * - * Version:1.6 - * V1.0:2012/05/01,create file. - * V1.2:2012/06/08,modify some warning. - * V1.4:2012/08/28,modified to support GT9XX - * V1.6:new proc name - */ - -#include "gt9xx.h" -#include <linux/mutex.h> -#include <linux/proc_fs.h> -#include <linux/debugfs.h> - -#define DATA_LENGTH_UINT 512 -#define CMD_HEAD_LENGTH (sizeof(struct st_cmd_head)) -static char procname[20] = {0}; - -struct st_cmd_head { - u8 wr; /* write read flag 0:R 1:W 2:PID 3: */ - u8 flag; /* 0:no need flag/int 1: need flag 2:need int */ - u8 flag_addr[2];/* flag address */ - u8 flag_val; /* flag val */ - u8 flag_relation; /* flag_val:flag 0:not equal 1:equal 2:> 3:< */ - u16 circle; /* polling cycle */ - u8 times; /* plling times */ - u8 retry; /* I2C retry times */ - u16 delay; /* delay before read or after write */ - u16 data_len; /* data length */ - u8 addr_len; /* address length */ - u8 addr[2]; /* address */ - u8 res[3]; /* reserved */ -} __packed; - -static struct st_cmd_head cmd_head; -static u8 *cmd_data; - -static struct i2c_client *gt_client; - -static struct proc_dir_entry *goodix_proc_entry; - -static struct mutex lock; - -static s32 (*tool_i2c_read)(u8 *, u16); -static s32 (*tool_i2c_write)(u8 *, u16); - -s32 data_length; -s8 ic_type[16] = {0}; - -static void tool_set_proc_name(char *procname) -{ - char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", - "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - char date[20] = {0}; - char month[4] = {0}; - int i = 0, n_month = 1, n_day = 0, n_year = 0, ret; - - ret = sscanf(date, "%s %d %d", month, &n_day, &n_year); - if (!ret) - return; - for (i = 0; i < 12; ++i) { - if (!memcmp(months[i], month, 3)) { - n_month = i+1; - break; - } - } - - snprintf(procname, 20, "gmnode%04d%02d%02d", n_year, n_month, n_day); -} - -static s32 tool_i2c_read_no_extra(u8 *buf, u16 len) -{ - s32 ret = -1; - u8 i = 0; - struct i2c_msg msgs[2] = { - { - .flags = !I2C_M_RD, - .addr = gt_client->addr, - .len = cmd_head.addr_len, - .buf = &buf[0], - }, - { - .flags = I2C_M_RD, - .addr = gt_client->addr, - .len = len, - .buf = &buf[GTP_ADDR_LENGTH], - }, - }; - - for (i = 0; i < cmd_head.retry; i++) { - ret = i2c_transfer(gt_client->adapter, msgs, 2); - if (ret > 0) - break; - } - - if (i == cmd_head.retry) { - dev_err(>_client->dev, "I2C read retry limit over\n"); - ret = -EIO; - } - - return ret; -} - -static s32 tool_i2c_write_no_extra(u8 *buf, u16 len) -{ - s32 ret = -1; - u8 i = 0; - struct i2c_msg msg = { - .flags = !I2C_M_RD, - .addr = gt_client->addr, - .len = len, - .buf = buf, - }; - - for (i = 0; i < cmd_head.retry; i++) { - ret = i2c_transfer(gt_client->adapter, &msg, 1); - if (ret > 0) - break; - } - - if (i == cmd_head.retry) { - dev_err(>_client->dev, "I2C write retry limit over\n"); - ret = -EIO; - } - - return ret; -} - -static s32 tool_i2c_read_with_extra(u8 *buf, u16 len) -{ - s32 ret = -1; - u8 pre[2] = {0x0f, 0xff}; - u8 end[2] = {0x80, 0x00}; - - tool_i2c_write_no_extra(pre, 2); - ret = tool_i2c_read_no_extra(buf, len); - tool_i2c_write_no_extra(end, 2); - - return ret; -} - -static s32 tool_i2c_write_with_extra(u8 *buf, u16 len) -{ - s32 ret = -1; - u8 pre[2] = {0x0f, 0xff}; - u8 end[2] = {0x80, 0x00}; - - tool_i2c_write_no_extra(pre, 2); - ret = tool_i2c_write_no_extra(buf, len); - tool_i2c_write_no_extra(end, 2); - - return ret; -} - -static void register_i2c_func(void) -{ - if (strcmp(ic_type, "GT8110") && strcmp(ic_type, "GT8105") - && strcmp(ic_type, "GT801") && strcmp(ic_type, "GT800") - && strcmp(ic_type, "GT801PLUS") && strcmp(ic_type, "GT811") - && strcmp(ic_type, "GTxxx")) { - tool_i2c_read = tool_i2c_read_with_extra; - tool_i2c_write = tool_i2c_write_with_extra; - pr_debug("I2C function: with pre and end cmd\n"); - } else { - tool_i2c_read = tool_i2c_read_no_extra; - tool_i2c_write = tool_i2c_write_no_extra; - pr_info("I2C function: without pre and end cmd\n"); - } -} - -static void unregister_i2c_func(void) -{ - tool_i2c_read = NULL; - tool_i2c_write = NULL; - pr_info("I2C function: unregister i2c transfer function\n"); -} - -void uninit_wr_node(void) -{ - cmd_data = NULL; - unregister_i2c_func(); - proc_remove(goodix_proc_entry); -} - -static u8 relation(u8 src, u8 dst, u8 rlt) -{ - u8 ret = 0; - - switch (rlt) { - - case 0: - ret = (src != dst) ? true : false; - break; - - case 1: - ret = (src == dst) ? true : false; - pr_debug("equal:src:0x%02x dst:0x%02x ret:%d\n", - src, dst, (s32)ret); - break; - - case 2: - ret = (src > dst) ? true : false; - break; - - case 3: - ret = (src < dst) ? true : false; - break; - - case 4: - ret = (src & dst) ? true : false; - break; - - case 5: - ret = (!(src | dst)) ? true : false; - break; - - default: - ret = false; - break; - } - - return ret; -} - -/* - * Function: - * Comfirm function. - * Input: - * None. - * Output: - * Return write length. - */ -static u8 comfirm(void) -{ - s32 i = 0; - u8 buf[32]; - - memcpy(buf, cmd_head.flag_addr, cmd_head.addr_len); - - for (i = 0; i < cmd_head.times; i++) { - if (tool_i2c_read(buf, 1) <= 0) { - dev_err(>_client->dev, "Read flag data failed"); - return FAIL; - } - if (true == relation(buf[GTP_ADDR_LENGTH], cmd_head.flag_val, - cmd_head.flag_relation)) { - pr_debug("value at flag addr:0x%02x\n", - buf[GTP_ADDR_LENGTH]); - pr_debug("flag value:0x%02x\n", cmd_head.flag_val); - break; - } - - msleep(cmd_head.circle); - } - - if (i >= cmd_head.times) { - dev_err(>_client->dev, "Didn't get the flag to continue"); - return FAIL; - } - - return SUCCESS; -} - -#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE -static s32 fill_update_info(char __user *user_buf, - size_t count, loff_t *ppos) -{ - u8 buf[4]; - - buf[0] = show_len >> 8; - buf[1] = show_len & 0xff; - buf[2] = total_len >> 8; - buf[3] = total_len & 0xff; - return simple_read_from_buffer(user_buf, count, ppos, - buf, sizeof(buf)); -} -#else -static s32 fill_update_info(char __user *user_buf, - size_t count, loff_t *ppos) -{ - return -ENODEV; -} -#endif - -/* - * Function: - * Goodix tool write function. - * Input: - * standard proc write function param. - * Output: - * Return write length. - */ -static ssize_t goodix_tool_write(struct file *filp, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - s32 ret = 0; - - mutex_lock(&lock); - ret = copy_from_user(&cmd_head, userbuf, CMD_HEAD_LENGTH); - if (ret) { - dev_err(>_client->dev, "copy_from_user failed"); - ret = -EFAULT; - goto exit; - } - - dev_dbg(>_client->dev, - "wr: 0x%02x, flag:0x%02x, flag addr:0x%02x%02x\n", cmd_head.wr, - cmd_head.flag, cmd_head.flag_addr[0], cmd_head.flag_addr[1]); - dev_dbg(>_client->dev, - "flag val:0x%02x, flag rel:0x%02x,\n", cmd_head.flag_val, - cmd_head.flag_relation); - dev_dbg(>_client->dev, "circle:%u, times:%u, retry:%u, delay:%u\n", - (s32) cmd_head.circle, (s32) cmd_head.times, - (s32) cmd_head.retry, (s32)cmd_head.delay); - dev_dbg(>_client->dev, - "data len:%u, addr len:%u, addr:0x%02x%02x, write len: %u\n", - (s32)cmd_head.data_len, (s32)cmd_head.addr_len, - cmd_head.addr[0], cmd_head.addr[1], (s32)count); - - if (cmd_head.data_len > (data_length - GTP_ADDR_LENGTH)) { - dev_err(>_client->dev, "data len %u > data buff %d, rejected\n", - cmd_head.data_len, (data_length - GTP_ADDR_LENGTH)); - ret = -EINVAL; - goto exit; - } - - if (cmd_head.wr == GTP_RW_WRITE) { - ret = copy_from_user(&cmd_data[GTP_ADDR_LENGTH], - &userbuf[CMD_HEAD_LENGTH], cmd_head.data_len); - if (ret) { - dev_err(>_client->dev, "copy_from_user failed"); - ret = -EFAULT; - goto exit; - } - - memcpy(&cmd_data[GTP_ADDR_LENGTH - cmd_head.addr_len], - cmd_head.addr, cmd_head.addr_len); - - if (cmd_head.flag == GTP_NEED_FLAG) { - if (comfirm() == FAIL) { - dev_err(>_client->dev, "Confirm fail"); - ret = -EINVAL; - goto exit; - } - } else if (cmd_head.flag == GTP_NEED_INTERRUPT) { - /* Need interrupt! */ - } - if (tool_i2c_write( - &cmd_data[GTP_ADDR_LENGTH - cmd_head.addr_len], - cmd_head.data_len + cmd_head.addr_len) <= 0) { - dev_err(>_client->dev, "Write data failed"); - ret = -EIO; - goto exit; - } - - if (cmd_head.delay) - msleep(cmd_head.delay); - - ret = cmd_head.data_len + CMD_HEAD_LENGTH; - goto exit; - } else if (cmd_head.wr == GTP_RW_WRITE_IC_TYPE) { /* Write ic type */ - ret = copy_from_user(&cmd_data[0], - &userbuf[CMD_HEAD_LENGTH], - cmd_head.data_len); - if (ret) { - dev_err(>_client->dev, "copy_from_user failed"); - ret = -EFAULT; - goto exit; - } - - if (cmd_head.data_len > sizeof(ic_type)) { - dev_err(>_client->dev, - "data len %u > data buff %zu, rejected\n", - cmd_head.data_len, sizeof(ic_type)); - ret = -EINVAL; - goto exit; - } - memcpy(ic_type, cmd_data, cmd_head.data_len); - - register_i2c_func(); - - ret = cmd_head.data_len + CMD_HEAD_LENGTH; - goto exit; - } else if (cmd_head.wr == GTP_RW_NO_WRITE) { - ret = cmd_head.data_len + CMD_HEAD_LENGTH; - goto exit; - } else if (cmd_head.wr == GTP_RW_DISABLE_IRQ) { /* disable irq! */ - gtp_irq_disable(i2c_get_clientdata(gt_client)); - - #if GTP_ESD_PROTECT - gtp_esd_switch(gt_client, SWITCH_OFF); - #endif - ret = CMD_HEAD_LENGTH; - goto exit; - } else if (cmd_head.wr == GTP_RW_ENABLE_IRQ) { /* enable irq! */ - gtp_irq_enable(i2c_get_clientdata(gt_client)); - - #if GTP_ESD_PROTECT - gtp_esd_switch(gt_client, SWITCH_ON); - #endif - ret = CMD_HEAD_LENGTH; - goto exit; - } else if (cmd_head.wr == GTP_RW_CHECK_RAWDIFF_MODE) { - struct goodix_ts_data *ts = i2c_get_clientdata(gt_client); - - ret = copy_from_user(&cmd_data[GTP_ADDR_LENGTH], - &userbuf[CMD_HEAD_LENGTH], cmd_head.data_len); - if (ret) { - dev_err(>_client->dev, "copy_from_user failed"); - goto exit; - } - if (cmd_data[GTP_ADDR_LENGTH]) { - pr_debug("gtp enter rawdiff\n"); - ts->gtp_rawdiff_mode = true; - } else { - ts->gtp_rawdiff_mode = false; - pr_debug("gtp leave rawdiff\n"); - } - ret = CMD_HEAD_LENGTH; - goto exit; - } else if (cmd_head.wr == GTP_RW_ENTER_UPDATE_MODE) { - /* Enter update mode! */ - if (gup_enter_update_mode(gt_client) == FAIL) { - ret = -EBUSY; - goto exit; - } - } else if (cmd_head.wr == GTP_RW_LEAVE_UPDATE_MODE) { - /* Leave update mode! */ - gup_leave_update_mode(gt_client); - } else if (cmd_head.wr == GTP_RW_UPDATE_FW) { - /* Update firmware! */ - show_len = 0; - total_len = 0; - if (cmd_head.data_len + 1 > data_length) { - dev_err(>_client->dev, "data len %u > data buff %d, rejected\n", - cmd_head.data_len + 1, data_length); - ret = -EINVAL; - goto exit; - } - memset(cmd_data, 0, cmd_head.data_len + 1); - memcpy(cmd_data, &userbuf[CMD_HEAD_LENGTH], - cmd_head.data_len); - - if (gup_update_proc((void *)cmd_data) == FAIL) { - ret = -EBUSY; - goto exit; - } - } - ret = CMD_HEAD_LENGTH; - -exit: - memset(&cmd_head, 0, sizeof(cmd_head)); - cmd_head.wr = 0xFF; - - mutex_unlock(&lock); - return ret; -} - -/* - * Function: - * Goodix tool read function. - * Input: - * standard proc read function param. - * Output: - * Return read length. - */ -static ssize_t goodix_tool_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - u16 data_len = 0; - s32 ret; - u8 buf[32]; - - mutex_lock(&lock); - if (cmd_head.wr & 0x1) { - dev_err(>_client->dev, "command head wrong\n"); - ret = -EINVAL; - goto exit; - } - - switch (cmd_head.wr) { - case GTP_RW_READ: - if (cmd_head.flag == GTP_NEED_FLAG) { - if (comfirm() == FAIL) { - dev_err(>_client->dev, "Confirm fail"); - ret = -EINVAL; - goto exit; - } - } else if (cmd_head.flag == GTP_NEED_INTERRUPT) { - /* Need interrupt! */ - } - - memcpy(cmd_data, cmd_head.addr, cmd_head.addr_len); - - pr_debug("[CMD HEAD DATA] ADDR:0x%02x%02x.\n", cmd_data[0], - cmd_data[1]); - pr_debug("[CMD HEAD ADDR] ADDR:0x%02x%02x.\n", cmd_head.addr[0], - cmd_head.addr[1]); - - if (cmd_head.delay) - msleep(cmd_head.delay); - - data_len = cmd_head.data_len; - if (data_len <= 0 || (data_len > data_length)) { - dev_err(>_client->dev, "Invalid data length %d\n", - data_len); - ret = -EINVAL; - goto exit; - } - if (data_len > count) - data_len = count; - - if (tool_i2c_read(cmd_data, data_len) <= 0) { - dev_err(>_client->dev, "Read data failed\n"); - ret = -EIO; - goto exit; - } - ret = simple_read_from_buffer(user_buf, count, ppos, - &cmd_data[GTP_ADDR_LENGTH], data_len); - break; - case GTP_RW_FILL_INFO: - ret = fill_update_info(user_buf, count, ppos); - break; - case GTP_RW_READ_VERSION: - /* Read driver version */ - data_len = scnprintf(buf, sizeof(buf), "%s\n", - GTP_DRIVER_VERSION); - ret = simple_read_from_buffer(user_buf, count, ppos, - buf, data_len); - break; - default: - ret = -EINVAL; - break; - } - -exit: - mutex_unlock(&lock); - return ret; -} - -static const struct file_operations goodix_proc_fops = { - .write = goodix_tool_write, - .read = goodix_tool_read, - .open = simple_open, - .owner = THIS_MODULE, -}; - -s32 init_wr_node(struct i2c_client *client) -{ - u8 i; - - gt_client = client; - memset(&cmd_head, 0, sizeof(cmd_head)); - cmd_data = NULL; - - i = GTP_I2C_RETRY_5; - while ((!cmd_data) && i) { - cmd_data = devm_kzalloc(&client->dev, - i * DATA_LENGTH_UINT, GFP_KERNEL); - if (cmd_data) - break; - i--; - } - if (i) { - data_length = i * DATA_LENGTH_UINT; - dev_dbg(&client->dev, "Applied memory size:%d", data_length); - } - - cmd_head.addr_len = 2; - cmd_head.retry = GTP_I2C_RETRY_5; - - register_i2c_func(); - - mutex_init(&lock); - tool_set_proc_name(procname); - goodix_proc_entry = proc_create(procname, - S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP, - goodix_proc_entry, - &goodix_proc_fops); - if (goodix_proc_entry == NULL) { - dev_err(&client->dev, "Couldn't create proc entry"); - return FAIL; - } - - return SUCCESS; -} diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c deleted file mode 100644 index ead935120624..000000000000 --- a/drivers/input/touchscreen/gt9xx/gt9xx.c +++ /dev/null @@ -1,2564 +0,0 @@ -/* drivers/input/touchscreen/gt9xx.c - * - * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved. - * - * 2010 - 2013 Goodix Technology. - * - * 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 a reference - * to you, when you are integrating the GOODiX's CTP IC into your system, - * 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. - * - * Version: 1.8 - * Authors: andrew@goodix.com, meta@goodix.com - * Release Date: 2013/04/25 - * Revision record: - * V1.0: - * first Release. By Andrew, 2012/08/31 - * V1.2: - * modify gtp_reset_guitar,slot report,tracking_id & 0x0F. - * By Andrew, 2012/10/15 - * V1.4: - * modify gt9xx_update.c. By Andrew, 2012/12/12 - * V1.6: - * 1. new heartbeat/esd_protect mechanism(add external watchdog) - * 2. doze mode, sliding wakeup - * 3. 3 more cfg_group(GT9 Sensor_ID: 0~5) - * 3. config length verification - * 4. names & comments - * By Meta, 2013/03/11 - * V1.8: - * 1. pen/stylus identification - * 2. read double check & fixed config support - * 2. new esd & slide wakeup optimization - * By Meta, 2013/06/08 - */ - -#include <linux/regulator/consumer.h> -#include "gt9xx.h" - -#include <linux/of_gpio.h> -#include <linux/irq.h> -#include <linux/module.h> -#include <linux/input/mt.h> -#include <linux/debugfs.h> -#include <linux/interrupt.h> - -#define GOODIX_DEV_NAME "Goodix-CTP" -#define CFG_MAX_TOUCH_POINTS 5 -#define GOODIX_COORDS_ARR_SIZE 4 -#define MAX_BUTTONS 4 - -#define GOODIX_VTG_MIN_UV 2600000 -#define GOODIX_VTG_MAX_UV 3300000 -#define GOODIX_I2C_VTG_MIN_UV 1800000 -#define GOODIX_I2C_VTG_MAX_UV 1800000 -#define GOODIX_VDD_LOAD_MIN_UA 0 -#define GOODIX_VDD_LOAD_MAX_UA 10000 -#define GOODIX_VIO_LOAD_MIN_UA 0 -#define GOODIX_VIO_LOAD_MAX_UA 10000 - -#define RESET_DELAY_T3_US 200 /* T3: > 100us */ -#define RESET_DELAY_T4 20 /* T4: > 5ms */ -#define SLEEP_DELAY_US 5000 -#define WAKE_UP_DELAY_US 5000 - -#define PHY_BUF_SIZE 32 -#define PROP_NAME_SIZE 24 - -#define GTP_MAX_TOUCH 5 -#define GTP_ESD_CHECK_CIRCLE_MS 2000 - -static void gtp_int_sync(struct goodix_ts_data *ts, int ms); -static int gtp_i2c_test(struct i2c_client *client); -static int goodix_power_off(struct goodix_ts_data *ts); -static int goodix_power_on(struct goodix_ts_data *ts); - -#if defined(CONFIG_FB) -static int fb_notifier_callback(struct notifier_block *self, - unsigned long event, void *data); -static int goodix_ts_suspend(struct device *dev); -static int goodix_ts_resume(struct device *dev); -#elif defined(CONFIG_HAS_EARLYSUSPEND) -static void goodix_ts_early_suspend(struct early_suspend *h); -static void goodix_ts_late_resume(struct early_suspend *h); -#endif - -#if GTP_ESD_PROTECT -static struct delayed_work gtp_esd_check_work; -static struct workqueue_struct *gtp_esd_check_workqueue; -static void gtp_esd_check_func(struct work_struct *work); -static int gtp_init_ext_watchdog(struct i2c_client *client); -#endif - -enum doze { - DOZE_DISABLED = 0, - DOZE_ENABLED = 1, - DOZE_WAKEUP = 2, -}; -static enum doze doze_status = DOZE_DISABLED; -static s8 gtp_enter_doze(struct goodix_ts_data *ts); - -bool init_done; -static u8 chip_gt9xxs; /* true if ic is gt9xxs, like gt915s */ -u8 grp_cfg_version; -struct i2c_client *i2c_connect_client; - -#define GTP_DEBUGFS_DIR "ts_debug" -#define GTP_DEBUGFS_FILE_SUSPEND "suspend" -#define GTP_DEBUGFS_FILE_DATA "data" -#define GTP_DEBUGFS_FILE_ADDR "addr" - -/******************************************************* -Function: - Read data from the i2c slave device. -Input: - client: i2c device. - buf[0~1]: read start address. - buf[2~len-1]: read data buffer. - len: GTP_ADDR_LENGTH + read bytes count -Output: - numbers of i2c_msgs to transfer: - 2: succeed, otherwise: failed -*********************************************************/ -int gtp_i2c_read(struct i2c_client *client, u8 *buf, int len) -{ - struct goodix_ts_data *ts = i2c_get_clientdata(client); - int ret = -EIO; - u8 retries; - struct i2c_msg msgs[2] = { - { - .flags = !I2C_M_RD, - .addr = client->addr, - .len = GTP_ADDR_LENGTH, - .buf = &buf[0], - }, - { - .flags = I2C_M_RD, - .addr = client->addr, - .len = len - GTP_ADDR_LENGTH, - .buf = &buf[GTP_ADDR_LENGTH], - }, - }; - - for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) { - ret = i2c_transfer(client->adapter, msgs, 2); - if (ret == 2) - break; - dev_err(&client->dev, "I2C retry: %d\n", retries + 1); - } - if (retries == GTP_I2C_RETRY_5) { - if (ts->pdata->slide_wakeup) - /* reset chip would quit doze mode */ - if (doze_status == DOZE_ENABLED) - return ret; - - if (init_done) - gtp_reset_guitar(ts, 10); - else - dev_warn(&client->dev, - "gtp_reset_guitar exit init_done=%d:\n", - init_done); - } - return ret; -} - -/******************************************************* -Function: - Write data to the i2c slave device. -Input: - client: i2c device. - buf[0~1]: write start address. - buf[2~len-1]: data buffer - len: GTP_ADDR_LENGTH + write bytes count -Output: - numbers of i2c_msgs to transfer: - 1: succeed, otherwise: failed -*********************************************************/ -int gtp_i2c_write(struct i2c_client *client, u8 *buf, int len) -{ - struct goodix_ts_data *ts = i2c_get_clientdata(client); - int ret = -EIO; - u8 retries; - struct i2c_msg msg = { - .flags = !I2C_M_RD, - .addr = client->addr, - .len = len, - .buf = buf, - }; - - for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) { - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret == 1) - break; - dev_err(&client->dev, "I2C retry: %d\n", retries + 1); - } - if (retries == GTP_I2C_RETRY_5) { - if (ts->pdata->slide_wakeup) - if (doze_status == DOZE_ENABLED) - return ret; - - if (init_done) - gtp_reset_guitar(ts, 10); - else - dev_warn(&client->dev, - "gtp_reset_guitar exit init_done=%d:\n", - init_done); - } - return ret; -} - -/******************************************************* -Function: - i2c read twice, compare the results -Input: - client: i2c device - addr: operate address - rxbuf: read data to store, if compare successful - len: bytes to read -Output: - FAIL: read failed - SUCCESS: read successful -*********************************************************/ -int gtp_i2c_read_dbl_check(struct i2c_client *client, - u16 addr, u8 *rxbuf, int len) -{ - u8 buf[16] = {0}; - u8 confirm_buf[16] = {0}; - u8 retry = 0; - - while (retry++ < GTP_I2C_RETRY_3) { - memset(buf, 0xAA, 16); - buf[0] = (u8)(addr >> 8); - buf[1] = (u8)(addr & 0xFF); - gtp_i2c_read(client, buf, len + 2); - - memset(confirm_buf, 0xAB, 16); - confirm_buf[0] = (u8)(addr >> 8); - confirm_buf[1] = (u8)(addr & 0xFF); - gtp_i2c_read(client, confirm_buf, len + 2); - - if (!memcmp(buf, confirm_buf, len + 2)) - break; - } - if (retry < GTP_I2C_RETRY_3) { - memcpy(rxbuf, confirm_buf + 2, len); - return SUCCESS; - } - dev_err(&client->dev, - "i2c read 0x%04X, %d bytes, double check failed!", addr, len); - return FAIL; -} - -/******************************************************* -Function: - Send config data. -Input: - client: i2c device. -Output: - result of i2c write operation. - > 0: succeed, otherwise: failed -*********************************************************/ -int gtp_send_cfg(struct goodix_ts_data *ts) -{ - int ret = 0; - int retry; - - if (ts->pdata->driver_send_cfg) { - if (ts->fixed_cfg) { - dev_dbg(&ts->client->dev, - "Ic fixed config, no config sent!"); - ret = 2; - } else { - for (retry = 0; retry < GTP_I2C_RETRY_5; retry++) { - ret = gtp_i2c_write(ts->client, - ts->config_data, - GTP_CONFIG_MAX_LENGTH + - GTP_ADDR_LENGTH); - if (ret > 0) - break; - } - } - } - - return ret; -} - -/******************************************************* -Function: - Disable irq function -Input: - ts: goodix i2c_client private data -Output: - None. -*********************************************************/ -void gtp_irq_disable(struct goodix_ts_data *ts) -{ - unsigned long irqflags; - - spin_lock_irqsave(&ts->irq_lock, irqflags); - if (!ts->irq_is_disabled) { - ts->irq_is_disabled = true; - disable_irq_nosync(ts->client->irq); - } - spin_unlock_irqrestore(&ts->irq_lock, irqflags); -} - -/******************************************************* -Function: - Enable irq function -Input: - ts: goodix i2c_client private data -Output: - None. -*********************************************************/ -void gtp_irq_enable(struct goodix_ts_data *ts) -{ - unsigned long irqflags = 0; - - spin_lock_irqsave(&ts->irq_lock, irqflags); - if (ts->irq_is_disabled) { - enable_irq(ts->client->irq); - ts->irq_is_disabled = false; - } - spin_unlock_irqrestore(&ts->irq_lock, irqflags); -} - -/******************************************************* -Function: - Report touch point event -Input: - ts: goodix i2c_client private data - id: trackId - x: input x coordinate - y: input y coordinate - w: input pressure -Output: - None. -*********************************************************/ -static void gtp_touch_down(struct goodix_ts_data *ts, int id, int x, int y, - int w) -{ - if (ts->pdata->change_x2y) - swap(x, y); - - input_mt_slot(ts->input_dev, id); - input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); - input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x); - input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y); - input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w); - input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); -} - -/******************************************************* -Function: - Report touch release event -Input: - ts: goodix i2c_client private data -Output: - None. -*********************************************************/ -static void gtp_touch_up(struct goodix_ts_data *ts, int id) -{ - input_mt_slot(ts->input_dev, id); - input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); -} - - - -/******************************************************* -Function: - Goodix touchscreen work function -Input: - work: work struct of goodix_workqueue -Output: - None. -*********************************************************/ -static void goodix_ts_work_func(struct work_struct *work) -{ - u8 end_cmd[3] = { GTP_READ_COOR_ADDR >> 8, - GTP_READ_COOR_ADDR & 0xFF, 0}; - u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1] = { - GTP_READ_COOR_ADDR >> 8, - GTP_READ_COOR_ADDR & 0xFF}; - u8 touch_num = 0; - u8 finger = 0; - static u16 pre_touch; - static u8 pre_key; - static u8 pre_pen; - u8 key_value = 0; - u8 *coor_data = NULL; - s32 input_x = 0; - s32 input_y = 0; - s32 input_w = 0; - s32 id = 0; - s32 i = 0; - int ret = -1; - struct goodix_ts_data *ts = NULL; - u8 doze_buf[3] = {0x81, 0x4B}; - - ts = container_of(work, struct goodix_ts_data, work); -#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE - if (ts->enter_update) - return; -#endif - - if (ts->pdata->slide_wakeup) { - if (doze_status == DOZE_ENABLED) { - ret = gtp_i2c_read(ts->client, doze_buf, 3); - if (ret > 0) { - if (doze_buf[2] == 0xAA) { - dev_dbg(&ts->client->dev, - "Slide(0xAA) To Light up the screen!"); - doze_status = DOZE_WAKEUP; - input_report_key( - ts->input_dev, KEY_POWER, 1); - input_sync(ts->input_dev); - input_report_key( - ts->input_dev, KEY_POWER, 0); - input_sync(ts->input_dev); - /* clear 0x814B */ - doze_buf[2] = 0x00; - gtp_i2c_write(ts->client, doze_buf, 3); - } else if (doze_buf[2] == 0xBB) { - dev_dbg(&ts->client->dev, - "Slide(0xBB) To Light up the screen!"); - doze_status = DOZE_WAKEUP; - input_report_key(ts->input_dev, - KEY_POWER, 1); - input_sync(ts->input_dev); - input_report_key(ts->input_dev, - KEY_POWER, 0); - input_sync(ts->input_dev); - /* clear 0x814B*/ - doze_buf[2] = 0x00; - gtp_i2c_write(ts->client, doze_buf, 3); - } else if (0xC0 == (doze_buf[2] & 0xC0)) { - dev_dbg(&ts->client->dev, - "double click to light up the screen!"); - doze_status = DOZE_WAKEUP; - input_report_key(ts->input_dev, - KEY_POWER, 1); - input_sync(ts->input_dev); - input_report_key(ts->input_dev, - KEY_POWER, 0); - input_sync(ts->input_dev); - /* clear 0x814B */ - doze_buf[2] = 0x00; - gtp_i2c_write(ts->client, doze_buf, 3); - } else { - gtp_enter_doze(ts); - } - } - if (ts->use_irq) - gtp_irq_enable(ts); - - return; - } - } - - ret = gtp_i2c_read(ts->client, point_data, 12); - if (ret < 0) { - dev_err(&ts->client->dev, - "I2C transfer error. errno:%d\n ", ret); - goto exit_work_func; - } - - finger = point_data[GTP_ADDR_LENGTH]; - if ((finger & 0x80) == 0) - goto exit_work_func; - - touch_num = finger & 0x0f; - if (touch_num > GTP_MAX_TOUCH) - goto exit_work_func; - - if (touch_num > 1) { - u8 buf[8 * GTP_MAX_TOUCH] = { (GTP_READ_COOR_ADDR + 10) >> 8, - (GTP_READ_COOR_ADDR + 10) & 0xff }; - - ret = gtp_i2c_read(ts->client, buf, - 2 + 8 * (touch_num - 1)); - memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1)); - } - - - key_value = point_data[3 + 8 * touch_num]; - - if (key_value || pre_key) { - for (i = 0; i < ts->pdata->num_button; i++) { - input_report_key(ts->input_dev, - ts->pdata->button_map[i], - key_value & (0x01<<i)); - } - touch_num = 0; - pre_touch = 0; - } - - pre_key = key_value; - - if (ts->pdata->with_pen) { - if (pre_pen && (touch_num == 0)) { - dev_dbg(&ts->client->dev, "Pen touch UP(Slot)!"); - input_report_key(ts->input_dev, BTN_TOOL_PEN, 0); - input_mt_slot(ts->input_dev, 5); - input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1); - pre_pen = 0; - } - } - - if (pre_touch || touch_num) { - s32 pos = 0; - u16 touch_index = 0; - - coor_data = &point_data[3]; - if (touch_num) { - id = coor_data[pos] & 0x0F; - if (ts->pdata->with_pen) { - id = coor_data[pos]; - if (id == 128) { - dev_dbg(&ts->client->dev, - "Pen touch DOWN(Slot)!"); - input_x = coor_data[pos + 1] - | (coor_data[pos + 2] << 8); - input_y = coor_data[pos + 3] - | (coor_data[pos + 4] << 8); - input_w = coor_data[pos + 5] - | (coor_data[pos + 6] << 8); - - input_report_key(ts->input_dev, - BTN_TOOL_PEN, 1); - input_mt_slot(ts->input_dev, 5); - input_report_abs(ts->input_dev, - ABS_MT_TRACKING_ID, 5); - input_report_abs(ts->input_dev, - ABS_MT_POSITION_X, input_x); - input_report_abs(ts->input_dev, - ABS_MT_POSITION_Y, input_y); - input_report_abs(ts->input_dev, - ABS_MT_TOUCH_MAJOR, input_w); - dev_dbg(&ts->client->dev, - "Pen/Stylus: (%d, %d)[%d]", - input_x, input_y, input_w); - pre_pen = 1; - pre_touch = 0; - } - } - - touch_index |= (0x01<<id); - } - - for (i = 0; i < GTP_MAX_TOUCH; i++) { - if (ts->pdata->with_pen) - if (pre_pen == 1) - break; - - if (touch_index & (0x01<<i)) { - input_x = coor_data[pos + 1] | - coor_data[pos + 2] << 8; - input_y = coor_data[pos + 3] | - coor_data[pos + 4] << 8; - input_w = coor_data[pos + 5] | - coor_data[pos + 6] << 8; - - gtp_touch_down(ts, id, - input_x, input_y, input_w); - pre_touch |= 0x01 << i; - - pos += 8; - id = coor_data[pos] & 0x0F; - touch_index |= (0x01<<id); - } else { - gtp_touch_up(ts, i); - pre_touch &= ~(0x01 << i); - } - } - } - input_sync(ts->input_dev); - -exit_work_func: - if (!ts->gtp_rawdiff_mode) { - ret = gtp_i2c_write(ts->client, end_cmd, 3); - if (ret < 0) - dev_warn(&ts->client->dev, "I2C write end_cmd error!\n"); - - } - if (ts->use_irq) - gtp_irq_enable(ts); - - return; -} - -/******************************************************* -Function: - External interrupt service routine for interrupt mode. -Input: - irq: interrupt number. - dev_id: private data pointer -Output: - Handle Result. - IRQ_HANDLED: interrupt handled successfully -*********************************************************/ -static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) -{ - struct goodix_ts_data *ts = dev_id; - - gtp_irq_disable(ts); - - queue_work(ts->goodix_wq, &ts->work); - - return IRQ_HANDLED; -} -/******************************************************* -Function: - Synchronization. -Input: - ms: synchronization time in millisecond. -Output: - None. -*******************************************************/ -void gtp_int_sync(struct goodix_ts_data *ts, int ms) -{ - gpio_direction_output(ts->pdata->irq_gpio, 0); - msleep(ms); - gpio_direction_input(ts->pdata->irq_gpio); -} - -/******************************************************* -Function: - Reset chip. -Input: - ms: reset time in millisecond, must >10ms -Output: - None. -*******************************************************/ -void gtp_reset_guitar(struct goodix_ts_data *ts, int ms) -{ - /* This reset sequence will selcet I2C slave address */ - gpio_direction_output(ts->pdata->reset_gpio, 0); - msleep(ms); - - if (ts->client->addr == GTP_I2C_ADDRESS_HIGH) - gpio_direction_output(ts->pdata->irq_gpio, 1); - else - gpio_direction_output(ts->pdata->irq_gpio, 0); - - usleep_range(RESET_DELAY_T3_US, RESET_DELAY_T3_US + 1); - gpio_direction_output(ts->pdata->reset_gpio, 1); - msleep(RESET_DELAY_T4); - - gpio_direction_input(ts->pdata->reset_gpio); - - gtp_int_sync(ts, 50); - -#if GTP_ESD_PROTECT - gtp_init_ext_watchdog(ts->client); -#endif -} - -#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_FB) -/******************************************************* -Function: - Enter doze mode for sliding wakeup. -Input: - ts: goodix tp private data -Output: - 1: succeed, otherwise failed -*******************************************************/ -static s8 gtp_enter_doze(struct goodix_ts_data *ts) -{ - int ret = -1; - s8 retry = 0; - u8 i2c_control_buf[3] = { - (u8)(GTP_REG_SLEEP >> 8), - (u8)GTP_REG_SLEEP, 8}; - - if (ts->pdata->dbl_clk_wakeup) - i2c_control_buf[2] = 0x09; - - gtp_irq_disable(ts); - - while (retry++ < GTP_I2C_RETRY_3) { - i2c_control_buf[0] = 0x80; - i2c_control_buf[1] = 0x46; - ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); - if (ret < 0) { - dev_err(&ts->client->dev, - "failed to set doze flag into 0x8046, %d", - retry); - continue; - } - i2c_control_buf[0] = 0x80; - i2c_control_buf[1] = 0x40; - ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); - if (ret > 0) { - doze_status = DOZE_ENABLED; - dev_dbg(&ts->client->dev, - "GTP has been working in doze mode!"); - gtp_irq_enable(ts); - return ret; - } - msleep(20); - } - dev_err(&ts->client->dev, "GTP send doze cmd failed.\n"); - gtp_irq_enable(ts); - return ret; -} -/** - * gtp_enter_sleep - Enter sleep mode - * @ts: driver private data - * - * Returns zero on success, else an error. - */ -static u8 gtp_enter_sleep(struct goodix_ts_data *ts) -{ - int ret = -1; - s8 retry = 0; - u8 i2c_control_buf[3] = { - (u8)(GTP_REG_SLEEP >> 8), - (u8)GTP_REG_SLEEP, 5}; - - ret = gpio_direction_output(ts->pdata->irq_gpio, 0); - if (ret) - dev_err(&ts->client->dev, - "GTP sleep: Cannot reconfig gpio %d.\n", - ts->pdata->irq_gpio); - if (ts->pdata->enable_power_off) { - ret = gpio_direction_output(ts->pdata->reset_gpio, 0); - if (ret) - dev_err(&ts->client->dev, - "GTP sleep: Cannot reconfig gpio %d.\n", - ts->pdata->reset_gpio); - ret = goodix_power_off(ts); - if (ret) { - dev_err(&ts->client->dev, "GTP power off failed.\n"); - return ret; - } - return 0; - } - usleep_range(SLEEP_DELAY_US, SLEEP_DELAY_US + 1); - while (retry++ < GTP_I2C_RETRY_5) { - ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); - if (ret == 1) { - dev_dbg(&ts->client->dev, "GTP enter sleep!"); - return 0; - } - msleep(20); - } - dev_err(&ts->client->dev, "GTP send sleep cmd failed.\n"); - return ret; -} - -/******************************************************* -Function: - Wakeup from sleep. -Input: - ts: private data. -Output: - Executive outcomes. - >0: succeed, otherwise: failed. -*******************************************************/ -static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts) -{ - u8 retry = 0; - s8 ret = -1; - - if (ts->pdata->enable_power_off) { - ret = gpio_direction_output(ts->pdata->irq_gpio, 0); - if (ret) - dev_err(&ts->client->dev, - "GTP wakeup: Cannot reconfig gpio %d.\n", - ts->pdata->irq_gpio); - ret = gpio_direction_output(ts->pdata->reset_gpio, 0); - if (ret) - dev_err(&ts->client->dev, - "GTP wakeup: Cannot reconfig gpio %d.\n", - ts->pdata->reset_gpio); - ret = goodix_power_on(ts); - if (ret) { - dev_err(&ts->client->dev, "GTP power on failed.\n"); - return 0; - } - - gtp_reset_guitar(ts, 20); - - ret = gtp_send_cfg(ts); - if (ret <= 0) { - dev_err(&ts->client->dev, - "GTP wakeup sleep failed.\n"); - return ret; - } - - dev_dbg(&ts->client->dev, - "Wakeup sleep send config success."); - } else { -err_retry: - if (ts->pdata->slide_wakeup) { /* wakeup not by slide */ - if (doze_status != DOZE_WAKEUP) - gtp_reset_guitar(ts, 10); - else - /* wakeup by slide */ - doze_status = DOZE_DISABLED; - } else { - if (chip_gt9xxs == 1) { - gtp_reset_guitar(ts, 10); - } else { - ret = gpio_direction_output( - ts->pdata->irq_gpio, 1); - usleep_range(WAKE_UP_DELAY_US, - WAKE_UP_DELAY_US + 1); - } - } - ret = gtp_i2c_test(ts->client); - if (ret == 2) { - dev_dbg(&ts->client->dev, "GTP wakeup sleep."); - if (!ts->pdata->slide_wakeup) { - if (chip_gt9xxs == 0) { - gtp_int_sync(ts, 25); - msleep(20); -#if GTP_ESD_PROTECT - gtp_init_ext_watchdog(ts->client); -#endif - } - } - return ret; - } - gtp_reset_guitar(ts, 20); - if (retry++ < GTP_I2C_RETRY_10) - goto err_retry; - dev_err(&ts->client->dev, "GTP wakeup sleep failed.\n"); - } - return ret; -} -#endif /* !CONFIG_HAS_EARLYSUSPEND && !CONFIG_FB*/ - -/******************************************************* -Function: - Initialize gtp. -Input: - ts: goodix private data -Output: - Executive outcomes. - > =0: succeed, otherwise: failed -*******************************************************/ -static int gtp_init_panel(struct goodix_ts_data *ts) -{ - struct i2c_client *client = ts->client; - unsigned char *config_data = NULL; - int ret = -EIO; - int i; - u8 check_sum = 0; - u8 opr_buf[16]; - u8 sensor_id = 0; - - if (ts->pdata->driver_send_cfg) { - for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) - dev_dbg(&client->dev, "Config Groups(%d) Lengths: %zu", - i, ts->pdata->config_data_len[i]); - - ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1); - if (ret == SUCCESS) { - if (opr_buf[0] != 0xBE) { - ts->fw_error = 1; - dev_err(&client->dev, - "Firmware error, no config sent!"); - return -EINVAL; - } - } - - for (i = 1; i < GOODIX_MAX_CFG_GROUP; i++) { - if (ts->pdata->config_data_len[i]) - break; - } - - if (i == GOODIX_MAX_CFG_GROUP) { - sensor_id = 0; - } else { - ret = gtp_i2c_read_dbl_check(ts->client, - GTP_REG_SENSOR_ID, &sensor_id, 1); - if (ret == SUCCESS) { - if (sensor_id >= GOODIX_MAX_CFG_GROUP) { - dev_err(&client->dev, - "Invalid sensor_id(0x%02X), No Config Sent!", - sensor_id); - return -EINVAL; - } - } else { - dev_err(&client->dev, - "Failed to get sensor_id, No config sent!"); - return -EINVAL; - } - } - - dev_info(&client->dev, "Sensor ID selected: %d", sensor_id); - - if (ts->pdata->config_data_len[sensor_id] < - GTP_CONFIG_MIN_LENGTH || - !ts->pdata->config_data[sensor_id]) { - dev_err(&client->dev, - "Sensor_ID(%d) matches with NULL or invalid config group!\n", - sensor_id); - return -EINVAL; - } - - ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, - &opr_buf[0], 1); - if (ret == SUCCESS) { - if (opr_buf[0] < 90) { - /* backup group config version */ - grp_cfg_version = - ts->pdata-> - config_data[sensor_id][GTP_ADDR_LENGTH]; - ts->pdata-> - config_data[sensor_id][GTP_ADDR_LENGTH] - = 0x00; - ts->fixed_cfg = 0; - } else { - /* treated as fixed config, not send config */ - dev_warn(&client->dev, - "Ic fixed config with config version(%d, 0x%02X)", - opr_buf[0], opr_buf[0]); - ts->fixed_cfg = 1; - } - } else { - dev_err(&client->dev, - "Failed to get ic config version!No config sent!"); - return -EINVAL; - } - - config_data = ts->pdata->config_data[sensor_id]; - ts->config_data = ts->pdata->config_data[sensor_id]; - ts->gtp_cfg_len = ts->pdata->config_data_len[sensor_id]; - -#if GTP_CUSTOM_CFG - config_data[RESOLUTION_LOC] = - (unsigned char)(GTP_MAX_WIDTH && 0xFF); - config_data[RESOLUTION_LOC + 1] = - (unsigned char)(GTP_MAX_WIDTH >> 8); - config_data[RESOLUTION_LOC + 2] = - (unsigned char)(GTP_MAX_HEIGHT && 0xFF); - config_data[RESOLUTION_LOC + 3] = - (unsigned char)(GTP_MAX_HEIGHT >> 8); - - if (GTP_INT_TRIGGER == 0) - config_data[TRIGGER_LOC] &= 0xfe; - else if (GTP_INT_TRIGGER == 1) - config_data[TRIGGER_LOC] |= 0x01; -#endif /* !GTP_CUSTOM_CFG */ - - check_sum = 0; - for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++) - check_sum += config_data[i]; - - config_data[ts->gtp_cfg_len] = (~check_sum) + 1; - - } else { /* DRIVER NOT SEND CONFIG */ - ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH; - ret = gtp_i2c_read(ts->client, config_data, - ts->gtp_cfg_len + GTP_ADDR_LENGTH); - if (ret < 0) { - dev_err(&client->dev, - "Read Config Failed, Using DEFAULT Resolution & INT Trigger!\n"); - ts->abs_x_max = GTP_MAX_WIDTH; - ts->abs_y_max = GTP_MAX_HEIGHT; - ts->int_trigger_type = GTP_INT_TRIGGER; - } - } /* !DRIVER NOT SEND CONFIG */ - - if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) { - ts->abs_x_max = (config_data[RESOLUTION_LOC + 1] << 8) - + config_data[RESOLUTION_LOC]; - ts->abs_y_max = (config_data[RESOLUTION_LOC + 3] << 8) - + config_data[RESOLUTION_LOC + 2]; - ts->int_trigger_type = (config_data[TRIGGER_LOC]) & 0x03; - } - ret = gtp_send_cfg(ts); - if (ret < 0) - dev_err(&client->dev, "%s: Send config error.\n", __func__); - - msleep(20); - return ret; -} - -/******************************************************* -Function: - Read firmware version -Input: - client: i2c device - version: buffer to keep ic firmware version -Output: - read operation return. - 0: succeed, otherwise: failed -*******************************************************/ -static int gtp_read_fw_version(struct i2c_client *client, u16 *version) -{ - int ret = 0; - u8 buf[GTP_FW_VERSION_BUFFER_MAXSIZE] = { - GTP_REG_FW_VERSION >> 8, GTP_REG_FW_VERSION & 0xff }; - - ret = gtp_i2c_read(client, buf, sizeof(buf)); - if (ret < 0) { - dev_err(&client->dev, "GTP read version failed.\n"); - return -EIO; - } - - if (version) - *version = (buf[3] << 8) | buf[2]; - - return ret; -} -/* - * Function: - * Read and check chip id. - * Input: - * client: i2c device - * Output: - * read operation return. - * 0: succeed, otherwise: failed - */ -static int gtp_check_product_id(struct i2c_client *client) -{ - int ret = 0; - char product_id[GTP_PRODUCT_ID_MAXSIZE]; - struct goodix_ts_data *ts = i2c_get_clientdata(client); - /* 04 bytes are used for the Product-id in the register space.*/ - u8 buf[GTP_PRODUCT_ID_BUFFER_MAXSIZE] = { - GTP_REG_PRODUCT_ID >> 8, GTP_REG_PRODUCT_ID & 0xff }; - - ret = gtp_i2c_read(client, buf, sizeof(buf)); - if (ret < 0) { - dev_err(&client->dev, "GTP read product_id failed.\n"); - return -EIO; - } - - if (buf[5] == 0x00) { - /* copy (GTP_PRODUCT_ID_MAXSIZE - 1) from buffer. Ex: 915 */ - strlcpy(product_id, &buf[2], GTP_PRODUCT_ID_MAXSIZE - 1); - } else { - if (buf[5] == 'S' || buf[5] == 's') - chip_gt9xxs = 1; - /* copy GTP_PRODUCT_ID_MAXSIZE from buffer. Ex: 915s */ - strlcpy(product_id, &buf[2], GTP_PRODUCT_ID_MAXSIZE); - } - - dev_info(&client->dev, "Goodix Product ID = %s\n", product_id); - - ret = strcmp(product_id, ts->pdata->product_id); - if (ret != 0) - return -EINVAL; - - return ret; -} - -/******************************************************* -Function: - I2c test Function. -Input: - client:i2c client. -Output: - Executive outcomes. - 2: succeed, otherwise failed. -*******************************************************/ -static int gtp_i2c_test(struct i2c_client *client) -{ - u8 buf[3] = { GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff }; - int retry = GTP_I2C_RETRY_5; - int ret = -EIO; - - while (retry--) { - ret = gtp_i2c_read(client, buf, 3); - if (ret > 0) - return ret; - dev_err(&client->dev, "GTP i2c test failed time %d.\n", retry); - msleep(20); - } - return ret; -} - -/******************************************************* -Function: - Request gpio(INT & RST) ports. -Input: - ts: private data. -Output: - Executive outcomes. - = 0: succeed, != 0: failed -*******************************************************/ -static int gtp_request_io_port(struct goodix_ts_data *ts) -{ - struct i2c_client *client = ts->client; - struct goodix_ts_platform_data *pdata = ts->pdata; - int ret; - - if (gpio_is_valid(pdata->irq_gpio)) { - ret = gpio_request(pdata->irq_gpio, "goodix_ts_irq_gpio"); - if (ret) { - dev_err(&client->dev, "Unable to request irq gpio [%d]\n", - pdata->irq_gpio); - goto err_pwr_off; - } - ret = gpio_direction_input(pdata->irq_gpio); - if (ret) { - dev_err(&client->dev, "Unable to set direction for irq gpio [%d]\n", - pdata->irq_gpio); - goto err_free_irq_gpio; - } - } else { - dev_err(&client->dev, "Invalid irq gpio [%d]!\n", - pdata->irq_gpio); - ret = -EINVAL; - goto err_pwr_off; - } - - if (gpio_is_valid(pdata->reset_gpio)) { - ret = gpio_request(pdata->reset_gpio, "goodix_ts_reset_gpio"); - if (ret) { - dev_err(&client->dev, "Unable to request reset gpio [%d]\n", - pdata->reset_gpio); - goto err_free_irq_gpio; - } - - ret = gpio_direction_output(pdata->reset_gpio, 0); - if (ret) { - dev_err(&client->dev, "Unable to set direction for reset gpio [%d]\n", - pdata->reset_gpio); - goto err_free_reset_gpio; - } - } else { - dev_err(&client->dev, "Invalid irq gpio [%d]!\n", - pdata->reset_gpio); - ret = -EINVAL; - goto err_free_irq_gpio; - } - /* IRQ GPIO is an input signal, but we are setting it to output - * direction and pulling it down, to comply with power up timing - * requirements, mentioned in power up timing section of device - * datasheet. - */ - ret = gpio_direction_output(pdata->irq_gpio, 0); - if (ret) - dev_warn(&client->dev, - "pull down interrupt gpio failed\n"); - ret = gpio_direction_output(pdata->reset_gpio, 0); - if (ret) - dev_warn(&client->dev, - "pull down reset gpio failed\n"); - - return ret; - -err_free_reset_gpio: - if (gpio_is_valid(pdata->reset_gpio)) - gpio_free(pdata->reset_gpio); -err_free_irq_gpio: - if (gpio_is_valid(pdata->irq_gpio)) - gpio_free(pdata->irq_gpio); -err_pwr_off: - return ret; -} - -/******************************************************* -Function: - Request interrupt. -Input: - ts: private data. -Output: - Executive outcomes. - 0: succeed, -1: failed. -*******************************************************/ -static int gtp_request_irq(struct goodix_ts_data *ts) -{ - int ret; - const u8 irq_table[] = GTP_IRQ_TAB; - - dev_dbg(&ts->client->dev, "INT trigger type:%x, irq=%d", - ts->int_trigger_type, - ts->client->irq); - - ret = request_threaded_irq(ts->client->irq, NULL, - goodix_ts_irq_handler, - irq_table[ts->int_trigger_type], - ts->client->name, ts); - if (ret) { - ts->use_irq = false; - return ret; - } - gtp_irq_disable(ts); - ts->use_irq = true; - return 0; -} - -/******************************************************* -Function: - Request input device Function. -Input: - ts:private data. -Output: - Executive outcomes. - 0: succeed, otherwise: failed. -*******************************************************/ -static int gtp_request_input_dev(struct goodix_ts_data *ts) -{ - int ret; - char phys[PHY_BUF_SIZE]; - int index = 0; - - ts->input_dev = input_allocate_device(); - if (ts->input_dev == NULL) { - dev_err(&ts->client->dev, - "Failed to allocate input device.\n"); - return -ENOMEM; - } - - ts->input_dev->evbit[0] = - BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - set_bit(BTN_TOOL_FINGER, ts->input_dev->keybit); - __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); - /* in case of "out of memory" */ - input_mt_init_slots(ts->input_dev, 10, 0); - - if (ts->pdata->have_touch_key) { - for (index = 0; index < ts->pdata->num_button; index++) { - input_set_capability(ts->input_dev, - EV_KEY, ts->pdata->button_map[index]); - } - } - - if (ts->pdata->slide_wakeup) - input_set_capability(ts->input_dev, EV_KEY, KEY_POWER); - - if (ts->pdata->with_pen) { /* pen support */ - __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit); - __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); - __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit); - } - - if (ts->pdata->change_x2y) - swap(ts->abs_x_max, ts->abs_y_max); - - input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, - 0, ts->abs_x_max, 0, 0); - input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, - 0, ts->abs_y_max, 0, 0); - input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, - 0, 255, 0, 0); - input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, - 0, 255, 0, 0); - input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, - 0, 255, 0, 0); - - snprintf(phys, PHY_BUF_SIZE, "input/ts"); - ts->input_dev->name = GOODIX_DEV_NAME; - ts->input_dev->phys = phys; - ts->input_dev->id.bustype = BUS_I2C; - ts->input_dev->id.vendor = 0xDEAD; - ts->input_dev->id.product = 0xBEEF; - ts->input_dev->id.version = 10427; - - ret = input_register_device(ts->input_dev); - if (ret) { - dev_err(&ts->client->dev, - "Register %s input device failed.\n", - ts->input_dev->name); - goto exit_free_inputdev; - } - - return 0; - -exit_free_inputdev: - input_free_device(ts->input_dev); - ts->input_dev = NULL; - return ret; -} - -static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA) -{ - return (regulator_count_voltages(reg) > 0) ? - regulator_set_load(reg, load_uA) : 0; -} - -/** - * goodix_power_on - Turn device power ON - * @ts: driver private data - * - * Returns zero on success, else an error. - */ -static int goodix_power_on(struct goodix_ts_data *ts) -{ - int ret; - - if (ts->power_on) { - dev_info(&ts->client->dev, - "Device already power on\n"); - return 0; - } - - if (!IS_ERR(ts->avdd)) { - ret = reg_set_optimum_mode_check(ts->avdd, - GOODIX_VDD_LOAD_MAX_UA); - if (ret < 0) { - dev_err(&ts->client->dev, - "Regulator avdd set_opt failed rc=%d\n", ret); - goto err_set_opt_avdd; - } - ret = regulator_enable(ts->avdd); - if (ret) { - dev_err(&ts->client->dev, - "Regulator avdd enable failed ret=%d\n", ret); - goto err_enable_avdd; - } - } - - if (!IS_ERR(ts->vdd)) { - ret = regulator_set_voltage(ts->vdd, GOODIX_VTG_MIN_UV, - GOODIX_VTG_MAX_UV); - if (ret) { - dev_err(&ts->client->dev, - "Regulator set_vtg failed vdd ret=%d\n", ret); - goto err_set_vtg_vdd; - } - ret = reg_set_optimum_mode_check(ts->vdd, - GOODIX_VDD_LOAD_MAX_UA); - if (ret < 0) { - dev_err(&ts->client->dev, - "Regulator vdd set_opt failed rc=%d\n", ret); - goto err_set_opt_vdd; - } - ret = regulator_enable(ts->vdd); - if (ret) { - dev_err(&ts->client->dev, - "Regulator vdd enable failed ret=%d\n", ret); - goto err_enable_vdd; - } - } - - if (!IS_ERR(ts->vcc_i2c)) { - ret = regulator_set_voltage(ts->vcc_i2c, GOODIX_I2C_VTG_MIN_UV, - GOODIX_I2C_VTG_MAX_UV); - if (ret) { - dev_err(&ts->client->dev, - "Regulator set_vtg failed vcc_i2c ret=%d\n", - ret); - goto err_set_vtg_vcc_i2c; - } - ret = reg_set_optimum_mode_check(ts->vcc_i2c, - GOODIX_VIO_LOAD_MAX_UA); - if (ret < 0) { - dev_err(&ts->client->dev, - "Regulator vcc_i2c set_opt failed rc=%d\n", - ret); - goto err_set_opt_vcc_i2c; - } - ret = regulator_enable(ts->vcc_i2c); - if (ret) { - dev_err(&ts->client->dev, - "Regulator vcc_i2c enable failed ret=%d\n", - ret); - regulator_disable(ts->vdd); - goto err_enable_vcc_i2c; - } - } - - ts->power_on = true; - return 0; - -err_enable_vcc_i2c: -err_set_opt_vcc_i2c: - if (!IS_ERR(ts->vcc_i2c)) - regulator_set_voltage(ts->vcc_i2c, 0, GOODIX_I2C_VTG_MAX_UV); -err_set_vtg_vcc_i2c: - if (!IS_ERR(ts->vdd)) - regulator_disable(ts->vdd); -err_enable_vdd: -err_set_opt_vdd: - if (!IS_ERR(ts->vdd)) - regulator_set_voltage(ts->vdd, 0, GOODIX_VTG_MAX_UV); -err_set_vtg_vdd: - if (!IS_ERR(ts->avdd)) - regulator_disable(ts->avdd); -err_enable_avdd: -err_set_opt_avdd: - ts->power_on = false; - return ret; -} - -/** - * goodix_power_off - Turn device power OFF - * @ts: driver private data - * - * Returns zero on success, else an error. - */ -static int goodix_power_off(struct goodix_ts_data *ts) -{ - int ret; - - if (!ts->power_on) { - dev_info(&ts->client->dev, - "Device already power off\n"); - return 0; - } - - if (!IS_ERR(ts->vcc_i2c)) { - ret = regulator_set_voltage(ts->vcc_i2c, 0, - GOODIX_I2C_VTG_MAX_UV); - if (ret < 0) - dev_err(&ts->client->dev, - "Regulator vcc_i2c set_vtg failed ret=%d\n", - ret); - ret = regulator_disable(ts->vcc_i2c); - if (ret) - dev_err(&ts->client->dev, - "Regulator vcc_i2c disable failed ret=%d\n", - ret); - } - - if (!IS_ERR(ts->vdd)) { - ret = regulator_set_voltage(ts->vdd, 0, GOODIX_VTG_MAX_UV); - if (ret < 0) - dev_err(&ts->client->dev, - "Regulator vdd set_vtg failed ret=%d\n", ret); - ret = regulator_disable(ts->vdd); - if (ret) - dev_err(&ts->client->dev, - "Regulator vdd disable failed ret=%d\n", ret); - } - - if (!IS_ERR(ts->avdd)) { - ret = regulator_disable(ts->avdd); - if (ret) - dev_err(&ts->client->dev, - "Regulator avdd disable failed ret=%d\n", ret); - } - - ts->power_on = false; - return 0; -} - -/** - * goodix_power_init - Initialize device power - * @ts: driver private data - * - * Returns zero on success, else an error. - */ -static int goodix_power_init(struct goodix_ts_data *ts) -{ - int ret; - - ts->avdd = regulator_get(&ts->client->dev, "avdd"); - if (IS_ERR(ts->avdd)) { - ret = PTR_ERR(ts->avdd); - dev_info(&ts->client->dev, - "Regulator get failed avdd ret=%d\n", ret); - } - - ts->vdd = regulator_get(&ts->client->dev, "vdd"); - if (IS_ERR(ts->vdd)) { - ret = PTR_ERR(ts->vdd); - dev_info(&ts->client->dev, - "Regulator get failed vdd ret=%d\n", ret); - } - - ts->vcc_i2c = regulator_get(&ts->client->dev, "vcc-i2c"); - if (IS_ERR(ts->vcc_i2c)) { - ret = PTR_ERR(ts->vcc_i2c); - dev_info(&ts->client->dev, - "Regulator get failed vcc_i2c ret=%d\n", ret); - } - - return 0; -} - -/** - * goodix_power_deinit - Deinitialize device power - * @ts: driver private data - * - * Returns zero on success, else an error. - */ -static int goodix_power_deinit(struct goodix_ts_data *ts) -{ - regulator_put(ts->vdd); - regulator_put(ts->vcc_i2c); - regulator_put(ts->avdd); - - return 0; -} - -static ssize_t gtp_fw_name_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct goodix_ts_data *ts = dev_get_drvdata(dev); - - if (!strlen(ts->fw_name)) - return snprintf(buf, GTP_FW_NAME_MAXSIZE - 1, - "No fw name has been given."); - else - return snprintf(buf, GTP_FW_NAME_MAXSIZE - 1, - "%s\n", ts->fw_name); -} - -static ssize_t gtp_fw_name_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct goodix_ts_data *ts = dev_get_drvdata(dev); - - if (size > GTP_FW_NAME_MAXSIZE - 1) { - dev_err(dev, "FW name size exceeds the limit."); - return -EINVAL; - } - - strlcpy(ts->fw_name, buf, size); - if (ts->fw_name[size-1] == '\n') - ts->fw_name[size-1] = '\0'; - - return size; -} - -static ssize_t gtp_fw_upgrade_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct goodix_ts_data *ts = dev_get_drvdata(dev); - - return snprintf(buf, 2, "%d\n", ts->fw_loading); -} - -static ssize_t gtp_fw_upgrade_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct goodix_ts_data *ts = dev_get_drvdata(dev); - unsigned int val; - int ret; - - if (size > 2) - return -EINVAL; - - ret = kstrtouint(buf, 10, &val); - if (ret) - return ret; - - if (ts->gtp_is_suspend) { - dev_err(&ts->client->dev, - "Can't start fw upgrade. Device is in suspend state"); - return -EBUSY; - } - - mutex_lock(&ts->input_dev->mutex); - if (!ts->fw_loading && val) { - disable_irq(ts->client->irq); - ts->fw_loading = true; - if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) { - ret = gup_update_proc(NULL); - if (ret == FAIL) - dev_err(&ts->client->dev, - "Fail to update GTP firmware\n"); - } - ts->fw_loading = false; - enable_irq(ts->client->irq); - } - mutex_unlock(&ts->input_dev->mutex); - - return size; -} - -static ssize_t gtp_force_fw_upgrade_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct goodix_ts_data *ts = dev_get_drvdata(dev); - unsigned int val; - int ret; - - if (size > 2) - return -EINVAL; - - ret = kstrtouint(buf, 10, &val); - if (ret) - return ret; - - if (ts->gtp_is_suspend) { - dev_err(&ts->client->dev, - "Can't start fw upgrade. Device is in suspend state."); - return -EBUSY; - } - - mutex_lock(&ts->input_dev->mutex); - if (!ts->fw_loading && val) { - disable_irq(ts->client->irq); - ts->fw_loading = true; - ts->force_update = true; - if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) { - ret = gup_update_proc(NULL); - if (ret == FAIL) - dev_err(&ts->client->dev, - "Fail to force update GTP firmware.\n"); - } - ts->force_update = false; - ts->fw_loading = false; - enable_irq(ts->client->irq); - } - mutex_unlock(&ts->input_dev->mutex); - - return size; -} - -static DEVICE_ATTR(fw_name, (S_IRUGO | S_IWUSR | S_IWGRP), - gtp_fw_name_show, - gtp_fw_name_store); -static DEVICE_ATTR(fw_upgrade, (S_IRUGO | S_IWUSR | S_IWGRP), - gtp_fw_upgrade_show, - gtp_fw_upgrade_store); -static DEVICE_ATTR(force_fw_upgrade, (S_IRUGO | S_IWUSR | S_IWGRP), - gtp_fw_upgrade_show, - gtp_force_fw_upgrade_store); - -static struct attribute *gtp_attrs[] = { - &dev_attr_fw_name.attr, - &dev_attr_fw_upgrade.attr, - &dev_attr_force_fw_upgrade.attr, - NULL -}; - -static const struct attribute_group gtp_attr_grp = { - .attrs = gtp_attrs, -}; - -static int gtp_debug_addr_is_valid(u16 addr) -{ - if (addr < GTP_VALID_ADDR_START || addr > GTP_VALID_ADDR_END) { - pr_err("GTP reg address is invalid: 0x%x\n", addr); - return false; - } - - return true; -} - -static int gtp_debug_data_set(void *_data, u64 val) -{ - struct goodix_ts_data *ts = _data; - - mutex_lock(&ts->input_dev->mutex); - if (gtp_debug_addr_is_valid(ts->addr)) - dev_err(&ts->client->dev, - "Writing to GTP registers not supported\n"); - mutex_unlock(&ts->input_dev->mutex); - - return 0; -} - -static int gtp_debug_data_get(void *_data, u64 *val) -{ - struct goodix_ts_data *ts = _data; - int ret; - u8 buf[3] = {0}; - - mutex_lock(&ts->input_dev->mutex); - buf[0] = ts->addr >> 8; - buf[1] = ts->addr & 0x00ff; - - if (gtp_debug_addr_is_valid(ts->addr)) { - ret = gtp_i2c_read(ts->client, buf, 3); - if (ret < 0) - dev_err(&ts->client->dev, - "GTP read register 0x%x failed (%d)\n", - ts->addr, ret); - else - *val = buf[2]; - } - mutex_unlock(&ts->input_dev->mutex); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, gtp_debug_data_get, - gtp_debug_data_set, "%llx\n"); - -static int gtp_debug_addr_set(void *_data, u64 val) -{ - struct goodix_ts_data *ts = _data; - - if (gtp_debug_addr_is_valid(val)) { - mutex_lock(&ts->input_dev->mutex); - ts->addr = val; - mutex_unlock(&ts->input_dev->mutex); - } - - return 0; -} - -static int gtp_debug_addr_get(void *_data, u64 *val) -{ - struct goodix_ts_data *ts = _data; - - mutex_lock(&ts->input_dev->mutex); - if (gtp_debug_addr_is_valid(ts->addr)) - *val = ts->addr; - mutex_unlock(&ts->input_dev->mutex); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, gtp_debug_addr_get, - gtp_debug_addr_set, "%llx\n"); - -static int gtp_debug_suspend_set(void *_data, u64 val) -{ - struct goodix_ts_data *ts = _data; - - mutex_lock(&ts->input_dev->mutex); - if (val) - goodix_ts_suspend(&ts->client->dev); - else - goodix_ts_resume(&ts->client->dev); - mutex_unlock(&ts->input_dev->mutex); - - return 0; -} - -static int gtp_debug_suspend_get(void *_data, u64 *val) -{ - struct goodix_ts_data *ts = _data; - - mutex_lock(&ts->input_dev->mutex); - *val = ts->gtp_is_suspend; - mutex_unlock(&ts->input_dev->mutex); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, gtp_debug_suspend_get, - gtp_debug_suspend_set, "%lld\n"); - -static int gtp_debugfs_init(struct goodix_ts_data *data) -{ - data->debug_base = debugfs_create_dir(GTP_DEBUGFS_DIR, NULL); - - if (IS_ERR_OR_NULL(data->debug_base)) { - dev_err(&data->client->dev, "Failed to create debugfs dir\n"); - return -EINVAL; - } - - if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_SUSPEND, - S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP, - data->debug_base, - data, - &debug_suspend_fops)))) { - dev_err(&data->client->dev, "Failed to create suspend file\n"); - debugfs_remove_recursive(data->debug_base); - return -EINVAL; - } - - if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_DATA, - S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP, - data->debug_base, - data, - &debug_data_fops)))) { - dev_err(&data->client->dev, "Failed to create data file\n"); - debugfs_remove_recursive(data->debug_base); - return -EINVAL; - } - - if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_ADDR, - S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP, - data->debug_base, - data, - &debug_addr_fops)))) { - dev_err(&data->client->dev, "Failed to create addr file\n"); - debugfs_remove_recursive(data->debug_base); - return -EINVAL; - } - - return 0; -} - -static int goodix_ts_get_dt_coords(struct device *dev, char *name, - struct goodix_ts_platform_data *pdata) -{ - struct property *prop; - struct device_node *np = dev->of_node; - int rc; - u32 coords[GOODIX_COORDS_ARR_SIZE]; - - prop = of_find_property(np, name, NULL); - if (!prop) - return -EINVAL; - if (!prop->value) - return -ENODATA; - - rc = of_property_read_u32_array(np, name, coords, - GOODIX_COORDS_ARR_SIZE); - if (rc && (rc != -EINVAL)) { - dev_err(dev, "Unable to read %s\n", name); - return rc; - } - - if (!strcmp(name, "goodix,panel-coords")) { - pdata->panel_minx = coords[0]; - pdata->panel_miny = coords[1]; - pdata->panel_maxx = coords[2]; - pdata->panel_maxy = coords[3]; - } else if (!strcmp(name, "goodix,display-coords")) { - pdata->x_min = coords[0]; - pdata->y_min = coords[1]; - pdata->x_max = coords[2]; - pdata->y_max = coords[3]; - } else { - dev_err(dev, "unsupported property %s\n", name); - return -EINVAL; - } - - return 0; -} - -static int goodix_parse_dt(struct device *dev, - struct goodix_ts_platform_data *pdata) -{ - int rc; - struct device_node *np = dev->of_node; - struct property *prop; - u32 temp_val, num_buttons; - u32 button_map[MAX_BUTTONS]; - char prop_name[PROP_NAME_SIZE]; - int i, read_cfg_num, temp; - - rc = goodix_ts_get_dt_coords(dev, "goodix,panel-coords", pdata); - if (rc && (rc != -EINVAL)) - return rc; - - rc = goodix_ts_get_dt_coords(dev, "goodix,display-coords", pdata); - if (rc) - return rc; - - pdata->i2c_pull_up = of_property_read_bool(np, - "goodix,i2c-pull-up"); - - pdata->force_update = of_property_read_bool(np, - "goodix,force-update"); - - pdata->enable_power_off = of_property_read_bool(np, - "goodix,enable-power-off"); - - pdata->have_touch_key = of_property_read_bool(np, - "goodix,have-touch-key"); - - pdata->driver_send_cfg = of_property_read_bool(np, - "goodix,driver-send-cfg"); - - pdata->change_x2y = of_property_read_bool(np, - "goodix,change-x2y"); - - pdata->with_pen = of_property_read_bool(np, - "goodix,with-pen"); - - pdata->slide_wakeup = of_property_read_bool(np, - "goodix,slide-wakeup"); - - pdata->dbl_clk_wakeup = of_property_read_bool(np, - "goodix,dbl_clk_wakeup"); - - /* reset, irq gpio info */ - pdata->reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", - 0, &pdata->reset_gpio_flags); - if (pdata->reset_gpio < 0) - return pdata->reset_gpio; - - pdata->irq_gpio = of_get_named_gpio_flags(np, "interrupt-gpios", - 0, &pdata->irq_gpio_flags); - if (pdata->irq_gpio < 0) - return pdata->irq_gpio; - - rc = of_property_read_string(np, "goodix,product-id", - &pdata->product_id); - if (rc && (rc != -EINVAL)) { - dev_err(dev, "Failed to parse product_id."); - return -EINVAL; - } - - rc = of_property_read_string(np, "goodix,fw_name", - &pdata->fw_name); - if (rc && (rc != -EINVAL)) { - dev_err(dev, "Failed to parse firmware name.\n"); - return -EINVAL; - } - - prop = of_find_property(np, "goodix,button-map", NULL); - if (prop) { - num_buttons = prop->length / sizeof(temp_val); - if (num_buttons > MAX_BUTTONS) - return -EINVAL; - - rc = of_property_read_u32_array(np, - "goodix,button-map", button_map, - num_buttons); - if (rc) { - dev_err(dev, "Unable to read key codes\n"); - return rc; - } - pdata->num_button = num_buttons; - memcpy(pdata->button_map, button_map, - pdata->num_button * sizeof(u32)); - } - - read_cfg_num = 0; - for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) { - temp = 0; - snprintf(prop_name, sizeof(prop_name), "goodix,cfg-data%d", i); - prop = of_find_property(np, prop_name, &temp); - if (!prop || !prop->value) { - pdata->config_data_len[i] = 0; - pdata->config_data[i] = NULL; - continue; - } - pdata->config_data_len[i] = temp; - pdata->config_data[i] = devm_kzalloc(dev, - GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH, - GFP_KERNEL); - if (!pdata->config_data[i]) { - dev_err(dev, - "Not enough memory for panel config data %d\n", - i); - return -ENOMEM; - } - pdata->config_data[i][0] = GTP_REG_CONFIG_DATA >> 8; - pdata->config_data[i][1] = GTP_REG_CONFIG_DATA & 0xff; - memcpy(&pdata->config_data[i][GTP_ADDR_LENGTH], - prop->value, pdata->config_data_len[i]); - read_cfg_num++; - } - dev_dbg(dev, "%d config data read from device tree\n", read_cfg_num); - - return 0; -} - -/******************************************************* -Function: - I2c probe. -Input: - client: i2c device struct. - id: device id. -Output: - Executive outcomes. - 0: succeed. -*******************************************************/ - -static int goodix_ts_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct goodix_ts_platform_data *pdata; - struct goodix_ts_data *ts; - u16 version_info; - int ret; - - dev_dbg(&client->dev, "GTP I2C Address: 0x%02x\n", client->addr); - if (client->dev.of_node) { - pdata = devm_kzalloc(&client->dev, - sizeof(struct goodix_ts_platform_data), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - ret = goodix_parse_dt(&client->dev, pdata); - if (ret) - return ret; - } else { - pdata = client->dev.platform_data; - } - - if (!pdata) { - dev_err(&client->dev, "GTP invalid pdata\n"); - return -EINVAL; - } - - i2c_connect_client = client; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(&client->dev, "GTP I2C not supported\n"); - return -ENODEV; - } - - ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); - if (!ts) - return -ENOMEM; - - memset(ts, 0, sizeof(*ts)); - ts->client = client; - ts->pdata = pdata; - /* For 2.6.39 & later use spin_lock_init(&ts->irq_lock) - * For 2.6.39 & before, use ts->irq_lock = SPIN_LOCK_UNLOCKED - */ - spin_lock_init(&ts->irq_lock); - i2c_set_clientdata(client, ts); - ts->gtp_rawdiff_mode = 0; - ts->power_on = false; - - ret = gtp_request_io_port(ts); - if (ret) { - dev_err(&client->dev, "GTP request IO port failed.\n"); - goto exit_free_client_data; - } - - ret = goodix_power_init(ts); - if (ret) { - dev_err(&client->dev, "GTP power init failed\n"); - goto exit_free_io_port; - } - - ret = goodix_power_on(ts); - if (ret) { - dev_err(&client->dev, "GTP power on failed\n"); - goto exit_deinit_power; - } - - gtp_reset_guitar(ts, 20); - - ret = gtp_i2c_test(client); - if (ret != 2) { - dev_err(&client->dev, "I2C communication ERROR\n"); - goto exit_power_off; - } - - if (pdata->force_update) - ts->force_update = true; - - if (pdata->fw_name) - strlcpy(ts->fw_name, pdata->fw_name, - strlen(pdata->fw_name) + 1); - - if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) { - ret = gup_init_update_proc(ts); - if (ret < 0) { - dev_err(&client->dev, - "GTP Create firmware update thread error\n"); - goto exit_power_off; - } - } - ret = gtp_init_panel(ts); - if (ret < 0) { - dev_err(&client->dev, "GTP init panel failed\n"); - ts->abs_x_max = GTP_MAX_WIDTH; - ts->abs_y_max = GTP_MAX_HEIGHT; - ts->int_trigger_type = GTP_INT_TRIGGER; - } - - ret = gtp_request_input_dev(ts); - if (ret) { - dev_err(&client->dev, "GTP request input dev failed\n"); - goto exit_free_inputdev; - } - input_set_drvdata(ts->input_dev, ts); - - mutex_init(&ts->lock); -#if defined(CONFIG_FB) - ts->fb_notif.notifier_call = fb_notifier_callback; - ret = fb_register_client(&ts->fb_notif); - if (ret) - dev_err(&ts->client->dev, - "Unable to register fb_notifier: %d\n", - ret); -#elif defined(CONFIG_HAS_EARLYSUSPEND) - ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; - ts->early_suspend.suspend = goodix_ts_early_suspend; - ts->early_suspend.resume = goodix_ts_late_resume; - register_early_suspend(&ts->early_suspend); -#endif - - ts->goodix_wq = create_singlethread_workqueue("goodix_wq"); - INIT_WORK(&ts->work, goodix_ts_work_func); - - ret = gtp_request_irq(ts); - if (ret) - dev_info(&client->dev, "GTP request irq failed %d\n", ret); - else - dev_info(&client->dev, "GTP works in interrupt mode\n"); - - ret = gtp_read_fw_version(client, &version_info); - if (ret != 2) - dev_err(&client->dev, "GTP firmware version read failed\n"); - - ret = gtp_check_product_id(client); - if (ret != 0) { - dev_err(&client->dev, "GTP Product id doesn't match\n"); - goto exit_free_irq; - } - if (ts->use_irq) - gtp_irq_enable(ts); - -#ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG - init_wr_node(client); -#endif - -#if GTP_ESD_PROTECT - gtp_esd_switch(client, SWITCH_ON); -#endif - ret = sysfs_create_group(&client->dev.kobj, >p_attr_grp); - if (ret < 0) { - dev_err(&client->dev, "sys file creation failed\n"); - goto exit_free_irq; - } - - ret = gtp_debugfs_init(ts); - if (ret != 0) - goto exit_remove_sysfs; - - init_done = true; - return 0; -exit_free_irq: - mutex_destroy(&ts->lock); -#if defined(CONFIG_FB) - if (fb_unregister_client(&ts->fb_notif)) - dev_err(&client->dev, - "Error occurred while unregistering fb_notifier\n"); -#elif defined(CONFIG_HAS_EARLYSUSPEND) - unregister_early_suspend(&ts->early_suspend); -#endif - if (ts->use_irq) - free_irq(client->irq, ts); - cancel_work_sync(&ts->work); - flush_workqueue(ts->goodix_wq); - destroy_workqueue(ts->goodix_wq); - - input_unregister_device(ts->input_dev); - if (ts->input_dev) { - input_free_device(ts->input_dev); - ts->input_dev = NULL; - } -exit_remove_sysfs: - sysfs_remove_group(&ts->input_dev->dev.kobj, >p_attr_grp); -exit_free_inputdev: - kfree(ts->config_data); -exit_power_off: - goodix_power_off(ts); -exit_deinit_power: - goodix_power_deinit(ts); -exit_free_io_port: - if (gpio_is_valid(pdata->reset_gpio)) - gpio_free(pdata->reset_gpio); - if (gpio_is_valid(pdata->irq_gpio)) - gpio_free(pdata->irq_gpio); -exit_free_client_data: - i2c_set_clientdata(client, NULL); - return ret; -} - -/******************************************************* -Function: - Goodix touchscreen driver release function. -Input: - client: i2c device struct. -Output: - Executive outcomes. 0---succeed. -*******************************************************/ -static int goodix_ts_remove(struct i2c_client *client) -{ - struct goodix_ts_data *ts = i2c_get_clientdata(client); - - sysfs_remove_group(&ts->input_dev->dev.kobj, >p_attr_grp); - -#if defined(CONFIG_FB) - if (fb_unregister_client(&ts->fb_notif)) - dev_err(&client->dev, - "Error occurred while unregistering fb_notifier\n"); -#elif defined(CONFIG_HAS_EARLYSUSPEND) - unregister_early_suspend(&ts->early_suspend); -#endif - mutex_destroy(&ts->lock); - -#ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG - uninit_wr_node(); -#endif - -#if GTP_ESD_PROTECT - cancel_work_sync(gtp_esd_check_workqueue); - flush_workqueue(gtp_esd_check_workqueue); - destroy_workqueue(gtp_esd_check_workqueue); -#endif - - if (ts) { - if (ts->use_irq) - free_irq(client->irq, ts); - - cancel_work_sync(&ts->work); - flush_workqueue(ts->goodix_wq); - destroy_workqueue(ts->goodix_wq); - - input_unregister_device(ts->input_dev); - if (ts->input_dev) { - input_free_device(ts->input_dev); - ts->input_dev = NULL; - } - - if (gpio_is_valid(ts->pdata->reset_gpio)) - gpio_free(ts->pdata->reset_gpio); - if (gpio_is_valid(ts->pdata->irq_gpio)) - gpio_free(ts->pdata->irq_gpio); - - goodix_power_off(ts); - goodix_power_deinit(ts); - i2c_set_clientdata(client, NULL); - } - debugfs_remove_recursive(ts->debug_base); - - return 0; -} - -#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_FB) -/******************************************************* -Function: - Early suspend function. -Input: - h: early_suspend struct. -Output: - None. -*******************************************************/ -static int goodix_ts_suspend(struct device *dev) -{ - struct goodix_ts_data *ts = dev_get_drvdata(dev); - int ret = 0, i; - - if (ts->gtp_is_suspend) { - dev_dbg(&ts->client->dev, "Already in suspend state\n"); - return 0; - } - - mutex_lock(&ts->lock); - - if (ts->fw_loading) { - dev_info(&ts->client->dev, - "Fw upgrade in progress, can't go to suspend."); - mutex_unlock(&ts->lock); - return 0; - } - -#if GTP_ESD_PROTECT - gtp_esd_switch(ts->client, SWITCH_OFF); -#endif - - if (ts->pdata->slide_wakeup) { - ret = gtp_enter_doze(ts); - } else { - if (ts->use_irq) - gtp_irq_disable(ts); - - for (i = 0; i < GTP_MAX_TOUCH; i++) - gtp_touch_up(ts, i); - - input_sync(ts->input_dev); - - ret = gtp_enter_sleep(ts); - if (ret < 0) - dev_err(&ts->client->dev, "GTP early suspend failed.\n"); - } - /* to avoid waking up while not sleeping, - * delay 48 + 10ms to ensure reliability - */ - msleep(58); - mutex_unlock(&ts->lock); - ts->gtp_is_suspend = 1; - - return ret; -} - -/******************************************************* -Function: - Late resume function. -Input: - h: early_suspend struct. -Output: - None. -*******************************************************/ -static int goodix_ts_resume(struct device *dev) -{ - struct goodix_ts_data *ts = dev_get_drvdata(dev); - int ret = 0; - - if (!ts->gtp_is_suspend) { - dev_dbg(&ts->client->dev, "Already in awake state\n"); - return 0; - } - - mutex_lock(&ts->lock); - ret = gtp_wakeup_sleep(ts); - - if (ts->pdata->slide_wakeup) - doze_status = DOZE_DISABLED; - - if (ret <= 0) - dev_err(&ts->client->dev, "GTP resume failed\n"); - - if (ts->use_irq) - gtp_irq_enable(ts); - -#if GTP_ESD_PROTECT - gtp_esd_switch(ts->client, SWITCH_ON); -#endif - mutex_unlock(&ts->lock); - ts->gtp_is_suspend = 0; - - return ret; -} - -#if defined(CONFIG_FB) -static int fb_notifier_callback(struct notifier_block *self, - unsigned long event, void *data) -{ - struct fb_event *evdata = data; - int *blank; - struct goodix_ts_data *ts = - container_of(self, struct goodix_ts_data, fb_notif); - - if (evdata && evdata->data && event == FB_EVENT_BLANK && - ts && ts->client) { - blank = evdata->data; - if (*blank == FB_BLANK_UNBLANK) - goodix_ts_resume(&ts->client->dev); - else if (*blank == FB_BLANK_POWERDOWN) - goodix_ts_suspend(&ts->client->dev); - } - - return 0; -} -#elif defined(CONFIG_HAS_EARLYSUSPEND) -/* - * Function: - * Early suspend function. - * Input: - * h: early_suspend struct. - * Output: - * None. - */ -static void goodix_ts_early_suspend(struct early_suspend *h) -{ - struct goodix_ts_data *ts; - - ts = container_of(h, struct goodix_ts_data, early_suspend); - goodix_ts_suspend(&ts->client->dev); - return; -} - -/* - * Function: - * Late resume function. - * Input: - * h: early_suspend struct. - * Output: - * None. - */ -static void goodix_ts_late_resume(struct early_suspend *h) -{ - struct goodix_ts_data *ts; - - ts = container_of(h, struct goodix_ts_data, early_suspend); - goodix_ts_late_resume(ts); -} -#endif -#endif /* !CONFIG_HAS_EARLYSUSPEND && !CONFIG_FB*/ - -#if GTP_ESD_PROTECT -/* - * Function: - * switch on & off esd delayed work - * Input: - * client: i2c device - * on: SWITCH_ON / SWITCH_OFF - * Output: - * void - */ -void gtp_esd_switch(struct i2c_client *client, int on) -{ - struct goodix_ts_data *ts; - - ts = i2c_get_clientdata(client); - if (on == SWITCH_ON) { - /* switch on esd */ - if (!ts->esd_running) { - ts->esd_running = 1; - dev_dbg(&client->dev, "Esd started\n"); - queue_delayed_work(gtp_esd_check_workqueue, - >p_esd_check_work, GTP_ESD_CHECK_CIRCLE); - } - } else { - /* switch off esd */ - if (ts->esd_running) { - ts->esd_running = 0; - dev_dbg(&client->dev, "Esd cancelled\n"); - cancel_delayed_work_sync(>p_esd_check_work); - } - } -} - -/******************************************************* -Function: - Initialize external watchdog for esd protect -Input: - client: i2c device. -Output: - result of i2c write operation. - 1: succeed, otherwise: failed -*********************************************************/ -static int gtp_init_ext_watchdog(struct i2c_client *client) -{ - /* in case of recursively reset by calling gtp_i2c_write*/ - struct i2c_msg msg; - u8 opr_buffer[4] = {0x80, 0x40, 0xAA, 0xAA}; - int ret; - int retries = 0; - - msg.flags = !I2C_M_RD; - msg.addr = client->addr; - msg.len = 4; - msg.buf = opr_buffer; - - while (retries < GTP_I2C_RETRY_5) { - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret == 1) - return 1; - retries++; - } - if (retries == GTP_I2C_RETRY_5) - dev_err(&client->dev, "init external watchdog failed!"); - return 0; -} - -/******************************************************* -Function: - Esd protect function. - Added external watchdog by meta, 2013/03/07 -Input: - work: delayed work -Output: - None. -*******************************************************/ -static void gtp_esd_check_func(struct work_struct *work) -{ - s32 retry; - s32 ret = -1; - struct goodix_ts_data *ts = NULL; - u8 test[4] = {0x80, 0x40}; - - ts = i2c_get_clientdata(i2c_connect_client); - - if (ts->gtp_is_suspend) { - dev_dbg(&ts->client->dev, "Esd terminated!\n"); - ts->esd_running = 0; - return; - } -#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE - if (ts->enter_update) - return; -#endif - - for (retry = 0; retry < GTP_I2C_RETRY_3; retry++) { - ret = gtp_i2c_read(ts->client, test, 4); - - if ((ret < 0)) { - /* IC works abnormally..*/ - continue; - } else { - if ((test[2] == 0xAA) || (test[3] != 0xAA)) { - /* IC works abnormally..*/ - retry = GTP_I2C_RETRY_3; - break; - } - /* IC works normally, Write 0x8040 0xAA*/ - test[2] = 0xAA; - gtp_i2c_write(ts->client, test, 3); - break; - } - } - if (retry == GTP_I2C_RETRY_3) { - dev_err(&ts->client->dev, - "IC Working ABNORMALLY, Resetting Guitar...\n"); - gtp_reset_guitar(ts, 50); - } - - if (!ts->gtp_is_suspend) - queue_delayed_work(gtp_esd_check_workqueue, - >p_esd_check_work, GTP_ESD_CHECK_CIRCLE); - else { - dev_dbg(&ts->client->dev, "Esd terminated!\n"); - ts->esd_running = 0; - } - - return; -} -#endif - -#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) -static const struct dev_pm_ops goodix_ts_dev_pm_ops = { - .suspend = goodix_ts_suspend, - .resume = goodix_ts_resume, -}; -#else -static const struct dev_pm_ops goodix_ts_dev_pm_ops = { -}; -#endif - -static const struct i2c_device_id goodix_ts_id[] = { - { GTP_I2C_NAME, 0 }, - { } -}; - -static const struct of_device_id goodix_match_table[] = { - { .compatible = "goodix,gt9xx", }, - { }, -}; - -static struct i2c_driver goodix_ts_driver = { - .probe = goodix_ts_probe, - .remove = goodix_ts_remove, -#ifdef CONFIG_HAS_EARLYSUSPEND - .suspend = goodix_ts_early_suspend, - .resume = goodix_ts_late_resume, -#endif - .id_table = goodix_ts_id, - .driver = { - .name = GTP_I2C_NAME, - .owner = THIS_MODULE, - .of_match_table = goodix_match_table, -#if CONFIG_PM - .pm = &goodix_ts_dev_pm_ops, -#endif - }, -}; - -/******************************************************* -Function: - Driver Install function. -Input: - None. -Output: - Executive Outcomes. 0---succeed. -********************************************************/ -static int __init goodix_ts_init(void) -{ - int ret; - -#if GTP_ESD_PROTECT - INIT_DELAYED_WORK(>p_esd_check_work, gtp_esd_check_func); - gtp_esd_check_workqueue = create_workqueue("gtp_esd_check"); -#endif - ret = i2c_add_driver(&goodix_ts_driver); - return ret; -} - -/******************************************************* -Function: - Driver uninstall function. -Input: - None. -Output: - Executive Outcomes. 0---succeed. -********************************************************/ -static void __exit goodix_ts_exit(void) -{ - i2c_del_driver(&goodix_ts_driver); -} - -module_init(goodix_ts_init); -module_exit(goodix_ts_exit); - -MODULE_DESCRIPTION("GTP Series Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h deleted file mode 100644 index 1e85e2fce276..000000000000 --- a/drivers/input/touchscreen/gt9xx/gt9xx.h +++ /dev/null @@ -1,220 +0,0 @@ -/* drivers/input/touchscreen/gt9xx.h - * - * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved. - * - * 2010 - 2013 Goodix Technology. - * - * 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 a reference - * to you, when you are integrating the GOODiX's CTP IC into your system, - * 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. - * - */ - -#ifndef _GOODIX_GT9XX_H_ -#define _GOODIX_GT9XX_H_ - -#include <linux/kernel.h> -#include <linux/i2c.h> -#include <linux/delay.h> -#include <linux/gpio.h> -#include <linux/uaccess.h> - -#if defined(CONFIG_FB) -#include <linux/notifier.h> -#include <linux/fb.h> -#elif defined(CONFIG_HAS_EARLYSUSPEND) -#include <linux/earlysuspend.h> -#define GOODIX_SUSPEND_LEVEL 1 -#endif - -#define MAX_BUTTONS 4 -#define GOODIX_MAX_CFG_GROUP 6 -#define GTP_FW_NAME_MAXSIZE 50 - -struct goodix_ts_platform_data { - int irq_gpio; - u32 irq_gpio_flags; - int reset_gpio; - u32 reset_gpio_flags; - const char *product_id; - const char *fw_name; - u32 x_max; - u32 y_max; - u32 x_min; - u32 y_min; - u32 panel_minx; - u32 panel_miny; - u32 panel_maxx; - u32 panel_maxy; - bool force_update; - bool i2c_pull_up; - bool enable_power_off; - size_t config_data_len[GOODIX_MAX_CFG_GROUP]; - u8 *config_data[GOODIX_MAX_CFG_GROUP]; - u32 button_map[MAX_BUTTONS]; - u8 num_button; - bool have_touch_key; - bool driver_send_cfg; - bool change_x2y; - bool with_pen; - bool slide_wakeup; - bool dbl_clk_wakeup; -}; -struct goodix_ts_data { - spinlock_t irq_lock; - struct i2c_client *client; - struct input_dev *input_dev; - struct goodix_ts_platform_data *pdata; - struct hrtimer timer; - struct workqueue_struct *goodix_wq; - struct work_struct work; - char fw_name[GTP_FW_NAME_MAXSIZE]; - struct delayed_work goodix_update_work; - s32 irq_is_disabled; - s32 use_irq; - u16 abs_x_max; - u16 abs_y_max; - u16 addr; - u8 max_touch_num; - u8 int_trigger_type; - u8 green_wake_mode; - u8 chip_type; - u8 *config_data; - u8 enter_update; - u8 gtp_is_suspend; - u8 gtp_rawdiff_mode; - u8 gtp_cfg_len; - u8 fixed_cfg; - u8 esd_running; - u8 fw_error; - bool power_on; - struct mutex lock; - bool fw_loading; - bool force_update; - struct regulator *avdd; - struct regulator *vdd; - struct regulator *vcc_i2c; -#if defined(CONFIG_FB) - struct notifier_block fb_notif; -#elif defined(CONFIG_HAS_EARLYSUSPEND) - struct early_suspend early_suspend; -#endif - struct dentry *debug_base; -}; - -extern u16 show_len; -extern u16 total_len; - -/***************************PART1:ON/OFF define*******************************/ -#define GTP_CUSTOM_CFG 0 -#define GTP_ESD_PROTECT 0 - -#define GTP_IRQ_TAB {\ - IRQ_TYPE_EDGE_RISING,\ - IRQ_TYPE_EDGE_FALLING,\ - IRQ_TYPE_LEVEL_LOW,\ - IRQ_TYPE_LEVEL_HIGH\ - } - - -#define GTP_IRQ_TAB_RISING 0 -#define GTP_IRQ_TAB_FALLING 1 -#if GTP_CUSTOM_CFG -#define GTP_MAX_HEIGHT 800 -#define GTP_MAX_WIDTH 480 -#define GTP_INT_TRIGGER GTP_IRQ_TAB_RISING -#else -#define GTP_MAX_HEIGHT 4096 -#define GTP_MAX_WIDTH 4096 -#define GTP_INT_TRIGGER GTP_IRQ_TAB_FALLING -#endif - -#define GTP_PRODUCT_ID_MAXSIZE 5 -#define GTP_PRODUCT_ID_BUFFER_MAXSIZE 6 -#define GTP_FW_VERSION_BUFFER_MAXSIZE 4 -#define GTP_MAX_TOUCH 5 -#define GTP_ESD_CHECK_CIRCLE 2000 /* jiffy: ms */ - -/***************************PART3:OTHER define*********************************/ -#define GTP_DRIVER_VERSION "V1.8.1<2013/09/01>" -#define GTP_I2C_NAME "Goodix-TS" -#define GTP_POLL_TIME 10 /* jiffy: ms*/ -#define GTP_ADDR_LENGTH 2 -#define GTP_CONFIG_MIN_LENGTH 186 -#define GTP_CONFIG_MAX_LENGTH 240 -#define FAIL 0 -#define SUCCESS 1 -#define SWITCH_OFF 0 -#define SWITCH_ON 1 - -/* Registers define */ -#define GTP_READ_COOR_ADDR 0x814E -#define GTP_REG_SLEEP 0x8040 -#define GTP_REG_SENSOR_ID 0x814A -#define GTP_REG_CONFIG_DATA 0x8047 -#define GTP_REG_FW_VERSION 0x8144 -#define GTP_REG_PRODUCT_ID 0x8140 - -#define GTP_I2C_RETRY_3 3 -#define GTP_I2C_RETRY_5 5 -#define GTP_I2C_RETRY_10 10 - -#define RESOLUTION_LOC 3 -#define TRIGGER_LOC 8 - -/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */ -#define GTP_I2C_ADDRESS_HIGH 0x14 -#define GTP_I2C_ADDRESS_LOW 0x5D -#define GTP_VALID_ADDR_START 0x8040 -#define GTP_VALID_ADDR_END 0x8177 - -/* GTP CM_HEAD RW flags */ -#define GTP_RW_READ 0 -#define GTP_RW_WRITE 1 -#define GTP_RW_READ_IC_TYPE 2 -#define GTP_RW_WRITE_IC_TYPE 3 -#define GTP_RW_FILL_INFO 4 -#define GTP_RW_NO_WRITE 5 -#define GTP_RW_READ_ERROR 6 -#define GTP_RW_DISABLE_IRQ 7 -#define GTP_RW_READ_VERSION 8 -#define GTP_RW_ENABLE_IRQ 9 -#define GTP_RW_ENTER_UPDATE_MODE 11 -#define GTP_RW_LEAVE_UPDATE_MODE 13 -#define GTP_RW_UPDATE_FW 15 -#define GTP_RW_CHECK_RAWDIFF_MODE 17 - -/* GTP need flag or interrupt */ -#define GTP_NO_NEED 0 -#define GTP_NEED_FLAG 1 -#define GTP_NEED_INTERRUPT 2 - -/*****************************End of Part III********************************/ - -void gtp_esd_switch(struct i2c_client *client, int on); - -int gtp_i2c_read_dbl_check(struct i2c_client *client, u16 addr, - u8 *rxbuf, int len); -int gtp_send_cfg(struct goodix_ts_data *ts); -void gtp_reset_guitar(struct goodix_ts_data *ts, int ms); -void gtp_irq_disable(struct goodix_ts_data *ts); -void gtp_irq_enable(struct goodix_ts_data *ts); - -#ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG -s32 init_wr_node(struct i2c_client *client); -void uninit_wr_node(void); -#endif - -u8 gup_init_update_proc(struct goodix_ts_data *ts); -s32 gup_enter_update_mode(struct i2c_client *client); -void gup_leave_update_mode(struct i2c_client *client); -s32 gup_update_proc(void *dir); -extern struct i2c_client *i2c_connect_client; -#endif /* _GOODIX_GT9XX_H_ */ diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c deleted file mode 100644 index 6bc243492272..000000000000 --- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c +++ /dev/null @@ -1,1530 +0,0 @@ -/* drivers/input/touchscreen/gt9xx_update.c - * - * 2010 - 2012 Goodix Technology. - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be a reference - * to you, when you are integrating the GOODiX's CTP IC into your system, - * 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. - * - * Latest Version:1.6 - * Author: andrew@goodix.com - * Revision Record: - * V1.0: - * first release. By Andrew, 2012/08/31 - * V1.2: - * add force update,GT9110P pid map. By Andrew, 2012/10/15 - * V1.4: - * 1. add config auto update function; - * 2. modify enter_update_mode; - * 3. add update file cal checksum. - * By Andrew, 2012/12/12 - * V1.6: - * 1. replace guitar_client with i2c_connect_client; - * 2. support firmware header array update. - * By Meta, 2013/03/11 - */ -#include "gt9xx.h" -#include <linux/firmware.h> -#include <linux/workqueue.h> -#include <linux/kernel.h> - -#define FIRMWARE_NAME_LEN_MAX 256 - -#define GUP_REG_HW_INFO 0x4220 -#define GUP_REG_FW_MSG 0x41E4 -#define GUP_REG_PID_VID 0x8140 - -#define GOODIX_FIRMWARE_FILE_NAME "_goodix_update_.bin" -#define GOODIX_CONFIG_FILE_NAME "_goodix_config_.cfg" - -#define FW_HEAD_LENGTH 14 -#define FW_SECTION_LENGTH 0x2000 -#define FW_DSP_ISP_LENGTH 0x1000 -#define FW_DSP_LENGTH 0x1000 -#define FW_BOOT_LENGTH 0x800 - -#define PACK_SIZE 256 -#define MAX_FRAME_CHECK_TIME 5 - -#define _bRW_MISCTL__SRAM_BANK 0x4048 -#define _bRW_MISCTL__MEM_CD_EN 0x4049 -#define _bRW_MISCTL__CACHE_EN 0x404B -#define _bRW_MISCTL__TMR0_EN 0x40B0 -#define _rRW_MISCTL__SWRST_B0_ 0x4180 -#define _bWO_MISCTL__CPU_SWRST_PULSE 0x4184 -#define _rRW_MISCTL__BOOTCTL_B0_ 0x4190 -#define _rRW_MISCTL__BOOT_OPT_B0_ 0x4218 -#define _rRW_MISCTL__BOOT_CTL_ 0x5094 - -#define FAIL 0 -#define SUCCESS 1 - -#define RESET_DELAY_US 20000 - -struct st_fw_head { - u8 hw_info[4]; /* hardware info */ - u8 pid[8]; /* product id */ - u16 vid; /* version id */ -} __packed; - -struct st_update_msg { - u8 force_update; - u8 fw_flag; - bool need_free; - u8 *fw_data; - u32 fw_len; - struct st_fw_head ic_fw_msg; -}; - -static struct st_update_msg update_msg; -u16 show_len; -u16 total_len; -u8 got_file_flag; -u8 searching_file; -/******************************************************* -Function: - Read data from the i2c slave device. -Input: - client: i2c device. - buf[0~1]: read start address. - buf[2~len-1]: read data buffer. - len: GTP_ADDR_LENGTH + read bytes count -Output: - numbers of i2c_msgs to transfer: - 2: succeed, otherwise: failed -*********************************************************/ -static s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len) -{ - s32 ret = -1; - u8 retries = 0; - struct i2c_msg msgs[2] = { - { - .flags = !I2C_M_RD, - .addr = client->addr, - .len = GTP_ADDR_LENGTH, - .buf = &buf[0], - }, - { - .flags = I2C_M_RD, - .addr = client->addr, - .len = len - GTP_ADDR_LENGTH, - .buf = &buf[GTP_ADDR_LENGTH], - }, - }; - - while (retries < 5) { - ret = i2c_transfer(client->adapter, msgs, 2); - if (ret == 2) - break; - retries++; - } - - if (retries == 5) { - dev_err(&client->dev, "I2C read retry limit over.\n"); - ret = -EIO; - } - - return ret; -} - -/******************************************************* -Function: - Write data to the i2c slave device. -Input: - client: i2c device. - buf[0~1]: write start address. - buf[2~len-1]: data buffer - len: GTP_ADDR_LENGTH + write bytes count -Output: - numbers of i2c_msgs to transfer: - 1: succeed, otherwise: failed -*********************************************************/ -s32 gup_i2c_write(struct i2c_client *client, u8 *buf, s32 len) -{ - s32 ret = -1; - u8 retries = 0; - struct i2c_msg msg = { - .flags = !I2C_M_RD, - .addr = client->addr, - .len = len, - .buf = buf, - }; - - while (retries < 5) { - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret == 1) - break; - retries++; - } - - if (retries == 5) { - dev_err(&client->dev, "I2C write retry limit over\n"); - ret = -EIO; - } - - return ret; -} - -static s32 gup_init_panel(struct goodix_ts_data *ts) -{ - struct i2c_client *client = ts->client; - u8 *config_data; - s32 ret = 0; - s32 i = 0; - u8 check_sum = 0; - u8 opr_buf[16]; - u8 sensor_id = 0; - - for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) - if (ts->pdata->config_data_len[i]) - break; - - if (i == GOODIX_MAX_CFG_GROUP) { - sensor_id = 0; - } else { - ret = gtp_i2c_read_dbl_check(client, GTP_REG_SENSOR_ID, - &sensor_id, 1); - if (ret == SUCCESS) { - if (sensor_id >= GOODIX_MAX_CFG_GROUP) { - pr_err("Invalid sensor_id(0x%02X), No Config Sent", - sensor_id); - return -EINVAL; - } - } else { - pr_err("Failed to get sensor_id, No config sent\n"); - return -EINVAL; - } - } - - pr_debug("Sensor ID selected: %d", sensor_id); - - if (ts->pdata->config_data_len[sensor_id] < GTP_CONFIG_MIN_LENGTH || - !ts->pdata->config_data_len[sensor_id]) { - pr_err("Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP", - sensor_id); - return -EINVAL; - } - - ret = gtp_i2c_read_dbl_check(client, GTP_REG_CONFIG_DATA, - &opr_buf[0], 1); - if (ret == SUCCESS) { - pr_debug("CFG_GROUP%d Config Version: %d, IC Config Version: %d", - sensor_id + 1, - ts->pdata->config_data[sensor_id][0], - opr_buf[0]); - - ts->pdata->config_data[sensor_id][0] = opr_buf[0]; - ts->fixed_cfg = 0; - } else { - pr_err("Failed to get ic config version. No config sent"); - return -EINVAL; - } - - config_data = ts->pdata->config_data[sensor_id]; - ts->config_data = ts->pdata->config_data[sensor_id]; - ts->gtp_cfg_len = ts->pdata->config_data_len[sensor_id]; - - pr_debug("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x\n", - ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type); - - config_data[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH; - config_data[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8); - config_data[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT; - config_data[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8); - - if (GTP_INT_TRIGGER == 0) /* RISING */ - config_data[TRIGGER_LOC] &= 0xfe; - else if (GTP_INT_TRIGGER == 1) /* FALLING */ - config_data[TRIGGER_LOC] |= 0x01; - - check_sum = 0; - for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++) - check_sum += config_data[i]; - - config_data[ts->gtp_cfg_len] = (~check_sum) + 1; - - ret = gtp_send_cfg(ts); - if (ret < 0) - pr_err("Send config error\n"); - - ts->config_data = NULL; - ts->gtp_cfg_len = 0; - msleep(20); - return 0; -} - -static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8 *msg, s32 len) -{ - u8 i = 0; - - msg[0] = (addr >> 8) & 0xff; - msg[1] = addr & 0xff; - - for (i = 0; i < 5; i++) - if (gup_i2c_read(client, msg, GTP_ADDR_LENGTH + len) > 0) - break; - - if (i >= 5) { - pr_err("Read data from 0x%02x%02x failed\n", msg[0], msg[1]); - return FAIL; - } - - return SUCCESS; -} - -static u8 gup_set_ic_msg(struct i2c_client *client, u16 addr, u8 val) -{ - u8 i = 0; - u8 msg[3] = { - (addr >> 8) & 0xff, - addr & 0xff, - val, - }; - - for (i = 0; i < 5; i++) - if (gup_i2c_write(client, msg, GTP_ADDR_LENGTH + 1) > 0) - break; - - if (i >= 5) { - pr_err("Set data to 0x%02x%02x failed\n", msg[0], msg[1]); - return FAIL; - } - - return SUCCESS; -} - -static u8 gup_get_ic_fw_msg(struct i2c_client *client) -{ - s32 ret = -1; - u8 retry = 0; - u8 buf[16]; - u8 i; - - /* step1:get hardware info */ - ret = gtp_i2c_read_dbl_check(client, GUP_REG_HW_INFO, - &buf[GTP_ADDR_LENGTH], 4); - if (ret == FAIL) { - pr_err("get hw_info failed,exit"); - return FAIL; - } - - /* buf[2~5]: 00 06 90 00 */ - /* hw_info: 00 90 06 00 */ - for (i = 0; i < 4; i++) - update_msg.ic_fw_msg.hw_info[i] = buf[GTP_ADDR_LENGTH + 3 - i]; - - pr_debug("IC Hardware info:%02x%02x%02x%02x\n", - update_msg.ic_fw_msg.hw_info[0], - update_msg.ic_fw_msg.hw_info[1], - update_msg.ic_fw_msg.hw_info[2], - update_msg.ic_fw_msg.hw_info[3]); - - /* step2:get firmware message */ - for (retry = 0; retry < 2; retry++) { - ret = gup_get_ic_msg(client, GUP_REG_FW_MSG, buf, 1); - if (ret == FAIL) { - pr_err("Read firmware message fail\n"); - return ret; - } - - update_msg.force_update = buf[GTP_ADDR_LENGTH]; - if ((update_msg.force_update != 0xBE) && (!retry)) { - pr_info("The check sum in ic is error\n"); - pr_info("The IC will be updated by force\n"); - continue; - } - break; - } - pr_debug("IC force update flag:0x%x\n", update_msg.force_update); - - /* step3:get pid & vid */ - ret = gtp_i2c_read_dbl_check(client, GUP_REG_PID_VID, - &buf[GTP_ADDR_LENGTH], 6); - if (ret == FAIL) { - pr_err("get pid & vid failed,exit"); - return FAIL; - } - - memset(update_msg.ic_fw_msg.pid, 0, sizeof(update_msg.ic_fw_msg.pid)); - memcpy(update_msg.ic_fw_msg.pid, &buf[GTP_ADDR_LENGTH], 4); - pr_debug("IC Product id:%s\n", update_msg.ic_fw_msg.pid); - - /* GT9XX PID MAPPING - * |-----FLASH-----RAM-----| - * |------918------918-----| - * |------968------968-----| - * |------913------913-----| - * |------913P-----913P----| - * |------927------927-----| - * |------927P-----927P----| - * |------9110-----9110----| - * |------9110P----9111----| - */ - if (update_msg.ic_fw_msg.pid[0] != 0) { - if (!memcmp(update_msg.ic_fw_msg.pid, "9111", 4)) { - pr_debug("IC Mapping Product id:%s\n", - update_msg.ic_fw_msg.pid); - memcpy(update_msg.ic_fw_msg.pid, "9110P", 5); - } - } - - update_msg.ic_fw_msg.vid = buf[GTP_ADDR_LENGTH + 4] + - (buf[GTP_ADDR_LENGTH + 5] << 8); - pr_debug("IC version id:%04x\n", update_msg.ic_fw_msg.vid); - - return SUCCESS; -} - -s32 gup_enter_update_mode(struct i2c_client *client) -{ - s32 ret = -1; - u8 retry = 0; - u8 rd_buf[3]; - struct goodix_ts_data *ts = i2c_get_clientdata(client); - - /* step1:RST output low last at least 2ms */ - gpio_direction_output(ts->pdata->reset_gpio, 0); - usleep_range(RESET_DELAY_US, RESET_DELAY_US + 1); - - /* step2:select I2C slave addr,INT:0--0xBA;1--0x28. */ - gpio_direction_output(ts->pdata->irq_gpio, - (client->addr == GTP_I2C_ADDRESS_HIGH)); - msleep(20); - - /* step3:RST output high reset guitar */ - gpio_direction_output(ts->pdata->reset_gpio, 1); - - /* 20121211 modify start */ - msleep(20); - while (retry++ < 200) { - /* step4:Hold ss51 & dsp */ - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); - if (ret <= 0) { - pr_debug("Hold ss51 & dsp I2C error,retry:%d\n", retry); - continue; - } - - /* step5:Confirm hold */ - ret = gup_get_ic_msg(client, _rRW_MISCTL__SWRST_B0_, rd_buf, 1); - if (ret <= 0) { - pr_debug("Hold ss51 & dsp I2C error,retry:%d\n", retry); - continue; - } - if (rd_buf[GTP_ADDR_LENGTH] == 0x0C) { - pr_debug("Hold ss51 & dsp confirm SUCCESS\n"); - break; - } - pr_debug("Hold ss51 & dsp confirm 0x4180 failed,value:%d\n", - rd_buf[GTP_ADDR_LENGTH]); - } - if (retry >= 200) { - pr_err("Enter update Hold ss51 failed\n"); - return FAIL; - } - - /* step6:DSP_CK and DSP_ALU_CK PowerOn */ - ret = gup_set_ic_msg(client, 0x4010, 0x00); - - /* 20121211 modify end */ - return ret; -} - -void gup_leave_update_mode(struct i2c_client *client) -{ - struct goodix_ts_data *ts = i2c_get_clientdata(client); - - gpio_direction_input(ts->pdata->irq_gpio); - pr_debug("reset chip"); - gtp_reset_guitar(ts, 20); -} - -/* Get the correct nvram data - * The correct conditions: - * 1. the hardware info is the same - * 2. the product id is the same - * 3. the firmware version in update file is greater than the firmware - * version in ic or the check sum in ic is wrong - - * Update Conditions: - * 1. Same hardware info - * 2. Same PID - * 3. File PID > IC PID - - * Force Update Conditions: - * 1. Wrong ic firmware checksum - * 2. INVALID IC PID or VID - * 3. IC PID == 91XX || File PID == 91XX - */ - -static u8 gup_enter_update_judge(struct i2c_client *client, - struct st_fw_head *fw_head) -{ - u16 u16_tmp; - s32 i = 0; - - u16_tmp = fw_head->vid; - fw_head->vid = (u16)(u16_tmp>>8) + (u16)(u16_tmp<<8); - - pr_debug("FILE HARDWARE INFO:%02x%02x%02x%02x\n", fw_head->hw_info[0], - fw_head->hw_info[1], fw_head->hw_info[2], fw_head->hw_info[3]); - pr_debug("FILE PID:%s\n", fw_head->pid); - pr_debug("FILE VID:%04x\n", fw_head->vid); - - pr_debug("IC HARDWARE INFO:%02x%02x%02x%02x\n", - update_msg.ic_fw_msg.hw_info[0], - update_msg.ic_fw_msg.hw_info[1], - update_msg.ic_fw_msg.hw_info[2], - update_msg.ic_fw_msg.hw_info[3]); - pr_debug("IC PID:%s\n", update_msg.ic_fw_msg.pid); - pr_debug("IC VID:%04x\n", update_msg.ic_fw_msg.vid); - - /* First two conditions */ - if (!memcmp(fw_head->hw_info, update_msg.ic_fw_msg.hw_info, - sizeof(update_msg.ic_fw_msg.hw_info))) { - pr_debug("Get the same hardware info\n"); - if (update_msg.force_update != 0xBE) { - pr_info("FW chksum error,need enter update\n"); - return SUCCESS; - } - - /* 20130523 start */ - if (strlen(update_msg.ic_fw_msg.pid) < 3) { - pr_info("Illegal IC pid, need enter update\n"); - return SUCCESS; - } - for (i = 0; i < 3; i++) { - if ((update_msg.ic_fw_msg.pid[i] < 0x30) || - (update_msg.ic_fw_msg.pid[i] > 0x39)) { - pr_info("Illegal IC pid, out of bound, need enter update\n"); - return SUCCESS; - } - } - /* 20130523 end */ - - if ((!memcmp(fw_head->pid, update_msg.ic_fw_msg.pid, - (strlen(fw_head->pid) < 3 ? 3 : strlen(fw_head->pid)))) || - (!memcmp(update_msg.ic_fw_msg.pid, "91XX", 4)) || - (!memcmp(fw_head->pid, "91XX", 4))) { - if (!memcmp(fw_head->pid, "91XX", 4)) - pr_debug("Force none same pid update mode\n"); - else - pr_debug("Get the same pid\n"); - - /* The third condition */ - if (fw_head->vid > update_msg.ic_fw_msg.vid) { - pr_info("Need enter update"); - return SUCCESS; - } - pr_err("Don't meet the third condition\n"); - pr_err("File VID <= Ic VID, update aborted\n"); - } else { - pr_err("File PID != Ic PID, update aborted\n"); - } - } else { - pr_err("Different Hardware, update aborted\n"); - } - - return FAIL; -} - -static s8 gup_update_config(struct i2c_client *client, - const struct firmware *cfg) -{ - s32 ret = 0; - s32 i = 0; - s32 file_cfg_len = 0; - u32 chip_cfg_len = 0; - s32 count = 0; - u8 *buf; - u8 *file_config; - u8 pid[8]; - u8 high, low; - - if (!cfg || !cfg->data) { - pr_err("No need to upgrade config"); - return FAIL; - } - - ret = gup_get_ic_msg(client, GUP_REG_PID_VID, pid, 6); - if (ret == FAIL) { - pr_err("Read product id & version id fail"); - return FAIL; - } - pid[5] = '\0'; - pr_debug("update cfg get pid:%s\n", &pid[GTP_ADDR_LENGTH]); - - chip_cfg_len = 186; - if (!memcmp(&pid[GTP_ADDR_LENGTH], "968", 3) || - !memcmp(&pid[GTP_ADDR_LENGTH], "910", 3) || - !memcmp(&pid[GTP_ADDR_LENGTH], "960", 3)) { - chip_cfg_len = 228; - } - pr_debug("config file ASCII len: %zu", cfg->size); - pr_debug("need config binary len: %u", chip_cfg_len); - if ((cfg->size + 5) < chip_cfg_len * 5) { - pr_err("Config length error"); - return -EINVAL; - } - - buf = devm_kzalloc(&client->dev, cfg->size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - file_config = devm_kzalloc(&client->dev, chip_cfg_len + GTP_ADDR_LENGTH, - GFP_KERNEL); - if (!file_config) - return -ENOMEM; - - pr_debug("Delete illegal character"); - for (i = 0, count = 0; i < cfg->size; i++) { - if (cfg->data[i] == ' ' || cfg->data[i] == '\r' - || cfg->data[i] == '\n') - continue; - buf[count++] = cfg->data[i]; - } - - pr_debug("Ascii to hex"); - file_config[0] = GTP_REG_CONFIG_DATA >> 8; - file_config[1] = GTP_REG_CONFIG_DATA & 0xff; - for (i = 0, file_cfg_len = GTP_ADDR_LENGTH; i < count; i = i + 5) { - if ((buf[i] == '0') && ((buf[i + 1] == 'x') || - (buf[i + 1] == 'X'))) { - ret = hex2bin(&high, &buf[i + 2], 1); - if (ret) { - pr_err("Failed to convert high address from hex2bin"); - return ret; - } - ret = hex2bin(&low, &buf[i + 3], 1); - if (ret) { - pr_err("Failed to convert low address from hex2bin"); - return ret; - } - - if ((high == 0xFF) || (low == 0xFF)) { - ret = 0; - pr_err("Illegal config file"); - return ret; - } - file_config[file_cfg_len++] = (high<<4) + low; - } else { - ret = 0; - pr_err("Illegal config file"); - return ret; - } - } - - i = 0; - while (i++ < 5) { - ret = gup_i2c_write(client, file_config, file_cfg_len); - if (ret > 0) - break; - pr_err("Send config i2c error"); - } - - return ret; -} - -static s32 gup_get_firmware_file(struct i2c_client *client, - struct st_update_msg *msg, u8 *path) -{ - s32 ret; - const struct firmware *fw = NULL; - - ret = request_firmware(&fw, path, &client->dev); - if (ret < 0) { - dev_info(&client->dev, "Cannot get firmware - %s (%d)\n", - path, ret); - return -EEXIST; - } - - dev_dbg(&client->dev, "Config File: %s size: %zu", path, fw->size); - msg->fw_data = - devm_kzalloc(&client->dev, fw->size, GFP_KERNEL); - if (!msg->fw_data) { - release_firmware(fw); - return -ENOMEM; - } - - memcpy(msg->fw_data, fw->data, fw->size); - msg->fw_len = fw->size; - msg->need_free = true; - release_firmware(fw); - return 0; -} - -static u8 gup_check_firmware_name(struct i2c_client *client, - u8 **path_p) -{ - u8 len; - u8 *fname; - - if (!(*path_p)) { - *path_p = GOODIX_FIRMWARE_FILE_NAME; - return 0; - } - - len = strnlen(*path_p, FIRMWARE_NAME_LEN_MAX); - if (len >= FIRMWARE_NAME_LEN_MAX) { - dev_err(&client->dev, "firmware name too long"); - return -EINVAL; - } - - fname = strrchr(*path_p, '/'); - if (fname) { - fname = fname + 1; - *path_p = fname; - } - return 0; -} - -static u8 gup_check_update_file(struct i2c_client *client, - struct st_fw_head *fw_head, u8 *path) -{ - s32 ret = 0; - s32 i = 0; - s32 fw_checksum = 0; - u16 temp; - const struct firmware *fw = NULL; - - ret = request_firmware(&fw, GOODIX_CONFIG_FILE_NAME, &client->dev); - if (ret < 0) { - dev_info(&client->dev, "Cannot get config file - %s (%d)\n", - GOODIX_CONFIG_FILE_NAME, ret); - } else { - dev_dbg(&client->dev, - "Update config File: %s", GOODIX_CONFIG_FILE_NAME); - ret = gup_update_config(client, fw); - if (ret <= 0) - dev_err(&client->dev, "Update config failed"); - release_firmware(fw); - } - - update_msg.need_free = false; - update_msg.fw_len = 0; - - if (gup_check_firmware_name(client, &path)) - goto load_failed; - - if (gup_get_firmware_file(client, &update_msg, path)) - goto load_failed; - - memcpy(fw_head, update_msg.fw_data, FW_HEAD_LENGTH); - - /* check firmware legality */ - fw_checksum = 0; - for (i = 0; i < FW_SECTION_LENGTH * 4 + FW_DSP_ISP_LENGTH + - FW_DSP_LENGTH + FW_BOOT_LENGTH; i += 2) { - temp = (update_msg.fw_data[FW_HEAD_LENGTH + i] << 8) + - update_msg.fw_data[FW_HEAD_LENGTH + i + 1]; - fw_checksum += temp; - } - - pr_debug("firmware checksum:%x", fw_checksum & 0xFFFF); - if (fw_checksum & 0xFFFF) { - dev_err(&client->dev, "Illegal firmware file"); - goto load_failed; - } - - return SUCCESS; - -load_failed: - if (update_msg.need_free) { - devm_kfree(&client->dev, update_msg.fw_data); - update_msg.need_free = false; - } - return FAIL; -} - -static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr, - u16 total_length) -{ - s32 ret = 0; - u16 burn_addr = start_addr; - u16 frame_length = 0; - u16 burn_length = 0; - u8 wr_buf[PACK_SIZE + GTP_ADDR_LENGTH]; - u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH]; - u8 retry = 0; - - pr_debug("Begin burn %dk data to addr 0x%x", (total_length / 1024), - start_addr); - while (burn_length < total_length) { - pr_debug("B/T:%04d/%04d", burn_length, total_length); - frame_length = ((total_length - burn_length) > PACK_SIZE) - ? PACK_SIZE : (total_length - burn_length); - wr_buf[0] = (u8)(burn_addr>>8); - rd_buf[0] = wr_buf[0]; - wr_buf[1] = (u8)burn_addr; - rd_buf[1] = wr_buf[1]; - memcpy(&wr_buf[GTP_ADDR_LENGTH], &burn_buf[burn_length], - frame_length); - - for (retry = 0; retry < MAX_FRAME_CHECK_TIME; retry++) { - ret = gup_i2c_write(client, wr_buf, - GTP_ADDR_LENGTH + frame_length); - if (ret <= 0) { - pr_err("Write frame data i2c error\n"); - continue; - } - ret = gup_i2c_read(client, rd_buf, GTP_ADDR_LENGTH + - frame_length); - if (ret <= 0) { - pr_err("Read back frame data i2c error\n"); - continue; - } - - if (memcmp(&wr_buf[GTP_ADDR_LENGTH], - &rd_buf[GTP_ADDR_LENGTH], frame_length)) { - pr_err("Check frame data fail,not equal\n"); - continue; - } else { - break; - } - } - if (retry >= MAX_FRAME_CHECK_TIME) { - pr_err("Burn frame data time out,exit\n"); - return FAIL; - } - burn_length += frame_length; - burn_addr += frame_length; - } - return SUCCESS; -} - -static u8 gup_load_section_file(u8 *buf, u16 offset, u16 length) -{ - if (!update_msg.fw_data || - update_msg.fw_len < FW_HEAD_LENGTH + offset + length) { - pr_err( - "<<-GTP->> cannot load section data. fw_len=%d read end=%d\n", - update_msg.fw_len, - FW_HEAD_LENGTH + offset + length); - return FAIL; - } - memcpy(buf, &update_msg.fw_data[FW_HEAD_LENGTH + offset], length); - - return SUCCESS; -} - -static u8 gup_recall_check(struct i2c_client *client, u8 *chk_src, - u16 start_rd_addr, u16 chk_length) -{ - u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH]; - s32 ret = 0; - u16 recall_addr = start_rd_addr; - u16 recall_length = 0; - u16 frame_length = 0; - - while (recall_length < chk_length) { - frame_length = ((chk_length - recall_length) > PACK_SIZE) - ? PACK_SIZE : (chk_length - recall_length); - ret = gup_get_ic_msg(client, recall_addr, rd_buf, frame_length); - if (ret <= 0) { - pr_err("recall i2c error,exit\n"); - return FAIL; - } - - if (memcmp(&rd_buf[GTP_ADDR_LENGTH], &chk_src[recall_length], - frame_length)) { - pr_err("Recall frame data fail,not equal\n"); - return FAIL; - } - - recall_length += frame_length; - recall_addr += frame_length; - } - pr_debug("Recall check %dk firmware success\n", (chk_length/1024)); - - return SUCCESS; -} - -static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section, - u16 start_addr, u8 bank_cmd) -{ - s32 ret = 0; - u8 rd_buf[5]; - - /* step1:hold ss51 & dsp */ - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); - if (ret <= 0) { - pr_err("hold ss51 & dsp fail"); - return FAIL; - } - - /* step2:set scramble */ - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); - if (ret <= 0) { - pr_err("set scramble fail"); - return FAIL; - } - - /* step3:select bank */ - ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, - (bank_cmd >> 4)&0x0F); - if (ret <= 0) { - pr_err("select bank %d fail", - (bank_cmd >> 4)&0x0F); - return FAIL; - } - - /* step4:enable accessing code */ - ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); - if (ret <= 0) { - pr_err("enable accessing code fail"); - return FAIL; - } - - /* step5:burn 8k fw section */ - ret = gup_burn_proc(client, fw_section, start_addr, FW_SECTION_LENGTH); - if (ret == FAIL) { - pr_err("burn fw_section fail"); - return FAIL; - } - - /* step6:hold ss51 & release dsp */ - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); - if (ret <= 0) { - pr_err("hold ss51 & release dsp fail"); - return FAIL; - } - /* must delay */ - msleep(20); - - /* step7:send burn cmd to move data to flash from sram */ - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, bank_cmd&0x0f); - if (ret <= 0) { - pr_err("send burn cmd fail"); - return FAIL; - } - pr_debug("Wait for the burn is complete"); - do { - ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); - if (ret <= 0) { - pr_err("Get burn state fail"); - return FAIL; - } - msleep(20); - } while (rd_buf[GTP_ADDR_LENGTH]); - - /* step8:select bank */ - ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, - (bank_cmd >> 4)&0x0F); - if (ret <= 0) { - pr_err("select bank %d fail", - (bank_cmd >> 4)&0x0F); - return FAIL; - } - - /* step9:enable accessing code */ - ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); - if (ret <= 0) { - pr_err("enable accessing code fail"); - return FAIL; - } - - /* step10:recall 8k fw section */ - ret = gup_recall_check(client, fw_section, start_addr, - FW_SECTION_LENGTH); - if (ret == FAIL) { - pr_err("recall check 8k firmware fail"); - return FAIL; - } - - /* step11:disable accessing code */ - ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x00); - if (ret <= 0) { - pr_err("disable accessing code fail"); - return FAIL; - } - - return SUCCESS; -} - -static u8 gup_burn_dsp_isp(struct i2c_client *client) -{ - s32 ret = 0; - u8 *fw_dsp_isp = NULL; - u8 retry = 0; - - pr_debug("Begin burn dsp isp"); - - /* step1:alloc memory */ - pr_debug("step1:alloc memory"); - while (retry++ < 5) { - fw_dsp_isp = devm_kzalloc(&client->dev, FW_DSP_ISP_LENGTH, - GFP_KERNEL); - if (fw_dsp_isp == NULL) { - continue; - } else { - break; - } - } - if (retry == 5) - return FAIL; - - /* step2:load dsp isp file data */ - pr_debug("step2:load dsp isp file data"); - ret = gup_load_section_file(fw_dsp_isp, (4 * FW_SECTION_LENGTH + - FW_DSP_LENGTH + FW_BOOT_LENGTH), FW_DSP_ISP_LENGTH); - if (ret == FAIL) { - pr_err("load firmware dsp_isp fail"); - return FAIL; - } - - /* step3:disable wdt,clear cache enable */ - pr_debug("step3:disable wdt,clear cache enable"); - ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00); - if (ret <= 0) { - pr_err("disable wdt fail"); - return FAIL; - } - ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00); - if (ret <= 0) { - pr_err("clear cache enable fail"); - return FAIL; - } - - /* step4:hold ss51 & dsp */ - pr_debug("step4:hold ss51 & dsp"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); - if (ret <= 0) { - pr_err("hold ss51 & dsp fail"); - return FAIL; - } - - /* step5:set boot from sram */ - pr_debug("step5:set boot from sram"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02); - if (ret <= 0) { - pr_err("set boot from sram fail"); - return FAIL; - } - - /* step6:software reboot */ - pr_debug("step6:software reboot"); - ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01); - if (ret <= 0) { - pr_err("software reboot fail"); - return FAIL; - } - - /* step7:select bank2 */ - pr_debug("step7:select bank2"); - ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02); - if (ret <= 0) { - pr_err("select bank2 fail"); - return FAIL; - } - - /* step8:enable accessing code */ - pr_debug("step8:enable accessing code"); - ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); - if (ret <= 0) { - pr_err("enable accessing code fail"); - return FAIL; - } - - /* step9:burn 4k dsp_isp */ - pr_debug("step9:burn 4k dsp_isp"); - ret = gup_burn_proc(client, fw_dsp_isp, 0xC000, FW_DSP_ISP_LENGTH); - if (ret == FAIL) { - pr_err("burn dsp_isp fail"); - return FAIL; - } - - /* step10:set scramble */ - pr_debug("step10:set scramble"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); - if (ret <= 0) { - pr_err("set scramble fail"); - return FAIL; - } - - return SUCCESS; -} - -static u8 gup_burn_fw_ss51(struct i2c_client *client) -{ - u8 *fw_ss51 = NULL; - u8 retry = 0; - s32 ret = 0; - - pr_debug("Begin burn ss51 firmware"); - - /* step1:alloc memory */ - pr_debug("step1:alloc memory"); - while (retry++ < 5) { - fw_ss51 = devm_kzalloc(&client->dev, FW_SECTION_LENGTH, - GFP_KERNEL); - if (fw_ss51 == NULL) { - continue; - } else { - break; - } - } - if (retry == 5) - return FAIL; - - /* step2:load ss51 firmware section 1 file data */ - pr_debug("step2:load ss51 firmware section 1 file data"); - ret = gup_load_section_file(fw_ss51, 0, FW_SECTION_LENGTH); - if (ret == FAIL) { - pr_err("load ss51 firmware section 1 fail"); - return FAIL; - } - - /* step3:clear control flag */ - pr_debug("step3:clear control flag"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00); - if (ret <= 0) { - pr_err("clear control flag fail"); - return FAIL; - } - - /* step4:burn ss51 firmware section 1 */ - pr_debug("step4:burn ss51 firmware section 1"); - ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01); - if (ret == FAIL) { - pr_err("burn ss51 firmware section 1 fail"); - return FAIL; - } - - /* step5:load ss51 firmware section 2 file data */ - pr_debug("step5:load ss51 firmware section 2 file data"); - ret = gup_load_section_file(fw_ss51, FW_SECTION_LENGTH, - FW_SECTION_LENGTH); - if (ret == FAIL) { - pr_err("[burn_fw_ss51]load ss51 firmware section 2 fail\n"); - return FAIL; - } - - /* step6:burn ss51 firmware section 2 */ - pr_debug("step6:burn ss51 firmware section 2"); - ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02); - if (ret == FAIL) { - pr_err("burn ss51 firmware section 2 fail"); - return FAIL; - } - - /* step7:load ss51 firmware section 3 file data */ - pr_debug("step7:load ss51 firmware section 3 file data"); - ret = gup_load_section_file(fw_ss51, 2*FW_SECTION_LENGTH, - FW_SECTION_LENGTH); - if (ret == FAIL) { - pr_err("load ss51 firmware section 3 fail"); - return FAIL; - } - - /* step8:burn ss51 firmware section 3 */ - pr_debug("step8:burn ss51 firmware section 3"); - ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13); - if (ret == FAIL) { - pr_err("burn ss51 firmware section 3 fail"); - return FAIL; - } - - /* step9:load ss51 firmware section 4 file data */ - pr_debug("step9:load ss51 firmware section 4 file data"); - ret = gup_load_section_file(fw_ss51, 3*FW_SECTION_LENGTH, - FW_SECTION_LENGTH); - if (ret == FAIL) { - pr_err("load ss51 firmware section 4 fail"); - return FAIL; - } - - /* step10:burn ss51 firmware section 4 */ - pr_debug("step10:burn ss51 firmware section 4"); - ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14); - if (ret == FAIL) { - pr_err("burn ss51 firmware section 4 fail"); - return FAIL; - } - - return SUCCESS; -} - -static u8 gup_burn_fw_dsp(struct i2c_client *client) -{ - s32 ret = 0; - u8 *fw_dsp = NULL; - u8 retry = 0; - u8 rd_buf[5]; - - pr_debug("Begin burn dsp firmware"); - /* step1:alloc memory */ - pr_debug("step1:alloc memory"); - while (retry++ < 5) { - fw_dsp = devm_kzalloc(&client->dev, FW_DSP_LENGTH, - GFP_KERNEL); - if (fw_dsp == NULL) { - continue; - } else { - break; - } - } - if (retry == 5) - return FAIL; - - /* step2:load firmware dsp */ - pr_debug("step2:load firmware dsp"); - ret = gup_load_section_file(fw_dsp, 4*FW_SECTION_LENGTH, FW_DSP_LENGTH); - if (ret == FAIL) { - pr_err("load firmware dsp fail"); - return ret; - } - - /* step3:select bank3 */ - pr_debug("step3:select bank3"); - ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03); - if (ret <= 0) { - pr_err("select bank3 fail"); - return FAIL; - } - - /* Step4:hold ss51 & dsp */ - pr_debug("step4:hold ss51 & dsp"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); - if (ret <= 0) { - pr_err("hold ss51 & dsp fail"); - return FAIL; - } - - /* step5:set scramble */ - pr_debug("step5:set scramble"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); - if (ret <= 0) { - pr_err("set scramble fail"); - return FAIL; - } - - /* step6:release ss51 & dsp */ - pr_debug("step6:release ss51 & dsp"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); - if (ret <= 0) { - pr_err("release ss51 & dsp fail"); - return FAIL; - } - /* must delay */ - msleep(20); - - /* step7:burn 4k dsp firmware */ - pr_debug("step7:burn 4k dsp firmware"); - ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH); - if (ret == FAIL) { - pr_err("[burn_fw_dsp]burn fw_section fail\n"); - return ret; - } - - /* step8:send burn cmd to move data to flash from sram */ - pr_debug("step8:send burn cmd to move data to flash from sram"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05); - if (ret <= 0) { - pr_err("send burn cmd fail"); - return ret; - } - pr_debug("Wait for the burn is complete"); - do { - ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); - if (ret <= 0) { - pr_err("Get burn state fail"); - return ret; - } - msleep(20); - } while (rd_buf[GTP_ADDR_LENGTH]); - - /* step9:recall check 4k dsp firmware */ - pr_debug("step9:recall check 4k dsp firmware"); - ret = gup_recall_check(client, fw_dsp, 0x9000, FW_DSP_LENGTH); - if (ret == FAIL) { - pr_err("recall check 4k dsp firmware fail"); - return ret; - } - - return SUCCESS; -} - -static u8 gup_burn_fw_boot(struct i2c_client *client) -{ - s32 ret = 0; - u8 *fw_boot = NULL; - u8 retry = 0; - u8 rd_buf[5]; - - pr_debug("Begin burn bootloader firmware"); - - /* step1:Alloc memory */ - pr_debug("step1:Alloc memory"); - while (retry++ < 5) { - fw_boot = devm_kzalloc(&client->dev, FW_BOOT_LENGTH, - GFP_KERNEL); - if (fw_boot == NULL) { - continue; - } else { - break; - } - } - if (retry == 5) - return FAIL; - - /* step2:load firmware bootloader */ - pr_debug("step2:load firmware bootloader"); - ret = gup_load_section_file(fw_boot, (4 * FW_SECTION_LENGTH + - FW_DSP_LENGTH), FW_BOOT_LENGTH); - if (ret == FAIL) { - pr_err("load firmware dsp fail"); - return ret; - } - - /* step3:hold ss51 & dsp */ - pr_debug("step3:hold ss51 & dsp"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); - if (ret <= 0) { - pr_err("hold ss51 & dsp fail"); - return FAIL; - } - - /* step4:set scramble */ - pr_debug("step4:set scramble"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); - if (ret <= 0) { - pr_err("set scramble fail"); - return FAIL; - } - - /* step5:release ss51 & dsp */ - pr_debug("step5:release ss51 & dsp"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); - if (ret <= 0) { - pr_err("release ss51 & dsp fail"); - return FAIL; - } - /* must delay */ - msleep(20); - - /* step6:select bank3 */ - pr_debug("step6:select bank3"); - ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03); - if (ret <= 0) { - pr_err("select bank3 fail"); - return FAIL; - } - - /* step7:burn 2k bootloader firmware */ - pr_debug("step7:burn 2k bootloader firmware"); - ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH); - if (ret == FAIL) { - pr_err("burn fw_section fail"); - return ret; - } - - /* step7:send burn cmd to move data to flash from sram */ - pr_debug("step7:send burn cmd to flash data from sram"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06); - if (ret <= 0) { - pr_err("send burn cmd fail"); - return ret; - } - pr_debug("Wait for the burn is complete"); - do { - ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); - if (ret <= 0) { - pr_err("Get burn state fail"); - return ret; - } - msleep(20); - } while (rd_buf[GTP_ADDR_LENGTH]); - - /* step8:recall check 2k bootloader firmware */ - pr_debug("step8:recall check 2k bootloader firmware"); - ret = gup_recall_check(client, fw_boot, 0x9000, FW_BOOT_LENGTH); - if (ret == FAIL) { - pr_err("recall check 4k dsp firmware fail"); - return ret; - } - - /* step9:enable download DSP code */ - pr_debug("step9:enable download DSP code "); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99); - if (ret <= 0) { - pr_err("enable download DSP code fail"); - return FAIL; - } - - /* step10:release ss51 & hold dsp */ - pr_debug("step10:release ss51 & hold dsp"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08); - if (ret <= 0) { - pr_err("release ss51 & hold dsp fail"); - return FAIL; - } - - return SUCCESS; -} - -s32 gup_update_proc(void *dir) -{ - s32 ret = 0; - u8 retry = 0; - struct st_fw_head fw_head; - struct goodix_ts_data *ts = NULL; - - pr_debug("Begin update."); - - if (!i2c_connect_client) { - pr_err("No i2c connect client for %s\n", __func__); - return -EIO; - } - - show_len = 1; - total_len = 100; - - ts = i2c_get_clientdata(i2c_connect_client); - - if (searching_file) { - /* exit .bin update file searching */ - searching_file = 0; - pr_info("Exiting searching .bin update file."); - /* wait for auto update quitted completely */ - while ((show_len != 200) && (show_len != 100)) - msleep(100); - } - - ret = gup_check_update_file(i2c_connect_client, &fw_head, (u8 *)dir); - if (ret == FAIL) { - pr_err("check update file fail"); - goto file_fail; - } - - /* gtp_reset_guitar(i2c_connect_client, 20); */ - ret = gup_get_ic_fw_msg(i2c_connect_client); - if (ret == FAIL) { - pr_err("get ic message fail"); - goto file_fail; - } - - if (ts->force_update) { - dev_dbg(&ts->client->dev, "Enter force update."); - } else { - ret = gup_enter_update_judge(ts->client, &fw_head); - if (ret == FAIL) { - dev_err(&ts->client->dev, - "Check *.bin file fail."); - goto file_fail; - } - } - - ts->enter_update = 1; - gtp_irq_disable(ts); -#if GTP_ESD_PROTECT - gtp_esd_switch(ts->client, SWITCH_OFF); -#endif - ret = gup_enter_update_mode(i2c_connect_client); - if (ret == FAIL) { - pr_err("enter update mode fail"); - goto update_fail; - } - - while (retry++ < 5) { - show_len = 10; - total_len = 100; - ret = gup_burn_dsp_isp(i2c_connect_client); - if (ret == FAIL) { - pr_err("burn dsp isp fail"); - continue; - } - - show_len += 10; - ret = gup_burn_fw_ss51(i2c_connect_client); - if (ret == FAIL) { - pr_err("burn ss51 firmware fail"); - continue; - } - - show_len += 40; - ret = gup_burn_fw_dsp(i2c_connect_client); - if (ret == FAIL) { - pr_err("burn dsp firmware fail"); - continue; - } - - show_len += 20; - ret = gup_burn_fw_boot(i2c_connect_client); - if (ret == FAIL) { - pr_err("burn bootloader fw fail"); - continue; - } - show_len += 10; - pr_info("UPDATE SUCCESS"); - break; - } - if (retry >= 5) { - pr_err("retry timeout,UPDATE FAIL"); - goto update_fail; - } - - pr_debug("leave update mode"); - gup_leave_update_mode(i2c_connect_client); - - msleep(100); - - if (ts->fw_error) { - pr_info("firmware error auto update, resent config\n"); - gup_init_panel(ts); - } - show_len = 100; - total_len = 100; - ts->enter_update = 0; - gtp_irq_enable(ts); - -#if GTP_ESD_PROTECT - gtp_esd_switch(ts->client, SWITCH_ON); -#endif - if (update_msg.need_free) { - devm_kfree(&ts->client->dev, update_msg.fw_data); - update_msg.need_free = false; - } - - return SUCCESS; - -update_fail: - ts->enter_update = 0; - gtp_irq_enable(ts); - -#if GTP_ESD_PROTECT - gtp_esd_switch(ts->client, SWITCH_ON); -#endif - -file_fail: - show_len = 200; - total_len = 100; - if (update_msg.need_free) { - devm_kfree(&ts->client->dev, update_msg.fw_data); - update_msg.need_free = false; - } - return FAIL; -} - -static void gup_update_work(struct work_struct *work) -{ - if (gup_update_proc(NULL) == FAIL) - pr_err("Goodix update work fail\n"); -} - -u8 gup_init_update_proc(struct goodix_ts_data *ts) -{ - dev_dbg(&ts->client->dev, "Ready to run update work\n"); - - INIT_DELAYED_WORK(&ts->goodix_update_work, gup_update_work); - schedule_delayed_work(&ts->goodix_update_work, - msecs_to_jiffies(3000)); - - return 0; -} diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c index 206941708141..3e85cb5e2ebc 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c @@ -3666,7 +3666,7 @@ static int synaptics_rmi4_probe(struct platform_device *pdev) return -EINVAL; } - rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL); + rmi4_data = devm_kzalloc(&pdev->dev, sizeof(*rmi4_data), GFP_KERNEL); if (!rmi4_data) { dev_err(&pdev->dev, "%s: Failed to alloc mem for rmi4_data\n", @@ -3684,6 +3684,12 @@ static int synaptics_rmi4_probe(struct platform_device *pdev) rmi4_data->fingers_on_2d = false; rmi4_data->update_coords = true; + rmi4_data->write_buf = devm_kzalloc(&pdev->dev, I2C_WRITE_BUF_MAX_LEN, + GFP_KERNEL); + if (!rmi4_data->write_buf) + return -ENOMEM; + rmi4_data->write_buf_len = I2C_WRITE_BUF_MAX_LEN; + rmi4_data->irq_enable = synaptics_rmi4_irq_enable; rmi4_data->reset_device = synaptics_rmi4_reset_device; diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h index 7d7e045d7917..a642092e2f63 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h @@ -101,6 +101,7 @@ #define PINCTRL_STATE_RELEASE "pmx_ts_release" #define SYNA_FW_NAME_MAX_LEN 50 +#define I2C_WRITE_BUF_MAX_LEN 32 enum exp_fn { RMI_DEV = 0, @@ -277,6 +278,8 @@ struct synaptics_rmi4_data { unsigned char no_sleep_setting; unsigned char intr_mask[MAX_INTR_REGISTERS]; unsigned char *button_txrx_mapping; + unsigned char *write_buf; + unsigned short write_buf_len; unsigned short num_of_intr_regs; unsigned short f01_query_base_addr; unsigned short f01_cmd_base_addr; diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c index 0ec16e606545..4787f2bcd768 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c @@ -102,6 +102,7 @@ (fwu->config_data[2] == config_id[2]) && \ (fwu->config_data[3] == config_id[3])) +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS static ssize_t fwu_sysfs_show_image(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, char *buf, loff_t pos, size_t count); @@ -157,6 +158,7 @@ static ssize_t fwu_sysfs_config_id_show(struct device *dev, static ssize_t fwu_sysfs_package_id_show(struct device *dev, struct device_attribute *attr, char *buf); +#endif enum bl_version { V5 = 5, @@ -296,6 +298,7 @@ struct synaptics_rmi4_fwu_handle { struct synaptics_rmi4_data *rmi4_data; }; +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS static struct bin_attribute dev_attr_data = { .attr = { .name = "data", @@ -305,9 +308,11 @@ static struct bin_attribute dev_attr_data = { .read = fwu_sysfs_show_image, .write = fwu_sysfs_store_image, }; +#endif static struct device_attribute attrs[] = { +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS __ATTR(force_update_fw, S_IWUSR | S_IWGRP, NULL, fwu_sysfs_force_reflash_store), @@ -353,6 +358,7 @@ static struct device_attribute attrs[] = { __ATTR(package_id, S_IRUGO, fwu_sysfs_package_id_show, synaptics_rmi4_store_error), +#endif }; static struct synaptics_rmi4_fwu_handle *fwu; @@ -1220,6 +1226,7 @@ write_config: return retval; } +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS static int fwu_start_write_config(void) { int retval; @@ -1395,6 +1402,7 @@ exit: return retval; } +#endif static int fwu_do_lockdown(void) { @@ -1585,6 +1593,7 @@ int synaptics_dsx_fw_updater(unsigned char *fw_data) } EXPORT_SYMBOL(synaptics_dsx_fw_updater); +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS static ssize_t fwu_sysfs_show_image(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, char *buf, loff_t pos, size_t count) @@ -1972,6 +1981,7 @@ static ssize_t fwu_sysfs_package_id_show(struct device *dev, (package_id[1] << 8) | package_id[0], (package_id[3] << 8) | package_id[2]); } +#endif static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data, unsigned char intr_mask) @@ -2045,6 +2055,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) fwu->do_lockdown = DO_LOCKDOWN; fwu->initialized = true; +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); if (retval < 0) { @@ -2053,6 +2064,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) __func__); goto exit_free_fwu; } +#endif for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, @@ -2074,7 +2086,9 @@ exit_remove_attrs: &attrs[attr_count].attr); } +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); +#endif exit_free_fwu: kfree(fwu); @@ -2096,7 +2110,9 @@ static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data) &attrs[attr_count].attr); } +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); +#endif kfree(fwu->read_config_buf); kfree(fwu); diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c index 0b3fbaf9f462..0be7e0bc9045 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c @@ -134,18 +134,33 @@ static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, { int retval; unsigned char retry; - unsigned char buf[length + 1]; struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); struct i2c_msg msg[] = { { .addr = i2c->addr, .flags = 0, .len = length + 1, - .buf = buf, } }; mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + /* + * Reassign memory for write_buf in case length is greater than 32 bytes + */ + if (rmi4_data->write_buf_len < length + 1) { + devm_kfree(rmi4_data->pdev->dev.parent, rmi4_data->write_buf); + rmi4_data->write_buf = devm_kzalloc(rmi4_data->pdev->dev.parent, + length + 1, GFP_KERNEL); + if (!rmi4_data->write_buf) { + rmi4_data->write_buf_len = 0; + retval = -ENOMEM; + goto exit; + } + rmi4_data->write_buf_len = length + 1; + } + + /* Assign the write_buf of driver structure to i2c_msg buf */ + msg[0].buf = rmi4_data->write_buf; retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr); if (retval != PAGE_SELECT_LEN) { @@ -153,8 +168,8 @@ static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, goto exit; } - buf[0] = addr & MASK_8BIT; - memcpy(&buf[1], &data[0], length); + rmi4_data->write_buf[0] = addr & MASK_8BIT; + memcpy(&rmi4_data->write_buf[1], &data[0], length); for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { if (i2c_transfer(i2c->adapter, msg, 1) == 1) { diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c index 52bd5cfc37c8..168318f85e53 100644 --- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c +++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c @@ -2218,10 +2218,12 @@ static int fwu_get_image_firmware_id(unsigned int *fw_id) __func__); return -ENOMEM; } - while (strptr[index] >= '0' && strptr[index] <= '9') { + while ((index < MAX_FIRMWARE_ID_LEN - 1) && strptr[index] >= '0' + && strptr[index] <= '9') { firmware_id[index] = strptr[index]; index++; } + firmware_id[index] = '\0'; retval = sstrtoul(firmware_id, 10, (unsigned long *)fw_id); kfree(firmware_id); diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 5975d76ce755..a0ef57483ebb 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -926,7 +926,7 @@ again: next_tail = (tail + sizeof(*cmd)) % CMD_BUFFER_SIZE; left = (head - next_tail) % CMD_BUFFER_SIZE; - if (left <= 2) { + if (left <= 0x20) { struct iommu_cmd sync_cmd; volatile u64 sem = 0; int ret; diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c index 7caf2fa237f2..4831eb910fc7 100644 --- a/drivers/iommu/amd_iommu_v2.c +++ b/drivers/iommu/amd_iommu_v2.c @@ -809,8 +809,10 @@ int amd_iommu_init_device(struct pci_dev *pdev, int pasids) goto out_free_domain; group = iommu_group_get(&pdev->dev); - if (!group) + if (!group) { + ret = -EINVAL; goto out_free_domain; + } ret = iommu_attach_group(dev_state->domain, group); if (ret != 0) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 03a691723349..51159711b1d8 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -2451,7 +2451,7 @@ static void arm_smmu_unassign_table(struct arm_smmu_domain *smmu_domain) int ret; int dest_vmids = VMID_HLOS; int dest_perms = PERM_READ | PERM_WRITE | PERM_EXEC; - int source_vmlist[2] = {VMID_HLOS, smmu_domain->secure_vmid}; + int source_vmlist[2] = {smmu_domain->secure_vmid, VMID_HLOS}; struct arm_smmu_pte_info *pte_info, *temp; if (!arm_smmu_is_master_side_secure(smmu_domain)) diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c index 2acb9242bcf8..8c6364f03eac 100644 --- a/drivers/iommu/dma-mapping-fast.c +++ b/drivers/iommu/dma-mapping-fast.c @@ -25,6 +25,13 @@ #define FAST_PAGE_SIZE (1UL << FAST_PAGE_SHIFT) #define FAST_PAGE_MASK (~(PAGE_SIZE - 1)) #define FAST_PTE_ADDR_MASK ((av8l_fast_iopte)0xfffffffff000) +#define FAST_MAIR_ATTR_IDX_CACHE 1 +#define FAST_PTE_ATTRINDX_SHIFT 2 +#define FAST_PTE_ATTRINDX_MASK 0x7 +#define FAST_PTE_SH_SHIFT 8 +#define FAST_PTE_SH_MASK (((av8l_fast_iopte)0x3) << FAST_PTE_SH_SHIFT) +#define FAST_PTE_SH_OS (((av8l_fast_iopte)2) << FAST_PTE_SH_SHIFT) +#define FAST_PTE_SH_IS (((av8l_fast_iopte)3) << FAST_PTE_SH_SHIFT) static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot, bool coherent) @@ -56,6 +63,36 @@ static void fast_dmac_clean_range(struct dma_fast_smmu_mapping *mapping, dmac_clean_range(start, end); } +static bool __fast_is_pte_coherent(av8l_fast_iopte *ptep) +{ + int attr_idx = (*ptep & (FAST_PTE_ATTRINDX_MASK << + FAST_PTE_ATTRINDX_SHIFT)) >> + FAST_PTE_ATTRINDX_SHIFT; + + if ((attr_idx == FAST_MAIR_ATTR_IDX_CACHE) && + (((*ptep & FAST_PTE_SH_MASK) == FAST_PTE_SH_IS) || + (*ptep & FAST_PTE_SH_MASK) == FAST_PTE_SH_OS)) + return true; + + return false; +} + +static bool is_dma_coherent(struct device *dev, struct dma_attrs *attrs) +{ + bool is_coherent; + + if (dma_get_attr(DMA_ATTR_FORCE_COHERENT, attrs)) + is_coherent = true; + else if (dma_get_attr(DMA_ATTR_FORCE_NON_COHERENT, attrs)) + is_coherent = false; + else if (is_device_dma_coherent(dev)) + is_coherent = true; + else + is_coherent = false; + + return is_coherent; +} + /* * Checks if the allocated range (ending at @end) covered the upcoming * stale bit. We don't need to know exactly where the range starts since @@ -315,7 +352,7 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page, int nptes = len >> FAST_PAGE_SHIFT; bool skip_sync = dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs); int prot = __fast_dma_direction_to_prot(dir); - bool is_coherent = is_device_dma_coherent(dev); + bool is_coherent = is_dma_coherent(dev, attrs); prot = __get_iommu_pgprot(attrs, prot, is_coherent); @@ -360,7 +397,7 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova, int nptes = len >> FAST_PAGE_SHIFT; struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK)); bool skip_sync = dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs); - bool is_coherent = is_device_dma_coherent(dev); + bool is_coherent = is_dma_coherent(dev, attrs); if (!skip_sync && !is_coherent) __fast_dma_page_dev_to_cpu(page, offset, size, dir); @@ -381,7 +418,7 @@ static void fast_smmu_sync_single_for_cpu(struct device *dev, unsigned long offset = iova & ~FAST_PAGE_MASK; struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK)); - if (!is_device_dma_coherent(dev)) + if (!__fast_is_pte_coherent(pmd)) __fast_dma_page_dev_to_cpu(page, offset, size, dir); } @@ -394,7 +431,7 @@ static void fast_smmu_sync_single_for_device(struct device *dev, unsigned long offset = iova & ~FAST_PAGE_MASK; struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK)); - if (!is_device_dma_coherent(dev)) + if (!__fast_is_pte_coherent(pmd)) __fast_dma_page_cpu_to_dev(page, offset, size, dir); } @@ -472,7 +509,7 @@ static void *fast_smmu_alloc(struct device *dev, size_t size, struct sg_mapping_iter miter; unsigned int count = ALIGN(size, SZ_4K) >> PAGE_SHIFT; int prot = IOMMU_READ | IOMMU_WRITE; /* TODO: extract from attrs */ - bool is_coherent = is_device_dma_coherent(dev); + bool is_coherent = is_dma_coherent(dev, attrs); pgprot_t remap_prot = __get_dma_pgprot(attrs, PAGE_KERNEL, is_coherent); struct page **pages; @@ -596,7 +633,7 @@ static int fast_smmu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, unsigned long uaddr = vma->vm_start; struct page **pages; int i, nr_pages, ret = 0; - bool coherent = is_device_dma_coherent(dev); + bool coherent = is_dma_coherent(dev, attrs); vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot, coherent); diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 59e9abd3345e..f0fc6f7b5d98 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1993,6 +1993,25 @@ static int domain_context_mapping_one(struct dmar_domain *domain, if (context_present(context)) goto out_unlock; + /* + * For kdump cases, old valid entries may be cached due to the + * in-flight DMA and copied pgtable, but there is no unmapping + * behaviour for them, thus we need an explicit cache flush for + * the newly-mapped device. For kdump, at this point, the device + * is supposed to finish reset at its driver probe stage, so no + * in-flight DMA will exist, and we don't need to worry anymore + * hereafter. + */ + if (context_copied(context)) { + u16 did_old = context_domain_id(context); + + if (did_old >= 0 && did_old < cap_ndoms(iommu->cap)) + iommu->flush.flush_context(iommu, did_old, + (((u16)bus) << 8) | devfn, + DMA_CCMD_MASK_NOBIT, + DMA_CCMD_DEVICE_INVL); + } + pgd = domain->pgd; context_clear_entry(context); @@ -3219,13 +3238,14 @@ static int __init init_dmars(void) iommu_identity_mapping |= IDENTMAP_GFX; #endif + check_tylersburg_isoch(); + if (iommu_identity_mapping) { ret = si_domain_init(hw_pass_through); if (ret) goto free_iommu; } - check_tylersburg_isoch(); /* * If we copied translations from a previous kernel in the kdump @@ -5020,6 +5040,25 @@ static void intel_iommu_remove_device(struct device *dev) } #ifdef CONFIG_INTEL_IOMMU_SVM +#define MAX_NR_PASID_BITS (20) +static inline unsigned long intel_iommu_get_pts(struct intel_iommu *iommu) +{ + /* + * Convert ecap_pss to extend context entry pts encoding, also + * respect the soft pasid_max value set by the iommu. + * - number of PASID bits = ecap_pss + 1 + * - number of PASID table entries = 2^(pts + 5) + * Therefore, pts = ecap_pss - 4 + * e.g. KBL ecap_pss = 0x13, PASID has 20 bits, pts = 15 + */ + if (ecap_pss(iommu->ecap) < 5) + return 0; + + /* pasid_max is encoded as actual number of entries not the bits */ + return find_first_bit((unsigned long *)&iommu->pasid_max, + MAX_NR_PASID_BITS) - 5; +} + int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sdev) { struct device_domain_info *info; @@ -5052,7 +5091,9 @@ int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sd if (!(ctx_lo & CONTEXT_PASIDE)) { context[1].hi = (u64)virt_to_phys(iommu->pasid_state_table); - context[1].lo = (u64)virt_to_phys(iommu->pasid_table) | ecap_pss(iommu->ecap); + context[1].lo = (u64)virt_to_phys(iommu->pasid_table) | + intel_iommu_get_pts(iommu); + wmb(); /* CONTEXT_TT_MULTI_LEVEL and CONTEXT_TT_DEV_IOTLB are both * extended to permit requests-with-PASID if the PASIDE bit diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 5f2b66286c0c..6a8a9492c771 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -89,6 +89,7 @@ #define ARM_LPAE_PTE_TYPE_TABLE 3 #define ARM_LPAE_PTE_TYPE_PAGE 3 +#define ARM_LPAE_PTE_SH_MASK (((arm_lpae_iopte)0x3) << 8) #define ARM_LPAE_PTE_NSTABLE (((arm_lpae_iopte)1) << 63) #define ARM_LPAE_PTE_XN (((arm_lpae_iopte)3) << 53) #define ARM_LPAE_PTE_AF (((arm_lpae_iopte)1) << 10) @@ -928,8 +929,9 @@ static bool __arm_lpae_is_iova_coherent(struct arm_lpae_io_pgtable *data, ARM_LPAE_PTE_ATTRINDX_SHIFT)) >> ARM_LPAE_PTE_ATTRINDX_SHIFT; if ((attr_idx == ARM_LPAE_MAIR_ATTR_IDX_CACHE) && - ((*ptep & ARM_LPAE_PTE_SH_IS) || - (*ptep & ARM_LPAE_PTE_SH_OS))) + (((*ptep & ARM_LPAE_PTE_SH_MASK) == ARM_LPAE_PTE_SH_IS) + || + (*ptep & ARM_LPAE_PTE_SH_MASK) == ARM_LPAE_PTE_SH_OS)) return true; } else { if (*ptep & ARM_LPAE_PTE_MEMATTR_OIWB) diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c index 0fea985ef1dc..d7af88534971 100644 --- a/drivers/irqchip/irq-bcm7038-l1.c +++ b/drivers/irqchip/irq-bcm7038-l1.c @@ -216,6 +216,31 @@ static int bcm7038_l1_set_affinity(struct irq_data *d, return 0; } +static void bcm7038_l1_cpu_offline(struct irq_data *d) +{ + struct cpumask *mask = irq_data_get_affinity_mask(d); + int cpu = smp_processor_id(); + cpumask_t new_affinity; + + /* This CPU was not on the affinity mask */ + if (!cpumask_test_cpu(cpu, mask)) + return; + + if (cpumask_weight(mask) > 1) { + /* + * Multiple CPU affinity, remove this CPU from the affinity + * mask + */ + cpumask_copy(&new_affinity, mask); + cpumask_clear_cpu(cpu, &new_affinity); + } else { + /* Only CPU, put on the lowest online CPU */ + cpumask_clear(&new_affinity); + cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity); + } + irq_set_affinity_locked(d, &new_affinity, false); +} + static int __init bcm7038_l1_init_one(struct device_node *dn, unsigned int idx, struct bcm7038_l1_chip *intc) @@ -267,6 +292,7 @@ static struct irq_chip bcm7038_l1_irq_chip = { .irq_mask = bcm7038_l1_mask, .irq_unmask = bcm7038_l1_unmask, .irq_set_affinity = bcm7038_l1_set_affinity, + .irq_cpu_offline = bcm7038_l1_cpu_offline, }; static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq, diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index c40a9f3b0724..4ff2ee2609ca 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -804,6 +804,14 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, gic_write_irouter(val, reg); /* + * It is possible that irq is disabled from SW perspective only, + * because kernel takes lazy disable approach. Therefore check irq + * descriptor if it should kept disabled. + */ + if (irqd_irq_disabled(d)) + enabled = 0; + + /* * If the interrupt was enabled, enabled it again. Otherwise, * just wait for the distributor to have digested our changes. */ diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index 2a506fe0c8a4..74bf1a17ae7c 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -762,8 +762,10 @@ static int __init ser_gigaset_init(void) driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS, GIGASET_MODULENAME, GIGASET_DEVNAME, &ops, THIS_MODULE); - if (!driver) + if (!driver) { + rc = -ENOMEM; goto error; + } rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc); if (rc != 0) { diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c index d7c286656a25..7b4ddf0a39ec 100644 --- a/drivers/isdn/hardware/eicon/message.c +++ b/drivers/isdn/hardware/eicon/message.c @@ -11304,7 +11304,8 @@ static void mixer_notify_update(PLCI *plci, byte others) ((CAPI_MSG *) msg)->header.ncci = 0; ((CAPI_MSG *) msg)->info.facility_req.Selector = SELECTOR_LINE_INTERCONNECT; ((CAPI_MSG *) msg)->info.facility_req.structs[0] = 3; - PUT_WORD(&(((CAPI_MSG *) msg)->info.facility_req.structs[1]), LI_REQ_SILENT_UPDATE); + ((CAPI_MSG *) msg)->info.facility_req.structs[1] = LI_REQ_SILENT_UPDATE & 0xff; + ((CAPI_MSG *) msg)->info.facility_req.structs[2] = LI_REQ_SILENT_UPDATE >> 8; ((CAPI_MSG *) msg)->info.facility_req.structs[3] = 0; w = api_put(notify_plci->appl, (CAPI_MSG *) msg); if (w != _QUEUE_FULL) diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c index 85a6be824485..817dfa3b2f53 100644 --- a/drivers/leds/leds-qpnp.c +++ b/drivers/leds/leds-qpnp.c @@ -897,9 +897,10 @@ static int qpnp_mpp_set(struct qpnp_led_data *led) } } - if (led->mpp_cfg->pwm_mode != MANUAL_MODE) + if (led->mpp_cfg->pwm_mode != MANUAL_MODE) { pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev); - else { + led->mpp_cfg->pwm_cfg->pwm_enabled = 1; + } else { if (led->cdev.brightness < LED_MPP_CURRENT_MIN) led->cdev.brightness = LED_MPP_CURRENT_MIN; else { @@ -950,6 +951,7 @@ static int qpnp_mpp_set(struct qpnp_led_data *led) led->mpp_cfg->pwm_mode = led->mpp_cfg->pwm_cfg->default_mode; pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev); + led->mpp_cfg->pwm_cfg->pwm_enabled = 0; } rc = qpnp_led_masked_write(led, LED_MPP_MODE_CTRL(led->base), @@ -1606,7 +1608,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led) dev_err(&led->pdev->dev, "pwm enable failed\n"); return rc; } - + led->kpdbl_cfg->pwm_cfg->pwm_enabled = 1; set_bit(led->kpdbl_cfg->row_id, kpdbl_leds_in_use); /* is_kpdbl_master_turn_on will be set to true when GPLED1 @@ -1642,6 +1644,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led) "pwm enable failed\n"); return rc; } + led->kpdbl_cfg->pwm_cfg->pwm_enabled = 1; } else { if (kpdbl_master) { pwm_disable(kpdbl_master); @@ -1660,6 +1663,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led) is_kpdbl_master_turn_on = false; } else { pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev); + led->kpdbl_cfg->pwm_cfg->pwm_enabled = 0; clear_bit(led->kpdbl_cfg->row_id, kpdbl_leds_in_use); if (bitmap_weight(kpdbl_leds_in_use, NUM_KPDBL_LEDS) == 1 && kpdbl_master && @@ -1727,20 +1731,17 @@ static int qpnp_rgb_set(struct qpnp_led_data *led) "Failed to write led enable reg\n"); return rc; } - - if (led->rgb_cfg->pwm_cfg->pwm_enabled) { - pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev); - led->rgb_cfg->pwm_cfg->pwm_enabled = 0; - } - - rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev); - if (!rc) + if (!led->rgb_cfg->pwm_cfg->pwm_enabled) { + pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev); led->rgb_cfg->pwm_cfg->pwm_enabled = 1; + } } else { led->rgb_cfg->pwm_cfg->mode = led->rgb_cfg->pwm_cfg->default_mode; - pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev); - led->rgb_cfg->pwm_cfg->pwm_enabled = 0; + if (led->rgb_cfg->pwm_cfg->pwm_enabled) { + pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev); + led->rgb_cfg->pwm_cfg->pwm_enabled = 0; + } rc = qpnp_led_masked_write(led, RGB_LED_EN_CTL(led->base), led->rgb_cfg->enable, RGB_LED_DISABLE); @@ -2183,11 +2184,17 @@ static ssize_t pwm_us_store(struct device *dev, previous_pwm_us = pwm_cfg->pwm_period_us; pwm_cfg->pwm_period_us = pwm_us; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) { pwm_cfg->pwm_period_us = previous_pwm_us; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); dev_err(&led->pdev->dev, @@ -2237,12 +2244,18 @@ static ssize_t pause_lo_store(struct device *dev, previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } pwm_cfg->lut_params.lut_pause_lo = pause_lo; ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) { pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); dev_err(&led->pdev->dev, @@ -2292,12 +2305,18 @@ static ssize_t pause_hi_store(struct device *dev, previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } pwm_cfg->lut_params.lut_pause_hi = pause_hi; ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) { pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); dev_err(&led->pdev->dev, @@ -2348,12 +2367,18 @@ static ssize_t start_idx_store(struct device *dev, previous_start_idx = pwm_cfg->duty_cycles->start_idx; pwm_cfg->duty_cycles->start_idx = start_idx; pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) { pwm_cfg->duty_cycles->start_idx = previous_start_idx; pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); dev_err(&led->pdev->dev, @@ -2403,12 +2428,18 @@ static ssize_t ramp_step_ms_store(struct device *dev, previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms; ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) { pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); dev_err(&led->pdev->dev, @@ -2458,12 +2489,18 @@ static ssize_t lut_flags_store(struct device *dev, previous_lut_flags = pwm_cfg->lut_params.flags; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } pwm_cfg->lut_params.flags = lut_flags; ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) { pwm_cfg->lut_params.flags = previous_lut_flags; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); dev_err(&led->pdev->dev, @@ -2543,7 +2580,11 @@ static ssize_t duty_pcts_store(struct device *dev, pwm_cfg->old_duty_pcts = previous_duty_pcts; pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } + ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (ret) goto restore; @@ -2558,7 +2599,10 @@ restore: pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts; pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts; pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts; - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); qpnp_led_set(&led->cdev, led->cdev.brightness); return ret; @@ -2588,7 +2632,10 @@ static void led_blink(struct qpnp_led_data *led, led->kpdbl_cfg->pwm_mode = pwm_cfg->default_mode; } - pwm_free(pwm_cfg->pwm_dev); + if (pwm_cfg->pwm_enabled) { + pwm_disable(pwm_cfg->pwm_dev); + pwm_cfg->pwm_enabled = 0; + } qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name); if (led->id == QPNP_ID_RGB_RED || led->id == QPNP_ID_RGB_GREEN || led->id == QPNP_ID_RGB_BLUE) { @@ -3541,8 +3588,11 @@ static int qpnp_get_config_kpdbl(struct qpnp_led_data *led, } rc = qpnp_get_config_pwm(led->kpdbl_cfg->pwm_cfg, led->pdev, node); - if (rc < 0) + if (rc < 0) { + if (led->kpdbl_cfg->pwm_cfg->pwm_dev) + pwm_put(led->kpdbl_cfg->pwm_cfg->pwm_dev); return rc; + } rc = of_property_read_u32(node, "qcom,row-id", &val); if (!rc) @@ -3605,8 +3655,11 @@ static int qpnp_get_config_rgb(struct qpnp_led_data *led, } rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->pdev, node); - if (rc < 0) + if (rc < 0) { + if (led->rgb_cfg->pwm_cfg->pwm_dev) + pwm_put(led->rgb_cfg->pwm_cfg->pwm_dev); return rc; + } return 0; } @@ -3729,8 +3782,11 @@ static int qpnp_get_config_mpp(struct qpnp_led_data *led, } rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->pdev, node); - if (rc < 0) + if (rc < 0) { + if (led->mpp_cfg->pwm_cfg && led->mpp_cfg->pwm_cfg->pwm_dev) + pwm_put(led->mpp_cfg->pwm_cfg->pwm_dev); goto err_config_mpp; + } return 0; diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 7d5aa2c5c81d..36ca4e4cbfb7 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -475,6 +475,21 @@ config DM_VERITY If unsure, say N. +config DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 + bool "Prefetch size 128" + +config DM_VERITY_HASH_PREFETCH_MIN_SIZE + int "Verity hash prefetch minimum size" + depends on DM_VERITY + range 1 4096 + default 128 if DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 + default 1 + ---help--- + This sets minimum number of hash blocks to prefetch for dm-verity. + For devices like eMMC, having larger prefetch size like 128 can improve + performance with increased memory consumption for keeping more hashes + in RAM. + config DM_VERITY_FEC bool "Verity forward error correction support" depends on DM_VERITY @@ -527,6 +542,7 @@ config DM_ANDROID_VERITY depends on ASYMMETRIC_KEY_TYPE depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE depends on MD_LINEAR + select DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 ---help--- This device-mapper target is virtually a VERITY target. This target is setup by reading the metadata contents piggybacked diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h index 6b420a55c745..c3ea03c9a1a8 100644 --- a/drivers/md/bcache/bcache.h +++ b/drivers/md/bcache/bcache.h @@ -425,7 +425,7 @@ struct cache { * until a gc finishes - otherwise we could pointlessly burn a ton of * cpu */ - unsigned invalidate_needs_gc:1; + unsigned invalidate_needs_gc; bool discard; /* Get rid of? */ @@ -593,8 +593,8 @@ struct cache_set { /* Counts how many sectors bio_insert has added to the cache */ atomic_t sectors_to_gc; + wait_queue_head_t gc_wait; - wait_queue_head_t moving_gc_wait; struct keybuf moving_gc_keys; /* Number of moving GC bios in flight */ struct semaphore moving_in_flight; diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index 22b9e34ceb75..5b815e64c1c9 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -1762,33 +1762,34 @@ static void bch_btree_gc(struct cache_set *c) bch_moving_gc(c); } -static int bch_gc_thread(void *arg) +static bool gc_should_run(struct cache_set *c) { - struct cache_set *c = arg; struct cache *ca; unsigned i; - while (1) { -again: - bch_btree_gc(c); + for_each_cache(ca, c, i) + if (ca->invalidate_needs_gc) + return true; - set_current_state(TASK_INTERRUPTIBLE); - if (kthread_should_stop()) - break; + if (atomic_read(&c->sectors_to_gc) < 0) + return true; - mutex_lock(&c->bucket_lock); + return false; +} - for_each_cache(ca, c, i) - if (ca->invalidate_needs_gc) { - mutex_unlock(&c->bucket_lock); - set_current_state(TASK_RUNNING); - goto again; - } +static int bch_gc_thread(void *arg) +{ + struct cache_set *c = arg; - mutex_unlock(&c->bucket_lock); + while (1) { + wait_event_interruptible(c->gc_wait, + kthread_should_stop() || gc_should_run(c)); - try_to_freeze(); - schedule(); + if (kthread_should_stop()) + break; + + set_gc_sectors(c); + bch_btree_gc(c); } return 0; @@ -1796,11 +1797,10 @@ again: int bch_gc_thread_start(struct cache_set *c) { - c->gc_thread = kthread_create(bch_gc_thread, c, "bcache_gc"); + c->gc_thread = kthread_run(bch_gc_thread, c, "bcache_gc"); if (IS_ERR(c->gc_thread)) return PTR_ERR(c->gc_thread); - set_task_state(c->gc_thread, TASK_INTERRUPTIBLE); return 0; } diff --git a/drivers/md/bcache/btree.h b/drivers/md/bcache/btree.h index 5c391fa01bed..9b80417cd547 100644 --- a/drivers/md/bcache/btree.h +++ b/drivers/md/bcache/btree.h @@ -260,8 +260,7 @@ void bch_initial_mark_key(struct cache_set *, int, struct bkey *); static inline void wake_up_gc(struct cache_set *c) { - if (c->gc_thread) - wake_up_process(c->gc_thread); + wake_up(&c->gc_wait); } #define MAP_DONE 0 diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c index 25fa8445bb24..2410df1c2a05 100644 --- a/drivers/md/bcache/request.c +++ b/drivers/md/bcache/request.c @@ -196,10 +196,8 @@ static void bch_data_insert_start(struct closure *cl) struct data_insert_op *op = container_of(cl, struct data_insert_op, cl); struct bio *bio = op->bio, *n; - if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0) { - set_gc_sectors(op->c); + if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0) wake_up_gc(op->c); - } if (op->bypass) return bch_data_invalidate(cl); diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 3d5c0ba13181..7b5880b8874c 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1489,6 +1489,7 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb) mutex_init(&c->bucket_lock); init_waitqueue_head(&c->btree_cache_wait); init_waitqueue_head(&c->bucket_wait); + init_waitqueue_head(&c->gc_wait); sema_init(&c->uuid_write_mutex, 1); spin_lock_init(&c->btree_gc_time.lock); @@ -1547,6 +1548,7 @@ static void run_cache_set(struct cache_set *c) for_each_cache(ca, c, i) c->nbuckets += ca->sb.nbuckets; + set_gc_sectors(c); if (CACHE_SYNC(&c->sb)) { LIST_HEAD(journal); diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index bb9b92ebbf8e..0da5efaad85c 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -248,7 +248,7 @@ struct cache { /* * Fields for converting from sectors to blocks. */ - uint32_t sectors_per_block; + sector_t sectors_per_block; int sectors_per_block_shift; spinlock_t lock; @@ -3544,11 +3544,11 @@ static void cache_status(struct dm_target *ti, status_type_t type, residency = policy_residency(cache->policy); - DMEMIT("%u %llu/%llu %u %llu/%llu %u %u %u %u %u %u %lu ", + DMEMIT("%u %llu/%llu %llu %llu/%llu %u %u %u %u %u %u %lu ", (unsigned)DM_CACHE_METADATA_BLOCK_SIZE, (unsigned long long)(nr_blocks_metadata - nr_free_blocks_metadata), (unsigned long long)nr_blocks_metadata, - cache->sectors_per_block, + (unsigned long long)cache->sectors_per_block, (unsigned long long) from_cblock(residency), (unsigned long long) from_cblock(cache->cache_size), (unsigned) atomic_read(&cache->stats.read_hit), diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index e540b7942eba..799b9a5ad4f5 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1500,12 +1500,15 @@ static int crypt_set_key(struct crypt_config *cc, char *key) if (!cc->key_size && strcmp(key, "-")) goto out; + /* clear the flag since following operations may invalidate previously valid key */ + clear_bit(DM_CRYPT_KEY_VALID, &cc->flags); + if (cc->key_size && crypt_decode_key(cc->key, key, cc->key_size) < 0) goto out; - set_bit(DM_CRYPT_KEY_VALID, &cc->flags); - r = crypt_setkey_allcpus(cc); + if (!r) + set_bit(DM_CRYPT_KEY_VALID, &cc->flags); out: /* Hex key string not needed after here, so wipe it. */ diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c index 8e9e928dafba..78f403b45ab3 100644 --- a/drivers/md/dm-flakey.c +++ b/drivers/md/dm-flakey.c @@ -200,11 +200,13 @@ static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv) if (!(fc->up_interval + fc->down_interval)) { ti->error = "Total (up + down) interval is zero"; + r = -EINVAL; goto bad; } if (fc->up_interval + fc->down_interval < fc->up_interval) { ti->error = "Interval overflow"; + r = -EINVAL; goto bad; } diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index 8289804ccd99..d5ea9f28ae70 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -175,6 +175,7 @@ static void dm_stat_free(struct rcu_head *head) int cpu; struct dm_stat *s = container_of(head, struct dm_stat, rcu_head); + kfree(s->histogram_boundaries); kfree(s->program_id); kfree(s->aux_data); for_each_possible_cpu(cpu) { diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 9d3d4b297201..c7e97cf6e7fb 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -501,6 +501,7 @@ static void verity_prefetch_io(struct work_struct *work) container_of(work, struct dm_verity_prefetch_work, work); struct dm_verity *v = pw->v; int i; + sector_t prefetch_size; for (i = v->levels - 2; i >= 0; i--) { sector_t hash_block_start; @@ -523,8 +524,14 @@ static void verity_prefetch_io(struct work_struct *work) hash_block_end = v->hash_blocks - 1; } no_prefetch_cluster: + // for emmc, it is more efficient to send bigger read + prefetch_size = max((sector_t)CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE, + hash_block_end - hash_block_start + 1); + if ((hash_block_start + prefetch_size) >= (v->hash_start + v->hash_blocks)) { + prefetch_size = hash_block_end - hash_block_start + 1; + } dm_bufio_prefetch(v->bufio, hash_block_start, - hash_block_end - hash_block_start + 1); + prefetch_size); } kfree(pw); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index dd1cde5458b8..e9b34de2319e 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1467,11 +1467,62 @@ void dm_accept_partial_bio(struct bio *bio, unsigned n_sectors) } EXPORT_SYMBOL_GPL(dm_accept_partial_bio); +/* + * Flush current->bio_list when the target map method blocks. + * This fixes deadlocks in snapshot and possibly in other targets. + */ +struct dm_offload { + struct blk_plug plug; + struct blk_plug_cb cb; +}; + +static void flush_current_bio_list(struct blk_plug_cb *cb, bool from_schedule) +{ + struct dm_offload *o = container_of(cb, struct dm_offload, cb); + struct bio_list list; + struct bio *bio; + + INIT_LIST_HEAD(&o->cb.list); + + if (unlikely(!current->bio_list)) + return; + + list = *current->bio_list; + bio_list_init(current->bio_list); + + while ((bio = bio_list_pop(&list))) { + struct bio_set *bs = bio->bi_pool; + if (unlikely(!bs) || bs == fs_bio_set) { + bio_list_add(current->bio_list, bio); + continue; + } + + spin_lock(&bs->rescue_lock); + bio_list_add(&bs->rescue_list, bio); + queue_work(bs->rescue_workqueue, &bs->rescue_work); + spin_unlock(&bs->rescue_lock); + } +} + +static void dm_offload_start(struct dm_offload *o) +{ + blk_start_plug(&o->plug); + o->cb.callback = flush_current_bio_list; + list_add(&o->cb.list, ¤t->plug->cb_list); +} + +static void dm_offload_end(struct dm_offload *o) +{ + list_del(&o->cb.list); + blk_finish_plug(&o->plug); +} + static void __map_bio(struct dm_target_io *tio) { int r; sector_t sector; struct mapped_device *md; + struct dm_offload o; struct bio *clone = &tio->clone; struct dm_target *ti = tio->ti; @@ -1484,7 +1535,11 @@ static void __map_bio(struct dm_target_io *tio) */ atomic_inc(&tio->io->io_count); sector = clone->bi_iter.bi_sector; + + dm_offload_start(&o); r = ti->type->map(ti, clone); + dm_offload_end(&o); + if (r == DM_MAPIO_REMAPPED) { /* the bio has been remapped so dispatch it */ diff --git a/drivers/md/linear.c b/drivers/md/linear.c index b7fe7e9fc777..6ba3227e29b2 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -52,18 +52,26 @@ static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector) return conf->disks + lo; } +/* + * In linear_congested() conf->raid_disks is used as a copy of + * mddev->raid_disks to iterate conf->disks[], because conf->raid_disks + * and conf->disks[] are created in linear_conf(), they are always + * consitent with each other, but mddev->raid_disks does not. + */ static int linear_congested(struct mddev *mddev, int bits) { struct linear_conf *conf; int i, ret = 0; - conf = mddev->private; + rcu_read_lock(); + conf = rcu_dereference(mddev->private); - for (i = 0; i < mddev->raid_disks && !ret ; i++) { + for (i = 0; i < conf->raid_disks && !ret ; i++) { struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev); ret |= bdi_congested(&q->backing_dev_info, bits); } + rcu_read_unlock(); return ret; } @@ -143,6 +151,19 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks) conf->disks[i-1].end_sector + conf->disks[i].rdev->sectors; + /* + * conf->raid_disks is copy of mddev->raid_disks. The reason to + * keep a copy of mddev->raid_disks in struct linear_conf is, + * mddev->raid_disks may not be consistent with pointers number of + * conf->disks[] when it is updated in linear_add() and used to + * iterate old conf->disks[] earray in linear_congested(). + * Here conf->raid_disks is always consitent with number of + * pointers in conf->disks[] array, and mddev->private is updated + * with rcu_assign_pointer() in linear_addr(), such race can be + * avoided. + */ + conf->raid_disks = raid_disks; + return conf; out: @@ -195,15 +216,23 @@ static int linear_add(struct mddev *mddev, struct md_rdev *rdev) if (!newconf) return -ENOMEM; + /* newconf->raid_disks already keeps a copy of * the increased + * value of mddev->raid_disks, WARN_ONCE() is just used to make + * sure of this. It is possible that oldconf is still referenced + * in linear_congested(), therefore kfree_rcu() is used to free + * oldconf until no one uses it anymore. + */ mddev_suspend(mddev); - oldconf = mddev->private; + oldconf = rcu_dereference(mddev->private); mddev->raid_disks++; - mddev->private = newconf; + WARN_ONCE(mddev->raid_disks != newconf->raid_disks, + "copied raid_disks doesn't match mddev->raid_disks"); + rcu_assign_pointer(mddev->private, newconf); md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); set_capacity(mddev->gendisk, mddev->array_sectors); mddev_resume(mddev); revalidate_disk(mddev->gendisk); - kfree(oldconf); + kfree_rcu(oldconf, rcu); return 0; } diff --git a/drivers/md/linear.h b/drivers/md/linear.h index b685ddd7d7f7..8d392e6098b3 100644 --- a/drivers/md/linear.h +++ b/drivers/md/linear.h @@ -10,6 +10,7 @@ struct linear_conf { struct rcu_head rcu; sector_t array_sectors; + int raid_disks; /* a copy of mddev->raid_disks */ struct dev_info disks[0]; }; #endif diff --git a/drivers/md/md.c b/drivers/md/md.c index c1c7d4fb4b77..eff554a12fb4 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -6771,7 +6771,7 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, /* need to ensure recovery thread has run */ wait_event_interruptible_timeout(mddev->sb_wait, !test_bit(MD_RECOVERY_NEEDED, - &mddev->flags), + &mddev->recovery), msecs_to_jiffies(5000)); if (cmd == STOP_ARRAY || cmd == STOP_ARRAY_RO) { /* Need to flush page cache, and ensure no-one else opens diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c index 7e44005595c1..20557e2c60c6 100644 --- a/drivers/md/persistent-data/dm-space-map-metadata.c +++ b/drivers/md/persistent-data/dm-space-map-metadata.c @@ -775,17 +775,15 @@ int dm_sm_metadata_create(struct dm_space_map *sm, memcpy(&smm->sm, &bootstrap_ops, sizeof(smm->sm)); r = sm_ll_new_metadata(&smm->ll, tm); + if (!r) { + if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS) + nr_blocks = DM_SM_METADATA_MAX_BLOCKS; + r = sm_ll_extend(&smm->ll, nr_blocks); + } + memcpy(&smm->sm, &ops, sizeof(smm->sm)); if (r) return r; - if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS) - nr_blocks = DM_SM_METADATA_MAX_BLOCKS; - r = sm_ll_extend(&smm->ll, nr_blocks); - if (r) - return r; - - memcpy(&smm->sm, &ops, sizeof(smm->sm)); - /* * Now we need to update the newly created data structures with the * allocated blocks that they were built from. diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 10ce885445f6..7af976934441 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -6980,6 +6980,15 @@ static int run(struct mddev *mddev) stripe = (stripe | (stripe-1)) + 1; mddev->queue->limits.discard_alignment = stripe; mddev->queue->limits.discard_granularity = stripe; + + /* + * We use 16-bit counter of active stripes in bi_phys_segments + * (minus one for over-loaded initialization) + */ + blk_queue_max_hw_sectors(mddev->queue, 0xfffe * STRIPE_SECTORS); + blk_queue_max_discard_sectors(mddev->queue, + 0xfffe * STRIPE_SECTORS); + /* * unaligned part of discard request will be ignored, so can't * guarantee discard_zeroes_data diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 1700447afe43..ba3287d176af 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -619,6 +619,7 @@ config VIDEO_S5K6A3 config VIDEO_S5K4ECGX tristate "Samsung S5K4ECGX sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select CRC32 ---help--- This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M camera sensor with an embedded SoC image signal processor. diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index c4307ad8594c..e543cbbf2ec4 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -2168,11 +2168,12 @@ static int dvb_register(struct cx23885_tsport *port) } port->i2c_client_tuner = client_tuner; break; - case CX23885_BOARD_HAUPPAUGE_HVR5525: - switch (port->nr) { + case CX23885_BOARD_HAUPPAUGE_HVR5525: { struct m88rs6000t_config m88rs6000t_config; struct a8293_platform_data a8293_pdata = {}; + switch (port->nr) { + /* port b - satellite */ case 1: /* attach frontend */ @@ -2267,6 +2268,7 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; + } default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig index 173daf0c0847..14fa7e40f2a6 100644 --- a/drivers/media/pci/dm1105/Kconfig +++ b/drivers/media/pci/dm1105/Kconfig @@ -1,6 +1,6 @@ config DVB_DM1105 tristate "SDMC DM1105 based PCI cards" - depends on DVB_CORE && PCI && I2C + depends on DVB_CORE && PCI && I2C && I2C_ALGOBIT select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h index 4ab6586c0467..f53e59e9c0ea 100644 --- a/drivers/media/pci/solo6x10/solo6x10.h +++ b/drivers/media/pci/solo6x10/solo6x10.h @@ -286,7 +286,10 @@ static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg) static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, u32 data) { + u16 val; + writel(data, solo_dev->reg_base + reg); + pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); } static inline void solo_irq_on(struct solo_dev *dev, u32 mask) diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index ba780c45f645..572bc043b62d 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1576,7 +1576,7 @@ static int vpfe_s_fmt(struct file *file, void *priv, return -EBUSY; } - ret = vpfe_try_fmt(file, priv, &format); + ret = __vpfe_get_format(vpfe, &format, &bpp); if (ret) return ret; diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index cff63e511e6d..b8f3d9fa66e9 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -214,6 +214,8 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) if (params->dlen > 24 || params->dlen <= 0) return -EINVAL; pctrl = devm_pinctrl_get(ppi->dev); + if (IS_ERR(pctrl)) + return PTR_ERR(pctrl); pstate = pinctrl_lookup_state(pctrl, pin_state[(params->dlen + 7) / 8 - 1]); if (pinctrl_select_state(pctrl, pstate)) diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c index 3985df780216..df0664b496ba 100644 --- a/drivers/media/platform/msm/camera_v2/camera/camera.c +++ b/drivers/media/platform/msm/camera_v2/camera/camera.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -486,6 +486,9 @@ static long camera_v4l2_vidioc_private_ioctl(struct file *filep, void *fh, if (WARN_ON(!k_ioctl || !pvdev)) return -EIO; + if (cmd != VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD) + return -EINVAL; + switch (k_ioctl->id) { case MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF: { struct msm_camera_return_buf ptr, *tmp = NULL; diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c index a0f1d5148c94..9cdcabb762c0 100644 --- a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c +++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c @@ -342,8 +342,14 @@ static void cam_smmu_check_vaddr_in_range(int idx, void *vaddr) mapping->ion_fd); } } - pr_err("Cannot find vaddr:%pK in SMMU. %s uses invalid virtual address\n", - vaddr, iommu_cb_set.cb_info[idx].name); + if (!strcmp(iommu_cb_set.cb_info[idx].name, "vfe")) + pr_err_ratelimited("Cannot find vaddr:%pK in SMMU.\n" + " %s uses invalid virtual address\n", + vaddr, iommu_cb_set.cb_info[idx].name); + else + pr_err("Cannot find vaddr:%pK in SMMU.\n" + " %s uses invalid virtual address\n", + vaddr, iommu_cb_set.cb_info[idx].name); return; } @@ -894,8 +900,13 @@ static int cam_smmu_attach_sec_cpp(int idx) rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_SECURE, MSM_CAMERA_TZ_HW_BLOCK_CPP); if (rc != 0) { - pr_err("fail to set secure mode for cpp, rc %d", rc); - return rc; + pr_err("secure mode TA notification for cpp unsuccessful, rc %d\n", + rc); + /* + * Although the TA notification failed, the flow should proceed + * without returning an error as at this point cpp had already + * entered the secure mode. + */ } iommu_cb_set.cb_info[idx].state = CAM_SMMU_ATTACH; @@ -910,8 +921,13 @@ static int cam_smmu_detach_sec_cpp(int idx) rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_NON_SECURE, MSM_CAMERA_TZ_HW_BLOCK_CPP); if (rc != 0) { - pr_err("fail to switch to non secure mode for cpp, rc %d", rc); - return rc; + pr_err("secure mode TA notification for cpp unsuccessful, rc %d\n", + rc); + /* + * Although the TA notification failed, the flow should proceed + * without returning an error, as at this point cpp is in secure + * mode and should be switched to non-secure regardless + */ } iommu_cb_set.cb_info[idx].state = CAM_SMMU_DETACH; @@ -950,8 +966,13 @@ static int cam_smmu_attach_sec_vfe_ns_stats(int idx) rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_SECURE, MSM_CAMERA_TZ_HW_BLOCK_ISP); if (rc != 0) { - pr_err("fail to set secure mode for vfe, rc %d", rc); - return rc; + pr_err("secure mode TA notification for vfe unsuccessful, rc %d\n", + rc); + /* + * Although the TA notification failed, the flow should proceed + * without returning an error as at this point vfe had already + * entered the secure mode + */ } return 0; @@ -964,8 +985,13 @@ static int cam_smmu_detach_sec_vfe_ns_stats(int idx) rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_NON_SECURE, MSM_CAMERA_TZ_HW_BLOCK_ISP); if (rc != 0) { - pr_err("fail to switch to non secure mode for vfe, rc %d", rc); - return rc; + pr_err("secure mode TA notification for vfe unsuccessful, rc %d\n", + rc); + /* + * Although the TA notification failed, the flow should proceed + * without returning an error, as at this point vfe is in secure + * mode and should be switched to non-secure regardless + */ } /* diff --git a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c index 34fffa8dd7ce..a0c606ce9a29 100644 --- a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c +++ b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -376,18 +376,17 @@ int msm_camera_clk_enable(struct device *dev, if (clk_rate == 0) { clk_rate = clk_round_rate(clk_ptr[i], 0); - if (clk_rate < 0) { + if (clk_rate <= 0) { pr_err("%s round rate failed\n", clk_info[i].clk_name); goto cam_clk_set_err; } - rc = clk_set_rate(clk_ptr[i], - clk_rate); - if (rc < 0) { - pr_err("%s set rate failed\n", - clk_info[i].clk_name); - goto cam_clk_set_err; - } + } + rc = clk_set_rate(clk_ptr[i], clk_rate); + if (rc < 0) { + pr_err("%s set rate failed\n", + clk_info[i].clk_name); + goto cam_clk_set_err; } } rc = clk_prepare_enable(clk_ptr[i]); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c index 8d66232dbda1..5dbed80f5b85 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c @@ -229,8 +229,8 @@ static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr, mapped_info->paddr += accu_length; accu_length += qbuf_buf->planes[i].length; - CDBG("%s: plane: %d addr:%lu\n", - __func__, i, (unsigned long)mapped_info->paddr); + CDBG("%s: plane: %d addr:%pK\n", + __func__, i, (void *)mapped_info->paddr); } buf_info->num_planes = qbuf_buf->num_planes; @@ -312,8 +312,8 @@ static int msm_isp_map_buf(struct msm_isp_buf_mgr *buf_mgr, pr_err_ratelimited("%s: cannot map address", __func__); goto smmu_map_error; } - CDBG("%s: addr:%lu\n", - __func__, (unsigned long)mapped_info->paddr); + CDBG("%s: addr:%pK\n", + __func__, (void *)mapped_info->paddr); return rc; smmu_map_error: @@ -1282,6 +1282,7 @@ static int msm_isp_deinit_isp_buf_mgr( int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr, unsigned int cmd, void *arg) { + int rc = -EINVAL; switch (cmd) { case VIDIOC_MSM_ISP_REQUEST_BUF: { struct msm_isp_buf_request *buf_req = arg; @@ -1290,7 +1291,7 @@ int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr, memcpy(&buf_req_ver2, buf_req, sizeof(struct msm_isp_buf_request)); buf_req_ver2.security_mode = NON_SECURE_MODE; - buf_mgr->ops->request_buf(buf_mgr, &buf_req_ver2); + rc = buf_mgr->ops->request_buf(buf_mgr, &buf_req_ver2); memcpy(buf_req, &buf_req_ver2, sizeof(struct msm_isp_buf_request)); break; @@ -1298,35 +1299,35 @@ int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr, case VIDIOC_MSM_ISP_REQUEST_BUF_VER2: { struct msm_isp_buf_request_ver2 *buf_req_ver2 = arg; - buf_mgr->ops->request_buf(buf_mgr, buf_req_ver2); + rc = buf_mgr->ops->request_buf(buf_mgr, buf_req_ver2); break; } case VIDIOC_MSM_ISP_ENQUEUE_BUF: { struct msm_isp_qbuf_info *qbuf_info = arg; - buf_mgr->ops->enqueue_buf(buf_mgr, qbuf_info); + rc = buf_mgr->ops->enqueue_buf(buf_mgr, qbuf_info); break; } case VIDIOC_MSM_ISP_DEQUEUE_BUF: { struct msm_isp_qbuf_info *qbuf_info = arg; - buf_mgr->ops->dequeue_buf(buf_mgr, qbuf_info); + rc = buf_mgr->ops->dequeue_buf(buf_mgr, qbuf_info); break; } case VIDIOC_MSM_ISP_RELEASE_BUF: { struct msm_isp_buf_request *buf_req = arg; - buf_mgr->ops->release_buf(buf_mgr, buf_req->handle); + rc = buf_mgr->ops->release_buf(buf_mgr, buf_req->handle); break; } case VIDIOC_MSM_ISP_UNMAP_BUF: { struct msm_isp_unmap_buf_req *unmap_req = arg; - buf_mgr->ops->unmap_buf(buf_mgr, unmap_req->fd); + rc = buf_mgr->ops->unmap_buf(buf_mgr, unmap_req->fd); break; } } - return 0; + return rc; } static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr, @@ -1335,14 +1336,15 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr, struct msm_isp_buffer *bufs = NULL; uint32_t i = 0, j = 0, k = 0, rc = 0; char *print_buf = NULL, temp_buf[100]; - uint32_t start_addr = 0, end_addr = 0, print_buf_size = 2000; + uint32_t print_buf_size = 2000; + unsigned long start_addr = 0, end_addr = 0; int buf_addr_delta = -1; int temp_delta = 0; uint32_t debug_stream_id = 0; uint32_t debug_buf_idx = 0; uint32_t debug_buf_plane = 0; - uint32_t debug_start_addr = 0; - uint32_t debug_end_addr = 0; + unsigned long debug_start_addr = 0; + unsigned long debug_end_addr = 0; uint32_t debug_frame_id = 0; enum msm_isp_buffer_state debug_state = MSM_ISP_BUFFER_STATE_UNUSED; unsigned long flags; @@ -1401,8 +1403,8 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr, debug_stream_id, debug_frame_id); pr_err("%s: nearby buf index %d, plane %d, state %d\n", __func__, debug_buf_idx, debug_buf_plane, debug_state); - pr_err("%s: buf address 0x%x -- 0x%x\n", __func__, - debug_start_addr, debug_end_addr); + pr_err("%s: buf address %pK -- %pK\n", __func__, + (void *)debug_start_addr, (void *)debug_end_addr); if (BUF_DEBUG_FULL) { print_buf = kzalloc(print_buf_size, GFP_ATOMIC); @@ -1437,9 +1439,10 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr, mapped_info[k].len; snprintf(temp_buf, sizeof(temp_buf), - " buf %d plane %d start_addr %x end_addr %x\n", - j, k, start_addr, - end_addr); + " buf %d plane %d start_addr %pK end_addr %pK\n", + j, k, + (void *)start_addr, + (void *)end_addr); strlcat(print_buf, temp_buf, print_buf_size); } diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c index bb3f0dca9d92..f2f3388b41c1 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c @@ -590,9 +590,9 @@ int vfe_hw_probe(struct platform_device *pdev) (struct msm_vfe_hardware_info *) match_dev->data; /* Cx ipeak support */ if (of_find_property(pdev->dev.of_node, - "qcom,vfe_cx_ipeak", NULL)) { + "qcom,vfe-cx-ipeak", NULL)) { vfe_dev->vfe_cx_ipeak = cx_ipeak_register( - pdev->dev.of_node, "qcom,vfe_cx_ipeak"); + pdev->dev.of_node, "qcom,vfe-cx-ipeak"); } } else { vfe_dev->hw_info = (struct msm_vfe_hardware_info *) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h index e54342e3935a..45c2dd588bc9 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -162,6 +162,8 @@ struct msm_vfe_irq_ops { void (*config_irq)(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, enum msm_isp_irq_operation); + void (*preprocess_camif_irq)(struct vfe_device *vfe_dev, + uint32_t irq_status0); }; struct msm_vfe_axi_ops { @@ -282,7 +284,7 @@ struct msm_vfe_stats_ops { void (*update_ping_pong_addr)(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, - uint32_t pingpong_status, dma_addr_t paddr); + uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size); uint32_t (*get_frame_id)(struct vfe_device *vfe_dev); uint32_t (*get_wm_mask)(uint32_t irq_status0, uint32_t irq_status1); @@ -494,6 +496,7 @@ struct msm_vfe_src_info { struct timeval time_stamp; enum msm_vfe_dual_hw_type dual_hw_type; struct msm_vfe_dual_hw_ms_info dual_hw_ms_info; + bool accept_frame; }; struct msm_vfe_fetch_engine_info { @@ -767,8 +770,8 @@ struct vfe_device { struct msm_cam_clk_info *hvx_clk_info; size_t num_hvx_clk; size_t num_norm_clk; + bool hvx_clk_state; enum cam_ahb_clk_vote ahb_vote; - bool turbo_vote; struct cx_ipeak_client *vfe_cx_ipeak; /* Sync variables*/ @@ -809,6 +812,7 @@ struct vfe_device { uint32_t is_split; uint32_t dual_vfe_enable; unsigned long page_fault_addr; + uint32_t vfe_hw_limit; /* Debug variables */ int dump_reg; @@ -830,6 +834,8 @@ struct vfe_device { uint32_t recovery_irq1_mask; /* Store the buf_idx for pd stats RDI stream */ uint8_t pd_buf_idx; + /* total bandwidth per vfe */ + uint64_t total_bandwidth; }; struct vfe_parent_device { diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c index bf18fc59585c..caf69af25601 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1305,7 +1305,7 @@ static void msm_vfe32_stats_enable_module(struct vfe_device *vfe_dev, static void msm_vfe32_stats_update_ping_pong_addr(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status, - dma_addr_t paddr) + dma_addr_t paddr, uint32_t buf_sz) { void __iomem *vfe_base = vfe_dev->vfe_base; int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c index 8e549c338bdd..f5533fd9062e 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2114,7 +2114,7 @@ static void msm_vfe40_stats_enable_module(struct vfe_device *vfe_dev, static void msm_vfe40_stats_update_ping_pong_addr( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, - uint32_t pingpong_status, dma_addr_t paddr) + uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz) { void __iomem *vfe_base = vfe_dev->vfe_base; int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, @@ -2218,6 +2218,7 @@ struct msm_vfe_hardware_info vfe40_hw_info = { .process_stats_irq = msm_isp_process_stats_irq, .process_epoch_irq = msm_vfe40_process_epoch_irq, .config_irq = msm_vfe40_config_irq, + .preprocess_camif_irq = msm_isp47_preprocess_camif_irq, }, .axi_ops = { .reload_wm = msm_vfe40_axi_reload_wm, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c index 957cbc292be3..c85bf1655b8c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1720,7 +1720,7 @@ static void msm_vfe44_stats_update_cgc_override(struct vfe_device *vfe_dev, static void msm_vfe44_stats_update_ping_pong_addr( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, - uint32_t pingpong_status, dma_addr_t paddr) + uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz) { void __iomem *vfe_base = vfe_dev->vfe_base; int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, @@ -1825,6 +1825,7 @@ struct msm_vfe_hardware_info vfe44_hw_info = { .process_stats_irq = msm_isp_process_stats_irq, .process_epoch_irq = msm_vfe44_process_epoch_irq, .config_irq = msm_vfe44_config_irq, + .preprocess_camif_irq = msm_isp47_preprocess_camif_irq, }, .axi_ops = { .reload_wm = msm_vfe44_axi_reload_wm, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c index cc768db875db..72ce32940c29 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1798,7 +1798,7 @@ static void msm_vfe46_stats_enable_module(struct vfe_device *vfe_dev, static void msm_vfe46_stats_update_ping_pong_addr( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, - uint32_t pingpong_status, dma_addr_t paddr) + uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz) { void __iomem *vfe_base = vfe_dev->vfe_base; int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, @@ -1902,6 +1902,7 @@ struct msm_vfe_hardware_info vfe46_hw_info = { .process_stats_irq = msm_isp_process_stats_irq, .process_epoch_irq = msm_vfe46_process_epoch_irq, .config_irq = msm_vfe46_config_irq, + .preprocess_camif_irq = msm_isp47_preprocess_camif_irq, }, .axi_ops = { .reload_wm = msm_vfe46_axi_reload_wm, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index d829aefe6c98..9c74695a6e32 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -331,7 +331,6 @@ int msm_vfe47_init_hardware(struct vfe_device *vfe_dev) goto ahb_vote_fail; } vfe_dev->ahb_vote = CAM_AHB_SVS_VOTE; - vfe_dev->turbo_vote = 0; vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = vfe_dev->vfe_base; @@ -387,6 +386,7 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev) vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks( vfe_dev, 0); + msm_vfe47_configure_hvx(vfe_dev, 0); vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); } @@ -489,7 +489,7 @@ void msm_vfe47_process_violation_status( return; } - pr_err("%s: VFE pipeline violation status %d\n", __func__, + pr_err_ratelimited("%s: VFE pipeline violation status %d\n", __func__, violation_status); } @@ -686,6 +686,15 @@ void msm_vfe47_process_epoch_irq(struct vfe_device *vfe_dev, } } +void msm_isp47_preprocess_camif_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0) +{ + if (irq_status0 & BIT(1)) + vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = false; + if (irq_status0 & BIT(0)) + vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = true; +} + void msm_vfe47_reg_update(struct vfe_device *vfe_dev, enum msm_vfe_input_src frame_src) { @@ -1509,16 +1518,19 @@ void msm_vfe47_configure_hvx(struct vfe_device *vfe_dev, pr_err("%s: no stream_clk\n", __func__); return; } - rc = msm_camera_clk_enable(&vfe_dev->pdev->dev, vfe_dev->hvx_clk_info, - vfe_dev->hvx_clk, vfe_dev->num_hvx_clk, is_stream_on); - if (rc) { - pr_err("%s: stream_clk enable failed, enable: %u\n", - __func__, - is_stream_on); - return; - } - if (is_stream_on == 1) { + if (is_stream_on) { /* Enable HVX */ + if (!vfe_dev->hvx_clk_state) { + rc = msm_camera_clk_enable(&vfe_dev->pdev->dev, + vfe_dev->hvx_clk_info, vfe_dev->hvx_clk, + vfe_dev->num_hvx_clk, is_stream_on); + if (rc) { + pr_err("%s: stream_clk enable failed\n", + __func__); + return; + } + vfe_dev->hvx_clk_state = true; + } val = msm_camera_io_r(vfe_dev->vfe_base + 0x50); val |= (1 << 3); msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50); @@ -1528,6 +1540,17 @@ void msm_vfe47_configure_hvx(struct vfe_device *vfe_dev, msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50); } else { /* Disable HVX */ + if (!vfe_dev->hvx_clk_state) + return; + rc = msm_camera_clk_enable(&vfe_dev->pdev->dev, + vfe_dev->hvx_clk_info, vfe_dev->hvx_clk, + vfe_dev->num_hvx_clk, is_stream_on); + if (rc) { + pr_err("%s: stream_clk disable failed\n", + __func__); + return; + } + vfe_dev->hvx_clk_state = false; val = msm_camera_io_r(vfe_dev->vfe_base + 0x50); val &= 0xFFFFFFF7; msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50); @@ -1896,7 +1919,7 @@ void msm_vfe47_update_ping_pong_addr( if (buf_size < 0) buf_size = 0; - paddr32_max = (paddr + buf_size) & 0xFFFFFFC0; + paddr32_max = (paddr + buf_size) & 0xFFFFFFE0; msm_camera_io_w(paddr32, vfe_base + VFE47_PING_PONG_BASE(wm_idx, pingpong_bit)); @@ -2384,18 +2407,23 @@ void msm_vfe47_stats_enable_module(struct vfe_device *vfe_dev, void msm_vfe47_stats_update_ping_pong_addr( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, - uint32_t pingpong_status, dma_addr_t paddr) + uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size) { void __iomem *vfe_base = vfe_dev->vfe_base; int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, stream_info); uint32_t paddr32 = (paddr & 0xFFFFFFFF); + uint32_t paddr32_max; int stats_idx; stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]); msm_camera_io_w(paddr32, vfe_base + VFE47_STATS_PING_PONG_BASE(stats_idx, pingpong_status)); + + paddr32_max = (paddr + buf_size) & 0xFFFFFFE0; + msm_camera_io_w(paddr32_max, vfe_base + + VFE47_STATS_PING_PONG_BASE(stats_idx, pingpong_status) + 0x4); } uint32_t msm_vfe47_stats_get_wm_mask( @@ -2529,6 +2557,7 @@ int msm_vfe47_get_clks(struct vfe_device *vfe_dev) vfe_dev->hvx_clk_info = &vfe_dev->vfe_clk_info[vfe_dev->num_clk-1]; vfe_dev->hvx_clk = &vfe_dev->vfe_clk[vfe_dev->num_clk-1]; + vfe_dev->hvx_clk_state = false; } for (i = 0; i < vfe_dev->num_clk; i++) { @@ -2554,8 +2583,8 @@ void msm_vfe47_put_clks(struct vfe_device *vfe_dev) int msm_vfe47_enable_clks(struct vfe_device *vfe_dev, int enable) { return msm_camera_clk_enable(&vfe_dev->pdev->dev, - vfe_dev->vfe_clk_info, - vfe_dev->vfe_clk, vfe_dev->num_norm_clk, enable); + vfe_dev->vfe_clk_info, + vfe_dev->vfe_clk, vfe_dev->num_norm_clk, enable); } int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate) @@ -2563,31 +2592,53 @@ int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate) int rc = 0; int clk_idx = vfe_dev->hw_info->vfe_clk_idx; int ret; + long clk_rate, prev_clk_rate; + + clk_rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate); + if (vfe_dev->msm_isp_vfe_clk_rate == clk_rate) + return rc; + prev_clk_rate = vfe_dev->msm_isp_vfe_clk_rate; + vfe_dev->msm_isp_vfe_clk_rate = clk_rate; + /* + * if cx_ipeak is supported vote first so that dsp throttling is + * reduced before we go to turbo + */ + if ((vfe_dev->vfe_cx_ipeak) && + (vfe_dev->msm_isp_vfe_clk_rate >= + vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL] + [vfe_dev->hw_info->vfe_clk_idx]) && + prev_clk_rate < + vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL] + [vfe_dev->hw_info->vfe_clk_idx]) { + ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, true); + if (ret) { + pr_err("%s: cx_ipeak_update failed %d\n", + __func__, ret); + return ret; + } + } + /*set vfe clock*/ rc = msm_camera_clk_set_rate(&vfe_dev->pdev->dev, vfe_dev->vfe_clk[clk_idx], *rate); if (rc < 0) return rc; - *rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate); - vfe_dev->msm_isp_vfe_clk_rate = *rate; - if (vfe_dev->vfe_cx_ipeak) { - if (vfe_dev->msm_isp_vfe_clk_rate >= - vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_TURBO] - [vfe_dev->hw_info->vfe_clk_idx] && - vfe_dev->turbo_vote == 0) { - ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, true); - if (ret) - pr_debug("%s: cx_ipeak_update failed %d\n", - __func__, ret); - else - vfe_dev->turbo_vote = 1; - } else if (vfe_dev->turbo_vote == 1) { - ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, false); - if (ret) - pr_debug("%s: cx_ipeak_update failed %d\n", - __func__, ret); - else - vfe_dev->turbo_vote = 0; + /* + * if cx_ipeak is supported remove the vote for non-turbo clock and + * if voting done earlier + */ + if ((vfe_dev->vfe_cx_ipeak) && + (vfe_dev->msm_isp_vfe_clk_rate < + vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL] + [vfe_dev->hw_info->vfe_clk_idx]) && + prev_clk_rate >= + vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL] + [vfe_dev->hw_info->vfe_clk_idx]) { + ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, false); + if (ret) { + pr_err("%s: cx_ipeak_update failed %d\n", + __func__, ret); + return ret; } } if (vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg) @@ -2742,6 +2793,8 @@ int msm_vfe47_enable_regulators(struct vfe_device *vfe_dev, int enable) int msm_vfe47_get_platform_data(struct vfe_device *vfe_dev) { int rc = 0; + void __iomem *vfe_fuse_base; + uint32_t vfe_fuse_base_size; vfe_dev->vfe_base = msm_camera_get_reg_base(vfe_dev->pdev, "vfe", 0); if (!vfe_dev->vfe_base) @@ -2766,7 +2819,18 @@ int msm_vfe47_get_platform_data(struct vfe_device *vfe_dev) rc = -ENOMEM; goto get_res_fail; } - + vfe_dev->vfe_hw_limit = 0; + vfe_fuse_base = msm_camera_get_reg_base(vfe_dev->pdev, + "vfe_fuse", 0); + vfe_fuse_base_size = msm_camera_get_res_size(vfe_dev->pdev, + "vfe_fuse"); + if (vfe_fuse_base) { + if (vfe_fuse_base_size) + vfe_dev->vfe_hw_limit = + (msm_camera_io_r(vfe_fuse_base) >> 5) & 0x1; + msm_camera_put_reg_base(vfe_dev->pdev, vfe_fuse_base, + "vfe_fuse", 0); + } rc = vfe_dev->hw_info->vfe_ops.platform_ops.get_regulators(vfe_dev); if (rc) goto get_regulator_fail; @@ -2881,6 +2945,7 @@ struct msm_vfe_hardware_info vfe47_hw_info = { .process_epoch_irq = msm_vfe47_process_epoch_irq, .config_irq = msm_vfe47_config_irq, .read_irq_status = msm_vfe47_read_irq_status, + .preprocess_camif_irq = msm_isp47_preprocess_camif_irq, }, .axi_ops = { .reload_wm = msm_vfe47_axi_reload_wm, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h index 22a1a21bce9a..60afe3a80091 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014,2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -42,6 +42,8 @@ void msm_vfe47_process_reg_update(struct vfe_device *vfe_dev, void msm_vfe47_process_epoch_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, struct msm_isp_timestamp *ts); +void msm_isp47_preprocess_camif_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0); void msm_vfe47_reg_update(struct vfe_device *vfe_dev, enum msm_vfe_input_src frame_src); long msm_vfe47_reset_hardware(struct vfe_device *vfe_dev, @@ -148,7 +150,7 @@ void msm_vfe47_stats_enable_module(struct vfe_device *vfe_dev, uint32_t stats_mask, uint8_t enable); void msm_vfe47_stats_update_ping_pong_addr( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, - uint32_t pingpong_status, dma_addr_t paddr); + uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size); uint32_t msm_vfe47_stats_get_wm_mask( uint32_t irq_status0, uint32_t irq_status1); uint32_t msm_vfe47_stats_get_comp_mask( diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c index 5b4d6aa63055..e3d8ecb410ff 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c @@ -237,6 +237,7 @@ static int msm_vfe48_get_clks(struct vfe_device *vfe_dev) vfe_dev->hvx_clk_info = &vfe_dev->vfe_clk_info[vfe_dev->num_clk-1]; vfe_dev->hvx_clk = &vfe_dev->vfe_clk[vfe_dev->num_clk-1]; + vfe_dev->hvx_clk_state = false; } for (i = 0; i < vfe_dev->num_clk; i++) { @@ -368,6 +369,7 @@ struct msm_vfe_hardware_info vfe48_hw_info = { .process_stats_irq = msm_isp_process_stats_irq, .process_epoch_irq = msm_vfe47_process_epoch_irq, .config_irq = msm_vfe47_config_irq, + .preprocess_camif_irq = msm_isp47_preprocess_camif_irq, }, .axi_ops = { .reload_wm = msm_vfe47_axi_reload_wm, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 4e74851dc67d..b44b7573e0e6 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -948,6 +948,8 @@ static void msm_isp_update_pd_stats_idx(struct vfe_device *vfe_dev, uint32_t pingpong_status = 0, pingpong_bit = 0; struct msm_isp_buffer *done_buf = NULL; int vfe_idx = -1; + /* initialize pd_buf_idx with an invalid index 0xF */ + vfe_dev->pd_buf_idx = 0xF; if (frame_src < VFE_RAW_0 || frame_src > VFE_RAW_2) return; @@ -1171,7 +1173,7 @@ void msm_isp_get_avtimer_ts( rc = avcs_core_query_timer(&avtimer_tick); if (rc < 0) { - pr_err("%s: Error: Invalid AVTimer Tick, rc=%d\n", + pr_err_ratelimited("%s: Error: Invalid AVTimer Tick, rc=%d\n", __func__, rc); /* In case of error return zero AVTimer Tick Value */ time_stamp->vt_time.tv_sec = 0; @@ -1734,7 +1736,7 @@ int msm_isp_print_ping_pong_address(struct vfe_device *vfe_dev, } temp = buf->mapped_info[0].paddr + buf->mapped_info[0].len; - pr_err("%s: stream %x ping bit %d uses buffer %pa-%pa, num_isp %d\n", + pr_err("%s: stream %x ping bit %d uses buffer %pK-%pK, num_isp %d\n", __func__, stream_info->stream_src, pingpong_bit, &buf->mapped_info[0].paddr, &temp, @@ -1743,10 +1745,10 @@ int msm_isp_print_ping_pong_address(struct vfe_device *vfe_dev, for (i = 0; i < stream_info->num_planes; i++) { for (k = 0; k < stream_info->num_isp; k++) { pr_debug( - "%s: stream_id %x ping-pong %d plane %d start_addr %lu addr_offset %x len %zx stride %d scanline %d\n" + "%s: stream_id %x ping-pong %d plane %d start_addr %pK addr_offset %x len %zx stride %d scanline %d\n" , __func__, stream_info->stream_id, - pingpong_bit, i, (unsigned long) - buf->mapped_info[i].paddr, + pingpong_bit, i, + (void *)buf->mapped_info[i].paddr, stream_info-> plane_cfg[k][i].plane_addr_offset, buf->mapped_info[i].len, @@ -2382,7 +2384,6 @@ static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev) struct msm_vfe_axi_stream *stream_info; uint64_t total_pix_bandwidth = 0, total_rdi_bandwidth = 0; uint32_t num_pix_streams = 0; - uint64_t total_bandwidth = 0; int vfe_idx; for (i = 0; i < VFE_AXI_SRC_MAX; i++) { @@ -2401,10 +2402,10 @@ static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev) } } } - total_bandwidth = total_pix_bandwidth + total_rdi_bandwidth; + vfe_dev->total_bandwidth = total_pix_bandwidth + total_rdi_bandwidth; rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id, - (total_bandwidth + vfe_dev->hw_info->min_ab), - (total_bandwidth + vfe_dev->hw_info->min_ib)); + (vfe_dev->total_bandwidth + vfe_dev->hw_info->min_ab), + (vfe_dev->total_bandwidth + vfe_dev->hw_info->min_ib)); if (rc < 0) pr_err("%s: update failed\n", __func__); @@ -2412,6 +2413,66 @@ static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev) return rc; } +int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg) +{ + int i, rc = 0; + uint64_t total_bandwidth = 0; + int vfe_idx; + unsigned long flags; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_dual_lpm_mode *ab_ib_vote = NULL; + + ab_ib_vote = (struct msm_vfe_dual_lpm_mode *)arg; + if (!ab_ib_vote) { + pr_err("%s: ab_ib_vote is NULL !!!\n", __func__); + rc = -1; + return rc; + } + if (ab_ib_vote->lpm_mode) { + for (i = 0; i < ab_ib_vote->num_src; i++) { + stream_info = + msm_isp_get_stream_common_data(vfe_dev, + ab_ib_vote->stream_src[i]); + spin_lock_irqsave(&stream_info->lock, flags); + if (stream_info->state == ACTIVE) { + vfe_idx = + msm_isp_get_vfe_idx_for_stream(vfe_dev, + stream_info); + total_bandwidth += + stream_info->bandwidth[ + vfe_idx]; + stream_info->state = PAUSED; + } + spin_unlock_irqrestore(&stream_info->lock, flags); + } + vfe_dev->total_bandwidth -= total_bandwidth; + rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id, + (vfe_dev->total_bandwidth - vfe_dev->hw_info->min_ab), + (vfe_dev->total_bandwidth - vfe_dev->hw_info->min_ib)); + } else { + for (i = 0; i < ab_ib_vote->num_src; i++) { + stream_info = + msm_isp_get_stream_common_data(vfe_dev, + ab_ib_vote->stream_src[i]); + spin_lock_irqsave(&stream_info->lock, flags); + if (stream_info->state == PAUSED) { + vfe_idx = + msm_isp_get_vfe_idx_for_stream(vfe_dev, + stream_info); + total_bandwidth += + stream_info->bandwidth[ + vfe_idx]; + stream_info->state = ACTIVE; + } + spin_unlock_irqrestore(&stream_info->lock, flags); + } + vfe_dev->total_bandwidth += total_bandwidth; + rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id, + (vfe_dev->total_bandwidth + vfe_dev->hw_info->min_ab), + (vfe_dev->total_bandwidth + vfe_dev->hw_info->min_ib)); + } + return rc; +} static int msm_isp_init_stream_ping_pong_reg( struct msm_vfe_axi_stream *stream_info) { @@ -2752,6 +2813,7 @@ static int __msm_isp_check_stream_state(struct msm_vfe_axi_stream *stream_info, case RESUMING: case RESUME_PENDING: case ACTIVE: + case PAUSED: if (cmd != 0) return -EALREADY; break; @@ -2818,9 +2880,11 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev, * those state transitions instead of directly forcing stream to * be INACTIVE */ - while (stream_info->state != ACTIVE) - __msm_isp_axi_stream_update(stream_info, + if (stream_info->state != PAUSED) { + while (stream_info->state != ACTIVE) + __msm_isp_axi_stream_update(stream_info, ×tamp); + } msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG); msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG); stream_info->undelivered_request_cnt = 0; @@ -2833,8 +2897,15 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev, vfe_dev->hw_info->vfe_ops.axi_ops. clear_wm_irq_mask(vfe_dev, stream_info); } - init_completion(&stream_info->inactive_comp); - stream_info->state = STOP_PENDING; + if (stream_info->state == ACTIVE) { + init_completion(&stream_info->inactive_comp); + stream_info->state = STOP_PENDING; + } else if (stream_info->state == PAUSED) { + /* don't wait for reg update */ + stream_info->state = STOP_PENDING; + msm_isp_axi_stream_enable_cfg(stream_info); + stream_info->state = INACTIVE; + } spin_unlock_irqrestore(&stream_info->lock, flags); } @@ -3262,6 +3333,7 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, int k; uint32_t wm_mask = 0; int vfe_idx; + uint32_t pingpong_bit = 0; if (!vfe_dev || !stream_info) { pr_err("%s %d failed: vfe_dev %pK stream_info %pK\n", __func__, @@ -3280,27 +3352,40 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, } frame_src = SRC_TO_INTF(stream_info->stream_src); + pingpong_status = vfe_dev->hw_info-> + vfe_ops.axi_ops.get_pingpong_status(vfe_dev); /* * If PIX stream is active then RDI path uses SOF frame ID of PIX * In case of standalone RDI streaming, SOF are used from * individual intf. */ - if (((vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id <= - vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id)) || - ((!vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id <= - vfe_dev->axi_data.src_info[frame_src].frame_id)) || - stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) { - pr_debug("%s:%d invalid request_frame %d cur frame id %d pix %d\n", + /* + * If frame_id = 1 then no eof check is needed + */ + if (vfe_dev->axi_data.src_info[VFE_PIX_0].active && + vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame == false) { + pr_debug("%s:%d invalid time to request frame %d\n", + __func__, __LINE__, frame_id); + goto error; + } + if ((vfe_dev->axi_data.src_info[VFE_PIX_0].active && (frame_id != + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id + vfe_dev-> + axi_data.src_info[VFE_PIX_0].sof_counter_step)) || + ((!vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id != + vfe_dev->axi_data.src_info[frame_src].frame_id + vfe_dev-> + axi_data.src_info[frame_src].sof_counter_step))) { + pr_debug("%s:%d invalid frame id %d cur frame id %d pix %d\n", __func__, __LINE__, frame_id, vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id, vfe_dev->axi_data.src_info[VFE_PIX_0].active); - - rc = msm_isp_return_empty_buffer(vfe_dev, stream_info, - user_stream_id, frame_id, buf_index, frame_src); - if (rc < 0) - pr_err("%s:%d failed: return_empty_buffer src %d\n", - __func__, __LINE__, frame_src); - return 0; + goto error; + } + if (stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) { + pr_debug("%s:%d invalid undelivered_request_cnt %d frame id %d\n", + __func__, __LINE__, + stream_info->undelivered_request_cnt, + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id); + goto error; } if ((frame_src == VFE_PIX_0) && !stream_info->undelivered_request_cnt && MSM_VFE_STREAM_STOP_PERIOD != @@ -3322,6 +3407,25 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, } spin_lock_irqsave(&stream_info->lock, flags); + vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); + if (stream_info->undelivered_request_cnt == 1) { + pingpong_status = + vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status( + vfe_dev); + pingpong_bit = ((pingpong_status >> + stream_info->wm[vfe_idx][0]) & 0x1); + if (stream_info->sw_ping_pong_bit == !pingpong_bit) { + ISP_DBG("%s:Return Empty Buffer stream id 0x%X\n", + __func__, stream_info->stream_id); + rc = msm_isp_return_empty_buffer(vfe_dev, stream_info, + user_stream_id, frame_id, buf_index, + frame_src); + spin_unlock_irqrestore(&stream_info->lock, + flags); + return 0; + } + } + queue_req = &stream_info->request_queue_cmd[stream_info->request_q_idx]; if (queue_req->cmd_used) { spin_unlock_irqrestore(&stream_info->lock, flags); @@ -3351,7 +3455,6 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, stream_info->request_q_cnt++; stream_info->undelivered_request_cnt++; - vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); stream_cfg_cmd.axi_stream_handle = stream_info->stream_handle[vfe_idx]; stream_cfg_cmd.frame_skip_pattern = NO_SKIP; stream_cfg_cmd.init_frame_drop = 0; @@ -3380,9 +3483,6 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, } stream_info->sw_ping_pong_bit = 0; } else if (stream_info->undelivered_request_cnt == 2) { - pingpong_status = - vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status( - vfe_dev); rc = msm_isp_cfg_ping_pong_address( stream_info, pingpong_status); if (rc) { @@ -3408,6 +3508,14 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, spin_unlock_irqrestore(&stream_info->lock, flags); return rc; +error: + rc = msm_isp_return_empty_buffer(vfe_dev, stream_info, + user_stream_id, frame_id, buf_index, frame_src); + if (rc < 0) + pr_err("%s:%d failed: return_empty_buffer src %d\n", + __func__, __LINE__, frame_src); + return 0; + } static int msm_isp_add_buf_queue(struct vfe_device *vfe_dev, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h index f9ae5fb74281..65009cb22286 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -160,4 +160,6 @@ static inline struct msm_vfe_axi_stream *msm_isp_vfe_get_stream( int msm_isp_cfg_offline_ping_pong_address(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status, uint32_t buf_idx); +int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, + void *arg); #endif /* __MSM_ISP_AXI_UTIL_H__ */ diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index f40af6e95272..f2cf4d477b3f 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c @@ -23,7 +23,8 @@ static inline void msm_isp_stats_cfg_wm_scratch(struct vfe_device *vfe_dev, { vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr( vfe_dev, stream_info, - pingpong_status, vfe_dev->buf_mgr->scratch_buf_stats_addr); + pingpong_status, vfe_dev->buf_mgr->scratch_buf_stats_addr, + SZ_32M); } static inline void msm_isp_stats_cfg_stream_scratch( @@ -123,7 +124,8 @@ static int msm_isp_stats_cfg_ping_pong_address( vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr( vfe_dev, stream_info, pingpong_status, buf->mapped_info[0].paddr + - stream_info->buffer_offset[k]); + stream_info->buffer_offset[k], + buf->mapped_info[0].len); } stream_info->buf[pingpong_bit] = buf; buf->pingpong_bit = pingpong_bit; @@ -832,6 +834,12 @@ static int msm_isp_stats_update_cgc_override(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info; int k; + if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) { + pr_err("%s invalid num_streams %d\n", __func__, + stream_cfg_cmd->num_streams); + return -EINVAL; + } + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); @@ -961,6 +969,11 @@ static int msm_isp_check_stream_cfg_cmd(struct vfe_device *vfe_dev, int vfe_idx; uint32_t stats_idx[MSM_ISP_STATS_MAX]; + if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) { + pr_err("%s invalid num_streams %d\n", __func__, + stream_cfg_cmd->num_streams); + return -EINVAL; + } memset(stats_idx, 0, sizeof(stats_idx)); for (i = 0; i < stream_cfg_cmd->num_streams; i++) { idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); @@ -1074,7 +1087,7 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0}; uint32_t num_stats_comp_mask = 0; struct msm_vfe_stats_stream *stream_info; - struct msm_vfe_stats_shared_data *stats_data; + struct msm_vfe_stats_shared_data *stats_data = NULL; int num_stream = 0; struct msm_vfe_stats_stream *streams[MSM_ISP_STATS_MAX]; struct msm_isp_timestamp timestamp; @@ -1136,10 +1149,12 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, comp_stats_mask[stream_info->composite_flag-1] |= 1 << idx; - ISP_DBG("%s: stats_mask %x %x active streams %d\n", + ISP_DBG("%s: stats_mask %x %x\n", __func__, comp_stats_mask[0], - comp_stats_mask[1], - stats_data->num_active_stream); + comp_stats_mask[1]); + if (stats_data) + ISP_DBG("%s: active_streams = %d\n", __func__, + stats_data->num_active_stream); streams[num_stream++] = stream_info; } @@ -1231,6 +1246,12 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg) int vfe_idx; int k; + if (update_cmd->num_streams > MSM_ISP_STATS_MAX) { + pr_err("%s: Invalid num_streams %d\n", + __func__, update_cmd->num_streams); + return -EINVAL; + } + /*validate request*/ for (i = 0; i < update_cmd->num_streams; i++) { update_info = (struct msm_vfe_axi_stream_cfg_update_info *) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index 86b500973538..f4e81c02208c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -951,6 +951,11 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd, rc = msm_isp_dual_hw_master_slave_sync(vfe_dev, arg); mutex_unlock(&vfe_dev->core_mutex); break; + case VIDIOC_MSM_ISP_DUAL_HW_LPM_MODE: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_ab_ib_update_lpm_mode(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; case VIDIOC_MSM_ISP_FETCH_ENG_START: case VIDIOC_MSM_ISP_MAP_BUF_START_FE: mutex_lock(&vfe_dev->core_mutex); @@ -1423,6 +1428,20 @@ static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev, vfe_dev->vfe_ub_policy = *cfg_data; break; } + case GET_VFE_HW_LIMIT: { + uint32_t *hw_limit = NULL; + + if (cmd_len < sizeof(uint32_t)) { + pr_err("%s:%d failed: invalid cmd len %u exp %zu\n", + __func__, __LINE__, cmd_len, + sizeof(uint32_t)); + return -EINVAL; + } + + hw_limit = (uint32_t *)cfg_data; + *hw_limit = vfe_dev->vfe_hw_limit; + break; + } } return 0; } @@ -2073,7 +2092,10 @@ irqreturn_t msm_isp_process_irq(int irq_num, void *data) __func__, vfe_dev->pdev->id); return IRQ_HANDLED; } - + if (vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq) { + vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq( + vfe_dev, irq_status0); + } if (msm_isp_process_overflow_irq(vfe_dev, &irq_status0, &irq_status1, 0)) { /* if overflow initiated no need to handle the interrupts */ @@ -2120,7 +2142,7 @@ void msm_isp_do_tasklet(unsigned long data) while (atomic_read(&vfe_dev->irq_cnt)) { spin_lock_irqsave(&vfe_dev->tasklet_lock, flags); - queue_cmd = list_first_entry(&vfe_dev->tasklet_q, + queue_cmd = list_first_entry_or_null(&vfe_dev->tasklet_q, struct msm_vfe_tasklet_queue_cmd, list); if (!queue_cmd) { atomic_set(&vfe_dev->irq_cnt, 0); diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index 1628c098622f..41d8ef577a27 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -73,7 +73,7 @@ static void msm_ispif_io_dump_reg(struct ispif_device *ispif) static inline int msm_ispif_is_intf_valid(uint32_t csid_version, - uint8_t intf_type) + enum msm_ispif_vfe_intf intf_type) { return ((csid_version <= CSID_VERSION_V22 && intf_type != VFE0) || (intf_type >= VFE_MAX)) ? false : true; @@ -1662,7 +1662,7 @@ static long msm_ispif_subdev_fops_ioctl(struct file *file, unsigned int cmd, static int ispif_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct ispif_device *ispif = v4l2_get_subdevdata(sd); - int rc; + int rc = 0; mutex_lock(&ispif->mutex); if (0 == ispif->open_cnt) { diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c index de27e585f63d..06e3ee4c353b 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -261,7 +261,7 @@ static int msm_jpeg_init_dev(struct platform_device *pdev) goto fail_4; } - platform_set_drvdata(pdev, &msm_jpeg_device_p); + platform_set_drvdata(pdev, msm_jpeg_device_p); JPEG_DBG("%s %s%d: success\n", __func__, MSM_JPEG_NAME, pdev->id); diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c index 88d90d0a7c08..994ba93907e3 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c @@ -1759,7 +1759,7 @@ void msm_jpegdma_hw_put(struct msm_jpegdma_device *dma) */ static int msm_jpegdma_hw_attach_iommu(struct msm_jpegdma_device *dma) { - int ret; + int ret = -EINVAL; mutex_lock(&dma->lock); diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index 08aab077eec7..8402e31364b9 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c @@ -980,6 +980,7 @@ static int cpp_init_hardware(struct cpp_device *cpp_dev) { int rc = 0; uint32_t vbif_version; + cpp_dev->turbo_vote = 0; rc = msm_camera_regulator_enable(cpp_dev->cpp_vdd, cpp_dev->num_reg, true); @@ -1432,6 +1433,14 @@ static int cpp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return -ENODEV; } + if (cpp_dev->turbo_vote == 1) { + rc = cx_ipeak_update(cpp_dev->cpp_cx_ipeak, false); + if (rc) + pr_err("cx_ipeak_update failed"); + else + cpp_dev->turbo_vote = 0; + } + cpp_dev->cpp_open_cnt--; if (cpp_dev->cpp_open_cnt == 0) { pr_debug("irq_status: 0x%x\n", @@ -2955,6 +2964,38 @@ static int msm_cpp_validate_input(unsigned int cmd, void *arg, return 0; } +unsigned long cpp_cx_ipeak_update(struct cpp_device *cpp_dev, + unsigned long clock, int idx) +{ + unsigned long clock_rate = 0; + int ret = 0; + + if ((clock >= cpp_dev->hw_info.freq_tbl + [(cpp_dev->hw_info.freq_tbl_count) - 1]) && + (cpp_dev->turbo_vote == 0)) { + ret = cx_ipeak_update(cpp_dev->cpp_cx_ipeak, true); + if (ret) { + pr_err("cx_ipeak voting failed setting clock below turbo"); + clock = cpp_dev->hw_info.freq_tbl + [(cpp_dev->hw_info.freq_tbl_count) - 2]; + } else { + cpp_dev->turbo_vote = 1; + } + clock_rate = msm_cpp_set_core_clk(cpp_dev, clock, idx); + } else if (clock < cpp_dev->hw_info.freq_tbl + [(cpp_dev->hw_info.freq_tbl_count) - 1]) { + clock_rate = msm_cpp_set_core_clk(cpp_dev, clock, idx); + if (cpp_dev->turbo_vote == 1) { + ret = cx_ipeak_update(cpp_dev->cpp_cx_ipeak, false); + if (ret) + pr_err("cx_ipeak unvoting failed"); + else + cpp_dev->turbo_vote = 0; + } + } + return clock_rate; +} + long msm_cpp_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { @@ -3337,9 +3378,15 @@ STREAM_BUFF_END: mutex_unlock(&cpp_dev->mutex); return -EINVAL; } - clock_rate = msm_cpp_set_core_clk(cpp_dev, - clock_settings.clock_rate, - msm_cpp_core_clk_idx); + if (cpp_dev->cpp_cx_ipeak) { + clock_rate = cpp_cx_ipeak_update(cpp_dev, + clock_settings.clock_rate, + msm_cpp_core_clk_idx); + } else { + clock_rate = msm_cpp_set_core_clk(cpp_dev, + clock_settings.clock_rate, + msm_cpp_core_clk_idx); + } if (rc < 0) { pr_err("Fail to set core clk\n"); mutex_unlock(&cpp_dev->mutex); @@ -4391,6 +4438,15 @@ static int cpp_probe(struct platform_device *pdev) } } + if (of_find_property(pdev->dev.of_node, "qcom,cpp-cx-ipeak", NULL)) { + cpp_dev->cpp_cx_ipeak = cx_ipeak_register( + pdev->dev.of_node, "qcom,cpp-cx-ipeak"); + if (cpp_dev->cpp_cx_ipeak) + CPP_DBG("Cx ipeak Registration Successful "); + else + pr_err("Cx ipeak Registration Unsuccessful"); + } + rc = msm_camera_get_reset_info(pdev, &cpp_dev->micro_iface_reset); if (rc < 0) { diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h index e69b9d633a1f..a05448091e42 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -24,6 +24,7 @@ #include "cam_soc_api.h" #include "cam_hw_ops.h" #include <media/msmb_pproc.h> +#include <soc/qcom/cx_ipeak.h> /* hw version info: 31:28 Major version @@ -284,6 +285,8 @@ struct cpp_device { uint32_t micro_reset; struct msm_cpp_payload_params payload_params; struct msm_cpp_vbif_data *vbif_data; + bool turbo_vote; + struct cx_ipeak_client *cpp_cx_ipeak; }; int msm_cpp_set_micro_clk(struct cpp_device *cpp_dev); diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c index be266641a105..214dd6f3406d 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c @@ -1069,7 +1069,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev) if (rc < 0) { pr_err("%s:%d csiphy config_vreg failed\n", __func__, __LINE__); - goto csiphy_resource_fail; + goto csiphy_vreg_config_fail; } rc = msm_camera_enable_vreg(&csiphy_dev->pdev->dev, csiphy_dev->csiphy_vreg, @@ -1089,7 +1089,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev) if (rc < 0) { pr_err("%s: csiphy clk enable failed\n", __func__); csiphy_dev->ref_count--; - goto csiphy_resource_fail; + goto csiphy_enable_clk_fail; } CDBG("%s:%d called\n", __func__, __LINE__); @@ -1117,12 +1117,17 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev) csiphy_dev->csiphy_state = CSIPHY_POWER_UP; return 0; +csiphy_enable_clk_fail: + msm_camera_enable_vreg(&csiphy_dev->pdev->dev, + csiphy_dev->csiphy_vreg, + csiphy_dev->regulator_count, NULL, 0, + &csiphy_dev->csiphy_reg_ptr[0], 0); top_vreg_enable_failed: msm_camera_config_vreg(&csiphy_dev->pdev->dev, csiphy_dev->csiphy_vreg, csiphy_dev->regulator_count, NULL, 0, &csiphy_dev->csiphy_reg_ptr[0], 0); -csiphy_resource_fail: +csiphy_vreg_config_fail: if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY, CAM_AHB_SUSPEND_VOTE) < 0) pr_err("%s: failed to vote for AHB\n", __func__); @@ -1169,7 +1174,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev) if (rc < 0) { pr_err("%s:%d csiphy config_vreg failed\n", __func__, __LINE__); - goto csiphy_resource_fail; + goto csiphy_vreg_config_fail; } rc = msm_camera_enable_vreg(&csiphy_dev->pdev->dev, csiphy_dev->csiphy_vreg, @@ -1189,7 +1194,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev) if (rc < 0) { pr_err("%s: csiphy clk enable failed\n", __func__); csiphy_dev->ref_count--; - goto csiphy_resource_fail; + goto csiphy_enable_clk_fail; } CDBG("%s:%d clk enable success\n", __func__, __LINE__); @@ -1213,12 +1218,18 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev) csiphy_dev->hw_version); csiphy_dev->csiphy_state = CSIPHY_POWER_UP; return 0; + +csiphy_enable_clk_fail: + msm_camera_enable_vreg(&csiphy_dev->pdev->dev, + csiphy_dev->csiphy_vreg, + csiphy_dev->regulator_count, NULL, 0, + &csiphy_dev->csiphy_reg_ptr[0], 0); top_vreg_enable_failed: msm_camera_config_vreg(&csiphy_dev->pdev->dev, csiphy_dev->csiphy_vreg, csiphy_dev->regulator_count, NULL, 0, &csiphy_dev->csiphy_reg_ptr[0], 0); -csiphy_resource_fail: +csiphy_vreg_config_fail: if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY, CAM_AHB_SUSPEND_VOTE) < 0) pr_err("%s: failed to vote for AHB\n", __func__); diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c index ef0ef1512211..2fc3ce406c27 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c @@ -3969,6 +3969,18 @@ static int mpq_dmx_process_video_packet_framing( mpq_dmx_update_decoder_stat(mpq_feed); + if (video_b_frame_events == 1) { + if (non_predicted_video_frame == 0) { + struct dmx_pts_dts_info *pts_dts; + + pts_dts = + &meta_data.info.framing.pts_dts_info; + pts_dts->pts_exist = 0; + pts_dts->pts = 0; + pts_dts->dts_exist = 0; + pts_dts->dts = 0; + } + } /* * Write meta-data that includes the framing information */ @@ -3986,14 +3998,7 @@ static int mpq_dmx_process_video_packet_framing( stream_buffer, &data, ret); /* Trigger ES Data Event for VPTS */ - if (video_b_frame_events == 1) { - if (non_predicted_video_frame == 1) - feed->data_ready_cb.ts - (&feed->feed.ts, &data); - } else { - feed->data_ready_cb.ts(&feed->feed.ts, - &data); - } + feed->data_ready_cb.ts(&feed->feed.ts, &data); if (feed_data->video_buffer->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c index 5c7b4df40d5d..e170c9ffafc7 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c @@ -539,6 +539,7 @@ static void *sde_rotator_get_userptr(void *alloc_ctx, buf->ctx = ctx; buf->rot_dev = rot_dev; if (ctx->secure_camera) { + buf->buffer = NULL; buf->handle = ion_import_dma_buf(iclient, buf->fd); if (IS_ERR_OR_NULL(buf->handle)) { @@ -552,6 +553,7 @@ static void *sde_rotator_get_userptr(void *alloc_ctx, buf->ctx->session_id, buf->fd, &buf->handle); } else { + buf->handle = NULL; buf->buffer = dma_buf_get(buf->fd); if (IS_ERR_OR_NULL(buf->buffer)) { SDEDEV_ERR(rot_dev->dev, @@ -578,6 +580,8 @@ error_buf_get: static void sde_rotator_put_userptr(void *buf_priv) { struct sde_rotator_buf_handle *buf = buf_priv; + struct ion_client *iclient; + struct sde_rotator_device *rot_dev; if (IS_ERR_OR_NULL(buf)) return; @@ -588,6 +592,9 @@ static void sde_rotator_put_userptr(void *buf_priv) return; } + rot_dev = buf->ctx->rot_dev; + iclient = buf->rot_dev->mdata->iclient; + SDEDEV_DBG(buf->rot_dev->dev, "put dmabuf s:%d fd:%d buf:%pad\n", buf->ctx->session_id, buf->fd, &buf->buffer); @@ -597,6 +604,11 @@ static void sde_rotator_put_userptr(void *buf_priv) buf->buffer = NULL; } + if (buf->handle) { + ion_free(iclient, buf->handle); + buf->handle = NULL; + } + kfree(buf_priv); } diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c index 0eaf1960ec27..882e3dcd6277 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c @@ -768,10 +768,17 @@ static int sde_mdp_get_img(struct sde_fb_data *img, len = &data->len; data->flags |= img->flags; data->offset = img->offset; - if (data->flags & SDE_ROT_EXT_DMA_BUF) + + if ((data->flags & SDE_SECURE_CAMERA_SESSION) && + IS_ERR_OR_NULL(img->handle)) { + SDEROT_ERR("error on ion_import_fb\n"); + ret = PTR_ERR(img->handle); + img->handle = NULL; + return ret; + } else if (data->flags & SDE_ROT_EXT_DMA_BUF) { data->srcp_dma_buf = img->buffer; - else if (IS_ERR(data->srcp_dma_buf)) { - SDEROT_ERR("error on ion_import_fd\n"); + } else if (IS_ERR(data->srcp_dma_buf)) { + SDEROT_ERR("error on dma_buf\n"); ret = PTR_ERR(data->srcp_dma_buf); data->srcp_dma_buf = NULL; return ret; @@ -845,8 +852,6 @@ static int sde_mdp_get_img(struct sde_fb_data *img, ret = 0; } while (0); - if (!IS_ERR_OR_NULL(ihandle)) - ion_free(iclient, ihandle); return ret; } diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index b7e2c297a1ad..24eb8fff905b 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -528,7 +528,6 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = { .step = 1, .menu_skip_mask = 0, .qmenu = NULL, - .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, }, { .id = V4L2_CID_MPEG_VIDC_VIDEO_NON_SECURE_OUTPUT2, diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index ce6736509d61..ca9d7fba4ee3 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -468,7 +468,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { }, { .id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, - .name = "I Frame Quantization", + .name = "H264 I Frame Quantization", .type = V4L2_CTRL_TYPE_INTEGER, .minimum = 1, .maximum = 51, @@ -479,7 +479,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { }, { .id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, - .name = "P Frame Quantization", + .name = "H264 P Frame Quantization", .type = V4L2_CTRL_TYPE_INTEGER, .minimum = 1, .maximum = 51, @@ -490,7 +490,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { }, { .id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, - .name = "B Frame Quantization", + .name = "H264 B Frame Quantization", .type = V4L2_CTRL_TYPE_INTEGER, .minimum = 1, .maximum = 51, @@ -500,6 +500,61 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .qmenu = NULL, }, { + .id = V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP, + .name = "H263 I Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 31, + .default_value = I_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP, + .name = "H263 P Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 31, + .default_value = P_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP, + .name = "H263 B Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 31, + .default_value = B_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP, + .name = "VPX I Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = I_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP, + .name = "VPX P Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = P_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP, .name = "H264 Minimum QP", .type = V4L2_CTRL_TYPE_INTEGER, @@ -522,6 +577,24 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .qmenu = NULL, }, { + .id = V4L2_CID_MPEG_VIDEO_VPX_MIN_QP, + .name = "VPX Minimum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = 0, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_MAX_QP, + .name = "VPX Maximum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = 127, + .step = 1, + }, + { .id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP, .name = "VP8 Minimum QP", .type = V4L2_CTRL_TYPE_INTEGER, @@ -540,6 +613,24 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .step = 1, }, { + .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP, + .name = "MPEG4 Minimum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 31, + .default_value = 1, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP, + .name = "MPEG4 Maximum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 31, + .default_value = 31, + .step = 1, + }, + { .id = V4L2_CID_MPEG_VIDEO_MIN_QP_PACKED, .name = "H264 Minimum QP PACKED", .type = V4L2_CTRL_TYPE_INTEGER, @@ -940,6 +1031,36 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .step = 0, }, { + .id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP, + .name = "Iframe initial QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 127, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP, + .name = "Pframe initial QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 127, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP, + .name = "Bframe initial QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 127, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { .id = V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP, .name = "Iframe initial QP", .type = V4L2_CTRL_TYPE_INTEGER, @@ -1122,7 +1243,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .name = "Set frame level QP", .type = V4L2_CTRL_TYPE_INTEGER, .minimum = 1, - .maximum = 51, + .maximum = 127, .default_value = 1, .step = 1, .qmenu = NULL, @@ -2230,6 +2351,9 @@ unknown_value: return -EINVAL; } +static int msm_venc_validate_qp_value(struct msm_vidc_inst *inst, + struct v4l2_ctrl *ctrl); + static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) { int rc = 0; @@ -2649,6 +2773,81 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) pdata = &quantization; break; } + case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: { + struct v4l2_ctrl *qpp, *qpb; + + qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP); + qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpi = ctrl->val; + quantization.qpp = qpp->val; + quantization.qpb = qpb->val; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } + case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: { + struct v4l2_ctrl *qpi, *qpb; + + qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP); + qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpp = ctrl->val; + quantization.qpi = qpi->val; + quantization.qpb = qpb->val; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } + case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: { + struct v4l2_ctrl *qpi, *qpp; + + qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP); + qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpb = ctrl->val; + quantization.qpi = qpi->val; + quantization.qpp = qpp->val; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } + case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: { + struct v4l2_ctrl *qpp; + + qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpi = ctrl->val; + quantization.qpp = qpp->val; + /* Bframes are not supported for VPX */ + quantization.qpb = 0; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } + case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: { + struct v4l2_ctrl *qpi; + + qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpp = ctrl->val; + quantization.qpi = qpi->val; + /* Bframes are not supported for VPX */ + quantization.qpb = 0; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: { struct v4l2_ctrl *qp_max; @@ -2689,8 +2888,85 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) pdata = &qp_range; break; } + case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: { + struct v4l2_ctrl *qp_max; + + qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP); + if (ctrl->val >= qp_max->val) { + dprintk(VIDC_ERR, + "Bad range: Min QP (%d) > Max QP(%d)\n", + ctrl->val, qp_max->val); + rc = -ERANGE; + break; + } + + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = qp_max->val; + qp_range.min_qp = ctrl->val; + + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: { + struct v4l2_ctrl *qp_min; + + qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP); + if (ctrl->val <= qp_min->val) { + dprintk(VIDC_ERR, + "Bad range: Max QP (%d) < Min QP(%d)\n", + ctrl->val, qp_min->val); + rc = -ERANGE; + break; + } + + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = ctrl->val; + qp_range.min_qp = qp_min->val; + + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP: { + struct v4l2_ctrl *qp_max; + + qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MAX_QP); + if (ctrl->val >= qp_max->val) { + dprintk(VIDC_ERR, + "Bad range: Min QP (%d) > Max QP(%d)\n", + ctrl->val, qp_max->val); + rc = -ERANGE; + break; + } + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = qp_max->val; + qp_range.min_qp = ctrl->val; + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: { + struct v4l2_ctrl *qp_min; + + qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MIN_QP); + if (ctrl->val <= qp_min->val) { + dprintk(VIDC_ERR, + "Bad range: Max QP (%d) < Min QP(%d)\n", + ctrl->val, qp_min->val); + rc = -ERANGE; + break; + } + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = ctrl->val; + qp_range.min_qp = qp_min->val; + pdata = &qp_range; + break; + } case V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP: { struct v4l2_ctrl *qp_max; + qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP); property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; qp_range.layer_id = 0; @@ -2701,6 +2977,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) } case V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP: { struct v4l2_ctrl *qp_min; + qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP); property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; qp_range.layer_id = 0; @@ -3222,6 +3499,14 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) pdata = &baselayerid; break; case V4L2_CID_MPEG_VIDC_VIDEO_CONFIG_QP: + /* Sanity check for the QP boundaries as we are using + * same control to set dynamic QP for all the codecs + */ + rc = msm_venc_validate_qp_value(inst, ctrl); + if (rc) { + dprintk(VIDC_ERR, "Invalid QP Config QP Range\n"); + break; + } property_id = HAL_CONFIG_VENC_FRAME_QP; frameqp = ctrl->val; pdata = &frameqp; @@ -3380,7 +3665,6 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) } v4l2_ctrl_lock(ctrl); -#undef TRY_GET_CTRL if (!rc && property_id) { dprintk(VIDC_DBG, "Control: HAL property=%x,ctrl_value=%d\n", @@ -3393,6 +3677,59 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) return rc; } +static int msm_venc_validate_qp_value(struct msm_vidc_inst *inst, + struct v4l2_ctrl *ctrl) +{ + int rc = 0, min, max; + struct v4l2_ctrl *temp_ctrl = NULL; + int qp_value = ctrl->val; + +#define VALIDATE_BOUNDARIES(__min, __max, __val) ({\ + int __rc = __val >= __min && \ + __val <= __max; \ + if (!__rc) \ + dprintk(VIDC_ERR, "QP beyond range: min(%d) max(%d) val(%d)", \ + __min, __max, __val); \ + __rc; \ +}) + + switch (inst->fmts[CAPTURE_PORT].fourcc) { + case V4L2_PIX_FMT_VP8: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MAX_QP); + max = temp_ctrl->maximum; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MIN_QP); + min = temp_ctrl->minimum; + if (!VALIDATE_BOUNDARIES(min, max, qp_value)) + rc = -EINVAL; + break; + case V4L2_PIX_FMT_H263: + case V4L2_PIX_FMT_MPEG4: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP); + max = temp_ctrl->maximum; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP); + min = temp_ctrl->minimum; + if (!VALIDATE_BOUNDARIES(min, max, qp_value)) + rc = -EINVAL; + break; + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_HEVC: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MAX_QP); + max = temp_ctrl->maximum; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MIN_QP); + min = temp_ctrl->minimum; + if (!VALIDATE_BOUNDARIES(min, max, qp_value)) + rc = -EINVAL; + break; + default: + dprintk(VIDC_ERR, "%s Invalid Codec\n", __func__); + return -EINVAL; + } + return rc; +#undef VALIDATE_BOUNDARIES +} + +#undef TRY_GET_CTRL + static int try_set_ext_ctrl(struct msm_vidc_inst *inst, struct v4l2_ext_controls *ctrl) { @@ -3408,6 +3745,7 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst, struct hal_aspect_ratio sar; struct hal_bitrate bitrate; struct hal_frame_size blur_res; + struct v4l2_ctrl *temp_ctrl; if (!inst || !inst->core || !inst->core->device || !ctrl) { dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); @@ -3470,6 +3808,48 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst, property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; pdata = &quant; break; + case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP: + /* Sanity check for the QP boundaries as we are using + * same control to set Initial QP for all the codecs + */ + temp_ctrl->id = + V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP; + temp_ctrl->val = control[i].value; + rc = msm_venc_validate_qp_value(inst, temp_ctrl); + if (rc) { + dprintk(VIDC_ERR, "Invalid Initial I QP\n"); + break; + } + quant.qpi = control[i].value; + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + pdata = &quant; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP: + temp_ctrl->id = + V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP; + temp_ctrl->val = control[i].value; + rc = msm_venc_validate_qp_value(inst, temp_ctrl); + if (rc) { + dprintk(VIDC_ERR, "Invalid Initial P QP\n"); + break; + } + quant.qpp = control[i].value; + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + pdata = &quant; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP: + temp_ctrl->id = + V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP; + temp_ctrl->val = control[i].value; + rc = msm_venc_validate_qp_value(inst, temp_ctrl); + if (rc) { + dprintk(VIDC_ERR, "Invalid Initial B QP\n"); + break; + } + quant.qpb = control[i].value; + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + pdata = &quant; + break; case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_X_RANGE: search_range.i_frame.x_subsampled = control[i].value; property_id = HAL_PARAM_VENC_SEARCH_RANGE; diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index 4739fc999c82..bc72c4a56c91 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -4472,7 +4472,7 @@ static int venus_hfi_get_fw_info(void *dev, struct hal_fw_info *fw_info) struct venus_hfi_device *device = dev; u32 smem_block_size = 0; u8 *smem_table_ptr; - char version[VENUS_VERSION_LENGTH]; + char version[VENUS_VERSION_LENGTH] = ""; const u32 smem_image_index_venus = 14 * 128; if (!device || !fw_info) { diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 0f301903aa6f..63165d324fff 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -263,6 +263,8 @@ static void ite_set_carrier_params(struct ite_dev *dev) if (allowance > ITE_RXDCR_MAX) allowance = ITE_RXDCR_MAX; + + use_demodulator = true; } } diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index c945e4c2fbd4..ec30a004f319 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -200,22 +200,30 @@ static int smsusb_start_streaming(struct smsusb_device_t *dev) static int smsusb_sendrequest(void *context, void *buffer, size_t size) { struct smsusb_device_t *dev = (struct smsusb_device_t *) context; - struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) buffer; - int dummy; + struct sms_msg_hdr *phdr; + int dummy, ret; if (dev->state != SMSUSB_ACTIVE) { pr_debug("Device not active yet\n"); return -ENOENT; } + phdr = kmalloc(size, GFP_KERNEL); + if (!phdr) + return -ENOMEM; + memcpy(phdr, buffer, size); + pr_debug("sending %s(%d) size: %d\n", smscore_translate_msg(phdr->msg_type), phdr->msg_type, phdr->msg_length); smsendian_handle_tx_message((struct sms_msg_data *) phdr); - smsendian_handle_message_header((struct sms_msg_hdr *)buffer); - return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), - buffer, size, &dummy, 1000); + smsendian_handle_message_header((struct sms_msg_hdr *)phdr); + ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), + phdr, size, &dummy, 1000); + + kfree(phdr); + return ret; } static char *smsusb1_fw_lkup[] = { diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index cfb868a48b5f..ff6feff21e94 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -416,7 +416,7 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, nextbuf = NULL; spin_unlock_irqrestore(&queue->irqlock, flags); - buf->state = buf->error ? VB2_BUF_STATE_ERROR : UVC_BUF_STATE_DONE; + buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE; vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused); vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ca092ef75cfe..12ffa4192605 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -532,6 +532,14 @@ config HDCP_QSEECOM Environment (QSEE) via the QSEECOM Driver and also calls the APIs exposed by the HDMI driver to communicate with the Receiver. +config PROFILER + tristate "Qualcomm Technologies, Inc. trustzone Communicator driver" + help + Provides a communication interface between userspace and + trustzone using Secure Channel Manager (SCM) interface. + It exposes APIs for userspace to get system profiling + information. + config VEXPRESS_SYSCFG bool "Versatile Express System Configuration driver" depends on VEXPRESS_CONFIG diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 40b7e3b603e2..c2941afc961e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_SRAM) += sram.o obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_QSEECOM) += qseecom.o +obj-$(CONFIG_PROFILER) += profiler.o obj-$(CONFIG_HDCP_QSEECOM) += hdcp.o ifdef CONFIG_COMPAT obj-$(CONFIG_QSEECOM) += compat_qseecom.o diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index e59838231703..be74a25708b2 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -399,7 +399,7 @@ bool mei_cldev_enabled(struct mei_cl_device *cldev) EXPORT_SYMBOL_GPL(mei_cldev_enabled); /** - * mei_cldev_enable_device - enable me client device + * mei_cldev_enable - enable me client device * create connection with me client * * @cldev: me client device diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 958af84884b5..2ff39fbc70d1 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -698,7 +698,7 @@ void mei_host_client_init(struct work_struct *work) pm_runtime_mark_last_busy(dev->dev); dev_dbg(dev->dev, "rpm: autosuspend\n"); - pm_runtime_autosuspend(dev->dev); + pm_request_autosuspend(dev->dev); } /** diff --git a/drivers/misc/profiler.c b/drivers/misc/profiler.c new file mode 100644 index 000000000000..a2887fcefbab --- /dev/null +++ b/drivers/misc/profiler.c @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "PROFILER: %s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/platform_device.h> +#include <linux/debugfs.h> +#include <linux/cdev.h> +#include <linux/uaccess.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/io.h> +#include <linux/types.h> +#include <soc/qcom/scm.h> +#include <soc/qcom/socinfo.h> +#include <asm/cacheflush.h> +#include <linux/delay.h> +#include <soc/qcom/profiler.h> + +#include <linux/compat.h> + +#define PROFILER_DEV "profiler" + +static struct class *driver_class; +static dev_t profiler_device_no; + +struct profiler_control { + struct device *pdev; + struct cdev cdev; +}; + +static struct profiler_control profiler; + +struct profiler_dev_handle { + bool released; + int abort; + atomic_t ioctl_count; +}; + + +static int profiler_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id, + const void *req_buf, void *resp_buf) +{ + int ret = 0; + uint32_t qseos_cmd_id = 0; + struct scm_desc desc = {0}; + + if (!req_buf || !resp_buf) { + pr_err("Invalid buffer pointer\n"); + return -EINVAL; + } + qseos_cmd_id = *(uint32_t *)req_buf; + + switch (svc_id) { + + case SCM_SVC_BW: + switch (qseos_cmd_id) { + case TZ_BW_SVC_START_ID: + case TZ_BW_SVC_GET_ID: + case TZ_BW_SVC_STOP_ID: + /* Send the command to TZ */ + desc.arginfo = SCM_ARGS(4, SCM_RW, SCM_VAL, + SCM_RW, SCM_VAL); + desc.args[0] = virt_to_phys(& + (((struct tz_bw_svc_buf *) + req_buf)->bwreq)); + desc.args[1] = ((struct tz_bw_svc_buf *) + req_buf)->req_size; + desc.args[2] = virt_to_phys(& + ((struct tz_bw_svc_buf *) + req_buf)->bwresp); + desc.args[3] = sizeof(struct tz_bw_svc_resp); + + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO, + TZ_SVC_BW_PROF_ID), &desc); + break; + default: + pr_err("cmd_id %d is not supported by scm_call2.\n", + qseos_cmd_id); + ret = -EINVAL; + } /*end of switch (qsee_cmd_id) */ + break; + default: + pr_err("svc_id 0x%x is not supported by armv8 scm_call2.\n", + svc_id); + ret = -EINVAL; + break; + } /*end of switch svc_id */ + return ret; +} + + +static int profiler_scm_call(u32 svc_id, u32 tz_cmd_id, const void *cmd_buf, + size_t cmd_len, void *resp_buf, size_t resp_len) +{ + if (!is_scm_armv8()) + return scm_call(svc_id, tz_cmd_id, cmd_buf, cmd_len, + resp_buf, resp_len); + else + return profiler_scm_call2(svc_id, tz_cmd_id, cmd_buf, resp_buf); +} + +static int bw_profiling_command(void *req) +{ + struct tz_bw_svc_resp *bw_resp = NULL; + uint32_t cmd_id = 0; + int ret; + + cmd_id = *(uint32_t *)req; + bw_resp = &((struct tz_bw_svc_buf *)req)->bwresp; + /* Flush buffers from cache to memory. */ + dmac_flush_range(req, req + + PAGE_ALIGN(sizeof(union tz_bw_svc_req))); + dmac_flush_range((void *)bw_resp, ((void *)bw_resp) + + sizeof(struct tz_bw_svc_resp)); + ret = profiler_scm_call(SCM_SVC_BW, TZ_SVC_BW_PROF_ID, req, + sizeof(struct tz_bw_svc_buf), + bw_resp, sizeof(struct tz_bw_svc_resp)); + if (ret) { + pr_err("profiler_scm_call failed with err: %d\n", ret); + return -EINVAL; + } + /* Invalidate cache. */ + dmac_inv_range((void *)bw_resp, ((void *)bw_resp) + + sizeof(struct tz_bw_svc_resp)); + /* Verify cmd id and Check that request succeeded.*/ + if ((bw_resp->status != E_BW_SUCCESS) || + (cmd_id != bw_resp->cmd_id)) { + ret = -1; + pr_err("Status: %d,Cmd: %d\n", + bw_resp->status, + bw_resp->cmd_id); + } + return ret; +} + +static int bw_profiling_start(struct tz_bw_svc_buf *bwbuf) +{ + struct tz_bw_svc_start_req *bwstartreq = NULL; + + bwstartreq = (struct tz_bw_svc_start_req *) &bwbuf->bwreq; + /* Populate request data */ + bwstartreq->cmd_id = TZ_BW_SVC_START_ID; + bwstartreq->version = TZ_BW_SVC_VERSION; + bwbuf->req_size = sizeof(struct tz_bw_svc_start_req); + return bw_profiling_command(bwbuf); +} + +static int bw_profiling_get(void __user *argp, struct tz_bw_svc_buf *bwbuf) +{ + struct tz_bw_svc_get_req *bwgetreq = NULL; + int ret; + char *buf = NULL; + const int numberofregs = 3; + struct profiler_bw_cntrs_req cnt_buf; + + bwgetreq = (struct tz_bw_svc_get_req *) &bwbuf->bwreq; + /* Allocate memory for get buffer */ + buf = kzalloc(PAGE_ALIGN(numberofregs * sizeof(uint32_t)), GFP_KERNEL); + if (buf == NULL) { + ret = -ENOMEM; + pr_err(" Failed to allocate memory\n"); + return ret; + } + /* Populate request data */ + bwgetreq->cmd_id = TZ_BW_SVC_GET_ID; + bwgetreq->buf_ptr = (uint64_t) virt_to_phys(buf); + bwgetreq->buf_size = numberofregs * sizeof(uint32_t); + bwbuf->req_size = sizeof(struct tz_bw_svc_get_req); + dmac_flush_range(buf, ((void *)buf) + PAGE_ALIGN(bwgetreq->buf_size)); + ret = bw_profiling_command(bwbuf); + if (ret) { + pr_err("bw_profiling_command failed\n"); + return ret; + } + dmac_inv_range(buf, ((void *)buf) + PAGE_ALIGN(bwgetreq->buf_size)); + cnt_buf.total = *(uint32_t *) (buf + 0 * sizeof(uint32_t)); + cnt_buf.cpu = *(uint32_t *) (buf + 1 * sizeof(uint32_t)); + cnt_buf.gpu = *(uint32_t *) (buf + 2 * sizeof(uint32_t)); + if (copy_to_user(argp, &cnt_buf, sizeof(struct profiler_bw_cntrs_req))) + pr_err("copy_to_user failed\n"); + /* Free memory for response */ + if (buf != NULL) { + kfree(buf); + buf = NULL; + } + return ret; +} + +static int bw_profiling_stop(struct tz_bw_svc_buf *bwbuf) +{ + struct tz_bw_svc_stop_req *bwstopreq = NULL; + + bwstopreq = (struct tz_bw_svc_stop_req *) &bwbuf->bwreq; + /* Populate request data */ + bwstopreq->cmd_id = TZ_BW_SVC_STOP_ID; + bwbuf->req_size = sizeof(struct tz_bw_svc_stop_req); + return bw_profiling_command(bwbuf); +} + + +static int profiler_get_bw_info(void __user *argp) +{ + int ret = 0; + struct tz_bw_svc_buf *bwbuf = NULL; + struct profiler_bw_cntrs_req cnt_buf; + + ret = copy_from_user(&cnt_buf, argp, + sizeof(struct profiler_bw_cntrs_req)); + if (ret) + return ret; + /* Allocate memory for request */ + bwbuf = kzalloc(PAGE_ALIGN(sizeof(struct tz_bw_svc_buf)), GFP_KERNEL); + if (bwbuf == NULL) + return -ENOMEM; + switch (cnt_buf.cmd) { + case TZ_BW_SVC_START_ID: + ret = bw_profiling_start(bwbuf); + if (ret) + pr_err("bw_profiling_start Failed with ret: %d\n", ret); + break; + case TZ_BW_SVC_GET_ID: + ret = bw_profiling_get(argp, bwbuf); + if (ret) + pr_err("bw_profiling_get Failed with ret: %d\n", ret); + break; + case TZ_BW_SVC_STOP_ID: + ret = bw_profiling_stop(bwbuf); + if (ret) + pr_err("bw_profiling_stop Failed with ret: %d\n", ret); + break; + default: + pr_err("Invalid IOCTL: 0x%x\n", cnt_buf.cmd); + ret = -EINVAL; + } + /* Free memory for command */ + if (bwbuf != NULL) { + kfree(bwbuf); + bwbuf = NULL; + } + return ret; +} + +static int profiler_open(struct inode *inode, struct file *file) +{ + int ret = 0; + struct profiler_dev_handle *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + file->private_data = data; + data->abort = 0; + data->released = false; + atomic_set(&data->ioctl_count, 0); + return ret; +} + +static int compat_get_profiler_bw_info( + struct compat_profiler_bw_cntrs_req __user *data32, + struct profiler_bw_cntrs_req __user *data) +{ + compat_uint_t total; + compat_uint_t cpu; + compat_uint_t gpu; + compat_uint_t cmd; + int err; + + err = get_user(total, &data32->total); + err |= put_user(total, &data->total); + err |= get_user(gpu, &data32->gpu); + err |= put_user(gpu, &data->gpu); + err |= get_user(cpu, &data32->cpu); + err |= put_user(cpu, &data->cpu); + err |= get_user(cmd, &data32->cmd); + err |= put_user(cmd, &data->cmd); + return err; +} + +static int compat_put_profiler_bw_info( + struct compat_profiler_bw_cntrs_req __user *data32, + struct profiler_bw_cntrs_req __user *data) +{ + compat_uint_t total; + compat_uint_t cpu; + compat_uint_t gpu; + compat_uint_t cmd; + int err; + + err = get_user(total, &data->total); + err |= put_user(total, &data32->total); + err |= get_user(gpu, &data->gpu); + err |= put_user(gpu, &data32->gpu); + err |= get_user(cpu, &data->cpu); + err |= put_user(cpu, &data32->cpu); + err |= get_user(cmd, &data->cmd); + err |= put_user(cmd, &data32->cmd); + return err; +} + +static unsigned int convert_cmd(unsigned int cmd) +{ + switch (cmd) { + case COMPAT_PROFILER_IOCTL_GET_BW_INFO: + return PROFILER_IOCTL_GET_BW_INFO; + + default: + return cmd; + } +} + + +long profiler_ioctl(struct file *file, unsigned cmd, unsigned long arg) +{ + int ret = 0; + struct profiler_dev_handle *data = file->private_data; + void __user *argp = (void __user *) arg; + + if (!data) { + pr_err("Invalid/uninitialized device handle\n"); + return -EINVAL; + } + + if (data->abort) { + pr_err("Aborting profiler driver\n"); + return -ENODEV; + } + + switch (cmd) { + case PROFILER_IOCTL_GET_BW_INFO: + atomic_inc(&data->ioctl_count); + ret = profiler_get_bw_info(argp); + if (ret) + pr_err("failed get system bandwidth info: %d\n", ret); + atomic_dec(&data->ioctl_count); + break; + default: + pr_err("Invalid IOCTL: 0x%x\n", cmd); + return -EINVAL; + } + return ret; +} + +long compat_profiler_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + long ret; + + switch (cmd) { + case COMPAT_PROFILER_IOCTL_GET_BW_INFO:{ + struct compat_profiler_bw_cntrs_req __user *data32; + struct profiler_bw_cntrs_req __user *data; + int err; + + data32 = compat_ptr(arg); + data = compat_alloc_user_space(sizeof(*data)); + if (data == NULL) + return -EFAULT; + err = compat_get_profiler_bw_info(data32, data); + if (err) + return err; + ret = profiler_ioctl(file, convert_cmd(cmd), + (unsigned long)data); + err = compat_put_profiler_bw_info(data32, data); + return ret ? ret : err; + } + default: + return -ENOIOCTLCMD; + } + return 0; +} + + +static int profiler_release(struct inode *inode, struct file *file) +{ + pr_info("profiler release\n"); + return 0; +} + +static const struct file_operations profiler_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = profiler_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = compat_profiler_ioctl, +#endif + .open = profiler_open, + .release = profiler_release +}; + +static int profiler_init(void) +{ + int rc; + struct device *class_dev; + + rc = alloc_chrdev_region(&profiler_device_no, 0, 1, PROFILER_DEV); + if (rc < 0) { + pr_err("alloc_chrdev_region failed %d\n", rc); + return rc; + } + + driver_class = class_create(THIS_MODULE, PROFILER_DEV); + if (IS_ERR(driver_class)) { + rc = -ENOMEM; + pr_err("class_create failed %d\n", rc); + goto exit_unreg_chrdev_region; + } + + class_dev = device_create(driver_class, NULL, profiler_device_no, NULL, + PROFILER_DEV); + if (IS_ERR(class_dev)) { + pr_err("class_device_create failed %d\n", rc); + rc = -ENOMEM; + goto exit_destroy_class; + } + + cdev_init(&profiler.cdev, &profiler_fops); + profiler.cdev.owner = THIS_MODULE; + + rc = cdev_add(&profiler.cdev, MKDEV(MAJOR(profiler_device_no), 0), 1); + if (rc < 0) { + pr_err("%s: cdev_add failed %d\n", __func__, rc); + goto exit_destroy_device; + } + + profiler.pdev = class_dev; + return 0; + +exit_destroy_device: + device_destroy(driver_class, profiler_device_no); +exit_destroy_class: + class_destroy(driver_class); +exit_unreg_chrdev_region: + unregister_chrdev_region(profiler_device_no, 1); + return rc; +} + +static void profiler_exit(void) +{ + pr_info("Exiting from profiler\n"); +} + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. trustzone Communicator"); + +module_init(profiler_init); +module_exit(profiler_exit); diff --git a/drivers/misc/qpnp-misc.c b/drivers/misc/qpnp-misc.c index 5b321b17c43e..c1570e32f749 100644 --- a/drivers/misc/qpnp-misc.c +++ b/drivers/misc/qpnp-misc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014,2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014,2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -14,7 +14,7 @@ #include <linux/module.h> #include <linux/err.h> #include <linux/slab.h> -#include <linux/spmi.h> +#include <linux/regmap.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/qpnp-misc.h> @@ -45,8 +45,7 @@ struct qpnp_misc_version { * exclusion between probing and accessing misc * driver information * @dev: Device pointer to the misc device - * @resource: Resource pointer that holds base address - * @spmi: Spmi pointer which holds spmi information + * @regmap: Regmap pointer to the misc device * @version: struct that holds the subtype and dig_major_rev * of the chip. */ @@ -54,10 +53,10 @@ struct qpnp_misc_dev { struct list_head list; struct mutex mutex; struct device *dev; - struct resource *resource; - struct spmi_device *spmi; + struct regmap *regmap; struct qpnp_misc_version version; + u32 base; u8 pwm_sel; bool enable_gp_driver; }; @@ -83,26 +82,29 @@ static struct qpnp_misc_version irq_support_version[] = { {0x16, 0x00}, /* PMDCALIFORNIUM */ }; -static int qpnp_write_byte(struct spmi_device *spmi, u16 addr, u8 val) +static int qpnp_write_byte(struct qpnp_misc_dev *mdev, u16 addr, u8 val) { int rc; - rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, addr, &val, 1); + rc = regmap_write(mdev->regmap, mdev->base + addr, val); if (rc) - pr_err("SPMI write failed rc=%d\n", rc); + pr_err("regmap write failed rc=%d\n", rc); return rc; } -static int qpnp_read_byte(struct spmi_device *spmi, u16 addr, u8 *val) +static int qpnp_read_byte(struct qpnp_misc_dev *mdev, u16 addr, u8 *val) { + unsigned int temp; int rc; - rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, val, 1); + rc = regmap_read(mdev->regmap, mdev->base + addr, &temp); if (rc) { - pr_err("SPMI read failed rc=%d\n", rc); + pr_err("regmap read failed rc=%d\n", rc); return rc; } + + *val = (u8)temp; return rc; } @@ -128,6 +130,47 @@ static bool __misc_irqs_available(struct qpnp_misc_dev *dev) return 1; } +int qpnp_misc_read_reg(struct device_node *node, u16 addr, u8 *val) +{ + struct qpnp_misc_dev *mdev = NULL; + struct qpnp_misc_dev *mdev_found = NULL; + int rc; + u8 temp; + + if (IS_ERR_OR_NULL(node)) { + pr_err("Invalid device node pointer\n"); + return -EINVAL; + } + + mutex_lock(&qpnp_misc_dev_list_mutex); + list_for_each_entry(mdev, &qpnp_misc_dev_list, list) { + if (mdev->dev->of_node == node) { + mdev_found = mdev; + break; + } + } + mutex_unlock(&qpnp_misc_dev_list_mutex); + + if (!mdev_found) { + /* + * No MISC device was found. This API should only + * be called by drivers which have specified the + * misc phandle in their device tree node. + */ + pr_err("no probed misc device found\n"); + return -EPROBE_DEFER; + } + + rc = qpnp_read_byte(mdev, addr, &temp); + if (rc < 0) { + dev_err(mdev->dev, "Failed to read addr %x, rc=%d\n", addr, rc); + return rc; + } + + *val = temp; + return 0; +} + int qpnp_misc_irqs_available(struct device *consumer_dev) { struct device_node *misc_node = NULL; @@ -168,16 +211,24 @@ int qpnp_misc_irqs_available(struct device *consumer_dev) static int qpnp_misc_dt_init(struct qpnp_misc_dev *mdev) { + struct device_node *node = mdev->dev->of_node; u32 val; + int rc; + + rc = of_property_read_u32(node, "reg", &mdev->base); + if (rc < 0 || !mdev->base) { + dev_err(mdev->dev, "Base address not defined or invalid\n"); + return -EINVAL; + } - if (!of_property_read_u32(mdev->dev->of_node, "qcom,pwm-sel", &val)) { + if (!of_property_read_u32(node, "qcom,pwm-sel", &val)) { if (val > PWM_SEL_MAX) { dev_err(mdev->dev, "Invalid value for pwm-sel\n"); return -EINVAL; } mdev->pwm_sel = (u8)val; } - mdev->enable_gp_driver = of_property_read_bool(mdev->dev->of_node, + mdev->enable_gp_driver = of_property_read_bool(node, "qcom,enable-gp-driver"); WARN((mdev->pwm_sel > 0 && !mdev->enable_gp_driver), @@ -197,18 +248,15 @@ static int qpnp_misc_config(struct qpnp_misc_dev *mdev) switch (version_name) { case PMDCALIFORNIUM: if (mdev->pwm_sel > 0 && mdev->enable_gp_driver) { - rc = qpnp_write_byte(mdev->spmi, - mdev->resource->start + REG_PWM_SEL, - mdev->pwm_sel); + rc = qpnp_write_byte(mdev, REG_PWM_SEL, mdev->pwm_sel); if (rc < 0) { dev_err(mdev->dev, "Failed to write PWM_SEL reg\n"); return rc; } - rc = qpnp_write_byte(mdev->spmi, - mdev->resource->start + REG_GP_DRIVER_EN, - GP_DRIVER_EN_BIT); + rc = qpnp_write_byte(mdev, REG_GP_DRIVER_EN, + GP_DRIVER_EN_BIT); if (rc < 0) { dev_err(mdev->dev, "Failed to write GP_DRIVER_EN reg\n"); @@ -223,35 +271,38 @@ static int qpnp_misc_config(struct qpnp_misc_dev *mdev) return 0; } -static int qpnp_misc_probe(struct spmi_device *spmi) +static int qpnp_misc_probe(struct platform_device *pdev) { - struct resource *resource; struct qpnp_misc_dev *mdev = ERR_PTR(-EINVAL); int rc; - resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0); - if (!resource) { - pr_err("Unable to get spmi resource for MISC\n"); - return -EINVAL; - } - - mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL); if (!mdev) return -ENOMEM; - mdev->spmi = spmi; - mdev->dev = &(spmi->dev); - mdev->resource = resource; + mdev->dev = &pdev->dev; + mdev->regmap = dev_get_regmap(mdev->dev->parent, NULL); + if (!mdev->regmap) { + dev_err(mdev->dev, "Parent regmap is unavailable\n"); + return -ENXIO; + } + + rc = qpnp_misc_dt_init(mdev); + if (rc < 0) { + dev_err(mdev->dev, + "Error reading device tree properties, rc=%d\n", rc); + return rc; + } + - rc = qpnp_read_byte(spmi, resource->start + REG_SUBTYPE, - &mdev->version.subtype); + rc = qpnp_read_byte(mdev, REG_SUBTYPE, &mdev->version.subtype); if (rc < 0) { dev_err(mdev->dev, "Failed to read subtype, rc=%d\n", rc); return rc; } - rc = qpnp_read_byte(spmi, resource->start + REG_DIG_MAJOR_REV, - &mdev->version.dig_major_rev); + rc = qpnp_read_byte(mdev, REG_DIG_MAJOR_REV, + &mdev->version.dig_major_rev); if (rc < 0) { dev_err(mdev->dev, "Failed to read dig_major_rev, rc=%d\n", rc); return rc; @@ -261,13 +312,6 @@ static int qpnp_misc_probe(struct spmi_device *spmi) list_add_tail(&mdev->list, &qpnp_misc_dev_list); mutex_unlock(&qpnp_misc_dev_list_mutex); - rc = qpnp_misc_dt_init(mdev); - if (rc < 0) { - dev_err(mdev->dev, - "Error reading device tree properties, rc=%d\n", rc); - return rc; - } - rc = qpnp_misc_config(mdev); if (rc < 0) { dev_err(mdev->dev, @@ -279,7 +323,7 @@ static int qpnp_misc_probe(struct spmi_device *spmi) return 0; } -static struct spmi_driver qpnp_misc_driver = { +static struct platform_driver qpnp_misc_driver = { .probe = qpnp_misc_probe, .driver = { .name = QPNP_MISC_DEV_NAME, @@ -290,15 +334,15 @@ static struct spmi_driver qpnp_misc_driver = { static int __init qpnp_misc_init(void) { - return spmi_driver_register(&qpnp_misc_driver); + return platform_driver_register(&qpnp_misc_driver); } static void __exit qpnp_misc_exit(void) { - return spmi_driver_unregister(&qpnp_misc_driver); + return platform_driver_unregister(&qpnp_misc_driver); } -module_init(qpnp_misc_init); +subsys_initcall(qpnp_misc_init); module_exit(qpnp_misc_exit); MODULE_DESCRIPTION(QPNP_MISC_DEV_NAME); diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 78f03fc75761..9855bee67627 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -2333,7 +2333,13 @@ static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp) ret); goto loadapp_err; } - + if (load_img_req.mdt_len > len || load_img_req.img_len > len) { + pr_err("ion len %zu is smaller than mdt_len %u or img_len %u\n", + len, load_img_req.mdt_len, + load_img_req.img_len); + ret = -EINVAL; + goto loadapp_err; + } /* Populate the structure for sending scm call to load image */ if (qseecom.qsee_version < QSEE_VERSION_40) { load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND; @@ -5149,6 +5155,12 @@ static int qseecom_load_external_elf(struct qseecom_dev_handle *data, ret); return ret; } + if (load_img_req.mdt_len > len || load_img_req.img_len > len) { + pr_err("ion len %zu is smaller than mdt_len %u or img_len %u\n", + len, load_img_req.mdt_len, + load_img_req.img_len); + return ret; + } /* Populate the structure for sending scm call to load image */ if (qseecom.qsee_version < QSEE_VERSION_40) { load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND; diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index ce0ecd1e9b7a..5d6c44b00bc2 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -875,6 +875,14 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, cmd.arg = idata->ic.arg; cmd.flags = idata->ic.flags; + if (idata->ic.postsleep_max_us < idata->ic.postsleep_min_us) { + pr_err("%s: min value: %u must not be greater than max value: %u\n", + __func__, idata->ic.postsleep_min_us, + idata->ic.postsleep_max_us); + WARN_ON(1); + return -EPERM; + } + if (idata->buf_bytes) { data.sg = &sg; data.sg_len = 1; diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 9766dcc95602..12c66919f06f 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -791,7 +791,7 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test, struct mmc_async_req *cur_areq = &test_areq[0].areq; struct mmc_async_req *other_areq = &test_areq[1].areq; int i; - int ret; + int ret = RESULT_OK; test_areq[0].test = test; test_areq[1].test = test; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 4567b7526469..542f1733d0dd 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -2032,10 +2032,10 @@ reinit: err = mmc_select_hs400(card); if (err) goto free_card; - } else if (mmc_card_hs(card)) { + } else { /* Select the desired bus width optionally */ err = mmc_select_bus_width(card); - if (!IS_ERR_VALUE(err)) { + if (!IS_ERR_VALUE(err) && mmc_card_hs(card)) { err = mmc_select_hs_ddr(card); if (err) goto free_card; diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 44ecebd1ea8c..c8b8ac66ff7e 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -309,6 +309,9 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host) cmd0 = BF_SSP(cmd->opcode, CMD0_CMD); cmd1 = cmd->arg; + if (cmd->opcode == MMC_STOP_TRANSMISSION) + cmd0 |= BM_SSP_CMD0_APPEND_8CYC; + if (host->sdio_irq_en) { ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK; cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN; @@ -417,8 +420,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) ssp->base + HW_SSP_BLOCK_SIZE); } - if ((cmd->opcode == MMC_STOP_TRANSMISSION) || - (cmd->opcode == SD_IO_RW_EXTENDED)) + if (cmd->opcode == SD_IO_RW_EXTENDED) cmd0 |= BM_SSP_CMD0_APPEND_8CYC; cmd1 = cmd->arg; diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 7274a6d2cce0..3e51c9a86250 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -3304,6 +3304,21 @@ static void sdhci_msm_cmdq_dump_debug_ram(struct sdhci_host *host) pr_err("-------------------------\n"); } +static void sdhci_msm_cache_debug_data(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = pltfm_host->priv; + struct sdhci_msm_debug_data *cached_data = &msm_host->cached_data; + + memcpy(&cached_data->copy_mmc, msm_host->mmc, + sizeof(struct mmc_host)); + if (msm_host->mmc->card) + memcpy(&cached_data->copy_card, msm_host->mmc->card, + sizeof(struct mmc_card)); + memcpy(&cached_data->copy_host, host, + sizeof(struct sdhci_host)); +} + void sdhci_msm_dump_vendor_regs(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -3316,6 +3331,7 @@ void sdhci_msm_dump_vendor_regs(struct sdhci_host *host) u32 debug_reg[MAX_TEST_BUS] = {0}; u32 sts = 0; + sdhci_msm_cache_debug_data(host); pr_info("----------- VENDOR REGISTER DUMP -----------\n"); if (host->cq_host) sdhci_msm_cmdq_dump_debug_ram(host); @@ -3897,8 +3913,8 @@ void sdhci_msm_pm_qos_cpu_init(struct sdhci_host *host, group->req.type = PM_QOS_REQ_AFFINE_CORES; cpumask_copy(&group->req.cpus_affine, &msm_host->pdata->pm_qos_data.cpu_group_map.mask[i]); - /* For initialization phase, set the performance mode latency */ - group->latency = latency[i].latency[SDHCI_PERFORMANCE_MODE]; + /* We set default latency here for all pm_qos cpu groups. */ + group->latency = PM_QOS_DEFAULT_VALUE; pm_qos_add_request(&group->req, PM_QOS_CPU_DMA_LATENCY, group->latency); pr_info("%s (): voted for group #%d (mask=0x%lx) latency=%d (0x%p)\n", diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h index c26636198a22..2e4f2179378e 100644 --- a/drivers/mmc/host/sdhci-msm.h +++ b/drivers/mmc/host/sdhci-msm.h @@ -171,6 +171,12 @@ struct sdhci_msm_ice_data { int state; }; +struct sdhci_msm_debug_data { + struct mmc_host copy_mmc; + struct mmc_card copy_card; + struct sdhci_host copy_host; +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -186,6 +192,7 @@ struct sdhci_msm_host { atomic_t clks_on; /* Set if clocks are enabled */ struct sdhci_msm_pltfm_data *pdata; struct mmc_host *mmc; + struct sdhci_msm_debug_data cached_data; struct sdhci_pltfm_data sdhci_msm_pdata; u32 curr_pwr_state; u32 curr_io_level; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 083cb8cdf1ca..44633dc5d2be 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2507,7 +2507,27 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) ctrl &= ~SDHCI_CTRL_EXEC_TUNING; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + sdhci_do_reset(host, SDHCI_RESET_CMD); + sdhci_do_reset(host, SDHCI_RESET_DATA); + err = -EIO; + + if (cmd.opcode != MMC_SEND_TUNING_BLOCK_HS200) + goto out; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + + spin_unlock_irqrestore(&host->lock, flags); + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = MMC_STOP_TRANSMISSION; + cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + cmd.busy_timeout = 50; + mmc_wait_for_cmd(mmc, &cmd, 0); + + spin_lock_irqsave(&host->lock, flags); + goto out; } @@ -3283,7 +3303,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) pr_err("%s: Card is consuming too much power!\n", mmc_hostname(host->mmc)); - if (intmask & SDHCI_INT_CARD_INT) { + if ((intmask & SDHCI_INT_CARD_INT) && + (host->ier & SDHCI_INT_CARD_INT)) { sdhci_enable_sdio_irq_nolock(host, false); host->thread_isr |= SDHCI_INT_CARD_INT; result = IRQ_WAKE_THREAD; diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index f9fa3fad728e..2051f28ddac6 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -139,15 +139,13 @@ static int __init init_msp_flash(void) } msp_maps[i].bankwidth = 1; - msp_maps[i].name = kmalloc(7, GFP_KERNEL); + msp_maps[i].name = kstrndup(flash_name, 7, GFP_KERNEL); if (!msp_maps[i].name) { iounmap(msp_maps[i].virt); kfree(msp_parts[i]); goto cleanup_loop; } - msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7); - for (j = 0; j < pcnt; j++) { part_name[5] = '0' + i; part_name[7] = '0' + j; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 093fd917274f..132d0d04726c 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -531,7 +531,7 @@ config MTD_NAND_FSMC Flexible Static Memory Controller (FSMC) config MTD_NAND_XWAY - tristate "Support for NAND on Lantiq XWAY SoC" + bool "Support for NAND on Lantiq XWAY SoC" depends on LANTIQ && SOC_TYPE_XWAY select MTD_NAND_PLATFORM help diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c index 7be393c96b1a..cf7c18947189 100644 --- a/drivers/net/can/c_can/c_can_pci.c +++ b/drivers/net/can/c_can/c_can_pci.c @@ -161,6 +161,7 @@ static int c_can_pci_probe(struct pci_dev *pdev, dev->irq = pdev->irq; priv->base = addr; + priv->device = &pdev->dev; if (!c_can_pci_data->freq) { dev_err(&pdev->dev, "no clock frequency defined\n"); diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 680d1ff07a55..6749b1829469 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -948,7 +948,12 @@ static int ti_hecc_probe(struct platform_device *pdev) netif_napi_add(ndev, &priv->napi, ti_hecc_rx_poll, HECC_DEF_NAPI_WEIGHT); - clk_enable(priv->clk); + err = clk_prepare_enable(priv->clk); + if (err) { + dev_err(&pdev->dev, "clk_prepare_enable() failed\n"); + goto probe_exit_clk; + } + err = register_candev(ndev); if (err) { dev_err(&pdev->dev, "register_candev() failed\n"); @@ -981,7 +986,7 @@ static int ti_hecc_remove(struct platform_device *pdev) struct ti_hecc_priv *priv = netdev_priv(ndev); unregister_candev(ndev); - clk_disable(priv->clk); + clk_disable_unprepare(priv->clk); clk_put(priv->clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iounmap(priv->base); @@ -1006,7 +1011,7 @@ static int ti_hecc_suspend(struct platform_device *pdev, pm_message_t state) hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_PDR); priv->can.state = CAN_STATE_SLEEPING; - clk_disable(priv->clk); + clk_disable_unprepare(priv->clk); return 0; } @@ -1015,8 +1020,11 @@ static int ti_hecc_resume(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); struct ti_hecc_priv *priv = netdev_priv(dev); + int err; - clk_enable(priv->clk); + err = clk_prepare_enable(priv->clk); + if (err) + return err; hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_PDR); priv->can.state = CAN_STATE_ERROR_ACTIVE; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index 5a2e341a6d1e..91be4575b524 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -872,23 +872,25 @@ lbl_free_candev: static void peak_usb_disconnect(struct usb_interface *intf) { struct peak_usb_device *dev; + struct peak_usb_device *dev_prev_siblings; /* unregister as many netdev devices as siblings */ - for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) { + for (dev = usb_get_intfdata(intf); dev; dev = dev_prev_siblings) { struct net_device *netdev = dev->netdev; char name[IFNAMSIZ]; + dev_prev_siblings = dev->prev_siblings; dev->state &= ~PCAN_USB_STATE_CONNECTED; strncpy(name, netdev->name, IFNAMSIZ); unregister_netdev(netdev); - free_candev(netdev); kfree(dev->cmd_buf); dev->next_siblings = NULL; if (dev->adapter->dev_free) dev->adapter->dev_free(dev); + free_candev(netdev); dev_info(&intf->dev, "%s removed\n", name); } diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c index a731720f1d13..449b2a47f9a8 100644 --- a/drivers/net/can/usb/usb_8dev.c +++ b/drivers/net/can/usb/usb_8dev.c @@ -954,8 +954,8 @@ static int usb_8dev_probe(struct usb_interface *intf, for (i = 0; i < MAX_TX_URBS; i++) priv->tx_contexts[i].echo_index = MAX_TX_URBS; - priv->cmd_msg_buffer = kzalloc(sizeof(struct usb_8dev_cmd_msg), - GFP_KERNEL); + priv->cmd_msg_buffer = devm_kzalloc(&intf->dev, sizeof(struct usb_8dev_cmd_msg), + GFP_KERNEL); if (!priv->cmd_msg_buffer) goto cleanup_candev; @@ -969,7 +969,7 @@ static int usb_8dev_probe(struct usb_interface *intf, if (err) { netdev_err(netdev, "couldn't register CAN device: %d\n", err); - goto cleanup_cmd_msg_buffer; + goto cleanup_candev; } err = usb_8dev_cmd_version(priv, &version); @@ -990,9 +990,6 @@ static int usb_8dev_probe(struct usb_interface *intf, cleanup_unregister_candev: unregister_netdev(priv->netdev); -cleanup_cmd_msg_buffer: - kfree(priv->cmd_msg_buffer); - cleanup_candev: free_candev(netdev); diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 858106352ce9..8860e74aa28f 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -732,11 +732,8 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, unsigned int c_index, last_c_index, last_tx_cn, num_tx_cbs; unsigned int pkts_compl = 0, bytes_compl = 0; struct bcm_sysport_cb *cb; - struct netdev_queue *txq; u32 hw_ind; - txq = netdev_get_tx_queue(ndev, ring->index); - /* Compute how many descriptors have been processed since last call */ hw_ind = tdma_readl(priv, TDMA_DESC_RING_PROD_CONS_INDEX(ring->index)); c_index = (hw_ind >> RING_CONS_INDEX_SHIFT) & RING_CONS_INDEX_MASK; @@ -767,9 +764,6 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, ring->c_index = c_index; - if (netif_tx_queue_stopped(txq) && pkts_compl) - netif_tx_wake_queue(txq); - netif_dbg(priv, tx_done, ndev, "ring=%d c_index=%d pkts_compl=%d, bytes_compl=%d\n", ring->index, ring->c_index, pkts_compl, bytes_compl); @@ -781,16 +775,33 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, static unsigned int bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, struct bcm_sysport_tx_ring *ring) { + struct netdev_queue *txq; unsigned int released; unsigned long flags; + txq = netdev_get_tx_queue(priv->netdev, ring->index); + spin_lock_irqsave(&ring->lock, flags); released = __bcm_sysport_tx_reclaim(priv, ring); + if (released) + netif_tx_wake_queue(txq); + spin_unlock_irqrestore(&ring->lock, flags); return released; } +/* Locked version of the per-ring TX reclaim, but does not wake the queue */ +static void bcm_sysport_tx_clean(struct bcm_sysport_priv *priv, + struct bcm_sysport_tx_ring *ring) +{ + unsigned long flags; + + spin_lock_irqsave(&ring->lock, flags); + __bcm_sysport_tx_reclaim(priv, ring); + spin_unlock_irqrestore(&ring->lock, flags); +} + static int bcm_sysport_tx_poll(struct napi_struct *napi, int budget) { struct bcm_sysport_tx_ring *ring = @@ -1275,7 +1286,7 @@ static void bcm_sysport_fini_tx_ring(struct bcm_sysport_priv *priv, napi_disable(&ring->napi); netif_napi_del(&ring->napi); - bcm_sysport_tx_reclaim(priv, ring); + bcm_sysport_tx_clean(priv, ring); kfree(ring->cbs); ring->cbs = NULL; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h index bdbd80423b17..9ff2881f933d 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -900,9 +900,7 @@ static inline void dsaf_write_reg(void __iomem *base, u32 reg, u32 value) { - u8 __iomem *reg_addr = ACCESS_ONCE(base); - - writel(value, reg_addr + reg); + writel(value, base + reg); } #define dsaf_write_dev(a, reg, value) \ @@ -910,9 +908,7 @@ static inline void dsaf_write_reg(void __iomem *base, u32 reg, u32 value) static inline u32 dsaf_read_reg(u8 __iomem *base, u32 reg) { - u8 __iomem *reg_addr = ACCESS_ONCE(base); - - return readl(reg_addr + reg); + return readl(base + reg); } #define dsaf_read_dev(a, reg) \ diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index a4beccf1fd46..0e67145bc418 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -772,6 +772,17 @@ struct mvpp2_rx_desc { u32 reserved8; }; +struct mvpp2_txq_pcpu_buf { + /* Transmitted SKB */ + struct sk_buff *skb; + + /* Physical address of transmitted buffer */ + dma_addr_t phys; + + /* Size transmitted */ + size_t size; +}; + /* Per-CPU Tx queue control */ struct mvpp2_txq_pcpu { int cpu; @@ -787,11 +798,8 @@ struct mvpp2_txq_pcpu { /* Number of Tx DMA descriptors reserved for each CPU */ int reserved_num; - /* Array of transmitted skb */ - struct sk_buff **tx_skb; - - /* Array of transmitted buffers' physical addresses */ - dma_addr_t *tx_buffs; + /* Infos about transmitted buffers */ + struct mvpp2_txq_pcpu_buf *buffs; /* Index of last TX DMA descriptor that was inserted */ int txq_put_index; @@ -981,10 +989,11 @@ static void mvpp2_txq_inc_put(struct mvpp2_txq_pcpu *txq_pcpu, struct sk_buff *skb, struct mvpp2_tx_desc *tx_desc) { - txq_pcpu->tx_skb[txq_pcpu->txq_put_index] = skb; - if (skb) - txq_pcpu->tx_buffs[txq_pcpu->txq_put_index] = - tx_desc->buf_phys_addr; + struct mvpp2_txq_pcpu_buf *tx_buf = + txq_pcpu->buffs + txq_pcpu->txq_put_index; + tx_buf->skb = skb; + tx_buf->size = tx_desc->data_size; + tx_buf->phys = tx_desc->buf_phys_addr + tx_desc->packet_offset; txq_pcpu->txq_put_index++; if (txq_pcpu->txq_put_index == txq_pcpu->size) txq_pcpu->txq_put_index = 0; @@ -4403,17 +4412,16 @@ static void mvpp2_txq_bufs_free(struct mvpp2_port *port, int i; for (i = 0; i < num; i++) { - dma_addr_t buf_phys_addr = - txq_pcpu->tx_buffs[txq_pcpu->txq_get_index]; - struct sk_buff *skb = txq_pcpu->tx_skb[txq_pcpu->txq_get_index]; + struct mvpp2_txq_pcpu_buf *tx_buf = + txq_pcpu->buffs + txq_pcpu->txq_get_index; mvpp2_txq_inc_get(txq_pcpu); - dma_unmap_single(port->dev->dev.parent, buf_phys_addr, - skb_headlen(skb), DMA_TO_DEVICE); - if (!skb) + dma_unmap_single(port->dev->dev.parent, tx_buf->phys, + tx_buf->size, DMA_TO_DEVICE); + if (!tx_buf->skb) continue; - dev_kfree_skb_any(skb); + dev_kfree_skb_any(tx_buf->skb); } } @@ -4664,15 +4672,10 @@ static int mvpp2_txq_init(struct mvpp2_port *port, for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); txq_pcpu->size = txq->size; - txq_pcpu->tx_skb = kmalloc(txq_pcpu->size * - sizeof(*txq_pcpu->tx_skb), - GFP_KERNEL); - if (!txq_pcpu->tx_skb) - goto error; - - txq_pcpu->tx_buffs = kmalloc(txq_pcpu->size * - sizeof(dma_addr_t), GFP_KERNEL); - if (!txq_pcpu->tx_buffs) + txq_pcpu->buffs = kmalloc(txq_pcpu->size * + sizeof(struct mvpp2_txq_pcpu_buf), + GFP_KERNEL); + if (!txq_pcpu->buffs) goto error; txq_pcpu->count = 0; @@ -4686,8 +4689,7 @@ static int mvpp2_txq_init(struct mvpp2_port *port, error: for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); - kfree(txq_pcpu->tx_skb); - kfree(txq_pcpu->tx_buffs); + kfree(txq_pcpu->buffs); } dma_free_coherent(port->dev->dev.parent, @@ -4706,8 +4708,7 @@ static void mvpp2_txq_deinit(struct mvpp2_port *port, for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); - kfree(txq_pcpu->tx_skb); - kfree(txq_pcpu->tx_buffs); + kfree(txq_pcpu->buffs); } if (txq->descs) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index bbff8ec6713e..28a4b34310b2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -502,8 +502,11 @@ void mlx4_en_recover_from_oom(struct mlx4_en_priv *priv) return; for (ring = 0; ring < priv->rx_ring_num; ring++) { - if (mlx4_en_is_ring_empty(priv->rx_ring[ring])) + if (mlx4_en_is_ring_empty(priv->rx_ring[ring])) { + local_bh_disable(); napi_reschedule(&priv->rx_cq[ring]->napi); + local_bh_enable(); + } } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 6cf6d93d8831..ba115ec7aa92 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -432,6 +432,13 @@ static int handle_hca_cap(struct mlx5_core_dev *dev) MLX5_SET(cmd_hca_cap, set_hca_cap, pkey_table_size, to_fw_pkey_sz(128)); + /* Check log_max_qp from HCA caps to set in current profile */ + if (MLX5_CAP_GEN_MAX(dev, log_max_qp) < profile[prof_sel].log_max_qp) { + mlx5_core_warn(dev, "log_max_qp value in current profile is %d, changing it to HCA capability limit (%d)\n", + profile[prof_sel].log_max_qp, + MLX5_CAP_GEN_MAX(dev, log_max_qp)); + profile[prof_sel].log_max_qp = MLX5_CAP_GEN_MAX(dev, log_max_qp); + } if (prof->mask & MLX5_PROF_MASK_QP_SIZE) MLX5_SET(cmd_hca_cap, set_hca_cap, log_max_qp, prof->log_max_qp); @@ -505,7 +512,6 @@ static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i) struct mlx5_priv *priv = &mdev->priv; struct msix_entry *msix = priv->msix_arr; int irq = msix[i + MLX5_EQ_VEC_COMP_BASE].vector; - int numa_node = priv->numa_node; int err; if (!zalloc_cpumask_var(&priv->irq_info[i].mask, GFP_KERNEL)) { @@ -513,7 +519,7 @@ static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i) return -ENOMEM; } - cpumask_set_cpu(cpumask_local_spread(i, numa_node), + cpumask_set_cpu(cpumask_local_spread(i, priv->numa_node), priv->irq_info[i].mask); err = irq_set_affinity_hint(irq, priv->irq_info[i].mask); diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h index 142f33d978c5..a0fbe00dd690 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.h +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h @@ -206,21 +206,21 @@ MLXSW_ITEM32(pci, eqe, owner, 0x0C, 0, 1); /* pci_eqe_cmd_token * Command completion event - token */ -MLXSW_ITEM32(pci, eqe, cmd_token, 0x08, 16, 16); +MLXSW_ITEM32(pci, eqe, cmd_token, 0x00, 16, 16); /* pci_eqe_cmd_status * Command completion event - status */ -MLXSW_ITEM32(pci, eqe, cmd_status, 0x08, 0, 8); +MLXSW_ITEM32(pci, eqe, cmd_status, 0x00, 0, 8); /* pci_eqe_cmd_out_param_h * Command completion event - output parameter - higher part */ -MLXSW_ITEM32(pci, eqe, cmd_out_param_h, 0x0C, 0, 32); +MLXSW_ITEM32(pci, eqe, cmd_out_param_h, 0x04, 0, 32); /* pci_eqe_cmd_out_param_l * Command completion event - output parameter - lower part */ -MLXSW_ITEM32(pci, eqe, cmd_out_param_l, 0x10, 0, 32); +MLXSW_ITEM32(pci, eqe, cmd_out_param_l, 0x08, 0, 32); #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 3be4a2355ead..cb165c2d4803 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -390,6 +390,7 @@ static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb, dev_kfree_skb_any(skb_orig); return NETDEV_TX_OK; } + dev_consume_skb_any(skb_orig); } if (eth_skb_pad(skb)) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index d85960cfb694..fb2d9a82ce3d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -313,6 +313,7 @@ static netdev_tx_t mlxsw_sx_port_xmit(struct sk_buff *skb, dev_kfree_skb_any(skb_orig); return NETDEV_TX_OK; } + dev_consume_skb_any(skb_orig); } mlxsw_sx_txhdr_construct(skb, &tx_info); len = skb->len; diff --git a/drivers/net/ethernet/msm/msm_rmnet_mhi.c b/drivers/net/ethernet/msm/msm_rmnet_mhi.c index 9117ea7d08c0..bf6502e27bdd 100644 --- a/drivers/net/ethernet/msm/msm_rmnet_mhi.c +++ b/drivers/net/ethernet/msm/msm_rmnet_mhi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,13 +27,12 @@ #include <linux/device.h> #include <linux/errno.h> #include <linux/of_device.h> +#include <linux/rtnetlink.h> #define RMNET_MHI_DRIVER_NAME "rmnet_mhi" -#define RMNET_MHI_DEV_NAME "rmnet_mhi%d" #define MHI_DEFAULT_MTU 8000 #define MHI_MAX_MRU 0xFFFF #define MHI_NAPI_WEIGHT_VALUE 12 -#define MHI_RX_HEADROOM 64 #define WATCHDOG_TIMEOUT (30 * HZ) #define RMNET_IPC_LOG_PAGES (100) @@ -79,6 +78,8 @@ struct __packed mhi_skb_priv { struct rmnet_mhi_private { struct list_head node; + u32 dev_id; + const char *interface_name; struct mhi_client_handle *tx_client_handle; struct mhi_client_handle *rx_client_handle; enum MHI_CLIENT_CHANNEL tx_channel; @@ -87,6 +88,8 @@ struct rmnet_mhi_private { struct sk_buff_head rx_buffers; atomic_t rx_pool_len; u32 mru; + u32 max_mru; + u32 max_mtu; struct napi_struct napi; gfp_t allocation_flags; uint32_t tx_buffers_max; @@ -110,6 +113,7 @@ struct rmnet_mhi_private { }; static LIST_HEAD(rmnet_mhi_ctxt_list); +static struct platform_driver rmnet_mhi_driver; static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr, struct sk_buff *skb, int frag) @@ -118,9 +122,9 @@ static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr, if (rmnet_mhi_ptr->frag_skb) { /* Merge the new skb into the old fragment */ temp_skb = skb_copy_expand(rmnet_mhi_ptr->frag_skb, - MHI_RX_HEADROOM, - skb->len, - GFP_ATOMIC); + 0, + skb->len, + GFP_ATOMIC); if (!temp_skb) { kfree(rmnet_mhi_ptr->frag_skb); rmnet_mhi_ptr->frag_skb = NULL; @@ -207,9 +211,8 @@ static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr, return -ENOMEM; } skb_priv = (struct mhi_skb_priv *)(skb->cb); - skb_priv->dma_size = cur_mru - MHI_RX_HEADROOM; + skb_priv->dma_size = cur_mru; skb_priv->dma_addr = 0; - skb_reserve(skb, MHI_RX_HEADROOM); /* These steps must be in atomic context */ spin_lock_irqsave(&rmnet_mhi_ptr->alloc_lock, flags); @@ -584,7 +587,10 @@ static int rmnet_mhi_stop(struct net_device *dev) static int rmnet_mhi_change_mtu(struct net_device *dev, int new_mtu) { - if (0 > new_mtu || MHI_MAX_MTU < new_mtu) + struct rmnet_mhi_private *rmnet_mhi_ptr = + *(struct rmnet_mhi_private **)netdev_priv(dev); + + if (new_mtu < 0 || rmnet_mhi_ptr->max_mtu < new_mtu) return -EINVAL; dev->mtu = new_mtu; @@ -667,11 +673,11 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr) switch (ext_cmd.extended_ioctl) { case RMNET_IOCTL_SET_MRU: - if (!ext_cmd.u.data || ext_cmd.u.data > MHI_MAX_MRU) { - rmnet_log(rmnet_mhi_ptr, - MSG_CRITICAL, - "Can't set MRU, value %u is invalid\n", - ext_cmd.u.data); + if (!ext_cmd.u.data || + ext_cmd.u.data > rmnet_mhi_ptr->max_mru) { + rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL, + "Can't set MRU, value:%u is invalid max:%u\n", + ext_cmd.u.data, rmnet_mhi_ptr->max_mru); return -EINVAL; } rmnet_log(rmnet_mhi_ptr, @@ -688,7 +694,7 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr) ext_cmd.u.data = 0; break; case RMNET_IOCTL_GET_DRIVER_NAME: - strlcpy(ext_cmd.u.if_name, RMNET_MHI_DRIVER_NAME, + strlcpy(ext_cmd.u.if_name, rmnet_mhi_ptr->interface_name, sizeof(ext_cmd.u.if_name)); break; case RMNET_IOCTL_SET_SLEEP_STATE: @@ -793,6 +799,9 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) int ret = 0; struct rmnet_mhi_private **rmnet_mhi_ctxt = NULL; int r = 0; + char ifalias[IFALIASZ]; + char ifname[IFNAMSIZ]; + struct mhi_client_handle *client_handle = NULL; rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered.\n"); @@ -826,6 +835,7 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) } else { rmnet_mhi_ptr->tx_enabled = 1; } + client_handle = rmnet_mhi_ptr->tx_client_handle; } if (rmnet_mhi_ptr->rx_client_handle != NULL) { rmnet_log(rmnet_mhi_ptr, @@ -841,11 +851,32 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) } else { rmnet_mhi_ptr->rx_enabled = 1; } + /* Both tx & rx client handle contain same device info */ + client_handle = rmnet_mhi_ptr->rx_client_handle; + } + + if (!client_handle) { + ret = -EINVAL; + goto net_dev_alloc_fail; } + + snprintf(ifalias, + sizeof(ifalias), + "%s_%04x_%02u.%02u.%02u_%u", + rmnet_mhi_ptr->interface_name, + client_handle->dev_id, + client_handle->domain, + client_handle->bus, + client_handle->slot, + rmnet_mhi_ptr->dev_id); + + snprintf(ifname, sizeof(ifname), "%s%%d", + rmnet_mhi_ptr->interface_name); + + rtnl_lock(); rmnet_mhi_ptr->dev = alloc_netdev(sizeof(struct rmnet_mhi_private *), - RMNET_MHI_DEV_NAME, - NET_NAME_PREDICTABLE, rmnet_mhi_setup); + ifname, NET_NAME_PREDICTABLE, rmnet_mhi_setup); if (!rmnet_mhi_ptr->dev) { rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL, @@ -854,7 +885,9 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) goto net_dev_alloc_fail; } SET_NETDEV_DEV(rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->pdev->dev); + dev_set_alias(rmnet_mhi_ptr->dev, ifalias, strlen(ifalias)); rmnet_mhi_ctxt = netdev_priv(rmnet_mhi_ptr->dev); + rtnl_unlock(); *rmnet_mhi_ctxt = rmnet_mhi_ptr; ret = dma_set_mask(&(rmnet_mhi_ptr->dev->dev), @@ -981,17 +1014,16 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info) } } -static struct mhi_client_info_t rmnet_mhi_info = {rmnet_mhi_cb}; - #ifdef CONFIG_DEBUG_FS struct dentry *dentry; static void rmnet_mhi_create_debugfs(struct rmnet_mhi_private *rmnet_mhi_ptr) { - char node_name[15]; + char node_name[32]; int i; const umode_t mode = (S_IRUSR | S_IWUSR); struct dentry *file; + struct mhi_client_handle *client_handle; const struct { char *name; @@ -1047,8 +1079,20 @@ static void rmnet_mhi_create_debugfs(struct rmnet_mhi_private *rmnet_mhi_ptr) }, }; - snprintf(node_name, sizeof(node_name), "%s%d", - RMNET_MHI_DRIVER_NAME, rmnet_mhi_ptr->pdev->id); + /* Both tx & rx client handle contain same device info */ + client_handle = rmnet_mhi_ptr->rx_client_handle; + if (!client_handle) + client_handle = rmnet_mhi_ptr->tx_client_handle; + + snprintf(node_name, + sizeof(node_name), + "%s_%04x_%02u.%02u.%02u_%u", + rmnet_mhi_ptr->interface_name, + client_handle->dev_id, + client_handle->domain, + client_handle->bus, + client_handle->slot, + rmnet_mhi_ptr->dev_id); if (IS_ERR_OR_NULL(dentry)) return; @@ -1109,11 +1153,16 @@ static int rmnet_mhi_probe(struct platform_device *pdev) int rc; u32 channel; struct rmnet_mhi_private *rmnet_mhi_ptr; - char node_name[15]; + struct mhi_client_handle *client_handle = NULL; + char node_name[32]; + struct mhi_client_info_t client_info; if (unlikely(!pdev->dev.of_node)) return -ENODEV; + if (!mhi_is_device_ready(&pdev->dev, "qcom,mhi")) + return -EPROBE_DEFER; + pdev->id = of_alias_get_id(pdev->dev.of_node, "mhi_rmnet"); if (unlikely(pdev->id < 0)) return -ENODEV; @@ -1135,15 +1184,59 @@ static int rmnet_mhi_probe(struct platform_device *pdev) } rc = of_property_read_u32(pdev->dev.of_node, + "cell-index", + &rmnet_mhi_ptr->dev_id); + if (unlikely(rc)) { + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "failed to get valid 'cell-index'\n"); + goto probe_fail; + } + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,mhi-max-mru", + &rmnet_mhi_ptr->max_mru); + if (likely(rc)) { + rmnet_log(rmnet_mhi_ptr, MSG_INFO, + "max-mru not defined, setting to max %d\n", + MHI_MAX_MRU); + rmnet_mhi_ptr->max_mru = MHI_MAX_MRU; + } + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,mhi-max-mtu", + &rmnet_mhi_ptr->max_mtu); + if (likely(rc)) { + rmnet_log(rmnet_mhi_ptr, MSG_INFO, + "max-mtu not defined, setting to max %d\n", + MHI_MAX_MTU); + rmnet_mhi_ptr->max_mtu = MHI_MAX_MTU; + } + + rc = of_property_read_string(pdev->dev.of_node, "qcom,interface-name", + &rmnet_mhi_ptr->interface_name); + if (likely(rc)) { + rmnet_log(rmnet_mhi_ptr, MSG_INFO, + "interface-name not defined, setting to default name %s\n", + RMNET_MHI_DRIVER_NAME); + rmnet_mhi_ptr->interface_name = rmnet_mhi_driver.driver.name; + } + + client_info.dev = &pdev->dev; + client_info.node_name = "qcom,mhi"; + client_info.mhi_client_cb = rmnet_mhi_cb; + client_info.user_data = rmnet_mhi_ptr; + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,mhi-tx-channel", &channel); if (rc == 0) { rmnet_mhi_ptr->tx_channel = channel; + client_info.chan = channel; + client_info.max_payload = rmnet_mhi_ptr->max_mtu; + rc = mhi_register_channel(&rmnet_mhi_ptr->tx_client_handle, - rmnet_mhi_ptr->tx_channel, - 0, - &rmnet_mhi_info, - rmnet_mhi_ptr); + &client_info); if (unlikely(rc)) { rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL, @@ -1152,6 +1245,7 @@ static int rmnet_mhi_probe(struct platform_device *pdev) rc); goto probe_fail; } + client_handle = rmnet_mhi_ptr->tx_client_handle; } rc = of_property_read_u32(pdev->dev.of_node, @@ -1159,11 +1253,10 @@ static int rmnet_mhi_probe(struct platform_device *pdev) &channel); if (rc == 0) { rmnet_mhi_ptr->rx_channel = channel; + client_info.max_payload = rmnet_mhi_ptr->max_mru; + client_info.chan = channel; rc = mhi_register_channel(&rmnet_mhi_ptr->rx_client_handle, - rmnet_mhi_ptr->rx_channel, - 0, - &rmnet_mhi_info, - rmnet_mhi_ptr); + &client_info); if (unlikely(rc)) { rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL, @@ -1172,22 +1265,31 @@ static int rmnet_mhi_probe(struct platform_device *pdev) rc); goto probe_fail; } - + /* overwriting tx_client_handle is ok because dev_id and + * bdf are same for both channels + */ + client_handle = rmnet_mhi_ptr->rx_client_handle; INIT_WORK(&rmnet_mhi_ptr->alloc_work, rmnet_mhi_alloc_work); spin_lock_init(&rmnet_mhi_ptr->alloc_lock); } /* We must've have @ least one valid channel */ - if (!rmnet_mhi_ptr->rx_client_handle && - !rmnet_mhi_ptr->tx_client_handle) { + if (!client_handle) { rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL, "No registered channels\n"); rc = -ENODEV; goto probe_fail; } - snprintf(node_name, sizeof(node_name), "%s%d", - RMNET_MHI_DRIVER_NAME, pdev->id); + snprintf(node_name, + sizeof(node_name), + "%s_%04x_%02u.%02u.%02u_%u", + rmnet_mhi_ptr->interface_name, + client_handle->dev_id, + client_handle->domain, + client_handle->bus, + client_handle->slot, + rmnet_mhi_ptr->dev_id); rmnet_mhi_ptr->rmnet_ipc_log = ipc_log_context_create(RMNET_IPC_LOG_PAGES, node_name, 0); diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c index 50d5604833ed..e0993eba5df3 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c @@ -2223,8 +2223,6 @@ static irqreturn_t vxge_isr_napi(int irq, void *dev_id) return IRQ_NONE; } -#ifdef CONFIG_PCI_MSI - static irqreturn_t vxge_tx_msix_handle(int irq, void *dev_id) { struct vxge_fifo *fifo = (struct vxge_fifo *)dev_id; @@ -2442,16 +2440,13 @@ static void vxge_rem_msix_isr(struct vxgedev *vdev) if (vdev->config.intr_type == MSI_X) pci_disable_msix(vdev->pdev); } -#endif static void vxge_rem_isr(struct vxgedev *vdev) { -#ifdef CONFIG_PCI_MSI - if (vdev->config.intr_type == MSI_X) { + if (IS_ENABLED(CONFIG_PCI_MSI) && + vdev->config.intr_type == MSI_X) { vxge_rem_msix_isr(vdev); - } else -#endif - if (vdev->config.intr_type == INTA) { + } else if (vdev->config.intr_type == INTA) { synchronize_irq(vdev->pdev->irq); free_irq(vdev->pdev->irq, vdev); } @@ -2460,11 +2455,10 @@ static void vxge_rem_isr(struct vxgedev *vdev) static int vxge_add_isr(struct vxgedev *vdev) { int ret = 0; -#ifdef CONFIG_PCI_MSI int vp_idx = 0, intr_idx = 0, intr_cnt = 0, msix_idx = 0, irq_req = 0; int pci_fun = PCI_FUNC(vdev->pdev->devfn); - if (vdev->config.intr_type == MSI_X) + if (IS_ENABLED(CONFIG_PCI_MSI) && vdev->config.intr_type == MSI_X) ret = vxge_enable_msix(vdev); if (ret) { @@ -2475,7 +2469,7 @@ static int vxge_add_isr(struct vxgedev *vdev) vdev->config.intr_type = INTA; } - if (vdev->config.intr_type == MSI_X) { + if (IS_ENABLED(CONFIG_PCI_MSI) && vdev->config.intr_type == MSI_X) { for (intr_idx = 0; intr_idx < (vdev->no_of_vpath * VXGE_HW_VPATH_MSIX_ACTIVE); intr_idx++) { @@ -2576,9 +2570,8 @@ static int vxge_add_isr(struct vxgedev *vdev) vdev->vxge_entries[intr_cnt].in_use = 1; vdev->vxge_entries[intr_cnt].arg = &vdev->vpaths[0]; } -INTA_MODE: -#endif +INTA_MODE: if (vdev->config.intr_type == INTA) { snprintf(vdev->desc[0], VXGE_INTR_STRLEN, "%s:vxge:INTA", vdev->ndev->name); @@ -3889,12 +3882,12 @@ static void vxge_device_config_init(struct vxge_hw_device_config *device_config, if (max_mac_vpath > VXGE_MAX_MAC_ADDR_COUNT) max_mac_vpath = VXGE_MAX_MAC_ADDR_COUNT; -#ifndef CONFIG_PCI_MSI - vxge_debug_init(VXGE_ERR, - "%s: This Kernel does not support " - "MSI-X. Defaulting to INTA", VXGE_DRIVER_NAME); - *intr_type = INTA; -#endif + if (!IS_ENABLED(CONFIG_PCI_MSI)) { + vxge_debug_init(VXGE_ERR, + "%s: This Kernel does not support " + "MSI-X. Defaulting to INTA", VXGE_DRIVER_NAME); + *intr_type = INTA; + } /* Configure whether MSI-X or IRQL. */ switch (*intr_type) { diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 467d41698fd5..549ad2018e7f 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -1330,6 +1330,19 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) buffer = PTR_ALIGN(priv->tx_align[q], DPTR_ALIGN) + entry / NUM_TX_DESC * DPTR_ALIGN; len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data; + /* Zero length DMA descriptors are problematic as they seem to + * terminate DMA transfers. Avoid them by simply using a length of + * DPTR_ALIGN (4) when skb data is aligned to DPTR_ALIGN. + * + * As skb is guaranteed to have at least ETH_ZLEN (60) bytes of + * data by the call to skb_put_padto() above this is safe with + * respect to both the length of the first DMA descriptor (len) + * overflowing the available data and the length of the second DMA + * descriptor (skb->len - len) being negative. + */ + if (len == 0) + len = DPTR_ALIGN; + memcpy(buffer, skb->data, len); dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE); if (dma_mapping_error(ndev->dev.parent, dma_addr)) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index a5b869eb4678..4b100ef4af9f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2939,12 +2939,6 @@ int stmmac_dvr_probe(struct device *device, spin_lock_init(&priv->lock); spin_lock_init(&priv->tx_lock); - ret = register_netdev(ndev); - if (ret) { - pr_err("%s: ERROR %i registering the device\n", __func__, ret); - goto error_netdev_register; - } - /* If a specific clk_csr value is passed from the platform * this means that the CSR Clock Range selection cannot be * changed at run-time and it is fixed. Viceversa the driver'll try to @@ -2969,11 +2963,21 @@ int stmmac_dvr_probe(struct device *device, } } - return 0; + ret = register_netdev(ndev); + if (ret) { + netdev_err(priv->dev, "%s: ERROR %i registering the device\n", + __func__, ret); + goto error_netdev_register; + } + + return ret; -error_mdio_register: - unregister_netdev(ndev); error_netdev_register: + if (priv->pcs != STMMAC_PCS_RGMII && + priv->pcs != STMMAC_PCS_TBI && + priv->pcs != STMMAC_PCS_RTBI) + stmmac_mdio_unregister(ndev); +error_mdio_register: netif_napi_del(&priv->napi); error_hw_init: clk_disable_unprepare(priv->pclk); diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index 77d26fe286c0..7e8bce46e6b4 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -549,7 +549,8 @@ fatal_error: static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) { - int queue, len; + int queue; + unsigned int len; struct cpmac_desc *desc; struct cpmac_priv *priv = netdev_priv(dev); @@ -559,7 +560,7 @@ static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(skb_padto(skb, ETH_ZLEN))) return NETDEV_TX_OK; - len = max(skb->len, ETH_ZLEN); + len = max_t(unsigned int, skb->len, ETH_ZLEN); queue = skb_get_queue_mapping(skb); netif_stop_subqueue(dev, queue); @@ -1236,7 +1237,7 @@ int cpmac_init(void) goto fail_alloc; } -#warning FIXME: unhardcode gpio&reset bits + /* FIXME: unhardcode gpio&reset bits */ ar7_gpio_disable(26); ar7_gpio_disable(27); ar7_device_reset(AR7_RESET_BIT_CPMAC_LO); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 409b48e1e589..e8a09ff9e724 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -40,6 +40,8 @@ #include "hyperv_net.h" +/* Restrict GSO size to account for NVGRE */ +#define NETVSC_GSO_MAX_SIZE 62768 #define RING_SIZE_MIN 64 static int ring_size = 128; @@ -1139,6 +1141,7 @@ static int netvsc_probe(struct hv_device *dev, nvdev = hv_get_drvdata(dev); netif_set_real_num_tx_queues(net, nvdev->num_chn); netif_set_real_num_rx_queues(net, nvdev->num_chn); + netif_set_gso_max_size(net, NETVSC_GSO_MAX_SIZE); ret = register_netdev(net); if (ret != 0) { diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c index 199a94a9c8bc..3a429f1a8002 100644 --- a/drivers/net/ieee802154/atusb.c +++ b/drivers/net/ieee802154/atusb.c @@ -110,13 +110,26 @@ static int atusb_read_reg(struct atusb *atusb, uint8_t reg) { struct usb_device *usb_dev = atusb->usb_dev; int ret; + uint8_t *buffer; uint8_t value; + buffer = kmalloc(1, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + dev_dbg(&usb_dev->dev, "atusb: reg = 0x%x\n", reg); ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0), ATUSB_REG_READ, ATUSB_REQ_FROM_DEV, - 0, reg, &value, 1, 1000); - return ret >= 0 ? value : ret; + 0, reg, buffer, 1, 1000); + + if (ret >= 0) { + value = buffer[0]; + kfree(buffer); + return value; + } else { + kfree(buffer); + return ret; + } } static int atusb_write_subreg(struct atusb *atusb, uint8_t reg, uint8_t mask, @@ -517,9 +530,13 @@ static struct ieee802154_ops atusb_ops = { static int atusb_get_and_show_revision(struct atusb *atusb) { struct usb_device *usb_dev = atusb->usb_dev; - unsigned char buffer[3]; + unsigned char *buffer; int ret; + buffer = kmalloc(3, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + /* Get a couple of the ATMega Firmware values */ ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0), ATUSB_ID, ATUSB_REQ_FROM_DEV, 0, 0, @@ -535,15 +552,20 @@ static int atusb_get_and_show_revision(struct atusb *atusb) dev_info(&usb_dev->dev, "Please update to version 0.2 or newer"); } + kfree(buffer); return ret; } static int atusb_get_and_show_build(struct atusb *atusb) { struct usb_device *usb_dev = atusb->usb_dev; - char build[ATUSB_BUILD_SIZE + 1]; + char *build; int ret; + build = kmalloc(ATUSB_BUILD_SIZE + 1, GFP_KERNEL); + if (!build) + return -ENOMEM; + ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0), ATUSB_BUILD, ATUSB_REQ_FROM_DEV, 0, 0, build, ATUSB_BUILD_SIZE, 1000); @@ -552,6 +574,7 @@ static int atusb_get_and_show_build(struct atusb *atusb) dev_info(&usb_dev->dev, "Firmware: build %s\n", build); } + kfree(build); return ret; } diff --git a/drivers/net/ieee802154/fakelb.c b/drivers/net/ieee802154/fakelb.c index 860d4aed8274..43617ded3773 100644 --- a/drivers/net/ieee802154/fakelb.c +++ b/drivers/net/ieee802154/fakelb.c @@ -30,7 +30,7 @@ static int numlbs = 2; static LIST_HEAD(fakelb_phys); -static DEFINE_SPINLOCK(fakelb_phys_lock); +static DEFINE_MUTEX(fakelb_phys_lock); static LIST_HEAD(fakelb_ifup_phys); static DEFINE_RWLOCK(fakelb_ifup_phys_lock); @@ -180,9 +180,9 @@ static int fakelb_add_one(struct device *dev) if (err) goto err_reg; - spin_lock(&fakelb_phys_lock); + mutex_lock(&fakelb_phys_lock); list_add_tail(&phy->list, &fakelb_phys); - spin_unlock(&fakelb_phys_lock); + mutex_unlock(&fakelb_phys_lock); return 0; @@ -214,10 +214,10 @@ static int fakelb_probe(struct platform_device *pdev) return 0; err_slave: - spin_lock(&fakelb_phys_lock); + mutex_lock(&fakelb_phys_lock); list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) fakelb_del(phy); - spin_unlock(&fakelb_phys_lock); + mutex_unlock(&fakelb_phys_lock); return err; } @@ -225,10 +225,10 @@ static int fakelb_remove(struct platform_device *pdev) { struct fakelb_phy *phy, *tmp; - spin_lock(&fakelb_phys_lock); + mutex_lock(&fakelb_phys_lock); list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) fakelb_del(phy); - spin_unlock(&fakelb_phys_lock); + mutex_unlock(&fakelb_phys_lock); return 0; } diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index dc7d970bd1c0..effcdbfb06e9 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -164,6 +164,7 @@ static void loopback_setup(struct net_device *dev) { dev->mtu = 64 * 1024; dev->hard_header_len = ETH_HLEN; /* 14 */ + dev->min_header_len = ETH_HLEN; /* 14 */ dev->addr_len = ETH_ALEN; /* 6 */ dev->type = ARPHRD_LOOPBACK; /* 0x0001*/ dev->flags = IFF_LOOPBACK; diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 159a68782bec..79de9608ac48 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -725,7 +725,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, ssize_t n; if (q->flags & IFF_VNET_HDR) { - vnet_hdr_len = q->vnet_hdr_sz; + vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz); err = -EINVAL; if (len < vnet_hdr_len) @@ -865,7 +865,7 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q, if (q->flags & IFF_VNET_HDR) { struct virtio_net_hdr vnet_hdr; - vnet_hdr_len = q->vnet_hdr_sz; + vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz); if (iov_iter_count(iter) < vnet_hdr_len) return -EINVAL; diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c index 86b28052bf06..9b709f78bb03 100644 --- a/drivers/net/phy/bcm63xx.c +++ b/drivers/net/phy/bcm63xx.c @@ -21,6 +21,23 @@ MODULE_DESCRIPTION("Broadcom 63xx internal PHY driver"); MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>"); MODULE_LICENSE("GPL"); +static int bcm63xx_config_intr(struct phy_device *phydev) +{ + int reg, err; + + reg = phy_read(phydev, MII_BCM63XX_IR); + if (reg < 0) + return reg; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + reg &= ~MII_BCM63XX_IR_GMASK; + else + reg |= MII_BCM63XX_IR_GMASK; + + err = phy_write(phydev, MII_BCM63XX_IR, reg); + return err; +} + static int bcm63xx_config_init(struct phy_device *phydev) { int reg, err; @@ -55,7 +72,7 @@ static struct phy_driver bcm63xx_driver[] = { .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, - .config_intr = bcm_phy_config_intr, + .config_intr = bcm63xx_config_intr, .driver = { .owner = THIS_MODULE }, }, { /* same phy as above, with just a different OUI */ @@ -68,7 +85,7 @@ static struct phy_driver bcm63xx_driver[] = { .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, - .config_intr = bcm_phy_config_intr, + .config_intr = bcm63xx_config_intr, .driver = { .owner = THIS_MODULE }, } }; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index dd7b7d64c90a..a444294fb555 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1105,9 +1105,11 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } if (tun->flags & IFF_VNET_HDR) { - if (len < tun->vnet_hdr_sz) + int vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz); + + if (len < vnet_hdr_sz) return -EINVAL; - len -= tun->vnet_hdr_sz; + len -= vnet_hdr_sz; n = copy_from_iter(&gso, sizeof(gso), from); if (n != sizeof(gso)) @@ -1119,7 +1121,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (tun16_to_cpu(tun, gso.hdr_len) > len) return -EINVAL; - iov_iter_advance(from, tun->vnet_hdr_sz - sizeof(gso)); + iov_iter_advance(from, vnet_hdr_sz - sizeof(gso)); } if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP) { @@ -1302,7 +1304,7 @@ static ssize_t tun_put_user(struct tun_struct *tun, vlan_hlen = VLAN_HLEN; if (tun->flags & IFF_VNET_HDR) - vnet_hdr_sz = tun->vnet_hdr_sz; + vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz); total = skb->len + vlan_hlen + vnet_hdr_sz; diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 7cba2c3759df..8c408aa2f208 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -462,6 +462,7 @@ static const struct driver_info wwan_info = { #define SAMSUNG_VENDOR_ID 0x04e8 #define LENOVO_VENDOR_ID 0x17ef #define NVIDIA_VENDOR_ID 0x0955 +#define HP_VENDOR_ID 0x03f0 static const struct usb_device_id products[] = { /* BLACKLIST !! @@ -608,6 +609,13 @@ static const struct usb_device_id products[] = { .driver_info = 0, }, +/* HP lt2523 (Novatel E371) - handled by qmi_wwan */ +{ + USB_DEVICE_AND_INTERFACE_INFO(HP_VENDOR_ID, 0x421d, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), + .driver_info = 0, +}, + /* AnyDATA ADU960S - handled by qmi_wwan */ { USB_DEVICE_AND_INTERFACE_INFO(0x16d5, 0x650a, USB_CLASS_COMM, diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index a34f491224c1..09052f9e324f 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -485,6 +485,13 @@ static const struct usb_device_id products[] = { USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&qmi_wwan_info, }, + { /* HP lt2523 (Novatel E371) */ + USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, + USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, + USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&qmi_wwan_info, + }, { /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7), .driver_info = (unsigned long)&qmi_wwan_info, diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 2fb637ad594a..fbb1867ff25c 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1645,7 +1645,7 @@ static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc) u8 checksum = CHECKSUM_NONE; u32 opts2, opts3; - if (tp->version == RTL_VER_01) + if (!(tp->netdev->features & NETIF_F_RXCSUM)) goto return_result; opts2 = le32_to_cpu(rx_desc->opts2); @@ -3442,43 +3442,93 @@ static bool delay_autosuspend(struct r8152 *tp) */ if (!sw_linking && tp->rtl_ops.in_nway(tp)) return true; + else if (!skb_queue_empty(&tp->tx_queue)) + return true; else return false; } -static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) +static int rtl8152_rumtime_suspend(struct r8152 *tp) { - struct r8152 *tp = usb_get_intfdata(intf); struct net_device *netdev = tp->netdev; int ret = 0; - mutex_lock(&tp->control); + if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) { + u32 rcr = 0; - if (PMSG_IS_AUTO(message)) { - if (netif_running(netdev) && delay_autosuspend(tp)) { + if (delay_autosuspend(tp)) { ret = -EBUSY; goto out1; } - set_bit(SELECTIVE_SUSPEND, &tp->flags); - } else { - netif_device_detach(netdev); + if (netif_carrier_ok(netdev)) { + u32 ocp_data; + + rcr = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); + ocp_data = rcr & ~RCR_ACPT_ALL; + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); + rxdy_gated_en(tp, true); + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, + PLA_OOB_CTRL); + if (!(ocp_data & RXFIFO_EMPTY)) { + rxdy_gated_en(tp, false); + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr); + ret = -EBUSY; + goto out1; + } + } + + clear_bit(WORK_ENABLE, &tp->flags); + usb_kill_urb(tp->intr_urb); + + rtl_runtime_suspend_enable(tp, true); + + if (netif_carrier_ok(netdev)) { + napi_disable(&tp->napi); + rtl_stop_rx(tp); + rxdy_gated_en(tp, false); + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr); + napi_enable(&tp->napi); + } } + set_bit(SELECTIVE_SUSPEND, &tp->flags); + +out1: + return ret; +} + +static int rtl8152_system_suspend(struct r8152 *tp) +{ + struct net_device *netdev = tp->netdev; + int ret = 0; + + netif_device_detach(netdev); + if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) { clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); napi_disable(&tp->napi); - if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { - rtl_stop_rx(tp); - rtl_runtime_suspend_enable(tp, true); - } else { - cancel_delayed_work_sync(&tp->schedule); - tp->rtl_ops.down(tp); - } + cancel_delayed_work_sync(&tp->schedule); + tp->rtl_ops.down(tp); napi_enable(&tp->napi); } -out1: + + return ret; +} + +static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct r8152 *tp = usb_get_intfdata(intf); + int ret; + + mutex_lock(&tp->control); + + if (PMSG_IS_AUTO(message)) + ret = rtl8152_rumtime_suspend(tp); + else + ret = rtl8152_system_suspend(tp); + mutex_unlock(&tp->control); return ret; @@ -4173,6 +4223,11 @@ static int rtl8152_probe(struct usb_interface *intf, NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | NETIF_F_TSO6; + if (tp->version == RTL_VER_01) { + netdev->features &= ~NETIF_F_RXCSUM; + netdev->hw_features &= ~NETIF_F_RXCSUM; + } + netdev->ethtool_ops = &ops; netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE); diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 903bda437839..d6b619667f1a 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -301,7 +301,9 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb, .flowi4_tos = RT_TOS(ip4h->tos), .flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_L3MDEV_SRC | FLOWI_FLAG_SKIP_NH_OIF, + .flowi4_proto = ip4h->protocol, .daddr = ip4h->daddr, + .saddr = ip4h->saddr, }; if (vrf_send_v4_prep(skb, &fl4, vrf_dev)) @@ -410,6 +412,8 @@ static int vrf_finish_output6(struct net *net, struct sock *sk, struct in6_addr *nexthop; int ret; + nf_reset(skb); + skb->protocol = htons(ETH_P_IPV6); skb->dev = dev; @@ -521,6 +525,8 @@ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *s u32 nexthop; int ret = -EINVAL; + nf_reset(skb); + /* Be paranoid, rather than too clever. */ if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) { struct sk_buff *skb2; @@ -919,6 +925,8 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev, return -EINVAL; vrf->tb_id = nla_get_u32(data[IFLA_VRF_TABLE]); + if (vrf->tb_id == RT_TABLE_UNSPEC) + return -EINVAL; dev->priv_flags |= IFF_L3MDEV_MASTER; diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile index 25b23bf2c8e6..5fe8bc184868 100644 --- a/drivers/net/wireless/ath/ath10k/Makefile +++ b/drivers/net/wireless/ath/ath10k/Makefile @@ -26,6 +26,7 @@ ath10k_pci-y += pci.o \ ce.o obj-$(CONFIG_ATH10K_TARGET_SNOC) += ath10k_snoc.o ath10k_snoc-y += snoc.o \ + qmi.o \ ce.o ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index b8a3a1ecabaa..9cda1303c9e1 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -455,6 +455,9 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, u32 desc_flags = 0; int ret = 0; + if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) + return -ESHUTDOWN; + if (nbytes > ce_state->src_sz_max) ath10k_warn(ar, "%s: send more we can (nbytes: %d, max: %d)\n", __func__, nbytes, ce_state->src_sz_max); @@ -942,6 +945,9 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar) struct ath10k_ce_pipe *ce_state; struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) + return; + if (ar->target_version == ATH10K_HW_WCN3990) intr_summary = 0xFFF; else diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index d37ed66d767b..9acaffa51516 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1536,7 +1536,6 @@ static void ath10k_core_restart(struct work_struct *work) struct ath10k *ar = container_of(work, struct ath10k, restart_work); set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags); - ath10k_gen_set_base_mac_addr(ar, ar->base_mac_addr); /* Place a barrier to make sure the compiler doesn't reorder * CRASH_FLUSH and calling other functions. @@ -2390,6 +2389,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, mutex_init(&ar->conf_mutex); spin_lock_init(&ar->data_lock); spin_lock_init(&ar->txqs_lock); + spin_lock_init(&ar->datapath_rx_stat_lock); INIT_LIST_HEAD(&ar->txqs); INIT_LIST_HEAD(&ar->peers); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 21c63d5d3ead..01d5ecc4f6b8 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -70,6 +70,20 @@ #define ATH10K_NAPI_BUDGET 64 #define ATH10K_NAPI_QUOTA_LIMIT 60 +#define ATH10K_RX_MCS_MIN 0 +#define ATH10K_RX_HT_MCS_MAX 32 +#define ATH10K_RX_VHT_RATEIDX_MAX 9 +#define ATH10K_RX_VHT_MCS_MAX 20 /* For 2x2 */ +#define ATH10K_RX_NSS_MIN 0 +#define ATH10K_RX_NSS_MAX 5 + +enum ath10k_datapath_rx_band { + ATH10K_BAND_MIN, + ATH10K_BAND_2GHZ = ATH10K_BAND_MIN, + ATH10K_BAND_5GHZ, + ATH10K_BAND_MAX, +}; + struct ath10k; enum ath10k_bus { @@ -703,6 +717,20 @@ struct ath10k_fw_components { struct ath10k_fw_file fw_file; }; +struct datapath_rx_stats { + u32 no_of_packets; + u32 short_gi_pkts; + u32 ht_rate_indx[ATH10K_RX_HT_MCS_MAX + 1]; + u32 vht_rate_indx[ATH10K_RX_VHT_MCS_MAX + 1]; + u32 ht_rate_packets; + u32 vht_rate_packets; + u32 legacy_pkt; + u32 nss[ATH10K_RX_NSS_MAX + 1]; + u32 num_pkts_40Mhz; + u32 num_pkts_80Mhz; + u32 band[ATH10K_BAND_MAX + 1]; +}; + struct ath10k { struct ath_common ath_common; struct ieee80211_hw *hw; @@ -729,7 +757,7 @@ struct ath10k { u32 max_spatial_stream; /* protected by conf_mutex */ bool ani_enabled; - + bool sifs_burst_enabled; bool p2p; struct { @@ -900,7 +928,10 @@ struct ath10k { enum ath10k_spectral_mode mode; struct ath10k_spec_scan config; } spectral; + struct datapath_rx_stats *rx_stats; #endif + /* prevent concurrency histogram for receiving data packet */ + spinlock_t datapath_rx_stat_lock; struct { /* protected by conf_mutex */ diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index c36c2481856b..ec8063e7986a 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -537,6 +537,230 @@ static const struct file_operations fops_fw_stats = { .llseek = default_llseek, }; +static inline int is_vht_rate_valid(u32 rate_indx) +{ + if ((rate_indx >= ATH10K_RX_MCS_MIN) && + (rate_indx <= ATH10K_RX_VHT_RATEIDX_MAX)) + return 1; + else + return 0; +} + +void fill_datapath_stats(struct ath10k *ar, struct ieee80211_rx_status *status) +{ + struct datapath_rx_stats *stat_cnt = ar->rx_stats; + + spin_lock_bh(&ar->datapath_rx_stat_lock); + + stat_cnt->no_of_packets += 1; + if (!(stat_cnt->no_of_packets)) { + memset(stat_cnt, 0, sizeof(*stat_cnt)); + stat_cnt->no_of_packets += 1; + } + + if (status->flag & RX_FLAG_SHORT_GI) + stat_cnt->short_gi_pkts += 1; + + if ((status->vht_nss >= ATH10K_RX_NSS_MIN) && + (status->vht_nss < ATH10K_RX_NSS_MAX)) { + stat_cnt->nss[status->vht_nss] += 1; + if (status->flag & RX_FLAG_VHT) { + stat_cnt->vht_rate_packets += 1; + if (is_vht_rate_valid(status->rate_idx)) { + stat_cnt->vht_rate_indx[((status->vht_nss - 1) * + 10) + status->rate_idx] += 1; + } else { + /*if we get index other than (>=0 and <=9)*/ + stat_cnt->vht_rate_indx[ATH10K_RX_VHT_MCS_MAX] += 1; + } + } else if (status->flag & RX_FLAG_HT) { + stat_cnt->ht_rate_packets += 1; + if ((status->rate_idx >= ATH10K_RX_MCS_MIN) && + (status->rate_idx < ATH10K_RX_HT_MCS_MAX)) + stat_cnt->ht_rate_indx[status->rate_idx] += 1; + else { + /*if we get index other than (>=0 and <=31)*/ + stat_cnt->ht_rate_indx[ATH10K_RX_HT_MCS_MAX] += 1; + } + } else { + /* if pkt is other than HT and VHT */ + stat_cnt->legacy_pkt += 1; + } + } else { + stat_cnt->nss[ATH10K_RX_NSS_MAX] += 1; + } + + if (status->flag & RX_FLAG_40MHZ) + stat_cnt->num_pkts_40Mhz += 1; + if (status->vht_flag & RX_VHT_FLAG_80MHZ) + stat_cnt->num_pkts_80Mhz += 1; + if ((status->band >= ATH10K_BAND_MIN) && + (status->band < ATH10K_BAND_MAX)) { + stat_cnt->band[status->band] += 1; + } else { + /*if band is other than 0,1 */ + stat_cnt->band[ATH10K_BAND_MAX] += 1; + } + + spin_unlock_bh(&ar->datapath_rx_stat_lock); +} + +size_t get_datapath_stat(char *buf, struct ath10k *ar) +{ + u8 i; + struct datapath_rx_stats *stat_cnt = ar->rx_stats; + size_t j = 0; + + spin_lock(&ar->datapath_rx_stat_lock); + + j = snprintf(buf, ATH10K_DATAPATH_BUF_SIZE, "\nNo of packets: %u\t" + "No of short_gi packets: %u\n" + "\nHT Packets: %u \t VHT Packets: %u\n" + "\n40Mhz Packets: %u \t 80Mhz Packets: %u\n" + "\n2.4GHz: %u \t 5GHz: %u \t band-error: %u\n\n", + stat_cnt->no_of_packets, + stat_cnt->short_gi_pkts, + stat_cnt->ht_rate_packets, + stat_cnt->vht_rate_packets, + stat_cnt->num_pkts_40Mhz, + stat_cnt->num_pkts_80Mhz, + stat_cnt->band[ATH10K_BAND_2GHZ], + stat_cnt->band[ATH10K_BAND_5GHZ], + stat_cnt->band[ATH10K_BAND_MAX]); + + for (i = 0; i <= ATH10K_RX_NSS_MAX; i++) { + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "NSS-%u: %u\t", i, stat_cnt->nss[i]); + } + + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "\n\n----HT Rate index------\n"); + + for (i = ATH10K_RX_MCS_MIN; i < ATH10K_RX_HT_MCS_MAX; + i += 4) { + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "ht_rate_indx[%02u]: %10u\tht_rate_indx[%02u]: %10u\t" + "ht_rate_indx[%02u]: %10u\tht_rate_indx[%02u]: %10u\n", + i, stat_cnt->ht_rate_indx[i], + i + 1, stat_cnt->ht_rate_indx[i + 1], + i + 2, stat_cnt->ht_rate_indx[i + 2], + i + 3, stat_cnt->ht_rate_indx[i + 3]); + } + + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "ht_rate_indx[OOB]: %10u\n", + stat_cnt->ht_rate_indx[ATH10K_RX_HT_MCS_MAX]); + + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "\n----VHT Rate index------\n"); + + for (i = ATH10K_RX_MCS_MIN; + i <= ATH10K_RX_VHT_RATEIDX_MAX; i++) { + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "vht_rate_indx[%02u]: %10u\tvht_rate_indx[%02u]: %10u\n", + i, stat_cnt->vht_rate_indx[i], + i + 10, stat_cnt->vht_rate_indx[i + 10]); + } + + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "vht_rate_indx[%02u]: %10u\n", + i + 10, stat_cnt->vht_rate_indx[i + 10]); + + j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j), + "\nnumber of pkt other than HT and VHT(legacy) : %u\n" + "----------------------\n", + stat_cnt->legacy_pkt); + + spin_unlock(&ar->datapath_rx_stat_lock); + + return j; +} + +static int ath10k_datapath_stats_open(struct inode *inode, struct file *file) +{ + struct ath10k *ar = inode->i_private; + int ret; + + spin_lock(&ar->datapath_rx_stat_lock); + + if (ar->state != ATH10K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + file->private_data = ar; + + spin_unlock(&ar->datapath_rx_stat_lock); + return 0; + +err_unlock: + spin_unlock(&ar->datapath_rx_stat_lock); + return ret; +} + +static ssize_t ath10k_datapath_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + size_t buf_len; + unsigned int ret; + void *buf = NULL; + + buf = vmalloc(ATH10K_DATAPATH_BUF_SIZE); + if (!buf) + return 0; + + buf_len = get_datapath_stat(buf, ar); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, buf_len); + vfree(buf); + + return ret; +} + +static ssize_t ath10k_datapath_stats_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + u32 filter; + int ret; + + if (kstrtouint_from_user(ubuf, count, 0, &filter)) + return -EINVAL; + + spin_lock(&ar->datapath_rx_stat_lock); + + if (ar->state != ATH10K_STATE_ON) { + ret = count; + goto err_unlock; + } + + if (!filter) + memset(ar->rx_stats, 0, sizeof(*ar->rx_stats)); + + ret = count; + +err_unlock: + spin_unlock(&ar->datapath_rx_stat_lock); + return ret; +} + +static int ath10k_datapath_stats_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations fops_datapath_stats = { + .open = ath10k_datapath_stats_open, + .read = ath10k_datapath_stats_read, + .write = ath10k_datapath_stats_write, + .release = ath10k_datapath_stats_release, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static ssize_t ath10k_debug_fw_reset_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -1572,6 +1796,64 @@ static const struct file_operations fops_ani_enable = { .llseek = default_llseek, }; +static ssize_t ath10k_write_sifs_burst_enable(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + int ret; + u8 enable; + + if (kstrtou8_from_user(user_buf, count, 0, &enable)) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->sifs_burst_enabled == enable) { + ret = count; + goto exit; + } + + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->burst_enable, + enable); + if (ret) { + ath10k_warn(ar, "sifs_burst_enable failed: %d\n", ret); + goto exit; + } + ar->sifs_burst_enabled = enable; + + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static ssize_t ath10k_read_sifs_burst_enable(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32]; + int len; + bool ret = false; + + if (ar->sifs_burst_enabled) + ret = true; + + len = scnprintf(buf, sizeof(buf), "%d\n", ret); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_sifs_burst_enable = { + .read = ath10k_read_sifs_burst_enable, + .write = ath10k_write_sifs_burst_enable, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static const struct file_operations fops_cal_data = { .open = ath10k_debug_cal_data_open, .read = ath10k_debug_cal_data_read, @@ -2343,7 +2625,11 @@ int ath10k_debug_create(struct ath10k *ar) ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN); if (!ar->debug.cal_data) - return -ENOMEM; + goto err_cal_data; + + ar->rx_stats = vzalloc(sizeof(*ar->rx_stats)); + if (!ar->rx_stats) + goto err_rx_stats; INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs); INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs); @@ -2351,6 +2637,13 @@ int ath10k_debug_create(struct ath10k *ar) INIT_LIST_HEAD(&ar->debug.fw_stats.peers_extd); return 0; + +err_rx_stats: + vfree(ar->debug.cal_data); + +err_cal_data: + vfree(ar->debug.fw_crash_data); + return -ENOMEM; } void ath10k_debug_destroy(struct ath10k *ar) @@ -2361,6 +2654,9 @@ void ath10k_debug_destroy(struct ath10k *ar) vfree(ar->debug.cal_data); ar->debug.cal_data = NULL; + vfree(ar->rx_stats); + ar->rx_stats = NULL; + ath10k_debug_fw_stats_reset(ar); kfree(ar->debug.tpc_stats); @@ -2383,6 +2679,9 @@ int ath10k_debug_register(struct ath10k *ar) init_completion(&ar->debug.tpc_complete); init_completion(&ar->debug.fw_stats_complete); + debugfs_create_file("datapath_rx_stats", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_datapath_stats); + debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_fw_stats); @@ -2432,6 +2731,9 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("ani_enable", S_IRUSR | S_IWUSR, ar->debug.debugfs_phy, ar, &fops_ani_enable); + debugfs_create_file("sifs_burst_enable", S_IRUSR | S_IWUSR, + ar->debug.debugfs_phy, ar, &fops_sifs_burst_enable); + if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED)) { debugfs_create_file("dfs_simulate_radar", S_IWUSR, ar->debug.debugfs_phy, ar, diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index b1db01a167ac..f963391e3544 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -38,7 +38,7 @@ enum ath10k_debug_mask { ATH10K_DBG_WMI_PRINT = 0x00002000, ATH10K_DBG_PCI_PS = 0x00004000, ATH10K_DBG_AHB = 0x00008000, - ATH10K_DBG_SNOC = 0x00009000, + ATH10K_DBG_SNOC = 0x00010000, ATH10K_DBG_ANY = 0xffffffff, }; @@ -59,6 +59,7 @@ enum ath10k_dbg_aggr_mode { /* FIXME: How to calculate the buffer size sanely? */ #define ATH10K_FW_STATS_BUF_SIZE (1024 * 1024) +#define ATH10K_DATAPATH_BUF_SIZE (1024 * 1024) extern unsigned int ath10k_debug_mask; @@ -95,6 +96,8 @@ int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw, void ath10k_debug_get_et_stats(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ethtool_stats *stats, u64 *data); +void fill_datapath_stats(struct ath10k *ar, struct ieee80211_rx_status *status); +size_t get_datapath_stat(char *buf, struct ath10k *ar); #else static inline int ath10k_debug_start(struct ath10k *ar) { @@ -145,6 +148,16 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar) return NULL; } +static inline void fill_datapath_stats(struct ath10k *ar, + struct ieee80211_rx_status *status) +{ +} + +static inline size_t get_datapath_stat(char *buf, struct ath10k *ar) +{ + return 0; +} + #define ATH10K_DFS_STAT_INC(ar, c) do { } while (0) #define ath10k_debug_get_et_strings NULL diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index ddf097e3a143..437ea2c192b3 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -940,7 +940,7 @@ static void ath10k_process_rx(struct ath10k *ar, status = IEEE80211_SKB_RXCB(skb); *status = *rx_status; - + fill_datapath_stats(ar, status); ath10k_dbg(ar, ATH10K_DBG_DATA, "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", skb, diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 2e51590dacbb..35e5d980ed49 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3506,6 +3506,7 @@ static int ath10k_mac_tx(struct ath10k *ar, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int ret; + skb_orphan(skb); /* We should disable CCK RATE due to P2P */ if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE) ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n"); @@ -4449,7 +4450,8 @@ static int ath10k_start(struct ieee80211_hw *hw) ar->state = ATH10K_STATE_ON; break; case ATH10K_STATE_RESTARTING: - ath10k_halt(ar); + if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) + ath10k_halt(ar); ar->state = ATH10K_STATE_RESTARTED; break; case ATH10K_STATE_ON: @@ -4537,6 +4539,7 @@ static int ath10k_start(struct ieee80211_hw *hw) ret); goto err_core_stop; } + ar->sifs_burst_enabled = false; } param = ar->wmi.pdev_param->ani_enable; diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c new file mode 100644 index 000000000000..7b5fc52d269a --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -0,0 +1,230 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <soc/qcom/subsystem_notif.h> +#include <soc/qcom/subsystem_restart.h> +#include <soc/qcom/service-notifier.h> +#include "core.h" +#include "qmi.h" +#include "snoc.h" +#include <soc/qcom/icnss.h> + +static int +ath10k_snoc_service_notifier_notify(struct notifier_block *nb, + unsigned long notification, void *data) +{ + struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc, + service_notifier_nb); + enum pd_subsys_state *state = data; + struct ath10k *ar = ar_snoc->ar; + + switch (notification) { + case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01: + ath10k_dbg(ar, ATH10K_DBG_SNOC, "Service down, data: 0x%pK\n", + data); + + if (!state || *state != ROOT_PD_SHUTDOWN) + atomic_set(&ar_snoc->fw_crashed, 1); + + ath10k_dbg(ar, ATH10K_DBG_SNOC, "PD went down %d\n", + atomic_read(&ar_snoc->fw_crashed)); + break; + case SERVREG_NOTIF_SERVICE_STATE_UP_V01: + ath10k_dbg(ar, ATH10K_DBG_SNOC, "Service up\n"); + queue_work(ar->workqueue, &ar->restart_work); + break; + default: + ath10k_dbg(ar, ATH10K_DBG_SNOC, + "Service state Unknown, notification: 0x%lx\n", + notification); + return NOTIFY_DONE; + } + return NOTIFY_OK; +} + +static int ath10k_snoc_get_service_location_notify(struct notifier_block *nb, + unsigned long opcode, + void *data) +{ + struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc, + get_service_nb); + struct ath10k *ar = ar_snoc->ar; + struct pd_qmi_client_data *pd = data; + int curr_state; + int ret; + int i; + struct ath10k_service_notifier_context *notifier; + + ath10k_dbg(ar, ATH10K_DBG_SNOC, "Get service notify opcode: %lu\n", + opcode); + + if (opcode != LOCATOR_UP) + return NOTIFY_DONE; + + if (!pd->total_domains) { + ath10k_err(ar, "Did not find any domains\n"); + ret = -ENOENT; + goto out; + } + + notifier = kcalloc(pd->total_domains, + sizeof(struct ath10k_service_notifier_context), + GFP_KERNEL); + if (!notifier) { + ret = -ENOMEM; + goto out; + } + + ar_snoc->service_notifier_nb.notifier_call = + ath10k_snoc_service_notifier_notify; + + for (i = 0; i < pd->total_domains; i++) { + ath10k_dbg(ar, ATH10K_DBG_SNOC, + "%d: domain_name: %s, instance_id: %d\n", i, + pd->domain_list[i].name, + pd->domain_list[i].instance_id); + + notifier[i].handle = + service_notif_register_notifier( + pd->domain_list[i].name, + pd->domain_list[i].instance_id, + &ar_snoc->service_notifier_nb, + &curr_state); + notifier[i].instance_id = pd->domain_list[i].instance_id; + strlcpy(notifier[i].name, pd->domain_list[i].name, + QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1); + + if (IS_ERR(notifier[i].handle)) { + ath10k_err(ar, "%d: Unable to register notifier for %s(0x%x)\n", + i, pd->domain_list->name, + pd->domain_list->instance_id); + ret = PTR_ERR(notifier[i].handle); + goto free_handle; + } + } + + ar_snoc->service_notifier = notifier; + ar_snoc->total_domains = pd->total_domains; + + ath10k_dbg(ar, ATH10K_DBG_SNOC, "PD restart enabled\n"); + + return NOTIFY_OK; + +free_handle: + for (i = 0; i < pd->total_domains; i++) { + if (notifier[i].handle) { + service_notif_unregister_notifier( + notifier[i].handle, + &ar_snoc->service_notifier_nb); + } + } + kfree(notifier); + +out: + ath10k_err(ar, "PD restart not enabled: %d\n", ret); + + return NOTIFY_OK; +} + +int ath10k_snoc_pd_restart_enable(struct ath10k *ar) +{ + int ret; + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + ath10k_dbg(ar, ATH10K_DBG_SNOC, "Get service location\n"); + + ar_snoc->get_service_nb.notifier_call = + ath10k_snoc_get_service_location_notify; + ret = get_service_location(ATH10K_SERVICE_LOCATION_CLIENT_NAME, + ATH10K_WLAN_SERVICE_NAME, + &ar_snoc->get_service_nb); + if (ret) { + ath10k_err(ar, "Get service location failed: %d\n", ret); + goto out; + } + + return 0; +out: + ath10k_err(ar, "PD restart not enabled: %d\n", ret); + return ret; +} + +int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar) +{ + int i; + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + for (i = 0; i < ar_snoc->total_domains; i++) { + if (ar_snoc->service_notifier[i].handle) + service_notif_unregister_notifier( + ar_snoc->service_notifier[i].handle, + &ar_snoc->service_notifier_nb); + } + + kfree(ar_snoc->service_notifier); + + ar_snoc->service_notifier = NULL; + + return 0; +} + +static int ath10k_snoc_modem_notifier_nb(struct notifier_block *nb, + unsigned long code, + void *data) +{ + struct notif_data *notif = data; + struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc, + modem_ssr_nb); + struct ath10k *ar = ar_snoc->ar; + + if (code != SUBSYS_BEFORE_SHUTDOWN) + return NOTIFY_OK; + + if (notif->crashed) + atomic_set(&ar_snoc->fw_crashed, 1); + + ath10k_dbg(ar, ATH10K_DBG_SNOC, "Modem went down %d\n", + atomic_read(&ar_snoc->fw_crashed)); + if (notif->crashed) + queue_work(ar->workqueue, &ar->restart_work); + + return NOTIFY_OK; +} + +int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar) +{ + int ret = 0; + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + ar_snoc->modem_ssr_nb.notifier_call = ath10k_snoc_modem_notifier_nb; + + ar_snoc->modem_notify_handler = + subsys_notif_register_notifier("modem", &ar_snoc->modem_ssr_nb); + + if (IS_ERR(ar_snoc->modem_notify_handler)) { + ret = PTR_ERR(ar_snoc->modem_notify_handler); + ath10k_err(ar, "Modem register notifier failed: %d\n", ret); + } + + return ret; +} + +int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar) +{ + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + subsys_notif_unregister_notifier(ar_snoc->modem_notify_handler, + &ar_snoc->modem_ssr_nb); + ar_snoc->modem_notify_handler = NULL; + + return 0; +} + diff --git a/drivers/net/wireless/ath/ath10k/qmi.h b/drivers/net/wireless/ath/ath10k/qmi.h new file mode 100644 index 000000000000..f8ba3288753b --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/qmi.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _QMI_H_ +#define _QMI_H_ +int ath10k_snoc_pd_restart_enable(struct ath10k *ar); +int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar); +int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar); +int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar); + +#endif diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 081e44b3277a..89042dcf70a0 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -24,6 +24,7 @@ #include "htc.h" #include "ce.h" #include "snoc.h" +#include "qmi.h" #include <soc/qcom/icnss.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -96,8 +97,8 @@ static struct ce_attr host_ce_config_wlan[] = { /* CE4: host->target HTT */ { .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, - .src_nentries = 256, - .src_sz_max = 256, + .src_nentries = 2048, + .src_sz_max = 2048, .dest_nentries = 0, .send_cb = ath10k_snoc_htt_tx_cb, }, @@ -413,6 +414,20 @@ static struct ath10k_shadow_reg_cfg target_shadow_reg_cfg_map[] = { { 11, WCN3990_DST_WR_INDEX_OFFSET}, }; +static bool ath10k_snoc_has_fw_crashed(struct ath10k *ar) +{ + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + return atomic_read(&ar_snoc->fw_crashed); +} + +static void ath10k_snoc_fw_crashed_clear(struct ath10k *ar) +{ + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + + atomic_set(&ar_snoc->fw_crashed, 0); +} + void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value) { struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); @@ -655,6 +670,9 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id, "snoc tx item %d paddr %pad len %d n_items %d\n", i, &items[i].paddr, items[i].len, n_items); + if (ath10k_snoc_has_fw_crashed(ar)) + return -EINVAL; + err = ath10k_ce_send_nolock(ce_pipe, items[i].transfer_context, items[i].paddr, @@ -867,11 +885,17 @@ static void ath10k_snoc_hif_stop(struct ath10k *ar) { if (!ar) return; - ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n"); - ath10k_snoc_irq_disable(ar); + if (ath10k_snoc_has_fw_crashed(ar) || + test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) { + ath10k_snoc_free_irq(ar); + } else { + ath10k_snoc_irq_disable(ar); + } + ath10k_snoc_flush(ar); napi_synchronize(&ar->napi); napi_disable(&ar->napi); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n"); } static int ath10k_snoc_alloc_pipes(struct ath10k *ar) @@ -1002,24 +1026,28 @@ static void ath10k_snoc_free_irq(struct ath10k *ar) static int ath10k_snoc_get_soc_info(struct ath10k *ar) { - int ret; - struct icnss_soc_info soc_info; + struct resource *res; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + struct platform_device *pdev; - memset(&soc_info, 0, sizeof(soc_info)); - - ret = icnss_get_soc_info(&soc_info); - if (ret < 0) { - ath10k_err(ar, "%s: icnss_get_soc_info error = %d", - __func__, ret); - return ret; + pdev = ar_snoc->dev; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase"); + if (!res) { + ath10k_err(ar, "Memory base not found in DT\n"); + return -EINVAL; } - ar_snoc->mem = soc_info.v_addr; - ar_snoc->mem_pa = soc_info.p_addr; + ar_snoc->mem_pa = res->start; + ar_snoc->mem = devm_ioremap(&pdev->dev, ar_snoc->mem_pa, + resource_size(res)); + if (!ar_snoc->mem) { + ath10k_err(ar, "Memory base ioremap failed: phy addr: %pa\n", + &ar_snoc->mem_pa); + return -EINVAL; + } - ar_snoc->target_info.soc_version = soc_info.soc_id; - ar_snoc->target_info.target_version = soc_info.soc_id; + ar_snoc->target_info.soc_version = ATH10K_HW_WCN3990; + ar_snoc->target_info.target_version = ATH10K_HW_WCN3990; ar_snoc->target_info.target_revision = 0; ath10k_dbg(ar, ATH10K_DBG_SNOC, @@ -1034,10 +1062,8 @@ static int ath10k_snoc_get_soc_info(struct ath10k *ar) static int ath10k_snoc_wlan_enable(struct ath10k *ar) { struct icnss_wlan_enable_cfg cfg; - int pipe_num, i; + int pipe_num; struct ath10k_ce_tgt_pipe_cfg tgt_cfg[CE_COUNT_MAX]; - struct ce_tgt_pipe_cfg *tmp_tgt_cfg; - struct ce_svc_pipe_cfg *tmp_svc_cfg; for (pipe_num = 0; pipe_num < CE_COUNT_MAX; pipe_num++) { tgt_cfg[pipe_num].pipe_num = @@ -1066,12 +1092,6 @@ static int ath10k_snoc_wlan_enable(struct ath10k *ar) cfg.shadow_reg_cfg = (struct icnss_shadow_reg_cfg *) &target_shadow_reg_cfg_map; - for (i = 0; i < cfg.num_ce_tgt_cfg; i++) - tmp_tgt_cfg = cfg.ce_tgt_cfg + i; - - for (i = 0; i < cfg.num_ce_svc_pipe_cfg; i++) - tmp_svc_cfg = cfg.ce_svc_cfg + i; - return icnss_wlan_enable(&cfg, ICNSS_MISSION, "5.1.0.26N"); } @@ -1091,9 +1111,14 @@ static int ath10k_snoc_bus_configure(struct ath10k *ar) static int ath10k_snoc_hif_start(struct ath10k *ar) { - ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n"); + if (ath10k_snoc_has_fw_crashed(ar)) { + ath10k_snoc_request_irq(ar); + ath10k_snoc_fw_crashed_clear(ar); + } ath10k_snoc_irq_enable(ar); ath10k_snoc_rx_post(ar); + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n"); return 0; } @@ -1114,7 +1139,8 @@ static int ath10k_snoc_hif_power_up(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 driver state = %d\n", __func__, ar->state); - if (ar->state == ATH10K_STATE_ON) { + if (ar->state == ATH10K_STATE_ON || + test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) { ret = ath10k_snoc_bus_configure(ar); if (ret) ath10k_err(ar, "failed to configure bus: %d\n", ret); @@ -1137,6 +1163,10 @@ static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget) struct ath10k *ar = container_of(ctx, struct ath10k, napi); int done = 0; + if (ath10k_snoc_has_fw_crashed(ar)) { + napi_complete(ctx); + return done; + } ath10k_ce_per_engine_service_any(ar); done = ath10k_htt_txrx_compl_task(ar, budget); @@ -1258,6 +1288,10 @@ static int ath10k_snoc_probe(struct platform_device *pdev) ath10k_err(ar, "failed to register driver core: %d\n", ret); goto err_free_irq; } + + ath10k_snoc_modem_ssr_register_notifier(ar); + ath10k_snoc_pd_restart_enable(ar); + ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 probed\n", __func__); return 0; @@ -1286,6 +1320,8 @@ static int ath10k_snoc_remove(struct platform_device *pdev) return -EINVAL; ath10k_core_unregister(ar); + ath10k_snoc_pdr_unregister_notifier(ar); + ath10k_snoc_modem_ssr_unregister_notifier(ar); ath10k_snoc_free_irq(ar); ath10k_snoc_release_resource(ar); ath10k_snoc_free_pipes(ar); diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h index 0a5f5bff37ec..c62519b2a340 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.h +++ b/drivers/net/wireless/ath/ath10k/snoc.h @@ -16,8 +16,11 @@ #include "hw.h" #include "ce.h" #include "pci.h" +#include <soc/qcom/service-locator.h> #define ATH10K_SNOC_RX_POST_RETRY_MS 50 #define CE_POLL_PIPE 4 +#define ATH10K_SERVICE_LOCATION_CLIENT_NAME "ATH10K-WLAN" +#define ATH10K_WLAN_SERVICE_NAME "wlan/fw" /* struct snoc_state: SNOC target state * @pipe_cfg_addr: pipe configuration address @@ -88,6 +91,17 @@ struct ath10k_target_info { u32 soc_version; }; +/* struct ath10k_service_notifier_context: service notification context + * @handle: notifier handle + * @instance_id: domain instance id + * @name: domain name + */ +struct ath10k_service_notifier_context { + void *handle; + u32 instance_id; + char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1]; +}; + /* struct ath10k_snoc: SNOC info struct * @dev: device structure * @ar:ath10k base structure @@ -101,6 +115,13 @@ struct ath10k_target_info { * @rx_post_retry: rx buffer post processing timer * @vaddr_rri_on_ddr: virtual address for RRI * @is_driver_probed: flag to indicate driver state + * @modem_ssr_nb: notifier callback for modem notification + * @modem_notify_handler: modem notification handler + * @service_notifier: notifier context for service notification + * @service_notifier_nb: notifier callback for service notification + * @total_domains: no of service domains + * @get_service_nb: notifier callback for service discovery + * @fw_crashed: fw state flag */ struct ath10k_snoc { struct bus_opaque opaque_ctx; @@ -115,6 +136,13 @@ struct ath10k_snoc { u32 ce_irqs[CE_COUNT_MAX]; u32 *vaddr_rri_on_ddr; bool is_driver_probed; + struct notifier_block modem_ssr_nb; + void *modem_notify_handler; + struct ath10k_service_notifier_context *service_notifier; + struct notifier_block service_notifier_nb; + int total_domains; + struct notifier_block get_service_nb; + atomic_t fw_crashed; }; /* struct ath10k_ce_tgt_pipe_cfg: target pipe configuration @@ -171,6 +199,11 @@ struct ath10k_wlan_enable_cfg { struct ath10k_shadow_reg_cfg *shadow_reg_cfg; }; +struct ath10k_event_pd_down_data { + bool crashed; + bool fw_rejuvenate; +}; + /* enum ath10k_driver_mode: ath10k driver mode * @ATH10K_MISSION: mission mode * @ATH10K_FTM: ftm mode diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c index 7d9b0da1b010..2ffc1fe4923b 100644 --- a/drivers/net/wireless/ath/ath10k/spectral.c +++ b/drivers/net/wireless/ath/ath10k/spectral.c @@ -338,7 +338,7 @@ static ssize_t write_file_spec_scan_ctl(struct file *file, } else { res = -EINVAL; } - } else if (strncmp("background", buf, 9) == 0) { + } else if (strncmp("background", buf, 10) == 0) { res = ath10k_spectral_scan_config(ar, SPECTRAL_BACKGROUND); } else if (strncmp("manual", buf, 6) == 0) { res = ath10k_spectral_scan_config(ar, SPECTRAL_MANUAL); diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 3fef967ca1ad..75f2528b8b84 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -553,7 +553,7 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb) ath10k_wmi_tlv_event_tx_pause(ar, skb); break; default: - ath10k_warn(ar, "Unknown eventid: %d\n", id); + ath10k_dbg(ar, ATH10K_DBG_WMI, "Unknown eventid: %d\n", id); break; } diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 4dd60b74853d..cbbba8d79e6b 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1799,6 +1799,9 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id) { int ret = -EOPNOTSUPP; + if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) + return -ESHUTDOWN; + might_sleep(); if (cmd_id == WMI_CMD_UNSUPPORTED) { diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index dc44cfef7517..16e052d02c94 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -502,8 +502,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, break; return -EOPNOTSUPP; default: - WARN_ON(1); - return -EINVAL; + return -EOPNOTSUPP; } mutex_lock(&ah->lock); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h index 694ca2e680e5..74670e08e6da 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h @@ -73,13 +73,13 @@ #define AR9300_OTP_BASE \ ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x30000 : 0x14000) #define AR9300_OTP_STATUS \ - ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x30018 : 0x15f18) + ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x31018 : 0x15f18) #define AR9300_OTP_STATUS_TYPE 0x7 #define AR9300_OTP_STATUS_VALID 0x4 #define AR9300_OTP_STATUS_ACCESS_BUSY 0x2 #define AR9300_OTP_STATUS_SM_BUSY 0x1 #define AR9300_OTP_READ_DATA \ - ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x3001c : 0x15f1c) + ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x3101c : 0x15f1c) enum targetPowerHTRates { HT_TARGET_RATE_0_8_16, diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index b42f4a963ef4..a660e40f2df1 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -959,6 +959,7 @@ struct ath_softc { struct survey_info *cur_survey; struct survey_info survey[ATH9K_NUM_CHANNELS]; + spinlock_t intr_lock; struct tasklet_struct intr_tq; struct tasklet_struct bcon_tasklet; struct ath_hw *sc_ah; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index bc70ce62bc03..0f5672f5c9ba 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -619,6 +619,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, common->bt_ant_diversity = 1; spin_lock_init(&common->cc_lock); + spin_lock_init(&sc->intr_lock); spin_lock_init(&sc->sc_serial_rw); spin_lock_init(&sc->sc_pm_lock); spin_lock_init(&sc->chan_lock); diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index bba85d1a6cd1..d937c39b3a0b 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -805,21 +805,12 @@ void ath9k_hw_disable_interrupts(struct ath_hw *ah) } EXPORT_SYMBOL(ath9k_hw_disable_interrupts); -void ath9k_hw_enable_interrupts(struct ath_hw *ah) +static void __ath9k_hw_enable_interrupts(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); u32 sync_default = AR_INTR_SYNC_DEFAULT; u32 async_mask; - if (!(ah->imask & ATH9K_INT_GLOBAL)) - return; - - if (!atomic_inc_and_test(&ah->intr_ref_cnt)) { - ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n", - atomic_read(&ah->intr_ref_cnt)); - return; - } - if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) || AR_SREV_9561(ah)) sync_default &= ~AR_INTR_SYNC_HOST1_FATAL; @@ -841,6 +832,39 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah) ath_dbg(common, INTERRUPT, "AR_IMR 0x%x IER 0x%x\n", REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER)); } + +void ath9k_hw_resume_interrupts(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + + if (!(ah->imask & ATH9K_INT_GLOBAL)) + return; + + if (atomic_read(&ah->intr_ref_cnt) != 0) { + ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n", + atomic_read(&ah->intr_ref_cnt)); + return; + } + + __ath9k_hw_enable_interrupts(ah); +} +EXPORT_SYMBOL(ath9k_hw_resume_interrupts); + +void ath9k_hw_enable_interrupts(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + + if (!(ah->imask & ATH9K_INT_GLOBAL)) + return; + + if (!atomic_inc_and_test(&ah->intr_ref_cnt)) { + ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n", + atomic_read(&ah->intr_ref_cnt)); + return; + } + + __ath9k_hw_enable_interrupts(ah); +} EXPORT_SYMBOL(ath9k_hw_enable_interrupts); void ath9k_hw_set_interrupts(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 7fbf7f965f61..1b63d26f30ce 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -748,6 +748,7 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah); void ath9k_hw_enable_interrupts(struct ath_hw *ah); void ath9k_hw_disable_interrupts(struct ath_hw *ah); void ath9k_hw_kill_interrupts(struct ath_hw *ah); +void ath9k_hw_resume_interrupts(struct ath_hw *ah); void ar9002_hw_attach_mac_ops(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 7be31f27266c..3abc64574116 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -373,21 +373,20 @@ void ath9k_tasklet(unsigned long data) struct ath_common *common = ath9k_hw_common(ah); enum ath_reset_type type; unsigned long flags; - u32 status = sc->intrstatus; + u32 status; u32 rxmask; + spin_lock_irqsave(&sc->intr_lock, flags); + status = sc->intrstatus; + sc->intrstatus = 0; + spin_unlock_irqrestore(&sc->intr_lock, flags); + ath9k_ps_wakeup(sc); spin_lock(&sc->sc_pcu_lock); if (status & ATH9K_INT_FATAL) { type = RESET_TYPE_FATAL_INT; ath9k_queue_reset(sc, type); - - /* - * Increment the ref. counter here so that - * interrupts are enabled in the reset routine. - */ - atomic_inc(&ah->intr_ref_cnt); ath_dbg(common, RESET, "FATAL: Skipping interrupts\n"); goto out; } @@ -403,11 +402,6 @@ void ath9k_tasklet(unsigned long data) type = RESET_TYPE_BB_WATCHDOG; ath9k_queue_reset(sc, type); - /* - * Increment the ref. counter here so that - * interrupts are enabled in the reset routine. - */ - atomic_inc(&ah->intr_ref_cnt); ath_dbg(common, RESET, "BB_WATCHDOG: Skipping interrupts\n"); goto out; @@ -420,7 +414,6 @@ void ath9k_tasklet(unsigned long data) if ((sc->gtt_cnt >= MAX_GTT_CNT) && !ath9k_hw_check_alive(ah)) { type = RESET_TYPE_TX_GTT; ath9k_queue_reset(sc, type); - atomic_inc(&ah->intr_ref_cnt); ath_dbg(common, RESET, "GTT: Skipping interrupts\n"); goto out; @@ -477,7 +470,7 @@ void ath9k_tasklet(unsigned long data) ath9k_btcoex_handle_interrupt(sc, status); /* re-enable hardware interrupt */ - ath9k_hw_enable_interrupts(ah); + ath9k_hw_resume_interrupts(ah); out: spin_unlock(&sc->sc_pcu_lock); ath9k_ps_restore(sc); @@ -541,7 +534,9 @@ irqreturn_t ath_isr(int irq, void *dev) return IRQ_NONE; /* Cache the status */ - sc->intrstatus = status; + spin_lock(&sc->intr_lock); + sc->intrstatus |= status; + spin_unlock(&sc->intr_lock); if (status & SCHED_INTR) sched = true; @@ -587,7 +582,7 @@ chip_reset: if (sched) { /* turn off every interrupt */ - ath9k_hw_disable_interrupts(ah); + ath9k_hw_kill_interrupts(ah); tasklet_schedule(&sc->intr_tq); } diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 7cdaf40c3057..ea7b8c25955f 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -27,7 +27,6 @@ static const struct pci_device_id ath_pci_id_table[] = { { PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */ - { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ #ifdef CONFIG_ATH9K_PCOEM /* Mini PCI AR9220 MB92 cards: Compex WLM200NX, Wistron DNMA-92 */ @@ -38,7 +37,7 @@ static const struct pci_device_id ath_pci_id_table[] = { .driver_data = ATH9K_PCI_LED_ACT_HI }, #endif - { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ + { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ #ifdef CONFIG_ATH9K_PCOEM { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, @@ -86,7 +85,11 @@ static const struct pci_device_id ath_pci_id_table[] = { 0x10CF, /* Fujitsu */ 0x1536), .driver_data = ATH9K_PCI_D3_L1_WAR }, +#endif + { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ + +#ifdef CONFIG_ATH9K_PCOEM /* AR9285 card for Asus */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x002B, diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index fe6d5ab7f95b..958c96b75fbb 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -891,6 +891,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) if (wil->hw_version == HW_VER_UNKNOWN) return -ENODEV; + wil_dbg_misc(wil, "Prevent DS in BL & mark FW to set T_POWER_ON=0\n"); + wil_s(wil, RGF_USER_USAGE_8, BIT_USER_PREVENT_DEEP_SLEEP | + BIT_USER_SUPPORT_T_POWER_ON_0); + if (wil->platform_ops.notify) { rc = wil->platform_ops.notify(wil->platform_handle, WIL_PLATFORM_EVT_PRE_RESET); @@ -993,6 +997,9 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) return rc; } + if (wil->tt_data_set) + wmi_set_tt_cfg(wil, &wil->tt_data); + wil_collect_fw_info(wil); if (wil->platform_ops.notify) { diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index a0acb2d0cb79..7260bef314a4 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -80,12 +80,20 @@ int wil_suspend(struct wil6210_priv *wil, bool is_runtime) } } - if (wil->platform_ops.suspend) + /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */ + wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n"); + wil_disable_irq(wil); + + if (wil->platform_ops.suspend) { rc = wil->platform_ops.suspend(wil->platform_handle); + if (rc) + wil_enable_irq(wil); + } out: wil_dbg_pm(wil, "suspend: %s => %d\n", is_runtime ? "runtime" : "system", rc); + return rc; } @@ -104,6 +112,9 @@ int wil_resume(struct wil6210_priv *wil, bool is_runtime) } } + wil_dbg_pm(wil, "Enabling PCIe IRQ\n"); + wil_enable_irq(wil); + /* if netif up, bring hardware up * During open(), IFF_UP set after actual device method * invocation. This prevent recursive call to wil_up() diff --git a/drivers/net/wireless/ath/wil6210/sysfs.c b/drivers/net/wireless/ath/wil6210/sysfs.c index 0faa26ca41c9..a3689738a070 100644 --- a/drivers/net/wireless/ath/wil6210/sysfs.c +++ b/drivers/net/wireless/ath/wil6210/sysfs.c @@ -94,8 +94,119 @@ static DEVICE_ATTR(ftm_txrx_offset, 0644, wil_ftm_txrx_offset_sysfs_show, wil_ftm_txrx_offset_sysfs_store); +static ssize_t +wil_tt_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct wil6210_priv *wil = dev_get_drvdata(dev); + ssize_t len; + struct wmi_tt_data tt_data; + int i, rc; + + rc = wmi_get_tt_cfg(wil, &tt_data); + if (rc) + return rc; + + len = snprintf(buf, PAGE_SIZE, " high max critical\n"); + + len += snprintf(buf + len, PAGE_SIZE - len, "bb: "); + if (tt_data.bb_enabled) + for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) + len += snprintf(buf + len, PAGE_SIZE - len, + "%03d-%03d ", + tt_data.bb_zones[i].temperature_high, + tt_data.bb_zones[i].temperature_low); + else + len += snprintf(buf + len, PAGE_SIZE - len, "* disabled *"); + len += snprintf(buf + len, PAGE_SIZE - len, "\nrf: "); + if (tt_data.rf_enabled) + for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) + len += snprintf(buf + len, PAGE_SIZE - len, + "%03d-%03d ", + tt_data.rf_zones[i].temperature_high, + tt_data.rf_zones[i].temperature_low); + else + len += snprintf(buf + len, PAGE_SIZE - len, "* disabled *"); + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + + return len; +} + +static ssize_t +wil_tt_sysfs_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wil6210_priv *wil = dev_get_drvdata(dev); + int i, rc = -EINVAL; + char *token, *dupbuf, *tmp; + struct wmi_tt_data tt_data = { + .bb_enabled = 0, + .rf_enabled = 0, + }; + + tmp = kmemdup(buf, count + 1, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + tmp[count] = '\0'; + dupbuf = tmp; + + /* Format for writing is 12 unsigned bytes separated by spaces: + * <bb_z1_h> <bb_z1_l> <bb_z2_h> <bb_z2_l> <bb_z3_h> <bb_z3_l> \ + * <rf_z1_h> <rf_z1_l> <rf_z2_h> <rf_z2_l> <rf_z3_h> <rf_z3_l> + * To disable thermal throttling for bb or for rf, use 0 for all + * its six set points. + */ + + /* bb */ + for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) { + token = strsep(&dupbuf, " "); + if (!token) + goto out; + if (kstrtou8(token, 0, &tt_data.bb_zones[i].temperature_high)) + goto out; + token = strsep(&dupbuf, " "); + if (!token) + goto out; + if (kstrtou8(token, 0, &tt_data.bb_zones[i].temperature_low)) + goto out; + + if (tt_data.bb_zones[i].temperature_high > 0 || + tt_data.bb_zones[i].temperature_low > 0) + tt_data.bb_enabled = 1; + } + /* rf */ + for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) { + token = strsep(&dupbuf, " "); + if (!token) + goto out; + if (kstrtou8(token, 0, &tt_data.rf_zones[i].temperature_high)) + goto out; + token = strsep(&dupbuf, " "); + if (!token) + goto out; + if (kstrtou8(token, 0, &tt_data.rf_zones[i].temperature_low)) + goto out; + + if (tt_data.rf_zones[i].temperature_high > 0 || + tt_data.rf_zones[i].temperature_low > 0) + tt_data.rf_enabled = 1; + } + + rc = wmi_set_tt_cfg(wil, &tt_data); + if (rc) + goto out; + + rc = count; +out: + kfree(tmp); + return rc; +} + +static DEVICE_ATTR(thermal_throttling, 0644, + wil_tt_sysfs_show, wil_tt_sysfs_store); + static struct attribute *wil6210_sysfs_entries[] = { &dev_attr_ftm_txrx_offset.attr, + &dev_attr_thermal_throttling.attr, NULL }; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index fcfbcf7fbd7d..f64323b03a3b 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -141,6 +141,9 @@ struct RGF_ICR { #define RGF_USER_USAGE_1 (0x880004) #define RGF_USER_USAGE_6 (0x880018) #define BIT_USER_OOB_MODE BIT(31) +#define RGF_USER_USAGE_8 (0x880020) + #define BIT_USER_PREVENT_DEEP_SLEEP BIT(0) + #define BIT_USER_SUPPORT_T_POWER_ON_0 BIT(1) #define RGF_USER_HW_MACHINE_STATE (0x8801dc) #define HW_MACHINE_BOOT_DONE (0x3fffffd) #define RGF_USER_USER_CPU_0 (0x8801e0) @@ -693,6 +696,8 @@ struct wil6210_priv { struct wil_halp halp; struct wil_ftm_priv ftm; + bool tt_data_set; + struct wmi_tt_data tt_data; #ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP @@ -851,6 +856,8 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short); int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short); int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid); +int wmi_set_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data); +int wmi_get_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data); int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid, u8 dialog_token, __le16 ba_param_set, __le16 ba_timeout, __le16 ba_seq_ctrl); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index bccd27dcb42b..6a6ba02beba0 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1763,6 +1763,67 @@ int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid) return rc; } +int wmi_set_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data) +{ + int rc; + struct wmi_set_thermal_throttling_cfg_cmd cmd = { + .tt_data = *tt_data, + }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_set_thermal_throttling_cfg_event evt; + } __packed reply; + + if (!test_bit(WMI_FW_CAPABILITY_THERMAL_THROTTLING, + wil->fw_capabilities)) + return -EOPNOTSUPP; + + memset(&reply, 0, sizeof(reply)); + rc = wmi_call(wil, WMI_SET_THERMAL_THROTTLING_CFG_CMDID, &cmd, + sizeof(cmd), WMI_SET_THERMAL_THROTTLING_CFG_EVENTID, + &reply, sizeof(reply), 100); + if (rc) { + wil_err(wil, "failed to set thermal throttling\n"); + return rc; + } + if (reply.evt.status) { + wil_err(wil, "set thermal throttling failed, error %d\n", + reply.evt.status); + return -EIO; + } + + wil->tt_data = *tt_data; + wil->tt_data_set = true; + + return 0; +} + +int wmi_get_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data) +{ + int rc; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_get_thermal_throttling_cfg_event evt; + } __packed reply; + + if (!test_bit(WMI_FW_CAPABILITY_THERMAL_THROTTLING, + wil->fw_capabilities)) + return -EOPNOTSUPP; + + rc = wmi_call(wil, WMI_GET_THERMAL_THROTTLING_CFG_CMDID, NULL, 0, + WMI_GET_THERMAL_THROTTLING_CFG_EVENTID, &reply, + sizeof(reply), 100); + if (rc) { + wil_err(wil, "failed to get thermal throttling\n"); + return rc; + } + + if (tt_data) + *tt_data = reply.evt.tt_data; + + return 0; +} + void wmi_event_flush(struct wil6210_priv *wil) { ulong flags; diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 7c9fee57aa91..f7f5f4f801e3 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -58,6 +58,7 @@ enum wmi_fw_capability { WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT = 3, WMI_FW_CAPABILITY_DISABLE_AP_SME = 4, WMI_FW_CAPABILITY_WMI_ONLY = 5, + WMI_FW_CAPABILITY_THERMAL_THROTTLING = 7, WMI_FW_CAPABILITY_MAX, }; @@ -142,8 +143,6 @@ enum wmi_command_id { WMI_MAINTAIN_RESUME_CMDID = 0x851, WMI_RS_MGMT_CMDID = 0x852, WMI_RF_MGMT_CMDID = 0x853, - WMI_THERMAL_THROTTLING_CTRL_CMDID = 0x854, - WMI_THERMAL_THROTTLING_GET_STATUS_CMDID = 0x855, WMI_OTP_READ_CMDID = 0x856, WMI_OTP_WRITE_CMDID = 0x857, WMI_LED_CFG_CMDID = 0x858, @@ -192,6 +191,8 @@ enum wmi_command_id { WMI_GET_MGMT_RETRY_LIMIT_CMDID = 0x931, WMI_NEW_STA_CMDID = 0x935, WMI_DEL_STA_CMDID = 0x936, + WMI_SET_THERMAL_THROTTLING_CFG_CMDID = 0x940, + WMI_GET_THERMAL_THROTTLING_CFG_CMDID = 0x941, WMI_TOF_SESSION_START_CMDID = 0x991, WMI_TOF_GET_CAPABILITIES_CMDID = 0x992, WMI_TOF_SET_LCR_CMDID = 0x993, @@ -438,16 +439,6 @@ struct wmi_rf_mgmt_cmd { __le32 rf_mgmt_type; } __packed; -/* WMI_THERMAL_THROTTLING_CTRL_CMDID */ -#define THERMAL_THROTTLING_USE_DEFAULT_MAX_TXOP_LENGTH (0xFFFFFFFF) - -/* WMI_THERMAL_THROTTLING_CTRL_CMDID */ -struct wmi_thermal_throttling_ctrl_cmd { - __le32 time_on_usec; - __le32 time_off_usec; - __le32 max_txop_length_usec; -} __packed; - /* WMI_RF_RX_TEST_CMDID */ struct wmi_rf_rx_test_cmd { __le32 sector; @@ -549,7 +540,7 @@ struct wmi_pcp_start_cmd { u8 hidden_ssid; u8 is_go; u8 reserved0[5]; - /* abft_len override if non-0 */ + /* A-BFT length override if non-0 */ u8 abft_len; u8 disable_ap_sme; u8 network_type; @@ -910,6 +901,39 @@ struct wmi_set_mgmt_retry_limit_cmd { u8 reserved[3]; } __packed; +/* Zones: HIGH, MAX, CRITICAL */ +#define WMI_NUM_OF_TT_ZONES (3) + +struct wmi_tt_zone_limits { + /* Above this temperature this zone is active */ + u8 temperature_high; + /* Below this temperature the adjacent lower zone is active */ + u8 temperature_low; + u8 reserved[2]; +} __packed; + +/* Struct used for both configuration and status commands of thermal + * throttling + */ +struct wmi_tt_data { + /* Enable/Disable TT algorithm for baseband */ + u8 bb_enabled; + u8 reserved0[3]; + /* Define zones for baseband */ + struct wmi_tt_zone_limits bb_zones[WMI_NUM_OF_TT_ZONES]; + /* Enable/Disable TT algorithm for radio */ + u8 rf_enabled; + u8 reserved1[3]; + /* Define zones for all radio chips */ + struct wmi_tt_zone_limits rf_zones[WMI_NUM_OF_TT_ZONES]; +} __packed; + +/* WMI_SET_THERMAL_THROTTLING_CFG_CMDID */ +struct wmi_set_thermal_throttling_cfg_cmd { + /* Command data */ + struct wmi_tt_data tt_data; +} __packed; + /* WMI_NEW_STA_CMDID */ struct wmi_new_sta_cmd { u8 dst_mac[WMI_MAC_LEN]; @@ -1040,7 +1064,6 @@ enum wmi_event_id { WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, WMI_RS_MGMT_DONE_EVENTID = 0x1852, WMI_RF_MGMT_STATUS_EVENTID = 0x1853, - WMI_THERMAL_THROTTLING_STATUS_EVENTID = 0x1855, WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838, WMI_RX_MGMT_PACKET_EVENTID = 0x1840, WMI_TX_MGMT_PACKET_EVENTID = 0x1841, @@ -1090,6 +1113,8 @@ enum wmi_event_id { WMI_BRP_SET_ANT_LIMIT_EVENTID = 0x1924, WMI_SET_MGMT_RETRY_LIMIT_EVENTID = 0x1930, WMI_GET_MGMT_RETRY_LIMIT_EVENTID = 0x1931, + WMI_SET_THERMAL_THROTTLING_CFG_EVENTID = 0x1940, + WMI_GET_THERMAL_THROTTLING_CFG_EVENTID = 0x1941, WMI_TOF_SESSION_END_EVENTID = 0x1991, WMI_TOF_GET_CAPABILITIES_EVENTID = 0x1992, WMI_TOF_SET_LCR_EVENTID = 0x1993, @@ -1133,13 +1158,6 @@ struct wmi_rf_mgmt_status_event { __le32 rf_status; } __packed; -/* WMI_THERMAL_THROTTLING_STATUS_EVENTID */ -struct wmi_thermal_throttling_status_event { - __le32 time_on_usec; - __le32 time_off_usec; - __le32 max_txop_length_usec; -} __packed; - /* WMI_GET_STATUS_DONE_EVENTID */ struct wmi_get_status_done_event { __le32 is_associated; @@ -2206,6 +2224,19 @@ struct wmi_tof_get_capabilities_event { __le32 aoa_supported_types; } __packed; +/* WMI_SET_THERMAL_THROTTLING_CFG_EVENTID */ +struct wmi_set_thermal_throttling_cfg_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + +/* WMI_GET_THERMAL_THROTTLING_CFG_EVENTID */ +struct wmi_get_thermal_throttling_cfg_event { + /* Status data */ + struct wmi_tt_data tt_data; +} __packed; + enum wmi_tof_session_end_status { WMI_TOF_SESSION_END_NO_ERROR = 0x00, WMI_TOF_SESSION_END_FAIL = 0x01, diff --git a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c index 16f64e9fb8d3..913f756f9520 100644 --- a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c +++ b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c @@ -98,6 +98,7 @@ static struct wcnss_prealloc wcnss_allocs[] = { {0, 64 * 1024, NULL}, {0, 64 * 1024, NULL}, {0, 64 * 1024, NULL}, + {0, 64 * 1024, NULL}, {0, 128 * 1024, NULL}, {0, 128 * 1024, NULL}, }; diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index 7a40d8dffa36..aab752328c26 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -1303,12 +1303,13 @@ EXPORT_SYMBOL_GPL(rtl_action_proc); static void setup_arp_tx(struct rtl_priv *rtlpriv, struct rtl_ps_ctl *ppsc) { + struct ieee80211_hw *hw = rtlpriv->hw; + rtlpriv->ra.is_special_data = true; if (rtlpriv->cfg->ops->get_btc_status()) rtlpriv->btcoexist.btc_ops->btc_special_packet_notify( rtlpriv, 1); - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); + rtl_lps_leave(hw); ppsc->last_delaylps_stamp_jiffies = jiffies; } @@ -1381,8 +1382,7 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx, if (is_tx) { rtlpriv->ra.is_special_data = true; - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); + rtl_lps_leave(hw); ppsc->last_delaylps_stamp_jiffies = jiffies; } diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index a4527880b2b7..8b537a5a4b01 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -1153,10 +1153,8 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, } else { mstatus = RT_MEDIA_DISCONNECT; - if (mac->link_state == MAC80211_LINKED) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); - } + if (mac->link_state == MAC80211_LINKED) + rtl_lps_leave(hw); if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE) rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); mac->link_state = MAC80211_NOLINK; @@ -1434,8 +1432,7 @@ static void rtl_op_sw_scan_start(struct ieee80211_hw *hw, } if (mac->link_state == MAC80211_LINKED) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); + rtl_lps_leave(hw); mac->link_state = MAC80211_LINKED_SCANNING; } else { rtl_ips_nic_on(hw); diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 5b4048041147..a52230377e2c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -664,11 +664,9 @@ tx_status_ok: } if (((rtlpriv->link_info.num_rx_inperiod + - rtlpriv->link_info.num_tx_inperiod) > 8) || - (rtlpriv->link_info.num_rx_inperiod > 2)) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); - } + rtlpriv->link_info.num_tx_inperiod) > 8) || + (rtlpriv->link_info.num_rx_inperiod > 2)) + rtl_lps_leave(hw); } static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw, @@ -919,10 +917,8 @@ new_trx_end: } if (((rtlpriv->link_info.num_rx_inperiod + rtlpriv->link_info.num_tx_inperiod) > 8) || - (rtlpriv->link_info.num_rx_inperiod > 2)) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); - } + (rtlpriv->link_info.num_rx_inperiod > 2)) + rtl_lps_leave(hw); skb = new_skb; no_new: if (rtlpriv->use_new_trx_flow) { diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.h b/drivers/net/wireless/realtek/rtlwifi/pci.h index 5da6703942d9..672f81ea02d0 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.h +++ b/drivers/net/wireless/realtek/rtlwifi/pci.h @@ -275,10 +275,10 @@ struct mp_adapter { }; struct rtl_pci_priv { + struct bt_coexist_info bt_coexist; + struct rtl_led_ctl ledctl; struct rtl_pci dev; struct mp_adapter ndis_adapter; - struct rtl_led_ctl ledctl; - struct bt_coexist_info bt_coexist; }; #define rtl_pcipriv(hw) (((struct rtl_pci_priv *)(rtl_priv(hw))->priv)) diff --git a/drivers/net/wireless/realtek/rtlwifi/ps.c b/drivers/net/wireless/realtek/rtlwifi/ps.c index b69321d45f04..626ff300352b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/ps.c +++ b/drivers/net/wireless/realtek/rtlwifi/ps.c @@ -414,8 +414,8 @@ void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) } } -/*Enter the leisure power save mode.*/ -void rtl_lps_enter(struct ieee80211_hw *hw) +/* Interrupt safe routine to enter the leisure power save mode.*/ +static void rtl_lps_enter_core(struct ieee80211_hw *hw) { struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); @@ -455,10 +455,9 @@ void rtl_lps_enter(struct ieee80211_hw *hw) spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); } -EXPORT_SYMBOL(rtl_lps_enter); -/*Leave the leisure power save mode.*/ -void rtl_lps_leave(struct ieee80211_hw *hw) +/* Interrupt safe routine to leave the leisure power save mode.*/ +static void rtl_lps_leave_core(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); @@ -488,7 +487,6 @@ void rtl_lps_leave(struct ieee80211_hw *hw) } spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); } -EXPORT_SYMBOL(rtl_lps_leave); /* For sw LPS*/ void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len) @@ -681,12 +679,34 @@ void rtl_lps_change_work_callback(struct work_struct *work) struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->enter_ps) - rtl_lps_enter(hw); + rtl_lps_enter_core(hw); else - rtl_lps_leave(hw); + rtl_lps_leave_core(hw); } EXPORT_SYMBOL_GPL(rtl_lps_change_work_callback); +void rtl_lps_enter(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (!in_interrupt()) + return rtl_lps_enter_core(hw); + rtlpriv->enter_ps = true; + schedule_work(&rtlpriv->works.lps_change_work); +} +EXPORT_SYMBOL_GPL(rtl_lps_enter); + +void rtl_lps_leave(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (!in_interrupt()) + return rtl_lps_leave_core(hw); + rtlpriv->enter_ps = false; + schedule_work(&rtlpriv->works.lps_change_work); +} +EXPORT_SYMBOL_GPL(rtl_lps_leave); + void rtl_swlps_wq_callback(void *data) { struct rtl_works *rtlworks = container_of_dwork_rtl(data, diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c index 5f14308e8eb3..b1601441991d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c @@ -1003,7 +1003,7 @@ static void _rtl92ee_hw_configure(struct ieee80211_hw *hw) rtl_write_word(rtlpriv, REG_SIFS_TRX, 0x100a); /* Note Data sheet don't define */ - rtl_write_word(rtlpriv, 0x4C7, 0x80); + rtl_write_byte(rtlpriv, 0x4C7, 0x80); rtl_write_byte(rtlpriv, REG_RX_PKT_LIMIT, 0x20); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index bbb789f8990b..c2103e7a8132 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -1127,7 +1127,7 @@ static u8 _rtl8821ae_dbi_read(struct rtl_priv *rtlpriv, u16 addr) } if (0 == tmp) { read_addr = REG_DBI_RDATA + addr % 4; - ret = rtl_read_word(rtlpriv, read_addr); + ret = rtl_read_byte(rtlpriv, read_addr); } return ret; } diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c index aac1ed3f7bb4..ad8390d2997b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.c +++ b/drivers/net/wireless/realtek/rtlwifi/usb.c @@ -834,12 +834,30 @@ static void rtl_usb_stop(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); + struct urb *urb; /* should after adapter start and interrupt enable. */ set_hal_stop(rtlhal); cancel_work_sync(&rtlpriv->works.fill_h2c_cmd); /* Enable software */ SET_USB_STOP(rtlusb); + + /* free pre-allocated URBs from rtl_usb_start() */ + usb_kill_anchored_urbs(&rtlusb->rx_submitted); + + tasklet_kill(&rtlusb->rx_work_tasklet); + cancel_work_sync(&rtlpriv->works.lps_change_work); + + flush_workqueue(rtlpriv->works.rtl_wq); + + skb_queue_purge(&rtlusb->rx_queue); + + while ((urb = usb_get_from_anchor(&rtlusb->rx_cleanup_urbs))) { + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); + } + rtlpriv->cfg->ops->hw_disable(hw); } @@ -1073,6 +1091,7 @@ int rtl_usb_probe(struct usb_interface *intf, return -ENOMEM; } rtlpriv = hw->priv; + rtlpriv->hw = hw; rtlpriv->usb_data = kzalloc(RTL_USB_MAX_RX_COUNT * sizeof(u32), GFP_KERNEL); if (!rtlpriv->usb_data) diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.h b/drivers/net/wireless/realtek/rtlwifi/usb.h index 685273ca9561..441c4412130c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.h +++ b/drivers/net/wireless/realtek/rtlwifi/usb.h @@ -150,8 +150,9 @@ struct rtl_usb { }; struct rtl_usb_priv { - struct rtl_usb dev; + struct bt_coexist_info bt_coexist; struct rtl_led_ctl ledctl; + struct rtl_usb dev; }; #define rtl_usbpriv(hw) (((struct rtl_usb_priv *)(rtl_priv(hw))->priv)) diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index d6abf191122a..1f445f357da1 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1391,6 +1391,8 @@ static void xennet_disconnect_backend(struct netfront_info *info) for (i = 0; i < num_queues && info->queues; ++i) { struct netfront_queue *queue = &info->queues[i]; + del_timer_sync(&queue->rx_refill_timer); + if (queue->tx_irq && (queue->tx_irq == queue->rx_irq)) unbind_from_irqhandler(queue->tx_irq, queue); if (queue->tx_irq && (queue->tx_irq != queue->rx_irq)) { @@ -1745,7 +1747,6 @@ static void xennet_destroy_queues(struct netfront_info *info) if (netif_running(info->netdev)) napi_disable(&queue->napi); - del_timer_sync(&queue->rx_refill_timer); netif_napi_del(&queue->napi); } diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c index 5b58475ae489..f4ad5bde06a8 100644 --- a/drivers/nfc/nq-nci.c +++ b/drivers/nfc/nq-nci.c @@ -267,7 +267,7 @@ static ssize_t nfc_write(struct file *filp, const char __user *buf, ret = i2c_master_send(nqx_dev->client, tmp, count); if (ret != count) { - dev_err(&nqx_dev->client->dev, + dev_dbg(&nqx_dev->client->dev, "%s: failed to write %d\n", __func__, ret); ret = -EIO; goto out_free; @@ -304,7 +304,7 @@ static int nqx_standby_write(struct nqx_dev *nqx_dev, for (retry_cnt = 1; retry_cnt <= MAX_RETRY_COUNT; retry_cnt++) { ret = i2c_master_send(nqx_dev->client, buf, len); if (ret < 0) { - dev_err(&nqx_dev->client->dev, + dev_dbg(&nqx_dev->client->dev, "%s: write failed, Maybe in Standby Mode - Retry(%d)\n", __func__, retry_cnt); usleep_range(1000, 1100); diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 60654d524858..ecc6fb9ca92f 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -1623,7 +1623,7 @@ ntb_transport_create_queue(void *data, struct device *client_dev, node = dev_to_node(&ndev->dev); - free_queue = ffs(nt->qp_bitmap); + free_queue = ffs(nt->qp_bitmap_free); if (!free_queue) goto err; @@ -2082,9 +2082,8 @@ module_init(ntb_transport_init); static void __exit ntb_transport_exit(void) { - debugfs_remove_recursive(nt_debugfs_dir); - ntb_unregister_client(&ntb_transport_client); bus_unregister(&ntb_transport_bus); + debugfs_remove_recursive(nt_debugfs_dir); } module_exit(ntb_transport_exit); diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index 62120c38d56b..aae7379af4e4 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -1534,6 +1534,7 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) static int find_pmem_label_set(struct nd_region *nd_region, struct nd_namespace_pmem *nspm) { + u64 altcookie = nd_region_interleave_set_altcookie(nd_region); u64 cookie = nd_region_interleave_set_cookie(nd_region); struct nd_namespace_label *nd_label; u8 select_id[NSLABEL_UUID_LEN]; @@ -1542,8 +1543,10 @@ static int find_pmem_label_set(struct nd_region *nd_region, int rc = -ENODEV, l; u16 i; - if (cookie == 0) + if (cookie == 0) { + dev_dbg(&nd_region->dev, "invalid interleave-set-cookie\n"); return -ENXIO; + } /* * Find a complete set of labels by uuid. By definition we can start @@ -1552,13 +1555,24 @@ static int find_pmem_label_set(struct nd_region *nd_region, for_each_label(l, nd_label, nd_region->mapping[0].labels) { u64 isetcookie = __le64_to_cpu(nd_label->isetcookie); - if (isetcookie != cookie) - continue; + if (isetcookie != cookie) { + dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n", + nd_label->uuid); + if (isetcookie != altcookie) + continue; + + dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n", + nd_label->uuid); + } + + for (i = 0; nd_region->ndr_mappings; i++) { + if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i)) + continue; + if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i)) + continue; + break; + } - for (i = 0; nd_region->ndr_mappings; i++) - if (!has_uuid_at_pos(nd_region, nd_label->uuid, - cookie, i)) - break; if (i < nd_region->ndr_mappings) { /* * Give up if we don't find an instance of a diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 417e521d299c..fc870e55bb66 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -245,6 +245,7 @@ struct nd_region *to_nd_region(struct device *dev); int nd_region_to_nstype(struct nd_region *nd_region); int nd_region_register_namespaces(struct nd_region *nd_region, int *err); u64 nd_region_interleave_set_cookie(struct nd_region *nd_region); +u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region); void nvdimm_bus_lock(struct device *dev); void nvdimm_bus_unlock(struct device *dev); bool is_nvdimm_bus_locked(struct device *dev); diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 9521696c9385..dc2e919daa39 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -379,6 +379,15 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region) return 0; } +u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region) +{ + struct nd_interleave_set *nd_set = nd_region->nd_set; + + if (nd_set) + return nd_set->altcookie; + return 0; +} + /* * Upon successful probe/remove, take/release a reference on the * associated interleave set (if present), and plant new btt + namespace diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c index 584ad96c703f..6e9a8649ee2f 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.c @@ -2511,6 +2511,48 @@ int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base, } EXPORT_SYMBOL(msm_pcie_debug_info); +#ifdef CONFIG_SYSFS +static ssize_t msm_pcie_enumerate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msm_pcie_dev_t *pcie_dev = (struct msm_pcie_dev_t *) + dev_get_drvdata(dev); + + if (pcie_dev) + msm_pcie_enumerate(pcie_dev->rc_idx); + + return count; +} + +static DEVICE_ATTR(enumerate, S_IWUSR, NULL, msm_pcie_enumerate_store); + +static void msm_pcie_sysfs_init(struct msm_pcie_dev_t *dev) +{ + int ret; + + ret = device_create_file(&dev->pdev->dev, &dev_attr_enumerate); + if (ret) + PCIE_DBG_FS(dev, + "RC%d: failed to create sysfs enumerate node\n", + dev->rc_idx); +} + +static void msm_pcie_sysfs_exit(struct msm_pcie_dev_t *dev) +{ + if (dev->pdev) + device_remove_file(&dev->pdev->dev, &dev_attr_enumerate); +} +#else +static void msm_pcie_sysfs_init(struct msm_pcie_dev_t *dev) +{ +} + +static void msm_pcie_sysfs_exit(struct msm_pcie_dev_t *dev) +{ +} +#endif + #ifdef CONFIG_DEBUG_FS static struct dentry *dent_msm_pcie; static struct dentry *dfile_rc_sel; @@ -6277,6 +6319,9 @@ static int msm_pcie_probe(struct platform_device *pdev) msm_pcie_dev[rc_idx].pcidev_table[i].registered = true; } + dev_set_drvdata(&msm_pcie_dev[rc_idx].pdev->dev, &msm_pcie_dev[rc_idx]); + msm_pcie_sysfs_init(&msm_pcie_dev[rc_idx]); + ret = msm_pcie_get_resources(&msm_pcie_dev[rc_idx], msm_pcie_dev[rc_idx].pdev); @@ -6490,11 +6535,16 @@ int __init pcie_init(void) static void __exit pcie_exit(void) { + int i; + PCIE_GEN_DBG("pcie:%s.\n", __func__); platform_driver_unregister(&msm_pcie_driver); msm_pcie_debugfs_exit(); + + for (i = 0; i < MAX_RC_NUM; i++) + msm_pcie_sysfs_exit(&msm_pcie_dev[i]); } subsys_initcall_sync(pcie_init); diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index e12bafdc42e0..f2fcbe944d94 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -258,8 +258,13 @@ static int dlpar_add_phb(char *drc_name, struct device_node *dn) static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn) { - if (vio_find_node(dn)) + struct vio_dev *vio_dev; + + vio_dev = vio_find_node(dn); + if (vio_dev) { + put_device(&vio_dev->dev); return -EINVAL; + } if (!vio_register_device_node(dn)) { printk(KERN_ERR @@ -335,6 +340,9 @@ static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn) return -EINVAL; vio_unregister_device(vio_dev); + + put_device(&vio_dev->dev); + return 0; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 42d8617352ae..e311a9bf2c90 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2043,6 +2043,10 @@ bool pci_dev_run_wake(struct pci_dev *dev) if (!dev->pme_support) return false; + /* PME-capable in principle, but not from the intended sleep state */ + if (!pci_pme_capable(dev, pci_target_state(dev))) + return false; + while (bus->parent) { struct pci_dev *bridge = bus->self; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 317e3558a35e..c6a012b5ba39 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -518,25 +518,32 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev) link = kzalloc(sizeof(*link), GFP_KERNEL); if (!link) return NULL; + INIT_LIST_HEAD(&link->sibling); INIT_LIST_HEAD(&link->children); INIT_LIST_HEAD(&link->link); link->pdev = pdev; - if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) { + + /* + * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe + * hierarchies. + */ + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT || + pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE) { + link->root = link; + } else { struct pcie_link_state *parent; + parent = pdev->bus->parent->self->link_state; if (!parent) { kfree(link); return NULL; } + link->parent = parent; + link->root = link->parent->root; list_add(&link->link, &parent->children); } - /* Setup a pointer to the root port link */ - if (!link->parent) - link->root = link; - else - link->root = link->parent->root; list_add(&link->sibling, &link_list); pdev->link_state = link; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b5843c255263..71d9a6d1bd56 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1019,6 +1019,7 @@ void set_pcie_port_type(struct pci_dev *pdev) pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); if (!pos) return; + pdev->pcie_cap = pos; pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); pdev->pcie_flags_reg = reg16; @@ -1026,13 +1027,14 @@ void set_pcie_port_type(struct pci_dev *pdev) pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD; /* - * A Root Port is always the upstream end of a Link. No PCIe - * component has two Links. Two Links are connected by a Switch - * that has a Port on each Link and internal logic to connect the - * two Ports. + * A Root Port or a PCI-to-PCIe bridge is always the upstream end + * of a Link. No PCIe component has two Links. Two Links are + * connected by a Switch that has a Port on each Link and internal + * logic to connect the two Ports. */ type = pci_pcie_type(pdev); - if (type == PCI_EXP_TYPE_ROOT_PORT) + if (type == PCI_EXP_TYPE_ROOT_PORT || + type == PCI_EXP_TYPE_PCIE_BRIDGE) pdev->has_secondary_link = 1; else if (type == PCI_EXP_TYPE_UPSTREAM || type == PCI_EXP_TYPE_DOWNSTREAM) { diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c index c3cb57ed6083..1e3e175d3e8d 100644 --- a/drivers/phy/phy-qcom-ufs.c +++ b/drivers/phy/phy-qcom-ufs.c @@ -113,6 +113,14 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, goto out; } + /* + * UFS PHY power management is managed by its parent (UFS host + * controller) hence set the no the no runtime PM callbacks flag + * on UFS PHY device to avoid any accidental attempt to call the + * PM callbacks for PHY device. + */ + pm_runtime_no_callbacks(&generic_phy->dev); + common_cfg->phy_spec_ops = phy_spec_ops; common_cfg->dev = dev; diff --git a/drivers/pinctrl/intel/pinctrl-broxton.c b/drivers/pinctrl/intel/pinctrl-broxton.c index 5979d38c46b2..7329500943a3 100644 --- a/drivers/pinctrl/intel/pinctrl-broxton.c +++ b/drivers/pinctrl/intel/pinctrl-broxton.c @@ -19,7 +19,7 @@ #define BXT_PAD_OWN 0x020 #define BXT_HOSTSW_OWN 0x080 -#define BXT_PADCFGLOCK 0x090 +#define BXT_PADCFGLOCK 0x060 #define BXT_GPI_IE 0x110 #define BXT_COMMUNITY(s, e) \ diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index 84943e4cff09..13730ca151ad 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -246,7 +246,7 @@ static int meson_pmx_request_gpio(struct pinctrl_dev *pcdev, { struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev); - meson_pmx_disable_other_groups(pc, range->pin_base + offset, -1); + meson_pmx_disable_other_groups(pc, offset, -1); return 0; } diff --git a/drivers/pinctrl/qcom/pinctrl-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpi.c index 67cac25689ef..4ca5d5fa0531 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-lpi.c @@ -117,12 +117,12 @@ static const u32 lpi_offset[] = { 0x00005010, 0x00005020, 0x00005030, - 0x00005040, - 0x00005050, 0x00006000, 0x00006010, 0x00007000, 0x00007010, + 0x00005040, + 0x00005050, 0x00008000, 0x00008010, 0x00008020, diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c index 863c3e30ce05..50f2014fed55 100644 --- a/drivers/pinctrl/sh-pfc/pinctrl.c +++ b/drivers/pinctrl/sh-pfc/pinctrl.c @@ -483,7 +483,8 @@ static bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin, switch (param) { case PIN_CONFIG_BIAS_DISABLE: - return true; + return pin->configs & + (SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN); case PIN_CONFIG_BIAS_PULL_UP: return pin->configs & SH_PFC_PIN_CFG_PULL_UP; diff --git a/drivers/platform/goldfish/pdev_bus.c b/drivers/platform/goldfish/pdev_bus.c index 1f52462f4cdd..dd9ea463c2a4 100644 --- a/drivers/platform/goldfish/pdev_bus.c +++ b/drivers/platform/goldfish/pdev_bus.c @@ -157,23 +157,26 @@ static int goldfish_new_pdev(void) static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id) { irqreturn_t ret = IRQ_NONE; + while (1) { u32 op = readl(pdev_bus_base + PDEV_BUS_OP); - switch (op) { - case PDEV_BUS_OP_DONE: - return IRQ_NONE; + switch (op) { case PDEV_BUS_OP_REMOVE_DEV: goldfish_pdev_remove(); + ret = IRQ_HANDLED; break; case PDEV_BUS_OP_ADD_DEV: goldfish_new_pdev(); + ret = IRQ_HANDLED; break; + + case PDEV_BUS_OP_DONE: + default: + return ret; } - ret = IRQ_HANDLED; } - return ret; } static int goldfish_pdev_bus_probe(struct platform_device *pdev) diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c index f48182cc04df..c2902beaa0b8 100644 --- a/drivers/platform/msm/gsi/gsi.c +++ b/drivers/platform/msm/gsi/gsi.c @@ -22,7 +22,7 @@ #include "gsi_reg.h" #define GSI_CMD_TIMEOUT (5*HZ) -#define GSI_STOP_CMD_TIMEOUT_MS 10 +#define GSI_STOP_CMD_TIMEOUT_MS 20 #define GSI_MAX_CH_LOW_WEIGHT 15 #define GSI_MHI_ER_START 10 #define GSI_MHI_ER_END 16 @@ -561,7 +561,7 @@ static void gsi_handle_irq(void) if (!type) break; - GSIDBG("type %x\n", type); + GSIDBG_LOW("type %x\n", type); if (type & GSI_EE_n_CNTXT_TYPE_IRQ_CH_CTRL_BMSK) gsi_handle_ch_ctrl(ee); @@ -2743,9 +2743,10 @@ void gsi_get_inst_ram_offset_and_size(unsigned long *base_offset, unsigned long *size) { if (base_offset) - *base_offset = GSI_GSI_INST_RAM_BASE_OFFS; + *base_offset = GSI_GSI_INST_RAM_n_OFFS(0); if (size) - *size = GSI_GSI_INST_RAM_SIZE; + *size = GSI_GSI_INST_RAM_n_WORD_SZ * + (GSI_GSI_INST_RAM_n_MAXn + 1); } EXPORT_SYMBOL(gsi_get_inst_ram_offset_and_size); @@ -2821,6 +2822,13 @@ static int msm_gsi_probe(struct platform_device *pdev) return -ENOMEM; } + gsi_ctx->ipc_logbuf = ipc_log_context_create(GSI_IPC_LOG_PAGES, + "gsi", 0); + if (gsi_ctx->ipc_logbuf == NULL) { + GSIERR("failed to get ipc_logbuf\n"); + return -ENOMEM; + } + gsi_ctx->dev = dev; init_completion(&gsi_ctx->gen_ee_cmd_compl); gsi_debugfs_init(); diff --git a/drivers/platform/msm/gsi/gsi.h b/drivers/platform/msm/gsi/gsi.h index d0eb162c49cf..f53a4bd8fa6b 100644 --- a/drivers/platform/msm/gsi/gsi.h +++ b/drivers/platform/msm/gsi/gsi.h @@ -18,6 +18,7 @@ #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/msm_gsi.h> +#include <linux/ipc_logging.h> #define GSI_CHAN_MAX 31 #define GSI_EVT_RING_MAX 23 @@ -26,10 +27,48 @@ #define gsi_readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; }) #define gsi_writel(v, c) ({ __iowmb(); writel_relaxed((v), (c)); }) -#define GSIERR(fmt, args...) \ - dev_err(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, ## args) +#define GSI_IPC_LOGGING(buf, fmt, args...) \ + do { \ + if (buf) \ + ipc_log_string((buf), fmt, __func__, __LINE__, \ + ## args); \ + } while (0) + #define GSIDBG(fmt, args...) \ - dev_dbg(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, ## args) + do { \ + dev_dbg(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, \ + ## args);\ + if (gsi_ctx) { \ + GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf, \ + "%s:%d " fmt, ## args); \ + GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf_low, \ + "%s:%d " fmt, ## args); \ + } \ + } while (0) + +#define GSIDBG_LOW(fmt, args...) \ + do { \ + dev_dbg(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, \ + ## args);\ + if (gsi_ctx) { \ + GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf_low, \ + "%s:%d " fmt, ## args); \ + } \ + } while (0) + +#define GSIERR(fmt, args...) \ + do { \ + dev_err(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, \ + ## args);\ + if (gsi_ctx) { \ + GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf, \ + "%s:%d " fmt, ## args); \ + GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf_low, \ + "%s:%d " fmt, ## args); \ + } \ + } while (0) + +#define GSI_IPC_LOG_PAGES 50 enum gsi_evt_ring_state { GSI_EVT_RING_STATE_NOT_ALLOCATED = 0x0, @@ -163,6 +202,8 @@ struct gsi_ctx { u32 max_ch; u32 max_ev; struct completion gen_ee_cmd_compl; + void *ipc_logbuf; + void *ipc_logbuf_low; }; enum gsi_re_type { diff --git a/drivers/platform/msm/gsi/gsi_dbg.c b/drivers/platform/msm/gsi/gsi_dbg.c index 5eb9084292a4..717c891788f2 100644 --- a/drivers/platform/msm/gsi/gsi_dbg.c +++ b/drivers/platform/msm/gsi/gsi_dbg.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -747,6 +747,45 @@ error: return -EFAULT; } +static ssize_t gsi_enable_ipc_low(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + unsigned long missing; + s8 option = 0; + + if (sizeof(dbg_buff) < count + 1) + return -EFAULT; + + missing = copy_from_user(dbg_buff, ubuf, count); + if (missing) + return -EFAULT; + + dbg_buff[count] = '\0'; + if (kstrtos8(dbg_buff, 0, &option)) + return -EFAULT; + + if (option) { + if (!gsi_ctx->ipc_logbuf_low) { + gsi_ctx->ipc_logbuf_low = + ipc_log_context_create(GSI_IPC_LOG_PAGES, + "gsi_low", 0); + } + + if (gsi_ctx->ipc_logbuf_low == NULL) { + TERR("failed to get ipc_logbuf_low\n"); + return -EFAULT; + } + } else { + if (gsi_ctx->ipc_logbuf_low) + ipc_log_context_destroy(gsi_ctx->ipc_logbuf_low); + gsi_ctx->ipc_logbuf_low = NULL; + } + + return count; +} + + + const struct file_operations gsi_ev_dump_ops = { .write = gsi_dump_evt, }; @@ -783,6 +822,10 @@ const struct file_operations gsi_print_dp_stats_ops = { .write = gsi_print_dp_stats, }; +const struct file_operations gsi_ipc_low_ops = { + .write = gsi_enable_ipc_low, +}; + void gsi_debugfs_init(void) { static struct dentry *dfile; @@ -858,6 +901,13 @@ void gsi_debugfs_init(void) goto fail; } + dfile = debugfs_create_file("ipc_low", write_only_mode, + dent, 0, &gsi_ipc_low_ops); + if (!dfile || IS_ERR(dfile)) { + TERR("could not create ipc_low\n"); + goto fail; + } + return; fail: debugfs_remove_recursive(dent); diff --git a/drivers/platform/msm/gsi/gsi_reg.h b/drivers/platform/msm/gsi/gsi_reg.h index d0462aad72d2..653cdd4823c6 100644 --- a/drivers/platform/msm/gsi/gsi_reg.h +++ b/drivers/platform/msm/gsi/gsi_reg.h @@ -688,8 +688,9 @@ #define GSI_GSI_IRAM_PTR_INT_MOD_STOPED_IRAM_PTR_BMSK 0xfff #define GSI_GSI_IRAM_PTR_INT_MOD_STOPED_IRAM_PTR_SHFT 0x0 +#define GSI_GSI_INST_RAM_n_WORD_SZ 0x4 #define GSI_GSI_INST_RAM_n_OFFS(n) \ - (GSI_GSI_REG_BASE_OFFS + 0x00004000 + 0x4 * (n)) + (GSI_GSI_REG_BASE_OFFS + 0x00004000 + GSI_GSI_INST_RAM_n_WORD_SZ * (n)) #define GSI_GSI_INST_RAM_n_RMSK 0xffffffff #define GSI_GSI_INST_RAM_n_MAXn 4095 #define GSI_GSI_INST_RAM_n_INST_BYTE_3_BMSK 0xff000000 @@ -1842,7 +1843,5 @@ #define GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_EV_CH_BIT_MAP_BMSK 0xffffffff #define GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_EV_CH_BIT_MAP_SHFT 0x0 -#define GSI_GSI_INST_RAM_BASE_OFFS 0x4000 -#define GSI_GSI_INST_RAM_SIZE 0x4000 #endif /* __GSI_REG_H__ */ diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c index 069f0a2e3fee..51c930a81c8d 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c @@ -74,6 +74,10 @@ struct ipa_uc_offload_ctx { static struct ipa_uc_offload_ctx *ipa_uc_offload_ctx[IPA_UC_MAX_PROT_SIZE]; +static int ipa_uc_ntn_cons_release(void); +static int ipa_uc_ntn_cons_request(void); +static void ipa_uc_offload_rm_notify(void *, enum ipa_rm_event, unsigned long); + static int ipa_commit_partial_hdr( struct ipa_ioc_add_hdr *hdr, const char *netdev_name, @@ -115,16 +119,37 @@ static int ipa_uc_offload_ntn_reg_intf( struct ipa_uc_offload_out_params *outp, struct ipa_uc_offload_ctx *ntn_ctx) { - struct ipa_ioc_add_hdr *hdr; + struct ipa_ioc_add_hdr *hdr = NULL; struct ipa_tx_intf tx; struct ipa_rx_intf rx; struct ipa_ioc_tx_intf_prop tx_prop[2]; struct ipa_ioc_rx_intf_prop rx_prop[2]; + struct ipa_rm_create_params param; u32 len; int ret = 0; IPA_UC_OFFLOAD_DBG("register interface for netdev %s\n", inp->netdev_name); + memset(¶m, 0, sizeof(param)); + param.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD; + param.reg_params.user_data = ntn_ctx; + param.reg_params.notify_cb = ipa_uc_offload_rm_notify; + param.floor_voltage = IPA_VOLTAGE_SVS; + ret = ipa_rm_create_resource(¶m); + if (ret) { + IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_PROD resource\n"); + return -EFAULT; + } + + memset(¶m, 0, sizeof(param)); + param.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS; + param.request_resource = ipa_uc_ntn_cons_request; + param.release_resource = ipa_uc_ntn_cons_release; + ret = ipa_rm_create_resource(¶m); + if (ret) { + IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_CONS resource\n"); + goto fail_create_rm_cons; + } memcpy(ntn_ctx->netdev_name, inp->netdev_name, IPA_RESOURCE_NAME_MAX); ntn_ctx->hdr_len = inp->hdr_info[0].hdr_len; @@ -136,7 +161,8 @@ static int ipa_uc_offload_ntn_reg_intf( hdr = kzalloc(len, GFP_KERNEL); if (hdr == NULL) { IPA_UC_OFFLOAD_ERR("fail to alloc %d bytes\n", len); - return -ENOMEM; + ret = -ENOMEM; + goto fail_alloc; } if (ipa_commit_partial_hdr(hdr, ntn_ctx->netdev_name, inp->hdr_info)) { @@ -197,8 +223,15 @@ static int ipa_uc_offload_ntn_reg_intf( init_completion(&ntn_ctx->ntn_completion); ntn_ctx->state = IPA_UC_OFFLOAD_STATE_INITIALIZED; + kfree(hdr); + return ret; + fail: kfree(hdr); +fail_alloc: + ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS); +fail_create_rm_cons: + ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); return ret; } @@ -301,9 +334,10 @@ int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp, struct ipa_ntn_conn_out_params *outp, struct ipa_uc_offload_ctx *ntn_ctx) { - struct ipa_rm_create_params param; int result = 0; + enum ipa_uc_offload_state prev_state; + prev_state = ntn_ctx->state; if (inp->dl.ring_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT || inp->dl.buff_pool_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT) { IPA_UC_OFFLOAD_ERR("alignment failure on TX\n"); @@ -315,42 +349,13 @@ int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp, return -EINVAL; } - memset(¶m, 0, sizeof(param)); - param.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD; - param.reg_params.user_data = ntn_ctx; - param.reg_params.notify_cb = ipa_uc_offload_rm_notify; - param.floor_voltage = IPA_VOLTAGE_SVS; - result = ipa_rm_create_resource(¶m); - if (result) { - IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_PROD resource\n"); - return -EFAULT; - } - - memset(¶m, 0, sizeof(param)); - param.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS; - param.request_resource = ipa_uc_ntn_cons_request; - param.release_resource = ipa_uc_ntn_cons_release; - result = ipa_rm_create_resource(¶m); + result = ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, + IPA_RM_RESOURCE_APPS_CONS); if (result) { - IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_CONS resource\n"); - goto fail_create_rm_cons; - } - - if (ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, - IPA_RM_RESOURCE_APPS_CONS)) { - IPA_UC_OFFLOAD_ERR("fail to add rm dependency\n"); - result = -EFAULT; - goto fail; - } - - if (ipa_setup_uc_ntn_pipes(inp, ntn_ctx->notify, - ntn_ctx->priv, ntn_ctx->hdr_len, outp)) { - IPA_UC_OFFLOAD_ERR("fail to setup uc offload pipes\n"); - result = -EFAULT; - goto fail; + IPA_UC_OFFLOAD_ERR("fail to add rm dependency: %d\n", result); + return result; } - ntn_ctx->state = IPA_UC_OFFLOAD_STATE_UP; result = ipa_rm_request_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); if (result == -EINPROGRESS) { if (wait_for_completion_timeout(&ntn_ctx->ntn_completion, @@ -365,13 +370,22 @@ int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp, goto fail; } + ntn_ctx->state = IPA_UC_OFFLOAD_STATE_UP; + result = ipa_setup_uc_ntn_pipes(inp, ntn_ctx->notify, + ntn_ctx->priv, ntn_ctx->hdr_len, outp); + if (result) { + IPA_UC_OFFLOAD_ERR("fail to setup uc offload pipes: %d\n", + result); + ntn_ctx->state = prev_state; + result = -EFAULT; + goto fail; + } + return 0; fail: - ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS); -fail_create_rm_cons: - ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); - + ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, + IPA_RM_RESOURCE_APPS_CONS); return result; } @@ -399,7 +413,8 @@ int ipa_uc_offload_conn_pipes(struct ipa_uc_offload_conn_in_params *inp, return -EINVAL; } - if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED) { + if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED && + offload_ctx->state != IPA_UC_OFFLOAD_STATE_DOWN) { IPA_UC_OFFLOAD_ERR("Invalid state %d\n", offload_ctx->state); return -EPERM; } @@ -454,32 +469,34 @@ EXPORT_SYMBOL(ipa_set_perf_profile); static int ipa_uc_ntn_disconn_pipes(struct ipa_uc_offload_ctx *ntn_ctx) { int ipa_ep_idx_ul, ipa_ep_idx_dl; + int ret = 0; ntn_ctx->state = IPA_UC_OFFLOAD_STATE_DOWN; - if (ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, - IPA_RM_RESOURCE_APPS_CONS)) { - IPA_UC_OFFLOAD_ERR("fail to delete rm dependency\n"); - return -EFAULT; - } - if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD)) { - IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_PROD resource\n"); + ret = ipa_rm_release_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD); + if (ret) { + IPA_UC_OFFLOAD_ERR("fail to release ODU_ADAPT_PROD res: %d\n", + ret); return -EFAULT; } - if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS)) { - IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_CONS resource\n"); + ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD, + IPA_RM_RESOURCE_APPS_CONS); + if (ret) { + IPA_UC_OFFLOAD_ERR("fail to del dep ODU->APPS, %d\n", ret); return -EFAULT; } ipa_ep_idx_ul = ipa_get_ep_mapping(IPA_CLIENT_ODU_PROD); ipa_ep_idx_dl = ipa_get_ep_mapping(IPA_CLIENT_ODU_TETH_CONS); - if (ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl)) { - IPA_UC_OFFLOAD_ERR("fail to tear down uc offload pipes\n"); + ret = ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl); + if (ret) { + IPA_UC_OFFLOAD_ERR("fail to tear down ntn offload pipes, %d\n", + ret); return -EFAULT; } - return 0; + return ret; } int ipa_uc_offload_disconn_pipes(u32 clnt_hdl) @@ -524,6 +541,16 @@ static int ipa_uc_ntn_cleanup(struct ipa_uc_offload_ctx *ntn_ctx) int len, result = 0; struct ipa_ioc_del_hdr *hdr; + if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD)) { + IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_PROD resource\n"); + return -EFAULT; + } + + if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS)) { + IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_CONS resource\n"); + return -EFAULT; + } + len = sizeof(struct ipa_ioc_del_hdr) + 2 * sizeof(struct ipa_hdr_del); hdr = kzalloc(len, GFP_KERNEL); if (hdr == NULL) { diff --git a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c index 54cad888cb7f..e10c75a473ce 100644 --- a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c +++ b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c @@ -227,7 +227,7 @@ int ipa_rm_dep_graph_delete_dependency(struct ipa_rm_dep_graph *graph, if (ipa_rm_dep_graph_get_resource(graph, resource_name, &dependent)) { - IPA_RM_ERR("%s does not exist\n", + IPA_RM_DBG("%s does not exist\n", ipa_rm_resource_str(resource_name)); result = -EINVAL; goto bail; @@ -236,7 +236,7 @@ int ipa_rm_dep_graph_delete_dependency(struct ipa_rm_dep_graph *graph, if (ipa_rm_dep_graph_get_resource(graph, depends_on_name, &dependency)) { - IPA_RM_ERR("%s does not exist\n", + IPA_RM_DBG("%s does not exist\n", ipa_rm_resource_str(depends_on_name)); result = -EINVAL; goto bail; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c index a2665c9e9688..25364e8efa38 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c @@ -1974,6 +1974,8 @@ static void ipa_cleanup_wlan_rx_common_cache(void) struct ipa_rx_pkt_wrapper *rx_pkt; struct ipa_rx_pkt_wrapper *tmp; + spin_lock_bh(&ipa_ctx->wc_memb.wlan_spinlock); + list_for_each_entry_safe(rx_pkt, tmp, &ipa_ctx->wc_memb.wlan_comm_desc_list, link) { list_del(&rx_pkt->link); @@ -1994,6 +1996,8 @@ static void ipa_cleanup_wlan_rx_common_cache(void) IPAERR("wlan comm buff total cnt: %d\n", ipa_ctx->wc_memb.wlan_comm_total_cnt); + spin_unlock_bh(&ipa_ctx->wc_memb.wlan_spinlock); + } static void ipa_alloc_wlan_rx_common_cache(u32 size) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c index 7ca2314d5839..985c3e560c86 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c @@ -1465,18 +1465,18 @@ void ipa_install_dflt_flt_rules(u32 ipa_ep_idx) mutex_lock(&ipa_ctx->lock); tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v4]; - tbl->sticky_rear = true; rule.action = IPA_PASS_TO_EXCEPTION; - __ipa_add_flt_rule(tbl, IPA_IP_v4, &rule, false, + __ipa_add_flt_rule(tbl, IPA_IP_v4, &rule, true, &ep->dflt_flt4_rule_hdl); ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v4); + tbl->sticky_rear = true; tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v6]; - tbl->sticky_rear = true; rule.action = IPA_PASS_TO_EXCEPTION; - __ipa_add_flt_rule(tbl, IPA_IP_v6, &rule, false, + __ipa_add_flt_rule(tbl, IPA_IP_v6, &rule, true, &ep->dflt_flt6_rule_hdl); ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v6); + tbl->sticky_rear = true; mutex_unlock(&ipa_ctx->lock); } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c index dfc3e06f452b..f8f8fd12161a 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -272,6 +272,14 @@ int ipa_query_intf_tx_props(struct ipa_ioc_query_intf_tx_props *tx) mutex_lock(&ipa_ctx->lock); list_for_each_entry(entry, &ipa_ctx->intf_list, link) { if (!strncmp(entry->name, tx->name, IPA_RESOURCE_NAME_MAX)) { + /* add the entry check */ + if (entry->num_tx_props != tx->num_tx_props) { + IPAERR("invalid entry number(%u %u)\n", + entry->num_tx_props, + tx->num_tx_props); + mutex_unlock(&ipa_ctx->lock); + return result; + } memcpy(tx->tx, entry->tx, entry->num_tx_props * sizeof(struct ipa_ioc_tx_intf_prop)); result = 0; @@ -305,6 +313,14 @@ int ipa_query_intf_rx_props(struct ipa_ioc_query_intf_rx_props *rx) mutex_lock(&ipa_ctx->lock); list_for_each_entry(entry, &ipa_ctx->intf_list, link) { if (!strncmp(entry->name, rx->name, IPA_RESOURCE_NAME_MAX)) { + /* add the entry check */ + if (entry->num_rx_props != rx->num_rx_props) { + IPAERR("invalid entry number(%u %u)\n", + entry->num_rx_props, + rx->num_rx_props); + mutex_unlock(&ipa_ctx->lock); + return result; + } memcpy(rx->rx, entry->rx, entry->num_rx_props * sizeof(struct ipa_ioc_rx_intf_prop)); result = 0; @@ -338,6 +354,14 @@ int ipa_query_intf_ext_props(struct ipa_ioc_query_intf_ext_props *ext) mutex_lock(&ipa_ctx->lock); list_for_each_entry(entry, &ipa_ctx->intf_list, link) { if (!strcmp(entry->name, ext->name)) { + /* add the entry check */ + if (entry->num_ext_props != ext->num_ext_props) { + IPAERR("invalid entry number(%u %u)\n", + entry->num_ext_props, + ext->num_ext_props); + mutex_unlock(&ipa_ctx->lock); + return result; + } memcpy(ext->ext, entry->ext, entry->num_ext_props * sizeof(struct ipa_ioc_ext_intf_prop)); result = 0; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c index e2ac9bfceed7..119d17cae9f5 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c @@ -655,9 +655,8 @@ int qmi_filter_notify_send(struct ipa_fltr_installed_notif_req_msg_v01 *req) /* check if the filter rules from IPACM is valid */ if (req->filter_index_list_len == 0) { - IPAWANERR(" delete UL filter rule for pipe %d\n", + IPAWANDBG(" delete UL filter rule for pipe %d\n", req->source_pipe_index); - return -EINVAL; } else if (req->filter_index_list_len > QMI_IPA_MAX_FILTERS_V01) { IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n", req->source_pipe_index, diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c index d14f8da15595..0b46ab2a8439 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c @@ -299,12 +299,6 @@ int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, /* setup ul ep cfg */ ep_ul->valid = 1; ep_ul->client = in->ul.client; - result = ipa_enable_data_path(ipa_ep_idx_ul); - if (result) { - IPAERR("disable data path failed res=%d clnt=%d.\n", result, - ipa_ep_idx_ul); - return -EFAULT; - } ep_ul->client_notify = notify; ep_ul->priv = priv; @@ -333,14 +327,6 @@ int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, /* setup dl ep cfg */ ep_dl->valid = 1; ep_dl->client = in->dl.client; - result = ipa_enable_data_path(ipa_ep_idx_dl); - if (result) { - IPAERR("disable data path failed res=%d clnt=%d.\n", result, - ipa_ep_idx_dl); - result = -EFAULT; - goto fail; - } - memset(&ep_dl->cfg, 0, sizeof(ep_ul->cfg)); ep_dl->cfg.nat.nat_en = IPA_BYPASS_NAT; ep_dl->cfg.hdr.hdr_len = hdr_len; @@ -359,6 +345,14 @@ int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, } outp->dl_uc_db_pa = IPA_UC_NTN_DB_PA_TX; ep_dl->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED; + + result = ipa_enable_data_path(ipa_ep_idx_dl); + if (result) { + IPAERR("Enable data path failed res=%d clnt=%d.\n", result, + ipa_ep_idx_dl); + result = -EFAULT; + goto fail; + } IPAERR("client %d (ep: %d) connected\n", in->dl.client, ipa_ep_idx_dl); @@ -402,28 +396,32 @@ int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, } IPA_ACTIVE_CLIENTS_INC_SIMPLE(); - /* teardown the UL pipe */ cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base; cmd_data->protocol = IPA_HW_FEATURE_NTN; - tear = &cmd_data->CommonCh_params.NtnCommonCh_params; - tear->params.ipa_pipe_number = ipa_ep_idx_ul; + + /* teardown the DL pipe */ + ipa_disable_data_path(ipa_ep_idx_dl); + /* + * Reset ep before sending cmd otherwise disconnect + * during data transfer will result into + * enormous suspend interrupts + */ + memset(&ipa_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa_ep_context)); + IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl); + tear->params.ipa_pipe_number = ipa_ep_idx_dl; result = ipa_uc_send_cmd((u32)(cmd.phys_base), IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN, IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, false, 10*HZ); if (result) { - IPAERR("fail to tear down ul pipe\n"); + IPAERR("fail to tear down dl pipe\n"); result = -EFAULT; goto fail; } - ipa_disable_data_path(ipa_ep_idx_ul); - ipa_delete_dflt_flt_rules(ipa_ep_idx_ul); - memset(&ipa_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa_ep_context)); - IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul); - /* teardown the DL pipe */ - tear->params.ipa_pipe_number = ipa_ep_idx_dl; + /* teardown the UL pipe */ + tear->params.ipa_pipe_number = ipa_ep_idx_ul; result = ipa_uc_send_cmd((u32)(cmd.phys_base), IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN, IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, @@ -433,9 +431,10 @@ int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, result = -EFAULT; goto fail; } - ipa_disable_data_path(ipa_ep_idx_dl); - memset(&ipa_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa_ep_context)); - IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl); + + ipa_delete_dflt_flt_rules(ipa_ep_idx_ul); + memset(&ipa_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa_ep_context)); + IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul); fail: dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c index b9f57552533e..b687b711dc20 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -275,6 +275,14 @@ int ipa3_query_intf_tx_props(struct ipa_ioc_query_intf_tx_props *tx) mutex_lock(&ipa3_ctx->lock); list_for_each_entry(entry, &ipa3_ctx->intf_list, link) { if (!strcmp(entry->name, tx->name)) { + /* add the entry check */ + if (entry->num_tx_props != tx->num_tx_props) { + IPAERR("invalid entry number(%u %u)\n", + entry->num_tx_props, + tx->num_tx_props); + mutex_unlock(&ipa3_ctx->lock); + return result; + } memcpy(tx->tx, entry->tx, entry->num_tx_props * sizeof(struct ipa_ioc_tx_intf_prop)); result = 0; @@ -314,6 +322,14 @@ int ipa3_query_intf_rx_props(struct ipa_ioc_query_intf_rx_props *rx) mutex_lock(&ipa3_ctx->lock); list_for_each_entry(entry, &ipa3_ctx->intf_list, link) { if (!strcmp(entry->name, rx->name)) { + /* add the entry check */ + if (entry->num_rx_props != rx->num_rx_props) { + IPAERR("invalid entry number(%u %u)\n", + entry->num_rx_props, + rx->num_rx_props); + mutex_unlock(&ipa3_ctx->lock); + return result; + } memcpy(rx->rx, entry->rx, entry->num_rx_props * sizeof(struct ipa_ioc_rx_intf_prop)); result = 0; @@ -348,6 +364,14 @@ int ipa3_query_intf_ext_props(struct ipa_ioc_query_intf_ext_props *ext) mutex_lock(&ipa3_ctx->lock); list_for_each_entry(entry, &ipa3_ctx->intf_list, link) { if (!strcmp(entry->name, ext->name)) { + /* add the entry check */ + if (entry->num_ext_props != ext->num_ext_props) { + IPAERR("invalid entry number(%u %u)\n", + entry->num_ext_props, + ext->num_ext_props); + mutex_unlock(&ipa3_ctx->lock); + return result; + } memcpy(ext->ext, entry->ext, entry->num_ext_props * sizeof(struct ipa_ioc_ext_intf_prop)); result = 0; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c index 3086311b5c2a..335e5283cc29 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c @@ -786,9 +786,8 @@ int ipa3_qmi_filter_notify_send( /* check if the filter rules from IPACM is valid */ if (req->rule_id_len == 0) { - IPAWANERR(" delete UL filter rule for pipe %d\n", + IPAWANDBG(" delete UL filter rule for pipe %d\n", req->source_pipe_index); - return -EINVAL; } else if (req->rule_id_len > QMI_IPA_MAX_FILTERS_V01) { IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n", req->source_pipe_index, diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c index 7b891843028d..fdb6d05f683d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c @@ -265,12 +265,6 @@ int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, /* setup ul ep cfg */ ep_ul->valid = 1; ep_ul->client = in->ul.client; - result = ipa3_enable_data_path(ipa_ep_idx_ul); - if (result) { - IPAERR("disable data path failed res=%d clnt=%d.\n", result, - ipa_ep_idx_ul); - return -EFAULT; - } ep_ul->client_notify = notify; ep_ul->priv = priv; @@ -299,14 +293,6 @@ int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, /* setup dl ep cfg */ ep_dl->valid = 1; ep_dl->client = in->dl.client; - result = ipa3_enable_data_path(ipa_ep_idx_dl); - if (result) { - IPAERR("disable data path failed res=%d clnt=%d.\n", result, - ipa_ep_idx_dl); - result = -EFAULT; - goto fail; - } - memset(&ep_dl->cfg, 0, sizeof(ep_ul->cfg)); ep_dl->cfg.nat.nat_en = IPA_BYPASS_NAT; ep_dl->cfg.hdr.hdr_len = hdr_len; @@ -325,6 +311,14 @@ int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in, } outp->dl_uc_db_pa = IPA_UC_NTN_DB_PA_TX; ep_dl->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED; + + result = ipa3_enable_data_path(ipa_ep_idx_dl); + if (result) { + IPAERR("Enable data path failed res=%d clnt=%d.\n", result, + ipa_ep_idx_dl); + result = -EFAULT; + goto fail; + } IPADBG("client %d (ep: %d) connected\n", in->dl.client, ipa_ep_idx_dl); @@ -368,28 +362,32 @@ int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, } IPA_ACTIVE_CLIENTS_INC_SIMPLE(); - /* teardown the UL pipe */ cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base; cmd_data->protocol = IPA_HW_FEATURE_NTN; - tear = &cmd_data->CommonCh_params.NtnCommonCh_params; - tear->params.ipa_pipe_number = ipa_ep_idx_ul; + + /* teardown the DL pipe */ + ipa3_disable_data_path(ipa_ep_idx_dl); + /* + * Reset ep before sending cmd otherwise disconnect + * during data transfer will result into + * enormous suspend interrupts + */ + memset(&ipa3_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa3_ep_context)); + IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl); + tear->params.ipa_pipe_number = ipa_ep_idx_dl; result = ipa3_uc_send_cmd((u32)(cmd.phys_base), IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN, IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, false, 10*HZ); if (result) { - IPAERR("fail to tear down ul pipe\n"); + IPAERR("fail to tear down dl pipe\n"); result = -EFAULT; goto fail; } - ipa3_disable_data_path(ipa_ep_idx_ul); - ipa3_delete_dflt_flt_rules(ipa_ep_idx_ul); - memset(&ipa3_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa3_ep_context)); - IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul); - /* teardown the DL pipe */ - tear->params.ipa_pipe_number = ipa_ep_idx_dl; + /* teardown the UL pipe */ + tear->params.ipa_pipe_number = ipa_ep_idx_ul; result = ipa3_uc_send_cmd((u32)(cmd.phys_base), IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN, IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS, @@ -399,9 +397,9 @@ int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, result = -EFAULT; goto fail; } - ipa3_disable_data_path(ipa_ep_idx_dl); + ipa3_delete_dflt_flt_rules(ipa_ep_idx_ul); memset(&ipa3_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa3_ep_context)); - IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl); + IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul); fail: dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 2f28ba673d5a..c8ff06ddda87 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -3565,11 +3565,6 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl) memset(&mem, 0, sizeof(mem)); - if (IPA_CLIENT_IS_PROD(ep->client)) { - res = gsi_stop_channel(ep->gsi_chan_hdl); - goto end_sequence; - } - for (i = 0; i < IPA_GSI_CHANNEL_STOP_MAX_RETRY; i++) { IPADBG("Calling gsi_stop_channel\n"); res = gsi_stop_channel(ep->gsi_chan_hdl); @@ -3577,12 +3572,14 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl) if (res != -GSI_STATUS_AGAIN && res != -GSI_STATUS_TIMED_OUT) goto end_sequence; - IPADBG("Inject a DMA_TASK with 1B packet to IPA and retry\n"); - /* Send a 1B packet DMA_TASK to IPA and try again */ - res = ipa3_inject_dma_task_for_gsi(); - if (res) { - IPAERR("Failed to inject DMA TASk for GSI\n"); - goto end_sequence; + if (IPA_CLIENT_IS_CONS(ep->client)) { + IPADBG("Inject a DMA_TASK with 1B packet to IPA\n"); + /* Send a 1B packet DMA_TASK to IPA and try again */ + res = ipa3_inject_dma_task_for_gsi(); + if (res) { + IPAERR("Failed to inject DMA TASk for GSI\n"); + goto end_sequence; + } } /* sleep for short period to flush IPA */ diff --git a/drivers/platform/msm/mhi/mhi.h b/drivers/platform/msm/mhi/mhi.h index 3d40d114437a..4bce96102525 100644 --- a/drivers/platform/msm/mhi/mhi.h +++ b/drivers/platform/msm/mhi/mhi.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,7 +29,6 @@ #include <linux/list.h> #include <linux/dma-mapping.h> -extern struct mhi_pcie_devices mhi_devices; struct mhi_device_ctxt; enum MHI_DEBUG_LEVEL { @@ -49,23 +48,56 @@ struct pcie_core_info { u32 mhi_ver; void __iomem *bar0_base; void __iomem *bar0_end; - void __iomem *bar2_base; - void __iomem *bar2_end; u32 irq_base; u32 max_nr_msis; + u32 domain; + u32 bus; + u32 slot; struct pci_saved_state *pcie_state; + bool pci_master; +}; + +struct firmware_info { + const char *fw_image; + size_t max_sbl_len; + size_t segment_size; +}; + +struct bhie_mem_info { + void *pre_aligned; + void *aligned; + size_t alloc_size; + size_t size; + phys_addr_t phys_addr; + dma_addr_t dma_handle; +}; + +struct bhie_vec_table { + struct scatterlist *sg_list; + struct bhie_mem_info *bhie_mem_info; + struct bhi_vec_entry *bhi_vec_entry; + unsigned segment_count; + u32 sequence; /* sequence to indicate new xfer */ }; struct bhi_ctxt_t { void __iomem *bhi_base; + void *unaligned_image_loc; + dma_addr_t dma_handle; + size_t alloc_size; void *image_loc; dma_addr_t phy_image_loc; size_t image_size; - void *unaligned_image_loc; dev_t bhi_dev; struct cdev cdev; - struct class *bhi_class; struct device *dev; + u32 alignment; + u32 poll_timeout; + /* BHI/E vector table */ + bool manage_boot; /* fw download done by MHI host */ + struct work_struct fw_load_work; + struct firmware_info firmware_info; + struct bhie_vec_table fw_table; }; enum MHI_CHAN_DIR { @@ -316,6 +348,11 @@ struct mhi_ring { u32 msi_disable_cntr; u32 msi_enable_cntr; spinlock_t ring_lock; + struct dma_pool *dma_pool; + struct tasklet_struct ev_task; + struct work_struct ev_worker; + struct mhi_device_ctxt *mhi_dev_ctxt; + int index; }; enum MHI_CMD_STATUS { @@ -344,25 +381,27 @@ enum MHI_INIT_ERROR_STAGE { }; enum STATE_TRANSITION { - STATE_TRANSITION_RESET = 0x0, - STATE_TRANSITION_READY = 0x1, - STATE_TRANSITION_M0 = 0x2, - STATE_TRANSITION_M1 = 0x3, - STATE_TRANSITION_M2 = 0x4, - STATE_TRANSITION_M3 = 0x5, - STATE_TRANSITION_BHI = 0x6, - STATE_TRANSITION_SBL = 0x7, - STATE_TRANSITION_AMSS = 0x8, - STATE_TRANSITION_LINK_DOWN = 0x9, - STATE_TRANSITION_WAKE = 0xA, - STATE_TRANSITION_SYS_ERR = 0xFF, - STATE_TRANSITION_reserved = 0x80000000 + STATE_TRANSITION_RESET = MHI_STATE_RESET, + STATE_TRANSITION_READY = MHI_STATE_READY, + STATE_TRANSITION_M0 = MHI_STATE_M0, + STATE_TRANSITION_M1 = MHI_STATE_M1, + STATE_TRANSITION_M2 = MHI_STATE_M2, + STATE_TRANSITION_M3 = MHI_STATE_M3, + STATE_TRANSITION_BHI, + STATE_TRANSITION_SBL, + STATE_TRANSITION_AMSS, + STATE_TRANSITION_LINK_DOWN, + STATE_TRANSITION_WAKE, + STATE_TRANSITION_BHIE, + STATE_TRANSITION_SYS_ERR, + STATE_TRANSITION_MAX }; enum MHI_EXEC_ENV { MHI_EXEC_ENV_PBL = 0x0, MHI_EXEC_ENV_SBL = 0x1, MHI_EXEC_ENV_AMSS = 0x2, + MHI_EXEC_ENV_BHIE = 0x3, MHI_EXEC_ENV_reserved = 0x80000000 }; @@ -382,7 +421,7 @@ struct mhi_chan_cfg { union mhi_cmd_pkt cmd_pkt; }; -struct mhi_client_handle { +struct mhi_client_config { struct mhi_chan_info chan_info; struct mhi_device_ctxt *mhi_dev_ctxt; struct mhi_client_info_t client_info; @@ -412,9 +451,12 @@ struct mhi_state_work_queue { struct mhi_buf_info { dma_addr_t bb_p_addr; + dma_addr_t pre_alloc_p_addr; void *bb_v_addr; + void *pre_alloc_v_addr; void *client_buf; size_t buf_len; + size_t pre_alloc_len; size_t filled_size; enum dma_data_direction dir; int bb_active; @@ -431,23 +473,19 @@ struct mhi_counters { u32 bb_used[MHI_MAX_CHANNELS]; atomic_t device_wake; atomic_t outbound_acks; - atomic_t events_pending; u32 *msi_counter; u32 mhi_reset_cntr; + u32 link_down_cntr; + u32 link_up_cntr; }; struct mhi_flags { u32 mhi_initialized; u32 link_up; - int stop_threads; - u32 kill_threads; - u32 ev_thread_stopped; - u32 st_thread_stopped; + bool bb_required; }; struct mhi_wait_queues { - wait_queue_head_t *mhi_event_wq; - wait_queue_head_t *state_change_event; wait_queue_head_t *m0_event; wait_queue_head_t *m3_event; wait_queue_head_t *bhi_event; @@ -486,13 +524,17 @@ struct mhi_dev_space { }; struct mhi_device_ctxt { + struct list_head node; + struct pcie_core_info core; + struct msm_pcie_register_event mhi_pci_link_event; + struct pci_dev *pcie_device; + struct bhi_ctxt_t bhi_ctxt; + struct platform_device *plat_dev; enum MHI_PM_STATE mhi_pm_state; /* Host driver state */ enum MHI_STATE mhi_state; /* protocol state */ enum MHI_EXEC_ENV dev_exec_env; struct mhi_dev_space dev_space; - struct mhi_pcie_dev_info *dev_info; - struct pcie_core_info *dev_props; struct mhi_ring chan_bb_list[MHI_MAX_CHANNELS]; struct mhi_ring mhi_local_chan_ctxt[MHI_MAX_CHANNELS]; @@ -500,12 +542,9 @@ struct mhi_device_ctxt { struct mhi_ring mhi_local_cmd_ctxt[NR_OF_CMD_RINGS]; struct mhi_chan_cfg mhi_chan_cfg[MHI_MAX_CHANNELS]; - struct mhi_client_handle *client_handle_list[MHI_MAX_CHANNELS]; struct mhi_event_ring_cfg *ev_ring_props; - struct task_struct *event_thread_handle; - struct task_struct *st_thread_handle; - struct tasklet_struct ev_task; /* Process control Events */ + struct work_struct st_thread_worker; struct work_struct process_m1_worker; struct mhi_wait_queues mhi_ev_wq; struct dev_mmio_info mmio_info; @@ -517,7 +556,9 @@ struct mhi_device_ctxt { struct hrtimer m1_timer; ktime_t m1_timeout; + u32 poll_reset_timeout_ms; + struct notifier_block mhi_ssr_nb; struct esoc_desc *esoc_handle; void *esoc_ssr_handle; @@ -534,35 +575,47 @@ struct mhi_device_ctxt { struct wakeup_source w_lock; char *chan_info; - struct dentry *mhi_parent_folder; -}; + struct dentry *child; + struct dentry *parent; + void *mhi_ipc_log; + + /* Shadow functions since not all device supports runtime pm */ + int (*bus_master_rt_get)(struct pci_dev *pci_dev); + void (*bus_master_rt_put)(struct pci_dev *pci_dev); + void (*runtime_get)(struct mhi_device_ctxt *mhi_dev_ctxt); + void (*runtime_put)(struct mhi_device_ctxt *mhi_dev_ctxt); + void (*assert_wake)(struct mhi_device_ctxt *mhi_dev_ctxt, + bool force_set); + void (*deassert_wake)(struct mhi_device_ctxt *mhi_dev_ctxt); -struct mhi_pcie_dev_info { - struct pcie_core_info core; - struct mhi_device_ctxt mhi_ctxt; - struct msm_pcie_register_event mhi_pci_link_event; - struct pci_dev *pcie_device; - struct pci_driver *mhi_pcie_driver; - struct bhi_ctxt_t bhi_ctxt; - struct platform_device *plat_dev; - u32 link_down_cntr; - u32 link_up_cntr; + struct completion cmd_complete; }; -struct mhi_pcie_devices { - struct mhi_pcie_dev_info device_list[MHI_MAX_SUPPORTED_DEVICES]; - s32 nr_of_devices; +struct mhi_device_driver { + struct mutex lock; + struct list_head head; + struct class *mhi_bhi_class; + struct dentry *parent; }; struct mhi_event_ring_cfg { u32 nr_desc; u32 msi_vec; u32 intmod; + enum MHI_CLIENT_CHANNEL chan; u32 flags; + /* + * Priority of event handling: + * 0 = highest, handle events in isr (reserved for future) + * 1 = handles event using tasklet + * 2 = handles events using workerthread + */ + u32 priority; enum MHI_RING_CLASS class; enum MHI_EVENT_RING_STATE state; irqreturn_t (*mhi_handler_ptr)(int , void *); }; +#define MHI_EV_PRIORITY_TASKLET (1) struct mhi_data_buf { dma_addr_t bounce_buffer; @@ -570,18 +623,20 @@ struct mhi_data_buf { u32 bounce_flag; }; +extern struct mhi_device_driver *mhi_device_drv; + irqreturn_t mhi_msi_ipa_handlr(int irq_number, void *dev_id); int mhi_reset_all_thread_queues( struct mhi_device_ctxt *mhi_dev_ctxt); int mhi_add_elements_to_event_rings( struct mhi_device_ctxt *mhi_dev_ctxt, enum STATE_TRANSITION new_state); -int get_nr_avail_ring_elements(struct mhi_ring *ring); +int get_nr_avail_ring_elements(struct mhi_device_ctxt *mhi_dev_ctxt, + struct mhi_ring *ring); int get_nr_enclosed_el(struct mhi_ring *ring, void *loc_1, void *loc_2, u32 *nr_el); int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt); -int mhi_init_device_ctxt(struct mhi_pcie_dev_info *dev_info, - struct mhi_device_ctxt *mhi_dev_ctxt); +int mhi_init_device_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt); int mhi_init_local_event_ring(struct mhi_device_ctxt *mhi_dev_ctxt, u32 nr_ev_el, u32 event_ring_index); int mhi_send_cmd(struct mhi_device_ctxt *dest_device, @@ -618,25 +673,24 @@ enum MHI_EVENT_CCS get_cmd_pkt(struct mhi_device_ctxt *mhi_dev_ctxt, union mhi_cmd_pkt **cmd_pkt, u32 event_index); int parse_cmd_event(struct mhi_device_ctxt *ctxt, union mhi_event_pkt *event, u32 event_index); -int parse_event_thread(void *ctxt); int mhi_test_for_device_ready( struct mhi_device_ctxt *mhi_dev_ctxt); int mhi_test_for_device_reset( struct mhi_device_ctxt *mhi_dev_ctxt); int validate_ring_el_addr(struct mhi_ring *ring, uintptr_t addr); int validate_ev_el_addr(struct mhi_ring *ring, uintptr_t addr); -int mhi_state_change_thread(void *ctxt); +void mhi_state_change_worker(struct work_struct *work); int mhi_init_state_transition(struct mhi_device_ctxt *mhi_dev_ctxt, enum STATE_TRANSITION new_state); int mhi_wait_for_mdm(struct mhi_device_ctxt *mhi_dev_ctxt); enum hrtimer_restart mhi_initiate_m1(struct hrtimer *timer); int mhi_pci_suspend(struct device *dev); int mhi_pci_resume(struct device *dev); -int mhi_init_pcie_device(struct mhi_pcie_dev_info *mhi_pcie_dev); +int mhi_init_pcie_device(struct mhi_device_ctxt *mhi_dev_ctxt); int mhi_init_pm_sysfs(struct device *dev); void mhi_rem_pm_sysfs(struct device *dev); void mhi_pci_remove(struct pci_dev *mhi_device); -int mhi_ctxt_init(struct mhi_pcie_dev_info *mhi_pcie_dev); +int mhi_ctxt_init(struct mhi_device_ctxt *mhi_dev_ctxt); int mhi_get_chan_max_buffers(u32 chan); int mhi_esoc_register(struct mhi_device_ctxt *mhi_dev_ctxt); void mhi_link_state_cb(struct msm_pcie_notify *notify); @@ -644,6 +698,10 @@ void mhi_notify_clients(struct mhi_device_ctxt *mhi_dev_ctxt, enum MHI_CB_REASON reason); void mhi_notify_client(struct mhi_client_handle *client_handle, enum MHI_CB_REASON reason); +void mhi_master_mode_runtime_get(struct mhi_device_ctxt *mhi_dev_ctxt); +void mhi_master_mode_runtime_put(struct mhi_device_ctxt *mhi_dev_ctxt); +void mhi_slave_mode_runtime_get(struct mhi_device_ctxt *mhi_dev_ctxt); +void mhi_slave_mode_runtime_put(struct mhi_device_ctxt *mhi_dev_ctxt); void mhi_deassert_device_wake(struct mhi_device_ctxt *mhi_dev_ctxt); void mhi_assert_device_wake(struct mhi_device_ctxt *mhi_dev_ctxt, bool force_set); @@ -691,10 +749,13 @@ void init_event_ctxt_array(struct mhi_device_ctxt *mhi_dev_ctxt); int create_local_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt); enum MHI_STATE mhi_get_m_state(struct mhi_device_ctxt *mhi_dev_ctxt); void process_m1_transition(struct work_struct *work); -int set_mhi_base_state(struct mhi_pcie_dev_info *mhi_pcie_dev); +int set_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt); void mhi_set_m_state(struct mhi_device_ctxt *mhi_dev_ctxt, enum MHI_STATE new_state); const char *state_transition_str(enum STATE_TRANSITION state); -void mhi_ctrl_ev_task(unsigned long data); +void mhi_ev_task(unsigned long data); +void process_event_ring(struct work_struct *work); +int process_m0_transition(struct mhi_device_ctxt *mhi_dev_ctxt); +int process_m3_transition(struct mhi_device_ctxt *mhi_dev_ctxt); #endif diff --git a/drivers/platform/msm/mhi/mhi_bhi.c b/drivers/platform/msm/mhi/mhi_bhi.c index 113791a62c38..0cc8967757ec 100644 --- a/drivers/platform/msm/mhi/mhi_bhi.c +++ b/drivers/platform/msm/mhi/mhi_bhi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -10,6 +10,7 @@ * GNU General Public License for more details. */ +#include <linux/firmware.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/slab.h> @@ -23,89 +24,207 @@ static int bhi_open(struct inode *mhi_inode, struct file *file_handle) { - file_handle->private_data = &mhi_devices.device_list[0]; + struct mhi_device_ctxt *mhi_dev_ctxt; + + mhi_dev_ctxt = container_of(mhi_inode->i_cdev, + struct mhi_device_ctxt, + bhi_ctxt.cdev); + file_handle->private_data = mhi_dev_ctxt; return 0; } -static ssize_t bhi_write(struct file *file, - const char __user *buf, - size_t count, loff_t *offp) +static int bhi_alloc_bhie_xfer(struct mhi_device_ctxt *mhi_dev_ctxt, + size_t size, + struct bhie_vec_table *vec_table) { - int ret_val = 0; - u32 pcie_word_val = 0; - u32 i = 0; - struct bhi_ctxt_t *bhi_ctxt = - &(((struct mhi_pcie_dev_info *)file->private_data)->bhi_ctxt); - struct mhi_device_ctxt *mhi_dev_ctxt = - &((struct mhi_pcie_dev_info *)file->private_data)->mhi_ctxt; - size_t amount_copied = 0; - uintptr_t align_len = 0x1000; - u32 tx_db_val = 0; - rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock; - const long bhi_timeout_ms = 1000; - long timeout; + struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt; + struct device *dev = &mhi_dev_ctxt->plat_dev->dev; + const phys_addr_t align = bhi_ctxt->alignment - 1; + size_t seg_size = bhi_ctxt->firmware_info.segment_size; + /* We need one additional entry for Vector Table */ + int segments = DIV_ROUND_UP(size, seg_size) + 1; + int i; + struct scatterlist *sg_list; + struct bhie_mem_info *bhie_mem_info, *info; - if (buf == NULL || 0 == count) - return -EIO; + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Total size:%lu total_seg:%d seg_size:%lu\n", + size, segments, seg_size); - if (count > BHI_MAX_IMAGE_SIZE) + sg_list = kcalloc(segments, sizeof(*sg_list), GFP_KERNEL); + if (!sg_list) return -ENOMEM; - timeout = wait_event_interruptible_timeout( - *mhi_dev_ctxt->mhi_ev_wq.bhi_event, - mhi_dev_ctxt->mhi_state == MHI_STATE_BHI, - msecs_to_jiffies(bhi_timeout_ms)); - if (timeout <= 0 && mhi_dev_ctxt->mhi_state != MHI_STATE_BHI) - return -EIO; + bhie_mem_info = kcalloc(segments, sizeof(*bhie_mem_info), GFP_KERNEL); + if (!bhie_mem_info) + goto alloc_bhi_mem_info_error; - mhi_log(MHI_MSG_INFO, "Entered. User Image size 0x%zx\n", count); + /* Allocate buffers for bhi/e vector table */ + for (i = 0; i < segments; i++) { + size_t size = seg_size; - bhi_ctxt->unaligned_image_loc = kmalloc(count + (align_len - 1), - GFP_KERNEL); + /* Last entry if for vector table */ + if (i == segments - 1) + size = sizeof(struct bhi_vec_entry) * i; + info = &bhie_mem_info[i]; + info->size = size; + info->alloc_size = info->size + align; + info->pre_aligned = + dma_alloc_coherent(dev, info->alloc_size, + &info->dma_handle, GFP_KERNEL); + if (!info->pre_aligned) + goto alloc_dma_error; + + info->phys_addr = (info->dma_handle + align) & ~align; + info->aligned = info->pre_aligned + + (info->phys_addr - info->dma_handle); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Seg:%d unaligned Img: 0x%llx aligned:0x%llx\n", + i, info->dma_handle, info->phys_addr); + } + + sg_init_table(sg_list, segments); + sg_set_buf(sg_list, info->aligned, info->size); + sg_dma_address(sg_list) = info->phys_addr; + sg_dma_len(sg_list) = info->size; + vec_table->sg_list = sg_list; + vec_table->bhie_mem_info = bhie_mem_info; + vec_table->bhi_vec_entry = info->aligned; + vec_table->segment_count = segments; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "BHI/E table successfully allocated\n"); + return 0; + +alloc_dma_error: + for (i = i - 1; i >= 0; i--) + dma_free_coherent(dev, + bhie_mem_info[i].alloc_size, + bhie_mem_info[i].pre_aligned, + bhie_mem_info[i].dma_handle); + kfree(bhie_mem_info); +alloc_bhi_mem_info_error: + kfree(sg_list); + return -ENOMEM; +} + +static int bhi_alloc_pbl_xfer(struct mhi_device_ctxt *mhi_dev_ctxt, + size_t size) +{ + struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt; + const phys_addr_t align_len = bhi_ctxt->alignment; + size_t alloc_size = size + (align_len - 1); + struct device *dev = &mhi_dev_ctxt->plat_dev->dev; + + bhi_ctxt->unaligned_image_loc = + dma_alloc_coherent(dev, alloc_size, &bhi_ctxt->dma_handle, + GFP_KERNEL); if (bhi_ctxt->unaligned_image_loc == NULL) return -ENOMEM; - mhi_log(MHI_MSG_INFO, "Unaligned Img Loc: %p\n", - bhi_ctxt->unaligned_image_loc); - bhi_ctxt->image_loc = - (void *)((uintptr_t)bhi_ctxt->unaligned_image_loc + - (align_len - (((uintptr_t)bhi_ctxt->unaligned_image_loc) % - align_len))); + bhi_ctxt->alloc_size = alloc_size; + bhi_ctxt->phy_image_loc = (bhi_ctxt->dma_handle + (align_len - 1)) & + ~(align_len - 1); + bhi_ctxt->image_loc = bhi_ctxt->unaligned_image_loc + + (bhi_ctxt->phy_image_loc - bhi_ctxt->dma_handle); + bhi_ctxt->image_size = size; - mhi_log(MHI_MSG_INFO, "Aligned Img Loc: %p\n", bhi_ctxt->image_loc); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "alloc_size:%lu image_size:%lu unal_addr:0x%llx0x al_addr:0x%llx\n", + bhi_ctxt->alloc_size, bhi_ctxt->image_size, + bhi_ctxt->dma_handle, bhi_ctxt->phy_image_loc); - bhi_ctxt->image_size = count; + return 0; +} - if (0 != copy_from_user(bhi_ctxt->image_loc, buf, count)) { - ret_val = -ENOMEM; - goto bhi_copy_error; +/* Load firmware via bhie protocol */ +static int bhi_load_bhie_firmware(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt; + struct bhie_vec_table *fw_table = &bhi_ctxt->fw_table; + const struct bhie_mem_info *bhie_mem_info = + &fw_table->bhie_mem_info[fw_table->segment_count - 1]; + u32 val; + const u32 tx_sequence = fw_table->sequence++; + unsigned long timeout; + rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock; + + /* Program TX/RX Vector table */ + read_lock_bh(pm_xfer_lock); + if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) { + read_unlock_bh(pm_xfer_lock); + return -EIO; } - amount_copied = count; - /* Flush the writes, in anticipation for a device read */ - wmb(); - mhi_log(MHI_MSG_INFO, - "Copied image from user at addr: %p\n", bhi_ctxt->image_loc); - bhi_ctxt->phy_image_loc = dma_map_single( - &mhi_dev_ctxt->dev_info->plat_dev->dev, - bhi_ctxt->image_loc, - bhi_ctxt->image_size, - DMA_TO_DEVICE); - - if (dma_mapping_error(NULL, bhi_ctxt->phy_image_loc)) { - ret_val = -EIO; - goto bhi_copy_error; + + val = HIGH_WORD(bhie_mem_info->phys_addr); + mhi_reg_write(mhi_dev_ctxt, + bhi_ctxt->bhi_base, + BHIE_TXVECADDR_HIGH_OFFS, + val); + val = LOW_WORD(bhie_mem_info->phys_addr); + mhi_reg_write(mhi_dev_ctxt, + bhi_ctxt->bhi_base, + BHIE_TXVECADDR_LOW_OFFS, + val); + val = (u32)bhie_mem_info->size; + mhi_reg_write(mhi_dev_ctxt, + bhi_ctxt->bhi_base, + BHIE_TXVECSIZE_OFFS, + val); + + /* Ring DB to begin Xfer */ + mhi_reg_write_field(mhi_dev_ctxt, + bhi_ctxt->bhi_base, + BHIE_TXVECDB_OFFS, + BHIE_TXVECDB_SEQNUM_BMSK, + BHIE_TXVECDB_SEQNUM_SHFT, + tx_sequence); + read_unlock_bh(pm_xfer_lock); + + timeout = jiffies + msecs_to_jiffies(bhi_ctxt->poll_timeout); + while (time_before(jiffies, timeout)) { + u32 current_seq, status; + + read_lock_bh(pm_xfer_lock); + if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) { + read_unlock_bh(pm_xfer_lock); + return -EIO; + } + val = mhi_reg_read(bhi_ctxt->bhi_base, BHIE_TXVECSTATUS_OFFS); + read_unlock_bh(pm_xfer_lock); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "TXVEC_STATUS:0x%x\n", val); + current_seq = (val & BHIE_TXVECSTATUS_SEQNUM_BMSK) >> + BHIE_TXVECSTATUS_SEQNUM_SHFT; + status = (val & BHIE_TXVECSTATUS_STATUS_BMSK) >> + BHIE_TXVECSTATUS_STATUS_SHFT; + if ((status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) && + (current_seq == tx_sequence)) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Image transfer complete\n"); + return 0; + } + msleep(BHI_POLL_SLEEP_TIME_MS); } - mhi_log(MHI_MSG_INFO, - "Mapped image to DMA addr 0x%lx:\n", - (uintptr_t)bhi_ctxt->phy_image_loc); - bhi_ctxt->image_size = count; + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Error xfering image via BHIE\n"); + return -EIO; +} + +static int bhi_load_firmware(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt; + u32 pcie_word_val = 0; + u32 tx_db_val = 0; + unsigned long timeout; + rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock; /* Write the image size */ read_lock_bh(pm_xfer_lock); if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) { read_unlock_bh(pm_xfer_lock); - goto bhi_copy_error; + return -EIO; } pcie_word_val = HIGH_WORD(bhi_ctxt->phy_image_loc); mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base, @@ -129,16 +248,15 @@ static ssize_t bhi_write(struct file *file, pcie_word_val = mhi_reg_read(bhi_ctxt->bhi_base, BHI_IMGTXDB); mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHI_IMGTXDB, 0xFFFFFFFF, 0, ++pcie_word_val); - - mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHI_INTVEC, 0); read_unlock_bh(pm_xfer_lock); - for (i = 0; i < BHI_POLL_NR_RETRIES; ++i) { + timeout = jiffies + msecs_to_jiffies(bhi_ctxt->poll_timeout); + while (time_before(jiffies, timeout)) { u32 err = 0, errdbg1 = 0, errdbg2 = 0, errdbg3 = 0; read_lock_bh(pm_xfer_lock); if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) { read_unlock_bh(pm_xfer_lock); - goto bhi_copy_error; + return -EIO; } err = mhi_reg_read(bhi_ctxt->bhi_base, BHI_ERRCODE); errdbg1 = mhi_reg_read(bhi_ctxt->bhi_base, BHI_ERRDBG1); @@ -149,34 +267,83 @@ static ssize_t bhi_write(struct file *file, BHI_STATUS_MASK, BHI_STATUS_SHIFT); read_unlock_bh(pm_xfer_lock); - mhi_log(MHI_MSG_CRITICAL, - "BHI STATUS 0x%x, err:0x%x errdbg1:0x%x errdbg2:0x%x errdbg3:0x%x\n", - tx_db_val, err, errdbg1, errdbg2, errdbg3); - if (BHI_STATUS_SUCCESS != tx_db_val) - mhi_log(MHI_MSG_CRITICAL, - "Incorrect BHI status: %d retry: %d\n", - tx_db_val, i); - else + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "%s 0x%x %s:0x%x %s:0x%x %s:0x%x %s:0x%x\n", + "BHI STATUS", tx_db_val, + "err", err, + "errdbg1", errdbg1, + "errdbg2", errdbg2, + "errdbg3", errdbg3); + if (tx_db_val == BHI_STATUS_SUCCESS) break; - usleep_range(20000, 25000); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "retrying...\n"); + msleep(BHI_POLL_SLEEP_TIME_MS); + } + + return (tx_db_val == BHI_STATUS_SUCCESS) ? 0 : -EIO; +} + +static ssize_t bhi_write(struct file *file, + const char __user *buf, + size_t count, loff_t *offp) +{ + int ret_val = 0; + struct mhi_device_ctxt *mhi_dev_ctxt = file->private_data; + struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt; + long timeout; + + if (buf == NULL || 0 == count) + return -EIO; + + if (count > BHI_MAX_IMAGE_SIZE) + return -ENOMEM; + + ret_val = bhi_alloc_pbl_xfer(mhi_dev_ctxt, count); + if (ret_val) + return -ENOMEM; + + if (copy_from_user(bhi_ctxt->image_loc, buf, count)) { + ret_val = -ENOMEM; + goto bhi_copy_error; } - dma_unmap_single(&mhi_dev_ctxt->dev_info->plat_dev->dev, - bhi_ctxt->phy_image_loc, - bhi_ctxt->image_size, DMA_TO_DEVICE); - kfree(bhi_ctxt->unaligned_image_loc); + timeout = wait_event_interruptible_timeout( + *mhi_dev_ctxt->mhi_ev_wq.bhi_event, + mhi_dev_ctxt->mhi_state == MHI_STATE_BHI, + msecs_to_jiffies(bhi_ctxt->poll_timeout)); + if (timeout <= 0 && mhi_dev_ctxt->mhi_state != MHI_STATE_BHI) { + ret_val = -EIO; + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Timed out waiting for BHI\n"); + goto bhi_copy_error; + } + ret_val = bhi_load_firmware(mhi_dev_ctxt); + if (ret_val) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to load bhi image\n"); + } + dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev, + bhi_ctxt->alloc_size, + bhi_ctxt->unaligned_image_loc, + bhi_ctxt->dma_handle); + + /* Regardless of failure set to RESET state */ ret_val = mhi_init_state_transition(mhi_dev_ctxt, STATE_TRANSITION_RESET); if (ret_val) { - mhi_log(MHI_MSG_CRITICAL, - "Failed to start state change event\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to start state change event\n"); } - return amount_copied; + return count; bhi_copy_error: - kfree(bhi_ctxt->unaligned_image_loc); - return amount_copied; + dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev, + bhi_ctxt->alloc_size, + bhi_ctxt->unaligned_image_loc, + bhi_ctxt->dma_handle); + + return ret_val; } static const struct file_operations bhi_fops = { @@ -184,48 +351,159 @@ static const struct file_operations bhi_fops = { .open = bhi_open, }; -int bhi_probe(struct mhi_pcie_dev_info *mhi_pcie_device) +int bhi_expose_dev_bhi(struct mhi_device_ctxt *mhi_dev_ctxt) { - struct bhi_ctxt_t *bhi_ctxt = &mhi_pcie_device->bhi_ctxt; - int ret_val = 0; - int r; + int ret_val; + struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt; + const struct pcie_core_info *core = &mhi_dev_ctxt->core; + char node_name[32]; - if (NULL == mhi_pcie_device || 0 == mhi_pcie_device->core.bar0_base - || 0 == mhi_pcie_device->core.bar0_end) - return -EIO; + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Creating dev node\n"); ret_val = alloc_chrdev_region(&bhi_ctxt->bhi_dev, 0, 1, "bhi"); if (IS_ERR_VALUE(ret_val)) { - mhi_log(MHI_MSG_CRITICAL, - "Failed to alloc char device %d\n", - ret_val); + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "Failed to alloc char device %d\n", ret_val); return -EIO; } - bhi_ctxt->bhi_class = class_create(THIS_MODULE, "bhi"); - if (IS_ERR(bhi_ctxt->bhi_class)) { - mhi_log(MHI_MSG_CRITICAL, - "Failed to instantiate class %d\n", - ret_val); - r = PTR_RET(bhi_ctxt->bhi_class); - goto err_class_create; - } cdev_init(&bhi_ctxt->cdev, &bhi_fops); bhi_ctxt->cdev.owner = THIS_MODULE; ret_val = cdev_add(&bhi_ctxt->cdev, bhi_ctxt->bhi_dev, 1); - bhi_ctxt->dev = device_create(bhi_ctxt->bhi_class, NULL, - bhi_ctxt->bhi_dev, NULL, - "bhi"); + snprintf(node_name, sizeof(node_name), + "bhi_%04X_%02u.%02u.%02u", + core->dev_id, core->domain, core->bus, core->slot); + bhi_ctxt->dev = device_create(mhi_device_drv->mhi_bhi_class, + NULL, + bhi_ctxt->bhi_dev, + NULL, + node_name); if (IS_ERR(bhi_ctxt->dev)) { - mhi_log(MHI_MSG_CRITICAL, - "Failed to add bhi cdev\n"); - r = PTR_RET(bhi_ctxt->dev); + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "Failed to add bhi cdev\n"); + ret_val = PTR_RET(bhi_ctxt->dev); goto err_dev_create; } return 0; + err_dev_create: cdev_del(&bhi_ctxt->cdev); - class_destroy(bhi_ctxt->bhi_class); -err_class_create: unregister_chrdev_region(MAJOR(bhi_ctxt->bhi_dev), 1); - return r; + return ret_val; +} + +void bhi_firmware_download(struct work_struct *work) +{ + struct mhi_device_ctxt *mhi_dev_ctxt; + struct bhi_ctxt_t *bhi_ctxt; + int ret; + long timeout; + + mhi_dev_ctxt = container_of(work, struct mhi_device_ctxt, + bhi_ctxt.fw_load_work); + bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n"); + + wait_event_interruptible(*mhi_dev_ctxt->mhi_ev_wq.bhi_event, + mhi_dev_ctxt->mhi_state == MHI_STATE_BHI); + + ret = bhi_load_firmware(mhi_dev_ctxt); + if (ret) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to Load sbl firmware\n"); + return; + } + mhi_init_state_transition(mhi_dev_ctxt, + STATE_TRANSITION_RESET); + + timeout = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.bhi_event, + mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_BHIE, + msecs_to_jiffies(bhi_ctxt->poll_timeout)); + if (!timeout) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to Enter EXEC_ENV_BHIE\n"); + return; + } + + ret = bhi_load_bhie_firmware(mhi_dev_ctxt); + if (ret) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to Load amss firmware\n"); + } +} + +int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt; + struct firmware_info *fw_info = &bhi_ctxt->firmware_info; + struct bhie_vec_table *fw_table = &bhi_ctxt->fw_table; + const struct firmware *firmware; + struct scatterlist *itr; + int ret, i; + size_t remainder; + const u8 *image; + + /* expose dev node to userspace */ + if (bhi_ctxt->manage_boot == false) + return bhi_expose_dev_bhi(mhi_dev_ctxt); + + /* Make sure minimum buffer we allocate for BHI/E is >= sbl image */ + while (fw_info->segment_size < fw_info->max_sbl_len) + fw_info->segment_size <<= 1; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "max sbl image size:%lu segment size:%lu\n", + fw_info->max_sbl_len, fw_info->segment_size); + + /* Read the fw image */ + ret = request_firmware(&firmware, fw_info->fw_image, + &mhi_dev_ctxt->plat_dev->dev); + if (ret) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Error request firmware for:%s ret:%d\n", + fw_info->fw_image, ret); + return ret; + } + + ret = bhi_alloc_bhie_xfer(mhi_dev_ctxt, + firmware->size, + fw_table); + if (ret) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Error Allocating memory for firmware image\n"); + release_firmware(firmware); + return ret; + } + + /* Copy the fw image to vector table */ + remainder = firmware->size; + image = firmware->data; + for (i = 0, itr = &fw_table->sg_list[1]; + i < fw_table->segment_count - 1; i++, itr++) { + size_t to_copy = min(remainder, fw_info->segment_size); + + memcpy(fw_table->bhie_mem_info[i].aligned, image, to_copy); + fw_table->bhi_vec_entry[i].phys_addr = + fw_table->bhie_mem_info[i].phys_addr; + fw_table->bhi_vec_entry[i].size = to_copy; + sg_set_buf(itr, fw_table->bhie_mem_info[i].aligned, to_copy); + sg_dma_address(itr) = fw_table->bhie_mem_info[i].phys_addr; + sg_dma_len(itr) = to_copy; + remainder -= to_copy; + image += to_copy; + } + + /* + * Re-use BHI/E pointer for BHI since we guranteed BHI/E segment + * is >= to SBL image. + */ + bhi_ctxt->phy_image_loc = sg_dma_address(&fw_table->sg_list[1]); + bhi_ctxt->image_size = fw_info->max_sbl_len; + + fw_table->sequence++; + release_firmware(firmware); + + /* Schedule a worker thread and wait for BHI Event */ + schedule_work(&bhi_ctxt->fw_load_work); + return 0; } diff --git a/drivers/platform/msm/mhi/mhi_bhi.h b/drivers/platform/msm/mhi/mhi_bhi.h index ca44f69cea42..15137ba5dfdf 100644 --- a/drivers/platform/msm/mhi/mhi_bhi.h +++ b/drivers/platform/msm/mhi/mhi_bhi.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -42,6 +42,38 @@ #define BHI_STATUS_SUCCESS (2) #define BHI_STATUS_RESET (0) +/* BHIE Offsets */ +#define BHIE_OFFSET (0x0124) /* BHIE register space offset from BHI base */ +#define BHIE_MSMSOCID_OFFS (BHIE_OFFSET + 0x0000) +#define BHIE_TXVECADDR_LOW_OFFS (BHIE_OFFSET + 0x002C) +#define BHIE_TXVECADDR_HIGH_OFFS (BHIE_OFFSET + 0x0030) +#define BHIE_TXVECSIZE_OFFS (BHIE_OFFSET + 0x0034) +#define BHIE_TXVECDB_OFFS (BHIE_OFFSET + 0x003C) +#define BHIE_TXVECDB_SEQNUM_BMSK (0x3FFFFFFF) +#define BHIE_TXVECDB_SEQNUM_SHFT (0) +#define BHIE_TXVECSTATUS_OFFS (BHIE_OFFSET + 0x0044) +#define BHIE_TXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF) +#define BHIE_TXVECSTATUS_SEQNUM_SHFT (0) +#define BHIE_TXVECSTATUS_STATUS_BMSK (0xC0000000) +#define BHIE_TXVECSTATUS_STATUS_SHFT (30) +#define BHIE_TXVECSTATUS_STATUS_RESET (0x00) +#define BHIE_TXVECSTATUS_STATUS_XFER_COMPL (0x02) +#define BHIE_TXVECSTATUS_STATUS_ERROR (0x03) +#define BHIE_RXVECADDR_LOW_OFFS (BHIE_OFFSET + 0x0060) +#define BHIE_RXVECADDR_HIGH_OFFS (BHIE_OFFSET + 0x0064) +#define BHIE_RXVECSIZE_OFFS (BHIE_OFFSET + 0x0068) +#define BHIE_RXVECDB_OFFS (BHIE_OFFSET + 0x0070) +#define BHIE_RXVECDB_SEQNUM_BMSK (0x3FFFFFFF) +#define BHIE_RXVECDB_SEQNUM_SHFT (0) +#define BHIE_RXVECSTATUS_OFFS (BHIE_OFFSET + 0x0078) +#define BHIE_RXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF) +#define BHIE_RXVECSTATUS_SEQNUM_SHFT (0) +#define BHIE_RXVECSTATUS_STATUS_BMSK (0xC0000000) +#define BHIE_RXVECSTATUS_STATUS_SHFT (30) +#define BHIE_RXVECSTATUS_STATUS_RESET (0x00) +#define BHIE_RXVECSTATUS_STATUS_XFER_COMPL (0x02) +#define BHIE_RXVECSTATUS_STATUS_ERROR (0x03) + #define BHI_MAJOR_VERSION 0x0 #define BHI_MINOR_VERSION 0x1 @@ -51,10 +83,12 @@ #define BHI_READBUF_SIZE sizeof(bhi_info_type) #define BHI_MAX_IMAGE_SIZE (256 * 1024) +#define BHI_DEFAULT_ALIGNMENT (0x1000) -#define BHI_POLL_SLEEP_TIME 1000 -#define BHI_POLL_NR_RETRIES 10 +#define BHI_POLL_SLEEP_TIME_MS 100 +#define BHI_POLL_TIMEOUT_MS 2000 -int bhi_probe(struct mhi_pcie_dev_info *mhi_pcie_device); +int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt); +void bhi_firmware_download(struct work_struct *work); #endif diff --git a/drivers/platform/msm/mhi/mhi_event.c b/drivers/platform/msm/mhi/mhi_event.c index fe163f3895a5..ae677bae63dc 100644 --- a/drivers/platform/msm/mhi/mhi_event.c +++ b/drivers/platform/msm/mhi/mhi_event.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -26,41 +26,57 @@ int mhi_populate_event_cfg(struct mhi_device_ctxt *mhi_dev_ctxt) int r, i; char dt_prop[MAX_BUF_SIZE]; const struct device_node *np = - mhi_dev_ctxt->dev_info->plat_dev->dev.of_node; + mhi_dev_ctxt->plat_dev->dev.of_node; r = of_property_read_u32(np, "mhi-event-rings", &mhi_dev_ctxt->mmio_info.nr_event_rings); if (r) { - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Failed to pull event ring info from DT, %d\n", r); - goto dt_error; + return -EINVAL; } mhi_dev_ctxt->ev_ring_props = kzalloc(sizeof(struct mhi_event_ring_cfg) * mhi_dev_ctxt->mmio_info.nr_event_rings, GFP_KERNEL); - if (!mhi_dev_ctxt->ev_ring_props) { - r = -ENOMEM; - goto dt_error; - } + if (!mhi_dev_ctxt->ev_ring_props) + return -ENOMEM; for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; ++i) { + u32 dt_configs[6]; + int no_elements; + scnprintf(dt_prop, MAX_BUF_SIZE, "%s%d", "mhi-event-cfg-", i); - r = of_property_read_u32_array(np, dt_prop, - (u32 *)&mhi_dev_ctxt->ev_ring_props[i], - 4); + no_elements = of_property_count_elems_of_size(np, dt_prop, + sizeof(dt_configs)); + if (no_elements != 1) + goto dt_error; + r = of_property_read_u32_array( + np, + dt_prop, + dt_configs, + sizeof(dt_configs) / sizeof(u32)); if (r) { - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Failed to pull ev ring %d info from DT %d\n", i, r); goto dt_error; } - mhi_log(MHI_MSG_INFO, - "Pulled ev ring %d,desc:0x%x,msi_vec:0x%x,intmod%d flags0x%x\n", - i, mhi_dev_ctxt->ev_ring_props[i].nr_desc, - mhi_dev_ctxt->ev_ring_props[i].msi_vec, - mhi_dev_ctxt->ev_ring_props[i].intmod, - mhi_dev_ctxt->ev_ring_props[i].flags); + mhi_dev_ctxt->ev_ring_props[i].nr_desc = dt_configs[0]; + mhi_dev_ctxt->ev_ring_props[i].msi_vec = dt_configs[1]; + mhi_dev_ctxt->ev_ring_props[i].intmod = dt_configs[2]; + mhi_dev_ctxt->ev_ring_props[i].chan = dt_configs[3]; + mhi_dev_ctxt->ev_ring_props[i].priority = dt_configs[4]; + mhi_dev_ctxt->ev_ring_props[i].flags = dt_configs[5]; + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "ev ring %d,desc:0x%x,msi:0x%x,intmod%d chan:%u priority:%u flags0x%x\n", + i, + mhi_dev_ctxt->ev_ring_props[i].nr_desc, + mhi_dev_ctxt->ev_ring_props[i].msi_vec, + mhi_dev_ctxt->ev_ring_props[i].intmod, + mhi_dev_ctxt->ev_ring_props[i].chan, + mhi_dev_ctxt->ev_ring_props[i].priority, + mhi_dev_ctxt->ev_ring_props[i].flags); if (GET_EV_PROPS(EV_MANAGED, mhi_dev_ctxt->ev_ring_props[i].flags)) mhi_dev_ctxt->ev_ring_props[i].mhi_handler_ptr = @@ -76,14 +92,18 @@ int mhi_populate_event_cfg(struct mhi_device_ctxt *mhi_dev_ctxt) mhi_dev_ctxt->ev_ring_props[i].class = MHI_SW_RING; mhi_dev_ctxt->mmio_info.nr_sw_event_rings++; } - mhi_log(MHI_MSG_INFO, - "Detected %d SW EV rings and %d HW EV rings out of %d EV rings\n", - mhi_dev_ctxt->mmio_info.nr_sw_event_rings, - mhi_dev_ctxt->mmio_info.nr_hw_event_rings, - mhi_dev_ctxt->mmio_info.nr_event_rings); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Detected %d SW EV rings and %d HW EV rings out of %d EV rings\n", + mhi_dev_ctxt->mmio_info.nr_sw_event_rings, + mhi_dev_ctxt->mmio_info.nr_hw_event_rings, + mhi_dev_ctxt->mmio_info.nr_event_rings); } + + return 0; dt_error: - return r; + kfree(mhi_dev_ctxt->ev_ring_props); + mhi_dev_ctxt->ev_ring_props = NULL; + return -EINVAL; } int create_local_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt) @@ -110,6 +130,9 @@ int create_local_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt) mhi_local_event_ctxt[i]; spin_lock_init(&mhi_ring->ring_lock); + tasklet_init(&mhi_ring->ev_task, mhi_ev_task, + (unsigned long)mhi_ring); + INIT_WORK(&mhi_ring->ev_worker, process_event_ring); } return r; @@ -139,6 +162,8 @@ void ring_ev_db(struct mhi_device_ctxt *mhi_dev_ctxt, u32 event_ring_index) static int mhi_event_ring_init(struct mhi_event_ctxt *ev_list, struct mhi_ring *ring, + struct mhi_device_ctxt *mhi_dev_ctxt, + int index, u32 el_per_ring, u32 intmodt_val, u32 msi_vec, @@ -148,6 +173,8 @@ static int mhi_event_ring_init(struct mhi_event_ctxt *ev_list, ev_list->mhi_msi_vector = msi_vec; ev_list->mhi_event_ring_len = el_per_ring*sizeof(union mhi_event_pkt); MHI_SET_EV_CTXT(EVENT_CTXT_INTMODT, ev_list, intmodt_val); + ring->mhi_dev_ctxt = mhi_dev_ctxt; + ring->index = index; ring->len = ((size_t)(el_per_ring)*sizeof(union mhi_event_pkt)); ring->el_size = sizeof(union mhi_event_pkt); ring->overwrite_en = 0; @@ -180,6 +207,7 @@ void init_event_ctxt_array(struct mhi_device_ctxt *mhi_dev_ctxt) event_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[i]; mhi_local_event_ctxt = &mhi_dev_ctxt->mhi_local_event_ctxt[i]; mhi_event_ring_init(event_ctxt, mhi_local_event_ctxt, + mhi_dev_ctxt, i, mhi_dev_ctxt->ev_ring_props[i].nr_desc, mhi_dev_ctxt->ev_ring_props[i].intmod, mhi_dev_ctxt->ev_ring_props[i].msi_vec, @@ -195,7 +223,7 @@ int init_local_ev_ring_by_type(struct mhi_device_ctxt *mhi_dev_ctxt, int ret_val = 0; u32 i; - mhi_log(MHI_MSG_INFO, "Entered\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n"); for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; i++) { if (GET_EV_PROPS(EV_TYPE, mhi_dev_ctxt->ev_ring_props[i].flags) == type && @@ -207,9 +235,10 @@ int init_local_ev_ring_by_type(struct mhi_device_ctxt *mhi_dev_ctxt, return ret_val; } ring_ev_db(mhi_dev_ctxt, i); - mhi_log(MHI_MSG_INFO, "Finished ev ring init %d\n", i); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Finished ev ring init %d\n", i); } - mhi_log(MHI_MSG_INFO, "Exited\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n"); return 0; } @@ -228,7 +257,7 @@ int mhi_add_elements_to_event_rings(struct mhi_device_ctxt *mhi_dev_ctxt, MHI_ER_DATA_TYPE); break; default: - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Unrecognized event stage, %d\n", new_state); ret_val = -EINVAL; break; @@ -247,23 +276,18 @@ int mhi_init_local_event_ring(struct mhi_device_ctxt *mhi_dev_ctxt, &mhi_dev_ctxt->mhi_local_event_ctxt[ring_index]; spinlock_t *lock = &event_ctxt->ring_lock; - if (NULL == mhi_dev_ctxt || 0 == nr_ev_el) { - mhi_log(MHI_MSG_ERROR, "Bad Input data, quitting\n"); - return -EINVAL; - } - spin_lock_irqsave(lock, flags); - mhi_log(MHI_MSG_INFO, "mmio_addr = 0x%p, mmio_len = 0x%llx\n", - mhi_dev_ctxt->mmio_info.mmio_addr, - mhi_dev_ctxt->mmio_info.mmio_len); - mhi_log(MHI_MSG_INFO, "Initializing event ring %d with %d desc\n", - ring_index, nr_ev_el); + mhi_log(mhi_dev_ctxt, + MHI_MSG_INFO, + "Initializing event ring %d with %d desc\n", + ring_index, + nr_ev_el); for (i = 0; i < nr_ev_el - 1; ++i) { ret_val = ctxt_add_element(event_ctxt, (void *)&ev_pkt); if (0 != ret_val) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to insert el in ev ctxt\n"); break; } @@ -279,7 +303,8 @@ void mhi_reset_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, struct mhi_event_ctxt *ev_ctxt; struct mhi_ring *local_ev_ctxt; - mhi_log(MHI_MSG_VERBOSE, "Resetting event index %d\n", index); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, + "Resetting event index %d\n", index); ev_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[index]; local_ev_ctxt = diff --git a/drivers/platform/msm/mhi/mhi_iface.c b/drivers/platform/msm/mhi/mhi_iface.c index 395e19c91f35..f1c562974816 100644 --- a/drivers/platform/msm/mhi/mhi_iface.c +++ b/drivers/platform/msm/mhi/mhi_iface.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -32,12 +32,11 @@ #include "mhi_hwio.h" #include "mhi_bhi.h" -struct mhi_pcie_devices mhi_devices; +struct mhi_device_driver *mhi_device_drv; static int mhi_pci_probe(struct pci_dev *pcie_device, const struct pci_device_id *mhi_device_id); static int __exit mhi_plat_remove(struct platform_device *pdev); -void *mhi_ipc_log; static DEFINE_PCI_DEVICE_TABLE(mhi_pcie_device_id) = { { MHI_PCIE_VENDOR_ID, MHI_PCIE_DEVICE_ID_9x35, @@ -59,129 +58,71 @@ static const struct of_device_id mhi_plat_match[] = { static void mhi_msm_fixup(struct pci_dev *pcie_device) { if (pcie_device->class == PCI_CLASS_NOT_DEFINED) { - mhi_log(MHI_MSG_INFO, "Setting msm pcie class\n"); pcie_device->class = PCI_CLASS_STORAGE_SCSI; } } -int mhi_ctxt_init(struct mhi_pcie_dev_info *mhi_pcie_dev) +int mhi_ctxt_init(struct mhi_device_ctxt *mhi_dev_ctxt) { int ret_val = 0; - u32 i = 0, j = 0; - u32 requested_msi_number = 32, actual_msi_number = 0; - struct mhi_device_ctxt *mhi_dev_ctxt = NULL; - struct pci_dev *pcie_device = NULL; + u32 j = 0; - if (NULL == mhi_pcie_dev) - return -EINVAL; - pcie_device = mhi_pcie_dev->pcie_device; - - ret_val = mhi_init_pcie_device(mhi_pcie_dev); - if (ret_val) { - mhi_log(MHI_MSG_CRITICAL, - "Failed to initialize pcie device, ret %d\n", - ret_val); - return -ENODEV; - } - ret_val = mhi_init_device_ctxt(mhi_pcie_dev, &mhi_pcie_dev->mhi_ctxt); - if (ret_val) { - mhi_log(MHI_MSG_CRITICAL, - "Failed to initialize main MHI ctxt ret %d\n", - ret_val); - goto msi_config_err; - } - ret_val = mhi_esoc_register(&mhi_pcie_dev->mhi_ctxt); + ret_val = mhi_init_device_ctxt(mhi_dev_ctxt); if (ret_val) { - mhi_log(MHI_MSG_ERROR, - "Failed to register with esoc ret %d.\n", - ret_val); - } - - device_disable_async_suspend(&pcie_device->dev); - ret_val = pci_enable_msi_range(pcie_device, 1, requested_msi_number); - if (IS_ERR_VALUE(ret_val)) { - mhi_log(MHI_MSG_ERROR, - "Failed to enable MSIs for pcie dev ret_val %d.\n", - ret_val); - goto msi_config_err; - } else if (ret_val) { - mhi_log(MHI_MSG_INFO, - "Hrmmm, got fewer MSIs than we requested. Requested %d, got %d.\n", - requested_msi_number, ret_val); - actual_msi_number = ret_val; - } else { - mhi_log(MHI_MSG_VERBOSE, - "Got all requested MSIs, moving on\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "Failed to initialize main MHI ctxt ret %d\n", ret_val); + return ret_val; } - mhi_dev_ctxt = &mhi_pcie_dev->mhi_ctxt; for (j = 0; j < mhi_dev_ctxt->mmio_info.nr_event_rings; j++) { - mhi_log(MHI_MSG_VERBOSE, - "MSI_number = %d, event ring number = %d\n", - mhi_dev_ctxt->ev_ring_props[j].msi_vec, j); - - ret_val = request_irq(pcie_device->irq + + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, + "MSI_number = %d, event ring number = %d\n", + mhi_dev_ctxt->ev_ring_props[j].msi_vec, j); + + /* outside of requested irq boundary */ + if (mhi_dev_ctxt->core.max_nr_msis <= + mhi_dev_ctxt->ev_ring_props[j].msi_vec) { + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "max msi supported:%d request:%d ev:%d\n", + mhi_dev_ctxt->core.max_nr_msis, + mhi_dev_ctxt->ev_ring_props[j].msi_vec, + j); + goto irq_error; + } + ret_val = request_irq(mhi_dev_ctxt->core.irq_base + mhi_dev_ctxt->ev_ring_props[j].msi_vec, mhi_dev_ctxt->ev_ring_props[j].mhi_handler_ptr, IRQF_NO_SUSPEND, "mhi_drv", - (void *)&pcie_device->dev); + (void *)mhi_dev_ctxt); if (ret_val) { - mhi_log(MHI_MSG_ERROR, - "Failed to register handler for MSI ret_val = %d\n", - ret_val); - goto msi_config_err; + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to register handler for MSI ret_val = %d\n", + ret_val); + goto irq_error; } } - mhi_pcie_dev->core.irq_base = pcie_device->irq; - mhi_log(MHI_MSG_VERBOSE, - "Setting IRQ Base to 0x%x\n", mhi_pcie_dev->core.irq_base); - mhi_pcie_dev->core.max_nr_msis = requested_msi_number; - ret_val = mhi_init_pm_sysfs(&pcie_device->dev); - if (ret_val) { - mhi_log(MHI_MSG_ERROR, "Failed to setup sysfs ret %d\n", - ret_val); - goto sysfs_config_err; - } - if (!mhi_init_debugfs(&mhi_pcie_dev->mhi_ctxt)) - mhi_log(MHI_MSG_ERROR, "Failed to init debugfs.\n"); - - mhi_pcie_dev->mhi_ctxt.mmio_info.mmio_addr = - mhi_pcie_dev->core.bar0_base; - pcie_device->dev.platform_data = &mhi_pcie_dev->mhi_ctxt; - mhi_pcie_dev->mhi_ctxt.dev_info->plat_dev->dev.platform_data = - &mhi_pcie_dev->mhi_ctxt; - ret_val = mhi_reg_notifiers(&mhi_pcie_dev->mhi_ctxt); - if (ret_val) { - mhi_log(MHI_MSG_ERROR, "Failed to register for notifiers\n"); - goto mhi_state_transition_error; - } - mhi_log(MHI_MSG_INFO, - "Finished all driver probing returning ret_val %d.\n", - ret_val); - return ret_val; -mhi_state_transition_error: + mhi_dev_ctxt->mmio_info.mmio_addr = mhi_dev_ctxt->core.bar0_base; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "exit\n"); + return 0; + +irq_error: kfree(mhi_dev_ctxt->state_change_work_item_list.q_lock); - kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq); - kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event); kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event); kfree(mhi_dev_ctxt->mhi_ev_wq.m3_event); kfree(mhi_dev_ctxt->mhi_ev_wq.bhi_event); - dma_free_coherent(&mhi_dev_ctxt->dev_info->plat_dev->dev, + dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev, mhi_dev_ctxt->dev_space.dev_mem_len, mhi_dev_ctxt->dev_space.dev_mem_start, mhi_dev_ctxt->dev_space.dma_dev_mem_start); kfree(mhi_dev_ctxt->ev_ring_props); - mhi_rem_pm_sysfs(&pcie_device->dev); -sysfs_config_err: - for (; i >= 0; --i) - free_irq(pcie_device->irq + i, &pcie_device->dev); - debugfs_remove_recursive(mhi_pcie_dev->mhi_ctxt.mhi_parent_folder); -msi_config_err: - pci_disable_device(pcie_device); - return ret_val; + for (j = j - 1; j >= 0; --j) + free_irq(mhi_dev_ctxt->core.irq_base + j, NULL); + + return -EINVAL; } static const struct dev_pm_ops pm_ops = { @@ -204,73 +145,154 @@ static int mhi_pci_probe(struct pci_dev *pcie_device, const struct pci_device_id *mhi_device_id) { int ret_val = 0; - struct mhi_pcie_dev_info *mhi_pcie_dev = NULL; struct platform_device *plat_dev; - struct mhi_device_ctxt *mhi_dev_ctxt; - u32 nr_dev = mhi_devices.nr_of_devices; + struct mhi_device_ctxt *mhi_dev_ctxt = NULL, *itr; + u32 domain = pci_domain_nr(pcie_device->bus); + u32 bus = pcie_device->bus->number; + u32 dev_id = pcie_device->device; + u32 slot = PCI_SLOT(pcie_device->devfn); + unsigned long msi_requested, msi_required; + struct msm_pcie_register_event *mhi_pci_link_event; + + /* Find correct device context based on bdf & dev_id */ + mutex_lock(&mhi_device_drv->lock); + list_for_each_entry(itr, &mhi_device_drv->head, node) { + struct pcie_core_info *core = &itr->core; + + if (core->domain == domain && + core->bus == bus && + core->dev_id == dev_id && + core->slot == slot) { + mhi_dev_ctxt = itr; + break; + } + } + mutex_unlock(&mhi_device_drv->lock); + if (!mhi_dev_ctxt) + return -EPROBE_DEFER; - mhi_log(MHI_MSG_INFO, "Entering\n"); - mhi_pcie_dev = &mhi_devices.device_list[mhi_devices.nr_of_devices]; - if (mhi_devices.nr_of_devices + 1 > MHI_MAX_SUPPORTED_DEVICES) { - mhi_log(MHI_MSG_ERROR, "Error: Too many devices\n"); - return -ENOMEM; + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Processing Domain:%02u Bus:%04u dev:0x%04x slot:%04u\n", + domain, bus, dev_id, slot); + + ret_val = of_property_read_u32(mhi_dev_ctxt->plat_dev->dev.of_node, + "mhi-event-rings", + (u32 *)&msi_required); + if (ret_val) { + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "Failed to pull ev ring info from DT, %d\n", ret_val); + return ret_val; } - mhi_devices.nr_of_devices++; - plat_dev = mhi_devices.device_list[nr_dev].plat_dev; + plat_dev = mhi_dev_ctxt->plat_dev; pcie_device->dev.of_node = plat_dev->dev.of_node; - mhi_dev_ctxt = &mhi_devices.device_list[nr_dev].mhi_ctxt; mhi_dev_ctxt->mhi_pm_state = MHI_PM_DISABLE; INIT_WORK(&mhi_dev_ctxt->process_m1_worker, process_m1_transition); + INIT_WORK(&mhi_dev_ctxt->st_thread_worker, mhi_state_change_worker); mutex_init(&mhi_dev_ctxt->pm_lock); rwlock_init(&mhi_dev_ctxt->pm_xfer_lock); spin_lock_init(&mhi_dev_ctxt->dev_wake_lock); - tasklet_init(&mhi_dev_ctxt->ev_task, - mhi_ctrl_ev_task, - (unsigned long)mhi_dev_ctxt); - + init_completion(&mhi_dev_ctxt->cmd_complete); mhi_dev_ctxt->flags.link_up = 1; - ret_val = mhi_set_bus_request(mhi_dev_ctxt, 1); - mhi_pcie_dev->pcie_device = pcie_device; - mhi_pcie_dev->mhi_pcie_driver = &mhi_pcie_driver; - mhi_pcie_dev->mhi_pci_link_event.events = - (MSM_PCIE_EVENT_LINKDOWN | MSM_PCIE_EVENT_WAKEUP); - mhi_pcie_dev->mhi_pci_link_event.user = pcie_device; - mhi_pcie_dev->mhi_pci_link_event.callback = mhi_link_state_cb; - mhi_pcie_dev->mhi_pci_link_event.notify.data = mhi_pcie_dev; - ret_val = msm_pcie_register_event(&mhi_pcie_dev->mhi_pci_link_event); + + /* Setup bus scale */ + mhi_dev_ctxt->bus_scale_table = msm_bus_cl_get_pdata(plat_dev); + if (!mhi_dev_ctxt->bus_scale_table) + return -ENODATA; + mhi_dev_ctxt->bus_client = msm_bus_scale_register_client + (mhi_dev_ctxt->bus_scale_table); + if (!mhi_dev_ctxt->bus_client) + return -EINVAL; + mhi_set_bus_request(mhi_dev_ctxt, 1); + + mhi_dev_ctxt->pcie_device = pcie_device; + + mhi_pci_link_event = &mhi_dev_ctxt->mhi_pci_link_event; + mhi_pci_link_event->events = + (MSM_PCIE_EVENT_LINKDOWN | MSM_PCIE_EVENT_WAKEUP); + mhi_pci_link_event->user = pcie_device; + mhi_pci_link_event->callback = mhi_link_state_cb; + mhi_pci_link_event->notify.data = mhi_dev_ctxt; + ret_val = msm_pcie_register_event(mhi_pci_link_event); if (ret_val) { - mhi_log(MHI_MSG_ERROR, - "Failed to register for link notifications %d.\n", + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to reg for link notifications %d\n", ret_val); + return ret_val; + } + + dev_set_drvdata(&pcie_device->dev, mhi_dev_ctxt); + + mhi_dev_ctxt->core.pci_master = true; + ret_val = mhi_init_pcie_device(mhi_dev_ctxt); + if (ret_val) { + mhi_log(mhi_dev_ctxt, + MHI_MSG_CRITICAL, + "Failed to initialize pcie device, ret %d\n", ret_val); return ret_val; } + pci_set_master(pcie_device); + device_disable_async_suspend(&pcie_device->dev); - /* Initialize MHI CNTXT */ - ret_val = mhi_ctxt_init(mhi_pcie_dev); + ret_val = mhi_esoc_register(mhi_dev_ctxt); if (ret_val) { - mhi_log(MHI_MSG_ERROR, - "MHI Initialization failed, ret %d\n", + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Failed to reg with esoc ret %d\n", ret_val); + } + + /* # of MSI requested must be power of 2 */ + msi_requested = 1 << find_last_bit(&msi_required, 32); + if (msi_requested < msi_required) + msi_requested <<= 1; + + ret_val = pci_enable_msi_range(pcie_device, 1, msi_requested); + if (IS_ERR_VALUE(ret_val) || (ret_val < msi_requested)) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to enable MSIs for pcie dev ret_val %d.\n", ret_val); + return -EIO; + } + + mhi_dev_ctxt->core.max_nr_msis = msi_requested; + mhi_dev_ctxt->core.irq_base = pcie_device->irq; + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, + "Setting IRQ Base to 0x%x\n", mhi_dev_ctxt->core.irq_base); + + /* Initialize MHI CNTXT */ + ret_val = mhi_ctxt_init(mhi_dev_ctxt); + if (ret_val) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "MHI Initialization failed, ret %d\n", ret_val); goto deregister_pcie; } - pci_set_master(mhi_pcie_dev->pcie_device); + + mhi_init_pm_sysfs(&pcie_device->dev); + mhi_init_debugfs(mhi_dev_ctxt); + mhi_reg_notifiers(mhi_dev_ctxt); + + /* setup shadow pm functions */ + mhi_dev_ctxt->assert_wake = mhi_assert_device_wake; + mhi_dev_ctxt->deassert_wake = mhi_deassert_device_wake; + mhi_dev_ctxt->runtime_get = mhi_master_mode_runtime_get; + mhi_dev_ctxt->runtime_put = mhi_master_mode_runtime_put; mutex_lock(&mhi_dev_ctxt->pm_lock); write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); mhi_dev_ctxt->mhi_pm_state = MHI_PM_POR; - ret_val = set_mhi_base_state(mhi_pcie_dev); + ret_val = set_mhi_base_state(mhi_dev_ctxt); + write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); if (ret_val) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, + MHI_MSG_ERROR, "Error Setting MHI Base State %d\n", ret_val); goto unlock_pm_lock; } if (mhi_dev_ctxt->base_state == STATE_TRANSITION_BHI) { - ret_val = bhi_probe(mhi_pcie_dev); + ret_val = bhi_probe(mhi_dev_ctxt); if (ret_val) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Error with bhi_probe ret:%d", ret_val); goto unlock_pm_lock; } @@ -312,33 +334,144 @@ static int mhi_pci_probe(struct pci_dev *pcie_device, unlock_pm_lock: mutex_unlock(&mhi_dev_ctxt->pm_lock); deregister_pcie: - msm_pcie_deregister_event(&mhi_pcie_dev->mhi_pci_link_event); + msm_pcie_deregister_event(&mhi_dev_ctxt->mhi_pci_link_event); return ret_val; } static int mhi_plat_probe(struct platform_device *pdev) { - u32 nr_dev = mhi_devices.nr_of_devices; + int r = 0, len; struct mhi_device_ctxt *mhi_dev_ctxt; - int r = 0; + struct pcie_core_info *core; + char node[32]; + struct device_node *of_node = pdev->dev.of_node; + u64 address_window[2]; - mhi_log(MHI_MSG_INFO, "Entered\n"); - mhi_dev_ctxt = &mhi_devices.device_list[nr_dev].mhi_ctxt; + if (of_node == NULL) + return -ENODEV; - mhi_dev_ctxt->bus_scale_table = msm_bus_cl_get_pdata(pdev); - if (!mhi_dev_ctxt->bus_scale_table) - return -ENODATA; - mhi_dev_ctxt->bus_client = msm_bus_scale_register_client - (mhi_dev_ctxt->bus_scale_table); - if (!mhi_dev_ctxt->bus_client) - return -EINVAL; + pdev->id = of_alias_get_id(of_node, "mhi"); + if (pdev->id < 0) + return -ENODEV; - mhi_devices.device_list[nr_dev].plat_dev = pdev; - r = dma_set_mask(&pdev->dev, MHI_DMA_MASK); + mhi_dev_ctxt = devm_kzalloc(&pdev->dev, + sizeof(*mhi_dev_ctxt), + GFP_KERNEL); + if (!mhi_dev_ctxt) + return -ENOMEM; + + if (!of_find_property(of_node, "qcom,mhi-address-window", &len)) + return -ENODEV; + + if (len != sizeof(address_window)) + return -ENODEV; + + r = of_property_read_u64_array(of_node, + "qcom,mhi-address-window", + address_window, + sizeof(address_window) / sizeof(u64)); + if (r) + return r; + + core = &mhi_dev_ctxt->core; + r = of_property_read_u32(of_node, "qcom,pci-dev_id", &core->dev_id); + if (r) + return r; + + r = of_property_read_u32(of_node, "qcom,pci-slot", &core->slot); + if (r) + return r; + + r = of_property_read_u32(of_node, "qcom,pci-domain", &core->domain); if (r) - mhi_log(MHI_MSG_CRITICAL, + return r; + + r = of_property_read_u32(of_node, "qcom,pci-bus", &core->bus); + if (r) + return r; + + snprintf(node, sizeof(node), + "mhi_%04x_%02u.%02u.%02u", + core->dev_id, core->domain, core->bus, core->slot); + mhi_dev_ctxt->mhi_ipc_log = + ipc_log_context_create(MHI_IPC_LOG_PAGES, node, 0); + if (!mhi_dev_ctxt->mhi_ipc_log) + pr_err("%s: Error creating ipc_log buffer\n", __func__); + + r = of_property_read_u32(of_node, "qcom,mhi-ready-timeout", + &mhi_dev_ctxt->poll_reset_timeout_ms); + if (r) + mhi_dev_ctxt->poll_reset_timeout_ms = + MHI_READY_STATUS_TIMEOUT_MS; + + mhi_dev_ctxt->dev_space.start_win_addr = address_window[0]; + mhi_dev_ctxt->dev_space.end_win_addr = address_window[1]; + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Start Addr:0x%llx End_Addr:0x%llx\n", + mhi_dev_ctxt->dev_space.start_win_addr, + mhi_dev_ctxt->dev_space.end_win_addr); + + r = of_property_read_u32(of_node, "qcom,bhi-alignment", + &mhi_dev_ctxt->bhi_ctxt.alignment); + if (r) + mhi_dev_ctxt->bhi_ctxt.alignment = BHI_DEFAULT_ALIGNMENT; + + r = of_property_read_u32(of_node, "qcom,bhi-poll-timeout", + &mhi_dev_ctxt->bhi_ctxt.poll_timeout); + if (r) + mhi_dev_ctxt->bhi_ctxt.poll_timeout = BHI_POLL_TIMEOUT_MS; + + mhi_dev_ctxt->bhi_ctxt.manage_boot = + of_property_read_bool(pdev->dev.of_node, + "qcom,mhi-manage-boot"); + if (mhi_dev_ctxt->bhi_ctxt.manage_boot) { + struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt; + struct firmware_info *fw_info = &bhi_ctxt->firmware_info; + + r = of_property_read_string(of_node, "qcom,mhi-fw-image", + &fw_info->fw_image); + if (r) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Error reading DT node 'qcom,mhi-fw-image'\n"); + return r; + } + r = of_property_read_u32(of_node, "qcom,mhi-max-sbl", + (u32 *)&fw_info->max_sbl_len); + if (r) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Error reading DT node 'qcom,mhi-max-sbl'\n"); + return r; + } + r = of_property_read_u32(of_node, "qcom,mhi-sg-size", + (u32 *)&fw_info->segment_size); + if (r) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Error reading DT node 'qcom,mhi-sg-size'\n"); + return r; + } + INIT_WORK(&bhi_ctxt->fw_load_work, bhi_firmware_download); + } + + mhi_dev_ctxt->flags.bb_required = + of_property_read_bool(pdev->dev.of_node, + "qcom,mhi-bb-required"); + + mhi_dev_ctxt->plat_dev = pdev; + platform_set_drvdata(pdev, mhi_dev_ctxt); + + r = dma_set_mask(&pdev->dev, MHI_DMA_MASK); + if (r) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to set mask for DMA ret %d\n", r); - mhi_log(MHI_MSG_INFO, "Exited\n"); + return r; + } + + mhi_dev_ctxt->parent = mhi_device_drv->parent; + mutex_lock(&mhi_device_drv->lock); + list_add_tail(&mhi_dev_ctxt->node, &mhi_device_drv->head); + mutex_unlock(&mhi_device_drv->lock); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n"); + return 0; } @@ -354,42 +487,59 @@ static struct platform_driver mhi_plat_driver = { static void __exit mhi_exit(void) { - ipc_log_context_destroy(mhi_ipc_log); pci_unregister_driver(&mhi_pcie_driver); platform_driver_unregister(&mhi_plat_driver); } static int __exit mhi_plat_remove(struct platform_device *pdev) { - platform_driver_unregister(&mhi_plat_driver); + struct mhi_device_ctxt *mhi_dev_ctxt = platform_get_drvdata(pdev); + + ipc_log_context_destroy(mhi_dev_ctxt->mhi_ipc_log); return 0; } static int __init mhi_init(void) { int r; + struct mhi_device_driver *mhi_dev_drv; + + mhi_dev_drv = kmalloc(sizeof(*mhi_dev_drv), GFP_KERNEL); + if (mhi_dev_drv == NULL) + return -ENOMEM; + + mutex_init(&mhi_dev_drv->lock); + mutex_lock(&mhi_dev_drv->lock); + INIT_LIST_HEAD(&mhi_dev_drv->head); + mutex_unlock(&mhi_dev_drv->lock); + mhi_dev_drv->mhi_bhi_class = class_create(THIS_MODULE, "bhi"); + if (IS_ERR(mhi_dev_drv->mhi_bhi_class)) { + pr_err("Error creating mhi_bhi_class\n"); + goto class_error; + } + mhi_dev_drv->parent = debugfs_create_dir("mhi", NULL); + mhi_device_drv = mhi_dev_drv; - mhi_log(MHI_MSG_INFO, "Entered\n"); r = platform_driver_register(&mhi_plat_driver); if (r) { - mhi_log(MHI_MSG_INFO, "Failed to probe platform ret %d\n", r); - return r; + pr_err("%s: Failed to probe platform ret %d\n", __func__, r); + goto platform_error; } r = pci_register_driver(&mhi_pcie_driver); if (r) { - mhi_log(MHI_MSG_INFO, - "Failed to register pcie drv ret %d\n", r); + pr_err("%s: Failed to register pcie drv ret %d\n", __func__, r); goto error; } - mhi_ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES, "mhi", 0); - if (!mhi_ipc_log) { - mhi_log(MHI_MSG_ERROR, - "Failed to create IPC logging context\n"); - } - mhi_log(MHI_MSG_INFO, "Exited\n"); + return 0; error: - pci_unregister_driver(&mhi_pcie_driver); + platform_driver_unregister(&mhi_plat_driver); +platform_error: + class_destroy(mhi_device_drv->mhi_bhi_class); + +class_error: + kfree(mhi_dev_drv); + mhi_device_drv = NULL; return r; } @@ -407,7 +557,7 @@ DECLARE_PCI_FIXUP_HEADER(MHI_PCIE_VENDOR_ID, module_exit(mhi_exit); -module_init(mhi_init); +subsys_initcall(mhi_init); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("MHI_CORE"); diff --git a/drivers/platform/msm/mhi/mhi_init.c b/drivers/platform/msm/mhi/mhi_init.c index a496c81239bf..b6edf707798b 100644 --- a/drivers/platform/msm/mhi/mhi_init.c +++ b/drivers/platform/msm/mhi/mhi_init.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -53,12 +53,12 @@ size_t calculate_mhi_space(struct mhi_device_ctxt *mhi_dev_ctxt) (NR_OF_CMD_RINGS * sizeof(struct mhi_chan_ctxt)) + (mhi_dev_ctxt->mmio_info.nr_event_rings * sizeof(struct mhi_event_ctxt)); - mhi_log(MHI_MSG_INFO, "Reserved %zd bytes for context info\n", - mhi_dev_mem); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Reserved %zd bytes for context info\n", mhi_dev_mem); /*Calculate size needed for cmd TREs */ mhi_dev_mem += (CMD_EL_PER_RING * sizeof(union mhi_cmd_pkt)); - mhi_log(MHI_MSG_INFO, "Final bytes for MHI device space %zd\n", - mhi_dev_mem); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Final bytes for MHI device space %zd\n", mhi_dev_mem); return mhi_dev_mem; } @@ -105,23 +105,6 @@ void init_local_chan_ctxt(struct mhi_ring *chan_ctxt, chan_ctxt->overwrite_en = 0; } -int populate_bb_list(struct list_head *bb_list, int num_bb) -{ - struct mhi_buf_info *mhi_buf = NULL; - int i; - - for (i = 0; i < num_bb; ++i) { - mhi_buf = kzalloc(sizeof(struct mhi_buf_info), GFP_KERNEL); - if (!mhi_buf) - return -ENOMEM; - mhi_buf->bb_p_addr = 0; - mhi_buf->bb_v_addr = NULL; - mhi_log(MHI_MSG_INFO, - "Allocated BB v_addr 0x%p, p_addr 0x%llx\n", - mhi_buf->bb_v_addr, (u64)mhi_buf->bb_p_addr); - } - return 0; -} /** * mhi_cmd_ring_init- Initialization of the command ring * @@ -153,91 +136,6 @@ static int mhi_cmd_ring_init(struct mhi_cmd_ctxt *cmd_ctxt, return 0; } -/* - * The device can have severe addressing limitations, and in this case - * the MHI driver may be restricted on where memory can be allocated. - * - * The allocation of the MHI control data structures takes place as one - * big, physically contiguous allocation. - * The device's addressing window, must be placed around that control segment - * allocation. - * Here we attempt to do this by building an addressing window around the - * initial allocated control segment. - * - * The window size is specified by the device and must be contiguous, - * but depending on where the control segment was allocated, it may be - * necessary to leave more room, before the ctrl segment start or after - * the ctrl segment end. - * The following assumptions are made: - * Assumption: 1. size of allocated ctrl seg << (device allocation window / 2) - * 2. allocated ctrl seg is physically contiguous - */ -static int calculate_mhi_addressing_window(struct mhi_device_ctxt *mhi_dev_ctxt) -{ - u64 dma_dev_mem_start = 0; - u64 dma_seg_size = 0; - u64 dma_max_addr = (dma_addr_t)(-1); - u64 dev_address_limit = 0; - int r = 0; - const struct device_node *np = - mhi_dev_ctxt->dev_info->plat_dev->dev.of_node; - - dma_dev_mem_start = mhi_dev_ctxt->dev_space.dma_dev_mem_start; - r = of_property_read_u64(np, "mhi-dev-address-win-size", - &dev_address_limit); - if (r) { - mhi_log(MHI_MSG_ERROR, - "Failed to get device addressing limit ret %d", - r); - return r; - } - /* Mask off the last 3 bits for address calculation */ - dev_address_limit &= ~0x7; - mhi_log(MHI_MSG_INFO, "Device Addressing limit 0x%llx\n", - dev_address_limit); - dma_seg_size = dev_address_limit / 2; - - /* - * The region of the allocated control segment is within the - * first half of the device's addressing limit - */ - if (dma_dev_mem_start < dma_seg_size) { - mhi_dev_ctxt->dev_space.start_win_addr = 0; - mhi_dev_ctxt->dev_space.end_win_addr = - dma_dev_mem_start + dma_seg_size + - (dma_seg_size - dma_dev_mem_start); - } else if (dma_dev_mem_start >= dma_seg_size && - dma_dev_mem_start <= (dma_max_addr - dma_seg_size)) { - /* - * The start of the control segment is located past - * halfway point of the device's addressing limit - * Place the control segment in the middle of the device's - * addressing range - */ - mhi_dev_ctxt->dev_space.start_win_addr = - dma_dev_mem_start - dma_seg_size; - mhi_dev_ctxt->dev_space.end_win_addr = - dma_dev_mem_start + dma_seg_size; - } else if (dma_dev_mem_start > (dma_max_addr - dma_seg_size)) { - /* - * The start of the control segment is located at the tail end - * of the host addressing space. Leave extra addressing space - * at window start - */ - mhi_dev_ctxt->dev_space.start_win_addr = dma_dev_mem_start; - mhi_dev_ctxt->dev_space.start_win_addr -= - dma_seg_size + (dma_seg_size - - (dma_max_addr - dma_dev_mem_start)); - mhi_dev_ctxt->dev_space.end_win_addr = dma_max_addr; - } - mhi_log(MHI_MSG_INFO, - "MHI start address at 0x%llx, Window Start 0x%llx Window End 0x%llx\n", - (u64)dma_dev_mem_start, - (u64)mhi_dev_ctxt->dev_space.start_win_addr, - (u64)mhi_dev_ctxt->dev_space.end_win_addr); - return 0; -} - int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt) { size_t mhi_mem_index = 0, ring_len; @@ -249,12 +147,12 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt) calculate_mhi_space(mhi_dev_ctxt); mhi_dev_ctxt->dev_space.dev_mem_start = - dma_alloc_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev, + dma_alloc_coherent(&mhi_dev_ctxt->plat_dev->dev, mhi_dev_ctxt->dev_space.dev_mem_len, &mhi_dev_ctxt->dev_space.dma_dev_mem_start, GFP_KERNEL); if (!mhi_dev_ctxt->dev_space.dev_mem_start) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to allocate memory of size %zd bytes\n", mhi_dev_ctxt->dev_space.dev_mem_len); return -ENOMEM; @@ -263,26 +161,20 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt) dma_dev_mem_start = mhi_dev_ctxt->dev_space.dma_dev_mem_start; memset(dev_mem_start, 0, mhi_dev_ctxt->dev_space.dev_mem_len); - r = calculate_mhi_addressing_window(mhi_dev_ctxt); - if (r) { - mhi_log(MHI_MSG_ERROR, - "Failed to calculate addressing window ret %d", r); - return r; - } + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Starting Seg address: virt 0x%p, dma 0x%llx\n", + dev_mem_start, (u64)dma_dev_mem_start); - mhi_log(MHI_MSG_INFO, "Starting Seg address: virt 0x%p, dma 0x%llx\n", - dev_mem_start, (u64)dma_dev_mem_start); - - mhi_log(MHI_MSG_INFO, "Initializing CCABAP at virt 0x%p, dma 0x%llx\n", - dev_mem_start + mhi_mem_index, - (u64)dma_dev_mem_start + mhi_mem_index); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Initializing CCABAP at dma 0x%llx\n", + (u64)dma_dev_mem_start + mhi_mem_index); mhi_dev_ctxt->dev_space.ring_ctxt.cc_list = dev_mem_start; mhi_dev_ctxt->dev_space.ring_ctxt.dma_cc_list = dma_dev_mem_start; mhi_mem_index += MHI_MAX_CHANNELS * sizeof(struct mhi_chan_ctxt); - mhi_log(MHI_MSG_INFO, "Initializing CRCBAP at virt 0x%p, dma 0x%llx\n", - dev_mem_start + mhi_mem_index, - (u64)dma_dev_mem_start + mhi_mem_index); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Initializing CRCBAP at dma 0x%llx\n", + (u64)dma_dev_mem_start + mhi_mem_index); mhi_dev_ctxt->dev_space.ring_ctxt.cmd_ctxt = dev_mem_start + mhi_mem_index; @@ -290,9 +182,9 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt) dma_dev_mem_start + mhi_mem_index; mhi_mem_index += NR_OF_CMD_RINGS * sizeof(struct mhi_chan_ctxt); - mhi_log(MHI_MSG_INFO, "Initializing ECABAP at virt 0x%p, dma 0x%llx\n", - dev_mem_start + mhi_mem_index, - (u64)dma_dev_mem_start + mhi_mem_index); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Initializing ECABAP at dma 0x%llx\n", + (u64)dma_dev_mem_start + mhi_mem_index); mhi_dev_ctxt->dev_space.ring_ctxt.ec_list = dev_mem_start + mhi_mem_index; mhi_dev_ctxt->dev_space.ring_ctxt.dma_ec_list = @@ -300,10 +192,9 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt) mhi_mem_index += mhi_dev_ctxt->mmio_info.nr_event_rings * sizeof(struct mhi_event_ctxt); - mhi_log(MHI_MSG_INFO, - "Initializing CMD context at virt 0x%p, dma 0x%llx\n", - dev_mem_start + mhi_mem_index, - (u64)dma_dev_mem_start + mhi_mem_index); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Initializing CMD context at dma 0x%llx\n", + (u64)dma_dev_mem_start + mhi_mem_index); /* TODO: Initialize both the local and device cmd context */ ring_len = (CMD_EL_PER_RING * sizeof(union mhi_cmd_pkt)); @@ -322,7 +213,7 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt) ring_len = sizeof(union mhi_event_pkt) * mhi_dev_ctxt->ev_ring_props[i].nr_desc; ring_addr = dma_alloc_coherent( - &mhi_dev_ctxt->dev_info->pcie_device->dev, + &mhi_dev_ctxt->plat_dev->dev, ring_len, &ring_dma_addr, GFP_KERNEL); if (!ring_addr) goto err_ev_alloc; @@ -330,9 +221,9 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt) ring_dma_addr, ring_len); init_local_ev_ctxt(&mhi_dev_ctxt->mhi_local_event_ctxt[i], ring_addr, ring_len); - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Initializing EV_%d TRE list at virt 0x%p dma 0x%llx\n", - i, ring_addr, (u64)ring_dma_addr); + i, ring_addr, (u64)ring_dma_addr); } return 0; @@ -344,12 +235,12 @@ err_ev_alloc: dev_ev_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[i]; ev_ctxt = &mhi_dev_ctxt->mhi_local_event_ctxt[i]; - dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev, + dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev, ev_ctxt->len, ev_ctxt->base, dev_ev_ctxt->mhi_event_ring_base_addr); } - dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev, + dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev, mhi_dev_ctxt->dev_space.dev_mem_len, mhi_dev_ctxt->dev_space.dev_mem_start, mhi_dev_ctxt->dev_space.dma_dev_mem_start); @@ -359,44 +250,28 @@ err_ev_alloc: static int mhi_init_events(struct mhi_device_ctxt *mhi_dev_ctxt) { - mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq = kmalloc( - sizeof(wait_queue_head_t), - GFP_KERNEL); - if (NULL == mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq) { - mhi_log(MHI_MSG_ERROR, "Failed to init event"); - return -ENOMEM; - } - mhi_dev_ctxt->mhi_ev_wq.state_change_event = - kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL); - if (NULL == mhi_dev_ctxt->mhi_ev_wq.state_change_event) { - mhi_log(MHI_MSG_ERROR, "Failed to init event"); - goto error_event_handle_alloc; - } /* Initialize the event which signals M0 */ mhi_dev_ctxt->mhi_ev_wq.m0_event = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL); if (NULL == mhi_dev_ctxt->mhi_ev_wq.m0_event) { - mhi_log(MHI_MSG_ERROR, "Failed to init event"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to init event"); goto error_state_change_event_handle; } /* Initialize the event which signals M0 */ mhi_dev_ctxt->mhi_ev_wq.m3_event = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL); if (NULL == mhi_dev_ctxt->mhi_ev_wq.m3_event) { - mhi_log(MHI_MSG_ERROR, "Failed to init event"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to init event"); goto error_m0_event; } /* Initialize the event which signals M0 */ mhi_dev_ctxt->mhi_ev_wq.bhi_event = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL); if (NULL == mhi_dev_ctxt->mhi_ev_wq.bhi_event) { - mhi_log(MHI_MSG_ERROR, "Failed to init event"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to init event"); goto error_bhi_event; } - /* Initialize the event which starts the event parsing thread */ - init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq); - /* Initialize the event which starts the state change thread */ - init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.state_change_event); + /* Initialize the event which triggers clients waiting to send */ init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.m0_event); /* Initialize the event which triggers D3hot */ @@ -409,9 +284,6 @@ error_bhi_event: error_m0_event: kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event); error_state_change_event_handle: - kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event); -error_event_handle_alloc: - kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq); return -ENOMEM; } @@ -448,102 +320,70 @@ static void mhi_init_wakelock(struct mhi_device_ctxt *mhi_dev_ctxt) wakeup_source_init(&mhi_dev_ctxt->w_lock, "mhi_wakeup_source"); } -static int mhi_spawn_threads(struct mhi_device_ctxt *mhi_dev_ctxt) -{ - mhi_dev_ctxt->event_thread_handle = kthread_run(parse_event_thread, - mhi_dev_ctxt, - "mhi_ev_thrd"); - if (IS_ERR(mhi_dev_ctxt->event_thread_handle)) - return PTR_ERR(mhi_dev_ctxt->event_thread_handle); - mhi_dev_ctxt->st_thread_handle = kthread_run(mhi_state_change_thread, - mhi_dev_ctxt, - "mhi_st_thrd"); - if (IS_ERR(mhi_dev_ctxt->event_thread_handle)) - return PTR_ERR(mhi_dev_ctxt->event_thread_handle); - return 0; -} - /** * @brief Main initialization function for a mhi struct device context * All threads, events mutexes, mhi specific data structures * are initialized here * - * @param dev_info [IN ] pcie struct device information structure to - which this mhi context belongs * @param mhi_struct device [IN/OUT] reference to a mhi context to be populated * * @return errno */ -int mhi_init_device_ctxt(struct mhi_pcie_dev_info *dev_info, - struct mhi_device_ctxt *mhi_dev_ctxt) +int mhi_init_device_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt) { int r = 0; - if (NULL == dev_info || NULL == mhi_dev_ctxt) - return -EINVAL; - - mhi_log(MHI_MSG_VERBOSE, "Entered\n"); - - mhi_dev_ctxt->dev_info = dev_info; - mhi_dev_ctxt->dev_props = &dev_info->core; + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Entered\n"); r = mhi_populate_event_cfg(mhi_dev_ctxt); if (r) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to get event ring properties ret %d\n", r); goto error_during_props; } r = mhi_init_sync(mhi_dev_ctxt); if (r) { - mhi_log(MHI_MSG_ERROR, "Failed to initialize mhi sync\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to initialize mhi sync\n"); goto error_during_sync; } r = create_local_ev_ctxt(mhi_dev_ctxt); if (r) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to initialize local event ctxt ret %d\n", r); goto error_during_local_ev_ctxt; } r = init_mhi_dev_mem(mhi_dev_ctxt); if (r) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to initialize device memory ret %d\n", r); goto error_during_dev_mem_init; } r = mhi_init_events(mhi_dev_ctxt); if (r) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to initialize mhi events ret %d\n", r); goto error_wq_init; } r = mhi_reset_all_thread_queues(mhi_dev_ctxt); if (r) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to initialize work queues ret %d\n", r); goto error_during_thread_init; } init_event_ctxt_array(mhi_dev_ctxt); mhi_dev_ctxt->mhi_state = MHI_STATE_RESET; - r = mhi_spawn_threads(mhi_dev_ctxt); - if (r) { - mhi_log(MHI_MSG_ERROR, "Failed to spawn threads ret %d\n", r); - goto error_during_thread_spawn; - } mhi_init_wakelock(mhi_dev_ctxt); return r; -error_during_thread_spawn: - kfree(mhi_dev_ctxt->state_change_work_item_list.q_lock); error_during_thread_init: - kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq); - kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event); kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event); kfree(mhi_dev_ctxt->mhi_ev_wq.m3_event); kfree(mhi_dev_ctxt->mhi_ev_wq.bhi_event); error_wq_init: - dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev, + dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev, mhi_dev_ctxt->dev_space.dev_mem_len, mhi_dev_ctxt->dev_space.dev_mem_start, mhi_dev_ctxt->dev_space.dma_dev_mem_start); @@ -623,7 +463,8 @@ int mhi_reset_all_thread_queues( ret_val = mhi_init_state_change_thread_work_queue( &mhi_dev_ctxt->state_change_work_item_list); if (ret_val) - mhi_log(MHI_MSG_ERROR, "Failed to reset STT work queue\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to reset STT work queue\n"); return ret_val; } diff --git a/drivers/platform/msm/mhi/mhi_isr.c b/drivers/platform/msm/mhi/mhi_isr.c index d7c604419593..9aa9aeb7e646 100644 --- a/drivers/platform/msm/mhi/mhi_isr.c +++ b/drivers/platform/msm/mhi/mhi_isr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,13 +29,14 @@ static int mhi_process_event_ring( struct mhi_ring *local_ev_ctxt = &mhi_dev_ctxt->mhi_local_event_ctxt[ev_index]; + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "enter ev_index:%u\n", ev_index); read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); if (unlikely(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE)) { - mhi_log(MHI_MSG_ERROR, "Invalid MHI PM State\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Invalid MHI PM State\n"); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); return -EIO; } - mhi_assert_device_wake(mhi_dev_ctxt, false); + mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); ev_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[ev_index]; @@ -77,10 +78,9 @@ static int mhi_process_event_ring( &cmd_pkt, ev_index); MHI_TRB_GET_INFO(CMD_TRB_CHID, cmd_pkt, chan); cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan]; - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "MHI CCE received ring 0x%x chan:%u\n", - ev_index, - chan); + ev_index, chan); spin_lock_irqsave(&cfg->event_lock, flags); cfg->cmd_pkt = *cmd_pkt; cfg->cmd_event_pkt = @@ -102,9 +102,8 @@ static int mhi_process_event_ring( __pm_stay_awake(&mhi_dev_ctxt->w_lock); chan = MHI_EV_READ_CHID(EV_CHID, &event_to_process); if (unlikely(!VALID_CHAN_NR(chan))) { - mhi_log(MHI_MSG_ERROR, - "Invalid chan:%d\n", - chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Invalid chan:%d\n", chan); break; } ring = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; @@ -115,6 +114,7 @@ static int mhi_process_event_ring( ev_index); spin_unlock_bh(&ring->ring_lock); __pm_relax(&mhi_dev_ctxt->w_lock); + event_quota--; break; } case MHI_PKT_TYPE_STATE_CHANGE_EVENT: @@ -122,13 +122,15 @@ static int mhi_process_event_ring( enum STATE_TRANSITION new_state; unsigned long flags; new_state = MHI_READ_STATE(&event_to_process); - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "MHI STE received ring 0x%x State:%s\n", - ev_index, - state_transition_str(new_state)); + ev_index, state_transition_str(new_state)); - /* If transitioning to M1 schedule worker thread */ - if (new_state == STATE_TRANSITION_M1) { + switch (new_state) { + case STATE_TRANSITION_M0: + process_m0_transition(mhi_dev_ctxt); + break; + case STATE_TRANSITION_M1: write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags); mhi_dev_ctxt->mhi_state = @@ -142,9 +144,15 @@ static int mhi_process_event_ring( write_unlock_irqrestore(&mhi_dev_ctxt-> pm_xfer_lock, flags); - } else { - mhi_init_state_transition(mhi_dev_ctxt, - new_state); + break; + case STATE_TRANSITION_M3: + process_m3_transition(mhi_dev_ctxt); + break; + default: + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Unsupported STE received ring 0x%x State:%s\n", + ev_index, + state_transition_str(new_state)); } break; } @@ -152,9 +160,8 @@ static int mhi_process_event_ring( { enum STATE_TRANSITION new_state; - mhi_log(MHI_MSG_INFO, - "MHI EEE received ring 0x%x\n", - ev_index); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "MHI EEE received ring 0x%x\n", ev_index); __pm_stay_awake(&mhi_dev_ctxt->w_lock); __pm_relax(&mhi_dev_ctxt->w_lock); switch (MHI_READ_EXEC_ENV(&event_to_process)) { @@ -168,21 +175,25 @@ static int mhi_process_event_ring( mhi_init_state_transition(mhi_dev_ctxt, new_state); break; + case MHI_EXEC_ENV_BHIE: + new_state = STATE_TRANSITION_BHIE; + mhi_init_state_transition(mhi_dev_ctxt, + new_state); } break; } case MHI_PKT_TYPE_STALE_EVENT: - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Stale Event received for chan:%u\n", MHI_EV_READ_CHID(EV_CHID, local_rp)); break; case MHI_PKT_TYPE_SYS_ERR_EVENT: - mhi_log(MHI_MSG_INFO, - "MHI System Error Detected. Triggering Reset\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "MHI System Error Detected. Triggering Reset\n"); BUG(); break; default: - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Unsupported packet type code 0x%x\n", MHI_TRB_READ_INFO(EV_TRB_TYPE, &event_to_process)); @@ -197,181 +208,139 @@ static int mhi_process_event_ring( ev_ctxt->mhi_event_read_ptr); spin_unlock_irqrestore(&local_ev_ctxt->ring_lock, flags); ret_val = 0; - --event_quota; } read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); - mhi_deassert_device_wake(mhi_dev_ctxt); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "exit ev_index:%u\n", ev_index); return ret_val; } -int parse_event_thread(void *ctxt) +void mhi_ev_task(unsigned long data) { - struct mhi_device_ctxt *mhi_dev_ctxt = ctxt; - u32 i = 0; - int ret_val = 0; - int ret_val_process_event = 0; - atomic_t *ev_pen_ptr = &mhi_dev_ctxt->counters.events_pending; + struct mhi_ring *mhi_ring = (struct mhi_ring *)data; + struct mhi_device_ctxt *mhi_dev_ctxt = + mhi_ring->mhi_dev_ctxt; + int ev_index = mhi_ring->index; - /* Go through all event rings */ - for (;;) { - ret_val = - wait_event_interruptible( - *mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq, - ((atomic_read( - &mhi_dev_ctxt->counters.events_pending) > 0) && - !mhi_dev_ctxt->flags.stop_threads) || - mhi_dev_ctxt->flags.kill_threads || - (mhi_dev_ctxt->flags.stop_threads && - !mhi_dev_ctxt->flags.ev_thread_stopped)); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Enter\n"); + /* Process event ring */ + mhi_process_event_ring(mhi_dev_ctxt, ev_index, U32_MAX); - switch (ret_val) { - case -ERESTARTSYS: - return 0; - default: - if (mhi_dev_ctxt->flags.kill_threads) { - mhi_log(MHI_MSG_INFO, - "Caught exit signal, quitting\n"); - return 0; - } - if (mhi_dev_ctxt->flags.stop_threads) { - mhi_dev_ctxt->flags.ev_thread_stopped = 1; - continue; - } - break; - } - mhi_dev_ctxt->flags.ev_thread_stopped = 0; - atomic_dec(&mhi_dev_ctxt->counters.events_pending); - for (i = 1; i < mhi_dev_ctxt->mmio_info.nr_event_rings; ++i) { - if (mhi_dev_ctxt->mhi_state == MHI_STATE_SYS_ERR) { - mhi_log(MHI_MSG_INFO, - "SYS_ERR detected, not processing events\n"); - atomic_set(&mhi_dev_ctxt-> - counters.events_pending, - 0); - break; - } - if (GET_EV_PROPS(EV_MANAGED, - mhi_dev_ctxt->ev_ring_props[i].flags)) { - ret_val_process_event = - mhi_process_event_ring(mhi_dev_ctxt, - i, - mhi_dev_ctxt-> - ev_ring_props[i].nr_desc); - if (ret_val_process_event == -EINPROGRESS) - atomic_inc(ev_pen_ptr); - } - } - } + enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, ev_index)); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Exit\n"); } -void mhi_ctrl_ev_task(unsigned long data) +void process_event_ring(struct work_struct *work) { + struct mhi_ring *mhi_ring = + container_of(work, struct mhi_ring, ev_worker); struct mhi_device_ctxt *mhi_dev_ctxt = - (struct mhi_device_ctxt *)data; - const unsigned CTRL_EV_RING = 0; - struct mhi_event_ring_cfg *ring_props = - &mhi_dev_ctxt->ev_ring_props[CTRL_EV_RING]; + mhi_ring->mhi_dev_ctxt; + int ev_index = mhi_ring->index; + + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Enter\n"); + /* Process event ring */ + mhi_process_event_ring(mhi_dev_ctxt, ev_index, U32_MAX); - mhi_log(MHI_MSG_VERBOSE, "Enter\n"); - /* Process control event ring */ - mhi_process_event_ring(mhi_dev_ctxt, - CTRL_EV_RING, - ring_props->nr_desc); - enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, CTRL_EV_RING)); - mhi_log(MHI_MSG_VERBOSE, "Exit\n"); + enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, ev_index)); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Exit\n"); } struct mhi_result *mhi_poll(struct mhi_client_handle *client_handle) { int ret_val; + struct mhi_client_config *client_config = client_handle->client_config; - client_handle->result.buf_addr = NULL; - client_handle->result.bytes_xferd = 0; - client_handle->result.transaction_status = 0; - ret_val = mhi_process_event_ring(client_handle->mhi_dev_ctxt, - client_handle->event_ring_index, + client_config->result.buf_addr = NULL; + client_config->result.bytes_xferd = 0; + client_config->result.transaction_status = 0; + ret_val = mhi_process_event_ring(client_config->mhi_dev_ctxt, + client_config->event_ring_index, 1); if (ret_val) - mhi_log(MHI_MSG_INFO, "NAPI failed to process event ring\n"); - return &(client_handle->result); + mhi_log(client_config->mhi_dev_ctxt, MHI_MSG_INFO, + "NAPI failed to process event ring\n"); + return &(client_config->result); } void mhi_mask_irq(struct mhi_client_handle *client_handle) { + struct mhi_client_config *client_config = client_handle->client_config; struct mhi_device_ctxt *mhi_dev_ctxt = - client_handle->mhi_dev_ctxt; + client_config->mhi_dev_ctxt; struct mhi_ring *ev_ring = &mhi_dev_ctxt-> - mhi_local_event_ctxt[client_handle->event_ring_index]; + mhi_local_event_ctxt[client_config->event_ring_index]; - disable_irq_nosync(MSI_TO_IRQ(mhi_dev_ctxt, client_handle->msi_vec)); + disable_irq_nosync(MSI_TO_IRQ(mhi_dev_ctxt, client_config->msi_vec)); ev_ring->msi_disable_cntr++; } void mhi_unmask_irq(struct mhi_client_handle *client_handle) { + struct mhi_client_config *client_config = client_handle->client_config; struct mhi_device_ctxt *mhi_dev_ctxt = - client_handle->mhi_dev_ctxt; + client_config->mhi_dev_ctxt; struct mhi_ring *ev_ring = &mhi_dev_ctxt-> - mhi_local_event_ctxt[client_handle->event_ring_index]; + mhi_local_event_ctxt[client_config->event_ring_index]; ev_ring->msi_enable_cntr++; - enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, client_handle->msi_vec)); + enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, client_config->msi_vec)); } irqreturn_t mhi_msi_handlr(int irq_number, void *dev_id) { - struct device *mhi_device = dev_id; - struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->platform_data; + struct mhi_device_ctxt *mhi_dev_ctxt = dev_id; int msi = IRQ_TO_MSI(mhi_dev_ctxt, irq_number); + struct mhi_ring *mhi_ring = &mhi_dev_ctxt->mhi_local_event_ctxt[msi]; + struct mhi_event_ring_cfg *ring_props = + &mhi_dev_ctxt->ev_ring_props[msi]; - if (!mhi_dev_ctxt) { - mhi_log(MHI_MSG_ERROR, "Failed to get a proper context\n"); - return IRQ_HANDLED; - } mhi_dev_ctxt->counters.msi_counter[ IRQ_TO_MSI(mhi_dev_ctxt, irq_number)]++; - mhi_log(MHI_MSG_VERBOSE, "Got MSI 0x%x\n", msi); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Got MSI 0x%x\n", msi); trace_mhi_msi(IRQ_TO_MSI(mhi_dev_ctxt, irq_number)); - - if (msi) { - atomic_inc(&mhi_dev_ctxt->counters.events_pending); - wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq); - } else { - disable_irq_nosync(irq_number); - tasklet_schedule(&mhi_dev_ctxt->ev_task); - } + disable_irq_nosync(irq_number); + if (ring_props->priority <= MHI_EV_PRIORITY_TASKLET) + tasklet_schedule(&mhi_ring->ev_task); + else + schedule_work(&mhi_ring->ev_worker); return IRQ_HANDLED; } irqreturn_t mhi_msi_ipa_handlr(int irq_number, void *dev_id) { - struct device *mhi_device = dev_id; - u32 client_index; - struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->platform_data; + struct mhi_device_ctxt *mhi_dev_ctxt = dev_id; + struct mhi_event_ring_cfg *ev_ring_props; struct mhi_client_handle *client_handle; + struct mhi_client_config *client_config; struct mhi_client_info_t *client_info; struct mhi_cb_info cb_info; int msi_num = (IRQ_TO_MSI(mhi_dev_ctxt, irq_number)); mhi_dev_ctxt->counters.msi_counter[msi_num]++; - mhi_log(MHI_MSG_VERBOSE, "Got MSI 0x%x\n", msi_num); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Got MSI 0x%x\n", msi_num); trace_mhi_msi(msi_num); - client_index = MHI_MAX_CHANNELS - - (mhi_dev_ctxt->mmio_info.nr_event_rings - msi_num); - client_handle = mhi_dev_ctxt->client_handle_list[client_index]; - client_info = &client_handle->client_info; - if (likely(client_handle)) { - client_handle->result.user_data = - client_handle->user_data; - if (likely(client_info->mhi_client_cb)) { - cb_info.result = &client_handle->result; - cb_info.cb_reason = MHI_CB_XFER; - cb_info.chan = client_handle->chan_info.chan_nr; - cb_info.result->transaction_status = 0; - client_info->mhi_client_cb(&cb_info); - } + + /* Obtain client config from MSI */ + ev_ring_props = &mhi_dev_ctxt->ev_ring_props[msi_num]; + client_handle = mhi_dev_ctxt->client_handle_list[ev_ring_props->chan]; + if (unlikely(!client_handle)) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Recv MSI for unreg chan:%u\n", ev_ring_props->chan); + return IRQ_HANDLED; } + + client_config = client_handle->client_config; + client_info = &client_config->client_info; + client_config->result.user_data = + client_config->user_data; + cb_info.result = &client_config->result; + cb_info.cb_reason = MHI_CB_XFER; + cb_info.chan = client_config->chan_info.chan_nr; + cb_info.result->transaction_status = 0; + client_info->mhi_client_cb(&cb_info); + return IRQ_HANDLED; } diff --git a/drivers/platform/msm/mhi/mhi_macros.h b/drivers/platform/msm/mhi/mhi_macros.h index 133c0eeb034e..fc0e6f4bc27d 100644 --- a/drivers/platform/msm/mhi/mhi_macros.h +++ b/drivers/platform/msm/mhi/mhi_macros.h @@ -39,7 +39,6 @@ #define MHI_WORK_Q_MAX_SIZE 128 #define MAX_XFER_WORK_ITEMS 100 -#define MHI_MAX_SUPPORTED_DEVICES 1 #define MHI_PCIE_VENDOR_ID 0x17CB #define MHI_PCIE_DEVICE_ID_9x35 0x0300 @@ -70,9 +69,9 @@ ((enum MHI_CLIENT_CHANNEL)(_CHAN_NR) < MHI_CLIENT_RESERVED_1_LOWER)) #define IRQ_TO_MSI(_MHI_DEV_CTXT, _IRQ_NR) \ - ((_IRQ_NR) - (_MHI_DEV_CTXT)->dev_info->core.irq_base) + ((_IRQ_NR) - (_MHI_DEV_CTXT)->core.irq_base) #define MSI_TO_IRQ(_MHI_DEV_CTXT, _MSI_NR) \ - ((_MHI_DEV_CTXT)->dev_info->core.irq_base + (_MSI_NR)) + ((_MHI_DEV_CTXT)->core.irq_base + (_MSI_NR)) #define VALID_CHAN_NR(_CHAN_NR) (IS_HARDWARE_CHANNEL(_CHAN_NR) || \ IS_SOFTWARE_CHANNEL(_CHAN_NR)) @@ -84,8 +83,8 @@ #define MHI_HW_INTMOD_VAL_MS 2 /* Timeout Values */ -#define MHI_READY_STATUS_TIMEOUT_MS 50 -#define MHI_THREAD_SLEEP_TIMEOUT_MS 20 +#define MHI_READY_STATUS_TIMEOUT_MS 500 +#define MHI_THREAD_SLEEP_TIMEOUT_MS 100 #define MHI_RESUME_WAKE_RETRIES 20 #define IS_HW_EV_RING(_mhi_dev_ctxt, _EV_INDEX) (_EV_INDEX >= \ diff --git a/drivers/platform/msm/mhi/mhi_main.c b/drivers/platform/msm/mhi/mhi_main.c index 430dc918af7e..644004672cd2 100644 --- a/drivers/platform/msm/mhi/mhi_main.c +++ b/drivers/platform/msm/mhi/mhi_main.c @@ -27,13 +27,23 @@ #include "mhi.h" #include "mhi_hwio.h" #include "mhi_macros.h" +#include "mhi_bhi.h" #include "mhi_trace.h" static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, union mhi_cmd_pkt *cmd_pkt); - -static int enable_bb_ctxt(struct mhi_ring *bb_ctxt, int nr_el) +static void disable_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, + struct mhi_ring *bb_ctxt); + +static int enable_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, + struct mhi_ring *bb_ctxt, + int nr_el, + int chan, + size_t max_payload) { + int i; + struct mhi_buf_info *mhi_buf_info; + bb_ctxt->el_size = sizeof(struct mhi_buf_info); bb_ctxt->len = bb_ctxt->el_size * nr_el; bb_ctxt->base = kzalloc(bb_ctxt->len, GFP_KERNEL); @@ -42,7 +52,46 @@ static int enable_bb_ctxt(struct mhi_ring *bb_ctxt, int nr_el) bb_ctxt->ack_rp = bb_ctxt->base; if (!bb_ctxt->base) return -ENOMEM; + + if (mhi_dev_ctxt->flags.bb_required) { + char pool_name[32]; + + snprintf(pool_name, sizeof(pool_name), "mhi%d_%d", + mhi_dev_ctxt->plat_dev->id, chan); + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Creating pool %s for chan:%d payload: 0x%lx\n", + pool_name, chan, max_payload); + + bb_ctxt->dma_pool = dma_pool_create(pool_name, + &mhi_dev_ctxt->plat_dev->dev, max_payload, 0, 0); + if (unlikely(!bb_ctxt->dma_pool)) + goto dma_pool_error; + + mhi_buf_info = (struct mhi_buf_info *)bb_ctxt->base; + for (i = 0; i < nr_el; i++, mhi_buf_info++) { + mhi_buf_info->pre_alloc_v_addr = + dma_pool_alloc(bb_ctxt->dma_pool, GFP_KERNEL, + &mhi_buf_info->pre_alloc_p_addr); + if (unlikely(!mhi_buf_info->pre_alloc_v_addr)) + goto dma_alloc_error; + mhi_buf_info->pre_alloc_len = max_payload; + } + } + return 0; + +dma_alloc_error: + for (--i, --mhi_buf_info; i >= 0; i--, mhi_buf_info--) + dma_pool_free(bb_ctxt->dma_pool, mhi_buf_info->pre_alloc_v_addr, + mhi_buf_info->pre_alloc_p_addr); + + dma_pool_destroy(bb_ctxt->dma_pool); + bb_ctxt->dma_pool = NULL; +dma_pool_error: + kfree(bb_ctxt->base); + bb_ctxt->base = NULL; + return -ENOMEM; } static void mhi_write_db(struct mhi_device_ctxt *mhi_dev_ctxt, @@ -66,7 +115,7 @@ static void mhi_update_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, mhi_trb_write_ptr = val; } else if (mhi_dev_ctxt->mmio_info.event_db_addr == io_addr) { if (chan < mhi_dev_ctxt->mmio_info.nr_event_rings) { - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "EV ctxt: %ld val 0x%llx WP 0x%llx RP: 0x%llx", chan, val, mhi_dev_ctxt->dev_space.ring_ctxt. @@ -76,7 +125,7 @@ static void mhi_update_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[chan]. mhi_event_write_ptr = val; } else { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Bad EV ring index: %lx\n", chan); } } @@ -84,65 +133,48 @@ static void mhi_update_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, wmb(); } -int mhi_init_pcie_device(struct mhi_pcie_dev_info *mhi_pcie_dev) +int mhi_init_pcie_device(struct mhi_device_ctxt *mhi_dev_ctxt) { int ret_val = 0; long int sleep_time = 100; - struct pci_dev *pcie_device = - (struct pci_dev *)mhi_pcie_dev->pcie_device; + struct pci_dev *pcie_device = mhi_dev_ctxt->pcie_device; + struct pcie_core_info *core = &mhi_dev_ctxt->core; do { - ret_val = pci_enable_device(mhi_pcie_dev->pcie_device); + ret_val = pci_enable_device(pcie_device); if (0 != ret_val) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to enable pcie struct device r: %d\n", ret_val); - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Sleeping for ~ %li uS, and retrying.\n", sleep_time); msleep(sleep_time); } } while (ret_val != 0); - mhi_log(MHI_MSG_INFO, "Successfully enabled pcie device.\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Successfully enabled pcie device.\n"); - mhi_pcie_dev->core.bar0_base = - ioremap_nocache(pci_resource_start(pcie_device, 0), - pci_resource_len(pcie_device, 0)); - if (!mhi_pcie_dev->core.bar0_base) + core->bar0_base = ioremap_nocache(pci_resource_start(pcie_device, 0), + pci_resource_len(pcie_device, 0)); + if (!core->bar0_base) goto mhi_device_list_error; - mhi_pcie_dev->core.bar0_end = mhi_pcie_dev->core.bar0_base + - pci_resource_len(pcie_device, 0); - mhi_pcie_dev->core.bar2_base = - ioremap_nocache(pci_resource_start(pcie_device, 2), - pci_resource_len(pcie_device, 2)); - if (!mhi_pcie_dev->core.bar2_base) - goto io_map_err; - - mhi_pcie_dev->core.bar2_end = mhi_pcie_dev->core.bar2_base + - pci_resource_len(pcie_device, 2); - - if (!mhi_pcie_dev->core.bar0_base) { - mhi_log(MHI_MSG_ERROR, - "Failed to register for pcie resources\n"); - goto mhi_pcie_read_ep_config_err; - } + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Device BAR0 address is at 0x%p\n", core->bar0_base); - mhi_log(MHI_MSG_INFO, "Device BAR0 address is at 0x%p\n", - mhi_pcie_dev->core.bar0_base); ret_val = pci_request_region(pcie_device, 0, "mhi"); if (ret_val) - mhi_log(MHI_MSG_ERROR, "Could not request BAR0 region\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Could not request BAR0 region\n"); - mhi_pcie_dev->core.manufact_id = pcie_device->vendor; - mhi_pcie_dev->core.dev_id = pcie_device->device; + core->manufact_id = pcie_device->vendor; + core->dev_id = pcie_device->device; return 0; -io_map_err: - iounmap((void *)mhi_pcie_dev->core.bar0_base); + mhi_device_list_error: pci_disable_device(pcie_device); -mhi_pcie_read_ep_config_err: return -EIO; } @@ -156,7 +188,7 @@ static void mhi_move_interrupts(struct mhi_device_ctxt *mhi_dev_ctxt, u32 cpu) GET_EV_PROPS(EV_TYPE, mhi_dev_ctxt->ev_ring_props[i].flags)) { irq_to_affin = mhi_dev_ctxt->ev_ring_props[i].msi_vec; - irq_to_affin += mhi_dev_ctxt->dev_props->irq_base; + irq_to_affin += mhi_dev_ctxt->core.irq_base; irq_set_affinity(irq_to_affin, get_cpu_mask(cpu)); } } @@ -198,8 +230,9 @@ int get_chan_props(struct mhi_device_ctxt *mhi_dev_ctxt, int chan, scnprintf(dt_prop, MAX_BUF_SIZE, "%s%d", "mhi-chan-cfg-", chan); r = of_property_read_u32_array( - mhi_dev_ctxt->dev_info->plat_dev->dev.of_node, - dt_prop, (u32 *)chan_info, + mhi_dev_ctxt->plat_dev->dev.of_node, + dt_prop, + (u32 *)chan_info, sizeof(struct mhi_chan_info) / sizeof(u32)); return r; } @@ -211,9 +244,10 @@ int mhi_release_chan_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, if (cc_list == NULL || ring == NULL) return -EINVAL; - dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev, - ring->len, ring->base, - cc_list->mhi_trb_ring_base_addr); + dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev, + ring->len, + ring->base, + cc_list->mhi_trb_ring_base_addr); mhi_init_chan_ctxt(cc_list, 0, 0, 0, 0, 0, ring, MHI_CHAN_STATE_DISABLED, false, @@ -221,38 +255,37 @@ int mhi_release_chan_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, return 0; } -void free_tre_ring(struct mhi_client_handle *client_handle) +void free_tre_ring(struct mhi_device_ctxt *mhi_dev_ctxt, int chan) { struct mhi_chan_ctxt *chan_ctxt; - struct mhi_device_ctxt *mhi_dev_ctxt = client_handle->mhi_dev_ctxt; - int chan = client_handle->chan_info.chan_nr; int r; chan_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan]; r = mhi_release_chan_ctxt(mhi_dev_ctxt, chan_ctxt, &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]); if (r) - mhi_log(MHI_MSG_ERROR, - "Failed to release chan %d ret %d\n", chan, r); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to release chan %d ret %d\n", chan, r); } -static int populate_tre_ring(struct mhi_client_handle *client_handle) +static int populate_tre_ring(struct mhi_client_config *client_config) { dma_addr_t ring_dma_addr; void *ring_local_addr; struct mhi_chan_ctxt *chan_ctxt; - struct mhi_device_ctxt *mhi_dev_ctxt = client_handle->mhi_dev_ctxt; - u32 chan = client_handle->chan_info.chan_nr; - u32 nr_desc = client_handle->chan_info.max_desc; + struct mhi_device_ctxt *mhi_dev_ctxt = client_config->mhi_dev_ctxt; + u32 chan = client_config->chan_info.chan_nr; + u32 nr_desc = client_config->chan_info.max_desc; - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered chan %d requested desc %d\n", chan, nr_desc); chan_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan]; - ring_local_addr = dma_alloc_coherent( - &mhi_dev_ctxt->dev_info->pcie_device->dev, - nr_desc * sizeof(union mhi_xfer_pkt), - &ring_dma_addr, GFP_KERNEL); + ring_local_addr = + dma_alloc_coherent(&mhi_dev_ctxt->plat_dev->dev, + nr_desc * sizeof(union mhi_xfer_pkt), + &ring_dma_addr, + GFP_KERNEL); if (ring_local_addr == NULL) return -ENOMEM; @@ -261,15 +294,15 @@ static int populate_tre_ring(struct mhi_client_handle *client_handle) (uintptr_t)ring_local_addr, nr_desc, GET_CHAN_PROPS(CHAN_DIR, - client_handle->chan_info.flags), - client_handle->chan_info.ev_ring, + client_config->chan_info.flags), + client_config->chan_info.ev_ring, &mhi_dev_ctxt->mhi_local_chan_ctxt[chan], MHI_CHAN_STATE_ENABLED, GET_CHAN_PROPS(PRESERVE_DB_STATE, - client_handle->chan_info.flags), + client_config->chan_info.flags), GET_CHAN_PROPS(BRSTMODE, - client_handle->chan_info.flags)); - mhi_log(MHI_MSG_INFO, "Exited\n"); + client_config->chan_info.flags)); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n"); return 0; } @@ -283,85 +316,64 @@ int mhi_open_channel(struct mhi_client_handle *client_handle) struct mhi_cmd_complete_event_pkt cmd_event_pkt; union mhi_cmd_pkt cmd_pkt; enum MHI_EVENT_CCS ev_code; + struct mhi_client_config *client_config = client_handle->client_config; - if (!client_handle || client_handle->magic != MHI_HANDLE_MAGIC) + if (client_config->magic != MHI_HANDLE_MAGIC) return -EINVAL; - mhi_dev_ctxt = client_handle->mhi_dev_ctxt; - ret_val = get_chan_props(mhi_dev_ctxt, - client_handle->chan_info.chan_nr, - &client_handle->chan_info); - if (ret_val) - return ret_val; + mhi_dev_ctxt = client_config->mhi_dev_ctxt; - chan = client_handle->chan_info.chan_nr; + chan = client_config->chan_info.chan_nr; cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan]; chan_ring = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; mutex_lock(&cfg->chan_lock); - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered: Client opening chan 0x%x\n", chan); if (mhi_dev_ctxt->dev_exec_env < GET_CHAN_PROPS(CHAN_BRINGUP_STAGE, - client_handle->chan_info.flags)) { - mhi_log(MHI_MSG_INFO, + client_config->chan_info.flags)) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Chan %d, MHI exec_env %d, not ready!\n", - chan, - mhi_dev_ctxt->dev_exec_env); + chan, mhi_dev_ctxt->dev_exec_env); mutex_unlock(&cfg->chan_lock); return -ENOTCONN; } - ret_val = populate_tre_ring(client_handle); + ret_val = populate_tre_ring(client_config); if (ret_val) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to initialize tre ring chan %d ret %d\n", - chan, - ret_val); - mutex_unlock(&cfg->chan_lock); - return ret_val; - } - client_handle->event_ring_index = - mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan]. - mhi_event_ring_index; - ret_val = enable_bb_ctxt(&mhi_dev_ctxt->chan_bb_list[chan], - client_handle->chan_info.max_desc); - if (ret_val) { - mhi_log(MHI_MSG_ERROR, - "Failed to initialize bb ctxt chan %d ret %d\n", - chan, - ret_val); - mutex_unlock(&cfg->chan_lock); - return ret_val; + chan, ret_val); + goto error_tre_ring; } + client_config->event_ring_index = + mhi_dev_ctxt->dev_space.ring_ctxt. + cc_list[chan].mhi_event_ring_index; - client_handle->msi_vec = + client_config->msi_vec = mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[ - client_handle->event_ring_index].mhi_msi_vector; - client_handle->intmod_t = + client_config->event_ring_index].mhi_msi_vector; + client_config->intmod_t = mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[ - client_handle->event_ring_index].mhi_intmodt; + client_config->event_ring_index].mhi_intmodt; init_completion(&cfg->cmd_complete); read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); if (unlikely(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE)) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "MHI State is disabled\n"); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); - mutex_unlock(&cfg->chan_lock); - return -EIO; + ret_val = -EIO; + goto error_pm_state; } - WARN_ON(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE); - mhi_assert_device_wake(mhi_dev_ctxt, false); + mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); - pm_runtime_get(&mhi_dev_ctxt->dev_info->pcie_device->dev); + mhi_dev_ctxt->runtime_get(mhi_dev_ctxt); - spin_lock_irq(&chan_ring->ring_lock); - chan_ring->ch_state = MHI_CHAN_STATE_ENABLED; - spin_unlock_irq(&chan_ring->ring_lock); - ret_val = mhi_send_cmd(client_handle->mhi_dev_ctxt, + ret_val = mhi_send_cmd(client_config->mhi_dev_ctxt, MHI_COMMAND_START_CHAN, chan); if (ret_val) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to send start cmd for chan %d ret %d\n", chan, ret_val); goto error_completion; @@ -369,9 +381,9 @@ int mhi_open_channel(struct mhi_client_handle *client_handle) ret_val = wait_for_completion_timeout(&cfg->cmd_complete, msecs_to_jiffies(MHI_MAX_CMD_TIMEOUT)); if (!ret_val) { - mhi_log(MHI_MSG_ERROR, - "Failed to receive cmd completion for %d\n", - chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to receive cmd completion for %d\n", chan); + ret_val = -EIO; goto error_completion; } else { ret_val = 0; @@ -385,76 +397,159 @@ int mhi_open_channel(struct mhi_client_handle *client_handle) ev_code = MHI_EV_READ_CODE(EV_TRB_CODE, ((union mhi_event_pkt *)&cmd_event_pkt)); if (ev_code != MHI_EVENT_CC_SUCCESS) { - mhi_log(MHI_MSG_ERROR, - "Error to receive event completion ev_code:0x%x\n", - ev_code); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Error to receive event comp. ev_code:0x%x\n", ev_code); ret_val = -EIO; goto error_completion; } - client_handle->chan_status = 1; - -error_completion: + spin_lock_irq(&chan_ring->ring_lock); + chan_ring->ch_state = MHI_CHAN_STATE_ENABLED; + spin_unlock_irq(&chan_ring->ring_lock); + client_config->chan_status = 1; read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); - mhi_deassert_device_wake(mhi_dev_ctxt); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); - pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->pcie_device->dev); + mhi_dev_ctxt->runtime_put(mhi_dev_ctxt); mutex_unlock(&cfg->chan_lock); - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "chan:%d opened successfully\n", chan); + return 0; + +error_completion: + read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); + read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); + mhi_dev_ctxt->runtime_put(mhi_dev_ctxt); +error_pm_state: + free_tre_ring(mhi_dev_ctxt, chan); +error_tre_ring: + mutex_unlock(&cfg->chan_lock); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited chan 0x%x ret:%d\n", chan, ret_val); return ret_val; } EXPORT_SYMBOL(mhi_open_channel); +bool mhi_is_device_ready(const struct device * const dev, + const char *node_name) +{ + struct mhi_device_ctxt *itr; + const struct device_node *of_node; + bool match_found = false; + + if (!mhi_device_drv) + return false; + if (dev->of_node == NULL) + return false; + + of_node = of_parse_phandle(dev->of_node, node_name, 0); + if (!of_node) + return false; + + mutex_lock(&mhi_device_drv->lock); + list_for_each_entry(itr, &mhi_device_drv->head, node) { + struct platform_device *pdev = itr->plat_dev; + + if (pdev->dev.of_node == of_node) { + match_found = true; + break; + } + } + mutex_unlock(&mhi_device_drv->lock); + return match_found; +} +EXPORT_SYMBOL(mhi_is_device_ready); + int mhi_register_channel(struct mhi_client_handle **client_handle, - enum MHI_CLIENT_CHANNEL chan, s32 device_index, - struct mhi_client_info_t *client_info, void *user_data) + struct mhi_client_info_t *client_info) { - struct mhi_device_ctxt *mhi_dev_ctxt = NULL; + struct mhi_device_ctxt *mhi_dev_ctxt = NULL, *itr; + const struct device_node *of_node; + struct mhi_client_config *client_config; + const char *node_name; + enum MHI_CLIENT_CHANNEL chan; + int ret; - if (!VALID_CHAN_NR(chan)) + if (!client_info || client_info->dev->of_node == NULL) return -EINVAL; - if (NULL == client_handle || device_index < 0) + node_name = client_info->node_name; + chan = client_info->chan; + of_node = of_parse_phandle(client_info->dev->of_node, node_name, 0); + if (!of_node || !mhi_device_drv || chan >= MHI_MAX_CHANNELS) return -EINVAL; - mhi_dev_ctxt = &(mhi_devices.device_list[device_index].mhi_ctxt); + /* Traverse thru the list */ + mutex_lock(&mhi_device_drv->lock); + list_for_each_entry(itr, &mhi_device_drv->head, node) { + struct platform_device *pdev = itr->plat_dev; + + if (pdev->dev.of_node == of_node) { + mhi_dev_ctxt = itr; + break; + } + } + mutex_unlock(&mhi_device_drv->lock); - if (NULL != mhi_dev_ctxt->client_handle_list[chan]) - return -EISCONN; + if (!mhi_dev_ctxt) + return -EINVAL; - mhi_log(MHI_MSG_INFO, - "Opened channel 0x%x for client\n", chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Registering channel 0x%x for client\n", chan); *client_handle = kzalloc(sizeof(struct mhi_client_handle), GFP_KERNEL); if (NULL == *client_handle) return -ENOMEM; + (*client_handle)->client_config = + kzalloc(sizeof(*(*client_handle)->client_config), GFP_KERNEL); + if ((*client_handle)->client_config == NULL) { + kfree(*client_handle); + *client_handle = NULL; + return -ENOMEM; + } mhi_dev_ctxt->client_handle_list[chan] = *client_handle; - (*client_handle)->mhi_dev_ctxt = mhi_dev_ctxt; - (*client_handle)->user_data = user_data; - (*client_handle)->magic = MHI_HANDLE_MAGIC; - (*client_handle)->chan_info.chan_nr = chan; + (*client_handle)->dev_id = mhi_dev_ctxt->core.dev_id; + (*client_handle)->domain = mhi_dev_ctxt->core.domain; + (*client_handle)->bus = mhi_dev_ctxt->core.bus; + (*client_handle)->slot = mhi_dev_ctxt->core.slot; + client_config = (*client_handle)->client_config; + client_config->mhi_dev_ctxt = mhi_dev_ctxt; + client_config->user_data = client_info->user_data; + client_config->magic = MHI_HANDLE_MAGIC; + client_config->chan_info.chan_nr = chan; if (NULL != client_info) - (*client_handle)->client_info = *client_info; + client_config->client_info = *client_info; if (MHI_CLIENT_IP_HW_0_OUT == chan) - (*client_handle)->intmod_t = 10; + client_config->intmod_t = 10; if (MHI_CLIENT_IP_HW_0_IN == chan) - (*client_handle)->intmod_t = 10; + client_config->intmod_t = 10; + + get_chan_props(mhi_dev_ctxt, chan, &client_config->chan_info); + ret = enable_bb_ctxt(mhi_dev_ctxt, &mhi_dev_ctxt->chan_bb_list[chan], + client_config->chan_info.max_desc, chan, + client_config->client_info.max_payload); + if (ret) { + kfree(mhi_dev_ctxt->client_handle_list[chan]->client_config); + kfree(mhi_dev_ctxt->client_handle_list[chan]); + mhi_dev_ctxt->client_handle_list[chan] = NULL; + return -ENOMEM; + } - if (mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_AMSS) { - mhi_log(MHI_MSG_INFO, - "Exec env is AMSS notifing client now chan: 0x%x\n", - chan); + if (mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_AMSS && + mhi_dev_ctxt->flags.mhi_initialized) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Exec env is AMSS notify client now chan:%u\n", chan); mhi_notify_client(*client_handle, MHI_CB_MHI_ENABLED); } - mhi_log(MHI_MSG_VERBOSE, - "Successfuly registered chan 0x%x\n", chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, + "Successfuly registered chan:%u\n", chan); return 0; } EXPORT_SYMBOL(mhi_register_channel); @@ -469,46 +564,54 @@ void mhi_close_channel(struct mhi_client_handle *client_handle) union mhi_cmd_pkt cmd_pkt; struct mhi_ring *chan_ring; enum MHI_EVENT_CCS ev_code; + struct mhi_client_config *client_config = + client_handle->client_config; - if (!client_handle || - client_handle->magic != MHI_HANDLE_MAGIC || - !client_handle->chan_status) + if (client_config->magic != MHI_HANDLE_MAGIC || + !client_config->chan_status) return; - mhi_dev_ctxt = client_handle->mhi_dev_ctxt; - chan = client_handle->chan_info.chan_nr; + mhi_dev_ctxt = client_config->mhi_dev_ctxt; + chan = client_config->chan_info.chan_nr; cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan]; - mhi_log(MHI_MSG_INFO, "Client attempting to close chan 0x%x\n", chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Client attempting to close chan 0x%x\n", chan); + chan_ring = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; mutex_lock(&cfg->chan_lock); /* No more processing events for this channel */ spin_lock_irq(&chan_ring->ring_lock); + if (chan_ring->ch_state != MHI_CHAN_STATE_ENABLED) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Chan %d is not enabled, cur state:0x%x\n", + chan, chan_ring->ch_state); + spin_unlock_irq(&chan_ring->ring_lock); + mutex_unlock(&cfg->chan_lock); + return; + } chan_ring->ch_state = MHI_CHAN_STATE_DISABLED; spin_unlock_irq(&chan_ring->ring_lock); init_completion(&cfg->cmd_complete); read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); WARN_ON(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE); - mhi_assert_device_wake(mhi_dev_ctxt, false); + mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); - pm_runtime_get(&mhi_dev_ctxt->dev_info->pcie_device->dev); - ret_val = mhi_send_cmd(client_handle->mhi_dev_ctxt, - MHI_COMMAND_RESET_CHAN, - chan); + mhi_dev_ctxt->runtime_get(mhi_dev_ctxt); + ret_val = mhi_send_cmd(mhi_dev_ctxt, + MHI_COMMAND_RESET_CHAN, chan); if (ret_val) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to send reset cmd for chan %d ret %d\n", - chan, - ret_val); + chan, ret_val); goto error_completion; } ret_val = wait_for_completion_timeout(&cfg->cmd_complete, msecs_to_jiffies(MHI_MAX_CMD_TIMEOUT)); if (!ret_val) { - mhi_log(MHI_MSG_ERROR, - "Failed to receive cmd completion for %d\n", - chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to receive cmd completion for %d\n", chan); goto error_completion; } @@ -519,28 +622,36 @@ void mhi_close_channel(struct mhi_client_handle *client_handle) ev_code = MHI_EV_READ_CODE(EV_TRB_CODE, ((union mhi_event_pkt *)&cmd_event_pkt)); if (ev_code != MHI_EVENT_CC_SUCCESS) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Error to receive event completion ev_cod:0x%x\n", ev_code); - goto error_completion; } +error_completion: ret_val = reset_chan_cmd(mhi_dev_ctxt, &cmd_pkt); if (ret_val) - mhi_log(MHI_MSG_ERROR, - "Error resetting cmd ret:%d\n", - ret_val); - -error_completion: + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Error resetting cmd ret:%d\n", ret_val); read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); - mhi_deassert_device_wake(mhi_dev_ctxt); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); - pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->pcie_device->dev); - mhi_log(MHI_MSG_INFO, "Freeing ring for chan 0x%x\n", chan); - free_tre_ring(client_handle); - mhi_log(MHI_MSG_INFO, "Chan 0x%x confirmed closed.\n", chan); - client_handle->chan_status = 0; + mhi_dev_ctxt->runtime_put(mhi_dev_ctxt); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "resetting bb_ring for chan 0x%x\n", chan); + mhi_dev_ctxt->chan_bb_list[chan].rp = + mhi_dev_ctxt->chan_bb_list[chan].base; + mhi_dev_ctxt->chan_bb_list[chan].wp = + mhi_dev_ctxt->chan_bb_list[chan].base; + mhi_dev_ctxt->chan_bb_list[chan].ack_rp = + mhi_dev_ctxt->chan_bb_list[chan].base; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Freeing ring for chan 0x%x\n", chan); + free_tre_ring(mhi_dev_ctxt, chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Chan 0x%x confirmed closed.\n", chan); + client_config->chan_status = 0; mutex_unlock(&cfg->chan_lock); } EXPORT_SYMBOL(mhi_close_channel); @@ -596,6 +707,7 @@ static inline int mhi_queue_tre(struct mhi_device_ctxt } return 0; } + static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt, int chan, void *buf, size_t buf_len, enum dma_data_direction dir, struct mhi_buf_info **bb) @@ -606,7 +718,8 @@ static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt, int r; uintptr_t bb_index, ctxt_index_wp, ctxt_index_rp; - mhi_log(MHI_MSG_RAW, "Entered chan %d\n", chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, + "Entered chan %d\n", chan); get_element_index(bb_ctxt, bb_ctxt->wp, &bb_index); get_element_index(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan], mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp, @@ -615,9 +728,9 @@ static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt, mhi_dev_ctxt->mhi_local_chan_ctxt[chan].rp, &ctxt_index_rp); BUG_ON(bb_index != ctxt_index_wp); - mhi_log(MHI_MSG_VERBOSE, - "Chan RP index %ld Chan WP index %ld, chan %d\n", - ctxt_index_rp, ctxt_index_wp, chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, + "Chan RP index %ld Chan WP index %ld, chan %d\n", + ctxt_index_rp, ctxt_index_wp, chan); r = ctxt_add_element(bb_ctxt, (void **)&bb_info); if (r) return r; @@ -626,88 +739,76 @@ static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt, bb_info->client_buf = buf; bb_info->dir = dir; bb_info->bb_p_addr = dma_map_single( - &mhi_dev_ctxt->dev_info->plat_dev->dev, + &mhi_dev_ctxt->plat_dev->dev, bb_info->client_buf, bb_info->buf_len, bb_info->dir); + bb_info->bb_active = 0; if (!VALID_BUF(bb_info->bb_p_addr, bb_info->buf_len, mhi_dev_ctxt)) { - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Buffer outside DMA range 0x%lx, size 0x%zx\n", - (uintptr_t)bb_info->bb_p_addr, buf_len); - dma_unmap_single(&mhi_dev_ctxt->dev_info->plat_dev->dev, + (uintptr_t)bb_info->bb_p_addr, buf_len); + dma_unmap_single(&mhi_dev_ctxt->plat_dev->dev, bb_info->bb_p_addr, bb_info->buf_len, bb_info->dir); - mhi_log(MHI_MSG_RAW, "Allocating BB, chan %d\n", chan); - bb_info->bb_v_addr = dma_alloc_coherent( - &mhi_dev_ctxt->dev_info->pcie_device->dev, - bb_info->buf_len, - &bb_info->bb_p_addr, - GFP_ATOMIC); - if (!bb_info->bb_v_addr) - return -ENOMEM; - mhi_dev_ctxt->counters.bb_used[chan]++; - if (dir == DMA_TO_DEVICE) { - mhi_log(MHI_MSG_INFO, "Copying client buf into BB.\n"); - memcpy(bb_info->bb_v_addr, buf, bb_info->buf_len); - /* Flush out data to bounce buffer */ - wmb(); - } - bb_info->bb_active = 1; + + if (likely((mhi_dev_ctxt->flags.bb_required && + bb_info->pre_alloc_len >= bb_info->buf_len))) { + bb_info->bb_p_addr = bb_info->pre_alloc_p_addr; + bb_info->bb_v_addr = bb_info->pre_alloc_v_addr; + mhi_dev_ctxt->counters.bb_used[chan]++; + if (dir == DMA_TO_DEVICE) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Copying client buf into BB.\n"); + memcpy(bb_info->bb_v_addr, buf, + bb_info->buf_len); + } + bb_info->bb_active = 1; + } else + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "No BB allocated\n"); } *bb = bb_info; - mhi_log(MHI_MSG_RAW, "Exited chan %d\n", chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Exited chan %d\n", chan); return 0; } +static void disable_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, + struct mhi_ring *bb_ctxt) +{ + if (mhi_dev_ctxt->flags.bb_required) { + struct mhi_buf_info *bb = + (struct mhi_buf_info *)bb_ctxt->base; + int nr_el = bb_ctxt->len / bb_ctxt->el_size; + int i = 0; + + for (i = 0; i < nr_el; i++, bb++) + dma_pool_free(bb_ctxt->dma_pool, bb->pre_alloc_v_addr, + bb->pre_alloc_p_addr); + dma_pool_destroy(bb_ctxt->dma_pool); + bb_ctxt->dma_pool = NULL; + } + + kfree(bb_ctxt->base); + bb_ctxt->base = NULL; +} + static void free_bounce_buffer(struct mhi_device_ctxt *mhi_dev_ctxt, struct mhi_buf_info *bb) { - mhi_log(MHI_MSG_RAW, "Entered\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Entered\n"); if (!bb->bb_active) /* This buffer was maped directly to device */ - dma_unmap_single(&mhi_dev_ctxt->dev_info->plat_dev->dev, + dma_unmap_single(&mhi_dev_ctxt->plat_dev->dev, bb->bb_p_addr, bb->buf_len, bb->dir); - else - /* This buffer was bounced */ - dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev, - bb->buf_len, - bb->bb_v_addr, - bb->bb_p_addr); - bb->bb_active = 0; - mhi_log(MHI_MSG_RAW, "Exited\n"); -} - -void reset_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, - struct mhi_ring *bb_ctxt) -{ - int r = 0; - struct mhi_buf_info *bb = NULL; - mhi_log(MHI_MSG_VERBOSE, "Entered\n"); - /* - Assumption: No events are expected during or after - this operation is occurring for this channel. - If a bounce buffer was allocated, the coherent memory is - expected to be already freed. - If the user's bounce buffer was mapped, it is expected to be - already unmapped. - Failure of any of the above conditions will result in - a memory leak or subtle memory corruption. - */ - while (!r) { - r = ctxt_del_element(bb_ctxt, (void **)&bb); - if (bb) - free_bounce_buffer(mhi_dev_ctxt, bb); - } - bb_ctxt->ack_rp = bb_ctxt->base; - bb_ctxt->rp = bb_ctxt->base; - bb_ctxt->wp = bb_ctxt->base; - mhi_log(MHI_MSG_VERBOSE, "Exited\n"); + bb->bb_active = 0; + mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Exited\n"); } static int mhi_queue_dma_xfer( - struct mhi_client_handle *client_handle, + struct mhi_client_config *client_config, dma_addr_t buf, size_t buf_len, enum MHI_FLAGS mhi_flags) { union mhi_xfer_pkt *pkt_loc; @@ -715,17 +816,17 @@ static int mhi_queue_dma_xfer( enum MHI_CLIENT_CHANNEL chan; struct mhi_device_ctxt *mhi_dev_ctxt; - mhi_dev_ctxt = client_handle->mhi_dev_ctxt; + mhi_dev_ctxt = client_config->mhi_dev_ctxt; MHI_ASSERT(VALID_BUF(buf, buf_len, mhi_dev_ctxt), "Client buffer is of invalid length\n"); - chan = client_handle->chan_info.chan_nr; + chan = client_config->chan_info.chan_nr; pkt_loc = mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp; pkt_loc->data_tx_pkt.buffer_ptr = buf; pkt_loc->type.info = mhi_flags; trace_mhi_tre(pkt_loc, chan, 0); - if (likely(0 != client_handle->intmod_t)) + if (likely(client_config->intmod_t)) MHI_TRB_SET_INFO(TX_TRB_BEI, pkt_loc, 1); else MHI_TRB_SET_INFO(TX_TRB_BEI, pkt_loc, 0); @@ -736,21 +837,21 @@ static int mhi_queue_dma_xfer( /* Ensure writes to descriptor are flushed */ wmb(); - mhi_log(MHI_MSG_VERBOSE, + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Channel %d Has buf size of %zd and buf addr %lx, flags 0x%x\n", - chan, buf_len, (uintptr_t)buf, mhi_flags); + chan, buf_len, (uintptr_t)buf, mhi_flags); /* Add the TRB to the correct transfer ring */ ret_val = ctxt_add_element(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan], (void *)&pkt_loc); if (unlikely(0 != ret_val)) { - mhi_log(MHI_MSG_VERBOSE, + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Failed to insert trb in xfer ring\n"); return ret_val; } if (MHI_OUT == - GET_CHAN_PROPS(CHAN_DIR, client_handle->chan_info.flags)) + GET_CHAN_PROPS(CHAN_DIR, client_config->chan_info.flags)) atomic_inc(&mhi_dev_ctxt->counters.outbound_acks); return ret_val; @@ -765,56 +866,55 @@ int mhi_queue_xfer(struct mhi_client_handle *client_handle, struct mhi_device_ctxt *mhi_dev_ctxt; u32 chan; unsigned long flags; + struct mhi_client_config *client_config; if (!client_handle || !buf || !buf_len) return -EINVAL; - mhi_dev_ctxt = client_handle->mhi_dev_ctxt; - chan = client_handle->chan_info.chan_nr; + client_config = client_handle->client_config; + mhi_dev_ctxt = client_config->mhi_dev_ctxt; + chan = client_config->chan_info.chan_nr; read_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags); if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE) { read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags); - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "MHI is not in active state\n"); return -EINVAL; } - - pm_runtime_get(&mhi_dev_ctxt->dev_info->pcie_device->dev); - mhi_assert_device_wake(mhi_dev_ctxt, false); + mhi_dev_ctxt->runtime_get(mhi_dev_ctxt); + mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false); read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags); - if (MHI_OUT == GET_CHAN_PROPS(CHAN_DIR, client_handle->chan_info.flags)) + if (GET_CHAN_PROPS(CHAN_DIR, client_config->chan_info.flags) == MHI_OUT) dma_dir = DMA_TO_DEVICE; else dma_dir = DMA_FROM_DEVICE; - r = create_bb(client_handle->mhi_dev_ctxt, - client_handle->chan_info.chan_nr, - buf, buf_len, dma_dir, &bb); + r = create_bb(client_config->mhi_dev_ctxt, + client_config->chan_info.chan_nr, + buf, + buf_len, + dma_dir, + &bb); if (r) { - mhi_log(MHI_MSG_VERBOSE, - "Failed to create BB, chan %d ret %d\n", - chan, - r); - pm_runtime_mark_last_busy(&mhi_dev_ctxt-> - dev_info->pcie_device->dev); - pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info-> - pcie_device->dev); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, + "Failed to create BB, chan %d ret %d\n", chan, r); + mhi_dev_ctxt->runtime_put(mhi_dev_ctxt); read_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags); - mhi_deassert_device_wake(mhi_dev_ctxt); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags); return r; } - mhi_log(MHI_MSG_VERBOSE, - "Queueing to HW: Client Buf 0x%p, size 0x%zx, DMA %llx, chan %d\n", - buf, buf_len, (u64)bb->bb_p_addr, - client_handle->chan_info.chan_nr); - r = mhi_queue_dma_xfer(client_handle, - bb->bb_p_addr, - bb->buf_len, - mhi_flags); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, + "Queueing to HW: Client Buf 0x%p, size 0x%zx, DMA %llx, chan %d\n", + buf, buf_len, (u64)bb->bb_p_addr, + client_config->chan_info.chan_nr); + r = mhi_queue_dma_xfer(client_config, + bb->bb_p_addr, + bb->buf_len, + mhi_flags); /* * Assumption: If create_bounce_buffer did not fail, we do not @@ -826,11 +926,8 @@ int mhi_queue_xfer(struct mhi_client_handle *client_handle, read_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags); mhi_queue_tre(mhi_dev_ctxt, chan, MHI_RING_TYPE_XFER_RING); if (dma_dir == DMA_FROM_DEVICE) { - pm_runtime_mark_last_busy(&mhi_dev_ctxt-> - dev_info->pcie_device->dev); - pm_runtime_put_noidle(&mhi_dev_ctxt-> - dev_info->pcie_device->dev); - mhi_deassert_device_wake(mhi_dev_ctxt); + mhi_dev_ctxt->runtime_put(mhi_dev_ctxt); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); } read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags); return 0; @@ -848,13 +945,12 @@ int mhi_send_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, mhi_local_cmd_ctxt[PRIMARY_CMD_RING]; if (chan >= MHI_MAX_CHANNELS || cmd >= MHI_COMMAND_MAX_NR) { - mhi_log(MHI_MSG_ERROR, - "Invalid channel id, received id: 0x%x", - chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Invalid channel id, received id: 0x%x", chan); return -EINVAL; } - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered, MHI state %s dev_exec_env %d chan %d cmd %d\n", TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state), mhi_dev_ctxt->dev_exec_env, chan, cmd); @@ -868,14 +964,16 @@ int mhi_send_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, ring_el_type = MHI_PKT_TYPE_START_CHAN_CMD; break; default: - mhi_log(MHI_MSG_ERROR, "Bad command received\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Bad command received\n"); return -EINVAL; } spin_lock_irqsave(&mhi_ring->ring_lock, flags); ret_val = ctxt_add_element(mhi_ring, (void *)&cmd_pkt); if (ret_val) { - mhi_log(MHI_MSG_ERROR, "Failed to insert element\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to insert element\n"); spin_unlock_irqrestore(&mhi_ring->ring_lock, flags); return ret_val; } @@ -886,13 +984,10 @@ int mhi_send_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, mhi_queue_tre(mhi_dev_ctxt, 0, MHI_RING_TYPE_CMD_RING); read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags2); spin_unlock_irqrestore(&mhi_ring->ring_lock, flags); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, + "Sent command 0x%x for chan %d\n", cmd, chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited ret %d.\n", ret_val); - mhi_log(MHI_MSG_VERBOSE, - "Sent command 0x%x for chan %d\n", - cmd, - chan); - - mhi_log(MHI_MSG_INFO, "Exited ret %d.\n", ret_val); return ret_val; } @@ -904,7 +999,7 @@ static void parse_inbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt, struct mhi_buf_info *bb; - mhi_log(MHI_MSG_INFO, "Entered\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n"); bb = bb_ctxt->rp; bb->filled_size = bounced_data_size; @@ -915,7 +1010,7 @@ static void parse_inbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt, if (bb->bb_active) { /* This is coherent memory, no cache management is needed */ memcpy(bb->client_buf, bb->bb_v_addr, bb->filled_size); - mhi_log(MHI_MSG_RAW, + mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Bounce from BB:0x%p to Client Buf: 0x%p Len 0x%zx\n", bb->client_buf, bb->bb_v_addr, bb->filled_size); } @@ -929,7 +1024,7 @@ static void parse_inbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt, * rp, since it can be moved async by mhi_poll_inbound */ free_bounce_buffer(mhi_dev_ctxt, bb); - mhi_log(MHI_MSG_INFO, "Exited\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n"); } static void parse_outbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt, @@ -940,7 +1035,7 @@ static void parse_outbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt, struct mhi_buf_info *bb; bb = bb_ctxt->rp; - mhi_log(MHI_MSG_RAW, "Entered\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Entered\n"); BUG_ON(bb->dir != DMA_TO_DEVICE); bb->filled_size = bounced_data_size; BUG_ON(bb->filled_size != bb->buf_len); @@ -948,7 +1043,7 @@ static void parse_outbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt, result->bytes_xferd = bb->filled_size; result->transaction_status = 0; free_bounce_buffer(mhi_dev_ctxt, bb); - mhi_log(MHI_MSG_RAW, "Exited\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Exited\n"); } static int parse_outbound(struct mhi_device_ctxt *mhi_dev_ctxt, @@ -957,71 +1052,75 @@ static int parse_outbound(struct mhi_device_ctxt *mhi_dev_ctxt, struct mhi_result *result = NULL; int ret_val = 0; struct mhi_client_handle *client_handle = NULL; + struct mhi_client_config *client_config; struct mhi_ring *local_chan_ctxt = NULL; struct mhi_cb_info cb_info; struct mhi_ring *bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan]; local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; client_handle = mhi_dev_ctxt->client_handle_list[chan]; + client_config = client_handle->client_config; /* If ring is empty */ MHI_ASSERT(!unlikely(mhi_dev_ctxt->mhi_local_chan_ctxt[chan].rp == mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp), "Empty Event Ring\n"); parse_outbound_bb(mhi_dev_ctxt, bb_ctxt, - &client_handle->result, xfer_len); + &client_config->result, xfer_len); - mhi_log(MHI_MSG_RAW, "Removing BB from head, chan %d\n", chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, + "Removing BB from head, chan %d\n", chan); atomic_dec(&mhi_dev_ctxt->counters.outbound_acks); - mhi_deassert_device_wake(mhi_dev_ctxt); - pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->pcie_device->dev); - pm_runtime_mark_last_busy(&mhi_dev_ctxt->dev_info->pcie_device->dev); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); + mhi_dev_ctxt->runtime_put(mhi_dev_ctxt); ret_val = ctxt_del_element(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan], NULL); BUG_ON(ret_val); ret_val = ctxt_del_element(bb_ctxt, NULL); BUG_ON(ret_val); - if (NULL != client_handle) { - result = &mhi_dev_ctxt->client_handle_list[chan]->result; - if (NULL != (&client_handle->client_info.mhi_client_cb)) { - client_handle->result.user_data = - client_handle->user_data; - cb_info.cb_reason = MHI_CB_XFER; - cb_info.result = &client_handle->result; - cb_info.chan = chan; - client_handle->client_info.mhi_client_cb(&cb_info); - } + + result = &client_config->result; + if (NULL != (&client_config->client_info.mhi_client_cb)) { + client_config->result.user_data = + client_config->user_data; + cb_info.cb_reason = MHI_CB_XFER; + cb_info.result = result; + cb_info.chan = chan; + client_config->client_info.mhi_client_cb(&cb_info); } - mhi_log(MHI_MSG_RAW, + + mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Processed outbound ack chan %d Pending acks %d.\n", chan, atomic_read(&mhi_dev_ctxt->counters.outbound_acks)); return 0; } static int parse_inbound(struct mhi_device_ctxt *mhi_dev_ctxt, - u32 chan, union mhi_xfer_pkt *local_ev_trb_loc, u16 xfer_len) + u32 chan, union mhi_xfer_pkt *local_ev_trb_loc, + u16 xfer_len, unsigned ev_ring) { struct mhi_client_handle *client_handle; + struct mhi_client_config *client_config; struct mhi_ring *local_chan_ctxt; struct mhi_result *result; struct mhi_cb_info cb_info; struct mhi_ring *bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan]; + bool ev_managed = GET_EV_PROPS(EV_MANAGED, + mhi_dev_ctxt->ev_ring_props[ev_ring].flags); int r; uintptr_t bb_index, ctxt_index_rp, ctxt_index_wp; client_handle = mhi_dev_ctxt->client_handle_list[chan]; + client_config = client_handle->client_config; local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; MHI_ASSERT(!unlikely(mhi_dev_ctxt->mhi_local_chan_ctxt[chan].rp == mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp), "Empty Event Ring\n"); - if (NULL != mhi_dev_ctxt->client_handle_list[chan]) - result = &mhi_dev_ctxt->client_handle_list[chan]->result; - - parse_inbound_bb(mhi_dev_ctxt, bb_ctxt, - &client_handle->result, xfer_len); + result = &client_config->result; + parse_inbound_bb(mhi_dev_ctxt, bb_ctxt, result, xfer_len); - if (unlikely(IS_SOFTWARE_CHANNEL(chan))) { + if (ev_managed) { MHI_TX_TRB_SET_LEN(TX_TRB_LEN, local_ev_trb_loc, xfer_len); r = ctxt_del_element(local_chan_ctxt, NULL); BUG_ON(r); @@ -1034,19 +1133,19 @@ static int parse_inbound(struct mhi_device_ctxt *mhi_dev_ctxt, get_element_index(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan], mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp, &ctxt_index_wp); - mhi_log(MHI_MSG_VERBOSE, + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Chan RP index %ld Chan WP index %ld chan %d\n", ctxt_index_rp, ctxt_index_wp, chan); BUG_ON(bb_index != ctxt_index_rp); - if (NULL != client_handle->client_info.mhi_client_cb) { - client_handle->result.user_data = - client_handle->user_data; + if (client_config->client_info.mhi_client_cb) { + client_config->result.user_data = + client_config->user_data; cb_info.cb_reason = MHI_CB_XFER; - cb_info.result = &client_handle->result; + cb_info.result = &client_config->result; cb_info.chan = chan; - client_handle->client_info.mhi_client_cb(&cb_info); + client_config->client_info.mhi_client_cb(&cb_info); } else { - mhi_log(MHI_MSG_VERBOSE, + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "No client registered chan %d\n", chan); } } else { @@ -1067,7 +1166,7 @@ static int parse_inbound(struct mhi_device_ctxt *mhi_dev_ctxt, &mhi_dev_ctxt->mhi_local_chan_ctxt[chan], mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp, &ctxt_index_wp); - mhi_log(MHI_MSG_VERBOSE, + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Chan RP index %ld Chan WP index %ld chan %d\n", ctxt_index_rp, ctxt_index_wp, chan); BUG_ON(bb_index != ctxt_index_rp); @@ -1088,27 +1187,32 @@ static int validate_xfer_el_addr(struct mhi_chan_ctxt *ring, -ERANGE : 0; } -static void print_tre(int chan, struct mhi_ring *ring, struct mhi_tx_pkt *tre) +static void print_tre(struct mhi_device_ctxt *mhi_dev_ctxt, + int chan, + struct mhi_ring *ring, + struct mhi_tx_pkt *tre) { uintptr_t el_index; get_element_index(ring, tre, &el_index); - mhi_log(MHI_MSG_ERROR, "Printing TRE 0x%p index %lx for channel %d:\n", - tre, el_index, chan); - mhi_log(MHI_MSG_ERROR, "Buffer Pointer 0x%llx, len 0x%x, info 0x%x\n", - tre->buffer_ptr, tre->buf_len, tre->info); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Printing TRE 0x%p index %lx for channel %d:\n", + tre, el_index, chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Buffer Pointer 0x%llx, len 0x%x, info 0x%x\n", + tre->buffer_ptr, tre->buf_len, tre->info); } -int parse_xfer_event(struct mhi_device_ctxt *ctxt, +int parse_xfer_event(struct mhi_device_ctxt *mhi_dev_ctxt, union mhi_event_pkt *event, u32 event_id) { - struct mhi_device_ctxt *mhi_dev_ctxt = (struct mhi_device_ctxt *)ctxt; struct mhi_result *result; u32 chan = MHI_MAX_CHANNELS; u16 xfer_len; uintptr_t phy_ev_trb_loc; union mhi_xfer_pkt *local_ev_trb_loc; struct mhi_client_handle *client_handle; + struct mhi_client_config *client_config; union mhi_xfer_pkt *local_trb_loc; struct mhi_chan_ctxt *chan_ctxt; u32 nr_trb_to_parse; @@ -1121,11 +1225,11 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt, local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; ev_code = MHI_EV_READ_CODE(EV_TRB_CODE, event); client_handle = mhi_dev_ctxt->client_handle_list[chan]; - client_handle->pkt_count++; - result = &client_handle->result; - mhi_log(MHI_MSG_VERBOSE, - "Event Received, chan %d, cc_code %d\n", - chan, ev_code); + client_config = client_handle->client_config; + client_config->pkt_count++; + result = &client_config->result; + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, + "Event Received, chan %d, cc_code %d\n", chan, ev_code); if (ev_code == MHI_EVENT_CC_OVERFLOW) result->transaction_status = -EOVERFLOW; else @@ -1158,10 +1262,9 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt, local_ev_trb_loc, &nr_trb_to_parse); if (unlikely(ret_val)) { - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Failed to get nr available trbs ret: %d.\n", ret_val); - panic("critical error"); return ret_val; } do { @@ -1177,14 +1280,14 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt, local_trb_loc); if (!VALID_BUF(trb_data_loc, xfer_len, mhi_dev_ctxt)) { - mhi_log(MHI_MSG_CRITICAL, - "Bad buffer ptr: %lx.\n", - (uintptr_t)trb_data_loc); + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "Bad buf ptr: %llx.\n", trb_data_loc); return -EINVAL; } if (local_chan_ctxt->dir == MHI_IN) { parse_inbound(mhi_dev_ctxt, chan, - local_ev_trb_loc, xfer_len); + local_ev_trb_loc, xfer_len, + event_id); } else { parse_outbound(mhi_dev_ctxt, chan, local_ev_trb_loc, xfer_len); @@ -1192,7 +1295,7 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt, mhi_dev_ctxt->counters.chan_pkts_xferd[chan]++; if (local_trb_loc == (union mhi_xfer_pkt *)local_chan_ctxt->rp) { - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Done. Processed until: %lx.\n", (uintptr_t)trb_data_loc); break; @@ -1208,7 +1311,8 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt, { u64 db_value = 0; - mhi_log(MHI_MSG_INFO, "DB_MODE/OOB Detected chan %d.\n", chan); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "DB_MODE/OOB Detected chan %d.\n", chan); local_chan_ctxt->db_mode.db_mode = 1; if (local_chan_ctxt->wp != local_chan_ctxt->rp) { @@ -1219,9 +1323,6 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt, mhi_dev_ctxt->mmio_info.chan_db_addr, chan, db_value); } - client_handle = mhi_dev_ctxt->client_handle_list[chan]; - if (client_handle) - result->transaction_status = -ENOTCONN; break; } case MHI_EVENT_CC_BAD_TRE: @@ -1229,15 +1330,16 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt, local_ev_trb_loc = (void *)mhi_p2v_addr(mhi_dev_ctxt, MHI_RING_TYPE_EVENT_RING, event_id, phy_ev_trb_loc); - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Received BAD TRE event for ring %d, pointer 0x%p\n", chan, local_ev_trb_loc); - print_tre(chan, &mhi_dev_ctxt->mhi_local_chan_ctxt[chan], + print_tre(mhi_dev_ctxt, chan, + &mhi_dev_ctxt->mhi_local_chan_ctxt[chan], (struct mhi_tx_pkt *)local_ev_trb_loc); BUG(); break; default: - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Unknown TX completion.\n"); break; @@ -1261,12 +1363,14 @@ int recycle_trb_and_ring(struct mhi_device_ctxt *mhi_dev_ctxt, ret_val = ctxt_del_element(ring, &removed_element); if (ret_val) { - mhi_log(MHI_MSG_ERROR, "Could not remove element from ring\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Could not remove element from ring\n"); return ret_val; } ret_val = ctxt_add_element(ring, &added_element); if (ret_val) { - mhi_log(MHI_MSG_ERROR, "Could not add element to ring\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Could not add element to ring\n"); return ret_val; } @@ -1296,9 +1400,7 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, struct mhi_ring *ev_ring; struct mhi_chan_ctxt *chan_ctxt; struct mhi_event_ctxt *ev_ctxt = NULL; - struct mhi_client_handle *client_handle = NULL; int pending_el = 0, i; - struct mhi_ring *bb_ctxt; unsigned long flags; union mhi_event_pkt *local_rp = NULL; union mhi_event_pkt *device_rp = NULL; @@ -1306,20 +1408,19 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, MHI_TRB_GET_INFO(CMD_TRB_CHID, cmd_pkt, chan); if (!VALID_CHAN_NR(chan)) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Bad channel number for CCE\n"); return -EINVAL; } - bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan]; - client_handle = mhi_dev_ctxt->client_handle_list[chan]; local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; chan_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan]; ev_ring = &mhi_dev_ctxt-> mhi_local_event_ctxt[chan_ctxt->mhi_event_ring_index]; ev_ctxt = &mhi_dev_ctxt-> dev_space.ring_ctxt.ec_list[chan_ctxt->mhi_event_ring_index]; - mhi_log(MHI_MSG_INFO, "Processed cmd reset event\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Processed cmd reset event\n"); /* Clear all stale events related to Channel */ spin_lock_irqsave(&ev_ring->ring_lock, flags); @@ -1357,21 +1458,18 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, local_chan_ctxt->rp, local_chan_ctxt->wp, &pending_el); - mhi_log(MHI_MSG_INFO, "Decrementing chan %d out acks by %d.\n", - chan, pending_el); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Decrementing chan %d out acks by %d.\n", chan, pending_el); atomic_sub(pending_el, &mhi_dev_ctxt->counters.outbound_acks); read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); for (i = 0; i < pending_el; i++) - mhi_deassert_device_wake(mhi_dev_ctxt); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); for (i = 0; i < pending_el; i++) { - pm_runtime_put_noidle(&mhi_dev_ctxt-> - dev_info->pcie_device->dev); - pm_runtime_mark_last_busy(&mhi_dev_ctxt-> - dev_info->pcie_device->dev); + mhi_dev_ctxt->runtime_put(mhi_dev_ctxt); } /* Reset the local channel context */ @@ -1384,10 +1482,7 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, chan_ctxt->mhi_trb_read_ptr = chan_ctxt->mhi_trb_ring_base_addr; chan_ctxt->mhi_trb_write_ptr = chan_ctxt->mhi_trb_ring_base_addr; - mhi_log(MHI_MSG_INFO, "Cleaning up BB list\n"); - reset_bb_ctxt(mhi_dev_ctxt, bb_ctxt); - - mhi_log(MHI_MSG_INFO, "Reset complete.\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Reset complete.\n"); return ret_val; } @@ -1418,15 +1513,17 @@ int mhi_poll_inbound(struct mhi_client_handle *client_handle, struct mhi_chan_cfg *cfg; struct mhi_ring *bb_ctxt = NULL; struct mhi_buf_info *bb = NULL; + struct mhi_client_config *client_config; int chan = 0, r = 0; - if (!client_handle || !result || !client_handle->mhi_dev_ctxt) + if (!client_handle || !result) return -EINVAL; + client_config = client_handle->client_config; + mhi_dev_ctxt = client_config->mhi_dev_ctxt; - mhi_log(MHI_MSG_VERBOSE, "Entered\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Entered\n"); - mhi_dev_ctxt = client_handle->mhi_dev_ctxt; - chan = client_handle->chan_info.chan_nr; + chan = client_config->chan_info.chan_nr; local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan]; bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan]; @@ -1437,7 +1534,7 @@ int mhi_poll_inbound(struct mhi_client_handle *client_handle, result->flags = pending_trb->info; bb = bb_ctxt->ack_rp; if (bb->bb_active) { - mhi_log(MHI_MSG_VERBOSE, + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Bounce buffer active chan %d, copying data\n", chan); } @@ -1458,7 +1555,7 @@ int mhi_poll_inbound(struct mhi_client_handle *client_handle, r = -ENODATA; } mutex_unlock(&cfg->chan_lock); - mhi_log(MHI_MSG_VERBOSE, + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Exited Result: Buf addr: 0x%p Bytes xfed 0x%zx chan %d\n", result->buf_addr, result->bytes_xferd, chan); return r; @@ -1488,11 +1585,11 @@ int mhi_wait_for_mdm(struct mhi_device_ctxt *mhi_dev_ctxt) while (mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr, MHIREGLEN) == 0xFFFFFFFF && j <= MHI_MAX_LINK_RETRIES) { - mhi_log(MHI_MSG_CRITICAL, - "Could not access device retry %d\n", j); + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "Could not access device retry %d\n", j); msleep(MHI_LINK_STABILITY_WAIT_MS); if (MHI_MAX_LINK_RETRIES == j) { - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Could not access device, FAILING!\n"); return -ETIME; } @@ -1503,9 +1600,12 @@ int mhi_wait_for_mdm(struct mhi_device_ctxt *mhi_dev_ctxt) int mhi_get_max_desc(struct mhi_client_handle *client_handle) { + struct mhi_client_config *client_config; + if (!client_handle) return -EINVAL; - return client_handle->chan_info.max_desc - 1; + client_config = client_handle->client_config; + return client_config->chan_info.max_desc - 1; } EXPORT_SYMBOL(mhi_get_max_desc); @@ -1514,6 +1614,27 @@ int mhi_get_epid(struct mhi_client_handle *client_handle) return MHI_EPID; } +void mhi_master_mode_runtime_get(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + pm_runtime_get(&mhi_dev_ctxt->pcie_device->dev); +} + +void mhi_master_mode_runtime_put(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + pm_runtime_mark_last_busy(&mhi_dev_ctxt->pcie_device->dev); + pm_runtime_put_noidle(&mhi_dev_ctxt->pcie_device->dev); +} + +void mhi_slave_mode_runtime_get(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + mhi_dev_ctxt->bus_master_rt_get(mhi_dev_ctxt->pcie_device); +} + +void mhi_slave_mode_runtime_put(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + mhi_dev_ctxt->bus_master_rt_put(mhi_dev_ctxt->pcie_device); +} + /* * mhi_assert_device_wake - Set WAKE_DB register * force_set - if true, will set bit regardless of counts @@ -1572,16 +1693,17 @@ void mhi_deassert_device_wake(struct mhi_device_ctxt *mhi_dev_ctxt) int mhi_set_lpm(struct mhi_client_handle *client_handle, bool enable_lpm) { - struct mhi_device_ctxt *mhi_dev_ctxt = client_handle->mhi_dev_ctxt; + struct mhi_client_config *client_config = client_handle->client_config; + struct mhi_device_ctxt *mhi_dev_ctxt = client_config->mhi_dev_ctxt; unsigned long flags; read_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags); /* Disable low power mode by asserting Wake */ if (enable_lpm == false) - mhi_assert_device_wake(mhi_dev_ctxt, false); + mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false); else - mhi_deassert_device_wake(mhi_dev_ctxt); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags); @@ -1592,26 +1714,147 @@ EXPORT_SYMBOL(mhi_set_lpm); int mhi_set_bus_request(struct mhi_device_ctxt *mhi_dev_ctxt, int index) { - mhi_log(MHI_MSG_INFO, "Setting bus request to index %d\n", index); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Setting bus request to index %d\n", index); return msm_bus_scale_client_update_request(mhi_dev_ctxt->bus_client, index); } -int mhi_deregister_channel(struct mhi_client_handle - *client_handle) { +int mhi_deregister_channel(struct mhi_client_handle *client_handle) +{ int ret_val = 0; int chan; + struct mhi_client_config *client_config; + struct mhi_device_ctxt *mhi_dev_ctxt; - if (!client_handle || client_handle->magic != MHI_HANDLE_MAGIC) + if (!client_handle) return -EINVAL; - chan = client_handle->chan_info.chan_nr; - client_handle->magic = 0; - client_handle->mhi_dev_ctxt->client_handle_list[chan] = NULL; + + client_config = client_handle->client_config; + mhi_dev_ctxt = client_config->mhi_dev_ctxt; + chan = client_config->chan_info.chan_nr; + client_config->magic = 0; + mhi_dev_ctxt->client_handle_list[chan] = NULL; + disable_bb_ctxt(mhi_dev_ctxt, &mhi_dev_ctxt->chan_bb_list[chan]); + kfree(client_config); kfree(client_handle); return ret_val; } EXPORT_SYMBOL(mhi_deregister_channel); +int mhi_register_device(struct mhi_device *mhi_device, + const char *node_name, + unsigned long user_data) +{ + const struct device_node *of_node; + struct mhi_device_ctxt *mhi_dev_ctxt = NULL, *itr; + struct pcie_core_info *core_info; + struct pci_dev *pci_dev = mhi_device->pci_dev; + u32 domain = pci_domain_nr(pci_dev->bus); + u32 bus = pci_dev->bus->number; + u32 dev_id = pci_dev->device; + u32 slot = PCI_SLOT(pci_dev->devfn); + int ret, i; + + of_node = of_parse_phandle(mhi_device->dev->of_node, node_name, 0); + if (!of_node) + return -EINVAL; + + if (!mhi_device_drv) + return -EPROBE_DEFER; + + /* Traverse thru the list */ + mutex_lock(&mhi_device_drv->lock); + list_for_each_entry(itr, &mhi_device_drv->head, node) { + struct platform_device *pdev = itr->plat_dev; + struct pcie_core_info *core = &itr->core; + + if (pdev->dev.of_node == of_node && + core->domain == domain && + core->bus == bus && + core->dev_id == dev_id && + core->slot == slot) { + mhi_dev_ctxt = itr; + break; + } + } + mutex_unlock(&mhi_device_drv->lock); + + /* perhaps we've not probed yet */ + if (!mhi_dev_ctxt) + return -EPROBE_DEFER; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Registering Domain:%02u Bus:%04u dev:0x%04x slot:%04u\n", + domain, bus, dev_id, slot); + + /* Set up pcie dev info */ + mhi_dev_ctxt->pcie_device = pci_dev; + mhi_dev_ctxt->mhi_pm_state = MHI_PM_DISABLE; + INIT_WORK(&mhi_dev_ctxt->process_m1_worker, process_m1_transition); + INIT_WORK(&mhi_dev_ctxt->st_thread_worker, mhi_state_change_worker); + mutex_init(&mhi_dev_ctxt->pm_lock); + rwlock_init(&mhi_dev_ctxt->pm_xfer_lock); + spin_lock_init(&mhi_dev_ctxt->dev_wake_lock); + init_completion(&mhi_dev_ctxt->cmd_complete); + mhi_dev_ctxt->flags.link_up = 1; + core_info = &mhi_dev_ctxt->core; + core_info->manufact_id = pci_dev->vendor; + core_info->pci_master = false; + + /* Go thru resources and set up */ + for (i = 0; i < ARRAY_SIZE(mhi_device->resources); i++) { + const struct resource *res = &mhi_device->resources[i]; + + switch (resource_type(res)) { + case IORESOURCE_MEM: + /* bus master already mapped it */ + core_info->bar0_base = (void __iomem *)res->start; + core_info->bar0_end = (void __iomem *)res->end; + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "bar mapped to:0x%llx - 0x%llx (virtual)\n", + res->start, res->end); + break; + case IORESOURCE_IRQ: + core_info->irq_base = (u32)res->start; + core_info->max_nr_msis = (u32)resource_size(res); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "irq mapped to: %u size:%u\n", + core_info->irq_base, + core_info->max_nr_msis); + break; + }; + } + + if (!core_info->bar0_base || !core_info->irq_base) + return -EINVAL; + + mhi_dev_ctxt->bus_master_rt_get = mhi_device->pm_runtime_get; + mhi_dev_ctxt->bus_master_rt_put = mhi_device->pm_runtime_noidle; + if (!mhi_dev_ctxt->bus_master_rt_get || + !mhi_dev_ctxt->bus_master_rt_put) + return -EINVAL; + + ret = mhi_ctxt_init(mhi_dev_ctxt); + if (ret) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "MHI Initialization failed, ret %d\n", ret); + return ret; + } + mhi_init_debugfs(mhi_dev_ctxt); + + /* setup shadow pm functions */ + mhi_dev_ctxt->assert_wake = mhi_assert_device_wake; + mhi_dev_ctxt->deassert_wake = mhi_deassert_device_wake; + mhi_dev_ctxt->runtime_get = mhi_slave_mode_runtime_get; + mhi_dev_ctxt->runtime_put = mhi_slave_mode_runtime_put; + mhi_device->mhi_dev_ctxt = mhi_dev_ctxt; + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit success\n"); + + return 0; +} +EXPORT_SYMBOL(mhi_register_device); + void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, void __iomem *io_addr, uintptr_t chan, @@ -1627,9 +1870,9 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, ring_ctxt = &mhi_dev_ctxt-> mhi_local_event_ctxt[chan]; - mhi_log(MHI_MSG_VERBOSE, - "db.set addr: %p io_offset 0x%lx val:0x%x\n", - io_addr, chan, val); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, + "db.set addr: %p io_offset 0x%lx val:0x%x\n", + io_addr, chan, val); mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val); @@ -1637,10 +1880,9 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, mhi_write_db(mhi_dev_ctxt, io_addr, chan, val); ring_ctxt->db_mode.db_mode = 0; } else { - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Not ringing xfer db, chan %ld, brstmode %d db_mode %d\n", - chan, - ring_ctxt->db_mode.brstmode, + chan, ring_ctxt->db_mode.brstmode, ring_ctxt->db_mode.db_mode); } } @@ -1650,10 +1892,9 @@ void mhi_process_db_brstmode_disable(struct mhi_device_ctxt *mhi_dev_ctxt, uintptr_t chan, u32 val) { - mhi_log(MHI_MSG_VERBOSE, - "db.set addr: %p io_offset 0x%lx val:0x%x\n", - io_addr, chan, val); - + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, + "db.set addr: %p io_offset 0x%lx val:0x%x\n", + io_addr, chan, val); mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val); mhi_write_db(mhi_dev_ctxt, io_addr, chan, val); } @@ -1663,9 +1904,9 @@ void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt, uintptr_t chan, u32 val) { - mhi_log(MHI_MSG_VERBOSE, - "db.set addr: %p io_offset 0x%lx val:0x%x\n", - io_addr, chan, val); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, + "db.set addr: %p io_offset 0x%lx val:0x%x\n", + io_addr, chan, val); mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val); @@ -1678,7 +1919,7 @@ void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt, mhi_write_db(mhi_dev_ctxt, io_addr, chan, val); chan_ctxt->db_mode.db_mode = 0; } else { - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Not ringing xfer db, chan %ld, brstmode %d db_mode %d\n", chan, chan_ctxt->db_mode.brstmode, chan_ctxt->db_mode.db_mode); @@ -1714,10 +1955,9 @@ void mhi_reg_write(struct mhi_device_ctxt *mhi_dev_ctxt, void __iomem *io_addr, uintptr_t io_offset, u32 val) { - mhi_log(MHI_MSG_RAW, "d.s 0x%p off: 0x%lx 0x%x\n", - io_addr, io_offset, val); + mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, + "d.s 0x%p off: 0x%lx 0x%x\n", io_addr, io_offset, val); iowrite32(val, io_addr + io_offset); - /* Flush write to device */ wmb(); } diff --git a/drivers/platform/msm/mhi/mhi_mmio_ops.c b/drivers/platform/msm/mhi/mhi_mmio_ops.c index b4447378683e..a991a2e68b34 100644 --- a/drivers/platform/msm/mhi/mhi_mmio_ops.c +++ b/drivers/platform/msm/mhi/mhi_mmio_ops.c @@ -29,93 +29,79 @@ int mhi_test_for_device_reset(struct mhi_device_ctxt *mhi_dev_ctxt) { u32 pcie_word_val = 0; - u32 expiry_counter; unsigned long flags; rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock; + unsigned long timeout; - mhi_log(MHI_MSG_INFO, "Waiting for MMIO RESET bit to be cleared.\n"); - read_lock_irqsave(pm_xfer_lock, flags); - if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) { - read_unlock_irqrestore(pm_xfer_lock, flags); - return -EIO; - } - pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr, - MHISTATUS); - MHI_READ_FIELD(pcie_word_val, - MHICTRL_RESET_MASK, - MHICTRL_RESET_SHIFT); - read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags); - if (pcie_word_val == 0xFFFFFFFF) - return -ENOTCONN; - - while (MHI_STATE_RESET != pcie_word_val && expiry_counter < 100) { - expiry_counter++; - mhi_log(MHI_MSG_ERROR, - "Device is not RESET, sleeping and retrying.\n"); - msleep(MHI_READY_STATUS_TIMEOUT_MS); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Waiting for MMIO RESET bit to be cleared.\n"); + + timeout = jiffies + + msecs_to_jiffies(mhi_dev_ctxt->poll_reset_timeout_ms); + while (time_before(jiffies, timeout)) { read_lock_irqsave(pm_xfer_lock, flags); if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) { read_unlock_irqrestore(pm_xfer_lock, flags); return -EIO; } pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr, - MHICTRL); + MHICTRL); + read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags); + if (pcie_word_val == 0xFFFFFFFF) + return -ENOTCONN; MHI_READ_FIELD(pcie_word_val, - MHICTRL_RESET_MASK, - MHICTRL_RESET_SHIFT); - read_unlock_irqrestore(pm_xfer_lock, flags); - } + MHICTRL_RESET_MASK, + MHICTRL_RESET_SHIFT); - if (MHI_STATE_READY != pcie_word_val) - return -ENOTCONN; - return 0; + if (!pcie_word_val) + return 0; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "MHI still in Reset sleeping\n"); + msleep(MHI_THREAD_SLEEP_TIMEOUT_MS); + } + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Timeout waiting for reset to be cleared\n"); + return -ETIMEDOUT; } int mhi_test_for_device_ready(struct mhi_device_ctxt *mhi_dev_ctxt) { u32 pcie_word_val = 0; - u32 expiry_counter; unsigned long flags; rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock; + unsigned long timeout; - mhi_log(MHI_MSG_INFO, "Waiting for MMIO Ready bit to be set\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Waiting for MMIO Ready bit to be set\n"); - read_lock_irqsave(pm_xfer_lock, flags); - if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) { - read_unlock_irqrestore(pm_xfer_lock, flags); - return -EIO; - } - /* Read MMIO and poll for READY bit to be set */ - pcie_word_val = mhi_reg_read( - mhi_dev_ctxt->mmio_info.mmio_addr, MHISTATUS); - MHI_READ_FIELD(pcie_word_val, - MHISTATUS_READY_MASK, - MHISTATUS_READY_SHIFT); - read_unlock_irqrestore(pm_xfer_lock, flags); - - if (pcie_word_val == 0xFFFFFFFF) - return -ENOTCONN; - expiry_counter = 0; - while (MHI_STATE_READY != pcie_word_val && expiry_counter < 50) { - expiry_counter++; - mhi_log(MHI_MSG_ERROR, - "Device is not ready, sleeping and retrying.\n"); - msleep(MHI_READY_STATUS_TIMEOUT_MS); + timeout = jiffies + + msecs_to_jiffies(mhi_dev_ctxt->poll_reset_timeout_ms); + while (time_before(jiffies, timeout)) { + /* Read MMIO and poll for READY bit to be set */ read_lock_irqsave(pm_xfer_lock, flags); if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) { read_unlock_irqrestore(pm_xfer_lock, flags); return -EIO; } + pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr, MHISTATUS); - MHI_READ_FIELD(pcie_word_val, - MHISTATUS_READY_MASK, MHISTATUS_READY_SHIFT); read_unlock_irqrestore(pm_xfer_lock, flags); + if (pcie_word_val == 0xFFFFFFFF) + return -ENOTCONN; + MHI_READ_FIELD(pcie_word_val, + MHISTATUS_READY_MASK, + MHISTATUS_READY_SHIFT); + if (pcie_word_val == MHI_STATE_READY) + return 0; + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Device is not ready, sleeping and retrying.\n"); + msleep(MHI_THREAD_SLEEP_TIMEOUT_MS); } - - if (pcie_word_val != MHI_STATE_READY) - return -ETIMEDOUT; - return 0; + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Device timed out waiting for ready\n"); + return -ETIMEDOUT; } int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt) @@ -125,28 +111,26 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt) u32 i = 0; int ret_val; - mhi_log(MHI_MSG_INFO, "~~~ Initializing MMIO ~~~\n"); - mhi_dev_ctxt->mmio_info.mmio_addr = mhi_dev_ctxt->dev_props->bar0_base; - - mhi_log(MHI_MSG_INFO, "Bar 0 address is at: 0x%p\n", - mhi_dev_ctxt->mmio_info.mmio_addr); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "~~~ Initializing MMIO ~~~\n"); + mhi_dev_ctxt->mmio_info.mmio_addr = mhi_dev_ctxt->core.bar0_base; mhi_dev_ctxt->mmio_info.mmio_len = mhi_reg_read( mhi_dev_ctxt->mmio_info.mmio_addr, MHIREGLEN); if (0 == mhi_dev_ctxt->mmio_info.mmio_len) { - mhi_log(MHI_MSG_ERROR, "Received mmio length as zero\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Received mmio length as zero\n"); return -EIO; } - mhi_log(MHI_MSG_INFO, "Testing MHI Ver\n"); - mhi_dev_ctxt->dev_props->mhi_ver = mhi_reg_read( + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Testing MHI Ver\n"); + mhi_dev_ctxt->core.mhi_ver = mhi_reg_read( mhi_dev_ctxt->mmio_info.mmio_addr, MHIVER); - if (MHI_VERSION != mhi_dev_ctxt->dev_props->mhi_ver) { - mhi_log(MHI_MSG_CRITICAL, - "Bad MMIO version, 0x%x\n", - mhi_dev_ctxt->dev_props->mhi_ver); + if (mhi_dev_ctxt->core.mhi_ver != MHI_VERSION) { + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "Bad MMIO version, 0x%x\n", mhi_dev_ctxt->core.mhi_ver); return ret_val; } @@ -159,9 +143,10 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt) else chan_ctxt->chstate = MHI_CHAN_STATE_DISABLED; } - mhi_log(MHI_MSG_INFO, - "Read back MMIO Ready bit successfully. Moving on..\n"); - mhi_log(MHI_MSG_INFO, "Reading channel doorbell offset\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Read back MMIO Ready bit successfully. Moving on..\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Reading channel doorbell offset\n"); mhi_dev_ctxt->mmio_info.chan_db_addr = mhi_dev_ctxt->mmio_info.mmio_addr; @@ -173,13 +158,15 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt) CHDBOFF, CHDBOFF_CHDBOFF_MASK, CHDBOFF_CHDBOFF_SHIFT); - mhi_log(MHI_MSG_INFO, "Reading event doorbell offset\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Reading event doorbell offset\n"); mhi_dev_ctxt->mmio_info.event_db_addr += mhi_reg_read_field( mhi_dev_ctxt->mmio_info.mmio_addr, ERDBOFF, ERDBOFF_ERDBOFF_MASK, ERDBOFF_ERDBOFF_SHIFT); - mhi_log(MHI_MSG_INFO, "Setting all MMIO values.\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Setting all MMIO values.\n"); mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, MHICFG, @@ -290,7 +277,7 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt) MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK, MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT, pcie_word_val); - mhi_log(MHI_MSG_INFO, "Done..\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Done..\n"); return 0; } diff --git a/drivers/platform/msm/mhi/mhi_pm.c b/drivers/platform/msm/mhi/mhi_pm.c index 2f44601e225e..d7a4f7aa93ef 100644 --- a/drivers/platform/msm/mhi/mhi_pm.c +++ b/drivers/platform/msm/mhi/mhi_pm.c @@ -20,14 +20,17 @@ #include "mhi_sys.h" #include "mhi.h" #include "mhi_hwio.h" +#include "mhi_bhi.h" /* Write only sysfs attributes */ static DEVICE_ATTR(MHI_M0, S_IWUSR, NULL, sysfs_init_m0); +static DEVICE_ATTR(MHI_M3, S_IWUSR, NULL, sysfs_init_m3); /* Read only sysfs attributes */ static struct attribute *mhi_attributes[] = { &dev_attr_MHI_M0.attr, + &dev_attr_MHI_M3.attr, NULL, }; @@ -38,9 +41,9 @@ static struct attribute_group mhi_attribute_group = { int mhi_pci_suspend(struct device *dev) { int r = 0; + struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev); - mhi_log(MHI_MSG_INFO, "Entered\n"); - + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n"); /* if rpm status still active then force suspend */ if (!pm_runtime_status_suspended(dev)) { r = mhi_runtime_suspend(dev); @@ -51,86 +54,134 @@ int mhi_pci_suspend(struct device *dev) pm_runtime_set_suspended(dev); pm_runtime_disable(dev); - mhi_log(MHI_MSG_INFO, "Exit\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit\n"); return r; } -int mhi_runtime_suspend(struct device *dev) +static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt, + bool force_m3) { int r = 0; - struct mhi_device_ctxt *mhi_dev_ctxt = dev->platform_data; - mutex_lock(&mhi_dev_ctxt->pm_lock); read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); - - mhi_log(MHI_MSG_INFO, "Entered with State:0x%x %s\n", + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Entered with State:0x%x %s\n", mhi_dev_ctxt->mhi_pm_state, TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); /* Link is already disabled */ if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE || mhi_dev_ctxt->mhi_pm_state == MHI_PM_M3) { - mhi_log(MHI_MSG_INFO, "Already in active state, exiting\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Already in M3 State\n"); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); - mutex_unlock(&mhi_dev_ctxt->pm_lock); return 0; } - if (unlikely(atomic_read(&mhi_dev_ctxt->counters.device_wake))) { - mhi_log(MHI_MSG_INFO, "Busy, Aborting Runtime Suspend\n"); + if (unlikely(atomic_read(&mhi_dev_ctxt->counters.device_wake) && + force_m3 == false)){ + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Busy, Aborting M3\n"); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); - mutex_unlock(&mhi_dev_ctxt->pm_lock); return -EBUSY; } - mhi_assert_device_wake(mhi_dev_ctxt, false); + mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event, mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || mhi_dev_ctxt->mhi_state == MHI_STATE_M1, msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); if (!r) { - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Failed to get M0||M1 event, timeout, current state:%s\n", TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); - r = -EIO; - goto rpm_suspend_exit; + return -EIO; } - mhi_log(MHI_MSG_INFO, "Allowing M3 State\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Allowing M3 State\n"); write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); - mhi_deassert_device_wake(mhi_dev_ctxt); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); mhi_dev_ctxt->mhi_pm_state = MHI_PM_M3_ENTER; mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M3); write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); - mhi_log(MHI_MSG_INFO, - "Waiting for M3 completion.\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Waiting for M3 completion.\n"); r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m3_event, mhi_dev_ctxt->mhi_state == MHI_STATE_M3, msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT)); if (!r) { - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Failed to get M3 event, timeout, current state:%s\n", TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); - r = -EIO; - goto rpm_suspend_exit; + return -EIO; } + return 0; +} + +static int mhi_pm_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + int r; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Entered with State:0x%x %s\n", + mhi_dev_ctxt->mhi_pm_state, + TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); + + write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); + mhi_dev_ctxt->mhi_pm_state = MHI_PM_M3_EXIT; + write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); + + /* Set and wait for M0 Event */ + write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); + mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M0); + write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); + r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event, + mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || + mhi_dev_ctxt->mhi_state == MHI_STATE_M1, + msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); + if (!r) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to get M0 event, timeout\n"); + r = -EIO; + } else + r = 0; + + return r; +} + +int mhi_runtime_suspend(struct device *dev) +{ + int r = 0; + struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev); + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n"); + + mutex_lock(&mhi_dev_ctxt->pm_lock); + r = mhi_pm_initiate_m3(mhi_dev_ctxt, false); + if (r) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "abort due to ret:%d\n", r); + mutex_unlock(&mhi_dev_ctxt->pm_lock); + return r; + } r = mhi_turn_off_pcie_link(mhi_dev_ctxt); if (r) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to Turn off link ret:%d\n", r); } -rpm_suspend_exit: - mhi_log(MHI_MSG_INFO, "Exited\n"); mutex_unlock(&mhi_dev_ctxt->pm_lock); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited with ret:%d\n", r); + return r; } int mhi_runtime_idle(struct device *dev) { - mhi_log(MHI_MSG_INFO, "Entered returning -EBUSY\n"); + struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev); + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Entered returning -EBUSY\n"); /* * RPM framework during runtime resume always calls @@ -150,7 +201,7 @@ int mhi_runtime_idle(struct device *dev) int mhi_runtime_resume(struct device *dev) { int r = 0; - struct mhi_device_ctxt *mhi_dev_ctxt = dev->platform_data; + struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev); mutex_lock(&mhi_dev_ctxt->pm_lock); read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); @@ -159,45 +210,24 @@ int mhi_runtime_resume(struct device *dev) /* turn on link */ r = mhi_turn_on_pcie_link(mhi_dev_ctxt); - if (r) { - mhi_log(MHI_MSG_CRITICAL, - "Failed to resume link\n"); - goto rpm_resume_exit; - } - - write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); - mhi_dev_ctxt->mhi_pm_state = MHI_PM_M3_EXIT; - write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); - - /* Set and wait for M0 Event */ - write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); - mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M0); - write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); - r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event, - mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || - mhi_dev_ctxt->mhi_state == MHI_STATE_M1, - msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); - if (!r) { - mhi_log(MHI_MSG_ERROR, - "Failed to get M0 event, timeout\n"); - r = -EIO; + if (r) goto rpm_resume_exit; - } - r = 0; /* no errors */ + r = mhi_pm_initiate_m0(mhi_dev_ctxt); rpm_resume_exit: mutex_unlock(&mhi_dev_ctxt->pm_lock); - mhi_log(MHI_MSG_INFO, "Exited with :%d\n", r); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited with :%d\n", r); return r; } int mhi_pci_resume(struct device *dev) { int r = 0; + struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev); r = mhi_runtime_resume(dev); if (r) { - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Failed to resume link\n"); } else { pm_runtime_set_active(dev); @@ -207,6 +237,97 @@ int mhi_pci_resume(struct device *dev) return r; } +static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + int ret_val; + u32 timeout = mhi_dev_ctxt->poll_reset_timeout_ms; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n"); + mutex_lock(&mhi_dev_ctxt->pm_lock); + write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); + mhi_dev_ctxt->mhi_pm_state = MHI_PM_POR; + ret_val = set_mhi_base_state(mhi_dev_ctxt); + write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); + + if (ret_val) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Error Setting MHI Base State %d\n", ret_val); + goto unlock_pm_lock; + } + + if (mhi_dev_ctxt->base_state != STATE_TRANSITION_BHI) { + ret_val = -EIO; + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Invalid Base State, cur_state:%s\n", + state_transition_str(mhi_dev_ctxt->base_state)); + goto unlock_pm_lock; + } + + reinit_completion(&mhi_dev_ctxt->cmd_complete); + init_mhi_base_state(mhi_dev_ctxt); + + /* + * Keep wake in Active until AMSS, @ AMSS we will + * decrement counts + */ + read_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); + mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false); + read_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); + + ret_val = wait_for_completion_timeout(&mhi_dev_ctxt->cmd_complete, + msecs_to_jiffies(timeout)); + if (!ret_val || mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_AMSS) + ret_val = -EIO; + else + ret_val = 0; + + if (ret_val) { + read_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); + read_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); + } + +unlock_pm_lock: + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", ret_val); + mutex_unlock(&mhi_dev_ctxt->pm_lock); + return ret_val; +} + +static int mhi_pm_slave_mode_suspend(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + int r; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n"); + mutex_lock(&mhi_dev_ctxt->pm_lock); + + r = mhi_pm_initiate_m3(mhi_dev_ctxt, false); + if (r) + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "abort due to ret:%d\n", r); + mutex_unlock(&mhi_dev_ctxt->pm_lock); + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", r); + + return r; +} + +static int mhi_pm_slave_mode_resume(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + int r; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n"); + mutex_lock(&mhi_dev_ctxt->pm_lock); + + r = mhi_pm_initiate_m0(mhi_dev_ctxt); + if (r) + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "M3 exit failed ret:%d\n", r); + mutex_unlock(&mhi_dev_ctxt->pm_lock); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", r); + + return r; +} + int mhi_init_pm_sysfs(struct device *dev) { return sysfs_create_group(&dev->kobj, &mhi_attribute_group); @@ -220,12 +341,29 @@ void mhi_rem_pm_sysfs(struct device *dev) ssize_t sysfs_init_m0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct mhi_device_ctxt *mhi_dev_ctxt = - &mhi_devices.device_list[0].mhi_ctxt; + struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev); + + pm_runtime_get(&mhi_dev_ctxt->pcie_device->dev); + pm_runtime_mark_last_busy(&mhi_dev_ctxt->pcie_device->dev); + pm_runtime_put_noidle(&mhi_dev_ctxt->pcie_device->dev); + + return count; +} + +ssize_t sysfs_init_m3(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev); + + if (atomic_read(&mhi_dev_ctxt->counters.device_wake) == 0) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Schedule RPM suspend"); + pm_runtime_mark_last_busy(&mhi_dev_ctxt-> + pcie_device->dev); + pm_request_autosuspend(&mhi_dev_ctxt-> + pcie_device->dev); + } - pm_runtime_get(&mhi_dev_ctxt->dev_info->pcie_device->dev); - pm_runtime_mark_last_busy(&mhi_dev_ctxt->dev_info->pcie_device->dev); - pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->pcie_device->dev); return count; } @@ -234,45 +372,45 @@ int mhi_turn_off_pcie_link(struct mhi_device_ctxt *mhi_dev_ctxt) struct pci_dev *pcie_dev; int r = 0; - mhi_log(MHI_MSG_INFO, "Entered...\n"); - pcie_dev = mhi_dev_ctxt->dev_info->pcie_device; + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered...\n"); + pcie_dev = mhi_dev_ctxt->pcie_device; if (0 == mhi_dev_ctxt->flags.link_up) { - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Link already marked as down, nothing to do\n"); goto exit; } r = pci_save_state(pcie_dev); if (r) { - mhi_log(MHI_MSG_CRITICAL, - "Failed to save pcie state ret: %d\n", - r); + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "Failed to save pcie state ret: %d\n", r); } - mhi_dev_ctxt->dev_props->pcie_state = pci_store_saved_state(pcie_dev); + mhi_dev_ctxt->core.pcie_state = pci_store_saved_state(pcie_dev); pci_disable_device(pcie_dev); r = pci_set_power_state(pcie_dev, PCI_D3hot); if (r) { - mhi_log(MHI_MSG_CRITICAL, - "Failed to set pcie power state to D3 hot ret: %d\n", - r); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Failed to set pcie power state to D3hot ret:%d\n", r); } r = msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcie_dev->bus->number, pcie_dev, - NULL, - 0); + NULL, + 0); if (r) - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Failed to suspend pcie bus ret 0x%x\n", r); r = mhi_set_bus_request(mhi_dev_ctxt, 0); if (r) - mhi_log(MHI_MSG_INFO, "Failed to set bus freq ret %d\n", r); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Failed to set bus freq ret %d\n", r); mhi_dev_ctxt->flags.link_up = 0; exit: - mhi_log(MHI_MSG_INFO, "Exited...\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited...\n"); + return 0; } @@ -281,17 +419,16 @@ int mhi_turn_on_pcie_link(struct mhi_device_ctxt *mhi_dev_ctxt) int r = 0; struct pci_dev *pcie_dev; - pcie_dev = mhi_dev_ctxt->dev_info->pcie_device; + pcie_dev = mhi_dev_ctxt->pcie_device; - mhi_log(MHI_MSG_INFO, "Entered...\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered...\n"); if (mhi_dev_ctxt->flags.link_up) goto exit; r = mhi_set_bus_request(mhi_dev_ctxt, 1); if (r) - mhi_log(MHI_MSG_CRITICAL, - "Could not set bus frequency ret: %d\n", - r); + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "Could not set bus frequency ret: %d\n", r); r = msm_pcie_pm_control(MSM_PCIE_RESUME, pcie_dev->bus->number, @@ -299,24 +436,50 @@ int mhi_turn_on_pcie_link(struct mhi_device_ctxt *mhi_dev_ctxt) NULL, 0); if (r) { - mhi_log(MHI_MSG_CRITICAL, - "Failed to resume pcie bus ret %d\n", r); + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "Failed to resume pcie bus ret %d\n", r); goto exit; } r = pci_enable_device(pcie_dev); if (r) - mhi_log(MHI_MSG_CRITICAL, - "Failed to enable device ret:%d\n", - r); + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "Failed to enable device ret:%d\n", r); pci_load_and_free_saved_state(pcie_dev, - &mhi_dev_ctxt->dev_props->pcie_state); + &mhi_dev_ctxt->core.pcie_state); pci_restore_state(pcie_dev); pci_set_master(pcie_dev); mhi_dev_ctxt->flags.link_up = 1; exit: - mhi_log(MHI_MSG_INFO, "Exited...\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited...\n"); return r; } + +int mhi_pm_control_device(struct mhi_device *mhi_device, + enum mhi_dev_ctrl ctrl) +{ + struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->mhi_dev_ctxt; + + if (!mhi_dev_ctxt) + return -EINVAL; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Entered with cmd:%d\n", ctrl); + + switch (ctrl) { + case MHI_DEV_CTRL_INIT: + return bhi_probe(mhi_dev_ctxt); + case MHI_DEV_CTRL_POWER_ON: + return mhi_pm_slave_mode_power_on(mhi_dev_ctxt); + case MHI_DEV_CTRL_SUSPEND: + return mhi_pm_slave_mode_suspend(mhi_dev_ctxt); + case MHI_DEV_CTRL_RESUME: + return mhi_pm_slave_mode_resume(mhi_dev_ctxt); + default: + break; + } + return -EINVAL; +} +EXPORT_SYMBOL(mhi_pm_control_device); diff --git a/drivers/platform/msm/mhi/mhi_ring_ops.c b/drivers/platform/msm/mhi/mhi_ring_ops.c index 07d0098a1b61..e15055f7db9c 100644 --- a/drivers/platform/msm/mhi/mhi_ring_ops.c +++ b/drivers/platform/msm/mhi/mhi_ring_ops.c @@ -21,7 +21,6 @@ static int add_element(struct mhi_ring *ring, void **rp, if (NULL == ring || 0 == ring->el_size || NULL == ring->base || 0 == ring->len) { - mhi_log(MHI_MSG_ERROR, "Bad input parameters, quitting.\n"); return -EINVAL; } @@ -39,8 +38,6 @@ static int add_element(struct mhi_ring *ring, void **rp, if (ring->overwrite_en) { ctxt_del_element(ring, NULL); } else { - mhi_log(MHI_MSG_INFO, "Ring 0x%lX is full\n", - (uintptr_t)ring->base); return -ENOSPC; } } @@ -92,8 +89,6 @@ int delete_element(struct mhi_ring *ring, void **rp, if (r) return r; if (d_wp == d_rp) { - mhi_log(MHI_MSG_VERBOSE, "Ring 0x%lx is empty\n", - (uintptr_t)ring->base); if (NULL != assigned_addr) *assigned_addr = NULL; return -ENODATA; @@ -113,23 +108,26 @@ int delete_element(struct mhi_ring *ring, void **rp, int mhi_get_free_desc(struct mhi_client_handle *client_handle) { u32 chan; + struct mhi_client_config *client_config; struct mhi_device_ctxt *ctxt; int bb_ring, ch_ring; - if (!client_handle || MHI_HANDLE_MAGIC != client_handle->magic || - !client_handle->mhi_dev_ctxt) + if (!client_handle) return -EINVAL; - ctxt = client_handle->mhi_dev_ctxt; - chan = client_handle->chan_info.chan_nr; + client_config = client_handle->client_config; + ctxt = client_config->mhi_dev_ctxt; + chan = client_config->chan_info.chan_nr; - bb_ring = get_nr_avail_ring_elements(&ctxt->chan_bb_list[chan]); - ch_ring = get_nr_avail_ring_elements(&ctxt->mhi_local_chan_ctxt[chan]); + bb_ring = get_nr_avail_ring_elements(ctxt, &ctxt->chan_bb_list[chan]); + ch_ring = get_nr_avail_ring_elements(ctxt, + &ctxt->mhi_local_chan_ctxt[chan]); return min(bb_ring, ch_ring); } EXPORT_SYMBOL(mhi_get_free_desc); -int get_nr_avail_ring_elements(struct mhi_ring *ring) +int get_nr_avail_ring_elements(struct mhi_device_ctxt *mhi_dev_ctxt, + struct mhi_ring *ring) { u32 nr_el = 0; uintptr_t ring_size = 0; @@ -138,7 +136,7 @@ int get_nr_avail_ring_elements(struct mhi_ring *ring) ring_size = ring->len / ring->el_size; ret_val = get_nr_enclosed_el(ring, ring->rp, ring->wp, &nr_el); if (ret_val != 0) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to get enclosed el ret %d.\n", ret_val); return 0; } @@ -155,19 +153,14 @@ int get_nr_enclosed_el(struct mhi_ring *ring, void *rp, if (NULL == ring || 0 == ring->el_size || NULL == ring->base || 0 == ring->len) { - mhi_log(MHI_MSG_ERROR, "Bad input parameters, quitting.\n"); return -EINVAL; } r = get_element_index(ring, rp, &index_rp); - if (r) { - mhi_log(MHI_MSG_CRITICAL, "Bad element index rp 0x%p.\n", rp); + if (r) return r; - } r = get_element_index(ring, wp, &index_wp); - if (r) { - mhi_log(MHI_MSG_CRITICAL, "Bad element index wp 0x%p.\n", wp); + if (r) return r; - } ring_size = ring->len / ring->el_size; if (index_rp < index_wp) diff --git a/drivers/platform/msm/mhi/mhi_ssr.c b/drivers/platform/msm/mhi/mhi_ssr.c index defd6f4fd137..22481dede21a 100644 --- a/drivers/platform/msm/mhi/mhi_ssr.c +++ b/drivers/platform/msm/mhi/mhi_ssr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -24,40 +24,35 @@ static int mhi_ssr_notify_cb(struct notifier_block *nb, unsigned long action, void *data) { - + struct mhi_device_ctxt *mhi_dev_ctxt = + container_of(nb, struct mhi_device_ctxt, mhi_ssr_nb); switch (action) { case SUBSYS_BEFORE_POWERUP: - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Received Subsystem event BEFORE_POWERUP\n"); break; case SUBSYS_AFTER_POWERUP: - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Received Subsystem event AFTER_POWERUP\n"); break; case SUBSYS_POWERUP_FAILURE: - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Received Subsystem event POWERUP_FAILURE\n"); break; case SUBSYS_BEFORE_SHUTDOWN: - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Received Subsystem event BEFORE_SHUTDOWN\n"); - mhi_log(MHI_MSG_INFO, - "Not notifying clients\n"); break; case SUBSYS_AFTER_SHUTDOWN: - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Received Subsystem event AFTER_SHUTDOWN\n"); - mhi_log(MHI_MSG_INFO, - "Not notifying clients\n"); break; case SUBSYS_RAMDUMP_NOTIFICATION: - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Received Subsystem event RAMDUMP\n"); - mhi_log(MHI_MSG_INFO, - "Not notifying clients\n"); break; default: - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Received ESOC notifcation %d, NOT handling\n", (int)action); break; @@ -65,36 +60,30 @@ static int mhi_ssr_notify_cb(struct notifier_block *nb, return NOTIFY_OK; } -static struct notifier_block mhi_ssr_nb = { - .notifier_call = mhi_ssr_notify_cb, -}; - int mhi_esoc_register(struct mhi_device_ctxt *mhi_dev_ctxt) { int ret_val = 0; struct device_node *np; - struct pci_driver *mhi_driver; - struct device *dev = &mhi_dev_ctxt->dev_info->pcie_device->dev; + struct device *dev = &mhi_dev_ctxt->pcie_device->dev; - mhi_driver = mhi_dev_ctxt->dev_info->mhi_pcie_driver; np = dev->of_node; mhi_dev_ctxt->esoc_handle = devm_register_esoc_client(dev, "mdm"); - mhi_log(MHI_MSG_VERBOSE, + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Of table of pcie struct device property is dev->of_node %p\n", np); if (IS_ERR_OR_NULL(mhi_dev_ctxt->esoc_handle)) { - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Failed to register for SSR, ret %lx\n", (uintptr_t)mhi_dev_ctxt->esoc_handle); return -EIO; } - + mhi_dev_ctxt->mhi_ssr_nb.notifier_call = mhi_ssr_notify_cb; mhi_dev_ctxt->esoc_ssr_handle = subsys_notif_register_notifier( mhi_dev_ctxt->esoc_handle->name, - &mhi_ssr_nb); + &mhi_dev_ctxt->mhi_ssr_nb); if (IS_ERR_OR_NULL(mhi_dev_ctxt->esoc_ssr_handle)) { ret_val = PTR_RET(mhi_dev_ctxt->esoc_ssr_handle); - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Can't find esoc desc ret 0x%lx\n", (uintptr_t)mhi_dev_ctxt->esoc_ssr_handle); } @@ -107,18 +96,25 @@ void mhi_notify_client(struct mhi_client_handle *client_handle, { struct mhi_cb_info cb_info = {0}; struct mhi_result result = {0}; + struct mhi_client_config *client_config; cb_info.result = NULL; cb_info.cb_reason = reason; - if (NULL != client_handle && - NULL != client_handle->client_info.mhi_client_cb) { - result.user_data = client_handle->user_data; - cb_info.chan = client_handle->chan_info.chan_nr; + if (client_handle == NULL) + return; + + client_config = client_handle->client_config; + + if (client_config->client_info.mhi_client_cb) { + result.user_data = client_config->user_data; + cb_info.chan = client_config->chan_info.chan_nr; cb_info.result = &result; - mhi_log(MHI_MSG_INFO, "Calling back for chan %d, reason %d\n", - cb_info.chan, reason); - client_handle->client_info.mhi_client_cb(&cb_info); + mhi_log(client_config->mhi_dev_ctxt, MHI_MSG_INFO, + "Calling back for chan %d, reason %d\n", + cb_info.chan, + reason); + client_config->client_info.mhi_client_cb(&cb_info); } } @@ -136,16 +132,22 @@ void mhi_notify_clients(struct mhi_device_ctxt *mhi_dev_ctxt, } } -int set_mhi_base_state(struct mhi_pcie_dev_info *mhi_pcie_dev) +int set_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt) { u32 pcie_word_val = 0; int r = 0; - struct mhi_device_ctxt *mhi_dev_ctxt = &mhi_pcie_dev->mhi_ctxt; - mhi_pcie_dev->bhi_ctxt.bhi_base = mhi_pcie_dev->core.bar0_base; - pcie_word_val = mhi_reg_read(mhi_pcie_dev->bhi_ctxt.bhi_base, BHIOFF); - mhi_pcie_dev->bhi_ctxt.bhi_base += pcie_word_val; - pcie_word_val = mhi_reg_read(mhi_pcie_dev->bhi_ctxt.bhi_base, + mhi_dev_ctxt->bhi_ctxt.bhi_base = mhi_dev_ctxt->core.bar0_base; + pcie_word_val = mhi_reg_read(mhi_dev_ctxt->bhi_ctxt.bhi_base, BHIOFF); + + /* confirm it's a valid reading */ + if (unlikely(pcie_word_val == U32_MAX)) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Invalid BHI Offset:0x%x\n", pcie_word_val); + return -EIO; + } + mhi_dev_ctxt->bhi_ctxt.bhi_base += pcie_word_val; + pcie_word_val = mhi_reg_read(mhi_dev_ctxt->bhi_ctxt.bhi_base, BHI_EXECENV); mhi_dev_ctxt->dev_exec_env = pcie_word_val; if (pcie_word_val == MHI_EXEC_ENV_AMSS) { @@ -153,55 +155,50 @@ int set_mhi_base_state(struct mhi_pcie_dev_info *mhi_pcie_dev) } else if (pcie_word_val == MHI_EXEC_ENV_PBL) { mhi_dev_ctxt->base_state = STATE_TRANSITION_BHI; } else { - mhi_log(MHI_MSG_ERROR, "Invalid EXEC_ENV: 0x%x\n", + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Invalid EXEC_ENV: 0x%x\n", pcie_word_val); r = -EIO; } - mhi_log(MHI_MSG_INFO, "EXEC_ENV: %d Base state %d\n", + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "EXEC_ENV: %d Base state %d\n", pcie_word_val, mhi_dev_ctxt->base_state); return r; } void mhi_link_state_cb(struct msm_pcie_notify *notify) { - - struct mhi_pcie_dev_info *mhi_pcie_dev; struct mhi_device_ctxt *mhi_dev_ctxt = NULL; - if (NULL == notify || NULL == notify->data) { - mhi_log(MHI_MSG_CRITICAL, - "Incomplete handle received\n"); + if (!notify || !notify->data) { + pr_err("%s: incomplete handle received\n", __func__); return; } - mhi_pcie_dev = notify->data; - mhi_dev_ctxt = &mhi_pcie_dev->mhi_ctxt; + mhi_dev_ctxt = notify->data; switch (notify->event) { case MSM_PCIE_EVENT_LINKDOWN: - mhi_log(MHI_MSG_INFO, "Received MSM_PCIE_EVENT_LINKDOWN\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Received MSM_PCIE_EVENT_LINKDOWN\n"); break; case MSM_PCIE_EVENT_LINKUP: - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Received MSM_PCIE_EVENT_LINKUP\n"); - mhi_pcie_dev->link_up_cntr++; + mhi_dev_ctxt->counters.link_up_cntr++; break; case MSM_PCIE_EVENT_WAKEUP: - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Received MSM_PCIE_EVENT_WAKE\n"); __pm_stay_awake(&mhi_dev_ctxt->w_lock); __pm_relax(&mhi_dev_ctxt->w_lock); if (mhi_dev_ctxt->flags.mhi_initialized) { - pm_runtime_get(&mhi_dev_ctxt-> - dev_info->pcie_device->dev); - pm_runtime_mark_last_busy(&mhi_dev_ctxt-> - dev_info->pcie_device->dev); - pm_runtime_put_noidle(&mhi_dev_ctxt-> - dev_info->pcie_device->dev); + mhi_dev_ctxt->runtime_get(mhi_dev_ctxt); + mhi_dev_ctxt->runtime_put(mhi_dev_ctxt); } break; default: - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Received bad link event\n"); return; } @@ -213,9 +210,9 @@ int init_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt) r = mhi_init_state_transition(mhi_dev_ctxt, mhi_dev_ctxt->base_state); if (r) { - mhi_log(MHI_MSG_CRITICAL, - "Failed to start state change event, to %d\n", - mhi_dev_ctxt->base_state); + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, + "Failed to start state change event, to %d\n", + mhi_dev_ctxt->base_state); } return r; } diff --git a/drivers/platform/msm/mhi/mhi_states.c b/drivers/platform/msm/mhi/mhi_states.c index 1021a56d1b3d..a4da6c21b50d 100644 --- a/drivers/platform/msm/mhi/mhi_states.c +++ b/drivers/platform/msm/mhi/mhi_states.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -19,24 +19,24 @@ const char *state_transition_str(enum STATE_TRANSITION state) { - static const char * const mhi_states_transition_str[] = { - "RESET", - "READY", - "M0", - "M1", - "M2", - "M3", - "BHI", - "SBL", - "AMSS", - "LINK_DOWN", - "WAKE" + static const char * const + mhi_states_transition_str[STATE_TRANSITION_MAX] = { + [STATE_TRANSITION_RESET] = "RESET", + [STATE_TRANSITION_READY] = "READY", + [STATE_TRANSITION_M0] = "M0", + [STATE_TRANSITION_M1] = "M1", + [STATE_TRANSITION_M2] = "M2", + [STATE_TRANSITION_M3] = "M3", + [STATE_TRANSITION_BHI] = "BHI", + [STATE_TRANSITION_SBL] = "SBL", + [STATE_TRANSITION_AMSS] = "AMSS", + [STATE_TRANSITION_LINK_DOWN] = "LINK_DOWN", + [STATE_TRANSITION_WAKE] = "WAKE", + [STATE_TRANSITION_BHIE] = "BHIE", + [STATE_TRANSITION_SYS_ERR] = "SYS_ERR", }; - if (state == STATE_TRANSITION_SYS_ERR) - return "SYS_ERR"; - - return (state <= STATE_TRANSITION_WAKE) ? + return (state < STATE_TRANSITION_MAX) ? mhi_states_transition_str[state] : "Invalid"; } @@ -94,7 +94,7 @@ static void ring_all_chan_dbs(struct mhi_device_ctxt *mhi_dev_ctxt, u32 i = 0; struct mhi_ring *local_ctxt = NULL; - mhi_log(MHI_MSG_VERBOSE, "Ringing chan dbs\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Ringing chan dbs\n"); for (i = 0; i < MHI_MAX_CHANNELS; ++i) if (VALID_CHAN_NR(i)) { local_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[i]; @@ -115,7 +115,7 @@ static void ring_all_cmd_dbs(struct mhi_device_ctxt *mhi_dev_ctxt) u64 rp = 0; struct mhi_ring *local_ctxt = NULL; - mhi_log(MHI_MSG_VERBOSE, "Ringing chan dbs\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Ringing chan dbs\n"); local_ctxt = &mhi_dev_ctxt->mhi_local_cmd_ctxt[PRIMARY_CMD_RING]; rp = mhi_v2p_addr(mhi_dev_ctxt, MHI_RING_TYPE_CMD_RING, @@ -158,12 +158,23 @@ static void ring_all_ev_dbs(struct mhi_device_ctxt *mhi_dev_ctxt) } } -static int process_m0_transition( - struct mhi_device_ctxt *mhi_dev_ctxt, - enum STATE_TRANSITION cur_work_item) +static int process_bhie_transition(struct mhi_device_ctxt *mhi_dev_ctxt, + enum STATE_TRANSITION cur_work_item) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n"); + mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_BHIE; + wake_up(mhi_dev_ctxt->mhi_ev_wq.bhi_event); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n"); + + return 0; +} - mhi_log(MHI_MSG_INFO, "Entered With State %s\n", +int process_m0_transition(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + unsigned long flags; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Entered With State %s\n", TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); switch (mhi_dev_ctxt->mhi_state) { @@ -177,12 +188,12 @@ static int process_m0_transition( break; } - write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); + write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags); mhi_dev_ctxt->mhi_state = MHI_STATE_M0; mhi_dev_ctxt->mhi_pm_state = MHI_PM_M0; - write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); + write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags); read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); - mhi_assert_device_wake(mhi_dev_ctxt, true); + mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, true); if (mhi_dev_ctxt->flags.mhi_initialized) { ring_all_ev_dbs(mhi_dev_ctxt); @@ -190,10 +201,11 @@ static int process_m0_transition( ring_all_cmd_dbs(mhi_dev_ctxt); } - mhi_deassert_device_wake(mhi_dev_ctxt); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); wake_up(mhi_dev_ctxt->mhi_ev_wq.m0_event); - mhi_log(MHI_MSG_INFO, "Exited\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n"); + return 0; } @@ -207,7 +219,7 @@ void process_m1_transition(struct work_struct *work) mutex_lock(&mhi_dev_ctxt->pm_lock); write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Processing M1 state transition from state %s\n", TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); @@ -218,7 +230,8 @@ void process_m1_transition(struct work_struct *work) return; } - mhi_log(MHI_MSG_INFO, "Transitioning to M2 Transition\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Transitioning to M2 Transition\n"); mhi_dev_ctxt->mhi_pm_state = MHI_PM_M1_M2_TRANSITION; mhi_dev_ctxt->counters.m1_m2++; mhi_dev_ctxt->mhi_state = MHI_STATE_M2; @@ -230,34 +243,32 @@ void process_m1_transition(struct work_struct *work) /* During DEBOUNCE Time We could be receiving M0 Event */ if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_M1_M2_TRANSITION) { - mhi_log(MHI_MSG_INFO, "Entered M2 State\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Entered M2 State\n"); mhi_dev_ctxt->mhi_pm_state = MHI_PM_M2; } write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); if (unlikely(atomic_read(&mhi_dev_ctxt->counters.device_wake))) { - mhi_log(MHI_MSG_INFO, "Exiting M2 Immediately, count:%d\n", + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Exiting M2 Immediately, count:%d\n", atomic_read(&mhi_dev_ctxt->counters.device_wake)); read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); - mhi_assert_device_wake(mhi_dev_ctxt, true); - mhi_deassert_device_wake(mhi_dev_ctxt); + mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, true); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); - } else { - mhi_log(MHI_MSG_INFO, "Schedule RPM suspend"); - pm_runtime_mark_last_busy(&mhi_dev_ctxt-> - dev_info->pcie_device->dev); - pm_request_autosuspend(&mhi_dev_ctxt-> - dev_info->pcie_device->dev); + } else if (mhi_dev_ctxt->core.pci_master) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Schedule RPM suspend"); + pm_runtime_mark_last_busy(&mhi_dev_ctxt->pcie_device->dev); + pm_request_autosuspend(&mhi_dev_ctxt->pcie_device->dev); } mutex_unlock(&mhi_dev_ctxt->pm_lock); } -static int process_m3_transition( - struct mhi_device_ctxt *mhi_dev_ctxt, - enum STATE_TRANSITION cur_work_item) +int process_m3_transition(struct mhi_device_ctxt *mhi_dev_ctxt) { - - mhi_log(MHI_MSG_INFO, + unsigned long flags; + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered with State %s\n", TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); @@ -272,45 +283,24 @@ static int process_m3_transition( break; } - write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); + write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags); mhi_dev_ctxt->mhi_state = MHI_STATE_M3; mhi_dev_ctxt->mhi_pm_state = MHI_PM_M3; - write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); + write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags); wake_up(mhi_dev_ctxt->mhi_ev_wq.m3_event); return 0; } -static int process_link_down_transition( - struct mhi_device_ctxt *mhi_dev_ctxt, - enum STATE_TRANSITION cur_work_item) -{ - mhi_log(MHI_MSG_INFO, - "Entered with State %s\n", - TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); - return -EIO; -} - -static int process_wake_transition( - struct mhi_device_ctxt *mhi_dev_ctxt, - enum STATE_TRANSITION cur_work_item) -{ - mhi_log(MHI_MSG_INFO, - "Entered with State %s\n", - TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); - return -EIO; - -} - static int process_bhi_transition( struct mhi_device_ctxt *mhi_dev_ctxt, enum STATE_TRANSITION cur_work_item) { - mhi_log(MHI_MSG_INFO, "Entered\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n"); write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); mhi_dev_ctxt->mhi_state = MHI_STATE_BHI; write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.bhi_event); - mhi_log(MHI_MSG_INFO, "Exited\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n"); return 0; } @@ -320,11 +310,12 @@ static int process_ready_transition( { int r = 0; - mhi_log(MHI_MSG_INFO, "Processing READY state transition\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Processing READY state transition\n"); r = mhi_reset_all_thread_queues(mhi_dev_ctxt); if (r) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to reset thread queues\n"); return r; } @@ -335,7 +326,7 @@ static int process_ready_transition( write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); /* Initialize MMIO */ if (r) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failure during MMIO initialization\n"); return r; } @@ -344,13 +335,12 @@ static int process_ready_transition( cur_work_item); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); if (r) { - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failure during event ring init\n"); return r; } write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); - mhi_dev_ctxt->flags.stop_threads = 0; mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr, MHICTRL, MHICTRL_MHISTATE_MASK, @@ -379,7 +369,8 @@ static int process_reset_transition( enum STATE_TRANSITION cur_work_item) { int r = 0, i = 0; - mhi_log(MHI_MSG_INFO, "Processing RESET state transition\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Processing RESET state transition\n"); write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); mhi_dev_ctxt->mhi_state = MHI_STATE_RESET; write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); @@ -387,11 +378,12 @@ static int process_reset_transition( mhi_dev_ctxt->counters.mhi_reset_cntr++; r = mhi_test_for_device_reset(mhi_dev_ctxt); if (r) - mhi_log(MHI_MSG_INFO, "Device not RESET ret %d\n", r); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Device not RESET ret %d\n", r); r = mhi_test_for_device_ready(mhi_dev_ctxt); if (r) { - mhi_log(MHI_MSG_ERROR, "timed out waiting for ready ret:%d\n", - r); + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "timed out waiting for ready ret:%d\n", r); return r; } @@ -417,22 +409,12 @@ static int process_reset_transition( r = mhi_init_state_transition(mhi_dev_ctxt, STATE_TRANSITION_READY); if (0 != r) - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Failed to initiate %s state trans\n", state_transition_str(STATE_TRANSITION_READY)); return r; } -static int process_syserr_transition( - struct mhi_device_ctxt *mhi_dev_ctxt, - enum STATE_TRANSITION cur_work_item) -{ - mhi_log(MHI_MSG_INFO, - "Entered with State %s\n", - TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); - return -EIO; -} - static void enable_clients(struct mhi_device_ctxt *mhi_dev_ctxt, enum MHI_EXEC_ENV exec_env) { @@ -443,7 +425,8 @@ static void enable_clients(struct mhi_device_ctxt *mhi_dev_ctxt, cb_info.cb_reason = MHI_CB_MHI_ENABLED; - mhi_log(MHI_MSG_INFO, "Enabling Clients, exec env %d.\n", exec_env); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Enabling Clients, exec env %d.\n", exec_env); for (i = 0; i < MHI_MAX_CHANNELS; ++i) { if (!VALID_CHAN_NR(i)) continue; @@ -455,14 +438,15 @@ static void enable_clients(struct mhi_device_ctxt *mhi_dev_ctxt, mhi_notify_client(client_handle, MHI_CB_MHI_ENABLED); } - mhi_log(MHI_MSG_INFO, "Done.\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Done.\n"); } static int process_sbl_transition( struct mhi_device_ctxt *mhi_dev_ctxt, enum STATE_TRANSITION cur_work_item) { - mhi_log(MHI_MSG_INFO, "Enabled\n"); + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enabled\n"); write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_SBL; write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); @@ -476,7 +460,8 @@ static int process_amss_transition( { int r = 0; - mhi_log(MHI_MSG_INFO, "Processing AMSS state transition\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Processing AMSS state transition\n"); write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_AMSS; write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); @@ -486,41 +471,40 @@ static int process_amss_transition( cur_work_item); mhi_dev_ctxt->flags.mhi_initialized = 1; if (r) { - mhi_log(MHI_MSG_CRITICAL, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Failed to set local chan state ret %d\n", r); - mhi_deassert_device_wake(mhi_dev_ctxt); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); return r; } - read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); - ring_all_chan_dbs(mhi_dev_ctxt, true); - read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); - mhi_log(MHI_MSG_INFO, + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Notifying clients that MHI is enabled\n"); enable_clients(mhi_dev_ctxt, mhi_dev_ctxt->dev_exec_env); } else { - mhi_log(MHI_MSG_INFO, "MHI is initialized\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "MHI is initialized\n"); } - read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); - ring_all_ev_dbs(mhi_dev_ctxt); - read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); + complete(&mhi_dev_ctxt->cmd_complete); /* * runtime_allow will decrement usage_count, counts were * incremented by pci fw pci_pm_init() or by * mhi shutdown/ssr apis. */ - mhi_log(MHI_MSG_INFO, "Allow runtime suspend\n"); + if (mhi_dev_ctxt->core.pci_master) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Allow runtime suspend\n"); - pm_runtime_mark_last_busy(&mhi_dev_ctxt->dev_info->pcie_device->dev); - pm_runtime_allow(&mhi_dev_ctxt->dev_info->pcie_device->dev); + pm_runtime_mark_last_busy(&mhi_dev_ctxt->pcie_device->dev); + pm_runtime_allow(&mhi_dev_ctxt->pcie_device->dev); + } /* During probe we incremented, releasing that count */ read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); - mhi_deassert_device_wake(mhi_dev_ctxt); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); - mhi_log(MHI_MSG_INFO, "Exited\n"); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n"); return 0; } @@ -530,7 +514,8 @@ static int process_stt_work_item( { int r = 0; - mhi_log(MHI_MSG_INFO, "Transitioning to %s\n", + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Transitioning to %s\n", state_transition_str(cur_work_item)); trace_mhi_state(cur_work_item); switch (cur_work_item) { @@ -549,25 +534,11 @@ static int process_stt_work_item( case STATE_TRANSITION_AMSS: r = process_amss_transition(mhi_dev_ctxt, cur_work_item); break; - case STATE_TRANSITION_M0: - r = process_m0_transition(mhi_dev_ctxt, cur_work_item); - break; - case STATE_TRANSITION_M3: - r = process_m3_transition(mhi_dev_ctxt, cur_work_item); - break; - case STATE_TRANSITION_SYS_ERR: - r = process_syserr_transition(mhi_dev_ctxt, - cur_work_item); - break; - case STATE_TRANSITION_LINK_DOWN: - r = process_link_down_transition(mhi_dev_ctxt, - cur_work_item); - break; - case STATE_TRANSITION_WAKE: - r = process_wake_transition(mhi_dev_ctxt, cur_work_item); + case STATE_TRANSITION_BHIE: + r = process_bhie_transition(mhi_dev_ctxt, cur_work_item); break; default: - mhi_log(MHI_MSG_ERROR, + mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL, "Unrecongized state: %s\n", state_transition_str(cur_work_item)); break; @@ -575,46 +546,26 @@ static int process_stt_work_item( return r; } -int mhi_state_change_thread(void *ctxt) +void mhi_state_change_worker(struct work_struct *work) { int r = 0; - unsigned long flags = 0; - struct mhi_device_ctxt *mhi_dev_ctxt = (struct mhi_device_ctxt *)ctxt; + struct mhi_device_ctxt *mhi_dev_ctxt = container_of(work, + struct mhi_device_ctxt, + st_thread_worker); enum STATE_TRANSITION cur_work_item; struct mhi_state_work_queue *work_q = &mhi_dev_ctxt->state_change_work_item_list; struct mhi_ring *state_change_q = &work_q->q_info; - if (NULL == mhi_dev_ctxt) { - mhi_log(MHI_MSG_ERROR, "Got bad context, quitting\n"); - return -EIO; - } - for (;;) { - r = wait_event_interruptible( - *mhi_dev_ctxt->mhi_ev_wq.state_change_event, - ((work_q->q_info.rp != work_q->q_info.wp) && - !mhi_dev_ctxt->flags.st_thread_stopped)); - if (r) { - mhi_log(MHI_MSG_INFO, - "Caught signal %d, quitting\n", r); - return 0; - } - - if (mhi_dev_ctxt->flags.kill_threads) { - mhi_log(MHI_MSG_INFO, - "Caught exit signal, quitting\n"); - return 0; - } - mhi_dev_ctxt->flags.st_thread_stopped = 0; - spin_lock_irqsave(work_q->q_lock, flags); + while (work_q->q_info.rp != work_q->q_info.wp) { + spin_lock_irq(work_q->q_lock); cur_work_item = *(enum STATE_TRANSITION *)(state_change_q->rp); r = ctxt_del_element(&work_q->q_info, NULL); MHI_ASSERT(r == 0, "Failed to delete element from STT workqueue\n"); - spin_unlock_irqrestore(work_q->q_lock, flags); + spin_unlock_irq(work_q->q_lock); r = process_stt_work_item(mhi_dev_ctxt, cur_work_item); } - return 0; } /** @@ -638,16 +589,17 @@ int mhi_init_state_transition(struct mhi_device_ctxt *mhi_dev_ctxt, &mhi_dev_ctxt->state_change_work_item_list; spin_lock_irqsave(work_q->q_lock, flags); - nr_avail_work_items = get_nr_avail_ring_elements(stt_ring); + nr_avail_work_items = + get_nr_avail_ring_elements(mhi_dev_ctxt, stt_ring); BUG_ON(nr_avail_work_items <= 0); - mhi_log(MHI_MSG_VERBOSE, + mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Processing state transition %s\n", state_transition_str(new_state)); *(enum STATE_TRANSITION *)stt_ring->wp = new_state; r = ctxt_add_element(stt_ring, (void **)&cur_work_item); BUG_ON(r); spin_unlock_irqrestore(work_q->q_lock, flags); - wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.state_change_event); + schedule_work(&mhi_dev_ctxt->st_thread_worker); return r; } diff --git a/drivers/platform/msm/mhi/mhi_sys.c b/drivers/platform/msm/mhi/mhi_sys.c index c5c025b8585a..3389de2f95b3 100644 --- a/drivers/platform/msm/mhi/mhi_sys.c +++ b/drivers/platform/msm/mhi/mhi_sys.c @@ -51,13 +51,13 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf, { int amnt_copied = 0; struct mhi_chan_ctxt *chan_ctxt; - struct mhi_device_ctxt *mhi_dev_ctxt = - &mhi_devices.device_list[0].mhi_ctxt; + struct mhi_device_ctxt *mhi_dev_ctxt = fp->private_data; uintptr_t v_wp_index; uintptr_t v_rp_index; int valid_chan = 0; struct mhi_chan_ctxt *cc_list; struct mhi_client_handle *client_handle; + struct mhi_client_config *client_config; int pkts_queued; if (NULL == mhi_dev_ctxt) @@ -76,6 +76,7 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf, continue; } client_handle = mhi_dev_ctxt->client_handle_list[*offp]; + client_config = client_handle->client_config; valid_chan = 1; } @@ -87,8 +88,9 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf, mhi_dev_ctxt->mhi_local_chan_ctxt[*offp].wp, &v_wp_index); - pkts_queued = client_handle->chan_info.max_desc - - get_nr_avail_ring_elements(&mhi_dev_ctxt-> + pkts_queued = client_config->chan_info.max_desc - + get_nr_avail_ring_elements(mhi_dev_ctxt, + &mhi_dev_ctxt-> mhi_local_chan_ctxt[*offp]) - 1; amnt_copied = scnprintf(mhi_dev_ctxt->chan_info, @@ -115,7 +117,7 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf, "pkts_queued", pkts_queued, "/", - client_handle->chan_info.max_desc, + client_config->chan_info.max_desc, "bb_used:", mhi_dev_ctxt->counters.bb_used[*offp]); @@ -128,9 +130,16 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf, return -ENOMEM; } +int mhi_dbgfs_open(struct inode *inode, struct file *fp) +{ + fp->private_data = inode->i_private; + return 0; +} + static const struct file_operations mhi_dbgfs_chan_fops = { .read = mhi_dbgfs_chan_read, .write = NULL, + .open = mhi_dbgfs_open, }; static ssize_t mhi_dbgfs_ev_read(struct file *fp, char __user *buf, @@ -143,8 +152,7 @@ static ssize_t mhi_dbgfs_ev_read(struct file *fp, char __user *buf, uintptr_t v_rp_index; uintptr_t device_p_rp_index; - struct mhi_device_ctxt *mhi_dev_ctxt = - &mhi_devices.device_list[0].mhi_ctxt; + struct mhi_device_ctxt *mhi_dev_ctxt = fp->private_data; if (NULL == mhi_dev_ctxt) return -EIO; *offp = (u32)(*offp) % mhi_dev_ctxt->mmio_info.nr_event_rings; @@ -209,31 +217,15 @@ static ssize_t mhi_dbgfs_ev_read(struct file *fp, char __user *buf, static const struct file_operations mhi_dbgfs_ev_fops = { .read = mhi_dbgfs_ev_read, .write = NULL, -}; - -static ssize_t mhi_dbgfs_trigger_msi(struct file *fp, const char __user *buf, - size_t count, loff_t *offp) -{ - u32 msi_nr = 0; - void *irq_ctxt = &((mhi_devices.device_list[0]).pcie_device->dev); - - if (copy_from_user(&msi_nr, buf, sizeof(msi_nr))) - return -ENOMEM; - mhi_msi_handlr(msi_nr, irq_ctxt); - return 0; -} - -static const struct file_operations mhi_dbgfs_trigger_msi_fops = { - .read = NULL, - .write = mhi_dbgfs_trigger_msi, + .open = mhi_dbgfs_open, }; static ssize_t mhi_dbgfs_state_read(struct file *fp, char __user *buf, size_t count, loff_t *offp) { int amnt_copied = 0; - struct mhi_device_ctxt *mhi_dev_ctxt = - &mhi_devices.device_list[0].mhi_ctxt; + struct mhi_device_ctxt *mhi_dev_ctxt = fp->private_data; + if (NULL == mhi_dev_ctxt) return -EIO; msleep(100); @@ -260,7 +252,7 @@ static ssize_t mhi_dbgfs_state_read(struct file *fp, char __user *buf, "device_wake:", atomic_read(&mhi_dev_ctxt->counters.device_wake), "usage_count:", - atomic_read(&mhi_dev_ctxt->dev_info->pcie_device->dev. + atomic_read(&mhi_dev_ctxt->pcie_device->dev. power.usage_count), "outbound_acks:", atomic_read(&mhi_dev_ctxt->counters.outbound_acks)); @@ -275,63 +267,61 @@ static ssize_t mhi_dbgfs_state_read(struct file *fp, char __user *buf, static const struct file_operations mhi_dbgfs_state_fops = { .read = mhi_dbgfs_state_read, .write = NULL, + .open = mhi_dbgfs_open, }; int mhi_init_debugfs(struct mhi_device_ctxt *mhi_dev_ctxt) { struct dentry *mhi_chan_stats; struct dentry *mhi_state_stats; - struct dentry *mhi_msi_trigger; struct dentry *mhi_ev_stats; - - mhi_dev_ctxt->mhi_parent_folder = - debugfs_create_dir("mhi", NULL); - if (mhi_dev_ctxt->mhi_parent_folder == NULL) { - mhi_log(MHI_MSG_INFO, "Failed to create debugfs parent dir.\n"); + const struct pcie_core_info *core = &mhi_dev_ctxt->core; + char node_name[32]; + + snprintf(node_name, + sizeof(node_name), + "%04x_%02u.%02u.%02u", + core->dev_id, core->domain, core->bus, core->slot); + + mhi_dev_ctxt->child = + debugfs_create_dir(node_name, mhi_dev_ctxt->parent); + if (mhi_dev_ctxt->child == NULL) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to create debugfs parent dir.\n"); return -EIO; } mhi_chan_stats = debugfs_create_file("mhi_chan_stats", 0444, - mhi_dev_ctxt->mhi_parent_folder, + mhi_dev_ctxt->child, mhi_dev_ctxt, &mhi_dbgfs_chan_fops); if (mhi_chan_stats == NULL) return -ENOMEM; mhi_ev_stats = debugfs_create_file("mhi_ev_stats", 0444, - mhi_dev_ctxt->mhi_parent_folder, + mhi_dev_ctxt->child, mhi_dev_ctxt, &mhi_dbgfs_ev_fops); if (mhi_ev_stats == NULL) goto clean_chan; mhi_state_stats = debugfs_create_file("mhi_state_stats", 0444, - mhi_dev_ctxt->mhi_parent_folder, + mhi_dev_ctxt->child, mhi_dev_ctxt, &mhi_dbgfs_state_fops); if (mhi_state_stats == NULL) goto clean_ev_stats; - mhi_msi_trigger = debugfs_create_file("mhi_msi_trigger", - 0444, - mhi_dev_ctxt->mhi_parent_folder, - mhi_dev_ctxt, - &mhi_dbgfs_trigger_msi_fops); - if (mhi_msi_trigger == NULL) - goto clean_state; mhi_dev_ctxt->chan_info = kmalloc(MHI_LOG_SIZE, GFP_KERNEL); if (mhi_dev_ctxt->chan_info == NULL) - goto clean_all; + goto clean_ev_stats; return 0; -clean_all: - debugfs_remove(mhi_msi_trigger); -clean_state: - debugfs_remove(mhi_state_stats); + clean_ev_stats: debugfs_remove(mhi_ev_stats); clean_chan: debugfs_remove(mhi_chan_stats); - debugfs_remove(mhi_dev_ctxt->mhi_parent_folder); + debugfs_remove(mhi_dev_ctxt->child); return -ENOMEM; } diff --git a/drivers/platform/msm/mhi/mhi_sys.h b/drivers/platform/msm/mhi/mhi_sys.h index a948a2354de7..712647dc9f7c 100644 --- a/drivers/platform/msm/mhi/mhi_sys.h +++ b/drivers/platform/msm/mhi/mhi_sys.h @@ -38,12 +38,13 @@ extern void *mhi_ipc_log; } \ } while (0) -#define mhi_log(_msg_lvl, _msg, ...) do { \ +#define mhi_log(mhi_dev_ctxt, _msg_lvl, _msg, ...) do { \ if ((_msg_lvl) >= mhi_msg_lvl) \ pr_alert("[%s] " _msg, __func__, ##__VA_ARGS__);\ - if (mhi_ipc_log && ((_msg_lvl) >= mhi_ipc_log_lvl)) \ - ipc_log_string(mhi_ipc_log, \ - "[%s] " _msg, __func__, ##__VA_ARGS__); \ + if (mhi_dev_ctxt->mhi_ipc_log && \ + ((_msg_lvl) >= mhi_ipc_log_lvl)) \ + ipc_log_string(mhi_dev_ctxt->mhi_ipc_log, \ + "[%s] " _msg, __func__, ##__VA_ARGS__); \ } while (0) extern const char * const mhi_states_str[MHI_STATE_LIMIT]; diff --git a/drivers/platform/msm/mhi_uci/mhi_uci.c b/drivers/platform/msm/mhi_uci/mhi_uci.c index 96c4671f994f..0e28ebdd8fea 100644 --- a/drivers/platform/msm/mhi_uci/mhi_uci.c +++ b/drivers/platform/msm/mhi_uci/mhi_uci.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -31,8 +31,6 @@ #define MHI_DEV_NODE_NAME_LEN 13 #define MHI_SOFTWARE_CLIENT_LIMIT 23 -#define TRE_TYPICAL_SIZE 0x1000 -#define TRE_MAX_SIZE 0xFFFF #define MHI_UCI_IPC_LOG_PAGES (25) #define MAX_NR_TRBS_PER_CHAN 10 @@ -129,9 +127,8 @@ struct uci_client { struct mhi_uci_ctxt_t { struct list_head node; - struct platform_dev *pdev; + struct platform_device *pdev; struct uci_client client_handles[MHI_SOFTWARE_CLIENT_LIMIT]; - struct mhi_client_info_t client_info; dev_t dev_t; struct mutex ctrl_mutex; struct cdev cdev[MHI_SOFTWARE_CLIENT_LIMIT]; @@ -332,8 +329,8 @@ static int mhi_uci_send_packet(struct mhi_client_handle **client_handle, return 0; for (i = 0; i < nr_avail_trbs; ++i) { - data_to_insert_now = min(data_left_to_insert, - TRE_MAX_SIZE); + data_to_insert_now = min_t(size_t, data_left_to_insert, + uci_handle->out_attr.max_packet_size); if (is_uspace_buf) { data_loc = kmalloc(data_to_insert_now, GFP_KERNEL); if (NULL == data_loc) { @@ -1172,6 +1169,9 @@ static void uci_xfer_cb(struct mhi_cb_info *cb_info) uci_handle = cb_info->result->user_data; switch (cb_info->cb_reason) { case MHI_CB_MHI_ENABLED: + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_INFO, + "MHI enabled CB received.\n"); atomic_set(&uci_handle->mhi_disabled, 0); break; case MHI_CB_MHI_DISABLED: @@ -1202,9 +1202,11 @@ static void uci_xfer_cb(struct mhi_cb_info *cb_info) } } -static int mhi_register_client(struct uci_client *mhi_client) +static int mhi_register_client(struct uci_client *mhi_client, + struct device *dev) { int ret_val = 0; + struct mhi_client_info_t client_info; uci_log(mhi_client->uci_ipc_log, UCI_DBG_INFO, @@ -1222,11 +1224,13 @@ static int mhi_register_client(struct uci_client *mhi_client) UCI_DBG_INFO, "Registering chan %d\n", mhi_client->out_chan); - ret_val = mhi_register_channel(&mhi_client->out_handle, - mhi_client->out_chan, - 0, - &mhi_client->uci_ctxt->client_info, - mhi_client); + client_info.dev = dev; + client_info.node_name = "qcom,mhi"; + client_info.user_data = mhi_client; + client_info.mhi_client_cb = uci_xfer_cb; + client_info.chan = mhi_client->out_chan; + client_info.max_payload = mhi_client->out_attr.max_packet_size; + ret_val = mhi_register_channel(&mhi_client->out_handle, &client_info); if (0 != ret_val) uci_log(mhi_client->uci_ipc_log, UCI_DBG_ERROR, @@ -1238,11 +1242,9 @@ static int mhi_register_client(struct uci_client *mhi_client) UCI_DBG_INFO, "Registering chan %d\n", mhi_client->in_chan); - ret_val = mhi_register_channel(&mhi_client->in_handle, - mhi_client->in_chan, - 0, - &mhi_client->uci_ctxt->client_info, - mhi_client); + client_info.max_payload = mhi_client->in_attr.max_packet_size; + client_info.chan = mhi_client->in_chan; + ret_val = mhi_register_channel(&mhi_client->in_handle, &client_info); if (0 != ret_val) uci_log(mhi_client->uci_ipc_log, UCI_DBG_ERROR, @@ -1266,13 +1268,16 @@ static int mhi_uci_probe(struct platform_device *pdev) struct mhi_uci_ctxt_t *uci_ctxt; int ret_val; int i; - char node_name[16]; + char node_name[32]; uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_INFO, "Entered with pdev:%p\n", pdev); + if (mhi_is_device_ready(&pdev->dev, "qcom,mhi") == false) + return -EPROBE_DEFER; + if (pdev->dev.of_node == NULL) return -ENODEV; @@ -1286,7 +1291,7 @@ static int mhi_uci_probe(struct platform_device *pdev) if (!uci_ctxt) return -ENOMEM; - uci_ctxt->client_info.mhi_client_cb = uci_xfer_cb; + uci_ctxt->pdev = pdev; mutex_init(&uci_ctxt->ctrl_mutex); uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, @@ -1309,7 +1314,8 @@ static int mhi_uci_probe(struct platform_device *pdev) uci_client->uci_ctxt = uci_ctxt; if (uci_client->in_attr.uci_ownership) { - ret_val = mhi_register_client(uci_client); + ret_val = mhi_register_client(uci_client, + &pdev->dev); if (ret_val) { uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_CRITICAL, @@ -1319,7 +1325,13 @@ static int mhi_uci_probe(struct platform_device *pdev) return -EIO; } - snprintf(node_name, sizeof(node_name), "mhi-uci%d", + snprintf(node_name, + sizeof(node_name), + "mhi_uci_%04x_%02u.%02u.%02u_%d", + uci_client->out_handle->dev_id, + uci_client->out_handle->domain, + uci_client->out_handle->bus, + uci_client->out_handle->slot, uci_client->out_attr.chan_id); uci_client->uci_ipc_log = ipc_log_context_create (MHI_UCI_IPC_LOG_PAGES, @@ -1364,11 +1376,16 @@ static int mhi_uci_probe(struct platform_device *pdev) } uci_client->dev = device_create(mhi_uci_drv_ctxt.mhi_uci_class, - NULL, - uci_ctxt->dev_t + i, - NULL, - DEVICE_NAME "_pipe_%d", - uci_client->out_chan); + NULL, + uci_ctxt->dev_t + i, + NULL, + DEVICE_NAME "_%04x_%02u.%02u.%02u%s%d", + uci_client->out_handle->dev_id, + uci_client->out_handle->domain, + uci_client->out_handle->bus, + uci_client->out_handle->slot, + "_pipe_", + uci_client->out_chan); if (IS_ERR(uci_client->dev)) { uci_log(uci_client->uci_ipc_log, UCI_DBG_ERROR, diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c index 7f97d7c6bab7..9b48282c812c 100644 --- a/drivers/platform/msm/msm_11ad/msm_11ad.c +++ b/drivers/platform/msm/msm_11ad/msm_11ad.c @@ -55,7 +55,6 @@ #define VDDIO_MAX_UV 2040000 #define VDDIO_MAX_UA 70300 -#define DISABLE_PCIE_L1_MASK 0xFFFFFFFD #define PCIE20_CAP_LINKCTRLSTATUS 0x80 #define WIGIG_MIN_CPU_BOOST_KBPS 150000 @@ -90,6 +89,7 @@ struct msm11ad_ctx { u32 rc_index; /* PCIE root complex index */ struct pci_dev *pcidev; struct pci_saved_state *pristine_state; + bool l1_enabled_in_enum; /* SMMU */ bool use_smmu; /* have SMMU enabled? */ @@ -479,6 +479,47 @@ static void msm_11ad_disable_clocks(struct msm11ad_ctx *ctx) msm_11ad_disable_clk(ctx, &ctx->rf_clk3); } +int msm_11ad_ctrl_aspm_l1(struct msm11ad_ctx *ctx, bool enable) +{ + int rc; + u32 val; + struct pci_dev *pdev = ctx->pcidev; + bool l1_enabled; + + /* Read current state */ + rc = pci_read_config_dword(pdev, + PCIE20_CAP_LINKCTRLSTATUS, &val); + if (rc) { + dev_err(ctx->dev, + "reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n", rc); + return rc; + } + dev_dbg(ctx->dev, "PCIE20_CAP_LINKCTRLSTATUS read returns 0x%x\n", val); + + l1_enabled = val & PCI_EXP_LNKCTL_ASPM_L1; + if (l1_enabled == enable) { + dev_dbg(ctx->dev, "ASPM_L1 is already %s\n", + l1_enabled ? "enabled" : "disabled"); + return 0; + } + + if (enable) + val |= PCI_EXP_LNKCTL_ASPM_L1; /* enable bit 1 */ + else + val &= ~PCI_EXP_LNKCTL_ASPM_L1; /* disable bit 1 */ + + dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n", + val); + rc = pci_write_config_dword(pdev, + PCIE20_CAP_LINKCTRLSTATUS, val); + if (rc) + dev_err(ctx->dev, + "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n", + val, rc); + + return rc; +} + static int ops_suspend(void *handle) { int rc; @@ -521,7 +562,6 @@ static int ops_resume(void *handle) int rc; struct msm11ad_ctx *ctx = handle; struct pci_dev *pcidev; - u32 val; pr_info("%s(%p)\n", __func__, handle); if (!ctx) { @@ -565,25 +605,14 @@ static int ops_resume(void *handle) goto err_suspend_rc; } - /* Disable L1 */ - rc = pci_read_config_dword(ctx->pcidev, - PCIE20_CAP_LINKCTRLSTATUS, &val); - if (rc) { - dev_err(ctx->dev, - "reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n", - rc); - goto err_suspend_rc; - } - val &= DISABLE_PCIE_L1_MASK; /* disable bit 1 */ - dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n", - val); - rc = pci_write_config_dword(ctx->pcidev, - PCIE20_CAP_LINKCTRLSTATUS, val); - if (rc) { - dev_err(ctx->dev, - "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n", - val, rc); - goto err_suspend_rc; + /* Disable L1, in case it is enabled */ + if (ctx->l1_enabled_in_enum) { + rc = msm_11ad_ctrl_aspm_l1(ctx, false); + if (rc) { + dev_err(ctx->dev, + "failed to disable L1, rc %d\n", rc); + goto err_suspend_rc; + } } return 0; @@ -992,8 +1021,8 @@ static int msm_11ad_probe(struct platform_device *pdev) } ctx->pcidev = pcidev; - /* Disable L1 */ - rc = pci_read_config_dword(ctx->pcidev, + /* Read current state */ + rc = pci_read_config_dword(pcidev, PCIE20_CAP_LINKCTRLSTATUS, &val); if (rc) { dev_err(ctx->dev, @@ -1001,16 +1030,19 @@ static int msm_11ad_probe(struct platform_device *pdev) rc); goto out_rc; } - val &= DISABLE_PCIE_L1_MASK; /* disable bit 1 */ - dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n", - val); - rc = pci_write_config_dword(ctx->pcidev, - PCIE20_CAP_LINKCTRLSTATUS, val); - if (rc) { - dev_err(ctx->dev, - "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n", - val, rc); - goto out_rc; + + ctx->l1_enabled_in_enum = val & PCI_EXP_LNKCTL_ASPM_L1; + dev_dbg(ctx->dev, "L1 is %s in enumeration\n", + ctx->l1_enabled_in_enum ? "enabled" : "disabled"); + + /* Disable L1, in case it is enabled */ + if (ctx->l1_enabled_in_enum) { + rc = msm_11ad_ctrl_aspm_l1(ctx, false); + if (rc) { + dev_err(ctx->dev, + "failed to disable L1, rc %d\n", rc); + goto out_rc; + } } rc = pci_save_state(pcidev); @@ -1258,6 +1290,13 @@ static int ops_notify(void *handle, enum wil_platform_event evt) * TODO: Enable rf_clk3 clock before resetting the device to * ensure stable ref clock during the device reset */ + /* Re-enable L1 in case it was enabled in enumeration */ + if (ctx->l1_enabled_in_enum) { + rc = msm_11ad_ctrl_aspm_l1(ctx, true); + if (rc) + dev_err(ctx->dev, + "failed to enable L1, rc %d\n", rc); + } break; case WIL_PLATFORM_EVT_FW_RDY: /* diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 131fee2b093e..a3661cc44f86 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -128,6 +128,15 @@ static const struct dmi_system_id asus_quirks[] = { }, { .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X45U", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X45U"), + }, + .driver_data = &quirk_asus_wapf4, + }, + { + .callback = dmi_matched, .ident = "ASUSTeK COMPUTER INC. X456UA", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 1fc0de870ff8..361770568ad0 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -77,7 +77,7 @@ static int mfld_pb_probe(struct platform_device *pdev) input_set_capability(input, EV_KEY, KEY_POWER); - error = request_threaded_irq(irq, NULL, mfld_pb_isr, 0, + error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_ONESHOT, DRIVER_NAME, input); if (error) { dev_err(&pdev->dev, "Unable to request irq %d for mfld power" diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 201a53e66ef0..438da2c51dd6 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -264,6 +264,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(dp_dm), POWER_SUPPLY_ATTR(input_current_limited), POWER_SUPPLY_ATTR(input_current_now), + POWER_SUPPLY_ATTR(charge_qnovo_enable), POWER_SUPPLY_ATTR(current_qnovo), POWER_SUPPLY_ATTR(voltage_qnovo), POWER_SUPPLY_ATTR(rerun_aicl), diff --git a/drivers/power/qcom/apm.c b/drivers/power/qcom/apm.c index b5ae7f4ade03..9455468f1734 100644 --- a/drivers/power/qcom/apm.c +++ b/drivers/power/qcom/apm.c @@ -629,9 +629,15 @@ done: #define MSM8953_APCC_APM_MODE 0x000002a8 #define MSM8953_APCC_APM_CTL_STS 0x000002b0 +/* 8953 constants */ +#define MSM8953_APM_SWITCH_TIMEOUT_US 500 + +/* Register bit mask definitions */ +#define MSM8953_APM_CTL_STS_MASK 0x1f + static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) { - int timeout = MSM_APM_SWITCH_TIMEOUT_US; + int timeout = MSM8953_APM_SWITCH_TIMEOUT_US; u32 regval; int ret = 0; unsigned long flags; @@ -648,7 +654,7 @@ static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) while (timeout > 0) { regval = readl_relaxed(ctrl_dev->reg_base + MSM8953_APCC_APM_CTL_STS); - if ((regval & MSM_APM_CTL_STS_MASK) == + if ((regval & MSM8953_APM_CTL_STS_MASK) == MSM8953_APM_MX_DONE_VAL) break; @@ -672,7 +678,7 @@ static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) { - int timeout = MSM_APM_SWITCH_TIMEOUT_US; + int timeout = MSM8953_APM_SWITCH_TIMEOUT_US; u32 regval; int ret = 0; unsigned long flags; @@ -689,7 +695,7 @@ static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) while (timeout > 0) { regval = readl_relaxed(ctrl_dev->reg_base + MSM8953_APCC_APM_CTL_STS); - if ((regval & MSM_APM_CTL_STS_MASK) == + if ((regval & MSM8953_APM_CTL_STS_MASK) == MSM8953_APM_APCC_DONE_VAL) break; diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index 5b320ca8e74e..0c80c8be7909 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -24,7 +24,7 @@ #include <linux/printk.h> #include <linux/pm_wakeup.h> #include <linux/slab.h> -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define DRV_MAJOR_VERSION 1 #define DRV_MINOR_VERSION 0 diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index 32a25b4e2c7b..0e15752eb9d2 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -29,7 +29,7 @@ #include <linux/string_helpers.h> #include <linux/types.h> #include <linux/uaccess.h> -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define fg_dbg(chip, reason, fmt, ...) \ do { \ @@ -372,6 +372,7 @@ struct fg_chip { bool esr_flt_cold_temp_en; bool bsoc_delta_irq_en; bool slope_limit_en; + bool use_ima_single_mode; struct completion soc_update; struct completion soc_ready; struct delayed_work profile_load_work; diff --git a/drivers/power/supply/qcom/fg-memif.c b/drivers/power/supply/qcom/fg-memif.c index 2dc76182ed15..8a949bfe61d0 100644 --- a/drivers/power/supply/qcom/fg-memif.c +++ b/drivers/power/supply/qcom/fg-memif.c @@ -48,6 +48,10 @@ static int fg_config_access_mode(struct fg_chip *chip, bool access, bool burst) int rc; u8 intf_ctl = 0; + fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "access: %d burst: %d\n", + access, burst); + + WARN_ON(burst && chip->use_ima_single_mode); intf_ctl = ((access == FG_WRITE) ? IMA_WR_EN_BIT : 0) | (burst ? MEM_ACS_BURST_BIT : 0); @@ -175,6 +179,7 @@ int fg_clear_dma_errors_if_any(struct fg_chip *chip) { int rc; u8 dma_sts; + bool error_present; rc = fg_read(chip, MEM_IF_DMA_STS(chip), &dma_sts, 1); if (rc < 0) { @@ -184,14 +189,13 @@ int fg_clear_dma_errors_if_any(struct fg_chip *chip) } fg_dbg(chip, FG_STATUS, "dma_sts: %x\n", dma_sts); - if (dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT)) { - rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip), - DMA_CLEAR_LOG_BIT, DMA_CLEAR_LOG_BIT); - if (rc < 0) { - pr_err("failed to write addr=0x%04x, rc=%d\n", - MEM_IF_DMA_CTL(chip), rc); - return rc; - } + error_present = dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT); + rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip), DMA_CLEAR_LOG_BIT, + error_present ? DMA_CLEAR_LOG_BIT : 0); + if (rc < 0) { + pr_err("failed to write addr=0x%04x, rc=%d\n", + MEM_IF_DMA_CTL(chip), rc); + return rc; } return 0; @@ -293,7 +297,9 @@ static int fg_check_iacs_ready(struct fg_chip *chip) /* check for error condition */ rc = fg_clear_ima_errors_if_any(chip, false); if (rc < 0) { - pr_err("Failed to check for ima errors rc=%d\n", rc); + if (rc != -EAGAIN) + pr_err("Failed to check for ima errors rc=%d\n", + rc); return rc; } @@ -357,7 +363,12 @@ static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address, /* check for error condition */ rc = fg_clear_ima_errors_if_any(chip, false); if (rc < 0) { - pr_err("Failed to check for ima errors rc=%d\n", rc); + if (rc == -EAGAIN) + pr_err("IMA error cleared, address [%d %d] len %d\n", + address, offset, len); + else + pr_err("Failed to check for ima errors rc=%d\n", + rc); return rc; } @@ -365,6 +376,15 @@ static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address, len -= num_bytes; offset = byte_enable = 0; + if (chip->use_ima_single_mode && len) { + address++; + rc = fg_set_address(chip, address); + if (rc < 0) { + pr_err("failed to set address rc = %d\n", rc); + return rc; + } + } + rc = fg_check_iacs_ready(chip); if (rc < 0) { pr_debug("IACS_RDY failed rc=%d\n", rc); @@ -403,22 +423,40 @@ static int __fg_interleaved_mem_read(struct fg_chip *chip, u16 address, /* check for error condition */ rc = fg_clear_ima_errors_if_any(chip, false); if (rc < 0) { - pr_err("Failed to check for ima errors rc=%d\n", rc); + if (rc == -EAGAIN) + pr_err("IMA error cleared, address [%d %d] len %d\n", + address, offset, len); + else + pr_err("Failed to check for ima errors rc=%d\n", + rc); return rc; } - if (len && len < BYTES_PER_SRAM_WORD) { - /* - * Move to single mode. Changing address is not - * required here as it must be in burst mode. Address - * will get incremented internally by FG HW once the MSB - * of RD_DATA is read. - */ - rc = fg_config_access_mode(chip, FG_READ, 0); - if (rc < 0) { - pr_err("failed to move to single mode rc=%d\n", - rc); - return -EIO; + if (chip->use_ima_single_mode) { + if (len) { + address++; + rc = fg_set_address(chip, address); + if (rc < 0) { + pr_err("failed to set address rc = %d\n", + rc); + return rc; + } + } + } else { + if (len && len < BYTES_PER_SRAM_WORD) { + /* + * Move to single mode. Changing address is not + * required here as it must be in burst mode. + * Address will get incremented internally by FG + * HW once the MSB of RD_DATA is read. + */ + rc = fg_config_access_mode(chip, FG_READ, + false); + if (rc < 0) { + pr_err("failed to move to single mode rc=%d\n", + rc); + return -EIO; + } } } @@ -489,6 +527,7 @@ static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val, u16 address, int offset, int len, bool access) { int rc = 0; + bool burst_mode = false; if (!is_mem_access_available(chip, access)) return -EBUSY; @@ -503,7 +542,8 @@ static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val, } /* configure for the read/write, single/burst mode */ - rc = fg_config_access_mode(chip, access, (offset + len) > 4); + burst_mode = chip->use_ima_single_mode ? false : ((offset + len) > 4); + rc = fg_config_access_mode(chip, access, burst_mode); if (rc < 0) { pr_err("failed to set memory access rc = %d\n", rc); return rc; @@ -583,7 +623,7 @@ retry: if (rc < 0) { count++; if (rc == -EAGAIN) { - pr_err("IMA access failed retry_count = %d\n", count); + pr_err("IMA read failed retry_count = %d\n", count); goto retry; } pr_err("failed to read SRAM address rc = %d\n", rc); @@ -667,8 +707,8 @@ retry: rc = __fg_interleaved_mem_write(chip, address, offset, val, len); if (rc < 0) { count++; - if ((rc == -EAGAIN) && (count < RETRY_COUNT)) { - pr_err("IMA access failed retry_count = %d\n", count); + if (rc == -EAGAIN) { + pr_err("IMA write failed retry_count = %d\n", count); goto retry; } pr_err("failed to write SRAM address rc = %d\n", rc); diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c index e1a92fb23912..3652cc7802eb 100644 --- a/drivers/power/supply/qcom/pmic-voter.c +++ b/drivers/power/supply/qcom/pmic-voter.c @@ -18,7 +18,7 @@ #include <linux/slab.h> #include <linux/string.h> -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define NUM_MAX_CLIENTS 8 #define DEBUG_FORCE_CLIENT "DEBUG_FORCE_CLIENT" @@ -188,6 +188,38 @@ void unlock_votable(struct votable *votable) } /** + * is_client_vote_enabled() - + * is_client_vote_enabled_locked() - + * The unlocked and locked variants of getting whether a client's + vote is enabled. + * @votable: the votable object + * @client_str: client of interest + * + * Returns: + * True if the client's vote is enabled; false otherwise. + */ +bool is_client_vote_enabled_locked(struct votable *votable, + const char *client_str) +{ + int client_id = get_client_id(votable, client_str); + + if (client_id < 0) + return false; + + return votable->votes[client_id].enabled; +} + +bool is_client_vote_enabled(struct votable *votable, const char *client_str) +{ + bool enabled; + + lock_votable(votable); + enabled = is_client_vote_enabled_locked(votable, client_str); + unlock_votable(votable); + return enabled; +} + +/** * get_client_vote() - * get_client_vote_locked() - * The unlocked and locked variants of getting a client's voted diff --git a/drivers/power/supply/qcom/pmic-voter.h b/drivers/power/supply/qcom/pmic-voter.h deleted file mode 100644 index 031b9a010a42..000000000000 --- a/drivers/power/supply/qcom/pmic-voter.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright (c) 2015-2016 The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __PMIC_VOTER_H -#define __PMIC_VOTER_H - -#include <linux/mutex.h> - -struct votable; - -enum votable_type { - VOTE_MIN, - VOTE_MAX, - VOTE_SET_ANY, - NUM_VOTABLE_TYPES, -}; - -int get_client_vote(struct votable *votable, const char *client_str); -int get_client_vote_locked(struct votable *votable, const char *client_str); -int get_effective_result(struct votable *votable); -int get_effective_result_locked(struct votable *votable); -const char *get_effective_client(struct votable *votable); -const char *get_effective_client_locked(struct votable *votable); -int vote(struct votable *votable, const char *client_str, bool state, int val); -int rerun_election(struct votable *votable); -struct votable *find_votable(const char *name); -struct votable *create_votable(const char *name, - int votable_type, - int (*callback)(struct votable *votable, - void *data, - int effective_result, - const char *effective_client), - void *data); -void destroy_votable(struct votable *votable); -void lock_votable(struct votable *votable); -void unlock_votable(struct votable *votable); - -#endif /* __PMIC_VOTER_H */ diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index 39afc235fbc3..aeffe1f154a1 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -525,7 +525,7 @@ static int fg_get_sram_prop(struct fg_chip *chip, enum fg_sram_param_id id, } #define CC_SOC_30BIT GENMASK(29, 0) -static int fg_get_cc_soc(struct fg_chip *chip, int *val) +static int fg_get_charge_raw(struct fg_chip *chip, int *val) { int rc, cc_soc; @@ -539,7 +539,7 @@ static int fg_get_cc_soc(struct fg_chip *chip, int *val) return 0; } -static int fg_get_cc_soc_sw(struct fg_chip *chip, int *val) +static int fg_get_charge_counter(struct fg_chip *chip, int *val) { int rc, cc_soc; @@ -1241,7 +1241,7 @@ static void fg_cap_learning_post_process(struct fg_chip *chip) chip->cl.final_cc_uah, old_cap, chip->cl.learned_cc_uah); } -static int fg_cap_learning_process_full_data(struct fg_chip *chip) +static int fg_cap_learning_process_full_data(struct fg_chip *chip) { int rc, cc_soc_sw, cc_soc_delta_pct; int64_t delta_cc_uah; @@ -1263,30 +1263,39 @@ static int fg_cap_learning_process_full_data(struct fg_chip *chip) return 0; } -static int fg_cap_learning_begin(struct fg_chip *chip, int batt_soc) +#define BATT_SOC_32BIT GENMASK(31, 0) +static int fg_cap_learning_begin(struct fg_chip *chip, u32 batt_soc) { - int rc, cc_soc_sw; + int rc, cc_soc_sw, batt_soc_msb; - if (DIV_ROUND_CLOSEST(batt_soc * 100, FULL_SOC_RAW) > + batt_soc_msb = batt_soc >> 24; + if (DIV_ROUND_CLOSEST(batt_soc_msb * 100, FULL_SOC_RAW) > chip->dt.cl_start_soc) { fg_dbg(chip, FG_CAP_LEARN, "Battery SOC %d is high!, not starting\n", - batt_soc); + batt_soc_msb); return -EINVAL; } - chip->cl.init_cc_uah = div64_s64(chip->cl.learned_cc_uah * batt_soc, + chip->cl.init_cc_uah = div64_s64(chip->cl.learned_cc_uah * batt_soc_msb, FULL_SOC_RAW); - rc = fg_get_sram_prop(chip, FG_SRAM_CC_SOC_SW, &cc_soc_sw); + + /* Prime cc_soc_sw with battery SOC when capacity learning begins */ + cc_soc_sw = div64_s64((int64_t)batt_soc * CC_SOC_30BIT, + BATT_SOC_32BIT); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word, + chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw, + chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC); if (rc < 0) { - pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc); - return rc; + pr_err("Error in writing cc_soc_sw, rc=%d\n", rc); + goto out; } chip->cl.init_cc_soc_sw = cc_soc_sw; chip->cl.active = true; fg_dbg(chip, FG_CAP_LEARN, "Capacity learning started @ battery SOC %d init_cc_soc_sw:%d\n", - batt_soc, chip->cl.init_cc_soc_sw); - return 0; + batt_soc_msb, chip->cl.init_cc_soc_sw); +out: + return rc; } static int fg_cap_learning_done(struct fg_chip *chip) @@ -1318,7 +1327,7 @@ out: #define FULL_SOC_RAW 255 static void fg_cap_learning_update(struct fg_chip *chip) { - int rc, batt_soc; + int rc, batt_soc, batt_soc_msb; mutex_lock(&chip->cl.lock); @@ -1337,11 +1346,9 @@ static void fg_cap_learning_update(struct fg_chip *chip) goto out; } - /* We need only the most significant byte here */ - batt_soc = (u32)batt_soc >> 24; - + batt_soc_msb = (u32)batt_soc >> 24; fg_dbg(chip, FG_CAP_LEARN, "Chg_status: %d cl_active: %d batt_soc: %d\n", - chip->charge_status, chip->cl.active, batt_soc); + chip->charge_status, chip->cl.active, batt_soc_msb); /* Initialize the starting point of learning capacity */ if (!chip->cl.active) { @@ -1363,7 +1370,7 @@ static void fg_cap_learning_update(struct fg_chip *chip) if (chip->charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) { fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n", - batt_soc); + batt_soc_msb); chip->cl.active = false; chip->cl.init_cc_uah = 0; } @@ -1598,6 +1605,9 @@ static int fg_rconn_config(struct fg_chip *chip) u64 scaling_factor; u32 val = 0; + if (!chip->dt.rconn_mohms) + return 0; + rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD, SW_CONFIG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT); if (rc < 0) { @@ -1696,6 +1706,9 @@ static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc) if (!chip->dt.auto_recharge_soc) return 0; + if (recharge_soc < 0 || recharge_soc > FULL_CAPACITY) + return 0; + fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, recharge_soc, &buf); rc = fg_sram_write(chip, chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word, @@ -1712,46 +1725,55 @@ static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc) static int fg_adjust_recharge_soc(struct fg_chip *chip) { int rc, msoc, recharge_soc, new_recharge_soc = 0; + bool recharge_soc_status; if (!chip->dt.auto_recharge_soc) return 0; recharge_soc = chip->dt.recharge_soc_thr; + recharge_soc_status = chip->recharge_soc_adjusted; /* * If the input is present and charging had been terminated, adjust * the recharge SOC threshold based on the monotonic SOC at which * the charge termination had happened. */ - if (is_input_present(chip) && !chip->recharge_soc_adjusted - && chip->charge_done) { - /* Get raw monotonic SOC for calculation */ - rc = fg_get_msoc(chip, &msoc); - if (rc < 0) { - pr_err("Error in getting msoc, rc=%d\n", rc); - return rc; - } + if (is_input_present(chip)) { + if (chip->charge_done) { + if (!chip->recharge_soc_adjusted) { + /* Get raw monotonic SOC for calculation */ + rc = fg_get_msoc(chip, &msoc); + if (rc < 0) { + pr_err("Error in getting msoc, rc=%d\n", + rc); + return rc; + } - /* Adjust the recharge_soc threshold */ - new_recharge_soc = msoc - (FULL_CAPACITY - recharge_soc); - } else if (chip->recharge_soc_adjusted && (!is_input_present(chip) - || chip->health == POWER_SUPPLY_HEALTH_GOOD)) { + /* Adjust the recharge_soc threshold */ + new_recharge_soc = msoc - (FULL_CAPACITY - + recharge_soc); + chip->recharge_soc_adjusted = true; + } else { + /* adjusted already, do nothing */ + return 0; + } + } else { + /* Charging, do nothing */ + return 0; + } + } else { /* Restore the default value */ new_recharge_soc = recharge_soc; + chip->recharge_soc_adjusted = false; } - if (new_recharge_soc > 0 && new_recharge_soc < FULL_CAPACITY) { - rc = fg_set_recharge_soc(chip, new_recharge_soc); - if (rc) { - pr_err("Couldn't set resume SOC for FG, rc=%d\n", rc); - return rc; - } - - chip->recharge_soc_adjusted = (new_recharge_soc != - recharge_soc); - fg_dbg(chip, FG_STATUS, "resume soc set to %d\n", - new_recharge_soc); + rc = fg_set_recharge_soc(chip, new_recharge_soc); + if (rc < 0) { + chip->recharge_soc_adjusted = recharge_soc_status; + pr_err("Couldn't set resume SOC for FG, rc=%d\n", rc); + return rc; } + fg_dbg(chip, FG_STATUS, "resume soc set to %d\n", new_recharge_soc); return 0; } @@ -2806,7 +2828,7 @@ static int fg_psy_get_property(struct power_supply *psy, pval->intval = chip->cyc_ctr.id; break; case POWER_SUPPLY_PROP_CHARGE_NOW_RAW: - rc = fg_get_cc_soc(chip, &pval->intval); + rc = fg_get_charge_raw(chip, &pval->intval); break; case POWER_SUPPLY_PROP_CHARGE_NOW: pval->intval = chip->cl.init_cc_uah; @@ -2815,7 +2837,7 @@ static int fg_psy_get_property(struct power_supply *psy, pval->intval = chip->cl.learned_cc_uah; break; case POWER_SUPPLY_PROP_CHARGE_COUNTER: - rc = fg_get_cc_soc_sw(chip, &pval->intval); + rc = fg_get_charge_counter(chip, &pval->intval); break; case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: rc = fg_get_time_to_full(chip, &pval->intval); @@ -3164,12 +3186,10 @@ static int fg_hw_init(struct fg_chip *chip) return rc; } - if (chip->dt.rconn_mohms > 0) { - rc = fg_rconn_config(chip); - if (rc < 0) { - pr_err("Error in configuring Rconn, rc=%d\n", rc); - return rc; - } + rc = fg_rconn_config(chip); + if (rc < 0) { + pr_err("Error in configuring Rconn, rc=%d\n", rc); + return rc; } fg_encode(chip->sp, FG_SRAM_ESR_TIGHT_FILTER, @@ -3216,20 +3236,19 @@ static irqreturn_t fg_mem_xcp_irq_handler(int irq, void *data) } fg_dbg(chip, FG_IRQ, "irq %d triggered, status:%d\n", irq, status); - if (status & MEM_XCP_BIT) { - rc = fg_clear_dma_errors_if_any(chip); - if (rc < 0) { - pr_err("Error in clearing DMA error, rc=%d\n", rc); - return IRQ_HANDLED; - } - mutex_lock(&chip->sram_rw_lock); + mutex_lock(&chip->sram_rw_lock); + rc = fg_clear_dma_errors_if_any(chip); + if (rc < 0) + pr_err("Error in clearing DMA error, rc=%d\n", rc); + + if (status & MEM_XCP_BIT) { rc = fg_clear_ima_errors_if_any(chip, true); if (rc < 0 && rc != -EAGAIN) pr_err("Error in checking IMA errors rc:%d\n", rc); - mutex_unlock(&chip->sram_rw_lock); } + mutex_unlock(&chip->sram_rw_lock); return IRQ_HANDLED; } @@ -3725,6 +3744,7 @@ static int fg_parse_dt(struct fg_chip *chip) case PM660_SUBTYPE: chip->sp = pmi8998_v2_sram_params; chip->alg_flags = pmi8998_v2_alg_flags; + chip->use_ima_single_mode = true; break; default: return -EINVAL; @@ -3945,9 +3965,7 @@ static int fg_parse_dt(struct fg_chip *chip) pr_err("Error in parsing Ki coefficients, rc=%d\n", rc); rc = of_property_read_u32(node, "qcom,fg-rconn-mohms", &temp); - if (rc < 0) - chip->dt.rconn_mohms = -EINVAL; - else + if (!rc) chip->dt.rconn_mohms = temp; rc = of_property_read_u32(node, "qcom,fg-esr-filter-switch-temp", diff --git a/drivers/power/supply/qcom/qpnp-qnovo.c b/drivers/power/supply/qcom/qpnp-qnovo.c index d36db8d8f3f1..8f9514a25f63 100644 --- a/drivers/power/supply/qcom/qpnp-qnovo.c +++ b/drivers/power/supply/qcom/qpnp-qnovo.c @@ -19,7 +19,7 @@ #include <linux/of.h> #include <linux/of_irq.h> #include <linux/qpnp/qpnp-revid.h> -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define QNOVO_REVISION1 0x00 #define QNOVO_REVISION2 0x01 @@ -29,6 +29,7 @@ #define QNOVO_PTRAIN_STS 0x08 #define QNOVO_ERROR_STS 0x09 #define QNOVO_ERROR_BIT BIT(0) +#define QNOVO_ERROR_STS2 0x0A #define QNOVO_INT_RT_STS 0x10 #define QNOVO_INT_SET_TYPE 0x11 #define QNOVO_INT_POLARITY_HIGH 0x12 @@ -151,7 +152,7 @@ struct qnovo { }; static int debug_mask; -module_param_named(debug_mask, debug_mask, int, S_IRUSR | S_IWUSR); +module_param_named(debug_mask, debug_mask, int, 0600); #define qnovo_dbg(chip, reason, fmt, ...) \ do { \ @@ -272,28 +273,22 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable, const char *client) { struct qnovo *chip = data; - int rc = 0; + union power_supply_propval pval = {0}; + int rc; - if (disable) { - rc = qnovo_batt_psy_update(chip, true); - if (rc < 0) - return rc; - } + if (!is_batt_available(chip)) + return -EINVAL; - rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, - disable ? 0 : QNOVO_PTRAIN_EN_BIT); + pval.intval = !disable; + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, + &pval); if (rc < 0) { - dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n", - disable ? "disable" : "enable", rc); - return rc; - } - - if (!disable) { - rc = qnovo_batt_psy_update(chip, false); - if (rc < 0) - return rc; + pr_err("Couldn't set prop qnovo_enable rc = %d\n", rc); + return -EINVAL; } + rc = qnovo_batt_psy_update(chip, disable); return rc; } @@ -348,13 +343,15 @@ static int qnovo_check_chg_version(struct qnovo *chip) enum { VER = 0, OK_TO_QNOVO, - ENABLE, + QNOVO_ENABLE, + PT_ENABLE, FV_REQUEST, FCC_REQUEST, PE_CTRL_REG, PE_CTRL2_REG, PTRAIN_STS_REG, INT_RT_STS_REG, + ERR_STS2_REG, PREST1, PPULS1, NREST1, @@ -394,6 +391,12 @@ struct param_info { }; static struct param_info params[] = { + [PT_ENABLE] = { + .name = "PT_ENABLE", + .start_addr = QNOVO_PTRAIN_EN, + .num_regs = 1, + .units_str = "", + }, [FV_REQUEST] = { .units_str = "uV", }, @@ -424,6 +427,12 @@ static struct param_info params[] = { .num_regs = 1, .units_str = "", }, + [ERR_STS2_REG] = { + .name = "RAW_CHGR_ERR", + .start_addr = QNOVO_ERROR_STS2, + .num_regs = 1, + .units_str = "", + }, [PREST1] = { .name = "PREST1", .start_addr = QNOVO_PREST1_CTRL, @@ -431,7 +440,7 @@ static struct param_info params[] = { .reg_to_unit_multiplier = 5, .reg_to_unit_divider = 1, .min_val = 5, - .max_val = 1275, + .max_val = 255, .units_str = "mS", }, [PPULS1] = { @@ -440,8 +449,8 @@ static struct param_info params[] = { .num_regs = 2, .reg_to_unit_multiplier = 1600, /* converts to uC */ .reg_to_unit_divider = 1, - .min_val = 0, - .max_val = 104856000, + .min_val = 30000, + .max_val = 65535000, .units_str = "uC", }, [NREST1] = { @@ -451,7 +460,7 @@ static struct param_info params[] = { .reg_to_unit_multiplier = 5, .reg_to_unit_divider = 1, .min_val = 5, - .max_val = 1275, + .max_val = 255, .units_str = "mS", }, [NPULS1] = { @@ -460,8 +469,8 @@ static struct param_info params[] = { .num_regs = 1, .reg_to_unit_multiplier = 5, .reg_to_unit_divider = 1, - .min_val = 5, - .max_val = 1275, + .min_val = 0, + .max_val = 255, .units_str = "mS", }, [PPCNT] = { @@ -470,7 +479,7 @@ static struct param_info params[] = { .num_regs = 1, .reg_to_unit_multiplier = 1, .reg_to_unit_divider = 1, - .min_val = 0, + .min_val = 1, .max_val = 255, .units_str = "pulses", }, @@ -480,8 +489,8 @@ static struct param_info params[] = { .num_regs = 2, .reg_to_unit_multiplier = 610350, /* converts to nV */ .reg_to_unit_divider = 1, - .min_val = 0, - .max_val = 5000000, + .min_val = 2200000, + .max_val = 4500000, .units_str = "uV", }, [PVOLT1] = { @@ -506,8 +515,6 @@ static struct param_info params[] = { .num_regs = 1, .reg_to_unit_multiplier = 2, .reg_to_unit_divider = 1, - .min_val = 5, - .max_val = 1275, .units_str = "S", }, [PREST2] = { @@ -517,7 +524,7 @@ static struct param_info params[] = { .reg_to_unit_multiplier = 5, .reg_to_unit_divider = 1, .min_val = 5, - .max_val = 327675, + .max_val = 65535, .units_str = "mS", }, [PPULS2] = { @@ -526,8 +533,8 @@ static struct param_info params[] = { .num_regs = 2, .reg_to_unit_multiplier = 1600, /* converts to uC */ .reg_to_unit_divider = 1, - .min_val = 0, - .max_val = 104856000, + .min_val = 30000, + .max_val = 65535000, .units_str = "uC", }, [NREST2] = { @@ -538,7 +545,7 @@ static struct param_info params[] = { .reg_to_unit_divider = 1, .reg_to_unit_offset = -5, .min_val = 5, - .max_val = 1280, + .max_val = 255, .units_str = "mS", }, [NPULS2] = { @@ -547,18 +554,18 @@ static struct param_info params[] = { .num_regs = 1, .reg_to_unit_multiplier = 5, .reg_to_unit_divider = 1, - .min_val = 5, - .max_val = 1275, + .min_val = 0, + .max_val = 255, .units_str = "mS", }, [VLIM2] = { - .name = "VLIM1", + .name = "VLIM2", .start_addr = QNOVO_VLIM2_LSB_CTRL, .num_regs = 2, .reg_to_unit_multiplier = 610350, /* converts to nV */ .reg_to_unit_divider = 1, - .min_val = 0, - .max_val = 5000000, + .min_val = 2200000, + .max_val = 4500000, .units_str = "uV", }, [PVOLT2] = { @@ -591,6 +598,8 @@ static struct param_info params[] = { .num_regs = 1, .reg_to_unit_multiplier = 1, .reg_to_unit_divider = 1, + .min_val = 0, + .max_val = 255, .units_str = "pulses", }, [VMAX] = { @@ -648,30 +657,70 @@ static ssize_t ok_to_qnovo_show(struct class *c, struct class_attribute *attr, return snprintf(buf, PAGE_SIZE, "%d\n", chip->cs.ok_to_qnovo); } -static ssize_t enable_show(struct class *c, struct class_attribute *attr, +static ssize_t qnovo_enable_show(struct class *c, struct class_attribute *attr, char *ubuf) { struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); - int val; + int val = get_effective_result(chip->disable_votable); - val = get_client_vote(chip->disable_votable, USER_VOTER); - val = !val; - return snprintf(ubuf, PAGE_SIZE, "%d\n", val); + return snprintf(ubuf, PAGE_SIZE, "%d\n", !val); +} + +static ssize_t qnovo_enable_store(struct class *c, struct class_attribute *attr, + const char *ubuf, size_t count) +{ + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + unsigned long val; + + if (kstrtoul(ubuf, 0, &val)) + return -EINVAL; + + vote(chip->disable_votable, USER_VOTER, !val, 0); + + return count; +} + +static ssize_t pt_enable_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + int i = attr - qnovo_attributes; + struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); + u8 buf[2] = {0, 0}; + u16 regval; + int rc; + + rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs); + if (rc < 0) { + pr_err("Couldn't read %s rc = %d\n", params[i].name, rc); + return -EINVAL; + } + regval = buf[1] << 8 | buf[0]; + + return snprintf(ubuf, PAGE_SIZE, "%d\n", + (int)(regval & QNOVO_PTRAIN_EN_BIT)); } -static ssize_t enable_store(struct class *c, struct class_attribute *attr, +static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr, const char *ubuf, size_t count) { struct qnovo *chip = container_of(c, struct qnovo, qnovo_class); unsigned long val; - bool disable; + int rc = 0; - if (kstrtoul(ubuf, 10, &val)) + if (get_effective_result(chip->disable_votable)) return -EINVAL; - disable = !val; + if (kstrtoul(ubuf, 0, &val)) + return -EINVAL; + + rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, + (bool)val ? QNOVO_PTRAIN_EN_BIT : 0); + if (rc < 0) { + dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n", + (bool)val ? "enable" : "disable", rc); + return rc; + } - vote(chip->disable_votable, USER_VOTER, disable, 0); return count; } @@ -688,7 +737,7 @@ static ssize_t val_show(struct class *c, struct class_attribute *attr, if (i == FCC_REQUEST) val = chip->fcc_uA_request; - return snprintf(ubuf, PAGE_SIZE, "%d%s\n", val, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "%d\n", val); } static ssize_t val_store(struct class *c, struct class_attribute *attr, @@ -698,7 +747,10 @@ static ssize_t val_store(struct class *c, struct class_attribute *attr, int i = attr - qnovo_attributes; unsigned long val; - if (kstrtoul(ubuf, 10, &val)) + if (get_effective_result(chip->disable_votable)) + return -EINVAL; + + if (kstrtoul(ubuf, 0, &val)) return -EINVAL; if (i == FV_REQUEST) @@ -707,6 +759,8 @@ static ssize_t val_store(struct class *c, struct class_attribute *attr, if (i == FCC_REQUEST) chip->fcc_uA_request = val; + qnovo_batt_psy_update(chip, false); + return count; } @@ -726,8 +780,7 @@ static ssize_t reg_show(struct class *c, struct class_attribute *attr, } regval = buf[1] << 8 | buf[0]; - return snprintf(ubuf, PAGE_SIZE, "0x%04x%s\n", - regval, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "0x%04x\n", regval); } static ssize_t reg_store(struct class *c, struct class_attribute *attr, @@ -739,7 +792,7 @@ static ssize_t reg_store(struct class *c, struct class_attribute *attr, unsigned long val; int rc; - if (kstrtoul(ubuf, 16, &val)) + if (kstrtoul(ubuf, 0, &val)) return -EINVAL; buf[0] = val & 0xFF; @@ -774,7 +827,7 @@ static ssize_t time_show(struct class *c, struct class_attribute *attr, / params[i].reg_to_unit_divider) - params[i].reg_to_unit_offset; - return snprintf(ubuf, PAGE_SIZE, "%d%s\n", val, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "%d\n", val); } static ssize_t time_store(struct class *c, struct class_attribute *attr, @@ -787,7 +840,7 @@ static ssize_t time_store(struct class *c, struct class_attribute *attr, unsigned long val; int rc; - if (kstrtoul(ubuf, 10, &val)) + if (kstrtoul(ubuf, 0, &val)) return -EINVAL; if (val < params[i].min_val || val > params[i].max_val) { @@ -841,11 +894,10 @@ static ssize_t current_show(struct class *c, struct class_attribute *attr, gain = chip->internal_i_gain_mega; } - comp_val_nA = div_s64(regval_nA * gain, 1000000) + offset_nA; + comp_val_nA = div_s64(regval_nA * gain, 1000000) - offset_nA; comp_val_uA = div_s64(comp_val_nA, 1000); - return snprintf(ubuf, PAGE_SIZE, "%d%s\n", - comp_val_uA, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uA); } static ssize_t voltage_show(struct class *c, struct class_attribute *attr, @@ -875,8 +927,7 @@ static ssize_t voltage_show(struct class *c, struct class_attribute *attr, comp_val_nV = div_s64(regval_nV * gain, 1000000) + offset_nV; comp_val_uV = div_s64(comp_val_nV, 1000); - return snprintf(ubuf, PAGE_SIZE, "%d%s\n", - comp_val_uV, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uV); } static ssize_t voltage_store(struct class *c, struct class_attribute *attr, @@ -890,7 +941,7 @@ static ssize_t voltage_store(struct class *c, struct class_attribute *attr, s64 regval_nV; s64 gain, offset_nV; - if (kstrtoul(ubuf, 10, &val_uV)) + if (kstrtoul(ubuf, 0, &val_uV)) return -EINVAL; if (val_uV < params[i].min_val || val_uV > params[i].max_val) { @@ -947,8 +998,7 @@ static ssize_t coulomb_show(struct class *c, struct class_attribute *attr, gain = chip->internal_i_gain_mega; comp_val_uC = div_s64(regval_uC * gain, 1000000); - return snprintf(ubuf, PAGE_SIZE, "%d%s\n", - comp_val_uC, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uC); } static ssize_t coulomb_store(struct class *c, struct class_attribute *attr, @@ -962,7 +1012,7 @@ static ssize_t coulomb_store(struct class *c, struct class_attribute *attr, s64 regval; s64 gain; - if (kstrtoul(ubuf, 10, &val_uC)) + if (kstrtoul(ubuf, 0, &val_uC)) return -EINVAL; if (val_uC < params[i].min_val || val_uC > params[i].max_val) { @@ -1014,74 +1064,75 @@ static ssize_t batt_prop_show(struct class *c, struct class_attribute *attr, return -EINVAL; } - return snprintf(ubuf, PAGE_SIZE, "%d%s\n", - pval.intval, params[i].units_str); + return snprintf(ubuf, PAGE_SIZE, "%d\n", pval.intval); } static struct class_attribute qnovo_attributes[] = { [VER] = __ATTR_RO(version), [OK_TO_QNOVO] = __ATTR_RO(ok_to_qnovo), - [ENABLE] = __ATTR(enable, S_IRUGO | S_IWUSR, - enable_show, enable_store), - [FV_REQUEST] = __ATTR(fv_uV_request, S_IRUGO | S_IWUSR, + [QNOVO_ENABLE] = __ATTR_RW(qnovo_enable), + [PT_ENABLE] = __ATTR_RW(pt_enable), + [FV_REQUEST] = __ATTR(fv_uV_request, 0644, val_show, val_store), - [FCC_REQUEST] = __ATTR(fcc_uA_request, S_IRUGO | S_IWUSR, + [FCC_REQUEST] = __ATTR(fcc_uA_request, 0644, val_show, val_store), - [PE_CTRL_REG] = __ATTR(PE_CTRL_REG, S_IRUGO | S_IWUSR, - reg_show, reg_store), - [PE_CTRL2_REG] = __ATTR(PE_CTRL2_REG, S_IRUGO | S_IWUSR, + [PE_CTRL_REG] = __ATTR(PE_CTRL_REG, 0644, reg_show, reg_store), - [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, S_IRUGO | S_IWUSR, + [PE_CTRL2_REG] = __ATTR(PE_CTRL2_REG, 0644, reg_show, reg_store), - [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, S_IRUGO | S_IWUSR, - reg_show, reg_store), - [PREST1] = __ATTR(PREST1_mS, S_IRUGO | S_IWUSR, + [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, 0444, + reg_show, NULL), + [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, 0444, + reg_show, NULL), + [ERR_STS2_REG] = __ATTR(ERR_STS2_REG, 0444, + reg_show, NULL), + [PREST1] = __ATTR(PREST1_mS, 0644, time_show, time_store), - [PPULS1] = __ATTR(PPULS1_uC, S_IRUGO | S_IWUSR, + [PPULS1] = __ATTR(PPULS1_uC, 0644, coulomb_show, coulomb_store), - [NREST1] = __ATTR(NREST1_mS, S_IRUGO | S_IWUSR, + [NREST1] = __ATTR(NREST1_mS, 0644, time_show, time_store), - [NPULS1] = __ATTR(NPULS1_mS, S_IRUGO | S_IWUSR, + [NPULS1] = __ATTR(NPULS1_mS, 0644, time_show, time_store), - [PPCNT] = __ATTR(PPCNT, S_IRUGO | S_IWUSR, + [PPCNT] = __ATTR(PPCNT, 0644, time_show, time_store), - [VLIM1] = __ATTR(VLIM1_uV, S_IRUGO | S_IWUSR, + [VLIM1] = __ATTR(VLIM1_uV, 0644, voltage_show, voltage_store), - [PVOLT1] = __ATTR(PVOLT1_uV, S_IRUGO, + [PVOLT1] = __ATTR(PVOLT1_uV, 0444, voltage_show, NULL), - [PCUR1] = __ATTR(PCUR1_uA, S_IRUGO, + [PCUR1] = __ATTR(PCUR1_uA, 0444, current_show, NULL), - [PTTIME] = __ATTR(PTTIME_S, S_IRUGO, + [PTTIME] = __ATTR(PTTIME_S, 0444, time_show, NULL), - [PREST2] = __ATTR(PREST2_mS, S_IRUGO | S_IWUSR, + [PREST2] = __ATTR(PREST2_mS, 0644, time_show, time_store), - [PPULS2] = __ATTR(PPULS2_mS, S_IRUGO | S_IWUSR, + [PPULS2] = __ATTR(PPULS2_uC, 0644, coulomb_show, coulomb_store), - [NREST2] = __ATTR(NREST2_mS, S_IRUGO | S_IWUSR, + [NREST2] = __ATTR(NREST2_mS, 0644, time_show, time_store), - [NPULS2] = __ATTR(NPULS2_mS, S_IRUGO | S_IWUSR, + [NPULS2] = __ATTR(NPULS2_mS, 0644, time_show, time_store), - [VLIM2] = __ATTR(VLIM2_uV, S_IRUGO | S_IWUSR, + [VLIM2] = __ATTR(VLIM2_uV, 0644, voltage_show, voltage_store), - [PVOLT2] = __ATTR(PVOLT2_uV, S_IRUGO, + [PVOLT2] = __ATTR(PVOLT2_uV, 0444, voltage_show, NULL), - [RVOLT2] = __ATTR(RVOLT2_uV, S_IRUGO, + [RVOLT2] = __ATTR(RVOLT2_uV, 0444, voltage_show, NULL), - [PCUR2] = __ATTR(PCUR2_uA, S_IRUGO, + [PCUR2] = __ATTR(PCUR2_uA, 0444, current_show, NULL), - [SCNT] = __ATTR(SCNT, S_IRUGO | S_IWUSR, + [SCNT] = __ATTR(SCNT, 0644, time_show, time_store), - [VMAX] = __ATTR(VMAX_uV, S_IRUGO, + [VMAX] = __ATTR(VMAX_uV, 0444, voltage_show, NULL), - [SNUM] = __ATTR(SNUM, S_IRUGO | S_IWUSR, - time_show, time_store), - [VBATT] = __ATTR(VBATT_uV, S_IRUGO, + [SNUM] = __ATTR(SNUM, 0444, + time_show, NULL), + [VBATT] = __ATTR(VBATT_uV, 0444, batt_prop_show, NULL), - [IBATT] = __ATTR(IBATT_uA, S_IRUGO, + [IBATT] = __ATTR(IBATT_uA, 0444, batt_prop_show, NULL), - [BATTTEMP] = __ATTR(BATTTEMP_deciDegC, S_IRUGO, + [BATTTEMP] = __ATTR(BATTTEMP_deciDegC, 0444, batt_prop_show, NULL), - [BATTSOC] = __ATTR(BATTSOC, S_IRUGO, + [BATTSOC] = __ATTR(BATTSOC, 0444, batt_prop_show, NULL), __ATTR_NULL, }; @@ -1140,8 +1191,7 @@ static void get_chg_status(struct qnovo *chip, const struct chg_props *cp, { cs->ok_to_qnovo = false; - if (cp->charging && - (cp->usb_online || cp->dc_online)) + if (cp->charging && (cp->usb_online || cp->dc_online)) cs->ok_to_qnovo = true; } @@ -1157,17 +1207,10 @@ static void status_change_work(struct work_struct *work) get_chg_status(chip, &cp, &cs); if (cs.ok_to_qnovo ^ chip->cs.ok_to_qnovo) { - /* - * when it is not okay to Qnovo charge, disable both voters, - * so that when it becomes okay to Qnovo charge the user voter - * has to specifically enable its vote to being Qnovo charging - */ - if (!cs.ok_to_qnovo) { - vote(chip->disable_votable, OK_TO_QNOVO_VOTER, 1, 0); - vote(chip->disable_votable, USER_VOTER, 1, 0); - } else { - vote(chip->disable_votable, OK_TO_QNOVO_VOTER, 0, 0); - } + vote(chip->disable_votable, OK_TO_QNOVO_VOTER, + !cs.ok_to_qnovo, 0); + if (!cs.ok_to_qnovo) + vote(chip->disable_votable, USER_VOTER, true, 0); notify_uevent = true; } @@ -1197,8 +1240,6 @@ static irqreturn_t handle_ptrain_done(int irq, void *data) { struct qnovo *chip = data; - /* disable user voter here */ - vote(chip->disable_votable, USER_VOTER, 0, 0); kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE); return IRQ_HANDLED; } @@ -1211,7 +1252,14 @@ static int qnovo_hw_init(struct qnovo *chip) u8 vadc_offset, vadc_gain; u8 val; - vote(chip->disable_votable, USER_VOTER, 1, 0); + vote(chip->disable_votable, USER_VOTER, true, 0); + + val = 0; + rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1); + if (rc < 0) { + pr_err("Couldn't write iadc bitstream control rc = %d\n", rc); + return rc; + } rc = qnovo_read(chip, QNOVO_IADC_OFFSET_0, &iadc_offset_external, 1); if (rc < 0) { @@ -1219,12 +1267,28 @@ static int qnovo_hw_init(struct qnovo *chip) return rc; } + /* stored as an 8 bit 2's complement signed integer */ + val = -1 * iadc_offset_external; + rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1); + if (rc < 0) { + pr_err("Couldn't write iadc offset rc = %d\n", rc); + return rc; + } + rc = qnovo_read(chip, QNOVO_IADC_OFFSET_1, &iadc_offset_internal, 1); if (rc < 0) { pr_err("Couldn't read iadc internal offset rc = %d\n", rc); return rc; } + /* stored as an 8 bit 2's complement signed integer */ + val = -1 * iadc_offset_internal; + rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1); + if (rc < 0) { + pr_err("Couldn't write iadc offset rc = %d\n", rc); + return rc; + } + rc = qnovo_read(chip, QNOVO_IADC_GAIN_0, &iadc_gain_external, 1); if (rc < 0) { pr_err("Couldn't read iadc external gain rc = %d\n", rc); @@ -1249,53 +1313,20 @@ static int qnovo_hw_init(struct qnovo *chip) return rc; } - chip->external_offset_nA = (s64)iadc_offset_external * IADC_LSB_NA; - chip->internal_offset_nA = (s64)iadc_offset_internal * IADC_LSB_NA; - chip->offset_nV = (s64)vadc_offset * VADC_LSB_NA; + chip->external_offset_nA = (s64)(s8)iadc_offset_external * IADC_LSB_NA; + chip->internal_offset_nA = (s64)(s8)iadc_offset_internal * IADC_LSB_NA; + chip->offset_nV = (s64)(s8)vadc_offset * VADC_LSB_NA; chip->external_i_gain_mega - = 1000000000 + (s64)iadc_gain_external * GAIN_LSB_FACTOR; + = 1000000000 + (s64)(s8)iadc_gain_external * GAIN_LSB_FACTOR; chip->external_i_gain_mega = div_s64(chip->external_i_gain_mega, 1000); chip->internal_i_gain_mega - = 1000000000 + (s64)iadc_gain_internal * GAIN_LSB_FACTOR; + = 1000000000 + (s64)(s8)iadc_gain_internal * GAIN_LSB_FACTOR; chip->internal_i_gain_mega = div_s64(chip->internal_i_gain_mega, 1000); - chip->v_gain_mega = 1000000000 + (s64)vadc_gain * GAIN_LSB_FACTOR; + chip->v_gain_mega = 1000000000 + (s64)(s8)vadc_gain * GAIN_LSB_FACTOR; chip->v_gain_mega = div_s64(chip->v_gain_mega, 1000); - val = 0; - rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1); - if (rc < 0) { - pr_err("Couldn't write iadc bitsteam control rc = %d\n", rc); - return rc; - } - - rc = qnovo_read(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1); - if (rc < 0) { - pr_err("Couldn't read iadc offset rc = %d\n", rc); - return rc; - } - - val *= -1; - rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1); - if (rc < 0) { - pr_err("Couldn't write iadc offset rc = %d\n", rc); - return rc; - } - - rc = qnovo_read(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1); - if (rc < 0) { - pr_err("Couldn't read iadc offset rc = %d\n", rc); - return rc; - } - - val *= -1; - rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1); - if (rc < 0) { - pr_err("Couldn't write iadc offset rc = %d\n", rc); - return rc; - } - return 0; } @@ -1333,6 +1364,9 @@ static int qnovo_request_interrupts(struct qnovo *chip) irq_ptrain_done, rc); return rc; } + + enable_irq_wake(irq_ptrain_done); + return rc; } @@ -1414,6 +1448,8 @@ static int qnovo_probe(struct platform_device *pdev) goto unreg_notifier; } + device_init_wakeup(chip->dev, true); + return rc; unreg_notifier: diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index bf7aa7a36784..e8249163e948 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -11,6 +11,7 @@ */ #include <linux/debugfs.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -25,7 +26,7 @@ #include "smb-reg.h" #include "smb-lib.h" #include "storm-watch.h" -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define SMB2_DEFAULT_WPWR_UW 8000000 @@ -837,7 +838,9 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_QNOVO, POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_QNOVO, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TECHNOLOGY, @@ -857,6 +860,7 @@ static int smb2_batt_get_prop(struct power_supply *psy, { struct smb_charger *chg = power_supply_get_drvdata(psy); int rc = 0; + union power_supply_propval pval = {0, }; switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -881,7 +885,14 @@ static int smb2_batt_get_prop(struct power_supply *psy, rc = smblib_get_prop_system_temp_level(chg, val); break; case POWER_SUPPLY_PROP_CHARGER_TEMP: - rc = smblib_get_prop_charger_temp(chg, val); + /* do not query RRADC if charger is not present */ + rc = smblib_get_prop_usb_present(chg, &pval); + if (rc < 0) + pr_err("Couldn't get usb present rc=%d\n", rc); + + rc = -ENODATA; + if (pval.intval) + rc = smblib_get_prop_charger_temp(chg, val); break; case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX: rc = smblib_get_prop_charger_temp_max(chg, val); @@ -901,6 +912,9 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MAX: val->intval = get_client_vote(chg->fv_votable, DEFAULT_VOTER); break; + case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE: + rc = smblib_get_prop_charge_qnovo_enable(chg, val); + break; case POWER_SUPPLY_PROP_VOLTAGE_QNOVO: val->intval = chg->qnovo_fv_uv; break; @@ -976,12 +990,17 @@ static int smb2_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MAX: vote(chg->fv_votable, DEFAULT_VOTER, true, val->intval); break; + case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE: + rc = smblib_set_prop_charge_qnovo_enable(chg, val); + break; case POWER_SUPPLY_PROP_VOLTAGE_QNOVO: chg->qnovo_fv_uv = val->intval; rc = rerun_election(chg->fv_votable); break; case POWER_SUPPLY_PROP_CURRENT_QNOVO: chg->qnovo_fcc_ua = val->intval; + vote(chg->pl_disable_votable, PL_QNOVO_VOTER, + val->intval != -EINVAL && val->intval < 2000000, 0); rc = rerun_election(chg->fcc_votable); break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: @@ -1324,6 +1343,7 @@ static int smb2_disable_typec(struct smb_charger *chg) { int rc; + /* Move to typeC mode */ /* configure FSM in idle state */ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, TYPEC_DISABLE_CMD_BIT, TYPEC_DISABLE_CMD_BIT); @@ -1332,6 +1352,39 @@ static int smb2_disable_typec(struct smb_charger *chg) return rc; } + /* wait for FSM to enter idle state */ + msleep(200); + /* configure TypeC mode */ + rc = smblib_masked_write(chg, TYPE_C_CFG_REG, + TYPE_C_OR_U_USB_BIT, 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't enable micro USB mode rc=%d\n", rc); + return rc; + } + + /* wait for mode change before enabling FSM */ + usleep_range(10000, 11000); + /* release FSM from idle state */ + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_DISABLE_CMD_BIT, 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't release FSM rc=%d\n", rc); + return rc; + } + + /* wait for FSM to start */ + msleep(100); + /* move to uUSB mode */ + /* configure FSM in idle state */ + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_DISABLE_CMD_BIT, TYPEC_DISABLE_CMD_BIT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't put FSM in idle rc=%d\n", rc); + return rc; + } + + /* wait for FSM to enter idle state */ + msleep(200); /* configure micro USB mode */ rc = smblib_masked_write(chg, TYPE_C_CFG_REG, TYPE_C_OR_U_USB_BIT, TYPE_C_OR_U_USB_BIT); @@ -1340,6 +1393,8 @@ static int smb2_disable_typec(struct smb_charger *chg) return rc; } + /* wait for mode change before enabling FSM */ + usleep_range(10000, 11000); /* release FSM from idle state */ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, TYPEC_DISABLE_CMD_BIT, 0); @@ -1420,10 +1475,10 @@ static int smb2_init_hw(struct smb2 *chip) DEFAULT_VOTER, true, chip->dt.fv_uv); vote(chg->dc_icl_votable, DEFAULT_VOTER, true, chip->dt.dc_icl_ua); - vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER, - chip->dt.hvdcp_disable, 0); vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 0); + vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER, + chip->dt.hvdcp_disable, 0); vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, @@ -1489,13 +1544,6 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } - rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG, - QNOVO_PT_ENABLE_CMD_BIT, QNOVO_PT_ENABLE_CMD_BIT); - if (rc < 0) { - dev_err(chg->dev, "Couldn't enable qnovo rc=%d\n", rc); - return rc; - } - /* configure step charging */ rc = smb2_config_step_charging(chip); if (rc < 0) { @@ -1608,6 +1656,15 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } +static int smb2_post_init(struct smb2 *chip) +{ + struct smb_charger *chg = &chip->chg; + + rerun_election(chg->usb_irq_enable_votable); + + return 0; +} + static int smb2_chg_config_init(struct smb2 *chip) { struct smb_charger *chg = &chip->chg; @@ -2137,6 +2194,8 @@ static int smb2_probe(struct platform_device *pdev) goto cleanup; } + smb2_post_init(chip); + smb2_create_debugfs(chip); rc = smblib_get_prop_usb_present(chg, &val); diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 4f126854728f..50af1087278a 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -22,7 +22,7 @@ #include "smb-lib.h" #include "smb-reg.h" #include "storm-watch.h" -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define smblib_err(chg, fmt, ...) \ pr_err("%s: %s: " fmt, chg->name, \ @@ -161,39 +161,14 @@ static int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua) int smblib_icl_override(struct smb_charger *chg, bool override) { int rc; - bool override_status; - u8 stat; - u16 reg; - switch (chg->smb_version) { - case PMI8998_SUBTYPE: - reg = APSD_RESULT_STATUS_REG; - break; - case PM660_SUBTYPE: - reg = AICL_STATUS_REG; - break; - default: - smblib_dbg(chg, PR_MISC, "Unknown chip version=%x\n", - chg->smb_version); - return -EINVAL; - } - - rc = smblib_read(chg, reg, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read reg=%x rc=%d\n", reg, rc); - return rc; - } - override_status = (bool)(stat & ICL_OVERRIDE_LATCH_BIT); + rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG, + ICL_OVERRIDE_AFTER_APSD_BIT, + override ? ICL_OVERRIDE_AFTER_APSD_BIT : 0); + if (rc < 0) + smblib_err(chg, "Couldn't override ICL rc=%d\n", rc); - if (override != override_status) { - rc = smblib_masked_write(chg, CMD_APSD_REG, - ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT); - if (rc < 0) { - smblib_err(chg, "Couldn't override ICL rc=%d\n", rc); - return rc; - } - } - return 0; + return rc; } /******************** @@ -727,21 +702,6 @@ static void smblib_uusb_removal(struct smb_charger *chg) rc); } -static bool smblib_sysok_reason_usbin(struct smb_charger *chg) -{ - int rc; - u8 stat; - - rc = smblib_read(chg, SYSOK_REASON_STATUS_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't get SYSOK_REASON_STATUS rc=%d\n", rc); - /* assuming 'not usbin' in case of read failure */ - return false; - } - - return stat & SYSOK_REASON_USBIN_BIT; -} - void smblib_suspend_on_debug_battery(struct smb_charger *chg) { int rc; @@ -779,11 +739,32 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg) return 0; apsd_result = smblib_get_apsd_result(chg); - if ((apsd_result->pst == POWER_SUPPLY_TYPE_UNKNOWN) - || (apsd_result->pst == POWER_SUPPLY_TYPE_USB)) { - smblib_rerun_apsd(chg); + if ((apsd_result->pst != POWER_SUPPLY_TYPE_UNKNOWN) + && (apsd_result->pst != POWER_SUPPLY_TYPE_USB)) + /* if type is not usb or unknown no need to rerun apsd */ + return 0; + + /* fetch the DPDM regulator */ + if (!chg->dpdm_reg && of_get_property(chg->dev->of_node, + "dpdm-supply", NULL)) { + chg->dpdm_reg = devm_regulator_get(chg->dev, "dpdm"); + if (IS_ERR(chg->dpdm_reg)) { + smblib_err(chg, "Couldn't get dpdm regulator rc=%ld\n", + PTR_ERR(chg->dpdm_reg)); + chg->dpdm_reg = NULL; + } } + if (chg->dpdm_reg && !regulator_is_enabled(chg->dpdm_reg)) { + smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n"); + rc = regulator_enable(chg->dpdm_reg); + if (rc < 0) + smblib_err(chg, "Couldn't enable dpdm regulator rc=%d\n", + rc); + } + + smblib_rerun_apsd(chg); + return 0; } @@ -1098,16 +1079,6 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable, int rc; if (apsd_disable) { - /* Don't run APSD on CC debounce when APSD is disabled */ - rc = smblib_masked_write(chg, TYPE_C_CFG_REG, - APSD_START_ON_CC_BIT, - 0); - if (rc < 0) { - smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n", - rc); - return rc; - } - rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, AUTO_SRC_DETECT_BIT, 0); @@ -1123,15 +1094,6 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable, smblib_err(chg, "Couldn't enable APSD rc=%d\n", rc); return rc; } - - rc = smblib_masked_write(chg, TYPE_C_CFG_REG, - APSD_START_ON_CC_BIT, - APSD_START_ON_CC_BIT); - if (rc < 0) { - smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", - rc); - return rc; - } } return 0; @@ -1168,6 +1130,26 @@ static int smblib_hvdcp_hw_inov_dis_vote_callback(struct votable *votable, return rc; } +static int smblib_usb_irq_enable_vote_callback(struct votable *votable, + void *data, int enable, const char *client) +{ + struct smb_charger *chg = data; + + if (!chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq || + !chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq) + return 0; + + if (enable) { + enable_irq(chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq); + enable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq); + } else { + disable_irq(chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq); + disable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq); + } + + return 0; +} + /******************* * VCONN REGULATOR * * *****************/ @@ -1740,6 +1722,23 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg, return 0; } +int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + u8 stat; + + rc = smblib_read(chg, QNOVO_PT_ENABLE_CMD_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read QNOVO_PT_ENABLE_CMD rc=%d\n", + rc); + return rc; + } + + val->intval = (bool)(stat & QNOVO_PT_ENABLE_CMD_BIT); + return 0; +} + /*********************** * BATTERY PSY SETTERS * ***********************/ @@ -1804,6 +1803,22 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg, return 0; } +int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc = 0; + + rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG, + QNOVO_PT_ENABLE_CMD_BIT, + val->intval ? QNOVO_PT_ENABLE_CMD_BIT : 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't enable qnovo rc=%d\n", rc); + return rc; + } + + return rc; +} + int smblib_rerun_aicl(struct smb_charger *chg) { int rc, settled_icl_ua; @@ -2510,10 +2525,6 @@ int smblib_set_prop_usb_voltage_max(struct smb_charger *chg, } chg->voltage_max_uv = max_uv; - rc = smblib_rerun_aicl(chg); - if (rc < 0) - smblib_err(chg, "Couldn't re-run AICL rc=%d\n", rc); - return rc; } @@ -2533,6 +2544,7 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, vote(chg->apsd_disable_votable, PD_VOTER, pd_active, 0); vote(chg->pd_allowed_votable, PD_VOTER, pd_active, 0); + vote(chg->usb_irq_enable_votable, PD_VOTER, pd_active, 0); /* * VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line @@ -2568,6 +2580,9 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, return rc; } + /* since PD was found the cable must be non-legacy */ + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); + /* clear USB ICL vote for DCP_VOTER */ rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0); if (rc < 0) @@ -2685,12 +2700,6 @@ int smblib_reg_block_restore(struct smb_charger *chg, static struct reg_info cc2_detach_settings[] = { { - .reg = TYPE_C_CFG_REG, - .mask = APSD_START_ON_CC_BIT, - .val = 0, - .desc = "TYPE_C_CFG_REG", - }, - { .reg = TYPE_C_CFG_2_REG, .mask = TYPE_C_UFP_MODE_BIT | EN_TRY_SOURCE_MODE_BIT, .val = TYPE_C_UFP_MODE_BIT, @@ -3370,6 +3379,10 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, /* could be a legacy cable, try doing hvdcp */ try_rerun_apsd_for_hvdcp(chg); + /* enable HDC and ICL irq for QC2/3 charger */ + if (qc_charger) + vote(chg->usb_irq_enable_votable, QC_VOTER, true, 0); + /* * HVDCP detection timeout done * If adapter is not QC2.0/QC3.0 - it is a plain old DCP. @@ -3406,6 +3419,37 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) +{ + switch (pst) { + case POWER_SUPPLY_TYPE_USB: + /* + * USB_PSY will vote to increase the current to 500/900mA once + * enumeration is done. Ensure that USB_PSY has at least voted + * for 100mA before releasing the LEGACY_UNKNOWN vote + */ + if (!is_client_vote_enabled(chg->usb_icl_votable, + USB_PSY_VOTER)) + vote(chg->usb_icl_votable, USB_PSY_VOTER, true, 100000); + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); + break; + case POWER_SUPPLY_TYPE_USB_CDP: + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000); + break; + case POWER_SUPPLY_TYPE_USB_DCP: + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000); + break; + case POWER_SUPPLY_TYPE_USB_HVDCP: + case POWER_SUPPLY_TYPE_USB_HVDCP_3: + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 3000000); + break; + default: + smblib_err(chg, "Unknown APSD %d; forcing 500mA\n", pst); + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 500000); + break; + } +} + #define HVDCP_DET_MS 2500 static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) { @@ -3415,6 +3459,10 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) return; apsd_result = smblib_update_usb_type(chg); + + if (!chg->pd_active) + smblib_force_legacy_icl(chg, apsd_result->pst); + switch (apsd_result->bit) { case SDP_CHARGER_BIT: case CDP_CHARGER_BIT: @@ -3495,6 +3543,9 @@ static void typec_source_removal(struct smb_charger *chg) { int rc; + /* reset legacy unknown vote */ + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); + /* reset both usbin current and voltage votes */ vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0); vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0); @@ -3548,6 +3599,15 @@ static void typec_source_removal(struct smb_charger *chg) static void typec_source_insertion(struct smb_charger *chg) { + /* + * at any time we want LEGACY_UNKNOWN, PD, or USB_PSY to be voting for + * ICL, so vote LEGACY_UNKNOWN here if none of the above three have + * casted their votes + */ + if (!is_client_vote_enabled(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER) + && !is_client_vote_enabled(chg->usb_icl_votable, PD_VOTER) + && !is_client_vote_enabled(chg->usb_icl_votable, USB_PSY_VOTER)) + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000); } static void typec_sink_insertion(struct smb_charger *chg) @@ -3568,11 +3628,13 @@ static void typec_sink_removal(struct smb_charger *chg) static void smblib_handle_typec_removal(struct smb_charger *chg) { + int rc; + vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0); - vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER, true, 0); - vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0); vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0); + vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0); + vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0); /* reset votes from vbus_cc_short */ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, @@ -3590,10 +3652,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->pulse_cnt = 0; chg->usb_icl_delta_ua = 0; - chg->usb_ever_removed = true; + /* enable APSD CC trigger for next insertion */ + rc = smblib_masked_write(chg, TYPE_C_CFG_REG, + APSD_START_ON_CC_BIT, APSD_START_ON_CC_BIT); + if (rc < 0) + smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", rc); smblib_update_usb_type(chg); - typec_source_removal(chg); typec_sink_removal(chg); } @@ -3601,12 +3666,16 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) static void smblib_handle_typec_insertion(struct smb_charger *chg, bool sink_attached, bool legacy_cable) { - int rp; - bool vbus_cc_short = false; - bool valid_legacy_cable; + int rp, rc; vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0); + /* disable APSD CC trigger since CC is attached */ + rc = smblib_masked_write(chg, TYPE_C_CFG_REG, APSD_START_ON_CC_BIT, 0); + if (rc < 0) + smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n", + rc); + if (sink_attached) { typec_source_removal(chg); typec_sink_insertion(chg); @@ -3615,25 +3684,18 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg, typec_sink_removal(chg); } - valid_legacy_cable = legacy_cable && - (chg->usb_ever_removed || !smblib_sysok_reason_usbin(chg)); - vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER, - valid_legacy_cable, 0); - - if (valid_legacy_cable) { - rp = smblib_get_prop_ufp_mode(chg); - if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH - || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) { - vbus_cc_short = true; - smblib_err(chg, "Disabling PD and HVDCP, VBUS-CC shorted, rp = %d found\n", - rp); - } + rp = smblib_get_prop_ufp_mode(chg); + if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH + || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) { + smblib_dbg(chg, PR_MISC, "VBUS & CC could be shorted; keeping HVDCP disabled\n"); + /* HVDCP is not going to be enabled; enable parallel */ + vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0); + vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, + true, 0); + } else { + vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, + false, 0); } - - vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, - vbus_cc_short, 0); - vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER, - vbus_cc_short, 0); } static void smblib_handle_typec_debounce_done(struct smb_charger *chg, @@ -4255,6 +4317,15 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } + chg->usb_irq_enable_votable = create_votable("USB_IRQ_DISABLE", + VOTE_SET_ANY, + smblib_usb_irq_enable_vote_callback, + chg); + if (IS_ERR(chg->usb_irq_enable_votable)) { + rc = PTR_ERR(chg->usb_irq_enable_votable); + return rc; + } + return rc; } diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index d6afca010502..32a1c29bb376 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -32,10 +32,12 @@ enum print_reason { #define USER_VOTER "USER_VOTER" #define PD_VOTER "PD_VOTER" #define DCP_VOTER "DCP_VOTER" +#define QC_VOTER "QC_VOTER" #define PL_USBIN_USBIN_VOTER "PL_USBIN_USBIN_VOTER" #define USB_PSY_VOTER "USB_PSY_VOTER" #define PL_TAPER_WORK_RUNNING_VOTER "PL_TAPER_WORK_RUNNING_VOTER" #define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER" +#define PL_QNOVO_VOTER "PL_QNOVO_VOTER" #define USBIN_I_VOTER "USBIN_I_VOTER" #define USBIN_V_VOTER "USBIN_V_VOTER" #define CHG_STATE_VOTER "CHG_STATE_VOTER" @@ -47,7 +49,6 @@ enum print_reason { #define PD_DISALLOWED_INDIRECT_VOTER "PD_DISALLOWED_INDIRECT_VOTER" #define PD_HARD_RESET_VOTER "PD_HARD_RESET_VOTER" #define VBUS_CC_SHORT_VOTER "VBUS_CC_SHORT_VOTER" -#define LEGACY_CABLE_VOTER "LEGACY_CABLE_VOTER" #define PD_INACTIVE_VOTER "PD_INACTIVE_VOTER" #define BOOST_BACK_VOTER "BOOST_BACK_VOTER" #define HVDCP_INDIRECT_VOTER "HVDCP_INDIRECT_VOTER" @@ -58,6 +59,7 @@ enum print_reason { #define CTM_VOTER "CTM_VOTER" #define SW_QC3_VOTER "SW_QC3_VOTER" #define AICL_RERUN_VOTER "AICL_RERUN_VOTER" +#define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER" #define VCONN_MAX_ATTEMPTS 3 #define OTG_MAX_ATTEMPTS 3 @@ -272,6 +274,7 @@ struct smb_charger { struct votable *hvdcp_enable_votable; struct votable *apsd_disable_votable; struct votable *hvdcp_hw_inov_dis_votable; + struct votable *usb_irq_enable_votable; /* work */ struct work_struct bms_update_work; @@ -312,10 +315,10 @@ struct smb_charger { u32 wa_flags; enum cc2_sink_type cc2_sink_detach_flag; int boost_current_ua; + int temp_speed_reading_count; /* extcon for VBUS / ID notification to USB for uUSB */ struct extcon_dev *extcon; - bool usb_ever_removed; int icl_reduction_ua; @@ -455,6 +458,8 @@ int smblib_get_prop_charger_temp_max(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_die_health(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg, + union power_supply_propval *val); int smblib_set_prop_pd_current_max(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_usb_current_max(struct smb_charger *chg, @@ -475,6 +480,8 @@ int smblib_get_prop_slave_current_now(struct smb_charger *chg, union power_supply_propval *val); int smblib_set_prop_ship_mode(struct smb_charger *chg, const union power_supply_propval *val); +int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg, + const union power_supply_propval *val); void smblib_suspend_on_debug_battery(struct smb_charger *chg); int smblib_rerun_apsd_if_required(struct smb_charger *chg); int smblib_get_prop_fcc_delta(struct smb_charger *chg, diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h index 54b6b38d134b..f7c13390d477 100644 --- a/drivers/power/supply/qcom/smb-reg.h +++ b/drivers/power/supply/qcom/smb-reg.h @@ -628,6 +628,7 @@ enum { #define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65) #define USBIN_OV_CH_LOAD_OPTION_BIT BIT(7) +#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4) #define USBIN_ICL_OPTIONS_REG (USBIN_BASE + 0x66) #define CFG_USB3P0_SEL_BIT BIT(2) diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index 1c7c1e78699f..c13f5103b02e 100644 --- a/drivers/power/supply/qcom/smb138x-charger.c +++ b/drivers/power/supply/qcom/smb138x-charger.c @@ -28,7 +28,7 @@ #include "smb-reg.h" #include "smb-lib.h" #include "storm-watch.h" -#include "pmic-voter.h" +#include <linux/pmic-voter.h> #define SMB138X_DEFAULT_FCC_UA 1000000 #define SMB138X_DEFAULT_ICL_UA 1500000 @@ -45,6 +45,7 @@ #define STACKED_DIODE_EN_BIT BIT(2) #define TDIE_AVG_COUNT 10 +#define MAX_SPEED_READING_TIMES 5 enum { OOB_COMP_WA_BIT = BIT(0), @@ -95,6 +96,7 @@ struct smb_dt_props { int dc_icl_ua; int chg_temp_max_mdegc; int connector_temp_max_mdegc; + int pl_mode; }; struct smb138x { @@ -126,8 +128,16 @@ static int smb138x_get_prop_charger_temp(struct smb138x *chip, union power_supply_propval pval; int rc = 0, avg = 0, i; struct smb_charger *chg = &chip->chg; + int die_avg_count; - for (i = 0; i < TDIE_AVG_COUNT; i++) { + if (chg->temp_speed_reading_count < MAX_SPEED_READING_TIMES) { + chg->temp_speed_reading_count++; + die_avg_count = 1; + } else { + die_avg_count = TDIE_AVG_COUNT; + } + + for (i = 0; i < die_avg_count; i++) { pval.intval = 0; rc = smblib_get_prop_charger_temp(chg, &pval); if (rc < 0) { @@ -137,7 +147,7 @@ static int smb138x_get_prop_charger_temp(struct smb138x *chip, } avg += pval.intval; } - val->intval = avg / TDIE_AVG_COUNT; + val->intval = avg / die_avg_count; return rc; } @@ -152,6 +162,11 @@ static int smb138x_parse_dt(struct smb138x *chip) return -EINVAL; } + rc = of_property_read_u32(node, + "qcom,parallel-mode", &chip->dt.pl_mode); + if (rc < 0) + chip->dt.pl_mode = POWER_SUPPLY_PL_USBMID_USBMID; + chip->dt.suspend_input = of_property_read_bool(node, "qcom,suspend-input"); @@ -579,7 +594,7 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, val->strval = "smb138x"; break; case POWER_SUPPLY_PROP_PARALLEL_MODE: - val->intval = POWER_SUPPLY_PL_USBMID_USBMID; + val->intval = chip->dt.pl_mode; break; case POWER_SUPPLY_PROP_CONNECTOR_HEALTH: val->intval = smb138x_get_prop_connector_health(chip); @@ -1449,6 +1464,15 @@ static int smb138x_slave_probe(struct smb138x *chip) goto cleanup; } + if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) { + rc = smb138x_init_vbus_regulator(chip); + if (rc < 0) { + pr_err("Couldn't initialize vbus regulator rc=%d\n", + rc); + return rc; + } + } + rc = smb138x_init_parallel_psy(chip); if (rc < 0) { pr_err("Couldn't initialize parallel psy rc=%d\n", rc); @@ -1473,6 +1497,8 @@ cleanup: smblib_deinit(chg); if (chip->parallel_psy) power_supply_unregister(chip->parallel_psy); + if (chg->vbus_vreg && chg->vbus_vreg->rdev) + regulator_unregister(chg->vbus_vreg->rdev); return rc; } diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 117fccf7934a..01a6a83f625d 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -65,7 +65,6 @@ #define PCA9685_MAXCHAN 0x10 #define LED_FULL (1 << 4) -#define MODE1_RESTART (1 << 7) #define MODE1_SLEEP (1 << 4) #define MODE2_INVRT (1 << 4) #define MODE2_OUTDRV (1 << 2) @@ -117,16 +116,6 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, udelay(500); pca->period_ns = period_ns; - - /* - * If the duty cycle did not change, restart PWM with - * the same duty cycle to period ratio and return. - */ - if (duty_ns == pca->duty_ns) { - regmap_update_bits(pca->regmap, PCA9685_MODE1, - MODE1_RESTART, 0x1); - return 0; - } } else { dev_err(chip->dev, "prescaler not set: period out of bounds!\n"); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index a878dc6a97db..4f9de7fda612 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2931,7 +2931,8 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, goto out2; if (rdev->supply && (rdev->desc->min_dropout_uV || - !rdev->desc->ops->get_voltage)) { + !(rdev->desc->ops->get_voltage || + rdev->desc->ops->get_voltage_sel))) { int current_supply_uV; int selector; @@ -4704,12 +4705,13 @@ static void regulator_summary_show_subtree(struct seq_file *s, seq_puts(s, "\n"); list_for_each_entry(consumer, &rdev->consumer_list, list) { - if (consumer->dev->class == ®ulator_class) + if (consumer->dev && consumer->dev->class == ®ulator_class) continue; seq_printf(s, "%*s%-*s ", (level + 1) * 3 + 1, "", - 30 - (level + 1) * 3, dev_name(consumer->dev)); + 30 - (level + 1) * 3, + consumer->dev ? dev_name(consumer->dev) : "deviceless"); switch (rdev->desc->type) { case REGULATOR_VOLTAGE: diff --git a/drivers/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c index 77e8bf4b9895..9a20b6925877 100644 --- a/drivers/regulator/cpr3-hmss-regulator.c +++ b/drivers/regulator/cpr3-hmss-regulator.c @@ -87,8 +87,9 @@ struct cpr3_msm8996_hmss_fuses { /* * Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0. * Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1. + * Fuse combos 16 - 23 map to CPR fusing revision 0 - 7 with speed bin fuse = 2. */ -#define CPR3_MSM8996_HMSS_FUSE_COMBO_COUNT 16 +#define CPR3_MSM8996_HMSS_FUSE_COMBO_COUNT 24 /* * Constants which define the name of each fuse corner. Note that no actual @@ -420,6 +421,9 @@ static const int msm8996_vdd_mx_fuse_ret_volt[] = { #define MSM8996_HMSS_AGING_SENSOR_ID 11 #define MSM8996_HMSS_AGING_BYPASS_MASK0 (GENMASK(7, 0) & ~BIT(3)) +/* Use scaled gate count (GCNT) for aging measurements */ +#define MSM8996_HMSS_AGING_GCNT_SCALING_FACTOR 1500 + /** * cpr3_msm8996_hmss_use_voltage_offset_fuse() - return if this part utilizes * voltage offset fuses or not @@ -1541,6 +1545,8 @@ static int cpr3_hmss_init_aging(struct cpr3_controller *ctrl) ctrl->aging_sensor->sensor_id = MSM8996_HMSS_AGING_SENSOR_ID; ctrl->aging_sensor->bypass_mask[0] = MSM8996_HMSS_AGING_BYPASS_MASK0; ctrl->aging_sensor->ro_scale = aging_ro_scale; + ctrl->aging_gcnt_scaling_factor + = MSM8996_HMSS_AGING_GCNT_SCALING_FACTOR; ctrl->aging_sensor->init_quot_diff = cpr3_convert_open_loop_voltage_fuse(0, diff --git a/drivers/regulator/cpr3-mmss-regulator.c b/drivers/regulator/cpr3-mmss-regulator.c index 41032dd3c15a..5a031f503b51 100644 --- a/drivers/regulator/cpr3-mmss-regulator.c +++ b/drivers/regulator/cpr3-mmss-regulator.c @@ -72,8 +72,9 @@ struct cpr3_msm8996_mmss_fuses { /* * Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0. * Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1. + * Fuse combos 16 - 23 map to CPR fusing revision 0 - 7 with speed bin fuse = 2. */ -#define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT 16 +#define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT 24 /* Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 */ #define CPR3_MSM8998_MMSS_FUSE_COMBO_COUNT 8 @@ -245,6 +246,9 @@ msm8998_v2_rev0_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = { #define MSM8996_MMSS_AGING_SENSOR_ID 29 #define MSM8996_MMSS_AGING_BYPASS_MASK0 (GENMASK(23, 0)) +/* Use scaled gate count (GCNT) for aging measurements */ +#define MSM8996_MMSS_AGING_GCNT_SCALING_FACTOR 1500 + #define MSM8998_MMSS_AGING_INIT_QUOT_DIFF_SCALE 1 #define MSM8998_MMSS_AGING_INIT_QUOT_DIFF_SIZE 8 @@ -808,6 +812,8 @@ static int cpr3_mmss_init_aging(struct cpr3_controller *ctrl) return -ENOMEM; ctrl->aging_sensor->ro_scale = aging_ro_scale; + ctrl->aging_gcnt_scaling_factor + = MSM8996_MMSS_AGING_GCNT_SCALING_FACTOR; if (cpr3_ctrl_is_msm8998(ctrl)) { ctrl->aging_sensor->sensor_id = MSM8998_MMSS_AGING_SENSOR_ID; diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c index 3ffe094fe53c..d131a8ea4144 100644 --- a/drivers/regulator/cpr3-regulator.c +++ b/drivers/regulator/cpr3-regulator.c @@ -317,6 +317,12 @@ */ #define CPRH_DELTA_QUOT_STEP_FACTOR 4 +/* + * The multiplier applied to scaling factor value used to derive GCNT + * for aging measurements. + */ +#define CPR3_AGING_GCNT_SCALING_UNITY 1000 + static DEFINE_MUTEX(cpr3_controller_list_mutex); static LIST_HEAD(cpr3_controller_list); static struct dentry *cpr3_debugfs_base; @@ -1011,7 +1017,8 @@ static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl) max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT | ((sdelta->allow_core_count_adj || sdelta->allow_boost) ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0) - | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop) + | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop + && sdelta->allow_core_count_adj) ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0) | (((ctrl->use_hw_closed_loop && !sdelta->allow_boost) || !ctrl->supports_hw_closed_loop) @@ -3563,7 +3570,12 @@ static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl, gcnt0_restore = cpr3_read(ctrl, CPR3_REG_GCNT(0)); gcnt1_restore = cpr3_read(ctrl, CPR3_REG_GCNT(1)); gcnt_ref = cpr3_regulator_get_gcnt(ctrl); - gcnt = gcnt_ref * 3 / 2; + + gcnt = gcnt_ref; + if (ctrl->aging_gcnt_scaling_factor) + gcnt = gcnt_ref * ctrl->aging_gcnt_scaling_factor + / CPR3_AGING_GCNT_SCALING_UNITY; + cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt); cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt); diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h index 1ac0f7b816b3..99c86e7b70c9 100644 --- a/drivers/regulator/cpr3-regulator.h +++ b/drivers/regulator/cpr3-regulator.h @@ -702,6 +702,13 @@ struct cpr3_panic_regs_info { * @aging_possible_val: Optional value that the masked aging_possible_reg * register must have in order for a CPR aging measurement * to be possible. + * @aging_gcnt_scaling_factor: The scaling factor used to derive the gate count + * used for aging measurements. This value is divided by + * 1000 when used as shown in the below equation: + * Aging_GCNT = GCNT_REF * scaling_factor / 1000. + * For example, a value of 1500 specifies that the gate + * count (GCNT) used for aging measurement should be 1.5 + * times of reference gate count (GCNT_REF). * @step_quot_fixed: Fixed step quotient value used for target quotient * adjustment if use_dynamic_step_quot is not set. * This parameter is only relevant for CPR4 controllers @@ -827,6 +834,7 @@ struct cpr3_controller { int aging_sensor_count; u32 aging_possible_mask; u32 aging_possible_val; + u32 aging_gcnt_scaling_factor; u32 step_quot_fixed; u32 initial_temp_band; diff --git a/drivers/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c index c75beec59a79..484f0412addf 100644 --- a/drivers/regulator/spm-regulator.c +++ b/drivers/regulator/spm-regulator.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -118,6 +118,12 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, #define QPNP_FTS2_STEP_MARGIN_NUM 4 #define QPNP_FTS2_STEP_MARGIN_DEN 5 +/* + * Settling delay for FTS2.5 + * Warm-up=20uS, 0-10% & 90-100% non-linear V-ramp delay = 50uS + */ +#define FTS2P5_SETTLING_DELAY_US 70 + /* VSET value to decide the range of ULT SMPS */ #define ULT_SMPS_RANGE_SPLIT 0x60 @@ -268,6 +274,7 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV) unsigned vlevel = spm_regulator_uv_to_vlevel(vreg, uV); bool spm_failed = false; int rc = 0; + u32 slew_delay; u8 reg; if (likely(!vreg->bypass_spm)) { @@ -295,7 +302,16 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV) if (uV > vreg->last_set_uV) { /* Wait for voltage stepping to complete. */ - udelay(DIV_ROUND_UP(uV - vreg->last_set_uV, vreg->step_rate)); + slew_delay = DIV_ROUND_UP(uV - vreg->last_set_uV, + vreg->step_rate); + if (vreg->regulator_type == QPNP_TYPE_FTS2p5) + slew_delay += FTS2P5_SETTLING_DELAY_US; + udelay(slew_delay); + } else if (vreg->regulator_type == QPNP_TYPE_FTS2p5) { + /* add the ramp-down delay */ + slew_delay = DIV_ROUND_UP(vreg->last_set_uV - uV, + vreg->step_rate) + FTS2P5_SETTLING_DELAY_US; + udelay(slew_delay); } vreg->last_set_uV = uV; diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c index 7d2ae3e9e942..342f5da79975 100644 --- a/drivers/regulator/stw481x-vmmc.c +++ b/drivers/regulator/stw481x-vmmc.c @@ -47,7 +47,8 @@ static struct regulator_desc vmmc_regulator = { .volt_table = stw481x_vmmc_voltages, .enable_time = 200, /* FIXME: look this up */ .enable_reg = STW_CONF1, - .enable_mask = STW_CONF1_PDN_VMMC, + .enable_mask = STW_CONF1_PDN_VMMC | STW_CONF1_MMC_LS_STATUS, + .enable_val = STW_CONF1_PDN_VMMC, .vsel_reg = STW_CONF1, .vsel_mask = STW_CONF1_VMMC_MASK, }; diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 5836751b8203..9bb934ed2a7a 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -748,9 +748,23 @@ EXPORT_SYMBOL_GPL(rtc_irq_set_freq); */ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) { + struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); + struct rtc_time tm; + ktime_t now; + timer->enabled = 1; + __rtc_read_time(rtc, &tm); + now = rtc_tm_to_ktime(tm); + + /* Skip over expired timers */ + while (next) { + if (next->expires.tv64 >= now.tv64) + break; + next = timerqueue_iterate_next(next); + } + timerqueue_add(&rtc->timerqueue, &timer->node); - if (&timer->node == timerqueue_getnext(&rtc->timerqueue)) { + if (!next) { struct rtc_wkalrm alarm; int err; alarm.time = rtc_ktime_to_tm(timer->node.expires); diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index c169a2cd4727..e29cc9fca0bf 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -37,9 +37,11 @@ /* Control register */ #define SUN6I_LOSC_CTRL 0x0000 +#define SUN6I_LOSC_CTRL_KEY (0x16aa << 16) #define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9) #define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8) #define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7) +#define SUN6I_LOSC_CTRL_EXT_OSC BIT(0) #define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7) /* RTC */ @@ -114,13 +116,17 @@ struct sun6i_rtc_dev { void __iomem *base; int irq; unsigned long alarm; + + spinlock_t lock; }; static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) { struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; + irqreturn_t ret = IRQ_NONE; u32 val; + spin_lock(&chip->lock); val = readl(chip->base + SUN6I_ALRM_IRQ_STA); if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { @@ -129,10 +135,11 @@ static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); - return IRQ_HANDLED; + ret = IRQ_HANDLED; } + spin_unlock(&chip->lock); - return IRQ_NONE; + return ret; } static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) @@ -140,6 +147,7 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) u32 alrm_val = 0; u32 alrm_irq_val = 0; u32 alrm_wake_val = 0; + unsigned long flags; if (to) { alrm_val = SUN6I_ALRM_EN_CNT_EN; @@ -150,9 +158,11 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) chip->base + SUN6I_ALRM_IRQ_STA); } + spin_lock_irqsave(&chip->lock, flags); writel(alrm_val, chip->base + SUN6I_ALRM_EN); writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); + spin_unlock_irqrestore(&chip->lock, flags); } static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) @@ -191,11 +201,15 @@ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) { struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + unsigned long flags; u32 alrm_st; u32 alrm_en; + spin_lock_irqsave(&chip->lock, flags); alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); + spin_unlock_irqrestore(&chip->lock, flags); + wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN); wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); rtc_time_to_tm(chip->alarm, &wkalrm->time); @@ -356,6 +370,7 @@ static int sun6i_rtc_probe(struct platform_device *pdev) chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; + spin_lock_init(&chip->lock); platform_set_drvdata(pdev, chip); chip->dev = &pdev->dev; @@ -404,6 +419,10 @@ static int sun6i_rtc_probe(struct platform_device *pdev) /* disable alarm wakeup */ writel(0, chip->base + SUN6I_ALARM_CONFIG); + /* switch to the external, more precise, oscillator */ + writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC, + chip->base + SUN6I_LOSC_CTRL); + chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev, &sun6i_rtc_ops, THIS_MODULE); if (IS_ERR(chip->rtc)) { diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 94a8f4ab57bc..ae1dc37e4068 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -892,7 +892,7 @@ dcssblk_direct_access (struct block_device *bdev, sector_t secnum, dev_info = bdev->bd_disk->private_data; if (!dev_info) return -ENODEV; - dev_sz = dev_info->end - dev_info->start; + dev_sz = dev_info->end - dev_info->start + 1; offset = secnum * 512; addr = (void *) (dev_info->start + offset); *pfn = virt_to_phys(addr) >> PAGE_SHIFT; diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index 799c1524c779..4b8de3e70cf2 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -872,7 +872,7 @@ static int __init vmlogrdr_init(void) goto cleanup; for (i=0; i < MAXMINOR; ++i ) { - sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL); + sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!sys_ser[i].buffer) { rc = -ENOMEM; break; diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 5d06253c2a7a..30e9fbbff051 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -147,11 +147,11 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) struct qdio_q *q; int i; - for_each_input_queue(irq, q, i) { - if (!references_shared_dsci(irq) && - has_multiple_inq_on_dsci(irq)) - xchg(q->irq_ptr->dsci, 0); + if (!references_shared_dsci(irq) && + has_multiple_inq_on_dsci(irq)) + xchg(irq->dsci, 0); + for_each_input_queue(irq, q, i) { if (q->u.in.queue_start_poll) { /* skip if polling is enabled or already in work */ if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED, diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 581001989937..d5bf36ec8a75 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -289,11 +289,12 @@ void zfcp_dbf_rec_trig(char *tag, struct zfcp_adapter *adapter, /** - * zfcp_dbf_rec_run - trace event related to running recovery + * zfcp_dbf_rec_run_lvl - trace event related to running recovery + * @level: trace level to be used for event * @tag: identifier for event * @erp: erp_action running */ -void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp) +void zfcp_dbf_rec_run_lvl(int level, char *tag, struct zfcp_erp_action *erp) { struct zfcp_dbf *dbf = erp->adapter->dbf; struct zfcp_dbf_rec *rec = &dbf->rec_buf; @@ -319,11 +320,21 @@ void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp) else rec->u.run.rec_count = atomic_read(&erp->adapter->erp_counter); - debug_event(dbf->rec, 1, rec, sizeof(*rec)); + debug_event(dbf->rec, level, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->rec_lock, flags); } /** + * zfcp_dbf_rec_run - trace event related to running recovery + * @tag: identifier for event + * @erp: erp_action running + */ +void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp) +{ + zfcp_dbf_rec_run_lvl(1, tag, erp); +} + +/** * zfcp_dbf_rec_run_wka - trace wka port event with info like running recovery * @tag: identifier for event * @wka_port: well known address port diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index 36d07584271d..db186d44cfaf 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -2,7 +2,7 @@ * zfcp device driver * debug feature declarations * - * Copyright IBM Corp. 2008, 2015 + * Copyright IBM Corp. 2008, 2016 */ #ifndef ZFCP_DBF_H @@ -283,6 +283,30 @@ struct zfcp_dbf { struct zfcp_dbf_scsi scsi_buf; }; +/** + * zfcp_dbf_hba_fsf_resp_suppress - true if we should not trace by default + * @req: request that has been completed + * + * Returns true if FCP response with only benign residual under count. + */ +static inline +bool zfcp_dbf_hba_fsf_resp_suppress(struct zfcp_fsf_req *req) +{ + struct fsf_qtcb *qtcb = req->qtcb; + u32 fsf_stat = qtcb->header.fsf_status; + struct fcp_resp *fcp_rsp; + u8 rsp_flags, fr_status; + + if (qtcb->prefix.qtcb_type != FSF_IO_COMMAND) + return false; /* not an FCP response */ + fcp_rsp = (struct fcp_resp *)&qtcb->bottom.io.fcp_rsp; + rsp_flags = fcp_rsp->fr_flags; + fr_status = fcp_rsp->fr_status; + return (fsf_stat == FSF_FCP_RSP_AVAILABLE) && + (rsp_flags == FCP_RESID_UNDER) && + (fr_status == SAM_STAT_GOOD); +} + static inline void zfcp_dbf_hba_fsf_resp(char *tag, int level, struct zfcp_fsf_req *req) { @@ -304,7 +328,9 @@ void zfcp_dbf_hba_fsf_response(struct zfcp_fsf_req *req) zfcp_dbf_hba_fsf_resp("fs_perr", 1, req); } else if (qtcb->header.fsf_status != FSF_GOOD) { - zfcp_dbf_hba_fsf_resp("fs_ferr", 1, req); + zfcp_dbf_hba_fsf_resp("fs_ferr", + zfcp_dbf_hba_fsf_resp_suppress(req) + ? 5 : 1, req); } else if ((req->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) || (req->fsf_command == FSF_QTCB_OPEN_LUN)) { @@ -388,4 +414,15 @@ void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag) _zfcp_dbf_scsi(tmp_tag, 1, scmnd, NULL); } +/** + * zfcp_dbf_scsi_nullcmnd() - trace NULLify of SCSI command in dev/tgt-reset. + * @scmnd: SCSI command that was NULLified. + * @fsf_req: request that owned @scmnd. + */ +static inline void zfcp_dbf_scsi_nullcmnd(struct scsi_cmnd *scmnd, + struct zfcp_fsf_req *fsf_req) +{ + _zfcp_dbf_scsi("scfc__1", 3, scmnd, fsf_req); +} + #endif /* ZFCP_DBF_H */ diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index a59d678125bd..7ccfce559034 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -3,7 +3,7 @@ * * Error Recovery Procedures (ERP). * - * Copyright IBM Corp. 2002, 2015 + * Copyright IBM Corp. 2002, 2016 */ #define KMSG_COMPONENT "zfcp" @@ -1204,6 +1204,62 @@ static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action) } } +/** + * zfcp_erp_try_rport_unblock - unblock rport if no more/new recovery + * @port: zfcp_port whose fc_rport we should try to unblock + */ +static void zfcp_erp_try_rport_unblock(struct zfcp_port *port) +{ + unsigned long flags; + struct zfcp_adapter *adapter = port->adapter; + int port_status; + struct Scsi_Host *shost = adapter->scsi_host; + struct scsi_device *sdev; + + write_lock_irqsave(&adapter->erp_lock, flags); + port_status = atomic_read(&port->status); + if ((port_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 || + (port_status & (ZFCP_STATUS_COMMON_ERP_INUSE | + ZFCP_STATUS_COMMON_ERP_FAILED)) != 0) { + /* new ERP of severity >= port triggered elsewhere meanwhile or + * local link down (adapter erp_failed but not clear unblock) + */ + zfcp_dbf_rec_run_lvl(4, "ertru_p", &port->erp_action); + write_unlock_irqrestore(&adapter->erp_lock, flags); + return; + } + spin_lock(shost->host_lock); + __shost_for_each_device(sdev, shost) { + struct zfcp_scsi_dev *zsdev = sdev_to_zfcp(sdev); + int lun_status; + + if (zsdev->port != port) + continue; + /* LUN under port of interest */ + lun_status = atomic_read(&zsdev->status); + if ((lun_status & ZFCP_STATUS_COMMON_ERP_FAILED) != 0) + continue; /* unblock rport despite failed LUNs */ + /* LUN recovery not given up yet [maybe follow-up pending] */ + if ((lun_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 || + (lun_status & ZFCP_STATUS_COMMON_ERP_INUSE) != 0) { + /* LUN blocked: + * not yet unblocked [LUN recovery pending] + * or meanwhile blocked [new LUN recovery triggered] + */ + zfcp_dbf_rec_run_lvl(4, "ertru_l", &zsdev->erp_action); + spin_unlock(shost->host_lock); + write_unlock_irqrestore(&adapter->erp_lock, flags); + return; + } + } + /* now port has no child or all children have completed recovery, + * and no ERP of severity >= port was meanwhile triggered elsewhere + */ + zfcp_scsi_schedule_rport_register(port); + spin_unlock(shost->host_lock); + write_unlock_irqrestore(&adapter->erp_lock, flags); +} + static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) { struct zfcp_adapter *adapter = act->adapter; @@ -1214,6 +1270,7 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) case ZFCP_ERP_ACTION_REOPEN_LUN: if (!(act->status & ZFCP_STATUS_ERP_NO_REF)) scsi_device_put(sdev); + zfcp_erp_try_rport_unblock(port); break; case ZFCP_ERP_ACTION_REOPEN_PORT: @@ -1224,7 +1281,7 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) */ if (act->step != ZFCP_ERP_STEP_UNINITIALIZED) if (result == ZFCP_ERP_SUCCEEDED) - zfcp_scsi_schedule_rport_register(port); + zfcp_erp_try_rport_unblock(port); /* fall through */ case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: put_device(&port->dev); diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index c8fed9fa1cca..21c8c689b02b 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -3,7 +3,7 @@ * * External function declarations. * - * Copyright IBM Corp. 2002, 2015 + * Copyright IBM Corp. 2002, 2016 */ #ifndef ZFCP_EXT_H @@ -35,6 +35,8 @@ extern void zfcp_dbf_adapter_unregister(struct zfcp_adapter *); extern void zfcp_dbf_rec_trig(char *, struct zfcp_adapter *, struct zfcp_port *, struct scsi_device *, u8, u8); extern void zfcp_dbf_rec_run(char *, struct zfcp_erp_action *); +extern void zfcp_dbf_rec_run_lvl(int level, char *tag, + struct zfcp_erp_action *erp); extern void zfcp_dbf_rec_run_wka(char *, struct zfcp_fc_wka_port *, u64); extern void zfcp_dbf_hba_fsf_uss(char *, struct zfcp_fsf_req *); extern void zfcp_dbf_hba_fsf_res(char *, int, struct zfcp_fsf_req *); diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 75f820ca17b7..27ff38f839fc 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1583,7 +1583,7 @@ out: int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port) { struct zfcp_qdio *qdio = wka_port->adapter->qdio; - struct zfcp_fsf_req *req = NULL; + struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_irq(&qdio->req_q_lock); @@ -1612,7 +1612,7 @@ int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port) zfcp_fsf_req_free(req); out: spin_unlock_irq(&qdio->req_q_lock); - if (req && !IS_ERR(req)) + if (!retval) zfcp_dbf_rec_run_wka("fsowp_1", wka_port, req->req_id); return retval; } @@ -1638,7 +1638,7 @@ static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req) int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port) { struct zfcp_qdio *qdio = wka_port->adapter->qdio; - struct zfcp_fsf_req *req = NULL; + struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_irq(&qdio->req_q_lock); @@ -1667,7 +1667,7 @@ int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port) zfcp_fsf_req_free(req); out: spin_unlock_irq(&qdio->req_q_lock); - if (req && !IS_ERR(req)) + if (!retval) zfcp_dbf_rec_run_wka("fscwp_1", wka_port, req->req_id); return retval; } diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index be1c04b334c5..ea3c76ac0de1 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -3,7 +3,7 @@ * * Interface to the FSF support functions. * - * Copyright IBM Corp. 2002, 2015 + * Copyright IBM Corp. 2002, 2016 */ #ifndef FSF_H @@ -78,6 +78,7 @@ #define FSF_APP_TAG_CHECK_FAILURE 0x00000082 #define FSF_REF_TAG_CHECK_FAILURE 0x00000083 #define FSF_ADAPTER_STATUS_AVAILABLE 0x000000AD +#define FSF_FCP_RSP_AVAILABLE 0x000000AF #define FSF_UNKNOWN_COMMAND 0x000000E2 #define FSF_UNKNOWN_OP_SUBTYPE 0x000000E3 #define FSF_INVALID_COMMAND_OPTION 0x000000E5 diff --git a/drivers/s390/scsi/zfcp_reqlist.h b/drivers/s390/scsi/zfcp_reqlist.h index 7c2c6194dfca..703fce59befe 100644 --- a/drivers/s390/scsi/zfcp_reqlist.h +++ b/drivers/s390/scsi/zfcp_reqlist.h @@ -4,7 +4,7 @@ * Data structure and helper functions for tracking pending FSF * requests. * - * Copyright IBM Corp. 2009 + * Copyright IBM Corp. 2009, 2016 */ #ifndef ZFCP_REQLIST_H @@ -180,4 +180,32 @@ static inline void zfcp_reqlist_move(struct zfcp_reqlist *rl, spin_unlock_irqrestore(&rl->lock, flags); } +/** + * zfcp_reqlist_apply_for_all() - apply a function to every request. + * @rl: the requestlist that contains the target requests. + * @f: the function to apply to each request; the first parameter of the + * function will be the target-request; the second parameter is the same + * pointer as given with the argument @data. + * @data: freely chosen argument; passed through to @f as second parameter. + * + * Uses :c:macro:`list_for_each_entry` to iterate over the lists in the hash- + * table (not a 'safe' variant, so don't modify the list). + * + * Holds @rl->lock over the entire request-iteration. + */ +static inline void +zfcp_reqlist_apply_for_all(struct zfcp_reqlist *rl, + void (*f)(struct zfcp_fsf_req *, void *), void *data) +{ + struct zfcp_fsf_req *req; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&rl->lock, flags); + for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++) + list_for_each_entry(req, &rl->buckets[i], list) + f(req, data); + spin_unlock_irqrestore(&rl->lock, flags); +} + #endif /* ZFCP_REQLIST_H */ diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 9069f98a1817..07ffdbb5107f 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -3,7 +3,7 @@ * * Interface to Linux SCSI midlayer. * - * Copyright IBM Corp. 2002, 2015 + * Copyright IBM Corp. 2002, 2016 */ #define KMSG_COMPONENT "zfcp" @@ -88,9 +88,7 @@ int zfcp_scsi_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scpnt) } if (unlikely(!(status & ZFCP_STATUS_COMMON_UNBLOCKED))) { - /* This could be either - * open LUN pending: this is temporary, will result in - * open LUN or ERP_FAILED, so retry command + /* This could be * call to rport_delete pending: mimic retry from * fc_remote_port_chkready until rport is BLOCKED */ @@ -209,6 +207,57 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) return retval; } +struct zfcp_scsi_req_filter { + u8 tmf_scope; + u32 lun_handle; + u32 port_handle; +}; + +static void zfcp_scsi_forget_cmnd(struct zfcp_fsf_req *old_req, void *data) +{ + struct zfcp_scsi_req_filter *filter = + (struct zfcp_scsi_req_filter *)data; + + /* already aborted - prevent side-effects - or not a SCSI command */ + if (old_req->data == NULL || old_req->fsf_command != FSF_QTCB_FCP_CMND) + return; + + /* (tmf_scope == FCP_TMF_TGT_RESET || tmf_scope == FCP_TMF_LUN_RESET) */ + if (old_req->qtcb->header.port_handle != filter->port_handle) + return; + + if (filter->tmf_scope == FCP_TMF_LUN_RESET && + old_req->qtcb->header.lun_handle != filter->lun_handle) + return; + + zfcp_dbf_scsi_nullcmnd((struct scsi_cmnd *)old_req->data, old_req); + old_req->data = NULL; +} + +static void zfcp_scsi_forget_cmnds(struct zfcp_scsi_dev *zsdev, u8 tm_flags) +{ + struct zfcp_adapter *adapter = zsdev->port->adapter; + struct zfcp_scsi_req_filter filter = { + .tmf_scope = FCP_TMF_TGT_RESET, + .port_handle = zsdev->port->handle, + }; + unsigned long flags; + + if (tm_flags == FCP_TMF_LUN_RESET) { + filter.tmf_scope = FCP_TMF_LUN_RESET; + filter.lun_handle = zsdev->lun_handle; + } + + /* + * abort_lock secures against other processings - in the abort-function + * and normal cmnd-handler - of (struct zfcp_fsf_req *)->data + */ + write_lock_irqsave(&adapter->abort_lock, flags); + zfcp_reqlist_apply_for_all(adapter->req_list, zfcp_scsi_forget_cmnd, + &filter); + write_unlock_irqrestore(&adapter->abort_lock, flags); +} + static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) { struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device); @@ -241,8 +290,10 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) { zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags); retval = FAILED; - } else + } else { zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags); + zfcp_scsi_forget_cmnds(zfcp_sdev, tm_flags); + } zfcp_fsf_req_free(fsf_req); return retval; diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c index 0d351cd3191b..26d38b1a45ab 100644 --- a/drivers/scsi/aacraid/comminit.c +++ b/drivers/scsi/aacraid/comminit.c @@ -50,9 +50,13 @@ struct aac_common aac_config = { static inline int aac_is_msix_mode(struct aac_dev *dev) { - u32 status; + u32 status = 0; - status = src_readl(dev, MUnit.OMR); + if (dev->pdev->device == PMC_DEVICE_S6 || + dev->pdev->device == PMC_DEVICE_S7 || + dev->pdev->device == PMC_DEVICE_S8) { + status = src_readl(dev, MUnit.OMR); + } return (status & AAC_INT_MODE_MSIX); } diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c index bc0203f3d243..e415e1c58eb5 100644 --- a/drivers/scsi/aacraid/src.c +++ b/drivers/scsi/aacraid/src.c @@ -413,16 +413,23 @@ static int aac_src_check_health(struct aac_dev *dev) u32 status = src_readl(dev, MUnit.OMR); /* + * Check to see if the board panic'd. + */ + if (unlikely(status & KERNEL_PANIC)) + goto err_blink; + + /* * Check to see if the board failed any self tests. */ if (unlikely(status & SELF_TEST_FAILED)) - return -1; + goto err_out; /* - * Check to see if the board panic'd. + * Check to see if the board failed any self tests. */ - if (unlikely(status & KERNEL_PANIC)) - return (status >> 16) & 0xFF; + if (unlikely(status & MONITOR_PANIC)) + goto err_out; + /* * Wait for the adapter to be up and running. */ @@ -432,6 +439,12 @@ static int aac_src_check_health(struct aac_dev *dev) * Everything is OK */ return 0; + +err_out: + return -1; + +err_blink: + return (status > 16) & 0xFF; } /** diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 33ec4fa39ccb..f224cdb2fce4 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -1182,6 +1182,7 @@ struct lpfc_mbx_wq_create { #define lpfc_mbx_wq_create_page_size_SHIFT 0 #define lpfc_mbx_wq_create_page_size_MASK 0x000000FF #define lpfc_mbx_wq_create_page_size_WORD word1 +#define LPFC_WQ_PAGE_SIZE_4096 0x1 #define lpfc_mbx_wq_create_wqe_size_SHIFT 8 #define lpfc_mbx_wq_create_wqe_size_MASK 0x0000000F #define lpfc_mbx_wq_create_wqe_size_WORD word1 @@ -1253,6 +1254,7 @@ struct rq_context { #define lpfc_rq_context_page_size_SHIFT 0 /* Version 1 Only */ #define lpfc_rq_context_page_size_MASK 0x000000FF #define lpfc_rq_context_page_size_WORD word0 +#define LPFC_RQ_PAGE_SIZE_4096 0x1 uint32_t reserved1; uint32_t word2; #define lpfc_rq_context_cq_id_SHIFT 16 diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 92dfd6a5178c..f5aeda8f014f 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -13475,7 +13475,7 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, LPFC_WQ_WQE_SIZE_128); bf_set(lpfc_mbx_wq_create_page_size, &wq_create->u.request_1, - (PAGE_SIZE/SLI4_PAGE_SIZE)); + LPFC_WQ_PAGE_SIZE_4096); page = wq_create->u.request_1.page; break; } @@ -13501,8 +13501,9 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, LPFC_WQ_WQE_SIZE_128); break; } - bf_set(lpfc_mbx_wq_create_page_size, &wq_create->u.request_1, - (PAGE_SIZE/SLI4_PAGE_SIZE)); + bf_set(lpfc_mbx_wq_create_page_size, + &wq_create->u.request_1, + LPFC_WQ_PAGE_SIZE_4096); page = wq_create->u.request_1.page; break; default: @@ -13688,7 +13689,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq, LPFC_RQE_SIZE_8); bf_set(lpfc_rq_context_page_size, &rq_create->u.request.context, - (PAGE_SIZE/SLI4_PAGE_SIZE)); + LPFC_RQ_PAGE_SIZE_4096); } else { switch (hrq->entry_count) { default: diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 021b994fdae8..96007633ad39 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -1856,6 +1856,8 @@ megasas_build_syspd_fusion(struct megasas_instance *instance, io_request->DevHandle = pd_sync->seq[pd_index].devHandle; pRAID_Context->regLockFlags |= (MR_RL_FLAGS_SEQ_NUM_ENABLE|MR_RL_FLAGS_GRANT_DESTINATION_CUDA); + pRAID_Context->Type = MPI2_TYPE_CUDA; + pRAID_Context->nseg = 0x1; } else if (fusion->fast_path_io) { pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id); pRAID_Context->configSeqNum = 0; @@ -1891,12 +1893,10 @@ megasas_build_syspd_fusion(struct megasas_instance *instance, pRAID_Context->timeoutValue = cpu_to_le16((os_timeout_value > timeout_limit) ? timeout_limit : os_timeout_value); - if (fusion->adapter_type == INVADER_SERIES) { - pRAID_Context->Type = MPI2_TYPE_CUDA; - pRAID_Context->nseg = 0x1; + if (fusion->adapter_type == INVADER_SERIES) io_request->IoFlags |= cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH); - } + cmd->request_desc->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); @@ -2648,6 +2648,7 @@ int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance, dev_err(&instance->pdev->dev, "pending commands remain after waiting, " "will reset adapter scsi%d.\n", instance->host->host_no); + *convert = 1; retval = 1; } out: diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 8cead04f26d6..f6a8e9958e75 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -51,6 +51,7 @@ #include <linux/workqueue.h> #include <linux/delay.h> #include <linux/pci.h> +#include <linux/pci-aspm.h> #include <linux/interrupt.h> #include <linux/aer.h> #include <linux/raid_class.h> @@ -8483,6 +8484,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) switch (hba_mpi_version) { case MPI2_VERSION: + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | + PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM); /* Use mpt2sas driver host template for SAS 2.0 HBA's */ shost = scsi_host_alloc(&mpt2sas_driver_template, sizeof(struct MPT3SAS_ADAPTER)); diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c index 9270d15ff1a4..7353ac8d0d39 100644 --- a/drivers/scsi/mvsas/mv_94xx.c +++ b/drivers/scsi/mvsas/mv_94xx.c @@ -621,7 +621,7 @@ static void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx) { u32 tmp; tmp = mvs_cr32(mvi, MVS_COMMAND_ACTIVE+(slot_idx >> 3)); - if (tmp && 1 << (slot_idx % 32)) { + if (tmp & 1 << (slot_idx % 32)) { mv_printk("command active %08X, slot [%x].\n", tmp, slot_idx); mvs_cw32(mvi, MVS_COMMAND_ACTIVE + (slot_idx >> 3), 1 << (slot_idx % 32)); diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index 9c780740fb82..e712fe745955 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -737,8 +737,8 @@ static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf mv_dprintk("device %016llx not ready.\n", SAS_ADDR(dev->sas_addr)); - rc = SAS_PHY_DOWN; - return rc; + rc = SAS_PHY_DOWN; + return rc; } tei.port = dev->port->lldd_port; if (tei.port && !tei.port->port_attached && !tmf) { diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index c44cbf46221c..3588a56aabb4 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -3365,7 +3365,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, sizeof(struct ct6_dsd), 0, SLAB_HWCACHE_ALIGN, NULL); if (!ctx_cachep) - goto fail_free_gid_list; + goto fail_free_srb_mempool; } ha->ctx_mempool = mempool_create_slab_pool(SRB_MIN_REQ, ctx_cachep); @@ -3518,7 +3518,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, ha->loop_id_map = kzalloc(BITS_TO_LONGS(LOOPID_MAP_SIZE) * sizeof(long), GFP_KERNEL); if (!ha->loop_id_map) - goto fail_async_pd; + goto fail_loop_id_map; else { qla2x00_set_reserved_loop_ids(ha); ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0123, @@ -3527,6 +3527,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, return 0; +fail_loop_id_map: + dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma); fail_async_pd: dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma); fail_ex_init_cb: @@ -3554,6 +3556,10 @@ fail_free_ms_iocb: dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma); ha->ms_iocb = NULL; ha->ms_iocb_dma = 0; + + if (ha->sns_cmd) + dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt), + ha->sns_cmd, ha->sns_cmd_dma); fail_dma_pool: if (IS_QLA82XX(ha) || ql2xenabledif) { dma_pool_destroy(ha->fcp_cmnd_dma_pool); @@ -3571,10 +3577,12 @@ fail_free_nvram: kfree(ha->nvram); ha->nvram = NULL; fail_free_ctx_mempool: - mempool_destroy(ha->ctx_mempool); + if (ha->ctx_mempool) + mempool_destroy(ha->ctx_mempool); ha->ctx_mempool = NULL; fail_free_srb_mempool: - mempool_destroy(ha->srb_mempool); + if (ha->srb_mempool) + mempool_destroy(ha->srb_mempool); ha->srb_mempool = NULL; fail_free_gid_list: dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha), diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c index e7649ed3f667..4d655b568269 100644 --- a/drivers/scsi/scsi_dh.c +++ b/drivers/scsi/scsi_dh.c @@ -289,20 +289,6 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) } EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); -static struct scsi_device *get_sdev_from_queue(struct request_queue *q) -{ - struct scsi_device *sdev; - unsigned long flags; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (!sdev || !get_device(&sdev->sdev_gendev)) - sdev = NULL; - spin_unlock_irqrestore(q->queue_lock, flags); - - return sdev; -} - /* * scsi_dh_activate - activate the path associated with the scsi_device * corresponding to the given request queue. @@ -321,7 +307,7 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) struct scsi_device *sdev; int err = SCSI_DH_NOSYS; - sdev = get_sdev_from_queue(q); + sdev = scsi_device_from_queue(q); if (!sdev) { if (fn) fn(data, err); @@ -368,7 +354,7 @@ int scsi_dh_set_params(struct request_queue *q, const char *params) struct scsi_device *sdev; int err = -SCSI_DH_NOSYS; - sdev = get_sdev_from_queue(q); + sdev = scsi_device_from_queue(q); if (!sdev) return err; @@ -391,7 +377,7 @@ int scsi_dh_attach(struct request_queue *q, const char *name) struct scsi_device_handler *scsi_dh; int err = 0; - sdev = get_sdev_from_queue(q); + sdev = scsi_device_from_queue(q); if (!sdev) return -ENODEV; @@ -429,7 +415,7 @@ const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) struct scsi_device *sdev; const char *handler_name = NULL; - sdev = get_sdev_from_queue(q); + sdev = scsi_device_from_queue(q); if (!sdev) return NULL; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index cf5b99e1f12b..887045ae5d10 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1120,7 +1120,8 @@ int scsi_init_io(struct scsi_cmnd *cmd) bool is_mq = (rq->mq_ctx != NULL); int error; - BUG_ON(!rq->nr_phys_segments); + if (WARN_ON_ONCE(!rq->nr_phys_segments)) + return -EINVAL; error = scsi_init_sgtable(rq, &cmd->sdb); if (error) @@ -2214,6 +2215,29 @@ void scsi_mq_destroy_tags(struct Scsi_Host *shost) blk_mq_free_tag_set(&shost->tag_set); } +/** + * scsi_device_from_queue - return sdev associated with a request_queue + * @q: The request queue to return the sdev from + * + * Return the sdev associated with a request queue or NULL if the + * request_queue does not reference a SCSI device. + */ +struct scsi_device *scsi_device_from_queue(struct request_queue *q) +{ + struct scsi_device *sdev = NULL; + + if (q->mq_ops) { + if (q->mq_ops == &scsi_mq_ops) + sdev = q->queuedata; + } else if (q->request_fn == scsi_request_fn) + sdev = q->queuedata; + if (!sdev || !get_device(&sdev->sdev_gendev)) + sdev = NULL; + + return sdev; +} +EXPORT_SYMBOL_GPL(scsi_device_from_queue); + /* * Function: scsi_block_requests() * diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 98cabf409bf0..9f2c9a2fdd74 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1031,10 +1031,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) struct request_queue *rq = sdev->request_queue; struct scsi_target *starget = sdev->sdev_target; - error = scsi_device_set_state(sdev, SDEV_RUNNING); - if (error) - return error; - error = scsi_target_add(starget); if (error) return error; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 77b2da269d6e..93a523b42d3d 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -592,6 +592,9 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) sg_io_hdr_t *hp; unsigned char cmnd[SG_MAX_CDB_SIZE]; + if (unlikely(segment_eq(get_fs(), KERNEL_DS))) + return -EINVAL; + if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) return -ENXIO; SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, @@ -1757,6 +1760,10 @@ sg_start_req(Sg_request *srp, unsigned char *cmd) return res; iov_iter_truncate(&i, hp->dxfer_len); + if (!iov_iter_count(&i)) { + kfree(iov); + return -EINVAL; + } res = blk_rq_map_user_iov(q, rq, md, &i, GFP_ATOMIC); kfree(iov); diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 0f636cc4c809..cd5c1c060481 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -135,6 +135,8 @@ struct hv_fc_wwn_packet { #define SRB_FLAGS_PORT_DRIVER_RESERVED 0x0F000000 #define SRB_FLAGS_CLASS_DRIVER_RESERVED 0xF0000000 +#define SP_UNTAGGED ((unsigned char) ~0) +#define SRB_SIMPLE_TAG_REQUEST 0x20 /* * Platform neutral description of a scsi request - @@ -354,6 +356,7 @@ enum storvsc_request_type { #define SRB_STATUS_SUCCESS 0x01 #define SRB_STATUS_ABORTED 0x02 #define SRB_STATUS_ERROR 0x04 +#define SRB_STATUS_DATA_OVERRUN 0x12 #define SRB_STATUS(status) \ (status & ~(SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_QUEUE_FROZEN)) @@ -864,6 +867,13 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb, switch (SRB_STATUS(vm_srb->srb_status)) { case SRB_STATUS_ERROR: /* + * Let upper layer deal with error when + * sense message is present. + */ + + if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID) + break; + /* * If there is an error; offline the device since all * error recovery strategies would have already been * deployed on the host side. However, if the command @@ -927,6 +937,7 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) struct hv_host_device *host_dev = shost_priv(scmnd->device->host); struct scsi_sense_hdr sense_hdr; struct vmscsi_request *vm_srb; + u32 data_transfer_length; struct Scsi_Host *host; struct storvsc_device *stor_dev; struct hv_device *dev = host_dev->dev; @@ -937,6 +948,7 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) host = stor_dev->host; vm_srb = &cmd_request->vstor_packet.vm_srb; + data_transfer_length = vm_srb->data_transfer_length; scmnd->result = vm_srb->scsi_status; @@ -947,13 +959,20 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) &sense_hdr); } - if (vm_srb->srb_status != SRB_STATUS_SUCCESS) + if (vm_srb->srb_status != SRB_STATUS_SUCCESS) { storvsc_handle_error(vm_srb, scmnd, host, sense_hdr.asc, sense_hdr.ascq); + /* + * The Windows driver set data_transfer_length on + * SRB_STATUS_DATA_OVERRUN. On other errors, this value + * is untouched. In these cases we set it to 0. + */ + if (vm_srb->srb_status != SRB_STATUS_DATA_OVERRUN) + data_transfer_length = 0; + } scsi_set_resid(scmnd, - cmd_request->payload->range.len - - vm_srb->data_transfer_length); + cmd_request->payload->range.len - data_transfer_length); scmnd->scsi_done(scmnd); @@ -1409,6 +1428,13 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) vm_srb->win8_extension.srb_flags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + if (scmnd->device->tagged_supported) { + vm_srb->win8_extension.srb_flags |= + (SRB_FLAGS_QUEUE_ACTION_ENABLE | SRB_FLAGS_NO_QUEUE_FREEZE); + vm_srb->win8_extension.queue_tag = SP_UNTAGGED; + vm_srb->win8_extension.queue_action = SRB_SIMPLE_TAG_REQUEST; + } + /* Build the SRB */ switch (scmnd->sc_data_direction) { case DMA_TO_DEVICE: diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index 14cf10b92122..2dc4208cbc51 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -213,6 +213,7 @@ struct edge_info { bool tx_blocked_signal_sent; struct kthread_work kwork; struct kthread_worker kworker; + struct work_struct wakeup_work; struct task_struct *task; struct tasklet_struct tasklet; struct srcu_struct use_ref; @@ -826,7 +827,6 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) int i; bool granted; unsigned long flags; - bool trigger_wakeup = false; int rcu_id; uint16_t rcid; uint32_t name_len; @@ -851,22 +851,10 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) srcu_read_unlock(&einfo->use_ref, rcu_id); return; } - if (!atomic_ctx) { - if (einfo->tx_resume_needed && fifo_write_avail(einfo)) { - einfo->tx_resume_needed = false; - einfo->xprt_if.glink_core_if_ptr->tx_resume( - &einfo->xprt_if); - } - spin_lock_irqsave(&einfo->write_lock, flags); - if (waitqueue_active(&einfo->tx_blocked_queue)) { - einfo->tx_blocked_signal_sent = false; - trigger_wakeup = true; - } - spin_unlock_irqrestore(&einfo->write_lock, flags); - if (trigger_wakeup) - wake_up_all(&einfo->tx_blocked_queue); - } + if ((atomic_ctx) && ((einfo->tx_resume_needed) || + (waitqueue_active(&einfo->tx_blocked_queue)))) /* tx waiting ?*/ + schedule_work(&einfo->wakeup_work); /* * Access to the fifo needs to be synchronized, however only the calls @@ -1174,6 +1162,39 @@ static void rx_worker_atomic(unsigned long param) } /** + * tx_wakeup_worker() - worker function to wakeup tx blocked thread + * @work: kwork associated with the edge to process commands on. + */ +static void tx_wakeup_worker(struct work_struct *work) +{ + struct edge_info *einfo; + bool trigger_wakeup = false; + unsigned long flags; + int rcu_id; + + einfo = container_of(work, struct edge_info, wakeup_work); + rcu_id = srcu_read_lock(&einfo->use_ref); + if (einfo->in_ssr) { + srcu_read_unlock(&einfo->use_ref, rcu_id); + return; + } + if (einfo->tx_resume_needed && fifo_write_avail(einfo)) { + einfo->tx_resume_needed = false; + einfo->xprt_if.glink_core_if_ptr->tx_resume( + &einfo->xprt_if); + } + spin_lock_irqsave(&einfo->write_lock, flags); + if (waitqueue_active(&einfo->tx_blocked_queue)) { /* tx waiting ?*/ + einfo->tx_blocked_signal_sent = false; + trigger_wakeup = true; + } + spin_unlock_irqrestore(&einfo->write_lock, flags); + if (trigger_wakeup) + wake_up_all(&einfo->tx_blocked_queue); + srcu_read_unlock(&einfo->use_ref, rcu_id); +} + +/** * rx_worker() - worker function to process received commands * @work: kwork associated with the edge to process commands on. */ @@ -2275,6 +2296,7 @@ static int glink_smem_native_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); + INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->read_from_fifo = read_from_fifo; einfo->write_to_fifo = write_to_fifo; @@ -2374,6 +2396,7 @@ request_irq_fail: reg_xprt_fail: smem_alloc_fail: flush_kthread_worker(&einfo->kworker); + flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; tasklet_kill(&einfo->tasklet); @@ -2462,6 +2485,7 @@ static int glink_rpm_native_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); + INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->intentless = true; einfo->read_from_fifo = memcpy32_fromio; @@ -2622,6 +2646,7 @@ request_irq_fail: reg_xprt_fail: toc_init_fail: flush_kthread_worker(&einfo->kworker); + flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; tasklet_kill(&einfo->tasklet); @@ -2753,6 +2778,7 @@ static int glink_mailbox_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); + INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->read_from_fifo = read_from_fifo; einfo->write_to_fifo = write_to_fifo; @@ -2873,6 +2899,7 @@ request_irq_fail: reg_xprt_fail: smem_alloc_fail: flush_kthread_worker(&einfo->kworker); + flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; tasklet_kill(&einfo->tasklet); diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 2fcd1a4c636c..16ab2400cd69 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -104,13 +104,16 @@ module_param(qmi_timeout, ulong, 0600); #ifdef CONFIG_ICNSS_DEBUG #define ICNSS_ASSERT(_condition) do { \ if (!(_condition)) { \ - icnss_pr_err("ASSERT at line %d\n", \ - __LINE__); \ + icnss_pr_err("ASSERT at line %d\n", __LINE__); \ BUG_ON(1); \ } \ } while (0) + +bool ignore_qmi_timeout; +#define ICNSS_QMI_ASSERT() ICNSS_ASSERT(ignore_qmi_timeout) #else #define ICNSS_ASSERT(_condition) do { } while (0) +#define ICNSS_QMI_ASSERT() do { } while (0) #endif enum icnss_debug_quirks { @@ -322,8 +325,9 @@ static struct icnss_priv { u32 pwr_pin_result; u32 phy_io_pin_result; u32 rf_pin_result; + uint32_t nr_mem_region; struct icnss_mem_region_info - icnss_mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01]; + mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01]; struct dentry *root_dentry; spinlock_t on_off_lock; struct icnss_stats stats; @@ -349,6 +353,15 @@ static struct icnss_priv { bool bypass_s1_smmu; } *penv; +#ifdef CONFIG_ICNSS_DEBUG +static void icnss_ignore_qmi_timeout(bool ignore) +{ + ignore_qmi_timeout = ignore; +} +#else +static void icnss_ignore_qmi_timeout(bool ignore) { } +#endif + static void icnss_pm_stay_awake(struct icnss_priv *priv) { if (atomic_inc_return(&priv->pm_count) != 1) @@ -952,7 +965,7 @@ int icnss_power_off(struct device *dev) } EXPORT_SYMBOL(icnss_power_off); -static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index) +static int icnss_map_msa_permissions(struct icnss_mem_region_info *mem_region) { int ret = 0; phys_addr_t addr; @@ -965,10 +978,10 @@ static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index) int source_nelems = sizeof(source_vmlist)/sizeof(u32); int dest_nelems = 0; - addr = priv->icnss_mem_region[index].reg_addr; - size = priv->icnss_mem_region[index].size; + addr = mem_region->reg_addr; + size = mem_region->size; - if (!priv->icnss_mem_region[index].secure_flag) { + if (!mem_region->secure_flag) { dest_vmids[2] = VMID_WLAN_CE; dest_nelems = 3; } else { @@ -978,19 +991,20 @@ static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index) ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems, dest_vmids, dest_perms, dest_nelems); if (ret) { - icnss_pr_err("Region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n", - index, &addr, size, ret); + icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n", + &addr, size, ret); goto out; } - icnss_pr_dbg("Hypervisor map for region %u: source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n", - index, source_vmlist[0], dest_nelems, - dest_vmids[0], dest_vmids[1], dest_vmids[2]); + + icnss_pr_dbg("Hypervisor map for source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n", + source_vmlist[0], dest_nelems, dest_vmids[0], + dest_vmids[1], dest_vmids[2]); out: return ret; } -static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index) +static int icnss_unmap_msa_permissions(struct icnss_mem_region_info *mem_region) { int ret = 0; phys_addr_t addr; @@ -1001,9 +1015,10 @@ static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index) int source_nelems = 0; int dest_nelems = sizeof(dest_vmids)/sizeof(u32); - addr = priv->icnss_mem_region[index].reg_addr; - size = priv->icnss_mem_region[index].size; - if (!priv->icnss_mem_region[index].secure_flag) { + addr = mem_region->reg_addr; + size = mem_region->size; + + if (!mem_region->secure_flag) { source_vmlist[2] = VMID_WLAN_CE; source_nelems = 3; } else { @@ -1014,14 +1029,13 @@ static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index) ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems, dest_vmids, dest_perms, dest_nelems); if (ret) { - icnss_pr_err("Region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n", - index, &addr, size, ret); + icnss_pr_err("Hyperviser unmap failed for PA=%pa size=%u err=%d\n", + &addr, size, ret); goto out; } - icnss_pr_dbg("hypervisor unmap for region %u, source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n", - index, source_nelems, - source_vmlist[0], source_vmlist[1], source_vmlist[2], - dest_vmids[0]); + icnss_pr_dbg("Hypervisor unmap for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n", + source_nelems, source_vmlist[0], source_vmlist[1], + source_vmlist[2], dest_vmids[0]); out: return ret; } @@ -1029,34 +1043,37 @@ out: static int icnss_setup_msa_permissions(struct icnss_priv *priv) { int ret; + int i; if (test_bit(ICNSS_MSA0_ASSIGNED, &priv->state)) return 0; - ret = icnss_map_msa_permissions(priv, 0); - if (ret) - return ret; + for (i = 0; i < priv->nr_mem_region; i++) { - ret = icnss_map_msa_permissions(priv, 1); - if (ret) - goto err_map_msa; + ret = icnss_map_msa_permissions(&priv->mem_region[i]); + if (ret) + goto err_unmap; + } set_bit(ICNSS_MSA0_ASSIGNED, &priv->state); - return ret; + return 0; -err_map_msa: - icnss_unmap_msa_permissions(priv, 0); +err_unmap: + for (i--; i >= 0; i--) + icnss_unmap_msa_permissions(&priv->mem_region[i]); return ret; } static void icnss_remove_msa_permissions(struct icnss_priv *priv) { + int i; + if (!test_bit(ICNSS_MSA0_ASSIGNED, &priv->state)) return; - icnss_unmap_msa_permissions(priv, 0); - icnss_unmap_msa_permissions(priv, 1); + for (i = 0; i < priv->nr_mem_region; i++) + icnss_unmap_msa_permissions(&priv->mem_region[i]); clear_bit(ICNSS_MSA0_ASSIGNED, &priv->state); } @@ -1107,7 +1124,7 @@ static int wlfw_msa_mem_info_send_sync_msg(void) icnss_pr_dbg("Receive mem_region_info_len: %d\n", resp.mem_region_info_len); - if (resp.mem_region_info_len > 2) { + if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) { icnss_pr_err("Invalid memory region length received: %d\n", resp.mem_region_info_len); ret = -EINVAL; @@ -1115,24 +1132,25 @@ static int wlfw_msa_mem_info_send_sync_msg(void) } penv->stats.msa_info_resp++; + penv->nr_mem_region = resp.mem_region_info_len; for (i = 0; i < resp.mem_region_info_len; i++) { - penv->icnss_mem_region[i].reg_addr = + penv->mem_region[i].reg_addr = resp.mem_region_info[i].region_addr; - penv->icnss_mem_region[i].size = + penv->mem_region[i].size = resp.mem_region_info[i].size; - penv->icnss_mem_region[i].secure_flag = + penv->mem_region[i].secure_flag = resp.mem_region_info[i].secure_flag; icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n", - i, penv->icnss_mem_region[i].reg_addr, - penv->icnss_mem_region[i].size, - penv->icnss_mem_region[i].secure_flag); + i, penv->mem_region[i].reg_addr, + penv->mem_region[i].size, + penv->mem_region[i].secure_flag); } return 0; out: penv->stats.msa_info_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1180,7 +1198,7 @@ static int wlfw_msa_ready_send_sync_msg(void) out: penv->stats.msa_ready_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1243,7 +1261,7 @@ static int wlfw_ind_register_send_sync_msg(void) out: penv->stats.ind_register_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1312,7 +1330,7 @@ static int wlfw_cap_send_sync_msg(void) out: penv->stats.cap_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1373,7 +1391,7 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode) out: penv->stats.mode_req_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1423,7 +1441,7 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data) out: penv->stats.cfg_req_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1476,7 +1494,7 @@ static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode) out: penv->stats.ini_req_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1642,7 +1660,7 @@ static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv) out: priv->stats.rejuvenate_ack_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1774,6 +1792,8 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle, case QMI_WLFW_REJUVENATE_IND_V01: icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n", msg_id, penv->state); + + icnss_ignore_qmi_timeout(true); event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); if (event_data == NULL) return; @@ -1916,6 +1936,9 @@ static int icnss_call_driver_probe(struct icnss_priv *priv) if (!priv->ops || !priv->ops->probe) return 0; + if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) + return -EINVAL; + icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state); icnss_hw_power_on(priv); @@ -2142,7 +2165,7 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, struct icnss_event_pd_service_down_data *event_data = data; if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) - return 0; + goto out; if (test_bit(ICNSS_PD_RESTART, &priv->state)) { icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n", @@ -2159,6 +2182,8 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, out: kfree(data); + icnss_ignore_qmi_timeout(false); + return ret; } @@ -2300,9 +2325,11 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, if (test_bit(ICNSS_PDR_ENABLED, &priv->state)) return NOTIFY_OK; - icnss_pr_info("Modem went down, state: %lx, crashed: %d\n", + icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n", priv->state, notif->crashed); + icnss_ignore_qmi_timeout(true); + event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); if (event_data == NULL) @@ -2409,6 +2436,8 @@ static int icnss_service_notifier_notify(struct notifier_block *nb, } event_post: + icnss_ignore_qmi_timeout(true); + icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, ICNSS_EVENT_SYNC, event_data); done: @@ -2517,7 +2546,7 @@ static int icnss_pd_restart_enable(struct icnss_priv *priv) return 0; out: - icnss_pr_err("PD restart not enabled: %d\n", ret); + icnss_pr_err("Failed to enable PD restart: %d\n", ret); return ret; } @@ -3130,22 +3159,23 @@ int icnss_trigger_recovery(struct device *dev) goto out; } - if (test_bit(ICNSS_PDR_ENABLED, &priv->state)) { - icnss_pr_err("PD restart not enabled: state: 0x%lx\n", + if (!test_bit(ICNSS_PDR_ENABLED, &priv->state)) { + icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n", priv->state); ret = -EOPNOTSUPP; goto out; } - if (!priv->service_notifier[0].handle) { + if (!priv->service_notifier || !priv->service_notifier[0].handle) { icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n", priv->state); ret = -EINVAL; goto out; } - icnss_pr_dbg("Initiate PD restart at WLAN FW, state: 0x%lx\n", - priv->state); + WARN_ON(1); + icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n", + priv->state); priv->stats.trigger_recovery++; /* @@ -3343,6 +3373,7 @@ static int icnss_fw_debug_show(struct seq_file *s, void *data) seq_puts(s, " VAL: 0 (Test mode disable)\n"); seq_puts(s, " VAL: 1 (WLAN FW test)\n"); seq_puts(s, " VAL: 2 (CCPM test)\n"); + seq_puts(s, " VAL: 3 (Trigger Recovery)\n"); seq_puts(s, "\nCMD: dynamic_feature_mask\n"); seq_puts(s, " VAL: (64 bit feature mask)\n"); diff --git a/drivers/soc/qcom/ipc_router_mhi_xprt.c b/drivers/soc/qcom/ipc_router_mhi_xprt.c index 9a0624804c21..f9d967fd0af6 100644 --- a/drivers/soc/qcom/ipc_router_mhi_xprt.c +++ b/drivers/soc/qcom/ipc_router_mhi_xprt.c @@ -792,20 +792,14 @@ static int ipc_router_mhi_driver_register( { int rc_status; - rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.out_handle, - mhi_xprtp->ch_hndl.out_chan_id, 0, - &mhi_xprtp->ch_hndl.out_clnt_info, - (void *)mhi_xprtp); + rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.out_handle, NULL); if (rc_status) { IPC_RTR_ERR("%s: Error %d registering out_chan for %s\n", __func__, rc_status, mhi_xprtp->xprt_name); return -EFAULT; } - rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.in_handle, - mhi_xprtp->ch_hndl.in_chan_id, 0, - &mhi_xprtp->ch_hndl.in_clnt_info, - (void *)mhi_xprtp); + rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.in_handle, NULL); if (rc_status) { mhi_deregister_channel(mhi_xprtp->ch_hndl.out_handle); IPC_RTR_ERR("%s: Error %d registering in_chan for %s\n", diff --git a/drivers/soc/qcom/mpm-of.c b/drivers/soc/qcom/mpm-of.c index 93f2de8a59dd..77c50528be4b 100644 --- a/drivers/soc/qcom/mpm-of.c +++ b/drivers/soc/qcom/mpm-of.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -129,18 +129,6 @@ static uint32_t *msm_mpm_falling_edge; static uint32_t *msm_mpm_rising_edge; static uint32_t *msm_mpm_polarity; -enum { - MSM_MPM_DEBUG_NON_DETECTABLE_IRQ = BIT(0), - MSM_MPM_DEBUG_PENDING_IRQ = BIT(1), - MSM_MPM_DEBUG_WRITE = BIT(2), - MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE = BIT(3), -}; - -static int msm_mpm_debug_mask = 1; -module_param_named( - debug_mask, msm_mpm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP -); - enum mpm_state { MSM_MPM_GIC_IRQ_MAPPING_DONE = BIT(0), MSM_MPM_GPIO_IRQ_MAPPING_DONE = BIT(1), @@ -174,9 +162,6 @@ static inline void msm_mpm_write( unsigned int offset = reg * MSM_MPM_REG_WIDTH + subreg_index + 2; __raw_writel(value, msm_mpm_dev_data.mpm_request_reg_base + offset * 4); - if (MSM_MPM_DEBUG_WRITE & msm_mpm_debug_mask) - pr_info("%s: reg %u.%u: 0x%08x\n", - __func__, reg, subreg_index, value); } static inline void msm_mpm_send_interrupt(void) @@ -513,37 +498,19 @@ int msm_mpm_set_pin_type(unsigned int pin, unsigned int flow_type) static bool msm_mpm_interrupts_detectable(int d, bool from_idle) { unsigned long *irq_bitmap; - bool debug_mask, ret = false; + bool ret = false; struct mpm_irqs *unlisted = &unlisted_irqs[d]; if (!msm_mpm_is_initialized()) return false; - if (from_idle) { + if (from_idle) irq_bitmap = unlisted->enabled_irqs; - debug_mask = msm_mpm_debug_mask & - MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE; - } else { + else irq_bitmap = unlisted->wakeup_irqs; - debug_mask = msm_mpm_debug_mask & - MSM_MPM_DEBUG_NON_DETECTABLE_IRQ; - } ret = (bool) bitmap_empty(irq_bitmap, unlisted->size); - if (debug_mask && !ret) { - int i = 0; - i = find_first_bit(irq_bitmap, unlisted->size); - pr_info("%s(): %s preventing system sleep modes during %s\n", - __func__, unlisted->domain_name, - from_idle ? "idle" : "suspend"); - - while (i < unlisted->size) { - pr_info("\thwirq: %d\n", i); - i = find_next_bit(irq_bitmap, unlisted->size, i + 1); - } - } - return ret; } @@ -601,10 +568,6 @@ void msm_mpm_exit_sleep(bool from_idle) pending = msm_mpm_read(MSM_MPM_REG_STATUS, i); pending &= enabled_intr[i]; - if (MSM_MPM_DEBUG_PENDING_IRQ & msm_mpm_debug_mask) - pr_info("%s: enabled_intr.%d pending.%d: 0x%08x 0x%08lx\n", - __func__, i, i, enabled_intr[i], pending); - k = find_first_bit(&pending, 32); while (k < 32) { unsigned int mpm_irq = 32 * i + k; diff --git a/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c b/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c index 10fb4cc8ebff..aafe82f59b9a 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is Mree software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1349,6 +1349,7 @@ static void unregister_adhoc(struct msm_bus_client_handle *cl) cl->first_hop, cl->active_only); commit_data(); msm_bus_dbg_remove_client(cl); + kfree(cl->name); kfree(cl); exit_unregister_client: rt_mutex_unlock(&msm_bus_adhoc_lock); diff --git a/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c b/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c index 06657a666f2e..305f31f31bf8 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c @@ -531,7 +531,7 @@ static int msm_bus_enable_node_qos_clk(struct msm_bus_node_device_type *node) { struct msm_bus_node_device_type *bus_node = NULL; int i; - int ret; + int ret = 0; long rounded_rate; if (!node || (!to_msm_bus_node(node->node_info->bus_device))) { diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index 21e9f17e6a7e..6e5ddc4a3a7d 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -465,6 +465,8 @@ static int pil_alloc_region(struct pil_priv *priv, phys_addr_t min_addr, if (region == NULL) { pil_err(priv->desc, "Failed to allocate relocatable region of size %zx\n", size); + priv->region_start = 0; + priv->region_end = 0; return -ENOMEM; } @@ -920,7 +922,8 @@ out: &desc->attrs); priv->region = NULL; } - pil_clear_segment(desc); + if (desc->clear_fw_region && priv->region_start) + pil_clear_segment(desc); pil_release_mmap(desc); } return ret; diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h index 802abe26a960..9521cf726069 100644 --- a/drivers/soc/qcom/peripheral-loader.h +++ b/drivers/soc/qcom/peripheral-loader.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -37,6 +37,7 @@ struct pil_priv; * This defaults to iounmap if not specified. * @shutdown_fail: Set if PIL op for shutting down subsystem fails. * @modem_ssr: true if modem is restarting, false if booting for first time. + * @clear_fw_region: Clear fw region on failure in loading. * @subsys_vmid: memprot id for the subsystem. */ struct pil_desc { @@ -56,6 +57,7 @@ struct pil_desc { void *map_data; bool shutdown_fail; bool modem_ssr; + bool clear_fw_region; u32 subsys_vmid; }; diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c index 0e023a019280..793edc5b67ed 100644 --- a/drivers/soc/qcom/pil-q6v5-mss.c +++ b/drivers/soc/qcom/pil-q6v5-mss.c @@ -276,8 +276,12 @@ static int pil_mss_loadable_init(struct modem_data *drv, if (q6->cx_ipeak_vote) { res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cxip_lm_vote_clear"); - q6->cxip_lm_vote_clear = devm_ioremap_resource(&pdev->dev, - res); + if (!res) { + dev_err(&pdev->dev, "Failed to get resource for ipeak reg\n"); + return -EINVAL; + } + q6->cxip_lm_vote_clear = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); if (!q6->cxip_lm_vote_clear) return -ENOMEM; } diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c index a39c2b6aa672..34228f072b28 100644 --- a/drivers/soc/qcom/pil-q6v5.c +++ b/drivers/soc/qcom/pil-q6v5.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -588,6 +588,7 @@ struct q6v5_data *pil_q6v5_init(struct platform_device *pdev) if (ret) return ERR_PTR(ret); + desc->clear_fw_region = false; desc->dev = &pdev->dev; drv->qdsp6v5_2_0 = of_device_is_compatible(pdev->dev.of_node, diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c index d6853e4bea72..0e7bf13c192b 100644 --- a/drivers/soc/qcom/qmi_interface.c +++ b/drivers/soc/qcom/qmi_interface.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -101,6 +101,7 @@ struct elem_info qmi_response_type_v01_ei[] = { .ei_array = NULL, }, }; +EXPORT_SYMBOL(qmi_response_type_v01_ei); struct elem_info qmi_error_resp_type_v01_ei[] = { { diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c index c7059d60d786..f0f9306ebe47 100644 --- a/drivers/soc/qcom/qpnp-haptic.c +++ b/drivers/soc/qcom/qpnp-haptic.c @@ -10,6 +10,8 @@ * GNU General Public License for more details. */ +#define pr_fmt(fmt) "haptic: %s: " fmt, __func__ + #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> @@ -25,23 +27,21 @@ #include <linux/qpnp/pwm.h> #include <linux/err.h> #include <linux/delay.h> -#include <linux/qpnp/qpnp-revid.h> +#include <linux/log2.h> +#include <linux/qpnp-misc.h> #include <linux/qpnp/qpnp-haptic.h> +#include <linux/qpnp/qpnp-revid.h> #include "../../staging/android/timed_output.h" -#define QPNP_IRQ_FLAGS (IRQF_TRIGGER_RISING | \ - IRQF_TRIGGER_FALLING | \ - IRQF_ONESHOT) - #define QPNP_HAP_STATUS(b) (b + 0x0A) #define QPNP_HAP_LRA_AUTO_RES_LO(b) (b + 0x0B) #define QPNP_HAP_LRA_AUTO_RES_HI(b) (b + 0x0C) #define QPNP_HAP_EN_CTL_REG(b) (b + 0x46) #define QPNP_HAP_EN_CTL2_REG(b) (b + 0x48) #define QPNP_HAP_AUTO_RES_CTRL(b) (b + 0x4B) -#define QPNP_HAP_ACT_TYPE_REG(b) (b + 0x4C) -#define QPNP_HAP_WAV_SHAPE_REG(b) (b + 0x4D) -#define QPNP_HAP_PLAY_MODE_REG(b) (b + 0x4E) +#define QPNP_HAP_CFG1_REG(b) (b + 0x4C) +#define QPNP_HAP_CFG2_REG(b) (b + 0x4D) +#define QPNP_HAP_SEL_REG(b) (b + 0x4E) #define QPNP_HAP_LRA_AUTO_RES_REG(b) (b + 0x4F) #define QPNP_HAP_VMAX_REG(b) (b + 0x51) #define QPNP_HAP_ILIM_REG(b) (b + 0x52) @@ -61,7 +61,7 @@ #define QPNP_HAP_TEST2_REG(b) (b + 0xE3) #define QPNP_HAP_STATUS_BUSY 0x02 -#define QPNP_HAP_ACT_TYPE_MASK 0xFE +#define QPNP_HAP_ACT_TYPE_MASK BIT(0) #define QPNP_HAP_LRA 0x0 #define QPNP_HAP_ERM 0x1 #define QPNP_HAP_AUTO_RES_MODE_MASK GENMASK(6, 4) @@ -75,35 +75,33 @@ #define QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT BIT(3) #define QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT 3 #define QPNP_HAP_PM660_LRA_ZXD_CAL_PERIOD_BIT GENMASK(2, 0) -#define QPNP_HAP_LRA_HIGH_Z_MASK GENMASK(3, 2) -#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2 -#define QPNP_HAP_LRA_RES_CAL_PER_MASK GENMASK(1, 0) +#define QPNP_HAP_LRA_HIGH_Z_MASK GENMASK(3, 2) +#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2 +#define QPNP_HAP_LRA_RES_CAL_PER_MASK GENMASK(1, 0) #define QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK GENMASK(2, 0) -#define QPNP_HAP_RES_CAL_PERIOD_MIN 4 -#define QPNP_HAP_RES_CAL_PERIOD_MAX 32 -#define QPNP_HAP_PM660_RES_CAL_PERIOD_MIN 4 +#define QPNP_HAP_RES_CAL_PERIOD_MIN 4 +#define QPNP_HAP_RES_CAL_PERIOD_MAX 32 #define QPNP_HAP_PM660_RES_CAL_PERIOD_MAX 256 -#define QPNP_HAP_PLAY_MODE_MASK 0xCF -#define QPNP_HAP_PLAY_MODE_SHFT 4 -#define QPNP_HAP_VMAX_MASK 0xC1 +#define QPNP_HAP_WF_SOURCE_MASK GENMASK(5, 4) +#define QPNP_HAP_WF_SOURCE_SHIFT 4 +#define QPNP_HAP_VMAX_MASK GENMASK(5, 1) #define QPNP_HAP_VMAX_SHIFT 1 #define QPNP_HAP_VMAX_MIN_MV 116 #define QPNP_HAP_VMAX_MAX_MV 3596 -#define QPNP_HAP_ILIM_MASK 0xFE +#define QPNP_HAP_ILIM_MASK BIT(0) #define QPNP_HAP_ILIM_MIN_MV 400 #define QPNP_HAP_ILIM_MAX_MV 800 -#define QPNP_HAP_SC_DEB_MASK 0xF8 -#define QPNP_HAP_SC_DEB_SUB 2 +#define QPNP_HAP_SC_DEB_MASK GENMASK(2, 0) #define QPNP_HAP_SC_DEB_CYCLES_MIN 0 #define QPNP_HAP_DEF_SC_DEB_CYCLES 8 #define QPNP_HAP_SC_DEB_CYCLES_MAX 32 #define QPNP_HAP_SC_CLR 1 -#define QPNP_HAP_INT_PWM_MASK 0xFC +#define QPNP_HAP_INT_PWM_MASK GENMASK(1, 0) #define QPNP_HAP_INT_PWM_FREQ_253_KHZ 253 #define QPNP_HAP_INT_PWM_FREQ_505_KHZ 505 #define QPNP_HAP_INT_PWM_FREQ_739_KHZ 739 #define QPNP_HAP_INT_PWM_FREQ_1076_KHZ 1076 -#define QPNP_HAP_WAV_SHAPE_MASK 0xFE +#define QPNP_HAP_WAV_SHAPE_MASK BIT(0) #define QPNP_HAP_RATE_CFG1_MASK 0xFF #define QPNP_HAP_RATE_CFG2_MASK 0xF0 #define QPNP_HAP_RATE_CFG2_SHFT 8 @@ -111,9 +109,9 @@ #define QPNP_HAP_WAV_PLAY_RATE_US_MIN 0 #define QPNP_HAP_DEF_WAVE_PLAY_RATE_US 5715 #define QPNP_HAP_WAV_PLAY_RATE_US_MAX 20475 -#define QPNP_HAP_WAV_REP_MASK 0x8F -#define QPNP_HAP_WAV_S_REP_MASK 0xFC -#define QPNP_HAP_WAV_REP_SHFT 4 +#define QPNP_HAP_WAV_REP_MASK GENMASK(6, 4) +#define QPNP_HAP_WAV_S_REP_MASK GENMASK(1, 0) +#define QPNP_HAP_WAV_REP_SHIFT 4 #define QPNP_HAP_WAV_REP_MIN 1 #define QPNP_HAP_WAV_REP_MAX 128 #define QPNP_HAP_WAV_S_REP_MIN 1 @@ -121,13 +119,13 @@ #define QPNP_HAP_BRAKE_PAT_MASK 0x3 #define QPNP_HAP_ILIM_MIN_MA 400 #define QPNP_HAP_ILIM_MAX_MA 800 -#define QPNP_HAP_EXT_PWM_MASK 0xFC +#define QPNP_HAP_EXT_PWM_MASK GENMASK(1, 0) #define QPNP_HAP_EXT_PWM_FREQ_25_KHZ 25 #define QPNP_HAP_EXT_PWM_FREQ_50_KHZ 50 #define QPNP_HAP_EXT_PWM_FREQ_75_KHZ 75 #define QPNP_HAP_EXT_PWM_FREQ_100_KHZ 100 #define PWM_MAX_DTEST_LINES 4 -#define QPNP_HAP_EXT_PWM_DTEST_MASK 0x0F +#define QPNP_HAP_EXT_PWM_DTEST_MASK GENMASK(6, 4) #define QPNP_HAP_EXT_PWM_DTEST_SHFT 4 #define QPNP_HAP_EXT_PWM_PEAK_DATA 0x7F #define QPNP_HAP_EXT_PWM_HALF_DUTY 50 @@ -140,10 +138,9 @@ #define QPNP_HAP_BRAKE_PAT_LEN 4 #define QPNP_HAP_PLAY_EN 0x80 #define QPNP_HAP_EN 0x80 -#define QPNP_HAP_BRAKE_MASK 0xFE -#define QPNP_HAP_TEST2_AUTO_RES_MASK 0x7F -#define QPNP_HAP_SEC_UNLOCK 0xA5 -#define AUTO_RES_ENABLE 0x80 +#define QPNP_HAP_BRAKE_MASK BIT(0) +#define QPNP_HAP_AUTO_RES_MASK BIT(7) +#define AUTO_RES_ENABLE BIT(7) #define AUTO_RES_ERR_BIT 0x10 #define SC_FOUND_BIT 0x08 #define SC_MAX_DURATION 5 @@ -155,12 +152,6 @@ #define QPNP_TEST_TIMER_MS 5 #define QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN 20000 - -#define MISC_TRIM_ERROR_RC19P2_CLK 0x09F5 -#define MISC_SEC_ACCESS 0x09D0 -#define MISC_SEC_UNLOCK 0xA5 -#define PMI8950_MISC_SID 2 - #define POLL_TIME_AUTO_RES_ERR_NS (20 * NSEC_PER_MSEC) #define MAX_POSITIVE_VARIATION_LRA_FREQ 30 @@ -172,11 +163,11 @@ ((MAX_POSITIVE_VARIATION_LRA_FREQ - MAX_NEGATIVE_VARIATION_LRA_FREQ) \ / FREQ_VARIATION_STEP) #define LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent) \ - (hap->init_drive_period_code = (hap->init_drive_period_code * \ - (1000 + rc_clk_err_percent_x10)) / 1000) + (hap->init_drive_period_code = (hap->init_drive_period_code * \ + (1000 + rc_clk_err_percent_x10)) / 1000) #define LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent) \ - (hap->init_drive_period_code = (hap->init_drive_period_code * \ - (1000 - rc_clk_err_percent_x10)) / 1000) + (hap->init_drive_period_code = (hap->init_drive_period_code * \ + (1000 - rc_clk_err_percent_x10)) / 1000) u32 adjusted_lra_play_rate_code[ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE]; @@ -318,15 +309,14 @@ struct qpnp_pwm_info { * @ lra_res_cal_period - period for resonance calibration * @ sc_duration - counter to determine the duration of short circuit condition * @ state - current state of haptics - * @ use_play_irq - play irq usage state - * @ use_sc_irq - short circuit irq usage state * @ wf_update - waveform update flag * @ pwm_cfg_state - pwm mode configuration state * @ buffer_cfg_state - buffer mode configuration state * @ en_brake - brake state * @ sup_brake_pat - support custom brake pattern * @ correct_lra_drive_freq - correct LRA Drive Frequency - * @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present + * @ misc_clk_trim_error_reg - MISC clock trim error register if present + * @ clk_trim_error_code - MISC clock trim error code * @ perform_lra_auto_resonance_search - whether lra auto resonance search * algorithm should be performed or not. */ @@ -344,11 +334,13 @@ struct qpnp_hap { struct qpnp_pwm_info pwm_info; struct mutex lock; struct mutex wf_lock; + spinlock_t bus_lock; struct completion completion; enum qpnp_hap_mode play_mode; enum qpnp_hap_high_z lra_high_z; int lra_qwd_drive_duration; int calibrate_at_eop; + u32 misc_clk_trim_error_reg; u32 init_drive_period_code; u32 timeout_ms; u32 time_required_to_generate_back_emf_us; @@ -380,10 +372,9 @@ struct qpnp_hap { u8 ext_pwm_dtest_line; u8 pmic_subtype; u8 auto_res_mode; + u8 clk_trim_error_code; bool vcc_pon_enabled; bool state; - bool use_play_irq; - bool use_sc_irq; bool manage_pon_supply; bool wf_update; bool pwm_cfg_state; @@ -391,82 +382,98 @@ struct qpnp_hap { bool en_brake; bool sup_brake_pat; bool correct_lra_drive_freq; - bool misc_trim_error_rc19p2_clk_reg_present; bool perform_lra_auto_resonance_search; }; static struct qpnp_hap *ghap; /* helper to read a pmic register */ -static int qpnp_hap_read_reg(struct qpnp_hap *hap, u8 *data, u16 addr) +static int qpnp_hap_read_reg(struct qpnp_hap *hap, u16 addr, u8 *val) { int rc; - uint val; + uint tmp; - rc = regmap_read(hap->regmap, addr, &val); + rc = regmap_read(hap->regmap, addr, &tmp); if (rc < 0) - dev_err(&hap->pdev->dev, - "Error reading address: %X - ret %X\n", addr, rc); - *data = (u8)val; + pr_err("Error reading address: %X - ret %X\n", addr, rc); + *val = (u8)tmp; return rc; } /* helper to write a pmic register */ -static int qpnp_hap_write_reg(struct qpnp_hap *hap, u8 *data, u16 addr) +static int qpnp_hap_write_reg(struct qpnp_hap *hap, u16 addr, u8 val) { + unsigned long flags; int rc; - rc = regmap_write(hap->regmap, addr, *data); + spin_lock_irqsave(&hap->bus_lock, flags); + rc = regmap_write(hap->regmap, addr, val); if (rc < 0) - dev_err(&hap->pdev->dev, - "Error writing address: %X - ret %X\n", addr, rc); + pr_err("Error writing address: %X - ret %X\n", addr, rc); - dev_dbg(&hap->pdev->dev, "write: HAP_0x%x = 0x%x\n", addr, *data); + spin_unlock_irqrestore(&hap->bus_lock, flags); + if (!rc) + pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val); return rc; } -static int -qpnp_hap_masked_write_reg(struct qpnp_hap *hap, u8 val, u16 addr, u8 mask) +/* helper to access secure registers */ +#define QPNP_HAP_SEC_UNLOCK 0xA5 +static int qpnp_hap_sec_masked_write_reg(struct qpnp_hap *hap, u16 addr, + u8 mask, u8 val) { + unsigned long flags; int rc; + u8 tmp = QPNP_HAP_SEC_UNLOCK; + + spin_lock_irqsave(&hap->bus_lock, flags); + rc = regmap_write(hap->regmap, QPNP_HAP_SEC_ACCESS_REG(hap->base), tmp); + if (rc < 0) { + pr_err("Error writing sec_code - ret %X\n", rc); + goto out; + } rc = regmap_update_bits(hap->regmap, addr, mask, val); if (rc < 0) - pr_err("Unable to update bits from 0x%04X, rc = %d\n", addr, - rc); - else - pr_debug("Wrote 0x%02X to addr 0x%04X\n", val, addr); + pr_err("Error writing address: %X - ret %X\n", addr, rc); +out: + spin_unlock_irqrestore(&hap->bus_lock, flags); + if (!rc) + pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val); return rc; } -/* helper to access secure registers */ -static int qpnp_hap_sec_access(struct qpnp_hap *hap) +static int qpnp_hap_masked_write_reg(struct qpnp_hap *hap, u16 addr, u8 mask, + u8 val) { + unsigned long flags; int rc; - u8 reg = QPNP_HAP_SEC_UNLOCK; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_SEC_ACCESS_REG(hap->base)); - if (rc) - return rc; + spin_lock_irqsave(&hap->bus_lock, flags); + rc = regmap_update_bits(hap->regmap, addr, mask, val); + if (rc < 0) + pr_err("Error writing address: %X - ret %X\n", addr, rc); - return 0; + spin_unlock_irqrestore(&hap->bus_lock, flags); + if (!rc) + pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val); + return rc; } static void qpnp_handle_sc_irq(struct work_struct *work) { struct qpnp_hap *hap = container_of(work, struct qpnp_hap, sc_work.work); - u8 val, reg; + u8 val; - qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); + qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); /* clear short circuit register */ if (val & SC_FOUND_BIT) { hap->sc_duration++; - reg = QPNP_HAP_SC_CLR; - qpnp_hap_write_reg(hap, ®, QPNP_HAP_SC_CLR_REG(hap->base)); + val = QPNP_HAP_SC_CLR; + qpnp_hap_write_reg(hap, QPNP_HAP_SC_CLR_REG(hap->base), val); } } @@ -484,10 +491,10 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on) unsigned long sleep_time = QPNP_HAP_CYCLS * hap->wave_play_rate_us; - rc = qpnp_hap_read_reg(hap, &val, - QPNP_HAP_STATUS(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), + &val); - dev_dbg(&hap->pdev->dev, "HAP_STATUS=0x%x\n", val); + pr_debug("HAP_STATUS=0x%x\n", val); /* wait for QPNP_HAP_CYCLS cycles of play rate */ if (val & QPNP_HAP_STATUS_BUSY) { @@ -500,14 +507,12 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on) } if (i >= QPNP_HAP_MAX_RETRIES) - dev_dbg(&hap->pdev->dev, - "Haptics Busy. Force disable\n"); + pr_debug("Haptics Busy. Force disable\n"); val &= ~QPNP_HAP_EN; } - rc = qpnp_hap_write_reg(hap, &val, - QPNP_HAP_EN_CTL_REG(hap->base)); + rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), val); if (rc < 0) return rc; @@ -527,8 +532,7 @@ static int qpnp_hap_play(struct qpnp_hap *hap, int on) else val &= ~QPNP_HAP_PLAY_EN; - rc = qpnp_hap_write_reg(hap, &val, - QPNP_HAP_PLAY_REG(hap->base)); + rc = qpnp_hap_write_reg(hap, QPNP_HAP_PLAY_REG(hap->base), val); if (rc < 0) return rc; @@ -548,7 +552,7 @@ static ssize_t qpnp_hap_dump_regs_show(struct device *dev, u8 val; for (i = 0; i < ARRAY_SIZE(qpnp_hap_dbg_regs); i++) { - qpnp_hap_read_reg(hap, &val, hap->base + qpnp_hap_dbg_regs[i]); + qpnp_hap_read_reg(hap, hap->base + qpnp_hap_dbg_regs[i], &val); count += snprintf(buf + count, PAGE_SIZE - count, "qpnp_haptics: REG_0x%x = 0x%x\n", hap->base + qpnp_hap_dbg_regs[i], @@ -566,15 +570,15 @@ static irqreturn_t qpnp_hap_play_irq(int irq, void *_hap) { struct qpnp_hap *hap = _hap; int i, rc; - u8 reg; + u8 val; mutex_lock(&hap->wf_lock); /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN && hap->wf_update; i++) { - reg = hap->wave_samp[i] = hap->shadow_wave_samp[i]; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_S_REG_BASE(hap->base) + i); + val = hap->wave_samp[i] = hap->shadow_wave_samp[i]; + rc = qpnp_hap_write_reg(hap, + QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val); if (rc) goto unlock; } @@ -591,13 +595,12 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) { struct qpnp_hap *hap = _hap; int rc; - u8 disable_haptics = 0x00; u8 val; - dev_dbg(&hap->pdev->dev, "Short circuit detected\n"); + pr_debug("Short circuit detected\n"); if (hap->sc_duration < SC_MAX_DURATION) { - qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); + qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); if (val & SC_FOUND_BIT) schedule_delayed_work(&hap->sc_work, QPNP_HAP_SC_IRQ_STATUS_DELAY); @@ -607,10 +610,10 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) /* Disable haptics module if the duration of short circuit * exceeds the maximum limit (5 secs). */ - rc = qpnp_hap_write_reg(hap, &disable_haptics, - QPNP_HAP_EN_CTL_REG(hap->base)); - dev_err(&hap->pdev->dev, - "Haptics disabled permanently due to short circuit\n"); + val = 0; + rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), + val); + pr_err("Haptics disabled permanently due to short circuit\n"); } return IRQ_HANDLED; @@ -619,8 +622,8 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) /* configuration api for buffer mode */ static int qpnp_hap_buffer_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, i, temp; + u8 val = 0; + int rc, i; /* Configure the WAVE_REPEAT register */ if (hap->wave_rep_cnt < QPNP_HAP_WAV_REP_MIN) @@ -633,44 +636,22 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap) else if (hap->wave_s_rep_cnt > QPNP_HAP_WAV_S_REP_MAX) hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MAX; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_WAV_REP_MASK; - temp = fls(hap->wave_rep_cnt) - 1; - reg |= (temp << QPNP_HAP_WAV_REP_SHFT); - reg &= QPNP_HAP_WAV_S_REP_MASK; - temp = fls(hap->wave_s_rep_cnt) - 1; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); + val = ilog2(hap->wave_rep_cnt) << QPNP_HAP_WAV_REP_SHIFT | + ilog2(hap->wave_s_rep_cnt); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base), + QPNP_HAP_WAV_REP_MASK | QPNP_HAP_WAV_S_REP_MASK, val); if (rc) return rc; /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ - for (i = 0, reg = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) { - reg = hap->wave_samp[i]; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_S_REG_BASE(hap->base) + i); + for (i = 0, val = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) { + val = hap->wave_samp[i]; + rc = qpnp_hap_write_reg(hap, + QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val); if (rc) return rc; } - /* setup play irq */ - if (hap->use_play_irq) { - rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq, - NULL, qpnp_hap_play_irq, - QPNP_IRQ_FLAGS, - "qpnp_play_irq", hap); - if (rc < 0) { - dev_err(&hap->pdev->dev, - "Unable to request play(%d) IRQ(err:%d)\n", - hap->play_irq, rc); - return rc; - } - } - hap->buffer_cfg_state = true; return 0; } @@ -678,59 +659,41 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap) /* configuration api for pwm */ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, temp; + u8 val = 0; + int rc; /* Configure the EXTERNAL_PWM register */ if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_25_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_25_KHZ; - temp = 0; + val = 0; } else if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_50_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_50_KHZ; - temp = 1; + val = 1; } else if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_75_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_75_KHZ; - temp = 2; + val = 2; } else { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_100_KHZ; - temp = 3; + val = 3; } - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_EXT_PWM_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_EXT_PWM_MASK; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_EXT_PWM_REG(hap->base)); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EXT_PWM_REG(hap->base), + QPNP_HAP_EXT_PWM_MASK, val); if (rc) return rc; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_TEST2_REG(hap->base)); - if (rc) - return rc; if (!hap->ext_pwm_dtest_line || hap->ext_pwm_dtest_line > PWM_MAX_DTEST_LINES) { - dev_err(&hap->pdev->dev, "invalid dtest line\n"); + pr_err("invalid dtest line\n"); return -EINVAL; } /* disable auto res for PWM mode */ - reg &= QPNP_HAP_EXT_PWM_DTEST_MASK; - temp = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT; - reg |= temp; - - /* TEST2 is a secure access register */ - rc = qpnp_hap_sec_access(hap); - if (rc) - return rc; - - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_TEST2_REG(hap->base)); + val = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT; + rc = qpnp_hap_sec_masked_write_reg(hap, QPNP_HAP_TEST2_REG(hap->base), + QPNP_HAP_EXT_PWM_DTEST_MASK | QPNP_HAP_AUTO_RES_MASK, val); if (rc) return rc; @@ -738,7 +701,7 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) hap->pwm_info.duty_us * NSEC_PER_USEC, hap->pwm_info.period_us * NSEC_PER_USEC); if (rc < 0) { - dev_err(&hap->pdev->dev, "hap pwm config failed\n"); + pr_err("hap pwm config failed\n"); pwm_free(hap->pwm_info.pwm_dev); return -ENODEV; } @@ -748,72 +711,171 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) return 0; } +static int qpnp_hap_lra_auto_res_config(struct qpnp_hap *hap) +{ + int rc; + u8 val, mask; + + /* disable auto resonance for ERM */ + if (hap->act_type == QPNP_HAP_ERM) { + val = 0x00; + rc = qpnp_hap_write_reg(hap, + QPNP_HAP_LRA_AUTO_RES_REG(hap->base), val); + return rc; + } + + if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN) + hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; + + if (hap->pmic_subtype == PM660_SUBTYPE) { + if (hap->lra_res_cal_period > + QPNP_HAP_PM660_RES_CAL_PERIOD_MAX) + hap->lra_res_cal_period = + QPNP_HAP_PM660_RES_CAL_PERIOD_MAX; + + if (hap->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD) + hap->lra_res_cal_period = 0; + } else { + if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX) + hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; + } + + val = mask = 0; + if (hap->lra_res_cal_period) + val = ilog2(hap->lra_res_cal_period / + QPNP_HAP_RES_CAL_PERIOD_MIN); + + if (hap->pmic_subtype == PM660_SUBTYPE) { + val |= hap->auto_res_mode << + QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT; + mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT; + val |= hap->lra_high_z << + QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT; + mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK; + if (hap->lra_qwd_drive_duration != -EINVAL) { + val |= hap->lra_qwd_drive_duration << + QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT; + mask |= QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT; + } + if (hap->calibrate_at_eop != -EINVAL) { + val |= hap->calibrate_at_eop << + QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT; + mask |= QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT; + } + mask |= QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK; + } else { + val |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT); + val |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); + mask = QPNP_HAP_AUTO_RES_MODE_MASK | QPNP_HAP_LRA_HIGH_Z_MASK | + QPNP_HAP_LRA_RES_CAL_PER_MASK; + } + + rc = qpnp_hap_masked_write_reg(hap, + QPNP_HAP_LRA_AUTO_RES_REG(hap->base), mask, val); + return rc; +} + /* configuration api for play mode */ static int qpnp_hap_play_mode_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, temp; + u8 val = 0; + int rc; - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PLAY_MODE_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_PLAY_MODE_MASK; - temp = hap->play_mode << QPNP_HAP_PLAY_MODE_SHFT; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_PLAY_MODE_REG(hap->base)); - if (rc) - return rc; - return 0; + val = hap->play_mode << QPNP_HAP_WF_SOURCE_SHIFT; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_SEL_REG(hap->base), + QPNP_HAP_WF_SOURCE_MASK, val); + return rc; } -/* configuration api for max volatge */ +/* configuration api for max voltage */ static int qpnp_hap_vmax_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, temp; + u8 val = 0; + int rc; if (hap->vmax_mv < QPNP_HAP_VMAX_MIN_MV) hap->vmax_mv = QPNP_HAP_VMAX_MIN_MV; else if (hap->vmax_mv > QPNP_HAP_VMAX_MAX_MV) hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV; - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_VMAX_MASK; - temp = hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV; - reg |= (temp << QPNP_HAP_VMAX_SHIFT); - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); - if (rc) - return rc; + val = (hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV) << QPNP_HAP_VMAX_SHIFT; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_VMAX_REG(hap->base), + QPNP_HAP_VMAX_MASK, val); + return rc; +} - return 0; +/* configuration api for ilim */ +static int qpnp_hap_ilim_config(struct qpnp_hap *hap) +{ + u8 val = 0; + int rc; + + if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA) + hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA; + else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA) + hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA; + + val = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) - 1; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_ILIM_REG(hap->base), + QPNP_HAP_ILIM_MASK, val); + return rc; } /* configuration api for short circuit debounce */ static int qpnp_hap_sc_deb_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, temp; + u8 val = 0; + int rc; if (hap->sc_deb_cycles < QPNP_HAP_SC_DEB_CYCLES_MIN) hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MIN; else if (hap->sc_deb_cycles > QPNP_HAP_SC_DEB_CYCLES_MAX) hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MAX; - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_SC_DEB_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_SC_DEB_MASK; - if (hap->sc_deb_cycles) { - temp = fls(hap->sc_deb_cycles) - 1; - reg |= temp - QPNP_HAP_SC_DEB_SUB; + if (hap->sc_deb_cycles != QPNP_HAP_SC_DEB_CYCLES_MIN) + val = ilog2(hap->sc_deb_cycles / + QPNP_HAP_DEF_SC_DEB_CYCLES) + 1; + else + val = QPNP_HAP_SC_DEB_CYCLES_MIN; + + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_SC_DEB_REG(hap->base), + QPNP_HAP_SC_DEB_MASK, val); + + return rc; +} + +static int qpnp_hap_int_pwm_config(struct qpnp_hap *hap) +{ + int rc; + u8 val; + + if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { + if (hap->pmic_subtype == PM660_SUBTYPE) { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; + val = 1; + } else { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ; + val = 0; + } + } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; + val = 1; + } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ; + val = 2; + } else { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ; + val = 3; } - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_SC_DEB_REG(hap->base)); + + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_INT_PWM_REG(hap->base), + QPNP_HAP_INT_PWM_MASK, val); if (rc) return rc; - return 0; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_PWM_CAP_REG(hap->base), + QPNP_HAP_INT_PWM_MASK, val); + return rc; } /* DT parsing api for buffer mode */ @@ -830,7 +892,7 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap) if (!rc) { hap->wave_rep_cnt = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read rep cnt\n"); + pr_err("Unable to read rep cnt\n"); return rc; } @@ -840,30 +902,20 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap) if (!rc) { hap->wave_s_rep_cnt = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read samp rep cnt\n"); + pr_err("Unable to read samp rep cnt\n"); return rc; } prop = of_find_property(pdev->dev.of_node, "qcom,wave-samples", &temp); if (!prop || temp != QPNP_HAP_WAV_SAMP_LEN) { - dev_err(&pdev->dev, "Invalid wave samples, use default"); + pr_err("Invalid wave samples, use default"); for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) hap->wave_samp[i] = QPNP_HAP_WAV_SAMP_MAX; } else { memcpy(hap->wave_samp, prop->value, QPNP_HAP_WAV_SAMP_LEN); } - hap->use_play_irq = of_property_read_bool(pdev->dev.of_node, - "qcom,use-play-irq"); - if (hap->use_play_irq) { - hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq"); - if (hap->play_irq < 0) { - dev_err(&pdev->dev, "Unable to get play irq\n"); - return hap->play_irq; - } - } - return 0; } @@ -880,7 +932,7 @@ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap) if (!rc) { hap->ext_pwm_freq_khz = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read ext pwm freq\n"); + pr_err("Unable to read ext pwm freq\n"); return rc; } @@ -895,7 +947,7 @@ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap) if (IS_ERR(hap->pwm_info.pwm_dev)) { rc = PTR_ERR(hap->pwm_info.pwm_dev); - dev_err(&pdev->dev, "Cannot get PWM device rc:(%d)\n", rc); + pr_err("Cannot get PWM device rc:(%d)\n", rc); hap->pwm_info.pwm_dev = NULL; return rc; } @@ -930,7 +982,7 @@ static ssize_t qpnp_hap_wf_samp_show(struct device *dev, char *buf, int index) timed_dev); if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) { - dev_err(dev, "Invalid sample index(%d)\n", index); + pr_err("Invalid sample index(%d)\n", index); return -EINVAL; } @@ -996,7 +1048,7 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev, int data, rc; if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) { - dev_err(dev, "Invalid sample index(%d)\n", index); + pr_err("Invalid sample index(%d)\n", index); return -EINVAL; } @@ -1005,7 +1057,7 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev, return rc; if (data < 0 || data > 0xff) { - dev_err(dev, "Invalid sample wf_%d (%d)\n", index, data); + pr_err("Invalid sample wf_%d (%d)\n", index, data); return -EINVAL; } @@ -1105,8 +1157,8 @@ static ssize_t qpnp_hap_wf_rep_store(struct device *dev, struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); - int data, rc, temp; - u8 reg; + int data, rc; + u8 val; rc = kstrtoint(buf, 10, &data); if (rc) @@ -1117,19 +1169,11 @@ static ssize_t qpnp_hap_wf_rep_store(struct device *dev, else if (data > QPNP_HAP_WAV_REP_MAX) data = QPNP_HAP_WAV_REP_MAX; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_WAV_REP_MASK; - temp = fls(data) - 1; - reg |= (temp << QPNP_HAP_WAV_REP_SHFT); - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc) - return rc; - - hap->wave_rep_cnt = data; + val = ilog2(data) << QPNP_HAP_WAV_REP_SHIFT; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base), + QPNP_HAP_WAV_REP_MASK, val); + if (!rc) + hap->wave_rep_cnt = data; return count; } @@ -1152,8 +1196,8 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev, struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); - int data, rc, temp; - u8 reg; + int data, rc; + u8 val; rc = kstrtoint(buf, 10, &data); if (rc) @@ -1164,19 +1208,11 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev, else if (data > QPNP_HAP_WAV_S_REP_MAX) data = QPNP_HAP_WAV_S_REP_MAX; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_WAV_S_REP_MASK; - temp = fls(data) - 1; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc) - return rc; - - hap->wave_s_rep_cnt = data; + val = ilog2(hap->wave_s_rep_cnt); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base), + QPNP_HAP_WAV_S_REP_MASK, val); + if (!rc) + hap->wave_s_rep_cnt = data; return count; } @@ -1397,25 +1433,22 @@ static int calculate_lra_code(struct qpnp_hap *hap) u8 neg_idx = 0, pos_idx = ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE - 1; int rc = 0; - rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), + &lra_drive_period_code_lo); if (rc) { - dev_err(&hap->pdev->dev, - "Error while reading RATE_CFG1 register\n"); + pr_err("Error while reading RATE_CFG1 register\n"); return rc; } - rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), + &lra_drive_period_code_hi); if (rc) { - dev_err(&hap->pdev->dev, - "Error while reading RATE_CFG2 register\n"); + pr_err("Error while reading RATE_CFG2 register\n"); return rc; } if (!lra_drive_period_code_lo && !lra_drive_period_code_hi) { - dev_err(&hap->pdev->dev, - "Unexpected Error: both RATE_CFG1 and RATE_CFG2 read 0\n"); + pr_err("Unexpected Error: both RATE_CFG1 and RATE_CFG2 read 0\n"); return -EINVAL; } @@ -1433,16 +1466,14 @@ static int calculate_lra_code(struct qpnp_hap *hap) start_variation -= AUTO_RES_ERROR_CAPTURE_RES; } - dev_dbg(&hap->pdev->dev, - "lra_drive_period_code_lo = 0x%x lra_drive_period_code_hi = 0x%x\n" + pr_debug("lra_drive_period_code_lo = 0x%x lra_drive_period_code_hi = 0x%x\n" "lra_drive_period_code = 0x%x, lra_drive_frequency_hz = 0x%x\n" "Calculated play rate code values are :\n", lra_drive_period_code_lo, lra_drive_period_code_hi, lra_drive_period_code, lra_drive_frequency_hz); for (i = 0; i < ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE; ++i) - dev_dbg(&hap->pdev->dev, - " 0x%x", adjusted_lra_play_rate_code[i]); + pr_debug(" 0x%x", adjusted_lra_play_rate_code[i]); return 0; } @@ -1450,25 +1481,18 @@ static int calculate_lra_code(struct qpnp_hap *hap) static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) { int rc = 0; - u8 val = 0; - u16 addr; - - - if (hap->pmic_subtype == PM660_SUBTYPE) { - addr = QPNP_HAP_AUTO_RES_CTRL(hap->base); - } else { - addr = QPNP_HAP_TEST2_REG(hap->base); - /* TEST2 is a secure access register */ - rc = qpnp_hap_sec_access(hap); - if (rc) - return rc; - } - - if (enable) - val |= AUTO_RES_ENABLE; + u8 val; - rc = qpnp_hap_masked_write_reg(hap, val, addr, AUTO_RES_ENABLE); - if (rc) + val = enable ? AUTO_RES_ENABLE : 0; + if (hap->pmic_subtype == PM660_SUBTYPE) + rc = qpnp_hap_masked_write_reg(hap, + QPNP_HAP_AUTO_RES_CTRL(hap->base), + QPNP_HAP_AUTO_RES_MASK, val); + else + rc = qpnp_hap_sec_masked_write_reg(hap, + QPNP_HAP_TEST2_REG(hap->base), + QPNP_HAP_AUTO_RES_MASK, val); + if (rc < 0) return rc; if (enable) @@ -1476,7 +1500,7 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) else hap->status_flags &= ~AUTO_RESONANCE_ENABLED; - return 0; + return rc; } static void update_lra_frequency(struct qpnp_hap *hap) @@ -1485,19 +1509,18 @@ static void update_lra_frequency(struct qpnp_hap *hap) u32 play_rate_code; int rc; - qpnp_hap_read_reg(hap, &lra_auto_res_lo, - QPNP_HAP_LRA_AUTO_RES_LO(hap->base)); - qpnp_hap_read_reg(hap, &lra_auto_res_hi, - QPNP_HAP_LRA_AUTO_RES_HI(hap->base)); + qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_LO(hap->base), + &lra_auto_res_lo); + qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_HI(hap->base), + &lra_auto_res_hi); play_rate_code = (lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF); - dev_dbg(&hap->pdev->dev, - "lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n", + pr_debug("lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n", lra_auto_res_lo, lra_auto_res_hi, play_rate_code); - rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); if (rc < 0) return; @@ -1511,22 +1534,25 @@ static void update_lra_frequency(struct qpnp_hap *hap) if ((val & AUTO_RES_ERR_BIT) || ((play_rate_code <= hap->drive_period_code_min_limit) || (play_rate_code >= hap->drive_period_code_max_limit))) { - dev_dbg(&hap->pdev->dev, - "Auto-resonance error, out of 25%%, [min: 0x%x, max: 0x%x]\n", - hap->drive_period_code_min_limit, - hap->drive_period_code_max_limit); + if (val & AUTO_RES_ERR_BIT) + pr_debug("Auto-resonance error %x\n", val); + else + pr_debug("play rate %x out of bounds [min: 0x%x, max: 0x%x]\n", + play_rate_code, + hap->drive_period_code_min_limit, + hap->drive_period_code_max_limit); rc = qpnp_hap_auto_res_enable(hap, 0); if (rc < 0) - dev_dbg(&hap->pdev->dev, "Auto-resonance write failed\n"); + pr_debug("Auto-resonance write failed\n"); return; } - qpnp_hap_write_reg(hap, &lra_auto_res_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); + qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), + lra_auto_res_lo); lra_auto_res_hi = lra_auto_res_hi >> 4; - qpnp_hap_write_reg(hap, &lra_auto_res_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), + lra_auto_res_hi); } static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) @@ -1535,7 +1561,6 @@ static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) auto_res_err_poll_timer); ktime_t currtime; - if (!(hap->status_flags & AUTO_RESONANCE_ENABLED)) return HRTIMER_NORESTART; @@ -1680,7 +1705,7 @@ int qpnp_hap_play_byte(u8 data, bool on) } if (hap->play_mode != QPNP_HAP_PWM) { - dev_err(&hap->pdev->dev, "only PWM mode is supported\n"); + pr_err("only PWM mode is supported\n"); return -EINVAL; } @@ -1721,8 +1746,7 @@ int qpnp_hap_play_byte(u8 data, bool on) if (rc) return rc; - dev_dbg(&hap->pdev->dev, "data=0x%x duty_per=%d\n", data, - duty_percent); + pr_debug("data=0x%x duty_per=%d\n", data, duty_percent); rc = qpnp_hap_set(hap, true); @@ -1741,8 +1765,8 @@ static void qpnp_hap_worker(struct work_struct *work) if (hap->vcc_pon && hap->state && !hap->vcc_pon_enabled) { rc = regulator_enable(hap->vcc_pon); if (rc < 0) - pr_err("%s: could not enable vcc_pon regulator rc=%d\n", - __func__, rc); + pr_err("could not enable vcc_pon regulator rc=%d\n", + rc); else hap->vcc_pon_enabled = true; } @@ -1751,8 +1775,8 @@ static void qpnp_hap_worker(struct work_struct *work) * exceeds the maximum limit (5 secs). */ if (hap->sc_duration == SC_MAX_DURATION) { - rc = qpnp_hap_write_reg(hap, &val, - QPNP_HAP_EN_CTL_REG(hap->base)); + rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), + val); } else { if (hap->play_mode == QPNP_HAP_PWM) qpnp_hap_mod_enable(hap, hap->state); @@ -1762,8 +1786,8 @@ static void qpnp_hap_worker(struct work_struct *work) if (hap->vcc_pon && !hap->state && hap->vcc_pon_enabled) { rc = regulator_disable(hap->vcc_pon); if (rc) - pr_err("%s: could not disable vcc_pon regulator rc=%d\n", - __func__, rc); + pr_err("could not disable vcc_pon regulator rc=%d\n", + rc); else hap->vcc_pon_enabled = false; } @@ -1827,99 +1851,25 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL); /* Configuration api for haptics registers */ static int qpnp_hap_config(struct qpnp_hap *hap) { - u8 reg = 0, unlock_val, mask; + u8 val = 0; u32 temp; int rc, i; - uint error_code = 0; /* * This denotes the percentage error in rc clock multiplied by 10 */ u8 rc_clk_err_percent_x10; - /* Configure the ACTUATOR TYPE register */ - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_ACT_TYPE_MASK; - reg |= hap->act_type; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); + /* Configure the CFG1 register for actuator type */ + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG1_REG(hap->base), + QPNP_HAP_ACT_TYPE_MASK, hap->act_type); if (rc) return rc; /* Configure auto resonance parameters */ - if (hap->act_type == QPNP_HAP_LRA) { - if (hap->pmic_subtype == PM660_SUBTYPE) { - if (hap->lra_res_cal_period < - QPNP_HAP_PM660_RES_CAL_PERIOD_MIN) - hap->lra_res_cal_period = - QPNP_HAP_PM660_RES_CAL_PERIOD_MIN; - else if (hap->lra_res_cal_period > - QPNP_HAP_PM660_RES_CAL_PERIOD_MAX) - hap->lra_res_cal_period = - QPNP_HAP_PM660_RES_CAL_PERIOD_MAX; - } else if (hap->pmic_subtype != PM660_SUBTYPE) { - if (hap->lra_res_cal_period < - QPNP_HAP_RES_CAL_PERIOD_MIN) - hap->lra_res_cal_period = - QPNP_HAP_RES_CAL_PERIOD_MIN; - else if (hap->lra_res_cal_period > - QPNP_HAP_RES_CAL_PERIOD_MAX) - hap->lra_res_cal_period = - QPNP_HAP_RES_CAL_PERIOD_MAX; - } - if (hap->pmic_subtype == PM660_SUBTYPE && - hap->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD) { - hap->lra_res_cal_period = 0; - } - - reg = mask = 0; - if (hap->pmic_subtype == PM660_SUBTYPE) { - reg |= hap->auto_res_mode << - QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT; - mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT; - reg |= hap->lra_high_z << - QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT; - mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK; - if (hap->lra_qwd_drive_duration != -EINVAL) { - reg |= hap->lra_qwd_drive_duration << - QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT; - mask |= QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT; - } - if (hap->calibrate_at_eop != -EINVAL) { - reg |= hap->calibrate_at_eop << - QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT; - mask |= QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT; - } - if (hap->lra_res_cal_period) { - temp = fls(hap->lra_res_cal_period) - 1; - reg |= (temp - 1); - } - mask |= QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK; - } else { - reg |= (hap->auto_res_mode << - QPNP_HAP_AUTO_RES_MODE_SHIFT); - mask = QPNP_HAP_AUTO_RES_MODE_MASK; - reg |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); - mask |= QPNP_HAP_LRA_HIGH_Z_MASK; - temp = fls(hap->lra_res_cal_period) - 1; - reg |= (temp - 2); - mask |= QPNP_HAP_LRA_RES_CAL_PER_MASK; - } - rc = qpnp_hap_masked_write_reg(hap, reg, - QPNP_HAP_LRA_AUTO_RES_REG(hap->base), - mask); - if (rc) - return rc; - } else { - /* disable auto resonance for ERM */ - reg = 0x00; - - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); - if (rc) - return rc; - } + rc = qpnp_hap_lra_auto_res_config(hap); + if (rc) + return rc; /* Configure the PLAY MODE register */ rc = qpnp_hap_play_mode_config(hap); @@ -1932,18 +1882,7 @@ static int qpnp_hap_config(struct qpnp_hap *hap) return rc; /* Configure the ILIM register */ - if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA) - hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA; - else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA) - hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA; - - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ILIM_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_ILIM_MASK; - temp = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) >> 1; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_ILIM_REG(hap->base)); + rc = qpnp_hap_ilim_config(hap); if (rc) return rc; @@ -1953,52 +1892,13 @@ static int qpnp_hap_config(struct qpnp_hap *hap) return rc; /* Configure the INTERNAL_PWM register */ - if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { - if (hap->pmic_subtype == PM660_SUBTYPE) { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; - temp = 1; - } else { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ; - temp = 0; - } - } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; - temp = 1; - } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ; - temp = 2; - } else { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ; - temp = 3; - } - - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_INT_PWM_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_INT_PWM_MASK; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_INT_PWM_REG(hap->base)); - if (rc) - return rc; - - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PWM_CAP_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_INT_PWM_MASK; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_PWM_CAP_REG(hap->base)); + rc = qpnp_hap_int_pwm_config(hap); if (rc) return rc; /* Configure the WAVE SHAPE register */ - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_WAV_SHAPE_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_WAV_SHAPE_MASK; - reg |= hap->wave_shape; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_SHAPE_REG(hap->base)); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG2_REG(hap->base), + QPNP_HAP_WAV_SHAPE_MASK, hap->wave_shape); if (rc) return rc; @@ -2019,35 +1919,24 @@ static int qpnp_hap_config(struct qpnp_hap *hap) * The frequency of 19.2Mzhz RC clock is subject to variation. Currently * a few PMI modules have MISC_TRIM_ERROR_RC19P2_CLK register * present in their MISC block. This register holds the frequency error - * in 19.2Mhz RC clock. + * in 19.2 MHz RC clock. */ if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq - && hap->misc_trim_error_rc19p2_clk_reg_present) { - unlock_val = MISC_SEC_UNLOCK; - /* - * This SID value may change depending on the PMI chip where - * the MISC block is present. - */ - rc = regmap_write(hap->regmap, MISC_SEC_ACCESS, unlock_val); - if (rc) - dev_err(&hap->pdev->dev, - "Unable to do SEC_ACCESS rc:%d\n", rc); - - regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK, - &error_code); - dev_dbg(&hap->pdev->dev, "TRIM register = 0x%x\n", error_code); + && hap->misc_clk_trim_error_reg) { + pr_debug("TRIM register = 0x%x\n", hap->clk_trim_error_code); /* * Extract the 4 LSBs and multiply by 7 to get * the %error in RC clock multiplied by 10 */ - rc_clk_err_percent_x10 = (error_code & 0x0F) * 7; + rc_clk_err_percent_x10 = (hap->clk_trim_error_code & 0x0F) * 7; /* * If the TRIM register holds value less than 0x80, * then there is a positive error in the RC clock. * If the TRIM register holds value greater than or equal to - * 0x80, then there is a negative error in the RC clock. + * 0x80, then there is a negative error in the RC clock. Bit 7 + * is the sign bit for error code. * * The adjusted play rate code is calculated as follows: * LRA drive period code (RATE_CFG) = @@ -2060,24 +1949,21 @@ static int qpnp_hap_config(struct qpnp_hap *hap) * Since 200KHz * 1/LRA drive frequency is already calculated * above we only do rest of the scaling here. */ - if (error_code >= 128) + if (hap->clk_trim_error_code & BIT(7)) LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent_x10); else LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent_x10); } - dev_dbg(&hap->pdev->dev, - "Play rate code 0x%x\n", hap->init_drive_period_code); + pr_debug("Play rate code 0x%x\n", hap->init_drive_period_code); - reg = hap->init_drive_period_code & QPNP_HAP_RATE_CFG1_MASK; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_RATE_CFG1_REG(hap->base)); + val = hap->init_drive_period_code & QPNP_HAP_RATE_CFG1_MASK; + rc = qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), val); if (rc) return rc; - reg = (hap->init_drive_period_code & 0xF00) >> QPNP_HAP_RATE_CFG2_SHFT; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + val = (hap->init_drive_period_code & 0xF00) >> QPNP_HAP_RATE_CFG2_SHFT; + rc = qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), val); if (rc) return rc; @@ -2094,45 +1980,40 @@ static int qpnp_hap_config(struct qpnp_hap *hap) (hap->init_drive_period_code * (100 - hap->drive_period_code_min_limit_percent_variation)) / 100; - dev_dbg(&hap->pdev->dev, "Drive period code max limit %x\n" - "Drive period code min limit %x\n", - hap->drive_period_code_max_limit, - hap->drive_period_code_min_limit); + pr_debug("Drive period code max limit %x min limit %x\n", + hap->drive_period_code_max_limit, + hap->drive_period_code_min_limit); } /* Configure BRAKE register */ - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_BRAKE_MASK; - reg |= hap->en_brake; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EN_CTL2_REG(hap->base), + QPNP_HAP_BRAKE_MASK, (u8)hap->en_brake); if (rc) return rc; if (hap->en_brake && hap->sup_brake_pat) { - for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, reg = 0; i >= 0; i--) { + for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, val = 0; i >= 0; i--) { hap->brake_pat[i] &= QPNP_HAP_BRAKE_PAT_MASK; temp = i << 1; - reg |= hap->brake_pat[i] << temp; + val |= hap->brake_pat[i] << temp; } - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_BRAKE_REG(hap->base)); + rc = qpnp_hap_write_reg(hap, QPNP_HAP_BRAKE_REG(hap->base), + val); if (rc) return rc; } /* Cache enable control register */ - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), &val); if (rc < 0) return rc; - hap->reg_en_ctl = reg; + hap->reg_en_ctl = val; /* Cache play register */ - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PLAY_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_PLAY_REG(hap->base), &val); if (rc < 0) return rc; - hap->reg_play = reg; + hap->reg_play = val; if (hap->play_mode == QPNP_HAP_BUFFER) rc = qpnp_hap_buffer_config(hap); @@ -2144,15 +2025,29 @@ static int qpnp_hap_config(struct qpnp_hap *hap) if (rc) return rc; + /* setup play irq */ + if (hap->play_irq >= 0) { + rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq, + NULL, qpnp_hap_play_irq, IRQF_ONESHOT, "qpnp_hap_play", + hap); + if (rc < 0) { + pr_err("Unable to request play(%d) IRQ(err:%d)\n", + hap->play_irq, rc); + return rc; + } + + /* use play_irq only for buffer mode */ + if (hap->play_mode != QPNP_HAP_BUFFER) + disable_irq(hap->play_irq); + } + /* setup short circuit irq */ - if (hap->use_sc_irq) { + if (hap->sc_irq >= 0) { rc = devm_request_threaded_irq(&hap->pdev->dev, hap->sc_irq, - NULL, qpnp_hap_sc_irq, - QPNP_IRQ_FLAGS, - "qpnp_sc_irq", hap); + NULL, qpnp_hap_sc_irq, IRQF_ONESHOT, "qpnp_hap_sc", + hap); if (rc < 0) { - dev_err(&hap->pdev->dev, - "Unable to request sc(%d) IRQ(err:%d)\n", + pr_err("Unable to request sc(%d) IRQ(err:%d)\n", hap->sc_irq, rc); return rc; } @@ -2167,18 +2062,46 @@ static int qpnp_hap_config(struct qpnp_hap *hap) static int qpnp_hap_parse_dt(struct qpnp_hap *hap) { struct platform_device *pdev = hap->pdev; + struct device_node *misc_node; struct property *prop; const char *temp_str; u32 temp; int rc; + if (of_find_property(pdev->dev.of_node, "qcom,pmic-misc", NULL)) { + misc_node = of_parse_phandle(pdev->dev.of_node, + "qcom,pmic-misc", 0); + if (!misc_node) + return -EINVAL; + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,misc-clk-trim-error-reg", &temp); + if (rc < 0) { + pr_err("Missing misc-clk-trim-error-reg\n"); + return rc; + } + + if (!temp || temp > 0xFF) { + pr_err("Invalid misc-clk-trim-error-reg\n"); + return -EINVAL; + } + + hap->misc_clk_trim_error_reg = temp; + rc = qpnp_misc_read_reg(misc_node, hap->misc_clk_trim_error_reg, + &hap->clk_trim_error_code); + if (rc < 0) { + pr_err("Couldn't get clk_trim_error_code, rc=%d\n", rc); + return -EPROBE_DEFER; + } + } + hap->timeout_ms = QPNP_HAP_TIMEOUT_MS_MAX; rc = of_property_read_u32(pdev->dev.of_node, "qcom,timeout-ms", &temp); if (!rc) { hap->timeout_ms = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read timeout\n"); + pr_err("Unable to read timeout\n"); return rc; } @@ -2191,11 +2114,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else if (strcmp(temp_str, "lra") == 0) hap->act_type = QPNP_HAP_LRA; else { - dev_err(&pdev->dev, "Invalid actuator type\n"); + pr_err("Invalid actuator type\n"); return -EINVAL; } } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read actuator type\n"); + pr_err("Unable to read actuator type\n"); return rc; } @@ -2231,7 +2154,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) QPNP_HAP_AUTO_RES_ZXD_EOP; } } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read auto res mode\n"); + pr_err("Unable to read auto res mode\n"); return rc; } @@ -2253,7 +2176,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read LRA high-z\n"); + pr_err("Unable to read LRA high-z\n"); return rc; } @@ -2272,7 +2195,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->lra_res_cal_period = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read cal period\n"); + pr_err("Unable to read cal period\n"); return rc; } @@ -2298,10 +2221,6 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) hap->drive_period_code_min_limit_percent_variation = (u8) temp; - hap->misc_trim_error_rc19p2_clk_reg_present = - of_property_read_bool(pdev->dev.of_node, - "qcom,misc-trim-error-rc19p2-clk-reg-present"); - if (hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) { hap->time_required_to_generate_back_emf_us = QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN; @@ -2328,11 +2247,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else if (strcmp(temp_str, "audio") == 0) hap->play_mode = QPNP_HAP_AUDIO; else { - dev_err(&pdev->dev, "Invalid play mode\n"); + pr_err("Invalid play mode\n"); return -EINVAL; } } else { - dev_err(&pdev->dev, "Unable to read play mode\n"); + pr_err("Unable to read play mode\n"); return rc; } @@ -2341,7 +2260,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->vmax_mv = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read vmax\n"); + pr_err("Unable to read vmax\n"); return rc; } @@ -2350,7 +2269,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->ilim_ma = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read ILim\n"); + pr_err("Unable to read ILim\n"); return rc; } @@ -2360,7 +2279,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->sc_deb_cycles = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read sc debounce\n"); + pr_err("Unable to read sc debounce\n"); return rc; } @@ -2370,7 +2289,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->int_pwm_freq_khz = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read int pwm freq\n"); + pr_err("Unable to read int pwm freq\n"); return rc; } @@ -2383,11 +2302,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else if (strcmp(temp_str, "square") == 0) hap->wave_shape = QPNP_HAP_WAV_SQUARE; else { - dev_err(&pdev->dev, "Unsupported wav shape\n"); + pr_err("Unsupported wav shape\n"); return -EINVAL; } } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read wav shape\n"); + pr_err("Unable to read wav shape\n"); return rc; } @@ -2397,7 +2316,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->wave_play_rate_us = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read play rate\n"); + pr_err("Unable to read play rate\n"); return rc; } @@ -2416,9 +2335,9 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) prop = of_find_property(pdev->dev.of_node, "qcom,brake-pattern", &temp); if (!prop) { - dev_info(&pdev->dev, "brake pattern not found"); + pr_info("brake pattern not found"); } else if (temp != QPNP_HAP_BRAKE_PAT_LEN) { - dev_err(&pdev->dev, "Invalid len of brake pattern\n"); + pr_err("Invalid len of brake pattern\n"); return -EINVAL; } else { hap->sup_brake_pat = true; @@ -2427,14 +2346,14 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) } } - hap->use_sc_irq = of_property_read_bool(pdev->dev.of_node, - "qcom,use-sc-irq"); - if (hap->use_sc_irq) { - hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq"); - if (hap->sc_irq < 0) { - dev_err(&pdev->dev, "Unable to get sc irq\n"); - return hap->sc_irq; - } + hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq"); + if (hap->play_irq < 0) + pr_warn("Unable to get play irq\n"); + + hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq"); + if (hap->sc_irq < 0) { + pr_err("Unable to get sc irq\n"); + return hap->sc_irq; } if (of_find_property(pdev->dev.of_node, "vcc_pon-supply", NULL)) @@ -2483,7 +2402,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) return -ENOMEM; hap->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!hap->regmap) { - dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); + pr_err("Couldn't get parent's regmap\n"); return -EINVAL; } @@ -2491,8 +2410,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = of_property_read_u32(pdev->dev.of_node, "reg", &base); if (rc < 0) { - dev_err(&pdev->dev, - "Couldn't find reg in node = %s rc = %d\n", + pr_err("Couldn't find reg in node = %s rc = %d\n", pdev->dev.of_node->full_name, rc); return rc; } @@ -2508,13 +2426,14 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = qpnp_hap_parse_dt(hap); if (rc) { - dev_err(&pdev->dev, "DT parsing failed\n"); + pr_err("DT parsing failed\n"); return rc; } + spin_lock_init(&hap->bus_lock); rc = qpnp_hap_config(hap); if (rc) { - dev_err(&pdev->dev, "hap config failed\n"); + pr_err("hap config failed\n"); return rc; } @@ -2542,7 +2461,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = timed_output_dev_register(&hap->timed_dev); if (rc < 0) { - dev_err(&pdev->dev, "timed_output registration failed\n"); + pr_err("timed_output registration failed\n"); goto timed_output_fail; } @@ -2550,7 +2469,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = sysfs_create_file(&hap->timed_dev.dev->kobj, &qpnp_hap_attrs[i].attr); if (rc < 0) { - dev_err(&pdev->dev, "sysfs creation failed\n"); + pr_err("sysfs creation failed\n"); goto sysfs_fail; } } @@ -2559,8 +2478,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) vcc_pon = regulator_get(&pdev->dev, "vcc_pon"); if (IS_ERR(vcc_pon)) { rc = PTR_ERR(vcc_pon); - dev_err(&pdev->dev, - "regulator get failed vcc_pon rc=%d\n", rc); + pr_err("regulator get failed vcc_pon rc=%d\n", rc); goto sysfs_fail; } hap->vcc_pon = vcc_pon; diff --git a/drivers/soc/qcom/rpm-smd.c b/drivers/soc/qcom/rpm-smd.c index 20f406b9a2f7..f2784dedbc7a 100644 --- a/drivers/soc/qcom/rpm-smd.c +++ b/drivers/soc/qcom/rpm-smd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -716,51 +716,6 @@ int msm_rpm_smd_buffer_request(struct msm_rpm_request *cdata, return 0; } -static void msm_rpm_print_sleep_buffer(struct slp_buf *s) -{ - char buf[DEBUG_PRINT_BUFFER_SIZE] = {0}; - int pos; - int buflen = DEBUG_PRINT_BUFFER_SIZE; - char ch[5] = {0}; - struct kvp *e; - uint32_t type; - unsigned int id; - - if (!s) - return; - - if (!s->valid) - return; - - type = get_rsc_type(s->buf); - id = get_rsc_id(s->buf); - - memcpy(ch, &type, sizeof(u32)); - - pos = scnprintf(buf, buflen, - "Sleep request type = 0x%08x(%s)", - type, ch); - pos += scnprintf(buf + pos, buflen - pos, " id = 0%x", - id); - for_each_kvp(s->buf, e) { - uint32_t i; - char *data = get_data(e); - - memcpy(ch, &e->k, sizeof(u32)); - - pos += scnprintf(buf + pos, buflen - pos, - "\n\t\tkey = 0x%08x(%s)", - e->k, ch); - pos += scnprintf(buf + pos, buflen - pos, - " sz= %d data =", e->s); - - for (i = 0; i < e->s; i++) - pos += scnprintf(buf + pos, buflen - pos, - " 0x%02X", data[i]); - } - pos += scnprintf(buf + pos, buflen - pos, "\n"); - printk(buf); -} static struct msm_rpm_driver_data msm_rpm_data = { .smd_open = COMPLETION_INITIALIZER(msm_rpm_data.smd_open), @@ -821,9 +776,6 @@ static int msm_rpm_flush_requests(bool print) if (!s->valid) continue; - if (print) - msm_rpm_print_sleep_buffer(s); - set_msg_id(s->buf, msm_rpm_get_next_msg_id()); if (!glink_enabled) diff --git a/drivers/soc/qcom/secure_buffer.c b/drivers/soc/qcom/secure_buffer.c index 90c7585d480c..4307937d9f6d 100644 --- a/drivers/soc/qcom/secure_buffer.c +++ b/drivers/soc/qcom/secure_buffer.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 Google, Inc - * Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -379,6 +379,7 @@ int hyp_assign_phys(phys_addr_t addr, u64 size, u32 *source_vm_list, sg_free_table(&table); return ret; } +EXPORT_SYMBOL(hyp_assign_phys); const char *msm_secure_vmid_to_string(int secure_vmid) { diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index 8581ed587ead..5ac2a58899f4 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -266,10 +266,9 @@ static int service_locator_send_msg(struct pd_qmi_client_data *pd) if (!domains_read) { db_rev_count = pd->db_rev_count = resp->db_rev_count; pd->total_domains = resp->total_domains; - if (!pd->total_domains && resp->domain_list_len) { - pr_err("total domains not set\n"); - pd->total_domains = resp->domain_list_len; - } + if (!resp->total_domains) + pr_info("No matching domains found\n"); + pd->domain_list = kmalloc( sizeof(struct servreg_loc_entry_v01) * resp->total_domains, GFP_KERNEL); @@ -286,6 +285,10 @@ static int service_locator_send_msg(struct pd_qmi_client_data *pd) rc = -EAGAIN; goto out; } + if (resp->domain_list_len > resp->total_domains) { + /* Always read total_domains from the response msg */ + resp->domain_list_len = resp->total_domains; + } /* Copy the response*/ store_get_domain_list_response(pd, resp, domains_read); domains_read += resp->domain_list_len; @@ -372,6 +375,7 @@ int get_service_location(char *client_name, char *service_name, if (!pqw) { rc = -ENOMEM; pr_err("Allocation failed\n"); + kfree(pqcd); goto err; } pqw->notifier = locator_nb; diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c index ebea5b7726e4..fa916ac5ade4 100644 --- a/drivers/soc/qcom/service-notifier.c +++ b/drivers/soc/qcom/service-notifier.c @@ -99,11 +99,12 @@ struct ind_req_resp { */ struct qmi_client_info { int instance_id; - int subsys_state; + enum pd_subsys_state subsys_state; struct work_struct svc_arrive; struct work_struct svc_exit; struct work_struct svc_rcv_msg; struct work_struct ind_ack; + struct work_struct qmi_handle_free; struct workqueue_struct *svc_event_wq; struct qmi_handle *clnt_handle; struct notifier_block notifier; @@ -123,6 +124,18 @@ static void root_service_clnt_recv_msg(struct work_struct *work); static void root_service_service_arrive(struct work_struct *work); static void root_service_exit_work(struct work_struct *work); +static void free_qmi_handle(struct work_struct *work) +{ + struct qmi_client_info *data = container_of(work, + struct qmi_client_info, qmi_handle_free); + + mutex_lock(&qmi_client_release_lock); + data->service_connected = false; + qmi_handle_destroy(data->clnt_handle); + data->clnt_handle = NULL; + mutex_unlock(&qmi_client_release_lock); +} + static struct service_notif_info *_find_service_info(const char *service_path) { struct service_notif_info *service_notif; @@ -426,11 +439,7 @@ static void root_service_service_exit(struct qmi_client_info *data, * Destroy client handle and try connecting when * service comes up again. */ - mutex_lock(&qmi_client_release_lock); - data->service_connected = false; - qmi_handle_destroy(data->clnt_handle); - data->clnt_handle = NULL; - mutex_unlock(&qmi_client_release_lock); + queue_work(data->svc_event_wq, &data->qmi_handle_free); } static void root_service_exit_work(struct work_struct *work) @@ -486,7 +495,7 @@ static int ssr_event_notify(struct notifier_block *this, info->subsys_state = ROOT_PD_SHUTDOWN; break; } - queue_work(info->svc_event_wq, &info->svc_exit); + root_service_service_exit(info, info->subsys_state); break; default: break; @@ -561,6 +570,7 @@ static void *add_service_notif(const char *service_path, int instance_id, INIT_WORK(&qmi_data->svc_exit, root_service_exit_work); INIT_WORK(&qmi_data->svc_rcv_msg, root_service_clnt_recv_msg); INIT_WORK(&qmi_data->ind_ack, send_ind_ack); + INIT_WORK(&qmi_data->qmi_handle_free, free_qmi_handle); *curr_state = service_notif->curr_state = SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01; diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index 07610877f140..457361ba5ff8 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -2241,7 +2241,7 @@ static ssize_t spcom_device_write(struct file *filp, } /** - * spcom_device_read() - handle channel file write() from user space. + * spcom_device_read() - handle channel file read() from user space. * * @filp: file pointer * @@ -2267,6 +2267,16 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, ch = filp->private_data; + if (ch == NULL) { + pr_err("invalid ch pointer, file [%s].\n", name); + return -EINVAL; + } + + if (!spcom_is_channel_open(ch)) { + pr_err("ch is not open, file [%s].\n", name); + return -EINVAL; + } + buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; @@ -2354,6 +2364,10 @@ static unsigned int spcom_device_poll(struct file *filp, done = (spcom_dev->link_state == GLINK_LINK_STATE_UP); break; case SPCOM_POLL_CH_CONNECT: + if (ch == NULL) { + pr_err("invalid ch pointer, file [%s].\n", name); + return -EINVAL; + } pr_debug("ch [%s] SPCOM_POLL_CH_CONNECT.\n", name); if (wait) { reinit_completion(&ch->connect); diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c index c6531de48f65..991bce363740 100644 --- a/drivers/soc/qcom/subsys-pil-tz.c +++ b/drivers/soc/qcom/subsys-pil-tz.c @@ -1034,6 +1034,7 @@ static int pil_tz_driver_probe(struct platform_device *pdev) d->desc.ops = &pil_ops_trusted; d->desc.proxy_timeout = PROXY_TIMEOUT_MS; + d->desc.clear_fw_region = true; rc = of_property_read_u32(pdev->dev.of_node, "qcom,proxy-timeout-ms", &proxy_timeout); diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c index 63bc3961de3b..e72663bd2138 100644 --- a/drivers/soundwire/swr-wcd-ctrl.c +++ b/drivers/soundwire/swr-wcd-ctrl.c @@ -224,6 +224,12 @@ static struct dentry *debugfs_poke; static struct dentry *debugfs_reg_dump; static unsigned int read_data; + +static bool swrm_is_msm_variant(int val) +{ + return (val == SWRM_VERSION_1_3); +} + static int swrm_debug_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; @@ -514,8 +520,17 @@ static int swrm_cmd_fifo_wr_cmd(struct swr_mstr_ctrl *swrm, u8 cmd_data, __func__, val, ret); goto err; } - if (cmd_id == 0xF) - wait_for_completion_timeout(&swrm->broadcast, (2 * HZ/10)); + if (cmd_id == 0xF) { + /* + * sleep for 10ms for MSM soundwire variant to allow broadcast + * command to complete. + */ + if (swrm_is_msm_variant(swrm->version)) + usleep_range(10000, 10100); + else + wait_for_completion_timeout(&swrm->broadcast, + (2 * HZ/10)); + } err: return ret; } @@ -1472,6 +1487,7 @@ static int swrm_probe(struct platform_device *pdev) mutex_unlock(&swrm->mlock); goto err_mstr_fail; } + swrm->version = swrm->read(swrm->handle, SWRM_COMP_HW_VERSION); /* Enumerate slave devices */ list_for_each_entry_safe(swr_dev, safe, &swrm->master.devices, diff --git a/drivers/soundwire/swr-wcd-ctrl.h b/drivers/soundwire/swr-wcd-ctrl.h index 8992318cdbd3..b7a3edac3e00 100755 --- a/drivers/soundwire/swr-wcd-ctrl.h +++ b/drivers/soundwire/swr-wcd-ctrl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -23,6 +23,10 @@ #define SWR_MSTR_PORT_LEN 8 /* Number of master ports */ +#define SWRM_VERSION_1_0 0x01010000 +#define SWRM_VERSION_1_2 0x01030000 +#define SWRM_VERSION_1_3 0x01040000 + enum { SWR_MSTR_PAUSE, SWR_MSTR_RESUME, @@ -88,6 +92,7 @@ struct swr_mstr_ctrl { int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq, void *data), void *swr_handle, int type); int irq; + int version; int num_enum_slaves; int slave_status; struct swr_mstr_port *mstr_port; diff --git a/drivers/soundwire/swrm_registers.h b/drivers/soundwire/swrm_registers.h index c6923f301f9f..50c3ecfdd47d 100755 --- a/drivers/soundwire/swrm_registers.h +++ b/drivers/soundwire/swrm_registers.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -15,6 +15,7 @@ #define SWRM_BASE_ADDRESS 0x00 +#define SWRM_COMP_HW_VERSION SWRM_BASE_ADDRESS #define SWRM_COMP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000004) #define SWRM_COMP_CFG_RMSK 0x3 #define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_BMSK 0x2 diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index a87cfd4ba17b..61a86d391599 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -127,37 +127,62 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed) tclk_hz = clk_get_rate(orion_spi->clk); if (devdata->typ == ARMADA_SPI) { - unsigned int clk, spr, sppr, sppr2, err; - unsigned int best_spr, best_sppr, best_err; - - best_err = speed; - best_spr = 0; - best_sppr = 0; - - /* Iterate over the valid range looking for best fit */ - for (sppr = 0; sppr < 8; sppr++) { - sppr2 = 0x1 << sppr; - - spr = tclk_hz / sppr2; - spr = DIV_ROUND_UP(spr, speed); - if ((spr == 0) || (spr > 15)) - continue; - - clk = tclk_hz / (spr * sppr2); - err = speed - clk; - - if (err < best_err) { - best_spr = spr; - best_sppr = sppr; - best_err = err; - } - } + /* + * Given the core_clk (tclk_hz) and the target rate (speed) we + * determine the best values for SPR (in [0 .. 15]) and SPPR (in + * [0..7]) such that + * + * core_clk / (SPR * 2 ** SPPR) + * + * is as big as possible but not bigger than speed. + */ - if ((best_sppr == 0) && (best_spr == 0)) - return -EINVAL; + /* best integer divider: */ + unsigned divider = DIV_ROUND_UP(tclk_hz, speed); + unsigned spr, sppr; + + if (divider < 16) { + /* This is the easy case, divider is less than 16 */ + spr = divider; + sppr = 0; + + } else { + unsigned two_pow_sppr; + /* + * Find the highest bit set in divider. This and the + * three next bits define SPR (apart from rounding). + * SPPR is then the number of zero bits that must be + * appended: + */ + sppr = fls(divider) - 4; + + /* + * As SPR only has 4 bits, we have to round divider up + * to the next multiple of 2 ** sppr. + */ + two_pow_sppr = 1 << sppr; + divider = (divider + two_pow_sppr - 1) & -two_pow_sppr; + + /* + * recalculate sppr as rounding up divider might have + * increased it enough to change the position of the + * highest set bit. In this case the bit that now + * doesn't make it into SPR is 0, so there is no need to + * round again. + */ + sppr = fls(divider) - 4; + spr = divider >> sppr; + + /* + * Now do range checking. SPR is constructed to have a + * width of 4 bits, so this is fine for sure. So we + * still need to check for sppr to fit into 3 bits: + */ + if (sppr > 7) + return -EINVAL; + } - prescale = ((best_sppr & 0x6) << 5) | - ((best_sppr & 0x1) << 4) | best_spr; + prescale = ((sppr & 0x6) << 5) | ((sppr & 0x1) << 4) | spr; } else { /* * the supported rates are: 4,6,8...30 diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index 0f28c08fcb3c..77b551da5728 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -909,6 +909,7 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus, if (err) { ssb_warn("WARNING: Using fallback SPROM failed (err %d)\n", err); + goto out_free; } else { ssb_dbg("Using SPROM revision %d provided by platform\n", sprom->revision); diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c index b132cff14f01..15ff4e149d75 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c @@ -30,6 +30,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/smp.h> +#include <linux/sysrq.h> #include <linux/timer.h> #include <linux/tty.h> #include <linux/tty_flip.h> @@ -401,7 +402,7 @@ static void fiq_debugger_work(struct work_struct *work) cmd += 6; while (*cmd == ' ') cmd++; - if (cmd != '\0') + if ((cmd != '\0') && sysrq_on()) kernel_restart(cmd); else kernel_restart(NULL); @@ -431,29 +432,39 @@ static void fiq_debugger_irq_exec(struct fiq_debugger_state *state, char *cmd) static void fiq_debugger_help(struct fiq_debugger_state *state) { fiq_debugger_printf(&state->output, - "FIQ Debugger commands:\n" - " pc PC status\n" - " regs Register dump\n" - " allregs Extended Register dump\n" - " bt Stack trace\n" - " reboot [<c>] Reboot with command <c>\n" - " reset [<c>] Hard reset with command <c>\n" - " irqs Interupt status\n" - " kmsg Kernel log\n" - " version Kernel version\n"); - fiq_debugger_printf(&state->output, - " sleep Allow sleep while in FIQ\n" - " nosleep Disable sleep while in FIQ\n" - " console Switch terminal to console\n" - " cpu Current CPU\n" - " cpu <number> Switch to CPU<number>\n"); + "FIQ Debugger commands:\n"); + if (sysrq_on()) { + fiq_debugger_printf(&state->output, + " pc PC status\n" + " regs Register dump\n" + " allregs Extended Register dump\n" + " bt Stack trace\n"); + fiq_debugger_printf(&state->output, + " reboot [<c>] Reboot with command <c>\n" + " reset [<c>] Hard reset with command <c>\n" + " irqs Interrupt status\n" + " kmsg Kernel log\n" + " version Kernel version\n"); + fiq_debugger_printf(&state->output, + " cpu Current CPU\n" + " cpu <number> Switch to CPU<number>\n" + " sysrq sysrq options\n" + " sysrq <param> Execute sysrq with <param>\n"); + } else { + fiq_debugger_printf(&state->output, + " reboot Reboot\n" + " reset Hard reset\n" + " irqs Interrupt status\n"); + } fiq_debugger_printf(&state->output, - " ps Process list\n" - " sysrq sysrq options\n" - " sysrq <param> Execute sysrq with <param>\n"); + " sleep Allow sleep while in FIQ\n" + " nosleep Disable sleep while in FIQ\n" + " console Switch terminal to console\n" + " ps Process list\n"); #ifdef CONFIG_KGDB - fiq_debugger_printf(&state->output, - " kgdb Enter kernel debugger\n"); + if (fiq_kgdb_enable) { + fiq_debugger_printf(&state->output, + " kgdb Enter kernel debugger\n"); #endif } @@ -485,18 +496,23 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) { fiq_debugger_help(state); } else if (!strcmp(cmd, "pc")) { - fiq_debugger_dump_pc(&state->output, regs); + if (sysrq_on()) + fiq_debugger_dump_pc(&state->output, regs); } else if (!strcmp(cmd, "regs")) { - fiq_debugger_dump_regs(&state->output, regs); + if (sysrq_on()) + fiq_debugger_dump_regs(&state->output, regs); } else if (!strcmp(cmd, "allregs")) { - fiq_debugger_dump_allregs(&state->output, regs); + if (sysrq_on()) + fiq_debugger_dump_allregs(&state->output, regs); } else if (!strcmp(cmd, "bt")) { - fiq_debugger_dump_stacktrace(&state->output, regs, 100, svc_sp); + if (sysrq_on()) + fiq_debugger_dump_stacktrace(&state->output, regs, + 100, svc_sp); } else if (!strncmp(cmd, "reset", 5)) { cmd += 5; while (*cmd == ' ') cmd++; - if (*cmd) { + if (*cmd && sysrq_on()) { char tmp_cmd[32]; strlcpy(tmp_cmd, cmd, sizeof(tmp_cmd)); machine_restart(tmp_cmd); @@ -506,9 +522,12 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, } else if (!strcmp(cmd, "irqs")) { fiq_debugger_dump_irqs(state); } else if (!strcmp(cmd, "kmsg")) { - fiq_debugger_dump_kernel_log(state); + if (sysrq_on()) + fiq_debugger_dump_kernel_log(state); } else if (!strcmp(cmd, "version")) { - fiq_debugger_printf(&state->output, "%s\n", linux_banner); + if (sysrq_on()) + fiq_debugger_printf(&state->output, "%s\n", + linux_banner); } else if (!strcmp(cmd, "sleep")) { state->no_sleep = false; fiq_debugger_printf(&state->output, "enabling sleep\n"); @@ -520,14 +539,17 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, fiq_debugger_uart_flush(state); state->console_enable = true; } else if (!strcmp(cmd, "cpu")) { - fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu); - } else if (!strncmp(cmd, "cpu ", 4)) { + if (sysrq_on()) + fiq_debugger_printf(&state->output, "cpu %d\n", + state->current_cpu); + } else if (!strncmp(cmd, "cpu ", 4) && sysrq_on()) { unsigned long cpu = 0; if (kstrtoul(cmd + 4, 10, &cpu) == 0) fiq_debugger_switch_cpu(state, cpu); else fiq_debugger_printf(&state->output, "invalid cpu\n"); - fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu); + fiq_debugger_printf(&state->output, "cpu %d\n", + state->current_cpu); } else { if (state->debug_busy) { fiq_debugger_printf(&state->output, diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 81bda878a7ec..b7fe42582e89 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -807,7 +807,7 @@ static int ion_debug_client_show(struct seq_file *s, void *unused) struct ion_handle *handle = rb_entry(n, struct ion_handle, node); - seq_printf(s, "%16.16s: %16zx : %16d : %12p", + seq_printf(s, "%16.16s: %16zx : %16d : %12pK", handle->buffer->heap->name, handle->buffer->size, atomic_read(&handle->ref.refcount), diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c index 03b2b8a38991..2ad4cc7a4785 100644 --- a/drivers/staging/android/ion/ion_system_heap.c +++ b/drivers/staging/android/ion/ion_system_heap.c @@ -2,7 +2,7 @@ * drivers/staging/android/ion/ion_system_heap.c * * Copyright (C) 2011 Google, Inc. - * Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -123,9 +123,11 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap, gfp_t gfp_mask = low_order_gfp_flags; if (order) gfp_mask = high_order_gfp_flags; + page = alloc_pages(gfp_mask, order); - ion_pages_sync_for_device(dev, page, PAGE_SIZE << order, - DMA_BIDIRECTIONAL); + if (page) + ion_pages_sync_for_device(dev, page, PAGE_SIZE << order, + DMA_BIDIRECTIONAL); } if (!page) return 0; diff --git a/drivers/staging/comedi/drivers/dt282x.c b/drivers/staging/comedi/drivers/dt282x.c index 5a536a00066f..b63472de761a 100644 --- a/drivers/staging/comedi/drivers/dt282x.c +++ b/drivers/staging/comedi/drivers/dt282x.c @@ -69,48 +69,49 @@ * Register map */ #define DT2821_ADCSR_REG 0x00 -#define DT2821_ADCSR_ADERR (1 << 15) -#define DT2821_ADCSR_ADCLK (1 << 9) -#define DT2821_ADCSR_MUXBUSY (1 << 8) -#define DT2821_ADCSR_ADDONE (1 << 7) -#define DT2821_ADCSR_IADDONE (1 << 6) +#define DT2821_ADCSR_ADERR BIT(15) +#define DT2821_ADCSR_ADCLK BIT(9) +#define DT2821_ADCSR_MUXBUSY BIT(8) +#define DT2821_ADCSR_ADDONE BIT(7) +#define DT2821_ADCSR_IADDONE BIT(6) #define DT2821_ADCSR_GS(x) (((x) & 0x3) << 4) #define DT2821_ADCSR_CHAN(x) (((x) & 0xf) << 0) #define DT2821_CHANCSR_REG 0x02 -#define DT2821_CHANCSR_LLE (1 << 15) -#define DT2821_CHANCSR_PRESLA(x) (((x) & 0xf) >> 8) +#define DT2821_CHANCSR_LLE BIT(15) +#define DT2821_CHANCSR_TO_PRESLA(x) (((x) >> 8) & 0xf) #define DT2821_CHANCSR_NUMB(x) ((((x) - 1) & 0xf) << 0) #define DT2821_ADDAT_REG 0x04 #define DT2821_DACSR_REG 0x06 -#define DT2821_DACSR_DAERR (1 << 15) +#define DT2821_DACSR_DAERR BIT(15) #define DT2821_DACSR_YSEL(x) ((x) << 9) -#define DT2821_DACSR_SSEL (1 << 8) -#define DT2821_DACSR_DACRDY (1 << 7) -#define DT2821_DACSR_IDARDY (1 << 6) -#define DT2821_DACSR_DACLK (1 << 5) -#define DT2821_DACSR_HBOE (1 << 1) -#define DT2821_DACSR_LBOE (1 << 0) +#define DT2821_DACSR_SSEL BIT(8) +#define DT2821_DACSR_DACRDY BIT(7) +#define DT2821_DACSR_IDARDY BIT(6) +#define DT2821_DACSR_DACLK BIT(5) +#define DT2821_DACSR_HBOE BIT(1) +#define DT2821_DACSR_LBOE BIT(0) #define DT2821_DADAT_REG 0x08 #define DT2821_DIODAT_REG 0x0a #define DT2821_SUPCSR_REG 0x0c -#define DT2821_SUPCSR_DMAD (1 << 15) -#define DT2821_SUPCSR_ERRINTEN (1 << 14) -#define DT2821_SUPCSR_CLRDMADNE (1 << 13) -#define DT2821_SUPCSR_DDMA (1 << 12) -#define DT2821_SUPCSR_DS_PIO (0 << 10) -#define DT2821_SUPCSR_DS_AD_CLK (1 << 10) -#define DT2821_SUPCSR_DS_DA_CLK (2 << 10) -#define DT2821_SUPCSR_DS_AD_TRIG (3 << 10) -#define DT2821_SUPCSR_BUFFB (1 << 9) -#define DT2821_SUPCSR_SCDN (1 << 8) -#define DT2821_SUPCSR_DACON (1 << 7) -#define DT2821_SUPCSR_ADCINIT (1 << 6) -#define DT2821_SUPCSR_DACINIT (1 << 5) -#define DT2821_SUPCSR_PRLD (1 << 4) -#define DT2821_SUPCSR_STRIG (1 << 3) -#define DT2821_SUPCSR_XTRIG (1 << 2) -#define DT2821_SUPCSR_XCLK (1 << 1) -#define DT2821_SUPCSR_BDINIT (1 << 0) +#define DT2821_SUPCSR_DMAD BIT(15) +#define DT2821_SUPCSR_ERRINTEN BIT(14) +#define DT2821_SUPCSR_CLRDMADNE BIT(13) +#define DT2821_SUPCSR_DDMA BIT(12) +#define DT2821_SUPCSR_DS(x) (((x) & 0x3) << 10) +#define DT2821_SUPCSR_DS_PIO DT2821_SUPCSR_DS(0) +#define DT2821_SUPCSR_DS_AD_CLK DT2821_SUPCSR_DS(1) +#define DT2821_SUPCSR_DS_DA_CLK DT2821_SUPCSR_DS(2) +#define DT2821_SUPCSR_DS_AD_TRIG DT2821_SUPCSR_DS(3) +#define DT2821_SUPCSR_BUFFB BIT(9) +#define DT2821_SUPCSR_SCDN BIT(8) +#define DT2821_SUPCSR_DACON BIT(7) +#define DT2821_SUPCSR_ADCINIT BIT(6) +#define DT2821_SUPCSR_DACINIT BIT(5) +#define DT2821_SUPCSR_PRLD BIT(4) +#define DT2821_SUPCSR_STRIG BIT(3) +#define DT2821_SUPCSR_XTRIG BIT(2) +#define DT2821_SUPCSR_XCLK BIT(1) +#define DT2821_SUPCSR_BDINIT BIT(0) #define DT2821_TMRCTR_REG 0x0e static const struct comedi_lrange range_dt282x_ai_lo_bipolar = { diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c index 35ab4a9ef95d..c975f6e8be49 100644 --- a/drivers/staging/comedi/drivers/ni_mio_common.c +++ b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -1929,7 +1929,7 @@ static int ni_ai_insn_read(struct comedi_device *dev, unsigned int *data) { struct ni_private *devpriv = dev->private; - unsigned int mask = (s->maxdata + 1) >> 1; + unsigned int mask = s->maxdata; int i, n; unsigned signbits; unsigned int d; @@ -1972,7 +1972,7 @@ static int ni_ai_insn_read(struct comedi_device *dev, return -ETIME; } d += signbits; - data[n] = d; + data[n] = d & 0xffff; } } else if (devpriv->is_6143) { for (n = 0; n < insn->n; n++) { @@ -2017,8 +2017,8 @@ static int ni_ai_insn_read(struct comedi_device *dev, data[n] = dl; } else { d = ni_readw(dev, NI_E_AI_FIFO_DATA_REG); - d += signbits; /* subtle: needs to be short addition */ - data[n] = d; + d += signbits; + data[n] = d & 0xffff; } } } diff --git a/drivers/staging/iio/adc/ad7606_core.c b/drivers/staging/iio/adc/ad7606_core.c index 5796ed2409d0..39bbbaaff07c 100644 --- a/drivers/staging/iio/adc/ad7606_core.c +++ b/drivers/staging/iio/adc/ad7606_core.c @@ -189,7 +189,7 @@ static ssize_t ad7606_store_oversampling_ratio(struct device *dev, mutex_lock(&indio_dev->mlock); gpio_set_value(st->pdata->gpio_os0, (ret >> 0) & 1); gpio_set_value(st->pdata->gpio_os1, (ret >> 1) & 1); - gpio_set_value(st->pdata->gpio_os1, (ret >> 2) & 1); + gpio_set_value(st->pdata->gpio_os2, (ret >> 2) & 1); st->oversampling = lval; mutex_unlock(&indio_dev->mlock); diff --git a/drivers/staging/rtl8188eu/core/rtw_recv.c b/drivers/staging/rtl8188eu/core/rtw_recv.c index 110b8c0b6cd7..0f2fe34e14c2 100644 --- a/drivers/staging/rtl8188eu/core/rtw_recv.c +++ b/drivers/staging/rtl8188eu/core/rtw_recv.c @@ -1405,6 +1405,9 @@ static int wlanhdr_to_ethhdr(struct recv_frame *precvframe) ptr = recvframe_pull(precvframe, (rmv_len-sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0))); } + if (!ptr) + return _FAIL; + memcpy(ptr, pattrib->dst, ETH_ALEN); memcpy(ptr+ETH_ALEN, pattrib->src, ETH_ALEN); diff --git a/drivers/staging/rtl8712/rtl871x_recv.c b/drivers/staging/rtl8712/rtl871x_recv.c index 4ff530155187..04ac23cc47a8 100644 --- a/drivers/staging/rtl8712/rtl871x_recv.c +++ b/drivers/staging/rtl8712/rtl871x_recv.c @@ -641,11 +641,16 @@ sint r8712_wlanhdr_to_ethhdr(union recv_frame *precvframe) /* append rx status for mp test packets */ ptr = recvframe_pull(precvframe, (rmv_len - sizeof(struct ethhdr) + 2) - 24); + if (!ptr) + return _FAIL; memcpy(ptr, get_rxmem(precvframe), 24); ptr += 24; - } else + } else { ptr = recvframe_pull(precvframe, (rmv_len - sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0))); + if (!ptr) + return _FAIL; + } memcpy(ptr, pattrib->dst, ETH_ALEN); memcpy(ptr + ETH_ALEN, pattrib->src, ETH_ALEN); diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index bd810c109277..6ed80b05d674 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -3436,7 +3436,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, if ((tpg->tpg_attrib.generate_node_acls == 0) && (tpg->tpg_attrib.demo_mode_discovery == 0) && - (!core_tpg_get_initiator_node_acl(&tpg->tpg_se_tpg, + (!target_tpg_has_node_acl(&tpg->tpg_se_tpg, cmd->conn->sess->sess_ops->InitiatorName))) { continue; } diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index 23c95cd14167..68261b7dcefe 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -260,7 +260,6 @@ err_out: iscsi_release_param_list(tpg->param_list); tpg->param_list = NULL; } - kfree(tpg); return -ENOMEM; } diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index dcd5ed26eb18..bb6a6c35324a 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -77,12 +77,16 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) &deve->read_bytes); se_lun = rcu_dereference(deve->se_lun); + + if (!percpu_ref_tryget_live(&se_lun->lun_ref)) { + se_lun = NULL; + goto out_unlock; + } + se_cmd->se_lun = rcu_dereference(deve->se_lun); se_cmd->pr_res_key = deve->pr_res_key; se_cmd->orig_fe_lun = unpacked_lun; se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD; - - percpu_ref_get(&se_lun->lun_ref); se_cmd->lun_ref_active = true; if ((se_cmd->data_direction == DMA_TO_DEVICE) && @@ -96,6 +100,7 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) goto ref_dev; } } +out_unlock: rcu_read_unlock(); if (!se_lun) { @@ -362,7 +367,15 @@ int core_enable_device_list_for_node( kfree(new); return -EINVAL; } - BUG_ON(orig->se_lun_acl != NULL); + if (orig->se_lun_acl != NULL) { + pr_warn_ratelimited("Detected existing explicit" + " se_lun_acl->se_lun_group reference for %s" + " mapped_lun: %llu, failing\n", + nacl->initiatorname, mapped_lun); + mutex_unlock(&nacl->lun_entry_mutex); + kfree(new); + return -EINVAL; + } rcu_assign_pointer(new->se_lun, lun); rcu_assign_pointer(new->se_lun_acl, lun_acl); @@ -818,6 +831,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) xcopy_lun = &dev->xcopy_lun; rcu_assign_pointer(xcopy_lun->lun_se_dev, dev); init_completion(&xcopy_lun->lun_ref_comp); + init_completion(&xcopy_lun->lun_shutdown_comp); INIT_LIST_HEAD(&xcopy_lun->lun_deve_list); INIT_LIST_HEAD(&xcopy_lun->lun_dev_link); mutex_init(&xcopy_lun->lun_tg_pt_md_mutex); diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index c220bb8dfa9d..2e27b1034ede 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -442,6 +442,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success, int *post_ret) { struct se_device *dev = cmd->se_dev; + sense_reason_t ret = TCM_NO_SENSE; /* * Only set SCF_COMPARE_AND_WRITE_POST to force a response fall-through @@ -449,9 +450,12 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success, * sent to the backend driver. */ spin_lock_irq(&cmd->t_state_lock); - if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status) { + if (cmd->transport_state & CMD_T_SENT) { cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST; *post_ret = 1; + + if (cmd->scsi_status == SAM_STAT_CHECK_CONDITION) + ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; } spin_unlock_irq(&cmd->t_state_lock); @@ -461,7 +465,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success, */ up(&dev->caw_sem); - return TCM_NO_SENSE; + return ret; } static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success, diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 5fb9dd7f08bb..2794c6ec5c3c 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -75,9 +75,21 @@ struct se_node_acl *core_tpg_get_initiator_node_acl( unsigned char *initiatorname) { struct se_node_acl *acl; - + /* + * Obtain se_node_acl->acl_kref using fabric driver provided + * initiatorname[] during node acl endpoint lookup driven by + * new se_session login. + * + * The reference is held until se_session shutdown -> release + * occurs via fabric driver invoked transport_deregister_session() + * or transport_free_session() code. + */ mutex_lock(&tpg->acl_node_mutex); acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname); + if (acl) { + if (!kref_get_unless_zero(&acl->acl_kref)) + acl = NULL; + } mutex_unlock(&tpg->acl_node_mutex); return acl; @@ -232,6 +244,25 @@ static void target_add_node_acl(struct se_node_acl *acl) acl->initiatorname); } +bool target_tpg_has_node_acl(struct se_portal_group *tpg, + const char *initiatorname) +{ + struct se_node_acl *acl; + bool found = false; + + mutex_lock(&tpg->acl_node_mutex); + list_for_each_entry(acl, &tpg->acl_node_list, acl_list) { + if (!strcmp(acl->initiatorname, initiatorname)) { + found = true; + break; + } + } + mutex_unlock(&tpg->acl_node_mutex); + + return found; +} +EXPORT_SYMBOL(target_tpg_has_node_acl); + struct se_node_acl *core_tpg_check_initiator_node_acl( struct se_portal_group *tpg, unsigned char *initiatorname) @@ -248,6 +279,15 @@ struct se_node_acl *core_tpg_check_initiator_node_acl( acl = target_alloc_node_acl(tpg, initiatorname); if (!acl) return NULL; + /* + * When allocating a dynamically generated node_acl, go ahead + * and take the extra kref now before returning to the fabric + * driver caller. + * + * Note this reference will be released at session shutdown + * time within transport_free_session() code. + */ + kref_get(&acl->acl_kref); acl->dynamic_node_acl = 1; /* @@ -499,7 +539,7 @@ static void core_tpg_lun_ref_release(struct percpu_ref *ref) { struct se_lun *lun = container_of(ref, struct se_lun, lun_ref); - complete(&lun->lun_ref_comp); + complete(&lun->lun_shutdown_comp); } int core_tpg_register( @@ -626,6 +666,7 @@ struct se_lun *core_tpg_alloc_lun( lun->lun_link_magic = SE_LUN_LINK_MAGIC; atomic_set(&lun->lun_acl_count, 0); init_completion(&lun->lun_ref_comp); + init_completion(&lun->lun_shutdown_comp); INIT_LIST_HEAD(&lun->lun_deve_list); INIT_LIST_HEAD(&lun->lun_dev_link); atomic_set(&lun->lun_tg_pt_secondary_offline, 0); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 2a67af4e2e13..df2059984e14 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -341,7 +341,6 @@ void __transport_register_session( &buf[0], PR_REG_ISID_LEN); se_sess->sess_bin_isid = get_unaligned_be64(&buf[0]); } - kref_get(&se_nacl->acl_kref); spin_lock_irq(&se_nacl->nacl_sess_lock); /* @@ -424,14 +423,27 @@ static void target_complete_nacl(struct kref *kref) { struct se_node_acl *nacl = container_of(kref, struct se_node_acl, acl_kref); + struct se_portal_group *se_tpg = nacl->se_tpg; - complete(&nacl->acl_free_comp); + if (!nacl->dynamic_stop) { + complete(&nacl->acl_free_comp); + return; + } + + mutex_lock(&se_tpg->acl_node_mutex); + list_del(&nacl->acl_list); + mutex_unlock(&se_tpg->acl_node_mutex); + + core_tpg_wait_for_nacl_pr_ref(nacl); + core_free_device_list_for_node(nacl, se_tpg); + kfree(nacl); } void target_put_nacl(struct se_node_acl *nacl) { kref_put(&nacl->acl_kref, target_complete_nacl); } +EXPORT_SYMBOL(target_put_nacl); void transport_deregister_session_configfs(struct se_session *se_sess) { @@ -464,6 +476,42 @@ EXPORT_SYMBOL(transport_deregister_session_configfs); void transport_free_session(struct se_session *se_sess) { + struct se_node_acl *se_nacl = se_sess->se_node_acl; + + /* + * Drop the se_node_acl->nacl_kref obtained from within + * core_tpg_get_initiator_node_acl(). + */ + if (se_nacl) { + struct se_portal_group *se_tpg = se_nacl->se_tpg; + const struct target_core_fabric_ops *se_tfo = se_tpg->se_tpg_tfo; + unsigned long flags; + + se_sess->se_node_acl = NULL; + + /* + * Also determine if we need to drop the extra ->cmd_kref if + * it had been previously dynamically generated, and + * the endpoint is not caching dynamic ACLs. + */ + mutex_lock(&se_tpg->acl_node_mutex); + if (se_nacl->dynamic_node_acl && + !se_tfo->tpg_check_demo_mode_cache(se_tpg)) { + spin_lock_irqsave(&se_nacl->nacl_sess_lock, flags); + if (list_empty(&se_nacl->acl_sess_list)) + se_nacl->dynamic_stop = true; + spin_unlock_irqrestore(&se_nacl->nacl_sess_lock, flags); + + if (se_nacl->dynamic_stop) + list_del(&se_nacl->acl_list); + } + mutex_unlock(&se_tpg->acl_node_mutex); + + if (se_nacl->dynamic_stop) + target_put_nacl(se_nacl); + + target_put_nacl(se_nacl); + } if (se_sess->sess_cmd_map) { percpu_ida_destroy(&se_sess->sess_tag_pool); kvfree(se_sess->sess_cmd_map); @@ -475,16 +523,12 @@ EXPORT_SYMBOL(transport_free_session); void transport_deregister_session(struct se_session *se_sess) { struct se_portal_group *se_tpg = se_sess->se_tpg; - const struct target_core_fabric_ops *se_tfo; - struct se_node_acl *se_nacl; unsigned long flags; - bool comp_nacl = true, drop_nacl = false; if (!se_tpg) { transport_free_session(se_sess); return; } - se_tfo = se_tpg->se_tpg_tfo; spin_lock_irqsave(&se_tpg->session_lock, flags); list_del(&se_sess->sess_list); @@ -492,37 +536,16 @@ void transport_deregister_session(struct se_session *se_sess) se_sess->fabric_sess_ptr = NULL; spin_unlock_irqrestore(&se_tpg->session_lock, flags); - /* - * Determine if we need to do extra work for this initiator node's - * struct se_node_acl if it had been previously dynamically generated. - */ - se_nacl = se_sess->se_node_acl; - - mutex_lock(&se_tpg->acl_node_mutex); - if (se_nacl && se_nacl->dynamic_node_acl) { - if (!se_tfo->tpg_check_demo_mode_cache(se_tpg)) { - list_del(&se_nacl->acl_list); - se_tpg->num_node_acls--; - drop_nacl = true; - } - } - mutex_unlock(&se_tpg->acl_node_mutex); - - if (drop_nacl) { - core_tpg_wait_for_nacl_pr_ref(se_nacl); - core_free_device_list_for_node(se_nacl, se_tpg); - kfree(se_nacl); - comp_nacl = false; - } pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n", se_tpg->se_tpg_tfo->get_fabric_name()); /* * If last kref is dropping now for an explicit NodeACL, awake sleeping * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group - * removal context. + * removal context from within transport_free_session() code. + * + * For dynamic ACL, target_put_nacl() uses target_complete_nacl() + * to release all remaining generate_node_acl=1 created ACL resources. */ - if (se_nacl && comp_nacl) - target_put_nacl(se_nacl); transport_free_session(se_sess); } @@ -2657,10 +2680,39 @@ void target_wait_for_sess_cmds(struct se_session *se_sess) } EXPORT_SYMBOL(target_wait_for_sess_cmds); +static void target_lun_confirm(struct percpu_ref *ref) +{ + struct se_lun *lun = container_of(ref, struct se_lun, lun_ref); + + complete(&lun->lun_ref_comp); +} + void transport_clear_lun_ref(struct se_lun *lun) { - percpu_ref_kill(&lun->lun_ref); + /* + * Mark the percpu-ref as DEAD, switch to atomic_t mode, drop + * the initial reference and schedule confirm kill to be + * executed after one full RCU grace period has completed. + */ + percpu_ref_kill_and_confirm(&lun->lun_ref, target_lun_confirm); + /* + * The first completion waits for percpu_ref_switch_to_atomic_rcu() + * to call target_lun_confirm after lun->lun_ref has been marked + * as __PERCPU_REF_DEAD on all CPUs, and switches to atomic_t + * mode so that percpu_ref_tryget_live() lookup of lun->lun_ref + * fails for all new incoming I/O. + */ wait_for_completion(&lun->lun_ref_comp); + /* + * The second completion waits for percpu_ref_put_many() to + * invoke ->release() after lun->lun_ref has switched to + * atomic_t mode, and lun->lun_ref.count has reached zero. + * + * At this point all target-core lun->lun_ref references have + * been dropped via transport_lun_remove_cmd(), and it's safe + * to proceed with the remaining LUN shutdown. + */ + wait_for_completion(&lun->lun_shutdown_comp); } static bool @@ -3058,7 +3110,6 @@ static void target_tmr_work(struct work_struct *work) spin_unlock_irqrestore(&cmd->t_state_lock, flags); goto check_stop; } - cmd->t_state = TRANSPORT_ISTATE_PROCESSING; spin_unlock_irqrestore(&cmd->t_state_lock, flags); cmd->se_tfo->queue_tm_rsp(cmd); @@ -3071,11 +3122,25 @@ int transport_generic_handle_tmr( struct se_cmd *cmd) { unsigned long flags; + bool aborted = false; spin_lock_irqsave(&cmd->t_state_lock, flags); - cmd->transport_state |= CMD_T_ACTIVE; + if (cmd->transport_state & CMD_T_ABORTED) { + aborted = true; + } else { + cmd->t_state = TRANSPORT_ISTATE_PROCESSING; + cmd->transport_state |= CMD_T_ACTIVE; + } spin_unlock_irqrestore(&cmd->t_state_lock, flags); + if (aborted) { + pr_warn_ratelimited("handle_tmr caught CMD_T_ABORTED TMR %d" + "ref_tag: %llu tag: %llu\n", cmd->se_tmr_req->function, + cmd->se_tmr_req->ref_task_tag, cmd->tag); + transport_cmd_check_stop_to_fabric(cmd); + return 0; + } + INIT_WORK(&cmd->work, target_tmr_work); queue_work(cmd->se_dev->tmr_wq, &cmd->work); return 0; diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 5e6d6cb348fc..a7d30e894cab 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -645,8 +645,6 @@ static int tcmu_check_expired_cmd(int id, void *p, void *data) target_complete_cmd(cmd->se_cmd, SAM_STAT_CHECK_CONDITION); cmd->se_cmd = NULL; - kmem_cache_free(tcmu_cmd_cache, cmd); - return 0; } diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c index 153a6f255b6d..6415e9b09a52 100644 --- a/drivers/target/target_core_xcopy.c +++ b/drivers/target/target_core_xcopy.c @@ -836,7 +836,7 @@ out: " CHECK_CONDITION -> sending response\n", rc); ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION; } - target_complete_cmd(ec_cmd, SAM_STAT_CHECK_CONDITION); + target_complete_cmd(ec_cmd, ec_cmd->scsi_status); } sense_reason_t target_do_xcopy(struct se_cmd *se_cmd) diff --git a/drivers/thermal/msm_lmh_dcvs.c b/drivers/thermal/msm_lmh_dcvs.c index cff5b6e3fc63..efe80c24d270 100644 --- a/drivers/thermal/msm_lmh_dcvs.c +++ b/drivers/thermal/msm_lmh_dcvs.c @@ -114,9 +114,9 @@ static void msm_lmh_dcvs_get_max_freq(uint32_t cpu, uint32_t *max_freq) static uint32_t msm_lmh_mitigation_notify(struct msm_lmh_dcvs_hw *hw) { - uint32_t max_limit = 0, val = 0; + uint32_t val = 0; struct device *cpu_dev = NULL; - unsigned long freq_val; + unsigned long freq_val, max_limit = 0; struct dev_pm_opp *opp_entry; val = readl_relaxed(hw->osm_hw_reg); diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index 06fd2ed9ef9d..705b0cafedbb 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -98,7 +98,7 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) int temperature; int ret; - ret = tz->ops->get_trip_temp(tz, 0, &temperature); + ret = tz->ops->get_crit_temp(tz, &temperature); if (ret) return ret; diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 644ddb841d9f..6d1e2f746ab4 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -114,7 +114,7 @@ #define DEFAULT_TX_BUF_COUNT 3 struct n_hdlc_buf { - struct n_hdlc_buf *link; + struct list_head list_item; int count; char buf[1]; }; @@ -122,8 +122,7 @@ struct n_hdlc_buf { #define N_HDLC_BUF_SIZE (sizeof(struct n_hdlc_buf) + maxframe) struct n_hdlc_buf_list { - struct n_hdlc_buf *head; - struct n_hdlc_buf *tail; + struct list_head list; int count; spinlock_t spinlock; }; @@ -136,7 +135,6 @@ struct n_hdlc_buf_list { * @backup_tty - TTY to use if tty gets closed * @tbusy - reentrancy flag for tx wakeup code * @woke_up - FIXME: describe this field - * @tbuf - currently transmitting tx buffer * @tx_buf_list - list of pending transmit frame buffers * @rx_buf_list - list of received frame buffers * @tx_free_buf_list - list unused transmit frame buffers @@ -149,7 +147,6 @@ struct n_hdlc { struct tty_struct *backup_tty; int tbusy; int woke_up; - struct n_hdlc_buf *tbuf; struct n_hdlc_buf_list tx_buf_list; struct n_hdlc_buf_list rx_buf_list; struct n_hdlc_buf_list tx_free_buf_list; @@ -159,7 +156,8 @@ struct n_hdlc { /* * HDLC buffer list manipulation functions */ -static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list); +static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list, + struct n_hdlc_buf *buf); static void n_hdlc_buf_put(struct n_hdlc_buf_list *list, struct n_hdlc_buf *buf); static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list); @@ -209,16 +207,9 @@ static void flush_tx_queue(struct tty_struct *tty) { struct n_hdlc *n_hdlc = tty2n_hdlc(tty); struct n_hdlc_buf *buf; - unsigned long flags; while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list))) n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf); - spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); - if (n_hdlc->tbuf) { - n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, n_hdlc->tbuf); - n_hdlc->tbuf = NULL; - } - spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); } static struct tty_ldisc_ops n_hdlc_ldisc = { @@ -284,7 +275,6 @@ static void n_hdlc_release(struct n_hdlc *n_hdlc) } else break; } - kfree(n_hdlc->tbuf); kfree(n_hdlc); } /* end of n_hdlc_release() */ @@ -403,13 +393,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) n_hdlc->woke_up = 0; spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); - /* get current transmit buffer or get new transmit */ - /* buffer from list of pending transmit buffers */ - - tbuf = n_hdlc->tbuf; - if (!tbuf) - tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); - + tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); while (tbuf) { if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d)sending frame %p, count=%d\n", @@ -421,7 +405,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) /* rollback was possible and has been done */ if (actual == -ERESTARTSYS) { - n_hdlc->tbuf = tbuf; + n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf); break; } /* if transmit error, throw frame away by */ @@ -436,10 +420,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) /* free current transmit buffer */ n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf); - - /* this tx buffer is done */ - n_hdlc->tbuf = NULL; - + /* wait up sleeping writers */ wake_up_interruptible(&tty->write_wait); @@ -449,10 +430,12 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d)frame %p pending\n", __FILE__,__LINE__,tbuf); - - /* buffer not accepted by driver */ - /* set this buffer as pending buffer */ - n_hdlc->tbuf = tbuf; + + /* + * the buffer was not accepted by driver, + * return it back into tx queue + */ + n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf); break; } } @@ -750,7 +733,8 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, int error = 0; int count; unsigned long flags; - + struct n_hdlc_buf *buf = NULL; + if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d)n_hdlc_tty_ioctl() called %d\n", __FILE__,__LINE__,cmd); @@ -764,8 +748,10 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, /* report count of read data available */ /* in next available frame (if any) */ spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags); - if (n_hdlc->rx_buf_list.head) - count = n_hdlc->rx_buf_list.head->count; + buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list, + struct n_hdlc_buf, list_item); + if (buf) + count = buf->count; else count = 0; spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags); @@ -777,8 +763,10 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, count = tty_chars_in_buffer(tty); /* add size of next output frame in queue */ spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags); - if (n_hdlc->tx_buf_list.head) - count += n_hdlc->tx_buf_list.head->count; + buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list, + struct n_hdlc_buf, list_item); + if (buf) + count += buf->count; spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags); error = put_user(count, (int __user *)arg); break; @@ -826,14 +814,14 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, poll_wait(filp, &tty->write_wait, wait); /* set bits for operations that won't block */ - if (n_hdlc->rx_buf_list.head) + if (!list_empty(&n_hdlc->rx_buf_list.list)) mask |= POLLIN | POLLRDNORM; /* readable */ if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) mask |= POLLHUP; if (tty_hung_up_p(filp)) mask |= POLLHUP; if (!tty_is_writelocked(tty) && - n_hdlc->tx_free_buf_list.head) + !list_empty(&n_hdlc->tx_free_buf_list.list)) mask |= POLLOUT | POLLWRNORM; /* writable */ } return mask; @@ -853,11 +841,16 @@ static struct n_hdlc *n_hdlc_alloc(void) if (!n_hdlc) return NULL; - n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list); - n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list); - n_hdlc_buf_list_init(&n_hdlc->rx_buf_list); - n_hdlc_buf_list_init(&n_hdlc->tx_buf_list); - + spin_lock_init(&n_hdlc->rx_free_buf_list.spinlock); + spin_lock_init(&n_hdlc->tx_free_buf_list.spinlock); + spin_lock_init(&n_hdlc->rx_buf_list.spinlock); + spin_lock_init(&n_hdlc->tx_buf_list.spinlock); + + INIT_LIST_HEAD(&n_hdlc->rx_free_buf_list.list); + INIT_LIST_HEAD(&n_hdlc->tx_free_buf_list.list); + INIT_LIST_HEAD(&n_hdlc->rx_buf_list.list); + INIT_LIST_HEAD(&n_hdlc->tx_buf_list.list); + /* allocate free rx buffer list */ for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) { buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL); @@ -885,63 +878,65 @@ static struct n_hdlc *n_hdlc_alloc(void) } /* end of n_hdlc_alloc() */ /** - * n_hdlc_buf_list_init - initialize specified HDLC buffer list - * @list - pointer to buffer list + * n_hdlc_buf_return - put the HDLC buffer after the head of the specified list + * @buf_list - pointer to the buffer list + * @buf - pointer to the buffer */ -static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list) +static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list, + struct n_hdlc_buf *buf) { - memset(list, 0, sizeof(*list)); - spin_lock_init(&list->spinlock); -} /* end of n_hdlc_buf_list_init() */ + unsigned long flags; + + spin_lock_irqsave(&buf_list->spinlock, flags); + + list_add(&buf->list_item, &buf_list->list); + buf_list->count++; + + spin_unlock_irqrestore(&buf_list->spinlock, flags); +} /** * n_hdlc_buf_put - add specified HDLC buffer to tail of specified list - * @list - pointer to buffer list + * @buf_list - pointer to buffer list * @buf - pointer to buffer */ -static void n_hdlc_buf_put(struct n_hdlc_buf_list *list, +static void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list, struct n_hdlc_buf *buf) { unsigned long flags; - spin_lock_irqsave(&list->spinlock,flags); - - buf->link=NULL; - if (list->tail) - list->tail->link = buf; - else - list->head = buf; - list->tail = buf; - (list->count)++; - - spin_unlock_irqrestore(&list->spinlock,flags); - + + spin_lock_irqsave(&buf_list->spinlock, flags); + + list_add_tail(&buf->list_item, &buf_list->list); + buf_list->count++; + + spin_unlock_irqrestore(&buf_list->spinlock, flags); } /* end of n_hdlc_buf_put() */ /** * n_hdlc_buf_get - remove and return an HDLC buffer from list - * @list - pointer to HDLC buffer list + * @buf_list - pointer to HDLC buffer list * * Remove and return an HDLC buffer from the head of the specified HDLC buffer * list. * Returns a pointer to HDLC buffer if available, otherwise %NULL. */ -static struct n_hdlc_buf* n_hdlc_buf_get(struct n_hdlc_buf_list *list) +static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list) { unsigned long flags; struct n_hdlc_buf *buf; - spin_lock_irqsave(&list->spinlock,flags); - - buf = list->head; + + spin_lock_irqsave(&buf_list->spinlock, flags); + + buf = list_first_entry_or_null(&buf_list->list, + struct n_hdlc_buf, list_item); if (buf) { - list->head = buf->link; - (list->count)--; + list_del(&buf->list_item); + buf_list->count--; } - if (!list->head) - list->tail = NULL; - - spin_unlock_irqrestore(&list->spinlock,flags); + + spin_unlock_irqrestore(&buf_list->spinlock, flags); return buf; - } /* end of n_hdlc_buf_get() */ static char hdlc_banner[] __initdata = diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 029de3f99752..5b24ffd93649 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -2880,6 +2880,8 @@ enum pci_board_num_t { pbn_b0_4_1152000_200, pbn_b0_8_1152000_200, + pbn_b0_4_1250000, + pbn_b0_2_1843200, pbn_b0_4_1843200, @@ -3113,6 +3115,13 @@ static struct pciserial_board pci_boards[] = { .uart_offset = 0x200, }, + [pbn_b0_4_1250000] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1250000, + .uart_offset = 8, + }, + [pbn_b0_2_1843200] = { .flags = FL_BASE0, .num_ports = 2, @@ -5778,6 +5787,10 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 }, { PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 }, + /* MKS Tenta SCOM-080x serial cards */ + { PCI_DEVICE(0x1601, 0x0800), .driver_data = pbn_b0_4_1250000 }, + { PCI_DEVICE(0x1601, 0xa801), .driver_data = pbn_b0_4_1250000 }, + /* * These entries match devices with class COMMUNICATION_SERIAL, * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index e0b89b961e1b..a0f911641b04 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -470,6 +470,14 @@ static void atmel_stop_tx(struct uart_port *port) /* disable PDC transmit */ atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS); } + + /* + * Disable the transmitter. + * This is mandatory when DMA is used, otherwise the DMA buffer + * is fully transmitted. + */ + atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS); + /* Disable interrupts */ atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask); @@ -502,6 +510,9 @@ static void atmel_start_tx(struct uart_port *port) /* Enable interrupts */ atmel_uart_writel(port, ATMEL_US_IER, atmel_port->tx_done_mask); + + /* re-enable the transmitter */ + atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN); } /* diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 8f68acd1d95d..db329230c7ca 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1834,6 +1834,7 @@ static const struct of_device_id msm_match_table[] = { { .compatible = "qcom,msm-uartdm" }, {} }; +MODULE_DEVICE_TABLE(of, msm_match_table); #ifdef CONFIG_PM_SLEEP static int msm_serial_suspend(struct device *dev) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index f0d5c96ac2e0..cc616d678d42 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -3,7 +3,7 @@ * MSM 7k High speed uart driver * * Copyright (c) 2008 Google Inc. - * Copyright (c) 2007-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2007-2017, The Linux Foundation. All rights reserved. * Modified: Nick Pelly <npelly@google.com> * * All source code in this file is licensed under the following license @@ -1436,13 +1436,13 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) hex_dump_ipc(msm_uport, tx->ipc_tx_ctxt, "Tx", &tx_buf->buf[tx_buf->tail], (u64)src_addr, tx_count); sps_pipe_handle = tx->cons.pipe_handle; - /* Queue transfer request to SPS */ - ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count, - msm_uport, flags); /* Set 1 second timeout */ mod_timer(&tx->tx_timeout_timer, jiffies + msecs_to_jiffies(MSEC_PER_SEC)); + /* Queue transfer request to SPS */ + ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count, + msm_uport, flags); MSM_HS_DBG("%s:Enqueue Tx Cmd, ret %d\n", __func__, ret); } diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index ab8308ff7e69..cad76b1cf672 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1030,8 +1030,10 @@ static int s3c64xx_serial_startup(struct uart_port *port) if (ourport->dma) { ret = s3c24xx_serial_request_dma(ourport); if (ret < 0) { - dev_warn(port->dev, "DMA request failed\n"); - return ret; + dev_warn(port->dev, + "DMA request failed, DMA will not be used\n"); + devm_kfree(port->dev, ourport->dma); + ourport->dma = NULL; } } diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index edb5305b9d4d..7d5ee8a13ac6 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1230,7 +1230,7 @@ static int sc16is7xx_probe(struct device *dev, /* Setup interrupt */ ret = devm_request_irq(dev, irq, sc16is7xx_irq, - IRQF_ONESHOT | flags, dev_name(dev), s); + flags, dev_name(dev), s); if (!ret) return 0; diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index e5139402e7f8..1ca9cea2eaf8 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -55,10 +55,11 @@ static int __read_mostly sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE; static bool __read_mostly sysrq_always_enabled; -static bool sysrq_on(void) +bool sysrq_on(void) { return sysrq_enabled || sysrq_always_enabled; } +EXPORT_SYMBOL(sysrq_on); /* * A value of 1 means 'all', other nonzero values are an op mask: @@ -945,8 +946,8 @@ static const struct input_device_id sysrq_ids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, - .evbit = { BIT_MASK(EV_KEY) }, - .keybit = { BIT_MASK(KEY_LEFTALT) }, + .evbit = { [BIT_WORD(EV_KEY)] = BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(KEY_LEFTALT)] = BIT_MASK(KEY_LEFTALT) }, }, { }, }; diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 41987a55a538..988c564b61a8 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -982,7 +982,7 @@ static void kbd_led_trigger_activate(struct led_classdev *cdev) KBD_LED_TRIGGER((_led_bit) + 8, _name) static struct kbd_led_trigger kbd_led_triggers[] = { - KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrollock"), + KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrolllock"), KBD_LED_TRIGGER(VC_NUMLOCK, "kbd-numlock"), KBD_LED_TRIGGER(VC_CAPSLOCK, "kbd-capslock"), KBD_LED_TRIGGER(VC_KANALOCK, "kbd-kanalock"), diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 5a048b7b92e8..2949289bb3c5 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -244,7 +244,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) struct ci_hdrc_platform_data pdata = { .name = dev_name(&pdev->dev), .capoffset = DEF_CAPOFFSET, - .flags = CI_HDRC_SET_NON_ZERO_TTHA, }; int ret; const struct of_device_id *of_id; diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 4d77745f439f..96849e2e7435 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1708,6 +1708,7 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */ .driver_info = QUIRK_CONTROL_LINE_STATE, }, { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */ + { USB_DEVICE(0x2184, 0x0036) }, /* GW Instek AFG-125 */ { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */ }, /* Motorola H24 HSPA module: */ diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index ff44cfa26af8..ac30a051ad71 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -205,6 +205,16 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, if (ifp->desc.bNumEndpoints >= num_ep) goto skip_to_next_endpoint_or_interface_descriptor; + /* Check for duplicate endpoint addresses */ + for (i = 0; i < ifp->desc.bNumEndpoints; ++i) { + if (ifp->endpoint[i].desc.bEndpointAddress == + d->bEndpointAddress) { + dev_warn(ddev, "config %d interface %d altsetting %d has a duplicate endpoint with address 0x%X, skipping\n", + cfgno, inum, asnum, d->bEndpointAddress); + goto skip_to_next_endpoint_or_interface_descriptor; + } + } + endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints]; ++ifp->desc.bNumEndpoints; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 29242ffe8dca..f84ef04284f5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -106,6 +106,7 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem); static void hub_release(struct kref *kref); static int usb_reset_and_verify_device(struct usb_device *udev); +static int hub_port_disable(struct usb_hub *hub, int port1, int set_state); static inline char *portspeed(struct usb_hub *hub, int portstatus) { @@ -894,88 +895,6 @@ static int hub_set_port_link_state(struct usb_hub *hub, int port1, } /* - * If USB 3.0 ports are placed into the Disabled state, they will no longer - * detect any device connects or disconnects. This is generally not what the - * USB core wants, since it expects a disabled port to produce a port status - * change event when a new device connects. - * - * Instead, set the link state to Disabled, wait for the link to settle into - * that state, clear any change bits, and then put the port into the RxDetect - * state. - */ -static int hub_usb3_port_disable(struct usb_hub *hub, int port1) -{ - int ret; - int total_time; - u16 portchange, portstatus; - - if (!hub_is_superspeed(hub->hdev)) - return -EINVAL; - - ret = hub_port_status(hub, port1, &portstatus, &portchange); - if (ret < 0) - return ret; - - /* - * USB controller Advanced Micro Devices, Inc. [AMD] FCH USB XHCI - * Controller [1022:7814] will have spurious result making the following - * usb 3.0 device hotplugging route to the 2.0 root hub and recognized - * as high-speed device if we set the usb 3.0 port link state to - * Disabled. Since it's already in USB_SS_PORT_LS_RX_DETECT state, we - * check the state here to avoid the bug. - */ - if ((portstatus & USB_PORT_STAT_LINK_STATE) == - USB_SS_PORT_LS_RX_DETECT) { - dev_dbg(&hub->ports[port1 - 1]->dev, - "Not disabling port; link state is RxDetect\n"); - return ret; - } - - ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED); - if (ret) - return ret; - - /* Wait for the link to enter the disabled state. */ - for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) { - ret = hub_port_status(hub, port1, &portstatus, &portchange); - if (ret < 0) - return ret; - - if ((portstatus & USB_PORT_STAT_LINK_STATE) == - USB_SS_PORT_LS_SS_DISABLED) - break; - if (total_time >= HUB_DEBOUNCE_TIMEOUT) - break; - msleep(HUB_DEBOUNCE_STEP); - } - if (total_time >= HUB_DEBOUNCE_TIMEOUT) - dev_warn(&hub->ports[port1 - 1]->dev, - "Could not disable after %d ms\n", total_time); - - return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT); -} - -static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) -{ - struct usb_port *port_dev = hub->ports[port1 - 1]; - struct usb_device *hdev = hub->hdev; - int ret = 0; - - if (port_dev->child && set_state) - usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED); - if (!hub->error) { - if (hub_is_superspeed(hub->hdev)) - ret = hub_usb3_port_disable(hub, port1); - else - ret = usb_clear_port_feature(hdev, port1, - USB_PORT_FEAT_ENABLE); - } - if (ret && ret != -ENODEV) - dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret); - return ret; -} - -/* * Disable a port and mark a logical connect-change event, so that some * time later hub_wq will disconnect() any existing usb_device on the port * and will re-enumerate if there actually is a device attached. @@ -4086,6 +4005,26 @@ void usb_unlocked_enable_lpm(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); +/* usb3 devices use U3 for disabled, make sure remote wakeup is disabled */ +static void hub_usb3_port_prepare_disable(struct usb_hub *hub, + struct usb_port *port_dev) +{ + struct usb_device *udev = port_dev->child; + int ret; + + if (udev && udev->port_is_suspended && udev->do_remote_wakeup) { + ret = hub_set_port_link_state(hub, port_dev->portnum, + USB_SS_PORT_LS_U0); + if (!ret) { + msleep(USB_RESUME_TIMEOUT); + ret = usb_disable_remote_wakeup(udev); + } + if (ret) + dev_warn(&udev->dev, + "Port disable: can't disable remote wake\n"); + udev->do_remote_wakeup = 0; + } +} #else /* CONFIG_PM */ @@ -4093,6 +4032,9 @@ EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); #define hub_resume NULL #define hub_reset_resume NULL +static inline void hub_usb3_port_prepare_disable(struct usb_hub *hub, + struct usb_port *port_dev) { } + int usb_disable_lpm(struct usb_device *udev) { return 0; @@ -4128,6 +4070,34 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, #endif /* CONFIG_PM */ +/* + * USB-3 does not have a similar link state as USB-2 that will avoid negotiating + * a connection with a plugged-in cable but will signal the host when the cable + * is unplugged. Disable remote wake and set link state to U3 for USB-3 devices + */ +static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) +{ + struct usb_port *port_dev = hub->ports[port1 - 1]; + struct usb_device *hdev = hub->hdev; + int ret = 0; + + if (!hub->error) { + if (hub_is_superspeed(hub->hdev)) { + hub_usb3_port_prepare_disable(hub, port_dev); + ret = hub_set_port_link_state(hub, port_dev->portnum, + USB_SS_PORT_LS_U3); + } else { + ret = usb_clear_port_feature(hdev, port1, + USB_PORT_FEAT_ENABLE); + } + } + if (port_dev->child && set_state) + usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED); + if (ret && ret != -ENODEV) + dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret); + return ret; +} + /* USB 2.0 spec, 7.1.7.3 / fig 7-29: * diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index d2e50a27140c..24f9f98968a5 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -37,6 +37,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* CBM - Flash disk */ { USB_DEVICE(0x0204, 0x6025), .driver_info = USB_QUIRK_RESET_RESUME }, + /* WORLDE easy key (easykey.25) MIDI controller */ + { USB_DEVICE(0x0218, 0x0401), .driver_info = + USB_QUIRK_CONFIG_INTF_STRINGS }, + /* HP 5300/5370C scanner */ { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 }, diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 9de5e06430e1..f6e2bea7b9aa 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -44,7 +44,7 @@ #define DWC3_XHCI_RESOURCES_NUM 2 #define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */ -#define DWC3_EVENT_BUFFERS_SIZE (2 * PAGE_SIZE) +#define DWC3_EVENT_BUFFERS_SIZE 4096 #define DWC3_EVENT_TYPE_MASK 0xfe #define DWC3_EVENT_TYPE_DEV 0 diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 3d731d1b5c60..d2c0c1a8d979 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -37,6 +37,7 @@ #define PCI_DEVICE_ID_INTEL_BXT 0x0aaa #define PCI_DEVICE_ID_INTEL_APL 0x5aaa #define PCI_DEVICE_ID_INTEL_KBP 0xa2b0 +#define PCI_DEVICE_ID_INTEL_GLK 0x31aa static const struct acpi_gpio_params reset_gpios = { 0, 0, false }; static const struct acpi_gpio_params cs_gpios = { 1, 0, false }; @@ -216,6 +217,7 @@ static const struct pci_device_id dwc3_pci_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BXT), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_APL), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KBP), }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GLK), }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), }, { } /* Terminating Entry */ }; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 9cd87513619c..c2a6fdbfcfee 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -62,20 +62,13 @@ static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) } } -static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, - u32 len, u32 type, bool chain) +static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum, + dma_addr_t buf_dma, u32 len, u32 type, bool chain) { - struct dwc3_gadget_ep_cmd_params params; struct dwc3_trb *trb; struct dwc3_ep *dep; - int ret; - dep = dwc->eps[epnum]; - if (dep->flags & DWC3_EP_BUSY) { - dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name); - return 0; - } trb = &dwc->ep0_trb[dep->free_slot]; @@ -96,15 +89,25 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, trb->ctrl |= (DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_LST); - if (chain) + trace_dwc3_prepare_trb(dep, trb); +} + +static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_ep *dep; + int ret; + + dep = dwc->eps[epnum]; + if (dep->flags & DWC3_EP_BUSY) { + dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name); return 0; + } memset(¶ms, 0, sizeof(params)); params.param0 = upper_32_bits(dwc->ep0_trb_addr); params.param1 = lower_32_bits(dwc->ep0_trb_addr); - trace_dwc3_prepare_trb(dep, trb); - ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_STARTTRANSFER, ¶ms); if (ret < 0) { @@ -333,9 +336,9 @@ void dwc3_ep0_out_start(struct dwc3 *dwc) { int ret; - ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8, + dwc3_ep0_prepare_one_trb(dwc, 0, dwc->ctrl_req_addr, 8, DWC3_TRBCTL_CONTROL_SETUP, false); - + ret = dwc3_ep0_start_trans(dwc, 0); if (WARN_ON_ONCE(ret < 0)) dbg_event(dwc->eps[0]->number, "EOUTSTART", ret); } @@ -923,9 +926,9 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, dwc->ep0_next_event = DWC3_EP0_COMPLETE; - ret = dwc3_ep0_start_trans(dwc, epnum, - dwc->ctrl_req_addr, 0, - DWC3_TRBCTL_CONTROL_DATA, false); + dwc3_ep0_prepare_one_trb(dwc, epnum, dwc->ctrl_req_addr, + 0, DWC3_TRBCTL_CONTROL_DATA, false); + ret = dwc3_ep0_start_trans(dwc, epnum); if (WARN_ON_ONCE(ret < 0)) dbg_event(epnum, "ECTRL_DATA", ret); } @@ -1010,9 +1013,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, req->direction = !!dep->number; if (req->request.length == 0) { - ret = dwc3_ep0_start_trans(dwc, dep->number, + dwc3_ep0_prepare_one_trb(dwc, dep->number, dwc->ctrl_req_addr, 0, DWC3_TRBCTL_CONTROL_DATA, false); + ret = dwc3_ep0_start_trans(dwc, dep->number); } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && (dep->number == 0)) { u32 transfer_size = 0; @@ -1030,7 +1034,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, if (req->request.length > DWC3_EP0_BOUNCE_SIZE) { transfer_size = ALIGN(req->request.length - maxpacket, maxpacket); - ret = dwc3_ep0_start_trans(dwc, dep->number, + dwc3_ep0_prepare_one_trb(dwc, dep->number, req->request.dma, transfer_size, DWC3_TRBCTL_CONTROL_DATA, @@ -1042,9 +1046,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, dwc->ep0_bounced = true; - ret = dwc3_ep0_start_trans(dwc, dep->number, + dwc3_ep0_prepare_one_trb(dwc, dep->number, dwc->ep0_bounce_addr, transfer_size, DWC3_TRBCTL_CONTROL_DATA, false); + ret = dwc3_ep0_start_trans(dwc, dep->number); } else { ret = usb_gadget_map_request(&dwc->gadget, &req->request, dep->number); @@ -1053,9 +1058,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, return; } - ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma, + dwc3_ep0_prepare_one_trb(dwc, dep->number, req->request.dma, req->request.length, DWC3_TRBCTL_CONTROL_DATA, false); + ret = dwc3_ep0_start_trans(dwc, dep->number); } dbg_queue(dep->number, &req->request, ret); @@ -1069,8 +1075,9 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3 : DWC3_TRBCTL_CONTROL_STATUS2; - return dwc3_ep0_start_trans(dwc, dep->number, + dwc3_ep0_prepare_one_trb(dwc, dep->number, dwc->ctrl_req_addr, 0, type, false); + return dwc3_ep0_start_trans(dwc, dep->number); } static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7a279db521ca..e1284b6cc2ef 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -285,11 +285,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, if (req->request.status == -EINPROGRESS) req->request.status = status; - if (dwc->ep0_bounced && dep->number == 0) + if (dwc->ep0_bounced && dep->number <= 1) dwc->ep0_bounced = false; - else - usb_gadget_unmap_request(&dwc->gadget, &req->request, - req->direction); + + usb_gadget_unmap_request(&dwc->gadget, &req->request, + req->direction); dev_dbg(dwc->dev, "request %pK from %s completed %d/%d ===> %d\n", req, dep->name, req->request.actual, @@ -388,8 +388,10 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, } return -ETIMEDOUT; } - - udelay(1); + if ((cmd & DWC3_DEPCMD_SETTRANSFRESOURCE)) + udelay(20); + else + udelay(1); } while (1); } diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index a21962c8f513..99ff6df063a7 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -28,23 +28,23 @@ struct dwc3; #define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget)) /* DEPCFG parameter 1 */ -#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0) +#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0) #define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8) #define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9) #define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10) #define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11) #define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13) -#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16) +#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16) #define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24) -#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25) +#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25) #define DWC3_DEPCFG_BULK_BASED (1 << 30) #define DWC3_DEPCFG_FIFO_BASED (1 << 31) /* DEPCFG parameter 0 */ -#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1) -#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3) -#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17) -#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22) +#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1) +#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) (((n) & 0x7ff) << 3) +#define DWC3_DEPCFG_FIFO_NUMBER(n) (((n) & 0x1f) << 17) +#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22) #define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26) /* This applies for core versions earlier than 1.94a */ #define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 9d795489c285..a844ea4d06db 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -160,11 +160,16 @@ int config_ep_by_speed(struct usb_gadget *g, ep_found: /* commit results */ - _ep->maxpacket = usb_endpoint_maxp(chosen_desc); + _ep->maxpacket = usb_endpoint_maxp(chosen_desc) & 0x7ff; _ep->desc = chosen_desc; _ep->comp_desc = NULL; _ep->maxburst = 0; - _ep->mult = 0; + _ep->mult = 1; + + if (g->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(_ep->desc) || + usb_endpoint_xfer_int(_ep->desc))) + _ep->mult = ((usb_endpoint_maxp(_ep->desc) & 0x1800) >> 11) + 1; + if (!want_comp_desc) return 0; @@ -181,7 +186,7 @@ ep_found: switch (usb_endpoint_type(_ep->desc)) { case USB_ENDPOINT_XFER_ISOC: /* mult: bits 1:0 of bmAttributes */ - _ep->mult = comp_desc->bmAttributes & 0x3; + _ep->mult = (comp_desc->bmAttributes & 0x3) + 1; case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: _ep->maxburst = comp_desc->bMaxBurst + 1; @@ -1750,9 +1755,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) value = min(w_length, (u16) 1); break; - /* function drivers must handle get/set altsetting; if there's - * no get() method, we know only altsetting zero works. - */ + /* function drivers must handle get/set altsetting */ case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != USB_RECIP_INTERFACE) goto unknown; @@ -1761,7 +1764,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) f = cdev->config->interface[intf]; if (!f) break; - if (w_value && !f->set_alt) + + /* + * If there's no get_alt() method, we know only altsetting zero + * works. There is no need to check if set_alt() is not NULL + * as we check this in usb_add_function(). + */ + if (w_value && !f->get_alt) break; value = f->set_alt(f, w_index, w_value); if (value == USB_GADGET_DELAYED_STATUS) { diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 7a64b24b8bf6..a42550950953 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -60,7 +60,7 @@ usb_f_cdev-y := f_cdev.o obj-$(CONFIG_USB_F_CDEV) += usb_f_cdev.o usb_f_qdss-y := f_qdss.o u_qdss.o obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o -usb_f_qcrndis-y := f_qc_rndis.o u_data_ipa.o +usb_f_qcrndis-y := f_qc_rndis.o rndis.o u_data_ipa.o obj-$(CONFIG_USB_F_QCRNDIS) += usb_f_qcrndis.o usb_f_rmnet_bam-y := f_rmnet.o u_ctrl_qti.o obj-$(CONFIG_USB_F_RMNET_BAM) += usb_f_rmnet_bam.o diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c index db7903d19c43..a2a9185a0143 100644 --- a/drivers/usb/gadget/function/f_audio_source.c +++ b/drivers/usb/gadget/function/f_audio_source.c @@ -989,6 +989,7 @@ static ssize_t audio_source_pcm_show(struct device *dev, struct device *create_function_device(char *name); +#define AUDIO_SOURCE_DEV_NAME_LENGTH 20 static struct usb_function_instance *audio_source_alloc_inst(void) { struct audio_source_instance *fi_audio; @@ -997,6 +998,8 @@ static struct usb_function_instance *audio_source_alloc_inst(void) struct device *dev; void *err_ptr; int err = 0; + char device_name[AUDIO_SOURCE_DEV_NAME_LENGTH]; + static u8 count; fi_audio = kzalloc(sizeof(*fi_audio), GFP_KERNEL); if (!fi_audio) @@ -1014,7 +1017,11 @@ static struct usb_function_instance *audio_source_alloc_inst(void) config_group_init_type_name(&fi_audio->func_inst.group, "", &audio_source_func_type); - dev = create_function_device("f_audio_source"); + + snprintf(device_name, AUDIO_SOURCE_DEV_NAME_LENGTH, + "f_audio_source%d", count++); + + dev = create_function_device(device_name); if (IS_ERR(dev)) { err_ptr = dev; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 31d3022b6ce7..a1c00525a598 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2437,6 +2437,8 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type, if (len < sizeof(*d) || h->interface >= ffs->interfaces_count) return -EINVAL; length = le32_to_cpu(d->dwSize); + if (len < length) + return -EINVAL; type = le32_to_cpu(d->dwPropertyDataType); if (type < USB_EXT_PROP_UNICODE || type > USB_EXT_PROP_UNICODE_MULTI) { @@ -2445,6 +2447,11 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type, return -EINVAL; } pnl = le16_to_cpu(d->wPropertyNameLength); + if (length < 14 + pnl) { + pr_vdebug("invalid os descriptor length: %d pnl:%d (descriptor %d)\n", + length, pnl, type); + return -EINVAL; + } pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl)); if (length != 14 + pnl + pdl) { pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n", @@ -2534,6 +2541,9 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, } } if (flags & (1 << i)) { + if (len < 4) { + goto error; + } os_descs_count = get_unaligned_le32(data); data += 4; len -= 4; @@ -2611,7 +2621,8 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, ffs_log("enter: len %zu", len); - if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC || + if (unlikely(len < 16 || + get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC || get_unaligned_le32(data + 4) != len)) goto error; str_count = get_unaligned_le32(data + 8); diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 4e35ed9654b7..2a7d57cd14cb 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1426,17 +1426,39 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) */ if (!ncm_opts->bound) { mutex_lock(&ncm_opts->lock); + ncm_opts->net = gether_setup_default(); + if (IS_ERR(ncm_opts->net)) { + status = PTR_ERR(ncm_opts->net); + mutex_unlock(&ncm_opts->lock); + goto error; + } gether_set_gadget(ncm_opts->net, cdev->gadget); status = gether_register_netdev(ncm_opts->net); mutex_unlock(&ncm_opts->lock); - if (status) - return status; + if (status) { + free_netdev(ncm_opts->net); + goto error; + } ncm_opts->bound = true; } + + /* export host's Ethernet address in CDC format */ + status = gether_get_host_addr_cdc(ncm_opts->net, ncm->ethaddr, + sizeof(ncm->ethaddr)); + if (status < 12) { /* strlen("01234567890a") */ + ERROR(cdev, "%s: failed to get host eth addr, err %d\n", + __func__, status); + status = -EINVAL; + goto netdev_cleanup; + } + ncm->port.ioport = netdev_priv(ncm_opts->net); + us = usb_gstrings_attach(cdev, ncm_strings, ARRAY_SIZE(ncm_string_defs)); - if (IS_ERR(us)) - return PTR_ERR(us); + if (IS_ERR(us)) { + status = PTR_ERR(us); + goto netdev_cleanup; + } ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id; ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id; ncm_data_intf.iInterface = us[STRING_DATA_IDX].id; @@ -1540,7 +1562,10 @@ fail: kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); } +netdev_cleanup: + gether_cleanup(netdev_priv(ncm_opts->net)); +error: ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); return status; @@ -1588,8 +1613,6 @@ static void ncm_free_inst(struct usb_function_instance *f) opts = container_of(f, struct f_ncm_opts, func_inst); if (opts->bound) gether_cleanup(netdev_priv(opts->net)); - else - free_netdev(opts->net); kfree(opts); } @@ -1602,12 +1625,6 @@ static struct usb_function_instance *ncm_alloc_inst(void) return ERR_PTR(-ENOMEM); mutex_init(&opts->lock); opts->func_inst.free_func_inst = ncm_free_inst; - opts->net = gether_setup_default(); - if (IS_ERR(opts->net)) { - struct net_device *net = opts->net; - kfree(opts); - return ERR_CAST(net); - } config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type); @@ -1630,6 +1647,8 @@ static void ncm_free(struct usb_function *f) static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_ncm *ncm = func_to_ncm(f); + struct f_ncm_opts *opts = container_of(f->fi, struct f_ncm_opts, + func_inst); DBG(c->cdev, "ncm unbind\n"); @@ -1641,13 +1660,15 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); + + gether_cleanup(netdev_priv(opts->net)); + opts->bound = false; } static struct usb_function *ncm_alloc(struct usb_function_instance *fi) { struct f_ncm *ncm; struct f_ncm_opts *opts; - int status; /* allocate and initialize one new instance */ ncm = kzalloc(sizeof(*ncm), GFP_KERNEL); @@ -1657,20 +1678,9 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi) opts = container_of(fi, struct f_ncm_opts, func_inst); mutex_lock(&opts->lock); opts->refcnt++; - - /* export host's Ethernet address in CDC format */ - status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr, - sizeof(ncm->ethaddr)); - if (status < 12) { /* strlen("01234567890a") */ - kfree(ncm); - mutex_unlock(&opts->lock); - return ERR_PTR(-EINVAL); - } ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr; - spin_lock_init(&ncm->lock); ncm_reset_values(ncm); - ncm->port.ioport = netdev_priv(opts->net); mutex_unlock(&opts->lock); ncm->port.is_fixed = true; ncm->port.supports_multi_frame = true; diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c index ac17d0aa46ae..45c39d3c4225 100644 --- a/drivers/usb/gadget/function/f_qc_rndis.c +++ b/drivers/usb/gadget/function/f_qc_rndis.c @@ -153,6 +153,7 @@ static unsigned int rndis_qc_bitrate(struct usb_gadget *g) /* interface descriptor: */ +/* interface descriptor: Supports "Wireless" RNDIS; auto-detected by Windows*/ static struct usb_interface_descriptor rndis_qc_control_intf = { .bLength = sizeof(rndis_qc_control_intf), .bDescriptorType = USB_DT_INTERFACE, @@ -160,9 +161,9 @@ static struct usb_interface_descriptor rndis_qc_control_intf = { /* .bInterfaceNumber = DYNAMIC */ /* status endpoint is optional; this could be patched later */ .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, + .bInterfaceClass = USB_CLASS_WIRELESS_CONTROLLER, + .bInterfaceSubClass = 0x01, + .bInterfaceProtocol = 0x03, /* .iInterface = DYNAMIC */ }; @@ -214,15 +215,16 @@ static struct usb_interface_descriptor rndis_qc_data_intf = { }; +/* Supports "Wireless" RNDIS; auto-detected by Windows */ static struct usb_interface_assoc_descriptor rndis_qc_iad_descriptor = { .bLength = sizeof(rndis_qc_iad_descriptor), .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, .bFirstInterface = 0, /* XXX, hardcoded */ .bInterfaceCount = 2, /* control + data */ - .bFunctionClass = USB_CLASS_COMM, - .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET, - .bFunctionProtocol = USB_CDC_PROTO_NONE, + .bFunctionClass = USB_CLASS_WIRELESS_CONTROLLER, + .bFunctionSubClass = 0x01, + .bFunctionProtocol = 0x03, /* .iFunction = DYNAMIC */ }; diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index 29263a84bbea..88db253aeef4 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -493,11 +493,7 @@ static void usb_qdss_disconnect_work(struct work_struct *work) NULL, NULL); - status = set_qdss_data_connection( - qdss->gadget, - qdss->port.data, - qdss->port.data->address, - 0); + status = set_qdss_data_connection(qdss, 0); if (status) pr_err("qdss_disconnect error"); } @@ -543,11 +539,7 @@ static void usb_qdss_connect_work(struct work_struct *work) } pr_debug("usb_qdss_connect_work\n"); - status = set_qdss_data_connection( - qdss->gadget, - qdss->port.data, - qdss->port.data->address, - 1); + status = set_qdss_data_connection(qdss, 1); if (status) { pr_err("set_qdss_data_connection error(%d)", status); return; @@ -868,14 +860,9 @@ void usb_qdss_close(struct usb_qdss_ch *ch) if (status) pr_err("%s: uninit_data error\n", __func__); - status = set_qdss_data_connection( - gadget, - qdss->port.data, - qdss->port.data->address, - 0); + status = set_qdss_data_connection(qdss, 0); if (status) pr_err("%s:qdss_disconnect error\n", __func__); - usb_gadget_restart(gadget); } EXPORT_SYMBOL(usb_qdss_close); diff --git a/drivers/usb/gadget/function/f_qdss.h b/drivers/usb/gadget/function/f_qdss.h index e3fe8ae03775..cad0f4cc06f9 100644 --- a/drivers/usb/gadget/function/f_qdss.h +++ b/drivers/usb/gadget/function/f_qdss.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -69,6 +69,5 @@ struct usb_qdss_opts { }; int uninit_data(struct usb_ep *ep); -int set_qdss_data_connection(struct usb_gadget *gadget, - struct usb_ep *data_ep, u8 data_addr, int enable); +int set_qdss_data_connection(struct f_qdss *qdss, int enable); #endif diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 12628dd36e55..12064d3bddf6 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1079,13 +1079,13 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc); if (!agdev->out_ep) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err; + return ret; } agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc); if (!agdev->in_ep) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); - goto err; + return ret; } uac2->p_prm.uac2 = uac2; @@ -1102,7 +1102,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL); if (ret) - goto err; + return ret; prm = &agdev->uac2.c_prm; prm->max_psize = hs_epout_desc.wMaxPacketSize; @@ -1117,19 +1117,19 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL); if (!prm->rbuf) { prm->max_psize = 0; - goto err_free_descs; + goto err; } ret = alsa_uac2_init(agdev); if (ret) - goto err_free_descs; + goto err; return 0; -err_free_descs: - usb_free_all_descriptors(fn); err: kfree(agdev->uac2.p_prm.rbuf); kfree(agdev->uac2.c_prm.rbuf); +err_free_descs: + usb_free_all_descriptors(fn); return -EINVAL; } diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c index 5718f71bcdea..d9a0b0e0b271 100644 --- a/drivers/usb/gadget/function/u_data_ipa.c +++ b/drivers/usb/gadget/function/u_data_ipa.c @@ -735,7 +735,7 @@ int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func, { struct ipa_data_ch_info *port; unsigned long flags; - int ret; + int ret = 0; pr_debug("dev:%pK port#%d src_connection_idx:%d dst_connection_idx:%d\n", gp, func, src_connection_idx, dst_connection_idx); diff --git a/drivers/usb/gadget/function/u_qdss.c b/drivers/usb/gadget/function/u_qdss.c index 42a9cda68659..0ef1e2ab34be 100644 --- a/drivers/usb/gadget/function/u_qdss.c +++ b/drivers/usb/gadget/function/u_qdss.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -40,19 +40,25 @@ static int alloc_sps_req(struct usb_ep *data_ep) } static int init_data(struct usb_ep *ep); -int set_qdss_data_connection(struct usb_gadget *gadget, - struct usb_ep *data_ep, u8 data_addr, int enable) +int set_qdss_data_connection(struct f_qdss *qdss, int enable) { enum usb_ctrl usb_bam_type; int res = 0; int idx; - struct f_qdss *qdss = data_ep->driver_data; - struct usb_qdss_bam_connect_info bam_info = qdss->bam_info; + struct usb_qdss_bam_connect_info bam_info; + struct usb_gadget *gadget; pr_debug("set_qdss_data_connection\n"); + if (!qdss) { + pr_err("%s: qdss ptr is NULL\n", __func__); + return -EINVAL; + } + + gadget = qdss->gadget; usb_bam_type = usb_bam_get_bam_type(gadget->name); + bam_info = qdss->bam_info; /* There is only one qdss pipe, so the pipe number can be set to 0 */ idx = usb_bam_get_connection_idx(usb_bam_type, QDSS_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, 0); @@ -67,14 +73,16 @@ int set_qdss_data_connection(struct usb_gadget *gadget, kzalloc(sizeof(struct sps_mem_buffer), GFP_KERNEL); if (!bam_info.data_fifo) { pr_err("qdss_data_connection: memory alloc failed\n"); + usb_bam_free_fifos(usb_bam_type, idx); return -ENOMEM; } get_bam2bam_connection_info(usb_bam_type, idx, &bam_info.usb_bam_pipe_idx, NULL, bam_info.data_fifo, NULL); - alloc_sps_req(data_ep); - msm_data_fifo_config(data_ep, bam_info.data_fifo->phys_base, + alloc_sps_req(qdss->port.data); + msm_data_fifo_config(qdss->port.data, + bam_info.data_fifo->phys_base, bam_info.data_fifo->size, bam_info.usb_bam_pipe_idx); init_data(qdss->port.data); diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 3d0d5d94a62f..0f01c04d7cbd 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -243,7 +243,7 @@ uvc_video_alloc_requests(struct uvc_video *video) req_size = video->ep->maxpacket * max_t(unsigned int, video->ep->maxburst, 1) - * (video->ep->mult + 1); + * (video->ep->mult); for (i = 0; i < UVC_NUM_REQUESTS; ++i) { video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL); diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index e57f48f9528f..de014436fb22 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1125,7 +1125,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) /* data and/or status stage for control request */ } else if (dev->state == STATE_DEV_SETUP) { - /* IN DATA+STATUS caller makes len <= wLength */ + len = min_t(size_t, len, dev->setup_wLength); if (dev->setup_in) { retval = setup_req (dev->gadget->ep0, dev->req, len); if (retval == 0) { @@ -1755,10 +1755,12 @@ static struct usb_gadget_driver probe_driver = { * such as configuration notifications. */ -static int is_valid_config (struct usb_config_descriptor *config) +static int is_valid_config(struct usb_config_descriptor *config, + unsigned int total) { return config->bDescriptorType == USB_DT_CONFIG && config->bLength == USB_DT_CONFIG_SIZE + && total >= USB_DT_CONFIG_SIZE && config->bConfigurationValue != 0 && (config->bmAttributes & USB_CONFIG_ATT_ONE) != 0 && (config->bmAttributes & USB_CONFIG_ATT_WAKEUP) == 0; @@ -1783,7 +1785,8 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) } spin_unlock_irq(&dev->lock); - if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) + if ((len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) || + (len > PAGE_SIZE * 4)) return -EINVAL; /* we might need to change message format someday */ @@ -1807,7 +1810,8 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) /* full or low speed config */ dev->config = (void *) kbuf; total = le16_to_cpu(dev->config->wTotalLength); - if (!is_valid_config (dev->config) || total >= length) + if (!is_valid_config(dev->config, total) || + total > length - USB_DT_DEVICE_SIZE) goto fail; kbuf += total; length -= total; @@ -1816,10 +1820,13 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) if (kbuf [1] == USB_DT_CONFIG) { dev->hs_config = (void *) kbuf; total = le16_to_cpu(dev->hs_config->wTotalLength); - if (!is_valid_config (dev->hs_config) || total >= length) + if (!is_valid_config(dev->hs_config, total) || + total > length - USB_DT_DEVICE_SIZE) goto fail; kbuf += total; length -= total; + } else { + dev->hs_config = NULL; } /* could support multiple configs, using another encoding! */ diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index dde44450dfa9..6610f7a023d3 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -330,7 +330,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep) /* caller must hold lock */ static void stop_activity(struct dummy *dum) { - struct dummy_ep *ep; + int i; /* prevent any more requests */ dum->address = 0; @@ -338,8 +338,8 @@ static void stop_activity(struct dummy *dum) /* The timer is left running so that outstanding URBs can fail */ /* nuke any pending requests first, so driver i/o is quiesced */ - list_for_each_entry(ep, &dum->gadget.ep_list, ep.ep_list) - nuke(dum, ep); + for (i = 0; i < DUMMY_ENDPOINTS; ++i) + nuke(dum, &dum->ep[i]); /* driver now does any non-usb quiescing necessary */ } @@ -1033,6 +1033,8 @@ static int dummy_udc_probe(struct platform_device *pdev) int rc; dum = *((void **)dev_get_platdata(&pdev->dev)); + /* Clear usb_gadget region for new registration to udc-core */ + memzero_explicit(&dum->gadget, sizeof(struct usb_gadget)); dum->gadget.name = gadget_name; dum->gadget.ops = &dummy_ops; dum->gadget.max_speed = USB_SPEED_SUPER; diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index aab5221d6c2e..aac0ce8aeb0b 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1249,6 +1249,12 @@ static const struct usb_gadget_ops fsl_gadget_ops = { .udc_stop = fsl_udc_stop, }; +/* + * Empty complete function used by this driver to fill in the req->complete + * field when creating a request since the complete field is mandatory. + */ +static void fsl_noop_complete(struct usb_ep *ep, struct usb_request *req) { } + /* Set protocol stall on ep0, protocol stall will automatically be cleared on new transaction */ static void ep0stall(struct fsl_udc *udc) @@ -1283,7 +1289,7 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction) req->req.length = 0; req->req.status = -EINPROGRESS; req->req.actual = 0; - req->req.complete = NULL; + req->req.complete = fsl_noop_complete; req->dtd_count = 0; ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); @@ -1366,7 +1372,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, req->req.length = 2; req->req.status = -EINPROGRESS; req->req.actual = 0; - req->req.complete = NULL; + req->req.complete = fsl_noop_complete; req->dtd_count = 0; ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c index 940304c33224..02260cfdedb1 100644 --- a/drivers/usb/host/uhci-pci.c +++ b/drivers/usb/host/uhci-pci.c @@ -129,6 +129,10 @@ static int uhci_pci_init(struct usb_hcd *hcd) if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP) uhci->wait_for_hp = 1; + /* Intel controllers use non-PME wakeup signalling */ + if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_INTEL) + device_set_run_wake(uhci_dev(uhci), 1); + /* Set up pointers to PCI-specific functions */ uhci->reset_hc = uhci_pci_reset_hc; uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc; diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 34388950f96b..a190c97d11e4 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -111,7 +111,7 @@ static void xhci_print_cap_regs(struct xhci_hcd *xhci) xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK); /* xhci 1.1 controllers have the HCCPARAMS2 register */ - if (hci_version > 100) { + if (hci_version > 0x100) { temp = readl(&xhci->cap_regs->hcc_params2); xhci_dbg(xhci, "HCC PARAMS2 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, " HC %s Force save context capability", diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 163de4bde2d8..d885033d3322 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1529,6 +1529,35 @@ int xhci_bus_suspend(struct usb_hcd *hcd) return 0; } +/* + * Workaround for missing Cold Attach Status (CAS) if device re-plugged in S3. + * warm reset a USB3 device stuck in polling or compliance mode after resume. + * See Intel 100/c230 series PCH specification update Doc #332692-006 Errata #8 + */ +static bool xhci_port_missing_cas_quirk(int port_index, + __le32 __iomem **port_array) +{ + u32 portsc; + + portsc = readl(port_array[port_index]); + + /* if any of these are set we are not stuck */ + if (portsc & (PORT_CONNECT | PORT_CAS)) + return false; + + if (((portsc & PORT_PLS_MASK) != XDEV_POLLING) && + ((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE)) + return false; + + /* clear wakeup/change bits, and do a warm port reset */ + portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); + portsc |= PORT_WR; + writel(portsc, port_array[port_index]); + /* flush write */ + readl(port_array[port_index]); + return true; +} + int xhci_bus_resume(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -1566,6 +1595,14 @@ int xhci_bus_resume(struct usb_hcd *hcd) u32 temp; temp = readl(port_array[port_index]); + + /* warm reset CAS limited ports stuck in polling/compliance */ + if ((xhci->quirks & XHCI_MISSING_CAS) && + (hcd->speed >= HCD_USB3) && + xhci_port_missing_cas_quirk(port_index, port_array)) { + xhci_dbg(xhci, "reset stuck port %d\n", port_index); + continue; + } if (DEV_SUPERSPEED_ANY(temp)) temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); else diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 8e5ab373dce9..82483599a882 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -964,6 +964,40 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) xhci->devs[slot_id] = NULL; } +/* + * Free a virt_device structure. + * If the virt_device added a tt_info (a hub) and has children pointing to + * that tt_info, then free the child first. Recursive. + * We can't rely on udev at this point to find child-parent relationships. + */ +void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) +{ + struct xhci_virt_device *vdev; + struct list_head *tt_list_head; + struct xhci_tt_bw_info *tt_info, *next; + int i; + + vdev = xhci->devs[slot_id]; + if (!vdev) + return; + + tt_list_head = &(xhci->rh_bw[vdev->real_port - 1].tts); + list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) { + /* is this a hub device that added a tt_info to the tts list */ + if (tt_info->slot_id == slot_id) { + /* are any devices using this tt_info? */ + for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) { + vdev = xhci->devs[i]; + if (vdev && (vdev->tt_info == tt_info)) + xhci_free_virt_devices_depth_first( + xhci, i); + } + } + } + /* we are now at a leaf device */ + xhci_free_virt_device(xhci, slot_id); +} + int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags) { @@ -1933,7 +1967,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) struct device *dev = xhci_to_hcd(xhci)->self.controller; int i, j, num_ports; - del_timer_sync(&xhci->cmd_timer); + cancel_delayed_work_sync(&xhci->cmd_timer); xhci_event_ring_cleanup(xhci); @@ -1956,8 +1990,8 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) } } - for (i = 1; i < MAX_HC_SLOTS; ++i) - xhci_free_virt_device(xhci, i); + for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--) + xhci_free_virt_devices_depth_first(xhci, i); dma_pool_destroy(xhci->segment_pool); xhci->segment_pool = NULL; @@ -2636,9 +2670,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) INIT_LIST_HEAD(&xhci->cmd_list); - /* init command timeout timer */ - setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout, - (unsigned long)xhci); + /* init command timeout work */ + INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout); + init_completion(&xhci->cmd_ring_stop_completion); page_size = readl(&xhci->op_regs->page_size); xhci_dbg_trace(xhci, trace_xhci_dbg_init, @@ -2677,7 +2711,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * "physically contiguous and 64-byte (cache line) aligned". */ xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, - GFP_KERNEL); + flags); if (!xhci->dcbaa) goto fail; memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa)); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index cf147ccac7d3..dd262f418140 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -51,6 +51,7 @@ #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f #define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8 #define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8 +#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8 static const char hcd_name[] = "xhci_hcd"; @@ -165,9 +166,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI)) { + pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) { xhci->quirks |= XHCI_PME_STUCK_QUIRK; } + if (pdev->vendor == PCI_VENDOR_ID_INTEL && + (pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) + xhci->quirks |= XHCI_MISSING_CAS; + if (pdev->vendor == PCI_VENDOR_ID_ETRON && pdev->device == PCI_DEVICE_ID_EJ168) { xhci->quirks |= XHCI_RESET_ON_RESUME; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 05d96fd8c07c..7423645c204c 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -38,12 +38,19 @@ static const struct xhci_driver_overrides xhci_plat_overrides __initconst = { static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) { + struct device_node *node = dev->of_node; + struct usb_xhci_pdata *pdata = dev_get_platdata(dev); + /* * As of now platform drivers don't provide MSI support so we ensure * here that the generic code does not try to make a pci_dev from our * dev struct in order to setup MSI */ xhci->quirks |= XHCI_PLAT; + + if ((node && of_property_read_bool(node, "usb3-lpm-capable")) || + (pdata && pdata->usb3_lpm_capable)) + xhci->quirks |= XHCI_LPM_SUPPORT; } /* called during probe() after chip reset completes */ @@ -129,7 +136,6 @@ static DEVICE_ATTR(config_imod, S_IRUGO | S_IWUSR, static int xhci_plat_probe(struct platform_device *pdev) { - struct device_node *node = pdev->dev.of_node; struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev); const struct hc_driver *driver; struct xhci_hcd *xhci; @@ -227,13 +233,6 @@ static int xhci_plat_probe(struct platform_device *pdev) hcd_to_bus(xhci->shared_hcd)->skip_resume = true; - if ((node && of_property_read_bool(node, "usb3-lpm-capable")) || - (pdata && pdata->usb3_lpm_capable)) - xhci->quirks |= XHCI_LPM_SUPPORT; - - if (HCC_MAX_PSA(xhci->hcc_params) >= 4) - xhci->shared_hcd->can_do_streams = 1; - hcd->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); if (IS_ERR(hcd->usb_phy)) { ret = PTR_ERR(hcd->usb_phy); @@ -252,6 +251,9 @@ static int xhci_plat_probe(struct platform_device *pdev) device_wakeup_enable(&hcd->self.root_hub->dev); + if (HCC_MAX_PSA(xhci->hcc_params) >= 4) + xhci->shared_hcd->can_do_streams = 1; + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED | IRQF_ONESHOT); if (ret) goto dealloc_usb2_hcd; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 98d01acc8b11..12e38cf022ff 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -280,23 +280,76 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci) readl(&xhci->dba->doorbell[0]); } -static int xhci_abort_cmd_ring(struct xhci_hcd *xhci) +static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay) +{ + return mod_delayed_work(system_wq, &xhci->cmd_timer, delay); +} + +static struct xhci_command *xhci_next_queued_cmd(struct xhci_hcd *xhci) +{ + return list_first_entry_or_null(&xhci->cmd_list, struct xhci_command, + cmd_list); +} + +/* + * Turn all commands on command ring with status set to "aborted" to no-op trbs. + * If there are other commands waiting then restart the ring and kick the timer. + * This must be called with command ring stopped and xhci->lock held. + */ +static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, + struct xhci_command *cur_cmd) +{ + struct xhci_command *i_cmd; + u32 cycle_state; + + /* Turn all aborted commands in list to no-ops, then restart */ + list_for_each_entry(i_cmd, &xhci->cmd_list, cmd_list) { + + if (i_cmd->status != COMP_CMD_ABORT) + continue; + + i_cmd->status = COMP_CMD_STOP; + + xhci_dbg(xhci, "Turn aborted command %pK to no-op\n", + i_cmd->command_trb); + /* get cycle state from the original cmd trb */ + cycle_state = le32_to_cpu( + i_cmd->command_trb->generic.field[3]) & TRB_CYCLE; + /* modify the command trb to no-op command */ + i_cmd->command_trb->generic.field[0] = 0; + i_cmd->command_trb->generic.field[1] = 0; + i_cmd->command_trb->generic.field[2] = 0; + i_cmd->command_trb->generic.field[3] = cpu_to_le32( + TRB_TYPE(TRB_CMD_NOOP) | cycle_state); + + /* + * caller waiting for completion is called when command + * completion event is received for these no-op commands + */ + } + + xhci->cmd_ring_state = CMD_RING_STATE_RUNNING; + + /* ring command ring doorbell to restart the command ring */ + if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) && + !(xhci->xhc_state & XHCI_STATE_DYING)) { + xhci->current_cmd = cur_cmd; + xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + xhci_ring_cmd_db(xhci); + } +} + +/* Must be called with xhci->lock held, releases and aquires lock back */ +static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags) { u64 temp_64; int ret; xhci_dbg(xhci, "Abort command ring\n"); - temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); - xhci->cmd_ring_state = CMD_RING_STATE_ABORTED; + reinit_completion(&xhci->cmd_ring_stop_completion); - /* - * Writing the CMD_RING_ABORT bit should cause a cmd completion event, - * however on some host hw the CMD_RING_RUNNING bit is correctly cleared - * but the completion event in never sent. Use the cmd timeout timer to - * handle those cases. Use twice the time to cover the bit polling retry - */ - mod_timer(&xhci->cmd_timer, jiffies + (2 * XHCI_CMD_DEFAULT_TIMEOUT)); + temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); xhci_write_64(xhci, temp_64 | CMD_RING_ABORT, &xhci->op_regs->cmd_ring); @@ -316,16 +369,30 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci) udelay(1000); ret = xhci_handshake(&xhci->op_regs->cmd_ring, CMD_RING_RUNNING, 0, 3 * 1000 * 1000); - if (ret == 0) - return 0; - - xhci_err(xhci, "Stopped the command ring failed, " - "maybe the host is dead\n"); - del_timer(&xhci->cmd_timer); - xhci->xhc_state |= XHCI_STATE_DYING; - xhci_quiesce(xhci); - xhci_halt(xhci); - return -ESHUTDOWN; + if (ret < 0) { + xhci_err(xhci, "Stopped the command ring failed, " + "maybe the host is dead\n"); + xhci->xhc_state |= XHCI_STATE_DYING; + xhci_quiesce(xhci); + xhci_halt(xhci); + return -ESHUTDOWN; + } + } + /* + * Writing the CMD_RING_ABORT bit should cause a cmd completion event, + * however on some host hw the CMD_RING_RUNNING bit is correctly cleared + * but the completion event in never sent. Wait 2 secs (arbitrary + * number) to handle those cases after negation of CMD_RING_RUNNING. + */ + spin_unlock_irqrestore(&xhci->lock, flags); + ret = wait_for_completion_timeout(&xhci->cmd_ring_stop_completion, + msecs_to_jiffies(2000)); + spin_lock_irqsave(&xhci->lock, flags); + if (!ret) { + xhci_dbg(xhci, "No stop event for abort, ring start fail?\n"); + xhci_cleanup_command_queue(xhci); + } else { + xhci_handle_stopped_cmd_ring(xhci, xhci_next_queued_cmd(xhci)); } return 0; @@ -846,17 +913,6 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) spin_lock_irqsave(&xhci->lock, flags); ep->stop_cmds_pending--; - if (xhci->xhc_state & XHCI_STATE_REMOVING) { - spin_unlock_irqrestore(&xhci->lock, flags); - return; - } - if (xhci->xhc_state & XHCI_STATE_DYING) { - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Stop EP timer ran, but another timer marked " - "xHCI as DYING, exiting."); - spin_unlock_irqrestore(&xhci->lock, flags); - return; - } if (!(ep->stop_cmds_pending == 0 && (ep->ep_state & EP_HALT_PENDING))) { xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Stop EP timer ran, but no command pending, " @@ -1208,101 +1264,62 @@ void xhci_cleanup_command_queue(struct xhci_hcd *xhci) xhci_complete_del_and_free_cmd(cur_cmd, COMP_CMD_ABORT); } -/* - * Turn all commands on command ring with status set to "aborted" to no-op trbs. - * If there are other commands waiting then restart the ring and kick the timer. - * This must be called with command ring stopped and xhci->lock held. - */ -static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, - struct xhci_command *cur_cmd) -{ - struct xhci_command *i_cmd, *tmp_cmd; - u32 cycle_state; - - /* Turn all aborted commands in list to no-ops, then restart */ - list_for_each_entry_safe(i_cmd, tmp_cmd, &xhci->cmd_list, - cmd_list) { - - if (i_cmd->status != COMP_CMD_ABORT) - continue; - - i_cmd->status = COMP_CMD_STOP; - - xhci_dbg(xhci, "Turn aborted command %pK to no-op\n", - i_cmd->command_trb); - /* get cycle state from the original cmd trb */ - cycle_state = le32_to_cpu( - i_cmd->command_trb->generic.field[3]) & TRB_CYCLE; - /* modify the command trb to no-op command */ - i_cmd->command_trb->generic.field[0] = 0; - i_cmd->command_trb->generic.field[1] = 0; - i_cmd->command_trb->generic.field[2] = 0; - i_cmd->command_trb->generic.field[3] = cpu_to_le32( - TRB_TYPE(TRB_CMD_NOOP) | cycle_state); - - /* - * caller waiting for completion is called when command - * completion event is received for these no-op commands - */ - } - - xhci->cmd_ring_state = CMD_RING_STATE_RUNNING; - - /* ring command ring doorbell to restart the command ring */ - if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) && - !(xhci->xhc_state & XHCI_STATE_DYING)) { - xhci->current_cmd = cur_cmd; - mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT); - xhci_ring_cmd_db(xhci); - } - return; -} - - -void xhci_handle_command_timeout(unsigned long data) +void xhci_handle_command_timeout(struct work_struct *work) { struct xhci_hcd *xhci; int ret; unsigned long flags; u64 hw_ring_state; - bool second_timeout = false; - xhci = (struct xhci_hcd *) data; - /* mark this command to be cancelled */ + xhci = container_of(to_delayed_work(work), struct xhci_hcd, cmd_timer); + spin_lock_irqsave(&xhci->lock, flags); - if (xhci->current_cmd) { - if (xhci->current_cmd->status == COMP_CMD_ABORT) - second_timeout = true; - xhci->current_cmd->status = COMP_CMD_ABORT; + + /* + * If timeout work is pending, or current_cmd is NULL, it means we + * raced with command completion. Command is handled so just return. + */ + if (!xhci->current_cmd || delayed_work_pending(&xhci->cmd_timer)) { + spin_unlock_irqrestore(&xhci->lock, flags); + return; } + /* mark this command to be cancelled */ + xhci->current_cmd->status = COMP_CMD_ABORT; /* Make sure command ring is running before aborting it */ hw_ring_state = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); if ((xhci->cmd_ring_state & CMD_RING_STATE_RUNNING) && (hw_ring_state & CMD_RING_RUNNING)) { - spin_unlock_irqrestore(&xhci->lock, flags); + /* Prevent new doorbell, and start command abort */ + xhci->cmd_ring_state = CMD_RING_STATE_ABORTED; xhci_dbg(xhci, "Command timeout\n"); - ret = xhci_abort_cmd_ring(xhci); + ret = xhci_abort_cmd_ring(xhci, flags); if (unlikely(ret == -ESHUTDOWN)) { xhci_err(xhci, "Abort command ring failed\n"); xhci_cleanup_command_queue(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); usb_hc_died(xhci_to_hcd(xhci)->primary_hcd); xhci_dbg(xhci, "xHCI host controller is dead.\n"); + + return; } - return; + + goto time_out_completed; } - /* command ring failed to restart, or host removed. Bail out */ - if (second_timeout || xhci->xhc_state & XHCI_STATE_REMOVING) { - spin_unlock_irqrestore(&xhci->lock, flags); - xhci_dbg(xhci, "command timed out twice, ring start fail?\n"); + /* host removed. Bail out */ + if (xhci->xhc_state & XHCI_STATE_REMOVING) { + xhci_dbg(xhci, "host removed, ring start fail?\n"); xhci_cleanup_command_queue(xhci); - return; + + goto time_out_completed; } /* command timeout on stopped ring, ring can't be aborted */ xhci_dbg(xhci, "Command timeout on stopped ring\n"); xhci_handle_stopped_cmd_ring(xhci, xhci->current_cmd); + +time_out_completed: spin_unlock_irqrestore(&xhci->lock, flags); return; } @@ -1335,7 +1352,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, cmd = list_entry(xhci->cmd_list.next, struct xhci_command, cmd_list); - del_timer(&xhci->cmd_timer); + cancel_delayed_work(&xhci->cmd_timer); trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event); @@ -1343,7 +1360,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, /* If CMD ring stopped we own the trbs between enqueue and dequeue */ if (cmd_comp_code == COMP_CMD_STOP) { - xhci_handle_stopped_cmd_ring(xhci, cmd); + complete_all(&xhci->cmd_ring_stop_completion); return; } @@ -1361,8 +1378,11 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, */ if (cmd_comp_code == COMP_CMD_ABORT) { xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; - if (cmd->status == COMP_CMD_ABORT) + if (cmd->status == COMP_CMD_ABORT) { + if (xhci->current_cmd == cmd) + xhci->current_cmd = NULL; goto event_handled; + } } cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3])); @@ -1423,7 +1443,9 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, if (cmd->cmd_list.next != &xhci->cmd_list) { xhci->current_cmd = list_entry(cmd->cmd_list.next, struct xhci_command, cmd_list); - mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + } else if (xhci->current_cmd == cmd) { + xhci->current_cmd = NULL; } event_handled: @@ -4206,9 +4228,9 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, /* if there are no other commands queued we start the timeout timer */ if (xhci->cmd_list.next == &cmd->cmd_list && - !timer_pending(&xhci->cmd_timer)) { + !delayed_work_pending(&xhci->cmd_timer)) { xhci->current_cmd = cmd; - mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); } queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8c6bb15ef51f..aab1c7903288 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -121,10 +121,10 @@ int xhci_halt(struct xhci_hcd *xhci) xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; - if (timer_pending(&xhci->cmd_timer)) { + if (delayed_work_pending(&xhci->cmd_timer)) { xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Cleanup command queue"); - del_timer(&xhci->cmd_timer); + cancel_delayed_work(&xhci->cmd_timer); xhci_cleanup_command_queue(xhci); } @@ -138,7 +138,13 @@ static int xhci_start(struct xhci_hcd *xhci) { u32 temp; int ret; + struct usb_hcd *hcd = xhci_to_hcd(xhci); + /* + * disable irq to avoid xhci_irq flooding due to unhandeled port + * change event in halt state, as soon as xhci_start clears halt bit + */ + disable_irq(hcd->irq); temp = readl(&xhci->op_regs->command); temp |= (CMD_RUN); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Turn on HC, cmd = 0x%x.", @@ -159,6 +165,8 @@ static int xhci_start(struct xhci_hcd *xhci) /* clear state flags. Including dying, halted or removing */ xhci->xhc_state = 0; + enable_irq(hcd->irq); + return ret; } @@ -1579,19 +1587,6 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) xhci_urb_free_priv(urb_priv); return ret; } - if ((xhci->xhc_state & XHCI_STATE_DYING) || - (xhci->xhc_state & XHCI_STATE_HALTED)) { - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Ep 0x%x: URB %pK to be canceled on " - "non-responsive xHCI host.", - urb->ep->desc.bEndpointAddress, urb); - /* Let the stop endpoint command watchdog timer (which set this - * state) finish cleaning up the endpoint TD lists. We must - * have caught it in the middle of dropping a lock and giving - * back an URB. - */ - goto done; - } ep_index = xhci_get_endpoint_index(&urb->ep->desc); ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index]; @@ -3818,8 +3813,10 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, mutex_lock(&xhci->mutex); - if (xhci->xhc_state) /* dying, removing or halted */ + if (xhci->xhc_state) { /* dying, removing or halted */ + ret = -ESHUTDOWN; goto out; + } if (!udev->slot_id) { xhci_dbg_trace(xhci, trace_xhci_dbg_address, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 097cfa9e4692..8fcec1be6b1a 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -312,6 +312,8 @@ struct xhci_op_regs { #define XDEV_U2 (0x2 << 5) #define XDEV_U3 (0x3 << 5) #define XDEV_INACTIVE (0x6 << 5) +#define XDEV_POLLING (0x7 << 5) +#define XDEV_COMP_MODE (0xa << 5) #define XDEV_RESUME (0xf << 5) /* true: port has power (see HCC_PPC) */ #define PORT_POWER (1 << 9) @@ -1553,7 +1555,8 @@ struct xhci_hcd { #define CMD_RING_STATE_STOPPED (1 << 2) struct list_head cmd_list; unsigned int cmd_ring_reserved_trbs; - struct timer_list cmd_timer; + struct delayed_work cmd_timer; + struct completion cmd_ring_stop_completion; struct xhci_command *current_cmd; struct xhci_ring *event_ring; struct xhci_erst erst; @@ -1639,6 +1642,7 @@ struct xhci_hcd { /* For controllers with a broken beyond repair streams implementation */ #define XHCI_BROKEN_STREAMS (1 << 19) #define XHCI_PME_STUCK_QUIRK (1 << 20) +#define XHCI_MISSING_CAS (1 << 24) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ @@ -1922,7 +1926,7 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, struct xhci_dequeue_state *deq_state); void xhci_stop_endpoint_command_watchdog(unsigned long arg); -void xhci_handle_command_timeout(unsigned long data); +void xhci_handle_command_timeout(struct work_struct *work); void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id); diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 1950e87b4219..775690bed4c0 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -787,12 +787,6 @@ static int iowarrior_probe(struct usb_interface *interface, iface_desc = interface->cur_altsetting; dev->product_id = le16_to_cpu(udev->descriptor.idProduct); - if (iface_desc->desc.bNumEndpoints < 1) { - dev_err(&interface->dev, "Invalid number of endpoints\n"); - retval = -EINVAL; - goto error; - } - /* set up the endpoint information */ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; @@ -803,6 +797,21 @@ static int iowarrior_probe(struct usb_interface *interface, /* this one will match for the IOWarrior56 only */ dev->int_out_endpoint = endpoint; } + + if (!dev->int_in_endpoint) { + dev_err(&interface->dev, "no interrupt-in endpoint found\n"); + retval = -ENODEV; + goto error; + } + + if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56) { + if (!dev->int_out_endpoint) { + dev_err(&interface->dev, "no interrupt-out endpoint found\n"); + retval = -ENODEV; + goto error; + } + } + /* we have to check the report_size often, so remember it in the endianness suitable for our machine */ dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint); if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) && diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 310238c6b5cd..896798071817 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -469,6 +469,7 @@ static const struct musb_platform_ops bfin_ops = { .init = bfin_musb_init, .exit = bfin_musb_exit, + .fifo_offset = bfin_fifo_offset, .readb = bfin_readb, .writeb = bfin_writeb, .readw = bfin_readw, diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index b03d3b867fca..9a9c82a4d35d 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -458,15 +458,11 @@ static int da8xx_musb_exit(struct musb *musb) } static const struct musb_platform_ops da8xx_ops = { - .quirks = MUSB_DMA_CPPI | MUSB_INDEXED_EP, + .quirks = MUSB_INDEXED_EP, .init = da8xx_musb_init, .exit = da8xx_musb_exit, .fifo_mode = 2, -#ifdef CONFIG_USB_TI_CPPI_DMA - .dma_init = cppi_dma_controller_create, - .dma_exit = cppi_dma_controller_destroy, -#endif .enable = da8xx_musb_enable, .disable = da8xx_musb_disable, diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 2337d7a7d62d..90de7900e4b8 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -214,6 +214,7 @@ struct musb_platform_ops { dma_addr_t *dma_addr, u32 *len); void (*pre_root_reset_end)(struct musb *musb); void (*post_root_reset_end)(struct musb *musb); + void (*clear_ep_rxintr)(struct musb *musb, int epnum); }; /* @@ -612,4 +613,10 @@ static inline void musb_platform_post_root_reset_end(struct musb *musb) musb->ops->post_root_reset_end(musb); } +static inline void musb_platform_clear_ep_rxintr(struct musb *musb, int epnum) +{ + if (musb->ops->clear_ep_rxintr) + musb->ops->clear_ep_rxintr(musb, epnum); +} + #endif /* __MUSB_CORE_H__ */ diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index eeb7d9ecf7df..5a021b26d7d2 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -301,6 +301,17 @@ static void otg_timer(unsigned long _musb) spin_unlock_irqrestore(&musb->lock, flags); } +void dsps_musb_clear_ep_rxintr(struct musb *musb, int epnum) +{ + u32 epintr; + struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent); + const struct dsps_musb_wrapper *wrp = glue->wrp; + + /* musb->lock might already been held */ + epintr = (1 << epnum) << wrp->rxep_shift; + musb_writel(musb->ctrl_base, wrp->epintr_status, epintr); +} + static irqreturn_t dsps_interrupt(int irq, void *hci) { struct musb *musb = hci; @@ -647,6 +658,7 @@ static struct musb_platform_ops dsps_ops = { .try_idle = dsps_musb_try_idle, .set_mode = dsps_musb_set_mode, .recover = dsps_musb_recover, + .clear_ep_rxintr = dsps_musb_clear_ep_rxintr, }; static u64 musb_dmamask = DMA_BIT_MASK(32); diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index e0a083f6ab68..13d5614f37f1 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2390,12 +2390,11 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) int is_in = usb_pipein(urb->pipe); int status = 0; u16 csr; + struct dma_channel *dma = NULL; musb_ep_select(regs, hw_end); if (is_dma_capable()) { - struct dma_channel *dma; - dma = is_in ? ep->rx_channel : ep->tx_channel; if (dma) { status = ep->musb->dma_controller->channel_abort(dma); @@ -2412,10 +2411,9 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) /* giveback saves bulk toggle */ csr = musb_h_flush_rxfifo(ep, 0); - /* REVISIT we still get an irq; should likely clear the - * endpoint's irq status here to avoid bogus irqs. - * clearing that status is platform-specific... - */ + /* clear the endpoint's irq status here to avoid bogus irqs */ + if (is_dma_capable() && dma) + musb_platform_clear_ep_rxintr(musb, ep->epnum); } else if (ep->epnum) { musb_h_tx_flush_fifo(ep); csr = musb_readw(epio, MUSB_TXCSR); diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h index f7b13fd25257..a3dcbd55e436 100644 --- a/drivers/usb/musb/musbhsdma.h +++ b/drivers/usb/musb/musbhsdma.h @@ -157,5 +157,5 @@ struct musb_dma_controller { void __iomem *base; u8 channel_count; u8 used_channels; - u8 irq; + int irq; }; diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 935bd0778bfb..c76ca5a94557 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -1417,6 +1417,7 @@ static void dr_swap(struct usbpd *pd) } pd_phy_update_roles(pd->current_dr, pd->current_pr); + dual_role_instance_changed(pd->dual_role); } @@ -2656,11 +2657,17 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role, static int usbpd_dr_prop_writeable(struct dual_role_phy_instance *dual_role, enum dual_role_property prop) { + struct usbpd *pd = dual_role_get_drvdata(dual_role); + switch (prop) { case DUAL_ROLE_PROP_MODE: + return 1; case DUAL_ROLE_PROP_DR: case DUAL_ROLE_PROP_PR: - return 1; + if (pd) + return pd->current_state == PE_SNK_READY || + pd->current_state == PE_SRC_READY; + break; default: break; } diff --git a/drivers/usb/phy/class-dual-role.c b/drivers/usb/phy/class-dual-role.c index 51fcb545a9d5..9ef889593ef5 100644 --- a/drivers/usb/phy/class-dual-role.c +++ b/drivers/usb/phy/class-dual-role.c @@ -70,15 +70,7 @@ static char *kstrdupcase(const char *str, gfp_t gfp, bool to_upper) return ret; } -static void dual_role_changed_work(struct work_struct *work) -{ - struct dual_role_phy_instance *dual_role = - container_of(work, struct dual_role_phy_instance, - changed_work); - - dev_dbg(&dual_role->dev, "%s\n", __func__); - kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE); -} +static void dual_role_changed_work(struct work_struct *work); void dual_role_instance_changed(struct dual_role_phy_instance *dual_role) { @@ -505,6 +497,17 @@ out: return ret; } +static void dual_role_changed_work(struct work_struct *work) +{ + struct dual_role_phy_instance *dual_role = + container_of(work, struct dual_role_phy_instance, + changed_work); + + dev_dbg(&dual_role->dev, "%s\n", __func__); + sysfs_update_group(&dual_role->dev.kobj, &dual_role_attr_group); + kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE); +} + /******************* Module Init ***********************************/ static int __init dual_role_class_init(void) diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c index 7b3035ff9434..1b4d742a2397 100644 --- a/drivers/usb/phy/phy-am335x-control.c +++ b/drivers/usb/phy/phy-am335x-control.c @@ -126,10 +126,12 @@ struct phy_control *am335x_get_phy_control(struct device *dev) return NULL; dev = bus_find_device(&platform_bus_type, NULL, node, match); + of_node_put(node); if (!dev) return NULL; ctrl_usb = dev_get_drvdata(dev); + put_device(dev); if (!ctrl_usb) return NULL; return &ctrl_usb->phy_ctrl; diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c index 64916f5566b5..aa11cf2f7417 100644 --- a/drivers/usb/phy/phy-msm-ssusb-qmp.c +++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,10 +27,10 @@ #include <linux/clk/msm-clk.h> #include <linux/reset.h> -enum core_ldo_levels { - CORE_LEVEL_NONE = 0, - CORE_LEVEL_MIN, - CORE_LEVEL_MAX, +enum ldo_levels { + VOLTAGE_LEVEL_NONE = 0, + VOLTAGE_LEVEL_MIN, + VOLTAGE_LEVEL_MAX, }; #define INIT_MAX_TIME_USEC 1000 @@ -40,6 +40,8 @@ enum core_ldo_levels { #define USB_SSPHY_1P2_VOL_MAX 1200000 /* uV */ #define USB_SSPHY_HPM_LOAD 23000 /* uA */ +#define USB_SSPHY_LOAD_DEFAULT -1 + /* USB3PHY_PCIE_USB3_PCS_PCS_STATUS bit */ #define PHYSTATUS BIT(6) @@ -83,6 +85,9 @@ struct msm_ssphy_qmp { int vdd_levels[3]; /* none, low, high */ struct regulator *core_ldo; int core_voltage_levels[3]; + struct regulator *fpc_redrive_ldo; + int redrive_voltage_levels[3]; + int redrive_load; struct clk *ref_clk_src; struct clk *ref_clk; struct clk *aux_clk; @@ -162,6 +167,33 @@ static void msm_ssusb_qmp_enable_autonomous(struct msm_ssphy_qmp *phy, } } +static int msm_ldo_enable(struct msm_ssphy_qmp *phy, + struct regulator *ldo, int *voltage_levels, int load) +{ + int ret = 0; + + dev_dbg(phy->phy.dev, + "ldo: min_vol:%duV max_vol:%duV\n", + voltage_levels[VOLTAGE_LEVEL_MIN], + voltage_levels[VOLTAGE_LEVEL_MAX]); + + if (load > 0) { + ret = regulator_set_load(ldo, load); + if (ret < 0) + return ret; + } + + ret = regulator_set_voltage(ldo, + voltage_levels[VOLTAGE_LEVEL_MIN], + voltage_levels[VOLTAGE_LEVEL_MAX]); + if (ret) + return ret; + + ret = regulator_enable(ldo); + + return ret; +} + static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on) { int min, rc = 0; @@ -181,74 +213,65 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on) if (!on) goto disable_regulators; - rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min], - phy->vdd_levels[2]); - if (rc) { - dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n"); - return rc; - } - - dev_dbg(phy->phy.dev, "min_vol:%d max_vol:%d\n", - phy->vdd_levels[min], phy->vdd_levels[2]); + if (phy->fpc_redrive_ldo) { + rc = msm_ldo_enable(phy, phy->fpc_redrive_ldo, + phy->redrive_voltage_levels, + phy->redrive_load); + if (rc < 0) { + dev_err(phy->phy.dev, + "enable phy->fpc_redrive_ldo failed\n"); + return rc; + } - rc = regulator_enable(phy->vdd); - if (rc) { - dev_err(phy->phy.dev, - "regulator_enable(phy->vdd) failed, ret=%d", - rc); - goto unconfig_vdd; + dev_dbg(phy->phy.dev, + "fpc redrive ldo: min_vol:%duV max_vol:%duV\n", + phy->redrive_voltage_levels[VOLTAGE_LEVEL_MIN], + phy->redrive_voltage_levels[VOLTAGE_LEVEL_MAX]); } - rc = regulator_set_load(phy->core_ldo, USB_SSPHY_HPM_LOAD); + rc = msm_ldo_enable(phy, phy->vdd, phy->vdd_levels, + USB_SSPHY_LOAD_DEFAULT); if (rc < 0) { - dev_err(phy->phy.dev, "Unable to set HPM of core_ldo\n"); - goto disable_vdd; + dev_err(phy->phy.dev, "enable phy->vdd failed\n"); + goto disable_fpc_redrive; } - rc = regulator_set_voltage(phy->core_ldo, - phy->core_voltage_levels[CORE_LEVEL_MIN], - phy->core_voltage_levels[CORE_LEVEL_MAX]); - if (rc) { - dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n"); - goto put_core_ldo_lpm; - } + dev_dbg(phy->phy.dev, + "vdd ldo: min_vol:%duV max_vol:%duV\n", + phy->vdd_levels[VOLTAGE_LEVEL_MIN], + phy->vdd_levels[VOLTAGE_LEVEL_MAX]); - rc = regulator_enable(phy->core_ldo); - if (rc) { - dev_err(phy->phy.dev, "Unable to enable core_ldo\n"); - goto unset_core_ldo; + rc = msm_ldo_enable(phy, phy->core_ldo, phy->core_voltage_levels, + USB_SSPHY_HPM_LOAD); + if (rc < 0) { + dev_err(phy->phy.dev, "enable phy->core_ldo failed\n"); + goto disable_vdd; } + dev_dbg(phy->phy.dev, + "core ldo: min_vol:%duV max_vol:%duV\n", + phy->core_voltage_levels[VOLTAGE_LEVEL_MIN], + phy->core_voltage_levels[VOLTAGE_LEVEL_MAX]); + return 0; disable_regulators: rc = regulator_disable(phy->core_ldo); if (rc) - dev_err(phy->phy.dev, "Unable to disable core_ldo\n"); - -unset_core_ldo: - rc = regulator_set_voltage(phy->core_ldo, - phy->core_voltage_levels[CORE_LEVEL_NONE], - phy->core_voltage_levels[CORE_LEVEL_MAX]); - if (rc) - dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n"); - -put_core_ldo_lpm: - rc = regulator_set_load(phy->core_ldo, 0); - if (rc < 0) - dev_err(phy->phy.dev, "Unable to set LPM of core_ldo\n"); + dev_err(phy->phy.dev, "disable phy->core_ldo failed\n"); disable_vdd: rc = regulator_disable(phy->vdd); if (rc) - dev_err(phy->phy.dev, "regulator_disable(phy->vdd) failed, ret=%d", - rc); + dev_err(phy->phy.dev, "disable phy->vdd failed\n"); -unconfig_vdd: - rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min], - phy->vdd_levels[2]); - if (rc) - dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n"); +disable_fpc_redrive: + if (phy->fpc_redrive_ldo) { + rc = regulator_disable(phy->fpc_redrive_ldo); + if (rc) + dev_err(phy->phy.dev, + "disable phy->fpc_redrive_ldo failed\n"); + } return rc < 0 ? rc : 0; } @@ -307,10 +330,6 @@ static int msm_ssphy_qmp_init(struct usb_phy *uphy) phy->clk_enabled = true; } - /* select usb3 phy mode */ - if (phy->tcsr_usb3_dp_phymode) - writel_relaxed(0x0, phy->tcsr_usb3_dp_phymode); - writel_relaxed(0x01, phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]); @@ -386,6 +405,10 @@ static int msm_ssphy_qmp_reset(struct usb_phy *uphy) goto deassert_phy_phy_reset; } + /* select usb3 phy mode */ + if (phy->tcsr_usb3_dp_phymode) + writel_relaxed(0x0, phy->tcsr_usb3_dp_phymode); + /* Deassert USB3 PHY CSR reset */ ret = reset_control_deassert(phy->phy_reset); if (ret) { @@ -683,9 +706,9 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev) } /* Set default core voltage values */ - phy->core_voltage_levels[CORE_LEVEL_NONE] = 0; - phy->core_voltage_levels[CORE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN; - phy->core_voltage_levels[CORE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX; + phy->core_voltage_levels[VOLTAGE_LEVEL_NONE] = 0; + phy->core_voltage_levels[VOLTAGE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN; + phy->core_voltage_levels[VOLTAGE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX; if (of_get_property(dev->of_node, "qcom,core-voltage-level", &len) && len == sizeof(phy->core_voltage_levels)) { @@ -729,6 +752,39 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev) goto err; } + phy->fpc_redrive_ldo = devm_regulator_get_optional(dev, "fpc-redrive"); + if (IS_ERR(phy->fpc_redrive_ldo)) { + phy->fpc_redrive_ldo = NULL; + dev_dbg(dev, "no FPC re-drive ldo regulator\n"); + } else { + if (of_get_property(dev->of_node, + "qcom,redrive-voltage-level", &len) && + len == sizeof(phy->redrive_voltage_levels)) { + ret = of_property_read_u32_array(dev->of_node, + "qcom,redrive-voltage-level", + (u32 *) phy->redrive_voltage_levels, + len / sizeof(u32)); + if (ret) { + dev_err(dev, + "err qcom,redrive-voltage-level\n"); + goto err; + } + } else { + ret = -EINVAL; + dev_err(dev, "err inputs for redrive-voltage-level\n"); + goto err; + } + + ret = of_property_read_u32(dev->of_node, "qcom,redrive-load", + &phy->redrive_load); + if (ret) { + dev_err(&pdev->dev, "unable to read redrive load\n"); + goto err; + } + + dev_dbg(dev, "Get FPC re-drive ldo regulator\n"); + } + phy->ref_clk_src = devm_clk_get(dev, "ref_clk_src"); if (IS_ERR(phy->ref_clk_src)) phy->ref_clk_src = NULL; diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 1532cde8a437..7812052dc700 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -99,10 +99,17 @@ static int ark3116_read_reg(struct usb_serial *serial, usb_rcvctrlpipe(serial->dev, 0), 0xfe, 0xc0, 0, reg, buf, 1, ARK_TIMEOUT); - if (result < 0) + if (result < 1) { + dev_err(&serial->interface->dev, + "failed to read register %u: %d\n", + reg, result); + if (result >= 0) + result = -EIO; + return result; - else - return buf[0]; + } + + return buf[0]; } static inline int calc_divisor(int bps) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index c73808f095bb..71133d96f97d 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -99,6 +99,8 @@ static int ch341_control_out(struct usb_device *dev, u8 request, r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, value, index, NULL, 0, DEFAULT_TIMEOUT); + if (r < 0) + dev_err(&dev->dev, "failed to send control message: %d\n", r); return r; } @@ -116,7 +118,20 @@ static int ch341_control_in(struct usb_device *dev, r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, value, index, buf, bufsize, DEFAULT_TIMEOUT); - return r; + if (r < bufsize) { + if (r >= 0) { + dev_err(&dev->dev, + "short control message received (%d < %u)\n", + r, bufsize); + r = -EIO; + } + + dev_err(&dev->dev, "failed to receive control message: %d\n", + r); + return r; + } + + return 0; } static int ch341_set_baudrate(struct usb_device *dev, @@ -158,9 +173,9 @@ static int ch341_set_handshake(struct usb_device *dev, u8 control) static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) { + const unsigned int size = 2; char *buffer; int r; - const unsigned size = 8; unsigned long flags; buffer = kmalloc(size, GFP_KERNEL); @@ -171,14 +186,9 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) if (r < 0) goto out; - /* setup the private status if available */ - if (r == 2) { - r = 0; - spin_lock_irqsave(&priv->lock, flags); - priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT; - spin_unlock_irqrestore(&priv->lock, flags); - } else - r = -EPROTO; + spin_lock_irqsave(&priv->lock, flags); + priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT; + spin_unlock_irqrestore(&priv->lock, flags); out: kfree(buffer); return r; @@ -188,9 +198,9 @@ out: kfree(buffer); static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) { + const unsigned int size = 2; char *buffer; int r; - const unsigned size = 8; buffer = kmalloc(size, GFP_KERNEL); if (!buffer) @@ -253,7 +263,6 @@ static int ch341_port_probe(struct usb_serial_port *port) spin_lock_init(&priv->lock); priv->baud_rate = DEFAULT_BAUD_RATE; - priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR; r = ch341_configure(port->serial->dev, priv); if (r < 0) @@ -315,7 +324,7 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) r = ch341_configure(serial->dev, priv); if (r) - goto out; + return r; if (tty) ch341_set_termios(tty, port, NULL); @@ -325,12 +334,19 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) if (r) { dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n", __func__, r); - goto out; + return r; } r = usb_serial_generic_open(tty, port); + if (r) + goto err_kill_interrupt_urb; -out: return r; + return 0; + +err_kill_interrupt_urb: + usb_kill_urb(port->interrupt_in_urb); + + return r; } /* Old_termios contains the original termios settings and @@ -345,26 +361,25 @@ static void ch341_set_termios(struct tty_struct *tty, baud_rate = tty_get_baud_rate(tty); - priv->baud_rate = baud_rate; - if (baud_rate) { - spin_lock_irqsave(&priv->lock, flags); - priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS); - spin_unlock_irqrestore(&priv->lock, flags); + priv->baud_rate = baud_rate; ch341_set_baudrate(port->serial->dev, priv); - } else { - spin_lock_irqsave(&priv->lock, flags); - priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS); - spin_unlock_irqrestore(&priv->lock, flags); } - ch341_set_handshake(port->serial->dev, priv->line_control); - /* Unimplemented: * (cflag & CSIZE) : data bits [5, 8] * (cflag & PARENB) : parity {NONE, EVEN, ODD} * (cflag & CSTOPB) : stop bits [1, 2] */ + + spin_lock_irqsave(&priv->lock, flags); + if (C_BAUD(tty) == B0) + priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS); + else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) + priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS); + spin_unlock_irqrestore(&priv->lock, flags); + + ch341_set_handshake(port->serial->dev, priv->line_control); } static void ch341_break_ctl(struct tty_struct *tty, int break_state) @@ -539,14 +554,23 @@ static int ch341_tiocmget(struct tty_struct *tty) static int ch341_reset_resume(struct usb_serial *serial) { - struct ch341_private *priv; - - priv = usb_get_serial_port_data(serial->port[0]); + struct usb_serial_port *port = serial->port[0]; + struct ch341_private *priv = usb_get_serial_port_data(port); + int ret; /* reconfigure ch341 serial port after bus-reset */ ch341_configure(serial->dev, priv); - return 0; + if (test_bit(ASYNCB_INITIALIZED, &port->port.flags)) { + ret = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); + if (ret) { + dev_err(&port->dev, "failed to submit interrupt urb: %d\n", + ret); + return ret; + } + } + + return usb_serial_generic_resume(serial); } static struct usb_serial_driver ch341_device = { diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index fe7452f0f38a..33cec50978b8 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -171,6 +171,8 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */ { USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */ { USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */ + { USB_DEVICE(0x1901, 0x0195) }, /* GE B850/B650/B450 CP2104 DP UART interface */ + { USB_DEVICE(0x1901, 0x0196) }, /* GE B850 CP2105 DP UART interface */ { USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */ { USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */ { USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */ diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 2916dea3ede8..8948f375e75d 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -50,6 +50,7 @@ #define CYBERJACK_PRODUCT_ID 0x0100 /* Function prototypes */ +static int cyberjack_attach(struct usb_serial *serial); static int cyberjack_port_probe(struct usb_serial_port *port); static int cyberjack_port_remove(struct usb_serial_port *port); static int cyberjack_open(struct tty_struct *tty, @@ -77,6 +78,7 @@ static struct usb_serial_driver cyberjack_device = { .description = "Reiner SCT Cyberjack USB card reader", .id_table = id_table, .num_ports = 1, + .attach = cyberjack_attach, .port_probe = cyberjack_port_probe, .port_remove = cyberjack_port_remove, .open = cyberjack_open, @@ -100,6 +102,14 @@ struct cyberjack_private { short wrsent; /* Data already sent */ }; +static int cyberjack_attach(struct usb_serial *serial) +{ + if (serial->num_bulk_out < serial->num_ports) + return -ENODEV; + + return 0; +} + static int cyberjack_port_probe(struct usb_serial_port *port) { struct cyberjack_private *priv; diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 3df7b7ec178e..e0b1fe2f60e1 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1483,16 +1483,20 @@ static int digi_read_oob_callback(struct urb *urb) struct usb_serial *serial = port->serial; struct tty_struct *tty; struct digi_port *priv = usb_get_serial_port_data(port); + unsigned char *buf = urb->transfer_buffer; int opcode, line, status, val; int i; unsigned int rts; + if (urb->actual_length < 4) + return -1; + /* handle each oob command */ - for (i = 0; i < urb->actual_length - 3;) { - opcode = ((unsigned char *)urb->transfer_buffer)[i++]; - line = ((unsigned char *)urb->transfer_buffer)[i++]; - status = ((unsigned char *)urb->transfer_buffer)[i++]; - val = ((unsigned char *)urb->transfer_buffer)[i++]; + for (i = 0; i < urb->actual_length - 3; i += 4) { + opcode = buf[i]; + line = buf[i + 1]; + status = buf[i + 2]; + val = buf[i + 3]; dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n", opcode, line, status, val); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index d3d6ec455151..19a98116c2ab 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1807,8 +1807,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) mutex_init(&priv->cfg_lock); - priv->flags = ASYNC_LOW_LATENCY; - if (quirk && quirk->port_probe) quirk->port_probe(priv); @@ -2072,6 +2070,20 @@ static int ftdi_process_packet(struct usb_serial_port *port, priv->prev_status = status; } + /* save if the transmitter is empty or not */ + if (packet[1] & FTDI_RS_TEMT) + priv->transmit_empty = 1; + else + priv->transmit_empty = 0; + + len -= 2; + if (!len) + return 0; /* status only */ + + /* + * Break and error status must only be processed for packets with + * data payload to avoid over-reporting. + */ flag = TTY_NORMAL; if (packet[1] & FTDI_RS_ERR_MASK) { /* Break takes precedence over parity, which takes precedence @@ -2094,15 +2106,6 @@ static int ftdi_process_packet(struct usb_serial_port *port, } } - /* save if the transmitter is empty or not */ - if (packet[1] & FTDI_RS_TEMT) - priv->transmit_empty = 1; - else - priv->transmit_empty = 0; - - len -= 2; - if (!len) - return 0; /* status only */ port->icount.rx += len; ch = packet + 2; @@ -2433,8 +2436,12 @@ static int ftdi_get_modem_status(struct usb_serial_port *port, FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, 0, priv->interface, buf, len, WDR_TIMEOUT); - if (ret < 0) { + + /* NOTE: We allow short responses and handle that below. */ + if (ret < 1) { dev_err(&port->dev, "failed to get modem status: %d\n", ret); + if (ret >= 0) + ret = -EIO; ret = usb_translate_errors(ret); goto out; } diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index db591d19d416..37d0e8cc7af6 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1044,6 +1044,7 @@ static int garmin_write_bulk(struct usb_serial_port *port, "%s - usb_submit_urb(write bulk) failed with status = %d\n", __func__, status); count = status; + kfree(buffer); } /* we are done with this urb, so let the host driver diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 1947ea0e0988..b63a6c3899c5 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2761,6 +2761,11 @@ static int edge_startup(struct usb_serial *serial) EDGE_COMPATIBILITY_MASK1, EDGE_COMPATIBILITY_MASK2 }; + if (serial->num_bulk_in < 1 || serial->num_interrupt_in < 1) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + dev = serial->dev; /* create our private serial structure */ diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index fce82fd79f77..f1a8fdcd8674 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1499,8 +1499,7 @@ static int do_boot_mode(struct edgeport_serial *serial, dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__); - /* return an error on purpose */ - return -ENODEV; + return 1; } stayinbootmode: @@ -1508,7 +1507,7 @@ stayinbootmode: dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__); serial->product_info.TiMode = TI_MODE_BOOT; - return 0; + return 1; } static int ti_do_config(struct edgeport_port *port, int feature, int on) @@ -1675,6 +1674,12 @@ static void edge_interrupt_callback(struct urb *urb) function = TIUMP_GET_FUNC_FROM_CODE(data[0]); dev_dbg(dev, "%s - port_number %d, function %d, info 0x%x\n", __func__, port_number, function, data[1]); + + if (port_number >= edge_serial->serial->num_ports) { + dev_err(dev, "bad port number %d\n", port_number); + goto exit; + } + port = edge_serial->serial->port[port_number]; edge_port = usb_get_serial_port_data(port); if (!edge_port) { @@ -1756,7 +1761,7 @@ static void edge_bulk_in_callback(struct urb *urb) port_number = edge_port->port->port_number; - if (edge_port->lsr_event) { + if (urb->actual_length > 0 && edge_port->lsr_event) { edge_port->lsr_event = 0; dev_dbg(dev, "%s ===== Port %u LSR Status = %02x, Data = %02x ======\n", __func__, port_number, edge_port->lsr_mask, *data); @@ -2549,6 +2554,13 @@ static int edge_startup(struct usb_serial *serial) int status; u16 product_id; + /* Make sure we have the required endpoints when in download mode. */ + if (serial->interface->cur_altsetting->desc.bNumEndpoints > 1) { + if (serial->num_bulk_in < serial->num_ports || + serial->num_bulk_out < serial->num_ports) + return -ENODEV; + } + /* create our private serial structure */ edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); if (!edge_serial) @@ -2556,14 +2568,18 @@ static int edge_startup(struct usb_serial *serial) mutex_init(&edge_serial->es_lock); edge_serial->serial = serial; + INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work); usb_set_serial_data(serial, edge_serial); status = download_fw(edge_serial); - if (status) { + if (status < 0) { kfree(edge_serial); return status; } + if (status > 0) + return 1; /* bind but do not register any ports */ + product_id = le16_to_cpu( edge_serial->serial->dev->descriptor.idProduct); @@ -2575,7 +2591,6 @@ static int edge_startup(struct usb_serial *serial) } } - INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work); edge_heartbeat_schedule(edge_serial); return 0; @@ -2583,6 +2598,9 @@ static int edge_startup(struct usb_serial *serial) static void edge_disconnect(struct usb_serial *serial) { + struct edgeport_serial *edge_serial = usb_get_serial_data(serial); + + cancel_delayed_work_sync(&edge_serial->heartbeat_work); } static void edge_release(struct usb_serial *serial) diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 5ad4a0fb4b26..7ed7d33d6c10 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -68,6 +68,16 @@ struct iuu_private { u32 clk; }; +static int iuu_attach(struct usb_serial *serial) +{ + unsigned char num_ports = serial->num_ports; + + if (serial->num_bulk_in < num_ports || serial->num_bulk_out < num_ports) + return -ENODEV; + + return 0; +} + static int iuu_port_probe(struct usb_serial_port *port) { struct iuu_private *priv; @@ -1196,6 +1206,7 @@ static struct usb_serial_driver iuu_device = { .tiocmset = iuu_tiocmset, .set_termios = iuu_set_termios, .init_termios = iuu_init_termios, + .attach = iuu_attach, .port_probe = iuu_port_probe, .port_remove = iuu_port_remove, }; diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 4f7e072e4e00..930be98d59b3 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -699,6 +699,19 @@ MODULE_FIRMWARE("keyspan_pda/keyspan_pda.fw"); MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw"); #endif +static int keyspan_pda_attach(struct usb_serial *serial) +{ + unsigned char num_ports = serial->num_ports; + + if (serial->num_bulk_out < num_ports || + serial->num_interrupt_in < num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int keyspan_pda_port_probe(struct usb_serial_port *port) { @@ -776,6 +789,7 @@ static struct usb_serial_driver keyspan_pda_device = { .break_ctl = keyspan_pda_break_ctl, .tiocmget = keyspan_pda_tiocmget, .tiocmset = keyspan_pda_tiocmset, + .attach = keyspan_pda_attach, .port_probe = keyspan_pda_port_probe, .port_remove = keyspan_pda_port_remove, }; diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index e020ad28a00c..83c823d32ff9 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -192,10 +192,11 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, status_buf, KLSI_STATUSBUF_LEN, 10000 ); - if (rc < 0) - dev_err(&port->dev, "Reading line status failed (error = %d)\n", - rc); - else { + if (rc != KLSI_STATUSBUF_LEN) { + dev_err(&port->dev, "reading line status failed: %d\n", rc); + if (rc >= 0) + rc = -EIO; + } else { status = get_unaligned_le16(status_buf); dev_info(&port->serial->dev->dev, "read status %x %x\n", @@ -296,7 +297,7 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) rc = usb_serial_generic_open(tty, port); if (rc) { retval = rc; - goto exit; + goto err_free_cfg; } rc = usb_control_msg(port->serial->dev, @@ -311,21 +312,38 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) if (rc < 0) { dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc); retval = rc; + goto err_generic_close; } else dev_dbg(&port->dev, "%s - enabled reading\n", __func__); rc = klsi_105_get_line_state(port, &line_state); - if (rc >= 0) { - spin_lock_irqsave(&priv->lock, flags); - priv->line_state = line_state; - spin_unlock_irqrestore(&priv->lock, flags); - dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state); - retval = 0; - } else + if (rc < 0) { retval = rc; + goto err_disable_read; + } + + spin_lock_irqsave(&priv->lock, flags); + priv->line_state = line_state; + spin_unlock_irqrestore(&priv->lock, flags); + dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, + line_state); + + return 0; -exit: +err_disable_read: + usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + KL5KUSB105A_SIO_CONFIGURE, + USB_TYPE_VENDOR | USB_DIR_OUT, + KL5KUSB105A_SIO_CONFIGURE_READ_OFF, + 0, /* index */ + NULL, 0, + KLSI_TIMEOUT); +err_generic_close: + usb_serial_generic_close(port); +err_free_cfg: kfree(cfg); + return retval; } diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 2363654cafc9..813035f51fe7 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -51,6 +51,7 @@ /* Function prototypes */ +static int kobil_attach(struct usb_serial *serial); static int kobil_port_probe(struct usb_serial_port *probe); static int kobil_port_remove(struct usb_serial_port *probe); static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port); @@ -86,6 +87,7 @@ static struct usb_serial_driver kobil_device = { .description = "KOBIL USB smart card terminal", .id_table = id_table, .num_ports = 1, + .attach = kobil_attach, .port_probe = kobil_port_probe, .port_remove = kobil_port_remove, .ioctl = kobil_ioctl, @@ -113,6 +115,16 @@ struct kobil_private { }; +static int kobil_attach(struct usb_serial *serial) +{ + if (serial->num_interrupt_out < serial->num_ports) { + dev_err(&serial->interface->dev, "missing interrupt-out endpoint\n"); + return -ENODEV; + } + + return 0; +} + static int kobil_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 63db004af21f..e56cdb436de3 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -65,8 +65,6 @@ struct moschip_port { struct urb *write_urb_pool[NUM_URBS]; }; -static struct usb_serial_driver moschip7720_2port_driver; - #define USB_VENDOR_ID_MOSCHIP 0x9710 #define MOSCHIP_DEVICE_ID_7720 0x7720 #define MOSCHIP_DEVICE_ID_7715 0x7715 @@ -970,25 +968,6 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) tty_port_tty_wakeup(&mos7720_port->port->port); } -/* - * mos77xx_probe - * this function installs the appropriate read interrupt endpoint callback - * depending on whether the device is a 7720 or 7715, thus avoiding costly - * run-time checks in the high-frequency callback routine itself. - */ -static int mos77xx_probe(struct usb_serial *serial, - const struct usb_device_id *id) -{ - if (id->idProduct == MOSCHIP_DEVICE_ID_7715) - moschip7720_2port_driver.read_int_callback = - mos7715_interrupt_callback; - else - moschip7720_2port_driver.read_int_callback = - mos7720_interrupt_callback; - - return 0; -} - static int mos77xx_calc_num_ports(struct usb_serial *serial) { u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); @@ -1920,6 +1899,11 @@ static int mos7720_startup(struct usb_serial *serial) u16 product; int ret_val; + if (serial->num_bulk_in < 2 || serial->num_bulk_out < 2) { + dev_err(&serial->interface->dev, "missing bulk endpoints\n"); + return -ENODEV; + } + product = le16_to_cpu(serial->dev->descriptor.idProduct); dev = serial->dev; @@ -1944,19 +1928,18 @@ static int mos7720_startup(struct usb_serial *serial) tmp->interrupt_in_endpointAddress; serial->port[1]->interrupt_in_urb = NULL; serial->port[1]->interrupt_in_buffer = NULL; + + if (serial->port[0]->interrupt_in_urb) { + struct urb *urb = serial->port[0]->interrupt_in_urb; + + urb->complete = mos7715_interrupt_callback; + } } /* setting configuration feature to one */ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), (__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000); - /* start the interrupt urb */ - ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL); - if (ret_val) - dev_err(&dev->dev, - "%s - Error %d submitting control urb\n", - __func__, ret_val); - #ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT if (product == MOSCHIP_DEVICE_ID_7715) { ret_val = mos7715_parport_init(serial); @@ -1964,6 +1947,13 @@ static int mos7720_startup(struct usb_serial *serial) return ret_val; } #endif + /* start the interrupt urb */ + ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL); + if (ret_val) { + dev_err(&dev->dev, "failed to submit interrupt urb: %d\n", + ret_val); + } + /* LSR For Port 1 */ read_mos_reg(serial, 0, MOS7720_LSR, &data); dev_dbg(&dev->dev, "LSR:%x\n", data); @@ -1973,6 +1963,8 @@ static int mos7720_startup(struct usb_serial *serial) static void mos7720_release(struct usb_serial *serial) { + usb_kill_urb(serial->port[0]->interrupt_in_urb); + #ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT /* close the parallel port */ @@ -2056,7 +2048,6 @@ static struct usb_serial_driver moschip7720_2port_driver = { .close = mos7720_close, .throttle = mos7720_throttle, .unthrottle = mos7720_unthrottle, - .probe = mos77xx_probe, .attach = mos7720_startup, .release = mos7720_release, .port_probe = mos7720_port_probe, @@ -2070,7 +2061,7 @@ static struct usb_serial_driver moschip7720_2port_driver = { .chars_in_buffer = mos7720_chars_in_buffer, .break_ctl = mos7720_break, .read_bulk_callback = mos7720_bulk_in_callback, - .read_int_callback = NULL /* dynamically assigned in probe() */ + .read_int_callback = mos7720_interrupt_callback, }; static struct usb_serial_driver * const serial_drivers[] = { diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 7f3ddd7ba2ce..d17685cc00c9 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1024,6 +1024,7 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) * (can't set it up in mos7840_startup as the structures * * were not set up at that time.) */ if (port0->open_ports == 1) { + /* FIXME: Buffer never NULL, so URB is not submitted. */ if (serial->port[0]->interrupt_in_buffer == NULL) { /* set up interrupt urb */ usb_fill_int_urb(serial->port[0]->interrupt_in_urb, @@ -2116,6 +2117,18 @@ static int mos7840_calc_num_ports(struct usb_serial *serial) return mos7840_num_ports; } +static int mos7840_attach(struct usb_serial *serial) +{ + if (serial->num_bulk_in < serial->num_ports || + serial->num_bulk_out < serial->num_ports || + serial->num_interrupt_in < 1) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int mos7840_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; @@ -2391,6 +2404,7 @@ static struct usb_serial_driver moschip7840_4port_device = { .tiocmset = mos7840_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, .get_icount = usb_serial_generic_get_icount, + .attach = mos7840_attach, .port_probe = mos7840_port_probe, .port_remove = mos7840_port_remove, .read_bulk_callback = mos7840_bulk_in_callback, diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index f6c6900bccf0..76564b3bebb9 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -38,6 +38,7 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int omninet_write_room(struct tty_struct *tty); static void omninet_disconnect(struct usb_serial *serial); +static int omninet_attach(struct usb_serial *serial); static int omninet_port_probe(struct usb_serial_port *port); static int omninet_port_remove(struct usb_serial_port *port); @@ -56,6 +57,7 @@ static struct usb_serial_driver zyxel_omninet_device = { .description = "ZyXEL - omni.net lcd plus usb", .id_table = id_table, .num_ports = 1, + .attach = omninet_attach, .port_probe = omninet_port_probe, .port_remove = omninet_port_remove, .open = omninet_open, @@ -104,6 +106,17 @@ struct omninet_data { __u8 od_outseq; /* Sequence number for bulk_out URBs */ }; +static int omninet_attach(struct usb_serial *serial) +{ + /* The second bulk-out endpoint is used for writing. */ + if (serial->num_bulk_out < 2) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int omninet_port_probe(struct usb_serial_port *port) { struct omninet_data *od; @@ -129,12 +142,6 @@ static int omninet_port_remove(struct usb_serial_port *port) static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; - struct usb_serial_port *wport; - - wport = serial->port[1]; - tty_port_tty_set(&wport->port, tty); - return usb_serial_generic_open(tty, port); } diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 4b7bfb394a32..64bf258e7e00 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -142,7 +142,7 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port) usb_clear_halt(port->serial->dev, port->read_urb->pipe); res = usb_serial_generic_open(tty, port); - if (!res) + if (res) return res; /* Request CTS line state, sometimes during opening the current diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 9894e341c6ac..42cc72e54c05 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -268,6 +268,8 @@ static void option_instat_callback(struct urb *urb); #define TELIT_PRODUCT_CC864_SINGLE 0x1006 #define TELIT_PRODUCT_DE910_DUAL 0x1010 #define TELIT_PRODUCT_UE910_V2 0x1012 +#define TELIT_PRODUCT_LE922_USBCFG1 0x1040 +#define TELIT_PRODUCT_LE922_USBCFG2 0x1041 #define TELIT_PRODUCT_LE922_USBCFG0 0x1042 #define TELIT_PRODUCT_LE922_USBCFG3 0x1043 #define TELIT_PRODUCT_LE922_USBCFG5 0x1045 @@ -1210,6 +1212,10 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0), .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1), + .driver_info = (kernel_ulong_t)&telit_le910_blacklist }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG2), + .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG3), .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff), @@ -1989,6 +1995,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) }, { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) }, + { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */ .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */ @@ -2000,6 +2007,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD200, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_6802, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD300, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, 0xff, 0xff, 0xff) }, /* HP lt2523 (Novatel E371) */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index a4b88bc038b6..b8bf52bf7a94 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -134,6 +134,7 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty); static int oti6858_tiocmget(struct tty_struct *tty); static int oti6858_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +static int oti6858_attach(struct usb_serial *serial); static int oti6858_port_probe(struct usb_serial_port *port); static int oti6858_port_remove(struct usb_serial_port *port); @@ -158,6 +159,7 @@ static struct usb_serial_driver oti6858_device = { .write_bulk_callback = oti6858_write_bulk_callback, .write_room = oti6858_write_room, .chars_in_buffer = oti6858_chars_in_buffer, + .attach = oti6858_attach, .port_probe = oti6858_port_probe, .port_remove = oti6858_port_remove, }; @@ -324,6 +326,20 @@ static void send_data(struct work_struct *work) usb_serial_port_softint(port); } +static int oti6858_attach(struct usb_serial *serial) +{ + unsigned char num_ports = serial->num_ports; + + if (serial->num_bulk_in < num_ports || + serial->num_bulk_out < num_ports || + serial->num_interrupt_in < num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int oti6858_port_probe(struct usb_serial_port *port) { struct oti6858_private *priv; diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ae682e4eeaef..1db4b61bdf7b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -49,6 +49,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, + { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID2) }, { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) }, @@ -220,9 +221,17 @@ static int pl2303_probe(struct usb_serial *serial, static int pl2303_startup(struct usb_serial *serial) { struct pl2303_serial_private *spriv; + unsigned char num_ports = serial->num_ports; enum pl2303_type type = TYPE_01; unsigned char *buf; + if (serial->num_bulk_in < num_ports || + serial->num_bulk_out < num_ports || + serial->num_interrupt_in < num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); if (!spriv) return -ENOMEM; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index e3b7af8adfb7..09d9be88209e 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -27,6 +27,7 @@ #define ATEN_VENDOR_ID 0x0557 #define ATEN_VENDOR_ID2 0x0547 #define ATEN_PRODUCT_ID 0x2008 +#define ATEN_PRODUCT_ID2 0x2118 #define IODATA_VENDOR_ID 0x04bb #define IODATA_PRODUCT_ID 0x0a03 diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 1bc6089b9008..696458db7e3c 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -124,6 +124,7 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */ {USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */ {USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */ + {USB_DEVICE(0x413c, 0x81a6)}, /* Dell DW5570 QDL (MC8805) */ {USB_DEVICE(0x1199, 0x68a4)}, /* Sierra Wireless QDL */ {USB_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */ {USB_DEVICE(0x1199, 0x68a8)}, /* Sierra Wireless QDL */ diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index b18974cbd995..a3ed07c58754 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -408,16 +408,12 @@ static void qt2_close(struct usb_serial_port *port) { struct usb_serial *serial; struct qt2_port_private *port_priv; - unsigned long flags; int i; serial = port->serial; port_priv = usb_get_serial_port_data(port); - spin_lock_irqsave(&port_priv->urb_lock, flags); usb_kill_urb(port_priv->write_urb); - port_priv->urb_in_use = false; - spin_unlock_irqrestore(&port_priv->urb_lock, flags); /* flush the port transmit buffer */ i = usb_control_msg(serial->dev, diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index b2dff0f14743..236ea43f7815 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -205,6 +205,11 @@ static void safe_process_read_urb(struct urb *urb) if (!safe) goto out; + if (length < 2) { + dev_err(&port->dev, "malformed packet\n"); + return; + } + fcs = fcs_compute10(data, length, CRC10_INITFCS); if (fcs) { dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs); diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index ef0dbf0703c5..ddfd787c461c 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -154,6 +154,19 @@ static int spcp8x5_probe(struct usb_serial *serial, return 0; } +static int spcp8x5_attach(struct usb_serial *serial) +{ + unsigned char num_ports = serial->num_ports; + + if (serial->num_bulk_in < num_ports || + serial->num_bulk_out < num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + return -ENODEV; + } + + return 0; +} + static int spcp8x5_port_probe(struct usb_serial_port *port) { const struct usb_device_id *id = usb_get_serial_data(port->serial); @@ -219,11 +232,17 @@ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_UART_STATUS, GET_UART_STATUS_TYPE, 0, GET_UART_STATUS_MSR, buf, 1, 100); - if (ret < 0) + if (ret < 1) { dev_err(&port->dev, "failed to get modem status: %d\n", ret); + if (ret >= 0) + ret = -EIO; + goto out; + } dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x\n", ret, *buf); *status = *buf; + ret = 0; +out: kfree(buf); return ret; @@ -477,6 +496,7 @@ static struct usb_serial_driver spcp8x5_device = { .tiocmget = spcp8x5_tiocmget, .tiocmset = spcp8x5_tiocmset, .probe = spcp8x5_probe, + .attach = spcp8x5_attach, .port_probe = spcp8x5_port_probe, .port_remove = spcp8x5_port_remove, }; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 2694df2f4559..535fcfafc097 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -339,6 +339,13 @@ static int ti_startup(struct usb_serial *serial) goto free_tdev; } + if (serial->num_bulk_in < serial->num_ports || + serial->num_bulk_out < serial->num_ports) { + dev_err(&serial->interface->dev, "missing endpoints\n"); + status = -ENODEV; + goto free_tdev; + } + return 0; free_tdev: diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 7ffe4209067b..640a2e2ec04d 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2135,6 +2135,13 @@ UNUSUAL_DEV( 0x22b8, 0x3010, 0x0001, 0x0001, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), +/* Reported-by George Cherian <george.cherian@cavium.com> */ +UNUSUAL_DEV(0x152d, 0x9561, 0x0000, 0x9999, + "JMicron", + "JMS56x", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), + /* * Patch by Constantin Baranov <const@tltsu.ru> * Report by Andreas Koenecke. diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c index 021755f7f32d..a04fa896361a 100644 --- a/drivers/video/fbdev/core/fbcmap.c +++ b/drivers/video/fbdev/core/fbcmap.c @@ -163,17 +163,18 @@ void fb_dealloc_cmap(struct fb_cmap *cmap) int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to) { - int tooff = 0, fromoff = 0; - int size; + unsigned int tooff = 0, fromoff = 0; + size_t size; if (to->start > from->start) fromoff = to->start - from->start; else tooff = from->start - to->start; - size = to->len - tooff; - if (size > (int) (from->len - fromoff)) - size = from->len - fromoff; - if (size <= 0) + if (fromoff >= from->len || tooff >= to->len) + return -EINVAL; + + size = min_t(size_t, to->len - tooff, from->len - fromoff); + if (size == 0) return -EINVAL; size *= sizeof(u16); @@ -187,22 +188,22 @@ int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to) int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to) { - u32 tooff = 0, fromoff = 0; - u32 size; + unsigned int tooff = 0, fromoff = 0; + size_t size; if (to->start > from->start) fromoff = to->start - from->start; else tooff = from->start - to->start; - if ((to->len <= tooff) || (from->len <= fromoff)) + if (fromoff >= from->len || tooff >= to->len) return -EINVAL; - size = to->len - tooff; - if (size > (from->len - fromoff)) - size = from->len - fromoff; - size *= sizeof(u16); + size = min_t(size_t, to->len - tooff, from->len - fromoff); if (size == 0) return -EINVAL; + size *= sizeof(u16); + + if (copy_to_user(to->red+tooff, from->red+fromoff, size)) return -EFAULT; diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index d9a4bd91f3eb..796246a856b4 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -210,6 +210,15 @@ enum mdss_mdp_pipe_type { MDSS_MDP_PIPE_TYPE_MAX, }; +enum mdss_mdp_intf_index { + MDSS_MDP_NO_INTF, + MDSS_MDP_INTF0, + MDSS_MDP_INTF1, + MDSS_MDP_INTF2, + MDSS_MDP_INTF3, + MDSS_MDP_MAX_INTF +}; + struct reg_bus_client { char name[MAX_CLIENT_NAME_LEN]; short usecase_ndx; diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c index 14d998d14eeb..17644e3556b6 100644 --- a/drivers/video/fbdev/msm/mdss_compat_utils.c +++ b/drivers/video/fbdev/msm/mdss_compat_utils.c @@ -125,6 +125,7 @@ static void __copy_atomic_commit_struct(struct mdp_layer_commit *commit, commit32->commit_v1.input_layer_cnt; commit->commit_v1.left_roi = commit32->commit_v1.left_roi; commit->commit_v1.right_roi = commit32->commit_v1.right_roi; + commit->commit_v1.bl_level = commit32->commit_v1.bl_level; memcpy(&commit->commit_v1.reserved, &commit32->commit_v1.reserved, sizeof(commit32->commit_v1.reserved)); } diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.h b/drivers/video/fbdev/msm/mdss_compat_utils.h index 626792925cb6..4f44cd1c9471 100644 --- a/drivers/video/fbdev/msm/mdss_compat_utils.h +++ b/drivers/video/fbdev/msm/mdss_compat_utils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -19,9 +19,9 @@ * To allow proper structure padding for 64bit/32bit target */ #ifdef __LP64 -#define MDP_LAYER_COMMIT_V1_PAD 3 +#define MDP_LAYER_COMMIT_V1_PAD 2 #else -#define MDP_LAYER_COMMIT_V1_PAD 4 +#define MDP_LAYER_COMMIT_V1_PAD 3 #endif struct mdp_buf_sync32 { @@ -537,6 +537,7 @@ struct mdp_layer_commit_v1_32 { compat_caddr_t dest_scaler; uint32_t dest_scaler_cnt; compat_caddr_t frc_info; + uint32_t bl_level; /* BL level to be updated in commit */ uint32_t reserved[MDP_LAYER_COMMIT_V1_PAD]; }; diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c index 3e1dbba7c9ae..e60869144339 100644 --- a/drivers/video/fbdev/msm/mdss_debug.c +++ b/drivers/video/fbdev/msm/mdss_debug.c @@ -44,6 +44,8 @@ #define INVALID_XIN_ID 0xFF +static DEFINE_MUTEX(mdss_debug_lock); + static char panel_reg[2] = {DEFAULT_READ_PANEL_POWER_MODE_REG, 0x00}; static int panel_debug_base_open(struct inode *inode, struct file *file) @@ -93,8 +95,10 @@ static ssize_t panel_debug_base_offset_write(struct file *file, if (cnt > (dbg->max_offset - off)) cnt = dbg->max_offset - off; + mutex_lock(&mdss_debug_lock); dbg->off = off; dbg->cnt = cnt; + mutex_unlock(&mdss_debug_lock); pr_debug("offset=%x cnt=%d\n", off, cnt); @@ -114,15 +118,21 @@ static ssize_t panel_debug_base_offset_read(struct file *file, if (*ppos) return 0; /* the end */ + mutex_lock(&mdss_debug_lock); len = snprintf(buf, sizeof(buf), "0x%02zx %zx\n", dbg->off, dbg->cnt); - if (len < 0 || len >= sizeof(buf)) + if (len < 0 || len >= sizeof(buf)) { + mutex_unlock(&mdss_debug_lock); return 0; + } - if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) + if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) { + mutex_unlock(&mdss_debug_lock); return -EFAULT; + } *ppos += len; /* increase offset */ + mutex_unlock(&mdss_debug_lock); return len; } @@ -220,11 +230,16 @@ static ssize_t panel_debug_base_reg_read(struct file *file, if (!dbg) return -ENODEV; - if (!dbg->cnt) + mutex_lock(&mdss_debug_lock); + if (!dbg->cnt) { + mutex_unlock(&mdss_debug_lock); return 0; + } - if (*ppos) + if (*ppos) { + mutex_unlock(&mdss_debug_lock); return 0; /* the end */ + } /* '0x' + 2 digit + blank = 5 bytes for each number */ reg_buf_len = (dbg->cnt * PANEL_REG_FORMAT_LEN) @@ -265,11 +280,13 @@ static ssize_t panel_debug_base_reg_read(struct file *file, kfree(panel_reg_buf); *ppos += len; /* increase offset */ + mutex_unlock(&mdss_debug_lock); return len; read_reg_fail: kfree(rx_buf); kfree(panel_reg_buf); + mutex_unlock(&mdss_debug_lock); return rc; } @@ -403,8 +420,10 @@ static ssize_t mdss_debug_base_offset_write(struct file *file, if (cnt > (dbg->max_offset - off)) cnt = dbg->max_offset - off; + mutex_lock(&mdss_debug_lock); dbg->off = off; dbg->cnt = cnt; + mutex_unlock(&mdss_debug_lock); pr_debug("offset=%x cnt=%x\n", off, cnt); @@ -424,15 +443,21 @@ static ssize_t mdss_debug_base_offset_read(struct file *file, if (*ppos) return 0; /* the end */ + mutex_lock(&mdss_debug_lock); len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt); - if (len < 0 || len >= sizeof(buf)) + if (len < 0 || len >= sizeof(buf)) { + mutex_unlock(&mdss_debug_lock); return 0; + } - if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) + if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) { + mutex_unlock(&mdss_debug_lock); return -EFAULT; + } *ppos += len; /* increase offset */ + mutex_unlock(&mdss_debug_lock); return len; } @@ -489,6 +514,8 @@ static ssize_t mdss_debug_base_reg_read(struct file *file, return -ENODEV; } + mutex_lock(&mdss_debug_lock); + if (!dbg->buf) { char dump_buf[64]; char *ptr; @@ -500,6 +527,7 @@ static ssize_t mdss_debug_base_reg_read(struct file *file, if (!dbg->buf) { pr_err("not enough memory to hold reg dump\n"); + mutex_unlock(&mdss_debug_lock); return -ENOMEM; } @@ -530,17 +558,21 @@ static ssize_t mdss_debug_base_reg_read(struct file *file, dbg->buf_len = tot; } - if (*ppos >= dbg->buf_len) + if (*ppos >= dbg->buf_len) { + mutex_unlock(&mdss_debug_lock); return 0; /* done reading */ + } len = min(count, dbg->buf_len - (size_t) *ppos); if (copy_to_user(user_buf, dbg->buf + *ppos, len)) { pr_err("failed to copy to user\n"); + mutex_unlock(&mdss_debug_lock); return -EFAULT; } *ppos += len; /* increase offset */ + mutex_unlock(&mdss_debug_lock); return len; } diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 153c733fdf43..67adc46d1e39 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1013,9 +1013,10 @@ static int dp_get_cable_status(struct platform_device *pdev, u32 vote) return hpd; } -static bool mdss_dp_is_dvi_mode(struct mdss_dp_drv_pdata *dp) +static bool mdss_dp_sink_audio_supp(struct mdss_dp_drv_pdata *dp) { - return hdmi_edid_is_dvi_mode(dp->panel_data.panel_info.edid_data); + return hdmi_edid_is_audio_supported( + dp->panel_data.panel_info.edid_data); } static int dp_audio_info_setup(struct platform_device *pdev, @@ -1290,6 +1291,23 @@ exit: return ret; } +static u32 mdss_dp_calc_max_pclk_rate(struct mdss_dp_drv_pdata *dp) +{ + u32 bpp = mdss_dp_get_bpp(dp); + u32 max_link_rate_khz = dp->dpcd.max_link_rate * + (DP_LINK_RATE_MULTIPLIER / 100); + u32 max_data_rate_khz = dp->dpcd.max_lane_count * + max_link_rate_khz * 8 / 10; + u32 max_pclk_rate_khz = max_data_rate_khz / bpp; + + pr_debug("bpp=%d, max_lane_cnt=%d, max_link_rate=%dKHz\n", bpp, + dp->dpcd.max_lane_count, max_link_rate_khz); + pr_debug("max_data_rate=%dKHz, max_pclk_rate=%dKHz\n", + max_data_rate_khz, max_pclk_rate_khz); + + return max_pclk_rate_khz; +} + static void mdss_dp_set_clock_rate(struct mdss_dp_drv_pdata *dp, char *name, u32 rate) { @@ -1689,14 +1707,17 @@ static int mdss_dp_send_audio_notification( goto end; } - if (!mdss_dp_is_dvi_mode(dp) || dp->audio_test_req) { + if (mdss_dp_sink_audio_supp(dp) || dp->audio_test_req) { dp->audio_test_req = false; + pr_debug("sending audio notification\n"); flags |= MSM_EXT_DISP_HPD_AUDIO; if (dp->ext_audio_data.intf_ops.hpd) ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev, dp->ext_audio_data.type, val, flags); + } else { + pr_debug("sink does not support audio\n"); } end: @@ -1981,6 +2002,7 @@ end: static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) { int ret; + u32 max_pclk_khz; if (dp->sink_info_read) return 0; @@ -1998,6 +2020,10 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) goto notify; } + max_pclk_khz = mdss_dp_calc_max_pclk_rate(dp); + hdmi_edid_set_max_pclk_rate(dp->panel_data.panel_info.edid_data, + min(dp->max_pclk_khz, max_pclk_khz)); + ret = hdmi_edid_parser(dp->panel_data.panel_info.edid_data); if (ret) { pr_err("edid parse failed, setting default resolution\n"); @@ -4022,8 +4048,6 @@ static int mdss_dp_probe(struct platform_device *pdev) pr_debug("done\n"); - dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); - return 0; probe_err: diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 5f1dde279732..17722eac3006 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -34,7 +34,6 @@ #include "mdss_dsi_phy.h" #include "mdss_dba_utils.h" -#define XO_CLK_RATE 19200000 #define CMDLINE_DSI_CTL_NUM_STRING_LEN 2 /* Master structure to hold all the information about the DSI/panel */ @@ -1464,15 +1463,6 @@ int mdss_dsi_on(struct mdss_panel_data *pdata) if (mipi->init_delay) usleep_range(mipi->init_delay, mipi->init_delay); - if (mipi->force_clk_lane_hs) { - u32 tmp; - - tmp = MIPI_INP((ctrl_pdata->ctrl_base) + 0xac); - tmp |= (1<<28); - MIPI_OUTP((ctrl_pdata->ctrl_base) + 0xac, tmp); - wmb(); - } - if (pdata->panel_info.type == MIPI_CMD_PANEL) mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle, MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_OFF); @@ -1576,10 +1566,12 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata) mdss_dsi_clk_ctrl(sctrl, sctrl->dsi_clk_handle, MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_ON); - if (mdss_dsi_is_panel_on_lp(pdata)) { + if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_LP) { pr_debug("%s: dsi_unblank with panel always on\n", __func__); if (ctrl_pdata->low_power_config) ret = ctrl_pdata->low_power_config(pdata, false); + if (!ret) + ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_LP; goto error; } @@ -1644,6 +1636,8 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state) pr_debug("%s: low power state requested\n", __func__); if (ctrl_pdata->low_power_config) ret = ctrl_pdata->low_power_config(pdata, true); + if (!ret) + ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_LP; goto error; } @@ -1686,7 +1680,8 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state) } ATRACE_END("dsi_panel_off"); } - ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT; + ctrl_pdata->ctrl_state &= ~(CTRL_STATE_PANEL_INIT | + CTRL_STATE_PANEL_LP); } error: @@ -1866,7 +1861,7 @@ static void __mdss_dsi_dyn_refresh_config( static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata) { - u32 esc_clk_rate = XO_CLK_RATE; + u32 esc_clk_rate_hz; u32 pipe_delay, pipe_delay2 = 0, pll_delay; u32 hsync_period = 0; u32 pclk_to_esc_ratio, byte_to_esc_ratio, hr_bit_to_esc_ratio; @@ -1893,9 +1888,11 @@ static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata) pinfo = &pdata->panel_info; pd = &(pinfo->mipi.dsi_phy_db); - pclk_to_esc_ratio = (ctrl_pdata->pclk_rate / esc_clk_rate); - byte_to_esc_ratio = (ctrl_pdata->byte_clk_rate / esc_clk_rate); - hr_bit_to_esc_ratio = ((ctrl_pdata->byte_clk_rate * 4) / esc_clk_rate); + esc_clk_rate_hz = ctrl_pdata->esc_clk_rate_hz; + pclk_to_esc_ratio = (ctrl_pdata->pclk_rate / esc_clk_rate_hz); + byte_to_esc_ratio = (ctrl_pdata->byte_clk_rate / esc_clk_rate_hz); + hr_bit_to_esc_ratio = ((ctrl_pdata->byte_clk_rate * 4) / + esc_clk_rate_hz); hsync_period = mdss_panel_get_htotal(pinfo, true); pipe_delay = (hsync_period + 1) / pclk_to_esc_ratio; @@ -1917,7 +1914,7 @@ static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata) ((pd->timing[4] >> 1) + 1)) / hr_bit_to_esc_ratio); /* 130 us pll delay recommended by h/w doc */ - pll_delay = ((130 * esc_clk_rate) / 1000000) * 2; + pll_delay = ((130 * esc_clk_rate_hz) / 1000000) * 2; MIPI_OUTP((ctrl_pdata->ctrl_base) + DSI_DYNAMIC_REFRESH_PIPE_DELAY, pipe_delay); @@ -2510,6 +2507,15 @@ static int mdss_dsi_register_mdp_callback(struct mdss_dsi_ctrl_pdata *ctrl, return 0; } +static int mdss_dsi_register_clamp_handler(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_intf_ulp_clamp *clamp_handler) +{ + mutex_lock(&ctrl->mutex); + ctrl->clamp_handler = clamp_handler; + mutex_unlock(&ctrl->mutex); + return 0; +} + static struct device_node *mdss_dsi_get_fb_node_cb(struct platform_device *pdev) { struct device_node *fb_node; @@ -2694,6 +2700,10 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, rc = mdss_dsi_register_mdp_callback(ctrl_pdata, (struct mdss_intf_recovery *)arg); break; + case MDSS_EVENT_REGISTER_CLAMP_HANDLER: + rc = mdss_dsi_register_clamp_handler(ctrl_pdata, + (struct mdss_intf_ulp_clamp *)arg); + break; case MDSS_EVENT_DSI_DYNAMIC_SWITCH: mode = (u32)(unsigned long) arg; mdss_dsi_switch_mode(pdata, mode); @@ -3052,7 +3062,7 @@ static int mdss_dsi_set_clk_rates(struct mdss_dsi_ctrl_pdata *ctrl_pdata) rc = mdss_dsi_clk_set_link_rate(ctrl_pdata->dsi_clk_handle, MDSS_DSI_LINK_ESC_CLK, - 19200000, + ctrl_pdata->esc_clk_rate_hz, MDSS_DSI_CLK_UPDATE_CLK_RATE_AT_ON); if (rc) { pr_err("%s: dsi_esc_clk - clk_set_rate failed\n", @@ -4231,6 +4241,9 @@ int dsi_panel_device_register(struct platform_device *ctrl_pdev, pr_debug("%s: pclk=%d, bclk=%d\n", __func__, ctrl_pdata->pclk_rate, ctrl_pdata->byte_clk_rate); + ctrl_pdata->esc_clk_rate_hz = pinfo->esc_clk_rate_hz; + pr_debug("%s: esc clk=%d\n", __func__, + ctrl_pdata->esc_clk_rate_hz); rc = mdss_dsi_get_dt_vreg_data(&ctrl_pdev->dev, pan_node, &ctrl_pdata->panel_power_data, DSI_PANEL_PM); diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index df24352ff87d..2a76466abf3e 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -166,6 +166,7 @@ enum dsi_pm_type { #define CTRL_STATE_PANEL_INIT BIT(0) #define CTRL_STATE_MDP_ACTIVE BIT(1) #define CTRL_STATE_DSI_ACTIVE BIT(2) +#define CTRL_STATE_PANEL_LP BIT(3) #define DSI_NON_BURST_SYNCH_PULSE 0 #define DSI_NON_BURST_SYNCH_EVENT 1 @@ -476,6 +477,7 @@ struct mdss_dsi_ctrl_pdata { u32 byte_clk_rate; u32 pclk_rate_bkp; u32 byte_clk_rate_bkp; + u32 esc_clk_rate_hz; bool refresh_clk_rate; /* flag to recalculate clk_rate */ struct dss_module_power panel_power_data; struct dss_module_power power_data[DSI_MAX_PM]; /* for 8x10 */ @@ -483,6 +485,7 @@ struct mdss_dsi_ctrl_pdata { struct mdss_hw *dsi_hw; struct mdss_intf_recovery *recovery; struct mdss_intf_recovery *mdp_callback; + struct mdss_intf_ulp_clamp *clamp_handler; struct dsi_panel_cmds on_cmds; struct dsi_panel_cmds post_dms_on_cmds; @@ -696,6 +699,8 @@ void mdss_dsi_dsc_config(struct mdss_dsi_ctrl_pdata *ctrl, struct dsc_desc *dsc); void mdss_dsi_dfps_config_8996(struct mdss_dsi_ctrl_pdata *ctrl); void mdss_dsi_set_burst_mode(struct mdss_dsi_ctrl_pdata *ctrl); +void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, + u32 bits, int set); void mdss_dsi_set_reg(struct mdss_dsi_ctrl_pdata *ctrl, int off, u32 mask, u32 val); int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl); diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index 734d3bee8fd0..9f4b7eb52492 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -41,6 +41,8 @@ #define LANE_SWAP_CTRL 0x0B0 #define LOGICAL_LANE_SWAP_CTRL 0x310 +#define MAX_BTA_WAIT_RETRY 5 + #define CEIL(x, y) (((x) + ((y)-1)) / (y)) struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX]; @@ -600,7 +602,7 @@ error: return rc; } -static void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, +void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, u32 bits, int set) { u32 data; @@ -611,6 +613,7 @@ static void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, else data &= ~bits; MIPI_OUTP(ctrl->ctrl_base + 0x0ac, data); + wmb(); /* make sure write happens */ } @@ -766,6 +769,8 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event) u32 data0, data1, mask = 0, data_lane_en = 0; struct mdss_dsi_ctrl_pdata *ctrl0, *ctrl1; u32 ln0, ln1, ln_ctrl0, ln_ctrl1, i; + int rc = 0; + /* * Add 2 ms delay suggested by HW team. * Check clk lane stop state after every 200 us @@ -787,9 +792,15 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event) ctrl0 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_0); ctrl1 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_1); - if (ctrl0->recovery) - ctrl0->recovery->fxn(ctrl0->recovery->data, + if (ctrl0->recovery) { + rc = ctrl0->recovery->fxn(ctrl0->recovery->data, MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW); + if (rc < 0) { + pr_debug("%s: Target is in suspend/shutdown\n", + __func__); + return; + } + } /* * Disable PHY contention detection and receive. * Configure the strength ctrl 1 register. @@ -879,9 +890,15 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event) */ udelay(200); } else { - if (ctrl->recovery) - ctrl->recovery->fxn(ctrl->recovery->data, + if (ctrl->recovery) { + rc = ctrl->recovery->fxn(ctrl->recovery->data, MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW); + if (rc < 0) { + pr_debug("%s: Target is in suspend/shutdown\n", + __func__); + return; + } + } /* Disable PHY contention detection and receive */ MIPI_OUTP((ctrl->phy_io.base) + 0x0188, 0); @@ -1437,8 +1454,7 @@ static int mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl) if (ctrl->ctrl_state & CTRL_STATE_MDP_ACTIVE) { mdss_dsi_wait4video_done(ctrl); v_total = mdss_panel_get_vtotal(pinfo); - v_blank = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_front_porch + - pinfo->lcdc.v_pulse_width; + v_blank = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width; if (pinfo->dynamic_fps && pinfo->current_fps) fps = pinfo->current_fps; else @@ -1469,6 +1485,8 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) int ret = 0; unsigned long flag; int ignore_underflow = 0; + int retry_count = 0; + int in_blanking = 0; if (ctrl_pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); @@ -1494,7 +1512,25 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) reinit_completion(&ctrl_pdata->bta_comp); mdss_dsi_enable_irq(ctrl_pdata, DSI_BTA_TERM); spin_unlock_irqrestore(&ctrl_pdata->mdp_lock, flag); +wait: mdss_dsi_wait4video_eng_busy(ctrl_pdata); + if (ctrl_pdata->panel_mode == DSI_VIDEO_MODE) { + in_blanking = ctrl_pdata->mdp_callback->fxn( + ctrl_pdata->mdp_callback->data, + MDP_INTF_CALLBACK_CHECK_LINE_COUNT); + /* Try for maximum of 5 attempts */ + if (in_blanking && (retry_count < MAX_BTA_WAIT_RETRY)) { + pr_debug("%s: not in active region\n", __func__); + retry_count++; + goto wait; + } + } + if (retry_count == MAX_BTA_WAIT_RETRY) + MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", + "dsi0_phy", "dsi1_ctrl", "dsi1_phy", + "vbif", "vbif_nrt", "dbg_bus", + "vbif_dbg_bus", "panic"); + /* mask out overflow errors */ if (ignore_underflow) mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0x0f0000); @@ -2134,8 +2170,8 @@ static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl, status = reg_val & DSI_INTR_CMD_DMA_DONE; if (status) { reg_val &= DSI_INTR_MASK_ALL; - /* clear CMD DMA isr only */ - reg_val |= DSI_INTR_CMD_DMA_DONE; + /* clear CMD DMA and BTA_DONE isr only */ + reg_val |= (DSI_INTR_CMD_DMA_DONE | DSI_INTR_BTA_DONE); MIPI_OUTP(ctrl->ctrl_base + 0x0110, reg_val); mdss_dsi_disable_irq_nosync(ctrl, DSI_CMD_TERM); complete(&ctrl->dma_comp); @@ -3030,6 +3066,13 @@ static bool mdss_dsi_fifo_status(struct mdss_dsi_ctrl_pdata *ctrl) pr_err("%s: status=%x\n", __func__, status); + /* + * if DSI FIFO overflow is masked, + * do not report overflow error + */ + if (MIPI_INP(base + 0x10c) & 0xf0000) + status = status & 0xaaaaffff; + if (status & 0x44440000) {/* DLNx_HS_FIFO_OVERFLOW */ dsi_send_events(ctrl, DSI_EV_DLNx_FIFO_OVERFLOW, 0); /* Ignore FIFO EMPTY when overflow happens */ diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index 7cc9ce6e034d..bf701e2a4ac5 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -2819,6 +2819,13 @@ static int mdss_panel_parse_dt(struct device_node *np, MSM_DBA_CHIP_NAME_MAX_LEN); } + rc = of_property_read_u32(np, + "qcom,mdss-dsi-host-esc-clk-freq-hz", + &pinfo->esc_clk_rate_hz); + if (rc) + pinfo->esc_clk_rate_hz = MDSS_DSI_MAX_ESC_CLK_RATE_HZ; + pr_debug("%s: esc clk %d\n", __func__, pinfo->esc_clk_rate_hz); + return 0; error: diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index db27842eaccc..fe79b6fd52b4 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -303,10 +303,23 @@ static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev, } } +static enum led_brightness mdss_fb_get_bl_brightness( + struct led_classdev *led_cdev) +{ + struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent); + enum led_brightness value; + + MDSS_BL_TO_BRIGHT(value, mfd->bl_level, mfd->panel_info->bl_max, + mfd->panel_info->brightness_max); + + return value; +} + static struct led_classdev backlight_led = { .name = "lcd-backlight", .brightness = MDSS_MAX_BL_BRIGHTNESS / 2, .brightness_set = mdss_fb_set_bl_brightness, + .brightness_get = mdss_fb_get_bl_brightness, .max_brightness = MDSS_MAX_BL_BRIGHTNESS, }; @@ -1262,6 +1275,7 @@ static int mdss_fb_probe(struct platform_device *pdev) mfd->fb_imgType = MDP_RGBA_8888; mfd->calib_mode_bl = 0; mfd->unset_bl_level = U32_MAX; + mfd->bl_extn_level = -1; mfd->pdev = pdev; @@ -3422,6 +3436,14 @@ int mdss_fb_atomic_commit(struct fb_info *info, mfd->msm_fb_backup.disp_commit.l_roi = commit_v1->left_roi; mfd->msm_fb_backup.disp_commit.r_roi = commit_v1->right_roi; mfd->msm_fb_backup.disp_commit.flags = commit_v1->flags; + if (commit_v1->flags & MDP_COMMIT_UPDATE_BRIGHTNESS) { + MDSS_BRIGHT_TO_BL(mfd->bl_extn_level, commit_v1->bl_level, + mfd->panel_info->bl_max, + mfd->panel_info->brightness_max); + if (!mfd->bl_extn_level && commit_v1->bl_level) + mfd->bl_extn_level = 1; + } else + mfd->bl_extn_level = -1; mutex_lock(&mfd->mdp_sync_pt_data.sync_mutex); atomic_inc(&mfd->mdp_sync_pt_data.commit_cnt); diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index d64580a35775..321531c72a08 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -241,6 +241,10 @@ struct msm_mdp_interface { out = (2 * (v) * (bl_max) + max_bright);\ do_div(out, 2 * max_bright);\ } while (0) +#define MDSS_BL_TO_BRIGHT(out, v, bl_max, max_bright) do {\ + out = ((v) * (max_bright));\ + do_div(out, bl_max);\ + } while (0) struct mdss_fb_file_info { struct file *file; @@ -305,6 +309,7 @@ struct msm_fb_data_type { u32 calib_mode_bl; u32 ad_bl_level; u32 bl_level; + int bl_extn_level; u32 bl_scale; u32 unset_bl_level; bool allow_bl_update; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 3d8fe2ceebac..502bc1570609 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -137,6 +137,7 @@ struct hdmi_edid_ctrl { u16 video_latency; u32 present_3d; u32 page_id; + bool basic_audio_supp; u8 audio_data_block[MAX_NUMBER_ADB * MAX_AUDIO_DATA_BLOCK_SIZE]; int adb_size; u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE]; @@ -1289,6 +1290,14 @@ static void hdmi_edid_extract_sink_caps(struct hdmi_edid_ctrl *edid_ctrl, return; } + /* Check if sink supports basic audio */ + if (in_buf[3] & BIT(6)) + edid_ctrl->basic_audio_supp = true; + else + edid_ctrl->basic_audio_supp = false; + pr_debug("%s: basic audio supported: %s\n", __func__, + edid_ctrl->basic_audio_supp ? "true" : "false"); + vsd = hdmi_edid_find_hfvsdb(in_buf); if (vsd) { @@ -2620,6 +2629,24 @@ void hdmi_edid_config_override(void *input, bool enable, } } +void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz) +{ + struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; + + edid_ctrl->init_data.max_pclk_khz = max_pclk_khz; +} + +bool hdmi_edid_is_audio_supported(void *input) +{ + struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; + + /* + * return true if basic audio is supported or if an audio + * data block was successfully parsed. + */ + return (edid_ctrl->basic_audio_supp || edid_ctrl->adb_size); +} + void hdmi_edid_deinit(void *input) { struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h index 16efb6ee4014..557e9326a81d 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h @@ -79,5 +79,7 @@ void hdmi_edid_get_hdr_data(void *edid_ctrl, struct hdmi_edid_hdr_data **hdr_data); void hdmi_edid_config_override(void *input, bool enable, struct hdmi_edid_override_data *data); +void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz); +bool hdmi_edid_is_audio_supported(void *input); #endif /* __HDMI_EDID_H__ */ diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 3cadfa4ecef7..171f44815430 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -2179,6 +2179,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) mdss_set_quirk(mdata, MDSS_QUIRK_HDR_SUPPORT_ENABLED); break; case MDSS_MDP_HW_REV_320: + mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU); + mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT); case MDSS_MDP_HW_REV_330: mdata->max_target_zorder = 7; /* excluding base layer */ mdata->max_cursor_size = 512; @@ -2206,6 +2208,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) set_bit(MDSS_QOS_TS_PREFILL, mdata->mdss_qos_map); set_bit(MDSS_QOS_IB_NOCR, mdata->mdss_qos_map); set_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, mdata->mdss_qos_map); + set_bit(MDSS_QOS_WB_QOS, mdata->mdss_qos_map); + set_bit(MDSS_CAPS_CWB_SUPPORTED, mdata->mdss_caps_map); set_bit(MDSS_CAPS_YUV_CONFIG, mdata->mdss_caps_map); set_bit(MDSS_CAPS_SCM_RESTORE_NOT_REQUIRED, mdata->mdss_caps_map); @@ -2216,8 +2220,6 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) set_bit(MDSS_CAPS_MDP_VOTE_CLK_NOT_SUPPORTED, mdata->mdss_caps_map); mdss_mdp_init_default_prefill_factors(mdata); - mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU); - mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT); mdss_set_quirk(mdata, MDSS_QUIRK_MMSS_GDSC_COLLAPSE); mdss_set_quirk(mdata, MDSS_QUIRK_MDP_CLK_SET_RATE); mdss_set_quirk(mdata, MDSS_QUIRK_DMA_BI_DIR); diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 36a866685f21..2fd047edd3e8 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -164,6 +164,14 @@ enum mdss_mdp_mixer_mux { MDSS_MDP_MIXER_MUX_RIGHT, }; +enum mdss_secure_transition { + SECURE_TRANSITION_NONE, + SD_NON_SECURE_TO_SECURE, + SD_SECURE_TO_NON_SECURE, + SC_NON_SECURE_TO_SECURE, + SC_SECURE_TO_NON_SECURE, +}; + static inline enum mdss_mdp_sspp_index get_pipe_num_from_ndx(u32 ndx) { u32 id; @@ -421,6 +429,9 @@ struct mdss_mdp_ctl_intfs_ops { /* to update lineptr, [1..yres] - enable, 0 - disable */ int (*update_lineptr)(struct mdss_mdp_ctl *ctl, bool enable); int (*avr_ctrl_fnc)(struct mdss_mdp_ctl *, bool enable); + + /* to wait for vsync */ + int (*wait_for_vsync_fnc)(struct mdss_mdp_ctl *ctl); }; struct mdss_mdp_cwb { @@ -650,6 +661,7 @@ struct mdss_mdp_img_data { struct dma_buf *srcp_dma_buf; struct dma_buf_attachment *srcp_attachment; struct sg_table *srcp_table; + struct ion_handle *ihandle; }; enum mdss_mdp_data_state { @@ -691,6 +703,8 @@ struct pp_hist_col_info { char __iomem *base; u32 intr_shift; u32 disp_num; + u32 expect_sum; + u32 next_sum; struct mdss_mdp_ctl *ctl; }; @@ -953,6 +967,8 @@ struct mdss_overlay_private { struct kthread_worker worker; struct kthread_work vsync_work; struct task_struct *thread; + + u8 secure_transition_state; }; struct mdss_mdp_set_ot_params { @@ -1976,6 +1992,7 @@ void mdss_mdp_disable_hw_irq(struct mdss_data_type *mdata); void mdss_mdp_set_supported_formats(struct mdss_data_type *mdata); int mdss_mdp_dest_scaler_setup_locked(struct mdss_mdp_mixer *mixer); +void *mdss_mdp_intf_get_ctx_base(struct mdss_mdp_ctl *ctl, int intf_num); #ifdef CONFIG_FB_MSM_MDP_NONE struct mdss_data_type *mdss_mdp_get_mdata(void) diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index c31df04643d6..bd70535e79f9 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -73,8 +73,18 @@ static void __mdss_mdp_mixer_write_cfg(struct mdss_mdp_mixer *mixer, static inline u64 fudge_factor(u64 val, u32 numer, u32 denom) { - u64 result = (val * (u64)numer); - do_div(result, denom); + u64 result = val; + + if (val) { + u64 temp = -1UL; + + do_div(temp, val); + if (temp > numer) { + /* no overflow, so we can do the operation*/ + result = (val * (u64)numer); + do_div(result, denom); + } + } return result; } @@ -4486,11 +4496,15 @@ end: */ static void mdss_mdp_pipe_reset(struct mdss_mdp_mixer *mixer, bool is_recovery) { - unsigned long pipe_map = mixer->pipe_mapped; + unsigned long pipe_map; u32 bit = 0; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); bool sw_rst_avail = mdss_mdp_pipe_is_sw_reset_available(mdata); + if (!mixer) + return; + + pipe_map = mixer->pipe_mapped; pr_debug("pipe_map=0x%lx\n", pipe_map); for_each_set_bit_from(bit, &pipe_map, MAX_PIPES_PER_LM) { struct mdss_mdp_pipe *pipe; @@ -5706,6 +5720,15 @@ static void mdss_mdp_force_border_color(struct mdss_mdp_ctl *ctl) ctl->mixer_right->params_changed++; } +static bool mdss_mdp_handle_backlight_extn(struct mdss_mdp_ctl *ctl) +{ + if (ctl->intf_type == MDSS_INTF_DSI && !ctl->is_video_mode && + ctl->mfd->bl_extn_level >= 0) + return true; + else + return false; +} + int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, struct mdss_mdp_commit_cb *commit_cb) { @@ -5874,6 +5897,15 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, if (ctl->ops.wait_pingpong && !mdata->serialize_wait4pp) mdss_mdp_display_wait4pingpong(ctl, false); + /* + * If backlight needs to change, wait for 1 vsync before setting + * PCC and kickoff + */ + if (mdss_mdp_handle_backlight_extn(ctl) && + ctl->ops.wait_for_vsync_fnc) { + ret = ctl->ops.wait_for_vsync_fnc(ctl); + } + /* Moved pp programming to post ping pong */ if (!ctl->is_video_mode && ctl->mfd && ctl->mfd->dcm_state != DTM_ENTER) { @@ -6022,6 +6054,17 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, if (ret) pr_warn("ctl %d error displaying frame\n", ctl->num); + /* update backlight in commit */ + if (mdss_mdp_handle_backlight_extn(ctl)) { + if (!IS_CALIB_MODE_BL(ctl->mfd) && (!ctl->mfd->ext_bl_ctrl || + !ctl->mfd->bl_level)) { + mutex_lock(&ctl->mfd->bl_lock); + mdss_fb_set_backlight(ctl->mfd, + ctl->mfd->bl_extn_level); + mutex_unlock(&ctl->mfd->bl_lock); + } + } + ctl->play_cnt++; ATRACE_END("flush_kickoff"); diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h index 294e05c2fbb0..7d495232c198 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h +++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -634,15 +634,6 @@ enum mdss_mdp_dspp_index { #define MDSS_MDP_DSPP_OP_PA_SIX_ZONE_SAT_MASK BIT(30) #define MDSS_MDP_DSPP_OP_PA_SIX_ZONE_VAL_MASK BIT(31) -enum mdss_mpd_intf_index { - MDSS_MDP_NO_INTF, - MDSS_MDP_INTF0, - MDSS_MDP_INTF1, - MDSS_MDP_INTF2, - MDSS_MDP_INTF3, - MDSS_MDP_MAX_INTF -}; - #define MDSS_MDP_REG_INTF_TIMING_ENGINE_EN 0x000 #define MDSS_MDP_REG_INTF_CONFIG 0x004 #define MDSS_MDP_REG_INTF_HSYNC_CTL 0x008 @@ -703,6 +694,8 @@ enum mdss_mpd_intf_index { #define MDSS_MDP_PANEL_FORMAT_RGB888 0x213F #define MDSS_MDP_PANEL_FORMAT_RGB666 0x212A +#define MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE 0x064 + #define MDSS_MDP_PANEL_FORMAT_PACK_ALIGN_MSB BIT(7) enum mdss_mdp_pingpong_index { diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index dfd6226ce602..c9ce56fb96a4 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -103,6 +103,7 @@ struct mdss_mdp_cmd_ctx { struct mdss_intf_recovery intf_recovery; struct mdss_intf_recovery intf_mdp_callback; + struct mdss_intf_ulp_clamp intf_clamp_handler; struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */ u32 pp_timeout_report_cnt; bool pingpong_split_slave; @@ -1197,7 +1198,7 @@ static int mdss_mdp_cmd_wait4readptr(struct mdss_mdp_cmd_ctx *ctx) return rc; } -static void mdss_mdp_cmd_intf_callback(void *data, int event) +static int mdss_mdp_cmd_intf_callback(void *data, int event) { struct mdss_mdp_cmd_ctx *ctx = data; struct mdss_mdp_pp_tear_check *te = NULL; @@ -1206,11 +1207,11 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event) if (!data) { pr_err("%s: invalid ctx\n", __func__); - return; + return -EINVAL; } if (!ctx->ctl) - return; + return -EINVAL; switch (event) { case MDP_INTF_CALLBACK_DSI_WAIT: @@ -1222,7 +1223,7 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event) * just return */ if (ctx->intf_stopped || !is_pingpong_split(ctx->ctl->mfd)) - return; + return -EINVAL; atomic_inc(&ctx->rdptr_cnt); /* enable clks and rd_ptr interrupt */ @@ -1231,7 +1232,7 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event) mixer = mdss_mdp_mixer_get(ctx->ctl, MDSS_MDP_MIXER_MUX_LEFT); if (!mixer) { pr_err("%s: null mixer\n", __func__); - return; + return -EINVAL; } /* wait for read pointer */ @@ -1255,6 +1256,7 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event) pr_debug("%s: unhandled event=%d\n", __func__, event); break; } + return 0; } static void mdss_mdp_cmd_lineptr_done(void *arg) @@ -1280,7 +1282,33 @@ static void mdss_mdp_cmd_lineptr_done(void *arg) spin_unlock(&ctx->clk_lock); } -static void mdss_mdp_cmd_intf_recovery(void *data, int event) +static int mdss_mdp_cmd_intf_clamp_ctrl(void *data, int intf_num, bool enable) +{ + struct mdss_mdp_ctl *ctl = data; + char __iomem *ctx_base = NULL; + + if (!data) { + pr_err("%s: invalid ctl\n", __func__); + return -EINVAL; + } + + ctx_base = + (char __iomem *)mdss_mdp_intf_get_ctx_base(ctl, intf_num); + if (!ctx_base) { + pr_err("%s: invalid ctx\n", __func__); + return -EINVAL; + } + + pr_debug("%s: intf num = %d, enable = %d\n", + __func__, intf_num, enable); + + writel_relaxed(enable, (ctx_base + MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE)); + wmb(); /* ensure clamp is enabled */ + + return 0; +} + +static int mdss_mdp_cmd_intf_recovery(void *data, int event) { struct mdss_mdp_cmd_ctx *ctx = data; unsigned long flags; @@ -1288,11 +1316,11 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event) if (!data) { pr_err("%s: invalid ctx\n", __func__); - return; + return -EINVAL; } if (!ctx->ctl) - return; + return -EINVAL; /* * Currently, only intf_fifo_underflow is @@ -1302,7 +1330,7 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event) if (event != MDP_INTF_DSI_CMD_FIFO_UNDERFLOW) { pr_warn("%s: unsupported recovery event:%d\n", __func__, event); - return; + return -EPERM; } if (atomic_read(&ctx->koff_cnt)) { @@ -1326,6 +1354,7 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event) if (notify_frame_timeout) mdss_mdp_ctl_notify(ctx->ctl, MDP_NOTIFY_FRAME_TIMEOUT); + return 0; } static void mdss_mdp_cmd_pingpong_done(void *arg) @@ -2917,6 +2946,41 @@ static void __mdss_mdp_kickoff(struct mdss_mdp_ctl *ctl, } } +int mdss_mdp_cmd_wait4_vsync(struct mdss_mdp_ctl *ctl) +{ + int rc = 0; + struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX]; + + if (!ctx) { + pr_err("invalid context to wait for vsync\n"); + return rc; + } + + atomic_inc(&ctx->rdptr_cnt); + + /* enable clks and rd_ptr interrupt */ + mdss_mdp_setup_vsync(ctx, true); + + /* wait for read pointer */ + MDSS_XLOG(atomic_read(&ctx->rdptr_cnt)); + pr_debug("%s: wait for vsync cnt:%d\n", + __func__, atomic_read(&ctx->rdptr_cnt)); + + rc = mdss_mdp_cmd_wait4readptr(ctx); + + /* wait for 1ms to make sure we are out from trigger window */ + usleep_range(1000, 1010); + + /* disable rd_ptr interrupt */ + mdss_mdp_setup_vsync(ctx, false); + + MDSS_XLOG(ctl->num); + pr_debug("%s: out from wait for rd_ptr ctl:%d\n", __func__, ctl->num); + + return rc; +} + + /* * There are 3 partial update possibilities * left only ==> enable left pingpong_done @@ -3170,6 +3234,14 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl, ctx->default_pp_num, NULL, NULL); memset(ctx, 0, sizeof(*ctx)); + /* intf stopped, no more kickoff */ + ctx->intf_stopped = 1; + /* + * Restore clamp handler as it might get called later + * when DSI panel events are handled. + */ + ctx->intf_clamp_handler.fxn = mdss_mdp_cmd_intf_clamp_ctrl; + ctx->intf_clamp_handler.data = ctl; return 0; } @@ -3310,12 +3382,21 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) (void *)&ctx->intf_mdp_callback, CTL_INTF_EVENT_FLAG_DEFAULT); + mdss_mdp_ctl_intf_event(ctl, + MDSS_EVENT_REGISTER_CLAMP_HANDLER, + (void *)&ctx->intf_clamp_handler, + CTL_INTF_EVENT_FLAG_DEFAULT); + mdss_mdp_tearcheck_enable(ctl, true); ctx->intf_stopped = 0; if (sctx) sctx->intf_stopped = 0; - + /* + * Tearcheck was disabled while entering LP2 state. + * Enable it back to allow updates in LP1 state. + */ + mdss_mdp_tearcheck_enable(ctl, true); goto end; } } @@ -3364,6 +3445,10 @@ panel_events: goto end; } + mdss_mdp_ctl_intf_event(ctl, + MDSS_EVENT_REGISTER_CLAMP_HANDLER, + NULL, CTL_INTF_EVENT_FLAG_DEFAULT); + pr_debug("%s: turn off panel\n", __func__); ctl->intf_ctx[MASTER_CTX] = NULL; ctl->intf_ctx[SLAVE_CTX] = NULL; @@ -3373,6 +3458,7 @@ panel_events: ctl->ops.add_vsync_handler = NULL; ctl->ops.remove_vsync_handler = NULL; ctl->ops.reconfigure = NULL; + ctl->ops.wait_for_vsync_fnc = NULL; end: if (!IS_ERR_VALUE(ret)) { @@ -3496,6 +3582,14 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl, ctx->intf_mdp_callback.fxn = mdss_mdp_cmd_intf_callback; ctx->intf_mdp_callback.data = ctx; + ctx->intf_clamp_handler.fxn = mdss_mdp_cmd_intf_clamp_ctrl; + ctx->intf_clamp_handler.data = ctl; + + mdss_mdp_ctl_intf_event(ctl, + MDSS_EVENT_REGISTER_CLAMP_HANDLER, + (void *)&ctx->intf_clamp_handler, + CTL_INTF_EVENT_FLAG_DEFAULT); + ctx->intf_stopped = 0; pr_debug("%s: ctx=%pK num=%d aux=%d\n", __func__, ctx, @@ -3773,6 +3867,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl) ctl->ops.pre_programming = mdss_mdp_cmd_pre_programming; ctl->ops.update_lineptr = mdss_mdp_cmd_update_lineptr; ctl->ops.panel_disable_cfg = mdss_mdp_cmd_panel_disable_cfg; + ctl->ops.wait_for_vsync_fnc = mdss_mdp_cmd_wait4_vsync; pr_debug("%s:-\n", __func__); return 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index 533239c99345..a3511a1a07ef 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -83,6 +83,8 @@ struct mdss_mdp_video_ctx { struct mutex vsync_mtx; struct list_head vsync_handlers; struct mdss_intf_recovery intf_recovery; + struct mdss_intf_recovery intf_mdp_callback; + struct mdss_intf_ulp_clamp intf_clamp_handler; struct work_struct early_wakeup_dfps_work; atomic_t lineptr_ref; @@ -149,6 +151,23 @@ static int mdss_mdp_intf_intr2index(u32 intr_type) return index; } +void *mdss_mdp_intf_get_ctx_base(struct mdss_mdp_ctl *ctl, int intf_num) +{ + struct mdss_mdp_video_ctx *head; + int i = 0; + + if (!ctl) + return NULL; + + head = ctl->mdata->video_intf; + for (i = 0; i < ctl->mdata->nintf; i++) { + if (head[i].intf_num == intf_num) + return (void *)head[i].base; + } + + return NULL; +} + int mdss_mdp_set_intf_intr_callback(struct mdss_mdp_video_ctx *ctx, u32 intr_type, void (*fnc_ptr)(void *), void *arg) { @@ -315,7 +334,30 @@ int mdss_mdp_video_addr_setup(struct mdss_data_type *mdata, return 0; } -static void mdss_mdp_video_intf_recovery(void *data, int event) +static int mdss_mdp_video_intf_clamp_ctrl(void *data, int intf_num, bool enable) +{ + struct mdss_mdp_video_ctx *ctx = data; + + if (!data) { + pr_err("%s: invalid ctl\n", __func__); + return -EINVAL; + } + + if (intf_num != ctx->intf_num) { + pr_err("%s: invalid intf num\n", __func__); + return -EINVAL; + } + + pr_debug("%s: ctx intf num = %d, enable = %d\n", + __func__, ctx->intf_num, enable); + + mdp_video_write(ctx, MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE, enable); + wmb(); /* ensure clamp is enabled */ + + return 0; +} + +static int mdss_mdp_video_intf_recovery(void *data, int event) { struct mdss_mdp_video_ctx *ctx; struct mdss_mdp_ctl *ctl = data; @@ -327,7 +369,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event) if (!data) { pr_err("%s: invalid ctl\n", __func__); - return; + return -EINVAL; } /* @@ -338,7 +380,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event) if (event != MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW) { pr_warn("%s: unsupported recovery event:%d\n", __func__, event); - return; + return -EPERM; } ctx = ctl->intf_ctx[MASTER_CTX]; @@ -353,7 +395,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event) clk_rate = DIV_ROUND_UP_ULL(clk_rate, 1000); /* in kHz */ if (!clk_rate) { pr_err("Unable to get proper clk_rate\n"); - return; + return -EINVAL; } /* * calculate clk_period as pico second to maintain good @@ -363,7 +405,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event) clk_period = DIV_ROUND_UP_ULL(1000000000, clk_rate); if (!clk_period) { pr_err("Unable to calculate clock period\n"); - return; + return -EINVAL; } min_ln_cnt = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width; active_lns_cnt = pinfo->yres; @@ -389,7 +431,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event) !ctx->timegen_en) { pr_warn("Target is in suspend or shutdown pending\n"); mutex_unlock(&ctl->offlock); - return; + return -EPERM; } line_cnt = mdss_mdp_video_line_count(ctl); @@ -399,7 +441,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event) pr_debug("%s, Needed lines left line_cnt=%d\n", __func__, line_cnt); mutex_unlock(&ctl->offlock); - return; + return 0; } else { pr_warn("line count is less. line_cnt = %d\n", line_cnt); @@ -1668,12 +1710,42 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg) return 0; } +static int mdss_mdp_video_splash_handoff(struct mdss_mdp_ctl *ctl) +{ + int i, ret = 0; + u32 data, flush; + + ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN, + NULL, CTL_INTF_EVENT_FLAG_DEFAULT); + + if (ret) { + pr_err("%s:ctl%d failed to handle 'CONT_SPLASH_BEGIN' event\n" + , __func__, ctl->num); + return ret; + } + + /* clear up mixer0 and mixer1 */ + flush = 0; + for (i = 0; i < 2; i++) { + data = mdss_mdp_ctl_read(ctl, + MDSS_MDP_REG_CTL_LAYER(i)); + if (data) { + mdss_mdp_ctl_write(ctl, + MDSS_MDP_REG_CTL_LAYER(i), + MDSS_MDP_LM_BORDER_COLOR); + flush |= (0x40 << i); + } + } + mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush); + + return ret; +} + int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, bool handoff) { struct mdss_panel_data *pdata; - int i, ret = 0, off; - u32 data, flush; + int ret = 0, off; struct mdss_mdp_video_ctx *ctx, *sctx = NULL; struct mdss_mdp_ctl *sctl; @@ -1707,29 +1779,20 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, } if (!handoff) { - ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN, - NULL, CTL_INTF_EVENT_FLAG_DEFAULT); - if (ret) { - pr_err("%s: Failed to handle 'CONT_SPLASH_BEGIN' event\n" - , __func__); - return ret; - } + ret = mdss_mdp_video_splash_handoff(ctl); - /* clear up mixer0 and mixer1 */ - flush = 0; - for (i = 0; i < 2; i++) { - data = mdss_mdp_ctl_read(ctl, - MDSS_MDP_REG_CTL_LAYER(i)); - if (data) { - mdss_mdp_ctl_write(ctl, - MDSS_MDP_REG_CTL_LAYER(i), - MDSS_MDP_LM_BORDER_COLOR); - flush |= (0x40 << i); - } - } - mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush); + if (!ret && sctl) + ret = mdss_mdp_video_splash_handoff(sctl); + + if (ret) + return ret; mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0); + + if (sctx) + mdp_video_write(sctx, + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0); + mdss_mdp_video_timegen_flush(ctl, sctx); /* wait for 1 VSYNC for the pipe to be unstaged */ @@ -1738,6 +1801,12 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_FINISH, NULL, CTL_INTF_EVENT_FLAG_DEFAULT); + + if (!ret && sctl) + ret = mdss_mdp_ctl_intf_event(sctl, + MDSS_EVENT_CONT_SPLASH_FINISH, NULL, + CTL_INTF_EVENT_FLAG_DEFAULT); + } return ret; @@ -1790,7 +1859,7 @@ static void mdss_mdp_fetch_end_config(struct mdss_mdp_video_ctx *ctx, static void mdss_mdp_fetch_start_config(struct mdss_mdp_video_ctx *ctx, struct mdss_mdp_ctl *ctl) { - int fetch_start, fetch_enable, v_total, h_total; + int fetch_start = 0, fetch_enable, v_total, h_total; struct mdss_data_type *mdata; struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info; @@ -1799,6 +1868,15 @@ static void mdss_mdp_fetch_start_config(struct mdss_mdp_video_ctx *ctx, pinfo->prg_fet = mdss_mdp_get_prefetch_lines(pinfo); if (!pinfo->prg_fet) { pr_debug("programmable fetch is not needed/supported\n"); + + fetch_enable = mdp_video_read(ctx, MDSS_MDP_REG_INTF_CONFIG); + fetch_enable &= ~BIT(31); + + mdp_video_write(ctx, MDSS_MDP_REG_INTF_CONFIG, fetch_enable); + mdp_video_write(ctx, MDSS_MDP_REG_INTF_PROG_FETCH_START, + fetch_start); + + MDSS_XLOG(ctx->intf_num, fetch_enable, fetch_start); return; } @@ -1896,6 +1974,58 @@ static void mdss_mdp_handoff_programmable_fetch(struct mdss_mdp_ctl *ctl, } } +static int mdss_mdp_video_intf_callback(void *data, int event) +{ + struct mdss_mdp_video_ctx *ctx; + struct mdss_mdp_ctl *ctl = data; + struct mdss_panel_info *pinfo; + u32 line_cnt, min_ln_cnt, active_lns_cnt, line_buff = 50; + + if (!data) { + pr_err("%s: invalid ctl\n", __func__); + return -EINVAL; + } + + ctx = ctl->intf_ctx[MASTER_CTX]; + pr_debug("%s: ctl num = %d, event = %d\n", + __func__, ctl->num, event); + + if (!ctl->is_video_mode) + return 0; + + pinfo = &ctl->panel_data->panel_info; + min_ln_cnt = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width; + active_lns_cnt = pinfo->yres; + + switch (event) { + case MDP_INTF_CALLBACK_CHECK_LINE_COUNT: + if (!ctl || !ctx || !ctx->timegen_en) { + pr_debug("%s: no need to check for active line\n", + __func__); + goto end; + } + + line_cnt = mdss_mdp_video_line_count(ctl); + + if ((line_cnt >= min_ln_cnt) && (line_cnt < + (min_ln_cnt + active_lns_cnt - line_buff))) { + pr_debug("%s: line count is within active range=%d\n", + __func__, line_cnt); + goto end; + } else { + pr_debug("line count is less. line_cnt = %d\n", + line_cnt); + return -EPERM; + } + break; + default: + pr_debug("%s: unhandled event!\n", __func__); + break; + } +end: + return 0; +} + static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, struct mdss_mdp_video_ctx *ctx, struct mdss_panel_info *pinfo) { @@ -1929,6 +2059,20 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, pr_err("Failed to register intf recovery handler\n"); return -EINVAL; } + + ctx->intf_mdp_callback.fxn = mdss_mdp_video_intf_callback; + ctx->intf_mdp_callback.data = ctl; + mdss_mdp_ctl_intf_event(ctl, + MDSS_EVENT_REGISTER_MDP_CALLBACK, + (void *)&ctx->intf_mdp_callback, + CTL_INTF_EVENT_FLAG_DEFAULT); + + ctx->intf_clamp_handler.fxn = mdss_mdp_video_intf_clamp_ctrl; + ctx->intf_clamp_handler.data = ctx; + mdss_mdp_ctl_intf_event(ctl, + MDSS_EVENT_REGISTER_CLAMP_HANDLER, + (void *)&ctx->intf_clamp_handler, + CTL_INTF_EVENT_FLAG_DEFAULT); } else { ctx->intf_recovery.fxn = NULL; ctx->intf_recovery.data = NULL; @@ -2348,6 +2492,7 @@ int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl) ctl->ops.early_wake_up_fnc = mdss_mdp_video_early_wake_up; ctl->ops.update_lineptr = mdss_mdp_video_lineptr_ctrl; ctl->ops.avr_ctrl_fnc = mdss_mdp_video_avr_ctrl; + ctl->ops.wait_for_vsync_fnc = NULL; return 0; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index c9e32d69d444..7c6938d40e0b 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -1123,6 +1123,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd, int ret = 0; u32 left_lm_w = left_lm_w_from_mfd(mfd); u64 flags; + bool is_right_blend = false; struct mdss_mdp_mixer *mixer = NULL; struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); @@ -1221,6 +1222,15 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd, goto end; } + /* scaling is not allowed for solid_fill layers */ + if ((pipe->flags & MDP_SOLID_FILL) && + ((pipe->src.w != pipe->dst.w) || + (pipe->src.h != pipe->dst.h))) { + pr_err("solid fill pipe:%d cannot have scaling\n", pipe->num); + ret = -EINVAL; + goto end; + } + /* * unstage the pipe if it's current z_order does not match with new * z_order because client may only call the validate. @@ -1234,6 +1244,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd, * staging, same pipe will be stagged on both layer mixers. */ if (mdata->has_src_split) { + is_right_blend = pipe->is_right_blend; if (left_blend_pipe) { if (__validate_pipe_priorities(left_blend_pipe, pipe)) { pr_err("priority limitation. left:%d rect:%d, right:%d rect:%d\n", @@ -1245,7 +1256,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd, goto end; } else { pr_debug("pipe%d is a right_pipe\n", pipe->num); - pipe->is_right_blend = true; + is_right_blend = true; } } else if (pipe->is_right_blend) { /* @@ -1254,7 +1265,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd, */ mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left); mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right); - pipe->is_right_blend = false; + is_right_blend = false; } if (is_split_lm(mfd) && __layer_needs_src_split(layer)) { @@ -1280,6 +1291,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd, } pipe->src_split_req = false; } + pipe->is_right_blend = is_right_blend; } pipe->multirect.mode = vinfo->multirect.mode; @@ -2261,6 +2273,78 @@ static int __validate_multirect(struct msm_fb_data_type *mfd, return 0; } +static int __check_source_split(struct mdp_input_layer *layer_list, + struct mdss_mdp_pipe **pipe_list, u32 index, + u32 left_lm_w, struct mdss_mdp_pipe **left_blend_pipe) +{ + int i = index - 1; + struct mdp_input_layer *curr, *prev; + struct mdp_rect *left, *right; + bool match = false; + struct mdss_mdp_pipe *left_pipe = NULL; + + /* + * check if current layer is at same z_order as any of the + * previous layers, and fail if any or both are async layers, + * as async layers should have unique z_order. + * + * If it has same z_order and qualifies as a right blend, + * pass a pointer to the pipe representing previous overlay or + * in other terms left blend layer. + * + * Following logic of selecting left_blend has an inherent + * assumption that layer list is sorted on dst_x within a + * same z_order. Otherwise it will fail based on z_order checks. + */ + curr = &layer_list[index]; + + while (i >= 0) { + if (layer_list[i].z_order == curr->z_order) { + pr_debug("z=%d found match @ %d of %d\n", + curr->z_order, i, index); + match = true; + break; + } + i--; + } + + if (match) { + left_pipe = pipe_list[i]; + prev = &layer_list[i]; + left = &prev->dst_rect; + right = &curr->dst_rect; + + if ((curr->flags & MDP_LAYER_ASYNC) + || (prev->flags & MDP_LAYER_ASYNC)) { + curr->error_code = -EINVAL; + pr_err("async curr should have unique z_order\n"); + return curr->error_code; + } + + /* + * check if curr is right blend by checking it's + * directly to the right. + */ + if (((left->x + left->w) == right->x) && + (left->y == right->y) && (left->h == right->h)) { + *left_blend_pipe = left_pipe; + MDSS_XLOG(curr->z_order, i, index); + } + + /* + * if the curr is right at the left lm boundary and + * src split is not required then right blend is not + * required as it will lie only on the left mixer + */ + if (!__layer_needs_src_split(prev) && + ((left->x + left->w) == left_lm_w)) + *left_blend_pipe = NULL; + } + + return 0; +} + + /* * __validate_layers() - validate input layers * @mfd: Framebuffer data structure for display @@ -2291,13 +2375,14 @@ static int __validate_layers(struct msm_fb_data_type *mfd, struct mdss_data_type *mdata = mfd_to_mdata(mfd); struct mdss_mdp_mixer *mixer = NULL; - struct mdp_input_layer *layer, *prev_layer, *layer_list; + struct mdp_input_layer *layer, *layer_list; struct mdss_mdp_validate_info_t *validate_info_list = NULL; bool is_single_layer = false, force_validate; enum layer_pipe_q pipe_q_type; enum layer_zorder_used zorder_used[MDSS_MDP_MAX_STAGE] = {0}; enum mdss_mdp_pipe_rect rect_num; struct mdp_destination_scaler_data *ds_data; + struct mdss_mdp_pipe *pipe_list[MAX_LAYER_COUNT] = {0}; ret = mutex_lock_interruptible(&mdp5_data->ov_lock); if (ret) @@ -2369,49 +2454,10 @@ static int __validate_layers(struct msm_fb_data_type *mfd, dst_x = layer->dst_rect.x; left_blend_pipe = NULL; - prev_layer = (i > 0) ? &layer_list[i - 1] : NULL; - /* - * check if current layer is at same z_order as - * previous one, and fail if any or both are async layers, - * as async layers should have unique z_order. - * - * If it has same z_order and qualifies as a right blend, - * pass a pointer to the pipe representing previous overlay or - * in other terms left blend layer. - * - * Following logic of selecting left_blend has an inherent - * assumption that layer list is sorted on dst_x within a - * same z_order. Otherwise it will fail based on z_order checks. - */ - if (prev_layer && (prev_layer->z_order == layer->z_order)) { - struct mdp_rect *left = &prev_layer->dst_rect; - struct mdp_rect *right = &layer->dst_rect; - - if ((layer->flags & MDP_LAYER_ASYNC) - || (prev_layer->flags & MDP_LAYER_ASYNC)) { - ret = -EINVAL; - layer->error_code = ret; - pr_err("async layer should have unique z_order\n"); - goto validate_exit; - } - - /* - * check if layer is right blend by checking it's - * directly to the right. - */ - if (((left->x + left->w) == right->x) && - (left->y == right->y) && (left->h == right->h)) - left_blend_pipe = pipe; - - /* - * if the layer is right at the left lm boundary and - * src split is not required then right blend is not - * required as it will lie only on the left mixer - */ - if (!__layer_needs_src_split(prev_layer) && - ((left->x + left->w) == left_lm_w)) - left_blend_pipe = NULL; - } + if ((i > 0) && + __check_source_split(layer_list, pipe_list, i, left_lm_w, + &left_blend_pipe)) + goto validate_exit; if (!is_split_lm(mfd) || __layer_needs_src_split(layer)) z = LAYER_ZORDER_BOTH; @@ -2456,6 +2502,8 @@ static int __validate_layers(struct msm_fb_data_type *mfd, else left_plist[left_cnt++] = pipe; + pipe_list[i] = pipe; + if (layer->flags & MDP_LAYER_PP) { memcpy(&pipe->pp_cfg, layer->pp_info, sizeof(struct mdp_overlay_pp_params)); @@ -2548,6 +2596,8 @@ static int __validate_layers(struct msm_fb_data_type *mfd, else left_plist[left_cnt++] = pipe; + pipe_list[i] = pipe; + pr_debug("id:0x%x flags:0x%x dst_x:%d\n", layer->pipe_ndx, layer->flags, layer->dst_rect.x); layer->z_order -= MDSS_MDP_STAGE_0; diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 9e295815da77..5daa8a7a2752 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -2322,14 +2322,12 @@ set_roi: } /* - * Enables/disable secure (display or camera) sessions + * Check if there is any change in secure state and store it. */ -static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd) +static void __overlay_set_secure_transition_state(struct msm_fb_data_type *mfd) { struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); - struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); struct mdss_mdp_pipe *pipe; - int ret = 0; int sd_in_pipe = 0; int sc_in_pipe = 0; u64 pipes_flags = 0; @@ -2350,25 +2348,53 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd) MDSS_XLOG(sd_in_pipe, sc_in_pipe, pipes_flags, mdp5_data->sc_enabled, mdp5_data->sd_enabled); pr_debug("sd:%d sd_in_pipe:%d sc:%d sc_in_pipe:%d flags:0x%llx\n", - mdp5_data->sd_enabled, sd_in_pipe, - mdp5_data->sc_enabled, sc_in_pipe, pipes_flags); + mdp5_data->sd_enabled, sd_in_pipe, + mdp5_data->sc_enabled, sc_in_pipe, pipes_flags); + + /* Reset the secure transition state */ + mdp5_data->secure_transition_state = SECURE_TRANSITION_NONE; /* - * Return early in only two conditions: - * 1. All the features are already disabled and state remains - * disabled for the pipes. - * 2. One of the features is already enabled and state remains - * enabled for the pipes. - */ + * Secure transition would be NONE in two conditions: + * 1. All the features are already disabled and state remains + * disabled for the pipes. + * 2. One of the features is already enabled and state remains + * enabled for the pipes. + */ if (!sd_in_pipe && !mdp5_data->sd_enabled && !sc_in_pipe && !mdp5_data->sc_enabled) - return ret; + return; else if ((sd_in_pipe && mdp5_data->sd_enabled) || (sc_in_pipe && mdp5_data->sc_enabled)) + return; + + /* Secure Display */ + if (!mdp5_data->sd_enabled && sd_in_pipe) + mdp5_data->secure_transition_state |= SD_NON_SECURE_TO_SECURE; + else if (mdp5_data->sd_enabled && !sd_in_pipe) + mdp5_data->secure_transition_state |= SD_SECURE_TO_NON_SECURE; + + /* Secure Camera */ + if (!mdp5_data->sc_enabled && sc_in_pipe) + mdp5_data->secure_transition_state |= SC_NON_SECURE_TO_SECURE; + else if (mdp5_data->sc_enabled && !sc_in_pipe) + mdp5_data->secure_transition_state |= SC_SECURE_TO_NON_SECURE; +} + +/* + * Enable/disable secure (display or camera) sessions + */ +static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd) +{ + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); + int ret = 0; + + if (mdp5_data->secure_transition_state == SECURE_TRANSITION_NONE) return ret; /* Secure Display */ - if (!mdp5_data->sd_enabled && sd_in_pipe) { + if (mdp5_data->secure_transition_state == SD_NON_SECURE_TO_SECURE) { if (!mdss_get_sd_client_cnt()) { MDSS_XLOG(0x11); /*wait for ping pong done */ @@ -2390,7 +2416,8 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd) } mdp5_data->sd_enabled = 1; mdss_update_sd_client(mdp5_data->mdata, true); - } else if (mdp5_data->sd_enabled && !sd_in_pipe) { + } else if (mdp5_data->secure_transition_state == + SD_SECURE_TO_NON_SECURE) { /* disable the secure display on last client */ if (mdss_get_sd_client_cnt() == 1) { MDSS_XLOG(0x22); @@ -2408,11 +2435,9 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd) } /* Secure Camera */ - if (!mdp5_data->sc_enabled && sc_in_pipe) { + if (mdp5_data->secure_transition_state == SC_NON_SECURE_TO_SECURE) { if (!mdss_get_sc_client_cnt()) { MDSS_XLOG(0x33); - if (ctl->ops.wait_pingpong) - mdss_mdp_display_wait4pingpong(ctl, true); ret = mdss_mdp_secure_session_ctrl(1, MDP_SECURE_CAMERA_OVERLAY_SESSION); if (ret) { @@ -2422,7 +2447,8 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd) } mdp5_data->sc_enabled = 1; mdss_update_sc_client(mdp5_data->mdata, true); - } else if (mdp5_data->sc_enabled && !sc_in_pipe) { + } else if (mdp5_data->secure_transition_state == + SC_SECURE_TO_NON_SECURE) { /* disable the secure camera on last client */ if (mdss_get_sc_client_cnt() == 1) { MDSS_XLOG(0x44); @@ -2501,15 +2527,23 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd, list_move(&pipe->list, &mdp5_data->pipes_destroy); } + __overlay_set_secure_transition_state(mfd); /* * go to secure state if required, this should be done * after moving the buffers from the previous commit to - * destroy list + * destroy list. + * For video mode panels, secure display/camera should be disabled + * after flushing the new buffer. Skip secure disable here for those + * cases. */ - ret = __overlay_secure_ctrl(mfd); - if (IS_ERR_VALUE(ret)) { - pr_err("secure operation failed %d\n", ret); - goto commit_fail; + if (!((mfd->panel_info->type == MIPI_VIDEO_PANEL) && + ((mdp5_data->secure_transition_state == SD_SECURE_TO_NON_SECURE) || + (mdp5_data->secure_transition_state == SC_SECURE_TO_NON_SECURE)))) { + ret = __overlay_secure_ctrl(mfd); + if (IS_ERR_VALUE(ret)) { + pr_err("secure operation failed %d\n", ret); + goto commit_fail; + } } /* call this function before any registers programming */ @@ -2581,6 +2615,17 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd, mutex_lock(&mdp5_data->ov_lock); + /* Disable secure display/camera for video mode panels */ + if ((mfd->panel_info->type == MIPI_VIDEO_PANEL) && + ((mdp5_data->secure_transition_state == SD_SECURE_TO_NON_SECURE) || + (mdp5_data->secure_transition_state == SC_SECURE_TO_NON_SECURE))) { + ret = __overlay_secure_ctrl(mfd); + if (IS_ERR_VALUE(ret)) { + pr_err("secure operation failed %d\n", ret); + goto commit_fail; + } + } + mdss_fb_update_notify_update(mfd); commit_fail: ATRACE_BEGIN("overlay_cleanup"); @@ -4367,12 +4412,21 @@ static int mdss_mdp_hw_cursor_pipe_update(struct msm_fb_data_type *mfd, start_y = 0; } + if ((img->width > mdata->max_cursor_size) || + (img->height > mdata->max_cursor_size) || + (img->depth != 32) || (start_x >= xres) || + (start_y >= yres)) { + pr_err("Invalid cursor image coordinates\n"); + ret = -EINVAL; + goto done; + } + roi.w = min(xres - start_x, img->width - roi.x); roi.h = min(yres - start_y, img->height - roi.y); if ((roi.w > mdata->max_cursor_size) || - (roi.h > mdata->max_cursor_size) || - (img->depth != 32) || (start_x >= xres) || (start_y >= yres)) { + (roi.h > mdata->max_cursor_size)) { + pr_err("Invalid cursor ROI size\n"); ret = -EINVAL; goto done; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index 1fe8fe6f7be8..f10d4fb60f52 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -2146,6 +2146,7 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix, unsigned long flag; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); u32 intr_mask; + u32 expected_sum = 0; if (!mdata) return -EPERM; @@ -2156,6 +2157,7 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix, block_type = DSPP; op_flags = BIT(16); hist_info = &mdss_pp_res->dspp_hist[mix->num]; + expected_sum = mix->width * mix->height; base = mdss_mdp_get_dspp_addr_off(PP_BLOCK(block)); if (IS_ERR(base)) { ret = -EPERM; @@ -2207,6 +2209,15 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix, else if (hist_info->col_en) *op |= op_flags; + if (hist_info->col_en) { + if (!hist_info->expect_sum) { + hist_info->expect_sum = expected_sum; + } else if (hist_info->expect_sum != expected_sum) { + hist_info->expect_sum = 0; + hist_info->next_sum = expected_sum; + } + } + spin_unlock_irqrestore(&hist_info->hist_lock, flag); mutex_unlock(&hist_info->hist_mutex); error: @@ -5276,8 +5287,7 @@ exit: static int pp_hist_collect(struct mdp_histogram_data *hist, struct pp_hist_col_info *hist_info, - char __iomem *ctl_base, u32 expect_sum, - u32 block) + char __iomem *ctl_base, u32 block) { int ret = 0; int sum = 0; @@ -5318,11 +5328,16 @@ static int pp_hist_collect(struct mdp_histogram_data *hist, if (sum < 0) { pr_err("failed to get the hist data, sum = %d\n", sum); ret = sum; - } else if (expect_sum && sum != expect_sum) { + } else if (hist_info->expect_sum && sum != hist_info->expect_sum) { pr_err_ratelimited("hist error: bin sum incorrect! (%d/%d)\n", - sum, expect_sum); + sum, hist_info->expect_sum); ret = -EINVAL; } + + if (hist_info->next_sum) { + hist_info->expect_sum = hist_info->next_sum; + hist_info->next_sum = 0; + } hist_collect_exit: mutex_unlock(&hist_info->hist_mutex); return ret; @@ -5387,8 +5402,7 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist) mdata->mixer_intf[dspp_num].height); if (ret) temp_ret = ret; - ret = pp_hist_collect(hist, hists[i], ctl_base, - exp_sum, DSPP); + ret = pp_hist_collect(hist, hists[i], ctl_base, DSPP); if (ret) pr_err_ratelimited("hist error: dspp[%d] collect %d\n", dspp_num, ret); @@ -5487,7 +5501,7 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist) if (ret) temp_ret = ret; ret = pp_hist_collect(hist, hist_info, ctl_base, - exp_sum, SSPP_VIG); + SSPP_VIG); if (ret) pr_debug("hist error: pipe[%d] collect: %d\n", pipe->num, ret); diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c index c14840ffd08d..d0bf61679f61 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_util.c +++ b/drivers/video/fbdev/msm/mdss_mdp_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -974,7 +974,9 @@ static int mdss_mdp_put_img(struct mdss_mdp_img_data *data, bool rotator, * be filled due to map call which will be unmapped above. * */ - pr_debug("skip memory unmapping for secure display/camera content\n"); + if (data->ihandle) + ion_free(iclient, data->ihandle); + pr_debug("free memory handle for secure display/camera content\n"); } else { return -ENOMEM; } @@ -1053,19 +1055,18 @@ static int mdss_mdp_get_img(struct msmfb_data *img, ret = 0; goto done; } else { - struct ion_handle *ihandle = NULL; struct sg_table *sg_ptr = NULL; + data->ihandle = ion_import_dma_buf(iclient, + img->memory_id); + if (IS_ERR_OR_NULL(data->ihandle)) { + ret = -EINVAL; + pr_err("ion import buffer failed\n"); + data->ihandle = NULL; + goto done; + } do { - ihandle = ion_import_dma_buf(iclient, - img->memory_id); - if (IS_ERR_OR_NULL(ihandle)) { - ret = -EINVAL; - pr_err("ion import buffer failed\n"); - break; - } - - sg_ptr = ion_sg_table(iclient, ihandle); + sg_ptr = ion_sg_table(iclient, data->ihandle); if (sg_ptr == NULL) { pr_err("ion sg table get failed\n"); ret = -EINVAL; @@ -1091,8 +1092,6 @@ static int mdss_mdp_get_img(struct msmfb_data *img, ret = 0; } while (0); - if (!IS_ERR_OR_NULL(ihandle)) - ion_free(iclient, ihandle); return ret; } } diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index e466c0097540..92413e078244 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -29,6 +29,9 @@ struct panel_id { #define DEFAULT_FRAME_RATE 60 #define DEFAULT_ROTATOR_FRAME_RATE 120 #define ROTATOR_LOW_FRAME_RATE 30 + +#define MDSS_DSI_MAX_ESC_CLK_RATE_HZ 19200000 + #define MDSS_DSI_RST_SEQ_LEN 10 /* worst case prefill lines for all chipsets including all vertical blank */ #define MDSS_MDP_MAX_PREFILL_FETCH 25 @@ -112,12 +115,6 @@ enum { }; enum { - MDSS_PANEL_BLANK_BLANK = 0, - MDSS_PANEL_BLANK_UNBLANK, - MDSS_PANEL_BLANK_LOW_POWER, -}; - -enum { MDSS_PANEL_LOW_PERSIST_MODE_OFF = 0, MDSS_PANEL_LOW_PERSIST_MODE_ON, }; @@ -192,10 +189,16 @@ struct mdss_panel_cfg { enum { MDP_INTF_CALLBACK_DSI_WAIT, + MDP_INTF_CALLBACK_CHECK_LINE_COUNT, }; struct mdss_intf_recovery { - void (*fxn)(void *ctx, int event); + int (*fxn)(void *ctx, int event); + void *data; +}; + +struct mdss_intf_ulp_clamp { + int (*fxn)(void *ctx, int intf_num, bool enable); void *data; }; @@ -304,6 +307,7 @@ enum mdss_intf_events { MDSS_EVENT_UPDATE_PANEL_PPM, MDSS_EVENT_DSI_TIMING_DB_CTRL, MDSS_EVENT_AVR_MODE, + MDSS_EVENT_REGISTER_CLAMP_HANDLER, MDSS_EVENT_MAX, }; @@ -360,6 +364,8 @@ static inline char *mdss_panel_intf_event_to_string(int event) return INTF_EVENT_STR(MDSS_EVENT_REGISTER_RECOVERY_HANDLER); case MDSS_EVENT_REGISTER_MDP_CALLBACK: return INTF_EVENT_STR(MDSS_EVENT_REGISTER_MDP_CALLBACK); + case MDSS_EVENT_REGISTER_CLAMP_HANDLER: + return INTF_EVENT_STR(MDSS_EVENT_REGISTER_CLAMP_HANDLER); case MDSS_EVENT_DSI_PANEL_STATUS: return INTF_EVENT_STR(MDSS_EVENT_DSI_PANEL_STATUS); case MDSS_EVENT_DSI_DYNAMIC_SWITCH: @@ -915,6 +921,9 @@ struct mdss_panel_info { /* HDR properties of display panel*/ struct mdss_panel_hdr_properties hdr_properties; + + /* esc clk recommended for the panel */ + u32 esc_clk_rate_hz; }; struct mdss_panel_timing { diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c index 0307d570ff64..fdd1c0153ce0 100644 --- a/drivers/video/fbdev/msm/mdss_rotator.c +++ b/drivers/video/fbdev/msm/mdss_rotator.c @@ -2065,10 +2065,12 @@ static int mdss_rotator_config_session(struct mdss_rot_mgr *mgr, return ret; } + mutex_lock(&mgr->lock); perf = mdss_rotator_find_session(private, config.session_id); if (!perf) { pr_err("No session with id=%u could be found\n", config.session_id); + mutex_unlock(&mgr->lock); return -EINVAL; } @@ -2091,6 +2093,7 @@ static int mdss_rotator_config_session(struct mdss_rot_mgr *mgr, config.output.format); done: ATRACE_END(__func__); + mutex_unlock(&mgr->lock); return ret; } diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index e6151b4c75a1..03e78733d168 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1863,8 +1863,10 @@ static int mdss_dsi_ulps_config_default(struct mdss_dsi_ctrl_pdata *ctrl, * to be in stop state. */ MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes << 16); + wmb(); /* ensure lanes are put to stop state */ MIPI_OUTP(ctrl->ctrl_base + 0x0AC, 0x0); + wmb(); /* ensure lanes are in proper state */ lane_status = MIPI_INP(ctrl->ctrl_base + 0xA8); } @@ -1983,6 +1985,7 @@ static int mdss_dsi_clamp_ctrl_default(struct mdss_dsi_ctrl_pdata *ctrl, struct mipi_panel_info *mipi = NULL; u32 clamp_reg, regval = 0; u32 clamp_reg_off; + u32 intf_num = 0; if (!ctrl) { pr_err("%s: invalid input\n", __func__); @@ -1994,6 +1997,21 @@ static int mdss_dsi_clamp_ctrl_default(struct mdss_dsi_ctrl_pdata *ctrl, return -EINVAL; } + /* + * For DSI HW version 2.1.0 ULPS_CLAMP register + * is moved to interface level. + */ + if (ctrl->shared_data->hw_rev == MDSS_DSI_HW_REV_201) { + intf_num = ctrl->ndx ? MDSS_MDP_INTF2 : MDSS_MDP_INTF1; + if (ctrl->clamp_handler) { + ctrl->clamp_handler->fxn(ctrl->clamp_handler->data, + intf_num, enable); + pr_debug("%s: ndx: %d enable: %d\n", + __func__, ctrl->ndx, enable); + } + return 0; + } + clamp_reg_off = ctrl->shared_data->ulps_clamp_ctrl_off; mipi = &ctrl->panel_data.panel_info.mipi; @@ -2256,6 +2274,8 @@ int mdss_dsi_pre_clkoff_cb(void *priv, pdata = &ctrl->panel_data; if ((clk & MDSS_DSI_LINK_CLK) && (new_state == MDSS_DSI_CLK_OFF)) { + if (pdata->panel_info.mipi.force_clk_lane_hs) + mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 0); /* * If ULPS feature is enabled, enter ULPS first. * However, when blanking the panel, we should enter ULPS @@ -2371,6 +2391,8 @@ int mdss_dsi_post_clkon_cb(void *priv, goto error; } } + if (pdata->panel_info.mipi.force_clk_lane_hs) + mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 1); } error: return rc; diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c index b79a74a98a23..ad94d8a45728 100644 --- a/drivers/vme/bridges/vme_ca91cx42.c +++ b/drivers/vme/bridges/vme_ca91cx42.c @@ -467,7 +467,7 @@ static int ca91cx42_slave_get(struct vme_slave_resource *image, int *enabled, vme_bound = ioread32(bridge->base + CA91CX42_VSI_BD[i]); pci_offset = ioread32(bridge->base + CA91CX42_VSI_TO[i]); - *pci_base = (dma_addr_t)vme_base + pci_offset; + *pci_base = (dma_addr_t)*vme_base + pci_offset; *size = (unsigned long long)((vme_bound - *vme_base) + granularity); *enabled = 0; diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index 049a884a756f..59d74d1b47a8 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -153,6 +153,9 @@ struct ds_device */ u16 spu_bit; + u8 st_buf[ST_SIZE]; + u8 byte_buf; + struct w1_bus_master master; }; @@ -174,7 +177,6 @@ struct ds_status u8 data_in_buffer_status; u8 reserved1; u8 reserved2; - }; static struct usb_device_id ds_id_table [] = { @@ -244,28 +246,6 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index) return err; } -static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st, - unsigned char *buf, int size) -{ - int count, err; - - memset(st, 0, sizeof(*st)); - - count = 0; - err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev, - dev->ep[EP_STATUS]), buf, size, &count, 1000); - if (err < 0) { - pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n", - dev->ep[EP_STATUS], err); - return err; - } - - if (count >= sizeof(*st)) - memcpy(st, buf, sizeof(*st)); - - return count; -} - static inline void ds_print_msg(unsigned char *buf, unsigned char *str, int off) { pr_info("%45s: %8x\n", str, buf[off]); @@ -324,6 +304,35 @@ static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count) } } +static int ds_recv_status(struct ds_device *dev, struct ds_status *st, + bool dump) +{ + int count, err; + + if (st) + memset(st, 0, sizeof(*st)); + + count = 0; + err = usb_interrupt_msg(dev->udev, + usb_rcvintpipe(dev->udev, + dev->ep[EP_STATUS]), + dev->st_buf, sizeof(dev->st_buf), + &count, 1000); + if (err < 0) { + pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n", + dev->ep[EP_STATUS], err); + return err; + } + + if (dump) + ds_dump_status(dev, dev->st_buf, count); + + if (st && count >= sizeof(*st)) + memcpy(st, dev->st_buf, sizeof(*st)); + + return count; +} + static void ds_reset_device(struct ds_device *dev) { ds_send_control_cmd(dev, CTL_RESET_DEVICE, 0); @@ -344,7 +353,6 @@ static void ds_reset_device(struct ds_device *dev) static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) { int count, err; - struct ds_status st; /* Careful on size. If size is less than what is available in * the input buffer, the device fails the bulk transfer and @@ -359,14 +367,9 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]), buf, size, &count, 1000); if (err < 0) { - u8 buf[ST_SIZE]; - int count; - pr_info("Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]); usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN])); - - count = ds_recv_status_nodump(dev, &st, buf, sizeof(buf)); - ds_dump_status(dev, buf, count); + ds_recv_status(dev, NULL, true); return err; } @@ -404,7 +407,6 @@ int ds_stop_pulse(struct ds_device *dev, int limit) { struct ds_status st; int count = 0, err = 0; - u8 buf[ST_SIZE]; do { err = ds_send_control(dev, CTL_HALT_EXE_IDLE, 0); @@ -413,7 +415,7 @@ int ds_stop_pulse(struct ds_device *dev, int limit) err = ds_send_control(dev, CTL_RESUME_EXE, 0); if (err) break; - err = ds_recv_status_nodump(dev, &st, buf, sizeof(buf)); + err = ds_recv_status(dev, &st, false); if (err) break; @@ -456,18 +458,17 @@ int ds_detect(struct ds_device *dev, struct ds_status *st) static int ds_wait_status(struct ds_device *dev, struct ds_status *st) { - u8 buf[ST_SIZE]; int err, count = 0; do { st->status = 0; - err = ds_recv_status_nodump(dev, st, buf, sizeof(buf)); + err = ds_recv_status(dev, st, false); #if 0 if (err >= 0) { int i; printk("0x%x: count=%d, status: ", dev->ep[EP_STATUS], err); for (i=0; i<err; ++i) - printk("%02x ", buf[i]); + printk("%02x ", dev->st_buf[i]); printk("\n"); } #endif @@ -485,7 +486,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st) * can do something with it). */ if (err > 16 || count >= 100 || err < 0) - ds_dump_status(dev, buf, err); + ds_dump_status(dev, dev->st_buf, err); /* Extended data isn't an error. Well, a short is, but the dump * would have already told the user that and we can't do anything @@ -608,7 +609,6 @@ static int ds_write_byte(struct ds_device *dev, u8 byte) { int err; struct ds_status st; - u8 rbyte; err = ds_send_control(dev, COMM_BYTE_IO | COMM_IM | dev->spu_bit, byte); if (err) @@ -621,11 +621,11 @@ static int ds_write_byte(struct ds_device *dev, u8 byte) if (err) return err; - err = ds_recv_data(dev, &rbyte, sizeof(rbyte)); + err = ds_recv_data(dev, &dev->byte_buf, 1); if (err < 0) return err; - return !(byte == rbyte); + return !(byte == dev->byte_buf); } static int ds_read_byte(struct ds_device *dev, u8 *byte) @@ -712,7 +712,6 @@ static void ds9490r_search(void *data, struct w1_master *master, int err; u16 value, index; struct ds_status st; - u8 st_buf[ST_SIZE]; int search_limit; int found = 0; int i; @@ -724,7 +723,12 @@ static void ds9490r_search(void *data, struct w1_master *master, /* FIFO 128 bytes, bulk packet size 64, read a multiple of the * packet size. */ - u64 buf[2*64/8]; + const size_t bufsize = 2 * 64; + u64 *buf; + + buf = kmalloc(bufsize, GFP_KERNEL); + if (!buf) + return; mutex_lock(&master->bus_mutex); @@ -745,10 +749,9 @@ static void ds9490r_search(void *data, struct w1_master *master, do { schedule_timeout(jtime); - if (ds_recv_status_nodump(dev, &st, st_buf, sizeof(st_buf)) < - sizeof(st)) { + err = ds_recv_status(dev, &st, false); + if (err < 0 || err < sizeof(st)) break; - } if (st.data_in_buffer_status) { /* Bulk in can receive partial ids, but when it does @@ -758,7 +761,7 @@ static void ds9490r_search(void *data, struct w1_master *master, * bulk without first checking if status says there * is data to read. */ - err = ds_recv_data(dev, (u8 *)buf, sizeof(buf)); + err = ds_recv_data(dev, (u8 *)buf, bufsize); if (err < 0) break; for (i = 0; i < err/8; ++i) { @@ -794,9 +797,14 @@ static void ds9490r_search(void *data, struct w1_master *master, } search_out: mutex_unlock(&master->bus_mutex); + kfree(buf); } #if 0 +/* + * FIXME: if this disabled code is ever used in the future all ds_send_data() + * calls must be changed to use a DMAable buffer. + */ static int ds_match_access(struct ds_device *dev, u64 init) { int err; @@ -845,13 +853,12 @@ static int ds_set_path(struct ds_device *dev, u64 init) static u8 ds9490r_touch_bit(void *data, u8 bit) { - u8 ret; struct ds_device *dev = data; - if (ds_touch_bit(dev, bit, &ret)) + if (ds_touch_bit(dev, bit, &dev->byte_buf)) return 0; - return ret; + return dev->byte_buf; } #if 0 @@ -866,13 +873,12 @@ static u8 ds9490r_read_bit(void *data) { struct ds_device *dev = data; int err; - u8 bit = 0; - err = ds_touch_bit(dev, 1, &bit); + err = ds_touch_bit(dev, 1, &dev->byte_buf); if (err) return 0; - return bit & 1; + return dev->byte_buf & 1; } #endif @@ -887,32 +893,52 @@ static u8 ds9490r_read_byte(void *data) { struct ds_device *dev = data; int err; - u8 byte = 0; - err = ds_read_byte(dev, &byte); + err = ds_read_byte(dev, &dev->byte_buf); if (err) return 0; - return byte; + return dev->byte_buf; } static void ds9490r_write_block(void *data, const u8 *buf, int len) { struct ds_device *dev = data; + u8 *tbuf; + + if (len <= 0) + return; + + tbuf = kmalloc(len, GFP_KERNEL); + if (!tbuf) + return; - ds_write_block(dev, (u8 *)buf, len); + memcpy(tbuf, buf, len); + ds_write_block(dev, tbuf, len); + + kfree(tbuf); } static u8 ds9490r_read_block(void *data, u8 *buf, int len) { struct ds_device *dev = data; int err; + u8 *tbuf; - err = ds_read_block(dev, buf, len); - if (err < 0) + if (len <= 0) + return 0; + + tbuf = kmalloc(len, GFP_KERNEL); + if (!tbuf) return 0; - return len; + err = ds_read_block(dev, tbuf, len); + if (err >= 0) + memcpy(buf, tbuf, len); + + kfree(tbuf); + + return err >= 0 ? len : 0; } static u8 ds9490r_reset(void *data) diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index c9a7ff67d395..39886edfa222 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -763,6 +763,7 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) dev_err(&dev->dev, "%s: Attaching %s failed.\n", __func__, sl->name); w1_family_put(sl->family); + atomic_dec(&sl->master->refcnt); kfree(sl); return err; } diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 1be5dd048622..308600adf6e0 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -804,7 +804,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) vma->vm_ops = &gntdev_vmops; - vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO; + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_MIXEDMAP; if (use_ptemod) vma->vm_flags |= VM_DONTCOPY; |