diff options
Diffstat (limited to 'drivers')
474 files changed, 11249 insertions, 21955 deletions
diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c index 5f97468df8ff..b2e50d8007fe 100644 --- a/drivers/acpi/acpica/hwxface.c +++ b/drivers/acpi/acpica/hwxface.c @@ -504,11 +504,20 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b) * Evaluate the \_Sx namespace object containing the register values * for this state */ - info->relative_pathname = - ACPI_CAST_PTR(char, acpi_gbl_sleep_state_names[sleep_state]); + info->relative_pathname = ACPI_CAST_PTR(char, + acpi_gbl_sleep_state_names + [sleep_state]); + status = acpi_ns_evaluate(info); if (ACPI_FAILURE(status)) { - goto cleanup; + if (status == AE_NOT_FOUND) { + + /* The _Sx states are optional, ignore NOT_FOUND */ + + goto final_cleanup; + } + + goto warning_cleanup; } /* Must have a return object */ @@ -517,7 +526,7 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b) ACPI_ERROR((AE_INFO, "No Sleep State object returned from [%s]", info->relative_pathname)); status = AE_AML_NO_RETURN_VALUE; - goto cleanup; + goto warning_cleanup; } /* Return object must be of type Package */ @@ -526,7 +535,7 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b) ACPI_ERROR((AE_INFO, "Sleep State return object is not a Package")); status = AE_AML_OPERAND_TYPE; - goto cleanup1; + goto return_value_cleanup; } /* @@ -570,16 +579,17 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b) break; } -cleanup1: +return_value_cleanup: acpi_ut_remove_reference(info->return_object); -cleanup: +warning_cleanup: if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "While evaluating Sleep State [%s]", info->relative_pathname)); } +final_cleanup: ACPI_FREE(info); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 3dd9c462d22a..8f8da9f92090 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -657,7 +657,7 @@ static int ghes_proc(struct ghes *ghes) ghes_do_proc(ghes, ghes->estatus); out: ghes_clear_estatus(ghes); - return 0; + return rc; } static void ghes_add_timer(struct ghes *ghes) diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 5230e8449d30..c097f477c74c 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -1806,6 +1806,9 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event) dev_dbg(dev, "%s: event: %d\n", __func__, event); + if (event != NFIT_NOTIFY_UPDATE) + return; + device_lock(dev); if (!dev->driver) { /* dev->driver may be null if we're being removed */ diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index 3d549a383659..13d6ec1ff055 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h @@ -45,6 +45,10 @@ enum { ND_BLK_DCR_LATCH = 2, }; +enum nfit_root_notifiers { + NFIT_NOTIFY_UPDATE = 0x80, +}; + struct nfit_spa { struct acpi_nfit_system_address *spa; struct list_head list; diff --git a/drivers/android/binder.c b/drivers/android/binder.c index c8c11de7d44c..0f6591a72cf8 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1113,7 +1113,7 @@ static int binder_dec_node(struct binder_node *node, int strong, int internal) static struct binder_ref *binder_get_ref(struct binder_proc *proc, - uint32_t desc, bool need_strong_ref) + u32 desc, bool need_strong_ref) { struct rb_node *n = proc->refs_by_desc.rb_node; struct binder_ref *ref; @@ -2117,7 +2117,7 @@ static void binder_transaction(struct binder_proc *proc, goto err_bad_offset; } if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) { - binder_user_error("%d:%d got transaction with unaligned buffers size, %lld\n", + binder_user_error("%d:%d got transaction with unaligned buffers size, %llu\n", proc->pid, thread->pid, (u64)extra_buffers_size); return_error = BR_FAILED_REPLY; diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c index 016f6f5f1e5e..7bc0d4a24c22 100644 --- a/drivers/base/dma-mapping.c +++ b/drivers/base/dma-mapping.c @@ -344,7 +344,7 @@ void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags, return; } - unmap_kernel_range((unsigned long)cpu_addr, size); + unmap_kernel_range((unsigned long)cpu_addr, PAGE_ALIGN(size)); vunmap(cpu_addr); } #endif diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 87a48268b663..1c6e4da01e69 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -307,8 +307,7 @@ static const char * const fw_path[] = { "/lib/firmware/updates/" UTS_RELEASE, "/lib/firmware/updates", "/lib/firmware/" UTS_RELEASE, - "/lib/firmware", - "/firmware/image" + "/lib/firmware" }; /* diff --git a/drivers/base/platform.c b/drivers/base/platform.c index bd70f8db469b..9920916a6220 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -96,7 +96,7 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) int ret; ret = of_irq_get(dev->dev.of_node, num); - if (ret >= 0 || ret == -EPROBE_DEFER) + if (ret > 0 || ret == -EPROBE_DEFER) return ret; } @@ -174,7 +174,7 @@ int platform_get_irq_byname(struct platform_device *dev, const char *name) int ret; ret = of_irq_get_byname(dev->dev.of_node, name); - if (ret >= 0 || ret == -EPROBE_DEFER) + if (ret > 0 || ret == -EPROBE_DEFER) return ret; } diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 7eea95d490e6..6c5bc3fadfcf 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1025,6 +1025,8 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a TRACE_DEVICE(dev); TRACE_SUSPEND(0); + dpm_wait_for_children(dev, async); + if (async_error) goto Complete; @@ -1036,8 +1038,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a if (dev->power.syscore || dev->power.direct_complete) goto Complete; - dpm_wait_for_children(dev, async); - if (dev->pm_domain) { info = "noirq power domain "; callback = pm_noirq_op(&dev->pm_domain->ops, state); @@ -1172,6 +1172,8 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as __pm_runtime_disable(dev, false); + dpm_wait_for_children(dev, async); + if (async_error) goto Complete; @@ -1183,8 +1185,6 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as if (dev->power.syscore || dev->power.direct_complete) goto Complete; - dpm_wait_for_children(dev, async); - if (dev->pm_domain) { info = "late power domain "; callback = pm_late_early_op(&dev->pm_domain->ops, state); diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 74d97f4bac34..1d58854c4a9f 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -1802,7 +1802,7 @@ int drbd_send(struct drbd_connection *connection, struct socket *sock, * do we need to block DRBD_SIG if sock == &meta.socket ?? * otherwise wake_asender() might interrupt some send_*Ack ! */ - rv = kernel_sendmsg(sock, &msg, &iov, 1, size); + rv = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len); if (rv == -EAGAIN) { if (we_should_drop_the_connection(connection, sock)) break; diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 3c59417b485a..eada042b6eab 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -1381,7 +1381,8 @@ static ssize_t hot_remove_store(struct class *class, zram = idr_find(&zram_index_idr, dev_id); if (zram) { ret = zram_remove(zram); - idr_remove(&zram_index_idr, dev_id); + if (!ret) + idr_remove(&zram_index_idr, dev_id); } else { ret = -ENODEV; } diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c index 7082c7268845..0f54cb7ddcbb 100644 --- a/drivers/bus/arm-ccn.c +++ b/drivers/bus/arm-ccn.c @@ -187,6 +187,7 @@ struct arm_ccn { struct arm_ccn_component *xp; struct arm_ccn_dt dt; + int mn_id; }; @@ -326,6 +327,7 @@ struct arm_ccn_pmu_event { static ssize_t arm_ccn_pmu_event_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev)); struct arm_ccn_pmu_event *event = container_of(attr, struct arm_ccn_pmu_event, attr); ssize_t res; @@ -352,6 +354,9 @@ static ssize_t arm_ccn_pmu_event_show(struct device *dev, res += snprintf(buf + res, PAGE_SIZE - res, ",cmp_l=?,cmp_h=?,mask=?"); break; + case CCN_TYPE_MN: + res += snprintf(buf + res, PAGE_SIZE - res, ",node=%d", ccn->mn_id); + break; default: res += snprintf(buf + res, PAGE_SIZE - res, ",node=?"); break; @@ -381,9 +386,9 @@ static umode_t arm_ccn_pmu_events_is_visible(struct kobject *kobj, } static struct arm_ccn_pmu_event arm_ccn_pmu_events[] = { - CCN_EVENT_MN(eobarrier, "dir=0,vc=0,cmp_h=0x1c00", CCN_IDX_MASK_OPCODE), - CCN_EVENT_MN(ecbarrier, "dir=0,vc=0,cmp_h=0x1e00", CCN_IDX_MASK_OPCODE), - CCN_EVENT_MN(dvmop, "dir=0,vc=0,cmp_h=0x2800", CCN_IDX_MASK_OPCODE), + CCN_EVENT_MN(eobarrier, "dir=1,vc=0,cmp_h=0x1c00", CCN_IDX_MASK_OPCODE), + CCN_EVENT_MN(ecbarrier, "dir=1,vc=0,cmp_h=0x1e00", CCN_IDX_MASK_OPCODE), + CCN_EVENT_MN(dvmop, "dir=1,vc=0,cmp_h=0x2800", CCN_IDX_MASK_OPCODE), CCN_EVENT_HNI(txdatflits, "dir=1,vc=3", CCN_IDX_MASK_ANY), CCN_EVENT_HNI(rxdatflits, "dir=0,vc=3", CCN_IDX_MASK_ANY), CCN_EVENT_HNI(txreqflits, "dir=1,vc=0", CCN_IDX_MASK_ANY), @@ -757,6 +762,12 @@ static int arm_ccn_pmu_event_init(struct perf_event *event) /* Validate node/xp vs topology */ switch (type) { + case CCN_TYPE_MN: + if (node_xp != ccn->mn_id) { + dev_warn(ccn->dev, "Invalid MN ID %d!\n", node_xp); + return -EINVAL; + } + break; case CCN_TYPE_XP: if (node_xp >= ccn->num_xps) { dev_warn(ccn->dev, "Invalid XP ID %d!\n", node_xp); @@ -884,6 +895,10 @@ static void arm_ccn_pmu_xp_dt_config(struct perf_event *event, int enable) struct arm_ccn_component *xp; u32 val, dt_cfg; + /* Nothing to do for cycle counter */ + if (hw->idx == CCN_IDX_PMU_CYCLE_COUNTER) + return; + if (CCN_CONFIG_TYPE(event->attr.config) == CCN_TYPE_XP) xp = &ccn->xp[CCN_CONFIG_XP(event->attr.config)]; else @@ -986,7 +1001,7 @@ static void arm_ccn_pmu_xp_watchpoint_config(struct perf_event *event) /* Comparison values */ writel(cmp_l & 0xffffffff, source->base + CCN_XP_DT_CMP_VAL_L(wp)); - writel((cmp_l >> 32) & 0xefffffff, + writel((cmp_l >> 32) & 0x7fffffff, source->base + CCN_XP_DT_CMP_VAL_L(wp) + 4); writel(cmp_h & 0xffffffff, source->base + CCN_XP_DT_CMP_VAL_H(wp)); writel((cmp_h >> 32) & 0x0fffffff, @@ -994,7 +1009,7 @@ static void arm_ccn_pmu_xp_watchpoint_config(struct perf_event *event) /* Mask */ writel(mask_l & 0xffffffff, source->base + CCN_XP_DT_CMP_MASK_L(wp)); - writel((mask_l >> 32) & 0xefffffff, + writel((mask_l >> 32) & 0x7fffffff, source->base + CCN_XP_DT_CMP_MASK_L(wp) + 4); writel(mask_h & 0xffffffff, source->base + CCN_XP_DT_CMP_MASK_H(wp)); writel((mask_h >> 32) & 0x0fffffff, @@ -1368,6 +1383,8 @@ static int arm_ccn_init_nodes(struct arm_ccn *ccn, int region, switch (type) { case CCN_TYPE_MN: + ccn->mn_id = id; + return 0; case CCN_TYPE_DT: return 0; case CCN_TYPE_XP: diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 88bd6afdeea5..9ca46ae54ce3 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1951,6 +1951,8 @@ static void fastrpc_channel_close(struct kref *kref) cid = ctx - &gcinfo[0]; fastrpc_glink_close(ctx->chan, cid); ctx->chan = 0; + glink_unregister_link_state_cb(ctx->link.link_notify_handle); + ctx->link.link_notify_handle = 0; mutex_unlock(&me->smd_mutex); pr_info("'closed /dev/%s c %d %d'\n", gcinfo[cid].name, MAJOR(me->dev_no), cid); diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 6f497aa1b276..cf25020576fa 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -84,14 +84,14 @@ static size_t rng_buffer_size(void) static void add_early_randomness(struct hwrng *rng) { - unsigned char bytes[16]; int bytes_read; + size_t size = min_t(size_t, 16, rng_buffer_size()); mutex_lock(&reading_mutex); - bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1); + bytes_read = rng_get_data(rng, rng_buffer, size, 1); mutex_unlock(&reading_mutex); if (bytes_read > 0) - add_device_randomness(bytes, bytes_read); + add_device_randomness(rng_buffer, bytes_read); } static inline void cleanup_rng(struct kref *kref) diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index 8a1432e8bb80..f5c26a5f6875 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -384,7 +384,12 @@ static int omap_rng_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to runtime_get device: %d\n", ret); + pm_runtime_put_noidle(&pdev->dev); + goto err_ioremap; + } ret = (dev->of_node) ? of_get_omap_rng_device_details(priv, pdev) : get_omap_rng_device_details(priv); @@ -435,8 +440,15 @@ static int __maybe_unused omap_rng_suspend(struct device *dev) static int __maybe_unused omap_rng_resume(struct device *dev) { struct omap_rng_dev *priv = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to runtime_get device: %d\n", ret); + pm_runtime_put_noidle(dev); + return ret; + } - pm_runtime_get_sync(dev); priv->pdata->init(priv); return 0; diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c index de0337ebd658..4f3137d9a35e 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -139,7 +139,7 @@ static ssize_t tpm_write(struct file *file, const char __user *buf, /* atomic tpm command send and result receive */ out_size = tpm_transmit(priv->chip, priv->data_buffer, - sizeof(priv->data_buffer)); + sizeof(priv->data_buffer), 0); if (out_size < 0) { mutex_unlock(&priv->buffer_mutex); return out_size; diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index c50637db3a8a..17abe52e6365 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -328,8 +328,8 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); /* * Internal kernel interface to transmit TPM commands */ -ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, - size_t bufsiz) +ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, + unsigned int flags) { ssize_t rc; u32 count, ordinal; @@ -348,7 +348,8 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, return -E2BIG; } - mutex_lock(&chip->tpm_mutex); + if (!(flags & TPM_TRANSMIT_UNLOCKED)) + mutex_lock(&chip->tpm_mutex); rc = chip->ops->send(chip, (u8 *) buf, count); if (rc < 0) { @@ -391,20 +392,21 @@ out_recv: dev_err(chip->pdev, "tpm_transmit: tpm_recv: error %zd\n", rc); out: - mutex_unlock(&chip->tpm_mutex); + if (!(flags & TPM_TRANSMIT_UNLOCKED)) + mutex_unlock(&chip->tpm_mutex); return rc; } #define TPM_DIGEST_SIZE 20 #define TPM_RET_CODE_IDX 6 -ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd, - int len, const char *desc) +ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd, + int len, unsigned int flags, const char *desc) { - struct tpm_output_header *header; + const struct tpm_output_header *header; int err; - len = tpm_transmit(chip, (u8 *) cmd, len); + len = tpm_transmit(chip, (const u8 *)cmd, len, flags); if (len < 0) return len; else if (len < TPM_HEADER_SIZE) @@ -452,7 +454,8 @@ ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap, tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = subcap_id; } - rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc); + rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0, + desc); if (!rc) *cap = tpm_cmd.params.getcap_out.cap; return rc; @@ -468,7 +471,7 @@ void tpm_gen_interrupt(struct tpm_chip *chip) tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; - rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, + rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0, "attempting to determine the timeouts"); } EXPORT_SYMBOL_GPL(tpm_gen_interrupt); @@ -489,7 +492,7 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) start_cmd.header.in = tpm_startup_header; start_cmd.params.startup_in.startup_type = startup_type; - return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, + return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, 0, "attempting to start the TPM"); } @@ -505,7 +508,8 @@ int tpm_get_timeouts(struct tpm_chip *chip) tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; - rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL); + rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0, + NULL); if (rc == TPM_ERR_INVALID_POSTINIT) { /* The TPM is not started, we are the first to talk to it. @@ -519,7 +523,7 @@ int tpm_get_timeouts(struct tpm_chip *chip) tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, - NULL); + 0, NULL); } if (rc) { dev_err(chip->pdev, @@ -580,7 +584,7 @@ duration: tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION; - rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, + rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0, "attempting to determine the durations"); if (rc) return rc; @@ -636,7 +640,7 @@ static int tpm_continue_selftest(struct tpm_chip *chip) struct tpm_cmd_t cmd; cmd.header.in = continue_selftest_header; - rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, + rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, 0, "continue selftest"); return rc; } @@ -656,7 +660,7 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) cmd.header.in = pcrread_header; cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx); - rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE, + rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE, 0, "attempting to read a pcr value"); if (rc == 0) @@ -754,7 +758,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) cmd.header.in = pcrextend_header; cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, + rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, 0, "attempting extend a PCR value"); tpm_chip_put(chip); @@ -793,7 +797,7 @@ int tpm_do_selftest(struct tpm_chip *chip) /* Attempt to read a PCR value */ cmd.header.in = pcrread_header; cmd.params.pcrread_in.pcr_idx = cpu_to_be32(0); - rc = tpm_transmit(chip, (u8 *) &cmd, READ_PCR_RESULT_SIZE); + rc = tpm_transmit(chip, (u8 *) &cmd, READ_PCR_RESULT_SIZE, 0); /* Some buggy TPMs will not respond to tpm_tis_ready() for * around 300ms while the self test is ongoing, keep trying * until the self test duration expires. */ @@ -834,7 +838,7 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen) if (chip == NULL) return -ENODEV; - rc = tpm_transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd"); + rc = tpm_transmit_cmd(chip, cmd, buflen, 0, "attempting tpm_cmd"); tpm_chip_put(chip); return rc; @@ -936,14 +940,15 @@ int tpm_pm_suspend(struct device *dev) cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr); memcpy(cmd.params.pcrextend_in.hash, dummy_hash, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, + rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, 0, "extending dummy pcr before suspend"); } /* now do the actual savestate */ for (try = 0; try < TPM_RETRY; try++) { cmd.header.in = savestate_header; - rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, NULL); + rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, 0, + NULL); /* * If the TPM indicates that it is too busy to respond to @@ -1027,8 +1032,8 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes); err = tpm_transmit_cmd(chip, &tpm_cmd, - TPM_GETRANDOM_RESULT_SIZE + num_bytes, - "attempting get random"); + TPM_GETRANDOM_RESULT_SIZE + num_bytes, + 0, "attempting get random"); if (err) break; diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index ee66fd4673f3..f880856aa75e 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -39,7 +39,7 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, struct tpm_chip *chip = dev_get_drvdata(dev); tpm_cmd.header.in = tpm_readpubek_header; - err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, + err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, 0, "attempting to read the PUBEK"); if (err) goto out; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index a4257a32964f..2216861f89f1 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -498,11 +498,15 @@ extern struct class *tpm_class; extern dev_t tpm_devt; extern const struct file_operations tpm_fops; +enum tpm_transmit_flags { + TPM_TRANSMIT_UNLOCKED = BIT(0), +}; + +ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, + unsigned int flags); +ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd, int len, + unsigned int flags, const char *desc); ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *); -ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, - size_t bufsiz); -ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd, int len, - const char *desc); extern int tpm_get_timeouts(struct tpm_chip *); extern void tpm_gen_interrupt(struct tpm_chip *); extern int tpm_do_selftest(struct tpm_chip *); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index c12130485fc1..cb7e4f6b70ba 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -264,7 +264,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) sizeof(cmd.params.pcrread_in.pcr_select)); cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7); - rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), + rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, "attempting to read a pcr value"); if (rc == 0) { buf = cmd.params.pcrread_out.digest; @@ -312,7 +312,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash) cmd.params.pcrextend_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1); memcpy(cmd.params.pcrextend_in.digest, hash, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), + rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, "attempting extend a PCR value"); return rc; @@ -358,7 +358,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max) cmd.header.in = tpm2_getrandom_header; cmd.params.getrandom_in.size = cpu_to_be16(num_bytes); - err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), + err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, "attempting get random"); if (err) break; @@ -416,12 +416,12 @@ static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle, } /** - * tpm2_seal_trusted() - seal a trusted key - * @chip_num: A specific chip number for the request or TPM_ANY_NUM - * @options: authentication values and other options + * tpm2_seal_trusted() - seal the payload of a trusted key + * @chip_num: TPM chip to use * @payload: the key data in clear and encrypted form + * @options: authentication values and other options * - * Returns < 0 on error and 0 on success. + * Return: < 0 on error and 0 on success. */ int tpm2_seal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, @@ -472,7 +472,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out; } - rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "sealing data"); + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 0, "sealing data"); if (rc) goto out; @@ -494,10 +494,18 @@ out: return rc; } -static int tpm2_load(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options, - u32 *blob_handle) +/** + * tpm2_load_cmd() - execute a TPM2_Load command + * @chip_num: TPM chip to use + * @payload: the key data in clear and encrypted form + * @options: authentication values and other options + * + * Return: same as with tpm_transmit_cmd + */ +static int tpm2_load_cmd(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options, + u32 *blob_handle, unsigned int flags) { struct tpm_buf buf; unsigned int private_len; @@ -532,7 +540,7 @@ static int tpm2_load(struct tpm_chip *chip, goto out; } - rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "loading blob"); + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, "loading blob"); if (!rc) *blob_handle = be32_to_cpup( (__be32 *) &buf.data[TPM_HEADER_SIZE]); @@ -546,7 +554,16 @@ out: return rc; } -static void tpm2_flush_context(struct tpm_chip *chip, u32 handle) +/** + * tpm2_flush_context_cmd() - execute a TPM2_FlushContext command + * @chip_num: TPM chip to use + * @payload: the key data in clear and encrypted form + * @options: authentication values and other options + * + * Return: same as with tpm_transmit_cmd + */ +static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, + unsigned int flags) { struct tpm_buf buf; int rc; @@ -560,7 +577,8 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle) tpm_buf_append_u32(&buf, handle); - rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "flushing context"); + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, + "flushing context"); if (rc) dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle, rc); @@ -568,10 +586,18 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle) tpm_buf_destroy(&buf); } -static int tpm2_unseal(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options, - u32 blob_handle) +/** + * tpm2_unseal_cmd() - execute a TPM2_Unload command + * @chip_num: TPM chip to use + * @payload: the key data in clear and encrypted form + * @options: authentication values and other options + * + * Return: same as with tpm_transmit_cmd + */ +static int tpm2_unseal_cmd(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options, + u32 blob_handle, unsigned int flags) { struct tpm_buf buf; u16 data_len; @@ -589,7 +615,7 @@ static int tpm2_unseal(struct tpm_chip *chip, options->blobauth /* hmac */, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "unsealing"); + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, "unsealing"); if (rc > 0) rc = -EPERM; @@ -608,12 +634,12 @@ static int tpm2_unseal(struct tpm_chip *chip, } /** - * tpm_unseal_trusted() - unseal a trusted key - * @chip_num: A specific chip number for the request or TPM_ANY_NUM - * @options: authentication values and other options + * tpm_unseal_trusted() - unseal the payload of a trusted key + * @chip_num: TPM chip to use * @payload: the key data in clear and encrypted form + * @options: authentication values and other options * - * Returns < 0 on error and 0 on success. + * Return: < 0 on error and 0 on success. */ int tpm2_unseal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, @@ -622,14 +648,17 @@ int tpm2_unseal_trusted(struct tpm_chip *chip, u32 blob_handle; int rc; - rc = tpm2_load(chip, payload, options, &blob_handle); + mutex_lock(&chip->tpm_mutex); + rc = tpm2_load_cmd(chip, payload, options, &blob_handle, + TPM_TRANSMIT_UNLOCKED); if (rc) - return rc; - - rc = tpm2_unseal(chip, payload, options, blob_handle); - - tpm2_flush_context(chip, blob_handle); + goto out; + rc = tpm2_unseal_cmd(chip, payload, options, blob_handle, + TPM_TRANSMIT_UNLOCKED); + tpm2_flush_context_cmd(chip, blob_handle, TPM_TRANSMIT_UNLOCKED); +out: + mutex_unlock(&chip->tpm_mutex); return rc; } @@ -655,9 +684,9 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(property_id); cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1); - rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), desc); + rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, desc); if (!rc) - *value = cmd.params.get_tpm_pt_out.value; + *value = be32_to_cpu(cmd.params.get_tpm_pt_out.value); return rc; } @@ -689,7 +718,7 @@ int tpm2_startup(struct tpm_chip *chip, u16 startup_type) cmd.header.in = tpm2_startup_header; cmd.params.startup_in.startup_type = cpu_to_be16(startup_type); - return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), + return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, "attempting to start the TPM"); } EXPORT_SYMBOL_GPL(tpm2_startup); @@ -718,7 +747,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) cmd.header.in = tpm2_shutdown_header; cmd.params.startup_in.startup_type = cpu_to_be16(shutdown_type); - rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), "stopping the TPM"); + rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, "stopping the TPM"); /* In places where shutdown command is sent there's no much we can do * except print the error code on a system failure. @@ -784,7 +813,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full) cmd.header.in = tpm2_selftest_header; cmd.params.selftest_in.full_test = full; - rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE, + rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE, 0, "continue selftest"); /* At least some prototype chips seem to give RC_TESTING error @@ -836,7 +865,7 @@ int tpm2_do_selftest(struct tpm_chip *chip) cmd.params.pcrread_in.pcr_select[1] = 0x00; cmd.params.pcrread_in.pcr_select[2] = 0x00; - rc = tpm_transmit_cmd(chip, (u8 *) &cmd, sizeof(cmd), NULL); + rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, NULL); if (rc < 0) break; @@ -885,7 +914,7 @@ int tpm2_probe(struct tpm_chip *chip) cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(0x100); cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1); - rc = tpm_transmit(chip, (const char *) &cmd, sizeof(cmd)); + rc = tpm_transmit(chip, (const u8 *)&cmd, sizeof(cmd), 0); if (rc < 0) return rc; else if (rc < TPM_HEADER_SIZE) diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 61e64293b765..2b21398c3adc 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -149,6 +149,11 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) struct crb_priv *priv = chip->vendor.priv; int rc = 0; + /* Zero the cancel register so that the next command will not get + * canceled. + */ + iowrite32(0, &priv->cca->cancel); + if (len > le32_to_cpu(ioread32(&priv->cca->cmd_size))) { dev_err(&chip->dev, "invalid command count value %x %zx\n", @@ -182,8 +187,6 @@ static void crb_cancel(struct tpm_chip *chip) if ((priv->flags & CRB_FL_ACPI_START) && crb_do_acpi_start(chip)) dev_err(&chip->dev, "ACPI Start failed\n"); - - iowrite32(0, &priv->cca->cancel); } static bool crb_req_canceled(struct tpm_chip *chip, u8 status) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index d2406fe25533..090183f812be 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1533,19 +1533,29 @@ static void remove_port_data(struct port *port) spin_lock_irq(&port->inbuf_lock); /* Remove unused data this port might have received. */ discard_port_data(port); + spin_unlock_irq(&port->inbuf_lock); /* Remove buffers we queued up for the Host to send us data in. */ - while ((buf = virtqueue_detach_unused_buf(port->in_vq))) - free_buf(buf, true); - spin_unlock_irq(&port->inbuf_lock); + do { + spin_lock_irq(&port->inbuf_lock); + buf = virtqueue_detach_unused_buf(port->in_vq); + spin_unlock_irq(&port->inbuf_lock); + if (buf) + free_buf(buf, true); + } while (buf); spin_lock_irq(&port->outvq_lock); reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); /* Free pending buffers from the out-queue. */ - while ((buf = virtqueue_detach_unused_buf(port->out_vq))) - free_buf(buf, true); - spin_unlock_irq(&port->outvq_lock); + do { + spin_lock_irq(&port->outvq_lock); + buf = virtqueue_detach_unused_buf(port->out_vq); + spin_unlock_irq(&port->outvq_lock); + if (buf) + free_buf(buf, true); + } while (buf); } /* diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 0c83ffc22dd2..f45d923ed3cd 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -354,7 +354,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, /* if read only, just return current value */ if (divider->flags & CLK_DIVIDER_READ_ONLY) { - bestdiv = readl(divider->reg) >> divider->shift; + bestdiv = clk_readl(divider->reg) >> divider->shift; bestdiv &= div_mask(divider->width); bestdiv = _get_div(divider->table, bestdiv, divider->flags, divider->width); diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index 7bc1c4527ae4..a5070f9cb0d4 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -700,6 +700,7 @@ static struct clk * __init create_mux_common(struct clockgen *cg, struct mux_hwclock *hwc, const struct clk_ops *ops, unsigned long min_rate, + unsigned long max_rate, unsigned long pct80_rate, const char *fmt, int idx) { @@ -728,6 +729,8 @@ static struct clk * __init create_mux_common(struct clockgen *cg, continue; if (rate < min_rate) continue; + if (rate > max_rate) + continue; parent_names[j] = div->name; hwc->parent_to_clksel[j] = i; @@ -759,14 +762,18 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx) struct mux_hwclock *hwc; const struct clockgen_pll_div *div; unsigned long plat_rate, min_rate; - u64 pct80_rate; + u64 max_rate, pct80_rate; u32 clksel; hwc = kzalloc(sizeof(*hwc), GFP_KERNEL); if (!hwc) return NULL; - hwc->reg = cg->regs + 0x20 * idx; + if (cg->info.flags & CG_VER3) + hwc->reg = cg->regs + 0x70000 + 0x20 * idx; + else + hwc->reg = cg->regs + 0x20 * idx; + hwc->info = cg->info.cmux_groups[cg->info.cmux_to_group[idx]]; /* @@ -783,8 +790,8 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx) return NULL; } - pct80_rate = clk_get_rate(div->clk); - pct80_rate *= 8; + max_rate = clk_get_rate(div->clk); + pct80_rate = max_rate * 8; do_div(pct80_rate, 10); plat_rate = clk_get_rate(cg->pll[PLATFORM_PLL].div[PLL_DIV1].clk); @@ -794,7 +801,7 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx) else min_rate = plat_rate / 2; - return create_mux_common(cg, hwc, &cmux_ops, min_rate, + return create_mux_common(cg, hwc, &cmux_ops, min_rate, max_rate, pct80_rate, "cg-cmux%d", idx); } @@ -809,7 +816,7 @@ static struct clk * __init create_one_hwaccel(struct clockgen *cg, int idx) hwc->reg = cg->regs + 0x20 * idx + 0x10; hwc->info = cg->info.hwaccel[idx]; - return create_mux_common(cg, hwc, &hwaccel_ops, 0, 0, + return create_mux_common(cg, hwc, &hwaccel_ops, 0, ULONG_MAX, 0, "cg-hwaccel%d", idx); } diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index 10224b01b97c..b134a8b15e2c 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -351,8 +351,8 @@ static int xgene_clk_set_rate(struct clk_hw *hw, unsigned long rate, /* Set new divider */ data = xgene_clk_read(pclk->param.divider_reg + pclk->param.reg_divider_offset); - data &= ~((1 << pclk->param.reg_divider_width) - 1) - << pclk->param.reg_divider_shift; + data &= ~(((1 << pclk->param.reg_divider_width) - 1) + << pclk->param.reg_divider_shift); data |= divider; xgene_clk_write(data, pclk->param.divider_reg + pclk->param.reg_divider_offset); diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c index b0978d3b83e2..d302ed3b8225 100644 --- a/drivers/clk/imx/clk-imx35.c +++ b/drivers/clk/imx/clk-imx35.c @@ -115,7 +115,7 @@ static void __init _mx35_clocks_init(void) } clk[ckih] = imx_clk_fixed("ckih", 24000000); - clk[ckil] = imx_clk_fixed("ckih", 32768); + clk[ckil] = imx_clk_fixed("ckil", 32768); clk[mpll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "mpll", "ckih", base + MX35_CCM_MPCTL); clk[ppll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "ppll", "ckih", base + MX35_CCM_PPCTL); diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index c1935081d34a..aab64205d866 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -550,6 +550,24 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) if (IS_ENABLED(CONFIG_PCI_IMX6)) clk_set_parent(clk[IMX6QDL_CLK_LVDS1_SEL], clk[IMX6QDL_CLK_SATA_REF_100M]); + /* + * Initialize the GPU clock muxes, so that the maximum specified clock + * rates for the respective SoC are not exceeded. + */ + if (clk_on_imx6dl()) { + clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL], + clk[IMX6QDL_CLK_PLL2_PFD1_594M]); + clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL], + clk[IMX6QDL_CLK_PLL2_PFD1_594M]); + } else if (clk_on_imx6q()) { + clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL], + clk[IMX6QDL_CLK_MMDC_CH0_AXI]); + clk_set_parent(clk[IMX6QDL_CLK_GPU3D_SHADER_SEL], + clk[IMX6QDL_CLK_PLL2_PFD1_594M]); + clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL], + clk[IMX6QDL_CLK_PLL3_USB_OTG]); + } + imx_register_uart_clocks(uart_clks); } CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init); diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index 251533d87c65..f261b1d292c7 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -313,7 +313,7 @@ static void __init mmp2_clk_init(struct device_node *np) } pxa_unit->apmu_base = of_iomap(np, 1); - if (!pxa_unit->mpmu_base) { + if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); return; } diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c index 64eaf4141c69..427f4bb08665 100644 --- a/drivers/clk/mmp/clk-of-pxa168.c +++ b/drivers/clk/mmp/clk-of-pxa168.c @@ -262,7 +262,7 @@ static void __init pxa168_clk_init(struct device_node *np) } pxa_unit->apmu_base = of_iomap(np, 1); - if (!pxa_unit->mpmu_base) { + if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); return; } diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c index 13d6173326a4..cdf5ba566d3b 100644 --- a/drivers/clk/mmp/clk-of-pxa910.c +++ b/drivers/clk/mmp/clk-of-pxa910.c @@ -282,7 +282,7 @@ static void __init pxa910_clk_init(struct device_node *np) } pxa_unit->apmu_base = of_iomap(np, 1); - if (!pxa_unit->mpmu_base) { + if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); return; } @@ -294,7 +294,7 @@ static void __init pxa910_clk_init(struct device_node *np) } pxa_unit->apbcp_base = of_iomap(np, 3); - if (!pxa_unit->mpmu_base) { + if (!pxa_unit->apbcp_base) { pr_err("failed to map apbcp registers\n"); return; } diff --git a/drivers/clk/msm/clock-mmss-8998.c b/drivers/clk/msm/clock-mmss-8998.c index 1661878fc650..6ebb3ed6ed91 100644 --- a/drivers/clk/msm/clock-mmss-8998.c +++ b/drivers/clk/msm/clock-mmss-8998.c @@ -664,8 +664,8 @@ static struct rcg_clk byte0_clk_src = { .parent = &ext_byte0_clk_src.c, .ops = &clk_ops_byte_multiparent, .flags = CLKFLAG_NO_RATE_CACHE, - VDD_DIG_FMAX_MAP3(LOWER, 150000000, LOW, 240000000, - NOMINAL, 357140000), + VDD_DIG_FMAX_MAP3(LOWER, 131250000, LOW, 210000000, + NOMINAL, 312500000), CLK_INIT(byte0_clk_src.c), }, }; @@ -681,8 +681,8 @@ static struct rcg_clk byte1_clk_src = { .parent = &ext_byte1_clk_src.c, .ops = &clk_ops_byte_multiparent, .flags = CLKFLAG_NO_RATE_CACHE, - VDD_DIG_FMAX_MAP3(LOWER, 150000000, LOW, 240000000, - NOMINAL, 357140000), + VDD_DIG_FMAX_MAP3(LOWER, 131250000, LOW, 210000000, + NOMINAL, 312500000), CLK_INIT(byte1_clk_src.c), }, }; @@ -722,8 +722,8 @@ static struct rcg_clk pclk0_clk_src = { .parent = &ext_pclk0_clk_src.c, .ops = &clk_ops_pixel_multiparent, .flags = CLKFLAG_NO_RATE_CACHE, - VDD_DIG_FMAX_MAP3(LOWER, 184000000, LOW, 295000000, - NOMINAL, 610000000), + VDD_DIG_FMAX_MAP3(LOWER, 175000000, LOW, 280000000, + NOMINAL, 416670000), CLK_INIT(pclk0_clk_src.c), }, }; @@ -739,8 +739,8 @@ static struct rcg_clk pclk1_clk_src = { .parent = &ext_pclk1_clk_src.c, .ops = &clk_ops_pixel_multiparent, .flags = CLKFLAG_NO_RATE_CACHE, - VDD_DIG_FMAX_MAP3(LOWER, 184000000, LOW, 295000000, - NOMINAL, 610000000), + VDD_DIG_FMAX_MAP3(LOWER, 175000000, LOW, 280000000, + NOMINAL, 416670000), CLK_INIT(pclk1_clk_src.c), }, }; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 9e5c0b6f7a0e..c9ba7f97eebe 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -513,7 +513,7 @@ static int clk_rcg2_enable(struct clk_hw *hw) * is always on while APPS is online. Therefore, the RCG can safely be * switched. */ - rate = clk_get_rate(hw->clk); + rate = rcg->current_freq; f = qcom_find_freq(rcg->freq_tbl, rate); if (!f) return -EINVAL; @@ -627,6 +627,9 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) rcg->new_index, false); } + /* Update current frequency with the frequency requested. */ + rcg->current_freq = rate; + return ret; } diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c index 6f3719d73390..e84877a2cacc 100644 --- a/drivers/clocksource/sun4i_timer.c +++ b/drivers/clocksource/sun4i_timer.c @@ -123,12 +123,16 @@ static struct clock_event_device sun4i_clockevent = { .set_next_event = sun4i_clkevt_next_event, }; +static void sun4i_timer_clear_interrupt(void) +{ + writel(TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_ST_REG); +} static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *evt = (struct clock_event_device *)dev_id; - writel(0x1, timer_base + TIMER_IRQ_ST_REG); + sun4i_timer_clear_interrupt(); evt->event_handler(evt); return IRQ_HANDLED; @@ -193,6 +197,9 @@ static void __init sun4i_timer_init(struct device_node *node) /* Make sure timer is stopped before playing with interrupts */ sun4i_clkevt_time_stop(0); + /* clear timer0 interrupt */ + sun4i_timer_clear_interrupt(); + sun4i_clockevent.cpumask = cpu_possible_mask; sun4i_clockevent.irq = irq; diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 6e80e4298274..7ff8b15a3422 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -285,14 +285,14 @@ static void intel_pstate_hwp_set(void) int min, hw_min, max, hw_max, cpu, range, adj_range; u64 value, cap; - rdmsrl(MSR_HWP_CAPABILITIES, cap); - hw_min = HWP_LOWEST_PERF(cap); - hw_max = HWP_HIGHEST_PERF(cap); - range = hw_max - hw_min; - get_online_cpus(); for_each_online_cpu(cpu) { + rdmsrl_on_cpu(cpu, MSR_HWP_CAPABILITIES, &cap); + hw_min = HWP_LOWEST_PERF(cap); + hw_max = HWP_HIGHEST_PERF(cap); + range = hw_max - hw_min; + rdmsrl_on_cpu(cpu, MSR_HWP_REQUEST, &value); adj_range = limits->min_perf_pct * range / 100; min = hw_min + adj_range; diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c index e342565e8715..1855b9ee807f 100644 --- a/drivers/cpuidle/cpuidle-arm.c +++ b/drivers/cpuidle/cpuidle-arm.c @@ -135,6 +135,7 @@ static int __init arm_idle_init(void) dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { pr_err("Failed to allocate cpuidle device\n"); + ret = -ENOMEM; goto out_fail; } dev->cpu = cpu; diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 823b7d988284..d8e571981c35 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.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. * Copyright (C) 2006-2007 Adam Belay <abelay@novell.com> * Copyright (C) 2009 Intel Corporation * @@ -36,6 +36,7 @@ #include <linux/moduleparam.h> #include <linux/sched.h> #include <linux/cpu_pm.h> +#include <linux/arm-smccc.h> #include <soc/qcom/spm.h> #include <soc/qcom/pm.h> #include <soc/qcom/rpm-notifier.h> @@ -161,13 +162,13 @@ s32 msm_cpuidle_get_deep_idle_latency(void) void lpm_suspend_wake_time(uint64_t wakeup_time) { if (wakeup_time <= 0) { - suspend_wake_time = msm_pm_sleep_time_override; + suspend_wake_time = msm_pm_sleep_time_override * MSEC_PER_SEC; return; } if (msm_pm_sleep_time_override && (msm_pm_sleep_time_override < wakeup_time)) - suspend_wake_time = msm_pm_sleep_time_override; + suspend_wake_time = msm_pm_sleep_time_override * MSEC_PER_SEC; else suspend_wake_time = wakeup_time; } @@ -793,7 +794,7 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster, if (!suspend_wake_time) return ~0ULL; else - return USEC_PER_SEC * suspend_wake_time; + return USEC_PER_MSEC * suspend_wake_time; } cpumask_and(&online_cpus_in_cluster, @@ -1412,7 +1413,6 @@ unlock_and_return: } #if !defined(CONFIG_CPU_V7) -asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64); bool psci_enter_sleep(struct lpm_cluster *cluster, int idx, bool from_idle) { /* diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index b3044219772c..2cde3796cb82 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -4542,6 +4542,15 @@ static int __init caam_algapi_init(void) if (!aes_inst && (alg_sel == OP_ALG_ALGSEL_AES)) continue; + /* + * Check support for AES modes not available + * on LP devices. + */ + if ((cha_vid & CHA_ID_LS_AES_MASK) == CHA_ID_LS_AES_LP) + if ((alg->class1_alg_type & OP_ALG_AAI_MASK) == + OP_ALG_AAI_XTS) + continue; + t_alg = caam_alg_alloc(alg); if (IS_ERR(t_alg)) { err = PTR_ERR(t_alg); diff --git a/drivers/crypto/vmx/ghash.c b/drivers/crypto/vmx/ghash.c index 2183a2e77641..9cb3a0b715e2 100644 --- a/drivers/crypto/vmx/ghash.c +++ b/drivers/crypto/vmx/ghash.c @@ -26,16 +26,13 @@ #include <linux/hardirq.h> #include <asm/switch_to.h> #include <crypto/aes.h> +#include <crypto/ghash.h> #include <crypto/scatterwalk.h> #include <crypto/internal/hash.h> #include <crypto/b128ops.h> #define IN_INTERRUPT in_interrupt() -#define GHASH_BLOCK_SIZE (16) -#define GHASH_DIGEST_SIZE (16) -#define GHASH_KEY_LEN (16) - void gcm_init_p8(u128 htable[16], const u64 Xi[2]); void gcm_gmult_p8(u64 Xi[2], const u128 htable[16]); void gcm_ghash_p8(u64 Xi[2], const u128 htable[16], @@ -55,16 +52,11 @@ struct p8_ghash_desc_ctx { static int p8_ghash_init_tfm(struct crypto_tfm *tfm) { - const char *alg; + const char *alg = "ghash-generic"; struct crypto_shash *fallback; struct crypto_shash *shash_tfm = __crypto_shash_cast(tfm); struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm); - if (!(alg = crypto_tfm_alg_name(tfm))) { - printk(KERN_ERR "Failed to get algorithm name.\n"); - return -ENOENT; - } - fallback = crypto_alloc_shash(alg, 0, CRYPTO_ALG_NEED_FALLBACK); if (IS_ERR(fallback)) { printk(KERN_ERR @@ -78,10 +70,18 @@ static int p8_ghash_init_tfm(struct crypto_tfm *tfm) crypto_shash_set_flags(fallback, crypto_shash_get_flags((struct crypto_shash *) tfm)); - ctx->fallback = fallback; - shash_tfm->descsize = sizeof(struct p8_ghash_desc_ctx) - + crypto_shash_descsize(fallback); + /* Check if the descsize defined in the algorithm is still enough. */ + if (shash_tfm->descsize < sizeof(struct p8_ghash_desc_ctx) + + crypto_shash_descsize(fallback)) { + printk(KERN_ERR + "Desc size of the fallback implementation (%s) does not match the expected value: %lu vs %u\n", + alg, + shash_tfm->descsize - sizeof(struct p8_ghash_desc_ctx), + crypto_shash_descsize(fallback)); + return -EINVAL; + } + ctx->fallback = fallback; return 0; } @@ -113,7 +113,7 @@ static int p8_ghash_setkey(struct crypto_shash *tfm, const u8 *key, { struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(tfm)); - if (keylen != GHASH_KEY_LEN) + if (keylen != GHASH_BLOCK_SIZE) return -EINVAL; preempt_disable(); @@ -215,7 +215,8 @@ struct shash_alg p8_ghash_alg = { .update = p8_ghash_update, .final = p8_ghash_final, .setkey = p8_ghash_setkey, - .descsize = sizeof(struct p8_ghash_desc_ctx), + .descsize = sizeof(struct p8_ghash_desc_ctx) + + sizeof(struct ghash_desc_ctx), .base = { .cra_name = "ghash", .cra_driver_name = "p8_ghash", diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index e44a1bfb0250..66c073fc8afc 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -864,8 +864,12 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan, * access. Hopefully we can access DDR through both ports (at least on * SAMA5D4x), so we can use the same interface for source and dest, * that solves the fact we don't know the direction. + * ERRATA: Even if useless for memory transfers, the PERID has to not + * match the one of another channel. If not, it could lead to spurious + * flag status. */ - u32 chan_cc = AT_XDMAC_CC_DIF(0) + u32 chan_cc = AT_XDMAC_CC_PERID(0x3f) + | AT_XDMAC_CC_DIF(0) | AT_XDMAC_CC_SIF(0) | AT_XDMAC_CC_MBSIZE_SIXTEEN | AT_XDMAC_CC_TYPE_MEM_TRAN; @@ -1042,8 +1046,12 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, * access DDR through both ports (at least on SAMA5D4x), so we can use * the same interface for source and dest, that solves the fact we * don't know the direction. + * ERRATA: Even if useless for memory transfers, the PERID has to not + * match the one of another channel. If not, it could lead to spurious + * flag status. */ - u32 chan_cc = AT_XDMAC_CC_DAM_INCREMENTED_AM + u32 chan_cc = AT_XDMAC_CC_PERID(0x3f) + | AT_XDMAC_CC_DAM_INCREMENTED_AM | AT_XDMAC_CC_SAM_INCREMENTED_AM | AT_XDMAC_CC_DIF(0) | AT_XDMAC_CC_SIF(0) @@ -1144,8 +1152,12 @@ static struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan, * access. Hopefully we can access DDR through both ports (at least on * SAMA5D4x), so we can use the same interface for source and dest, * that solves the fact we don't know the direction. + * ERRATA: Even if useless for memory transfers, the PERID has to not + * match the one of another channel. If not, it could lead to spurious + * flag status. */ - u32 chan_cc = AT_XDMAC_CC_DAM_UBS_AM + u32 chan_cc = AT_XDMAC_CC_PERID(0x3f) + | AT_XDMAC_CC_DAM_UBS_AM | AT_XDMAC_CC_SAM_INCREMENTED_AM | AT_XDMAC_CC_DIF(0) | AT_XDMAC_CC_SIF(0) @@ -1183,8 +1195,8 @@ static struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan, desc->lld.mbr_cfg = chan_cc; dev_dbg(chan2dev(chan), - "%s: lld: mbr_da=%pad, mbr_ds=%pad, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n", - __func__, &desc->lld.mbr_da, &desc->lld.mbr_ds, desc->lld.mbr_ubc, + "%s: lld: mbr_da=%pad, mbr_ds=0x%08x, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n", + __func__, &desc->lld.mbr_da, desc->lld.mbr_ds, desc->lld.mbr_ubc, desc->lld.mbr_cfg); return desc; @@ -2055,7 +2067,7 @@ err_dma_unregister: err_clk_disable: clk_disable_unprepare(atxdmac->clk); err_free_irq: - free_irq(atxdmac->irq, atxdmac->dma.dev); + free_irq(atxdmac->irq, atxdmac); return ret; } @@ -2071,7 +2083,7 @@ static int at_xdmac_remove(struct platform_device *pdev) synchronize_irq(atxdmac->irq); - free_irq(atxdmac->irq, atxdmac->dma.dev); + free_irq(atxdmac->irq, atxdmac); for (i = 0; i < atxdmac->dma.chancnt; i++) { struct at_xdmac_chan *atchan = &atxdmac->chan[i]; diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c index 2bf37e68ad0f..dd184b50e5b4 100644 --- a/drivers/dma/ipu/ipu_irq.c +++ b/drivers/dma/ipu/ipu_irq.c @@ -286,22 +286,21 @@ static void ipu_irq_handler(struct irq_desc *desc) raw_spin_unlock(&bank_lock); while ((line = ffs(status))) { struct ipu_irq_map *map; - unsigned int irq = NO_IRQ; + unsigned int irq; line--; status &= ~(1UL << line); raw_spin_lock(&bank_lock); map = src2map(32 * i + line); - if (map) - irq = map->irq; - raw_spin_unlock(&bank_lock); - if (!map) { + raw_spin_unlock(&bank_lock); pr_err("IPU: Interrupt on unmapped source %u bank %d\n", line, i); continue; } + irq = map->irq; + raw_spin_unlock(&bank_lock); generic_handle_irq(irq); } } diff --git a/drivers/esoc/Kconfig b/drivers/esoc/Kconfig index 91e5c0b8b57b..ce6efd8e529c 100644 --- a/drivers/esoc/Kconfig +++ b/drivers/esoc/Kconfig @@ -58,4 +58,12 @@ config ESOC_MDM_DBG_ENG by command engine to the external modem. Also allows masking of certain notifications being sent to the external modem. +config MDM_DBG_REQ_ENG + tristate "manual request engine for 4x series external modems" + depends on ESOC_MDM_DBG_ENG + help + Provides a user interface to handle incoming requests from + the external modem. Allows for debugging of IPC mechanism + between the external modem and the primary soc. + endif diff --git a/drivers/esoc/esoc-mdm-dbg-eng.c b/drivers/esoc/esoc-mdm-dbg-eng.c index 4d465c18a646..d4ad25123565 100644 --- a/drivers/esoc/esoc-mdm-dbg-eng.c +++ b/drivers/esoc/esoc-mdm-dbg-eng.c @@ -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 @@ -179,7 +179,165 @@ static ssize_t notifier_mask_store(struct device_driver *drv, const char *buf, } static DRIVER_ATTR(notifier_mask, S_IWUSR, NULL, notifier_mask_store); -int mdm_dbg_eng_init(struct esoc_drv *esoc_drv) +#ifdef CONFIG_MDM_DBG_REQ_ENG +static struct esoc_clink *dbg_clink; +/* Last recorded request from esoc */ +static enum esoc_req last_req; +static DEFINE_SPINLOCK(req_lock); +/* + * esoc_to_user: Conversion of esoc ids to user visible strings + * id: esoc request, command, notifier, event id + * str: string equivalent of the above + */ +struct esoc_to_user { + unsigned int id; + char str[20]; +}; + +static struct esoc_to_user in_to_resp[] = { + { + .id = ESOC_IMG_XFER_DONE, + .str = "XFER_DONE", + }, + { + .id = ESOC_BOOT_DONE, + .str = "BOOT_DONE", + }, + { + .id = ESOC_BOOT_FAIL, + .str = "BOOT_FAIL", + }, + { + .id = ESOC_IMG_XFER_RETRY, + .str = "XFER_RETRY", + }, + { .id = ESOC_IMG_XFER_FAIL, + .str = "XFER_FAIL", + }, + { + .id = ESOC_UPGRADE_AVAILABLE, + .str = "UPGRADE", + }, + { .id = ESOC_DEBUG_DONE, + .str = "DEBUG_DONE", + }, + { + .id = ESOC_DEBUG_FAIL, + .str = "DEBUG_FAIL", + }, +}; + +static struct esoc_to_user req_to_str[] = { + { + .id = ESOC_REQ_IMG, + .str = "REQ_IMG", + }, + { + .id = ESOC_REQ_DEBUG, + .str = "REQ_DEBUG", + }, + { + .id = ESOC_REQ_SHUTDOWN, + .str = "REQ_SHUTDOWN", + }, +}; + +static ssize_t req_eng_resp_store(struct device_driver *drv, const char *buf, + size_t count) +{ + unsigned int i; + const struct esoc_clink_ops *const clink_ops = dbg_clink->clink_ops; + + dev_dbg(&dbg_clink->dev, "user input req eng response %s\n", buf); + for (i = 0; i < ARRAY_SIZE(in_to_resp); i++) { + size_t len1 = strlen(buf); + size_t len2 = strlen(in_to_resp[i].str); + + if (len1 == len2 && !strcmp(buf, in_to_resp[i].str)) { + clink_ops->notify(in_to_resp[i].id, dbg_clink); + break; + } + } + if (i > ARRAY_SIZE(in_to_resp)) + dev_err(&dbg_clink->dev, "Invalid resp %s, specified\n", buf); + return count; +} + +static DRIVER_ATTR(req_eng_resp, S_IWUSR, NULL, req_eng_resp_store); + +static ssize_t last_esoc_req_show(struct device_driver *drv, char *buf) +{ + unsigned int i; + unsigned long flags; + size_t count; + + spin_lock_irqsave(&req_lock, flags); + for (i = 0; i < ARRAY_SIZE(req_to_str); i++) { + if (last_req == req_to_str[i].id) { + count = snprintf(buf, PAGE_SIZE, "%s\n", + req_to_str[i].str); + break; + } + } + spin_unlock_irqrestore(&req_lock, flags); + return count; +} +static DRIVER_ATTR(last_esoc_req, S_IRUSR, last_esoc_req_show, NULL); + +static void esoc_handle_req(enum esoc_req req, struct esoc_eng *eng) +{ + unsigned long flags; + + spin_lock_irqsave(&req_lock, flags); + last_req = req; + spin_unlock_irqrestore(&req_lock, flags); +} + +static void esoc_handle_evt(enum esoc_evt evt, struct esoc_eng *eng) +{ +} + +static struct esoc_eng dbg_req_eng = { + .handle_clink_req = esoc_handle_req, + .handle_clink_evt = esoc_handle_evt, +}; + +int register_dbg_req_eng(struct esoc_clink *clink, + struct device_driver *drv) +{ + int ret; + + dbg_clink = clink; + ret = driver_create_file(drv, &driver_attr_req_eng_resp); + if (ret) + return ret; + ret = driver_create_file(drv, &driver_attr_last_esoc_req); + if (ret) { + dev_err(&clink->dev, "Unable to create last esoc req\n"); + goto last_req_err; + } + ret = esoc_clink_register_req_eng(clink, &dbg_req_eng); + if (ret) { + pr_err("Unable to register req eng\n"); + goto req_eng_fail; + } + spin_lock_init(&req_lock); + return 0; +last_req_err: + driver_remove_file(drv, &driver_attr_last_esoc_req); +req_eng_fail: + driver_remove_file(drv, &driver_attr_req_eng_resp); + return ret; +} +#else +int register_dbg_req_eng(struct esoc_clink *clink, struct device_driver *d) +{ + return 0; +} +#endif + +int mdm_dbg_eng_init(struct esoc_drv *esoc_drv, + struct esoc_clink *clink) { int ret; struct device_driver *drv = &esoc_drv->driver; @@ -194,7 +352,14 @@ int mdm_dbg_eng_init(struct esoc_drv *esoc_drv) pr_err("Unable to create notify mask file\n"); goto notify_mask_err; } + ret = register_dbg_req_eng(clink, drv); + if (ret) { + pr_err("Failed to register esoc dbg req eng\n"); + goto dbg_req_fail; + } return 0; +dbg_req_fail: + driver_remove_file(drv, &driver_attr_notifier_mask); notify_mask_err: driver_remove_file(drv, &driver_attr_command_mask); cmd_mask_err: diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c index 0c14929a0339..9abe125f28b5 100644 --- a/drivers/esoc/esoc-mdm-drv.c +++ b/drivers/esoc/esoc-mdm-drv.c @@ -258,7 +258,7 @@ int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv) ret = register_reboot_notifier(&mdm_drv->esoc_restart); if (ret) dev_err(&esoc_clink->dev, "register for reboot failed\n"); - ret = mdm_dbg_eng_init(drv); + ret = mdm_dbg_eng_init(drv, esoc_clink); if (ret) { debug_init_done = false; dev_err(&esoc_clink->dev, "dbg engine failure\n"); diff --git a/drivers/esoc/esoc_client.c b/drivers/esoc/esoc_client.c index 6dee2f37b46d..e9932ea3e964 100644 --- a/drivers/esoc/esoc_client.c +++ b/drivers/esoc/esoc_client.c @@ -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 @@ -47,6 +47,8 @@ struct esoc_desc *devm_register_esoc_client(struct device *dev, for (index = 0;; index++) { esoc_prop = kasprintf(GFP_KERNEL, "esoc-%d", index); + if (IS_ERR_OR_NULL(esoc_prop)) + return ERR_PTR(-ENOMEM); parp = of_get_property(np, esoc_prop, NULL); if (parp == NULL) { dev_err(dev, "esoc device not present\n"); diff --git a/drivers/esoc/mdm-dbg.h b/drivers/esoc/mdm-dbg.h index ae31339ab152..ffba87c6f7bb 100644 --- a/drivers/esoc/mdm-dbg.h +++ b/drivers/esoc/mdm-dbg.h @@ -24,7 +24,8 @@ static inline bool dbg_check_notify_mask(unsigned int notify) return false; } -static inline int mdm_dbg_eng_init(struct esoc_drv *drv) +static inline int mdm_dbg_eng_init(struct esoc_drv *drv, + struct esoc_clink *clink) { return 0; } @@ -32,7 +33,8 @@ static inline int mdm_dbg_eng_init(struct esoc_drv *drv) #else extern bool dbg_check_cmd_mask(unsigned int cmd); extern bool dbg_check_notify_mask(unsigned int notify); -extern int mdm_dbg_eng_init(struct esoc_drv *drv); +extern int mdm_dbg_eng_init(struct esoc_drv *drv, + struct esoc_clink *clink); #endif static inline bool mdm_dbg_stall_cmd(unsigned int cmd) diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index f4ea80d602f7..b9d2f76a0cf7 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -73,13 +73,13 @@ struct rfc2734_header { #define fwnet_get_hdr_lf(h) (((h)->w0 & 0xc0000000) >> 30) #define fwnet_get_hdr_ether_type(h) (((h)->w0 & 0x0000ffff)) -#define fwnet_get_hdr_dg_size(h) (((h)->w0 & 0x0fff0000) >> 16) +#define fwnet_get_hdr_dg_size(h) ((((h)->w0 & 0x0fff0000) >> 16) + 1) #define fwnet_get_hdr_fg_off(h) (((h)->w0 & 0x00000fff)) #define fwnet_get_hdr_dgl(h) (((h)->w1 & 0xffff0000) >> 16) -#define fwnet_set_hdr_lf(lf) ((lf) << 30) +#define fwnet_set_hdr_lf(lf) ((lf) << 30) #define fwnet_set_hdr_ether_type(et) (et) -#define fwnet_set_hdr_dg_size(dgs) ((dgs) << 16) +#define fwnet_set_hdr_dg_size(dgs) (((dgs) - 1) << 16) #define fwnet_set_hdr_fg_off(fgo) (fgo) #define fwnet_set_hdr_dgl(dgl) ((dgl) << 16) @@ -578,6 +578,9 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, int retval; u16 ether_type; + if (len <= RFC2374_UNFRAG_HDR_SIZE) + return 0; + hdr.w0 = be32_to_cpu(buf[0]); lf = fwnet_get_hdr_lf(&hdr); if (lf == RFC2374_HDR_UNFRAG) { @@ -602,7 +605,12 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, return fwnet_finish_incoming_packet(net, skb, source_node_id, is_broadcast, ether_type); } + /* A datagram fragment has been received, now the fun begins. */ + + if (len <= RFC2374_FRAG_HDR_SIZE) + return 0; + hdr.w1 = ntohl(buf[1]); buf += 2; len -= RFC2374_FRAG_HDR_SIZE; @@ -614,7 +622,10 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, fg_off = fwnet_get_hdr_fg_off(&hdr); } datagram_label = fwnet_get_hdr_dgl(&hdr); - dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */ + dg_size = fwnet_get_hdr_dg_size(&hdr); + + if (fg_off + len > dg_size) + return 0; spin_lock_irqsave(&dev->lock, flags); @@ -722,6 +733,22 @@ static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r, fw_send_response(card, r, rcode); } +static int gasp_source_id(__be32 *p) +{ + return be32_to_cpu(p[0]) >> 16; +} + +static u32 gasp_specifier_id(__be32 *p) +{ + return (be32_to_cpu(p[0]) & 0xffff) << 8 | + (be32_to_cpu(p[1]) & 0xff000000) >> 24; +} + +static u32 gasp_version(__be32 *p) +{ + return be32_to_cpu(p[1]) & 0xffffff; +} + static void fwnet_receive_broadcast(struct fw_iso_context *context, u32 cycle, size_t header_length, void *header, void *data) { @@ -731,9 +758,6 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context, __be32 *buf_ptr; int retval; u32 length; - u16 source_node_id; - u32 specifier_id; - u32 ver; unsigned long offset; unsigned long flags; @@ -750,22 +774,17 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context, spin_unlock_irqrestore(&dev->lock, flags); - specifier_id = (be32_to_cpu(buf_ptr[0]) & 0xffff) << 8 - | (be32_to_cpu(buf_ptr[1]) & 0xff000000) >> 24; - ver = be32_to_cpu(buf_ptr[1]) & 0xffffff; - source_node_id = be32_to_cpu(buf_ptr[0]) >> 16; - - if (specifier_id == IANA_SPECIFIER_ID && - (ver == RFC2734_SW_VERSION + if (length > IEEE1394_GASP_HDR_SIZE && + gasp_specifier_id(buf_ptr) == IANA_SPECIFIER_ID && + (gasp_version(buf_ptr) == RFC2734_SW_VERSION #if IS_ENABLED(CONFIG_IPV6) - || ver == RFC3146_SW_VERSION + || gasp_version(buf_ptr) == RFC3146_SW_VERSION #endif - )) { - buf_ptr += 2; - length -= IEEE1394_GASP_HDR_SIZE; - fwnet_incoming_packet(dev, buf_ptr, length, source_node_id, + )) + fwnet_incoming_packet(dev, buf_ptr + 2, + length - IEEE1394_GASP_HDR_SIZE, + gasp_source_id(buf_ptr), context->card->generation, true); - } packet.payload_length = dev->rcv_buffer_size; packet.interrupt = 1; diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 9411b1ab2c76..68489ef7422f 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -173,6 +173,9 @@ config QCOM_SCM_64 def_bool y depends on QCOM_SCM && ARM64 +config HAVE_ARM_SMCCC + bool + source "drivers/firmware/broadcom/Kconfig" source "drivers/firmware/google/Kconfig" source "drivers/firmware/efi/Kconfig" diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index ec379a4164cc..f292917b00e7 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -18,3 +18,6 @@ obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o obj-$(CONFIG_EFI_STUB) += libstub/ obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o + +arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o +obj-$(CONFIG_ARM64) += $(arm-obj-y) diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c new file mode 100644 index 000000000000..9e15d571b53c --- /dev/null +++ b/drivers/firmware/efi/arm-init.c @@ -0,0 +1,209 @@ +/* + * Extensible Firmware Interface + * + * Based on Extensible Firmware Interface Specification version 2.4 + * + * Copyright (C) 2013 - 2015 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/efi.h> +#include <linux/init.h> +#include <linux/memblock.h> +#include <linux/mm_types.h> +#include <linux/of.h> +#include <linux/of_fdt.h> + +#include <asm/efi.h> + +struct efi_memory_map memmap; + +u64 efi_system_table; + +static int __init is_normal_ram(efi_memory_desc_t *md) +{ + if (md->attribute & EFI_MEMORY_WB) + return 1; + return 0; +} + +/* + * Translate a EFI virtual address into a physical address: this is necessary, + * as some data members of the EFI system table are virtually remapped after + * SetVirtualAddressMap() has been called. + */ +static phys_addr_t efi_to_phys(unsigned long addr) +{ + efi_memory_desc_t *md; + + for_each_efi_memory_desc(&memmap, md) { + if (!(md->attribute & EFI_MEMORY_RUNTIME)) + continue; + if (md->virt_addr == 0) + /* no virtual mapping has been installed by the stub */ + break; + if (md->virt_addr <= addr && + (addr - md->virt_addr) < (md->num_pages << EFI_PAGE_SHIFT)) + return md->phys_addr + addr - md->virt_addr; + } + return addr; +} + +static int __init uefi_init(void) +{ + efi_char16_t *c16; + void *config_tables; + size_t table_size; + char vendor[100] = "unknown"; + int i, retval; + + efi.systab = early_memremap(efi_system_table, + sizeof(efi_system_table_t)); + if (efi.systab == NULL) { + pr_warn("Unable to map EFI system table.\n"); + return -ENOMEM; + } + + set_bit(EFI_BOOT, &efi.flags); + if (IS_ENABLED(CONFIG_64BIT)) + set_bit(EFI_64BIT, &efi.flags); + + /* + * Verify the EFI Table + */ + if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { + pr_err("System table signature incorrect\n"); + retval = -EINVAL; + goto out; + } + if ((efi.systab->hdr.revision >> 16) < 2) + pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n", + efi.systab->hdr.revision >> 16, + efi.systab->hdr.revision & 0xffff); + + /* Show what we know for posterity */ + c16 = early_memremap(efi_to_phys(efi.systab->fw_vendor), + sizeof(vendor) * sizeof(efi_char16_t)); + if (c16) { + for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i) + vendor[i] = c16[i]; + vendor[i] = '\0'; + early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t)); + } + + pr_info("EFI v%u.%.02u by %s\n", + efi.systab->hdr.revision >> 16, + efi.systab->hdr.revision & 0xffff, vendor); + + table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables; + config_tables = early_memremap(efi_to_phys(efi.systab->tables), + table_size); + if (config_tables == NULL) { + pr_warn("Unable to map EFI config table array.\n"); + retval = -ENOMEM; + goto out; + } + retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables, + sizeof(efi_config_table_t), NULL); + + early_memunmap(config_tables, table_size); +out: + early_memunmap(efi.systab, sizeof(efi_system_table_t)); + return retval; +} + +/* + * Return true for RAM regions we want to permanently reserve. + */ +static __init int is_reserve_region(efi_memory_desc_t *md) +{ + switch (md->type) { + case EFI_LOADER_CODE: + case EFI_LOADER_DATA: + case EFI_BOOT_SERVICES_CODE: + case EFI_BOOT_SERVICES_DATA: + case EFI_CONVENTIONAL_MEMORY: + case EFI_PERSISTENT_MEMORY: + return 0; + default: + break; + } + return is_normal_ram(md); +} + +static __init void reserve_regions(void) +{ + efi_memory_desc_t *md; + u64 paddr, npages, size; + + if (efi_enabled(EFI_DBG)) + pr_info("Processing EFI memory map:\n"); + + for_each_efi_memory_desc(&memmap, md) { + paddr = md->phys_addr; + npages = md->num_pages; + + if (efi_enabled(EFI_DBG)) { + char buf[64]; + + pr_info(" 0x%012llx-0x%012llx %s", + paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1, + efi_md_typeattr_format(buf, sizeof(buf), md)); + } + + memrange_efi_to_native(&paddr, &npages); + size = npages << PAGE_SHIFT; + + if (is_normal_ram(md)) + early_init_dt_add_memory_arch(paddr, size); + + if (is_reserve_region(md)) { + memblock_mark_nomap(paddr, size); + if (efi_enabled(EFI_DBG)) + pr_cont("*"); + } + + if (efi_enabled(EFI_DBG)) + pr_cont("\n"); + } + + set_bit(EFI_MEMMAP, &efi.flags); +} + +void __init efi_init(void) +{ + struct efi_fdt_params params; + + /* Grab UEFI information placed in FDT by stub */ + if (!efi_get_fdt_params(¶ms)) + return; + + efi_system_table = params.system_table; + + memmap.phys_map = params.mmap; + memmap.map = early_memremap(params.mmap, params.mmap_size); + if (memmap.map == NULL) { + /* + * If we are booting via UEFI, the UEFI memory map is the only + * description of memory we have, so there is little point in + * proceeding if we cannot access it. + */ + panic("Unable to map EFI memory map.\n"); + } + memmap.map_end = memmap.map + params.mmap_size; + memmap.desc_size = params.desc_size; + memmap.desc_version = params.desc_ver; + + if (uefi_init() < 0) + return; + + 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))); +} diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c new file mode 100644 index 000000000000..6ae21e41a429 --- /dev/null +++ b/drivers/firmware/efi/arm-runtime.c @@ -0,0 +1,135 @@ +/* + * Extensible Firmware Interface + * + * Based on Extensible Firmware Interface Specification version 2.4 + * + * Copyright (C) 2013, 2014 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/efi.h> +#include <linux/io.h> +#include <linux/memblock.h> +#include <linux/mm_types.h> +#include <linux/preempt.h> +#include <linux/rbtree.h> +#include <linux/rwsem.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <asm/cacheflush.h> +#include <asm/efi.h> +#include <asm/mmu.h> +#include <asm/pgalloc.h> +#include <asm/pgtable.h> + +extern u64 efi_system_table; + +static struct mm_struct efi_mm = { + .mm_rb = RB_ROOT, + .mm_users = ATOMIC_INIT(2), + .mm_count = ATOMIC_INIT(1), + .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem), + .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock), + .mmlist = LIST_HEAD_INIT(efi_mm.mmlist), +}; + +static bool __init efi_virtmap_init(void) +{ + efi_memory_desc_t *md; + + efi_mm.pgd = pgd_alloc(&efi_mm); + init_new_context(NULL, &efi_mm); + + for_each_efi_memory_desc(&memmap, md) { + phys_addr_t phys = md->phys_addr; + int ret; + + if (!(md->attribute & EFI_MEMORY_RUNTIME)) + continue; + if (md->virt_addr == 0) + return false; + + ret = efi_create_mapping(&efi_mm, md); + if (!ret) { + pr_info(" EFI remap %pa => %p\n", + &phys, (void *)(unsigned long)md->virt_addr); + } else { + pr_warn(" EFI remap %pa: failed to create mapping (%d)\n", + &phys, ret); + return false; + } + } + return true; +} + +/* + * Enable the UEFI Runtime Services if all prerequisites are in place, i.e., + * non-early mapping of the UEFI system table and virtual mappings for all + * EFI_MEMORY_RUNTIME regions. + */ +static int __init arm_enable_runtime_services(void) +{ + u64 mapsize; + + if (!efi_enabled(EFI_BOOT)) { + pr_info("EFI services will not be available.\n"); + return 0; + } + + if (efi_runtime_disabled()) { + pr_info("EFI runtime services will be disabled.\n"); + return 0; + } + + pr_info("Remapping and enabling EFI services.\n"); + + mapsize = memmap.map_end - memmap.map; + memmap.map = (__force void *)ioremap_cache(memmap.phys_map, + mapsize); + if (!memmap.map) { + pr_err("Failed to remap EFI memory map\n"); + return -ENOMEM; + } + memmap.map_end = memmap.map + mapsize; + efi.memmap = &memmap; + + efi.systab = (__force void *)ioremap_cache(efi_system_table, + sizeof(efi_system_table_t)); + if (!efi.systab) { + pr_err("Failed to remap EFI System Table\n"); + return -ENOMEM; + } + set_bit(EFI_SYSTEM_TABLES, &efi.flags); + + if (!efi_virtmap_init()) { + pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n"); + return -ENOMEM; + } + + /* Set up runtime services function pointers */ + efi_native_runtime_setup(); + set_bit(EFI_RUNTIME_SERVICES, &efi.flags); + + efi.runtime_version = efi.systab->hdr.revision; + + return 0; +} +early_initcall(arm_enable_runtime_services); + +void efi_virtmap_load(void) +{ + preempt_disable(); + efi_set_pgd(&efi_mm); +} + +void efi_virtmap_unload(void) +{ + efi_set_pgd(current->active_mm); + preempt_enable(); +} diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 3b52677f459a..c51f3b2fe3c0 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -25,6 +25,8 @@ #include <linux/io.h> #include <linux/platform_device.h> +#include <asm/early_ioremap.h> + struct efi __read_mostly efi = { .mps = EFI_INVALID_TABLE_ADDR, .acpi = EFI_INVALID_TABLE_ADDR, diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c index d24f35d74b27..af0060d6a22a 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci.c @@ -13,6 +13,8 @@ #define pr_fmt(fmt) "psci: " fmt +#include <linux/arm-smccc.h> +#include <linux/cpuidle.h> #include <linux/errno.h> #include <linux/linkage.h> #include <linux/of.h> @@ -20,10 +22,12 @@ #include <linux/printk.h> #include <linux/psci.h> #include <linux/reboot.h> +#include <linux/slab.h> #include <linux/suspend.h> #include <uapi/linux/psci.h> +#include <asm/cpuidle.h> #include <asm/cputype.h> #include <asm/system_misc.h> #include <asm/smp_plat.h> @@ -58,8 +62,6 @@ struct psci_operations psci_ops; typedef unsigned long (psci_fn)(unsigned long, unsigned long, unsigned long, unsigned long); -asmlinkage psci_fn __invoke_psci_fn_hvc; -asmlinkage psci_fn __invoke_psci_fn_smc; static psci_fn *invoke_psci_fn; enum psci_function { @@ -225,6 +227,119 @@ static int __init psci_features(u32 psci_func_id) psci_func_id, 0, 0); } +#ifdef CONFIG_CPU_IDLE +static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state); + +static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) +{ + int i, ret, count = 0; + u32 *psci_states; + struct device_node *state_node; + + /* + * If the PSCI cpu_suspend function hook has not been initialized + * idle states must not be enabled, so bail out + */ + if (!psci_ops.cpu_suspend) + return -EOPNOTSUPP; + + /* Count idle states */ + while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states", + count))) { + count++; + of_node_put(state_node); + } + + if (!count) + return -ENODEV; + + psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL); + if (!psci_states) + return -ENOMEM; + + for (i = 0; i < count; i++) { + u32 state; + + state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); + + ret = of_property_read_u32(state_node, + "arm,psci-suspend-param", + &state); + if (ret) { + pr_warn(" * %s missing arm,psci-suspend-param property\n", + state_node->full_name); + of_node_put(state_node); + goto free_mem; + } + + of_node_put(state_node); + pr_debug("psci-power-state %#x index %d\n", state, i); + if (!psci_power_state_is_valid(state)) { + pr_warn("Invalid PSCI power state %#x\n", state); + ret = -EINVAL; + goto free_mem; + } + psci_states[i] = state; + } + /* Idle states parsed correctly, initialize per-cpu pointer */ + per_cpu(psci_power_state, cpu) = psci_states; + return 0; + +free_mem: + kfree(psci_states); + return ret; +} + +int psci_cpu_init_idle(unsigned int cpu) +{ + struct device_node *cpu_node; + int ret; + + cpu_node = of_get_cpu_node(cpu, NULL); + if (!cpu_node) + return -ENODEV; + + ret = psci_dt_cpu_init_idle(cpu_node, cpu); + + of_node_put(cpu_node); + + return ret; +} + +static int psci_suspend_finisher(unsigned long state_id) +{ + return psci_ops.cpu_suspend(state_id, virt_to_phys(cpu_resume)); +} + +int psci_cpu_suspend_enter(unsigned long state_id) +{ + int ret; + /* + * idle state_id 0 corresponds to wfi, should never be called + * from the cpu_suspend operations + */ + if (WARN_ON_ONCE(!state_id)) + return -EINVAL; + + if (!psci_power_state_loses_context(state_id)) + ret = psci_ops.cpu_suspend(state_id, 0); + else + ret = cpu_suspend(state_id, psci_suspend_finisher); + + return ret; +} + +/* ARM specific CPU idle operations */ +#ifdef CONFIG_ARM +static struct cpuidle_ops psci_cpuidle_ops __initdata = { + .suspend = psci_cpu_suspend_enter, + .init = psci_dt_cpu_init_idle, +}; + +CPUIDLE_METHOD_OF_DECLARE(psci, "arm,psci", &psci_cpuidle_ops); +#endif +#endif + static int psci_system_suspend(unsigned long unused) { return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND), diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 48ef368347ab..9e02cb6afb0b 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -329,7 +329,7 @@ static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int irq, irq_hw_number_t hwirq) { irq_set_chip_data(irq, h->host_data); - irq_set_chip_and_handler(irq, &mpc8xxx_irq_chip, handle_level_irq); + irq_set_chip_and_handler(irq, &mpc8xxx_irq_chip, handle_edge_irq); return 0; } diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c index 990fa9023e22..3b6bce0518ab 100644 --- a/drivers/gpio/gpio-sa1100.c +++ b/drivers/gpio/gpio-sa1100.c @@ -155,7 +155,7 @@ static int sa1100_gpio_irqdomain_map(struct irq_domain *d, { irq_set_chip_and_handler(irq, &sa1100_gpio_irq_chip, handle_edge_irq); - irq_set_noprobe(irq); + irq_set_probe(irq); return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index ff5566c69f7d..e8e962f7b5cb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -532,6 +532,7 @@ struct amdgpu_bo { u64 metadata_flags; void *metadata; u32 metadata_size; + unsigned prime_shared_count; /* list of all virtual address to which this bo * is associated to */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c index f82a2dd83874..3c7a7235988d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c @@ -117,7 +117,7 @@ static int amdgpu_bo_list_set(struct amdgpu_device *adev, entry->allowed_domains = AMDGPU_GEM_DOMAIN_GTT; } entry->tv.bo = &entry->robj->tbo; - entry->tv.shared = true; + entry->tv.shared = !entry->robj->prime_shared_count; if (entry->prefered_domains == AMDGPU_GEM_DOMAIN_GDS) gds_obj = entry->robj; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c index fe36caf1b7d7..14f57d9915e3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c @@ -113,24 +113,26 @@ void amdgpu_dpm_print_ps_status(struct amdgpu_device *adev, printk("\n"); } + u32 amdgpu_dpm_get_vblank_time(struct amdgpu_device *adev) { struct drm_device *dev = adev->ddev; struct drm_crtc *crtc; struct amdgpu_crtc *amdgpu_crtc; - u32 line_time_us, vblank_lines; + u32 vblank_in_pixels; u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */ if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) { list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { amdgpu_crtc = to_amdgpu_crtc(crtc); if (crtc->enabled && amdgpu_crtc->enabled && amdgpu_crtc->hw_mode.clock) { - line_time_us = (amdgpu_crtc->hw_mode.crtc_htotal * 1000) / - amdgpu_crtc->hw_mode.clock; - vblank_lines = amdgpu_crtc->hw_mode.crtc_vblank_end - + vblank_in_pixels = + amdgpu_crtc->hw_mode.crtc_htotal * + (amdgpu_crtc->hw_mode.crtc_vblank_end - amdgpu_crtc->hw_mode.crtc_vdisplay + - (amdgpu_crtc->v_border * 2); - vblank_time_us = vblank_lines * line_time_us; + (amdgpu_crtc->v_border * 2)); + + vblank_time_us = vblank_in_pixels * 1000 / amdgpu_crtc->hw_mode.clock; break; } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 4488e82f87b0..a5c824078472 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -227,7 +227,7 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file type = AMD_IP_BLOCK_TYPE_UVD; ring_mask = adev->uvd.ring.ready ? 1 : 0; ib_start_alignment = AMDGPU_GPU_PAGE_SIZE; - ib_size_alignment = 8; + ib_size_alignment = 16; break; case AMDGPU_HW_IP_VCE: type = AMD_IP_BLOCK_TYPE_VCE; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c index 59f735a933a9..e6a7d30c3747 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c @@ -77,20 +77,36 @@ struct drm_gem_object *amdgpu_gem_prime_import_sg_table(struct drm_device *dev, list_add_tail(&bo->list, &adev->gem.objects); mutex_unlock(&adev->gem.mutex); + bo->prime_shared_count = 1; return &bo->gem_base; } int amdgpu_gem_prime_pin(struct drm_gem_object *obj) { struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); - int ret = 0; + long ret = 0; ret = amdgpu_bo_reserve(bo, false); if (unlikely(ret != 0)) return ret; + /* + * Wait for all shared fences to complete before we switch to future + * use of exclusive fence on this prime shared bo. + */ + ret = reservation_object_wait_timeout_rcu(bo->tbo.resv, true, false, + MAX_SCHEDULE_TIMEOUT); + if (unlikely(ret < 0)) { + DRM_DEBUG_PRIME("Fence wait failed: %li\n", ret); + amdgpu_bo_unreserve(bo); + return ret; + } + /* pin buffer into GTT */ ret = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT, NULL); + if (likely(ret == 0)) + bo->prime_shared_count++; + amdgpu_bo_unreserve(bo); return ret; } @@ -105,6 +121,8 @@ void amdgpu_gem_prime_unpin(struct drm_gem_object *obj) return; amdgpu_bo_unpin(bo); + if (bo->prime_shared_count) + bo->prime_shared_count--; amdgpu_bo_unreserve(bo); } diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_dp.c b/drivers/gpu/drm/amd/amdgpu/atombios_dp.c index 21aacc1f45c1..7f85c2c1d681 100644 --- a/drivers/gpu/drm/amd/amdgpu/atombios_dp.c +++ b/drivers/gpu/drm/amd/amdgpu/atombios_dp.c @@ -265,15 +265,27 @@ static int amdgpu_atombios_dp_get_dp_link_config(struct drm_connector *connector unsigned max_lane_num = drm_dp_max_lane_count(dpcd); unsigned lane_num, i, max_pix_clock; - for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) { - for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) { - max_pix_clock = (lane_num * link_rates[i] * 8) / bpp; + if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) == + ENCODER_OBJECT_ID_NUTMEG) { + for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) { + max_pix_clock = (lane_num * 270000 * 8) / bpp; if (max_pix_clock >= pix_clock) { *dp_lanes = lane_num; - *dp_rate = link_rates[i]; + *dp_rate = 270000; return 0; } } + } else { + for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) { + for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) { + max_pix_clock = (lane_num * link_rates[i] * 8) / bpp; + if (max_pix_clock >= pix_clock) { + *dp_lanes = lane_num; + *dp_rate = link_rates[i]; + return 0; + } + } + } } return -EINVAL; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index 4dcc8fba5792..5b261adb4b69 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -419,16 +419,6 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev) list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); - if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || - connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { - /* don't try to enable hpd on eDP or LVDS avoid breaking the - * aux dp channel on imac and help (but not completely fix) - * https://bugzilla.redhat.com/show_bug.cgi?id=726143 - * also avoid interrupt storms during dpms. - */ - continue; - } - switch (amdgpu_connector->hpd.hpd) { case AMDGPU_HPD_1: idx = 0; @@ -452,6 +442,19 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev) continue; } + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || + connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { + /* don't try to enable hpd on eDP or LVDS avoid breaking the + * aux dp channel on imac and help (but not completely fix) + * https://bugzilla.redhat.com/show_bug.cgi?id=726143 + * also avoid interrupt storms during dpms. + */ + tmp = RREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx]); + tmp = REG_SET_FIELD(tmp, DC_HPD_INT_CONTROL, DC_HPD_INT_EN, 0); + WREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx], tmp); + continue; + } + tmp = RREG32(mmDC_HPD_CONTROL + hpd_offsets[idx]); tmp = REG_SET_FIELD(tmp, DC_HPD_CONTROL, DC_HPD_EN, 1); WREG32(mmDC_HPD_CONTROL + hpd_offsets[idx], tmp); diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index 8f1e51128b33..c161eeda417b 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -409,16 +409,6 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev) list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); - if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || - connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { - /* don't try to enable hpd on eDP or LVDS avoid breaking the - * aux dp channel on imac and help (but not completely fix) - * https://bugzilla.redhat.com/show_bug.cgi?id=726143 - * also avoid interrupt storms during dpms. - */ - continue; - } - switch (amdgpu_connector->hpd.hpd) { case AMDGPU_HPD_1: idx = 0; @@ -442,6 +432,19 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev) continue; } + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || + connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { + /* don't try to enable hpd on eDP or LVDS avoid breaking the + * aux dp channel on imac and help (but not completely fix) + * https://bugzilla.redhat.com/show_bug.cgi?id=726143 + * also avoid interrupt storms during dpms. + */ + tmp = RREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx]); + tmp = REG_SET_FIELD(tmp, DC_HPD_INT_CONTROL, DC_HPD_INT_EN, 0); + WREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx], tmp); + continue; + } + tmp = RREG32(mmDC_HPD_CONTROL + hpd_offsets[idx]); tmp = REG_SET_FIELD(tmp, DC_HPD_CONTROL, DC_HPD_EN, 1); WREG32(mmDC_HPD_CONTROL + hpd_offsets[idx], tmp); @@ -3030,6 +3033,7 @@ static int dce_v11_0_sw_fini(void *handle) dce_v11_0_afmt_fini(adev); + drm_mode_config_cleanup(adev->ddev); adev->mode_info.mode_config_initialized = false; return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index 42d954dc436d..9b4dcf76ce6c 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -392,15 +392,6 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev) list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); - if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || - connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { - /* don't try to enable hpd on eDP or LVDS avoid breaking the - * aux dp channel on imac and help (but not completely fix) - * https://bugzilla.redhat.com/show_bug.cgi?id=726143 - * also avoid interrupt storms during dpms. - */ - continue; - } switch (amdgpu_connector->hpd.hpd) { case AMDGPU_HPD_1: WREG32(mmDC_HPD1_CONTROL, tmp); @@ -423,6 +414,45 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev) default: break; } + + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || + connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { + /* don't try to enable hpd on eDP or LVDS avoid breaking the + * aux dp channel on imac and help (but not completely fix) + * https://bugzilla.redhat.com/show_bug.cgi?id=726143 + * also avoid interrupt storms during dpms. + */ + u32 dc_hpd_int_cntl_reg, dc_hpd_int_cntl; + + switch (amdgpu_connector->hpd.hpd) { + case AMDGPU_HPD_1: + dc_hpd_int_cntl_reg = mmDC_HPD1_INT_CONTROL; + break; + case AMDGPU_HPD_2: + dc_hpd_int_cntl_reg = mmDC_HPD2_INT_CONTROL; + break; + case AMDGPU_HPD_3: + dc_hpd_int_cntl_reg = mmDC_HPD3_INT_CONTROL; + break; + case AMDGPU_HPD_4: + dc_hpd_int_cntl_reg = mmDC_HPD4_INT_CONTROL; + break; + case AMDGPU_HPD_5: + dc_hpd_int_cntl_reg = mmDC_HPD5_INT_CONTROL; + break; + case AMDGPU_HPD_6: + dc_hpd_int_cntl_reg = mmDC_HPD6_INT_CONTROL; + break; + default: + continue; + } + + dc_hpd_int_cntl = RREG32(dc_hpd_int_cntl_reg); + dc_hpd_int_cntl &= ~DC_HPD1_INT_CONTROL__DC_HPD1_INT_EN_MASK; + WREG32(dc_hpd_int_cntl_reg, dc_hpd_int_cntl); + continue; + } + dce_v8_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd); amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd); } diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index 59d1269626b1..e231176cb66b 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -316,19 +316,19 @@ atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane, u32 *coeff_tab = heo_upscaling_ycoef; u32 max_memsize; - if (state->crtc_w < state->src_w) + if (state->crtc_h < state->src_h) coeff_tab = heo_downscaling_ycoef; for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++) atmel_hlcdc_layer_update_cfg(&plane->layer, 33 + i, 0xffffffff, coeff_tab[i]); - factor = ((8 * 256 * state->src_w) - (256 * 4)) / - state->crtc_w; + factor = ((8 * 256 * state->src_h) - (256 * 4)) / + state->crtc_h; factor++; - max_memsize = ((factor * state->crtc_w) + (256 * 4)) / + max_memsize = ((factor * state->crtc_h) + (256 * 4)) / 2048; - if (max_memsize > state->src_w) + if (max_memsize > state->src_h) factor--; factor_reg |= (factor << 16) | 0x80000000; } diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 2485fb652716..7cb2815e815e 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -909,6 +909,7 @@ static void drm_dp_destroy_port(struct kref *kref) /* no need to clean up vcpi * as if we have no connector we never setup a vcpi */ drm_dp_port_teardown_pdt(port, port->pdt); + port->pdt = DP_PEER_DEVICE_NONE; } kfree(port); } @@ -1154,7 +1155,9 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, drm_dp_put_port(port); goto out; } - if (port->port_num >= DP_MST_LOGICAL_PORT_0) { + if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV || + port->pdt == DP_PEER_DEVICE_SST_SINK) && + port->port_num >= DP_MST_LOGICAL_PORT_0) { port->cached_edid = drm_get_edid(port->connector, &port->aux.ddc); drm_mode_connector_set_tile_property(port->connector); } @@ -2872,6 +2875,7 @@ static void drm_dp_destroy_connector_work(struct work_struct *work) mgr->cbs->destroy_connector(mgr, port->connector); drm_dp_port_teardown_pdt(port, port->pdt); + port->pdt = DP_PEER_DEVICE_NONE; if (!port->input && port->vcpi.vcpi > 0) { drm_dp_mst_reset_vcpi_slots(mgr, port); diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c index 57676f8d7ecf..a6289752be16 100644 --- a/drivers/gpu/drm/drm_ioc32.c +++ b/drivers/gpu/drm/drm_ioc32.c @@ -1015,6 +1015,7 @@ static int compat_drm_wait_vblank(struct file *file, unsigned int cmd, return 0; } +#if defined(CONFIG_X86) || defined(CONFIG_IA64) typedef struct drm_mode_fb_cmd232 { u32 fb_id; u32 width; @@ -1071,6 +1072,7 @@ static int compat_drm_mode_addfb2(struct file *file, unsigned int cmd, return 0; } +#endif static drm_ioctl_compat_t *drm_compat_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_VERSION32)] = compat_drm_version, @@ -1104,7 +1106,9 @@ static drm_ioctl_compat_t *drm_compat_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_UPDATE_DRAW32)] = compat_drm_update_draw, #endif [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK32)] = compat_drm_wait_vblank, +#if defined(CONFIG_X86) || defined(CONFIG_IA64) [DRM_IOCTL_NR(DRM_IOCTL_MODE_ADDFB232)] = compat_drm_mode_addfb2, +#endif }; /** diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 9f935f55d74c..968b31f39884 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -339,14 +339,17 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { * using the PRIME helpers. */ struct dma_buf *drm_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags) + struct drm_gem_object *obj, + int flags) { - DEFINE_DMA_BUF_EXPORT_INFO(exp_info); - - exp_info.ops = &drm_gem_prime_dmabuf_ops; - exp_info.size = obj->size; - exp_info.flags = flags; - exp_info.priv = obj; + struct dma_buf_export_info exp_info = { + .exp_name = KBUILD_MODNAME, /* white lie for debug */ + .owner = dev->driver->fops->owner, + .ops = &drm_gem_prime_dmabuf_ops, + .size = obj->size, + .flags = flags, + .priv = obj, + }; if (dev->driver->gem_prime_res_obj) exp_info.resv = dev->driver->gem_prime_res_obj(obj); diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index 7f55ba6771c6..011211e4167d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -101,7 +101,7 @@ int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file) return 0; err: - list_for_each_entry_reverse(subdrv, &subdrv->list, list) { + list_for_each_entry_continue_reverse(subdrv, &exynos_drm_subdrv_list, list) { if (subdrv->close) subdrv->close(dev, subdrv->dev, file); } diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 17cea400ae32..d3de377dc857 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -220,7 +220,7 @@ i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter) * FIXME: This is the old dp aux helper, gma500 is the last driver that needs to * be ported over to the new helper code in drm_dp_helper.c like i915 or radeon. */ -static int __deprecated +static int i2c_dp_aux_add_bus(struct i2c_adapter *adapter) { int error; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index d400d6773bbb..fb9f647bb5cd 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2150,21 +2150,19 @@ struct drm_i915_gem_object { /** Record of address bit 17 of each page at last unbind. */ unsigned long *bit_17; - union { - /** for phy allocated objects */ - struct drm_dma_handle *phys_handle; - - struct i915_gem_userptr { - uintptr_t ptr; - unsigned read_only :1; - unsigned workers :4; + struct i915_gem_userptr { + uintptr_t ptr; + unsigned read_only :1; + unsigned workers :4; #define I915_GEM_USERPTR_MAX_WORKERS 15 - struct i915_mm_struct *mm; - struct i915_mmu_object *mmu_object; - struct work_struct *work; - } userptr; - }; + struct i915_mm_struct *mm; + struct i915_mmu_object *mmu_object; + struct work_struct *work; + } userptr; + + /** for phys allocated objects */ + struct drm_dma_handle *phys_handle; }; #define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base) diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 87e919a06b27..5d2323a40c25 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -108,17 +108,28 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) pci_read_config_dword(dev->pdev, 0x5c, &base); base &= ~((1<<20) - 1); } else if (IS_I865G(dev)) { + u32 tseg_size = 0; u16 toud = 0; + u8 tmp; + + pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0), + I845_ESMRAMC, &tmp); + + if (tmp & TSEG_ENABLE) { + switch (tmp & I845_TSEG_SIZE_MASK) { + case I845_TSEG_SIZE_512K: + tseg_size = KB(512); + break; + case I845_TSEG_SIZE_1M: + tseg_size = MB(1); + break; + } + } - /* - * FIXME is the graphics stolen memory region - * always at TOUD? Ie. is it always the last - * one to be allocated by the BIOS? - */ pci_bus_read_config_word(dev->pdev->bus, PCI_DEVFN(0, 0), I865_TOUD, &toud); - base = toud << 16; + base = (toud << 16) + tseg_size; } else if (IS_I85X(dev)) { u32 tseg_size = 0; u32 tom; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a3254c3bcc7c..909d1d71d130 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2950,13 +2950,13 @@ u32 intel_fb_stride_alignment(struct drm_device *dev, uint64_t fb_modifier, } } -unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane, - struct drm_i915_gem_object *obj, - unsigned int plane) +u32 intel_plane_obj_offset(struct intel_plane *intel_plane, + struct drm_i915_gem_object *obj, + unsigned int plane) { const struct i915_ggtt_view *view = &i915_ggtt_view_normal; struct i915_vma *vma; - unsigned char *offset; + u64 offset; if (intel_rotation_90_or_270(intel_plane->base.state->rotation)) view = &i915_ggtt_view_rotated; @@ -2966,14 +2966,16 @@ unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane, view->type)) return -1; - offset = (unsigned char *)vma->node.start; + offset = vma->node.start; if (plane == 1) { offset += vma->ggtt_view.rotation_info.uv_start_page * PAGE_SIZE; } - return (unsigned long)offset; + WARN_ON(upper_32_bits(offset)); + + return lower_32_bits(offset); } static void skl_detach_scaler(struct intel_crtc *intel_crtc, int id) @@ -3099,7 +3101,7 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc, u32 tile_height, plane_offset, plane_size; unsigned int rotation; int x_offset, y_offset; - unsigned long surf_addr; + u32 surf_addr; struct intel_crtc_state *crtc_state = intel_crtc->config; struct intel_plane_state *plane_state; int src_x = 0, src_y = 0, src_w = 0, src_h = 0; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index ebbd23407a80..0f8367da0663 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4648,7 +4648,7 @@ static bool bxt_digital_port_connected(struct drm_i915_private *dev_priv, * * Return %true if @port is connected, %false otherwise. */ -bool intel_digital_port_connected(struct drm_i915_private *dev_priv, +static bool intel_digital_port_connected(struct drm_i915_private *dev_priv, struct intel_digital_port *port) { if (HAS_PCH_IBX(dev_priv)) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 67f72a7ee7cb..722aa159cd28 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1177,9 +1177,9 @@ void intel_modeset_preclose(struct drm_device *dev, struct drm_file *file); int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state); int skl_max_scale(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state); -unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane, - struct drm_i915_gem_object *obj, - unsigned int plane); +u32 intel_plane_obj_offset(struct intel_plane *intel_plane, + struct drm_i915_gem_object *obj, + unsigned int plane); u32 skl_plane_ctl_format(uint32_t pixel_format); u32 skl_plane_ctl_tiling(uint64_t fb_modifier); @@ -1231,8 +1231,6 @@ void intel_edp_drrs_disable(struct intel_dp *intel_dp); void intel_edp_drrs_invalidate(struct drm_device *dev, unsigned frontbuffer_bits); void intel_edp_drrs_flush(struct drm_device *dev, unsigned frontbuffer_bits); -bool intel_digital_port_connected(struct drm_i915_private *dev_priv, - struct intel_digital_port *port); void hsw_dp_set_ddi_pll_sel(struct intel_crtc_state *pipe_config); /* intel_dp_mst.c */ diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index dff69fef47e0..1ea8532f5ab2 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1331,19 +1331,18 @@ intel_hdmi_unset_edid(struct drm_connector *connector) } static bool -intel_hdmi_set_edid(struct drm_connector *connector, bool force) +intel_hdmi_set_edid(struct drm_connector *connector) { struct drm_i915_private *dev_priv = to_i915(connector->dev); struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); - struct edid *edid = NULL; + struct edid *edid; bool connected = false; intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS); - if (force) - edid = drm_get_edid(connector, - intel_gmbus_get_adapter(dev_priv, - intel_hdmi->ddc_bus)); + edid = drm_get_edid(connector, + intel_gmbus_get_adapter(dev_priv, + intel_hdmi->ddc_bus)); intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS); @@ -1371,37 +1370,16 @@ static enum drm_connector_status intel_hdmi_detect(struct drm_connector *connector, bool force) { enum drm_connector_status status; - struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); struct drm_i915_private *dev_priv = to_i915(connector->dev); - bool live_status = false; - unsigned int try; DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS); - for (try = 0; !live_status && try < 9; try++) { - if (try) - msleep(10); - live_status = intel_digital_port_connected(dev_priv, - hdmi_to_dig_port(intel_hdmi)); - } - - if (!live_status) { - DRM_DEBUG_KMS("HDMI live status down\n"); - /* - * Live status register is not reliable on all intel platforms. - * So consider live_status only for certain platforms, for - * others, read EDID to determine presence of sink. - */ - if (INTEL_INFO(dev_priv)->gen < 7 || IS_IVYBRIDGE(dev_priv)) - live_status = true; - } - intel_hdmi_unset_edid(connector); - if (intel_hdmi_set_edid(connector, live_status)) { + if (intel_hdmi_set_edid(connector)) { struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI; @@ -1427,7 +1405,7 @@ intel_hdmi_force(struct drm_connector *connector) if (connector->status != connector_status_connected) return; - intel_hdmi_set_edid(connector, true); + intel_hdmi_set_edid(connector); hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI; } @@ -2019,6 +1997,50 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE; } +static u8 intel_hdmi_ddc_pin(struct drm_i915_private *dev_priv, + enum port port) +{ + const struct ddi_vbt_port_info *info = + &dev_priv->vbt.ddi_port_info[port]; + u8 ddc_pin; + + if (info->alternate_ddc_pin) { + DRM_DEBUG_KMS("Using DDC pin 0x%x for port %c (VBT)\n", + info->alternate_ddc_pin, port_name(port)); + return info->alternate_ddc_pin; + } + + switch (port) { + case PORT_B: + if (IS_BROXTON(dev_priv)) + ddc_pin = GMBUS_PIN_1_BXT; + else + ddc_pin = GMBUS_PIN_DPB; + break; + case PORT_C: + if (IS_BROXTON(dev_priv)) + ddc_pin = GMBUS_PIN_2_BXT; + else + ddc_pin = GMBUS_PIN_DPC; + break; + case PORT_D: + if (IS_CHERRYVIEW(dev_priv)) + ddc_pin = GMBUS_PIN_DPD_CHV; + else + ddc_pin = GMBUS_PIN_DPD; + break; + default: + MISSING_CASE(port); + ddc_pin = GMBUS_PIN_DPB; + break; + } + + DRM_DEBUG_KMS("Using DDC pin 0x%x for port %c (platform default)\n", + ddc_pin, port_name(port)); + + return ddc_pin; +} + void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector) { @@ -2028,7 +2050,6 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, struct drm_device *dev = intel_encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; enum port port = intel_dig_port->port; - uint8_t alternate_ddc_pin; DRM_DEBUG_KMS("Adding HDMI connector on port %c\n", port_name(port)); @@ -2041,12 +2062,10 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, connector->doublescan_allowed = 0; connector->stereo_allowed = 1; + intel_hdmi->ddc_bus = intel_hdmi_ddc_pin(dev_priv, port); + switch (port) { case PORT_B: - if (IS_BROXTON(dev_priv)) - intel_hdmi->ddc_bus = GMBUS_PIN_1_BXT; - else - intel_hdmi->ddc_bus = GMBUS_PIN_DPB; /* * On BXT A0/A1, sw needs to activate DDIA HPD logic and * interrupts to check the external panel connection. @@ -2057,46 +2076,17 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, intel_encoder->hpd_pin = HPD_PORT_B; break; case PORT_C: - if (IS_BROXTON(dev_priv)) - intel_hdmi->ddc_bus = GMBUS_PIN_2_BXT; - else - intel_hdmi->ddc_bus = GMBUS_PIN_DPC; intel_encoder->hpd_pin = HPD_PORT_C; break; case PORT_D: - if (WARN_ON(IS_BROXTON(dev_priv))) - intel_hdmi->ddc_bus = GMBUS_PIN_DISABLED; - else if (IS_CHERRYVIEW(dev_priv)) - intel_hdmi->ddc_bus = GMBUS_PIN_DPD_CHV; - else - intel_hdmi->ddc_bus = GMBUS_PIN_DPD; intel_encoder->hpd_pin = HPD_PORT_D; break; case PORT_E: - /* On SKL PORT E doesn't have seperate GMBUS pin - * We rely on VBT to set a proper alternate GMBUS pin. */ - alternate_ddc_pin = - dev_priv->vbt.ddi_port_info[PORT_E].alternate_ddc_pin; - switch (alternate_ddc_pin) { - case DDC_PIN_B: - intel_hdmi->ddc_bus = GMBUS_PIN_DPB; - break; - case DDC_PIN_C: - intel_hdmi->ddc_bus = GMBUS_PIN_DPC; - break; - case DDC_PIN_D: - intel_hdmi->ddc_bus = GMBUS_PIN_DPD; - break; - default: - MISSING_CASE(alternate_ddc_pin); - } intel_encoder->hpd_pin = HPD_PORT_E; break; - case PORT_A: - intel_encoder->hpd_pin = HPD_PORT_A; - /* Internal port only for eDP. */ default: - BUG(); + MISSING_CASE(port); + return; } if (IS_VALLEYVIEW(dev)) { diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 1e851e037c29..3f802163f7d4 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2097,32 +2097,34 @@ static void intel_read_wm_latency(struct drm_device *dev, uint16_t wm[8]) GEN9_MEM_LATENCY_LEVEL_MASK; /* + * If a level n (n > 1) has a 0us latency, all levels m (m >= n) + * need to be disabled. We make sure to sanitize the values out + * of the punit to satisfy this requirement. + */ + for (level = 1; level <= max_level; level++) { + if (wm[level] == 0) { + for (i = level + 1; i <= max_level; i++) + wm[i] = 0; + break; + } + } + + /* * WaWmMemoryReadLatency:skl * * punit doesn't take into account the read latency so we need - * to add 2us to the various latency levels we retrieve from - * the punit. - * - W0 is a bit special in that it's the only level that - * can't be disabled if we want to have display working, so - * we always add 2us there. - * - For levels >=1, punit returns 0us latency when they are - * disabled, so we respect that and don't add 2us then - * - * Additionally, if a level n (n > 1) has a 0us latency, all - * levels m (m >= n) need to be disabled. We make sure to - * sanitize the values out of the punit to satisfy this - * requirement. + * to add 2us to the various latency levels we retrieve from the + * punit when level 0 response data us 0us. */ - wm[0] += 2; - for (level = 1; level <= max_level; level++) - if (wm[level] != 0) + if (wm[0] == 0) { + wm[0] += 2; + for (level = 1; level <= max_level; level++) { + if (wm[level] == 0) + break; wm[level] += 2; - else { - for (i = level + 1; i <= max_level; i++) - wm[i] = 0; - - break; } + } + } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { uint64_t sskpd = I915_READ64(MCH_SSKPD); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 56dc132e8e20..2cc6aa072f4c 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -195,7 +195,7 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc, int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); const struct drm_intel_sprite_colorkey *key = &to_intel_plane_state(drm_plane->state)->ckey; - unsigned long surf_addr; + u32 surf_addr; u32 tile_height, plane_offset, plane_size; unsigned int rotation; int x_offset, y_offset; diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 62675198d6ac..5838545468f8 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -11,6 +11,7 @@ config DRM_MSM select TMPFS select QCOM_SCM select BACKLIGHT_CLASS_DEVICE + select MSM_EXT_DISPLAY default y help DRM/KMS driver for MSM/snapdragon. diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index d81ec8918ce7..79ea5a9f90ea 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -100,7 +100,8 @@ msm_drm-$(CONFIG_DRM_MSM_DSI_STAGING) += dsi-staging/dsi_phy.o \ msm_drm-$(CONFIG_DRM_SDE_HDMI) += \ hdmi-staging/sde_hdmi.o \ hdmi-staging/sde_hdmi_bridge.o \ - hdmi-staging/sde_hdmi_audio.o + hdmi-staging/sde_hdmi_audio.o \ + hdmi-staging/sde_hdmi_edid.o msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \ dsi/pll/dsi_pll_28nm.o diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 15e2d69827e7..347b78886b24 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -22,8 +22,10 @@ #include <linux/of.h> #include <linux/gpio.h> #include <linux/of_irq.h> +#include <linux/of_platform.h> #include "sde_kms.h" +#include "sde_connector.h" #include "msm_drv.h" #include "sde_hdmi.h" @@ -402,6 +404,13 @@ static void _sde_hdmi_hotplug_work(struct work_struct *work) } connector = sde_hdmi->ctrl.ctrl->connector; + + if (sde_hdmi->connected) + sde_hdmi_get_edid(connector, sde_hdmi); + else + sde_hdmi_free_edid(sde_hdmi); + + sde_hdmi_notify_clients(connector, sde_hdmi->connected); drm_helper_hpd_irq_event(connector->dev); } @@ -431,7 +440,8 @@ static void _sde_hdmi_connector_irq(struct sde_hdmi *sde_hdmi) hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT; hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl); - queue_work(hdmi->workq, &sde_hdmi->hpd_work); + if (!sde_hdmi->non_pluggable) + queue_work(hdmi->workq, &sde_hdmi->hpd_work); } } @@ -460,6 +470,148 @@ static irqreturn_t _sde_hdmi_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static int _sde_hdmi_audio_info_setup(struct platform_device *pdev, + struct msm_ext_disp_audio_setup_params *params) +{ + int rc = -EPERM; + struct sde_hdmi *display = NULL; + struct hdmi *hdmi = NULL; + + display = platform_get_drvdata(pdev); + + if (!display || !params) { + SDE_ERROR("invalid param(s), display %pK, params %pK\n", + display, params); + return -ENODEV; + } + + hdmi = display->ctrl.ctrl; + + if (hdmi->hdmi_mode) + rc = sde_hdmi_audio_on(hdmi, params); + + return rc; +} + +static int _sde_hdmi_get_audio_edid_blk(struct platform_device *pdev, + struct msm_ext_disp_audio_edid_blk *blk) +{ + struct sde_hdmi *display = platform_get_drvdata(pdev); + + if (!display || !blk) { + SDE_ERROR("invalid param(s), display %pK, blk %pK\n", + display, blk); + return -ENODEV; + } + + blk->audio_data_blk = display->edid.audio_data_block; + blk->audio_data_blk_size = display->edid.adb_size; + + blk->spk_alloc_data_blk = display->edid.spkr_alloc_data_block; + blk->spk_alloc_data_blk_size = display->edid.sadb_size; + + return 0; +} + +static int _sde_hdmi_get_cable_status(struct platform_device *pdev, u32 vote) +{ + struct sde_hdmi *display = NULL; + struct hdmi *hdmi = NULL; + + display = platform_get_drvdata(pdev); + + if (!display) { + SDE_ERROR("invalid param(s), display %pK\n", display); + return -ENODEV; + } + + hdmi = display->ctrl.ctrl; + + return hdmi->power_on && display->connected; +} + +static int _sde_hdmi_ext_disp_init(struct sde_hdmi *display) +{ + int rc = 0; + struct device_node *pd_np; + const char *phandle = "qcom,msm_ext_disp"; + + if (!display) { + SDE_ERROR("[%s]Invalid params\n", display->name); + return -EINVAL; + } + + display->ext_audio_data.type = EXT_DISPLAY_TYPE_HDMI; + display->ext_audio_data.pdev = display->pdev; + display->ext_audio_data.codec_ops.audio_info_setup = + _sde_hdmi_audio_info_setup; + display->ext_audio_data.codec_ops.get_audio_edid_blk = + _sde_hdmi_get_audio_edid_blk; + display->ext_audio_data.codec_ops.cable_status = + _sde_hdmi_get_cable_status; + + if (!display->pdev->dev.of_node) { + SDE_ERROR("[%s]cannot find sde_hdmi of_node\n", display->name); + return -ENODEV; + } + + pd_np = of_parse_phandle(display->pdev->dev.of_node, phandle, 0); + if (!pd_np) { + SDE_ERROR("[%s]cannot find %s device node\n", + display->name, phandle); + return -ENODEV; + } + + display->ext_pdev = of_find_device_by_node(pd_np); + if (!display->ext_pdev) { + SDE_ERROR("[%s]cannot find %s platform device\n", + display->name, phandle); + return -ENODEV; + } + + rc = msm_ext_disp_register_intf(display->ext_pdev, + &display->ext_audio_data); + if (rc) + SDE_ERROR("[%s]failed to register disp\n", display->name); + + return rc; +} + +void sde_hdmi_notify_clients(struct drm_connector *connector, + bool connected) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + int state = connected ? + EXT_DISPLAY_CABLE_CONNECT : EXT_DISPLAY_CABLE_DISCONNECT; + + if (display && display->ext_audio_data.intf_ops.hpd) { + struct hdmi *hdmi = display->ctrl.ctrl; + u32 flags = MSM_EXT_DISP_HPD_VIDEO; + + if (hdmi->hdmi_mode) + flags |= MSM_EXT_DISP_HPD_AUDIO; + + display->ext_audio_data.intf_ops.hpd(display->ext_pdev, + display->ext_audio_data.type, state, flags); + } +} + +void sde_hdmi_ack_state(struct drm_connector *connector, + enum drm_connector_status status) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + + if (display) { + struct hdmi *hdmi = display->ctrl.ctrl; + + if (hdmi->hdmi_mode && display->ext_audio_data.intf_ops.notify) + display->ext_audio_data.intf_ops.notify( + display->ext_pdev, status); + } +} + void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on) { uint32_t ctrl = 0; @@ -645,13 +797,29 @@ sde_hdmi_connector_detect(struct drm_connector *connector, return status; } +int _sde_hdmi_update_modes(struct drm_connector *connector, + struct sde_hdmi *display) +{ + int rc = 0; + struct hdmi_edid_ctrl *edid_ctrl = &display->edid; + + if (edid_ctrl->edid) { + drm_mode_connector_update_edid_property(connector, + edid_ctrl->edid); + + rc = drm_add_edid_modes(connector, edid_ctrl->edid); + return rc; + } + + drm_mode_connector_update_edid_property(connector, NULL); + + return rc; +} + int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display) { struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display; - struct hdmi *hdmi; - struct edid *edid; struct drm_display_mode *mode, *m; - uint32_t hdmi_ctrl; int ret = 0; if (!connector || !display) { @@ -662,7 +830,6 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display) SDE_DEBUG("\n"); - hdmi = hdmi_display->ctrl.ctrl; if (hdmi_display->non_pluggable) { list_for_each_entry(mode, &hdmi_display->mode_list, head) { m = drm_mode_duplicate(connector->dev, mode); @@ -675,21 +842,7 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display) } ret = hdmi_display->num_of_modes; } else { - /* Read EDID */ - hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL); - hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE); - - edid = drm_get_edid(connector, hdmi->i2c); - - hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl); - - hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid); - drm_mode_connector_update_edid_property(connector, edid); - - if (edid) { - ret = drm_add_edid_modes(connector, edid); - kfree(edid); - } + ret = _sde_hdmi_update_modes(connector, display); } return ret; @@ -778,6 +931,20 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) if (rc) { SDE_ERROR("[%s]Debugfs init failed, rc=%d\n", display->name, rc); + goto debug_error; + } + + rc = _sde_hdmi_ext_disp_init(display); + if (rc) { + SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n", + display->name, rc); + goto error; + } + + rc = sde_hdmi_edid_init(display); + if (rc) { + SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n", + display->name, rc); goto error; } @@ -787,6 +954,8 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) display->drm_dev = drm; error: + (void)_sde_hdmi_debugfs_deinit(display); +debug_error: mutex_unlock(&display->display_lock); return rc; } @@ -809,6 +978,7 @@ static void sde_hdmi_unbind(struct device *dev, struct device *master, } mutex_lock(&display->display_lock); (void)_sde_hdmi_debugfs_deinit(display); + (void)sde_hdmi_edid_deinit(display); display->drm_dev = NULL; mutex_unlock(&display->display_lock); } diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index 1c13d9f875f2..869d1bebf9db 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -25,6 +25,10 @@ #include <drm/drm_crtc.h> #include "hdmi.h" +#define MAX_NUMBER_ADB 5 +#define MAX_AUDIO_DATA_BLOCK_SIZE 30 +#define MAX_SPKR_ALLOC_DATA_BLOCK_SIZE 3 + /** * struct sde_hdmi_info - defines hdmi display properties * @display_type: Display type as defined by device tree. @@ -60,6 +64,14 @@ struct sde_hdmi_ctrl { u32 hdmi_ctrl_idx; }; +struct hdmi_edid_ctrl { + struct edid *edid; + 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]; + int sadb_size; +}; + /** * struct sde_hdmi - hdmi display information * @pdev: Pointer to platform device. @@ -88,6 +100,10 @@ struct sde_hdmi { struct sde_hdmi_ctrl ctrl; + struct platform_device *ext_pdev; + struct msm_ext_disp_init_data ext_audio_data; + struct hdmi_edid_ctrl edid; + bool non_pluggable; u32 num_of_modes; struct list_head mode_list; @@ -268,6 +284,61 @@ void sde_hdmi_audio_off(struct hdmi *hdmi); * Return: error code. */ int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set); + +/** + * sde_hdmi_notify_clients() - notify hdmi clients of the connection status. + * @connector: Handle to the drm_connector. + * @connected: connection status. + * + * Return: void. + */ +void sde_hdmi_notify_clients(struct drm_connector *connector, + bool connected); + +/** + * sde_hdmi_ack_state() - acknowledge the connection status. + * @connector: Handle to the drm_connector. + * @status: connection status. + * + * Return: void. + */ +void sde_hdmi_ack_state(struct drm_connector *connector, + enum drm_connector_status status); + +/** + * sde_hdmi_edid_init() - init edid structure. + * @display: Handle to the sde_hdmi. + * + * Return: error code. + */ +int sde_hdmi_edid_init(struct sde_hdmi *display); + +/** + * sde_hdmi_edid_deinit() - deinit edid structure. + * @display: Handle to the sde_hdmi. + * + * Return: error code. + */ +int sde_hdmi_edid_deinit(struct sde_hdmi *display); + +/** + * sde_hdmi_get_edid() - get edid info. + * @connector: Handle to the drm_connector. + * @display: Handle to the sde_hdmi. + * + * Return: void. + */ +void sde_hdmi_get_edid(struct drm_connector *connector, + struct sde_hdmi *display); + +/** + * sde_hdmi_free_edid() - free edid structure. + * @display: Handle to the sde_hdmi. + * + * Return: error code. + */ +int sde_hdmi_free_edid(struct sde_hdmi *display); + #else /*#ifdef CONFIG_DRM_SDE_HDMI*/ static inline u32 sde_hdmi_get_num_of_displays(void) diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index ecfff7e88689..681dca501f9b 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -18,6 +18,7 @@ #include "drm_edid.h" #include "sde_kms.h" +#include "sde_connector.h" #include "sde_hdmi.h" #include "hdmi.h" @@ -111,7 +112,6 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) if (!hdmi->power_on) { _sde_hdmi_bridge_power_on(bridge); hdmi->power_on = true; - hdmi_audio_update(hdmi); } if (phy) @@ -121,14 +121,42 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported) hdmi_hdcp_ctrl_on(hdmi->hdcp_ctrl); + + sde_hdmi_ack_state(hdmi->connector, EXT_DISPLAY_CABLE_CONNECT); +} + +static void sde_hdmi_force_update_audio(struct drm_connector *connector, + enum drm_connector_status status) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + + if (display && display->non_pluggable) { + display->ext_audio_data.intf_ops.hpd(display->ext_pdev, + display->ext_audio_data.type, + status, + MSM_EXT_DISP_HPD_AUDIO); + } } static void _sde_hdmi_bridge_enable(struct drm_bridge *bridge) { + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + + /* force update audio ops when there's no HPD event */ + sde_hdmi_force_update_audio(hdmi->connector, + EXT_DISPLAY_CABLE_CONNECT); } static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge) { + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + + /* force update audio ops when there's no HPD event */ + sde_hdmi_force_update_audio(hdmi->connector, + EXT_DISPLAY_CABLE_DISCONNECT); } static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) @@ -140,6 +168,8 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported) hdmi_hdcp_ctrl_off(hdmi->hdcp_ctrl); + sde_hdmi_audio_off(hdmi); + DRM_DEBUG("power down"); sde_hdmi_set_mode(hdmi, false); @@ -149,8 +179,9 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) if (hdmi->power_on) { _sde_hdmi_bridge_power_off(bridge); hdmi->power_on = false; - hdmi_audio_update(hdmi); } + + sde_hdmi_ack_state(hdmi->connector, EXT_DISPLAY_CABLE_DISCONNECT); } static void _sde_hdmi_bridge_set_avi_infoframe(struct hdmi *hdmi, @@ -342,8 +373,6 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge, _sde_hdmi_bridge_set_spd_infoframe(hdmi, mode); DRM_DEBUG("hdmi setup info frame\n"); } - - hdmi_audio_update(hdmi); } static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = { diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c new file mode 100644 index 000000000000..57c79e2aa812 --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c @@ -0,0 +1,227 @@ +/* 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 <drm/drm_edid.h> + +#include "sde_kms.h" +#include "sde_hdmi.h" + +/* TODO: copy from drm_edid.c and mdss_hdmi_edid.c. remove if using ELD */ +#define DBC_START_OFFSET 4 +#define EDID_DTD_LEN 18 + +enum data_block_types { + RESERVED, + AUDIO_DATA_BLOCK, + VIDEO_DATA_BLOCK, + VENDOR_SPECIFIC_DATA_BLOCK, + SPEAKER_ALLOCATION_DATA_BLOCK, + VESA_DTC_DATA_BLOCK, + RESERVED2, + USE_EXTENDED_TAG +}; + +static u8 *_sde_hdmi_edid_find_cea_extension(struct edid *edid) +{ + u8 *edid_ext = NULL; + int i; + + /* No EDID or EDID extensions */ + if (edid == NULL || edid->extensions == 0) + return NULL; + + /* Find CEA extension */ + for (i = 0; i < edid->extensions; i++) { + edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); + if (edid_ext[0] == CEA_EXT) + break; + } + + if (i == edid->extensions) + return NULL; + + return edid_ext; +} + +static const u8 *_sde_hdmi_edid_find_block(const u8 *in_buf, u32 start_offset, + u8 type, u8 *len) +{ + /* the start of data block collection, start of Video Data Block */ + u32 offset = start_offset; + u32 dbc_offset = in_buf[2]; + + /* + * * edid buffer 1, byte 2 being 4 means no non-DTD/Data block + * collection present. + * * edid buffer 1, byte 2 being 0 means no non-DTD/DATA block + * collection present and no DTD data present. + */ + if ((dbc_offset == 0) || (dbc_offset == 4)) { + SDE_ERROR("EDID: no DTD or non-DTD data present\n"); + return NULL; + } + + while (offset < dbc_offset) { + u8 block_len = in_buf[offset] & 0x1F; + + if ((offset + block_len <= dbc_offset) && + (in_buf[offset] >> 5) == type) { + *len = block_len; + SDE_DEBUG("EDID: block=%d found @ 0x%x w/ len=%d\n", + type, offset, block_len); + + return in_buf + offset; + } + offset += 1 + block_len; + } + + return NULL; +} + +static void _sde_hdmi_extract_audio_data_blocks( + struct hdmi_edid_ctrl *edid_ctrl) +{ + u8 len = 0; + u8 adb_max = 0; + const u8 *adb = NULL; + u32 offset = DBC_START_OFFSET; + u8 *cea = NULL; + + if (!edid_ctrl) { + SDE_ERROR("invalid edid_ctrl\n"); + return; + } + + cea = _sde_hdmi_edid_find_cea_extension(edid_ctrl->edid); + if (!cea) { + SDE_DEBUG("CEA extension not found\n"); + return; + } + + edid_ctrl->adb_size = 0; + + memset(edid_ctrl->audio_data_block, 0, + sizeof(edid_ctrl->audio_data_block)); + + do { + len = 0; + adb = _sde_hdmi_edid_find_block(cea, offset, AUDIO_DATA_BLOCK, + &len); + + if ((adb == NULL) || (len > MAX_AUDIO_DATA_BLOCK_SIZE || + adb_max >= MAX_NUMBER_ADB)) { + if (!edid_ctrl->adb_size) { + SDE_DEBUG("No/Invalid Audio Data Block\n"); + return; + } + + continue; + } + + memcpy(edid_ctrl->audio_data_block + edid_ctrl->adb_size, + adb + 1, len); + offset = (adb - cea) + 1 + len; + + edid_ctrl->adb_size += len; + adb_max++; + } while (adb); + +} + +static void _sde_hdmi_extract_speaker_allocation_data( + struct hdmi_edid_ctrl *edid_ctrl) +{ + u8 len; + const u8 *sadb = NULL; + u8 *cea = NULL; + + if (!edid_ctrl) { + SDE_ERROR("invalid edid_ctrl\n"); + return; + } + + cea = _sde_hdmi_edid_find_cea_extension(edid_ctrl->edid); + if (!cea) { + SDE_DEBUG("CEA extension not found\n"); + return; + } + + sadb = _sde_hdmi_edid_find_block(cea, DBC_START_OFFSET, + SPEAKER_ALLOCATION_DATA_BLOCK, &len); + if ((sadb == NULL) || (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE)) { + SDE_DEBUG("No/Invalid Speaker Allocation Data Block\n"); + return; + } + + memcpy(edid_ctrl->spkr_alloc_data_block, sadb + 1, len); + edid_ctrl->sadb_size = len; + + SDE_DEBUG("EDID: speaker alloc data SP byte = %08x %s%s%s%s%s%s%s\n", + sadb[1], + (sadb[1] & BIT(0)) ? "FL/FR," : "", + (sadb[1] & BIT(1)) ? "LFE," : "", + (sadb[1] & BIT(2)) ? "FC," : "", + (sadb[1] & BIT(3)) ? "RL/RR," : "", + (sadb[1] & BIT(4)) ? "RC," : "", + (sadb[1] & BIT(5)) ? "FLC/FRC," : "", + (sadb[1] & BIT(6)) ? "RLC/RRC," : ""); +} + +int sde_hdmi_edid_init(struct sde_hdmi *display) +{ + int rc = 0; + + if (!display) { + SDE_ERROR("[%s]Invalid params\n", display->name); + return -EINVAL; + } + + memset(&display->edid, 0, sizeof(display->edid)); + + return rc; +} + +int sde_hdmi_free_edid(struct sde_hdmi *display) +{ + struct hdmi_edid_ctrl *edid_ctrl = &display->edid; + + kfree(edid_ctrl->edid); + edid_ctrl->edid = NULL; + + return 0; +} + +int sde_hdmi_edid_deinit(struct sde_hdmi *display) +{ + return sde_hdmi_free_edid(display); +} + +void sde_hdmi_get_edid(struct drm_connector *connector, + struct sde_hdmi *display) +{ + u32 hdmi_ctrl; + struct hdmi_edid_ctrl *edid_ctrl = &display->edid; + struct hdmi *hdmi = display->ctrl.ctrl; + + /* Read EDID */ + hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL); + hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE); + edid_ctrl->edid = drm_get_edid(connector, hdmi->i2c); + hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl); + + if (edid_ctrl->edid) { + hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid_ctrl->edid); + + _sde_hdmi_extract_audio_data_blocks(edid_ctrl); + _sde_hdmi_extract_speaker_allocation_data(edid_ctrl); + } +}; diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c index 42bbbdcab2c9..dc7827872276 100644 --- a/drivers/gpu/drm/msm/sde/sde_formats.c +++ b/drivers/gpu/drm/msm/sde/sde_formats.c @@ -102,169 +102,169 @@ flg, fm, np) \ static const struct sde_format sde_format_map[] = { INTERLEAVED_RGB_FMT(ARGB8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, true, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ABGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XBGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBA8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, true, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRA8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, true, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRX8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, false, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XRGB8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, false, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBX8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, false, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGB888, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, + C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3, false, 3, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGR888, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3, + C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, false, 3, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGB565, 0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, + C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGR565, 0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3, + C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ARGB1555, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ABGR1555, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBA5551, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRA5551, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XRGB1555, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XBGR1555, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBX5551, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRX5551, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ARGB4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ABGR4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBA4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRA4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XRGB4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XBGR4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBX4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRX4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), @@ -366,13 +366,13 @@ static const struct sde_format sde_format_map[] = { PLANAR_YUV_FMT(YUV420, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C0_G_Y, C1_B_Cb, C2_R_Cr, + C2_R_Cr, C1_B_Cb, C0_G_Y, false, SDE_CHROMA_420, 1, SDE_FORMAT_FLAG_YUV, SDE_FETCH_LINEAR, 3), PLANAR_YUV_FMT(YVU420, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C0_G_Y, C2_R_Cr, C1_B_Cb, + C1_B_Cb, C2_R_Cr, C0_G_Y, false, SDE_CHROMA_420, 1, SDE_FORMAT_FLAG_YUV, SDE_FETCH_LINEAR, 3), }; @@ -384,19 +384,19 @@ static const struct sde_format sde_format_map[] = { * the data will be passed by user-space. */ static const struct sde_format sde_format_map_ubwc[] = { - INTERLEAVED_RGB_FMT(RGB565, + INTERLEAVED_RGB_FMT(BGR565, 0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, false, 2, 0, SDE_FETCH_UBWC, 2), - INTERLEAVED_RGB_FMT(RGBA8888, + INTERLEAVED_RGB_FMT(ABGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 4, 0, SDE_FETCH_UBWC, 2), - INTERLEAVED_RGB_FMT(RGBX8888, + INTERLEAVED_RGB_FMT(XBGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, false, 4, 0, @@ -513,14 +513,15 @@ static int _sde_format_get_plane_sizes_ubwc( ALIGN(DIV_ROUND_UP(height / 2, uv_tile_height), 16), 4096); - } else if (fmt->base.pixel_format == DRM_FORMAT_RGBA8888 || - fmt->base.pixel_format == DRM_FORMAT_RGBX8888 || - fmt->base.pixel_format == DRM_FORMAT_RGBA1010102 || - fmt->base.pixel_format == DRM_FORMAT_RGBX1010102 || - fmt->base.pixel_format == DRM_FORMAT_RGB565) { + } else if (fmt->base.pixel_format == DRM_FORMAT_ABGR8888 || + fmt->base.pixel_format == DRM_FORMAT_XBGR8888 || + fmt->base.pixel_format == DRM_FORMAT_BGRA1010102 || + fmt->base.pixel_format == DRM_FORMAT_BGRX1010102 || + fmt->base.pixel_format == DRM_FORMAT_BGR565) { + uint32_t stride_alignment, aligned_bitstream_width; - if (fmt->base.pixel_format == DRM_FORMAT_RGB565) + if (fmt->base.pixel_format == DRM_FORMAT_BGR565) stride_alignment = 128; else stride_alignment = 64; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c index bfcc6408a772..b7f4b826febe 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c @@ -36,7 +36,10 @@ nv04_fifo_dma_object_dtor(struct nvkm_fifo_chan *base, int cookie) { struct nv04_fifo_chan *chan = nv04_fifo_chan(base); struct nvkm_instmem *imem = chan->fifo->base.engine.subdev.device->imem; + + mutex_lock(&chan->fifo->base.engine.subdev.mutex); nvkm_ramht_remove(imem->ramht, cookie); + mutex_unlock(&chan->fifo->base.engine.subdev.mutex); } static int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c index 4bef72a9d106..3fda594700e0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c @@ -59,9 +59,11 @@ static void nv40_perfctr_next(struct nvkm_pm *pm, struct nvkm_perfdom *dom) { struct nvkm_device *device = pm->engine.subdev.device; - if (pm->sequence != pm->sequence) { + struct nv40_pm *nv40pm = container_of(pm, struct nv40_pm, base); + + if (nv40pm->sequence != pm->sequence) { nvkm_wr32(device, 0x400084, 0x00000020); - pm->sequence = pm->sequence; + nv40pm->sequence = pm->sequence; } } diff --git a/drivers/gpu/drm/qxl/qxl_draw.c b/drivers/gpu/drm/qxl/qxl_draw.c index 56e1d633875e..6e6c76080d6a 100644 --- a/drivers/gpu/drm/qxl/qxl_draw.c +++ b/drivers/gpu/drm/qxl/qxl_draw.c @@ -136,6 +136,8 @@ static int qxl_palette_create_1bit(struct qxl_bo *palette_bo, * correctly globaly, since that would require * tracking all of our palettes. */ ret = qxl_bo_kmap(palette_bo, (void **)&pal); + if (ret) + return ret; pal->num_ents = 2; pal->unique = unique++; if (visual == FB_VISUAL_TRUECOLOR || visual == FB_VISUAL_DIRECTCOLOR) { diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 79bab6fd76bb..6755d4768f59 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -275,6 +275,8 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) atombios_enable_crtc_memreq(crtc, ATOM_ENABLE); atombios_blank_crtc(crtc, ATOM_DISABLE); drm_vblank_post_modeset(dev, radeon_crtc->crtc_id); + /* Make sure vblank interrupt is still enabled if needed */ + radeon_irq_set(rdev); radeon_crtc_load_lut(crtc); break; case DRM_MODE_DPMS_STANDBY: diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 44ee72e04df9..b5760851195c 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -315,15 +315,27 @@ int radeon_dp_get_dp_link_config(struct drm_connector *connector, unsigned max_lane_num = drm_dp_max_lane_count(dpcd); unsigned lane_num, i, max_pix_clock; - for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) { - for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) { - max_pix_clock = (lane_num * link_rates[i] * 8) / bpp; + if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) == + ENCODER_OBJECT_ID_NUTMEG) { + for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) { + max_pix_clock = (lane_num * 270000 * 8) / bpp; if (max_pix_clock >= pix_clock) { *dp_lanes = lane_num; - *dp_rate = link_rates[i]; + *dp_rate = 270000; return 0; } } + } else { + for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) { + for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) { + max_pix_clock = (lane_num * link_rates[i] * 8) / bpp; + if (max_pix_clock >= pix_clock) { + *dp_lanes = lane_num; + *dp_rate = link_rates[i]; + return 0; + } + } + } } return -EINVAL; diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 158872eb78e4..a3a321208fd8 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -1396,9 +1396,7 @@ static void cayman_pcie_gart_fini(struct radeon_device *rdev) void cayman_cp_int_cntl_setup(struct radeon_device *rdev, int ring, u32 cp_int_cntl) { - u32 srbm_gfx_cntl = RREG32(SRBM_GFX_CNTL) & ~3; - - WREG32(SRBM_GFX_CNTL, srbm_gfx_cntl | (ring & 3)); + WREG32(SRBM_GFX_CNTL, RINGID(ring)); WREG32(CP_INT_CNTL, cp_int_cntl); } diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index fa2154493cf1..470af4aa4a6a 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -156,19 +156,20 @@ u32 r600_dpm_get_vblank_time(struct radeon_device *rdev) struct drm_device *dev = rdev->ddev; struct drm_crtc *crtc; struct radeon_crtc *radeon_crtc; - u32 line_time_us, vblank_lines; + u32 vblank_in_pixels; u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */ if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) { list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { radeon_crtc = to_radeon_crtc(crtc); if (crtc->enabled && radeon_crtc->enabled && radeon_crtc->hw_mode.clock) { - line_time_us = (radeon_crtc->hw_mode.crtc_htotal * 1000) / - radeon_crtc->hw_mode.clock; - vblank_lines = radeon_crtc->hw_mode.crtc_vblank_end - - radeon_crtc->hw_mode.crtc_vdisplay + - (radeon_crtc->v_border * 2); - vblank_time_us = vblank_lines * line_time_us; + vblank_in_pixels = + radeon_crtc->hw_mode.crtc_htotal * + (radeon_crtc->hw_mode.crtc_vblank_end - + radeon_crtc->hw_mode.crtc_vdisplay + + (radeon_crtc->v_border * 2)); + + vblank_time_us = vblank_in_pixels * 1000 / radeon_crtc->hw_mode.clock; break; } } diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index e2dd5d19c32c..4aa2cbe4c85f 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -660,8 +660,9 @@ bool radeon_card_posted(struct radeon_device *rdev) { uint32_t reg; - /* for pass through, always force asic_init */ - if (radeon_device_is_virtual()) + /* for pass through, always force asic_init for CI */ + if (rdev->family >= CHIP_BONAIRE && + radeon_device_is_virtual()) return false; /* required for EFI mode on macbook2,1 which uses an r5xx asic */ diff --git a/drivers/gpu/drm/radeon/radeon_dp_auxch.c b/drivers/gpu/drm/radeon/radeon_dp_auxch.c index db64e0062689..3b0c229d7dcd 100644 --- a/drivers/gpu/drm/radeon/radeon_dp_auxch.c +++ b/drivers/gpu/drm/radeon/radeon_dp_auxch.c @@ -105,7 +105,7 @@ radeon_dp_aux_transfer_native(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg tmp &= AUX_HPD_SEL(0x7); tmp |= AUX_HPD_SEL(chan->rec.hpd); - tmp |= AUX_EN | AUX_LS_READ_EN | AUX_HPD_DISCON(0x1); + tmp |= AUX_EN | AUX_LS_READ_EN; WREG32(AUX_CONTROL + aux_offset[instance], tmp); diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c index 678b4386540d..89f22bdde298 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c @@ -331,6 +331,8 @@ static void radeon_crtc_dpms(struct drm_crtc *crtc, int mode) WREG32_P(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl, ~(mask | crtc_ext_cntl)); } drm_vblank_post_modeset(dev, radeon_crtc->crtc_id); + /* Make sure vblank interrupt is still enabled if needed */ + radeon_irq_set(rdev); radeon_crtc_load_lut(crtc); break; case DRM_MODE_DPMS_STANDBY: diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index caa73de584a5..10191b935937 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -2999,6 +2999,49 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, int i; struct si_dpm_quirk *p = si_dpm_quirk_list; + /* limit all SI kickers */ + if (rdev->family == CHIP_PITCAIRN) { + if ((rdev->pdev->revision == 0x81) || + (rdev->pdev->device == 0x6810) || + (rdev->pdev->device == 0x6811) || + (rdev->pdev->device == 0x6816) || + (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->device == 0x6604) || + (rdev->pdev->device == 0x6605)) { + max_sclk = 75000; + max_mclk = 80000; + } + } else if (rdev->family == CHIP_HAINAN) { + if ((rdev->pdev->revision == 0x81) || + (rdev->pdev->revision == 0x83) || + (rdev->pdev->revision == 0xC3) || + (rdev->pdev->device == 0x6664) || + (rdev->pdev->device == 0x6665) || + (rdev->pdev->device == 0x6667)) { + max_sclk = 75000; + max_mclk = 80000; + } + } /* Apply dpm quirks */ while (p && p->chip_device != 0) { if (rdev->pdev->vendor == p->chip_vendor && @@ -3011,10 +3054,6 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, } ++p; } - /* limit mclk on all R7 370 parts for stability */ - if (rdev->pdev->device == 0x6811 && - rdev->pdev->revision == 0x81) - max_mclk = 120000; if (rps->vce_active) { rps->evclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].evclk; @@ -4106,7 +4145,7 @@ static int si_populate_smc_voltage_tables(struct radeon_device *rdev, &rdev->pm.dpm.dyn_state.phase_shedding_limits_table)) { si_populate_smc_voltage_table(rdev, &si_pi->vddc_phase_shed_table, table); - table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] = + table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC_PHASE_SHEDDING] = cpu_to_be32(si_pi->vddc_phase_shed_table.mask_low); si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_phase_shedding_delay, diff --git a/drivers/gpu/drm/radeon/sislands_smc.h b/drivers/gpu/drm/radeon/sislands_smc.h index 3c779838d9ab..966e3a556011 100644 --- a/drivers/gpu/drm/radeon/sislands_smc.h +++ b/drivers/gpu/drm/radeon/sislands_smc.h @@ -194,6 +194,7 @@ typedef struct SISLANDS_SMC_SWSTATE SISLANDS_SMC_SWSTATE; #define SISLANDS_SMC_VOLTAGEMASK_VDDC 0 #define SISLANDS_SMC_VOLTAGEMASK_MVDD 1 #define SISLANDS_SMC_VOLTAGEMASK_VDDCI 2 +#define SISLANDS_SMC_VOLTAGEMASK_VDDC_PHASE_SHEDDING 3 #define SISLANDS_SMC_VOLTAGEMASK_MAX 4 struct SISLANDS_SMC_VOLTAGEMASKTABLE diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 4948c1529836..ecf15cf0c3fd 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -3830,14 +3830,14 @@ static void *vmw_execbuf_cmdbuf(struct vmw_private *dev_priv, int ret; *header = NULL; - if (!dev_priv->cman || kernel_commands) - return kernel_commands; - if (command_size > SVGA_CB_MAX_SIZE) { DRM_ERROR("Command buffer is too large.\n"); return ERR_PTR(-EINVAL); } + if (!dev_priv->cman || kernel_commands) + return kernel_commands; + /* If possible, add a little space for fencing. */ cmdbuf_size = command_size + 512; cmdbuf_size = min_t(size_t, cmdbuf_size, SVGA_CB_MAX_SIZE); diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 973884c2c5e7..87300096fbf1 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -161,6 +161,7 @@ static const struct { { adreno_is_a530, a530_efuse_speed_bin }, { adreno_is_a505, a530_efuse_speed_bin }, { adreno_is_a512, a530_efuse_speed_bin }, + { adreno_is_a508, a530_efuse_speed_bin }, }; static void a5xx_check_features(struct adreno_device *adreno_dev) @@ -1166,7 +1167,7 @@ static const struct kgsl_hwcg_reg a512_hwcg_regs[] = { {A5XX_RBBM_CLOCK_CNTL_CCU0, 0x00022220}, {A5XX_RBBM_CLOCK_CNTL_CCU1, 0x00022220}, {A5XX_RBBM_CLOCK_CNTL_RAC, 0x05522222}, - {A5XX_RBBM_CLOCK_CNTL2_RAC, 0x00555555}, + {A5XX_RBBM_CLOCK_CNTL2_RAC, 0x00505555}, {A5XX_RBBM_CLOCK_HYST_RB_CCU0, 0x04040404}, {A5XX_RBBM_CLOCK_HYST_RB_CCU1, 0x04040404}, {A5XX_RBBM_CLOCK_HYST_RAC, 0x07444044}, diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 601e7a23101b..1de8e212a703 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -4753,6 +4753,7 @@ error_close_mmu: error_pwrctrl_close: kgsl_pwrctrl_close(device); error: + kgsl_device_debugfs_close(device); _unregister_device(device); return status; } @@ -4782,6 +4783,7 @@ void kgsl_device_platform_remove(struct kgsl_device *device) kgsl_pwrctrl_close(device); + kgsl_device_debugfs_close(device); _unregister_device(device); } EXPORT_SYMBOL(kgsl_device_platform_remove); diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c index 7758fc956055..37d92428f02c 100644 --- a/drivers/gpu/msm/kgsl_debugfs.c +++ b/drivers/gpu/msm/kgsl_debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2008-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 @@ -87,6 +87,11 @@ void kgsl_device_debugfs_init(struct kgsl_device *device) &pwr_log_fops); } +void kgsl_device_debugfs_close(struct kgsl_device *device) +{ + debugfs_remove_recursive(device->d_debugfs); +} + struct type_entry { int type; const char *str; diff --git a/drivers/gpu/msm/kgsl_debugfs.h b/drivers/gpu/msm/kgsl_debugfs.h index 34875954bb8b..949aed81581c 100644 --- a/drivers/gpu/msm/kgsl_debugfs.h +++ b/drivers/gpu/msm/kgsl_debugfs.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2011,2013,2015 The Linux Foundation. +/* Copyright (c) 2002,2008-2011,2013,2015,2017 The Linux Foundation. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -23,6 +23,7 @@ void kgsl_core_debugfs_init(void); void kgsl_core_debugfs_close(void); void kgsl_device_debugfs_init(struct kgsl_device *device); +void kgsl_device_debugfs_close(struct kgsl_device *device); extern struct dentry *kgsl_debugfs_dir; static inline struct dentry *kgsl_get_debugfs_dir(void) @@ -34,6 +35,7 @@ void kgsl_process_init_debugfs(struct kgsl_process_private *); #else static inline void kgsl_core_debugfs_init(void) { } static inline void kgsl_device_debugfs_init(struct kgsl_device *device) { } +static inline void kgsl_device_debugfs_close(struct kgsl_device *device) { } static inline void kgsl_core_debugfs_close(void) { } static inline struct dentry *kgsl_get_debugfs_dir(void) { return NULL; } static inline void kgsl_process_init_debugfs(struct kgsl_process_private *priv) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index fe6aa45901d0..e639e197de93 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -361,6 +361,26 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, if (new_level == old_level) return; + if (pwr->gpu_cx_ipeak) { + unsigned int old_freq = pwr->pwrlevels[old_level].gpu_freq; + unsigned int new_freq = pwr->pwrlevels[new_level].gpu_freq; + + /* + * Set Cx ipeak vote for GPU if it tries to cross + * threshold frequency. + */ + if (old_freq < pwr->gpu_cx_ipeak_clk && + new_freq >= pwr->gpu_cx_ipeak_clk) { + int ret = cx_ipeak_update(pwr->gpu_cx_ipeak, true); + + if (ret) { + KGSL_PWR_ERR(device, + "cx_ipeak_update failed %d\n", ret); + return; + } + } + } + kgsl_pwrscale_update_stats(device); /* @@ -422,6 +442,24 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, /* Timestamp the frequency change */ device->pwrscale.freq_change_time = ktime_to_ms(ktime_get()); + + if (pwr->gpu_cx_ipeak) { + unsigned int old_freq = pwr->pwrlevels[old_level].gpu_freq; + unsigned int new_freq = pwr->pwrlevels[new_level].gpu_freq; + + /* + * Reset Cx ipeak vote for GPU if it goes below + * threshold frequency. + */ + if (old_freq >= pwr->gpu_cx_ipeak_clk && + new_freq < pwr->gpu_cx_ipeak_clk) { + int ret = cx_ipeak_update(pwr->gpu_cx_ipeak, false); + + if (ret) + KGSL_PWR_ERR(device, + "cx_ipeak_update failed %d\n", ret); + } + } } EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change); @@ -2217,8 +2255,37 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) of_property_read_string(pdev->dev.of_node, "qcom,tsens-name", &pwr->tsens_name); + /* Cx ipeak client support */ + if (of_find_property(pdev->dev.of_node, "qcom,gpu-cx-ipeak", NULL)) { + if (!of_property_read_u32(pdev->dev.of_node, + "qcom,gpu-cx-ipeak-clk", &pwr->gpu_cx_ipeak_clk)) { + pwr->gpu_cx_ipeak = cx_ipeak_register(pdev->dev.of_node, + "qcom,gpu-cx-ipeak"); + } else { + KGSL_PWR_ERR(device, "failed to get gpu cxip clk\n"); + result = -EINVAL; + goto error_cleanup_pwr_limit; + } + + if (IS_ERR(pwr->gpu_cx_ipeak)) { + result = PTR_ERR(pwr->gpu_cx_ipeak); + KGSL_PWR_ERR(device, + "Failed to register Cx ipeak client %d\n", + result); + goto error_cleanup_pwr_limit; + } + } return result; +error_cleanup_pwr_limit: + pwr->power_flags = 0; + + if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) { + list_del(&pwr->sysfs_pwr_limit->node); + kfree(pwr->sysfs_pwr_limit); + pwr->sysfs_pwr_limit = NULL; + } + kfree(pwr->bus_ib); error_cleanup_pcl: _close_pcl(pwr); error_cleanup_ocmem_pcl: @@ -2238,6 +2305,8 @@ void kgsl_pwrctrl_close(struct kgsl_device *device) KGSL_PWR_INFO(device, "close device %d\n", device->id); + cx_ipeak_unregister(pwr->gpu_cx_ipeak); + pwr->power_flags = 0; if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) { diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 2de42d87bcbe..42f918b80fcd 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.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 @@ -14,6 +14,7 @@ #define __KGSL_PWRCTRL_H #include <linux/pm_qos.h> +#include <soc/qcom/cx_ipeak.h> /***************************************************************************** ** power flags @@ -153,6 +154,8 @@ struct kgsl_regulator { * isense_clk_indx - index of isense clock, 0 if no isense * isense_clk_on_level - isense clock rate is XO rate below this level. * tsens_name - pointer to temperature sensor name of GPU temperature sensor + * gpu_cx_ipeak - pointer to cx ipeak client used by GPU + * gpu_cx_ipeak_clk - GPU threshold frequency to call cx ipeak driver API */ struct kgsl_pwrctrl { @@ -206,6 +209,8 @@ struct kgsl_pwrctrl { unsigned int gpu_bimc_int_clk_freq; bool gpu_bimc_interface_enabled; const char *tsens_name; + struct cx_ipeak_client *gpu_cx_ipeak; + unsigned int gpu_cx_ipeak_clk; }; int kgsl_pwrctrl_init(struct kgsl_device *device); diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c index 1caa673db6ff..7de43dd27ffe 100644 --- a/drivers/gpu/msm/kgsl_snapshot.c +++ b/drivers/gpu/msm/kgsl_snapshot.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 @@ -156,8 +156,10 @@ static size_t snapshot_os(struct kgsl_device *device, header->osid = KGSL_SNAPSHOT_OS_LINUX_V3; /* Get the kernel build information */ - strlcpy(header->release, utsname()->release, sizeof(header->release)); - strlcpy(header->version, utsname()->version, sizeof(header->version)); + strlcpy(header->release, init_utsname()->release, + sizeof(header->release)); + strlcpy(header->version, init_utsname()->version, + sizeof(header->version)); /* Get the Unix time for the timestamp */ header->seconds = get_seconds(); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 909ab0176ef2..e37030624165 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -168,6 +168,7 @@ #define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 #define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 #define USB_DEVICE_ID_ATEN_CS682 0x2213 +#define USB_DEVICE_ID_ATEN_CS692 0x8021 #define USB_VENDOR_ID_ATMEL 0x03eb #define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index dc8e6adf95a4..6ca6ab00fa93 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -61,6 +61,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS682, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS692, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FIGHTERSTICK, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE, HID_QUIRK_NOGET }, diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 7994ec2e4151..41f5896224bd 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -283,10 +283,14 @@ static void heartbeat_onchannelcallback(void *context) u8 *hbeat_txf_buf = util_heartbeat.recv_buffer; struct icmsg_negotiate *negop = NULL; - vmbus_recvpacket(channel, hbeat_txf_buf, - PAGE_SIZE, &recvlen, &requestid); + while (1) { + + vmbus_recvpacket(channel, hbeat_txf_buf, + PAGE_SIZE, &recvlen, &requestid); + + if (!recvlen) + break; - if (recvlen > 0) { icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ sizeof(struct vmbuspipe_hdr)]; diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c index 827c03703128..a7f886961830 100644 --- a/drivers/hwmon/adt7411.c +++ b/drivers/hwmon/adt7411.c @@ -30,6 +30,7 @@ #define ADT7411_REG_CFG1 0x18 #define ADT7411_CFG1_START_MONITOR (1 << 0) +#define ADT7411_CFG1_RESERVED_BIT3 (1 << 3) #define ADT7411_REG_CFG2 0x19 #define ADT7411_CFG2_DISABLE_AVG (1 << 5) @@ -296,8 +297,10 @@ static int adt7411_probe(struct i2c_client *client, mutex_init(&data->device_lock); mutex_init(&data->update_lock); + /* According to the datasheet, we must only write 1 to bit 3 */ ret = adt7411_modify_bit(client, ADT7411_REG_CFG1, - ADT7411_CFG1_START_MONITOR, 1); + ADT7411_CFG1_RESERVED_BIT3 + | ADT7411_CFG1_START_MONITOR, 1); if (ret < 0) return ret; diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index 3a11b061e5b0..596a36ed7dba 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.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 @@ -3725,12 +3725,6 @@ static int tpdm_probe(struct platform_device *pdev) clk_disable_unprepare(drvdata->clk); - ret = tpdm_datasets_alloc(drvdata); - if (ret) - return ret; - - tpdm_init_default_data(drvdata); - drvdata->traceid = traceid++; desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 78fbee463628..65dbde778181 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -59,7 +59,6 @@ config I2C_CHARDEV config I2C_MUX tristate "I2C bus multiplexing support" - depends on HAS_IOMEM help Say Y here if you want the I2C core to support the ability to handle multiplexed I2C bus topologies, by presenting each diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 76e699f9ed97..eef3aa6007f1 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -773,13 +773,6 @@ static int pch_i2c_probe(struct pci_dev *pdev, /* Set the number of I2C channel instance */ adap_info->ch_num = id->driver_data; - ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED, - KBUILD_MODNAME, adap_info); - if (ret) { - pch_pci_err(pdev, "request_irq FAILED\n"); - goto err_request_irq; - } - for (i = 0; i < adap_info->ch_num; i++) { pch_adap = &adap_info->pch_data[i].pch_adapter; adap_info->pch_i2c_suspended = false; @@ -796,6 +789,17 @@ static int pch_i2c_probe(struct pci_dev *pdev, adap_info->pch_data[i].pch_base_address = base_addr + 0x100 * i; pch_adap->dev.parent = &pdev->dev; + } + + ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED, + KBUILD_MODNAME, adap_info); + if (ret) { + pch_pci_err(pdev, "request_irq FAILED\n"); + goto err_request_irq; + } + + for (i = 0; i < adap_info->ch_num; i++) { + pch_adap = &adap_info->pch_data[i].pch_adapter; pch_i2c_init(&adap_info->pch_data[i]); diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c index 04b1b62f85c3..bf2a1dd7cf15 100644 --- a/drivers/i2c/busses/i2c-msm-v2.c +++ b/drivers/i2c/busses/i2c-msm-v2.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 @@ -50,6 +50,8 @@ static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl, static int i2c_msm_pm_resume(struct device *dev); static void i2c_msm_pm_suspend(struct device *dev); static void i2c_msm_clk_path_init(struct i2c_msm_ctrl *ctrl); +static void i2c_msm_pm_pinctrl_state(struct i2c_msm_ctrl *ctrl, + bool runtime_active); /* string table for enum i2c_msm_xfer_mode_id */ const char * const i2c_msm_mode_str_tbl[] = { @@ -2157,27 +2159,54 @@ static bool i2c_msm_xfer_next_buf(struct i2c_msm_ctrl *ctrl) return true; } -static void i2c_msm_pm_clk_disable_unprepare(struct i2c_msm_ctrl *ctrl) +static void i2c_msm_pm_clk_unprepare(struct i2c_msm_ctrl *ctrl) { - clk_disable_unprepare(ctrl->rsrcs.core_clk); - clk_disable_unprepare(ctrl->rsrcs.iface_clk); + clk_unprepare(ctrl->rsrcs.core_clk); + clk_unprepare(ctrl->rsrcs.iface_clk); } -static int i2c_msm_pm_clk_prepare_enable(struct i2c_msm_ctrl *ctrl) +static int i2c_msm_pm_clk_prepare(struct i2c_msm_ctrl *ctrl) { int ret; - ret = clk_prepare_enable(ctrl->rsrcs.iface_clk); + ret = clk_prepare(ctrl->rsrcs.iface_clk); if (ret) { dev_err(ctrl->dev, - "error on clk_prepare_enable(iface_clk):%d\n", ret); + "error on clk_prepare(iface_clk):%d\n", ret); return ret; } - ret = clk_prepare_enable(ctrl->rsrcs.core_clk); + ret = clk_prepare(ctrl->rsrcs.core_clk); + if (ret) { + clk_unprepare(ctrl->rsrcs.iface_clk); + dev_err(ctrl->dev, + "error clk_prepare(core_clk):%d\n", ret); + } + return ret; +} + +static void i2c_msm_pm_clk_disable(struct i2c_msm_ctrl *ctrl) +{ + clk_disable(ctrl->rsrcs.core_clk); + clk_disable(ctrl->rsrcs.iface_clk); +} + +static int i2c_msm_pm_clk_enable(struct i2c_msm_ctrl *ctrl) +{ + int ret; + + ret = clk_enable(ctrl->rsrcs.iface_clk); + if (ret) { + dev_err(ctrl->dev, + "error on clk_enable(iface_clk):%d\n", ret); + i2c_msm_pm_clk_unprepare(ctrl); + return ret; + } + ret = clk_enable(ctrl->rsrcs.core_clk); if (ret) { - clk_disable_unprepare(ctrl->rsrcs.iface_clk); + clk_disable(ctrl->rsrcs.iface_clk); + i2c_msm_pm_clk_unprepare(ctrl); dev_err(ctrl->dev, - "error clk_prepare_enable(core_clk):%d\n", ret); + "error clk_enable(core_clk):%d\n", ret); } return ret; } @@ -2198,6 +2227,7 @@ static int i2c_msm_pm_xfer_start(struct i2c_msm_ctrl *ctrl) return -EIO; } + i2c_msm_pm_pinctrl_state(ctrl, true); pm_runtime_get_sync(ctrl->dev); /* * if runtime PM callback was not invoked (when both runtime-pm @@ -2208,7 +2238,7 @@ static int i2c_msm_pm_xfer_start(struct i2c_msm_ctrl *ctrl) i2c_msm_pm_resume(ctrl->dev); } - ret = i2c_msm_pm_clk_prepare_enable(ctrl); + ret = i2c_msm_pm_clk_enable(ctrl); if (ret) { mutex_unlock(&ctrl->xfer.mtx); return ret; @@ -2235,13 +2265,14 @@ static void i2c_msm_pm_xfer_end(struct i2c_msm_ctrl *ctrl) if (ctrl->xfer.mode_id == I2C_MSM_XFER_MODE_DMA) i2c_msm_dma_free_channels(ctrl); - i2c_msm_pm_clk_disable_unprepare(ctrl); + i2c_msm_pm_clk_disable(ctrl); if (!pm_runtime_enabled(ctrl->dev)) i2c_msm_pm_suspend(ctrl->dev); pm_runtime_mark_last_busy(ctrl->dev); pm_runtime_put_autosuspend(ctrl->dev); + i2c_msm_pm_pinctrl_state(ctrl, false); mutex_unlock(&ctrl->xfer.mtx); } @@ -2663,7 +2694,7 @@ static void i2c_msm_pm_suspend(struct device *dev) return; } i2c_msm_dbg(ctrl, MSM_DBG, "suspending..."); - i2c_msm_pm_pinctrl_state(ctrl, false); + i2c_msm_pm_clk_unprepare(ctrl); i2c_msm_clk_path_unvote(ctrl); /* @@ -2690,7 +2721,7 @@ static int i2c_msm_pm_resume(struct device *dev) i2c_msm_dbg(ctrl, MSM_DBG, "resuming..."); i2c_msm_clk_path_vote(ctrl); - i2c_msm_pm_pinctrl_state(ctrl, true); + i2c_msm_pm_clk_prepare(ctrl); ctrl->pwr_state = I2C_MSM_PM_RT_ACTIVE; return 0; } @@ -2870,9 +2901,13 @@ static int i2c_msm_probe(struct platform_device *pdev) /* vote for clock to enable reading the version number off the HW */ i2c_msm_clk_path_vote(ctrl); - ret = i2c_msm_pm_clk_prepare_enable(ctrl); + ret = i2c_msm_pm_clk_prepare(ctrl); + if (ret) + goto clk_err; + + ret = i2c_msm_pm_clk_enable(ctrl); if (ret) { - dev_err(ctrl->dev, "error in enabling clocks:%d\n", ret); + i2c_msm_pm_clk_unprepare(ctrl); goto clk_err; } @@ -2884,7 +2919,8 @@ static int i2c_msm_probe(struct platform_device *pdev) if (ret) dev_err(ctrl->dev, "error error on qup software reset\n"); - i2c_msm_pm_clk_disable_unprepare(ctrl); + i2c_msm_pm_clk_disable(ctrl); + i2c_msm_pm_clk_unprepare(ctrl); i2c_msm_clk_path_unvote(ctrl); ret = i2c_msm_rsrcs_gpio_pinctrl_init(ctrl); diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index fdcbdab808e9..33b11563cde7 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -727,7 +727,8 @@ static int qup_i2c_pm_resume_runtime(struct device *device) #ifdef CONFIG_PM_SLEEP static int qup_i2c_suspend(struct device *device) { - qup_i2c_pm_suspend_runtime(device); + if (!pm_runtime_suspended(device)) + return qup_i2c_pm_suspend_runtime(device); return 0; } diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c index 4233f5695352..3c38029e3fe9 100644 --- a/drivers/i2c/busses/i2c-xgene-slimpro.c +++ b/drivers/i2c/busses/i2c-xgene-slimpro.c @@ -105,7 +105,7 @@ struct slimpro_i2c_dev { struct mbox_chan *mbox_chan; struct mbox_client mbox_client; struct completion rd_complete; - u8 dma_buffer[I2C_SMBUS_BLOCK_MAX]; + u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* dma_buffer[0] is used for length */ u32 *resp_msg; }; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index ba8eb087f224..d625167357cc 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1876,6 +1876,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) /* add the driver to the list of i2c drivers in the driver core */ driver->driver.owner = owner; driver->driver.bus = &i2c_bus_type; + INIT_LIST_HEAD(&driver->clients); /* When registration returns, the driver core * will have called probe() for all matching-but-unbound devices. @@ -1886,7 +1887,6 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name); - INIT_LIST_HEAD(&driver->clients); /* Walk the adapters that are already present */ i2c_for_each_dev(driver, __process_new_driver); diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index f06b0e24673b..af2a63cb4056 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -63,6 +63,7 @@ config I2C_MUX_PINCTRL config I2C_MUX_REG tristate "Register-based I2C multiplexer" + depends on HAS_IOMEM help If you say yes to this option, support will be included for a register based I2C multiplexer. This driver provides access to diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 291c61a41c9a..fa24d5196615 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -68,6 +68,9 @@ #define BMC150_ACCEL_REG_PMU_BW 0x10 #define BMC150_ACCEL_DEF_BW 125 +#define BMC150_ACCEL_REG_RESET 0x14 +#define BMC150_ACCEL_RESET_VAL 0xB6 + #define BMC150_ACCEL_REG_INT_MAP_0 0x19 #define BMC150_ACCEL_INT_MAP_0_BIT_SLOPE BIT(2) @@ -1487,6 +1490,14 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data) int ret, i; unsigned int val; + /* + * Reset chip to get it in a known good state. A delay of 1.8ms after + * reset is required according to the data sheets of supported chips. + */ + regmap_write(data->regmap, BMC150_ACCEL_REG_RESET, + BMC150_ACCEL_RESET_VAL); + usleep_range(1800, 2500); + ret = regmap_read(data->regmap, BMC150_ACCEL_REG_CHIP_ID, &val); if (ret < 0) { dev_err(data->dev, diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 3a9f106787d2..9d72d4bcf5e9 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -160,11 +160,13 @@ static int kxsd9_read_raw(struct iio_dev *indio_dev, if (ret < 0) goto error_ret; *val = ret; + ret = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C)); if (ret < 0) goto error_ret; + *val = 0; *val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK]; ret = IIO_VAL_INT_PLUS_MICRO; break; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 90135f496aaf..51cb6d8cffda 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -333,6 +333,7 @@ config QCOM_TADC config ROCKCHIP_SARADC tristate "Rockchip SARADC driver" depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST) + depends on RESET_CONTROLLER help Say yes here to build support for the SARADC found in SoCs from Rockchip. diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index 01d71588d752..ba82de25a797 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -533,6 +533,7 @@ static struct attribute_group ad799x_event_attrs_group = { static const struct iio_info ad7991_info = { .read_raw = &ad799x_read_raw, .driver_module = THIS_MODULE, + .update_scan_mode = ad799x_update_scan_mode, }; static const struct iio_info ad7993_4_7_8_noirq_info = { diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 7b40925dd4ff..93986f0590ef 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -381,8 +381,8 @@ static irqreturn_t at91_adc_rl_interrupt(int irq, void *private) st->ts_bufferedmeasure = false; input_report_key(st->ts_input, BTN_TOUCH, 0); input_sync(st->ts_input); - } else if (status & AT91_ADC_EOC(3)) { - /* Conversion finished */ + } else if (status & AT91_ADC_EOC(3) && st->ts_input) { + /* Conversion finished and we've a touchscreen */ if (st->ts_bufferedmeasure) { /* * Last measurement is always discarded, since it can diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index 9c311c1e1ac7..dffff64b5989 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -21,6 +21,8 @@ #include <linux/of_device.h> #include <linux/clk.h> #include <linux/completion.h> +#include <linux/delay.h> +#include <linux/reset.h> #include <linux/regulator/consumer.h> #include <linux/iio/iio.h> @@ -53,6 +55,7 @@ struct rockchip_saradc { struct clk *clk; struct completion completion; struct regulator *vref; + struct reset_control *reset; const struct rockchip_saradc_data *data; u16 last_val; }; @@ -171,6 +174,16 @@ static const struct of_device_id rockchip_saradc_match[] = { }; MODULE_DEVICE_TABLE(of, rockchip_saradc_match); +/** + * Reset SARADC Controller. + */ +static void rockchip_saradc_reset_controller(struct reset_control *reset) +{ + reset_control_assert(reset); + usleep_range(10, 20); + reset_control_deassert(reset); +} + static int rockchip_saradc_probe(struct platform_device *pdev) { struct rockchip_saradc *info = NULL; @@ -199,6 +212,20 @@ static int rockchip_saradc_probe(struct platform_device *pdev) if (IS_ERR(info->regs)) return PTR_ERR(info->regs); + /* + * The reset should be an optional property, as it should work + * with old devicetrees as well + */ + info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb"); + if (IS_ERR(info->reset)) { + ret = PTR_ERR(info->reset); + if (ret != -ENOENT) + return ret; + + dev_dbg(&pdev->dev, "no reset control found\n"); + info->reset = NULL; + } + init_completion(&info->completion); irq = platform_get_irq(pdev, 0); @@ -233,6 +260,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev) return PTR_ERR(info->vref); } + if (info->reset) + rockchip_saradc_reset_controller(info->reset); + /* * Use a default value for the converter clock. * This may become user-configurable in the future. diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index c1e05532d437..0470fc843d4e 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -32,6 +32,7 @@ struct tiadc_device { struct ti_tscadc_dev *mfd_tscadc; + struct mutex fifo1_lock; /* to protect fifo access */ int channels; u8 channel_line[8]; u8 channel_step[8]; @@ -360,6 +361,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct tiadc_device *adc_dev = iio_priv(indio_dev); + int ret = IIO_VAL_INT; int i, map_val; unsigned int fifo1count, read, stepid; bool found = false; @@ -373,13 +375,14 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, if (!step_en) return -EINVAL; + mutex_lock(&adc_dev->fifo1_lock); fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); while (fifo1count--) tiadc_readl(adc_dev, REG_FIFO1); am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en); - timeout = jiffies + usecs_to_jiffies + timeout = jiffies + msecs_to_jiffies (IDLE_TIMEOUT * adc_dev->channels); /* Wait for Fifo threshold interrupt */ while (1) { @@ -389,7 +392,8 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, if (time_after(jiffies, timeout)) { am335x_tsc_se_adc_done(adc_dev->mfd_tscadc); - return -EAGAIN; + ret = -EAGAIN; + goto err_unlock; } } map_val = adc_dev->channel_step[chan->scan_index]; @@ -415,8 +419,11 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, am335x_tsc_se_adc_done(adc_dev->mfd_tscadc); if (found == false) - return -EBUSY; - return IIO_VAL_INT; + ret = -EBUSY; + +err_unlock: + mutex_unlock(&adc_dev->fifo1_lock); + return ret; } static const struct iio_info tiadc_info = { @@ -485,6 +492,7 @@ static int tiadc_probe(struct platform_device *pdev) tiadc_step_config(indio_dev); tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD); + mutex_init(&adc_dev->fifo1_lock); err = tiadc_channel_init(indio_dev, adc_dev->channels); if (err < 0) diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index e81f434760f4..b5beea53d6f6 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -30,34 +30,34 @@ static struct { u32 usage_id; int unit; /* 0 for default others from HID sensor spec */ int scale_val0; /* scale, whole number */ - int scale_val1; /* scale, fraction in micros */ + int scale_val1; /* scale, fraction in nanos */ } unit_conversion[] = { - {HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650}, + {HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650000}, {HID_USAGE_SENSOR_ACCEL_3D, HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0}, {HID_USAGE_SENSOR_ACCEL_3D, - HID_USAGE_SENSOR_UNITS_G, 9, 806650}, + HID_USAGE_SENSOR_UNITS_G, 9, 806650000}, - {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453}, + {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453293}, {HID_USAGE_SENSOR_GYRO_3D, HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0}, {HID_USAGE_SENSOR_GYRO_3D, - HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453}, + HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453293}, - {HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000}, + {HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000000}, {HID_USAGE_SENSOR_COMPASS_3D, HID_USAGE_SENSOR_UNITS_GAUSS, 1, 0}, - {HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453}, + {HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453293}, {HID_USAGE_SENSOR_INCLINOMETER_3D, - HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453}, + HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293}, {HID_USAGE_SENSOR_INCLINOMETER_3D, HID_USAGE_SENSOR_UNITS_RADIANS, 1, 0}, {HID_USAGE_SENSOR_ALS, 0, 1, 0}, {HID_USAGE_SENSOR_ALS, HID_USAGE_SENSOR_UNITS_LUX, 1, 0}, - {HID_USAGE_SENSOR_PRESSURE, 0, 100000, 0}, - {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 1, 0}, + {HID_USAGE_SENSOR_PRESSURE, 0, 100, 0}, + {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 0, 1000000}, }; static int pow_10(unsigned power) @@ -266,15 +266,15 @@ EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value); /* * This fuction applies the unit exponent to the scale. * For example: - * 9.806650 ->exp:2-> val0[980]val1[665000] - * 9.000806 ->exp:2-> val0[900]val1[80600] - * 0.174535 ->exp:2-> val0[17]val1[453500] - * 1.001745 ->exp:0-> val0[1]val1[1745] - * 1.001745 ->exp:2-> val0[100]val1[174500] - * 1.001745 ->exp:4-> val0[10017]val1[450000] - * 9.806650 ->exp:-2-> val0[0]val1[98066] + * 9.806650000 ->exp:2-> val0[980]val1[665000000] + * 9.000806000 ->exp:2-> val0[900]val1[80600000] + * 0.174535293 ->exp:2-> val0[17]val1[453529300] + * 1.001745329 ->exp:0-> val0[1]val1[1745329] + * 1.001745329 ->exp:2-> val0[100]val1[174532900] + * 1.001745329 ->exp:4-> val0[10017]val1[453290000] + * 9.806650000 ->exp:-2-> val0[0]val1[98066500] */ -static void adjust_exponent_micro(int *val0, int *val1, int scale0, +static void adjust_exponent_nano(int *val0, int *val1, int scale0, int scale1, int exp) { int i; @@ -285,32 +285,32 @@ static void adjust_exponent_micro(int *val0, int *val1, int scale0, if (exp > 0) { *val0 = scale0 * pow_10(exp); res = 0; - if (exp > 6) { + if (exp > 9) { *val1 = 0; return; } for (i = 0; i < exp; ++i) { - x = scale1 / pow_10(5 - i); + x = scale1 / pow_10(8 - i); res += (pow_10(exp - 1 - i) * x); - scale1 = scale1 % pow_10(5 - i); + scale1 = scale1 % pow_10(8 - i); } *val0 += res; *val1 = scale1 * pow_10(exp); } else if (exp < 0) { exp = abs(exp); - if (exp > 6) { + if (exp > 9) { *val0 = *val1 = 0; return; } *val0 = scale0 / pow_10(exp); rem = scale0 % pow_10(exp); res = 0; - for (i = 0; i < (6 - exp); ++i) { - x = scale1 / pow_10(5 - i); - res += (pow_10(5 - exp - i) * x); - scale1 = scale1 % pow_10(5 - i); + for (i = 0; i < (9 - exp); ++i) { + x = scale1 / pow_10(8 - i); + res += (pow_10(8 - exp - i) * x); + scale1 = scale1 % pow_10(8 - i); } - *val1 = rem * pow_10(6 - exp) + res; + *val1 = rem * pow_10(9 - exp) + res; } else { *val0 = scale0; *val1 = scale1; @@ -332,14 +332,14 @@ int hid_sensor_format_scale(u32 usage_id, unit_conversion[i].unit == attr_info->units) { exp = hid_sensor_convert_exponent( attr_info->unit_expo); - adjust_exponent_micro(val0, val1, + adjust_exponent_nano(val0, val1, unit_conversion[i].scale_val0, unit_conversion[i].scale_val1, exp); break; } } - return IIO_VAL_INT_PLUS_MICRO; + return IIO_VAL_INT_PLUS_NANO; } EXPORT_SYMBOL(hid_sensor_format_scale); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 7afd226a3321..32bb036069eb 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -110,7 +110,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, DEFINE_WAIT_FUNC(wait, woken_wake_function); size_t datum_size; size_t to_wait; - int ret; + int ret = 0; if (!indio_dev->info) return -ENODEV; @@ -153,7 +153,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, ret = rb->access->read_first_n(rb, n, buf); if (ret == 0 && (filp->f_flags & O_NONBLOCK)) ret = -EAGAIN; - } while (ret == 0); + } while (ret == 0); remove_wait_queue(&rb->pollq, &wait); return ret; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 159ede61f793..131b434af994 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -433,23 +433,21 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals) scale_db = true; case IIO_VAL_INT_PLUS_MICRO: if (vals[1] < 0) - return sprintf(buf, "-%ld.%06u%s\n", abs(vals[0]), - -vals[1], - scale_db ? " dB" : ""); + return sprintf(buf, "-%d.%06u%s\n", abs(vals[0]), + -vals[1], scale_db ? " dB" : ""); else return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1], scale_db ? " dB" : ""); case IIO_VAL_INT_PLUS_NANO: if (vals[1] < 0) - return sprintf(buf, "-%ld.%09u\n", abs(vals[0]), - -vals[1]); + return sprintf(buf, "-%d.%09u\n", abs(vals[0]), + -vals[1]); else return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); case IIO_VAL_FRACTIONAL: tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]); - vals[1] = do_div(tmp, 1000000000LL); - vals[0] = tmp; - return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); + vals[0] = (int)div_s64_rem(tmp, 1000000000, &vals[1]); + return sprintf(buf, "%d.%09u\n", vals[0], abs(vals[1])); case IIO_VAL_FRACTIONAL_LOG2: tmp = (s64)vals[0] * 1000000000LL >> vals[1]; vals[1] = do_div(tmp, 1000000000LL); diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index b98b9d94d184..a97e802ca523 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -335,6 +335,7 @@ static struct platform_driver hid_dev_rot_platform_driver = { .id_table = hid_dev_rot_ids, .driver = { .name = KBUILD_MODNAME, + .pm = &hid_sensor_pm_ops, }, .probe = hid_dev_rot_probe, .remove = hid_dev_rot_remove, diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index e2f926cdcad2..a0aedda7dfd7 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -392,7 +392,7 @@ static int as3935_probe(struct spi_device *spi) return ret; } - ret = iio_triggered_buffer_setup(indio_dev, NULL, + ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time, &as3935_trigger_handler, NULL); if (ret) { diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 4d8e7f18a9af..941cd9b83941 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -80,6 +80,8 @@ static struct ib_cm { __be32 random_id_operand; struct list_head timewait_list; struct workqueue_struct *wq; + /* Sync on cm change port state */ + spinlock_t state_lock; } cm; /* Counter indexes ordered by attribute ID */ @@ -161,6 +163,8 @@ struct cm_port { struct ib_mad_agent *mad_agent; struct kobject port_obj; u8 port_num; + struct list_head cm_priv_prim_list; + struct list_head cm_priv_altr_list; struct cm_counter_group counter_group[CM_COUNTER_GROUPS]; }; @@ -241,6 +245,12 @@ struct cm_id_private { u8 service_timeout; u8 target_ack_delay; + struct list_head prim_list; + struct list_head altr_list; + /* Indicates that the send port mad is registered and av is set */ + int prim_send_port_not_ready; + int altr_send_port_not_ready; + struct list_head work_list; atomic_t work_count; }; @@ -259,20 +269,47 @@ static int cm_alloc_msg(struct cm_id_private *cm_id_priv, struct ib_mad_agent *mad_agent; struct ib_mad_send_buf *m; struct ib_ah *ah; + struct cm_av *av; + unsigned long flags, flags2; + int ret = 0; + /* don't let the port to be released till the agent is down */ + spin_lock_irqsave(&cm.state_lock, flags2); + spin_lock_irqsave(&cm.lock, flags); + if (!cm_id_priv->prim_send_port_not_ready) + av = &cm_id_priv->av; + else if (!cm_id_priv->altr_send_port_not_ready && + (cm_id_priv->alt_av.port)) + av = &cm_id_priv->alt_av; + else { + pr_info("%s: not valid CM id\n", __func__); + ret = -ENODEV; + spin_unlock_irqrestore(&cm.lock, flags); + goto out; + } + spin_unlock_irqrestore(&cm.lock, flags); + /* Make sure the port haven't released the mad yet */ mad_agent = cm_id_priv->av.port->mad_agent; - ah = ib_create_ah(mad_agent->qp->pd, &cm_id_priv->av.ah_attr); - if (IS_ERR(ah)) - return PTR_ERR(ah); + if (!mad_agent) { + pr_info("%s: not a valid MAD agent\n", __func__); + ret = -ENODEV; + goto out; + } + ah = ib_create_ah(mad_agent->qp->pd, &av->ah_attr); + if (IS_ERR(ah)) { + ret = PTR_ERR(ah); + goto out; + } m = ib_create_send_mad(mad_agent, cm_id_priv->id.remote_cm_qpn, - cm_id_priv->av.pkey_index, + av->pkey_index, 0, IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA, GFP_ATOMIC, IB_MGMT_BASE_VERSION); if (IS_ERR(m)) { ib_destroy_ah(ah); - return PTR_ERR(m); + ret = PTR_ERR(m); + goto out; } /* Timeout set by caller if response is expected. */ @@ -282,7 +319,10 @@ static int cm_alloc_msg(struct cm_id_private *cm_id_priv, atomic_inc(&cm_id_priv->refcount); m->context[0] = cm_id_priv; *msg = m; - return 0; + +out: + spin_unlock_irqrestore(&cm.state_lock, flags2); + return ret; } static int cm_alloc_response_msg(struct cm_port *port, @@ -352,7 +392,8 @@ static void cm_init_av_for_response(struct cm_port *port, struct ib_wc *wc, grh, &av->ah_attr); } -static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av) +static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av, + struct cm_id_private *cm_id_priv) { struct cm_device *cm_dev; struct cm_port *port = NULL; @@ -387,7 +428,17 @@ static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av) &av->ah_attr); av->timeout = path->packet_life_time + 1; - return 0; + spin_lock_irqsave(&cm.lock, flags); + if (&cm_id_priv->av == av) + list_add_tail(&cm_id_priv->prim_list, &port->cm_priv_prim_list); + else if (&cm_id_priv->alt_av == av) + list_add_tail(&cm_id_priv->altr_list, &port->cm_priv_altr_list); + else + ret = -EINVAL; + + spin_unlock_irqrestore(&cm.lock, flags); + + return ret; } static int cm_alloc_id(struct cm_id_private *cm_id_priv) @@ -677,6 +728,8 @@ struct ib_cm_id *ib_create_cm_id(struct ib_device *device, spin_lock_init(&cm_id_priv->lock); init_completion(&cm_id_priv->comp); INIT_LIST_HEAD(&cm_id_priv->work_list); + INIT_LIST_HEAD(&cm_id_priv->prim_list); + INIT_LIST_HEAD(&cm_id_priv->altr_list); atomic_set(&cm_id_priv->work_count, -1); atomic_set(&cm_id_priv->refcount, 1); return &cm_id_priv->id; @@ -892,6 +945,15 @@ retest: break; } + spin_lock_irq(&cm.lock); + if (!list_empty(&cm_id_priv->altr_list) && + (!cm_id_priv->altr_send_port_not_ready)) + list_del(&cm_id_priv->altr_list); + if (!list_empty(&cm_id_priv->prim_list) && + (!cm_id_priv->prim_send_port_not_ready)) + list_del(&cm_id_priv->prim_list); + spin_unlock_irq(&cm.lock); + cm_free_id(cm_id->local_id); cm_deref_id(cm_id_priv); wait_for_completion(&cm_id_priv->comp); @@ -1192,12 +1254,13 @@ int ib_send_cm_req(struct ib_cm_id *cm_id, goto out; } - ret = cm_init_av_by_path(param->primary_path, &cm_id_priv->av); + ret = cm_init_av_by_path(param->primary_path, &cm_id_priv->av, + cm_id_priv); if (ret) goto error1; if (param->alternate_path) { ret = cm_init_av_by_path(param->alternate_path, - &cm_id_priv->alt_av); + &cm_id_priv->alt_av, cm_id_priv); if (ret) goto error1; } @@ -1639,7 +1702,8 @@ static int cm_req_handler(struct cm_work *work) cm_format_paths_from_req(req_msg, &work->path[0], &work->path[1]); memcpy(work->path[0].dmac, cm_id_priv->av.ah_attr.dmac, ETH_ALEN); - ret = cm_init_av_by_path(&work->path[0], &cm_id_priv->av); + ret = cm_init_av_by_path(&work->path[0], &cm_id_priv->av, + cm_id_priv); if (ret) { ib_get_cached_gid(work->port->cm_dev->ib_device, work->port->port_num, 0, &work->path[0].sgid, @@ -1650,7 +1714,8 @@ static int cm_req_handler(struct cm_work *work) goto rejected; } if (req_msg->alt_local_lid) { - ret = cm_init_av_by_path(&work->path[1], &cm_id_priv->alt_av); + ret = cm_init_av_by_path(&work->path[1], &cm_id_priv->alt_av, + cm_id_priv); if (ret) { ib_send_cm_rej(cm_id, IB_CM_REJ_INVALID_ALT_GID, &work->path[0].sgid, @@ -2705,7 +2770,8 @@ int ib_send_cm_lap(struct ib_cm_id *cm_id, goto out; } - ret = cm_init_av_by_path(alternate_path, &cm_id_priv->alt_av); + ret = cm_init_av_by_path(alternate_path, &cm_id_priv->alt_av, + cm_id_priv); if (ret) goto out; cm_id_priv->alt_av.timeout = @@ -2817,7 +2883,8 @@ static int cm_lap_handler(struct cm_work *work) cm_init_av_for_response(work->port, work->mad_recv_wc->wc, work->mad_recv_wc->recv_buf.grh, &cm_id_priv->av); - cm_init_av_by_path(param->alternate_path, &cm_id_priv->alt_av); + cm_init_av_by_path(param->alternate_path, &cm_id_priv->alt_av, + cm_id_priv); ret = atomic_inc_and_test(&cm_id_priv->work_count); if (!ret) list_add_tail(&work->list, &cm_id_priv->work_list); @@ -3009,7 +3076,7 @@ int ib_send_cm_sidr_req(struct ib_cm_id *cm_id, return -EINVAL; cm_id_priv = container_of(cm_id, struct cm_id_private, id); - ret = cm_init_av_by_path(param->path, &cm_id_priv->av); + ret = cm_init_av_by_path(param->path, &cm_id_priv->av, cm_id_priv); if (ret) goto out; @@ -3446,7 +3513,9 @@ out: static int cm_migrate(struct ib_cm_id *cm_id) { struct cm_id_private *cm_id_priv; + struct cm_av tmp_av; unsigned long flags; + int tmp_send_port_not_ready; int ret = 0; cm_id_priv = container_of(cm_id, struct cm_id_private, id); @@ -3455,7 +3524,14 @@ static int cm_migrate(struct ib_cm_id *cm_id) (cm_id->lap_state == IB_CM_LAP_UNINIT || cm_id->lap_state == IB_CM_LAP_IDLE)) { cm_id->lap_state = IB_CM_LAP_IDLE; + /* Swap address vector */ + tmp_av = cm_id_priv->av; cm_id_priv->av = cm_id_priv->alt_av; + cm_id_priv->alt_av = tmp_av; + /* Swap port send ready state */ + tmp_send_port_not_ready = cm_id_priv->prim_send_port_not_ready; + cm_id_priv->prim_send_port_not_ready = cm_id_priv->altr_send_port_not_ready; + cm_id_priv->altr_send_port_not_ready = tmp_send_port_not_ready; } else ret = -EINVAL; spin_unlock_irqrestore(&cm_id_priv->lock, flags); @@ -3875,6 +3951,9 @@ static void cm_add_one(struct ib_device *ib_device) port->cm_dev = cm_dev; port->port_num = i; + INIT_LIST_HEAD(&port->cm_priv_prim_list); + INIT_LIST_HEAD(&port->cm_priv_altr_list); + ret = cm_create_port_fs(port); if (ret) goto error1; @@ -3932,6 +4011,8 @@ static void cm_remove_one(struct ib_device *ib_device, void *client_data) { struct cm_device *cm_dev = client_data; struct cm_port *port; + struct cm_id_private *cm_id_priv; + struct ib_mad_agent *cur_mad_agent; struct ib_port_modify port_modify = { .clr_port_cap_mask = IB_PORT_CM_SUP }; @@ -3955,15 +4036,27 @@ static void cm_remove_one(struct ib_device *ib_device, void *client_data) port = cm_dev->port[i-1]; ib_modify_port(ib_device, port->port_num, 0, &port_modify); + /* Mark all the cm_id's as not valid */ + spin_lock_irq(&cm.lock); + list_for_each_entry(cm_id_priv, &port->cm_priv_altr_list, altr_list) + cm_id_priv->altr_send_port_not_ready = 1; + list_for_each_entry(cm_id_priv, &port->cm_priv_prim_list, prim_list) + cm_id_priv->prim_send_port_not_ready = 1; + spin_unlock_irq(&cm.lock); /* * We flush the queue here after the going_down set, this * verify that no new works will be queued in the recv handler, * after that we can call the unregister_mad_agent */ flush_workqueue(cm.wq); - ib_unregister_mad_agent(port->mad_agent); + spin_lock_irq(&cm.state_lock); + cur_mad_agent = port->mad_agent; + port->mad_agent = NULL; + spin_unlock_irq(&cm.state_lock); + ib_unregister_mad_agent(cur_mad_agent); cm_remove_port_fs(port); } + device_unregister(cm_dev->device); kfree(cm_dev); } @@ -3976,6 +4069,7 @@ static int __init ib_cm_init(void) INIT_LIST_HEAD(&cm.device_list); rwlock_init(&cm.device_lock); spin_lock_init(&cm.lock); + spin_lock_init(&cm.state_lock); cm.listen_service_table = RB_ROOT; cm.listen_service_id = be64_to_cpu(IB_CM_ASSIGN_SERVICE_ID); cm.remote_id_table = RB_ROOT; diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c index bb6685fb08c6..6aa648cb5381 100644 --- a/drivers/infiniband/core/multicast.c +++ b/drivers/infiniband/core/multicast.c @@ -106,7 +106,6 @@ struct mcast_group { atomic_t refcount; enum mcast_group_state state; struct ib_sa_query *query; - int query_id; u16 pkey_index; u8 leave_state; int retries; @@ -339,11 +338,7 @@ static int send_join(struct mcast_group *group, struct mcast_member *member) member->multicast.comp_mask, 3000, GFP_KERNEL, join_handler, group, &group->query); - if (ret >= 0) { - group->query_id = ret; - ret = 0; - } - return ret; + return (ret > 0) ? 0 : ret; } static int send_leave(struct mcast_group *group, u8 leave_state) @@ -363,11 +358,7 @@ static int send_leave(struct mcast_group *group, u8 leave_state) IB_SA_MCMEMBER_REC_JOIN_STATE, 3000, GFP_KERNEL, leave_handler, group, &group->query); - if (ret >= 0) { - group->query_id = ret; - ret = 0; - } - return ret; + return (ret > 0) ? 0 : ret; } static void join_group(struct mcast_group *group, struct mcast_member *member, diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 38acb3cfc545..04f3c0db9126 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -175,7 +175,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, cur_base = addr & PAGE_MASK; - if (npages == 0) { + if (npages == 0 || npages > UINT_MAX) { ret = -EINVAL; goto out; } diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h index 94bbd8c155fc..a2d19d136099 100644 --- a/drivers/infiniband/core/uverbs.h +++ b/drivers/infiniband/core/uverbs.h @@ -116,6 +116,7 @@ struct ib_uverbs_event_file { struct ib_uverbs_file { struct kref ref; struct mutex mutex; + struct mutex cleanup_mutex; /* protect cleanup */ struct ib_uverbs_device *device; struct ib_ucontext *ucontext; struct ib_event_handler event_handler; diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 24f3ca2c4ad7..7becef27cbbe 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -244,12 +244,9 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, container_of(uobj, struct ib_uqp_object, uevent.uobject); idr_remove_uobj(&ib_uverbs_qp_idr, uobj); - if (qp != qp->real_qp) { - ib_close_qp(qp); - } else { + if (qp == qp->real_qp) ib_uverbs_detach_umcast(qp, uqp); - ib_destroy_qp(qp); - } + ib_destroy_qp(qp); ib_uverbs_release_uevent(file, &uqp->uevent); kfree(uqp); } @@ -922,6 +919,7 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp) file->async_file = NULL; kref_init(&file->ref); mutex_init(&file->mutex); + mutex_init(&file->cleanup_mutex); filp->private_data = file; kobject_get(&dev->kobj); @@ -947,18 +945,20 @@ static int ib_uverbs_close(struct inode *inode, struct file *filp) { struct ib_uverbs_file *file = filp->private_data; struct ib_uverbs_device *dev = file->device; - struct ib_ucontext *ucontext = NULL; + + mutex_lock(&file->cleanup_mutex); + if (file->ucontext) { + ib_uverbs_cleanup_ucontext(file, file->ucontext); + file->ucontext = NULL; + } + mutex_unlock(&file->cleanup_mutex); mutex_lock(&file->device->lists_mutex); - ucontext = file->ucontext; - file->ucontext = NULL; if (!file->is_closed) { list_del(&file->list); file->is_closed = 1; } mutex_unlock(&file->device->lists_mutex); - if (ucontext) - ib_uverbs_cleanup_ucontext(file, ucontext); if (file->async_file) kref_put(&file->async_file->ref, ib_uverbs_release_event_file); @@ -1172,22 +1172,30 @@ static void ib_uverbs_free_hw_resources(struct ib_uverbs_device *uverbs_dev, mutex_lock(&uverbs_dev->lists_mutex); while (!list_empty(&uverbs_dev->uverbs_file_list)) { struct ib_ucontext *ucontext; - file = list_first_entry(&uverbs_dev->uverbs_file_list, struct ib_uverbs_file, list); file->is_closed = 1; - ucontext = file->ucontext; list_del(&file->list); - file->ucontext = NULL; kref_get(&file->ref); mutex_unlock(&uverbs_dev->lists_mutex); - /* We must release the mutex before going ahead and calling - * disassociate_ucontext. disassociate_ucontext might end up - * indirectly calling uverbs_close, for example due to freeing - * the resources (e.g mmput). - */ + ib_uverbs_event_handler(&file->event_handler, &event); + + mutex_lock(&file->cleanup_mutex); + ucontext = file->ucontext; + file->ucontext = NULL; + mutex_unlock(&file->cleanup_mutex); + + /* At this point ib_uverbs_close cannot be running + * ib_uverbs_cleanup_ucontext + */ if (ucontext) { + /* We must release the mutex before going ahead and + * calling disassociate_ucontext. disassociate_ucontext + * might end up indirectly calling uverbs_close, + * for example due to freeing the resources + * (e.g mmput). + */ ib_dev->disassociate_ucontext(ucontext); ib_uverbs_cleanup_ucontext(file, ucontext); } diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c index 06da56bda201..c007c766c61e 100644 --- a/drivers/infiniband/hw/mlx4/ah.c +++ b/drivers/infiniband/hw/mlx4/ah.c @@ -102,7 +102,10 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr if (vlan_tag < 0x1000) vlan_tag |= (ah_attr->sl & 7) << 13; ah->av.eth.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24)); - ah->av.eth.gid_index = mlx4_ib_gid_index_to_real_index(ibdev, ah_attr->port_num, ah_attr->grh.sgid_index); + ret = mlx4_ib_gid_index_to_real_index(ibdev, ah_attr->port_num, ah_attr->grh.sgid_index); + if (ret < 0) + return ERR_PTR(ret); + ah->av.eth.gid_index = ret; ah->av.eth.vlan = cpu_to_be16(vlan_tag); if (ah_attr->static_rate) { ah->av.eth.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET; diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c index b88fc8f5ab18..57e1a08925d9 100644 --- a/drivers/infiniband/hw/mlx4/cq.c +++ b/drivers/infiniband/hw/mlx4/cq.c @@ -253,11 +253,14 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, if (context) if (ib_copy_to_udata(udata, &cq->mcq.cqn, sizeof (__u32))) { err = -EFAULT; - goto err_dbmap; + goto err_cq_free; } return &cq->ibcq; +err_cq_free: + mlx4_cq_free(dev->dev, &cq->mcq); + err_dbmap: if (context) mlx4_ib_db_unmap_user(to_mucontext(context), &cq->db); diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index 05179f47bbde..d862b9b7910e 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -1080,6 +1080,27 @@ void handle_port_mgmt_change_event(struct work_struct *work) /* Generate GUID changed event */ if (changed_attr & MLX4_EQ_PORT_INFO_GID_PFX_CHANGE_MASK) { + if (mlx4_is_master(dev->dev)) { + union ib_gid gid; + int err = 0; + + if (!eqe->event.port_mgmt_change.params.port_info.gid_prefix) + err = __mlx4_ib_query_gid(&dev->ib_dev, port, 0, &gid, 1); + else + gid.global.subnet_prefix = + eqe->event.port_mgmt_change.params.port_info.gid_prefix; + if (err) { + pr_warn("Could not change QP1 subnet prefix for port %d: query_gid error (%d)\n", + port, err); + } else { + pr_debug("Changing QP1 subnet prefix for port %d. old=0x%llx. new=0x%llx\n", + port, + (u64)atomic64_read(&dev->sriov.demux[port - 1].subnet_prefix), + be64_to_cpu(gid.global.subnet_prefix)); + atomic64_set(&dev->sriov.demux[port - 1].subnet_prefix, + be64_to_cpu(gid.global.subnet_prefix)); + } + } mlx4_ib_dispatch_event(dev, port, IB_EVENT_GID_CHANGE); /*if master, notify all slaves*/ if (mlx4_is_master(dev->dev)) @@ -2154,6 +2175,8 @@ int mlx4_ib_init_sriov(struct mlx4_ib_dev *dev) if (err) goto demux_err; dev->sriov.demux[i].guid_cache[0] = gid.global.interface_id; + atomic64_set(&dev->sriov.demux[i].subnet_prefix, + be64_to_cpu(gid.global.subnet_prefix)); err = alloc_pv_object(dev, mlx4_master_func_num(dev->dev), i + 1, &dev->sriov.sqps[i]); if (err) diff --git a/drivers/infiniband/hw/mlx4/mcg.c b/drivers/infiniband/hw/mlx4/mcg.c index 99451d887266..36ec8aa048aa 100644 --- a/drivers/infiniband/hw/mlx4/mcg.c +++ b/drivers/infiniband/hw/mlx4/mcg.c @@ -489,7 +489,7 @@ static u8 get_leave_state(struct mcast_group *group) if (!group->members[i]) leave_state |= (1 << i); - return leave_state & (group->rec.scope_join_state & 7); + return leave_state & (group->rec.scope_join_state & 0xf); } static int join_group(struct mcast_group *group, int slave, u8 join_mask) @@ -564,8 +564,8 @@ static void mlx4_ib_mcg_timeout_handler(struct work_struct *work) } else mcg_warn_group(group, "DRIVER BUG\n"); } else if (group->state == MCAST_LEAVE_SENT) { - if (group->rec.scope_join_state & 7) - group->rec.scope_join_state &= 0xf8; + if (group->rec.scope_join_state & 0xf) + group->rec.scope_join_state &= 0xf0; group->state = MCAST_IDLE; mutex_unlock(&group->lock); if (release_group(group, 1)) @@ -605,7 +605,7 @@ static int handle_leave_req(struct mcast_group *group, u8 leave_mask, static int handle_join_req(struct mcast_group *group, u8 join_mask, struct mcast_req *req) { - u8 group_join_state = group->rec.scope_join_state & 7; + u8 group_join_state = group->rec.scope_join_state & 0xf; int ref = 0; u16 status; struct ib_sa_mcmember_data *sa_data = (struct ib_sa_mcmember_data *)req->sa_mad.data; @@ -690,8 +690,8 @@ static void mlx4_ib_mcg_work_handler(struct work_struct *work) u8 cur_join_state; resp_join_state = ((struct ib_sa_mcmember_data *) - group->response_sa_mad.data)->scope_join_state & 7; - cur_join_state = group->rec.scope_join_state & 7; + group->response_sa_mad.data)->scope_join_state & 0xf; + cur_join_state = group->rec.scope_join_state & 0xf; if (method == IB_MGMT_METHOD_GET_RESP) { /* successfull join */ @@ -710,7 +710,7 @@ process_requests: req = list_first_entry(&group->pending_list, struct mcast_req, group_list); sa_data = (struct ib_sa_mcmember_data *)req->sa_mad.data; - req_join_state = sa_data->scope_join_state & 0x7; + req_join_state = sa_data->scope_join_state & 0xf; /* For a leave request, we will immediately answer the VF, and * update our internal counters. The actual leave will be sent diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index 1caa11edac03..78f29e91653a 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -441,7 +441,7 @@ struct mlx4_ib_demux_ctx { struct workqueue_struct *wq; struct workqueue_struct *ud_wq; spinlock_t ud_lock; - __be64 subnet_prefix; + atomic64_t subnet_prefix; __be64 guid_cache[128]; struct mlx4_ib_dev *dev; /* the following lock protects both mcg_table and mcg_mgid0_list */ diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index ea1e2ddaddf5..f350f2d61c15 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -2331,24 +2331,27 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_ud_wr *wr, sqp->ud_header.grh.flow_label = ah->av.ib.sl_tclass_flowlabel & cpu_to_be32(0xfffff); sqp->ud_header.grh.hop_limit = ah->av.ib.hop_limit; - if (is_eth) + if (is_eth) { memcpy(sqp->ud_header.grh.source_gid.raw, sgid.raw, 16); - else { - if (mlx4_is_mfunc(to_mdev(ib_dev)->dev)) { - /* When multi-function is enabled, the ib_core gid - * indexes don't necessarily match the hw ones, so - * we must use our own cache */ - sqp->ud_header.grh.source_gid.global.subnet_prefix = - to_mdev(ib_dev)->sriov.demux[sqp->qp.port - 1]. - subnet_prefix; - sqp->ud_header.grh.source_gid.global.interface_id = - to_mdev(ib_dev)->sriov.demux[sqp->qp.port - 1]. - guid_cache[ah->av.ib.gid_index]; - } else - ib_get_cached_gid(ib_dev, - be32_to_cpu(ah->av.ib.port_pd) >> 24, - ah->av.ib.gid_index, - &sqp->ud_header.grh.source_gid, NULL); + } else { + if (mlx4_is_mfunc(to_mdev(ib_dev)->dev)) { + /* When multi-function is enabled, the ib_core gid + * indexes don't necessarily match the hw ones, so + * we must use our own cache + */ + sqp->ud_header.grh.source_gid.global.subnet_prefix = + cpu_to_be64(atomic64_read(&(to_mdev(ib_dev)->sriov. + demux[sqp->qp.port - 1]. + subnet_prefix))); + sqp->ud_header.grh.source_gid.global.interface_id = + to_mdev(ib_dev)->sriov.demux[sqp->qp.port - 1]. + guid_cache[ah->av.ib.gid_index]; + } else { + ib_get_cached_gid(ib_dev, + be32_to_cpu(ah->av.ib.port_pd) >> 24, + ah->av.ib.gid_index, + &sqp->ud_header.grh.source_gid, NULL); + } } memcpy(sqp->ud_header.grh.destination_gid.raw, ah->av.ib.dgid, 16); diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index 8184267c7901..02c8deab1fff 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -787,8 +787,7 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, if (err) goto err_create; } else { - /* for now choose 64 bytes till we have a proper interface */ - cqe_size = 64; + cqe_size = cache_line_size() == 128 ? 128 : 64; err = create_cq_kernel(dev, cq, entries, cqe_size, &cqb, &index, &inlen); if (err) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index bfc940ff9c8a..2a1fdcaa3044 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -947,13 +947,13 @@ static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context, { struct mlx5_ib_dev *ibdev = (struct mlx5_ib_dev *)context; struct ib_event ibev; - + bool fatal = false; u8 port = 0; switch (event) { case MLX5_DEV_EVENT_SYS_ERROR: - ibdev->ib_active = false; ibev.event = IB_EVENT_DEVICE_FATAL; + fatal = true; break; case MLX5_DEV_EVENT_PORT_UP: @@ -998,6 +998,9 @@ static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context, if (ibdev->ib_active) ib_dispatch_event(&ibev); + + if (fatal) + ibdev->ib_active = false; } static void get_ext_port_caps(struct mlx5_ib_dev *dev) diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index 3ede10309754..69a151ae8261 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -472,6 +472,7 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_ah *address, u32 qpn); void ipoib_reap_ah(struct work_struct *work); +struct ipoib_path *__path_find(struct net_device *dev, void *gid); void ipoib_mark_paths_invalid(struct net_device *dev); void ipoib_flush_paths(struct net_device *dev); struct ipoib_dev_priv *ipoib_intf_alloc(const char *format); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 3ae9726efb98..8ca75af0e6d1 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -1299,6 +1299,8 @@ void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx) } } +#define QPN_AND_OPTIONS_OFFSET 4 + static void ipoib_cm_tx_start(struct work_struct *work) { struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv, @@ -1307,6 +1309,7 @@ static void ipoib_cm_tx_start(struct work_struct *work) struct ipoib_neigh *neigh; struct ipoib_cm_tx *p; unsigned long flags; + struct ipoib_path *path; int ret; struct ib_sa_path_rec pathrec; @@ -1319,7 +1322,19 @@ static void ipoib_cm_tx_start(struct work_struct *work) p = list_entry(priv->cm.start_list.next, typeof(*p), list); list_del_init(&p->list); neigh = p->neigh; + qpn = IPOIB_QPN(neigh->daddr); + /* + * As long as the search is with these 2 locks, + * path existence indicates its validity. + */ + path = __path_find(dev, neigh->daddr + QPN_AND_OPTIONS_OFFSET); + if (!path) { + pr_info("%s ignore not valid path %pI6\n", + __func__, + neigh->daddr + QPN_AND_OPTIONS_OFFSET); + goto free_neigh; + } memcpy(&pathrec, &p->path->pathrec, sizeof pathrec); spin_unlock_irqrestore(&priv->lock, flags); @@ -1331,6 +1346,7 @@ static void ipoib_cm_tx_start(struct work_struct *work) spin_lock_irqsave(&priv->lock, flags); if (ret) { +free_neigh: neigh = p->neigh; if (neigh) { neigh->cm = NULL; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index fa9c42ff1fb0..85de078fb0ce 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -1028,8 +1028,17 @@ static void __ipoib_ib_dev_flush(struct ipoib_dev_priv *priv, } if (level == IPOIB_FLUSH_LIGHT) { + int oper_up; ipoib_mark_paths_invalid(dev); + /* Set IPoIB operation as down to prevent races between: + * the flush flow which leaves MCG and on the fly joins + * which can happen during that time. mcast restart task + * should deal with join requests we missed. + */ + oper_up = test_and_clear_bit(IPOIB_FLAG_OPER_UP, &priv->flags); ipoib_mcast_dev_flush(dev); + if (oper_up) + set_bit(IPOIB_FLAG_OPER_UP, &priv->flags); ipoib_flush_ah(dev); } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 942dffca6a9d..5f7681b975d0 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -481,7 +481,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf) return -EINVAL; } -static struct ipoib_path *__path_find(struct net_device *dev, void *gid) +struct ipoib_path *__path_find(struct net_device *dev, void *gid) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct rb_node *n = priv->path_tree.rb_node; diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c index 907e4e278fce..c877e56a9bd5 100644 --- a/drivers/input/keyboard/goldfish_events.c +++ b/drivers/input/keyboard/goldfish_events.c @@ -17,11 +17,15 @@ #include <linux/interrupt.h> #include <linux/types.h> #include <linux/input.h> +#include <linux/input/mt.h> #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/acpi.h> + +#define GOLDFISH_MAX_FINGERS 5 enum { REG_READ = 0x00, @@ -51,7 +55,21 @@ static irqreturn_t events_interrupt(int irq, void *dev_id) value = __raw_readl(edev->addr + REG_READ); input_event(edev->input, type, code, value); - input_sync(edev->input); + // Send an extra (EV_SYN, SYN_REPORT, 0x0) event + // if a key was pressed. Some keyboard device + // drivers may only send the EV_KEY event and + // not EV_SYN. + // Note that sending an extra SYN_REPORT is not + // necessary nor correct protocol with other + // devices such as touchscreens, which will send + // their own SYN_REPORT's when sufficient event + // information has been collected (e.g., for + // touchscreens, when pressure and X/Y coordinates + // have been received). Hence, we will only send + // this extra SYN_REPORT if type == EV_KEY. + if (type == EV_KEY) { + input_sync(edev->input); + } return IRQ_HANDLED; } @@ -153,6 +171,15 @@ static int events_probe(struct platform_device *pdev) input_dev->name = edev->name; input_dev->id.bustype = BUS_HOST; + // Set the Goldfish Device to be multi-touch. + // In the Ranchu kernel, there is multi-touch-specific + // code for handling ABS_MT_SLOT events. + // See drivers/input/input.c:input_handle_abs_event. + // If we do not issue input_mt_init_slots, + // the kernel will filter out needed ABS_MT_SLOT + // events when we touch the screen in more than one place, + // preventing multi-touch with more than one finger from working. + input_mt_init_slots(input_dev, GOLDFISH_MAX_FINGERS, 0); events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX); events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX); @@ -178,10 +205,26 @@ static int events_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_events_of_match[] = { + { .compatible = "google,goldfish-events-keypad", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_events_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id goldfish_events_acpi_match[] = { + { "GFSH0002", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goldfish_events_acpi_match); +#endif + static struct platform_driver events_driver = { .probe = events_probe, .driver = { .name = "goldfish_events", + .of_match_table = goldfish_events_of_match, + .acpi_match_table = ACPI_PTR(goldfish_events_acpi_match), }, }; diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c index 7fbf7247e65f..7e5222aec7c1 100644 --- a/drivers/input/keyreset.c +++ b/drivers/input/keyreset.c @@ -32,8 +32,7 @@ struct keyreset_state { static void do_restart(struct work_struct *unused) { - sys_sync(); - kernel_restart(NULL); + orderly_reboot(); } static void do_reset_fn(void *priv) diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index be5b399da5d3..43482ae1e049 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1163,6 +1163,13 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = { DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"), }, }, + { + /* Fujitsu H760 also has a middle button */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"), + }, + }, #endif { } }; @@ -1507,10 +1514,10 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { }, }, { - /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */ + /* Fujitsu H760 does not work with crc_enabled == 0 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"), + DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"), }, }, { @@ -1521,6 +1528,20 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { }, }, { + /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"), + }, + }, + { + /* Fujitsu LIFEBOOK E556 does not work with crc_enabled == 0 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E556"), + }, + }, + { /* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h index a5eed2ade53d..34da81c006b6 100644 --- a/drivers/input/serio/i8042-io.h +++ b/drivers/input/serio/i8042-io.h @@ -81,7 +81,7 @@ static inline int i8042_platform_init(void) return -EBUSY; #endif - i8042_reset = 1; + i8042_reset = I8042_RESET_ALWAYS; return 0; } diff --git a/drivers/input/serio/i8042-ip22io.h b/drivers/input/serio/i8042-ip22io.h index ee1ad27d6ed0..08a1c10a1448 100644 --- a/drivers/input/serio/i8042-ip22io.h +++ b/drivers/input/serio/i8042-ip22io.h @@ -61,7 +61,7 @@ static inline int i8042_platform_init(void) return -EBUSY; #endif - i8042_reset = 1; + i8042_reset = I8042_RESET_ALWAYS; return 0; } diff --git a/drivers/input/serio/i8042-ppcio.h b/drivers/input/serio/i8042-ppcio.h index f708c75d16f1..1aabea43329e 100644 --- a/drivers/input/serio/i8042-ppcio.h +++ b/drivers/input/serio/i8042-ppcio.h @@ -44,7 +44,7 @@ static inline void i8042_write_command(int val) static inline int i8042_platform_init(void) { - i8042_reset = 1; + i8042_reset = I8042_RESET_ALWAYS; return 0; } diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index afcd1c1a05b2..6231d63860ee 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -130,7 +130,7 @@ static int __init i8042_platform_init(void) } } - i8042_reset = 1; + i8042_reset = I8042_RESET_ALWAYS; return 0; } diff --git a/drivers/input/serio/i8042-unicore32io.h b/drivers/input/serio/i8042-unicore32io.h index 73f5cc124a36..455747552f85 100644 --- a/drivers/input/serio/i8042-unicore32io.h +++ b/drivers/input/serio/i8042-unicore32io.h @@ -61,7 +61,7 @@ static inline int i8042_platform_init(void) if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042")) return -EBUSY; - i8042_reset = 1; + i8042_reset = I8042_RESET_ALWAYS; return 0; } diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 68f5f4a0f1e7..073246c7d163 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -510,6 +510,90 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { { } }; +/* + * On some Asus laptops, just running self tests cause problems. + */ +static const struct dmi_system_id i8042_dmi_noselftest_table[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "A455LD"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "K401LB"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "K501LB"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "K501LX"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "R409L"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "V502LX"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X302LA"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X450LD"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X455LAB"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X455LDB"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X455LF"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "Z450LA"), + }, + }, + { } +}; static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { { /* MSI Wind U-100 */ @@ -793,6 +877,13 @@ static const struct dmi_system_id __initconst i8042_dmi_kbdreset_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "P34"), }, }, + { + /* Schenker XMG C504 - Elantech touchpad */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "XMG"), + DMI_MATCH(DMI_PRODUCT_NAME, "C504"), + }, + }, { } }; @@ -1072,12 +1163,18 @@ static int __init i8042_platform_init(void) return retval; #if defined(__ia64__) - i8042_reset = true; + i8042_reset = I8042_RESET_ALWAYS; #endif #ifdef CONFIG_X86 - if (dmi_check_system(i8042_dmi_reset_table)) - i8042_reset = true; + /* Honor module parameter when value is not default */ + if (i8042_reset == I8042_RESET_DEFAULT) { + if (dmi_check_system(i8042_dmi_reset_table)) + i8042_reset = I8042_RESET_ALWAYS; + + if (dmi_check_system(i8042_dmi_noselftest_table)) + i8042_reset = I8042_RESET_NEVER; + } if (dmi_check_system(i8042_dmi_noloop_table)) i8042_noloop = true; diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 405252a884dd..89abfdb539ac 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -48,9 +48,39 @@ static bool i8042_unlock; module_param_named(unlock, i8042_unlock, bool, 0); MODULE_PARM_DESC(unlock, "Ignore keyboard lock."); -static bool i8042_reset; -module_param_named(reset, i8042_reset, bool, 0); -MODULE_PARM_DESC(reset, "Reset controller during init and cleanup."); +enum i8042_controller_reset_mode { + I8042_RESET_NEVER, + I8042_RESET_ALWAYS, + I8042_RESET_ON_S2RAM, +#define I8042_RESET_DEFAULT I8042_RESET_ON_S2RAM +}; +static enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT; +static int i8042_set_reset(const char *val, const struct kernel_param *kp) +{ + enum i8042_controller_reset_mode *arg = kp->arg; + int error; + bool reset; + + if (val) { + error = kstrtobool(val, &reset); + if (error) + return error; + } else { + reset = true; + } + + *arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER; + return 0; +} + +static const struct kernel_param_ops param_ops_reset_param = { + .flags = KERNEL_PARAM_OPS_FL_NOARG, + .set = i8042_set_reset, +}; +#define param_check_reset_param(name, p) \ + __param_check(name, p, enum i8042_controller_reset_mode) +module_param_named(reset, i8042_reset, reset_param, 0); +MODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both"); static bool i8042_direct; module_param_named(direct, i8042_direct, bool, 0); @@ -1019,7 +1049,7 @@ static int i8042_controller_init(void) * Reset the controller and reset CRT to the original value set by BIOS. */ -static void i8042_controller_reset(bool force_reset) +static void i8042_controller_reset(bool s2r_wants_reset) { i8042_flush(); @@ -1044,8 +1074,10 @@ static void i8042_controller_reset(bool force_reset) * Reset the controller if requested. */ - if (i8042_reset || force_reset) + if (i8042_reset == I8042_RESET_ALWAYS || + (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) { i8042_controller_selftest(); + } /* * Restore the original control register setting. @@ -1110,7 +1142,7 @@ static void i8042_dritek_enable(void) * before suspending. */ -static int i8042_controller_resume(bool force_reset) +static int i8042_controller_resume(bool s2r_wants_reset) { int error; @@ -1118,7 +1150,8 @@ static int i8042_controller_resume(bool force_reset) if (error) return error; - if (i8042_reset || force_reset) { + if (i8042_reset == I8042_RESET_ALWAYS || + (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) { error = i8042_controller_selftest(); if (error) return error; @@ -1195,7 +1228,7 @@ static int i8042_pm_resume_noirq(struct device *dev) static int i8042_pm_resume(struct device *dev) { - bool force_reset; + bool want_reset; int i; for (i = 0; i < I8042_NUM_PORTS; i++) { @@ -1218,9 +1251,9 @@ static int i8042_pm_resume(struct device *dev) * off control to the platform firmware, otherwise we can simply restore * the mode. */ - force_reset = pm_resume_via_firmware(); + want_reset = pm_resume_via_firmware(); - return i8042_controller_resume(force_reset); + return i8042_controller_resume(want_reset); } static int i8042_pm_thaw(struct device *dev) @@ -1482,7 +1515,7 @@ static int __init i8042_probe(struct platform_device *dev) i8042_platform_device = dev; - if (i8042_reset) { + if (i8042_reset == I8042_RESET_ALWAYS) { error = i8042_controller_selftest(); if (error) return error; diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 0397985a2601..5975d76ce755 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -1833,6 +1833,9 @@ static void dma_ops_domain_free(struct dma_ops_domain *dom) kfree(dom->aperture[i]); } + if (dom->domain.id) + domain_id_free(dom->domain.id); + kfree(dom); } diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index ce1eb562be36..03a691723349 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1835,6 +1835,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, .oas = oas, .tlb = &arm_smmu_gather_ops, .iommu_dev = smmu->dev, + .iova_base = domain->geometry.aperture_start, + .iova_end = domain->geometry.aperture_end, }; } diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c index 6d90221486c9..2acb9242bcf8 100644 --- a/drivers/iommu/dma-mapping-fast.c +++ b/drivers/iommu/dma-mapping-fast.c @@ -151,7 +151,9 @@ static dma_addr_t __fast_smmu_alloc_iova(struct dma_fast_smmu_mapping *mapping, iommu_tlbiall(mapping->domain); mapping->have_stale_tlbs = false; - av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, skip_sync); + av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, mapping->base, + mapping->base + mapping->size - 1, + skip_sync); } return (bit << FAST_PAGE_SHIFT) + mapping->base; @@ -328,7 +330,7 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page, if (unlikely(iova == DMA_ERROR_CODE)) goto fail; - pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova); + pmd = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, iova); if (unlikely(av8l_fast_map_public(pmd, phys_to_map, len, prot))) goto fail_free_iova; @@ -351,7 +353,8 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova, { struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast; unsigned long flags; - av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova); + av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, + mapping->base, iova); unsigned long offset = iova & ~FAST_PAGE_MASK; size_t len = ALIGN(size + offset, FAST_PAGE_SIZE); int nptes = len >> FAST_PAGE_SHIFT; @@ -373,7 +376,8 @@ static void fast_smmu_sync_single_for_cpu(struct device *dev, dma_addr_t iova, size_t size, enum dma_data_direction dir) { struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast; - av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova); + av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, + mapping->base, iova); unsigned long offset = iova & ~FAST_PAGE_MASK; struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK)); @@ -385,7 +389,8 @@ static void fast_smmu_sync_single_for_device(struct device *dev, dma_addr_t iova, size_t size, enum dma_data_direction dir) { struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast; - av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova); + av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, + mapping->base, iova); unsigned long offset = iova & ~FAST_PAGE_MASK; struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK)); @@ -513,7 +518,8 @@ static void *fast_smmu_alloc(struct device *dev, size_t size, while (sg_miter_next(&miter)) { int nptes = miter.length >> FAST_PAGE_SHIFT; - ptep = iopte_pmd_offset(mapping->pgtbl_pmds, iova_iter); + ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, + iova_iter); if (unlikely(av8l_fast_map_public( ptep, page_to_phys(miter.page), miter.length, prot))) { @@ -541,7 +547,7 @@ static void *fast_smmu_alloc(struct device *dev, size_t size, out_unmap: /* need to take the lock again for page tables and iova */ spin_lock_irqsave(&mapping->lock, flags); - ptep = iopte_pmd_offset(mapping->pgtbl_pmds, dma_addr); + ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_addr); av8l_fast_unmap_public(ptep, size); fast_dmac_clean_range(mapping, ptep, ptep + count); out_free_iova: @@ -573,7 +579,7 @@ static void fast_smmu_free(struct device *dev, size_t size, pages = area->pages; dma_common_free_remap(vaddr, size, VM_USERMAP, false); - ptep = iopte_pmd_offset(mapping->pgtbl_pmds, dma_handle); + ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_handle); spin_lock_irqsave(&mapping->lock, flags); av8l_fast_unmap_public(ptep, size); fast_dmac_clean_range(mapping, ptep, ptep + count); @@ -745,6 +751,9 @@ int fast_smmu_attach_device(struct device *dev, mapping->fast->domain = domain; mapping->fast->dev = dev; + domain->geometry.aperture_start = mapping->base; + domain->geometry.aperture_end = mapping->base + size - 1; + if (iommu_attach_device(domain, dev)) return -EINVAL; diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 3821c4786662..e913a930ac80 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -326,7 +326,9 @@ static int dmar_pci_bus_notifier(struct notifier_block *nb, struct pci_dev *pdev = to_pci_dev(data); struct dmar_pci_notify_info *info; - /* Only care about add/remove events for physical functions */ + /* Only care about add/remove events for physical functions. + * For VFs we actually do the lookup based on the corresponding + * PF in device_to_iommu() anyway. */ if (pdev->is_virtfn) return NOTIFY_DONE; if (action != BUS_NOTIFY_ADD_DEVICE && @@ -1858,10 +1860,11 @@ static int dmar_hp_remove_drhd(struct acpi_dmar_header *header, void *arg) /* * All PCI devices managed by this unit should have been destroyed. */ - if (!dmaru->include_all && dmaru->devices && dmaru->devices_cnt) + if (!dmaru->include_all && dmaru->devices && dmaru->devices_cnt) { for_each_active_dev_scope(dmaru->devices, dmaru->devices_cnt, i, dev) return -EBUSY; + } ret = dmar_ir_hotplug(dmaru, false); if (ret == 0) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 24d81308a1a6..59e9abd3345e 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -885,7 +885,13 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf return NULL; if (dev_is_pci(dev)) { + struct pci_dev *pf_pdev; + pdev = to_pci_dev(dev); + /* VFs aren't listed in scope tables; we need to look up + * the PF instead to find the IOMMU. */ + pf_pdev = pci_physfn(pdev); + dev = &pf_pdev->dev; segment = pci_domain_nr(pdev->bus); } else if (has_acpi_companion(dev)) dev = &ACPI_COMPANION(dev)->dev; @@ -898,6 +904,13 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf for_each_active_dev_scope(drhd->devices, drhd->devices_cnt, i, tmp) { if (tmp == dev) { + /* For a VF use its original BDF# not that of the PF + * which we used for the IOMMU lookup. Strictly speaking + * we could do this for all PCI devices; we only need to + * get the BDF# from the scope table for ACPI matches. */ + if (pdev->is_virtfn) + goto got_pdev; + *bus = drhd->devices[i].bus; *devfn = drhd->devices[i].devfn; goto out; @@ -1672,6 +1685,7 @@ static void disable_dmar_iommu(struct intel_iommu *iommu) if (!iommu->domains || !iommu->domain_ids) return; +again: spin_lock_irqsave(&device_domain_lock, flags); list_for_each_entry_safe(info, tmp, &device_domain_list, global) { struct dmar_domain *domain; @@ -1684,10 +1698,19 @@ static void disable_dmar_iommu(struct intel_iommu *iommu) domain = info->domain; - dmar_remove_one_dev_info(domain, info->dev); + __dmar_remove_one_dev_info(info); - if (!domain_type_is_vm_or_si(domain)) + if (!domain_type_is_vm_or_si(domain)) { + /* + * The domain_exit() function can't be called under + * device_domain_lock, as it takes this lock itself. + * So release the lock here and re-run the loop + * afterwards. + */ + spin_unlock_irqrestore(&device_domain_lock, flags); domain_exit(domain); + goto again; + } } spin_unlock_irqrestore(&device_domain_lock, flags); @@ -4182,10 +4205,11 @@ int dmar_check_one_atsr(struct acpi_dmar_header *hdr, void *arg) if (!atsru) return 0; - if (!atsru->include_all && atsru->devices && atsru->devices_cnt) + if (!atsru->include_all && atsru->devices && atsru->devices_cnt) { for_each_active_dev_scope(atsru->devices, atsru->devices_cnt, i, dev) return -EBUSY; + } return 0; } diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c index d9939fa9b588..f929879ecae6 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c @@ -39,10 +39,18 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu) struct page *pages; int order; - order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT; - if (order < 0) - order = 0; - + /* Start at 2 because it's defined as 2^(1+PSS) */ + iommu->pasid_max = 2 << ecap_pss(iommu->ecap); + + /* Eventually I'm promised we will get a multi-level PASID table + * and it won't have to be physically contiguous. Until then, + * limit the size because 8MiB contiguous allocations can be hard + * to come by. The limit of 0x20000, which is 1MiB for each of + * the PASID and PASID-state tables, is somewhat arbitrary. */ + if (iommu->pasid_max > 0x20000) + iommu->pasid_max = 0x20000; + + order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max); pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); if (!pages) { pr_warn("IOMMU: %s: Failed to allocate PASID table\n", @@ -53,6 +61,8 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu) pr_info("%s: Allocated order %d PASID table.\n", iommu->name, order); if (ecap_dis(iommu->ecap)) { + /* Just making it explicit... */ + BUILD_BUG_ON(sizeof(struct pasid_entry) != sizeof(struct pasid_state_entry)); pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); if (pages) iommu->pasid_state_table = page_address(pages); @@ -68,11 +78,7 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu) int intel_svm_free_pasid_tables(struct intel_iommu *iommu) { - int order; - - order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT; - if (order < 0) - order = 0; + int order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max); if (iommu->pasid_table) { free_pages((unsigned long)iommu->pasid_table, order); @@ -371,8 +377,8 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_ } svm->iommu = iommu; - if (pasid_max > 2 << ecap_pss(iommu->ecap)) - pasid_max = 2 << ecap_pss(iommu->ecap); + if (pasid_max > iommu->pasid_max) + pasid_max = iommu->pasid_max; /* Do not use PASID 0 in caching mode (virtualised IOMMU) */ ret = idr_alloc(&iommu->pasid_idr, svm, diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c index 5d32a382e291..3582e206db68 100644 --- a/drivers/iommu/io-pgtable-fast.c +++ b/drivers/iommu/io-pgtable-fast.c @@ -19,6 +19,7 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/io-pgtable-fast.h> +#include <linux/mm.h> #include <asm/cacheflush.h> #include <linux/vmalloc.h> @@ -42,6 +43,9 @@ struct av8l_fast_io_pgtable { av8l_fast_iopte *puds[4]; av8l_fast_iopte *pmds; struct page **pages; /* page table memory */ + int nr_pages; + dma_addr_t base; + dma_addr_t end; }; /* Page table bits */ @@ -168,12 +172,14 @@ static void __av8l_check_for_stale_tlb(av8l_fast_iopte *ptep) } } -void av8l_fast_clear_stale_ptes(av8l_fast_iopte *pmds, bool skip_sync) +void av8l_fast_clear_stale_ptes(av8l_fast_iopte *pmds, u64 base, + u64 end, bool skip_sync) { int i; av8l_fast_iopte *pmdp = pmds; - for (i = 0; i < ((SZ_1G * 4UL) >> AV8L_FAST_PAGE_SHIFT); ++i) { + for (i = base >> AV8L_FAST_PAGE_SHIFT; + i <= (end >> AV8L_FAST_PAGE_SHIFT); ++i) { if (!(*pmdp & AV8L_FAST_PTE_VALID)) { *pmdp = 0; if (!skip_sync) @@ -224,7 +230,7 @@ static int av8l_fast_map(struct io_pgtable_ops *ops, unsigned long iova, phys_addr_t paddr, size_t size, int prot) { struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops); - av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, iova); + av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova); unsigned long nptes = size >> AV8L_FAST_PAGE_SHIFT; av8l_fast_map_public(ptep, paddr, size, prot); @@ -255,7 +261,7 @@ static size_t av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova, size_t size) { struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops); - av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, iova); + av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova); unsigned long nptes = size >> AV8L_FAST_PAGE_SHIFT; __av8l_fast_unmap(ptep, size, false); @@ -333,7 +339,7 @@ av8l_fast_alloc_pgtable_data(struct io_pgtable_cfg *cfg) } /* - * We need 1 page for the pgd, 4 pages for puds (1GB VA per pud page) and + * We need max 1 page for the pgd, 4 pages for puds (1GB VA per pud page) and * 2048 pages for pmds (each pud page contains 512 table entries, each * pointing to a pmd). */ @@ -342,12 +348,38 @@ av8l_fast_alloc_pgtable_data(struct io_pgtable_cfg *cfg) #define NUM_PMD_PAGES 2048 #define NUM_PGTBL_PAGES (NUM_PGD_PAGES + NUM_PUD_PAGES + NUM_PMD_PAGES) +/* undefine arch specific definitions which depends on page table format */ +#undef pud_index +#undef pud_mask +#undef pud_next +#undef pmd_index +#undef pmd_mask +#undef pmd_next + +#define pud_index(addr) (((addr) >> 30) & 0x3) +#define pud_mask(addr) ((addr) & ~((1UL << 30) - 1)) +#define pud_next(addr, end) \ +({ unsigned long __boundary = pud_mask(addr + (1UL << 30));\ + (__boundary - 1 < (end) - 1) ? __boundary : (end); \ +}) + +#define pmd_index(addr) (((addr) >> 21) & 0x1ff) +#define pmd_mask(addr) ((addr) & ~((1UL << 21) - 1)) +#define pmd_next(addr, end) \ +({ unsigned long __boundary = pmd_mask(addr + (1UL << 21));\ + (__boundary - 1 < (end) - 1) ? __boundary : (end); \ +}) + static int av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data, struct io_pgtable_cfg *cfg, void *cookie) { int i, j, pg = 0; struct page **pages, *page; + dma_addr_t base = cfg->iova_base; + dma_addr_t end = cfg->iova_end; + dma_addr_t pud, pmd; + int pmd_pg_index; pages = kmalloc(sizeof(*pages) * NUM_PGTBL_PAGES, __GFP_NOWARN | __GFP_NORETRY); @@ -365,10 +397,11 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data, data->pgd = page_address(page); /* - * We need 2048 entries at level 2 to map 4GB of VA space. A page - * can hold 512 entries, so we need 4 pages. + * We need max 2048 entries at level 2 to map 4GB of VA space. A page + * can hold 512 entries, so we need max 4 pages. */ - for (i = 0; i < 4; ++i) { + for (i = pud_index(base), pud = base; pud < end; + ++i, pud = pud_next(pud, end)) { av8l_fast_iopte pte, *ptep; page = alloc_page(GFP_KERNEL | __GFP_ZERO); @@ -383,18 +416,26 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data, dmac_clean_range(data->pgd, data->pgd + 4); /* - * We have 4 puds, each of which can point to 512 pmds, so we'll - * have 2048 pmds, each of which can hold 512 ptes, for a grand + * We have max 4 puds, each of which can point to 512 pmds, so we'll + * have max 2048 pmds, each of which can hold 512 ptes, for a grand * total of 2048*512=1048576 PTEs. */ - for (i = 0; i < 4; ++i) { - for (j = 0; j < 512; ++j) { + pmd_pg_index = pg; + for (i = pud_index(base), pud = base; pud < end; + ++i, pud = pud_next(pud, end)) { + for (j = pmd_index(pud), pmd = pud; pmd < pud_next(pud, end); + ++j, pmd = pmd_next(pmd, end)) { av8l_fast_iopte pte, *pudp; + void *addr; page = alloc_page(GFP_KERNEL | __GFP_ZERO); if (!page) goto err_free_pages; pages[pg++] = page; + + addr = page_address(page); + dmac_clean_range(addr, addr + SZ_4K); + pte = page_to_phys(page) | AV8L_FAST_PTE_TYPE_TABLE; pudp = data->puds[i] + j; *pudp = pte; @@ -402,21 +443,21 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data, dmac_clean_range(data->puds[i], data->puds[i] + 512); } - if (WARN_ON(pg != NUM_PGTBL_PAGES)) - goto err_free_pages; - /* * We map the pmds into a virtually contiguous space so that we * don't have to traverse the first two levels of the page tables * to find the appropriate pud. Instead, it will be a simple * offset from the virtual base of the pmds. */ - data->pmds = vmap(&pages[NUM_PGD_PAGES + NUM_PUD_PAGES], NUM_PMD_PAGES, + data->pmds = vmap(&pages[pmd_pg_index], pg - pmd_pg_index, VM_IOREMAP, PAGE_KERNEL); if (!data->pmds) goto err_free_pages; data->pages = pages; + data->nr_pages = pg; + data->base = base; + data->end = end; return 0; err_free_pages: @@ -516,7 +557,7 @@ static void av8l_fast_free_pgtable(struct io_pgtable *iop) struct av8l_fast_io_pgtable *data = iof_pgtable_to_data(iop); vunmap(data->pmds); - for (i = 0; i < NUM_PGTBL_PAGES; ++i) + for (i = 0; i < data->nr_pages; ++i) __free_page(data->pages[i]); kvfree(data->pages); kfree(data); @@ -588,6 +629,7 @@ static int __init av8l_fast_positive_testing(void) struct av8l_fast_io_pgtable *data; av8l_fast_iopte *pmds; u64 max = SZ_1G * 4ULL - 1; + u64 base = 0; cfg = (struct io_pgtable_cfg) { .quirks = 0, @@ -595,6 +637,8 @@ static int __init av8l_fast_positive_testing(void) .ias = 32, .oas = 32, .pgsize_bitmap = SZ_4K, + .iova_base = base, + .iova_end = max, }; cfg_cookie = &cfg; @@ -607,81 +651,81 @@ static int __init av8l_fast_positive_testing(void) pmds = data->pmds; /* map the entire 4GB VA space with 4K map calls */ - for (iova = 0; iova < max; iova += SZ_4K) { + for (iova = base; iova < max; iova += SZ_4K) { if (WARN_ON(ops->map(ops, iova, iova, SZ_4K, IOMMU_READ))) { failed++; continue; } } - if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0, - max))) + if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base, + base, max - base))) failed++; /* unmap it all */ - for (iova = 0; iova < max; iova += SZ_4K) { + for (iova = base; iova < max; iova += SZ_4K) { if (WARN_ON(ops->unmap(ops, iova, SZ_4K) != SZ_4K)) failed++; } /* sweep up TLB proving PTEs */ - av8l_fast_clear_stale_ptes(pmds, false); + av8l_fast_clear_stale_ptes(pmds, base, max, false); /* map the entire 4GB VA space with 8K map calls */ - for (iova = 0; iova < max; iova += SZ_8K) { + for (iova = base; iova < max; iova += SZ_8K) { if (WARN_ON(ops->map(ops, iova, iova, SZ_8K, IOMMU_READ))) { failed++; continue; } } - if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0, - max))) + if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base, + base, max - base))) failed++; /* unmap it all with 8K unmap calls */ - for (iova = 0; iova < max; iova += SZ_8K) { + for (iova = base; iova < max; iova += SZ_8K) { if (WARN_ON(ops->unmap(ops, iova, SZ_8K) != SZ_8K)) failed++; } /* sweep up TLB proving PTEs */ - av8l_fast_clear_stale_ptes(pmds, false); + av8l_fast_clear_stale_ptes(pmds, base, max, false); /* map the entire 4GB VA space with 16K map calls */ - for (iova = 0; iova < max; iova += SZ_16K) { + for (iova = base; iova < max; iova += SZ_16K) { if (WARN_ON(ops->map(ops, iova, iova, SZ_16K, IOMMU_READ))) { failed++; continue; } } - if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0, - max))) + if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base, + base, max - base))) failed++; /* unmap it all */ - for (iova = 0; iova < max; iova += SZ_16K) { + for (iova = base; iova < max; iova += SZ_16K) { if (WARN_ON(ops->unmap(ops, iova, SZ_16K) != SZ_16K)) failed++; } /* sweep up TLB proving PTEs */ - av8l_fast_clear_stale_ptes(pmds, false); + av8l_fast_clear_stale_ptes(pmds, base, max, false); /* map the entire 4GB VA space with 64K map calls */ - for (iova = 0; iova < max; iova += SZ_64K) { + for (iova = base; iova < max; iova += SZ_64K) { if (WARN_ON(ops->map(ops, iova, iova, SZ_64K, IOMMU_READ))) { failed++; continue; } } - if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0, - max))) + if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base, + base, max - base))) failed++; /* unmap it all at once */ - if (WARN_ON(ops->unmap(ops, 0, max) != max)) + if (WARN_ON(ops->unmap(ops, base, max - base) != (max - base))) failed++; free_io_pgtable_ops(ops); diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h index f4533040806f..e6939c2212d4 100644 --- a/drivers/iommu/io-pgtable.h +++ b/drivers/iommu/io-pgtable.h @@ -69,6 +69,8 @@ struct io_pgtable_cfg { unsigned int oas; const struct iommu_gather_ops *tlb; struct device *iommu_dev; + dma_addr_t iova_base; + dma_addr_t iova_end; /* Low-level data specific to the table format */ union { diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c index 8a0c7f288198..981c3959da59 100644 --- a/drivers/irqchip/irq-atmel-aic.c +++ b/drivers/irqchip/irq-atmel-aic.c @@ -176,6 +176,7 @@ static int aic_irq_domain_xlate(struct irq_domain *d, { struct irq_domain_chip_generic *dgc = d->gc; struct irq_chip_generic *gc; + unsigned long flags; unsigned smr; int idx; int ret; @@ -194,12 +195,12 @@ static int aic_irq_domain_xlate(struct irq_domain *d, gc = dgc->gc[idx]; - irq_gc_lock(gc); + irq_gc_lock_irqsave(gc, flags); smr = irq_reg_readl(gc, AT91_AIC_SMR(*out_hwirq)); ret = aic_common_set_priority(intspec[2], &smr); if (!ret) irq_reg_writel(gc, smr, AT91_AIC_SMR(*out_hwirq)); - irq_gc_unlock(gc); + irq_gc_unlock_irqrestore(gc, flags); return ret; } diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c index 62bb840c613f..7dee71bde350 100644 --- a/drivers/irqchip/irq-atmel-aic5.c +++ b/drivers/irqchip/irq-atmel-aic5.c @@ -258,6 +258,7 @@ static int aic5_irq_domain_xlate(struct irq_domain *d, unsigned int *out_type) { struct irq_chip_generic *bgc = irq_get_domain_generic_chip(d, 0); + unsigned long flags; unsigned smr; int ret; @@ -269,13 +270,13 @@ static int aic5_irq_domain_xlate(struct irq_domain *d, if (ret) return ret; - irq_gc_lock(bgc); + irq_gc_lock_irqsave(bgc, flags); irq_reg_writel(bgc, *out_hwirq, AT91_AIC5_SSR); smr = irq_reg_readl(bgc, AT91_AIC5_SMR); ret = aic_common_set_priority(intspec[2], &smr); if (!ret) irq_reg_writel(bgc, intspec[2] | smr, AT91_AIC5_SMR); - irq_gc_unlock(bgc); + irq_gc_unlock_irqrestore(bgc, flags); return ret; } diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index cfdc235c1d28..c40a9f3b0724 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -154,7 +154,7 @@ static void gic_enable_redist(bool enable) return; /* No PM support in this redistributor */ } - while (count--) { + while (--count) { val = readl_relaxed(rbase + GICR_WAKER); if (enable ^ (val & GICR_WAKER_ChildrenAsleep)) break; @@ -702,7 +702,7 @@ static struct notifier_block gic_cpu_notifier = { static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask, unsigned long cluster_id) { - int cpu = *base_cpu; + int next_cpu, cpu = *base_cpu; unsigned long mpidr = cpu_logical_map(cpu); u16 tlist = 0; @@ -716,9 +716,10 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask, tlist |= 1 << (mpidr & 0xf); - cpu = cpumask_next(cpu, mask); - if (cpu >= nr_cpu_ids) + next_cpu = cpumask_next(cpu, mask); + if (next_cpu >= nr_cpu_ids) goto out; + cpu = next_cpu; mpidr = cpu_logical_map(cpu); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 966227a3df1a..620268b63b2a 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -596,18 +596,9 @@ config LEDS_QPNP LEDs in both PWM and light pattern generator (LPG) modes. For older PMICs, it also supports WLEDs and flash LEDs. -config LEDS_QPNP_FLASH - tristate "Support for QPNP Flash LEDs" - depends on LEDS_CLASS && SPMI - help - This driver supports the flash LED functionality of Qualcomm - Technologies, Inc. QPNP PMICs. This driver supports PMICs up through - PM8994. It can configure the flash LED target current for several - independent channels. - config LEDS_QPNP_FLASH_V2 tristate "Support for QPNP V2 Flash LEDs" - depends on LEDS_CLASS && MFD_SPMI_PMIC && !LEDS_QPNP_FLASH + depends on LEDS_CLASS && MFD_SPMI_PMIC help This driver supports the flash V2 LED functionality of Qualcomm Technologies, Inc. QPNP PMICs. This driver supports PMICs starting diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 8d8ba9175810..aa5ba0cf4de6 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -61,7 +61,6 @@ obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o obj-$(CONFIG_LEDS_QPNP) += leds-qpnp.o -obj-$(CONFIG_LEDS_QPNP_FLASH) += leds-qpnp-flash.o obj-$(CONFIG_LEDS_QPNP_FLASH_V2) += leds-qpnp-flash-v2.o obj-$(CONFIG_LEDS_QPNP_WLED) += leds-qpnp-wled.o obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c deleted file mode 100644 index cd76941b87ca..000000000000 --- a/drivers/leds/leds-qpnp-flash.c +++ /dev/null @@ -1,2683 +0,0 @@ -/* 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 - * 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/module.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/regmap.h> -#include <linux/errno.h> -#include <linux/leds.h> -#include <linux/slab.h> -#include <linux/of_device.h> -#include <linux/spmi.h> -#include <linux/platform_device.h> -#include <linux/err.h> -#include <linux/delay.h> -#include <linux/of.h> -#include <linux/regulator/consumer.h> -#include <linux/workqueue.h> -#include <linux/power_supply.h> -#include <linux/leds-qpnp-flash.h> -#include <linux/qpnp/qpnp-adc.h> -#include <linux/qpnp/qpnp-revid.h> -#include <linux/debugfs.h> -#include <linux/uaccess.h> -#include "leds.h" - -#define FLASH_LED_PERIPHERAL_SUBTYPE(base) (base + 0x05) -#define FLASH_SAFETY_TIMER(base) (base + 0x40) -#define FLASH_MAX_CURRENT(base) (base + 0x41) -#define FLASH_LED0_CURRENT(base) (base + 0x42) -#define FLASH_LED1_CURRENT(base) (base + 0x43) -#define FLASH_CLAMP_CURRENT(base) (base + 0x44) -#define FLASH_MODULE_ENABLE_CTRL(base) (base + 0x46) -#define FLASH_LED_STROBE_CTRL(base) (base + 0x47) -#define FLASH_LED_TMR_CTRL(base) (base + 0x48) -#define FLASH_HEADROOM(base) (base + 0x4A) -#define FLASH_STARTUP_DELAY(base) (base + 0x4B) -#define FLASH_MASK_ENABLE(base) (base + 0x4C) -#define FLASH_VREG_OK_FORCE(base) (base + 0x4F) -#define FLASH_FAULT_DETECT(base) (base + 0x51) -#define FLASH_THERMAL_DRATE(base) (base + 0x52) -#define FLASH_CURRENT_RAMP(base) (base + 0x54) -#define FLASH_VPH_PWR_DROOP(base) (base + 0x5A) -#define FLASH_HDRM_SNS_ENABLE_CTRL0(base) (base + 0x5C) -#define FLASH_HDRM_SNS_ENABLE_CTRL1(base) (base + 0x5D) -#define FLASH_LED_UNLOCK_SECURE(base) (base + 0xD0) -#define FLASH_PERPH_RESET_CTRL(base) (base + 0xDA) -#define FLASH_TORCH(base) (base + 0xE4) - -#define FLASH_STATUS_REG_MASK 0xFF -#define FLASH_LED_FAULT_STATUS(base) (base + 0x08) -#define INT_LATCHED_STS(base) (base + 0x18) -#define IN_POLARITY_HIGH(base) (base + 0x12) -#define INT_SET_TYPE(base) (base + 0x11) -#define INT_EN_SET(base) (base + 0x15) -#define INT_LATCHED_CLR(base) (base + 0x14) - -#define FLASH_HEADROOM_MASK 0x03 -#define FLASH_STARTUP_DLY_MASK 0x03 -#define FLASH_VREG_OK_FORCE_MASK 0xC0 -#define FLASH_FAULT_DETECT_MASK 0x80 -#define FLASH_THERMAL_DERATE_MASK 0xBF -#define FLASH_SECURE_MASK 0xFF -#define FLASH_TORCH_MASK 0x03 -#define FLASH_CURRENT_MASK 0x7F -#define FLASH_TMR_MASK 0x03 -#define FLASH_TMR_SAFETY 0x00 -#define FLASH_SAFETY_TIMER_MASK 0x7F -#define FLASH_MODULE_ENABLE_MASK 0xE0 -#define FLASH_STROBE_MASK 0xC0 -#define FLASH_CURRENT_RAMP_MASK 0xBF -#define FLASH_VPH_PWR_DROOP_MASK 0xF3 -#define FLASH_LED_HDRM_SNS_ENABLE_MASK 0x81 -#define FLASH_MASK_MODULE_CONTRL_MASK 0xE0 -#define FLASH_FOLLOW_OTST2_RB_MASK 0x08 - -#define FLASH_LED_TRIGGER_DEFAULT "none" -#define FLASH_LED_HEADROOM_DEFAULT_MV 500 -#define FLASH_LED_STARTUP_DELAY_DEFAULT_US 128 -#define FLASH_LED_CLAMP_CURRENT_DEFAULT_MA 200 -#define FLASH_LED_THERMAL_DERATE_THRESHOLD_DEFAULT_C 80 -#define FLASH_LED_RAMP_UP_STEP_DEFAULT_US 3 -#define FLASH_LED_RAMP_DN_STEP_DEFAULT_US 3 -#define FLASH_LED_VPH_PWR_DROOP_THRESHOLD_DEFAULT_MV 3200 -#define FLASH_LED_VPH_PWR_DROOP_DEBOUNCE_TIME_DEFAULT_US 10 -#define FLASH_LED_THERMAL_DERATE_RATE_DEFAULT_PERCENT 2 -#define FLASH_RAMP_UP_DELAY_US_MIN 1000 -#define FLASH_RAMP_UP_DELAY_US_MAX 1001 -#define FLASH_RAMP_DN_DELAY_US_MIN 2160 -#define FLASH_RAMP_DN_DELAY_US_MAX 2161 -#define FLASH_BOOST_REGULATOR_PROBE_DELAY_MS 2000 -#define FLASH_TORCH_MAX_LEVEL 0x0F -#define FLASH_MAX_LEVEL 0x4F -#define FLASH_LED_FLASH_HW_VREG_OK 0x40 -#define FLASH_LED_FLASH_SW_VREG_OK 0x80 -#define FLASH_LED_STROBE_TYPE_HW 0x04 -#define FLASH_DURATION_DIVIDER 10 -#define FLASH_LED_HEADROOM_DIVIDER 100 -#define FLASH_LED_HEADROOM_OFFSET 2 -#define FLASH_LED_MAX_CURRENT_MA 1000 -#define FLASH_LED_THERMAL_THRESHOLD_MIN 95 -#define FLASH_LED_THERMAL_DEVIDER 10 -#define FLASH_LED_VPH_DROOP_THRESHOLD_MIN_MV 2500 -#define FLASH_LED_VPH_DROOP_THRESHOLD_DIVIDER 100 -#define FLASH_LED_HDRM_SNS_ENABLE 0x81 -#define FLASH_LED_HDRM_SNS_DISABLE 0x01 -#define FLASH_LED_UA_PER_MA 1000 -#define FLASH_LED_MASK_MODULE_MASK2_ENABLE 0x20 -#define FLASH_LED_MASK3_ENABLE_SHIFT 7 -#define FLASH_LED_MODULE_CTRL_DEFAULT 0x60 -#define FLASH_LED_CURRENT_READING_DELAY_MIN 5000 -#define FLASH_LED_CURRENT_READING_DELAY_MAX 5001 -#define FLASH_LED_OPEN_FAULT_DETECTED 0xC - -#define FLASH_UNLOCK_SECURE 0xA5 -#define FLASH_LED_TORCH_ENABLE 0x00 -#define FLASH_LED_TORCH_DISABLE 0x03 -#define FLASH_MODULE_ENABLE 0x80 -#define FLASH_LED0_TRIGGER 0x80 -#define FLASH_LED1_TRIGGER 0x40 -#define FLASH_LED0_ENABLEMENT 0x40 -#define FLASH_LED1_ENABLEMENT 0x20 -#define FLASH_LED_DISABLE 0x00 -#define FLASH_LED_MIN_CURRENT_MA 13 -#define FLASH_SUBTYPE_DUAL 0x01 -#define FLASH_SUBTYPE_SINGLE 0x02 - -/* - * ID represents physical LEDs for individual control purpose. - */ -enum flash_led_id { - FLASH_LED_0 = 0, - FLASH_LED_1, - FLASH_LED_SWITCH, -}; - -enum flash_led_type { - FLASH = 0, - TORCH, - SWITCH, -}; - -enum thermal_derate_rate { - RATE_1_PERCENT = 0, - RATE_1P25_PERCENT, - RATE_2_PERCENT, - RATE_2P5_PERCENT, - RATE_5_PERCENT, -}; - -enum current_ramp_steps { - RAMP_STEP_0P2_US = 0, - RAMP_STEP_0P4_US, - RAMP_STEP_0P8_US, - RAMP_STEP_1P6_US, - RAMP_STEP_3P3_US, - RAMP_STEP_6P7_US, - RAMP_STEP_13P5_US, - RAMP_STEP_27US, -}; - -struct flash_regulator_data { - struct regulator *regs; - const char *reg_name; - u32 max_volt_uv; -}; - -/* - * Configurations for each individual LED - */ -struct flash_node_data { - struct platform_device *pdev; - struct regmap *regmap; - struct led_classdev cdev; - struct work_struct work; - struct flash_regulator_data *reg_data; - u16 max_current; - u16 prgm_current; - u16 prgm_current2; - u16 duration; - u8 id; - u8 type; - u8 trigger; - u8 enable; - u8 num_regulators; - bool flash_on; -}; - -/* - * Flash LED configuration read from device tree - */ -struct flash_led_platform_data { - unsigned int temp_threshold_num; - unsigned int temp_derate_curr_num; - unsigned int *die_temp_derate_curr_ma; - unsigned int *die_temp_threshold_degc; - u16 ramp_up_step; - u16 ramp_dn_step; - u16 vph_pwr_droop_threshold; - u16 headroom; - u16 clamp_current; - u8 thermal_derate_threshold; - u8 vph_pwr_droop_debounce_time; - u8 startup_dly; - u8 thermal_derate_rate; - bool pmic_charger_support; - bool self_check_en; - bool thermal_derate_en; - bool current_ramp_en; - bool vph_pwr_droop_en; - bool hdrm_sns_ch0_en; - bool hdrm_sns_ch1_en; - bool power_detect_en; - bool mask3_en; - bool follow_rb_disable; - bool die_current_derate_en; -}; - -struct qpnp_flash_led_buffer { - struct mutex debugfs_lock; /* Prevent thread concurrency */ - size_t rpos; - size_t wpos; - size_t len; - char data[0]; -}; - -/* - * Flash LED data structure containing flash LED attributes - */ -struct qpnp_flash_led { - struct pmic_revid_data *revid_data; - struct platform_device *pdev; - struct regmap *regmap; - struct flash_led_platform_data *pdata; - struct pinctrl *pinctrl; - struct pinctrl_state *gpio_state_active; - struct pinctrl_state *gpio_state_suspend; - struct flash_node_data *flash_node; - struct power_supply *battery_psy; - struct workqueue_struct *ordered_workq; - struct qpnp_vadc_chip *vadc_dev; - struct mutex flash_led_lock; - struct qpnp_flash_led_buffer *log; - struct dentry *dbgfs_root; - int num_leds; - u32 buffer_cnt; - u16 base; - u16 current_addr; - u16 current2_addr; - u8 peripheral_type; - u8 fault_reg; - bool gpio_enabled; - bool charging_enabled; - bool strobe_debug; - bool dbg_feature_en; - bool open_fault; -}; - -static u8 qpnp_flash_led_ctrl_dbg_regs[] = { - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x4A, 0x4B, 0x4C, 0x4F, 0x51, 0x52, 0x54, 0x55, 0x5A, 0x5C, 0x5D, -}; - -static int flash_led_dbgfs_file_open(struct qpnp_flash_led *led, - struct file *file) -{ - struct qpnp_flash_led_buffer *log; - size_t logbufsize = SZ_4K; - - log = kzalloc(logbufsize, GFP_KERNEL); - if (!log) - return -ENOMEM; - - log->rpos = 0; - log->wpos = 0; - log->len = logbufsize - sizeof(*log); - mutex_init(&log->debugfs_lock); - led->log = log; - - led->buffer_cnt = 1; - file->private_data = led; - - return 0; -} - -static int flash_led_dfs_open(struct inode *inode, struct file *file) -{ - struct qpnp_flash_led *led = inode->i_private; - - return flash_led_dbgfs_file_open(led, file); -} - -static int flash_led_dfs_close(struct inode *inode, struct file *file) -{ - struct qpnp_flash_led *led = file->private_data; - - if (led && led->log) { - file->private_data = NULL; - mutex_destroy(&led->log->debugfs_lock); - kfree(led->log); - } - - return 0; -} - -#define MIN_BUFFER_WRITE_LEN 20 -static int print_to_log(struct qpnp_flash_led_buffer *log, - const char *fmt, ...) -{ - va_list args; - int cnt; - char *log_buf; - size_t size = log->len - log->wpos; - - if (size < MIN_BUFFER_WRITE_LEN) - return 0; /* not enough buffer left */ - - log_buf = &log->data[log->wpos]; - va_start(args, fmt); - cnt = vscnprintf(log_buf, size, fmt, args); - va_end(args); - - log->wpos += cnt; - return cnt; -} - -static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf, - size_t count, loff_t *ppos) { - struct qpnp_flash_led *led = fp->private_data; - struct qpnp_flash_led_buffer *log = led->log; - uint val; - int rc = 0; - size_t len; - size_t ret; - - mutex_lock(&log->debugfs_lock); - if ((log->rpos >= log->wpos && led->buffer_cnt == 0) || - ((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN)) - goto unlock_mutex; - - rc = regmap_read(led->regmap, INT_LATCHED_STS(led->base), &val); - if (rc) { - dev_err(&led->pdev->dev, - "Unable to read from address %x, rc(%d)\n", - INT_LATCHED_STS(led->base), rc); - goto unlock_mutex; - } - led->buffer_cnt--; - - rc = print_to_log(log, "0x%05X ", INT_LATCHED_STS(led->base)); - if (rc == 0) - goto unlock_mutex; - - rc = print_to_log(log, "0x%02X ", val); - if (rc == 0) - goto unlock_mutex; - - if (log->wpos > 0 && log->data[log->wpos - 1] == ' ') - log->data[log->wpos - 1] = '\n'; - - len = min(count, log->wpos - log->rpos); - - ret = copy_to_user(buf, &log->data[log->rpos], len); - if (ret) { - pr_err("error copy register value to user\n"); - rc = -EFAULT; - goto unlock_mutex; - } - - len -= ret; - *ppos += len; - log->rpos += len; - - rc = len; - -unlock_mutex: - mutex_unlock(&log->debugfs_lock); - return rc; -} - -static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf, - size_t count, loff_t *ppos) { - struct qpnp_flash_led *led = fp->private_data; - struct qpnp_flash_led_buffer *log = led->log; - int rc = 0; - size_t len; - size_t ret; - - mutex_lock(&log->debugfs_lock); - if ((log->rpos >= log->wpos && led->buffer_cnt == 0) || - ((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN)) - goto unlock_mutex; - - led->buffer_cnt--; - - rc = print_to_log(log, "0x%05X ", FLASH_LED_FAULT_STATUS(led->base)); - if (rc == 0) - goto unlock_mutex; - - rc = print_to_log(log, "0x%02X ", led->fault_reg); - if (rc == 0) - goto unlock_mutex; - - if (log->wpos > 0 && log->data[log->wpos - 1] == ' ') - log->data[log->wpos - 1] = '\n'; - - len = min(count, log->wpos - log->rpos); - - ret = copy_to_user(buf, &log->data[log->rpos], len); - if (ret) { - pr_err("error copy register value to user\n"); - rc = -EFAULT; - goto unlock_mutex; - } - - len -= ret; - *ppos += len; - log->rpos += len; - - rc = len; - -unlock_mutex: - mutex_unlock(&log->debugfs_lock); - return rc; -} - -static ssize_t flash_led_dfs_fault_reg_enable(struct file *file, - const char __user *buf, size_t count, loff_t *ppos) { - - u8 *val; - int pos = 0; - int cnt = 0; - int data; - size_t ret = 0; - - struct qpnp_flash_led *led = file->private_data; - char *kbuf; - - mutex_lock(&led->log->debugfs_lock); - kbuf = kmalloc(count + 1, GFP_KERNEL); - if (!kbuf) { - ret = -ENOMEM; - goto unlock_mutex; - } - - ret = copy_from_user(kbuf, buf, count); - if (!ret) { - pr_err("failed to copy data from user\n"); - ret = -EFAULT; - goto free_buf; - } - - count -= ret; - *ppos += count; - kbuf[count] = '\0'; - val = kbuf; - while (sscanf(kbuf + pos, "%i", &data) == 1) { - pos++; - val[cnt++] = data & 0xff; - } - - if (!cnt) - goto free_buf; - - ret = count; - if (*val == 1) - led->strobe_debug = true; - else - led->strobe_debug = false; - -free_buf: - kfree(kbuf); -unlock_mutex: - mutex_unlock(&led->log->debugfs_lock); - return ret; -} - -static ssize_t flash_led_dfs_dbg_enable(struct file *file, - const char __user *buf, size_t count, loff_t *ppos) { - - u8 *val; - int pos = 0; - int cnt = 0; - int data; - size_t ret = 0; - struct qpnp_flash_led *led = file->private_data; - char *kbuf; - - mutex_lock(&led->log->debugfs_lock); - kbuf = kmalloc(count + 1, GFP_KERNEL); - if (!kbuf) { - ret = -ENOMEM; - goto unlock_mutex; - } - - ret = copy_from_user(kbuf, buf, count); - if (ret == count) { - pr_err("failed to copy data from user\n"); - ret = -EFAULT; - goto free_buf; - } - count -= ret; - *ppos += count; - kbuf[count] = '\0'; - val = kbuf; - while (sscanf(kbuf + pos, "%i", &data) == 1) { - pos++; - val[cnt++] = data & 0xff; - } - - if (!cnt) - goto free_buf; - - ret = count; - if (*val == 1) - led->dbg_feature_en = true; - else - led->dbg_feature_en = false; - -free_buf: - kfree(kbuf); -unlock_mutex: - mutex_unlock(&led->log->debugfs_lock); - return ret; -} - -static const struct file_operations flash_led_dfs_latched_reg_fops = { - .open = flash_led_dfs_open, - .release = flash_led_dfs_close, - .read = flash_led_dfs_latched_reg_read, -}; - -static const struct file_operations flash_led_dfs_strobe_reg_fops = { - .open = flash_led_dfs_open, - .release = flash_led_dfs_close, - .read = flash_led_dfs_fault_reg_read, - .write = flash_led_dfs_fault_reg_enable, -}; - -static const struct file_operations flash_led_dfs_dbg_feature_fops = { - .open = flash_led_dfs_open, - .release = flash_led_dfs_close, - .write = flash_led_dfs_dbg_enable, -}; - -static int -qpnp_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask, u8 val) -{ - int rc; - - rc = regmap_update_bits(led->regmap, addr, mask, val); - if (rc) - dev_err(&led->pdev->dev, - "Unable to update_bits to addr=%x, rc(%d)\n", addr, rc); - - dev_dbg(&led->pdev->dev, "Write 0x%02X to addr 0x%02X\n", val, addr); - - return rc; -} - -static int qpnp_flash_led_get_allowed_die_temp_curr(struct qpnp_flash_led *led, - int64_t die_temp_degc) -{ - int die_temp_curr_ma; - - if (die_temp_degc >= led->pdata->die_temp_threshold_degc[0]) - die_temp_curr_ma = 0; - else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[1]) - die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[0]; - else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[2]) - die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[1]; - else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[3]) - die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[2]; - else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[4]) - die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[3]; - else - die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[4]; - - return die_temp_curr_ma; -} - -static int64_t qpnp_flash_led_get_die_temp(struct qpnp_flash_led *led) -{ - struct qpnp_vadc_result die_temp_result; - int rc; - - rc = qpnp_vadc_read(led->vadc_dev, SPARE2, &die_temp_result); - if (rc) { - pr_err("failed to read the die temp\n"); - return -EINVAL; - } - - return die_temp_result.physical; -} - -static int qpnp_get_pmic_revid(struct qpnp_flash_led *led) -{ - struct device_node *revid_dev_node; - - revid_dev_node = of_parse_phandle(led->pdev->dev.of_node, - "qcom,pmic-revid", 0); - if (!revid_dev_node) { - dev_err(&led->pdev->dev, - "qcom,pmic-revid property missing\n"); - return -EINVAL; - } - - led->revid_data = get_revid_data(revid_dev_node); - if (IS_ERR(led->revid_data)) { - pr_err("Couldn't get revid data rc = %ld\n", - PTR_ERR(led->revid_data)); - return PTR_ERR(led->revid_data); - } - - return 0; -} - -static int -qpnp_flash_led_get_max_avail_current(struct flash_node_data *flash_node, - struct qpnp_flash_led *led) -{ - union power_supply_propval prop; - int64_t chg_temp_milidegc, die_temp_degc; - int max_curr_avail_ma = 2000; - int allowed_die_temp_curr_ma = 2000; - int rc; - - if (led->pdata->power_detect_en) { - if (!led->battery_psy) { - dev_err(&led->pdev->dev, - "Failed to query power supply\n"); - return -EINVAL; - } - - /* - * When charging is enabled, enforce this new enablement - * sequence to reduce fuel gauge reading resolution. - */ - if (led->charging_enabled) { - rc = qpnp_led_masked_write(led, - FLASH_MODULE_ENABLE_CTRL(led->base), - FLASH_MODULE_ENABLE, FLASH_MODULE_ENABLE); - if (rc) { - dev_err(&led->pdev->dev, - "Module enable reg write failed\n"); - return -EINVAL; - } - - usleep_range(FLASH_LED_CURRENT_READING_DELAY_MIN, - FLASH_LED_CURRENT_READING_DELAY_MAX); - } - - power_supply_get_property(led->battery_psy, - POWER_SUPPLY_PROP_FLASH_CURRENT_MAX, &prop); - if (!prop.intval) { - dev_err(&led->pdev->dev, - "battery too low for flash\n"); - return -EINVAL; - } - - max_curr_avail_ma = (prop.intval / FLASH_LED_UA_PER_MA); - } - - /* - * When thermal mitigation is available, this logic will execute to - * derate current based upon the PMIC die temperature. - */ - if (led->pdata->die_current_derate_en) { - chg_temp_milidegc = qpnp_flash_led_get_die_temp(led); - if (chg_temp_milidegc < 0) - return -EINVAL; - - die_temp_degc = div_s64(chg_temp_milidegc, 1000); - allowed_die_temp_curr_ma = - qpnp_flash_led_get_allowed_die_temp_curr(led, - die_temp_degc); - if (allowed_die_temp_curr_ma < 0) - return -EINVAL; - } - - max_curr_avail_ma = (max_curr_avail_ma >= allowed_die_temp_curr_ma) - ? allowed_die_temp_curr_ma : max_curr_avail_ma; - - return max_curr_avail_ma; -} - -static ssize_t qpnp_flash_led_die_temp_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct qpnp_flash_led *led; - struct flash_node_data *flash_node; - unsigned long val; - struct led_classdev *led_cdev = dev_get_drvdata(dev); - ssize_t ret; - - ret = kstrtoul(buf, 10, &val); - if (ret) - return ret; - - flash_node = container_of(led_cdev, struct flash_node_data, cdev); - led = dev_get_drvdata(&flash_node->pdev->dev); - - /*'0' for disable die_temp feature; non-zero to enable feature*/ - if (val == 0) - led->pdata->die_current_derate_en = false; - else - led->pdata->die_current_derate_en = true; - - return count; -} - -static ssize_t qpnp_led_strobe_type_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct flash_node_data *flash_node; - unsigned long state; - struct led_classdev *led_cdev = dev_get_drvdata(dev); - ssize_t ret = -EINVAL; - - ret = kstrtoul(buf, 10, &state); - if (ret) - return ret; - - flash_node = container_of(led_cdev, struct flash_node_data, cdev); - - /* '0' for sw strobe; '1' for hw strobe */ - if (state == 1) - flash_node->trigger |= FLASH_LED_STROBE_TYPE_HW; - else - flash_node->trigger &= ~FLASH_LED_STROBE_TYPE_HW; - - return count; -} - -static ssize_t qpnp_flash_led_dump_regs_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct qpnp_flash_led *led; - struct flash_node_data *flash_node; - struct led_classdev *led_cdev = dev_get_drvdata(dev); - int rc, i, count = 0; - u16 addr; - uint val; - - flash_node = container_of(led_cdev, struct flash_node_data, cdev); - led = dev_get_drvdata(&flash_node->pdev->dev); - for (i = 0; i < ARRAY_SIZE(qpnp_flash_led_ctrl_dbg_regs); i++) { - addr = led->base + qpnp_flash_led_ctrl_dbg_regs[i]; - rc = regmap_read(led->regmap, addr, &val); - if (rc) { - dev_err(&led->pdev->dev, - "Unable to read from addr=%x, rc(%d)\n", - addr, rc); - return -EINVAL; - } - - count += snprintf(buf + count, PAGE_SIZE - count, - "REG_0x%x = 0x%02x\n", addr, val); - - if (count >= PAGE_SIZE) - return PAGE_SIZE - 1; - } - - return count; -} - -static ssize_t qpnp_flash_led_current_derate_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct qpnp_flash_led *led; - struct flash_node_data *flash_node; - unsigned long val; - struct led_classdev *led_cdev = dev_get_drvdata(dev); - ssize_t ret; - - ret = kstrtoul(buf, 10, &val); - if (ret) - return ret; - - flash_node = container_of(led_cdev, struct flash_node_data, cdev); - led = dev_get_drvdata(&flash_node->pdev->dev); - - /*'0' for disable derate feature; non-zero to enable derate feature */ - if (val == 0) - led->pdata->power_detect_en = false; - else - led->pdata->power_detect_en = true; - - return count; -} - -static ssize_t qpnp_flash_led_max_current_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct qpnp_flash_led *led; - struct flash_node_data *flash_node; - struct led_classdev *led_cdev = dev_get_drvdata(dev); - int max_curr_avail_ma = 0; - - flash_node = container_of(led_cdev, struct flash_node_data, cdev); - led = dev_get_drvdata(&flash_node->pdev->dev); - - if (led->flash_node[0].flash_on) - max_curr_avail_ma += led->flash_node[0].max_current; - if (led->flash_node[1].flash_on) - max_curr_avail_ma += led->flash_node[1].max_current; - - if (led->pdata->power_detect_en || - led->pdata->die_current_derate_en) { - max_curr_avail_ma = - qpnp_flash_led_get_max_avail_current(flash_node, led); - - if (max_curr_avail_ma < 0) - return -EINVAL; - } - - return snprintf(buf, PAGE_SIZE, "%u\n", max_curr_avail_ma); -} - -static struct device_attribute qpnp_flash_led_attrs[] = { - __ATTR(strobe, 0664, NULL, qpnp_led_strobe_type_store), - __ATTR(reg_dump, 0664, qpnp_flash_led_dump_regs_show, NULL), - __ATTR(enable_current_derate, 0664, NULL, - qpnp_flash_led_current_derate_store), - __ATTR(max_allowed_current, 0664, qpnp_flash_led_max_current_show, - NULL), - __ATTR(enable_die_temp_current_derate, 0664, NULL, - qpnp_flash_led_die_temp_store), -}; - -static int qpnp_flash_led_get_thermal_derate_rate(const char *rate) -{ - /* - * return 5% derate as default value if user specifies - * a value un-supported - */ - if (strcmp(rate, "1_PERCENT") == 0) - return RATE_1_PERCENT; - else if (strcmp(rate, "1P25_PERCENT") == 0) - return RATE_1P25_PERCENT; - else if (strcmp(rate, "2_PERCENT") == 0) - return RATE_2_PERCENT; - else if (strcmp(rate, "2P5_PERCENT") == 0) - return RATE_2P5_PERCENT; - else if (strcmp(rate, "5_PERCENT") == 0) - return RATE_5_PERCENT; - else - return RATE_5_PERCENT; -} - -static int qpnp_flash_led_get_ramp_step(const char *step) -{ - /* - * return 27 us as default value if user specifies - * a value un-supported - */ - if (strcmp(step, "0P2_US") == 0) - return RAMP_STEP_0P2_US; - else if (strcmp(step, "0P4_US") == 0) - return RAMP_STEP_0P4_US; - else if (strcmp(step, "0P8_US") == 0) - return RAMP_STEP_0P8_US; - else if (strcmp(step, "1P6_US") == 0) - return RAMP_STEP_1P6_US; - else if (strcmp(step, "3P3_US") == 0) - return RAMP_STEP_3P3_US; - else if (strcmp(step, "6P7_US") == 0) - return RAMP_STEP_6P7_US; - else if (strcmp(step, "13P5_US") == 0) - return RAMP_STEP_13P5_US; - else - return RAMP_STEP_27US; -} - -static u8 qpnp_flash_led_get_droop_debounce_time(u8 val) -{ - /* - * return 10 us as default value if user specifies - * a value un-supported - */ - switch (val) { - case 0: - return 0; - case 10: - return 1; - case 32: - return 2; - case 64: - return 3; - default: - return 1; - } -} - -static u8 qpnp_flash_led_get_startup_dly(u8 val) -{ - /* - * return 128 us as default value if user specifies - * a value un-supported - */ - switch (val) { - case 10: - return 0; - case 32: - return 1; - case 64: - return 2; - case 128: - return 3; - default: - return 3; - } -} - -static int -qpnp_flash_led_get_peripheral_type(struct qpnp_flash_led *led) -{ - int rc; - uint val; - - rc = regmap_read(led->regmap, - FLASH_LED_PERIPHERAL_SUBTYPE(led->base), &val); - if (rc) { - dev_err(&led->pdev->dev, - "Unable to read peripheral subtype\n"); - return -EINVAL; - } - - return val; -} - -static int qpnp_flash_led_module_disable(struct qpnp_flash_led *led, - struct flash_node_data *flash_node) -{ - union power_supply_propval psy_prop; - int rc; - uint val, tmp; - - rc = regmap_read(led->regmap, FLASH_LED_STROBE_CTRL(led->base), &val); - if (rc) { - dev_err(&led->pdev->dev, "Unable to read strobe reg\n"); - return -EINVAL; - } - - tmp = (~flash_node->trigger) & val; - if (!tmp) { - if (flash_node->type == TORCH) { - rc = qpnp_led_masked_write(led, - FLASH_LED_UNLOCK_SECURE(led->base), - FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE); - if (rc) { - dev_err(&led->pdev->dev, - "Secure reg write failed\n"); - return -EINVAL; - } - - rc = qpnp_led_masked_write(led, - FLASH_TORCH(led->base), - FLASH_TORCH_MASK, FLASH_LED_TORCH_DISABLE); - if (rc) { - dev_err(&led->pdev->dev, - "Torch reg write failed\n"); - return -EINVAL; - } - } - - if (led->battery_psy && - led->revid_data->pmic_subtype == PMI8996_SUBTYPE && - !led->revid_data->rev3) { - psy_prop.intval = false; - rc = power_supply_set_property(led->battery_psy, - POWER_SUPPLY_PROP_FLASH_TRIGGER, - &psy_prop); - if (rc) { - dev_err(&led->pdev->dev, - "Failed to enble charger i/p current limit\n"); - return -EINVAL; - } - } - - rc = qpnp_led_masked_write(led, - FLASH_MODULE_ENABLE_CTRL(led->base), - FLASH_MODULE_ENABLE_MASK, - FLASH_LED_MODULE_CTRL_DEFAULT); - if (rc) { - dev_err(&led->pdev->dev, "Module disable failed\n"); - return -EINVAL; - } - - if (led->pinctrl) { - rc = pinctrl_select_state(led->pinctrl, - led->gpio_state_suspend); - if (rc) { - dev_err(&led->pdev->dev, - "failed to disable GPIO\n"); - return -EINVAL; - } - led->gpio_enabled = false; - } - - if (led->battery_psy) { - psy_prop.intval = false; - rc = power_supply_set_property(led->battery_psy, - POWER_SUPPLY_PROP_FLASH_ACTIVE, - &psy_prop); - if (rc) { - dev_err(&led->pdev->dev, - "Failed to setup OTG pulse skip enable\n"); - return -EINVAL; - } - } - } - - if (flash_node->trigger & FLASH_LED0_TRIGGER) { - rc = qpnp_led_masked_write(led, - led->current_addr, - FLASH_CURRENT_MASK, 0x00); - if (rc) { - dev_err(&led->pdev->dev, - "current register write failed\n"); - return -EINVAL; - } - } - - if (flash_node->trigger & FLASH_LED1_TRIGGER) { - rc = qpnp_led_masked_write(led, - led->current2_addr, - FLASH_CURRENT_MASK, 0x00); - if (rc) { - dev_err(&led->pdev->dev, - "current register write failed\n"); - return -EINVAL; - } - } - - if (flash_node->id == FLASH_LED_SWITCH) - flash_node->trigger &= FLASH_LED_STROBE_TYPE_HW; - - return 0; -} - -static enum -led_brightness qpnp_flash_led_brightness_get(struct led_classdev *led_cdev) -{ - return led_cdev->brightness; -} - -static int flash_regulator_parse_dt(struct qpnp_flash_led *led, - struct flash_node_data *flash_node) { - - int i = 0, rc; - struct device_node *node = flash_node->cdev.dev->of_node; - struct device_node *temp = NULL; - const char *temp_string; - u32 val; - - flash_node->reg_data = devm_kzalloc(&led->pdev->dev, - sizeof(struct flash_regulator_data *) * - flash_node->num_regulators, - GFP_KERNEL); - if (!flash_node->reg_data) { - dev_err(&led->pdev->dev, - "Unable to allocate memory\n"); - return -ENOMEM; - } - - for_each_child_of_node(node, temp) { - rc = of_property_read_string(temp, "regulator-name", - &temp_string); - if (!rc) - flash_node->reg_data[i].reg_name = temp_string; - else { - dev_err(&led->pdev->dev, - "Unable to read regulator name\n"); - return rc; - } - - rc = of_property_read_u32(temp, "max-voltage", &val); - if (!rc) { - flash_node->reg_data[i].max_volt_uv = val; - } else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, - "Unable to read max voltage\n"); - return rc; - } - - i++; - } - - return 0; -} - -static int flash_regulator_setup(struct qpnp_flash_led *led, - struct flash_node_data *flash_node, bool on) -{ - int i, rc = 0; - - if (on == false) { - i = flash_node->num_regulators; - goto error_regulator_setup; - } - - for (i = 0; i < flash_node->num_regulators; i++) { - flash_node->reg_data[i].regs = - regulator_get(flash_node->cdev.dev, - flash_node->reg_data[i].reg_name); - if (IS_ERR(flash_node->reg_data[i].regs)) { - rc = PTR_ERR(flash_node->reg_data[i].regs); - dev_err(&led->pdev->dev, - "Failed to get regulator\n"); - goto error_regulator_setup; - } - - if (regulator_count_voltages(flash_node->reg_data[i].regs) - > 0) { - rc = regulator_set_voltage(flash_node->reg_data[i].regs, - flash_node->reg_data[i].max_volt_uv, - flash_node->reg_data[i].max_volt_uv); - if (rc) { - dev_err(&led->pdev->dev, - "regulator set voltage failed\n"); - regulator_put(flash_node->reg_data[i].regs); - goto error_regulator_setup; - } - } - } - - return rc; - -error_regulator_setup: - while (i--) { - if (regulator_count_voltages(flash_node->reg_data[i].regs) - > 0) { - regulator_set_voltage(flash_node->reg_data[i].regs, - 0, flash_node->reg_data[i].max_volt_uv); - } - - regulator_put(flash_node->reg_data[i].regs); - } - - return rc; -} - -static int flash_regulator_enable(struct qpnp_flash_led *led, - struct flash_node_data *flash_node, bool on) -{ - int i, rc = 0; - - if (on == false) { - i = flash_node->num_regulators; - goto error_regulator_enable; - } - - for (i = 0; i < flash_node->num_regulators; i++) { - rc = regulator_enable(flash_node->reg_data[i].regs); - if (rc) { - dev_err(&led->pdev->dev, - "regulator enable failed\n"); - goto error_regulator_enable; - } - } - - return rc; - -error_regulator_enable: - while (i--) - regulator_disable(flash_node->reg_data[i].regs); - - return rc; -} - -int qpnp_flash_led_prepare(struct led_trigger *trig, int options, - int *max_current) -{ - struct led_classdev *led_cdev = trigger_to_lcdev(trig); - struct flash_node_data *flash_node; - struct qpnp_flash_led *led; - int rc; - - if (!led_cdev) { - pr_err("Invalid led_trigger provided\n"); - return -EINVAL; - } - - flash_node = container_of(led_cdev, struct flash_node_data, cdev); - led = dev_get_drvdata(&flash_node->pdev->dev); - - if (!(options & FLASH_LED_PREPARE_OPTIONS_MASK)) { - dev_err(&led->pdev->dev, "Invalid options %d\n", options); - return -EINVAL; - } - - if (options & ENABLE_REGULATOR) { - rc = flash_regulator_enable(led, flash_node, true); - if (rc < 0) { - dev_err(&led->pdev->dev, - "enable regulator failed, rc=%d\n", rc); - return rc; - } - } - - if (options & DISABLE_REGULATOR) { - rc = flash_regulator_enable(led, flash_node, false); - if (rc < 0) { - dev_err(&led->pdev->dev, - "disable regulator failed, rc=%d\n", rc); - return rc; - } - } - - if (options & QUERY_MAX_CURRENT) { - rc = qpnp_flash_led_get_max_avail_current(flash_node, led); - if (rc < 0) { - dev_err(&led->pdev->dev, - "query max current failed, rc=%d\n", rc); - return rc; - } - *max_current = rc; - } - - return 0; -} - -static void qpnp_flash_led_work(struct work_struct *work) -{ - struct flash_node_data *flash_node = container_of(work, - struct flash_node_data, work); - struct qpnp_flash_led *led = dev_get_drvdata(&flash_node->pdev->dev); - union power_supply_propval psy_prop; - int rc, brightness = flash_node->cdev.brightness; - int max_curr_avail_ma = 0; - int total_curr_ma = 0; - int i; - u8 val; - uint temp; - - mutex_lock(&led->flash_led_lock); - - if (!brightness) - goto turn_off; - - if (led->open_fault) { - dev_err(&led->pdev->dev, "Open fault detected\n"); - mutex_unlock(&led->flash_led_lock); - return; - } - - if (!flash_node->flash_on && flash_node->num_regulators > 0) { - rc = flash_regulator_enable(led, flash_node, true); - if (rc) { - mutex_unlock(&led->flash_led_lock); - return; - } - } - - if (!led->gpio_enabled && led->pinctrl) { - rc = pinctrl_select_state(led->pinctrl, - led->gpio_state_active); - if (rc) { - dev_err(&led->pdev->dev, "failed to enable GPIO\n"); - goto error_enable_gpio; - } - led->gpio_enabled = true; - } - - if (led->dbg_feature_en) { - rc = qpnp_led_masked_write(led, - INT_SET_TYPE(led->base), - FLASH_STATUS_REG_MASK, 0x1F); - if (rc) { - dev_err(&led->pdev->dev, - "INT_SET_TYPE write failed\n"); - goto exit_flash_led_work; - } - - rc = qpnp_led_masked_write(led, - IN_POLARITY_HIGH(led->base), - FLASH_STATUS_REG_MASK, 0x1F); - if (rc) { - dev_err(&led->pdev->dev, - "IN_POLARITY_HIGH write failed\n"); - goto exit_flash_led_work; - } - - rc = qpnp_led_masked_write(led, - INT_EN_SET(led->base), - FLASH_STATUS_REG_MASK, 0x1F); - if (rc) { - dev_err(&led->pdev->dev, "INT_EN_SET write failed\n"); - goto exit_flash_led_work; - } - - rc = qpnp_led_masked_write(led, - INT_LATCHED_CLR(led->base), - FLASH_STATUS_REG_MASK, 0x1F); - if (rc) { - dev_err(&led->pdev->dev, - "INT_LATCHED_CLR write failed\n"); - goto exit_flash_led_work; - } - } - - if (led->flash_node[led->num_leds - 1].id == FLASH_LED_SWITCH && - flash_node->id != FLASH_LED_SWITCH) { - led->flash_node[led->num_leds - 1].trigger |= - (0x80 >> flash_node->id); - if (flash_node->id == FLASH_LED_0) - led->flash_node[led->num_leds - 1].prgm_current = - flash_node->prgm_current; - else if (flash_node->id == FLASH_LED_1) - led->flash_node[led->num_leds - 1].prgm_current2 = - flash_node->prgm_current; - } - - if (flash_node->type == TORCH) { - rc = qpnp_led_masked_write(led, - FLASH_LED_UNLOCK_SECURE(led->base), - FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE); - if (rc) { - dev_err(&led->pdev->dev, "Secure reg write failed\n"); - goto exit_flash_led_work; - } - - rc = qpnp_led_masked_write(led, - FLASH_TORCH(led->base), - FLASH_TORCH_MASK, FLASH_LED_TORCH_ENABLE); - if (rc) { - dev_err(&led->pdev->dev, "Torch reg write failed\n"); - goto exit_flash_led_work; - } - - if (flash_node->id == FLASH_LED_SWITCH) { - val = (u8)(flash_node->prgm_current * - FLASH_TORCH_MAX_LEVEL - / flash_node->max_current); - rc = qpnp_led_masked_write(led, - led->current_addr, - FLASH_CURRENT_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, - "Torch reg write failed\n"); - goto exit_flash_led_work; - } - - val = (u8)(flash_node->prgm_current2 * - FLASH_TORCH_MAX_LEVEL - / flash_node->max_current); - rc = qpnp_led_masked_write(led, - led->current2_addr, - FLASH_CURRENT_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, - "Torch reg write failed\n"); - goto exit_flash_led_work; - } - } else { - val = (u8)(flash_node->prgm_current * - FLASH_TORCH_MAX_LEVEL / - flash_node->max_current); - if (flash_node->id == FLASH_LED_0) { - rc = qpnp_led_masked_write(led, - led->current_addr, - FLASH_CURRENT_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, - "current reg write failed\n"); - goto exit_flash_led_work; - } - } else { - rc = qpnp_led_masked_write(led, - led->current2_addr, - FLASH_CURRENT_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, - "current reg write failed\n"); - goto exit_flash_led_work; - } - } - } - - rc = qpnp_led_masked_write(led, - FLASH_MAX_CURRENT(led->base), - FLASH_CURRENT_MASK, FLASH_TORCH_MAX_LEVEL); - if (rc) { - dev_err(&led->pdev->dev, - "Max current reg write failed\n"); - goto exit_flash_led_work; - } - - rc = qpnp_led_masked_write(led, - FLASH_MODULE_ENABLE_CTRL(led->base), - FLASH_MODULE_ENABLE_MASK, FLASH_MODULE_ENABLE); - if (rc) { - dev_err(&led->pdev->dev, - "Module enable reg write failed\n"); - goto exit_flash_led_work; - } - - if (led->pdata->hdrm_sns_ch0_en || - led->pdata->hdrm_sns_ch1_en) { - if (flash_node->id == FLASH_LED_SWITCH) { - rc = qpnp_led_masked_write(led, - FLASH_HDRM_SNS_ENABLE_CTRL0(led->base), - FLASH_LED_HDRM_SNS_ENABLE_MASK, - flash_node->trigger & - FLASH_LED0_TRIGGER ? - FLASH_LED_HDRM_SNS_ENABLE : - FLASH_LED_HDRM_SNS_DISABLE); - if (rc) { - dev_err(&led->pdev->dev, - "Headroom sense enable failed\n"); - goto exit_flash_led_work; - } - - rc = qpnp_led_masked_write(led, - FLASH_HDRM_SNS_ENABLE_CTRL1(led->base), - FLASH_LED_HDRM_SNS_ENABLE_MASK, - flash_node->trigger & - FLASH_LED1_TRIGGER ? - FLASH_LED_HDRM_SNS_ENABLE : - FLASH_LED_HDRM_SNS_DISABLE); - if (rc) { - dev_err(&led->pdev->dev, - "Headroom sense enable failed\n"); - goto exit_flash_led_work; - } - } else if (flash_node->id == FLASH_LED_0) { - rc = qpnp_led_masked_write(led, - FLASH_HDRM_SNS_ENABLE_CTRL0(led->base), - FLASH_LED_HDRM_SNS_ENABLE_MASK, - FLASH_LED_HDRM_SNS_ENABLE); - if (rc) { - dev_err(&led->pdev->dev, - "Headroom sense disable failed\n"); - goto exit_flash_led_work; - } - } else if (flash_node->id == FLASH_LED_1) { - rc = qpnp_led_masked_write(led, - FLASH_HDRM_SNS_ENABLE_CTRL1(led->base), - FLASH_LED_HDRM_SNS_ENABLE_MASK, - FLASH_LED_HDRM_SNS_ENABLE); - if (rc) { - dev_err(&led->pdev->dev, - "Headroom sense disable failed\n"); - goto exit_flash_led_work; - } - } - } - - rc = qpnp_led_masked_write(led, - FLASH_LED_STROBE_CTRL(led->base), - (flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK - | FLASH_LED_STROBE_TYPE_HW - : flash_node->trigger | - FLASH_LED_STROBE_TYPE_HW), - flash_node->trigger); - if (rc) { - dev_err(&led->pdev->dev, "Strobe reg write failed\n"); - goto exit_flash_led_work; - } - } else if (flash_node->type == FLASH) { - if (flash_node->trigger & FLASH_LED0_TRIGGER) - max_curr_avail_ma += flash_node->max_current; - if (flash_node->trigger & FLASH_LED1_TRIGGER) - max_curr_avail_ma += flash_node->max_current; - - psy_prop.intval = true; - rc = power_supply_set_property(led->battery_psy, - POWER_SUPPLY_PROP_FLASH_ACTIVE, - &psy_prop); - if (rc) { - dev_err(&led->pdev->dev, - "Failed to setup OTG pulse skip enable\n"); - goto exit_flash_led_work; - } - - if (led->pdata->power_detect_en || - led->pdata->die_current_derate_en) { - if (led->battery_psy) { - power_supply_get_property(led->battery_psy, - POWER_SUPPLY_PROP_STATUS, - &psy_prop); - if (psy_prop.intval < 0) { - dev_err(&led->pdev->dev, - "Invalid battery status\n"); - goto exit_flash_led_work; - } - - if (psy_prop.intval == - POWER_SUPPLY_STATUS_CHARGING) - led->charging_enabled = true; - else if (psy_prop.intval == - POWER_SUPPLY_STATUS_DISCHARGING - || psy_prop.intval == - POWER_SUPPLY_STATUS_NOT_CHARGING) - led->charging_enabled = false; - } - max_curr_avail_ma = - qpnp_flash_led_get_max_avail_current - (flash_node, led); - if (max_curr_avail_ma < 0) { - dev_err(&led->pdev->dev, - "Failed to get max avail curr\n"); - goto exit_flash_led_work; - } - } - - if (flash_node->id == FLASH_LED_SWITCH) { - if (flash_node->trigger & FLASH_LED0_TRIGGER) - total_curr_ma += flash_node->prgm_current; - if (flash_node->trigger & FLASH_LED1_TRIGGER) - total_curr_ma += flash_node->prgm_current2; - - if (max_curr_avail_ma < total_curr_ma) { - flash_node->prgm_current = - (flash_node->prgm_current * - max_curr_avail_ma) / total_curr_ma; - flash_node->prgm_current2 = - (flash_node->prgm_current2 * - max_curr_avail_ma) / total_curr_ma; - } - - val = (u8)(flash_node->prgm_current * - FLASH_MAX_LEVEL / flash_node->max_current); - rc = qpnp_led_masked_write(led, - led->current_addr, FLASH_CURRENT_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, - "Current register write failed\n"); - goto exit_flash_led_work; - } - - val = (u8)(flash_node->prgm_current2 * - FLASH_MAX_LEVEL / flash_node->max_current); - rc = qpnp_led_masked_write(led, - led->current2_addr, FLASH_CURRENT_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, - "Current register write failed\n"); - goto exit_flash_led_work; - } - } else { - if (max_curr_avail_ma < flash_node->prgm_current) { - dev_err(&led->pdev->dev, - "battery only supprots %d mA\n", - max_curr_avail_ma); - flash_node->prgm_current = - (u16)max_curr_avail_ma; - } - - val = (u8)(flash_node->prgm_current * - FLASH_MAX_LEVEL - / flash_node->max_current); - if (flash_node->id == FLASH_LED_0) { - rc = qpnp_led_masked_write( - led, - led->current_addr, - FLASH_CURRENT_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, - "current reg write failed\n"); - goto exit_flash_led_work; - } - } else if (flash_node->id == FLASH_LED_1) { - rc = qpnp_led_masked_write( - led, - led->current2_addr, - FLASH_CURRENT_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, - "current reg write failed\n"); - goto exit_flash_led_work; - } - } - } - - val = (u8)((flash_node->duration - FLASH_DURATION_DIVIDER) - / FLASH_DURATION_DIVIDER); - rc = qpnp_led_masked_write(led, - FLASH_SAFETY_TIMER(led->base), - FLASH_SAFETY_TIMER_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, - "Safety timer reg write failed\n"); - goto exit_flash_led_work; - } - - rc = qpnp_led_masked_write(led, - FLASH_MAX_CURRENT(led->base), - FLASH_CURRENT_MASK, FLASH_MAX_LEVEL); - if (rc) { - dev_err(&led->pdev->dev, - "Max current reg write failed\n"); - goto exit_flash_led_work; - } - - if (!led->charging_enabled) { - rc = qpnp_led_masked_write(led, - FLASH_MODULE_ENABLE_CTRL(led->base), - FLASH_MODULE_ENABLE, FLASH_MODULE_ENABLE); - if (rc) { - dev_err(&led->pdev->dev, - "Module enable reg write failed\n"); - goto exit_flash_led_work; - } - - usleep_range(FLASH_RAMP_UP_DELAY_US_MIN, - FLASH_RAMP_UP_DELAY_US_MAX); - } - - if (led->revid_data->pmic_subtype == PMI8996_SUBTYPE && - !led->revid_data->rev3) { - rc = power_supply_set_property(led->battery_psy, - POWER_SUPPLY_PROP_FLASH_TRIGGER, - &psy_prop); - if (rc) { - dev_err(&led->pdev->dev, - "Failed to disable charger i/p curr limit\n"); - goto exit_flash_led_work; - } - } - - if (led->pdata->hdrm_sns_ch0_en || - led->pdata->hdrm_sns_ch1_en) { - if (flash_node->id == FLASH_LED_SWITCH) { - rc = qpnp_led_masked_write(led, - FLASH_HDRM_SNS_ENABLE_CTRL0(led->base), - FLASH_LED_HDRM_SNS_ENABLE_MASK, - (flash_node->trigger & - FLASH_LED0_TRIGGER ? - FLASH_LED_HDRM_SNS_ENABLE : - FLASH_LED_HDRM_SNS_DISABLE)); - if (rc) { - dev_err(&led->pdev->dev, - "Headroom sense enable failed\n"); - goto exit_flash_led_work; - } - - rc = qpnp_led_masked_write(led, - FLASH_HDRM_SNS_ENABLE_CTRL1(led->base), - FLASH_LED_HDRM_SNS_ENABLE_MASK, - (flash_node->trigger & - FLASH_LED1_TRIGGER ? - FLASH_LED_HDRM_SNS_ENABLE : - FLASH_LED_HDRM_SNS_DISABLE)); - if (rc) { - dev_err(&led->pdev->dev, - "Headroom sense enable failed\n"); - goto exit_flash_led_work; - } - } else if (flash_node->id == FLASH_LED_0) { - rc = qpnp_led_masked_write(led, - FLASH_HDRM_SNS_ENABLE_CTRL0(led->base), - FLASH_LED_HDRM_SNS_ENABLE_MASK, - FLASH_LED_HDRM_SNS_ENABLE); - if (rc) { - dev_err(&led->pdev->dev, - "Headroom sense disable failed\n"); - goto exit_flash_led_work; - } - } else if (flash_node->id == FLASH_LED_1) { - rc = qpnp_led_masked_write(led, - FLASH_HDRM_SNS_ENABLE_CTRL1(led->base), - FLASH_LED_HDRM_SNS_ENABLE_MASK, - FLASH_LED_HDRM_SNS_ENABLE); - if (rc) { - dev_err(&led->pdev->dev, - "Headroom sense disable failed\n"); - goto exit_flash_led_work; - } - } - } - - rc = qpnp_led_masked_write(led, - FLASH_LED_STROBE_CTRL(led->base), - (flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK - | FLASH_LED_STROBE_TYPE_HW - : flash_node->trigger | - FLASH_LED_STROBE_TYPE_HW), - flash_node->trigger); - if (rc) { - dev_err(&led->pdev->dev, "Strobe reg write failed\n"); - goto exit_flash_led_work; - } - - if (led->strobe_debug && led->dbg_feature_en) { - udelay(2000); - rc = regmap_read(led->regmap, - FLASH_LED_FAULT_STATUS(led->base), - &temp); - if (rc) { - dev_err(&led->pdev->dev, - "Unable to read from addr= %x, rc(%d)\n", - FLASH_LED_FAULT_STATUS(led->base), rc); - goto exit_flash_led_work; - } - led->fault_reg = temp; - } - } else { - pr_err("Both Torch and Flash cannot be select at same time\n"); - for (i = 0; i < led->num_leds; i++) - led->flash_node[i].flash_on = false; - goto turn_off; - } - - flash_node->flash_on = true; - mutex_unlock(&led->flash_led_lock); - - return; - -turn_off: - if (led->flash_node[led->num_leds - 1].id == FLASH_LED_SWITCH && - flash_node->id != FLASH_LED_SWITCH) - led->flash_node[led->num_leds - 1].trigger &= - ~(0x80 >> flash_node->id); - if (flash_node->type == TORCH) { - /* - * Checking LED fault status detects hardware open fault. - * If fault occurs, all subsequent LED enablement requests - * will be rejected to protect hardware. - */ - rc = regmap_read(led->regmap, - FLASH_LED_FAULT_STATUS(led->base), &temp); - if (rc) { - dev_err(&led->pdev->dev, - "Failed to read out fault status register\n"); - goto exit_flash_led_work; - } - - led->open_fault |= (val & FLASH_LED_OPEN_FAULT_DETECTED); - } - - rc = qpnp_led_masked_write(led, - FLASH_LED_STROBE_CTRL(led->base), - (flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK - | FLASH_LED_STROBE_TYPE_HW - : flash_node->trigger - | FLASH_LED_STROBE_TYPE_HW), - FLASH_LED_DISABLE); - if (rc) { - dev_err(&led->pdev->dev, "Strobe disable failed\n"); - goto exit_flash_led_work; - } - - usleep_range(FLASH_RAMP_DN_DELAY_US_MIN, FLASH_RAMP_DN_DELAY_US_MAX); -exit_flash_hdrm_sns: - if (led->pdata->hdrm_sns_ch0_en) { - if (flash_node->id == FLASH_LED_0 || - flash_node->id == FLASH_LED_SWITCH) { - rc = qpnp_led_masked_write(led, - FLASH_HDRM_SNS_ENABLE_CTRL0(led->base), - FLASH_LED_HDRM_SNS_ENABLE_MASK, - FLASH_LED_HDRM_SNS_DISABLE); - if (rc) { - dev_err(&led->pdev->dev, - "Headroom sense disable failed\n"); - goto exit_flash_hdrm_sns; - } - } - } - - if (led->pdata->hdrm_sns_ch1_en) { - if (flash_node->id == FLASH_LED_1 || - flash_node->id == FLASH_LED_SWITCH) { - rc = qpnp_led_masked_write(led, - FLASH_HDRM_SNS_ENABLE_CTRL1(led->base), - FLASH_LED_HDRM_SNS_ENABLE_MASK, - FLASH_LED_HDRM_SNS_DISABLE); - if (rc) { - dev_err(&led->pdev->dev, - "Headroom sense disable failed\n"); - goto exit_flash_hdrm_sns; - } - } - } -exit_flash_led_work: - rc = qpnp_flash_led_module_disable(led, flash_node); - if (rc) { - dev_err(&led->pdev->dev, "Module disable failed\n"); - goto exit_flash_led_work; - } -error_enable_gpio: - if (flash_node->flash_on && flash_node->num_regulators > 0) - flash_regulator_enable(led, flash_node, false); - - flash_node->flash_on = false; - mutex_unlock(&led->flash_led_lock); -} - -static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - struct flash_node_data *flash_node; - struct qpnp_flash_led *led; - - flash_node = container_of(led_cdev, struct flash_node_data, cdev); - led = dev_get_drvdata(&flash_node->pdev->dev); - - if (value < LED_OFF) { - pr_err("Invalid brightness value\n"); - return; - } - - if (value > flash_node->cdev.max_brightness) - value = flash_node->cdev.max_brightness; - - flash_node->cdev.brightness = value; - if (led->flash_node[led->num_leds - 1].id == - FLASH_LED_SWITCH) { - if (flash_node->type == TORCH) - led->flash_node[led->num_leds - 1].type = TORCH; - else if (flash_node->type == FLASH) - led->flash_node[led->num_leds - 1].type = FLASH; - - led->flash_node[led->num_leds - 1].max_current - = flash_node->max_current; - - if (flash_node->id == FLASH_LED_0 || - flash_node->id == FLASH_LED_1) { - if (value < FLASH_LED_MIN_CURRENT_MA && value != 0) - value = FLASH_LED_MIN_CURRENT_MA; - - flash_node->prgm_current = value; - flash_node->flash_on = value ? true : false; - } else if (flash_node->id == FLASH_LED_SWITCH) { - if (!value) { - flash_node->prgm_current = 0; - flash_node->prgm_current2 = 0; - } - } - } else { - if (value < FLASH_LED_MIN_CURRENT_MA && value != 0) - value = FLASH_LED_MIN_CURRENT_MA; - flash_node->prgm_current = value; - } - - queue_work(led->ordered_workq, &flash_node->work); -} - -static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) -{ - int rc; - u8 val, temp_val; - uint val_int; - - rc = qpnp_led_masked_write(led, - FLASH_MODULE_ENABLE_CTRL(led->base), - FLASH_MODULE_ENABLE_MASK, - FLASH_LED_MODULE_CTRL_DEFAULT); - if (rc) { - dev_err(&led->pdev->dev, "Module disable failed\n"); - return rc; - } - - rc = qpnp_led_masked_write(led, - FLASH_LED_STROBE_CTRL(led->base), - FLASH_STROBE_MASK, FLASH_LED_DISABLE); - if (rc) { - dev_err(&led->pdev->dev, "Strobe disable failed\n"); - return rc; - } - - rc = qpnp_led_masked_write(led, - FLASH_LED_TMR_CTRL(led->base), - FLASH_TMR_MASK, FLASH_TMR_SAFETY); - if (rc) { - dev_err(&led->pdev->dev, - "LED timer ctrl reg write failed(%d)\n", rc); - return rc; - } - - val = (u8)(led->pdata->headroom / FLASH_LED_HEADROOM_DIVIDER - - FLASH_LED_HEADROOM_OFFSET); - rc = qpnp_led_masked_write(led, - FLASH_HEADROOM(led->base), - FLASH_HEADROOM_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, "Headroom reg write failed\n"); - return rc; - } - - val = qpnp_flash_led_get_startup_dly(led->pdata->startup_dly); - - rc = qpnp_led_masked_write(led, - FLASH_STARTUP_DELAY(led->base), - FLASH_STARTUP_DLY_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, "Startup delay reg write failed\n"); - return rc; - } - - val = (u8)(led->pdata->clamp_current * FLASH_MAX_LEVEL / - FLASH_LED_MAX_CURRENT_MA); - rc = qpnp_led_masked_write(led, - FLASH_CLAMP_CURRENT(led->base), - FLASH_CURRENT_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, "Clamp current reg write failed\n"); - return rc; - } - - if (led->pdata->pmic_charger_support) - val = FLASH_LED_FLASH_HW_VREG_OK; - else - val = FLASH_LED_FLASH_SW_VREG_OK; - rc = qpnp_led_masked_write(led, - FLASH_VREG_OK_FORCE(led->base), - FLASH_VREG_OK_FORCE_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, "VREG OK force reg write failed\n"); - return rc; - } - - if (led->pdata->self_check_en) - val = FLASH_MODULE_ENABLE; - else - val = FLASH_LED_DISABLE; - rc = qpnp_led_masked_write(led, - FLASH_FAULT_DETECT(led->base), - FLASH_FAULT_DETECT_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, "Fault detect reg write failed\n"); - return rc; - } - - val = 0x0; - val |= led->pdata->mask3_en << FLASH_LED_MASK3_ENABLE_SHIFT; - val |= FLASH_LED_MASK_MODULE_MASK2_ENABLE; - rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base), - FLASH_MASK_MODULE_CONTRL_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, "Mask module enable failed\n"); - return rc; - } - - rc = regmap_read(led->regmap, FLASH_PERPH_RESET_CTRL(led->base), - &val_int); - if (rc) { - dev_err(&led->pdev->dev, - "Unable to read from address %x, rc(%d)\n", - FLASH_PERPH_RESET_CTRL(led->base), rc); - return -EINVAL; - } - val = (u8)val_int; - - if (led->pdata->follow_rb_disable) { - rc = qpnp_led_masked_write(led, - FLASH_LED_UNLOCK_SECURE(led->base), - FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE); - if (rc) { - dev_err(&led->pdev->dev, "Secure reg write failed\n"); - return -EINVAL; - } - - val |= FLASH_FOLLOW_OTST2_RB_MASK; - rc = qpnp_led_masked_write(led, - FLASH_PERPH_RESET_CTRL(led->base), - FLASH_FOLLOW_OTST2_RB_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, - "failed to reset OTST2_RB bit\n"); - return rc; - } - } else { - rc = qpnp_led_masked_write(led, - FLASH_LED_UNLOCK_SECURE(led->base), - FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE); - if (rc) { - dev_err(&led->pdev->dev, "Secure reg write failed\n"); - return -EINVAL; - } - - val &= ~FLASH_FOLLOW_OTST2_RB_MASK; - rc = qpnp_led_masked_write(led, - FLASH_PERPH_RESET_CTRL(led->base), - FLASH_FOLLOW_OTST2_RB_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, - "failed to reset OTST2_RB bit\n"); - return rc; - } - } - - if (!led->pdata->thermal_derate_en) - val = 0x0; - else { - val = led->pdata->thermal_derate_en << 7; - val |= led->pdata->thermal_derate_rate << 3; - val |= (led->pdata->thermal_derate_threshold - - FLASH_LED_THERMAL_THRESHOLD_MIN) / - FLASH_LED_THERMAL_DEVIDER; - } - rc = qpnp_led_masked_write(led, - FLASH_THERMAL_DRATE(led->base), - FLASH_THERMAL_DERATE_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, "Thermal derate reg write failed\n"); - return rc; - } - - if (!led->pdata->current_ramp_en) - val = 0x0; - else { - val = led->pdata->current_ramp_en << 7; - val |= led->pdata->ramp_up_step << 3; - val |= led->pdata->ramp_dn_step; - } - rc = qpnp_led_masked_write(led, - FLASH_CURRENT_RAMP(led->base), - FLASH_CURRENT_RAMP_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, "Current ramp reg write failed\n"); - return rc; - } - - if (!led->pdata->vph_pwr_droop_en) - val = 0x0; - else { - val = led->pdata->vph_pwr_droop_en << 7; - val |= ((led->pdata->vph_pwr_droop_threshold - - FLASH_LED_VPH_DROOP_THRESHOLD_MIN_MV) / - FLASH_LED_VPH_DROOP_THRESHOLD_DIVIDER) << 4; - temp_val = - qpnp_flash_led_get_droop_debounce_time( - led->pdata->vph_pwr_droop_debounce_time); - if (temp_val == 0xFF) { - dev_err(&led->pdev->dev, "Invalid debounce time\n"); - return temp_val; - } - - val |= temp_val; - } - rc = qpnp_led_masked_write(led, - FLASH_VPH_PWR_DROOP(led->base), - FLASH_VPH_PWR_DROOP_MASK, val); - if (rc) { - dev_err(&led->pdev->dev, "VPH PWR droop reg write failed\n"); - return rc; - } - - led->battery_psy = power_supply_get_by_name("battery"); - if (!led->battery_psy) { - dev_err(&led->pdev->dev, - "Failed to get battery power supply\n"); - return -EINVAL; - } - - return 0; -} - -static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, - struct flash_node_data *flash_node) -{ - const char *temp_string; - struct device_node *node = flash_node->cdev.dev->of_node; - struct device_node *temp = NULL; - int rc = 0, num_regs = 0; - u32 val; - - rc = of_property_read_string(node, "label", &temp_string); - if (!rc) { - if (strcmp(temp_string, "flash") == 0) - flash_node->type = FLASH; - else if (strcmp(temp_string, "torch") == 0) - flash_node->type = TORCH; - else if (strcmp(temp_string, "switch") == 0) - flash_node->type = SWITCH; - else { - dev_err(&led->pdev->dev, "Wrong flash LED type\n"); - return -EINVAL; - } - } else if (rc < 0) { - dev_err(&led->pdev->dev, "Unable to read flash type\n"); - return rc; - } - - rc = of_property_read_u32(node, "qcom,current", &val); - if (!rc) { - if (val < FLASH_LED_MIN_CURRENT_MA) - val = FLASH_LED_MIN_CURRENT_MA; - flash_node->prgm_current = val; - } else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, "Unable to read current\n"); - return rc; - } - - rc = of_property_read_u32(node, "qcom,id", &val); - if (!rc) - flash_node->id = (u8)val; - else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, "Unable to read led ID\n"); - return rc; - } - - if (flash_node->type == SWITCH || flash_node->type == FLASH) { - rc = of_property_read_u32(node, "qcom,duration", &val); - if (!rc) - flash_node->duration = (u16)val; - else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, "Unable to read duration\n"); - return rc; - } - } - - switch (led->peripheral_type) { - case FLASH_SUBTYPE_SINGLE: - flash_node->trigger = FLASH_LED0_TRIGGER; - break; - case FLASH_SUBTYPE_DUAL: - if (flash_node->id == FLASH_LED_0) - flash_node->trigger = FLASH_LED0_TRIGGER; - else if (flash_node->id == FLASH_LED_1) - flash_node->trigger = FLASH_LED1_TRIGGER; - break; - default: - dev_err(&led->pdev->dev, "Invalid peripheral type\n"); - } - - while ((temp = of_get_next_child(node, temp))) { - if (of_find_property(temp, "regulator-name", NULL)) - num_regs++; - } - - if (num_regs) - flash_node->num_regulators = num_regs; - - return rc; -} - -static int qpnp_flash_led_parse_common_dt( - struct qpnp_flash_led *led, - struct device_node *node) -{ - int rc; - u32 val, temp_val; - const char *temp; - - led->pdata->headroom = FLASH_LED_HEADROOM_DEFAULT_MV; - rc = of_property_read_u32(node, "qcom,headroom", &val); - if (!rc) - led->pdata->headroom = (u16)val; - else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, "Unable to read headroom\n"); - return rc; - } - - led->pdata->startup_dly = FLASH_LED_STARTUP_DELAY_DEFAULT_US; - rc = of_property_read_u32(node, "qcom,startup-dly", &val); - if (!rc) - led->pdata->startup_dly = (u8)val; - else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, "Unable to read startup delay\n"); - return rc; - } - - led->pdata->clamp_current = FLASH_LED_CLAMP_CURRENT_DEFAULT_MA; - rc = of_property_read_u32(node, "qcom,clamp-current", &val); - if (!rc) { - if (val < FLASH_LED_MIN_CURRENT_MA) - val = FLASH_LED_MIN_CURRENT_MA; - led->pdata->clamp_current = (u16)val; - } else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, "Unable to read clamp current\n"); - return rc; - } - - led->pdata->pmic_charger_support = - of_property_read_bool(node, - "qcom,pmic-charger-support"); - - led->pdata->self_check_en = - of_property_read_bool(node, "qcom,self-check-enabled"); - - led->pdata->thermal_derate_en = - of_property_read_bool(node, - "qcom,thermal-derate-enabled"); - - if (led->pdata->thermal_derate_en) { - led->pdata->thermal_derate_rate = - FLASH_LED_THERMAL_DERATE_RATE_DEFAULT_PERCENT; - rc = of_property_read_string(node, "qcom,thermal-derate-rate", - &temp); - if (!rc) { - temp_val = - qpnp_flash_led_get_thermal_derate_rate(temp); - if (temp_val < 0) { - dev_err(&led->pdev->dev, - "Invalid thermal derate rate\n"); - return -EINVAL; - } - - led->pdata->thermal_derate_rate = (u8)temp_val; - } else { - dev_err(&led->pdev->dev, - "Unable to read thermal derate rate\n"); - return -EINVAL; - } - - led->pdata->thermal_derate_threshold = - FLASH_LED_THERMAL_DERATE_THRESHOLD_DEFAULT_C; - rc = of_property_read_u32(node, "qcom,thermal-derate-threshold", - &val); - if (!rc) - led->pdata->thermal_derate_threshold = (u8)val; - else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, - "Unable to read thermal derate threshold\n"); - return rc; - } - } - - led->pdata->current_ramp_en = - of_property_read_bool(node, - "qcom,current-ramp-enabled"); - if (led->pdata->current_ramp_en) { - led->pdata->ramp_up_step = FLASH_LED_RAMP_UP_STEP_DEFAULT_US; - rc = of_property_read_string(node, "qcom,ramp_up_step", &temp); - if (!rc) { - temp_val = qpnp_flash_led_get_ramp_step(temp); - if (temp_val < 0) { - dev_err(&led->pdev->dev, - "Invalid ramp up step values\n"); - return -EINVAL; - } - led->pdata->ramp_up_step = (u8)temp_val; - } else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, - "Unable to read ramp up steps\n"); - return rc; - } - - led->pdata->ramp_dn_step = FLASH_LED_RAMP_DN_STEP_DEFAULT_US; - rc = of_property_read_string(node, "qcom,ramp_dn_step", &temp); - if (!rc) { - temp_val = qpnp_flash_led_get_ramp_step(temp); - if (temp_val < 0) { - dev_err(&led->pdev->dev, - "Invalid ramp down step values\n"); - return rc; - } - led->pdata->ramp_dn_step = (u8)temp_val; - } else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, - "Unable to read ramp down steps\n"); - return rc; - } - } - - led->pdata->vph_pwr_droop_en = of_property_read_bool(node, - "qcom,vph-pwr-droop-enabled"); - if (led->pdata->vph_pwr_droop_en) { - led->pdata->vph_pwr_droop_threshold = - FLASH_LED_VPH_PWR_DROOP_THRESHOLD_DEFAULT_MV; - rc = of_property_read_u32(node, - "qcom,vph-pwr-droop-threshold", &val); - if (!rc) { - led->pdata->vph_pwr_droop_threshold = (u16)val; - } else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, - "Unable to read VPH PWR droop threshold\n"); - return rc; - } - - led->pdata->vph_pwr_droop_debounce_time = - FLASH_LED_VPH_PWR_DROOP_DEBOUNCE_TIME_DEFAULT_US; - rc = of_property_read_u32(node, - "qcom,vph-pwr-droop-debounce-time", &val); - if (!rc) - led->pdata->vph_pwr_droop_debounce_time = (u8)val; - else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, - "Unable to read VPH PWR droop debounce time\n"); - return rc; - } - } - - led->pdata->hdrm_sns_ch0_en = of_property_read_bool(node, - "qcom,headroom-sense-ch0-enabled"); - - led->pdata->hdrm_sns_ch1_en = of_property_read_bool(node, - "qcom,headroom-sense-ch1-enabled"); - - led->pdata->power_detect_en = of_property_read_bool(node, - "qcom,power-detect-enabled"); - - led->pdata->mask3_en = of_property_read_bool(node, - "qcom,otst2-module-enabled"); - - led->pdata->follow_rb_disable = of_property_read_bool(node, - "qcom,follow-otst2-rb-disabled"); - - led->pdata->die_current_derate_en = of_property_read_bool(node, - "qcom,die-current-derate-enabled"); - - if (led->pdata->die_current_derate_en) { - led->vadc_dev = qpnp_get_vadc(&led->pdev->dev, "die-temp"); - if (IS_ERR(led->vadc_dev)) { - pr_err("VADC channel property Missing\n"); - return -EINVAL; - } - - if (of_find_property(node, "qcom,die-temp-threshold", - &led->pdata->temp_threshold_num)) { - if (led->pdata->temp_threshold_num > 0) { - led->pdata->die_temp_threshold_degc = - devm_kzalloc(&led->pdev->dev, - led->pdata->temp_threshold_num, - GFP_KERNEL); - - if (led->pdata->die_temp_threshold_degc - == NULL) { - dev_err(&led->pdev->dev, - "failed to allocate die temp array\n"); - return -ENOMEM; - } - led->pdata->temp_threshold_num /= - sizeof(unsigned int); - - rc = of_property_read_u32_array(node, - "qcom,die-temp-threshold", - led->pdata->die_temp_threshold_degc, - led->pdata->temp_threshold_num); - if (rc) { - dev_err(&led->pdev->dev, - "couldn't read temp threshold rc=%d\n", - rc); - return rc; - } - } - } - - if (of_find_property(node, "qcom,die-temp-derate-current", - &led->pdata->temp_derate_curr_num)) { - if (led->pdata->temp_derate_curr_num > 0) { - led->pdata->die_temp_derate_curr_ma = - devm_kzalloc(&led->pdev->dev, - led->pdata->temp_derate_curr_num, - GFP_KERNEL); - if (led->pdata->die_temp_derate_curr_ma - == NULL) { - dev_err(&led->pdev->dev, - "failed to allocate die derate current array\n"); - return -ENOMEM; - } - led->pdata->temp_derate_curr_num /= - sizeof(unsigned int); - - rc = of_property_read_u32_array(node, - "qcom,die-temp-derate-current", - led->pdata->die_temp_derate_curr_ma, - led->pdata->temp_derate_curr_num); - if (rc) { - dev_err(&led->pdev->dev, - "couldn't read temp limits rc =%d\n", - rc); - return rc; - } - } - } - if (led->pdata->temp_threshold_num != - led->pdata->temp_derate_curr_num) { - pr_err("Both array size are not same\n"); - return -EINVAL; - } - } - - led->pinctrl = devm_pinctrl_get(&led->pdev->dev); - if (IS_ERR_OR_NULL(led->pinctrl)) { - dev_err(&led->pdev->dev, "Unable to acquire pinctrl\n"); - led->pinctrl = NULL; - return 0; - } - - led->gpio_state_active = pinctrl_lookup_state(led->pinctrl, - "flash_led_enable"); - if (IS_ERR_OR_NULL(led->gpio_state_active)) { - dev_err(&led->pdev->dev, "Cannot lookup LED active state\n"); - devm_pinctrl_put(led->pinctrl); - led->pinctrl = NULL; - return PTR_ERR(led->gpio_state_active); - } - - led->gpio_state_suspend = pinctrl_lookup_state(led->pinctrl, - "flash_led_disable"); - if (IS_ERR_OR_NULL(led->gpio_state_suspend)) { - dev_err(&led->pdev->dev, "Cannot lookup LED disable state\n"); - devm_pinctrl_put(led->pinctrl); - led->pinctrl = NULL; - return PTR_ERR(led->gpio_state_suspend); - } - - return 0; -} - -static int qpnp_flash_led_probe(struct platform_device *pdev) -{ - struct qpnp_flash_led *led; - unsigned int base; - struct device_node *node, *temp; - struct dentry *root, *file; - int rc, i = 0, j, num_leds = 0; - u32 val; - - root = NULL; - node = pdev->dev.of_node; - if (node == NULL) { - dev_info(&pdev->dev, "No flash device defined\n"); - return -ENODEV; - } - - 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", - pdev->dev.of_node->full_name, rc); - return rc; - } - - led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); - if (!led) - return -ENOMEM; - - led->regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!led->regmap) { - dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); - return -EINVAL; - } - - led->base = base; - led->pdev = pdev; - led->current_addr = FLASH_LED0_CURRENT(led->base); - led->current2_addr = FLASH_LED1_CURRENT(led->base); - - led->pdata = devm_kzalloc(&pdev->dev, sizeof(*led->pdata), GFP_KERNEL); - if (!led->pdata) - return -ENOMEM; - - led->peripheral_type = (u8)qpnp_flash_led_get_peripheral_type(led); - if (led->peripheral_type < 0) { - dev_err(&pdev->dev, "Failed to get peripheral type\n"); - return rc; - } - - rc = qpnp_flash_led_parse_common_dt(led, node); - if (rc) { - dev_err(&pdev->dev, - "Failed to get common config for flash LEDs\n"); - return rc; - } - - rc = qpnp_flash_led_init_settings(led); - if (rc) { - dev_err(&pdev->dev, "Failed to initialize flash LED\n"); - return rc; - } - - rc = qpnp_get_pmic_revid(led); - if (rc) - return rc; - - temp = NULL; - while ((temp = of_get_next_child(node, temp))) - num_leds++; - - if (!num_leds) - return -ECHILD; - - led->flash_node = devm_kzalloc(&pdev->dev, - (sizeof(struct flash_node_data) * num_leds), - GFP_KERNEL); - if (!led->flash_node) { - dev_err(&pdev->dev, "Unable to allocate memory\n"); - return -ENOMEM; - } - - mutex_init(&led->flash_led_lock); - - led->ordered_workq = alloc_ordered_workqueue("flash_led_workqueue", 0); - if (!led->ordered_workq) { - dev_err(&pdev->dev, "Failed to allocate ordered workqueue\n"); - return -ENOMEM; - } - - for_each_child_of_node(node, temp) { - led->flash_node[i].cdev.brightness_set = - qpnp_flash_led_brightness_set; - led->flash_node[i].cdev.brightness_get = - qpnp_flash_led_brightness_get; - led->flash_node[i].pdev = pdev; - - INIT_WORK(&led->flash_node[i].work, qpnp_flash_led_work); - rc = of_property_read_string(temp, "qcom,led-name", - &led->flash_node[i].cdev.name); - if (rc < 0) { - dev_err(&led->pdev->dev, - "Unable to read flash name\n"); - return rc; - } - - rc = of_property_read_string(temp, "qcom,default-led-trigger", - &led->flash_node[i].cdev.default_trigger); - if (rc < 0) { - dev_err(&led->pdev->dev, - "Unable to read trigger name\n"); - return rc; - } - - rc = of_property_read_u32(temp, "qcom,max-current", &val); - if (!rc) { - if (val < FLASH_LED_MIN_CURRENT_MA) - val = FLASH_LED_MIN_CURRENT_MA; - led->flash_node[i].max_current = (u16)val; - led->flash_node[i].cdev.max_brightness = val; - } else { - dev_err(&led->pdev->dev, - "Unable to read max current\n"); - return rc; - } - rc = led_classdev_register(&pdev->dev, - &led->flash_node[i].cdev); - if (rc) { - dev_err(&pdev->dev, "Unable to register led\n"); - goto error_led_register; - } - - led->flash_node[i].cdev.dev->of_node = temp; - - rc = qpnp_flash_led_parse_each_led_dt(led, &led->flash_node[i]); - if (rc) { - dev_err(&pdev->dev, - "Failed to parse config for each LED\n"); - goto error_led_register; - } - - if (led->flash_node[i].num_regulators) { - rc = flash_regulator_parse_dt(led, &led->flash_node[i]); - if (rc) { - dev_err(&pdev->dev, - "Unable to parse regulator data\n"); - goto error_led_register; - } - - rc = flash_regulator_setup(led, &led->flash_node[i], - true); - if (rc) { - dev_err(&pdev->dev, - "Unable to set up regulator\n"); - goto error_led_register; - } - } - - for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) { - rc = - sysfs_create_file(&led->flash_node[i].cdev.dev->kobj, - &qpnp_flash_led_attrs[j].attr); - if (rc) - goto error_led_register; - } - - i++; - } - - led->num_leds = i; - - root = debugfs_create_dir("flashLED", NULL); - if (IS_ERR_OR_NULL(root)) { - pr_err("Error creating top level directory err%ld", - (long)root); - if (PTR_ERR(root) == -ENODEV) - pr_err("debugfs is not enabled in kernel"); - goto error_led_debugfs; - } - - led->dbgfs_root = root; - file = debugfs_create_file("enable_debug", 0600, root, led, - &flash_led_dfs_dbg_feature_fops); - if (!file) { - pr_err("error creating 'enable_debug' entry\n"); - goto error_led_debugfs; - } - - file = debugfs_create_file("latched", 0600, root, led, - &flash_led_dfs_latched_reg_fops); - if (!file) { - pr_err("error creating 'latched' entry\n"); - goto error_led_debugfs; - } - - file = debugfs_create_file("strobe", 0600, root, led, - &flash_led_dfs_strobe_reg_fops); - if (!file) { - pr_err("error creating 'strobe' entry\n"); - goto error_led_debugfs; - } - - dev_set_drvdata(&pdev->dev, led); - - return 0; - -error_led_debugfs: - i = led->num_leds - 1; - j = ARRAY_SIZE(qpnp_flash_led_attrs) - 1; -error_led_register: - for (; i >= 0; i--) { - for (; j >= 0; j--) - sysfs_remove_file(&led->flash_node[i].cdev.dev->kobj, - &qpnp_flash_led_attrs[j].attr); - j = ARRAY_SIZE(qpnp_flash_led_attrs) - 1; - led_classdev_unregister(&led->flash_node[i].cdev); - } - debugfs_remove_recursive(root); - mutex_destroy(&led->flash_led_lock); - destroy_workqueue(led->ordered_workq); - - return rc; -} - -static int qpnp_flash_led_remove(struct platform_device *pdev) -{ - struct qpnp_flash_led *led = dev_get_drvdata(&pdev->dev); - int i, j; - - for (i = led->num_leds - 1; i >= 0; i--) { - if (led->flash_node[i].reg_data) { - if (led->flash_node[i].flash_on) - flash_regulator_enable(led, - &led->flash_node[i], false); - flash_regulator_setup(led, &led->flash_node[i], - false); - } - for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) - sysfs_remove_file(&led->flash_node[i].cdev.dev->kobj, - &qpnp_flash_led_attrs[j].attr); - led_classdev_unregister(&led->flash_node[i].cdev); - } - debugfs_remove_recursive(led->dbgfs_root); - mutex_destroy(&led->flash_led_lock); - destroy_workqueue(led->ordered_workq); - - return 0; -} - -static const struct of_device_id spmi_match_table[] = { - { .compatible = "qcom,qpnp-flash-led",}, - { }, -}; - -static struct platform_driver qpnp_flash_led_driver = { - .driver = { - .name = "qcom,qpnp-flash-led", - .of_match_table = spmi_match_table, - }, - .probe = qpnp_flash_led_probe, - .remove = qpnp_flash_led_remove, -}; - -static int __init qpnp_flash_led_init(void) -{ - return platform_driver_register(&qpnp_flash_led_driver); -} -late_initcall(qpnp_flash_led_init); - -static void __exit qpnp_flash_led_exit(void) -{ - platform_driver_unregister(&qpnp_flash_led_driver); -} -module_exit(qpnp_flash_led_exit); - -MODULE_DESCRIPTION("QPNP Flash LED driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("leds:leds-qpnp-flash"); diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c index 1e24c79c3f0a..950244f1e4e8 100644 --- a/drivers/leds/leds-qpnp-wled.c +++ b/drivers/leds/leds-qpnp-wled.c @@ -33,6 +33,7 @@ /* ctrl registers */ #define QPNP_WLED_FAULT_STATUS(b) (b + 0x08) +#define QPNP_WLED_INT_RT_STS(b) (b + 0x10) #define QPNP_WLED_EN_REG(b) (b + 0x46) #define QPNP_WLED_FDBK_OP_REG(b) (b + 0x48) #define QPNP_WLED_VREF_REG(b) (b + 0x49) @@ -44,6 +45,7 @@ #define QPNP_WLED_SOFTSTART_RAMP_DLY(b) (b + 0x53) #define QPNP_WLED_VLOOP_COMP_RES_REG(b) (b + 0x55) #define QPNP_WLED_VLOOP_COMP_GM_REG(b) (b + 0x56) +#define QPNP_WLED_EN_PSM_REG(b) (b + 0x5A) #define QPNP_WLED_PSM_CTRL_REG(b) (b + 0x5B) #define QPNP_WLED_LCD_AUTO_PFM_REG(b) (b + 0x5C) #define QPNP_WLED_SC_PRO_REG(b) (b + 0x5E) @@ -82,12 +84,13 @@ #define QPNP_WLED_VREF_PSM_MIN_MV 400 #define QPNP_WLED_VREF_PSM_MAX_MV 750 #define QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV 450 -#define QPNP_WLED_PSM_CTRL_OVERWRITE 0x80 +#define QPNP_WLED_PSM_OVERWRITE_BIT BIT(7) #define QPNP_WLED_LCD_AUTO_PFM_DFLT_THRESH 1 #define QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX 0xF #define QPNP_WLED_LCD_AUTO_PFM_EN_SHIFT 7 #define QPNP_WLED_LCD_AUTO_PFM_EN_BIT BIT(7) #define QPNP_WLED_LCD_AUTO_PFM_THRESH_MASK GENMASK(3, 0) +#define QPNP_WLED_EN_PSM_BIT BIT(7) #define QPNP_WLED_ILIM_MASK GENMASK(2, 0) #define QPNP_WLED_ILIM_OVERWRITE BIT(7) @@ -117,6 +120,9 @@ QPNP_WLED_TEST4_EN_CLAMP_BIT | \ QPNP_WLED_TEST4_EN_SOFT_START_BIT) #define QPNP_WLED_TEST4_EN_IIND_UP 0x1 +#define QPNP_WLED_ILIM_FAULT_BIT BIT(0) +#define QPNP_WLED_OVP_FAULT_BIT BIT(1) +#define QPNP_WLED_SC_FAULT_BIT BIT(2) /* sink registers */ #define QPNP_WLED_CURR_SINK_REG(b) (b + 0x46) @@ -335,6 +341,7 @@ static struct wled_vref_setting vref_setting_pmi8998 = { * @ lcd_auto_pfm_thresh - the threshold for lcd auto pfm mode * @ loop_auto_gm_en - select if auto gm is enabled * @ lcd_auto_pfm_en - select if auto pfm is enabled in lcd mode + * @ lcd_psm_ctrl - select if psm needs to be controlled in lcd mode * @ avdd_mode_spmi - enable avdd programming via spmi * @ en_9b_dim_res - enable or disable 9bit dimming * @ en_phase_stag - enable or disable phase staggering @@ -380,6 +387,7 @@ struct qpnp_wled { u8 lcd_auto_pfm_thresh; bool loop_auto_gm_en; bool lcd_auto_pfm_en; + bool lcd_psm_ctrl; bool avdd_mode_spmi; bool en_9b_dim_res; bool en_phase_stag; @@ -549,6 +557,30 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level) return 0; } +static int qpnp_wled_psm_config(struct qpnp_wled *wled, bool enable) +{ + int rc; + + if (!wled->lcd_psm_ctrl) + return 0; + + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_EN_PSM_REG(wled->ctrl_base), + QPNP_WLED_EN_PSM_BIT, + enable ? QPNP_WLED_EN_PSM_BIT : 0); + if (rc < 0) + return rc; + + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), + QPNP_WLED_PSM_OVERWRITE_BIT, + enable ? QPNP_WLED_PSM_OVERWRITE_BIT : 0); + if (rc < 0) + return rc; + + return 0; +} + static int qpnp_wled_module_en(struct qpnp_wled *wled, u16 base_addr, bool state) { @@ -561,21 +593,31 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled, if (rc < 0) return rc; - if (wled->ovp_irq > 0) { - if (state && wled->ovp_irq_disabled) { - /* - * Wait for at least 10ms before enabling OVP fault - * interrupt after enabling the module so that soft - * start is completed. Keep OVP interrupt disabled - * when the module is disabled. - */ - usleep_range(10000, 11000); + /* + * Wait for at least 10ms before enabling OVP fault interrupt after + * enabling the module so that soft start is completed. Also, this + * delay can be used to control PSM during enable when required. Keep + * OVP interrupt disabled when the module is disabled. + */ + if (state) { + usleep_range(10000, 11000); + rc = qpnp_wled_psm_config(wled, false); + if (rc < 0) + return rc; + + if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) { enable_irq(wled->ovp_irq); wled->ovp_irq_disabled = false; - } else if (!state && !wled->ovp_irq_disabled) { + } + } else { + if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) { disable_irq(wled->ovp_irq); wled->ovp_irq_disabled = true; } + + rc = qpnp_wled_psm_config(wled, true); + if (rc < 0) + return rc; } return 0; @@ -990,7 +1032,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr) reg &= QPNP_WLED_VREF_PSM_MASK; reg |= ((wled->vref_psm_mv - QPNP_WLED_VREF_PSM_MIN_MV)/ QPNP_WLED_VREF_PSM_STEP_MV); - reg |= QPNP_WLED_PSM_CTRL_OVERWRITE; + reg |= QPNP_WLED_PSM_OVERWRITE_BIT; rc = qpnp_wled_write_reg(wled, QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), reg); if (rc) @@ -1053,16 +1095,25 @@ static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled) { struct qpnp_wled *wled = _wled; int rc; - u8 val; + u8 fault_sts, int_sts; rc = qpnp_wled_read_reg(wled, - QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &val); + QPNP_WLED_INT_RT_STS(wled->ctrl_base), &int_sts); + if (rc < 0) { + pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc); + return IRQ_HANDLED; + } + + rc = qpnp_wled_read_reg(wled, + QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_sts); if (rc < 0) { pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc); return IRQ_HANDLED; } - pr_err("WLED OVP fault detected, fault_status= %x\n", val); + if (fault_sts & (QPNP_WLED_OVP_FAULT_BIT | QPNP_WLED_ILIM_FAULT_BIT)) + pr_err("WLED OVP fault detected, int_sts=%x fault_sts= %x\n", + int_sts, fault_sts); return IRQ_HANDLED; } @@ -1677,6 +1728,8 @@ static int qpnp_wled_config(struct qpnp_wled *wled) wled->ovp_irq, rc); return rc; } + disable_irq(wled->ovp_irq); + wled->ovp_irq_disabled = true; } if (wled->sc_irq >= 0) { @@ -2063,6 +2116,8 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) wled->en_ext_pfet_sc_pro = of_property_read_bool(pdev->dev.of_node, "qcom,en-ext-pfet-sc-pro"); + wled->lcd_psm_ctrl = of_property_read_bool(pdev->dev.of_node, + "qcom,lcd-psm-ctrl"); return 0; } diff --git a/drivers/lightnvm/rrpc.c b/drivers/lightnvm/rrpc.c index a9859489acf6..596347f345db 100644 --- a/drivers/lightnvm/rrpc.c +++ b/drivers/lightnvm/rrpc.c @@ -287,8 +287,10 @@ static int rrpc_move_valid_pages(struct rrpc *rrpc, struct rrpc_block *rblk) } page = mempool_alloc(rrpc->page_pool, GFP_NOIO); - if (!page) + if (!page) { + bio_put(bio); return -ENOMEM; + } while ((slot = find_first_zero_bit(rblk->invalid_pages, nr_pgs_per_blk)) < nr_pgs_per_blk) { diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index e85bcae50f65..e540b7942eba 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -112,8 +112,7 @@ struct iv_tcw_private { * and encrypts / decrypts at the same time. */ enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID, - DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD, - DM_CRYPT_EXIT_THREAD}; + DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD }; /* * The fields in here must be read only after initialization. @@ -1204,18 +1203,20 @@ continue_locked: if (!RB_EMPTY_ROOT(&cc->write_tree)) goto pop_from_list; - if (unlikely(test_bit(DM_CRYPT_EXIT_THREAD, &cc->flags))) { - spin_unlock_irq(&cc->write_thread_wait.lock); - break; - } - - __set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_INTERRUPTIBLE); __add_wait_queue(&cc->write_thread_wait, &wait); spin_unlock_irq(&cc->write_thread_wait.lock); + if (unlikely(kthread_should_stop())) { + set_task_state(current, TASK_RUNNING); + remove_wait_queue(&cc->write_thread_wait, &wait); + break; + } + schedule(); + set_task_state(current, TASK_RUNNING); spin_lock_irq(&cc->write_thread_wait.lock); __remove_wait_queue(&cc->write_thread_wait, &wait); goto continue_locked; @@ -1530,13 +1531,8 @@ static void crypt_dtr(struct dm_target *ti) if (!cc) return; - if (cc->write_thread) { - spin_lock_irq(&cc->write_thread_wait.lock); - set_bit(DM_CRYPT_EXIT_THREAD, &cc->flags); - wake_up_locked(&cc->write_thread_wait); - spin_unlock_irq(&cc->write_thread_wait.lock); + if (cc->write_thread) kthread_stop(cc->write_thread); - } if (cc->io_queue) destroy_workqueue(cc->io_queue); @@ -1928,6 +1924,13 @@ static int crypt_map(struct dm_target *ti, struct bio *bio) return DM_MAPIO_REMAPPED; } + /* + * Check if bio is too large, split as needed. + */ + if (unlikely(bio->bi_iter.bi_size > (BIO_MAX_PAGES << PAGE_SHIFT)) && + bio_data_dir(bio) == WRITE) + dm_accept_partial_bio(bio, ((BIO_MAX_PAGES << PAGE_SHIFT) >> SECTOR_SHIFT)); + io = dm_per_bio_data(bio, cc->per_bio_data_size); crypt_io_init(io, cc, bio, dm_target_offset(ti, bio->bi_iter.bi_sector)); io->ctx.req = (struct ablkcipher_request *)(io + 1); diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c index cd0a93df4cb7..8e9e928dafba 100644 --- a/drivers/md/dm-flakey.c +++ b/drivers/md/dm-flakey.c @@ -289,15 +289,13 @@ static int flakey_map(struct dm_target *ti, struct bio *bio) pb->bio_submitted = true; /* - * Map reads as normal only if corrupt_bio_byte set. + * Error reads if neither corrupt_bio_byte or drop_writes are set. + * Otherwise, flakey_end_io() will decide if the reads should be modified. */ if (bio_data_dir(bio) == READ) { - /* If flags were specified, only corrupt those that match. */ - if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == READ) && - all_corrupt_bio_flags_match(bio, fc)) - goto map_bio; - else + if (!fc->corrupt_bio_byte && !test_bit(DROP_WRITES, &fc->flags)) return -EIO; + goto map_bio; } /* @@ -334,14 +332,21 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio, int error) struct flakey_c *fc = ti->private; struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data)); - /* - * Corrupt successful READs while in down state. - */ if (!error && pb->bio_submitted && (bio_data_dir(bio) == READ)) { - if (fc->corrupt_bio_byte) + if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == READ) && + all_corrupt_bio_flags_match(bio, fc)) { + /* + * Corrupt successful matching READs while in down state. + */ corrupt_bio_data(bio, fc); - else + + } else if (!test_bit(DROP_WRITES, &fc->flags)) { + /* + * Error read during the down_interval if drop_writes + * wasn't configured. + */ return -EIO; + } } return error; diff --git a/drivers/md/dm-log-writes.c b/drivers/md/dm-log-writes.c index 624589d51c2c..c8b513ee117c 100644 --- a/drivers/md/dm-log-writes.c +++ b/drivers/md/dm-log-writes.c @@ -258,12 +258,12 @@ static int log_one_block(struct log_writes_c *lc, goto out; sector++; - bio = bio_alloc(GFP_KERNEL, block->vec_cnt); + atomic_inc(&lc->io_blocks); + bio = bio_alloc(GFP_KERNEL, min(block->vec_cnt, BIO_MAX_PAGES)); if (!bio) { DMERR("Couldn't alloc log bio"); goto error; } - atomic_inc(&lc->io_blocks); bio->bi_iter.bi_size = 0; bio->bi_iter.bi_sector = sector; bio->bi_bdev = lc->logdev->bdev; @@ -280,7 +280,7 @@ static int log_one_block(struct log_writes_c *lc, if (ret != block->vecs[i].bv_len) { atomic_inc(&lc->io_blocks); submit_bio(WRITE, bio); - bio = bio_alloc(GFP_KERNEL, block->vec_cnt - i); + bio = bio_alloc(GFP_KERNEL, min(block->vec_cnt - i, BIO_MAX_PAGES)); if (!bio) { DMERR("Couldn't alloc log bio"); goto error; @@ -456,9 +456,9 @@ static int log_writes_ctr(struct dm_target *ti, unsigned int argc, char **argv) goto bad; } - ret = -EINVAL; lc->log_kthread = kthread_run(log_writes_kthread, lc, "log-write"); - if (!lc->log_kthread) { + if (IS_ERR(lc->log_kthread)) { + ret = PTR_ERR(lc->log_kthread); ti->error = "Couldn't alloc kthread"; dm_put_device(ti, lc->dev); dm_put_device(ti, lc->logdev); diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index cfa29f574c2a..5b2ef966012b 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1220,10 +1220,10 @@ static void activate_path(struct work_struct *work) { struct pgpath *pgpath = container_of(work, struct pgpath, activate_path.work); + struct request_queue *q = bdev_get_queue(pgpath->path.dev->bdev); - if (pgpath->is_active) - scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev), - pg_init_done, pgpath); + if (pgpath->is_active && !blk_queue_dying(q)) + scsi_dh_activate(q, pg_init_done, pgpath); else pg_init_done(pgpath, SCSI_DH_DEV_OFFLINED); } diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index f2a363a89629..115bd3846c3f 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -1288,6 +1288,7 @@ static int mirror_end_io(struct dm_target *ti, struct bio *bio, int error) dm_bio_restore(bd, bio); bio_record->details.bi_bdev = NULL; + bio->bi_error = 0; queue_bio(ms, bio, rw); return DM_ENDIO_INCOMPLETE; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index d8615788b17d..dd1cde5458b8 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2267,8 +2267,6 @@ static void cleanup_mapped_device(struct mapped_device *md) if (md->bs) bioset_free(md->bs); - cleanup_srcu_struct(&md->io_barrier); - if (md->disk) { spin_lock(&_minor_lock); md->disk->private_data = NULL; @@ -2280,6 +2278,8 @@ static void cleanup_mapped_device(struct mapped_device *md) if (md->queue) blk_cleanup_queue(md->queue); + cleanup_srcu_struct(&md->io_barrier); + if (md->bdev) { bdput(md->bdev); md->bdev = NULL; @@ -2876,6 +2876,7 @@ EXPORT_SYMBOL_GPL(dm_device_name); static void __dm_destroy(struct mapped_device *md, bool wait) { + struct request_queue *q = dm_get_md_queue(md); struct dm_table *map; int srcu_idx; @@ -2886,6 +2887,10 @@ static void __dm_destroy(struct mapped_device *md, bool wait) set_bit(DMF_FREEING, &md->flags); spin_unlock(&_minor_lock); + spin_lock_irq(q->queue_lock); + queue_flag_set(QUEUE_FLAG_DYING, q); + spin_unlock_irq(q->queue_lock); + if (dm_request_based(md) && md->kworker_task) flush_kthread_worker(&md->kworker); @@ -3252,10 +3257,11 @@ static int __dm_resume(struct mapped_device *md, struct dm_table *map) int dm_resume(struct mapped_device *md) { - int r = -EINVAL; + int r; struct dm_table *map = NULL; retry: + r = -EINVAL; mutex_lock_nested(&md->suspend_lock, SINGLE_DEPTH_NESTING); if (!dm_suspended_md(md)) @@ -3279,8 +3285,6 @@ retry: goto out; clear_bit(DMF_SUSPENDED, &md->flags); - - r = 0; out: mutex_unlock(&md->suspend_lock); diff --git a/drivers/md/md.c b/drivers/md/md.c index c57fdf847b47..c1c7d4fb4b77 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -7572,16 +7572,12 @@ EXPORT_SYMBOL(unregister_md_cluster_operations); int md_setup_cluster(struct mddev *mddev, int nodes) { - int err; - - err = request_module("md-cluster"); - if (err) { - pr_err("md-cluster module not found.\n"); - return -ENOENT; - } - + if (!md_cluster_ops) + request_module("md-cluster"); spin_lock(&pers_lock); + /* ensure module won't be unloaded */ if (!md_cluster_ops || !try_module_get(md_cluster_mod)) { + pr_err("can't find md-cluster module or get it's reference.\n"); spin_unlock(&pers_lock); return -ENOENT; } diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index cfc005ee11d8..7fc72de2434c 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -71,25 +71,27 @@ static struct regdata mb86a20s_init1[] = { }; static struct regdata mb86a20s_init2[] = { - { 0x28, 0x22 }, { 0x29, 0x00 }, { 0x2a, 0x1f }, { 0x2b, 0xf0 }, + { 0x50, 0xd1 }, { 0x51, 0x22 }, + { 0x39, 0x01 }, + { 0x71, 0x00 }, { 0x3b, 0x21 }, - { 0x3c, 0x38 }, + { 0x3c, 0x3a }, { 0x01, 0x0d }, - { 0x04, 0x08 }, { 0x05, 0x03 }, + { 0x04, 0x08 }, { 0x05, 0x05 }, { 0x04, 0x0e }, { 0x05, 0x00 }, - { 0x04, 0x0f }, { 0x05, 0x37 }, - { 0x04, 0x0b }, { 0x05, 0x78 }, + { 0x04, 0x0f }, { 0x05, 0x14 }, + { 0x04, 0x0b }, { 0x05, 0x8c }, { 0x04, 0x00 }, { 0x05, 0x00 }, - { 0x04, 0x01 }, { 0x05, 0x1e }, - { 0x04, 0x02 }, { 0x05, 0x07 }, - { 0x04, 0x03 }, { 0x05, 0xd0 }, + { 0x04, 0x01 }, { 0x05, 0x07 }, + { 0x04, 0x02 }, { 0x05, 0x0f }, + { 0x04, 0x03 }, { 0x05, 0xa0 }, { 0x04, 0x09 }, { 0x05, 0x00 }, { 0x04, 0x0a }, { 0x05, 0xff }, - { 0x04, 0x27 }, { 0x05, 0x00 }, + { 0x04, 0x27 }, { 0x05, 0x64 }, { 0x04, 0x28 }, { 0x05, 0x00 }, - { 0x04, 0x1e }, { 0x05, 0x00 }, - { 0x04, 0x29 }, { 0x05, 0x64 }, - { 0x04, 0x32 }, { 0x05, 0x02 }, + { 0x04, 0x1e }, { 0x05, 0xff }, + { 0x04, 0x29 }, { 0x05, 0x0a }, + { 0x04, 0x32 }, { 0x05, 0x0a }, { 0x04, 0x14 }, { 0x05, 0x02 }, { 0x04, 0x04 }, { 0x05, 0x00 }, { 0x04, 0x05 }, { 0x05, 0x22 }, @@ -97,8 +99,6 @@ static struct regdata mb86a20s_init2[] = { { 0x04, 0x07 }, { 0x05, 0xd8 }, { 0x04, 0x12 }, { 0x05, 0x00 }, { 0x04, 0x13 }, { 0x05, 0xff }, - { 0x04, 0x15 }, { 0x05, 0x4e }, - { 0x04, 0x16 }, { 0x05, 0x20 }, /* * On this demod, when the bit count reaches the count below, @@ -152,42 +152,36 @@ static struct regdata mb86a20s_init2[] = { { 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */ { 0x45, 0x04 }, /* CN symbol 4 */ { 0x48, 0x04 }, /* CN manual mode */ - + { 0x50, 0xd5 }, { 0x51, 0x01 }, { 0x50, 0xd6 }, { 0x51, 0x1f }, { 0x50, 0xd2 }, { 0x51, 0x03 }, - { 0x50, 0xd7 }, { 0x51, 0xbf }, - { 0x28, 0x74 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0xff }, - { 0x28, 0x46 }, { 0x29, 0x00 }, { 0x2a, 0x1a }, { 0x2b, 0x0c }, - - { 0x04, 0x40 }, { 0x05, 0x00 }, - { 0x28, 0x00 }, { 0x2b, 0x08 }, - { 0x28, 0x05 }, { 0x2b, 0x00 }, + { 0x50, 0xd7 }, { 0x51, 0x3f }, { 0x1c, 0x01 }, - { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x1f }, - { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x18 }, - { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x12 }, - { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x30 }, - { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x37 }, - { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 }, - { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x09 }, - { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x06 }, - { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x7b }, - { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x76 }, - { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x7d }, - { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x08 }, - { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0b }, - { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 }, - { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xf2 }, - { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xf3 }, - { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x05 }, - { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 }, - { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f }, - { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xef }, - { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xd8 }, - { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xf1 }, - { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x3d }, - { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x94 }, - { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0xba }, + { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x03 }, + { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0d }, + { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 }, + { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x01 }, + { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x21 }, + { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x29 }, + { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 }, + { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x31 }, + { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0e }, + { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x4e }, + { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x46 }, + { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f }, + { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x56 }, + { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x35 }, + { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbe }, + { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0x84 }, + { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x03 }, { 0x2b, 0xee }, + { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x98 }, + { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x9f }, + { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xb2 }, + { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0xc2 }, + { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0x4a }, + { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbc }, + { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x04 }, { 0x2b, 0xba }, + { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0x14 }, { 0x50, 0x1e }, { 0x51, 0x5d }, { 0x50, 0x22 }, { 0x51, 0x00 }, { 0x50, 0x23 }, { 0x51, 0xc8 }, @@ -196,9 +190,7 @@ static struct regdata mb86a20s_init2[] = { { 0x50, 0x26 }, { 0x51, 0x00 }, { 0x50, 0x27 }, { 0x51, 0xc3 }, { 0x50, 0x39 }, { 0x51, 0x02 }, - { 0xec, 0x0f }, - { 0xeb, 0x1f }, - { 0x28, 0x6a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 }, + { 0x50, 0xd5 }, { 0x51, 0x01 }, { 0xd0, 0x00 }, }; @@ -317,7 +309,11 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, enum fe_status *status) if (val >= 7) *status |= FE_HAS_SYNC; - if (val >= 8) /* Maybe 9? */ + /* + * Actually, on state S8, it starts receiving TS, but the TS + * output is only on normal state after the transition to S9. + */ + if (val >= 9) *status |= FE_HAS_LOCK; dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n", @@ -2067,6 +2063,11 @@ static void mb86a20s_release(struct dvb_frontend *fe) kfree(state); } +static int mb86a20s_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + static struct dvb_frontend_ops mb86a20s_ops; struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, @@ -2140,6 +2141,7 @@ static struct dvb_frontend_ops mb86a20s_ops = { .read_status = mb86a20s_read_status_and_stats, .read_signal_strength = mb86a20s_read_signal_strength_from_cache, .tune = mb86a20s_tune, + .get_frontend_algo = mb86a20s_get_frontend_algo, }; MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware"); diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index f0480d687f17..ba780c45f645 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -1706,7 +1706,7 @@ static int vpfe_get_app_input_index(struct vpfe_device *vpfe, sdinfo = &cfg->sub_devs[i]; client = v4l2_get_subdevdata(sdinfo->sd); if (client->addr == curr_client->addr && - client->adapter->nr == client->adapter->nr) { + client->adapter->nr == curr_client->adapter->nr) { if (vpfe->current_input >= 1) return -1; *app_input_index = j + vpfe->current_input; 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 f7eb0f8ac5a8..8d66232dbda1 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 @@ -196,6 +196,13 @@ static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr, __func__, stream_id); return -EINVAL; } + + if (qbuf_buf->num_planes > MAX_PLANES_PER_STREAM) { + pr_err("%s: Invalid num_planes %d , stream id %x\n", + __func__, qbuf_buf->num_planes, stream_id); + return -EINVAL; + } + for (i = 0; i < qbuf_buf->num_planes; i++) { mapped_info = &buf_info->mapped_info[i]; mapped_info->buf_fd = qbuf_buf->planes[i].addr; @@ -249,6 +256,12 @@ static void msm_isp_unprepare_v4l2_buf( return; } + if (buf_info->num_planes > VIDEO_MAX_PLANES) { + pr_err("%s: Invalid num_planes %d , stream id %x\n", + __func__, buf_info->num_planes, stream_id); + return; + } + bufq = msm_isp_get_bufq(buf_mgr, buf_info->bufq_handle); if (!bufq) { pr_err("%s: Invalid bufq, stream id %x\n", 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 840d84388a17..bb3f0dca9d92 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.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 @@ -588,6 +588,12 @@ int vfe_hw_probe(struct platform_device *pdev) } vfe_dev->hw_info = (struct msm_vfe_hardware_info *) match_dev->data; + /* Cx ipeak support */ + if (of_find_property(pdev->dev.of_node, + "qcom,vfe_cx_ipeak", NULL)) { + vfe_dev->vfe_cx_ipeak = cx_ipeak_register( + pdev->dev.of_node, "qcom,vfe_cx_ipeak"); + } } else { vfe_dev->hw_info = (struct msm_vfe_hardware_info *) platform_get_device_id(pdev)->driver_data; 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 f6fabc61620d..aca8e99650ba 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -29,6 +29,7 @@ #include "msm_buf_mgr.h" #include "cam_hw_ops.h" +#include <soc/qcom/cx_ipeak.h> #define VFE40_8974V1_VERSION 0x10000018 #define VFE40_8974V2_VERSION 0x1001001A @@ -767,6 +768,8 @@ struct vfe_device { size_t num_hvx_clk; size_t num_norm_clk; enum cam_ahb_clk_vote ahb_vote; + bool turbo_vote; + struct cx_ipeak_client *vfe_cx_ipeak; /* Sync variables*/ struct completion reset_complete; 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 c7f3b97c83c9..57373c1fc74c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -331,6 +331,7 @@ 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; @@ -763,7 +764,7 @@ long msm_vfe47_reset_hardware(struct vfe_device *vfe_dev, } if (blocking_call) { - rc = wait_for_completion_timeout( + rc = wait_for_completion_interruptible_timeout( &vfe_dev->reset_complete, msecs_to_jiffies(100)); if (rc <= 0) { pr_err("%s:%d failed: reset timeout\n", __func__, @@ -1930,7 +1931,7 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev, init_completion(&vfe_dev->halt_complete); /* Halt AXI Bus Bridge */ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x400); - rc = wait_for_completion_timeout( + rc = wait_for_completion_interruptible_timeout( &vfe_dev->halt_complete, msecs_to_jiffies(500)); if (rc <= 0) pr_err("%s:VFE%d halt timeout rc=%d\n", __func__, @@ -2556,6 +2557,7 @@ 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; rc = msm_camera_clk_set_rate(&vfe_dev->pdev->dev, vfe_dev->vfe_clk[clk_idx], *rate); @@ -2563,7 +2565,26 @@ int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate) 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 (vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg) vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg(vfe_dev, NULL); return 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 5264bba57c8d..1628c098622f 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.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 @@ -133,7 +133,7 @@ static void msm_ispif_get_pack_mask_from_cfg( pack_mask[0] |= temp; CDBG("%s:num %d cid %d mode %d pack_mask %x %x\n", __func__, entry->num_cids, entry->cids[i], - pack_cfg[i].pack_mode, + pack_cfg[entry->cids[i]].pack_mode, pack_mask[0], pack_mask[1]); } @@ -166,6 +166,16 @@ static int msm_ispif_config2(struct ispif_device *ispif, } for (i = 0; i < params->num; i++) { + int j; + + if (params->entries[i].num_cids > MAX_CID_CH_PARAM_ENTRY) + return -EINVAL; + for (j = 0; j < params->entries[i].num_cids; j++) + if (params->entries[i].cids[j] >= CID_MAX) + return -EINVAL; + } + + for (i = 0; i < params->num; i++) { intftype = params->entries[i].intftype; vfe_intf = params->entries[i].vfe_intf; @@ -443,7 +453,7 @@ static int msm_ispif_reset_hw(struct ispif_device *ispif) msm_camera_io_w(ISPIF_RST_CMD_MASK, ispif->base + ISPIF_RST_CMD_ADDR); - timeout = wait_for_completion_timeout( + timeout = wait_for_completion_interruptible_timeout( &ispif->reset_complete[VFE0], msecs_to_jiffies(500)); CDBG("%s: VFE0 done\n", __func__); @@ -457,7 +467,7 @@ static int msm_ispif_reset_hw(struct ispif_device *ispif) atomic_set(&ispif->reset_trig[VFE1], 1); msm_camera_io_w(ISPIF_RST_CMD_1_MASK, ispif->base + ISPIF_RST_CMD_1_ADDR); - timeout = wait_for_completion_timeout( + timeout = wait_for_completion_interruptible_timeout( &ispif->reset_complete[VFE1], msecs_to_jiffies(500)); CDBG("%s: VFE1 done\n", __func__); @@ -1120,7 +1130,7 @@ static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif, /* initiate reset of ISPIF */ msm_camera_io_w(ISPIF_RST_CMD_MASK_RESTART, ispif->base + ISPIF_RST_CMD_ADDR); - timeout = wait_for_completion_timeout( + timeout = wait_for_completion_interruptible_timeout( &ispif->reset_complete[VFE0], msecs_to_jiffies(500)); if (timeout <= 0) { pr_err("%s: VFE0 reset wait timeout\n", __func__); @@ -1133,7 +1143,7 @@ static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif, atomic_set(&ispif->reset_trig[VFE1], 1); msm_camera_io_w(ISPIF_RST_CMD_1_MASK_RESTART, ispif->base + ISPIF_RST_CMD_1_ADDR); - timeout = wait_for_completion_timeout( + timeout = wait_for_completion_interruptible_timeout( &ispif->reset_complete[VFE1], msecs_to_jiffies(500)); if (timeout <= 0) { 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 3b38882c4c45..88d90d0a7c08 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 @@ -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 @@ -72,6 +72,18 @@ static const struct msm_jpegdma_block msm_jpegdma_block_sel[] = { }; /* +* jpegdma_do_div - long division. +* @num: dividend +* @den: divisor +* returns quotient value. +*/ +static inline long long jpegdma_do_div(long long num, long long den) +{ + do_div(num, den); + return num; +} + +/* * msm_jpegdma_hw_read_reg - dma read from register. * @dma: Pointer to dma device. * @base_idx: dma memory resource index. @@ -819,9 +831,9 @@ static int msm_jpegdma_hw_calc_speed(struct msm_jpegdma_device *dma, } speed->bus_ab = calc_rate * 2; - speed->bus_ib = (real_clock * - (MSM_JPEGDMA_BW_NUM + MSM_JPEGDMA_BW_DEN - 1)) / - MSM_JPEGDMA_BW_DEN; + speed->bus_ib = jpegdma_do_div((real_clock * + (MSM_JPEGDMA_BW_NUM + MSM_JPEGDMA_BW_DEN - 1)), + MSM_JPEGDMA_BW_DEN); speed->core_clock = real_clock; dev_dbg(dma->dev, "Speed core clk %llu ab %llu ib %llu fps %d\n", speed->core_clock, speed->bus_ab, speed->bus_ib, size->fps); @@ -923,13 +935,15 @@ static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg, in_width = size_cfg->in_size.width; out_width = size_cfg->out_size.width; - scale_hor = (in_width * MSM_JPEGDMA_SCALE_UNI) / out_width; + scale_hor = jpegdma_do_div((in_width * MSM_JPEGDMA_SCALE_UNI), + out_width); if (scale_hor != MSM_JPEGDMA_SCALE_UNI) config->scale_cfg.enable = 1; in_height = size_cfg->in_size.height; out_height = size_cfg->out_size.height; - scale_ver = (in_height * MSM_JPEGDMA_SCALE_UNI) / out_height; + scale_ver = jpegdma_do_div((in_height * MSM_JPEGDMA_SCALE_UNI), + out_height); if (scale_ver != MSM_JPEGDMA_SCALE_UNI) config->scale_cfg.enable = 1; @@ -946,23 +960,23 @@ static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg, config->block_cfg.block = msm_jpegdma_block_sel[i]; if (plane->active_pipes > 1) { - phase = (out_height * scale_ver + (plane->active_pipes - 1)) / - plane->active_pipes; + phase = jpegdma_do_div((out_height * scale_ver + + (plane->active_pipes - 1)), plane->active_pipes); phase &= (MSM_JPEGDMA_SCALE_UNI - 1); - out_height = (out_height + (plane->active_pipes - 1)) / - plane->active_pipes; + out_height = jpegdma_do_div((out_height + + (plane->active_pipes - 1)), plane->active_pipes); in_height = (out_height * scale_ver) / MSM_JPEGDMA_SCALE_UNI; } - config->block_cfg.blocks_per_row = out_width / - config->block_cfg.block.width; + config->block_cfg.blocks_per_row = (uint32_t) jpegdma_do_div(out_width, + config->block_cfg.block.width); config->block_cfg.blocks_per_col = out_height; config->block_cfg.h_step = config->block_cfg.block.width; - - config->block_cfg.h_step_last = out_width % - config->block_cfg.block.width; + config->size_cfg.out_size.width = out_width; + config->block_cfg.h_step_last = (uint32_t) do_div(out_width, + config->block_cfg.block.width); if (!config->block_cfg.h_step_last) config->block_cfg.h_step_last = config->block_cfg.h_step; else @@ -974,7 +988,6 @@ static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg, config->size_cfg = *size_cfg; config->size_cfg.in_size.width = in_width; config->size_cfg.in_size.height = in_height; - config->size_cfg.out_size.width = out_width; config->size_cfg.out_size.height = out_height; config->in_offset = 0; config->out_offset = 0; @@ -1013,14 +1026,16 @@ int msm_jpegdma_hw_check_config(struct msm_jpegdma_device *dma, in_width = size_cfg->in_size.width; out_width = size_cfg->out_size.width; - scale = ((in_width * MSM_JPEGDMA_SCALE_UNI)) / out_width; + scale = jpegdma_do_div(((in_width * MSM_JPEGDMA_SCALE_UNI)), + out_width); if (scale < MSM_JPEGDMA_SCALE_UNI) return -EINVAL; in_height = size_cfg->in_size.height; out_height = size_cfg->out_size.height; - scale = (in_height * MSM_JPEGDMA_SCALE_UNI) / out_height; + scale = jpegdma_do_div((in_height * MSM_JPEGDMA_SCALE_UNI), + out_height); if (scale < MSM_JPEGDMA_SCALE_UNI) return -EINVAL; @@ -1827,7 +1842,7 @@ int msm_jpegdma_hw_map_buffer(struct msm_jpegdma_device *dma, int fd, buf->fd = fd; ret = cam_smmu_get_phy_addr(dma->iommu_hndl, buf->fd, - CAM_SMMU_MAP_RW, &buf->addr, &buf->size); + CAM_SMMU_MAP_RW, &buf->addr, (size_t *)&buf->size); if (ret < 0) { dev_err(dma->dev, "Can not get physical address\n"); goto error_get_phy; diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index 5cf5582b55ab..c2b42a854d35 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -1119,17 +1119,21 @@ long msm_copy_camera_private_ioctl_args(unsigned long arg, struct msm_camera_private_ioctl_arg *k_ioctl, void __user **tmp_compat_ioctl_ptr) { - struct msm_camera_private_ioctl_arg *up_ioctl_ptr = - (struct msm_camera_private_ioctl_arg *)arg; + struct msm_camera_private_ioctl_arg up_ioctl; if (WARN_ON(!arg || !k_ioctl || !tmp_compat_ioctl_ptr)) return -EIO; - k_ioctl->id = up_ioctl_ptr->id; - k_ioctl->size = up_ioctl_ptr->size; - k_ioctl->result = up_ioctl_ptr->result; - k_ioctl->reserved = up_ioctl_ptr->reserved; - *tmp_compat_ioctl_ptr = compat_ptr(up_ioctl_ptr->ioctl_ptr); + if (copy_from_user(&up_ioctl, + (struct msm_camera_private_ioctl_arg *)arg, + sizeof(struct msm_camera_private_ioctl_arg))) + return -EFAULT; + + k_ioctl->id = up_ioctl.id; + k_ioctl->size = up_ioctl.size; + k_ioctl->result = up_ioctl.result; + k_ioctl->reserved = up_ioctl.reserved; + *tmp_compat_ioctl_ptr = compat_ptr(up_ioctl.ioctl_ptr); return 0; } diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c index 730f8b32ff1a..76a7c6942c68 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.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 @@ -149,10 +149,7 @@ static int32_t msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev, list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) { if ((bufs->session_id == buf_info->session_id) && (bufs->stream_id == buf_info->stream_id) && - (bufs->vb2_v4l2_buf->vb2_buf.index == - buf_info->index)) { - bufs->vb2_v4l2_buf->sequence = buf_info->frame_id; - bufs->vb2_v4l2_buf->timestamp = buf_info->timestamp; + (bufs->index == buf_info->index)) { ret = buf_mngr_dev->vb2_ops.buf_done (bufs->vb2_v4l2_buf, buf_info->session_id, @@ -181,7 +178,7 @@ static int32_t msm_buf_mngr_put_buf(struct msm_buf_mngr_device *buf_mngr_dev, list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) { if ((bufs->session_id == buf_info->session_id) && (bufs->stream_id == buf_info->stream_id) && - (bufs->vb2_v4l2_buf->vb2_buf.index == buf_info->index)) { + (bufs->index == buf_info->index)) { ret = buf_mngr_dev->vb2_ops.put_buf(bufs->vb2_v4l2_buf, buf_info->session_id, buf_info->stream_id); list_del_init(&bufs->entry); @@ -214,7 +211,7 @@ static int32_t msm_generic_buf_mngr_flush( buf_info->session_id, buf_info->stream_id, 0, &ts, 0); pr_err("Bufs not flushed: str_id = %d buf_index = %d ret = %d\n", - buf_info->stream_id, bufs->vb2_v4l2_buf->vb2_buf.index, + buf_info->stream_id, bufs->index, ret); list_del_init(&bufs->entry); kfree(bufs); diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h index 07d522cb01bb..9120a4cc85ca 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h @@ -1,4 +1,4 @@ -/* 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 @@ -99,6 +99,7 @@ struct csiphy_reg_3ph_parms_t csiphy_v5_0_1_3ph = { {0x70C, 0xA5}, {0x38, 0xFE}, {0x81c, 0x2}, + {0x700, 0x80}, }; struct csiphy_settings_t csiphy_combo_mode_v5_0_1 = { diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h index 198d130b24fc..8591f0646080 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h @@ -1,4 +1,4 @@ -/* 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 @@ -100,6 +100,7 @@ struct csiphy_reg_3ph_parms_t csiphy_v5_0_3ph = { {0x70C, 0x16}, {0x38, 0xFE}, {0x81c, 0x6}, + {0x700, 0x80}, }; struct csiphy_settings_t csiphy_combo_mode_v5_0 = { 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 a7cd44636d1d..be266641a105 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 @@ -1,4 +1,4 @@ -/* 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 @@ -46,6 +46,7 @@ #define MSM_CSIPHY_DRV_NAME "msm_csiphy" #define CLK_LANE_OFFSET 1 #define NUM_LANES_OFFSET 4 +#define CLOCK_LANE 0x02 #define CSI_3PHASE_HW 1 #define MAX_DPHY_DATA_LN 4 @@ -683,12 +684,21 @@ static int msm_csiphy_2phase_lane_config_v50( csiphybase + csiphy_dev->ctrl_reg-> csiphy_3ph_reg. mipi_csiphy_2ph_lnn_ctrl15.addr + offset); - msm_camera_io_w(csiphy_dev->ctrl_reg-> - csiphy_3ph_reg. - mipi_csiphy_2ph_lnn_ctrl0.data, - csiphybase + csiphy_dev->ctrl_reg-> - csiphy_3ph_reg. - mipi_csiphy_2ph_lnn_ctrl0.addr + offset); + if (mask == CLOCK_LANE) + msm_camera_io_w(csiphy_dev->ctrl_reg-> + csiphy_3ph_reg. + mipi_csiphy_2ph_lnck_ctrl0.data, + csiphybase + csiphy_dev->ctrl_reg-> + csiphy_3ph_reg. + mipi_csiphy_2ph_lnck_ctrl0.addr); + else + msm_camera_io_w(csiphy_dev->ctrl_reg-> + csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_ctrl0.data, + csiphybase + csiphy_dev->ctrl_reg-> + csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_ctrl0.addr + + offset); msm_camera_io_w(csiphy_dev->ctrl_reg-> csiphy_3ph_reg. mipi_csiphy_2ph_lnn_cfg1.data, diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h index 70462dcd3b12..c1a9748e8af5 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h @@ -1,4 +1,4 @@ -/* 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 @@ -141,6 +141,7 @@ struct csiphy_reg_3ph_parms_t { struct csiphy_reg_t mipi_csiphy_2ph_lnck_ctrl3; struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl14; struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl7_cphy; + struct csiphy_reg_t mipi_csiphy_2ph_lnck_ctrl0; }; struct csiphy_ctrl_t { diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c index a2da663e2046..f41382b5b20c 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c @@ -990,11 +990,14 @@ static int sde_rotator_debug_base_release(struct inode *inode, { struct sde_rotator_debug_base *dbg = file->private_data; - if (dbg && dbg->buf) { + if (dbg) { + mutex_lock(&dbg->buflock); kfree(dbg->buf); dbg->buf_len = 0; dbg->buf = NULL; + mutex_unlock(&dbg->buflock); } + return 0; } @@ -1026,8 +1029,10 @@ static ssize_t sde_rotator_debug_base_offset_write(struct file *file, if (cnt > (dbg->max_offset - off)) cnt = dbg->max_offset - off; + mutex_lock(&dbg->buflock); dbg->off = off; dbg->cnt = cnt; + mutex_unlock(&dbg->buflock); SDEROT_DBG("offset=%x cnt=%x\n", off, cnt); @@ -1047,7 +1052,10 @@ static ssize_t sde_rotator_debug_base_offset_read(struct file *file, if (*ppos) return 0; /* the end */ + mutex_lock(&dbg->buflock); len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt); + mutex_unlock(&dbg->buflock); + if (len < 0 || len >= sizeof(buf)) return 0; @@ -1086,6 +1094,8 @@ static ssize_t sde_rotator_debug_base_reg_write(struct file *file, if (off >= dbg->max_offset) return -EFAULT; + mutex_lock(&dbg->buflock); + /* Enable Clock for register access */ sde_rotator_clk_ctrl(dbg->mgr, true); @@ -1094,6 +1104,8 @@ static ssize_t sde_rotator_debug_base_reg_write(struct file *file, /* Disable Clock after register access */ sde_rotator_clk_ctrl(dbg->mgr, false); + mutex_unlock(&dbg->buflock); + SDEROT_DBG("addr=%zx data=%x\n", off, data); return count; @@ -1104,12 +1116,14 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file, { struct sde_rotator_debug_base *dbg = file->private_data; size_t len; + int rc = 0; if (!dbg) { SDEROT_ERR("invalid handle\n"); return -ENODEV; } + mutex_lock(&dbg->buflock); if (!dbg->buf) { char dump_buf[64]; char *ptr; @@ -1121,7 +1135,8 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file, if (!dbg->buf) { SDEROT_ERR("not enough memory to hold reg dump\n"); - return -ENOMEM; + rc = -ENOMEM; + goto debug_read_error; } ptr = dbg->base + dbg->off; @@ -1151,18 +1166,26 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file, dbg->buf_len = tot; } - if (*ppos >= dbg->buf_len) - return 0; /* done reading */ + if (*ppos >= dbg->buf_len) { + rc = 0; /* done reading */ + goto debug_read_error; + } len = min(count, dbg->buf_len - (size_t) *ppos); if (copy_to_user(user_buf, dbg->buf + *ppos, len)) { SDEROT_ERR("failed to copy to user\n"); - return -EFAULT; + rc = -EFAULT; + goto debug_read_error; } *ppos += len; /* increase offset */ + mutex_unlock(&dbg->buflock); return len; + +debug_read_error: + mutex_unlock(&dbg->buflock); + return rc; } static const struct file_operations sde_rotator_off_fops = { @@ -1196,6 +1219,9 @@ int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev, if (!dbg) return -ENOMEM; + mutex_init(&dbg->buflock); + mutex_lock(&dbg->buflock); + if (name) strlcpy(dbg->name, name, sizeof(dbg->name)); dbg->base = io_data->base; @@ -1217,6 +1243,7 @@ int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev, dbg->base += rot_dev->mdata->regdump ? rot_dev->mdata->regdump[0].offset : 0; } + mutex_unlock(&dbg->buflock); strlcpy(dbgname + prefix_len, "off", sizeof(dbgname) - prefix_len); ent_off = debugfs_create_file(dbgname, 0644, debugfs_root, dbg, @@ -1234,7 +1261,9 @@ int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev, goto reg_fail; } + mutex_lock(&dbg->buflock); dbg->mgr = rot_dev->mgr; + mutex_unlock(&dbg->buflock); return 0; reg_fail: diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h index c2c6f9775602..c6d0151d37de 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.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 @@ -53,6 +53,7 @@ struct sde_rotator_debug_base { char *buf; size_t buf_len; struct sde_rot_mgr *mgr; + struct mutex buflock; }; #if defined(CONFIG_DEBUG_FS) diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c index 368df242a707..a63279715de6 100644 --- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.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 @@ -222,6 +222,14 @@ static int msm_v4l2_enum_framesizes(struct file *file, void *fh, return msm_vidc_enum_framesizes((void *)vidc_inst, fsize); } +static int msm_v4l2_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *ctrl) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_query_ctrl((void *)vidc_inst, ctrl); +} + static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = { .vidioc_querycap = msm_v4l2_querycap, .vidioc_enum_fmt_vid_cap_mplane = msm_v4l2_enum_fmt, @@ -238,6 +246,7 @@ static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = { .vidioc_streamoff = msm_v4l2_streamoff, .vidioc_s_ctrl = msm_v4l2_s_ctrl, .vidioc_g_ctrl = msm_v4l2_g_ctrl, + .vidioc_queryctrl = msm_v4l2_queryctrl, .vidioc_s_ext_ctrls = msm_v4l2_s_ext_ctrl, .vidioc_subscribe_event = msm_v4l2_subscribe_event, .vidioc_unsubscribe_event = msm_v4l2_unsubscribe_event, diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c index ec3246ed9d67..644203b65999 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -110,6 +110,34 @@ int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f) } EXPORT_SYMBOL(msm_vidc_enum_fmt); +int msm_vidc_query_ctrl(void *instance, struct v4l2_queryctrl *ctrl) +{ + struct msm_vidc_inst *inst = instance; + int rc = 0; + + if (!inst || !ctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE: + ctrl->maximum = inst->capability.hier_p_hybrid.max; + ctrl->minimum = inst->capability.hier_p_hybrid.min; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS: + ctrl->maximum = inst->capability.hier_b.max; + ctrl->minimum = inst->capability.hier_b.min; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS: + ctrl->maximum = inst->capability.hier_p.max; + ctrl->minimum = inst->capability.hier_p.min; + break; + default: + rc = -EINVAL; + } + return rc; +} +EXPORT_SYMBOL(msm_vidc_query_ctrl); + int msm_vidc_s_fmt(void *instance, struct v4l2_format *f) { struct msm_vidc_inst *inst = instance; diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 2aebc62d09d5..8b459e4da618 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -959,6 +959,48 @@ static void print_cap(const char *type, type, cap->min, cap->max, cap->step_size); } + +static void msm_vidc_comm_update_ctrl_limits(struct msm_vidc_inst *inst) +{ + struct v4l2_ctrl *ctrl = NULL; + + ctrl = v4l2_ctrl_find(&inst->ctrl_handler, + V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE); + if (ctrl) { + v4l2_ctrl_modify_range(ctrl, inst->capability.hier_p_hybrid.min, + inst->capability.hier_p_hybrid.max, ctrl->step, + inst->capability.hier_p_hybrid.min); + dprintk(VIDC_DBG, + "%s: Updated Range = %lld --> %lld Def value = %lld\n", + ctrl->name, ctrl->minimum, ctrl->maximum, + ctrl->default_value); + } + + ctrl = v4l2_ctrl_find(&inst->ctrl_handler, + V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS); + if (ctrl) { + v4l2_ctrl_modify_range(ctrl, inst->capability.hier_b.min, + inst->capability.hier_b.max, ctrl->step, + inst->capability.hier_b.min); + dprintk(VIDC_DBG, + "%s: Updated Range = %lld --> %lld Def value = %lld\n", + ctrl->name, ctrl->minimum, ctrl->maximum, + ctrl->default_value); + } + + ctrl = v4l2_ctrl_find(&inst->ctrl_handler, + V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS); + if (ctrl) { + v4l2_ctrl_modify_range(ctrl, inst->capability.hier_p.min, + inst->capability.hier_p.max, ctrl->step, + inst->capability.hier_p.min); + dprintk(VIDC_DBG, + "%s: Updated Range = %lld --> %lld Def value = %lld\n", + ctrl->name, ctrl->minimum, ctrl->maximum, + ctrl->default_value); + } +} + static void handle_session_init_done(enum hal_command_response cmd, void *data) { struct msm_vidc_cb_cmd_done *response = data; @@ -1052,8 +1094,16 @@ static void handle_session_init_done(enum hal_command_response cmd, void *data) print_cap("ltr_count", &inst->capability.ltr_count); print_cap("mbs_per_sec_low_power", &inst->capability.mbs_per_sec_power_save); + print_cap("hybrid-hp", &inst->capability.hier_p_hybrid); signal_session_msg_receipt(cmd, inst); + + /* + * Update controls after informing session_init_done to avoid + * timeouts. + */ + + msm_vidc_comm_update_ctrl_limits(inst); put_inst(inst); } diff --git a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c index 65f70d901f2a..3e269576c126 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c @@ -225,7 +225,7 @@ void msm_dcvs_init_load(struct msm_vidc_inst *inst) core = inst->core; dcvs = &inst->dcvs; res = &core->resources; - dcvs->load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS); + dcvs->load = msm_comm_get_inst_load(inst, LOAD_CALC_IGNORE_TURBO_LOAD); num_rows = res->dcvs_tbl_size; table = res->dcvs_tbl; diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c index 022c776a6096..a65e22c66e30 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c @@ -22,6 +22,7 @@ #include "msm_vidc_res_parse.h" #include "venus_boot.h" #include "soc/qcom/secure_buffer.h" +#include "soc/qcom/cx_ipeak.h" enum clock_properties { CLOCK_PROP_HAS_SCALING = 1 << 0, @@ -171,6 +172,8 @@ void msm_vidc_free_platform_resources( msm_vidc_free_qdss_addr_table(res); msm_vidc_free_bus_vectors(res); msm_vidc_free_buffer_usage_table(res); + cx_ipeak_unregister(res->cx_ipeak_context); + res->cx_ipeak_context = NULL; } static int msm_vidc_load_reg_table(struct msm_vidc_platform_resources *res) @@ -1133,8 +1136,36 @@ int read_platform_resources_from_dt( of_property_read_u32(pdev->dev.of_node, "qcom,max-secure-instances", &res->max_secure_inst_count); + + res->cx_ipeak_context = cx_ipeak_register(pdev->dev.of_node, + "qcom,cx-ipeak-data"); + + if (IS_ERR(res->cx_ipeak_context)) { + rc = PTR_ERR(res->cx_ipeak_context); + if (rc == -EPROBE_DEFER) + dprintk(VIDC_INFO, + "cx-ipeak register failed. Deferring probe!"); + else + dprintk(VIDC_ERR, + "cx-ipeak register failed. rc: %d", rc); + + res->cx_ipeak_context = NULL; + goto err_register_cx_ipeak; + } else if (res->cx_ipeak_context) { + dprintk(VIDC_INFO, "cx-ipeak register successful"); + } else { + dprintk(VIDC_INFO, "cx-ipeak register not implemented"); + } + + of_property_read_u32(pdev->dev.of_node, + "qcom,clock-freq-threshold", + &res->clk_freq_threshold); + dprintk(VIDC_DBG, "cx ipeak threshold frequency = %u\n", + res->clk_freq_threshold); + return rc; +err_register_cx_ipeak: err_setup_legacy_cb: err_load_max_hw_load: msm_vidc_free_allowed_clocks_table(res); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h index 03b31d7fd9d1..3a329d989918 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h @@ -17,6 +17,7 @@ #include <linux/devfreq.h> #include <linux/platform_device.h> #include <media/msm_vidc.h> +#include "soc/qcom/cx_ipeak.h" #define MAX_BUFFER_TYPES 32 struct platform_version_table { @@ -191,6 +192,8 @@ struct msm_vidc_platform_resources { uint32_t pm_qos_latency_us; uint32_t max_inst_count; uint32_t max_secure_inst_count; + uint32_t clk_freq_threshold; + struct cx_ipeak_client *cx_ipeak_context; }; static inline bool is_iommu_present(struct msm_vidc_platform_resources *res) diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index 777fb52b2201..4739fc999c82 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -27,6 +27,7 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/workqueue.h> +#include <soc/qcom/cx_ipeak.h> #include <soc/qcom/scm.h> #include <soc/qcom/smem.h> #include <soc/qcom/subsystem_restart.h> @@ -1301,8 +1302,12 @@ static int venus_hfi_suspend(void *dev) } dprintk(VIDC_DBG, "Suspending Venus\n"); - rc = flush_delayed_work(&venus_hfi_pm_work); + flush_delayed_work(&venus_hfi_pm_work); + mutex_lock(&device->lock); + if (device->power_enabled) + rc = -EBUSY; + mutex_unlock(&device->lock); return rc; } @@ -1385,6 +1390,39 @@ static int __halt_axi(struct venus_hfi_device *device) return rc; } +static int __set_clk_rate(struct venus_hfi_device *device, + struct clock_info *cl, u64 rate) { + int rc = 0, rc1 = 0; + u64 toggle_freq = device->res->clk_freq_threshold; + struct cx_ipeak_client *ipeak = device->res->cx_ipeak_context; + struct clk *clk = cl->clk; + + if (device->clk_freq < toggle_freq && rate >= toggle_freq) { + rc1 = cx_ipeak_update(ipeak, true); + dprintk(VIDC_PROF, "Voting up: %d\n", rc); + } + + rc = clk_set_rate(clk, rate); + if (rc) + dprintk(VIDC_ERR, + "%s: Failed to set clock rate %llu %s: %d\n", + __func__, rate, cl->name, rc); + + if (device->clk_freq >= toggle_freq && rate < toggle_freq) { + rc1 = cx_ipeak_update(ipeak, false); + dprintk(VIDC_PROF, "Voting down: %d\n", rc); + } + + if (rc1) + dprintk(VIDC_ERR, + "cx_ipeak_update failed! ipeak %pK\n", ipeak); + + if (!rc) + device->clk_freq = rate; + + return rc; +} + static int __scale_clocks_cycles_per_mb(struct venus_hfi_device *device, struct vidc_clk_scale_data *data, unsigned long instant_bitrate) { @@ -1458,14 +1496,10 @@ get_clock_freq: if (!cl->has_scaling) continue; - device->clk_freq = rate; - rc = clk_set_rate(cl->clk, rate); - if (rc) { - dprintk(VIDC_ERR, - "%s: Failed to set clock rate %llu %s: %d\n", - __func__, rate, cl->name, rc); + rc = __set_clk_rate(device, cl, rate); + if (rc) return rc; - } + if (!strcmp(cl->name, "core_clk")) device->scaled_rate = rate; @@ -1506,14 +1540,11 @@ static int __scale_clocks_load(struct venus_hfi_device *device, int load, load, data, instant_bitrate); } - device->clk_freq = rate; - rc = clk_set_rate(cl->clk, rate); - if (rc) { - dprintk(VIDC_ERR, - "Failed to set clock rate %lu %s: %d\n", - rate, cl->name, rc); + + rc = __set_clk_rate(device, cl, rate); + if (rc) return rc; - } + if (!strcmp(cl->name, "core_clk")) device->scaled_rate = rate; @@ -3794,7 +3825,8 @@ static inline int __prepare_enable_clks(struct venus_hfi_device *device) * it to the lowest frequency possible */ if (cl->has_scaling) - clk_set_rate(cl->clk, clk_round_rate(cl->clk, 0)); + __set_clk_rate(device, cl, + clk_round_rate(cl->clk, 0)); if (cl->has_mem_retention) { rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_PERIPH); diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c index 491913778bcc..2f52d66b4dae 100644 --- a/drivers/media/usb/cx231xx/cx231xx-avcore.c +++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c @@ -1264,7 +1264,10 @@ int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev, dev->board.agc_analog_digital_select_gpio, analog_or_digital); - return status; + if (status < 0) + return status; + + return 0; } int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3) diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 4a117a58c39a..8389c162bc89 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -486,7 +486,7 @@ struct cx231xx_board cx231xx_boards[] = { .output_mode = OUT_MODE_VIP11, .demod_xfer_mode = 0, .ctl_pin_status_mask = 0xFFFFFFC4, - .agc_analog_digital_select_gpio = 0x00, /* According with PV cxPolaris.inf file */ + .agc_analog_digital_select_gpio = 0x1c, .tuner_sif_gpio = -1, .tuner_scl_gpio = -1, .tuner_sda_gpio = -1, diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index a2fd49b6be83..19b0293312a0 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -712,6 +712,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) break; case CX231XX_BOARD_CNXT_RDE_253S: case CX231XX_BOARD_CNXT_RDU_253S: + case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1); break; case CX231XX_BOARD_HAUPPAUGE_EXETER: @@ -738,7 +739,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID: case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL: case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC: - errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0); + errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0); break; default: break; diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index 0d248ce02a9b..ab58f0b9da5c 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -677,7 +677,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) struct dvb_usb_device *d = purb->context; struct dib0700_rc_response *poll_reply; enum rc_type protocol; - u32 uninitialized_var(keycode); + u32 keycode; u8 toggle; deb_info("%s()\n", __func__); @@ -719,7 +719,8 @@ static void dib0700_rc_urb_completion(struct urb *purb) poll_reply->nec.data == 0x00 && poll_reply->nec.not_data == 0xff) { poll_reply->data_state = 2; - break; + rc_repeat(d->rc_dev); + goto resubmit; } if ((poll_reply->nec.data ^ poll_reply->nec.not_data) != 0xff) { diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index a19b5c8b56ff..1a9e1e556706 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -507,9 +507,8 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, if (dev->disconnected) return -ENODEV; - rc = rt_mutex_trylock(&dev->i2c_bus_lock); - if (rc < 0) - return rc; + if (!rt_mutex_trylock(&dev->i2c_bus_lock)) + return -EAGAIN; /* Switch I2C bus if needed */ if (bus != dev->cur_i2c_bus && diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c index f23df4a9d8c5..52b88e9e656b 100644 --- a/drivers/media/usb/gspca/cpia1.c +++ b/drivers/media/usb/gspca/cpia1.c @@ -1624,7 +1624,7 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; + struct sd *sd __maybe_unused = (struct sd *) gspca_dev; command_pause(gspca_dev); diff --git a/drivers/media/usb/gspca/konica.c b/drivers/media/usb/gspca/konica.c index 39c96bb4c985..0712b1bc90b4 100644 --- a/drivers/media/usb/gspca/konica.c +++ b/drivers/media/usb/gspca/konica.c @@ -243,7 +243,7 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; + struct sd *sd __maybe_unused = (struct sd *) gspca_dev; konica_stream_off(gspca_dev); #if IS_ENABLED(CONFIG_INPUT) diff --git a/drivers/media/usb/gspca/t613.c b/drivers/media/usb/gspca/t613.c index e2cc4e5a0ccb..bb52fc1fe598 100644 --- a/drivers/media/usb/gspca/t613.c +++ b/drivers/media/usb/gspca/t613.c @@ -837,7 +837,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ { - struct sd *sd = (struct sd *) gspca_dev; + struct sd *sd __maybe_unused = (struct sd *) gspca_dev; int pkt_type; if (data[0] == 0x5a) { diff --git a/drivers/memstick/host/rtsx_usb_ms.c b/drivers/memstick/host/rtsx_usb_ms.c index 1105db2355d2..83bfb1659abe 100644 --- a/drivers/memstick/host/rtsx_usb_ms.c +++ b/drivers/memstick/host/rtsx_usb_ms.c @@ -524,6 +524,7 @@ static void rtsx_usb_ms_handle_req(struct work_struct *work) int rc; if (!host->req) { + pm_runtime_get_sync(ms_dev(host)); do { rc = memstick_next_req(msh, &host->req); dev_dbg(ms_dev(host), "next req %d\n", rc); @@ -544,6 +545,7 @@ static void rtsx_usb_ms_handle_req(struct work_struct *work) host->req->error); } } while (!rc); + pm_runtime_put(ms_dev(host)); } } @@ -570,6 +572,7 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh, dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n", __func__, param, value); + pm_runtime_get_sync(ms_dev(host)); mutex_lock(&ucr->dev_mutex); err = rtsx_usb_card_exclusive_check(ucr, RTSX_USB_MS_CARD); @@ -635,6 +638,7 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh, } out: mutex_unlock(&ucr->dev_mutex); + pm_runtime_put(ms_dev(host)); /* power-on delay */ if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON) @@ -681,6 +685,7 @@ static int rtsx_usb_detect_ms_card(void *__host) int err; for (;;) { + pm_runtime_get_sync(ms_dev(host)); mutex_lock(&ucr->dev_mutex); /* Check pending MS card changes */ @@ -703,6 +708,7 @@ static int rtsx_usb_detect_ms_card(void *__host) } poll_again: + pm_runtime_put(ms_dev(host)); if (host->eject) break; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2346635ccff3..3087618b1b26 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1474,6 +1474,7 @@ config MFD_WM8350 config MFD_WM8350_I2C bool "Wolfson Microelectronics WM8350 with I2C" select MFD_WM8350 + select REGMAP_I2C depends on I2C=y help The WM8350 is an integrated audio and power management diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c index 06c205868573..c216c3a55793 100644 --- a/drivers/mfd/atmel-hlcdc.c +++ b/drivers/mfd/atmel-hlcdc.c @@ -50,8 +50,9 @@ static int regmap_atmel_hlcdc_reg_write(void *context, unsigned int reg, if (reg <= ATMEL_HLCDC_DIS) { u32 status; - readl_poll_timeout(hregmap->regs + ATMEL_HLCDC_SR, status, - !(status & ATMEL_HLCDC_SIP), 1, 100); + readl_poll_timeout_atomic(hregmap->regs + ATMEL_HLCDC_SR, + status, !(status & ATMEL_HLCDC_SIP), + 1, 100); } writel(val, hregmap->regs + reg); diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index 88e80ec772f6..fe89e5e337d5 100644 --- a/drivers/mfd/intel-lpss.c +++ b/drivers/mfd/intel-lpss.c @@ -494,9 +494,6 @@ int intel_lpss_suspend(struct device *dev) for (i = 0; i < LPSS_PRIV_REG_COUNT; i++) lpss->priv_ctx[i] = readl(lpss->priv + i * 4); - /* Put the device into reset state */ - writel(0, lpss->priv + LPSS_PRIV_RESETS); - return 0; } EXPORT_SYMBOL_GPL(intel_lpss_suspend); diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 60b60dc63ddd..022c9374ce8b 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -354,6 +354,8 @@ int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones) clones[i]); } + put_device(dev); + return 0; } EXPORT_SYMBOL(mfd_clone_cell); diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c index dbd907d7170e..691dab791f7a 100644 --- a/drivers/mfd/rtsx_usb.c +++ b/drivers/mfd/rtsx_usb.c @@ -46,9 +46,6 @@ static void rtsx_usb_sg_timed_out(unsigned long data) dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__); usb_sg_cancel(&ucr->current_sg); - - /* we know the cancellation is caused by time-out */ - ucr->current_sg.status = -ETIMEDOUT; } static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr, @@ -67,12 +64,15 @@ static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr, ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout); add_timer(&ucr->sg_timer); usb_sg_wait(&ucr->current_sg); - del_timer_sync(&ucr->sg_timer); + if (!del_timer_sync(&ucr->sg_timer)) + ret = -ETIMEDOUT; + else + ret = ucr->current_sg.status; if (act_len) *act_len = ucr->current_sg.bytes; - return ucr->current_sg.status; + return ret; } int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe, diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index 222367cc8c81..524660510599 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -352,17 +352,27 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, if (copy_from_user(sgl->lpage, user_addr + user_size - sgl->lpage_size, sgl->lpage_size)) { rc = -EFAULT; - goto err_out1; + goto err_out2; } } return 0; + err_out2: + __genwqe_free_consistent(cd, PAGE_SIZE, sgl->lpage, + sgl->lpage_dma_addr); + sgl->lpage = NULL; + sgl->lpage_dma_addr = 0; err_out1: __genwqe_free_consistent(cd, PAGE_SIZE, sgl->fpage, sgl->fpage_dma_addr); + sgl->fpage = NULL; + sgl->fpage_dma_addr = 0; err_out: __genwqe_free_consistent(cd, sgl->sgl_size, sgl->sgl, sgl->sgl_dma_addr); + sgl->sgl = NULL; + sgl->sgl_dma_addr = 0; + sgl->sgl_size = 0; return -ENOMEM; } diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index bd21f8cca2aa..460ffc79f566 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -2103,7 +2103,8 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle) (rc == 0) && (rsp_buf->status == 0)) { pr_debug("Got Auth_Stream_Ready, nothing sent to rx\n"); - if (!hdcp_lib_enable_encryption(handle)) { + if (!handle->authenticated && + !hdcp_lib_enable_encryption(handle)) { handle->authenticated = true; cdata.cmd = HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS; diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 020de5919c21..bdc7fcd80eca 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -151,7 +151,7 @@ static int mei_nfc_if_version(struct mei_cl *cl, ret = 0; bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length); - if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { + if (bytes_recv < if_version_length) { dev_err(bus->dev, "Could not read IF version\n"); ret = -EIO; goto err; diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index a77643954523..e59838231703 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -144,7 +144,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) mutex_lock(&bus->device_lock); if (!mei_cl_is_connected(cl)) { - rets = -EBUSY; + rets = -ENODEV; goto out; } } diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index a8a68acd3267..a2661381ddfc 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -121,6 +121,10 @@ #define MEI_DEV_ID_SPT_2 0x9D3B /* Sunrise Point 2 */ #define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */ #define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */ + +#define MEI_DEV_ID_KBP 0xA2BA /* Kaby Point */ +#define MEI_DEV_ID_KBP_2 0xA2BB /* Kaby Point 2 */ + /* * MEI HW Section */ diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 25b1997a62cb..36333750c512 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1258,8 +1258,14 @@ static bool mei_me_fw_type_nm(struct pci_dev *pdev) static bool mei_me_fw_type_sps(struct pci_dev *pdev) { u32 reg; - /* Read ME FW Status check for SPS Firmware */ - pci_read_config_dword(pdev, PCI_CFG_HFS_1, ®); + unsigned int devfn; + + /* + * Read ME FW Status register to check for SPS Firmware + * The SPS FW is only signaled in pci function 0 + */ + devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); + pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_1, ®); /* if bits [19:16] = 15, running SPS Firmware */ return (reg & 0xf0000) == 0xf0000; } diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index bae680c648ff..396d75d9fb11 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -972,11 +972,13 @@ static bool mei_txe_check_and_ack_intrs(struct mei_device *dev, bool do_ack) hisr = mei_txe_br_reg_read(hw, HISR_REG); aliveness = mei_txe_aliveness_get(dev); - if (hhisr & IPC_HHIER_SEC && aliveness) + if (hhisr & IPC_HHIER_SEC && aliveness) { ipc_isr = mei_txe_sec_reg_read_silent(hw, SEC_IPC_HOST_INT_STATUS_REG); - else + } else { ipc_isr = 0; + hhisr &= ~IPC_HHIER_SEC; + } generated = generated || (hisr & HISR_INT_STS_MSK) || diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 80f9afcb1382..4ef189a7a2fb 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -207,7 +207,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, mutex_lock(&dev->device_lock); if (!mei_cl_is_connected(cl)) { - rets = -EBUSY; + rets = -ENODEV; goto out; } } diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 27678d8154e0..01e20384ac44 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -84,8 +84,11 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, mei_me_pch8_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, mei_me_pch8_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, mei_me_pch8_cfg)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_sps_cfg)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, mei_me_pch8_sps_cfg)}, + + {MEI_PCI_DEVICE(MEI_DEV_ID_KBP, mei_me_pch8_cfg)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, mei_me_pch8_cfg)}, /* required last entry */ {0, } diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 134995c9cd3c..8d03c36858b3 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -7043,7 +7043,11 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg) break; } pr_debug("SET_MEM_PARAM: qseecom addr = 0x%pK\n", data); + mutex_lock(&app_access_lock); + atomic_inc(&data->ioctl_count); ret = qseecom_set_client_mem_param(data, argp); + atomic_dec(&data->ioctl_count); + mutex_unlock(&app_access_lock); if (ret) pr_err("failed Qqseecom_set_mem_param request: %d\n", ret); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 60b02f28a8ff..ce0ecd1e9b7a 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2848,7 +2848,7 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq, struct mmc_blk_data *md = mq->data; struct mmc_packed *packed = mqrq->packed; bool do_rel_wr, do_data_tag; - u32 *packed_cmd_hdr; + __le32 *packed_cmd_hdr; u8 hdr_blocks; u8 i = 1; @@ -3544,7 +3544,7 @@ void mmc_blk_cmdq_complete_rq(struct request *rq) else if (mrq->data && mrq->data->error) err = mrq->data->error; - if (err || cmdq_req->resp_err) { + if ((err || cmdq_req->resp_err) && !cmdq_req->skip_err_handling) { pr_err("%s: %s: txfr error(%d)/resp_err(%d)\n", mmc_hostname(mrq->host), __func__, err, cmdq_req->resp_err); @@ -3581,6 +3581,17 @@ void mmc_blk_cmdq_complete_rq(struct request *rq) blk_end_request_all(rq, err); goto out; } + /* + * In case of error, cmdq_req->data.bytes_xfered is set to 0. + * If we call blk_end_request() with nr_bytes as 0 then the request + * never gets completed. So in case of error, to complete a request + * with error we should use blk_end_request_all(). + */ + if (err && cmdq_req->skip_err_handling) { + cmdq_req->skip_err_handling = false; + blk_end_request_all(rq, err); + goto out; + } blk_end_request(rq, err, cmdq_req->data.bytes_xfered); @@ -4114,7 +4125,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, set_capacity(md->disk, size); if (mmc_host_cmd23(card->host)) { - if (mmc_card_mmc(card) || + if ((mmc_card_mmc(card) && + card->csd.mmca_vsn >= CSD_SPEC_VER_3) || (mmc_card_sd(card) && card->scr.cmds & SD_SCR_CMD23_SUPPORT)) md->flags |= MMC_BLK_CMD23; diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h index 253979b51c84..505712f0e1b0 100644 --- a/drivers/mmc/card/queue.h +++ b/drivers/mmc/card/queue.h @@ -25,7 +25,7 @@ enum mmc_packed_type { struct mmc_packed { struct list_head list; - u32 cmd_hdr[1024]; + __le32 cmd_hdr[1024]; unsigned int blocks; u8 nr_entries; u8 retries; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 26e57f3c5228..5396e1d00178 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -948,6 +948,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) pr_debug("%s: %d bytes transferred: %d\n", mmc_hostname(host), mrq->data->bytes_xfered, mrq->data->error); +#ifdef CONFIG_BLOCK if (mrq->lat_hist_enabled) { ktime_t completion; u_int64_t delta_us; @@ -959,6 +960,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) (mrq->data->flags & MMC_DATA_READ), delta_us); } +#endif trace_mmc_blk_rw_end(cmd->opcode, cmd->arg, mrq->data); } @@ -1709,11 +1711,13 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, } if (!err && areq) { +#ifdef CONFIG_BLOCK if (host->latency_hist_enabled) { areq->mrq->io_start = ktime_get(); areq->mrq->lat_hist_enabled = 1; } else areq->mrq->lat_hist_enabled = 0; +#endif trace_mmc_blk_rw_start(areq->mrq->cmd->opcode, areq->mrq->cmd->arg, areq->mrq->data); @@ -2110,6 +2114,38 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) EXPORT_SYMBOL(__mmc_claim_host); /** + * mmc_try_claim_host - try exclusively to claim a host + * and keep trying for given time, with a gap of 10ms + * @host: mmc host to claim + * @dealy_ms: delay in ms + * + * Returns %1 if the host is claimed, %0 otherwise. + */ +int mmc_try_claim_host(struct mmc_host *host, unsigned int delay_ms) +{ + int claimed_host = 0; + unsigned long flags; + int retry_cnt = delay_ms/10; + + do { + spin_lock_irqsave(&host->lock, flags); + if (!host->claimed || host->claimer == current) { + host->claimed = 1; + host->claimer = current; + host->claim_cnt += 1; + claimed_host = 1; + } + spin_unlock_irqrestore(&host->lock, flags); + if (!claimed_host) + mmc_delay(10); + } while (!claimed_host && retry_cnt--); + if (host->ops->enable && claimed_host && host->claim_cnt == 1) + host->ops->enable(host); + return claimed_host; +} +EXPORT_SYMBOL(mmc_try_claim_host); + +/** * mmc_release_host - release a host * @host: mmc host to release * @@ -4415,6 +4451,7 @@ static void __exit mmc_exit(void) destroy_workqueue(workqueue); } +#ifdef CONFIG_BLOCK static ssize_t latency_hist_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -4462,6 +4499,7 @@ mmc_latency_hist_sysfs_exit(struct mmc_host *host) { device_remove_file(&host->class_dev, &dev_attr_latency_hist); } +#endif subsys_initcall(mmc_init); module_exit(mmc_exit); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 333f691a73c7..e8294502a701 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -883,7 +883,9 @@ int mmc_add_host(struct mmc_host *host) pr_err("%s: failed to create sysfs group with err %d\n", __func__, err); +#ifdef CONFIG_BLOCK mmc_latency_hist_sysfs_init(host); +#endif mmc_start_host(host); if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) @@ -915,7 +917,9 @@ void mmc_remove_host(struct mmc_host *host) sysfs_remove_group(&host->parent->kobj, &dev_attr_grp); sysfs_remove_group(&host->class_dev.kobj, &clk_scaling_attr_grp); +#ifdef CONFIG_BLOCK mmc_latency_hist_sysfs_exit(host); +#endif device_del(&host->class_dev); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 7e7d7eb4da2a..ec5ce79e84e7 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1147,7 +1147,17 @@ static void mmc_sd_detect(struct mmc_host *host) BUG_ON(!host); BUG_ON(!host->card); - mmc_get_card(host->card); + /* + * Try to acquire claim host. If failed to get the lock in 2 sec, + * just return; This is to ensure that when this call is invoked + * due to pm_suspend, not to block suspend for longer duration. + */ + pm_runtime_get_sync(&host->card->dev); + if (!mmc_try_claim_host(host, 2000)) { + pm_runtime_mark_last_busy(&host->card->dev); + pm_runtime_put_autosuspend(&host->card->dev); + return; + } /* * Just check if our card has been removed. diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c index 96d4fbf1a823..1ad5fd0e0a78 100644 --- a/drivers/mmc/host/cmdq_hci.c +++ b/drivers/mmc/host/cmdq_hci.c @@ -145,6 +145,29 @@ static void cmdq_clear_set_irqs(struct cmdq_host *cq_host, u32 clear, u32 set) mb(); } +static int cmdq_clear_task_poll(struct cmdq_host *cq_host, unsigned int tag) +{ + int retries = 100; + + cmdq_clear_set_irqs(cq_host, CQIS_TCL, 0); + cmdq_writel(cq_host, 1<<tag, CQTCLR); + while (retries) { + /* + * Task Clear register and doorbell, + * both should indicate that task is cleared + */ + if ((cmdq_readl(cq_host, CQTCLR) & 1<<tag) || + (cmdq_readl(cq_host, CQTDBR) & 1<<tag)) { + udelay(5); + retries--; + continue; + } else + break; + } + + cmdq_clear_set_irqs(cq_host, 0, CQIS_TCL); + return retries ? 0 : -ETIMEDOUT; +} #define DRV_NAME "cmdq-host" @@ -857,6 +880,8 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err) struct mmc_request *mrq; int ret; u32 dbr_set = 0; + u32 dev_pend_set = 0; + int stat_err = 0; status = cmdq_readl(cq_host, CQIS); @@ -865,7 +890,9 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err) MMC_TRACE(mmc, "%s: CQIS: 0x%x err: %d\n", __func__, status, err); - if (err || (status & CQIS_RED)) { + stat_err = status & (CQIS_RED | CQIS_GCE | CQIS_ICCE); + + if (err || stat_err) { err_info = cmdq_readl(cq_host, CQTERRI); pr_err("%s: err: %d status: 0x%08x task-err-info (0x%08lx)\n", mmc_hostname(mmc), err, status, err_info); @@ -968,7 +995,7 @@ skip_cqterri: * CQE detected a reponse error from device * In most cases, this would require a reset. */ - if (status & CQIS_RED) { + if (stat_err & CQIS_RED) { /* * will check if the RED error is due to a bkops * exception once the queue is empty @@ -987,6 +1014,62 @@ skip_cqterri: mrq->cmdq_req->resp_arg = cmdq_readl(cq_host, CQCRA); } + /* + * Generic Crypto error detected by CQE. + * Its a fatal, would require cmdq reset. + */ + if (stat_err & CQIS_GCE) { + if (mrq->data) + mrq->data->error = -EIO; + pr_err("%s: Crypto generic error while processing task %lu!", + mmc_hostname(mmc), tag); + MMC_TRACE(mmc, "%s: GCE error detected with tag %lu\n", + __func__, tag); + } + /* + * Invalid crypto config error detected by CQE, clear the task. + * Task can be cleared only when CQE is halt state. + */ + if (stat_err & CQIS_ICCE) { + /* + * Invalid Crypto Config Error is detected at the + * beginning of the transfer before the actual execution + * started. So just clear the task in CQE. No need to + * clear in device. Only the task which caused ICCE has + * to be cleared. Other tasks can be continue processing + * The first task which is about to be prepared would + * cause ICCE Error. + */ + dbr_set = cmdq_readl(cq_host, CQTDBR); + dev_pend_set = cmdq_readl(cq_host, CQDPT); + if (dbr_set ^ dev_pend_set) + tag = ffs(dbr_set ^ dev_pend_set) - 1; + mrq = get_req_by_tag(cq_host, tag); + pr_err("%s: Crypto config error while processing task %lu!", + mmc_hostname(mmc), tag); + MMC_TRACE(mmc, "%s: ICCE error with tag %lu\n", + __func__, tag); + if (mrq->data) + mrq->data->error = -EIO; + else if (mrq->cmd) + mrq->cmd->error = -EIO; + /* + * If CQE is halted and tag is valid then clear the task + * then un-halt CQE and set flag to skip error recovery. + * If any of the condtions is not met thene it will + * enter into default error recovery path. + */ + if (!ret && (dbr_set ^ dev_pend_set)) { + ret = cmdq_clear_task_poll(cq_host, tag); + if (ret) { + pr_err("%s: %s: task[%lu] clear failed ret=%d\n", + mmc_hostname(mmc), + __func__, tag, ret); + } else if (!cmdq_halt_poll(mmc, false)) { + mrq->cmdq_req->skip_err_handling = true; + } + } + } cmdq_finish_data(mmc, tag); } else { cmdq_writel(cq_host, status, CQIS); @@ -1052,6 +1135,7 @@ static int cmdq_halt_poll(struct mmc_host *mmc, bool halt) cq_host->ops->clear_set_irqs(mmc, true); cmdq_writel(cq_host, cmdq_readl(cq_host, CQCTL) & ~HALT, CQCTL); + mmc_host_clr_halt(mmc); return 0; } diff --git a/drivers/mmc/host/cmdq_hci.h b/drivers/mmc/host/cmdq_hci.h index 6c10ab3859d1..db0cd956ae90 100644 --- a/drivers/mmc/host/cmdq_hci.h +++ b/drivers/mmc/host/cmdq_hci.h @@ -37,6 +37,8 @@ #define CQIS_TCC (1 << 1) #define CQIS_RED (1 << 2) #define CQIS_TCL (1 << 3) +#define CQIS_GCE (1 << 4) +#define CQIS_ICCE (1 << 5) /* interrupt status enable */ #define CQISTE 0x14 @@ -112,7 +114,7 @@ /* command response argument */ #define CQCRA 0x5C -#define CQ_INT_ALL 0xF +#define CQ_INT_ALL 0x3F #define CQIC_DEFAULT_ICCTH 31 #define CQIC_DEFAULT_ICTOVAL 1 diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 7e1d13b68b06..7dcfb1d5034f 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -59,12 +59,13 @@ int dw_mci_pltfm_register(struct platform_device *pdev, host->pdata = pdev->dev.platform_data; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - /* Get registers' physical base address */ - host->phy_regs = (void *)(regs->start); host->regs = devm_ioremap_resource(&pdev->dev, regs); if (IS_ERR(host->regs)) return PTR_ERR(host->regs); + /* Get registers' physical base address */ + host->phy_regs = regs->start; + platform_set_drvdata(pdev, host); return dw_mci_probe(host); } diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 7a6cedbe48a8..fb204ee6ff89 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -699,7 +699,7 @@ static int dw_mci_edmac_start_dma(struct dw_mci *host, int ret = 0; /* Set external dma config: burst size, burst width */ - cfg.dst_addr = (dma_addr_t)(host->phy_regs + fifo_offset); + cfg.dst_addr = host->phy_regs + fifo_offset; cfg.src_addr = cfg.dst_addr; cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index d839147e591d..44ecebd1ea8c 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -661,13 +661,13 @@ static int mxs_mmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mmc); + spin_lock_init(&host->lock); + ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0, dev_name(&pdev->dev), host); if (ret) goto out_free_dma; - spin_lock_init(&host->lock); - ret = mmc_add_host(mmc); if (ret) goto out_free_dma; diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 28a057fae0a1..72bbb12fb938 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -798,14 +798,16 @@ static int pxamci_probe(struct platform_device *pdev) gpio_direction_output(gpio_power, host->pdata->gpio_power_invert); } - if (gpio_is_valid(gpio_ro)) + if (gpio_is_valid(gpio_ro)) { ret = mmc_gpio_request_ro(mmc, gpio_ro); - if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_ro); - goto out; - } else { - mmc->caps2 |= host->pdata->gpio_card_ro_invert ? - 0 : MMC_CAP2_RO_ACTIVE_HIGH; + if (ret) { + dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", + gpio_ro); + goto out; + } else { + mmc->caps2 |= host->pdata->gpio_card_ro_invert ? + 0 : MMC_CAP2_RO_ACTIVE_HIGH; + } } if (gpio_is_valid(gpio_cd)) diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index 6c71fc9f76c7..da9f71b8deb0 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -1138,11 +1138,6 @@ static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) dev_dbg(sdmmc_dev(host), "%s\n", __func__); mutex_lock(&ucr->dev_mutex); - if (rtsx_usb_card_exclusive_check(ucr, RTSX_USB_SD_CARD)) { - mutex_unlock(&ucr->dev_mutex); - return; - } - sd_set_power_mode(host, ios->power_mode); sd_set_bus_width(host, ios->bus_width); sd_set_timing(host, ios->timing, &host->ddr_mode); @@ -1314,6 +1309,7 @@ static void rtsx_usb_update_led(struct work_struct *work) container_of(work, struct rtsx_usb_sdmmc, led_work); struct rtsx_ucr *ucr = host->ucr; + pm_runtime_get_sync(sdmmc_dev(host)); mutex_lock(&ucr->dev_mutex); if (host->led.brightness == LED_OFF) @@ -1322,6 +1318,7 @@ static void rtsx_usb_update_led(struct work_struct *work) rtsx_usb_turn_on_led(ucr); mutex_unlock(&ucr->dev_mutex); + pm_runtime_put(sdmmc_dev(host)); } #endif diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 43853306a6bb..21d2a4b8f7ae 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -774,7 +774,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) * host->clock is in Hz. target_timeout is in us. * Hence, us = 1000000 * cycles / Hz. Round up. */ - val = 1000000 * data->timeout_clks; + val = 1000000ULL * data->timeout_clks; if (do_div(val, host->clock)) target_timeout++; target_timeout += val; diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index 744ca5cacc9b..f9fa3fad728e 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -75,15 +75,15 @@ static int __init init_msp_flash(void) printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); - msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL); + msp_flash = kcalloc(fcnt, sizeof(*msp_flash), GFP_KERNEL); if (!msp_flash) return -ENOMEM; - msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); + msp_parts = kcalloc(fcnt, sizeof(*msp_parts), GFP_KERNEL); if (!msp_parts) goto free_msp_flash; - msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL); + msp_maps = kcalloc(fcnt, sizeof(*msp_maps), GFP_KERNEL); if (!msp_maps) goto free_msp_parts; diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 142fc3d79463..784c6e1a0391 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -230,8 +230,10 @@ static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev, info->mtd = mtd_concat_create(cdev, info->num_subdev, plat->name); - if (info->mtd == NULL) + if (info->mtd == NULL) { ret = -ENXIO; + goto err; + } } info->mtd->dev.parent = &pdev->dev; diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index c72313d66cf6..bc054a5ed7f8 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -241,6 +241,9 @@ static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode) unsigned long flags; u32 val; + /* Reset ECC hardware */ + davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET); + spin_lock_irqsave(&davinci_nand_lock, flags); /* Start 4-bit ECC calculation for read/write */ diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c index 990898b9dc72..bba7dd1b5ebf 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c @@ -513,10 +513,11 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, unsigned long long ec = be64_to_cpu(ech->ec); unmap_peb(ai, pnum); dbg_bld("Adding PEB to free: %i", pnum); + if (err == UBI_IO_FF_BITFLIPS) - add_aeb(ai, free, pnum, ec, 1); - else - add_aeb(ai, free, pnum, ec, 0); + scrub = 1; + + add_aeb(ai, free, pnum, ec, scrub); continue; } else if (err == 0 || err == UBI_IO_BITFLIPS) { dbg_bld("Found non empty PEB:%i in pool", pnum); @@ -748,11 +749,11 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, fmvhdr->vol_type, be32_to_cpu(fmvhdr->last_eb_bytes)); - if (!av) - goto fail_bad; - if (PTR_ERR(av) == -EINVAL) { - ubi_err(ubi, "volume (ID %i) already exists", - fmvhdr->vol_id); + if (IS_ERR(av)) { + if (PTR_ERR(av) == -EEXIST) + ubi_err(ubi, "volume (ID %i) already exists", + fmvhdr->vol_id); + goto fail_bad; } diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 22a30bbaa1bd..a90728786000 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -663,7 +663,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, int shutdown) { int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0; - int vol_id = -1, lnum = -1; + int erase = 0, keep = 0, vol_id = -1, lnum = -1; #ifdef CONFIG_MTD_UBI_FASTMAP int anchor = wrk->anchor; #endif @@ -799,6 +799,16 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, e1->pnum); scrubbing = 1; goto out_not_moved; + } else if (ubi->fast_attach && err == UBI_IO_BAD_HDR_EBADMSG) { + /* + * While a full scan would detect interrupted erasures + * at attach time we can face them here when attached from + * Fastmap. + */ + dbg_wl("PEB %d has ECC errors, maybe from an interrupted erasure", + e1->pnum); + erase = 1; + goto out_not_moved; } ubi_err(ubi, "error %d while reading VID header from PEB %d", @@ -834,6 +844,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, * Target PEB had bit-flips or write error - torture it. */ torture = 1; + keep = 1; goto out_not_moved; } @@ -930,7 +941,7 @@ out_not_moved: ubi->erroneous_peb_count += 1; } else if (scrubbing) wl_tree_add(e1, &ubi->scrub); - else + else if (keep) wl_tree_add(e1, &ubi->used); if (dst_leb_clean) { wl_tree_add(e2, &ubi->free); @@ -951,6 +962,12 @@ out_not_moved: goto out_ro; } + if (erase) { + err = do_sync_erase(ubi, e1, vol_id, lnum, 1); + if (err) + goto out_ro; + } + mutex_unlock(&ubi->move_mutex); return 0; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index b3d70a7a5262..5dca77e0ffed 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1317,9 +1317,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) slave_dev->name); } - /* already enslaved */ - if (slave_dev->flags & IFF_SLAVE) { - netdev_dbg(bond_dev, "Error: Device was already enslaved\n"); + /* already in-use? */ + if (netdev_is_rx_handler_busy(slave_dev)) { + netdev_err(bond_dev, + "Error: Device is in use and cannot be enslaved\n"); return -EBUSY; } diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index ad535a854e5c..eab132778e67 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -21,6 +21,7 @@ #include <linux/slab.h> #include <linux/netdevice.h> #include <linux/if_arp.h> +#include <linux/workqueue.h> #include <linux/can.h> #include <linux/can/dev.h> #include <linux/can/skb.h> @@ -471,9 +472,8 @@ EXPORT_SYMBOL_GPL(can_free_echo_skb); /* * CAN device restart for bus-off recovery */ -static void can_restart(unsigned long data) +static void can_restart(struct net_device *dev) { - struct net_device *dev = (struct net_device *)data; struct can_priv *priv = netdev_priv(dev); struct net_device_stats *stats = &dev->stats; struct sk_buff *skb; @@ -513,6 +513,14 @@ restart: netdev_err(dev, "Error %d during restart", err); } +static void can_restart_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct can_priv *priv = container_of(dwork, struct can_priv, restart_work); + + can_restart(priv->dev); +} + int can_restart_now(struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); @@ -526,8 +534,8 @@ int can_restart_now(struct net_device *dev) if (priv->state != CAN_STATE_BUS_OFF) return -EBUSY; - /* Runs as soon as possible in the timer context */ - mod_timer(&priv->restart_timer, jiffies); + cancel_delayed_work_sync(&priv->restart_work); + can_restart(dev); return 0; } @@ -548,8 +556,8 @@ void can_bus_off(struct net_device *dev) netif_carrier_off(dev); if (priv->restart_ms) - mod_timer(&priv->restart_timer, - jiffies + (priv->restart_ms * HZ) / 1000); + schedule_delayed_work(&priv->restart_work, + msecs_to_jiffies(priv->restart_ms)); } EXPORT_SYMBOL_GPL(can_bus_off); @@ -658,6 +666,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max) return NULL; priv = netdev_priv(dev); + priv->dev = dev; if (echo_skb_max) { priv->echo_skb_max = echo_skb_max; @@ -667,7 +676,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max) priv->state = CAN_STATE_STOPPED; - init_timer(&priv->restart_timer); + INIT_DELAYED_WORK(&priv->restart_work, can_restart_work); return dev; } @@ -748,8 +757,6 @@ int open_candev(struct net_device *dev) if (!netif_carrier_ok(dev)) netif_carrier_on(dev); - setup_timer(&priv->restart_timer, can_restart, (unsigned long)dev); - return 0; } EXPORT_SYMBOL_GPL(open_candev); @@ -764,7 +771,7 @@ void close_candev(struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); - del_timer_sync(&priv->restart_timer); + cancel_delayed_work_sync(&priv->restart_work); can_flush_echo_skb(dev); } EXPORT_SYMBOL_GPL(close_candev); diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 41c0fc9f3b14..16f7cadda5c3 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -1268,11 +1268,10 @@ static int __maybe_unused flexcan_suspend(struct device *device) struct flexcan_priv *priv = netdev_priv(dev); int err; - err = flexcan_chip_disable(priv); - if (err) - return err; - if (netif_running(dev)) { + err = flexcan_chip_disable(priv); + if (err) + return err; netif_stop_queue(dev); netif_device_detach(dev); } @@ -1285,13 +1284,17 @@ static int __maybe_unused flexcan_resume(struct device *device) { struct net_device *dev = dev_get_drvdata(device); struct flexcan_priv *priv = netdev_priv(dev); + int err; priv->can.state = CAN_STATE_ERROR_ACTIVE; if (netif_running(dev)) { netif_device_attach(dev); netif_start_queue(dev); + err = flexcan_chip_enable(priv); + if (err) + return err; } - return flexcan_chip_enable(priv); + return 0; } static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume); diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 6f946fedbb77..0864f05633a2 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -1137,6 +1137,7 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phydev) { struct bcm_sf2_priv *priv = ds_to_priv(ds); + struct ethtool_eee *p = &priv->port_sts[port].eee; u32 id_mode_dis = 0, port_mode; const char *str = NULL; u32 reg; @@ -1211,6 +1212,9 @@ force_link: reg |= DUPLX_MODE; core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port)); + + if (!phydev->is_pseudo_fixed_link) + p->eee_enabled = bcm_sf2_eee_init(ds, port, phydev); } static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h index 6bba1c98d764..c7994e372284 100644 --- a/drivers/net/dsa/bcm_sf2.h +++ b/drivers/net/dsa/bcm_sf2.h @@ -187,8 +187,8 @@ static inline void name##_writeq(struct bcm_sf2_priv *priv, u64 val, \ static inline void intrl2_##which##_mask_clear(struct bcm_sf2_priv *priv, \ u32 mask) \ { \ - intrl2_##which##_writel(priv, mask, INTRL2_CPU_MASK_CLEAR); \ priv->irq##which##_mask &= ~(mask); \ + intrl2_##which##_writel(priv, mask, INTRL2_CPU_MASK_CLEAR); \ } \ static inline void intrl2_##which##_mask_set(struct bcm_sf2_priv *priv, \ u32 mask) \ diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index c32f5d32f811..b56c9c581359 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -314,6 +314,10 @@ static void bgmac_dma_rx_enable(struct bgmac *bgmac, u32 ctl; ctl = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_RX_CTL); + + /* preserve ONLY bits 16-17 from current hardware value */ + ctl &= BGMAC_DMA_RX_ADDREXT_MASK; + if (bgmac->core->id.rev >= 4) { ctl &= ~BGMAC_DMA_RX_BL_MASK; ctl |= BGMAC_DMA_RX_BL_128 << BGMAC_DMA_RX_BL_SHIFT; @@ -324,7 +328,6 @@ static void bgmac_dma_rx_enable(struct bgmac *bgmac, ctl &= ~BGMAC_DMA_RX_PT_MASK; ctl |= BGMAC_DMA_RX_PT_1 << BGMAC_DMA_RX_PT_SHIFT; } - ctl &= BGMAC_DMA_RX_ADDREXT_MASK; ctl |= BGMAC_DMA_RX_ENABLE; ctl |= BGMAC_DMA_RX_PARITY_DISABLE; ctl |= BGMAC_DMA_RX_OVERFLOW_CONT; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 2e611dc5f162..1c8123816745 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -14819,6 +14819,10 @@ static int bnx2x_get_fc_npiv(struct net_device *dev, } offset = SHMEM2_RD(bp, fc_npiv_nvram_tbl_addr[BP_PORT(bp)]); + if (!offset) { + DP(BNX2X_MSG_MCP, "No FC-NPIV in NVRAM\n"); + goto out; + } DP(BNX2X_MSG_MCP, "Offset of FC-NPIV in NVRAM: %08x\n", offset); /* Read the table contents from nvram */ diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 0fb3f8de88e9..91627561c58d 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1168,6 +1168,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, struct bcmgenet_tx_ring *ring) { struct bcmgenet_priv *priv = netdev_priv(dev); + struct device *kdev = &priv->pdev->dev; struct enet_cb *tx_cb_ptr; struct netdev_queue *txq; unsigned int pkts_compl = 0; @@ -1195,7 +1196,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, pkts_compl++; dev->stats.tx_packets++; dev->stats.tx_bytes += tx_cb_ptr->skb->len; - dma_unmap_single(&dev->dev, + dma_unmap_single(kdev, dma_unmap_addr(tx_cb_ptr, dma_addr), dma_unmap_len(tx_cb_ptr, dma_len), DMA_TO_DEVICE); @@ -1203,7 +1204,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, } else if (dma_unmap_addr(tx_cb_ptr, dma_addr)) { dev->stats.tx_bytes += dma_unmap_len(tx_cb_ptr, dma_len); - dma_unmap_page(&dev->dev, + dma_unmap_page(kdev, dma_unmap_addr(tx_cb_ptr, dma_addr), dma_unmap_len(tx_cb_ptr, dma_len), DMA_TO_DEVICE); @@ -1754,6 +1755,7 @@ static int bcmgenet_alloc_rx_buffers(struct bcmgenet_priv *priv, static void bcmgenet_free_rx_buffers(struct bcmgenet_priv *priv) { + struct device *kdev = &priv->pdev->dev; struct enet_cb *cb; int i; @@ -1761,7 +1763,7 @@ static void bcmgenet_free_rx_buffers(struct bcmgenet_priv *priv) cb = &priv->rx_cbs[i]; if (dma_unmap_addr(cb, dma_addr)) { - dma_unmap_single(&priv->dev->dev, + dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr), priv->rx_buf_len, DMA_FROM_DEVICE); dma_unmap_addr_set(cb, dma_addr, 0); diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index ca5ac5d6f4e6..49056c33be74 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -18142,14 +18142,14 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev, rtnl_lock(); - /* We needn't recover from permanent error */ - if (state == pci_channel_io_frozen) - tp->pcierr_recovery = true; - /* We probably don't have netdev yet */ if (!netdev || !netif_running(netdev)) goto done; + /* We needn't recover from permanent error */ + if (state == pci_channel_io_frozen) + tp->pcierr_recovery = true; + tg3_phy_stop(tp); tg3_netif_stop(tp); @@ -18246,7 +18246,7 @@ static void tg3_io_resume(struct pci_dev *pdev) rtnl_lock(); - if (!netif_running(netdev)) + if (!netdev || !netif_running(netdev)) goto done; tg3_full_lock(tp, 0); diff --git a/drivers/net/ethernet/cavium/thunder/nic_reg.h b/drivers/net/ethernet/cavium/thunder/nic_reg.h index afb10e326b4f..fab35a593898 100644 --- a/drivers/net/ethernet/cavium/thunder/nic_reg.h +++ b/drivers/net/ethernet/cavium/thunder/nic_reg.h @@ -170,7 +170,6 @@ #define NIC_QSET_SQ_0_7_DOOR (0x010838) #define NIC_QSET_SQ_0_7_STATUS (0x010840) #define NIC_QSET_SQ_0_7_DEBUG (0x010848) -#define NIC_QSET_SQ_0_7_CNM_CHG (0x010860) #define NIC_QSET_SQ_0_7_STAT_0_1 (0x010900) #define NIC_QSET_RBDR_0_1_CFG (0x010C00) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c index a12b2e38cf61..ff1d777f3ed9 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c @@ -380,7 +380,10 @@ static void nicvf_get_regs(struct net_device *dev, p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_DOOR, q); p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_STATUS, q); p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_DEBUG, q); - p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_CNM_CHG, q); + /* Padding, was NIC_QSET_SQ_0_7_CNM_CHG, which + * produces bus errors when read + */ + p[i++] = 0; p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_STAT_0_1, q); reg_offset = NIC_QSET_SQ_0_7_STAT_0_1 | (1 << 3); p[i++] = nicvf_queue_reg_read(nic, reg_offset, q); diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index f6147ffc7fbc..ab716042bdd2 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -944,11 +944,11 @@ fec_restart(struct net_device *ndev) * enet-mac reset will reset mac address registers too, * so need to reconfigure it. */ - if (fep->quirks & FEC_QUIRK_ENET_MAC) { - memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); - writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW); - writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH); - } + memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); + writel((__force u32)cpu_to_be32(temp_mac[0]), + fep->hwp + FEC_ADDR_LOW); + writel((__force u32)cpu_to_be32(temp_mac[1]), + fep->hwp + FEC_ADDR_HIGH); /* Clear any outstanding interrupt. */ writel(0xffffffff, fep->hwp + FEC_IEVENT); diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 2d74c6e4d7b6..1cf715c72683 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -302,13 +302,15 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc, void *buffer, u16 buf_len) { struct i40e_aq_desc *aq_desc = (struct i40e_aq_desc *)desc; - u16 len = le16_to_cpu(aq_desc->datalen); + u16 len; u8 *buf = (u8 *)buffer; u16 i = 0; if ((!(mask & hw->debug_mask)) || (desc == NULL)) return; + len = le16_to_cpu(aq_desc->datalen); + i40e_debug(hw, mask, "AQ CMD: opcode 0x%04X, flags 0x%04X, datalen 0x%04X, retval 0x%04X\n", le16_to_cpu(aq_desc->opcode), diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2215bebe208e..4edbab6ca7ef 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -8595,7 +8595,7 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, return 0; return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode, - nlflags, 0, 0, filter_mask, NULL); + 0, 0, nlflags, filter_mask, NULL); } #define I40E_MAX_TUNNEL_HDR_LEN 80 @@ -10853,6 +10853,12 @@ static pci_ers_result_t i40e_pci_error_detected(struct pci_dev *pdev, dev_info(&pdev->dev, "%s: error %d\n", __func__, error); + if (!pf) { + dev_info(&pdev->dev, + "Cannot recover - error happened during device probe\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + /* shutdown all operations */ if (!test_bit(__I40E_SUSPENDED, &pf->state)) { rtnl_lock(); diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index 5606a043063e..4b62aa1f9ff8 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -5220,6 +5220,19 @@ static SIMPLE_DEV_PM_OPS(sky2_pm_ops, sky2_suspend, sky2_resume); static void sky2_shutdown(struct pci_dev *pdev) { + struct sky2_hw *hw = pci_get_drvdata(pdev); + int port; + + for (port = 0; port < hw->ports; port++) { + struct net_device *ndev = hw->dev[port]; + + rtnl_lock(); + if (netif_running(ndev)) { + dev_close(ndev); + netif_device_detach(ndev); + } + rtnl_unlock(); + } sky2_suspend(&pdev->dev); pci_wake_from_d3(pdev, device_may_wakeup(&pdev->dev)); pci_set_power_state(pdev, PCI_D3hot); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 67e9633ea9c7..232191417b93 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2282,7 +2282,7 @@ static int mlx4_en_set_vf_mac(struct net_device *dev, int queue, u8 *mac) struct mlx4_en_dev *mdev = en_priv->mdev; u64 mac_u64 = mlx4_mac_to_u64(mac); - if (!is_valid_ether_addr(mac)) + if (is_multicast_ether_addr(mac)) return -EINVAL; return mlx4_set_vf_mac(mdev->dev, en_priv->port, queue, mac_u64); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 037fc4cdf5af..cc199063612a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -143,13 +143,14 @@ static struct mlx5_cmd_layout *get_inst(struct mlx5_cmd *cmd, int idx) return cmd->cmd_buf + (idx << cmd->log_stride); } -static u8 xor8_buf(void *buf, int len) +static u8 xor8_buf(void *buf, size_t offset, int len) { u8 *ptr = buf; u8 sum = 0; int i; + int end = len + offset; - for (i = 0; i < len; i++) + for (i = offset; i < end; i++) sum ^= ptr[i]; return sum; @@ -157,41 +158,49 @@ static u8 xor8_buf(void *buf, int len) static int verify_block_sig(struct mlx5_cmd_prot_block *block) { - if (xor8_buf(block->rsvd0, sizeof(*block) - sizeof(block->data) - 1) != 0xff) + size_t rsvd0_off = offsetof(struct mlx5_cmd_prot_block, rsvd0); + int xor_len = sizeof(*block) - sizeof(block->data) - 1; + + if (xor8_buf(block, rsvd0_off, xor_len) != 0xff) return -EINVAL; - if (xor8_buf(block, sizeof(*block)) != 0xff) + if (xor8_buf(block, 0, sizeof(*block)) != 0xff) return -EINVAL; return 0; } -static void calc_block_sig(struct mlx5_cmd_prot_block *block, u8 token, - int csum) +static void calc_block_sig(struct mlx5_cmd_prot_block *block) { - block->token = token; - if (csum) { - block->ctrl_sig = ~xor8_buf(block->rsvd0, sizeof(*block) - - sizeof(block->data) - 2); - block->sig = ~xor8_buf(block, sizeof(*block) - 1); - } + int ctrl_xor_len = sizeof(*block) - sizeof(block->data) - 2; + size_t rsvd0_off = offsetof(struct mlx5_cmd_prot_block, rsvd0); + + block->ctrl_sig = ~xor8_buf(block, rsvd0_off, ctrl_xor_len); + block->sig = ~xor8_buf(block, 0, sizeof(*block) - 1); } -static void calc_chain_sig(struct mlx5_cmd_msg *msg, u8 token, int csum) +static void calc_chain_sig(struct mlx5_cmd_msg *msg) { struct mlx5_cmd_mailbox *next = msg->next; - - while (next) { - calc_block_sig(next->buf, token, csum); + int size = msg->len; + int blen = size - min_t(int, sizeof(msg->first.data), size); + int n = (blen + MLX5_CMD_DATA_BLOCK_SIZE - 1) + / MLX5_CMD_DATA_BLOCK_SIZE; + int i = 0; + + for (i = 0; i < n && next; i++) { + calc_block_sig(next->buf); next = next->next; } } static void set_signature(struct mlx5_cmd_work_ent *ent, int csum) { - ent->lay->sig = ~xor8_buf(ent->lay, sizeof(*ent->lay)); - calc_chain_sig(ent->in, ent->token, csum); - calc_chain_sig(ent->out, ent->token, csum); + ent->lay->sig = ~xor8_buf(ent->lay, 0, sizeof(*ent->lay)); + if (csum) { + calc_chain_sig(ent->in); + calc_chain_sig(ent->out); + } } static void poll_timeout(struct mlx5_cmd_work_ent *ent) @@ -222,12 +231,17 @@ static int verify_signature(struct mlx5_cmd_work_ent *ent) struct mlx5_cmd_mailbox *next = ent->out->next; int err; u8 sig; + int size = ent->out->len; + int blen = size - min_t(int, sizeof(ent->out->first.data), size); + int n = (blen + MLX5_CMD_DATA_BLOCK_SIZE - 1) + / MLX5_CMD_DATA_BLOCK_SIZE; + int i = 0; - sig = xor8_buf(ent->lay, sizeof(*ent->lay)); + sig = xor8_buf(ent->lay, 0, sizeof(*ent->lay)); if (sig != 0xff) return -EINVAL; - while (next) { + for (i = 0; i < n && next; i++) { err = verify_block_sig(next->buf); if (err) return err; @@ -641,7 +655,6 @@ static void cmd_work_handler(struct work_struct *work) spin_unlock_irqrestore(&cmd->alloc_lock, flags); } - ent->token = alloc_token(cmd); cmd->ent_arr[ent->idx] = ent; lay = get_inst(cmd, ent->idx); ent->lay = lay; @@ -755,7 +768,8 @@ static u8 *get_status_ptr(struct mlx5_outbox_hdr *out) static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, struct mlx5_cmd_msg *out, void *uout, int uout_size, mlx5_cmd_cbk_t callback, - void *context, int page_queue, u8 *status) + void *context, int page_queue, u8 *status, + u8 token) { struct mlx5_cmd *cmd = &dev->cmd; struct mlx5_cmd_work_ent *ent; @@ -772,6 +786,8 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, if (IS_ERR(ent)) return PTR_ERR(ent); + ent->token = token; + if (!callback) init_completion(&ent->done); @@ -844,7 +860,8 @@ static const struct file_operations fops = { .write = dbg_write, }; -static int mlx5_copy_to_msg(struct mlx5_cmd_msg *to, void *from, int size) +static int mlx5_copy_to_msg(struct mlx5_cmd_msg *to, void *from, int size, + u8 token) { struct mlx5_cmd_prot_block *block; struct mlx5_cmd_mailbox *next; @@ -870,6 +887,7 @@ static int mlx5_copy_to_msg(struct mlx5_cmd_msg *to, void *from, int size) memcpy(block->data, from, copy); from += copy; size -= copy; + block->token = token; next = next->next; } @@ -939,7 +957,8 @@ static void free_cmd_box(struct mlx5_core_dev *dev, } static struct mlx5_cmd_msg *mlx5_alloc_cmd_msg(struct mlx5_core_dev *dev, - gfp_t flags, int size) + gfp_t flags, int size, + u8 token) { struct mlx5_cmd_mailbox *tmp, *head = NULL; struct mlx5_cmd_prot_block *block; @@ -968,6 +987,7 @@ static struct mlx5_cmd_msg *mlx5_alloc_cmd_msg(struct mlx5_core_dev *dev, tmp->next = head; block->next = cpu_to_be64(tmp->next ? tmp->next->dma : 0); block->block_num = cpu_to_be32(n - i - 1); + block->token = token; head = tmp; } msg->next = head; @@ -1351,7 +1371,7 @@ static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size, } if (IS_ERR(msg)) - msg = mlx5_alloc_cmd_msg(dev, gfp, in_size); + msg = mlx5_alloc_cmd_msg(dev, gfp, in_size, 0); return msg; } @@ -1376,6 +1396,7 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, int err; u8 status = 0; u32 drv_synd; + u8 token; if (pci_channel_offline(dev->pdev) || dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) { @@ -1394,20 +1415,22 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, return err; } - err = mlx5_copy_to_msg(inb, in, in_size); + token = alloc_token(&dev->cmd); + + err = mlx5_copy_to_msg(inb, in, in_size, token); if (err) { mlx5_core_warn(dev, "err %d\n", err); goto out_in; } - outb = mlx5_alloc_cmd_msg(dev, gfp, out_size); + outb = mlx5_alloc_cmd_msg(dev, gfp, out_size, token); if (IS_ERR(outb)) { err = PTR_ERR(outb); goto out_in; } err = mlx5_cmd_invoke(dev, inb, outb, out, out_size, callback, context, - pages_queue, &status); + pages_queue, &status, token); if (err) goto out_out; @@ -1475,7 +1498,7 @@ static int create_msg_cache(struct mlx5_core_dev *dev) INIT_LIST_HEAD(&cmd->cache.med.head); for (i = 0; i < NUM_LONG_LISTS; i++) { - msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, LONG_LIST_SIZE); + msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, LONG_LIST_SIZE, 0); if (IS_ERR(msg)) { err = PTR_ERR(msg); goto ex_err; @@ -1485,7 +1508,7 @@ static int create_msg_cache(struct mlx5_core_dev *dev) } for (i = 0; i < NUM_MED_LISTS; i++) { - msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, MED_LIST_SIZE); + msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, MED_LIST_SIZE, 0); if (IS_ERR(msg)) { err = PTR_ERR(msg); goto ex_err; diff --git a/drivers/net/ethernet/msm/msm_rmnet_mhi.c b/drivers/net/ethernet/msm/msm_rmnet_mhi.c index 50d8e72a96c8..9117ea7d08c0 100644 --- a/drivers/net/ethernet/msm/msm_rmnet_mhi.c +++ b/drivers/net/ethernet/msm/msm_rmnet_mhi.c @@ -26,18 +26,16 @@ #include <linux/ipc_logging.h> #include <linux/device.h> #include <linux/errno.h> +#include <linux/of_device.h> #define RMNET_MHI_DRIVER_NAME "rmnet_mhi" #define RMNET_MHI_DEV_NAME "rmnet_mhi%d" #define MHI_DEFAULT_MTU 8000 -#define MHI_DEFAULT_MRU 8000 #define MHI_MAX_MRU 0xFFFF #define MHI_NAPI_WEIGHT_VALUE 12 #define MHI_RX_HEADROOM 64 #define WATCHDOG_TIMEOUT (30 * HZ) -#define MHI_RMNET_DEVICE_COUNT 1 #define RMNET_IPC_LOG_PAGES (100) -#define IS_INBOUND(_chan) (((u32)(_chan)) % 2) enum DBG_LVL { MSG_VERBOSE = 0x1, @@ -49,110 +47,69 @@ enum DBG_LVL { MSG_reserved = 0x80000000 }; +struct debug_params { + enum DBG_LVL rmnet_msg_lvl; + enum DBG_LVL rmnet_ipc_log_lvl; + u64 tx_interrupts_count; + u64 rx_interrupts_count; + u64 tx_ring_full_count; + u64 tx_queued_packets_count; + u64 rx_interrupts_in_masked_irq; + u64 rx_napi_skb_burst_min; + u64 rx_napi_skb_burst_max; + u64 tx_cb_skb_free_burst_min; + u64 tx_cb_skb_free_burst_max; + u64 rx_napi_budget_overflow; + u64 rx_fragmentation; +}; + struct __packed mhi_skb_priv { dma_addr_t dma_addr; size_t dma_size; }; -enum DBG_LVL rmnet_msg_lvl = MSG_CRITICAL; - -#ifdef CONFIG_MSM_MHI_DEBUG -enum DBG_LVL rmnet_ipc_log_lvl = MSG_VERBOSE; -#else -enum DBG_LVL rmnet_ipc_log_lvl = MSG_ERROR; -#endif - -module_param(rmnet_msg_lvl , uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(rmnet_msg_lvl, "dbg lvl"); -module_param(rmnet_ipc_log_lvl, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(rmnet_ipc_log_lvl, "dbg lvl"); - -unsigned int mru = MHI_DEFAULT_MRU; -module_param(mru, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(mru, "MRU interface setting"); - -void *rmnet_ipc_log; - -#define rmnet_log(_msg_lvl, _msg, ...) do { \ - if ((_msg_lvl) >= rmnet_msg_lvl) \ +#define rmnet_log(rmnet_mhi_ptr, _msg_lvl, _msg, ...) do { \ + if ((_msg_lvl) >= rmnet_mhi_ptr->debug.rmnet_msg_lvl) \ pr_alert("[%s] " _msg, __func__, ##__VA_ARGS__);\ - if (rmnet_ipc_log && ((_msg_lvl) >= rmnet_ipc_log_lvl)) \ - ipc_log_string(rmnet_ipc_log, \ + if (rmnet_mhi_ptr->rmnet_ipc_log && \ + ((_msg_lvl) >= rmnet_mhi_ptr->debug.rmnet_ipc_log_lvl)) \ + ipc_log_string(rmnet_mhi_ptr->rmnet_ipc_log, \ "[%s] " _msg, __func__, ##__VA_ARGS__); \ } while (0) -unsigned long tx_interrupts_count[MHI_RMNET_DEVICE_COUNT]; -module_param_array(tx_interrupts_count, ulong, 0, S_IRUGO); -MODULE_PARM_DESC(tx_interrupts_count, "Tx interrupts"); - -unsigned long rx_interrupts_count[MHI_RMNET_DEVICE_COUNT]; -module_param_array(rx_interrupts_count, ulong, 0, S_IRUGO); -MODULE_PARM_DESC(rx_interrupts_count, "RX interrupts"); - -unsigned long tx_ring_full_count[MHI_RMNET_DEVICE_COUNT]; -module_param_array(tx_ring_full_count, ulong, 0, S_IRUGO); -MODULE_PARM_DESC(tx_ring_full_count, "RING FULL errors from MHI Core"); - - -unsigned long tx_queued_packets_count[MHI_RMNET_DEVICE_COUNT]; -module_param_array(tx_queued_packets_count, ulong, 0, S_IRUGO); -MODULE_PARM_DESC(tx_queued_packets_count, "TX packets queued in MHI core"); - -unsigned long rx_interrupts_in_masked_irq[MHI_RMNET_DEVICE_COUNT]; -module_param_array(rx_interrupts_in_masked_irq, ulong, 0, S_IRUGO); -MODULE_PARM_DESC(rx_interrupts_in_masked_irq, - "RX interrupts while IRQs are masked"); - -unsigned long rx_napi_skb_burst_min[MHI_RMNET_DEVICE_COUNT]; -module_param_array(rx_napi_skb_burst_min, ulong, 0, S_IRUGO); -MODULE_PARM_DESC(rx_napi_skb_burst_min, "MIN SKBs sent to NS during NAPI"); - -unsigned long rx_napi_skb_burst_max[MHI_RMNET_DEVICE_COUNT]; -module_param_array(rx_napi_skb_burst_max, ulong, 0, S_IRUGO); -MODULE_PARM_DESC(rx_napi_skb_burst_max, "MAX SKBs sent to NS during NAPI"); - -unsigned long tx_cb_skb_free_burst_min[MHI_RMNET_DEVICE_COUNT]; -module_param_array(tx_cb_skb_free_burst_min, ulong, 0, S_IRUGO); -MODULE_PARM_DESC(tx_cb_skb_free_burst_min, "MIN SKBs freed during TX CB"); - -unsigned long tx_cb_skb_free_burst_max[MHI_RMNET_DEVICE_COUNT]; -module_param_array(tx_cb_skb_free_burst_max, ulong, 0, S_IRUGO); -MODULE_PARM_DESC(tx_cb_skb_free_burst_max, "MAX SKBs freed during TX CB"); - -unsigned long rx_napi_budget_overflow[MHI_RMNET_DEVICE_COUNT]; -module_param_array(rx_napi_budget_overflow, ulong, 0, S_IRUGO); -MODULE_PARM_DESC(rx_napi_budget_overflow, - "Budget hit with more items to read counter"); - -unsigned long rx_fragmentation[MHI_RMNET_DEVICE_COUNT]; -module_param_array(rx_fragmentation, ulong, 0, S_IRUGO); -MODULE_PARM_DESC(rx_fragmentation, - "Number of fragmented packets received"); - struct rmnet_mhi_private { - int dev_index; + struct list_head node; struct mhi_client_handle *tx_client_handle; struct mhi_client_handle *rx_client_handle; enum MHI_CLIENT_CHANNEL tx_channel; enum MHI_CLIENT_CHANNEL rx_channel; struct sk_buff_head tx_buffers; struct sk_buff_head rx_buffers; - uint32_t mru; + atomic_t rx_pool_len; + u32 mru; struct napi_struct napi; gfp_t allocation_flags; uint32_t tx_buffers_max; uint32_t rx_buffers_max; + u32 alloc_fail; u32 tx_enabled; u32 rx_enabled; u32 mhi_enabled; + struct platform_device *pdev; struct net_device *dev; atomic_t irq_masked_cntr; - rwlock_t out_chan_full_lock; + spinlock_t out_chan_full_lock; /* tx queue lock */ atomic_t pending_data; struct sk_buff *frag_skb; + struct work_struct alloc_work; + /* lock to queue hardware and internal queue */ + spinlock_t alloc_lock; + void *rmnet_ipc_log; + struct debug_params debug; + struct dentry *dentry; }; -static struct rmnet_mhi_private rmnet_mhi_ctxt_list[MHI_RMNET_DEVICE_COUNT]; +static LIST_HEAD(rmnet_mhi_ctxt_list); static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr, struct sk_buff *skb, int frag) @@ -184,7 +141,7 @@ static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr, if (frag) { /* This is the first fragment */ rmnet_mhi_ptr->frag_skb = skb; - rx_fragmentation[rmnet_mhi_ptr->dev_index]++; + rmnet_mhi_ptr->debug.rx_fragmentation++; } else { netif_receive_skb(skb); } @@ -196,8 +153,10 @@ static void rmnet_mhi_internal_clean_unmap_buffers(struct net_device *dev, enum dma_data_direction dir) { struct mhi_skb_priv *skb_priv; + struct rmnet_mhi_private *rmnet_mhi_ptr = + *(struct rmnet_mhi_private **)netdev_priv(dev); - rmnet_log(MSG_INFO, "Entered\n"); + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n"); while (!skb_queue_empty(queue)) { struct sk_buff *skb = skb_dequeue(queue); skb_priv = (struct mhi_skb_priv *)(skb->cb); @@ -205,7 +164,7 @@ static void rmnet_mhi_internal_clean_unmap_buffers(struct net_device *dev, kfree_skb(skb); } } - rmnet_log(MSG_INFO, "Exited\n"); + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited\n"); } static __be16 rmnet_mhi_ip_type_trans(struct sk_buff *skb) @@ -228,6 +187,83 @@ static __be16 rmnet_mhi_ip_type_trans(struct sk_buff *skb) return protocol; } +static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr, + gfp_t alloc_flags) +{ + u32 cur_mru = rmnet_mhi_ptr->mru; + struct mhi_skb_priv *skb_priv; + unsigned long flags; + int ret; + struct sk_buff *skb; + + while (atomic_read(&rmnet_mhi_ptr->rx_pool_len) < + rmnet_mhi_ptr->rx_buffers_max) { + skb = alloc_skb(cur_mru, alloc_flags); + if (!skb) { + rmnet_log(rmnet_mhi_ptr, + MSG_INFO, + "SKB Alloc failed with flags:0x%x\n", + alloc_flags); + return -ENOMEM; + } + skb_priv = (struct mhi_skb_priv *)(skb->cb); + skb_priv->dma_size = cur_mru - MHI_RX_HEADROOM; + 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); + + /* It's possible by the time alloc_skb (GFP_KERNEL) + * returns we already called rmnet_alloc_rx + * in atomic context and allocated memory using + * GFP_ATOMIC and returned. + */ + if (unlikely(atomic_read(&rmnet_mhi_ptr->rx_pool_len) >= + rmnet_mhi_ptr->rx_buffers_max)) { + spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock, + flags); + dev_kfree_skb_any(skb); + return 0; + } + + ret = mhi_queue_xfer( + rmnet_mhi_ptr->rx_client_handle, + skb->data, + skb_priv->dma_size, + MHI_EOT); + if (unlikely(ret != 0)) { + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "mhi_queue_xfer failed, error %d", ret); + spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock, + flags); + dev_kfree_skb_any(skb); + return ret; + } + skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb); + atomic_inc(&rmnet_mhi_ptr->rx_pool_len); + spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock, flags); + } + + return 0; +} + +static void rmnet_mhi_alloc_work(struct work_struct *work) +{ + struct rmnet_mhi_private *rmnet_mhi_ptr = container_of(work, + struct rmnet_mhi_private, + alloc_work); + int ret; + + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n"); + ret = rmnet_alloc_rx(rmnet_mhi_ptr, + rmnet_mhi_ptr->allocation_flags); + + WARN_ON(ret == -ENOMEM); + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exit\n"); +} + static int rmnet_mhi_poll(struct napi_struct *napi, int budget) { int received_packets = 0; @@ -238,20 +274,22 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget) bool should_reschedule = true; struct sk_buff *skb; struct mhi_skb_priv *skb_priv; - int r, cur_mru; + int r; + + rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n"); - rmnet_log(MSG_VERBOSE, "Entered\n"); - rmnet_mhi_ptr->mru = mru; while (received_packets < budget) { struct mhi_result *result = mhi_poll(rmnet_mhi_ptr->rx_client_handle); if (result->transaction_status == -ENOTCONN) { - rmnet_log(MSG_INFO, + rmnet_log(rmnet_mhi_ptr, + MSG_INFO, "Transaction status not ready, continuing\n"); break; } else if (result->transaction_status != 0 && result->transaction_status != -EOVERFLOW) { - rmnet_log(MSG_CRITICAL, + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, "mhi_poll failed, error %d\n", result->transaction_status); break; @@ -259,15 +297,15 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget) /* Nothing more to read, or out of buffers in MHI layer */ if (unlikely(!result->buf_addr || !result->bytes_xferd)) { - rmnet_log(MSG_CRITICAL, - "Not valid buff not rescheduling\n"); should_reschedule = false; break; } + atomic_dec(&rmnet_mhi_ptr->rx_pool_len); skb = skb_dequeue(&(rmnet_mhi_ptr->rx_buffers)); if (unlikely(!skb)) { - rmnet_log(MSG_CRITICAL, + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, "No RX buffers to match"); break; } @@ -285,7 +323,7 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget) else r = rmnet_mhi_process_fragment(rmnet_mhi_ptr, skb, 0); if (r) { - rmnet_log(MSG_CRITICAL, + rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL, "Failed to process fragmented packet ret %d", r); BUG(); @@ -296,45 +334,18 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget) dev->stats.rx_packets++; dev->stats.rx_bytes += result->bytes_xferd; - /* Need to allocate a new buffer instead of this one */ - cur_mru = rmnet_mhi_ptr->mru; - skb = alloc_skb(cur_mru, GFP_ATOMIC); - if (unlikely(!skb)) { - rmnet_log(MSG_CRITICAL, - "Can't allocate a new RX buffer for MHI"); - break; - } - skb_priv = (struct mhi_skb_priv *)(skb->cb); - skb_priv->dma_size = cur_mru; - - rmnet_log(MSG_VERBOSE, - "Allocated SKB of MRU 0x%x, SKB_DATA 0%p SKB_LEN 0x%x\n", - rmnet_mhi_ptr->mru, skb->data, skb->len); - /* Reserve headroom, tail == data */ - skb_reserve(skb, MHI_RX_HEADROOM); - skb_priv->dma_size -= MHI_RX_HEADROOM; - skb_priv->dma_addr = 0; - - rmnet_log(MSG_VERBOSE, - "Mapped SKB %p to DMA Addr 0x%lx, DMA_SIZE: 0x%lx\n", - skb->data, - (uintptr_t)skb->data, - (uintptr_t)skb_priv->dma_size); - - - res = mhi_queue_xfer( - rmnet_mhi_ptr->rx_client_handle, - skb->data, skb_priv->dma_size, MHI_EOT); - - if (unlikely(0 != res)) { - rmnet_log(MSG_CRITICAL, - "mhi_queue_xfer failed, error %d", res); - dev_kfree_skb_irq(skb); - break; - } - skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb); } /* while (received_packets < budget) or any other error */ + /* Queue new buffers */ + res = rmnet_alloc_rx(rmnet_mhi_ptr, GFP_ATOMIC); + if (res == -ENOMEM) { + rmnet_log(rmnet_mhi_ptr, + MSG_INFO, + "out of mem, queuing bg worker\n"); + rmnet_mhi_ptr->alloc_fail++; + schedule_work(&rmnet_mhi_ptr->alloc_work); + } + napi_complete(napi); /* We got a NULL descriptor back */ @@ -342,22 +353,24 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget) if (atomic_read(&rmnet_mhi_ptr->irq_masked_cntr)) { atomic_dec(&rmnet_mhi_ptr->irq_masked_cntr); mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle); + mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, true); } } else { if (received_packets == budget) - rx_napi_budget_overflow[rmnet_mhi_ptr->dev_index]++; + rmnet_mhi_ptr->debug.rx_napi_budget_overflow++; napi_reschedule(napi); } - rx_napi_skb_burst_min[rmnet_mhi_ptr->dev_index] = - min((unsigned long)received_packets, - rx_napi_skb_burst_min[rmnet_mhi_ptr->dev_index]); + rmnet_mhi_ptr->debug.rx_napi_skb_burst_min = + min((u64)received_packets, + rmnet_mhi_ptr->debug.rx_napi_skb_burst_min); - rx_napi_skb_burst_max[rmnet_mhi_ptr->dev_index] = - max((unsigned long)received_packets, - rx_napi_skb_burst_max[rmnet_mhi_ptr->dev_index]); + rmnet_mhi_ptr->debug.rx_napi_skb_burst_max = + max((u64)received_packets, + rmnet_mhi_ptr->debug.rx_napi_skb_burst_max); - rmnet_log(MSG_VERBOSE, "Exited, polled %d pkts\n", received_packets); + rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, + "Exited, polled %d pkts\n", received_packets); return received_packets; } @@ -365,7 +378,8 @@ void rmnet_mhi_clean_buffers(struct net_device *dev) { struct rmnet_mhi_private *rmnet_mhi_ptr = *(struct rmnet_mhi_private **)netdev_priv(dev); - rmnet_log(MSG_INFO, "Entered\n"); + + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n"); /* Clean TX buffers */ rmnet_mhi_internal_clean_unmap_buffers(dev, &rmnet_mhi_ptr->tx_buffers, @@ -375,16 +389,16 @@ void rmnet_mhi_clean_buffers(struct net_device *dev) rmnet_mhi_internal_clean_unmap_buffers(dev, &rmnet_mhi_ptr->rx_buffers, DMA_FROM_DEVICE); - rmnet_log(MSG_INFO, "Exited\n"); + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited\n"); } static int rmnet_mhi_disable_channels(struct rmnet_mhi_private *rmnet_mhi_ptr) { - rmnet_log(MSG_INFO, "Closing MHI TX channel\n"); + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Closing MHI TX channel\n"); mhi_close_channel(rmnet_mhi_ptr->tx_client_handle); - rmnet_log(MSG_INFO, "Closing MHI RX channel\n"); + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Closing MHI RX channel\n"); mhi_close_channel(rmnet_mhi_ptr->rx_client_handle); - rmnet_log(MSG_INFO, "Clearing Pending TX buffers.\n"); + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Clearing Pending TX buffers.\n"); rmnet_mhi_clean_buffers(rmnet_mhi_ptr->dev); rmnet_mhi_ptr->tx_client_handle = NULL; rmnet_mhi_ptr->rx_client_handle = NULL; @@ -394,52 +408,19 @@ static int rmnet_mhi_disable_channels(struct rmnet_mhi_private *rmnet_mhi_ptr) static int rmnet_mhi_init_inbound(struct rmnet_mhi_private *rmnet_mhi_ptr) { - u32 i; int res; - struct mhi_skb_priv *rx_priv; - u32 cur_mru = rmnet_mhi_ptr->mru; - struct sk_buff *skb; - rmnet_log(MSG_INFO, "Entered\n"); + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n"); rmnet_mhi_ptr->tx_buffers_max = mhi_get_max_desc( rmnet_mhi_ptr->tx_client_handle); rmnet_mhi_ptr->rx_buffers_max = mhi_get_max_desc( rmnet_mhi_ptr->rx_client_handle); + atomic_set(&rmnet_mhi_ptr->rx_pool_len, 0); + res = rmnet_alloc_rx(rmnet_mhi_ptr, + rmnet_mhi_ptr->allocation_flags); - for (i = 0; i < rmnet_mhi_ptr->rx_buffers_max; i++) { - - skb = alloc_skb(cur_mru, rmnet_mhi_ptr->allocation_flags); - - if (!skb) { - rmnet_log(MSG_CRITICAL, - "SKB allocation failure during open"); - return -ENOMEM; - } - rx_priv = (struct mhi_skb_priv *)(skb->cb); - - skb_reserve(skb, MHI_RX_HEADROOM); - rx_priv->dma_size = cur_mru - MHI_RX_HEADROOM; - rx_priv->dma_addr = 0; - skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb); - } - - /* Submit the RX buffers */ - for (i = 0; i < rmnet_mhi_ptr->rx_buffers_max; i++) { - skb = skb_dequeue(&rmnet_mhi_ptr->rx_buffers); - rx_priv = (struct mhi_skb_priv *)(skb->cb); - res = mhi_queue_xfer(rmnet_mhi_ptr->rx_client_handle, - skb->data, - rx_priv->dma_size, - MHI_EOT); - if (0 != res) { - rmnet_log(MSG_CRITICAL, - "mhi_queue_xfer failed, error %d", res); - return -EIO; - } - skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb); - } - rmnet_log(MSG_INFO, "Exited\n"); - return 0; + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited with %d\n", res); + return res; } static void rmnet_mhi_tx_cb(struct mhi_result *result) @@ -451,9 +432,9 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result) rmnet_mhi_ptr = result->user_data; dev = rmnet_mhi_ptr->dev; - tx_interrupts_count[rmnet_mhi_ptr->dev_index]++; + rmnet_mhi_ptr->debug.tx_interrupts_count++; - rmnet_log(MSG_VERBOSE, "Entered\n"); + rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n"); if (!result->buf_addr || !result->bytes_xferd) return; /* Free the buffers which are TX'd up to the provided address */ @@ -461,7 +442,8 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result) struct sk_buff *skb = skb_dequeue(&(rmnet_mhi_ptr->tx_buffers)); if (!skb) { - rmnet_log(MSG_CRITICAL, + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, "NULL buffer returned, error"); break; } else { @@ -482,20 +464,21 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result) */ } } /* While TX queue is not empty */ - tx_cb_skb_free_burst_min[rmnet_mhi_ptr->dev_index] = - min(burst_counter, - tx_cb_skb_free_burst_min[rmnet_mhi_ptr->dev_index]); - tx_cb_skb_free_burst_max[rmnet_mhi_ptr->dev_index] = - max(burst_counter, - tx_cb_skb_free_burst_max[rmnet_mhi_ptr->dev_index]); + rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min = + min((u64)burst_counter, + rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min); + + rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max = + max((u64)burst_counter, + rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max); /* In case we couldn't write again, now we can! */ - read_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, flags); - rmnet_log(MSG_VERBOSE, "Waking up queue\n"); + spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, flags); + rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Waking up queue\n"); netif_wake_queue(dev); - read_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, flags); - rmnet_log(MSG_VERBOSE, "Exited\n"); + spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, flags); + rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n"); } static void rmnet_mhi_rx_cb(struct mhi_result *result) @@ -505,17 +488,18 @@ static void rmnet_mhi_rx_cb(struct mhi_result *result) rmnet_mhi_ptr = result->user_data; dev = rmnet_mhi_ptr->dev; - rmnet_log(MSG_VERBOSE, "Entered\n"); - rx_interrupts_count[rmnet_mhi_ptr->dev_index]++; + rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n"); + rmnet_mhi_ptr->debug.rx_interrupts_count++; if (napi_schedule_prep(&(rmnet_mhi_ptr->napi))) { mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle); atomic_inc(&rmnet_mhi_ptr->irq_masked_cntr); + mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false); __napi_schedule(&(rmnet_mhi_ptr->napi)); } else { - rx_interrupts_in_masked_irq[rmnet_mhi_ptr->dev_index]++; + rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++; } - rmnet_log(MSG_VERBOSE, "Exited\n"); + rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n"); } static int rmnet_mhi_open(struct net_device *dev) @@ -523,11 +507,19 @@ static int rmnet_mhi_open(struct net_device *dev) struct rmnet_mhi_private *rmnet_mhi_ptr = *(struct rmnet_mhi_private **)netdev_priv(dev); - rmnet_log(MSG_INFO, - "Opened net dev interface for MHI chans %d and %d\n", - rmnet_mhi_ptr->tx_channel, - rmnet_mhi_ptr->rx_channel); - netif_start_queue(dev); + rmnet_log(rmnet_mhi_ptr, + MSG_INFO, + "Opened net dev interface for MHI chans %d and %d\n", + rmnet_mhi_ptr->tx_channel, + rmnet_mhi_ptr->rx_channel); + + /* tx queue may not necessarily be stopped already + * so stop the queue if tx path is not enabled + */ + if (!rmnet_mhi_ptr->tx_client_handle) + netif_stop_queue(dev); + else + netif_start_queue(dev); /* Poll to check if any buffers are accumulated in the * transport buffers @@ -535,9 +527,10 @@ static int rmnet_mhi_open(struct net_device *dev) if (napi_schedule_prep(&(rmnet_mhi_ptr->napi))) { mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle); atomic_inc(&rmnet_mhi_ptr->irq_masked_cntr); + mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false); __napi_schedule(&(rmnet_mhi_ptr->napi)); } else { - rx_interrupts_in_masked_irq[rmnet_mhi_ptr->dev_index]++; + rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++; } return 0; @@ -575,14 +568,17 @@ static int rmnet_mhi_stop(struct net_device *dev) { struct rmnet_mhi_private *rmnet_mhi_ptr = *(struct rmnet_mhi_private **)netdev_priv(dev); + netif_stop_queue(dev); - rmnet_log(MSG_VERBOSE, "Entered\n"); + rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n"); if (atomic_read(&rmnet_mhi_ptr->irq_masked_cntr)) { mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle); atomic_dec(&rmnet_mhi_ptr->irq_masked_cntr); - rmnet_log(MSG_ERROR, "IRQ was masked, unmasking...\n"); + rmnet_log(rmnet_mhi_ptr, + MSG_ERROR, + "IRQ was masked, unmasking...\n"); } - rmnet_log(MSG_VERBOSE, "Exited\n"); + rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n"); return 0; } @@ -601,61 +597,53 @@ static int rmnet_mhi_xmit(struct sk_buff *skb, struct net_device *dev) *(struct rmnet_mhi_private **)netdev_priv(dev); int res = 0; unsigned long flags; - int retry = 0; struct mhi_skb_priv *tx_priv; - rmnet_log(MSG_VERBOSE, "Entered chan %d\n", rmnet_mhi_ptr->tx_channel); + rmnet_log(rmnet_mhi_ptr, + MSG_VERBOSE, + "Entered chan %d\n", + rmnet_mhi_ptr->tx_channel); tx_priv = (struct mhi_skb_priv *)(skb->cb); tx_priv->dma_size = skb->len; tx_priv->dma_addr = 0; - do { - retry = 0; - res = mhi_queue_xfer(rmnet_mhi_ptr->tx_client_handle, - skb->data, - skb->len, - MHI_EOT); - - if (-ENOSPC == res) { - write_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, - flags); - if (!mhi_get_free_desc( - rmnet_mhi_ptr->tx_client_handle)) { - /* Stop writing until we can write again */ - tx_ring_full_count[rmnet_mhi_ptr->dev_index]++; - netif_stop_queue(dev); - rmnet_log(MSG_VERBOSE, "Stopping Queue\n"); - write_unlock_irqrestore( - &rmnet_mhi_ptr->out_chan_full_lock, - flags); - goto rmnet_mhi_xmit_error_cleanup; - } else { - retry = 1; - } - write_unlock_irqrestore( - &rmnet_mhi_ptr->out_chan_full_lock, - flags); - } - } while (retry); - if (0 != res) { + if (mhi_get_free_desc(rmnet_mhi_ptr->tx_client_handle) <= 0) { + rmnet_log(rmnet_mhi_ptr, + MSG_VERBOSE, + "Stopping Queue\n"); + spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, + flags); + rmnet_mhi_ptr->debug.tx_ring_full_count++; + netif_stop_queue(dev); + spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, + flags); + return NETDEV_TX_BUSY; + } + res = mhi_queue_xfer(rmnet_mhi_ptr->tx_client_handle, + skb->data, + skb->len, + MHI_EOT); + + if (res != 0) { + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "Failed to queue with reason:%d\n", + res); + spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, + flags); netif_stop_queue(dev); - rmnet_log(MSG_CRITICAL, - "mhi_queue_xfer failed, error %d\n", res); - goto rmnet_mhi_xmit_error_cleanup; + spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, + flags); + return NETDEV_TX_BUSY; } skb_queue_tail(&(rmnet_mhi_ptr->tx_buffers), skb); - dev->trans_start = jiffies; + rmnet_mhi_ptr->debug.tx_queued_packets_count++; - tx_queued_packets_count[rmnet_mhi_ptr->dev_index]++; - rmnet_log(MSG_VERBOSE, "Exited\n"); - return 0; - -rmnet_mhi_xmit_error_cleanup: - rmnet_log(MSG_VERBOSE, "Ring full\n"); - return NETDEV_TX_BUSY; + rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n"); + return NETDEV_TX_OK; } static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr) @@ -670,24 +658,27 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr) sizeof(struct rmnet_ioctl_extended_s)); if (rc) { - rmnet_log(MSG_CRITICAL, - "copy_from_user failed ,error %d", rc); + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "copy_from_user failed ,error %d", + rc); return rc; } switch (ext_cmd.extended_ioctl) { case RMNET_IOCTL_SET_MRU: - if ((0 > ext_cmd.u.data) || (ext_cmd.u.data > MHI_MAX_MRU)) { - rmnet_log(MSG_CRITICAL, - "Can't set MRU, value %u is invalid\n", - ext_cmd.u.data); + 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); return -EINVAL; } - rmnet_log(MSG_INFO, - "MRU change request to 0x%x\n", - ext_cmd.u.data); - mru = ext_cmd.u.data; - rmnet_mhi_ptr->mru = mru; + rmnet_log(rmnet_mhi_ptr, + MSG_INFO, + "MRU change request to 0x%x\n", + ext_cmd.u.data); + rmnet_mhi_ptr->mru = ext_cmd.u.data; break; case RMNET_IOCTL_GET_EPID: ext_cmd.u.data = @@ -706,7 +697,8 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr) mhi_set_lpm(rmnet_mhi_ptr->tx_client_handle, ext_cmd.u.data); } else { - rmnet_log(MSG_ERROR, + rmnet_log(rmnet_mhi_ptr, + MSG_ERROR, "Cannot set LPM value, MHI is not up.\n"); return -ENODEV; } @@ -720,9 +712,10 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr) sizeof(struct rmnet_ioctl_extended_s)); if (rc) - rmnet_log(MSG_CRITICAL, - "copy_to_user failed, error %d\n", - rc); + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "copy_to_user failed, error %d\n", + rc); return rc; } @@ -801,50 +794,49 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) struct rmnet_mhi_private **rmnet_mhi_ctxt = NULL; int r = 0; - memset(tx_interrupts_count, 0, sizeof(tx_interrupts_count)); - memset(rx_interrupts_count, 0, sizeof(rx_interrupts_count)); - memset(rx_interrupts_in_masked_irq, 0, - sizeof(rx_interrupts_in_masked_irq)); - memset(rx_napi_skb_burst_min, 0, sizeof(rx_napi_skb_burst_min)); - memset(rx_napi_skb_burst_max, 0, sizeof(rx_napi_skb_burst_max)); - memset(tx_cb_skb_free_burst_min, 0, sizeof(tx_cb_skb_free_burst_min)); - memset(tx_cb_skb_free_burst_max, 0, sizeof(tx_cb_skb_free_burst_max)); - memset(tx_ring_full_count, 0, sizeof(tx_ring_full_count)); - memset(tx_queued_packets_count, 0, sizeof(tx_queued_packets_count)); - memset(rx_napi_budget_overflow, 0, sizeof(rx_napi_budget_overflow)); - - rmnet_log(MSG_INFO, "Entered.\n"); - - if (rmnet_mhi_ptr == NULL) { - rmnet_log(MSG_CRITICAL, "Bad input args.\n"); - return -EINVAL; - } - - rx_napi_skb_burst_min[rmnet_mhi_ptr->dev_index] = UINT_MAX; - tx_cb_skb_free_burst_min[rmnet_mhi_ptr->dev_index] = UINT_MAX; + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered.\n"); + + rmnet_mhi_ptr->debug.tx_interrupts_count = 0; + rmnet_mhi_ptr->debug.rx_interrupts_count = 0; + rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq = 0; + rmnet_mhi_ptr->debug.rx_napi_skb_burst_min = 0; + rmnet_mhi_ptr->debug.rx_napi_skb_burst_max = 0; + rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min = 0; + rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max = 0; + rmnet_mhi_ptr->debug.tx_ring_full_count = 0; + rmnet_mhi_ptr->debug.tx_queued_packets_count = 0; + rmnet_mhi_ptr->debug.rx_napi_budget_overflow = 0; + rmnet_mhi_ptr->debug.rx_napi_skb_burst_min = UINT_MAX; + rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min = UINT_MAX; skb_queue_head_init(&(rmnet_mhi_ptr->tx_buffers)); skb_queue_head_init(&(rmnet_mhi_ptr->rx_buffers)); if (rmnet_mhi_ptr->tx_client_handle != NULL) { - rmnet_log(MSG_INFO, - "Opening TX channel\n"); + rmnet_log(rmnet_mhi_ptr, + MSG_INFO, + "Opening TX channel\n"); r = mhi_open_channel(rmnet_mhi_ptr->tx_client_handle); if (r != 0) { - rmnet_log(MSG_CRITICAL, - "Failed to start TX chan ret %d\n", r); + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "Failed to start TX chan ret %d\n", + r); goto mhi_tx_chan_start_fail; } else { rmnet_mhi_ptr->tx_enabled = 1; } } if (rmnet_mhi_ptr->rx_client_handle != NULL) { - rmnet_log(MSG_INFO, - "Opening RX channel\n"); + rmnet_log(rmnet_mhi_ptr, + MSG_INFO, + "Opening RX channel\n"); r = mhi_open_channel(rmnet_mhi_ptr->rx_client_handle); if (r != 0) { - rmnet_log(MSG_CRITICAL, - "Failed to start RX chan ret %d\n", r); + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "Failed to start RX chan ret %d\n", + r); goto mhi_rx_chan_start_fail; } else { rmnet_mhi_ptr->rx_enabled = 1; @@ -855,11 +847,13 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) RMNET_MHI_DEV_NAME, NET_NAME_PREDICTABLE, rmnet_mhi_setup); if (!rmnet_mhi_ptr->dev) { - rmnet_log(MSG_CRITICAL, "Network device allocation failed\n"); + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "Network device allocation failed\n"); ret = -ENOMEM; goto net_dev_alloc_fail; } - + SET_NETDEV_DEV(rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->pdev->dev); rmnet_mhi_ctxt = netdev_priv(rmnet_mhi_ptr->dev); *rmnet_mhi_ctxt = rmnet_mhi_ptr; @@ -872,8 +866,10 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) r = rmnet_mhi_init_inbound(rmnet_mhi_ptr); if (r) { - rmnet_log(MSG_CRITICAL, - "Failed to init inbound ret %d\n", r); + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "Failed to init inbound ret %d\n", + r); } netif_napi_add(rmnet_mhi_ptr->dev, &(rmnet_mhi_ptr->napi), @@ -882,13 +878,14 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) rmnet_mhi_ptr->mhi_enabled = 1; ret = register_netdev(rmnet_mhi_ptr->dev); if (ret) { - rmnet_log(MSG_CRITICAL, + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, "Network device registration failed\n"); goto net_dev_reg_fail; } napi_enable(&(rmnet_mhi_ptr->napi)); - rmnet_log(MSG_INFO, "Exited.\n"); + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited.\n"); return 0; @@ -901,7 +898,7 @@ net_dev_alloc_fail: mhi_rx_chan_start_fail: mhi_close_channel(rmnet_mhi_ptr->tx_client_handle); mhi_tx_chan_start_fail: - rmnet_log(MSG_INFO, "Exited ret %d.\n", ret); + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited ret %d.\n", ret); return ret; } @@ -911,52 +908,60 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info) struct mhi_result *result; int r = 0; - if (NULL != cb_info && NULL != cb_info->result) { - result = cb_info->result; - rmnet_mhi_ptr = result->user_data; - } else { - rmnet_log(MSG_CRITICAL, - "Invalid data in MHI callback, quitting\n"); + if (!cb_info || !cb_info->result) { + pr_err("%s: Invalid data in MHI callback\n", __func__); return; } + result = cb_info->result; + rmnet_mhi_ptr = result->user_data; + switch (cb_info->cb_reason) { case MHI_CB_MHI_DISABLED: - rmnet_log(MSG_CRITICAL, - "Got MHI_DISABLED notification. Stopping stack\n"); + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "Got MHI_DISABLED notification. Stopping stack\n"); if (rmnet_mhi_ptr->mhi_enabled) { rmnet_mhi_ptr->mhi_enabled = 0; /* Ensure MHI is disabled before other mem ops */ wmb(); while (atomic_read(&rmnet_mhi_ptr->pending_data)) { - rmnet_log(MSG_CRITICAL, - "Waiting for channels to stop.\n"); + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "Waiting for channels to stop.\n"); msleep(25); } rmnet_mhi_disable(rmnet_mhi_ptr); } break; case MHI_CB_MHI_ENABLED: - rmnet_log(MSG_CRITICAL, - "Got MHI_ENABLED notification. Starting stack\n"); - if (IS_INBOUND(cb_info->chan)) + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "Got MHI_ENABLED notification. Starting stack\n"); + if (cb_info->chan == rmnet_mhi_ptr->rx_channel) rmnet_mhi_ptr->rx_enabled = 1; else rmnet_mhi_ptr->tx_enabled = 1; - if (rmnet_mhi_ptr->tx_enabled && - rmnet_mhi_ptr->rx_enabled) { - rmnet_log(MSG_INFO, - "Both RX/TX are enabled, enabling iface.\n"); + if ((rmnet_mhi_ptr->tx_enabled && rmnet_mhi_ptr->rx_enabled) || + (rmnet_mhi_ptr->tx_enabled && + !rmnet_mhi_ptr->rx_client_handle) || + (rmnet_mhi_ptr->rx_enabled && + !rmnet_mhi_ptr->tx_client_handle)) { + rmnet_log(rmnet_mhi_ptr, + MSG_INFO, + "enabling iface.\n"); r = rmnet_mhi_enable_iface(rmnet_mhi_ptr); if (r) - rmnet_log(MSG_CRITICAL, - "Failed to enable iface for chan %d\n", - cb_info->chan); + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "Failed to enable iface for chan %d\n", + cb_info->chan); else - rmnet_log(MSG_INFO, - "Enabled iface for chan %d\n", - cb_info->chan); + rmnet_log(rmnet_mhi_ptr, + MSG_INFO, + "Enabled iface for chan %d\n", + cb_info->chan); } break; case MHI_CB_XFER: @@ -964,7 +969,7 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info) /* Flush pending data is set before any other mem operations */ wmb(); if (rmnet_mhi_ptr->mhi_enabled) { - if (IS_INBOUND(cb_info->chan)) + if (cb_info->chan == rmnet_mhi_ptr->rx_channel) rmnet_mhi_rx_cb(cb_info->result); else rmnet_mhi_tx_cb(cb_info->result); @@ -978,64 +983,261 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info) static struct mhi_client_info_t rmnet_mhi_info = {rmnet_mhi_cb}; -static int __init rmnet_mhi_init(void) +#ifdef CONFIG_DEBUG_FS +struct dentry *dentry; + +static void rmnet_mhi_create_debugfs(struct rmnet_mhi_private *rmnet_mhi_ptr) { + char node_name[15]; int i; - int res = 0; - struct rmnet_mhi_private *rmnet_mhi_ptr = 0; - rmnet_ipc_log = ipc_log_context_create(RMNET_IPC_LOG_PAGES, - "mhi_rmnet", 0); - - for (i = 0; i < MHI_RMNET_DEVICE_COUNT; i++) { - rmnet_mhi_ptr = &rmnet_mhi_ctxt_list[i]; - - rmnet_mhi_ptr->tx_channel = MHI_CLIENT_IP_HW_0_OUT + - (enum MHI_CLIENT_CHANNEL)(i * 2); - rmnet_mhi_ptr->rx_channel = MHI_CLIENT_IP_HW_0_IN + - (enum MHI_CLIENT_CHANNEL)((i * 2)); - - rmnet_mhi_ptr->tx_client_handle = 0; - rmnet_mhi_ptr->rx_client_handle = 0; - rwlock_init(&rmnet_mhi_ptr->out_chan_full_lock); - - rmnet_mhi_ptr->mru = MHI_DEFAULT_MRU; - rmnet_mhi_ptr->dev_index = i; - - res = mhi_register_channel( - &(rmnet_mhi_ptr->tx_client_handle), - rmnet_mhi_ptr->tx_channel, 0, - &rmnet_mhi_info, rmnet_mhi_ptr); - - if (0 != res) { - rmnet_mhi_ptr->tx_client_handle = 0; - rmnet_log(MSG_CRITICAL, - "mhi_register_channel failed chan %d ret %d\n", - rmnet_mhi_ptr->tx_channel, res); + const umode_t mode = (S_IRUSR | S_IWUSR); + struct dentry *file; + + const struct { + char *name; + u64 *ptr; + } debugfs_table[] = { + { + "tx_interrupts_count", + &rmnet_mhi_ptr->debug.tx_interrupts_count + }, + { + "rx_interrupts_count", + &rmnet_mhi_ptr->debug.rx_interrupts_count + }, + { + "tx_ring_full_count", + &rmnet_mhi_ptr->debug.tx_ring_full_count + }, + { + "tx_queued_packets_count", + &rmnet_mhi_ptr->debug.tx_queued_packets_count + }, + { + "rx_interrupts_in_masked_irq", + &rmnet_mhi_ptr-> + debug.rx_interrupts_in_masked_irq + }, + { + "rx_napi_skb_burst_min", + &rmnet_mhi_ptr->debug.rx_napi_skb_burst_min + }, + { + "rx_napi_skb_burst_max", + &rmnet_mhi_ptr->debug.rx_napi_skb_burst_max + }, + { + "tx_cb_skb_free_burst_min", + &rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min + }, + { + "tx_cb_skb_free_burst_max", + &rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max + }, + { + "rx_napi_budget_overflow", + &rmnet_mhi_ptr->debug.rx_napi_budget_overflow + }, + { + "rx_fragmentation", + &rmnet_mhi_ptr->debug.rx_fragmentation + }, + { + NULL, NULL + }, + }; + + snprintf(node_name, sizeof(node_name), "%s%d", + RMNET_MHI_DRIVER_NAME, rmnet_mhi_ptr->pdev->id); + + if (IS_ERR_OR_NULL(dentry)) + return; + + rmnet_mhi_ptr->dentry = debugfs_create_dir(node_name, dentry); + if (IS_ERR_OR_NULL(rmnet_mhi_ptr->dentry)) + return; + + file = debugfs_create_u32("msg_lvl", + mode, + rmnet_mhi_ptr->dentry, + (u32 *)&rmnet_mhi_ptr->debug.rmnet_msg_lvl); + if (IS_ERR_OR_NULL(file)) + return; + + file = debugfs_create_u32("ipc_log_lvl", + mode, + rmnet_mhi_ptr->dentry, + (u32 *)&rmnet_mhi_ptr-> + debug.rmnet_ipc_log_lvl); + if (IS_ERR_OR_NULL(file)) + return; + + file = debugfs_create_u32("mru", + mode, + rmnet_mhi_ptr->dentry, + &rmnet_mhi_ptr->mru); + if (IS_ERR_OR_NULL(file)) + return; + + /* Add debug stats table */ + for (i = 0; debugfs_table[i].name; i++) { + file = debugfs_create_u64(debugfs_table[i].name, + mode, + rmnet_mhi_ptr->dentry, + debugfs_table[i].ptr); + if (IS_ERR_OR_NULL(file)) + return; + } +} + +static void rmnet_mhi_create_debugfs_dir(void) +{ + dentry = debugfs_create_dir(RMNET_MHI_DRIVER_NAME, 0); +} +#else +static void rmnet_mhi_create_debugfs(struct rmnet_mhi_private *rmnet_mhi_ptr) +{ +} + +static void rmnet_mhi_create_debugfs_dir(void) +{ +} +#endif + +static int rmnet_mhi_probe(struct platform_device *pdev) +{ + int rc; + u32 channel; + struct rmnet_mhi_private *rmnet_mhi_ptr; + char node_name[15]; + + if (unlikely(!pdev->dev.of_node)) + return -ENODEV; + + pdev->id = of_alias_get_id(pdev->dev.of_node, "mhi_rmnet"); + if (unlikely(pdev->id < 0)) + return -ENODEV; + + rmnet_mhi_ptr = kzalloc(sizeof(*rmnet_mhi_ptr), GFP_KERNEL); + if (unlikely(!rmnet_mhi_ptr)) + return -ENOMEM; + rmnet_mhi_ptr->pdev = pdev; + spin_lock_init(&rmnet_mhi_ptr->out_chan_full_lock); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,mhi-mru", + &rmnet_mhi_ptr->mru); + if (unlikely(rc)) { + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "failed to get valid mru\n"); + goto probe_fail; + } + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,mhi-tx-channel", + &channel); + if (rc == 0) { + rmnet_mhi_ptr->tx_channel = channel; + rc = mhi_register_channel(&rmnet_mhi_ptr->tx_client_handle, + rmnet_mhi_ptr->tx_channel, + 0, + &rmnet_mhi_info, + rmnet_mhi_ptr); + if (unlikely(rc)) { + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "mhi_register_channel failed chan %d ret %d\n", + rmnet_mhi_ptr->tx_channel, + rc); + goto probe_fail; } - res = mhi_register_channel( - &(rmnet_mhi_ptr->rx_client_handle), - rmnet_mhi_ptr->rx_channel, 0, - &rmnet_mhi_info, rmnet_mhi_ptr); - - if (0 != res) { - rmnet_mhi_ptr->rx_client_handle = 0; - rmnet_log(MSG_CRITICAL, - "mhi_register_channel failed chan %d, ret %d\n", - rmnet_mhi_ptr->rx_channel, res); + } + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,mhi-rx-channel", + &channel); + if (rc == 0) { + rmnet_mhi_ptr->rx_channel = channel; + rc = mhi_register_channel(&rmnet_mhi_ptr->rx_client_handle, + rmnet_mhi_ptr->rx_channel, + 0, + &rmnet_mhi_info, + rmnet_mhi_ptr); + if (unlikely(rc)) { + rmnet_log(rmnet_mhi_ptr, + MSG_CRITICAL, + "mhi_register_channel failed chan %d ret %d\n", + rmnet_mhi_ptr->rx_channel, + rc); + goto probe_fail; } + + 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) { + 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); + rmnet_mhi_ptr->rmnet_ipc_log = + ipc_log_context_create(RMNET_IPC_LOG_PAGES, + node_name, 0); + rmnet_mhi_ptr->debug.rmnet_msg_lvl = MSG_CRITICAL; + +#ifdef CONFIG_MSM_MHI_DEBUG + rmnet_mhi_ptr->debug.rmnet_ipc_log_lvl = MSG_VERBOSE; +#else + rmnet_mhi_ptr->debug.rmnet_ipc_log_lvl = MSG_ERROR; +#endif + + rmnet_mhi_create_debugfs(rmnet_mhi_ptr); + list_add_tail(&rmnet_mhi_ptr->node, &rmnet_mhi_ctxt_list); return 0; + +probe_fail: + kfree(rmnet_mhi_ptr); + return rc; +} + +static const struct of_device_id msm_mhi_match_table[] = { + {.compatible = "qcom,mhi-rmnet"}, + {}, +}; + +static struct platform_driver rmnet_mhi_driver = { + .probe = rmnet_mhi_probe, + .driver = { + .name = RMNET_MHI_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = msm_mhi_match_table, + }, +}; + +static int __init rmnet_mhi_init(void) +{ + rmnet_mhi_create_debugfs_dir(); + + return platform_driver_register(&rmnet_mhi_driver); } static void __exit rmnet_mhi_exit(void) { struct rmnet_mhi_private *rmnet_mhi_ptr = 0; - int index = 0; - for (index = 0; index < MHI_RMNET_DEVICE_COUNT; index++) { - rmnet_mhi_ptr = &rmnet_mhi_ctxt_list[index]; - mhi_deregister_channel(rmnet_mhi_ptr->tx_client_handle); - mhi_deregister_channel(rmnet_mhi_ptr->rx_client_handle); + list_for_each_entry(rmnet_mhi_ptr, &rmnet_mhi_ctxt_list, node) { + if (rmnet_mhi_ptr->tx_client_handle) + mhi_deregister_channel(rmnet_mhi_ptr->tx_client_handle); + if (rmnet_mhi_ptr->rx_client_handle) + mhi_deregister_channel(rmnet_mhi_ptr->rx_client_handle); } } diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 36fc9427418f..480f3dae0780 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -832,7 +832,7 @@ static struct sh_eth_cpu_data r7s72100_data = { .ecsr_value = ECSR_ICD, .ecsipr_value = ECSIPR_ICDIP, - .eesipr_value = 0xff7f009f, + .eesipr_value = 0xe77f009f, .tx_check = EESR_TC1 | EESR_FTC, .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT | diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 0e2fc1a844ab..23a038810083 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -540,7 +540,7 @@ static inline void smc_rcv(struct net_device *dev) #define smc_special_lock(lock, flags) spin_lock_irqsave(lock, flags) #define smc_special_unlock(lock, flags) spin_unlock_irqrestore(lock, flags) #else -#define smc_special_trylock(lock, flags) (flags == flags) +#define smc_special_trylock(lock, flags) ((void)flags, true) #define smc_special_lock(lock, flags) do { flags = 0; } while (0) #define smc_special_unlock(lock, flags) do { flags = 0; } while (0) #endif @@ -2269,6 +2269,13 @@ static int smc_drv_probe(struct platform_device *pdev) if (pd) { memcpy(&lp->cfg, pd, sizeof(lp->cfg)); lp->io_shift = SMC91X_IO_SHIFT(lp->cfg.flags); + + if (!SMC_8BIT(lp) && !SMC_16BIT(lp)) { + dev_err(&pdev->dev, + "at least one of 8-bit or 16-bit access support is required.\n"); + ret = -ENXIO; + goto out_free_netdev; + } } #if IS_BUILTIN(CONFIG_OF) diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h index a3c129e1e40a..29df0465daf4 100644 --- a/drivers/net/ethernet/smsc/smc91x.h +++ b/drivers/net/ethernet/smsc/smc91x.h @@ -37,6 +37,27 @@ #include <linux/smc91x.h> /* + * Any 16-bit access is performed with two 8-bit accesses if the hardware + * can't do it directly. Most registers are 16-bit so those are mandatory. + */ +#define SMC_outw_b(x, a, r) \ + do { \ + unsigned int __val16 = (x); \ + unsigned int __reg = (r); \ + SMC_outb(__val16, a, __reg); \ + SMC_outb(__val16 >> 8, a, __reg + (1 << SMC_IO_SHIFT)); \ + } while (0) + +#define SMC_inw_b(a, r) \ + ({ \ + unsigned int __val16; \ + unsigned int __reg = r; \ + __val16 = SMC_inb(a, __reg); \ + __val16 |= SMC_inb(a, __reg + (1 << SMC_IO_SHIFT)) << 8; \ + __val16; \ + }) + +/* * Define your architecture specific bus configuration parameters here. */ @@ -55,10 +76,30 @@ #define SMC_IO_SHIFT (lp->io_shift) #define SMC_inb(a, r) readb((a) + (r)) -#define SMC_inw(a, r) readw((a) + (r)) +#define SMC_inw(a, r) \ + ({ \ + unsigned int __smc_r = r; \ + SMC_16BIT(lp) ? readw((a) + __smc_r) : \ + SMC_8BIT(lp) ? SMC_inw_b(a, __smc_r) : \ + ({ BUG(); 0; }); \ + }) + #define SMC_inl(a, r) readl((a) + (r)) #define SMC_outb(v, a, r) writeb(v, (a) + (r)) +#define SMC_outw(v, a, r) \ + do { \ + unsigned int __v = v, __smc_r = r; \ + if (SMC_16BIT(lp)) \ + __SMC_outw(__v, a, __smc_r); \ + else if (SMC_8BIT(lp)) \ + SMC_outw_b(__v, a, __smc_r); \ + else \ + BUG(); \ + } while (0) + #define SMC_outl(v, a, r) writel(v, (a) + (r)) +#define SMC_insb(a, r, p, l) readsb((a) + (r), p, l) +#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, l) #define SMC_insw(a, r, p, l) readsw((a) + (r), p, l) #define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) #define SMC_insl(a, r, p, l) readsl((a) + (r), p, l) @@ -66,7 +107,7 @@ #define SMC_IRQ_FLAGS (-1) /* from resource */ /* We actually can't write halfwords properly if not word aligned */ -static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg) +static inline void __SMC_outw(u16 val, void __iomem *ioaddr, int reg) { if ((machine_is_mainstone() || machine_is_stargate2() || machine_is_pxa_idp()) && reg & 2) { @@ -405,24 +446,8 @@ smc_pxa_dma_insw(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma, #if ! SMC_CAN_USE_16BIT -/* - * Any 16-bit access is performed with two 8-bit accesses if the hardware - * can't do it directly. Most registers are 16-bit so those are mandatory. - */ -#define SMC_outw(x, ioaddr, reg) \ - do { \ - unsigned int __val16 = (x); \ - SMC_outb( __val16, ioaddr, reg ); \ - SMC_outb( __val16 >> 8, ioaddr, reg + (1 << SMC_IO_SHIFT));\ - } while (0) -#define SMC_inw(ioaddr, reg) \ - ({ \ - unsigned int __val16; \ - __val16 = SMC_inb( ioaddr, reg ); \ - __val16 |= SMC_inb( ioaddr, reg + (1 << SMC_IO_SHIFT)) << 8; \ - __val16; \ - }) - +#define SMC_outw(x, ioaddr, reg) SMC_outw_b(x, ioaddr, reg) +#define SMC_inw(ioaddr, reg) SMC_inw_b(ioaddr, reg) #define SMC_insw(a, r, p, l) BUG() #define SMC_outsw(a, r, p, l) BUG() diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 69e31e2a68fc..f0961cbaf87e 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -440,7 +440,7 @@ static struct sk_buff **geneve_gro_receive(struct sk_buff **head, skb_gro_pull(skb, gh_len); skb_gro_postpull_rcsum(skb, gh, gh_len); - pp = ptype->callbacks.gro_receive(head, skb); + pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb); out_unlock: rcu_read_unlock(); @@ -815,7 +815,6 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, struct geneve_dev *geneve = netdev_priv(dev); struct geneve_sock *gs4 = geneve->sock4; struct rtable *rt = NULL; - const struct iphdr *iip; /* interior IP header */ int err = -EINVAL; struct flowi4 fl4; __u8 tos, ttl; @@ -842,8 +841,6 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); skb_reset_mac_header(skb); - iip = ip_hdr(skb); - if (info) { const struct ip_tunnel_key *key = &info->key; u8 *opts = NULL; @@ -859,7 +856,7 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, if (unlikely(err)) goto err; - tos = ip_tunnel_ecn_encap(key->tos, iip, skb); + tos = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb); ttl = key->ttl; df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; } else { @@ -869,7 +866,7 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, if (unlikely(err)) goto err; - tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, iip, skb); + tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, ip_hdr(skb), skb); ttl = geneve->ttl; if (!ttl && IN_MULTICAST(ntohl(fl4.daddr))) ttl = 1; @@ -903,7 +900,6 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, struct geneve_dev *geneve = netdev_priv(dev); struct geneve_sock *gs6 = geneve->sock6; struct dst_entry *dst = NULL; - const struct iphdr *iip; /* interior IP header */ int err = -EINVAL; struct flowi6 fl6; __u8 prio, ttl; @@ -927,8 +923,6 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); skb_reset_mac_header(skb); - iip = ip_hdr(skb); - if (info) { const struct ip_tunnel_key *key = &info->key; u8 *opts = NULL; @@ -945,7 +939,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, if (unlikely(err)) goto err; - prio = ip_tunnel_ecn_encap(key->tos, iip, skb); + prio = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb); ttl = key->ttl; } else { udp_csum = false; @@ -954,7 +948,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, if (unlikely(err)) goto err; - prio = ip_tunnel_ecn_encap(fl6.flowi6_tos, iip, skb); + prio = ip_tunnel_ecn_encap(fl6.flowi6_tos, ip_hdr(skb), skb); ttl = geneve->ttl; if (!ttl && ipv6_addr_is_multicast(&fl6.daddr)) ttl = 1; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 47cd306dbb3c..bba0ca786aaa 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -640,8 +640,10 @@ phy_err: int phy_start_interrupts(struct phy_device *phydev) { atomic_set(&phydev->irq_disable, 0); - if (request_irq(phydev->irq, phy_interrupt, 0, "phy_interrupt", - phydev) < 0) { + if (request_irq(phydev->irq, phy_interrupt, + IRQF_SHARED, + "phy_interrupt", + phydev) < 0) { pr_warn("%s: Can't get IRQ %d (PHY)\n", phydev->bus->name, phydev->irq); phydev->irq = PHY_POLL; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 935e0b45e151..dd7b7d64c90a 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -862,10 +862,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) goto drop; - if (skb->sk && sk_fullsock(skb->sk)) { - sock_tx_timestamp(skb->sk, &skb_shinfo(skb)->tx_flags); - sw_tx_timestamp(skb); - } + skb_tx_timestamp(skb); /* Orphan the skb - required as we might hang on to it * for indefinite time. diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index f94ab786088f..0e2a19e58923 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1465,6 +1465,11 @@ static void virtnet_free_queues(struct virtnet_info *vi) netif_napi_del(&vi->rq[i].napi); } + /* We called napi_hash_del() before netif_napi_del(), + * we need to respect an RCU grace period before freeing vi->rq + */ + synchronize_net(); + kfree(vi->rq); kfree(vi->sq); } diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 003780901628..6fa8e165878e 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -593,7 +593,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, } } - pp = eth_gro_receive(head, skb); + pp = call_gro_receive(eth_gro_receive, head, skb); out: skb_gro_remcsum_cleanup(skb, &grc); diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index f1ead7c28823..b8a3a1ecabaa 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -21,7 +21,7 @@ /* * Support for Copy Engine hardware, which is mainly used for - * communication between Host and Target over a PCIe interconnect. + * communication between Host and Target over a PCIe/SNOC/AHB interconnect. */ /* @@ -32,7 +32,7 @@ * Each ring consists of a number of descriptors which specify * an address, length, and meta-data. * - * Typically, one side of the PCIe interconnect (Host or Target) + * Typically, one side of the PCIe/AHB/SNOC interconnect (Host or Target) * controls one ring and the other side controls the other ring. * The source side chooses when to initiate a transfer and it * chooses what to send (buffer address, length). The destination @@ -62,215 +62,294 @@ static inline void ath10k_ce_dest_ring_write_index_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - ar->bus_write32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS, n); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + ar_opaque->bus_ops->write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->dst_wr_index_addr, n); } static inline u32 ath10k_ce_dest_ring_write_index_get(struct ath10k *ar, u32 ce_ctrl_addr) { - return ar->bus_read32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + return ar_opaque->bus_ops->read32(ar, + ce_ctrl_addr + ar->hw_ce_regs->dst_wr_index_addr); } static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - ar->bus_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + ar_opaque->bus_ops->write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->sr_wr_index_addr, n); } static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar, u32 ce_ctrl_addr) { - return ar->bus_read32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + return ar_opaque->bus_ops->read32(ar, + ce_ctrl_addr + ar->hw_ce_regs->sr_wr_index_addr); } static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar, u32 ce_ctrl_addr) { - return ar->bus_read32(ar, ce_ctrl_addr + CURRENT_SRRI_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + return ar_opaque->bus_ops->read32(ar, + ce_ctrl_addr + ar->hw_ce_regs->current_srri_addr); } static inline void ath10k_ce_shadow_src_ring_write_index_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - ar->bus_write32(ar, shadow_sr_wr_ind_addr(ar, ce_ctrl_addr), n); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + ar_opaque->bus_ops->write32(ar, shadow_sr_wr_ind_addr(ar, + ce_ctrl_addr), n); } static inline void ath10k_ce_shadow_dest_ring_write_index_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - ar->bus_write32(ar, shadow_dst_wr_ind_addr(ar, ce_ctrl_addr), n); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + ar_opaque->bus_ops->write32(ar, shadow_dst_wr_ind_addr(ar, + ce_ctrl_addr), + n); } static inline void ath10k_ce_src_ring_base_addr_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int addr) { - ar->bus_write32(ar, ce_ctrl_addr + SR_BA_ADDRESS, addr); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + ar_opaque->bus_ops->write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->sr_base_addr, addr); } static inline void ath10k_ce_src_ring_size_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - ar->bus_write32(ar, ce_ctrl_addr + SR_SIZE_ADDRESS, n); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + ar_opaque->bus_ops->write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->sr_size_addr, n); } static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - u32 ctrl1_addr = ar->bus_read32((ar), - (ce_ctrl_addr) + CE_CTRL1_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; - ar->bus_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS, - (ctrl1_addr & ~CE_CTRL1_DMAX_LENGTH_MASK) | - CE_CTRL1_DMAX_LENGTH_SET(n)); + u32 ctrl1_addr = ar_opaque->bus_ops->read32((ar), + (ce_ctrl_addr) + ctrl_regs->addr); + + ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + ctrl_regs->addr, + (ctrl1_addr & ~(ctrl_regs->dmax->mask)) | + ctrl_regs->dmax->set(n, ctrl_regs->dmax)); } static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - u32 ctrl1_addr = ar->bus_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; + + u32 ctrl1_addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + + ctrl_regs->addr); - ar->bus_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS, - (ctrl1_addr & ~CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) | - CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(n)); + ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + ctrl_regs->addr, + (ctrl1_addr & ~(ctrl_regs->src_ring->mask)) | + ctrl_regs->src_ring->set(n, ctrl_regs->src_ring)); } static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - u32 ctrl1_addr = ar->bus_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; - ar->bus_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS, - (ctrl1_addr & ~CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) | - CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(n)); + u32 ctrl1_addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + + ctrl_regs->addr); + + ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + ctrl_regs->addr, + (ctrl1_addr & ~(ctrl_regs->dst_ring->mask)) | + ctrl_regs->dst_ring->set(n, ctrl_regs->dst_ring)); } static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar, u32 ce_ctrl_addr) { - return ar->bus_read32(ar, ce_ctrl_addr + CURRENT_DRRI_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + return ar_opaque->bus_ops->read32(ar, + ce_ctrl_addr + ar->hw_ce_regs->current_drri_addr); } static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar, u32 ce_ctrl_addr, u32 addr) { - ar->bus_write32(ar, ce_ctrl_addr + DR_BA_ADDRESS, addr); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + ar_opaque->bus_ops->write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->dr_base_addr, addr); } static inline void ath10k_ce_dest_ring_size_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - ar->bus_write32(ar, ce_ctrl_addr + DR_SIZE_ADDRESS, n); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + ar_opaque->bus_ops->write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->dr_size_addr, n); } static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - u32 addr = ar->bus_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr; + u32 addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + srcr_wm->addr); - ar->bus_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS, - (addr & ~SRC_WATERMARK_HIGH_MASK) | - SRC_WATERMARK_HIGH_SET(n)); + ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + srcr_wm->addr, + (addr & ~(srcr_wm->wm_high->mask)) | + (srcr_wm->wm_high->set(n, srcr_wm->wm_high))); } static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - u32 addr = ar->bus_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr; + u32 addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + srcr_wm->addr); - ar->bus_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS, - (addr & ~SRC_WATERMARK_LOW_MASK) | - SRC_WATERMARK_LOW_SET(n)); + ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + srcr_wm->addr, + (addr & ~(srcr_wm->wm_low->mask)) | + (srcr_wm->wm_low->set(n, srcr_wm->wm_low))); } static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - u32 addr = ar->bus_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr; + u32 addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + dstr_wm->addr); - ar->bus_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS, - (addr & ~DST_WATERMARK_HIGH_MASK) | - DST_WATERMARK_HIGH_SET(n)); + ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + dstr_wm->addr, + (addr & ~(dstr_wm->wm_high->mask)) | + (dstr_wm->wm_high->set(n, dstr_wm->wm_high))); } static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - u32 addr = ar->bus_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr; + u32 addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + dstr_wm->addr); - ar->bus_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS, - (addr & ~DST_WATERMARK_LOW_MASK) | - DST_WATERMARK_LOW_SET(n)); + ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + dstr_wm->addr, + (addr & ~(dstr_wm->wm_low->mask)) | + (dstr_wm->wm_low->set(n, dstr_wm->wm_low))); } static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar, u32 ce_ctrl_addr) { - u32 host_ie_addr = ar->bus_read32(ar, - ce_ctrl_addr + HOST_IE_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie; - ar->bus_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS, - host_ie_addr | HOST_IE_COPY_COMPLETE_MASK); + u32 host_ie_addr = ar_opaque->bus_ops->read32(ar, + ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr); + + ar_opaque->bus_ops->write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr, + host_ie_addr | host_ie->copy_complete->mask); } static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { - u32 host_ie_addr = ar->bus_read32(ar, - ce_ctrl_addr + HOST_IE_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie; + + u32 host_ie_addr = ar_opaque->bus_ops->read32(ar, + ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr); - ar->bus_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS, - host_ie_addr & ~HOST_IE_COPY_COMPLETE_MASK); + ar_opaque->bus_ops->write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr, + host_ie_addr & ~(host_ie->copy_complete->mask)); } static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { - u32 host_ie_addr = ar->bus_read32(ar, - ce_ctrl_addr + HOST_IE_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; - ar->bus_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS, - host_ie_addr & ~CE_WATERMARK_MASK); + u32 host_ie_addr = ar_opaque->bus_ops->read32(ar, + ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr); + + ar_opaque->bus_ops->write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr, + host_ie_addr & ~(wm_regs->wm_mask)); } static inline void ath10k_ce_error_intr_enable(struct ath10k *ar, u32 ce_ctrl_addr) { - u32 misc_ie_addr = ar->bus_read32(ar, - ce_ctrl_addr + MISC_IE_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs; + + u32 misc_ie_addr = ar_opaque->bus_ops->read32(ar, + ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr); - ar->bus_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS, - misc_ie_addr | CE_ERROR_MASK); + ar_opaque->bus_ops->write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr, + misc_ie_addr | misc_regs->err_mask); } static inline void ath10k_ce_error_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { - u32 misc_ie_addr = ar->bus_read32(ar, - ce_ctrl_addr + MISC_IE_ADDRESS); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs; + + u32 misc_ie_addr = ar_opaque->bus_ops->read32(ar, + ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr); - ar->bus_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS, - misc_ie_addr & ~CE_ERROR_MASK); + ar_opaque->bus_ops->write32(ar, + ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr, + misc_ie_addr & ~(misc_regs->err_mask)); } static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int mask) { - ar->bus_write32(ar, ce_ctrl_addr + HOST_IS_ADDRESS, mask); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; + + ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + wm_regs->addr, mask); } u32 shadow_sr_wr_ind_addr(struct ath10k *ar, u32 ctrl_addr) @@ -339,6 +418,21 @@ u32 shadow_dst_wr_ind_addr(struct ath10k *ar, u32 ctrl_addr) return addr; } +static inline void ath10k_ce_snoc_addr_config(struct ce_desc *sdesc, + dma_addr_t buffer, + unsigned int flags) +{ + __le32 *addr = (__le32 *)&sdesc->addr; + + flags |= upper_32_bits(buffer) & CE_DESC_FLAGS_GET_MASK; + addr[0] = __cpu_to_le32(buffer); + addr[1] = flags; + if (flags & CE_SEND_FLAG_GATHER) + addr[1] |= CE_WCN3990_DESC_FLAGS_GATHER; + else + addr[1] &= ~CE_WCN3990_DESC_FLAGS_GATHER; +} + /* * Guts of ath10k_ce_send, used by both ath10k_ce_send and * ath10k_ce_sendlist_send. @@ -382,17 +476,10 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, if (flags & CE_SEND_FLAG_BYTE_SWAP) desc_flags |= CE_DESC_FLAGS_BYTE_SWAP; - if (QCA_REV_WCN3990(ar)) { - flags |= upper_32_bits(buffer) & CE_DESC_FLAGS_GET_MASK; - sdesc.addr_lo = __cpu_to_le32(buffer); - sdesc.addr_hi = flags; - if (flags & CE_SEND_FLAG_GATHER) - sdesc.addr_hi |= CE_WCN3990_DESC_FLAGS_GATHER; - else - sdesc.addr_hi &= ~CE_WCN3990_DESC_FLAGS_GATHER; - } else { + if (QCA_REV_WCN3990(ar)) + ath10k_ce_snoc_addr_config(&sdesc, buffer, flags); + else sdesc.addr = __cpu_to_le32(buffer); - } sdesc.nbytes = __cpu_to_le16(nbytes); sdesc.flags = __cpu_to_le16(desc_flags); @@ -422,10 +509,11 @@ exit: void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe) { struct ath10k *ar = pipe->ar; + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); struct ath10k_ce_ring *src_ring = pipe->src_ring; u32 ctrl_addr = pipe->ctrl_addr; - lockdep_assert_held(&ar->ce_lock); + lockdep_assert_held(&ar_opaque->ce_lock); /* * This function must be called only if there is an incomplete @@ -453,12 +541,13 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, unsigned int flags) { struct ath10k *ar = ce_state->ar; + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); int ret; - spin_lock_bh(&ar->ce_lock); + spin_lock_bh(&ar_opaque->ce_lock); ret = ath10k_ce_send_nolock(ce_state, per_transfer_context, buffer, nbytes, transfer_id, flags); - spin_unlock_bh(&ar->ce_lock); + spin_unlock_bh(&ar_opaque->ce_lock); return ret; } @@ -466,13 +555,14 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe) { struct ath10k *ar = pipe->ar; + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); int delta; - spin_lock_bh(&ar->ce_lock); + spin_lock_bh(&ar_opaque->ce_lock); delta = CE_RING_DELTA(pipe->src_ring->nentries_mask, pipe->src_ring->write_index, pipe->src_ring->sw_index - 1); - spin_unlock_bh(&ar->ce_lock); + spin_unlock_bh(&ar_opaque->ce_lock); return delta; } @@ -480,12 +570,13 @@ int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe) int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe) { struct ath10k *ar = pipe->ar; + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); struct ath10k_ce_ring *dest_ring = pipe->dest_ring; unsigned int nentries_mask = dest_ring->nentries_mask; unsigned int write_index = dest_ring->write_index; unsigned int sw_index = dest_ring->sw_index; - lockdep_assert_held(&ar->ce_lock); + lockdep_assert_held(&ar_opaque->ce_lock); return CE_RING_DELTA(nentries_mask, write_index, sw_index - 1); } @@ -494,6 +585,7 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, dma_addr_t paddr) { struct ath10k *ar = pipe->ar; + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); struct ath10k_ce_ring *dest_ring = pipe->dest_ring; unsigned int nentries_mask = dest_ring->nentries_mask; unsigned int write_index = dest_ring->write_index; @@ -502,7 +594,7 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index); u32 ctrl_addr = pipe->ctrl_addr; - lockdep_assert_held(&ar->ce_lock); + lockdep_assert_held(&ar_opaque->ce_lock); if ((pipe->id != 5) && CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) == 0) @@ -542,11 +634,12 @@ int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, dma_addr_t paddr) { struct ath10k *ar = pipe->ar; + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); int ret; - spin_lock_bh(&ar->ce_lock); + spin_lock_bh(&ar_opaque->ce_lock); ret = __ath10k_ce_rx_post_buf(pipe, ctx, paddr); - spin_unlock_bh(&ar->ce_lock); + spin_unlock_bh(&ar_opaque->ce_lock); return ret; } @@ -609,13 +702,14 @@ int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state, unsigned int *nbytesp) { struct ath10k *ar = ce_state->ar; + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); int ret; - spin_lock_bh(&ar->ce_lock); + spin_lock_bh(&ar_opaque->ce_lock); ret = ath10k_ce_completed_recv_next_nolock(ce_state, per_transfer_contextp, nbytesp); - spin_unlock_bh(&ar->ce_lock); + spin_unlock_bh(&ar_opaque->ce_lock); return ret; } @@ -630,6 +724,7 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state, unsigned int write_index; int ret; struct ath10k *ar; + struct bus_opaque *ar_opaque; dest_ring = ce_state->dest_ring; @@ -637,8 +732,9 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state, return -EIO; ar = ce_state->ar; + ar_opaque = ath10k_bus_priv(ar); - spin_lock_bh(&ar->ce_lock); + spin_lock_bh(&ar_opaque->ce_lock); nentries_mask = dest_ring->nentries_mask; sw_index = dest_ring->sw_index; @@ -666,7 +762,7 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state, ret = -EIO; } - spin_unlock_bh(&ar->ce_lock); + spin_unlock_bh(&ar_opaque->ce_lock); return ret; } @@ -734,6 +830,7 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state, unsigned int write_index; int ret; struct ath10k *ar; + struct bus_opaque *ar_opaque; src_ring = ce_state->src_ring; @@ -741,8 +838,9 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state, return -EIO; ar = ce_state->ar; + ar_opaque = ath10k_bus_priv(ar); - spin_lock_bh(&ar->ce_lock); + spin_lock_bh(&ar_opaque->ce_lock); nentries_mask = src_ring->nentries_mask; sw_index = src_ring->sw_index; @@ -773,7 +871,7 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state, ret = -EIO; } - spin_unlock_bh(&ar->ce_lock); + spin_unlock_bh(&ar_opaque->ce_lock); return ret; } @@ -782,12 +880,13 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp) { struct ath10k *ar = ce_state->ar; + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); int ret; - spin_lock_bh(&ar->ce_lock); + spin_lock_bh(&ar_opaque->ce_lock); ret = ath10k_ce_completed_send_next_nolock(ce_state, per_transfer_contextp); - spin_unlock_bh(&ar->ce_lock); + spin_unlock_bh(&ar_opaque->ce_lock); return ret; } @@ -800,17 +899,18 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state, */ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) { - struct ath10k_ce_pipe *ce_state = - ((struct ath10k_ce_pipe *)ar->ce_states + ce_id); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id]; + struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; u32 ctrl_addr = ce_state->ctrl_addr; - spin_lock_bh(&ar->ce_lock); + spin_lock_bh(&ar_opaque->ce_lock); /* Clear the copy-complete interrupts that will be handled here. */ ath10k_ce_engine_int_status_clear(ar, ctrl_addr, - HOST_IS_COPY_COMPLETE_MASK); + wm_regs->cc_mask); - spin_unlock_bh(&ar->ce_lock); + spin_unlock_bh(&ar_opaque->ce_lock); if (ce_state->recv_cb) ce_state->recv_cb(ce_state); @@ -818,15 +918,15 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) if (ce_state->send_cb) ce_state->send_cb(ce_state); - spin_lock_bh(&ar->ce_lock); + spin_lock_bh(&ar_opaque->ce_lock); /* * Misc CE interrupts are not being handled, but still need * to be cleared. */ - ath10k_ce_engine_int_status_clear(ar, ctrl_addr, CE_WATERMARK_MASK); + ath10k_ce_engine_int_status_clear(ar, ctrl_addr, wm_regs->wm_mask); - spin_unlock_bh(&ar->ce_lock); + spin_unlock_bh(&ar_opaque->ce_lock); } /* @@ -840,11 +940,12 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar) int ce_id; u32 intr_summary; struct ath10k_ce_pipe *ce_state; + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); if (ar->target_version == ATH10K_HW_WCN3990) intr_summary = 0xFFF; else - intr_summary = CE_INTERRUPT_SUMMARY(ar); + intr_summary = CE_INTERRUPT_SUMMARY(ar, ar_opaque); for (ce_id = 0; intr_summary && (ce_id < CE_COUNT); ce_id++) { if (intr_summary & (1 << ce_id)) @@ -853,7 +954,7 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar) /* no intr pending on this CE */ continue; - ce_state = ((struct ath10k_ce_pipe *)ar->ce_states + ce_id); + ce_state = &ar_opaque->ce_states[ce_id]; if (ce_state->send_cb || ce_state->recv_cb) ath10k_ce_per_engine_service(ar, ce_id); } @@ -899,42 +1000,47 @@ int ath10k_ce_disable_interrupts(struct ath10k *ar) void ath10k_ce_enable_interrupts(struct ath10k *ar) { + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); int ce_id; + struct ath10k_ce_pipe *ce_state; /* Skip the last copy engine, CE7 the diagnostic window, as that * uses polling and isn't initialized for interrupts. */ - for (ce_id = 0; ce_id < CE_COUNT - 1; ce_id++) - ath10k_ce_per_engine_handler_adjust( - ((struct ath10k_ce_pipe *)ar->ce_states + ce_id)); + for (ce_id = 0; ce_id < CE_COUNT - 1; ce_id++) { + ce_state = &ar_opaque->ce_states[ce_id]; + ath10k_ce_per_engine_handler_adjust(ce_state); + } } void ath10k_ce_enable_per_ce_interrupts(struct ath10k *ar, unsigned int ce_id) { u32 offset; u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); - offset = HOST_IE_ADDRESS + ctrl_addr; - ar->bus_write32(ar, offset, 1); - ar->bus_read32(ar, offset); + offset = ar->hw_ce_regs->host_ie_addr + ctrl_addr; + ar_opaque->bus_ops->write32(ar, offset, 1); + ar_opaque->bus_ops->read32(ar, offset); } void ath10k_ce_disable_per_ce_interrupts(struct ath10k *ar, unsigned int ce_id) { u32 offset; u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); - offset = HOST_IE_ADDRESS + ctrl_addr; - ar->bus_write32(ar, offset, 0); - ar->bus_read32(ar, offset); + offset = ar->hw_ce_regs->host_ie_addr + ctrl_addr; + ar_opaque->bus_ops->write32(ar, offset, 0); + ar_opaque->bus_ops->read32(ar, offset); } static int ath10k_ce_init_src_ring(struct ath10k *ar, unsigned int ce_id, const struct ce_attr *attr) { - struct ath10k_ce_pipe *ce_state = - ((struct ath10k_ce_pipe *)ar->ce_states + ce_id); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id]; struct ath10k_ce_ring *src_ring = ce_state->src_ring; u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id); @@ -970,8 +1076,8 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar, unsigned int ce_id, const struct ce_attr *attr) { - struct ath10k_ce_pipe *ce_state = - ((struct ath10k_ce_pipe *)ar->ce_states + ce_id); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id]; struct ath10k_ce_ring *dest_ring = ce_state->dest_ring; u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id); @@ -1178,8 +1284,8 @@ void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id) int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, const struct ce_attr *attr) { - struct ath10k_ce_pipe *ce_state = - ((struct ath10k_ce_pipe *)ar->ce_states + ce_id); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id]; int ret; /* @@ -1235,8 +1341,8 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id) { - struct ath10k_ce_pipe *ce_state = - ((struct ath10k_ce_pipe *)ar->ce_states + ce_id); + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id]; if (ce_state->src_ring) { kfree(ce_state->src_ring->shadow_base_unaligned); diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index 160a13e681df..936f0698c0f0 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -58,13 +58,7 @@ struct ce_desc { }; #else struct ce_desc { - union { - __le64 addr; - struct { - __le32 addr_lo; - __le32 addr_hi; - }; - }; + __le64 addr; u16 nbytes; /* length in register map */ u16 flags; /* fw_metadata_high */ u32 toeplitz_hash_result; @@ -201,6 +195,24 @@ struct ce_attr; u32 shadow_sr_wr_ind_addr(struct ath10k *ar, u32 ctrl_addr); u32 shadow_dst_wr_ind_addr(struct ath10k *ar, u32 ctrl_addr); +struct ath10k_bus_ops { + u32 (*read32)(struct ath10k *ar, u32 offset); + void (*write32)(struct ath10k *ar, u32 offset, u32 value); + int (*get_num_banks)(struct ath10k *ar); +}; + +static inline struct bus_opaque *ath10k_bus_priv(struct ath10k *ar) +{ + return (struct bus_opaque *)ar->drv_priv; +} + +struct bus_opaque { + /* protects CE info */ + spinlock_t ce_lock; + const struct ath10k_bus_ops *bus_ops; + struct ath10k_ce_pipe ce_states[CE_COUNT_MAX]; +}; + /*==================Send====================*/ /* ath10k_ce_send flags */ @@ -344,311 +356,6 @@ struct ce_attr { void (*recv_cb)(struct ath10k_ce_pipe *); }; -#ifndef CONFIG_ATH10K_SNOC -#define SR_BA_ADDRESS 0x0000 -#define SR_SIZE_ADDRESS 0x0004 -#define DR_BA_ADDRESS 0x0008 -#define DR_SIZE_ADDRESS 0x000c -#define CE_CMD_ADDRESS 0x0018 - -#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MSB 17 -#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB 17 -#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK 0x00020000 -#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \ - (((0 | (x)) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \ - CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) - -#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MSB 16 -#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB 16 -#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK 0x00010000 -#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_GET(x) \ - (((x) & CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) >> \ - CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) -#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \ - (((0 | (x)) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \ - CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) - -#define CE_CTRL1_DMAX_LENGTH_MSB 15 -#define CE_CTRL1_DMAX_LENGTH_LSB 0 -#define CE_CTRL1_DMAX_LENGTH_MASK 0x0000ffff -#define CE_CTRL1_DMAX_LENGTH_GET(x) \ - (((x) & CE_CTRL1_DMAX_LENGTH_MASK) >> CE_CTRL1_DMAX_LENGTH_LSB) -#define CE_CTRL1_DMAX_LENGTH_SET(x) \ - (((0 | (x)) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK) - -#define CE_CTRL1_ADDRESS 0x0010 -#define CE_CTRL1_HW_MASK 0x0007ffff -#define CE_CTRL1_SW_MASK 0x0007ffff -#define CE_CTRL1_HW_WRITE_MASK 0x00000000 -#define CE_CTRL1_SW_WRITE_MASK 0x0007ffff -#define CE_CTRL1_RSTMASK 0xffffffff -#define CE_CTRL1_RESET 0x00000080 - -#define CE_CMD_HALT_STATUS_MSB 3 -#define CE_CMD_HALT_STATUS_LSB 3 -#define CE_CMD_HALT_STATUS_MASK 0x00000008 -#define CE_CMD_HALT_STATUS_GET(x) \ - (((x) & CE_CMD_HALT_STATUS_MASK) >> CE_CMD_HALT_STATUS_LSB) -#define CE_CMD_HALT_STATUS_SET(x) \ - (((0 | (x)) << CE_CMD_HALT_STATUS_LSB) & CE_CMD_HALT_STATUS_MASK) -#define CE_CMD_HALT_STATUS_RESET 0 -#define CE_CMD_HALT_MSB 0 -#define CE_CMD_HALT_MASK 0x00000001 - -#define HOST_IE_COPY_COMPLETE_MSB 0 -#define HOST_IE_COPY_COMPLETE_LSB 0 -#define HOST_IE_COPY_COMPLETE_MASK 0x00000001 -#define HOST_IE_COPY_COMPLETE_GET(x) \ - (((x) & HOST_IE_COPY_COMPLETE_MASK) >> HOST_IE_COPY_COMPLETE_LSB) -#define HOST_IE_COPY_COMPLETE_SET(x) \ - (((0 | (x)) << HOST_IE_COPY_COMPLETE_LSB) & HOST_IE_COPY_COMPLETE_MASK) -#define HOST_IE_COPY_COMPLETE_RESET 0 -#define HOST_IE_ADDRESS 0x002c - -#define HOST_IS_DST_RING_LOW_WATERMARK_MASK 0x00000010 -#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK 0x00000008 -#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK 0x00000004 -#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK 0x00000002 -#define HOST_IS_COPY_COMPLETE_MASK 0x00000001 -#define HOST_IS_ADDRESS 0x0030 - -#define MISC_IE_ADDRESS 0x0034 - -#define MISC_IS_AXI_ERR_MASK 0x00000400 - -#define MISC_IS_DST_ADDR_ERR_MASK 0x00000200 -#define MISC_IS_SRC_LEN_ERR_MASK 0x00000100 -#define MISC_IS_DST_MAX_LEN_VIO_MASK 0x00000080 -#define MISC_IS_DST_RING_OVERFLOW_MASK 0x00000040 -#define MISC_IS_SRC_RING_OVERFLOW_MASK 0x00000020 - -#define MISC_IS_ADDRESS 0x0038 - -#define SR_WR_INDEX_ADDRESS 0x003c - -#define DST_WR_INDEX_ADDRESS 0x0040 - -#define CURRENT_SRRI_ADDRESS 0x0044 - -#define CURRENT_DRRI_ADDRESS 0x0048 - -#define SRC_WATERMARK_LOW_MSB 31 -#define SRC_WATERMARK_LOW_LSB 16 -#define SRC_WATERMARK_LOW_MASK 0xffff0000 -#define SRC_WATERMARK_LOW_GET(x) \ - (((x) & SRC_WATERMARK_LOW_MASK) >> SRC_WATERMARK_LOW_LSB) -#define SRC_WATERMARK_LOW_SET(x) \ - (((0 | (x)) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK) -#define SRC_WATERMARK_LOW_RESET 0 -#define SRC_WATERMARK_HIGH_MSB 15 -#define SRC_WATERMARK_HIGH_LSB 0 -#define SRC_WATERMARK_HIGH_MASK 0x0000ffff -#define SRC_WATERMARK_HIGH_GET(x) \ - (((x) & SRC_WATERMARK_HIGH_MASK) >> SRC_WATERMARK_HIGH_LSB) -#define SRC_WATERMARK_HIGH_SET(x) \ - (((0 | (x)) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK) -#define SRC_WATERMARK_HIGH_RESET 0 -#define SRC_WATERMARK_ADDRESS 0x004c - -#define DST_WATERMARK_LOW_LSB 16 -#define DST_WATERMARK_LOW_MASK 0xffff0000 -#define DST_WATERMARK_LOW_SET(x) \ - (((0 | (x)) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK) -#define DST_WATERMARK_LOW_RESET 0 -#define DST_WATERMARK_HIGH_MSB 15 -#define DST_WATERMARK_HIGH_LSB 0 -#define DST_WATERMARK_HIGH_MASK 0x0000ffff -#define DST_WATERMARK_HIGH_GET(x) \ - (((x) & DST_WATERMARK_HIGH_MASK) >> DST_WATERMARK_HIGH_LSB) -#define DST_WATERMARK_HIGH_SET(x) \ - (((0 | (x)) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK) -#define DST_WATERMARK_HIGH_RESET 0 -#define DST_WATERMARK_ADDRESS 0x0050 - -#else -#define WCN3990_CE0_SR_BA_LOW (0x00240000) -#define WCN3990_CE1_SR_BA_LOW (0x00241000) -#define WCN3990_CE2_SR_BA_LOW (0x00242000) -#define WCN3990_CE3_SR_BA_LOW (0x00243000) -#define WCN3990_CE4_SR_BA_LOW (0x00244000) -#define WCN3990_CE5_SR_BA_LOW (0x00245000) -#define WCN3990_CE6_SR_BA_LOW (0x00246000) -#define WCN3990_CE7_SR_BA_LOW (0x00247000) -#define WCN3990_CE8_SR_BA_LOW (0x00248000) -#define WCN3990_CE9_SR_BA_LOW (0x00249000) -#define WCN3990_CE10_SR_BA_LOW (0x0024A000) -#define WCN3990_CE11_SR_BA_LOW (0x0024B000) -#define WCN3990_CE0_DR_BA_LOW (0x0024000C) -#define WNC3990_CE0_DR_SIZE (0x00240014) -#define WCN3990_CE0_CE_CTRL1 (0x00240018) -#define WCN3990_CE0_HOST_IE (0x0024002C) -#define WCN3990_CE0_HOST_IS (0x00240030) -#define WCN3990_CE0_MISC_IE (0x00240034) -#define WCN3990_CE0_MISC_IS (0x00240038) -#define WCN3990_CE0_SRC_WR_INDEX (0x0024003C) -#define WCN3990_CE0_CURRENT_SRRI (0x00240044) -#define WCN3990_CE0_CURRENT_DRRI (0x00240048) -#define WCN3990_CE0_SRC_WATERMARK (0x0024004C) -#define WCN3990_CE0_DST_WATERMARK (0x00240050) -#define WCN3990_CE0_SR_SIZE (0x00240008) -#define HOST_IE_COPY_COMPLETE_MASK (0x00000001) -#define WCN3990_CE_WRAPPER_HOST_INTERRUPT_SUMMARY 0x0024C000 -#define WCN3990_CE_WRAPPER_INDEX_BASE_LOW 0x0024C004 -#define WCN3990_CE_WRAPPER_INDEX_BASE_HIGH 0x0024C008 -#define CE_CTRL1_IDX_UPD_EN 0x00080000 - -#define WCN3990_CE_WRAPPER_BASE_ADDRESS \ - WCN3990_CE_WRAPPER_HOST_INTERRUPT_SUMMARY -#define WCN3990_CE0_BASE_ADDRESS \ - WCN3990_CE0_SR_BA_LOW -#define WCN3990_CE1_BASE_ADDRESS \ - WCN3990_CE1_SR_BA_LOW -#define WCN3990_CE2_BASE_ADDRESS \ - WCN3990_CE2_SR_BA_LOW -#define WCN3990_CE3_BASE_ADDRESS \ - WCN3990_CE3_SR_BA_LOW -#define WCN3990_CE4_BASE_ADDRESS \ - WCN3990_CE4_SR_BA_LOW -#define WCN3990_CE5_BASE_ADDRESS \ - WCN3990_CE5_SR_BA_LOW -#define WCN3990_CE6_BASE_ADDRESS \ - WCN3990_CE6_SR_BA_LOW -#define WCN3990_CE7_BASE_ADDRESS \ - WCN3990_CE7_SR_BA_LOW -#define WCN3990_CE8_BASE_ADDRESS \ - WCN3990_CE8_SR_BA_LOW -#define WCN3990_CE9_BASE_ADDRESS \ - WCN3990_CE9_SR_BA_LOW -#define WCN3990_CE10_BASE_ADDRESS \ - WCN3990_CE10_SR_BA_LOW -#define WCN3990_CE11_BASE_ADDRESS \ - WCN3990_CE11_SR_BA_LOW - -#define SR_BA_ADDRESS (WCN3990_CE0_SR_BA_LOW\ - - WCN3990_CE0_BASE_ADDRESS) -#define SR_SIZE_ADDRESS (WCN3990_CE0_SR_SIZE \ - - WCN3990_CE0_BASE_ADDRESS) -#define DR_BA_ADDRESS (WCN3990_CE0_DR_BA_LOW\ - - WCN3990_CE0_BASE_ADDRESS) -#define DR_SIZE_ADDRESS (WNC3990_CE0_DR_SIZE\ - - WCN3990_CE0_BASE_ADDRESS) -#define WCN3990_CE_DDR_ADDRESS_FOR_RRI_LOW \ - (WCN3990_CE_WRAPPER_INDEX_BASE_LOW - WCN3990_CE_WRAPPER_BASE_ADDRESS) - -#define WCN3990_CE_DDR_ADDRESS_FOR_RRI_HIGH \ - (WCN3990_CE_WRAPPER_INDEX_BASE_HIGH - WCN3990_CE_WRAPPER_BASE_ADDRESS) - -#define CE_RRI_LOW (WCN3990_CE_WRAPPER_BASE_ADDRESS \ - + WCN3990_CE_DDR_ADDRESS_FOR_RRI_LOW) - -#define CE_RRI_HIGH (WCN3990_CE_WRAPPER_BASE_ADDRESS \ - + WCN3990_CE_DDR_ADDRESS_FOR_RRI_HIGH) - -#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MSB 18 -#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB 18 - -#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK 0x00040000 -#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \ - (((0 | (x)) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \ - CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) - -#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MSB 16 -#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB 16 - -#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK 0x00020000 -#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_GET(x) \ - (((x) & CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) >> \ - CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) -#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \ - (((0 | (x)) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \ - CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) - -#define CE_CTRL1_DMAX_LENGTH_MSB 0 -#define CE_CTRL1_DMAX_LENGTH_LSB 0 - -#define CE_CTRL1_DMAX_LENGTH_MASK 0x0000FFFF -#define CE_CTRL1_DMAX_LENGTH_GET(x) \ - (((x) & CE_CTRL1_DMAX_LENGTH_MASK) >> CE_CTRL1_DMAX_LENGTH_LSB) -#define CE_CTRL1_DMAX_LENGTH_SET(x) \ - (((0 | (x)) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK) - -#define CE_CTRL1_ADDRESS (WCN3990_CE0_CE_CTRL1 \ - - WCN3990_CE0_BASE_ADDRESS) - -#define HOST_IE_ADDRESS (WCN3990_CE0_HOST_IE\ - - WCN3990_CE0_BASE_ADDRESS) - -#define HOST_IS_DST_RING_LOW_WATERMARK_MASK 0x00000010 -#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK 0x00000008 -#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK 0x00000004 -#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK 0x00000002 -#define HOST_IS_COPY_COMPLETE_MASK 0x00000001 -#define HOST_IS_ADDRESS (WCN3990_CE0_HOST_IS \ - - WCN3990_CE0_BASE_ADDRESS) -#define MISC_IE_ADDRESS (WCN3990_CE0_MISC_IE \ - - WCN3990_CE0_BASE_ADDRESS) - -#define MISC_IS_AXI_ERR_MASK 0x00000100 -#define MISC_IS_DST_ADDR_ERR_MASK 0x00000200 -#define MISC_IS_SRC_LEN_ERR_MASK 0x00000100 -#define MISC_IS_DST_MAX_LEN_VIO_MASK 0x00000080 -#define MISC_IS_DST_RING_OVERFLOW_MASK 0x00000040 -#define MISC_IS_SRC_RING_OVERFLOW_MASK 0x00000020 -#define MISC_IS_ADDRESS (WCN3990_CE0_MISC_IS \ - - WCN3990_CE0_BASE_ADDRESS) - -#define SR_WR_INDEX_ADDRESS 0x3C -#define DST_WR_INDEX_ADDRESS 0x40 - -#define CURRENT_SRRI_ADDRESS (WCN3990_CE0_CURRENT_SRRI\ - - WCN3990_CE0_BASE_ADDRESS) -#define CURRENT_DRRI_ADDRESS (WCN3990_CE0_CURRENT_DRRI\ - - WCN3990_CE0_BASE_ADDRESS) - -#define SRC_WATERMARK_LOW_MSB 0 -#define SRC_WATERMARK_LOW_LSB 16 - -#define SRC_WATERMARK_LOW_MASK 0xffff0000 -#define SRC_WATERMARK_LOW_GET(x) \ - (((x) & SRC_WATERMARK_LOW_MASK) >> SRC_WATERMARK_LOW_LSB) -#define SRC_WATERMARK_LOW_SET(x) \ - (((0 | (x)) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK) - -#define SRC_WATERMARK_LOW_RESET 0 -#define SRC_WATERMARK_HIGH_MSB 15 -#define SRC_WATERMARK_HIGH_LSB 0 -#define SRC_WATERMARK_HIGH_MASK 0x0000ffff -#define SRC_WATERMARK_HIGH_GET(x) \ - (((x) & SRC_WATERMARK_HIGH_MASK) >> SRC_WATERMARK_HIGH_LSB) -#define SRC_WATERMARK_HIGH_SET(x) \ - (((0 | (x)) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK) - -#define SRC_WATERMARK_HIGH_RESET 0 -#define SRC_WATERMARK_ADDRESS (WCN3990_CE0_SRC_WATERMARK\ - - WCN3990_CE0_BASE_ADDRESS) - -#define DST_WATERMARK_LOW_LSB 16 -#define DST_WATERMARK_LOW_MASK 0xffff0000 -#define DST_WATERMARK_LOW_SET(x) \ - (((0 | (x)) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK) -#define DST_WATERMARK_LOW_RESET 0 -#define DST_WATERMARK_HIGH_MSB 15 -#define DST_WATERMARK_HIGH_LSB 0 -#define DST_WATERMARK_HIGH_MASK 0x0000ffff -#define DST_WATERMARK_HIGH_GET(x) \ - (((x) & DST_WATERMARK_HIGH_MASK) >> DST_WATERMARK_HIGH_LSB) -#define DST_WATERMARK_HIGH_SET(x) \ - (((0 | (x)) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK) -#define DST_WATERMARK_HIGH_RESET 0 -#define DST_WATERMARK_ADDRESS (WCN3990_CE0_DST_WATERMARK \ - - WCN3990_CE0_BASE_ADDRESS) - -#define BITS0_TO_31(val) ((uint32_t)((uint64_t)(val)\ - & (uint64_t)(0xFFFFFFFF))) -#define BITS32_TO_35(val) ((uint32_t)(((uint64_t)(val)\ - & (uint64_t)(0xF00000000)) >> 32)) -#endif - #define COPY_ENGINE_ID(COPY_ENGINE_BASE_ADDRESS) ((COPY_ENGINE_BASE_ADDRESS \ - CE0_BASE_ADDRESS) / (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS)) @@ -657,18 +364,6 @@ static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id) return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id; } -#define CE_WATERMARK_MASK (HOST_IS_SRC_RING_LOW_WATERMARK_MASK | \ - HOST_IS_SRC_RING_HIGH_WATERMARK_MASK | \ - HOST_IS_DST_RING_LOW_WATERMARK_MASK | \ - HOST_IS_DST_RING_HIGH_WATERMARK_MASK) - -#define CE_ERROR_MASK (MISC_IS_AXI_ERR_MASK | \ - MISC_IS_DST_ADDR_ERR_MASK | \ - MISC_IS_SRC_LEN_ERR_MASK | \ - MISC_IS_DST_MAX_LEN_VIO_MASK | \ - MISC_IS_DST_RING_OVERFLOW_MASK | \ - MISC_IS_SRC_RING_OVERFLOW_MASK) - #define CE_SRC_RING_TO_DESC(baddr, idx) \ (&(((struct ce_desc *)baddr)[idx])) @@ -692,9 +387,9 @@ static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id) CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB) #define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS 0x0000 -#define CE_INTERRUPT_SUMMARY(ar) \ +#define CE_INTERRUPT_SUMMARY(ar, ar_opaque) \ CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( \ - ar->bus_read32((ar), CE_WRAPPER_BASE_ADDRESS + \ + ar_opaque->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS + \ CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS)) #endif /* _CE_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 052ebd7dd26b..d37ed66d767b 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -2324,28 +2324,34 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, case ATH10K_HW_QCA988X: case ATH10K_HW_QCA9887: ar->regs = &qca988x_regs; + ar->hw_ce_regs = &qcax_ce_regs; ar->hw_values = &qca988x_values; break; case ATH10K_HW_QCA6174: case ATH10K_HW_QCA9377: ar->regs = &qca6174_regs; + ar->hw_ce_regs = &qcax_ce_regs; ar->hw_values = &qca6174_values; break; case ATH10K_HW_QCA99X0: case ATH10K_HW_QCA9984: ar->regs = &qca99x0_regs; + ar->hw_ce_regs = &qcax_ce_regs; ar->hw_values = &qca99x0_values; break; case ATH10K_HW_QCA9888: ar->regs = &qca99x0_regs; + ar->hw_ce_regs = &qcax_ce_regs; ar->hw_values = &qca9888_values; break; case ATH10K_HW_QCA4019: ar->regs = &qca4019_regs; + ar->hw_ce_regs = &qcax_ce_regs; ar->hw_values = &qca4019_values; break; case ATH10K_HW_WCN3990: ar->regs = &wcn3990_regs; + ar->hw_ce_regs = &wcn3990_ce_regs; ar->hw_values = &wcn3990_values; /* WCN3990 chip set is non bmi based */ ar->is_bmi = false; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index bb2c5fb9a125..21c63d5d3ead 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -740,6 +740,7 @@ struct ath10k { struct completion target_suspend; const struct ath10k_hw_regs *regs; + const struct ath10k_hw_ce_regs *hw_ce_regs; const struct ath10k_hw_values *hw_values; struct ath10k_shadow_reg_value *shadow_reg_value; struct ath10k_shadow_reg_address *shadow_reg_address; @@ -923,10 +924,6 @@ struct ath10k { struct net_device napi_dev; struct napi_struct napi; - void (*bus_write32)(void *ar, u32 offset, u32 value); - u32 (*bus_read32)(void *ar, u32 offset); - spinlock_t ce_lock; /* lock for CE access */ - void *ce_states; struct fw_flag *fw_flags; /* set for bmi chip sets */ bool is_bmi; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 82a4c67f3672..c36c2481856b 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1909,7 +1909,7 @@ int ath10k_debug_start(struct ath10k *ar) ath10k_warn(ar, "failed to disable pktlog: %d\n", ret); } - if (ar->debug.nf_cal_period) { + if (ar->debug.nf_cal_period && !QCA_REV_WCN3990(ar)) { ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->cal_period, ar->debug.nf_cal_period); @@ -1926,7 +1926,8 @@ void ath10k_debug_stop(struct ath10k *ar) { lockdep_assert_held(&ar->conf_mutex); - ath10k_debug_cal_data_fetch(ar); + if (!QCA_REV_WCN3990(ar)) + ath10k_debug_cal_data_fetch(ar); /* Must not use _sync to avoid deadlock, we do that in * ath10k_debug_destroy(). The check for htt_stats_mask is to avoid @@ -2419,15 +2420,18 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("fw_dbglog", S_IRUSR | S_IWUSR, ar->debug.debugfs_phy, ar, &fops_fw_dbglog); - debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy, - ar, &fops_cal_data); + if (!QCA_REV_WCN3990(ar)) { + debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_cal_data); + + debugfs_create_file("nf_cal_period", S_IRUSR | S_IWUSR, + ar->debug.debugfs_phy, ar, + &fops_nf_cal_period); + } debugfs_create_file("ani_enable", S_IRUSR | S_IWUSR, ar->debug.debugfs_phy, ar, &fops_ani_enable); - debugfs_create_file("nf_cal_period", S_IRUSR | S_IWUSR, - ar->debug.debugfs_phy, ar, &fops_nf_cal_period); - 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/hif.h b/drivers/net/wireless/ath/ath10k/hif.h index 861446a41066..65723124985e 100644 --- a/drivers/net/wireless/ath/ath10k/hif.h +++ b/drivers/net/wireless/ath/ath10k/hif.h @@ -74,9 +74,9 @@ struct ath10k_hif_ops { u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id); - u32 (*read32)(void *ar, u32 address); + u32 (*read32)(struct ath10k *ar, u32 address); - void (*write32)(void *ar, u32 address, u32 value); + void (*write32)(struct ath10k *ar, u32 address, u32 value); /* Power up the device and enter BMI transfer mode for FW download */ int (*power_up)(struct ath10k *ar); diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index b2678984f2de..6dd396430f19 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -541,6 +541,7 @@ struct htt_rx_indication_hdr { #define HTT_RX_INDICATION_INFO2_SERVICE_LSB 24 #define HTT_WCN3990_PADDR_MASK 0x1F +#define HTT_WCN3990_ARCH_PADDR_MASK 0x1FFFFFFFFF enum htt_rx_legacy_rate { HTT_RX_OFDM_48 = 0, @@ -865,8 +866,7 @@ struct htt_rx_offload_ind { struct htt_rx_in_ord_msdu_desc { #ifdef CONFIG_ATH10K_SNOC - __le32 msdu_paddr_lo; - __le32 msdu_paddr_hi; + __le64 msdu_paddr; #else __le32 msdu_paddr; #endif diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index cbb61267eb10..ddf097e3a143 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -429,9 +429,8 @@ static int ath10k_htt_rx_pop_paddr_list(struct ath10k_htt *htt, while (msdu_count--) { #ifdef CONFIG_ATH10K_SNOC - paddr = __le32_to_cpu(msdu_desc->msdu_paddr_lo); - paddr |= ((u64)(msdu_desc->msdu_paddr_hi & - HTT_WCN3990_PADDR_MASK) << 32); + paddr = __le64_to_cpu(msdu_desc->msdu_paddr); + paddr &= HTT_WCN3990_ARCH_PADDR_MASK; #else paddr = __le32_to_cpu(msdu_desc->msdu_paddr); #endif diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 1a8f3a388ce2..1d37b2c8426b 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -164,6 +164,301 @@ const struct ath10k_hw_regs wcn3990_regs = { .pcie_intr_fw_mask = 0x00100000, }; +static unsigned int +ath10k_set_ring_byte(unsigned int offset, + struct ath10k_hw_ce_regs_addr_map *addr_map) +{ + return (((0 | (offset)) << addr_map->lsb) & addr_map->mask); +} + +static unsigned int +ath10k_get_ring_byte(unsigned int offset, + struct ath10k_hw_ce_regs_addr_map *addr_map) +{ + return (((offset) & addr_map->mask) >> (addr_map->lsb)); +} + +struct ath10k_hw_ce_regs_addr_map wcn3990_src_ring = { + .msb = 0x00000010, + .lsb = 0x00000010, + .mask = 0x00020000, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_regs_addr_map wcn3990_dst_ring = { + .msb = 0x00000012, + .lsb = 0x00000012, + .mask = 0x00040000, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_regs_addr_map wcn3990_dmax = { + .msb = 0x00000000, + .lsb = 0x00000000, + .mask = 0x0000ffff, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_ctrl1 wcn3990_ctrl1 = { + .addr = 0x00000018, + .src_ring = &wcn3990_src_ring, + .dst_ring = &wcn3990_dst_ring, + .dmax = &wcn3990_dmax, +}; + +struct ath10k_hw_ce_regs_addr_map wcn3990_host_ie_cc = { + .mask = 0x00000001, +}; + +struct ath10k_hw_ce_host_ie wcn3990_host_ie = { + .copy_complete = &wcn3990_host_ie_cc, +}; + +struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = { + .dstr_lmask = 0x00000010, + .dstr_hmask = 0x00000008, + .srcr_lmask = 0x00000004, + .srcr_hmask = 0x00000002, + .cc_mask = 0x00000001, + .wm_mask = 0x0000001E, + .addr = 0x00000030, +}; + +struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = { + .axi_err = 0x00000100, + .dstr_add_err = 0x00000200, + .srcr_len_err = 0x00000100, + .dstr_mlen_vio = 0x00000080, + .dstr_overflow = 0x00000040, + .srcr_overflow = 0x00000020, + .err_mask = 0x000003E0, + .addr = 0x00000038, +}; + +struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_low = { + .msb = 0x00000000, + .lsb = 0x00000010, + .mask = 0xffff0000, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_high = { + .msb = 0x0000000f, + .lsb = 0x00000000, + .mask = 0x0000ffff, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = { + .addr = 0x0000004c, + .low_rst = 0x00000000, + .high_rst = 0x00000000, + .wm_low = &wcn3990_src_wm_low, + .wm_high = &wcn3990_src_wm_high, +}; + +struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_low = { + .lsb = 0x00000010, + .mask = 0xffff0000, + .set = &ath10k_set_ring_byte, +}; + +struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_high = { + .msb = 0x0000000f, + .lsb = 0x00000000, + .mask = 0x0000ffff, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = { + .addr = 0x00000050, + .low_rst = 0x00000000, + .high_rst = 0x00000000, + .wm_low = &wcn3990_dst_wm_low, + .wm_high = &wcn3990_dst_wm_high, +}; + +struct ath10k_hw_ce_regs wcn3990_ce_regs = { + .sr_base_addr = 0x00000000, + .sr_size_addr = 0x00000008, + .dr_base_addr = 0x0000000c, + .dr_size_addr = 0x00000014, + .misc_ie_addr = 0x00000034, + .sr_wr_index_addr = 0x0000003c, + .dst_wr_index_addr = 0x00000040, + .current_srri_addr = 0x00000044, + .current_drri_addr = 0x00000048, + .ddr_addr_for_rri_low = 0x00000004, + .ddr_addr_for_rri_high = 0x00000008, + .ce_rri_low = 0x0024C004, + .ce_rri_high = 0x0024C008, + .host_ie_addr = 0x0000002c, + .ctrl1_regs = &wcn3990_ctrl1, + .host_ie = &wcn3990_host_ie, + .wm_regs = &wcn3990_wm_reg, + .misc_regs = &wcn3990_misc_reg, + .wm_srcr = &wcn3990_wm_src_ring, + .wm_dstr = &wcn3990_wm_dst_ring, +}; + +struct ath10k_hw_ce_regs_addr_map qcax_src_ring = { + .msb = 0x00000010, + .lsb = 0x00000010, + .mask = 0x00010000, + .set = &ath10k_set_ring_byte, +}; + +struct ath10k_hw_ce_regs_addr_map qcax_dst_ring = { + .msb = 0x00000011, + .lsb = 0x00000011, + .mask = 0x00020000, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_regs_addr_map qcax_dmax = { + .msb = 0x0000000f, + .lsb = 0x00000000, + .mask = 0x0000ffff, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_ctrl1 qcax_ctrl1 = { + .addr = 0x00000010, + .hw_mask = 0x0007ffff, + .sw_mask = 0x0007ffff, + .hw_wr_mask = 0x00000000, + .sw_wr_mask = 0x0007ffff, + .reset_mask = 0xffffffff, + .reset = 0x00000080, + .src_ring = &qcax_src_ring, + .dst_ring = &qcax_dst_ring, + .dmax = &qcax_dmax, +}; + +struct ath10k_hw_ce_regs_addr_map qcax_cmd_halt_status = { + .msb = 0x00000003, + .lsb = 0x00000003, + .mask = 0x00000008, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_cmd_halt qcax_cmd_halt = { + .msb = 0x00000000, + .mask = 0x00000001, + .status_reset = 0x00000000, + .status = &qcax_cmd_halt_status, +}; + +struct ath10k_hw_ce_regs_addr_map qcax_host_ie_cc = { + .msb = 0x00000000, + .lsb = 0x00000000, + .mask = 0x00000001, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_host_ie qcax_host_ie = { + .copy_complete_reset = 0x00000000, + .copy_complete = &qcax_host_ie_cc, +}; + +struct ath10k_hw_ce_host_wm_regs qcax_wm_reg = { + .dstr_lmask = 0x00000010, + .dstr_hmask = 0x00000008, + .srcr_lmask = 0x00000004, + .srcr_hmask = 0x00000002, + .cc_mask = 0x00000001, + .wm_mask = 0x0000001E, + .addr = 0x00000030, +}; + +struct ath10k_hw_ce_misc_regs qcax_misc_reg = { + .axi_err = 0x00000400, + .dstr_add_err = 0x00000200, + .srcr_len_err = 0x00000100, + .dstr_mlen_vio = 0x00000080, + .dstr_overflow = 0x00000040, + .srcr_overflow = 0x00000020, + .err_mask = 0x000007E0, + .addr = 0x00000038, +}; + +struct ath10k_hw_ce_regs_addr_map qcax_src_wm_low = { + .msb = 0x0000001f, + .lsb = 0x00000010, + .mask = 0xffff0000, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_regs_addr_map qcax_src_wm_high = { + .msb = 0x0000000f, + .lsb = 0x00000000, + .mask = 0x0000ffff, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_src_ring = { + .addr = 0x0000004c, + .low_rst = 0x00000000, + .high_rst = 0x00000000, + .wm_low = &qcax_src_wm_low, + .wm_high = &qcax_src_wm_high, +}; + +struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_low = { + .lsb = 0x00000010, + .mask = 0xffff0000, + .set = &ath10k_set_ring_byte, +}; + +struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_high = { + .msb = 0x0000000f, + .lsb = 0x00000000, + .mask = 0x0000ffff, + .set = &ath10k_set_ring_byte, + .get = &ath10k_get_ring_byte, +}; + +struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_dst_ring = { + .addr = 0x00000050, + .low_rst = 0x00000000, + .high_rst = 0x00000000, + .wm_low = &qcax_dst_wm_low, + .wm_high = &qcax_dst_wm_high, +}; + +struct ath10k_hw_ce_regs qcax_ce_regs = { + .sr_base_addr = 0x00000000, + .sr_size_addr = 0x00000004, + .dr_base_addr = 0x00000008, + .dr_size_addr = 0x0000000c, + .ce_cmd_addr = 0x00000018, + .misc_ie_addr = 0x00000034, + .sr_wr_index_addr = 0x0000003c, + .dst_wr_index_addr = 0x00000040, + .current_srri_addr = 0x00000044, + .current_drri_addr = 0x00000048, + .host_ie_addr = 0x0000002c, + .ctrl1_regs = &qcax_ctrl1, + .cmd_halt = &qcax_cmd_halt, + .host_ie = &qcax_host_ie, + .wm_regs = &qcax_wm_reg, + .misc_regs = &qcax_misc_reg, + .wm_srcr = &qcax_wm_src_ring, + .wm_dstr = &qcax_wm_dst_ring, +}; + const struct ath10k_hw_values qca988x_values = { .rtc_state_val_on = 3, .ce_count = 8, diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index ce87f8112928..0f2422480c4e 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -268,6 +268,98 @@ extern const struct ath10k_hw_regs qca99x0_regs; extern const struct ath10k_hw_regs qca4019_regs; extern const struct ath10k_hw_regs wcn3990_regs; +struct ath10k_hw_ce_regs_addr_map { + u32 msb; + u32 lsb; + u32 mask; + unsigned int (*set)(unsigned int offset, + struct ath10k_hw_ce_regs_addr_map *addr_map); + unsigned int (*get)(unsigned int offset, + struct ath10k_hw_ce_regs_addr_map *addr_map); +}; + +struct ath10k_hw_ce_ctrl1 { + u32 addr; + u32 hw_mask; + u32 sw_mask; + u32 hw_wr_mask; + u32 sw_wr_mask; + u32 reset_mask; + u32 reset; + struct ath10k_hw_ce_regs_addr_map *src_ring; + struct ath10k_hw_ce_regs_addr_map *dst_ring; + struct ath10k_hw_ce_regs_addr_map *dmax; +}; + +struct ath10k_hw_ce_cmd_halt { + u32 status_reset; + u32 msb; + u32 mask; + struct ath10k_hw_ce_regs_addr_map *status; +}; + +struct ath10k_hw_ce_host_ie { + u32 copy_complete_reset; + struct ath10k_hw_ce_regs_addr_map *copy_complete; +}; + +struct ath10k_hw_ce_host_wm_regs { + u32 dstr_lmask; + u32 dstr_hmask; + u32 srcr_lmask; + u32 srcr_hmask; + u32 cc_mask; + u32 wm_mask; + u32 addr; +}; + +struct ath10k_hw_ce_misc_regs { + u32 axi_err; + u32 dstr_add_err; + u32 srcr_len_err; + u32 dstr_mlen_vio; + u32 dstr_overflow; + u32 srcr_overflow; + u32 err_mask; + u32 addr; +}; + +struct ath10k_hw_ce_dst_src_wm_regs { + u32 addr; + u32 low_rst; + u32 high_rst; + struct ath10k_hw_ce_regs_addr_map *wm_low; + struct ath10k_hw_ce_regs_addr_map *wm_high; +}; + +struct ath10k_hw_ce_regs { + u32 sr_base_addr; + u32 sr_size_addr; + u32 dr_base_addr; + u32 dr_size_addr; + u32 ce_cmd_addr; + u32 misc_ie_addr; + u32 sr_wr_index_addr; + u32 dst_wr_index_addr; + u32 current_srri_addr; + u32 current_drri_addr; + u32 ddr_addr_for_rri_low; + u32 ddr_addr_for_rri_high; + u32 ce_rri_low; + u32 ce_rri_high; + u32 host_ie_addr; + struct ath10k_hw_ce_host_wm_regs *wm_regs; + struct ath10k_hw_ce_misc_regs *misc_regs; + struct ath10k_hw_ce_ctrl1 *ctrl1_regs; + struct ath10k_hw_ce_cmd_halt *cmd_halt; + struct ath10k_hw_ce_host_ie *host_ie; + struct ath10k_hw_ce_dst_src_wm_regs *wm_srcr; + struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr; +}; + +extern struct ath10k_hw_ce_regs wcn3990_ce_regs; +extern struct ath10k_hw_ce_regs qcax_ce_regs; + extern struct fw_flag wcn3990_fw_flags; struct ath10k_hw_values { diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 072e008900e6..9e607b2fa2d4 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -669,18 +669,18 @@ static u32 ath10k_bus_pci_read32(struct ath10k *ar, u32 offset) return val; } -inline void ath10k_pci_write32(void *ar, u32 offset, u32 value) +inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - ar_pci->bus_ops->write32(ar, offset, value); + ar_pci->opaque_ctx.bus_ops->write32(ar, offset, value); } -inline u32 ath10k_pci_read32(void *ar, u32 offset) +inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - return ar_pci->bus_ops->read32(ar, offset); + return ar_pci->opaque_ctx.bus_ops->read32(ar, offset); } u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr) @@ -780,9 +780,9 @@ static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe) ATH10K_SKB_RXCB(skb)->paddr = paddr; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ar_pci->opaque_ctx.ce_lock); ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock); if (ret) { dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); @@ -806,9 +806,9 @@ static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe) if (!ce_pipe->dest_ring) return; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ar_pci->opaque_ctx.ce_lock); num = __ath10k_ce_rx_num_free_bufs(ce_pipe); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock); while (num >= 0) { ret = __ath10k_pci_rx_post_buf(pipe); @@ -886,7 +886,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, void *data_buf = NULL; int i; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ar_pci->opaque_ctx.ce_lock); ce_diag = ar_pci->ce_diag; @@ -987,7 +987,7 @@ done: dma_free_coherent(ar->dev, alloc_nbytes, data_buf, ce_data_base); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock); return ret; } @@ -1044,7 +1044,7 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, dma_addr_t ce_data_base = 0; int i; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ar_pci->opaque_ctx.ce_lock); ce_diag = ar_pci->ce_diag; @@ -1148,7 +1148,7 @@ done: ath10k_warn(ar, "failed to write diag value at 0x%x: %d\n", address, ret); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock); return ret; } @@ -1351,7 +1351,7 @@ int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, unsigned int write_index; int err, i = 0; - spin_lock_bh(&ar_pci->ce_lock); + spin_lock_bh(&ar_pci->opaque_ctx.ce_lock); nentries_mask = src_ring->nentries_mask; sw_index = src_ring->sw_index; @@ -1397,14 +1397,14 @@ int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, if (err) goto err; - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock); return 0; err: for (; i > 0; i--) __ath10k_ce_send_revert(ce_pipe); - spin_unlock_bh(&ar_pci->ce_lock); + spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock); return err; } @@ -1990,7 +1990,7 @@ static int ath10k_bus_get_num_banks(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - return ar_pci->bus_ops->get_num_banks(ar); + return ar_pci->opaque_ctx.bus_ops->get_num_banks(ar); } int ath10k_pci_init_config(struct ath10k *ar) @@ -2165,7 +2165,7 @@ int ath10k_pci_alloc_pipes(struct ath10k *ar) for (i = 0; i < CE_COUNT; i++) { pipe = &ar_pci->pipe_info[i]; - pipe->ce_hdl = &ar_pci->ce_states[i]; + pipe->ce_hdl = &ar_pci->opaque_ctx.ce_states[i]; pipe->pipe_num = i; pipe->hif_ce_state = ar; @@ -2792,6 +2792,7 @@ static int ath10k_pci_napi_poll(struct napi_struct *ctx, int budget) { struct ath10k *ar = container_of(ctx, struct ath10k, napi); int done = 0; + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); if (ath10k_pci_has_fw_crashed(ar)) { ath10k_pci_fw_crashed_clear(ar); @@ -2814,7 +2815,7 @@ static int ath10k_pci_napi_poll(struct napi_struct *ctx, int budget) * interrupts safer to check for pending interrupts for * immediate servicing. */ - if (CE_INTERRUPT_SUMMARY(ar)) { + if (CE_INTERRUPT_SUMMARY(ar, ar_opaque)) { napi_reschedule(ctx); goto out; } @@ -3132,7 +3133,7 @@ int ath10k_pci_setup_resource(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; - spin_lock_init(&ar_pci->ce_lock); + spin_lock_init(&ar_pci->opaque_ctx.ce_lock); spin_lock_init(&ar_pci->ps_lock); setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry, @@ -3243,7 +3244,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ar_pci->ar = ar; ar->dev_id = pci_dev->device; ar_pci->pci_ps = pci_ps; - ar_pci->bus_ops = &ath10k_pci_bus_ops; + ar_pci->opaque_ctx.bus_ops = &ath10k_pci_bus_ops; ar_pci->pci_soft_reset = pci_soft_reset; ar_pci->pci_hard_reset = pci_hard_reset; @@ -3252,14 +3253,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ar->id.subsystem_vendor = pdev->subsystem_vendor; ar->id.subsystem_device = pdev->subsystem_device; - spin_lock_init(&ar_pci->ce_lock); spin_lock_init(&ar_pci->ps_lock); - - ar->bus_write32 = ath10k_pci_write32; - ar->bus_read32 = ath10k_pci_read32; - ar->ce_lock = ar_pci->ce_lock; - ar->ce_states = ar_pci->ce_states; - setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry, (unsigned long)ar); setup_timer(&ar_pci->ps_timer, ath10k_pci_ps_timer, diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 06d0bd3993d3..22730c700af3 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -155,12 +155,6 @@ struct ath10k_pci_supp_chip { u32 rev_id; }; -struct ath10k_bus_ops { - u32 (*read32)(struct ath10k *ar, u32 offset); - void (*write32)(struct ath10k *ar, u32 offset, u32 value); - int (*get_num_banks)(struct ath10k *ar); -}; - enum ath10k_pci_irq_mode { ATH10K_PCI_IRQ_AUTO = 0, ATH10K_PCI_IRQ_LEGACY = 1, @@ -168,6 +162,7 @@ enum ath10k_pci_irq_mode { }; struct ath10k_pci { + struct bus_opaque opaque_ctx; struct pci_dev *pdev; struct device *dev; struct ath10k *ar; @@ -182,11 +177,6 @@ struct ath10k_pci { /* Copy Engine used for Diagnostic Accesses */ struct ath10k_ce_pipe *ce_diag; - /* FIXME: document what this really protects */ - spinlock_t ce_lock; - - /* Map CE id to ce_state */ - struct ath10k_ce_pipe ce_states[CE_COUNT_MAX]; struct timer_list rx_post_retry; /* Due to HW quirks it is recommended to disable ASPM during device @@ -230,8 +220,6 @@ struct ath10k_pci { */ bool pci_ps; - const struct ath10k_bus_ops *bus_ops; - /* Chip specific pci reset routine used to do a safe reset */ int (*pci_soft_reset)(struct ath10k *ar); @@ -263,11 +251,11 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) /* Wait up to this many Ms for a Diagnostic Access CE operation to complete */ #define DIAG_ACCESS_CE_TIMEOUT_MS 10 -void ath10k_pci_write32(void *ar, u32 offset, u32 value); +void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value); void ath10k_pci_soc_write32(struct ath10k *ar, u32 addr, u32 val); void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val); -u32 ath10k_pci_read32(void *ar, u32 offset); +u32 ath10k_pci_read32(struct ath10k *ar, u32 offset); u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr); u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr); diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 6c8797d5e5fc..081e44b3277a 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -413,9 +413,9 @@ static struct ath10k_shadow_reg_cfg target_shadow_reg_cfg_map[] = { { 11, WCN3990_DST_WR_INDEX_OFFSET}, }; -void ath10k_snoc_write32(void *ar, u32 offset, u32 value) +void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value) { - struct ath10k_snoc *ar_snoc = ath10k_snoc_priv((struct ath10k *)ar); + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); if (!ar_snoc) return; @@ -423,9 +423,9 @@ void ath10k_snoc_write32(void *ar, u32 offset, u32 value) iowrite32(value, ar_snoc->mem + offset); } -u32 ath10k_snoc_read32(void *ar, u32 offset) +u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset) { - struct ath10k_snoc *ar_snoc = ath10k_snoc_priv((struct ath10k *)ar); + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); u32 val; if (!ar_snoc) @@ -462,9 +462,9 @@ static int __ath10k_snoc_rx_post_buf(struct ath10k_snoc_pipe *pipe) ATH10K_SKB_RXCB(skb)->paddr = paddr; - spin_lock_bh(&ar_snoc->ce_lock); + spin_lock_bh(&ar_snoc->opaque_ctx.ce_lock); ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr); - spin_unlock_bh(&ar_snoc->ce_lock); + spin_unlock_bh(&ar_snoc->opaque_ctx.ce_lock); if (ret) { dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); @@ -488,9 +488,9 @@ static void ath10k_snoc_rx_post_pipe(struct ath10k_snoc_pipe *pipe) if (!ce_pipe->dest_ring) return; - spin_lock_bh(&ar_snoc->ce_lock); + spin_lock_bh(&ar_snoc->opaque_ctx.ce_lock); num = __ath10k_ce_rx_num_free_bufs(ce_pipe); - spin_unlock_bh(&ar_snoc->ce_lock); + spin_unlock_bh(&ar_snoc->opaque_ctx.ce_lock); while (num--) { ret = __ath10k_snoc_rx_post_buf(pipe); if (ret) { @@ -638,7 +638,7 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id, snoc_pipe = &ar_snoc->pipe_info[pipe_id]; ce_pipe = snoc_pipe->ce_hdl; src_ring = ce_pipe->src_ring; - spin_lock_bh(&ar_snoc->ce_lock); + spin_lock_bh(&ar_snoc->opaque_ctx.ce_lock); nentries_mask = src_ring->nentries_mask; sw_index = src_ring->sw_index; @@ -678,14 +678,14 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id, if (err) goto err; - spin_unlock_bh(&ar_snoc->ce_lock); + spin_unlock_bh(&ar_snoc->opaque_ctx.ce_lock); return 0; err: for (; i > 0; i--) __ath10k_ce_send_revert(ce_pipe); - spin_unlock_bh(&ar_snoc->ce_lock); + spin_unlock_bh(&ar_snoc->opaque_ctx.ce_lock); return err; } @@ -882,7 +882,7 @@ static int ath10k_snoc_alloc_pipes(struct ath10k *ar) for (i = 0; i < CE_COUNT; i++) { pipe = &ar_snoc->pipe_info[i]; - pipe->ce_hdl = &ar_snoc->ce_states[i]; + pipe->ce_hdl = &ar_snoc->opaque_ctx.ce_states[i]; pipe->pipe_num = i; pipe->hif_ce_state = ar; @@ -1184,6 +1184,11 @@ static const struct ath10k_hif_ops ath10k_snoc_hif_ops = { .write32 = ath10k_snoc_write32, }; +static const struct ath10k_bus_ops ath10k_snoc_bus_ops = { + .read32 = ath10k_snoc_read32, + .write32 = ath10k_snoc_write32, +}; + static int ath10k_snoc_probe(struct platform_device *pdev) { int ret; @@ -1210,11 +1215,8 @@ static int ath10k_snoc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ar); ar_snoc->ar = ar; - spin_lock_init(&ar_snoc->ce_lock); - ar->bus_write32 = ath10k_snoc_write32; - ar->bus_read32 = ath10k_snoc_read32; - ar->ce_lock = ar_snoc->ce_lock; - ar->ce_states = ar_snoc->ce_states; + spin_lock_init(&ar_snoc->opaque_ctx.ce_lock); + ar_snoc->opaque_ctx.bus_ops = &ath10k_snoc_bus_ops; ath10k_snoc_resource_init(ar); ar->target_version = ATH10K_HW_WCN3990; diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h index 1754a3e91a00..0a5f5bff37ec 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.h +++ b/drivers/net/wireless/ath/ath10k/snoc.h @@ -103,6 +103,7 @@ struct ath10k_target_info { * @is_driver_probed: flag to indicate driver state */ struct ath10k_snoc { + struct bus_opaque opaque_ctx; struct platform_device *dev; struct ath10k *ar; void __iomem *mem; @@ -110,9 +111,6 @@ struct ath10k_snoc { struct ath10k_target_info target_info; size_t mem_len; struct ath10k_snoc_pipe pipe_info[CE_COUNT_MAX]; - /* protects CE info */ - spinlock_t ce_lock; - struct ath10k_ce_pipe ce_states[CE_COUNT_MAX]; struct timer_list rx_post_retry; u32 ce_irqs[CE_COUNT_MAX]; u32 *vaddr_rri_on_ddr; @@ -191,10 +189,10 @@ static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar) return (struct ath10k_snoc *)ar->drv_priv; } -void ath10k_snoc_write32(void *ar, u32 offset, u32 value); +void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value); void ath10k_snoc_soc_write32(struct ath10k *ar, u32 addr, u32 val); void ath10k_snoc_reg_write32(struct ath10k *ar, u32 addr, u32 val); -u32 ath10k_snoc_read32(void *ar, u32 offset); +u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset); u32 ath10k_snoc_soc_read32(struct ath10k *ar, u32 addr); u32 ath10k_snoc_reg_read32(struct ath10k *ar, u32 addr); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 8b4561e8ce1a..ef493271c712 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -4176,7 +4176,7 @@ static void ath9k_hw_ar9300_set_board_values(struct ath_hw *ah, if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah) && !AR_SREV_9531(ah)) ar9003_hw_internal_regulator_apply(ah); ar9003_hw_apply_tuning_caps(ah); - ar9003_hw_apply_minccapwr_thresh(ah, chan); + ar9003_hw_apply_minccapwr_thresh(ah, is2ghz); ar9003_hw_txend_to_xpa_off_apply(ah, is2ghz); ar9003_hw_thermometer_apply(ah); ar9003_hw_thermo_cal_apply(ah); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 1bdeacf7b257..bc70ce62bc03 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -869,8 +869,8 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE); - hw->wiphy->iface_combinations = if_comb; - hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + hw->wiphy->iface_combinations = if_comb; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); } hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5cc0ddc254eb..7be31f27266c 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1550,13 +1550,13 @@ static int ath9k_sta_state(struct ieee80211_hw *hw, struct ath_common *common = ath9k_hw_common(sc->sc_ah); int ret = 0; - if (old_state == IEEE80211_STA_AUTH && - new_state == IEEE80211_STA_ASSOC) { + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) { ret = ath9k_sta_add(hw, vif, sta); ath_dbg(common, CONFIG, "Add station: %pM\n", sta->addr); - } else if (old_state == IEEE80211_STA_ASSOC && - new_state == IEEE80211_STA_AUTH) { + } else if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST) { ret = ath9k_sta_remove(hw, vif, sta); ath_dbg(common, CONFIG, "Remove station: %pM\n", sta->addr); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 75ae474367f9..fe6d5ab7f95b 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -705,9 +705,6 @@ static int wil_target_reset(struct wil6210_priv *wil) wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN | BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC); - /* Enable fix for PCIe HW bug, set "No snoop" for RX transactions */ - wil_s(wil, RGF_DMA_PEDI_DIF, BIT_DMA_WR_CMD_ATTR_NO_SNOOP); - wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY); return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 410a6645d316..59cef6c69fe8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -726,8 +726,10 @@ int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev, return -ENOMEM; err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, glom_skb); - if (err) + if (err) { + brcmu_pkt_buf_free_skb(glom_skb); goto done; + } skb_queue_walk(pktq, skb) { memcpy(skb->data, glom_skb->data, skb->len); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index deb5f78dcacc..70a6985334d5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -2408,7 +2408,7 @@ static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si) WL_BSS_INFO_MAX); if (err) { brcmf_err("Failed to get bss info (%d)\n", err); - return; + goto out_kfree; } si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period); @@ -2420,6 +2420,9 @@ static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si) si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + +out_kfree: + kfree(buf); } static s32 @@ -4099,7 +4102,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, (u8 *)&settings->beacon.head[ie_offset], settings->beacon.head_len - ie_offset, WLAN_EID_SSID); - if (!ssid_ie) + if (!ssid_ie || ssid_ie->len > IEEE80211_MAX_SSID_LEN) return -EINVAL; memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/brcm80211/brcmsmac/dma.c index 796f5f9d5d5a..b7df576bb84d 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.c @@ -1079,8 +1079,10 @@ bool dma_rxfill(struct dma_pub *pub) pa = dma_map_single(di->dmadev, p->data, di->rxbufsize, DMA_FROM_DEVICE); - if (dma_mapping_error(di->dmadev, pa)) + if (dma_mapping_error(di->dmadev, pa)) { + brcmu_pkt_buf_free_skb(p); return false; + } /* save the free packet pointer */ di->rxp[rxout] = p; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.c b/drivers/net/wireless/brcm80211/brcmsmac/stf.c index dd9162722495..0ab865de1491 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/stf.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/stf.c @@ -87,7 +87,7 @@ void brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, u16 *ss_algo_channel, u16 chanspec) { - struct tx_power power; + struct tx_power power = { }; u8 siso_mcs_id, cdd_mcs_id, stbc_mcs_id; /* Clear previous settings */ diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c index 93bdf684babe..ae047ab7a4df 100644 --- a/drivers/net/wireless/iwlegacy/3945.c +++ b/drivers/net/wireless/iwlegacy/3945.c @@ -1019,12 +1019,13 @@ il3945_hw_txq_ctx_free(struct il_priv *il) int txq_id; /* Tx queues */ - if (il->txq) + if (il->txq) { for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) if (txq_id == IL39_CMD_QUEUE_NUM) il_cmd_queue_free(il); else il_tx_queue_free(il, txq_id); + } /* free tx queue structure */ il_free_txq_mem(il); diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c index 20e6aa910700..c148085742a0 100644 --- a/drivers/net/wireless/iwlwifi/dvm/calib.c +++ b/drivers/net/wireless/iwlwifi/dvm/calib.c @@ -901,7 +901,7 @@ static void iwlagn_gain_computation(struct iwl_priv *priv, /* bound gain by 2 bits value max, 3rd bit is sign */ data->delta_gain_code[i] = min(abs(delta_g), - (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); + (s32) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); if (delta_g < 0) /* diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index 610c442c7ab2..9584f950fd2f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -935,7 +935,8 @@ int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id) } mvm->fw_dbg_conf = conf_id; - return ret; + + return 0; } static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index f96ab2f4b90e..ce12717e656a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -3992,8 +3992,8 @@ static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx, if (idx != 0) return -ENOENT; - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS)) + if (!fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS)) return -ENOENT; mutex_lock(&mvm->mutex); @@ -4039,8 +4039,8 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS)) + if (!fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS)) return; /* if beacon filtering isn't on mac80211 does it anyway */ diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c index b0f59fdd287c..d7d72adb6343 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sf.c +++ b/drivers/net/wireless/iwlwifi/mvm/sf.c @@ -215,7 +215,7 @@ static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id, enum iwl_sf_state new_state) { struct iwl_sf_cfg_cmd sf_cmd = { - .state = cpu_to_le32(SF_FULL_ON), + .state = cpu_to_le32(new_state), }; struct ieee80211_sta *sta; int ret = 0; diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index d58c094f2f04..f7e6a09926dd 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -475,48 +475,64 @@ static const struct pci_device_id iwl_hw_card_ids[] = { MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); #ifdef CONFIG_ACPI -#define SPL_METHOD "SPLC" -#define SPL_DOMAINTYPE_MODULE BIT(0) -#define SPL_DOMAINTYPE_WIFI BIT(1) -#define SPL_DOMAINTYPE_WIGIG BIT(2) -#define SPL_DOMAINTYPE_RFEM BIT(3) +#define ACPI_SPLC_METHOD "SPLC" +#define ACPI_SPLC_DOMAIN_WIFI (0x07) -static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx) +static u64 splc_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splc) { - union acpi_object *limits, *domain_type, *power_limit; - - if (splx->type != ACPI_TYPE_PACKAGE || - splx->package.count != 2 || - splx->package.elements[0].type != ACPI_TYPE_INTEGER || - splx->package.elements[0].integer.value != 0) { - IWL_ERR(trans, "Unsupported splx structure\n"); + union acpi_object *data_pkg, *dflt_pwr_limit; + int i; + + /* We need at least two elements, one for the revision and one + * for the data itself. Also check that the revision is + * supported (currently only revision 0). + */ + if (splc->type != ACPI_TYPE_PACKAGE || + splc->package.count < 2 || + splc->package.elements[0].type != ACPI_TYPE_INTEGER || + splc->package.elements[0].integer.value != 0) { + IWL_DEBUG_INFO(trans, + "Unsupported structure returned by the SPLC method. Ignoring.\n"); return 0; } - limits = &splx->package.elements[1]; - if (limits->type != ACPI_TYPE_PACKAGE || - limits->package.count < 2 || - limits->package.elements[0].type != ACPI_TYPE_INTEGER || - limits->package.elements[1].type != ACPI_TYPE_INTEGER) { - IWL_ERR(trans, "Invalid limits element\n"); - return 0; + /* loop through all the packages to find the one for WiFi */ + for (i = 1; i < splc->package.count; i++) { + union acpi_object *domain; + + data_pkg = &splc->package.elements[i]; + + /* Skip anything that is not a package with the right + * amount of elements (i.e. at least 2 integers). + */ + if (data_pkg->type != ACPI_TYPE_PACKAGE || + data_pkg->package.count < 2 || + data_pkg->package.elements[0].type != ACPI_TYPE_INTEGER || + data_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) + continue; + + domain = &data_pkg->package.elements[0]; + if (domain->integer.value == ACPI_SPLC_DOMAIN_WIFI) + break; + + data_pkg = NULL; } - domain_type = &limits->package.elements[0]; - power_limit = &limits->package.elements[1]; - if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) { - IWL_DEBUG_INFO(trans, "WiFi power is not limited\n"); + if (!data_pkg) { + IWL_DEBUG_INFO(trans, + "No element for the WiFi domain returned by the SPLC method.\n"); return 0; } - return power_limit->integer.value; + dflt_pwr_limit = &data_pkg->package.elements[1]; + return dflt_pwr_limit->integer.value; } static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) { acpi_handle pxsx_handle; acpi_handle handle; - struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_buffer splc = {ACPI_ALLOCATE_BUFFER, NULL}; acpi_status status; pxsx_handle = ACPI_HANDLE(&pdev->dev); @@ -527,23 +543,24 @@ static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) } /* Get the method's handle */ - status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle); + status = acpi_get_handle(pxsx_handle, (acpi_string)ACPI_SPLC_METHOD, + &handle); if (ACPI_FAILURE(status)) { - IWL_DEBUG_INFO(trans, "SPL method not found\n"); + IWL_DEBUG_INFO(trans, "SPLC method not found\n"); return; } /* Call SPLC with no arguments */ - status = acpi_evaluate_object(handle, NULL, NULL, &splx); + status = acpi_evaluate_object(handle, NULL, NULL, &splc); if (ACPI_FAILURE(status)) { IWL_ERR(trans, "SPLC invocation failed (0x%x)\n", status); return; } - trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer); + trans->dflt_pwr_limit = splc_get_pwr_limit(trans, splc.pointer); IWL_DEBUG_INFO(trans, "Default power limit set to %lld\n", trans->dflt_pwr_limit); - kfree(splx.pointer); + kfree(splc.pointer); } #else /* CONFIG_ACPI */ diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index a8c8a4a7420b..8dfe6b2bc703 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1508,9 +1508,9 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, /* start the TFD with the scratchbuf */ scratch_size = min_t(int, copy_size, IWL_HCMD_SCRATCHBUF_SIZE); - memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size); + memcpy(&txq->scratchbufs[idx], &out_cmd->hdr, scratch_size); iwl_pcie_txq_build_tfd(trans, txq, - iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr), + iwl_pcie_get_scratchbuf_dma(txq, idx), scratch_size, true); /* map first command fragment, if any remains */ diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 4073116e6e9f..c3331d6201c3 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2144,8 +2144,9 @@ done: is_scanning_required = 1; } else { mwifiex_dbg(priv->adapter, MSG, - "info: trying to associate to '%s' bssid %pM\n", - (char *)req_ssid.ssid, bss->bssid); + "info: trying to associate to '%.*s' bssid %pM\n", + req_ssid.ssid_len, (char *)req_ssid.ssid, + bss->bssid); memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN); break; } @@ -2202,8 +2203,8 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, } mwifiex_dbg(adapter, INFO, - "info: Trying to associate to %s and bssid %pM\n", - (char *)sme->ssid, sme->bssid); + "info: Trying to associate to %.*s and bssid %pM\n", + (int)sme->ssid_len, (char *)sme->ssid, sme->bssid); ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, priv->bss_mode, sme->channel, sme, 0); @@ -2333,8 +2334,8 @@ mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, } mwifiex_dbg(priv->adapter, MSG, - "info: trying to join to %s and bssid %pM\n", - (char *)params->ssid, params->bssid); + "info: trying to join to %.*s and bssid %pM\n", + params->ssid_len, (char *)params->ssid, params->bssid); mwifiex_set_ibss_params(priv, params); diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 3cda1f956f0b..6378dfd3b4e8 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -661,9 +661,8 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN, sizeof(priv->assoc_rsp_buf)); - memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size); - assoc_rsp->a_id = cpu_to_le16(aid); + memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size); if (status_code) { priv->adapter->dbg.num_cmd_assoc_failure++; diff --git a/drivers/net/wireless/realtek/rtlwifi/regd.c b/drivers/net/wireless/realtek/rtlwifi/regd.c index 5be34118e0af..f67e7e5b13e1 100644 --- a/drivers/net/wireless/realtek/rtlwifi/regd.c +++ b/drivers/net/wireless/realtek/rtlwifi/regd.c @@ -345,9 +345,9 @@ static const struct ieee80211_regdomain *_rtl_regdomain_select( return &rtl_regdom_no_midband; case COUNTRY_CODE_IC: return &rtl_regdom_11; - case COUNTRY_CODE_ETSI: case COUNTRY_CODE_TELEC_NETGEAR: return &rtl_regdom_60_64; + case COUNTRY_CODE_ETSI: case COUNTRY_CODE_SPAIN: case COUNTRY_CODE_FRANCE: case COUNTRY_CODE_ISRAEL: @@ -406,6 +406,8 @@ static u8 channel_plan_to_country_code(u8 channelplan) return COUNTRY_CODE_WORLD_WIDE_13; case 0x22: return COUNTRY_CODE_IC; + case 0x25: + return COUNTRY_CODE_ETSI; case 0x32: return COUNTRY_CODE_TELEC_NETGEAR; case 0x41: diff --git a/drivers/nfc/fdp/fdp.c b/drivers/nfc/fdp/fdp.c index ccb07a1b153d..23e53780728b 100644 --- a/drivers/nfc/fdp/fdp.c +++ b/drivers/nfc/fdp/fdp.c @@ -352,7 +352,7 @@ static int fdp_nci_patch_otp(struct nci_dev *ndev) { struct fdp_nci_info *info = nci_get_drvdata(ndev); struct device *dev = &info->phy->i2c_dev->dev; - u8 conn_id; + int conn_id; int r = 0; if (info->otp_version >= info->otp_patch_version) @@ -423,7 +423,7 @@ static int fdp_nci_patch_ram(struct nci_dev *ndev) { struct fdp_nci_info *info = nci_get_drvdata(ndev); struct device *dev = &info->phy->i2c_dev->dev; - u8 conn_id; + int conn_id; int r = 0; if (info->ram_version >= info->ram_patch_version) diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c index 83deda4bb4d6..6f9563a96488 100644 --- a/drivers/nfc/mei_phy.c +++ b/drivers/nfc/mei_phy.c @@ -133,7 +133,7 @@ static int mei_nfc_if_version(struct nfc_mei_phy *phy) return -ENOMEM; bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, if_version_length); - if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { + if (bytes_recv < 0 || bytes_recv < if_version_length) { pr_err("Could not read IF version\n"); r = -EIO; goto err; diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index cffd4d31d3c1..22528d051845 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -127,8 +127,12 @@ static int __init __reserved_mem_alloc_size(unsigned long node, } /* Need adjust the alignment to satisfy the CMA requirement */ - if (IS_ENABLED(CONFIG_CMA) && of_flat_dt_is_compatible(node, "shared-dma-pool")) - align = max(align, (phys_addr_t)PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order)); + if (IS_ENABLED(CONFIG_CMA) && of_flat_dt_is_compatible(node, "shared-dma-pool")) { + unsigned long order = + max_t(unsigned long, MAX_ORDER - 1, pageblock_order); + + align = max(align, (phys_addr_t)PAGE_SIZE << order); + } prop = of_get_flat_dt_prop(node, "alloc-ranges", &len); if (prop) { diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c index cd105a0bf5d1..584ad96c703f 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.c @@ -2534,13 +2534,14 @@ static ssize_t msm_pcie_cmd_debug(struct file *file, char str[MAX_MSG_LEN]; unsigned int testcase = 0; int i; + u32 size = sizeof(str) < count ? sizeof(str) : count; - memset(str, 0, sizeof(str)); - ret = copy_from_user(str, buf, sizeof(str)); + memset(str, 0, size); + ret = copy_from_user(str, buf, size); if (ret) return -EFAULT; - for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i) + for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i) testcase = (testcase * 10) + (str[i] - '0'); if (!rc_sel) @@ -2569,13 +2570,14 @@ static ssize_t msm_pcie_set_rc_sel(struct file *file, char str[MAX_MSG_LEN]; int i; u32 new_rc_sel = 0; + u32 size = sizeof(str) < count ? sizeof(str) : count; - memset(str, 0, sizeof(str)); - ret = copy_from_user(str, buf, sizeof(str)); + memset(str, 0, size); + ret = copy_from_user(str, buf, size); if (ret) return -EFAULT; - for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i) + for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i) new_rc_sel = (new_rc_sel * 10) + (str[i] - '0'); if ((!new_rc_sel) || (new_rc_sel > rc_sel_max)) { @@ -2612,13 +2614,14 @@ static ssize_t msm_pcie_set_base_sel(struct file *file, int i; u32 new_base_sel = 0; char *base_sel_name; + u32 size = sizeof(str) < count ? sizeof(str) : count; - memset(str, 0, sizeof(str)); - ret = copy_from_user(str, buf, sizeof(str)); + memset(str, 0, size); + ret = copy_from_user(str, buf, size); if (ret) return -EFAULT; - for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i) + for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i) new_base_sel = (new_base_sel * 10) + (str[i] - '0'); if (!new_base_sel || new_base_sel > 5) { @@ -2713,14 +2716,15 @@ static ssize_t msm_pcie_set_wr_offset(struct file *file, unsigned long ret; char str[MAX_MSG_LEN]; int i; + u32 size = sizeof(str) < count ? sizeof(str) : count; - memset(str, 0, sizeof(str)); - ret = copy_from_user(str, buf, sizeof(str)); + memset(str, 0, size); + ret = copy_from_user(str, buf, size); if (ret) return -EFAULT; wr_offset = 0; - for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i) + for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i) wr_offset = (wr_offset * 10) + (str[i] - '0'); pr_alert("PCIe: wr_offset is now 0x%x\n", wr_offset); @@ -2739,14 +2743,15 @@ static ssize_t msm_pcie_set_wr_mask(struct file *file, unsigned long ret; char str[MAX_MSG_LEN]; int i; + u32 size = sizeof(str) < count ? sizeof(str) : count; - memset(str, 0, sizeof(str)); - ret = copy_from_user(str, buf, sizeof(str)); + memset(str, 0, size); + ret = copy_from_user(str, buf, size); if (ret) return -EFAULT; wr_mask = 0; - for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i) + for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i) wr_mask = (wr_mask * 10) + (str[i] - '0'); pr_alert("PCIe: wr_mask is now 0x%x\n", wr_mask); @@ -2764,14 +2769,15 @@ static ssize_t msm_pcie_set_wr_value(struct file *file, unsigned long ret; char str[MAX_MSG_LEN]; int i; + u32 size = sizeof(str) < count ? sizeof(str) : count; - memset(str, 0, sizeof(str)); - ret = copy_from_user(str, buf, sizeof(str)); + memset(str, 0, size); + ret = copy_from_user(str, buf, size); if (ret) return -EFAULT; wr_value = 0; - for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i) + for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i) wr_value = (wr_value * 10) + (str[i] - '0'); pr_alert("PCIe: wr_value is now 0x%x\n", wr_value); @@ -2890,14 +2896,15 @@ static ssize_t msm_pcie_set_corr_counter_limit(struct file *file, unsigned long ret; char str[MAX_MSG_LEN]; int i; + u32 size = sizeof(str) < count ? sizeof(str) : count; - memset(str, 0, sizeof(str)); - ret = copy_from_user(str, buf, sizeof(str)); + memset(str, 0, size); + ret = copy_from_user(str, buf, size); if (ret) return -EFAULT; corr_counter_limit = 0; - for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i) + for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i) corr_counter_limit = (corr_counter_limit * 10) + (str[i] - '0'); pr_info("PCIe: corr_counter_limit is now %lu\n", corr_counter_limit); diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index 182224acedbe..58f1419a68ae 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -283,20 +283,6 @@ out: return 0; } -static struct pci_dev *pcie_find_root_port(struct pci_dev *dev) -{ - while (1) { - if (!pci_is_pcie(dev)) - break; - if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) - return dev; - if (!dev->bus->self) - break; - dev = dev->bus->self; - } - return NULL; -} - static int find_aer_device_iter(struct device *device, void *data) { struct pcie_device **result = data; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9757cf9037a2..b5843c255263 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1415,6 +1415,21 @@ static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp) dev_warn(&dev->dev, "PCI-X settings not supported\n"); } +static bool pcie_root_rcb_set(struct pci_dev *dev) +{ + struct pci_dev *rp = pcie_find_root_port(dev); + u16 lnkctl; + + if (!rp) + return false; + + pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &lnkctl); + if (lnkctl & PCI_EXP_LNKCTL_RCB) + return true; + + return false; +} + static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) { int pos; @@ -1444,9 +1459,20 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) ~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or); /* Initialize Link Control Register */ - if (pcie_cap_has_lnkctl(dev)) + if (pcie_cap_has_lnkctl(dev)) { + + /* + * If the Root Port supports Read Completion Boundary of + * 128, set RCB to 128. Otherwise, clear it. + */ + hpp->pci_exp_lnkctl_and |= PCI_EXP_LNKCTL_RCB; + hpp->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB; + if (pcie_root_rcb_set(dev)) + hpp->pci_exp_lnkctl_or |= PCI_EXP_LNKCTL_RCB; + pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL, ~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or); + } /* Find Advanced Error Reporting Enhanced Capability */ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 42774bc39786..254192b5dad1 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3136,6 +3136,7 @@ static void quirk_no_bus_reset(struct pci_dev *dev) DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0030, quirk_no_bus_reset); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0032, quirk_no_bus_reset); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003c, quirk_no_bus_reset); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0033, quirk_no_bus_reset); static void quirk_no_pm_reset(struct pci_dev *dev) { diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 13b79438af1c..4c75b4d392c6 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -972,6 +972,7 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu) if (i > 0 && spi != using_spi) { pr_err("PPI/SPI IRQ type mismatch for %s!\n", dn->name); + of_node_put(dn); kfree(irqs); return -EINVAL; } diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 4e377599d266..a009ae34c5ef 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1564,12 +1564,15 @@ static int chv_pinctrl_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP -static int chv_pinctrl_suspend(struct device *dev) +static int chv_pinctrl_suspend_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct chv_pinctrl *pctrl = platform_get_drvdata(pdev); + unsigned long flags; int i; + raw_spin_lock_irqsave(&chv_lock, flags); + pctrl->saved_intmask = readl(pctrl->regs + CHV_INTMASK); for (i = 0; i < pctrl->community->npins; i++) { @@ -1590,15 +1593,20 @@ static int chv_pinctrl_suspend(struct device *dev) ctx->padctrl1 = readl(reg); } + raw_spin_unlock_irqrestore(&chv_lock, flags); + return 0; } -static int chv_pinctrl_resume(struct device *dev) +static int chv_pinctrl_resume_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct chv_pinctrl *pctrl = platform_get_drvdata(pdev); + unsigned long flags; int i; + raw_spin_lock_irqsave(&chv_lock, flags); + /* * Mask all interrupts before restoring per-pin configuration * registers because we don't know in which state BIOS left them @@ -1643,12 +1651,15 @@ static int chv_pinctrl_resume(struct device *dev) chv_writel(0xffff, pctrl->regs + CHV_INTSTAT); chv_writel(pctrl->saved_intmask, pctrl->regs + CHV_INTMASK); + raw_spin_unlock_irqrestore(&chv_lock, flags); + return 0; } #endif static const struct dev_pm_ops chv_pinctrl_pm_ops = { - SET_LATE_SYSTEM_SLEEP_PM_OPS(chv_pinctrl_suspend, chv_pinctrl_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(chv_pinctrl_suspend_noirq, + chv_pinctrl_resume_noirq) }; static const struct acpi_device_id chv_pinctrl_acpi_match[] = { diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c index b3235fd2950c..271cca63e9bd 100644 --- a/drivers/pinctrl/pinctrl-at91-pio4.c +++ b/drivers/pinctrl/pinctrl-at91-pio4.c @@ -1002,7 +1002,7 @@ static int atmel_pinctrl_probe(struct platform_device *pdev) atmel_pioctrl->irqs[i] = res->start; irq_set_chained_handler(res->start, atmel_gpio_irq_handler); irq_set_handler_data(res->start, atmel_pioctrl); - dev_dbg(dev, "bank %i: hwirq=%u\n", i, res->start); + dev_dbg(dev, "bank %i: irq=%pr\n", i, res); } atmel_pioctrl->irq_domain = irq_domain_add_linear(dev->of_node, diff --git a/drivers/pinctrl/pinctrl-pistachio.c b/drivers/pinctrl/pinctrl-pistachio.c index 6b1a47f8c096..98a459b1c095 100644 --- a/drivers/pinctrl/pinctrl-pistachio.c +++ b/drivers/pinctrl/pinctrl-pistachio.c @@ -809,17 +809,17 @@ static const struct pistachio_pin_group pistachio_groups[] = { PADS_FUNCTION_SELECT2, 12, 0x3), MFIO_MUX_PIN_GROUP(83, MIPS_PLL_LOCK, MIPS_TRACE_DATA, USB_DEBUG, PADS_FUNCTION_SELECT2, 14, 0x3), - MFIO_MUX_PIN_GROUP(84, SYS_PLL_LOCK, MIPS_TRACE_DATA, USB_DEBUG, + MFIO_MUX_PIN_GROUP(84, AUDIO_PLL_LOCK, MIPS_TRACE_DATA, USB_DEBUG, PADS_FUNCTION_SELECT2, 16, 0x3), - MFIO_MUX_PIN_GROUP(85, WIFI_PLL_LOCK, MIPS_TRACE_DATA, SDHOST_DEBUG, + MFIO_MUX_PIN_GROUP(85, RPU_V_PLL_LOCK, MIPS_TRACE_DATA, SDHOST_DEBUG, PADS_FUNCTION_SELECT2, 18, 0x3), - MFIO_MUX_PIN_GROUP(86, BT_PLL_LOCK, MIPS_TRACE_DATA, SDHOST_DEBUG, + MFIO_MUX_PIN_GROUP(86, RPU_L_PLL_LOCK, MIPS_TRACE_DATA, SDHOST_DEBUG, PADS_FUNCTION_SELECT2, 20, 0x3), - MFIO_MUX_PIN_GROUP(87, RPU_V_PLL_LOCK, DREQ2, SOCIF_DEBUG, + MFIO_MUX_PIN_GROUP(87, SYS_PLL_LOCK, DREQ2, SOCIF_DEBUG, PADS_FUNCTION_SELECT2, 22, 0x3), - MFIO_MUX_PIN_GROUP(88, RPU_L_PLL_LOCK, DREQ3, SOCIF_DEBUG, + MFIO_MUX_PIN_GROUP(88, WIFI_PLL_LOCK, DREQ3, SOCIF_DEBUG, PADS_FUNCTION_SELECT2, 24, 0x3), - MFIO_MUX_PIN_GROUP(89, AUDIO_PLL_LOCK, DREQ4, DREQ5, + MFIO_MUX_PIN_GROUP(89, BT_PLL_LOCK, DREQ4, DREQ5, PADS_FUNCTION_SELECT2, 26, 0x3), PIN_GROUP(TCK, "tck"), PIN_GROUP(TRSTN, "trstn"), diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c index 55083d278bb1..51fbf85301be 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c @@ -485,12 +485,12 @@ static const struct sunxi_desc_pin sun8i_a23_pins[] = { SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8), SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "uart2"), /* RTS */ + SUNXI_FUNCTION(0x2, "uart1"), /* RTS */ SUNXI_FUNCTION_IRQ_BANK(0x4, 2, 8)), /* PG_EINT8 */ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9), SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "uart2"), /* CTS */ + SUNXI_FUNCTION(0x2, "uart1"), /* CTS */ SUNXI_FUNCTION_IRQ_BANK(0x4, 2, 9)), /* PG_EINT9 */ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10), SUNXI_FUNCTION(0x0, "gpio_in"), diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c index 8b381d69df86..584cdedea7a4 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c @@ -407,12 +407,12 @@ static const struct sunxi_desc_pin sun8i_a33_pins[] = { SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8), SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "uart2"), /* RTS */ + SUNXI_FUNCTION(0x2, "uart1"), /* RTS */ SUNXI_FUNCTION_IRQ_BANK(0x4, 1, 8)), /* PG_EINT8 */ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9), SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "uart2"), /* CTS */ + SUNXI_FUNCTION(0x2, "uart1"), /* CTS */ SUNXI_FUNCTION_IRQ_BANK(0x4, 1, 9)), /* PG_EINT9 */ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10), SUNXI_FUNCTION(0x0, "gpio_in"), diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c index 589872cc8adb..a19c29c79b0a 100644 --- a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c @@ -73,6 +73,12 @@ static void uniphier_pctl_pin_dbg_show(struct pinctrl_dev *pctldev, case UNIPHIER_PIN_PULL_DOWN: pull_dir = "DOWN"; break; + case UNIPHIER_PIN_PULL_UP_FIXED: + pull_dir = "UP(FIXED)"; + break; + case UNIPHIER_PIN_PULL_DOWN_FIXED: + pull_dir = "DOWN(FIXED)"; + break; case UNIPHIER_PIN_PULL_NONE: pull_dir = "NONE"; break; diff --git a/drivers/platform/goldfish/Makefile b/drivers/platform/goldfish/Makefile index d3487125838c..277a820ee4e1 100644 --- a/drivers/platform/goldfish/Makefile +++ b/drivers/platform/goldfish/Makefile @@ -2,4 +2,5 @@ # Makefile for Goldfish platform specific drivers # obj-$(CONFIG_GOLDFISH_BUS) += pdev_bus.o -obj-$(CONFIG_GOLDFISH_PIPE) += goldfish_pipe.o +obj-$(CONFIG_GOLDFISH_PIPE) += goldfish_pipe_all.o +goldfish_pipe_all-objs := goldfish_pipe.o goldfish_pipe_v2.o diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 3215a33cf4fe..fd1452e28352 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -15,51 +15,11 @@ * */ -/* This source file contains the implementation of a special device driver - * that intends to provide a *very* fast communication channel between the - * guest system and the QEMU emulator. - * - * Usage from the guest is simply the following (error handling simplified): - * - * int fd = open("/dev/qemu_pipe",O_RDWR); - * .... write() or read() through the pipe. - * - * This driver doesn't deal with the exact protocol used during the session. - * It is intended to be as simple as something like: - * - * // do this _just_ after opening the fd to connect to a specific - * // emulator service. - * const char* msg = "<pipename>"; - * if (write(fd, msg, strlen(msg)+1) < 0) { - * ... could not connect to <pipename> service - * close(fd); - * } - * - * // after this, simply read() and write() to communicate with the - * // service. Exact protocol details left as an exercise to the reader. - * - * This driver is very fast because it doesn't copy any data through - * intermediate buffers, since the emulator is capable of translating - * guest user addresses into host ones. - * - * Note that we must however ensure that each user page involved in the - * exchange is properly mapped during a transfer. +/* This source file contains the implementation of the legacy version of + * a goldfish pipe device driver. See goldfish_pipe_v2.c for the current + * version. */ - -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/spinlock.h> -#include <linux/miscdevice.h> -#include <linux/platform_device.h> -#include <linux/poll.h> -#include <linux/sched.h> -#include <linux/bitops.h> -#include <linux/slab.h> -#include <linux/io.h> -#include <linux/goldfish.h> -#include <linux/mm.h> -#include <linux/acpi.h> +#include "goldfish_pipe.h" /* * IMPORTANT: The following constants must match the ones used and defined @@ -109,29 +69,15 @@ #define PIPE_WAKE_READ (1 << 1) /* pipe can now be read from */ #define PIPE_WAKE_WRITE (1 << 2) /* pipe can now be written to */ -struct access_params { - unsigned long channel; - u32 size; - unsigned long address; - u32 cmd; - u32 result; - /* reserved for future extension */ - u32 flags; -}; +#define MAX_PAGES_TO_GRAB 32 -/* The global driver data. Holds a reference to the i/o page used to - * communicate with the emulator, and a wake queue for blocked tasks - * waiting to be awoken. - */ -struct goldfish_pipe_dev { - spinlock_t lock; - unsigned char __iomem *base; - struct access_params *aps; - int irq; - u32 version; -}; +#define DEBUG 0 -static struct goldfish_pipe_dev pipe_dev[1]; +#if DEBUG +#define DPRINT(...) { printk(KERN_ERR __VA_ARGS__); } +#else +#define DPRINT(...) +#endif /* This data type models a given pipe instance */ struct goldfish_pipe { @@ -141,6 +87,15 @@ struct goldfish_pipe { wait_queue_head_t wake_queue; }; +struct access_params { + unsigned long channel; + u32 size; + unsigned long address; + u32 cmd; + u32 result; + /* reserved for future extension */ + u32 flags; +}; /* Bit flags for the 'flags' field */ enum { @@ -231,8 +186,10 @@ static int setup_access_params_addr(struct platform_device *pdev, if (valid_batchbuffer_addr(dev, aps)) { dev->aps = aps; return 0; - } else + } else { + devm_kfree(&pdev->dev, aps); return -1; + } } /* A value that will not be set by qemu emulator */ @@ -269,6 +226,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, struct goldfish_pipe *pipe = filp->private_data; struct goldfish_pipe_dev *dev = pipe->dev; unsigned long address, address_end; + struct page* pages[MAX_PAGES_TO_GRAB] = {}; int count = 0, ret = -EINVAL; /* If the emulator already closed the pipe, no need to go further */ @@ -292,45 +250,62 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, address_end = address + bufflen; while (address < address_end) { - unsigned long page_end = (address & PAGE_MASK) + PAGE_SIZE; - unsigned long next = page_end < address_end ? page_end - : address_end; - unsigned long avail = next - address; - int status, wakeBit; - - struct page *page; - - /* Either vaddr or paddr depending on the device version */ - unsigned long xaddr; + unsigned long page_end = (address & PAGE_MASK) + PAGE_SIZE; + unsigned long next, avail; + int status, wakeBit, page_i, num_contiguous_pages; + long first_page, last_page, requested_pages; + unsigned long xaddr, xaddr_prev, xaddr_i; /* - * We grab the pages on a page-by-page basis in case user - * space gives us a potentially huge buffer but the read only - * returns a small amount, then there's no need to pin that - * much memory to the process. + * Attempt to grab multiple physically contiguous pages. */ - down_read(¤t->mm->mmap_sem); - ret = get_user_pages(current, current->mm, address, 1, - !is_write, 0, &page, NULL); - up_read(¤t->mm->mmap_sem); - if (ret < 0) + first_page = address & PAGE_MASK; + last_page = (address_end - 1) & PAGE_MASK; + requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1; + if (requested_pages > MAX_PAGES_TO_GRAB) { + requested_pages = MAX_PAGES_TO_GRAB; + } + ret = get_user_pages_fast(first_page, requested_pages, + !is_write, pages); + + DPRINT("%s: requested pages: %d %d %p\n", __FUNCTION__, + ret, requested_pages, first_page); + if (ret == 0) { + DPRINT("%s: error: (requested pages == 0) (wanted %d)\n", + __FUNCTION__, requested_pages); + mutex_unlock(&pipe->lock); return ret; + } + if (ret < 0) { + DPRINT("%s: (requested pages < 0) %d \n", + __FUNCTION__, requested_pages); + mutex_unlock(&pipe->lock); + return ret; + } - if (dev->version) { - /* Device version 1 or newer (qemu-android) expects the - * physical address. */ - xaddr = page_to_phys(page) | (address & ~PAGE_MASK); - } else { - /* Device version 0 (classic emulator) expects the - * virtual address. */ - xaddr = address; + xaddr = page_to_phys(pages[0]) | (address & ~PAGE_MASK); + xaddr_prev = xaddr; + num_contiguous_pages = ret == 0 ? 0 : 1; + for (page_i = 1; page_i < ret; page_i++) { + xaddr_i = page_to_phys(pages[page_i]) | (address & ~PAGE_MASK); + if (xaddr_i == xaddr_prev + PAGE_SIZE) { + page_end += PAGE_SIZE; + xaddr_prev = xaddr_i; + num_contiguous_pages++; + } else { + DPRINT("%s: discontinuous page boundary: %d pages instead\n", + __FUNCTION__, page_i); + break; + } } + next = page_end < address_end ? page_end : address_end; + avail = next - address; /* Now, try to transfer the bytes in the current page */ spin_lock_irqsave(&dev->lock, irq_flags); if (access_with_param(dev, - is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER, - xaddr, avail, pipe, &status)) { + is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER, + xaddr, avail, pipe, &status)) { gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL, dev->base + PIPE_REG_CHANNEL_HIGH); writel(avail, dev->base + PIPE_REG_SIZE); @@ -343,9 +318,13 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, } spin_unlock_irqrestore(&dev->lock, irq_flags); - if (status > 0 && !is_write) - set_page_dirty(page); - put_page(page); + for (page_i = 0; page_i < ret; page_i++) { + if (status > 0 && !is_write && + page_i < num_contiguous_pages) { + set_page_dirty(pages[page_i]); + } + put_page(pages[page_i]); + } if (status > 0) { /* Correct transfer */ count += status; @@ -367,7 +346,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, */ if (status != PIPE_ERROR_AGAIN) pr_info_ratelimited("goldfish_pipe: backend returned error %d on %s\n", - status, is_write ? "write" : "read"); + status, is_write ? "write" : "read"); ret = 0; break; } @@ -377,7 +356,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, * non-blocking mode, just return the error code. */ if (status != PIPE_ERROR_AGAIN || - (filp->f_flags & O_NONBLOCK) != 0) { + (filp->f_flags & O_NONBLOCK) != 0) { ret = goldfish_pipe_error_convert(status); break; } @@ -391,7 +370,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, /* Tell the emulator we're going to wait for a wake event */ goldfish_cmd(pipe, - is_write ? CMD_WAKE_ON_WRITE : CMD_WAKE_ON_READ); + is_write ? CMD_WAKE_ON_WRITE : CMD_WAKE_ON_READ); /* Unlock the pipe, then wait for the wake signal */ mutex_unlock(&pipe->lock); @@ -399,22 +378,16 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, while (test_bit(wakeBit, &pipe->flags)) { if (wait_event_interruptible( pipe->wake_queue, - !test_bit(wakeBit, &pipe->flags))) { - ret = -ERESTARTSYS; - break; - } + !test_bit(wakeBit, &pipe->flags))) + return -ERESTARTSYS; - if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) { - ret = -EIO; - break; - } + if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) + return -EIO; } /* Try to re-acquire the lock */ - if (mutex_lock_interruptible(&pipe->lock)) { - ret = -ERESTARTSYS; - break; - } + if (mutex_lock_interruptible(&pipe->lock)) + return -ERESTARTSYS; } mutex_unlock(&pipe->lock); @@ -543,6 +516,8 @@ static int goldfish_pipe_open(struct inode *inode, struct file *file) pipe->dev = dev; mutex_init(&pipe->lock); + DPRINT("%s: call. pipe_dev pipe_dev=0x%lx new_pipe_addr=0x%lx file=0x%lx\n", __FUNCTION__, pipe_dev, pipe, file); + // spin lock init, write head of list, i guess init_waitqueue_head(&pipe->wake_queue); /* @@ -565,6 +540,7 @@ static int goldfish_pipe_release(struct inode *inode, struct file *filp) { struct goldfish_pipe *pipe = filp->private_data; + DPRINT("%s: call. pipe=0x%lx file=0x%lx\n", __FUNCTION__, pipe, filp); /* The guest is closing the channel, so tell the emulator right now */ goldfish_cmd(pipe, CMD_CLOSE); kfree(pipe); @@ -581,96 +557,33 @@ static const struct file_operations goldfish_pipe_fops = { .release = goldfish_pipe_release, }; -static struct miscdevice goldfish_pipe_device = { +static struct miscdevice goldfish_pipe_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "goldfish_pipe", .fops = &goldfish_pipe_fops, }; -static int goldfish_pipe_probe(struct platform_device *pdev) +int goldfish_pipe_device_init_v1(struct platform_device *pdev) { - int err; - struct resource *r; struct goldfish_pipe_dev *dev = pipe_dev; - - /* not thread safe, but this should not happen */ - WARN_ON(dev->base != NULL); - - spin_lock_init(&dev->lock); - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL || resource_size(r) < PAGE_SIZE) { - dev_err(&pdev->dev, "can't allocate i/o page\n"); - return -EINVAL; - } - dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE); - if (dev->base == NULL) { - dev_err(&pdev->dev, "ioremap failed\n"); - return -EINVAL; - } - - r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (r == NULL) { - err = -EINVAL; - goto error; - } - dev->irq = r->start; - - err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt, + int err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt, IRQF_SHARED, "goldfish_pipe", dev); if (err) { - dev_err(&pdev->dev, "unable to allocate IRQ\n"); - goto error; + dev_err(&pdev->dev, "unable to allocate IRQ for v1\n"); + return err; } - err = misc_register(&goldfish_pipe_device); + err = misc_register(&goldfish_pipe_dev); if (err) { - dev_err(&pdev->dev, "unable to register device\n"); - goto error; + dev_err(&pdev->dev, "unable to register v1 device\n"); + return err; } - setup_access_params_addr(pdev, dev); - /* Although the pipe device in the classic Android emulator does not - * recognize the 'version' register, it won't treat this as an error - * either and will simply return 0, which is fine. */ - dev->version = readl(dev->base + PIPE_REG_VERSION); + setup_access_params_addr(pdev, dev); return 0; - -error: - dev->base = NULL; - return err; } -static int goldfish_pipe_remove(struct platform_device *pdev) +void goldfish_pipe_device_deinit_v1(struct platform_device *pdev) { - struct goldfish_pipe_dev *dev = pipe_dev; - misc_deregister(&goldfish_pipe_device); - dev->base = NULL; - return 0; + misc_deregister(&goldfish_pipe_dev); } - -static const struct acpi_device_id goldfish_pipe_acpi_match[] = { - { "GFSH0003", 0 }, - { }, -}; -MODULE_DEVICE_TABLE(acpi, goldfish_pipe_acpi_match); - -static const struct of_device_id goldfish_pipe_of_match[] = { - { .compatible = "generic,android-pipe", }, - {}, -}; -MODULE_DEVICE_TABLE(of, goldfish_pipe_of_match); - -static struct platform_driver goldfish_pipe = { - .probe = goldfish_pipe_probe, - .remove = goldfish_pipe_remove, - .driver = { - .name = "goldfish_pipe", - .of_match_table = goldfish_pipe_of_match, - .acpi_match_table = ACPI_PTR(goldfish_pipe_acpi_match), - } -}; - -module_platform_driver(goldfish_pipe); -MODULE_AUTHOR("David Turner <digit@google.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/platform/goldfish/goldfish_pipe.h b/drivers/platform/goldfish/goldfish_pipe.h new file mode 100644 index 000000000000..9b75a51dba24 --- /dev/null +++ b/drivers/platform/goldfish/goldfish_pipe.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef GOLDFISH_PIPE_H +#define GOLDFISH_PIPE_H + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/miscdevice.h> +#include <linux/platform_device.h> +#include <linux/poll.h> +#include <linux/sched.h> +#include <linux/bitops.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/goldfish.h> +#include <linux/mm.h> +#include <linux/acpi.h> + + +/* Initialize the legacy version of the pipe device driver */ +int goldfish_pipe_device_init_v1(struct platform_device *pdev); + +/* Deinitialize the legacy version of the pipe device driver */ +void goldfish_pipe_device_deinit_v1(struct platform_device *pdev); + +/* Forward declarations for the device struct */ +struct goldfish_pipe; +struct goldfish_pipe_device_buffers; + +/* The global driver data. Holds a reference to the i/o page used to + * communicate with the emulator, and a wake queue for blocked tasks + * waiting to be awoken. + */ +struct goldfish_pipe_dev { + /* + * Global device spinlock. Protects the following members: + * - pipes, pipes_capacity + * - [*pipes, *pipes + pipes_capacity) - array data + * - first_signalled_pipe, + * goldfish_pipe::prev_signalled, + * goldfish_pipe::next_signalled, + * goldfish_pipe::signalled_flags - all singnalled-related fields, + * in all allocated pipes + * - open_command_params - PIPE_CMD_OPEN-related buffers + * + * It looks like a lot of different fields, but the trick is that the only + * operation that happens often is the signalled pipes array manipulation. + * That's why it's OK for now to keep the rest of the fields under the same + * lock. If we notice too much contention because of PIPE_CMD_OPEN, + * then we should add a separate lock there. + */ + spinlock_t lock; + + /* + * Array of the pipes of |pipes_capacity| elements, + * indexed by goldfish_pipe::id + */ + struct goldfish_pipe **pipes; + u32 pipes_capacity; + + /* Pointers to the buffers host uses for interaction with this driver */ + struct goldfish_pipe_dev_buffers *buffers; + + /* Head of a doubly linked list of signalled pipes */ + struct goldfish_pipe *first_signalled_pipe; + + /* Some device-specific data */ + int irq; + int version; + unsigned char __iomem *base; + + /* v1-specific access parameters */ + struct access_params *aps; +}; + +extern struct goldfish_pipe_dev pipe_dev[1]; + +#endif /* GOLDFISH_PIPE_H */ diff --git a/drivers/platform/goldfish/goldfish_pipe_v2.c b/drivers/platform/goldfish/goldfish_pipe_v2.c new file mode 100644 index 000000000000..ad373ed36555 --- /dev/null +++ b/drivers/platform/goldfish/goldfish_pipe_v2.c @@ -0,0 +1,889 @@ +/* + * Copyright (C) 2012 Intel, Inc. + * Copyright (C) 2013 Intel, Inc. + * Copyright (C) 2014 Linaro Limited + * Copyright (C) 2011-2016 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* This source file contains the implementation of a special device driver + * that intends to provide a *very* fast communication channel between the + * guest system and the QEMU emulator. + * + * Usage from the guest is simply the following (error handling simplified): + * + * int fd = open("/dev/qemu_pipe",O_RDWR); + * .... write() or read() through the pipe. + * + * This driver doesn't deal with the exact protocol used during the session. + * It is intended to be as simple as something like: + * + * // do this _just_ after opening the fd to connect to a specific + * // emulator service. + * const char* msg = "<pipename>"; + * if (write(fd, msg, strlen(msg)+1) < 0) { + * ... could not connect to <pipename> service + * close(fd); + * } + * + * // after this, simply read() and write() to communicate with the + * // service. Exact protocol details left as an exercise to the reader. + * + * This driver is very fast because it doesn't copy any data through + * intermediate buffers, since the emulator is capable of translating + * guest user addresses into host ones. + * + * Note that we must however ensure that each user page involved in the + * exchange is properly mapped during a transfer. + */ + +#include "goldfish_pipe.h" + + +/* + * Update this when something changes in the driver's behavior so the host + * can benefit from knowing it + */ +enum { + PIPE_DRIVER_VERSION = 2, + PIPE_CURRENT_DEVICE_VERSION = 2 +}; + +/* + * IMPORTANT: The following constants must match the ones used and defined + * in external/qemu/hw/goldfish_pipe.c in the Android source tree. + */ + +/* List of bitflags returned in status of CMD_POLL command */ +enum PipePollFlags { + PIPE_POLL_IN = 1 << 0, + PIPE_POLL_OUT = 1 << 1, + PIPE_POLL_HUP = 1 << 2 +}; + +/* Possible status values used to signal errors - see goldfish_pipe_error_convert */ +enum PipeErrors { + PIPE_ERROR_INVAL = -1, + PIPE_ERROR_AGAIN = -2, + PIPE_ERROR_NOMEM = -3, + PIPE_ERROR_IO = -4 +}; + +/* Bit-flags used to signal events from the emulator */ +enum PipeWakeFlags { + PIPE_WAKE_CLOSED = 1 << 0, /* emulator closed pipe */ + PIPE_WAKE_READ = 1 << 1, /* pipe can now be read from */ + PIPE_WAKE_WRITE = 1 << 2 /* pipe can now be written to */ +}; + +/* Bit flags for the 'flags' field */ +enum PipeFlagsBits { + BIT_CLOSED_ON_HOST = 0, /* pipe closed by host */ + BIT_WAKE_ON_WRITE = 1, /* want to be woken on writes */ + BIT_WAKE_ON_READ = 2, /* want to be woken on reads */ +}; + +enum PipeRegs { + PIPE_REG_CMD = 0, + + PIPE_REG_SIGNAL_BUFFER_HIGH = 4, + PIPE_REG_SIGNAL_BUFFER = 8, + PIPE_REG_SIGNAL_BUFFER_COUNT = 12, + + PIPE_REG_OPEN_BUFFER_HIGH = 20, + PIPE_REG_OPEN_BUFFER = 24, + + PIPE_REG_VERSION = 36, + + PIPE_REG_GET_SIGNALLED = 48, +}; + +enum PipeCmdCode { + PIPE_CMD_OPEN = 1, /* to be used by the pipe device itself */ + PIPE_CMD_CLOSE, + PIPE_CMD_POLL, + PIPE_CMD_WRITE, + PIPE_CMD_WAKE_ON_WRITE, + PIPE_CMD_READ, + PIPE_CMD_WAKE_ON_READ, + + /* + * TODO(zyy): implement a deferred read/write execution to allow parallel + * processing of pipe operations on the host. + */ + PIPE_CMD_WAKE_ON_DONE_IO, +}; + +enum { + MAX_BUFFERS_PER_COMMAND = 336, + MAX_SIGNALLED_PIPES = 64, + INITIAL_PIPES_CAPACITY = 64 +}; + +struct goldfish_pipe_dev; +struct goldfish_pipe; +struct goldfish_pipe_command; + +/* A per-pipe command structure, shared with the host */ +struct goldfish_pipe_command { + s32 cmd; /* PipeCmdCode, guest -> host */ + s32 id; /* pipe id, guest -> host */ + s32 status; /* command execution status, host -> guest */ + s32 reserved; /* to pad to 64-bit boundary */ + union { + /* Parameters for PIPE_CMD_{READ,WRITE} */ + struct { + u32 buffers_count; /* number of buffers, guest -> host */ + s32 consumed_size; /* number of consumed bytes, host -> guest */ + u64 ptrs[MAX_BUFFERS_PER_COMMAND]; /* buffer pointers, guest -> host */ + u32 sizes[MAX_BUFFERS_PER_COMMAND]; /* buffer sizes, guest -> host */ + } rw_params; + }; +}; + +/* A single signalled pipe information */ +struct signalled_pipe_buffer { + u32 id; + u32 flags; +}; + +/* Parameters for the PIPE_CMD_OPEN command */ +struct open_command_param { + u64 command_buffer_ptr; + u32 rw_params_max_count; +}; + +/* Device-level set of buffers shared with the host */ +struct goldfish_pipe_dev_buffers { + struct open_command_param open_command_params; + struct signalled_pipe_buffer signalled_pipe_buffers[MAX_SIGNALLED_PIPES]; +}; + +/* This data type models a given pipe instance */ +struct goldfish_pipe { + u32 id; /* pipe ID - index into goldfish_pipe_dev::pipes array */ + unsigned long flags; /* The wake flags pipe is waiting for + * Note: not protected with any lock, uses atomic operations + * and barriers to make it thread-safe. + */ + unsigned long signalled_flags; /* wake flags host have signalled, + * - protected by goldfish_pipe_dev::lock */ + + struct goldfish_pipe_command *command_buffer; /* A pointer to command buffer */ + + /* doubly linked list of signalled pipes, protected by goldfish_pipe_dev::lock */ + struct goldfish_pipe *prev_signalled; + struct goldfish_pipe *next_signalled; + + /* + * A pipe's own lock. Protects the following: + * - *command_buffer - makes sure a command can safely write its parameters + * to the host and read the results back. + */ + struct mutex lock; + + wait_queue_head_t wake_queue; /* A wake queue for sleeping until host signals an event */ + struct goldfish_pipe_dev *dev; /* Pointer to the parent goldfish_pipe_dev instance */ +}; + +struct goldfish_pipe_dev pipe_dev[1] = {}; + +static int goldfish_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) +{ + pipe->command_buffer->cmd = cmd; + pipe->command_buffer->status = PIPE_ERROR_INVAL; /* failure by default */ + writel(pipe->id, pipe->dev->base + PIPE_REG_CMD); + return pipe->command_buffer->status; +} + +static int goldfish_cmd(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) +{ + int status; + if (mutex_lock_interruptible(&pipe->lock)) + return PIPE_ERROR_IO; + status = goldfish_cmd_locked(pipe, cmd); + mutex_unlock(&pipe->lock); + return status; +} + +/* + * This function converts an error code returned by the emulator through + * the PIPE_REG_STATUS i/o register into a valid negative errno value. + */ +static int goldfish_pipe_error_convert(int status) +{ + switch (status) { + case PIPE_ERROR_AGAIN: + return -EAGAIN; + case PIPE_ERROR_NOMEM: + return -ENOMEM; + case PIPE_ERROR_IO: + return -EIO; + default: + return -EINVAL; + } +} + +static int pin_user_pages(unsigned long first_page, unsigned long last_page, + unsigned last_page_size, int is_write, + struct page *pages[MAX_BUFFERS_PER_COMMAND], unsigned *iter_last_page_size) +{ + int ret; + int requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1; + if (requested_pages > MAX_BUFFERS_PER_COMMAND) { + requested_pages = MAX_BUFFERS_PER_COMMAND; + *iter_last_page_size = PAGE_SIZE; + } else { + *iter_last_page_size = last_page_size; + } + + ret = get_user_pages_fast( + first_page, requested_pages, !is_write, pages); + if (ret <= 0) + return -EFAULT; + if (ret < requested_pages) + *iter_last_page_size = PAGE_SIZE; + return ret; + +} + +static void release_user_pages(struct page **pages, int pages_count, + int is_write, s32 consumed_size) +{ + int i; + for (i = 0; i < pages_count; i++) { + if (!is_write && consumed_size > 0) { + set_page_dirty(pages[i]); + } + put_page(pages[i]); + } +} + +/* Populate the call parameters, merging adjacent pages together */ +static void populate_rw_params( + struct page **pages, int pages_count, + unsigned long address, unsigned long address_end, + unsigned long first_page, unsigned long last_page, + unsigned iter_last_page_size, int is_write, + struct goldfish_pipe_command *command) +{ + /* + * Process the first page separately - it's the only page that + * needs special handling for its start address. + */ + unsigned long xaddr = page_to_phys(pages[0]); + unsigned long xaddr_prev = xaddr; + int buffer_idx = 0; + int i = 1; + int size_on_page = first_page == last_page + ? (int)(address_end - address) + : (PAGE_SIZE - (address & ~PAGE_MASK)); + command->rw_params.ptrs[0] = (u64)(xaddr | (address & ~PAGE_MASK)); + command->rw_params.sizes[0] = size_on_page; + for (; i < pages_count; ++i) { + xaddr = page_to_phys(pages[i]); + size_on_page = (i == pages_count - 1) ? iter_last_page_size : PAGE_SIZE; + if (xaddr == xaddr_prev + PAGE_SIZE) { + command->rw_params.sizes[buffer_idx] += size_on_page; + } else { + ++buffer_idx; + command->rw_params.ptrs[buffer_idx] = (u64)xaddr; + command->rw_params.sizes[buffer_idx] = size_on_page; + } + xaddr_prev = xaddr; + } + command->rw_params.buffers_count = buffer_idx + 1; +} + +static int transfer_max_buffers(struct goldfish_pipe* pipe, + unsigned long address, unsigned long address_end, int is_write, + unsigned long last_page, unsigned int last_page_size, + s32* consumed_size, int* status) +{ + struct page *pages[MAX_BUFFERS_PER_COMMAND]; + unsigned long first_page = address & PAGE_MASK; + unsigned int iter_last_page_size; + int pages_count = pin_user_pages(first_page, last_page, + last_page_size, is_write, + pages, &iter_last_page_size); + if (pages_count < 0) + return pages_count; + + /* Serialize access to the pipe command buffers */ + if (mutex_lock_interruptible(&pipe->lock)) + return -ERESTARTSYS; + + populate_rw_params(pages, pages_count, address, address_end, + first_page, last_page, iter_last_page_size, is_write, + pipe->command_buffer); + + /* Transfer the data */ + *status = goldfish_cmd_locked(pipe, + is_write ? PIPE_CMD_WRITE : PIPE_CMD_READ); + + *consumed_size = pipe->command_buffer->rw_params.consumed_size; + + mutex_unlock(&pipe->lock); + + release_user_pages(pages, pages_count, is_write, *consumed_size); + + return 0; +} + +static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write) +{ + u32 wakeBit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ; + set_bit(wakeBit, &pipe->flags); + + /* Tell the emulator we're going to wait for a wake event */ + (void)goldfish_cmd(pipe, + is_write ? PIPE_CMD_WAKE_ON_WRITE : PIPE_CMD_WAKE_ON_READ); + + while (test_bit(wakeBit, &pipe->flags)) { + if (wait_event_interruptible( + pipe->wake_queue, + !test_bit(wakeBit, &pipe->flags))) + return -ERESTARTSYS; + + if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) + return -EIO; + } + + return 0; +} + +static ssize_t goldfish_pipe_read_write(struct file *filp, + char __user *buffer, size_t bufflen, int is_write) +{ + struct goldfish_pipe *pipe = filp->private_data; + int count = 0, ret = -EINVAL; + unsigned long address, address_end, last_page; + unsigned int last_page_size; + + /* If the emulator already closed the pipe, no need to go further */ + if (unlikely(test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))) + return -EIO; + /* Null reads or writes succeeds */ + if (unlikely(bufflen == 0)) + return 0; + /* Check the buffer range for access */ + if (unlikely(!access_ok(is_write ? VERIFY_WRITE : VERIFY_READ, + buffer, bufflen))) + return -EFAULT; + + address = (unsigned long)buffer; + address_end = address + bufflen; + last_page = (address_end - 1) & PAGE_MASK; + last_page_size = ((address_end - 1) & ~PAGE_MASK) + 1; + + while (address < address_end) { + s32 consumed_size; + int status; + ret = transfer_max_buffers(pipe, address, address_end, is_write, + last_page, last_page_size, &consumed_size, &status); + if (ret < 0) + break; + + if (consumed_size > 0) { + /* No matter what's the status, we've transfered something */ + count += consumed_size; + address += consumed_size; + } + if (status > 0) + continue; + if (status == 0) { + /* EOF */ + ret = 0; + break; + } + if (count > 0) { + /* + * An error occured, but we already transfered + * something on one of the previous iterations. + * Just return what we already copied and log this + * err. + */ + if (status != PIPE_ERROR_AGAIN) + pr_info_ratelimited("goldfish_pipe: backend error %d on %s\n", + status, is_write ? "write" : "read"); + break; + } + + /* + * If the error is not PIPE_ERROR_AGAIN, or if we are in + * non-blocking mode, just return the error code. + */ + if (status != PIPE_ERROR_AGAIN || (filp->f_flags & O_NONBLOCK) != 0) { + ret = goldfish_pipe_error_convert(status); + break; + } + + status = wait_for_host_signal(pipe, is_write); + if (status < 0) + return status; + } + + if (count > 0) + return count; + return ret; +} + +static ssize_t goldfish_pipe_read(struct file *filp, char __user *buffer, + size_t bufflen, loff_t *ppos) +{ + return goldfish_pipe_read_write(filp, buffer, bufflen, /* is_write */ 0); +} + +static ssize_t goldfish_pipe_write(struct file *filp, + const char __user *buffer, size_t bufflen, + loff_t *ppos) +{ + return goldfish_pipe_read_write(filp, + /* cast away the const */(char __user *)buffer, bufflen, + /* is_write */ 1); +} + +static unsigned int goldfish_pipe_poll(struct file *filp, poll_table *wait) +{ + struct goldfish_pipe *pipe = filp->private_data; + unsigned int mask = 0; + int status; + + poll_wait(filp, &pipe->wake_queue, wait); + + status = goldfish_cmd(pipe, PIPE_CMD_POLL); + if (status < 0) { + return -ERESTARTSYS; + } + + if (status & PIPE_POLL_IN) + mask |= POLLIN | POLLRDNORM; + if (status & PIPE_POLL_OUT) + mask |= POLLOUT | POLLWRNORM; + if (status & PIPE_POLL_HUP) + mask |= POLLHUP; + if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) + mask |= POLLERR; + + return mask; +} + +static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev, + u32 id, u32 flags) +{ + struct goldfish_pipe *pipe; + + BUG_ON(id >= dev->pipes_capacity); + + pipe = dev->pipes[id]; + if (!pipe) + return; + pipe->signalled_flags |= flags; + + if (pipe->prev_signalled || pipe->next_signalled + || dev->first_signalled_pipe == pipe) + return; /* already in the list */ + pipe->next_signalled = dev->first_signalled_pipe; + if (dev->first_signalled_pipe) { + dev->first_signalled_pipe->prev_signalled = pipe; + } + dev->first_signalled_pipe = pipe; +} + +static void signalled_pipes_remove_locked(struct goldfish_pipe_dev *dev, + struct goldfish_pipe *pipe) { + if (pipe->prev_signalled) + pipe->prev_signalled->next_signalled = pipe->next_signalled; + if (pipe->next_signalled) + pipe->next_signalled->prev_signalled = pipe->prev_signalled; + if (pipe == dev->first_signalled_pipe) + dev->first_signalled_pipe = pipe->next_signalled; + pipe->prev_signalled = NULL; + pipe->next_signalled = NULL; +} + +static struct goldfish_pipe *signalled_pipes_pop_front(struct goldfish_pipe_dev *dev, + int *wakes) +{ + struct goldfish_pipe *pipe; + unsigned long flags; + spin_lock_irqsave(&dev->lock, flags); + + pipe = dev->first_signalled_pipe; + if (pipe) { + *wakes = pipe->signalled_flags; + pipe->signalled_flags = 0; + /* + * This is an optimized version of signalled_pipes_remove_locked() - + * we want to make it as fast as possible to wake the sleeping pipe + * operations faster + */ + dev->first_signalled_pipe = pipe->next_signalled; + if (dev->first_signalled_pipe) + dev->first_signalled_pipe->prev_signalled = NULL; + pipe->next_signalled = NULL; + } + + spin_unlock_irqrestore(&dev->lock, flags); + return pipe; +} + +static void goldfish_interrupt_task(unsigned long unused) +{ + struct goldfish_pipe_dev *dev = pipe_dev; + /* Iterate over the signalled pipes and wake them one by one */ + struct goldfish_pipe *pipe; + int wakes; + while ((pipe = signalled_pipes_pop_front(dev, &wakes)) != NULL) { + if (wakes & PIPE_WAKE_CLOSED) { + pipe->flags = 1 << BIT_CLOSED_ON_HOST; + } else { + if (wakes & PIPE_WAKE_READ) + clear_bit(BIT_WAKE_ON_READ, &pipe->flags); + if (wakes & PIPE_WAKE_WRITE) + clear_bit(BIT_WAKE_ON_WRITE, &pipe->flags); + } + /* + * wake_up_interruptible() implies a write barrier, so don't explicitly + * add another one here. + */ + wake_up_interruptible(&pipe->wake_queue); + } +} +DECLARE_TASKLET(goldfish_interrupt_tasklet, goldfish_interrupt_task, 0); + +/* + * The general idea of the interrupt handling: + * + * 1. device raises an interrupt if there's at least one signalled pipe + * 2. IRQ handler reads the signalled pipes and their count from the device + * 3. device writes them into a shared buffer and returns the count + * it only resets the IRQ if it has returned all signalled pipes, + * otherwise it leaves it raised, so IRQ handler will be called + * again for the next chunk + * 4. IRQ handler adds all returned pipes to the device's signalled pipes list + * 5. IRQ handler launches a tasklet to process the signalled pipes from the + * list in a separate context + */ +static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id) +{ + u32 count; + u32 i; + unsigned long flags; + struct goldfish_pipe_dev *dev = dev_id; + if (dev != pipe_dev) + return IRQ_NONE; + + /* Request the signalled pipes from the device */ + spin_lock_irqsave(&dev->lock, flags); + + count = readl(dev->base + PIPE_REG_GET_SIGNALLED); + if (count == 0) { + spin_unlock_irqrestore(&dev->lock, flags); + return IRQ_NONE; + } + if (count > MAX_SIGNALLED_PIPES) + count = MAX_SIGNALLED_PIPES; + + for (i = 0; i < count; ++i) + signalled_pipes_add_locked(dev, + dev->buffers->signalled_pipe_buffers[i].id, + dev->buffers->signalled_pipe_buffers[i].flags); + + spin_unlock_irqrestore(&dev->lock, flags); + + tasklet_schedule(&goldfish_interrupt_tasklet); + return IRQ_HANDLED; +} + +static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev) +{ + int id; + for (id = 0; id < dev->pipes_capacity; ++id) + if (!dev->pipes[id]) + return id; + + { + /* Reallocate the array */ + u32 new_capacity = 2 * dev->pipes_capacity; + struct goldfish_pipe **pipes = + kcalloc(new_capacity, sizeof(*pipes), + GFP_ATOMIC); + if (!pipes) + return -ENOMEM; + memcpy(pipes, dev->pipes, sizeof(*pipes) * dev->pipes_capacity); + kfree(dev->pipes); + dev->pipes = pipes; + id = dev->pipes_capacity; + dev->pipes_capacity = new_capacity; + } + return id; +} + +/** + * goldfish_pipe_open - open a channel to the AVD + * @inode: inode of device + * @file: file struct of opener + * + * Create a new pipe link between the emulator and the use application. + * Each new request produces a new pipe. + * + * Note: we use the pipe ID as a mux. All goldfish emulations are 32bit + * right now so this is fine. A move to 64bit will need this addressing + */ +static int goldfish_pipe_open(struct inode *inode, struct file *file) +{ + struct goldfish_pipe_dev *dev = pipe_dev; + unsigned long flags; + int id; + int status; + + /* Allocate new pipe kernel object */ + struct goldfish_pipe *pipe = kzalloc(sizeof(*pipe), GFP_KERNEL); + if (pipe == NULL) + return -ENOMEM; + + pipe->dev = dev; + mutex_init(&pipe->lock); + init_waitqueue_head(&pipe->wake_queue); + + /* + * Command buffer needs to be allocated on its own page to make sure it is + * physically contiguous in host's address space. + */ + pipe->command_buffer = + (struct goldfish_pipe_command*)__get_free_page(GFP_KERNEL); + if (!pipe->command_buffer) { + status = -ENOMEM; + goto err_pipe; + } + + spin_lock_irqsave(&dev->lock, flags); + + id = get_free_pipe_id_locked(dev); + if (id < 0) { + status = id; + goto err_id_locked; + } + + dev->pipes[id] = pipe; + pipe->id = id; + pipe->command_buffer->id = id; + + /* Now tell the emulator we're opening a new pipe. */ + dev->buffers->open_command_params.rw_params_max_count = + MAX_BUFFERS_PER_COMMAND; + dev->buffers->open_command_params.command_buffer_ptr = + (u64)(unsigned long)__pa(pipe->command_buffer); + status = goldfish_cmd_locked(pipe, PIPE_CMD_OPEN); + spin_unlock_irqrestore(&dev->lock, flags); + if (status < 0) + goto err_cmd; + /* All is done, save the pipe into the file's private data field */ + file->private_data = pipe; + return 0; + +err_cmd: + spin_lock_irqsave(&dev->lock, flags); + dev->pipes[id] = NULL; +err_id_locked: + spin_unlock_irqrestore(&dev->lock, flags); + free_page((unsigned long)pipe->command_buffer); +err_pipe: + kfree(pipe); + return status; +} + +static int goldfish_pipe_release(struct inode *inode, struct file *filp) +{ + unsigned long flags; + struct goldfish_pipe *pipe = filp->private_data; + struct goldfish_pipe_dev *dev = pipe->dev; + + /* The guest is closing the channel, so tell the emulator right now */ + (void)goldfish_cmd(pipe, PIPE_CMD_CLOSE); + + spin_lock_irqsave(&dev->lock, flags); + dev->pipes[pipe->id] = NULL; + signalled_pipes_remove_locked(dev, pipe); + spin_unlock_irqrestore(&dev->lock, flags); + + filp->private_data = NULL; + free_page((unsigned long)pipe->command_buffer); + kfree(pipe); + return 0; +} + +static const struct file_operations goldfish_pipe_fops = { + .owner = THIS_MODULE, + .read = goldfish_pipe_read, + .write = goldfish_pipe_write, + .poll = goldfish_pipe_poll, + .open = goldfish_pipe_open, + .release = goldfish_pipe_release, +}; + +static struct miscdevice goldfish_pipe_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "goldfish_pipe", + .fops = &goldfish_pipe_fops, +}; + +static int goldfish_pipe_device_init_v2(struct platform_device *pdev) +{ + char *page; + struct goldfish_pipe_dev *dev = pipe_dev; + int err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt, + IRQF_SHARED, "goldfish_pipe", dev); + if (err) { + dev_err(&pdev->dev, "unable to allocate IRQ for v2\n"); + return err; + } + + err = misc_register(&goldfish_pipe_dev); + if (err) { + dev_err(&pdev->dev, "unable to register v2 device\n"); + return err; + } + + dev->first_signalled_pipe = NULL; + dev->pipes_capacity = INITIAL_PIPES_CAPACITY; + dev->pipes = kcalloc(dev->pipes_capacity, sizeof(*dev->pipes), GFP_KERNEL); + if (!dev->pipes) + return -ENOMEM; + + /* + * We're going to pass two buffers, open_command_params and + * signalled_pipe_buffers, to the host. This means each of those buffers + * needs to be contained in a single physical page. The easiest choice is + * to just allocate a page and place the buffers in it. + */ + BUG_ON(sizeof(*dev->buffers) > PAGE_SIZE); + page = (char*)__get_free_page(GFP_KERNEL); + if (!page) { + kfree(dev->pipes); + return -ENOMEM; + } + dev->buffers = (struct goldfish_pipe_dev_buffers*)page; + + /* Send the buffer addresses to the host */ + { + u64 paddr = __pa(&dev->buffers->signalled_pipe_buffers); + writel((u32)(unsigned long)(paddr >> 32), dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH); + writel((u32)(unsigned long)paddr, dev->base + PIPE_REG_SIGNAL_BUFFER); + writel((u32)MAX_SIGNALLED_PIPES, dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT); + + paddr = __pa(&dev->buffers->open_command_params); + writel((u32)(unsigned long)(paddr >> 32), dev->base + PIPE_REG_OPEN_BUFFER_HIGH); + writel((u32)(unsigned long)paddr, dev->base + PIPE_REG_OPEN_BUFFER); + } + return 0; +} + +static void goldfish_pipe_device_deinit_v2(struct platform_device *pdev) { + struct goldfish_pipe_dev *dev = pipe_dev; + misc_deregister(&goldfish_pipe_dev); + kfree(dev->pipes); + free_page((unsigned long)dev->buffers); +} + +static int goldfish_pipe_probe(struct platform_device *pdev) +{ + int err; + struct resource *r; + struct goldfish_pipe_dev *dev = pipe_dev; + + BUG_ON(sizeof(struct goldfish_pipe_command) > PAGE_SIZE); + + /* not thread safe, but this should not happen */ + WARN_ON(dev->base != NULL); + + spin_lock_init(&dev->lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL || resource_size(r) < PAGE_SIZE) { + dev_err(&pdev->dev, "can't allocate i/o page\n"); + return -EINVAL; + } + dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE); + if (dev->base == NULL) { + dev_err(&pdev->dev, "ioremap failed\n"); + return -EINVAL; + } + + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (r == NULL) { + err = -EINVAL; + goto error; + } + dev->irq = r->start; + + /* + * Exchange the versions with the host device + * + * Note: v1 driver used to not report its version, so we write it before + * reading device version back: this allows the host implementation to + * detect the old driver (if there was no version write before read). + */ + writel((u32)PIPE_DRIVER_VERSION, dev->base + PIPE_REG_VERSION); + dev->version = readl(dev->base + PIPE_REG_VERSION); + if (dev->version < PIPE_CURRENT_DEVICE_VERSION) { + /* initialize the old device version */ + err = goldfish_pipe_device_init_v1(pdev); + } else { + /* Host device supports the new interface */ + err = goldfish_pipe_device_init_v2(pdev); + } + if (!err) + return 0; + +error: + dev->base = NULL; + return err; +} + +static int goldfish_pipe_remove(struct platform_device *pdev) +{ + struct goldfish_pipe_dev *dev = pipe_dev; + if (dev->version < PIPE_CURRENT_DEVICE_VERSION) + goldfish_pipe_device_deinit_v1(pdev); + else + goldfish_pipe_device_deinit_v2(pdev); + dev->base = NULL; + return 0; +} + +static const struct acpi_device_id goldfish_pipe_acpi_match[] = { + { "GFSH0003", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goldfish_pipe_acpi_match); + +static const struct of_device_id goldfish_pipe_of_match[] = { + { .compatible = "google,android-pipe", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_pipe_of_match); + +static struct platform_driver goldfish_pipe_driver = { + .probe = goldfish_pipe_probe, + .remove = goldfish_pipe_remove, + .driver = { + .name = "goldfish_pipe", + .of_match_table = goldfish_pipe_of_match, + .acpi_match_table = ACPI_PTR(goldfish_pipe_acpi_match), + } +}; + +module_platform_driver(goldfish_pipe_driver); +MODULE_AUTHOR("David Turner <digit@google.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index 024c66ac8e57..66bdc593f811 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -217,4 +217,11 @@ config USB_BAM Enabling this option adds USB BAM Driver. USB BAM driver was added to supports SPS Peripheral-to-Peripheral transfers between the USB and other peripheral. + +config MSM_EXT_DISPLAY + bool "MSM External Display Driver" + help + Enabling this option adds MSM External Display Driver. + External Display driver was added to support the communication + between external display driver and its couterparts. endmenu diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile index d5e87c209c21..d985aa81a3bb 100644 --- a/drivers/platform/msm/Makefile +++ b/drivers/platform/msm/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_SEEMP_CORE) += seemp_core/ obj-$(CONFIG_SSM) += ssm.o obj-$(CONFIG_USB_BAM) += usb_bam.o obj-$(CONFIG_MSM_MHI_DEV) += mhi_dev/ +obj-$(CONFIG_MSM_EXT_DISPLAY) += msm_ext_display.o diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c index 23b0428bcf34..f48182cc04df 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 1 +#define GSI_STOP_CMD_TIMEOUT_MS 10 #define GSI_MAX_CH_LOW_WEIGHT 15 #define GSI_MHI_ER_START 10 #define GSI_MHI_ER_END 16 diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c index fde087f07226..d4b0dd9910e1 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c @@ -1581,7 +1581,7 @@ bail: static int ipa_init_smem_region(int memory_region_size, int memory_region_offset) { - struct ipa_hw_imm_cmd_dma_shared_mem cmd; + struct ipa_hw_imm_cmd_dma_shared_mem *cmd = NULL; struct ipa_desc desc; struct ipa_mem_buffer mem; int rc; @@ -1590,7 +1590,6 @@ static int ipa_init_smem_region(int memory_region_size, return 0; memset(&desc, 0, sizeof(desc)); - memset(&cmd, 0, sizeof(cmd)); memset(&mem, 0, sizeof(mem)); mem.size = memory_region_size; @@ -1602,13 +1601,22 @@ static int ipa_init_smem_region(int memory_region_size, } memset(mem.base, 0, mem.size); - cmd.size = mem.size; - cmd.system_addr = mem.phys_base; - cmd.local_addr = ipa_ctx->smem_restricted_bytes + + + cmd = kzalloc(sizeof(*cmd), + GFP_KERNEL); + if (cmd == NULL) { + IPAERR("Failed to alloc immediate command object\n"); + rc = -ENOMEM; + goto fail_send_cmd; + } + + cmd->size = mem.size; + cmd->system_addr = mem.phys_base; + cmd->local_addr = ipa_ctx->smem_restricted_bytes + memory_region_offset; desc.opcode = IPA_DMA_SHARED_MEM; - desc.pyld = &cmd; - desc.len = sizeof(cmd); + desc.pyld = cmd; + desc.len = sizeof(*cmd); desc.type = IPA_IMM_CMD_DESC; rc = ipa_send_cmd(1, &desc); @@ -1617,6 +1625,8 @@ static int ipa_init_smem_region(int memory_region_size, rc = -EFAULT; } + kfree(cmd); +fail_send_cmd: dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base); @@ -2153,7 +2163,7 @@ int _ipa_init_sram_v2(void) { u32 *ipa_sram_mmio; unsigned long phys_addr; - struct ipa_hw_imm_cmd_dma_shared_mem cmd = {0}; + struct ipa_hw_imm_cmd_dma_shared_mem *cmd = NULL; struct ipa_desc desc = {0}; struct ipa_mem_buffer mem; int rc = 0; @@ -2193,11 +2203,18 @@ int _ipa_init_sram_v2(void) } memset(mem.base, 0, mem.size); - cmd.size = mem.size; - cmd.system_addr = mem.phys_base; - cmd.local_addr = IPA_STATUS_CLEAR_OFST; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) { + IPAERR("Failed to alloc immediate command object\n"); + rc = -ENOMEM; + goto fail_send_cmd; + } + + cmd->size = mem.size; + cmd->system_addr = mem.phys_base; + cmd->local_addr = IPA_STATUS_CLEAR_OFST; desc.opcode = IPA_DMA_SHARED_MEM; - desc.pyld = &cmd; + desc.pyld = (void *)cmd; desc.len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem); desc.type = IPA_IMM_CMD_DESC; @@ -2206,6 +2223,8 @@ int _ipa_init_sram_v2(void) rc = -EFAULT; } + kfree(cmd); +fail_send_cmd: dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base); return rc; } @@ -2294,7 +2313,7 @@ int _ipa_init_hdr_v2(void) { struct ipa_desc desc = { 0 }; struct ipa_mem_buffer mem; - struct ipa_hdr_init_local cmd; + struct ipa_hdr_init_local *cmd = NULL; int rc = 0; mem.size = IPA_MEM_PART(modem_hdr_size) + IPA_MEM_PART(apps_hdr_size); @@ -2306,13 +2325,20 @@ int _ipa_init_hdr_v2(void) } memset(mem.base, 0, mem.size); - cmd.hdr_table_src_addr = mem.phys_base; - cmd.size_hdr_table = mem.size; - cmd.hdr_table_dst_addr = ipa_ctx->smem_restricted_bytes + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) { + IPAERR("Failed to alloc header init command object\n"); + rc = -ENOMEM; + goto fail_send_cmd; + } + + cmd->hdr_table_src_addr = mem.phys_base; + cmd->size_hdr_table = mem.size; + cmd->hdr_table_dst_addr = ipa_ctx->smem_restricted_bytes + IPA_MEM_PART(modem_hdr_ofst); desc.opcode = IPA_HDR_INIT_LOCAL; - desc.pyld = &cmd; + desc.pyld = (void *)cmd; desc.len = sizeof(struct ipa_hdr_init_local); desc.type = IPA_IMM_CMD_DESC; IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size); @@ -2322,6 +2348,8 @@ int _ipa_init_hdr_v2(void) rc = -EFAULT; } + kfree(cmd); +fail_send_cmd: dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base); return rc; } @@ -2330,8 +2358,8 @@ int _ipa_init_hdr_v2_5(void) { struct ipa_desc desc = { 0 }; struct ipa_mem_buffer mem; - struct ipa_hdr_init_local cmd = { 0 }; - struct ipa_hw_imm_cmd_dma_shared_mem dma_cmd = { 0 }; + struct ipa_hdr_init_local *cmd = NULL; + struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd = NULL; mem.size = IPA_MEM_PART(modem_hdr_size) + IPA_MEM_PART(apps_hdr_size); mem.base = dma_alloc_coherent(ipa_ctx->pdev, mem.size, &mem.phys_base, @@ -2342,25 +2370,34 @@ int _ipa_init_hdr_v2_5(void) } memset(mem.base, 0, mem.size); - cmd.hdr_table_src_addr = mem.phys_base; - cmd.size_hdr_table = mem.size; - cmd.hdr_table_dst_addr = ipa_ctx->smem_restricted_bytes + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) { + IPAERR("Failed to alloc header init command object\n"); + dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, + mem.phys_base); + return -ENOMEM; + } + + cmd->hdr_table_src_addr = mem.phys_base; + cmd->size_hdr_table = mem.size; + cmd->hdr_table_dst_addr = ipa_ctx->smem_restricted_bytes + IPA_MEM_PART(modem_hdr_ofst); desc.opcode = IPA_HDR_INIT_LOCAL; - desc.pyld = &cmd; + desc.pyld = (void *)cmd; desc.len = sizeof(struct ipa_hdr_init_local); desc.type = IPA_IMM_CMD_DESC; IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size); if (ipa_send_cmd(1, &desc)) { IPAERR("fail to send immediate command\n"); - dma_free_coherent(ipa_ctx->pdev, - mem.size, mem.base, + kfree(cmd); + dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base); return -EFAULT; } + kfree(cmd); dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base); mem.size = IPA_MEM_PART(modem_hdr_proc_ctx_size) + @@ -2374,18 +2411,29 @@ int _ipa_init_hdr_v2_5(void) memset(mem.base, 0, mem.size); memset(&desc, 0, sizeof(desc)); - dma_cmd.system_addr = mem.phys_base; - dma_cmd.local_addr = ipa_ctx->smem_restricted_bytes + + dma_cmd = kzalloc(sizeof(*dma_cmd), GFP_KERNEL); + if (dma_cmd == NULL) { + IPAERR("Failed to alloc immediate command object\n"); + dma_free_coherent(ipa_ctx->pdev, + mem.size, + mem.base, + mem.phys_base); + return -ENOMEM; + } + + dma_cmd->system_addr = mem.phys_base; + dma_cmd->local_addr = ipa_ctx->smem_restricted_bytes + IPA_MEM_PART(modem_hdr_proc_ctx_ofst); - dma_cmd.size = mem.size; + dma_cmd->size = mem.size; desc.opcode = IPA_DMA_SHARED_MEM; - desc.pyld = &dma_cmd; + desc.pyld = (void *)dma_cmd; desc.len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem); desc.type = IPA_IMM_CMD_DESC; IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size); if (ipa_send_cmd(1, &desc)) { IPAERR("fail to send immediate command\n"); + kfree(dma_cmd); dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, @@ -2395,8 +2443,9 @@ int _ipa_init_hdr_v2_5(void) ipa_write_reg(ipa_ctx->mmio, IPA_LOCAL_PKT_PROC_CNTXT_BASE_OFST, - dma_cmd.local_addr); + dma_cmd->local_addr); + kfree(dma_cmd); dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base); return 0; @@ -2412,7 +2461,7 @@ int _ipa_init_rt4_v2(void) { struct ipa_desc desc = { 0 }; struct ipa_mem_buffer mem; - struct ipa_ip_v4_routing_init v4_cmd; + struct ipa_ip_v4_routing_init *v4_cmd = NULL; u32 *entry; int i; int rc = 0; @@ -2437,15 +2486,22 @@ int _ipa_init_rt4_v2(void) entry++; } + v4_cmd = kzalloc(sizeof(*v4_cmd), GFP_KERNEL); + if (v4_cmd == NULL) { + IPAERR("Failed to alloc v4 routing init command object\n"); + rc = -ENOMEM; + goto fail_send_cmd; + } + desc.opcode = IPA_IP_V4_ROUTING_INIT; - v4_cmd.ipv4_rules_addr = mem.phys_base; - v4_cmd.size_ipv4_rules = mem.size; - v4_cmd.ipv4_addr = ipa_ctx->smem_restricted_bytes + + v4_cmd->ipv4_rules_addr = mem.phys_base; + v4_cmd->size_ipv4_rules = mem.size; + v4_cmd->ipv4_addr = ipa_ctx->smem_restricted_bytes + IPA_MEM_PART(v4_rt_ofst); IPADBG("putting Routing IPv4 rules to phys 0x%x", - v4_cmd.ipv4_addr); + v4_cmd->ipv4_addr); - desc.pyld = &v4_cmd; + desc.pyld = (void *)v4_cmd; desc.len = sizeof(struct ipa_ip_v4_routing_init); desc.type = IPA_IMM_CMD_DESC; IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size); @@ -2455,6 +2511,8 @@ int _ipa_init_rt4_v2(void) rc = -EFAULT; } + kfree(v4_cmd); +fail_send_cmd: dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base); return rc; } @@ -2463,7 +2521,7 @@ int _ipa_init_rt6_v2(void) { struct ipa_desc desc = { 0 }; struct ipa_mem_buffer mem; - struct ipa_ip_v6_routing_init v6_cmd; + struct ipa_ip_v6_routing_init *v6_cmd = NULL; u32 *entry; int i; int rc = 0; @@ -2488,15 +2546,22 @@ int _ipa_init_rt6_v2(void) entry++; } + v6_cmd = kzalloc(sizeof(*v6_cmd), GFP_KERNEL); + if (v6_cmd == NULL) { + IPAERR("Failed to alloc v6 routing init command object\n"); + rc = -ENOMEM; + goto fail_send_cmd; + } + desc.opcode = IPA_IP_V6_ROUTING_INIT; - v6_cmd.ipv6_rules_addr = mem.phys_base; - v6_cmd.size_ipv6_rules = mem.size; - v6_cmd.ipv6_addr = ipa_ctx->smem_restricted_bytes + + v6_cmd->ipv6_rules_addr = mem.phys_base; + v6_cmd->size_ipv6_rules = mem.size; + v6_cmd->ipv6_addr = ipa_ctx->smem_restricted_bytes + IPA_MEM_PART(v6_rt_ofst); IPADBG("putting Routing IPv6 rules to phys 0x%x", - v6_cmd.ipv6_addr); + v6_cmd->ipv6_addr); - desc.pyld = &v6_cmd; + desc.pyld = (void *)v6_cmd; desc.len = sizeof(struct ipa_ip_v6_routing_init); desc.type = IPA_IMM_CMD_DESC; IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size); @@ -2506,6 +2571,8 @@ int _ipa_init_rt6_v2(void) rc = -EFAULT; } + kfree(v6_cmd); +fail_send_cmd: dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base); return rc; } @@ -2514,7 +2581,7 @@ int _ipa_init_flt4_v2(void) { struct ipa_desc desc = { 0 }; struct ipa_mem_buffer mem; - struct ipa_ip_v4_filter_init v4_cmd; + struct ipa_ip_v4_filter_init *v4_cmd = NULL; u32 *entry; int i; int rc = 0; @@ -2537,15 +2604,22 @@ int _ipa_init_flt4_v2(void) entry++; } + v4_cmd = kzalloc(sizeof(*v4_cmd), GFP_KERNEL); + if (v4_cmd == NULL) { + IPAERR("Failed to alloc v4 fliter init command object\n"); + rc = -ENOMEM; + goto fail_send_cmd; + } + desc.opcode = IPA_IP_V4_FILTER_INIT; - v4_cmd.ipv4_rules_addr = mem.phys_base; - v4_cmd.size_ipv4_rules = mem.size; - v4_cmd.ipv4_addr = ipa_ctx->smem_restricted_bytes + + v4_cmd->ipv4_rules_addr = mem.phys_base; + v4_cmd->size_ipv4_rules = mem.size; + v4_cmd->ipv4_addr = ipa_ctx->smem_restricted_bytes + IPA_MEM_PART(v4_flt_ofst); IPADBG("putting Filtering IPv4 rules to phys 0x%x", - v4_cmd.ipv4_addr); + v4_cmd->ipv4_addr); - desc.pyld = &v4_cmd; + desc.pyld = (void *)v4_cmd; desc.len = sizeof(struct ipa_ip_v4_filter_init); desc.type = IPA_IMM_CMD_DESC; IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size); @@ -2555,6 +2629,8 @@ int _ipa_init_flt4_v2(void) rc = -EFAULT; } + kfree(v4_cmd); +fail_send_cmd: dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base); return rc; } @@ -2563,7 +2639,7 @@ int _ipa_init_flt6_v2(void) { struct ipa_desc desc = { 0 }; struct ipa_mem_buffer mem; - struct ipa_ip_v6_filter_init v6_cmd; + struct ipa_ip_v6_filter_init *v6_cmd = NULL; u32 *entry; int i; int rc = 0; @@ -2586,15 +2662,22 @@ int _ipa_init_flt6_v2(void) entry++; } + v6_cmd = kzalloc(sizeof(*v6_cmd), GFP_KERNEL); + if (v6_cmd == NULL) { + IPAERR("Failed to alloc v6 fliter init command object\n"); + rc = -ENOMEM; + goto fail_send_cmd; + } + desc.opcode = IPA_IP_V6_FILTER_INIT; - v6_cmd.ipv6_rules_addr = mem.phys_base; - v6_cmd.size_ipv6_rules = mem.size; - v6_cmd.ipv6_addr = ipa_ctx->smem_restricted_bytes + + v6_cmd->ipv6_rules_addr = mem.phys_base; + v6_cmd->size_ipv6_rules = mem.size; + v6_cmd->ipv6_addr = ipa_ctx->smem_restricted_bytes + IPA_MEM_PART(v6_flt_ofst); IPADBG("putting Filtering IPv6 rules to phys 0x%x", - v6_cmd.ipv6_addr); + v6_cmd->ipv6_addr); - desc.pyld = &v6_cmd; + desc.pyld = (void *)v6_cmd; desc.len = sizeof(struct ipa_ip_v6_filter_init); desc.type = IPA_IMM_CMD_DESC; IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size); @@ -2604,6 +2687,8 @@ int _ipa_init_flt6_v2(void) rc = -EFAULT; } + kfree(v6_cmd); +fail_send_cmd: dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base); return rc; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c index 51f34f0d5101..e23de3f26613 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c @@ -266,8 +266,8 @@ int __ipa_commit_hdr_v2(void) { struct ipa_desc desc = { 0 }; struct ipa_mem_buffer mem; - struct ipa_hdr_init_system cmd; - struct ipa_hw_imm_cmd_dma_shared_mem dma_cmd; + struct ipa_hdr_init_system *cmd = NULL; + struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd = NULL; int rc = -EFAULT; if (ipa_generate_hdr_hw_tbl(&mem)) { @@ -279,14 +279,21 @@ int __ipa_commit_hdr_v2(void) if (mem.size > IPA_MEM_PART(apps_hdr_size)) { IPAERR("tbl too big, needed %d avail %d\n", mem.size, IPA_MEM_PART(apps_hdr_size)); - goto end; + goto fail_send_cmd; } else { - dma_cmd.system_addr = mem.phys_base; - dma_cmd.size = mem.size; - dma_cmd.local_addr = ipa_ctx->smem_restricted_bytes + + dma_cmd = kzalloc(sizeof(*dma_cmd), GFP_ATOMIC); + if (dma_cmd == NULL) { + IPAERR("fail to alloc immediate cmd\n"); + rc = -ENOMEM; + goto fail_send_cmd; + } + + dma_cmd->system_addr = mem.phys_base; + dma_cmd->size = mem.size; + dma_cmd->local_addr = ipa_ctx->smem_restricted_bytes + IPA_MEM_PART(apps_hdr_ofst); desc.opcode = IPA_DMA_SHARED_MEM; - desc.pyld = &dma_cmd; + desc.pyld = (void *)dma_cmd; desc.len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem); } @@ -294,11 +301,17 @@ int __ipa_commit_hdr_v2(void) if (mem.size > IPA_MEM_PART(apps_hdr_size_ddr)) { IPAERR("tbl too big, needed %d avail %d\n", mem.size, IPA_MEM_PART(apps_hdr_size_ddr)); - goto end; + goto fail_send_cmd; } else { - cmd.hdr_table_addr = mem.phys_base; + cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC); + if (cmd == NULL) { + IPAERR("fail to alloc hdr init cmd\n"); + rc = -ENOMEM; + goto fail_send_cmd; + } + cmd->hdr_table_addr = mem.phys_base; desc.opcode = IPA_HDR_INIT_SYSTEM; - desc.pyld = &cmd; + desc.pyld = (void *)cmd; desc.len = sizeof(struct ipa_hdr_init_system); } } @@ -311,6 +324,10 @@ int __ipa_commit_hdr_v2(void) else rc = 0; + kfree(dma_cmd); + kfree(cmd); + +fail_send_cmd: if (ipa_ctx->hdr_tbl_lcl) { dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base); @@ -322,6 +339,9 @@ int __ipa_commit_hdr_v2(void) ipa_ctx->hdr_mem.base, ipa_ctx->hdr_mem.phys_base); ipa_ctx->hdr_mem = mem; + } else { + dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, + mem.phys_base); } } @@ -335,10 +355,10 @@ int __ipa_commit_hdr_v2_5(void) struct ipa_mem_buffer hdr_mem; struct ipa_mem_buffer ctx_mem; struct ipa_mem_buffer aligned_ctx_mem; - struct ipa_hdr_init_system hdr_init_cmd = {0}; - struct ipa_hw_imm_cmd_dma_shared_mem dma_cmd_hdr = {0}; - struct ipa_hw_imm_cmd_dma_shared_mem dma_cmd_ctx = {0}; - struct ipa_register_write reg_write_cmd = {0}; + struct ipa_hdr_init_system *hdr_init_cmd = NULL; + struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd_hdr = NULL; + struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd_ctx = NULL; + struct ipa_register_write *reg_write_cmd = NULL; int rc = -EFAULT; u32 proc_ctx_size; u32 proc_ctx_ofst; @@ -361,15 +381,21 @@ int __ipa_commit_hdr_v2_5(void) if (hdr_mem.size > IPA_MEM_PART(apps_hdr_size)) { IPAERR("tbl too big needed %d avail %d\n", hdr_mem.size, IPA_MEM_PART(apps_hdr_size)); - goto end; + goto fail_send_cmd1; } else { - dma_cmd_hdr.system_addr = hdr_mem.phys_base; - dma_cmd_hdr.size = hdr_mem.size; - dma_cmd_hdr.local_addr = + dma_cmd_hdr = kzalloc(sizeof(*dma_cmd_hdr), GFP_ATOMIC); + if (dma_cmd_hdr == NULL) { + IPAERR("fail to alloc immediate cmd\n"); + rc = -ENOMEM; + goto fail_send_cmd1; + } + dma_cmd_hdr->system_addr = hdr_mem.phys_base; + dma_cmd_hdr->size = hdr_mem.size; + dma_cmd_hdr->local_addr = ipa_ctx->smem_restricted_bytes + IPA_MEM_PART(apps_hdr_ofst); desc[0].opcode = IPA_DMA_SHARED_MEM; - desc[0].pyld = &dma_cmd_hdr; + desc[0].pyld = (void *)dma_cmd_hdr; desc[0].len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem); } @@ -377,11 +403,18 @@ int __ipa_commit_hdr_v2_5(void) if (hdr_mem.size > IPA_MEM_PART(apps_hdr_size_ddr)) { IPAERR("tbl too big needed %d avail %d\n", hdr_mem.size, IPA_MEM_PART(apps_hdr_size_ddr)); - goto end; + goto fail_send_cmd1; } else { - hdr_init_cmd.hdr_table_addr = hdr_mem.phys_base; + hdr_init_cmd = kzalloc(sizeof(*hdr_init_cmd), + GFP_ATOMIC); + if (hdr_init_cmd == NULL) { + IPAERR("fail to alloc immediate cmd\n"); + rc = -ENOMEM; + goto fail_send_cmd1; + } + hdr_init_cmd->hdr_table_addr = hdr_mem.phys_base; desc[0].opcode = IPA_HDR_INIT_SYSTEM; - desc[0].pyld = &hdr_init_cmd; + desc[0].pyld = (void *)hdr_init_cmd; desc[0].len = sizeof(struct ipa_hdr_init_system); } } @@ -395,15 +428,22 @@ int __ipa_commit_hdr_v2_5(void) IPAERR("tbl too big needed %d avail %d\n", aligned_ctx_mem.size, proc_ctx_size); - goto end; + goto fail_send_cmd1; } else { - dma_cmd_ctx.system_addr = aligned_ctx_mem.phys_base; - dma_cmd_ctx.size = aligned_ctx_mem.size; - dma_cmd_ctx.local_addr = + dma_cmd_ctx = kzalloc(sizeof(*dma_cmd_ctx), + GFP_ATOMIC); + if (dma_cmd_ctx == NULL) { + IPAERR("fail to alloc immediate cmd\n"); + rc = -ENOMEM; + goto fail_send_cmd1; + } + dma_cmd_ctx->system_addr = aligned_ctx_mem.phys_base; + dma_cmd_ctx->size = aligned_ctx_mem.size; + dma_cmd_ctx->local_addr = ipa_ctx->smem_restricted_bytes + proc_ctx_ofst; desc[1].opcode = IPA_DMA_SHARED_MEM; - desc[1].pyld = &dma_cmd_ctx; + desc[1].pyld = (void *)dma_cmd_ctx; desc[1].len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem); } @@ -413,15 +453,23 @@ int __ipa_commit_hdr_v2_5(void) IPAERR("tbl too big, needed %d avail %d\n", aligned_ctx_mem.size, proc_ctx_size_ddr); - goto end; + goto fail_send_cmd1; } else { - reg_write_cmd.offset = IPA_SYS_PKT_PROC_CNTXT_BASE_OFST; - reg_write_cmd.value = aligned_ctx_mem.phys_base; - reg_write_cmd.value_mask = + reg_write_cmd = kzalloc(sizeof(*reg_write_cmd), + GFP_ATOMIC); + if (reg_write_cmd == NULL) { + IPAERR("fail to alloc immediate cmd\n"); + rc = -ENOMEM; + goto fail_send_cmd1; + } + reg_write_cmd->offset = + IPA_SYS_PKT_PROC_CNTXT_BASE_OFST; + reg_write_cmd->value = aligned_ctx_mem.phys_base; + reg_write_cmd->value_mask = ~(IPA_HDR_PROC_CTX_TABLE_ALIGNMENT_BYTE - 1); - desc[1].pyld = ®_write_cmd; + desc[1].pyld = (void *)reg_write_cmd; desc[1].opcode = IPA_REGISTER_WRITE; - desc[1].len = sizeof(reg_write_cmd); + desc[1].len = sizeof(*reg_write_cmd); } } desc[1].type = IPA_IMM_CMD_DESC; @@ -432,22 +480,16 @@ int __ipa_commit_hdr_v2_5(void) else rc = 0; - if (ipa_ctx->hdr_tbl_lcl) { - dma_free_coherent(ipa_ctx->pdev, hdr_mem.size, hdr_mem.base, - hdr_mem.phys_base); - } else { - if (!rc) { - if (ipa_ctx->hdr_mem.phys_base) - dma_free_coherent(ipa_ctx->pdev, - ipa_ctx->hdr_mem.size, - ipa_ctx->hdr_mem.base, - ipa_ctx->hdr_mem.phys_base); - ipa_ctx->hdr_mem = hdr_mem; - } - } +fail_send_cmd1: + + kfree(dma_cmd_hdr); + kfree(hdr_init_cmd); + kfree(dma_cmd_ctx); + kfree(reg_write_cmd); if (ipa_ctx->hdr_proc_ctx_tbl_lcl) { - dma_free_coherent(ipa_ctx->pdev, ctx_mem.size, ctx_mem.base, + dma_free_coherent(ipa_ctx->pdev, ctx_mem.size, + ctx_mem.base, ctx_mem.phys_base); } else { if (!rc) { @@ -457,9 +499,31 @@ int __ipa_commit_hdr_v2_5(void) ipa_ctx->hdr_proc_ctx_mem.base, ipa_ctx->hdr_proc_ctx_mem.phys_base); ipa_ctx->hdr_proc_ctx_mem = ctx_mem; + } else { + dma_free_coherent(ipa_ctx->pdev, ctx_mem.size, + ctx_mem.base, + ctx_mem.phys_base); } } + if (ipa_ctx->hdr_tbl_lcl) { + dma_free_coherent(ipa_ctx->pdev, hdr_mem.size, + hdr_mem.base, + hdr_mem.phys_base); + } else { + if (!rc) { + if (ipa_ctx->hdr_mem.phys_base) + dma_free_coherent(ipa_ctx->pdev, + ipa_ctx->hdr_mem.size, + ipa_ctx->hdr_mem.base, + ipa_ctx->hdr_mem.phys_base); + ipa_ctx->hdr_mem = hdr_mem; + } else { + dma_free_coherent(ipa_ctx->pdev, hdr_mem.size, + hdr_mem.base, + hdr_mem.phys_base); + } + } end: return rc; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index 069c5cbcf4f3..f5dea76764f8 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -696,8 +696,8 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip) struct ipa_desc desc[2]; struct ipa_mem_buffer body; struct ipa_mem_buffer head; - struct ipa_hw_imm_cmd_dma_shared_mem cmd1 = {0}; - struct ipa_hw_imm_cmd_dma_shared_mem cmd2 = {0}; + struct ipa_hw_imm_cmd_dma_shared_mem *cmd1 = NULL; + struct ipa_hw_imm_cmd_dma_shared_mem *cmd2 = NULL; u16 avail; u32 num_modem_rt_index; int rc = 0; @@ -747,34 +747,50 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip) goto fail_send_cmd; } - cmd1.size = head.size; - cmd1.system_addr = head.phys_base; - cmd1.local_addr = local_addr1; + cmd1 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem), + GFP_KERNEL); + if (cmd1 == NULL) { + IPAERR("Failed to alloc immediate command object\n"); + rc = -ENOMEM; + goto fail_send_cmd; + } + + cmd1->size = head.size; + cmd1->system_addr = head.phys_base; + cmd1->local_addr = local_addr1; desc[0].opcode = IPA_DMA_SHARED_MEM; - desc[0].pyld = &cmd1; + desc[0].pyld = (void *)cmd1; desc[0].len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem); desc[0].type = IPA_IMM_CMD_DESC; if (lcl) { - cmd2.size = body.size; - cmd2.system_addr = body.phys_base; - cmd2.local_addr = local_addr2; + cmd2 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem), + GFP_KERNEL); + if (cmd2 == NULL) { + IPAERR("Failed to alloc immediate command object\n"); + rc = -ENOMEM; + goto fail_send_cmd1; + } + + cmd2->size = body.size; + cmd2->system_addr = body.phys_base; + cmd2->local_addr = local_addr2; desc[1].opcode = IPA_DMA_SHARED_MEM; - desc[1].pyld = &cmd2; + desc[1].pyld = (void *)cmd2; desc[1].len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem); desc[1].type = IPA_IMM_CMD_DESC; if (ipa_send_cmd(2, desc)) { IPAERR("fail to send immediate command\n"); rc = -EFAULT; - goto fail_send_cmd; + goto fail_send_cmd2; } } else { if (ipa_send_cmd(1, desc)) { IPAERR("fail to send immediate command\n"); rc = -EFAULT; - goto fail_send_cmd; + goto fail_send_cmd1; } } @@ -785,6 +801,11 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip) IPA_DUMP_BUFF(body.base, body.phys_base, body.size); } __ipa_reap_sys_rt_tbls(ip); + +fail_send_cmd2: + kfree(cmd2); +fail_send_cmd1: + kfree(cmd1); fail_send_cmd: dma_free_coherent(ipa_ctx->pdev, head.size, head.base, head.phys_base); if (body.size) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index e3dfe8927682..81eae05d7ed9 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -1777,7 +1777,8 @@ dealloc_chan_fail: int ipa3_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, bool should_force_clear, u32 qmi_req_id, bool is_dpl) { - struct ipa3_ep_context *ul_ep, *dl_ep; + struct ipa3_ep_context *ul_ep = NULL; + struct ipa3_ep_context *dl_ep; int result = -EFAULT; u32 source_pipe_bitmask = 0; bool dl_data_pending = true; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c index 483b2ca118fa..06f65906841d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.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 @@ -853,6 +853,10 @@ void ipa3_dma_async_memcpy_notify_cb(void *priv mem_info = (struct ipa_mem_buffer *)data; ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS); + if (ep_idx < 0) { + IPADMA_ERR("IPA Client mapping failed\n"); + return; + } sys = ipa3_ctx->ep[ep_idx].sys; spin_lock_irqsave(&ipa3_dma_ctx->async_lock, flags); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 5c678f1cfc28..8ec0974711a4 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -337,7 +337,7 @@ int ipa3_send_one(struct ipa3_sys_context *sys, struct ipa3_desc *desc, int result; u16 sps_flags = SPS_IOVEC_FLAG_EOT; dma_addr_t dma_address; - u16 len; + u16 len = 0; u32 mem_flag = GFP_ATOMIC; if (unlikely(!in_atomic)) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c index c3a12dd0b17c..362294b0f695 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c @@ -14,7 +14,6 @@ #include "ipahal/ipahal.h" #include "ipahal/ipahal_fltrt.h" -#define IPA_FLT_TABLE_INDEX_NOT_FOUND (-1) #define IPA_FLT_STATUS_OF_ADD_FAILED (-1) #define IPA_FLT_STATUS_OF_DEL_FAILED (-1) #define IPA_FLT_STATUS_OF_MDFY_FAILED (-1) @@ -1001,7 +1000,7 @@ error: static int __ipa_add_flt_get_ep_idx(enum ipa_client_type ep, int *ipa_ep_idx) { *ipa_ep_idx = ipa3_get_ep_mapping(ep); - if (*ipa_ep_idx == IPA_FLT_TABLE_INDEX_NOT_FOUND) { + if (*ipa_ep_idx < 0) { IPAERR("ep not valid ep=%d\n", ep); return -EINVAL; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c index 4ef1a96c8450..9e2ffe70170c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.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 @@ -153,10 +153,16 @@ int ipa3_mhi_reset_channel_internal(enum ipa_client_type client) int ipa3_mhi_start_channel_internal(enum ipa_client_type client) { int res; + int ipa_ep_idx; IPA_MHI_FUNC_ENTRY(); - res = ipa3_enable_data_path(ipa3_get_ep_mapping(client)); + ipa_ep_idx = ipa3_get_ep_mapping(client); + if (ipa_ep_idx < 0) { + IPA_MHI_ERR("Invalid client %d\n", client); + return -EINVAL; + } + res = ipa3_enable_data_path(ipa_ep_idx); if (res) { IPA_MHI_ERR("ipa3_enable_data_path failed %d\n", res); return res; @@ -521,6 +527,10 @@ int ipa3_mhi_resume_channels_internal(enum ipa_client_type client, IPA_MHI_FUNC_ENTRY(); ipa_ep_idx = ipa3_get_ep_mapping(client); + if (ipa_ep_idx < 0) { + IPA_MHI_ERR("Invalid client %d\n", client); + return -EINVAL; + } ep = &ipa3_ctx->ep[ipa_ep_idx]; if (brstmode_enabled && !LPTransitionRejected) { @@ -557,11 +567,14 @@ int ipa3_mhi_query_ch_info(enum ipa_client_type client, IPA_MHI_FUNC_ENTRY(); ipa_ep_idx = ipa3_get_ep_mapping(client); - + if (ipa_ep_idx < 0) { + IPA_MHI_ERR("Invalid client %d\n", client); + return -EINVAL; + } ep = &ipa3_ctx->ep[ipa_ep_idx]; res = gsi_query_channel_info(ep->gsi_chan_hdl, ch_info); if (res) { - IPAERR("gsi_query_channel_info failed\n"); + IPA_MHI_ERR("gsi_query_channel_info failed\n"); return res; } @@ -596,7 +609,10 @@ int ipa3_mhi_destroy_channel(enum ipa_client_type client) struct ipa3_ep_context *ep; ipa_ep_idx = ipa3_get_ep_mapping(client); - + if (ipa_ep_idx < 0) { + IPA_MHI_ERR("Invalid client %d\n", client); + return -EINVAL; + } ep = &ipa3_ctx->ep[ipa_ep_idx]; IPA_MHI_DBG("reset event ring (hdl: %lu, ep: %d)\n", diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c index 21ce28204069..c1d1d9659850 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.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 @@ -498,7 +498,7 @@ static int ipa3_uc_send_cmd_64b_param(u32 cmd_lo, u32 cmd_hi, u32 opcode, { int index; union IpaHwCpuCmdCompletedResponseData_t uc_rsp; - unsigned long flags; + unsigned long flags = 0; int retries = 0; send_cmd_lock: @@ -775,7 +775,7 @@ int ipa3_uc_send_cmd(u32 cmd, u32 opcode, u32 expected_status, void ipa3_uc_register_handlers(enum ipa3_hw_features feature, struct ipa3_uc_hdlrs *hdlrs) { - unsigned long flags; + unsigned long flags = 0; if (0 > feature || IPA_HW_FEATURE_MAX <= feature) { IPAERR("Feature %u is invalid, not registering hdlrs\n", diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 9f38af1b520b..2f28ba673d5a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -929,7 +929,7 @@ int ipa3_get_ep_mapping(enum ipa_client_type client) if (client >= IPA_CLIENT_MAX || client < 0) { IPAERR("Bad client number! client =%d\n", client); - return -EINVAL; + return IPA_EP_NOT_ALLOCATED; } ipa_ep_idx = ipa3_ep_mapping[ipa3_get_hw_type_index()][client].pipe_num; @@ -3446,6 +3446,11 @@ void ipa3_suspend_apps_pipes(bool suspend) cfg.ipa_ep_suspend = suspend; ipa_ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_CONS); + if (ipa_ep_idx < 0) { + IPAERR("IPA client mapping failed\n"); + ipa_assert(); + return; + } ep = &ipa3_ctx->ep[ipa_ep_idx]; if (ep->valid) { IPADBG("%s pipe %d\n", suspend ? "suspend" : "unsuspend", diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h index 154045fe4f56..8f85d4ede6b1 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h @@ -322,11 +322,13 @@ struct ipahal_imm_cmd_dma_task_32b_addr { /* * struct ipahal_imm_cmd_pyld - Immediate cmd payload information * @len: length of the buffer + * @reserved: padding bytes to make data buffer aligned * @data: buffer contains the immediate command payload. Buffer goes * back to back with this structure */ struct ipahal_imm_cmd_pyld { u16 len; + u16 reserved; u8 data[0]; }; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c index 72cc4764e7aa..053a581b1d26 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c @@ -2728,7 +2728,7 @@ static int ipa_fltrt_alloc_init_tbl_hdr( params->nhash_hdr.base = dma_alloc_coherent(ipahal_ctx->ipa_pdev, params->nhash_hdr.size, ¶ms->nhash_hdr.phys_base, GFP_KERNEL); - if (!params->nhash_hdr.size) { + if (!params->nhash_hdr.base) { IPAHAL_ERR("fail to alloc DMA buff of size %d\n", params->nhash_hdr.size); goto nhash_alloc_fail; diff --git a/drivers/platform/msm/mhi_uci/mhi_uci.c b/drivers/platform/msm/mhi_uci/mhi_uci.c index 88a213b1b241..96c4671f994f 100644 --- a/drivers/platform/msm/mhi_uci/mhi_uci.c +++ b/drivers/platform/msm/mhi_uci/mhi_uci.c @@ -27,18 +27,17 @@ #include <linux/errno.h> #include <linux/device.h> #include <linux/errno.h> +#include <linux/of_device.h> #define MHI_DEV_NODE_NAME_LEN 13 -#define MHI_MAX_NR_OF_CLIENTS 23 -#define MHI_SOFTWARE_CLIENT_START 0 #define MHI_SOFTWARE_CLIENT_LIMIT 23 -#define MHI_MAX_SOFTWARE_CHANNELS 46 #define TRE_TYPICAL_SIZE 0x1000 #define TRE_MAX_SIZE 0xFFFF -#define MHI_UCI_IPC_LOG_PAGES (100) +#define MHI_UCI_IPC_LOG_PAGES (25) #define MAX_NR_TRBS_PER_CHAN 10 #define DEVICE_NAME "mhi" +#define MHI_UCI_DRIVER_NAME "mhi_uci" #define CTRL_MAGIC 0x4C525443 enum UCI_DBG_LEVEL { @@ -58,8 +57,6 @@ enum UCI_DBG_LEVEL mhi_uci_ipc_log_lvl = UCI_DBG_VERBOSE; enum UCI_DBG_LEVEL mhi_uci_ipc_log_lvl = UCI_DBG_ERROR; #endif -void *mhi_uci_ipc_log; - struct __packed rs232_ctrl_msg { u32 preamble; u32 msg_id; @@ -103,11 +100,12 @@ struct chan_attr { }; struct uci_client { - u32 client_index; u32 out_chan; u32 in_chan; u32 out_chan_state; u32 in_chan_state; + struct chan_attr in_attr; + struct chan_attr out_attr; struct mhi_client_handle *out_handle; struct mhi_client_handle *in_handle; size_t pending_data; @@ -126,22 +124,29 @@ struct uci_client { struct mhi_uci_ctxt_t *uci_ctxt; struct mutex in_chan_lock; struct mutex out_chan_lock; + void *uci_ipc_log; }; struct mhi_uci_ctxt_t { - struct chan_attr chan_attrib[MHI_MAX_SOFTWARE_CHANNELS]; + struct list_head node; + struct platform_dev *pdev; struct uci_client client_handles[MHI_SOFTWARE_CLIENT_LIMIT]; struct mhi_client_info_t client_info; - dev_t start_ctrl_nr; - struct mhi_client_handle *ctrl_handle; + dev_t dev_t; struct mutex ctrl_mutex; - struct cdev cdev[MHI_MAX_SOFTWARE_CHANNELS]; - struct class *mhi_uci_class; - u32 ctrl_chan_id; + struct cdev cdev[MHI_SOFTWARE_CLIENT_LIMIT]; + struct uci_client *ctrl_client; atomic_t mhi_disabled; atomic_t mhi_enable_notif_wq_active; }; +struct mhi_uci_drv_ctxt { + struct list_head head; + struct mutex list_lock; + struct class *mhi_uci_class; + void *mhi_uci_ipc_log; +}; + #define CHAN_TO_CLIENT(_CHAN_NR) (_CHAN_NR / 2) #define CTRL_MSG_ID @@ -219,13 +224,13 @@ struct mhi_uci_ctxt_t { (_PKT)->size = new_val; \ }; -#define uci_log(_msg_lvl, _msg, ...) do { \ +#define uci_log(uci_ipc_log, _msg_lvl, _msg, ...) do { \ if (_msg_lvl >= mhi_uci_msg_lvl) { \ pr_err("[%s] "_msg, __func__, ##__VA_ARGS__); \ } \ - if (mhi_uci_ipc_log && (_msg_lvl >= mhi_uci_ipc_log_lvl)) { \ - ipc_log_string(mhi_uci_ipc_log, \ - "[%s] " _msg, __func__, ##__VA_ARGS__); \ + if (uci_ipc_log && (_msg_lvl >= mhi_uci_ipc_log_lvl)) { \ + ipc_log_string(uci_ipc_log, \ + "[%s] " _msg, __func__, ##__VA_ARGS__); \ } \ } while (0) @@ -246,20 +251,20 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait); static long mhi_uci_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -static struct mhi_uci_ctxt_t uci_ctxt; +static struct mhi_uci_drv_ctxt mhi_uci_drv_ctxt; -static int mhi_init_inbound(struct uci_client *client_handle, - enum MHI_CLIENT_CHANNEL chan) +static int mhi_init_inbound(struct uci_client *client_handle) { int ret_val = 0; u32 i = 0; - struct chan_attr *chan_attributes = - &uci_ctxt.chan_attrib[chan]; + struct chan_attr *chan_attributes = &client_handle->in_attr; void *data_loc = NULL; size_t buf_size = chan_attributes->max_packet_size; if (client_handle == NULL) { - uci_log(UCI_DBG_ERROR, "Bad Input data, quitting\n"); + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_ERROR, + "Bad Input data, quitting\n"); return -EINVAL; } chan_attributes->nr_trbs = @@ -270,12 +275,17 @@ static int mhi_init_inbound(struct uci_client *client_handle, if (!client_handle->in_buf_list) return -ENOMEM; - uci_log(UCI_DBG_INFO, "Channel %d supports %d desc\n", - i, chan_attributes->nr_trbs); + uci_log(client_handle->uci_ipc_log, + UCI_DBG_INFO, "Channel %d supports %d desc\n", + chan_attributes->chan_id, + chan_attributes->nr_trbs); for (i = 0; i < chan_attributes->nr_trbs; ++i) { data_loc = kmalloc(buf_size, GFP_KERNEL); - uci_log(UCI_DBG_INFO, "Allocated buffer %p size %zd\n", - data_loc, buf_size); + uci_log(client_handle->uci_ipc_log, + UCI_DBG_INFO, + "Allocated buffer %p size %zd\n", + data_loc, + buf_size); if (data_loc == NULL) return -ENOMEM; client_handle->in_buf_list[i] = data_loc; @@ -283,9 +293,11 @@ static int mhi_init_inbound(struct uci_client *client_handle, data_loc, buf_size, MHI_EOT); if (0 != ret_val) { kfree(data_loc); - uci_log(UCI_DBG_ERROR, + uci_log(client_handle->uci_ipc_log, + UCI_DBG_ERROR, "Failed insertion for chan %d, ret %d\n", - chan, ret_val); + chan_attributes->chan_id, + ret_val); break; } } @@ -325,7 +337,8 @@ static int mhi_uci_send_packet(struct mhi_client_handle **client_handle, if (is_uspace_buf) { data_loc = kmalloc(data_to_insert_now, GFP_KERNEL); if (NULL == data_loc) { - uci_log(UCI_DBG_ERROR, + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, "Failed to allocate memory 0x%zx\n", data_to_insert_now); return -ENOMEM; @@ -343,13 +356,15 @@ static int mhi_uci_send_packet(struct mhi_client_handle **client_handle, flags = MHI_EOT; if (data_left_to_insert - data_to_insert_now > 0) flags |= MHI_CHAIN | MHI_EOB; - uci_log(UCI_DBG_VERBOSE, - "At trb i = %d/%d, chain = %d, eob = %d, addr 0x%p chan %d\n", - i, nr_avail_trbs, - flags & MHI_CHAIN, - flags & MHI_EOB, - data_loc, - uci_handle->out_chan); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "At trb i = %d/%d, chain = %d, eob = %d, addr 0x%p chan %d\n", + i, + nr_avail_trbs, + flags & MHI_CHAIN, + flags & MHI_EOB, + data_loc, + uci_handle->out_chan); ret_val = mhi_queue_xfer(*client_handle, data_loc, data_to_insert_now, flags); @@ -375,25 +390,34 @@ static int mhi_uci_send_status_cmd(struct uci_client *client) { struct rs232_ctrl_msg *rs232_pkt = NULL; struct uci_client *uci_ctrl_handle; - u32 ctrl_chan_id = uci_ctxt.ctrl_chan_id; - + struct mhi_uci_ctxt_t *uci_ctxt = client->uci_ctxt; int ret_val = 0; size_t pkt_size = sizeof(struct rs232_ctrl_msg); u32 amount_sent; - uci_ctrl_handle = &uci_ctxt.client_handles[ctrl_chan_id/2]; + if (!uci_ctxt->ctrl_client) { + uci_log(client->uci_ipc_log, + UCI_DBG_INFO, + "Control channel is not defined\n"); + return -EIO; + } + + uci_ctrl_handle = uci_ctxt->ctrl_client; mutex_lock(&uci_ctrl_handle->out_chan_lock); if (!atomic_read(&uci_ctrl_handle->mhi_disabled) && !uci_ctrl_handle->out_chan_state) { - uci_log(UCI_DBG_INFO, - "Opening outbound control channel %d\n", - uci_ctrl_handle->out_chan); + uci_log(uci_ctrl_handle->uci_ipc_log, + UCI_DBG_INFO, + "Opening outbound control channel %d for chan:%d\n", + uci_ctrl_handle->out_chan, + client->out_chan); ret_val = mhi_open_channel(uci_ctrl_handle->out_handle); if (0 != ret_val) { - uci_log(UCI_DBG_CRITICAL, + uci_log(uci_ctrl_handle->uci_ipc_log, + UCI_DBG_CRITICAL, "Could not open chan %d, for sideband ctrl\n", - client->out_chan); + uci_ctrl_handle->out_chan); ret_val = -EIO; goto error_open; } @@ -405,7 +429,8 @@ static int mhi_uci_send_status_cmd(struct uci_client *client) ret_val = -ENOMEM; goto error_open; } - uci_log(UCI_DBG_VERBOSE, + uci_log(uci_ctrl_handle->uci_ipc_log, + UCI_DBG_VERBOSE, "Received request to send msg for chan %d\n", client->out_chan); rs232_pkt->preamble = CTRL_MAGIC; @@ -423,9 +448,11 @@ static int mhi_uci_send_status_cmd(struct uci_client *client) pkt_size, 0); if (pkt_size != amount_sent) { - uci_log(UCI_DBG_INFO, - "Failed to send signal on chan %d, ret : %d\n", - client->out_chan, ret_val); + uci_log(uci_ctrl_handle->uci_ipc_log, + UCI_DBG_INFO, + "Failed to send signal for chan %d, ret : %d\n", + client->out_chan, + ret_val); goto error; } error_open: @@ -451,15 +478,20 @@ static int mhi_uci_tiocm_set(struct uci_client *client_ctxt, u32 set, u32 clear) client_ctxt->local_tiocm |= status_set; client_ctxt->local_tiocm &= ~status_clear; - uci_log(UCI_DBG_VERBOSE, + uci_log(client_ctxt->uci_ipc_log, + UCI_DBG_VERBOSE, "Old TIOCM0x%x for chan %d, Current TIOCM 0x%x\n", - old_status, client_ctxt->out_chan, client_ctxt->local_tiocm); + old_status, + client_ctxt->out_chan, + client_ctxt->local_tiocm); mutex_unlock(&client_ctxt->uci_ctxt->ctrl_mutex); if (client_ctxt->local_tiocm != old_status) { - uci_log(UCI_DBG_VERBOSE, + uci_log(client_ctxt->uci_ipc_log, + UCI_DBG_VERBOSE, "Setting TIOCM to 0x%x for chan %d\n", - client_ctxt->local_tiocm, client_ctxt->out_chan); + client_ctxt->local_tiocm, + client_ctxt->out_chan); return mhi_uci_send_status_cmd(client_ctxt); } return 0; @@ -474,26 +506,35 @@ static long mhi_uci_ctl_ioctl(struct file *file, unsigned int cmd, uci_handle = file->private_data; if (uci_handle == NULL) { - uci_log(UCI_DBG_VERBOSE, - "Invalid handle for client.\n"); + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_VERBOSE, + "Invalid handle for client\n"); return -ENODEV; } - uci_log(UCI_DBG_VERBOSE, - "Attempting to dtr cmd 0x%x arg 0x%lx for chan %d\n", - cmd, arg, uci_handle->out_chan); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "Attempting to dtr cmd 0x%x arg 0x%lx for chan %d\n", + cmd, + arg, + uci_handle->out_chan); switch (cmd) { case TIOCMGET: - uci_log(UCI_DBG_VERBOSE, - "Returning 0x%x mask\n", uci_handle->local_tiocm); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "Returning 0x%x mask\n", + uci_handle->local_tiocm); ret_val = uci_handle->local_tiocm; break; case TIOCMSET: if (0 != copy_from_user(&set_val, (void *)arg, sizeof(set_val))) return -ENOMEM; - uci_log(UCI_DBG_VERBOSE, + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, "Attempting to set cmd 0x%x arg 0x%x for chan %d\n", - cmd, set_val, uci_handle->out_chan); + cmd, + set_val, + uci_handle->out_chan); ret_val = mhi_uci_tiocm_set(uci_handle, set_val, ~set_val); break; default: @@ -506,28 +547,36 @@ static long mhi_uci_ctl_ioctl(struct file *file, unsigned int cmd, static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; - struct uci_client *uci_handle = NULL; - uci_handle = file->private_data; + struct uci_client *uci_handle = file->private_data; + struct mhi_uci_ctxt_t *uci_ctxt; if (uci_handle == NULL) return -ENODEV; + + uci_ctxt = uci_handle->uci_ctxt; poll_wait(file, &uci_handle->read_wq, wait); poll_wait(file, &uci_handle->write_wq, wait); if (atomic_read(&uci_handle->avail_pkts) > 0) { - uci_log(UCI_DBG_VERBOSE, - "Client can read chan %d\n", uci_handle->in_chan); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "Client can read chan %d\n", + uci_handle->in_chan); mask |= POLLIN | POLLRDNORM; } - if (!atomic_read(&uci_ctxt.mhi_disabled) && + if (!atomic_read(&uci_ctxt->mhi_disabled) && (mhi_get_free_desc(uci_handle->out_handle) > 0)) { - uci_log(UCI_DBG_VERBOSE, - "Client can write chan %d\n", uci_handle->out_chan); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "Client can write chan %d\n", + uci_handle->out_chan); mask |= POLLOUT | POLLWRNORM; } - uci_log(UCI_DBG_VERBOSE, + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, "Client attempted to poll chan %d, returning mask 0x%x\n", - uci_handle->in_chan, mask); + uci_handle->in_chan, + mask); return mask; } @@ -535,10 +584,12 @@ static int open_client_mhi_channels(struct uci_client *uci_client) { int ret_val = 0; int r = 0; - uci_log(UCI_DBG_INFO, - "Starting channels %d %d.\n", - uci_client->out_chan, - uci_client->in_chan); + + uci_log(uci_client->uci_ipc_log, + UCI_DBG_INFO, + "Starting channels %d %d\n", + uci_client->out_chan, + uci_client->in_chan); mutex_lock(&uci_client->out_chan_lock); mutex_lock(&uci_client->in_chan_lock); ret_val = mhi_open_channel(uci_client->out_handle); @@ -553,21 +604,26 @@ static int open_client_mhi_channels(struct uci_client *uci_client) ret_val = mhi_open_channel(uci_client->in_handle); if (ret_val != 0) { - uci_log(UCI_DBG_ERROR, - "Failed to open chan %d, ret 0x%x\n", - uci_client->out_chan, ret_val); + uci_log(uci_client->uci_ipc_log, + UCI_DBG_ERROR, + "Failed to open chan %d, ret 0x%x\n", + uci_client->out_chan, + ret_val); goto handle_in_err; } - uci_log(UCI_DBG_INFO, - "Initializing inbound chan %d.\n", - uci_client->in_chan); + uci_log(uci_client->uci_ipc_log, + UCI_DBG_INFO, + "Initializing inbound chan %d\n", + uci_client->in_chan); uci_client->in_chan_state = 1; - ret_val = mhi_init_inbound(uci_client, uci_client->in_chan); + ret_val = mhi_init_inbound(uci_client); if (0 != ret_val) { - uci_log(UCI_DBG_ERROR, - "Failed to init inbound 0x%x, ret 0x%x\n", - uci_client->in_chan, ret_val); + uci_log(uci_client->uci_ipc_log, + UCI_DBG_ERROR, + "Failed to init inbound 0x%x, ret 0x%x\n", + uci_client->in_chan, + ret_val); } mutex_unlock(&uci_client->in_chan_lock); @@ -583,37 +639,54 @@ handle_not_rdy_err: return r; } -static int mhi_uci_client_open(struct inode *mhi_inode, +static int mhi_uci_client_open(struct inode *inode, struct file *file_handle) { struct uci_client *uci_handle = NULL; + struct mhi_uci_ctxt_t *uci_ctxt = NULL, *itr; int r = 0; - int client_id = iminor(mhi_inode); - uci_handle = &uci_ctxt.client_handles[client_id]; - - if (NULL == uci_handle) + int client_id = iminor(inode); + int major = imajor(inode); + + /* Find the uci ctxt from major */ + mutex_lock(&mhi_uci_drv_ctxt.list_lock); + list_for_each_entry(itr, &mhi_uci_drv_ctxt.head, node) { + if (MAJOR(itr->dev_t) == major) { + uci_ctxt = itr; + break; + } + } + mutex_unlock(&mhi_uci_drv_ctxt.list_lock); + if (!uci_ctxt || client_id >= MHI_SOFTWARE_CLIENT_LIMIT) return -EINVAL; + uci_handle = &uci_ctxt->client_handles[client_id]; if (atomic_read(&uci_handle->mhi_disabled)) { - uci_log(UCI_DBG_INFO, - "MHI is still recovering from SSR, client %d\n", + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_INFO, + "MHI channel still disable for, client %d\n", client_id); msleep(500); return -EAGAIN; } - uci_log(UCI_DBG_INFO, + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_INFO, "Client opened device node 0x%x, ref count 0x%x\n", - iminor(mhi_inode), atomic_read(&uci_handle->ref_count)); + client_id, + atomic_read(&uci_handle->ref_count)); if (1 == atomic_add_return(1, &uci_handle->ref_count)) { - uci_handle->uci_ctxt = &uci_ctxt; - uci_log(UCI_DBG_INFO, - "Opening channels client %d\n", client_id); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_INFO, + "Opening channels client %d\n", + client_id); r = open_client_mhi_channels(uci_handle); if (r) - uci_log(UCI_DBG_INFO, - "Failed to open channels ret %d\n", r); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_INFO, + "Failed to open channels ret %d\n", + r); } file_handle->private_data = uci_handle; return r; @@ -623,7 +696,6 @@ static int mhi_uci_client_release(struct inode *mhi_inode, struct file *file_handle) { struct uci_client *uci_handle = file_handle->private_data; - struct mhi_uci_ctxt_t *uci_ctxt; u32 nr_in_bufs = 0; int in_chan = 0; int i = 0; @@ -632,21 +704,22 @@ static int mhi_uci_client_release(struct inode *mhi_inode, if (uci_handle == NULL) return -EINVAL; - uci_ctxt = uci_handle->uci_ctxt; - in_chan = iminor(mhi_inode) + 1; - nr_in_bufs = uci_ctxt->chan_attrib[in_chan].nr_trbs; - buf_size = uci_ctxt->chan_attrib[in_chan].max_packet_size; + in_chan = uci_handle->in_attr.chan_id; + nr_in_bufs = uci_handle->in_attr.nr_trbs; + buf_size = uci_handle->in_attr.max_packet_size; if (atomic_sub_return(1, &uci_handle->ref_count) == 0) { - uci_log(UCI_DBG_ERROR, - "Last client left, closing channel 0x%x\n", - iminor(mhi_inode)); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_ERROR, + "Last client left, closing channel 0x%x\n", + in_chan); if (atomic_read(&uci_handle->out_pkt_pend_ack)) - uci_log(UCI_DBG_CRITICAL, + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_CRITICAL, "Still waiting on %d acks!, chan %d\n", atomic_read(&uci_handle->out_pkt_pend_ack), - iminor(mhi_inode)); + uci_handle->out_attr.chan_id); mhi_close_channel(uci_handle->out_handle); mhi_close_channel(uci_handle->in_handle); @@ -659,7 +732,8 @@ static int mhi_uci_client_release(struct inode *mhi_inode, kfree(uci_handle->in_buf_list); atomic_set(&uci_handle->avail_pkts, 0); } else { - uci_log(UCI_DBG_ERROR, + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_ERROR, "Client close chan %d, ref count 0x%x\n", iminor(mhi_inode), atomic_read(&uci_handle->ref_count)); @@ -689,17 +763,23 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, mutex = &uci_handle->in_chan_lock; chan = uci_handle->in_chan; mutex_lock(mutex); - buf_size = uci_ctxt.chan_attrib[chan].max_packet_size; + buf_size = uci_handle->in_attr.max_packet_size; + result.buf_addr = NULL; - uci_log(UCI_DBG_VERBOSE, "Client attempted read on chan %d\n", chan); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "Client attempted read on chan %d\n", + chan); do { if (!uci_handle->pkt_loc && - !atomic_read(&uci_ctxt.mhi_disabled)) { + !atomic_read(&uci_handle->uci_ctxt->mhi_disabled)) { ret_val = mhi_poll_inbound(client_handle, &result); if (ret_val) { - uci_log(UCI_DBG_ERROR, - "Failed to poll inbound ret %d avail pkt %d\n", - ret_val, atomic_read(&uci_handle->avail_pkts)); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_ERROR, + "Failed to poll inbound ret %d avail pkt %d\n", + ret_val, + atomic_read(&uci_handle->avail_pkts)); } if (result.buf_addr) uci_handle->pkt_loc = result.buf_addr; @@ -707,23 +787,27 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, uci_handle->pkt_loc = 0; uci_handle->pkt_size = result.bytes_xferd; *bytes_pending = uci_handle->pkt_size; - uci_log(UCI_DBG_VERBOSE, + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, "Got pkt size 0x%zx at addr 0x%lx, chan %d\n", uci_handle->pkt_size, - (uintptr_t)result.buf_addr, chan); + (uintptr_t)result.buf_addr, + chan); } if ((*bytes_pending == 0 || uci_handle->pkt_loc == 0) && (atomic_read(&uci_handle->avail_pkts) <= 0)) { /* If nothing was copied yet, wait for data */ - uci_log(UCI_DBG_VERBOSE, - "No data avail_pkts %d, chan %d\n", - atomic_read(&uci_handle->avail_pkts), - chan); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "No data avail_pkts %d, chan %d\n", + atomic_read(&uci_handle->avail_pkts), + chan); ret_val = wait_event_interruptible( uci_handle->read_wq, (atomic_read(&uci_handle->avail_pkts) > 0)); if (ret_val == -ERESTARTSYS) { - uci_log(UCI_DBG_ERROR, + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_ERROR, "Exit signal caught\n"); goto error; } @@ -732,7 +816,8 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, 0 == uci_handle->pkt_size && 0 == uci_handle->pkt_loc && uci_handle->mhi_status == -ENETRESET) { - uci_log(UCI_DBG_ERROR, + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_ERROR, "Detected pending reset, reporting chan %d\n", chan); atomic_dec(&uci_handle->avail_pkts); @@ -743,23 +828,24 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, } else if (atomic_read(&uci_handle->avail_pkts) && uci_handle->pkt_size != 0 && uci_handle->pkt_loc != 0) { - uci_log(UCI_DBG_VERBOSE, - "Got packet: avail pkts %d phy_adr 0x%p, chan %d\n", - atomic_read(&uci_handle->avail_pkts), - result.buf_addr, - chan); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "Got packet: avail pkts %d phy_adr 0x%p, chan %d\n", + atomic_read(&uci_handle->avail_pkts), + result.buf_addr, + chan); break; /* * MHI did not return a valid packet, but we have one * which we did not finish returning to user */ } else { - uci_log(UCI_DBG_CRITICAL, - "chan %d err: avail pkts %d phy_adr 0x%p mhi_stat%d\n", - chan, - atomic_read(&uci_handle->avail_pkts), - result.buf_addr, - uci_handle->mhi_status); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_CRITICAL, + "chan %d err: avail pkts %d mhi_stat%d\n", + chan, + atomic_read(&uci_handle->avail_pkts), + uci_handle->mhi_status); return -EIO; } } while (!uci_handle->pkt_loc); @@ -775,9 +861,12 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, bytes_copied = *bytes_pending; *bytes_pending = 0; - uci_log(UCI_DBG_VERBOSE, - "Copied 0x%zx of 0x%x, chan %d\n", - bytes_copied, (u32)*bytes_pending, chan); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "Copied 0x%zx of 0x%x, chan %d\n", + bytes_copied, + (u32)*bytes_pending, + chan); } else { addr_offset = uci_handle->pkt_size - *bytes_pending; if (copy_to_user(buf, @@ -789,39 +878,50 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf, } bytes_copied = uspace_buf_size; *bytes_pending -= uspace_buf_size; - uci_log(UCI_DBG_VERBOSE, - "Copied 0x%zx of 0x%x,chan %d\n", - bytes_copied, - (u32)*bytes_pending, - chan); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "Copied 0x%zx of 0x%x,chan %d\n", + bytes_copied, + (u32)*bytes_pending, + chan); } /* We finished with this buffer, map it back */ if (*bytes_pending == 0) { - uci_log(UCI_DBG_VERBOSE, "Pkt loc %p ,chan %d\n", - uci_handle->pkt_loc, chan); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "Pkt loc %p ,chan %d\n", + uci_handle->pkt_loc, + chan); memset(uci_handle->pkt_loc, 0, buf_size); atomic_dec(&uci_handle->avail_pkts); - uci_log(UCI_DBG_VERBOSE, - "Decremented avail pkts avail 0x%x\n", - atomic_read(&uci_handle->avail_pkts)); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "Decremented avail pkts avail 0x%x\n", + atomic_read(&uci_handle->avail_pkts)); ret_val = mhi_queue_xfer(client_handle, uci_handle->pkt_loc, buf_size, MHI_EOT); if (0 != ret_val) { - uci_log(UCI_DBG_ERROR, - "Failed to recycle element\n"); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_ERROR, + "Failed to recycle element\n"); ret_val = -EIO; goto error; } uci_handle->pkt_loc = 0; } - uci_log(UCI_DBG_VERBOSE, - "Returning 0x%zx bytes, 0x%x bytes left\n", - bytes_copied, (u32)*bytes_pending); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "Returning 0x%zx bytes, 0x%x bytes left\n", + bytes_copied, + (u32)*bytes_pending); mutex_unlock(mutex); return bytes_copied; error: mutex_unlock(mutex); - uci_log(UCI_DBG_ERROR, "Returning %d\n", ret_val); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_ERROR, + "Returning %d\n", + ret_val); return ret_val; } @@ -846,9 +946,10 @@ static ssize_t mhi_uci_client_write(struct file *file, ret_val = mhi_uci_send_packet(&uci_handle->out_handle, (void *)buf, count, 1); if (!ret_val) { - uci_log(UCI_DBG_VERBOSE, - "No descriptors available, did we poll, chan %d?\n", - chan); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, + "No descriptors available, did we poll, chan %d?\n", + chan); mutex_unlock(&uci_handle->out_chan_lock); ret_val = wait_event_interruptible( @@ -857,8 +958,9 @@ static ssize_t mhi_uci_client_write(struct file *file, mutex_lock(&uci_handle->out_chan_lock); if (-ERESTARTSYS == ret_val) { goto sys_interrupt; - uci_log(UCI_DBG_WARNING, - "Waitqueue cancelled by system\n"); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_WARNING, + "Waitqueue cancelled by system\n"); } } } @@ -867,60 +969,79 @@ sys_interrupt: return ret_val; } -static int uci_init_client_attributes(struct mhi_uci_ctxt_t - *uci_ctxt) +static int uci_init_client_attributes(struct mhi_uci_ctxt_t *uci_ctxt, + struct device_node *of_node) { - u32 i = 0; - struct chan_attr *chan_attrib = NULL; - size_t max_packet_size = TRE_TYPICAL_SIZE; - - for (i = 0; i < MHI_MAX_SOFTWARE_CHANNELS; ++i) { - max_packet_size = TRE_TYPICAL_SIZE; - chan_attrib = &uci_ctxt->chan_attrib[i]; - switch (i) { - case MHI_CLIENT_SAHARA_IN: - max_packet_size = TRE_MAX_SIZE; - case MHI_CLIENT_SAHARA_OUT: - case MHI_CLIENT_LOOPBACK_OUT: - case MHI_CLIENT_LOOPBACK_IN: - case MHI_CLIENT_EFS_OUT: - case MHI_CLIENT_EFS_IN: - case MHI_CLIENT_QMI_OUT: - case MHI_CLIENT_QMI_IN: - case MHI_CLIENT_IP_CTRL_0_OUT: - case MHI_CLIENT_IP_CTRL_0_IN: - case MHI_CLIENT_IP_CTRL_1_OUT: - case MHI_CLIENT_IP_CTRL_1_IN: - case MHI_CLIENT_BL_OUT: - case MHI_CLIENT_BL_IN: - case MHI_CLIENT_DUN_OUT: - case MHI_CLIENT_DUN_IN: - case MHI_CLIENT_TF_OUT: - case MHI_CLIENT_TF_IN: + int num_rows, ret_val = 0; + int i, dir; + u32 ctrl_chan = -1; + u32 *chan_info, *itr; + const char *prop_name = "qcom,mhi-uci-channels"; + + ret_val = of_property_read_u32(of_node, "qcom,mhi-uci-ctrlchan", + &ctrl_chan); + if (ret_val) { + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_INFO, + "Could not find property 'qcom,mhi-uci-ctrlchan'\n"); + } + + num_rows = of_property_count_elems_of_size(of_node, prop_name, + sizeof(u32) * 4); + /* At least one pair of channels should exist */ + if (num_rows < 1) { + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_CRITICAL, + "Missing or invalid property 'qcom,mhi-uci-channels'\n"); + return -ENODEV; + } + + if (num_rows > MHI_SOFTWARE_CLIENT_LIMIT) + num_rows = MHI_SOFTWARE_CLIENT_LIMIT; + + chan_info = kmalloc_array(num_rows, 4 * sizeof(*chan_info), GFP_KERNEL); + if (!chan_info) + return -ENOMEM; + + ret_val = of_property_read_u32_array(of_node, prop_name, chan_info, + num_rows * 4); + if (ret_val) + goto error_dts; + + for (i = 0, itr = chan_info; i < num_rows; i++) { + struct uci_client *client = &uci_ctxt->client_handles[i]; + struct chan_attr *chan_attrib; + + for (dir = 0; dir < 2; dir++) { + chan_attrib = (dir) ? + &client->in_attr : &client->out_attr; chan_attrib->uci_ownership = 1; - break; - default: - chan_attrib->uci_ownership = 0; - break; - } - if (chan_attrib->uci_ownership) { - chan_attrib->chan_id = i; - chan_attrib->max_packet_size = max_packet_size; + chan_attrib->chan_id = *itr++; + chan_attrib->max_packet_size = *itr++; + if (dir == 0) + chan_attrib->dir = MHI_DIR_OUT; + else + chan_attrib->dir = MHI_DIR_IN; + + if (chan_attrib->chan_id == ctrl_chan) + uci_ctxt->ctrl_client = client; } - if (i % 2 == 0) - chan_attrib->dir = MHI_DIR_OUT; - else - chan_attrib->dir = MHI_DIR_IN; } - return 0; + +error_dts: + kfree(chan_info); + return ret_val; } static int process_mhi_disabled_notif_sync(struct uci_client *uci_handle) { - uci_log(UCI_DBG_INFO, "Entered.\n"); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_INFO, + "Entered.\n"); if (uci_handle->mhi_status != -ENETRESET) { - uci_log(UCI_DBG_CRITICAL, - "Setting reset for chan %d.\n", + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_CRITICAL, + "Setting reset for chan %d\n", uci_handle->out_chan); uci_handle->pkt_size = 0; uci_handle->pkt_loc = NULL; @@ -932,39 +1053,53 @@ static int process_mhi_disabled_notif_sync(struct uci_client *uci_handle) uci_handle->in_chan_state = 0; wake_up(&uci_handle->read_wq); } else { - uci_log(UCI_DBG_CRITICAL, - "Chan %d state already reset.\n", + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_CRITICAL, + "Chan %d state already reset\n", uci_handle->out_chan); } - uci_log(UCI_DBG_INFO, "Exited.\n"); + uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO, "Exited\n"); return 0; } -static void process_rs232_state(struct mhi_result *result) +static void process_rs232_state(struct uci_client *ctrl_client, + struct mhi_result *result) { struct rs232_ctrl_msg *rs232_pkt; - struct uci_client *client; + struct uci_client *client = NULL; + struct mhi_uci_ctxt_t *uci_ctxt = ctrl_client->uci_ctxt; u32 msg_id; - int ret_val; + int ret_val, i; u32 chan; - mutex_lock(&uci_ctxt.ctrl_mutex); + mutex_lock(&uci_ctxt->ctrl_mutex); if (result->transaction_status != 0) { - uci_log(UCI_DBG_ERROR, + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_ERROR, "Non successful transfer code 0x%x\n", - result->transaction_status); + result->transaction_status); goto error_bad_xfer; } if (result->bytes_xferd != sizeof(struct rs232_ctrl_msg)) { - uci_log(UCI_DBG_ERROR, - "Buffer is of wrong size is: 0x%zx: expected 0x%zx\n", - result->bytes_xferd, sizeof(struct rs232_ctrl_msg)); + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_ERROR, + "Buffer is of wrong size is: 0x%zx: expected 0x%zx\n", + result->bytes_xferd, + sizeof(struct rs232_ctrl_msg)); goto error_size; } rs232_pkt = result->buf_addr; MHI_GET_CTRL_DEST_ID(CTRL_DEST_ID, rs232_pkt, chan); - client = &uci_ctxt.client_handles[chan / 2]; + for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; i++) + if (chan == uci_ctxt->client_handles[i].out_chan || + chan == uci_ctxt->client_handles[i].in_chan) { + client = &uci_ctxt->client_handles[i]; + break; + } + /* No valid channel found */ + if (!client) + goto error_bad_xfer; MHI_GET_CTRL_MSG_ID(CTRL_MSG_ID, rs232_pkt, msg_id); client->local_tiocm = 0; @@ -982,35 +1117,38 @@ static void process_rs232_state(struct mhi_result *result) error_bad_xfer: error_size: memset(rs232_pkt, 0, sizeof(struct rs232_ctrl_msg)); - ret_val = mhi_queue_xfer(client->in_handle, - result->buf_addr, - result->bytes_xferd, - result->flags); + ret_val = mhi_queue_xfer(ctrl_client->in_handle, + result->buf_addr, + result->bytes_xferd, + result->flags); if (0 != ret_val) { - uci_log(UCI_DBG_ERROR, - "Failed to recycle ctrl msg buffer\n"); + uci_log(ctrl_client->uci_ipc_log, + UCI_DBG_ERROR, + "Failed to recycle ctrl msg buffer\n"); } - mutex_unlock(&uci_ctxt.ctrl_mutex); + mutex_unlock(&uci_ctxt->ctrl_mutex); } static void parse_inbound_ack(struct uci_client *uci_handle, struct mhi_result *result) { atomic_inc(&uci_handle->avail_pkts); - uci_log(UCI_DBG_VERBOSE, + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, "Received cb on chan %d, avail pkts: 0x%x\n", uci_handle->in_chan, atomic_read(&uci_handle->avail_pkts)); wake_up(&uci_handle->read_wq); - if (uci_handle->in_chan == MHI_CLIENT_IP_CTRL_1_IN) - process_rs232_state(result); + if (uci_handle == uci_handle->uci_ctxt->ctrl_client) + process_rs232_state(uci_handle, result); } static void parse_outbound_ack(struct uci_client *uci_handle, struct mhi_result *result) { kfree(result->buf_addr); - uci_log(UCI_DBG_VERBOSE, + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, "Received ack on chan %d, pending acks: 0x%x\n", uci_handle->out_chan, atomic_read(&uci_handle->out_pkt_pend_ack)); @@ -1021,88 +1159,96 @@ static void parse_outbound_ack(struct uci_client *uci_handle, static void uci_xfer_cb(struct mhi_cb_info *cb_info) { - int chan_nr; struct uci_client *uci_handle = NULL; - u32 client_index; struct mhi_result *result; if (!cb_info || !cb_info->result) { - uci_log(UCI_DBG_CRITICAL, "Bad CB info from MHI.\n"); + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_CRITICAL, + "Bad CB info from MHI\n"); return; } - chan_nr = (uintptr_t)cb_info->result->user_data; - client_index = CHAN_TO_CLIENT(chan_nr); - uci_handle = &uci_ctxt.client_handles[client_index]; - + uci_handle = cb_info->result->user_data; switch (cb_info->cb_reason) { case MHI_CB_MHI_ENABLED: atomic_set(&uci_handle->mhi_disabled, 0); break; case MHI_CB_MHI_DISABLED: atomic_set(&uci_handle->mhi_disabled, 1); - uci_log(UCI_DBG_INFO, "MHI disabled CB received.\n"); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_INFO, + "MHI disabled CB received\n"); process_mhi_disabled_notif_sync(uci_handle); break; case MHI_CB_XFER: if (!cb_info->result) { - uci_log(UCI_DBG_CRITICAL, - "Failed to obtain mhi result from CB.\n"); + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_CRITICAL, + "Failed to obtain mhi result from CB\n"); return; } result = cb_info->result; - chan_nr = (uintptr_t)result->user_data; - client_index = chan_nr / 2; - uci_handle = - &uci_ctxt.client_handles[client_index]; - if (chan_nr % 2) + if (cb_info->chan % 2) parse_inbound_ack(uci_handle, result); else parse_outbound_ack(uci_handle, result); break; default: - uci_log(UCI_DBG_VERBOSE, + uci_log(uci_handle->uci_ipc_log, + UCI_DBG_VERBOSE, "Cannot handle cb reason 0x%x\n", cb_info->cb_reason); } } -static int mhi_register_client(struct uci_client *mhi_client, int index) +static int mhi_register_client(struct uci_client *mhi_client) { int ret_val = 0; - uci_log(UCI_DBG_INFO, "Setting up workqueues.\n"); + uci_log(mhi_client->uci_ipc_log, + UCI_DBG_INFO, + "Setting up workqueues\n"); init_waitqueue_head(&mhi_client->read_wq); init_waitqueue_head(&mhi_client->write_wq); - mhi_client->out_chan = index * 2; - mhi_client->in_chan = index * 2 + 1; - mhi_client->client_index = index; + mhi_client->out_chan = mhi_client->out_attr.chan_id; + mhi_client->in_chan = mhi_client->in_attr.chan_id; mutex_init(&mhi_client->in_chan_lock); mutex_init(&mhi_client->out_chan_lock); atomic_set(&mhi_client->mhi_disabled, 1); - uci_log(UCI_DBG_INFO, "Registering chan %d.\n", mhi_client->out_chan); + uci_log(mhi_client->uci_ipc_log, + 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, - &uci_ctxt.client_info, - (void *)(uintptr_t)(mhi_client->out_chan)); + &mhi_client->uci_ctxt->client_info, + mhi_client); if (0 != ret_val) - uci_log(UCI_DBG_ERROR, + uci_log(mhi_client->uci_ipc_log, + UCI_DBG_ERROR, "Failed to init outbound chan 0x%x, ret 0x%x\n", - mhi_client->out_chan, ret_val); + mhi_client->out_chan, + ret_val); - uci_log(UCI_DBG_INFO, "Registering chan %d.\n", mhi_client->in_chan); + uci_log(mhi_client->uci_ipc_log, + 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, - &uci_ctxt.client_info, - (void *)(uintptr_t)(mhi_client->in_chan)); + &mhi_client->uci_ctxt->client_info, + mhi_client); if (0 != ret_val) - uci_log(UCI_DBG_ERROR, + uci_log(mhi_client->uci_ipc_log, + UCI_DBG_ERROR, "Failed to init inbound chan 0x%x, ret 0x%x\n", - mhi_client->in_chan, ret_val); + mhi_client->in_chan, + ret_val); return 0; } @@ -1115,119 +1261,215 @@ static const struct file_operations mhi_uci_client_fops = { .unlocked_ioctl = mhi_uci_ctl_ioctl, }; -static int mhi_uci_init(void) +static int mhi_uci_probe(struct platform_device *pdev) { - u32 i = 0; - int ret_val = 0; - struct uci_client *mhi_client = NULL; - s32 r = 0; - mhi_uci_ipc_log = ipc_log_context_create(MHI_UCI_IPC_LOG_PAGES, - "mhi-uci", 0); - if (mhi_uci_ipc_log == NULL) { - uci_log(UCI_DBG_WARNING, - "Failed to create IPC logging context\n"); - } - uci_log(UCI_DBG_INFO, "Setting up work queues.\n"); - uci_ctxt.client_info.mhi_client_cb = uci_xfer_cb; + struct mhi_uci_ctxt_t *uci_ctxt; + int ret_val; + int i; + char node_name[16]; + + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_INFO, + "Entered with pdev:%p\n", + pdev); + + if (pdev->dev.of_node == NULL) + return -ENODEV; + + pdev->id = of_alias_get_id(pdev->dev.of_node, "mhi_uci"); + if (pdev->id < 0) + return -ENODEV; + + uci_ctxt = devm_kzalloc(&pdev->dev, + sizeof(*uci_ctxt), + GFP_KERNEL); + if (!uci_ctxt) + return -ENOMEM; - mutex_init(&uci_ctxt.ctrl_mutex); + uci_ctxt->client_info.mhi_client_cb = uci_xfer_cb; + mutex_init(&uci_ctxt->ctrl_mutex); - uci_log(UCI_DBG_INFO, "Setting up channel attributes.\n"); - ret_val = uci_init_client_attributes(&uci_ctxt); + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_INFO, + "Setting up channel attributes\n"); + ret_val = uci_init_client_attributes(uci_ctxt, + pdev->dev.of_node); if (ret_val) { - uci_log(UCI_DBG_ERROR, - "Failed to init client attributes\n"); + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_ERROR, + "Failed to init client attributes\n"); return -EIO; } - uci_ctxt.ctrl_chan_id = MHI_CLIENT_IP_CTRL_1_OUT; - uci_log(UCI_DBG_INFO, "Registering for MHI events.\n"); + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_INFO, + "Registering for MHI events\n"); for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) { - if (uci_ctxt.chan_attrib[i * 2].uci_ownership) { - mhi_client = &uci_ctxt.client_handles[i]; - r = mhi_register_client(mhi_client, i); - if (r) { - uci_log(UCI_DBG_CRITICAL, + struct uci_client *uci_client = &uci_ctxt->client_handles[i]; + + uci_client->uci_ctxt = uci_ctxt; + if (uci_client->in_attr.uci_ownership) { + ret_val = mhi_register_client(uci_client); + if (ret_val) { + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_CRITICAL, "Failed to reg client %d ret %d\n", - r, i); + ret_val, + i); + + return -EIO; } + snprintf(node_name, sizeof(node_name), "mhi-uci%d", + uci_client->out_attr.chan_id); + uci_client->uci_ipc_log = ipc_log_context_create + (MHI_UCI_IPC_LOG_PAGES, + node_name, + 0); } } - uci_log(UCI_DBG_INFO, "Allocating char devices.\n"); - r = alloc_chrdev_region(&uci_ctxt.start_ctrl_nr, - 0, MHI_MAX_SOFTWARE_CHANNELS, - DEVICE_NAME); - - if (IS_ERR_VALUE(r)) { - uci_log(UCI_DBG_ERROR, - "Failed to alloc char devs, ret 0x%x\n", r); + + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_INFO, + "Allocating char devices\n"); + ret_val = alloc_chrdev_region(&uci_ctxt->dev_t, + 0, + MHI_SOFTWARE_CLIENT_LIMIT, + DEVICE_NAME); + if (IS_ERR_VALUE(ret_val)) { + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_ERROR, + "Failed to alloc char devs, ret 0x%x\n", ret_val); goto failed_char_alloc; } - uci_log(UCI_DBG_INFO, "Creating class\n"); - uci_ctxt.mhi_uci_class = class_create(THIS_MODULE, - DEVICE_NAME); - if (IS_ERR(uci_ctxt.mhi_uci_class)) { - uci_log(UCI_DBG_ERROR, - "Failed to instantiate class, ret 0x%x\n", r); - r = -ENOMEM; - goto failed_class_add; - } - uci_log(UCI_DBG_INFO, "Setting up device nodes.\n"); + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_INFO, + "Setting up device nodes. for dev_t: 0x%x major:0x%x\n", + uci_ctxt->dev_t, + MAJOR(uci_ctxt->dev_t)); for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) { - if (uci_ctxt.chan_attrib[i*2].uci_ownership) { - cdev_init(&uci_ctxt.cdev[i], &mhi_uci_client_fops); - uci_ctxt.cdev[i].owner = THIS_MODULE; - r = cdev_add(&uci_ctxt.cdev[i], - uci_ctxt.start_ctrl_nr + i , 1); - if (IS_ERR_VALUE(r)) { - uci_log(UCI_DBG_ERROR, + struct uci_client *uci_client = &uci_ctxt->client_handles[i]; + + if (uci_client->in_attr.uci_ownership) { + cdev_init(&uci_ctxt->cdev[i], &mhi_uci_client_fops); + uci_ctxt->cdev[i].owner = THIS_MODULE; + ret_val = cdev_add(&uci_ctxt->cdev[i], + uci_ctxt->dev_t + i, 1); + if (IS_ERR_VALUE(ret_val)) { + uci_log(uci_client->uci_ipc_log, + UCI_DBG_ERROR, "Failed to add cdev %d, ret 0x%x\n", - i, r); + i, ret_val); goto failed_char_add; } - uci_ctxt.client_handles[i].dev = - device_create(uci_ctxt.mhi_uci_class, NULL, - uci_ctxt.start_ctrl_nr + i, - NULL, DEVICE_NAME "_pipe_%d", - i * 2); - - if (IS_ERR(uci_ctxt.client_handles[i].dev)) { - uci_log(UCI_DBG_ERROR, - "Failed to add cdev %d\n", i); - cdev_del(&uci_ctxt.cdev[i]); + 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); + if (IS_ERR(uci_client->dev)) { + uci_log(uci_client->uci_ipc_log, + UCI_DBG_ERROR, + "Failed to add cdev %d\n", i); + cdev_del(&uci_ctxt->cdev[i]); + ret_val = -EIO; goto failed_device_create; } } } + platform_set_drvdata(pdev, uci_ctxt); + mutex_lock(&mhi_uci_drv_ctxt.list_lock); + list_add_tail(&uci_ctxt->node, &mhi_uci_drv_ctxt.head); + mutex_unlock(&mhi_uci_drv_ctxt.list_lock); return 0; failed_char_add: failed_device_create: while (--i >= 0) { - cdev_del(&uci_ctxt.cdev[i]); - device_destroy(uci_ctxt.mhi_uci_class, - MKDEV(MAJOR(uci_ctxt.start_ctrl_nr), i * 2)); + cdev_del(&uci_ctxt->cdev[i]); + device_destroy(mhi_uci_drv_ctxt.mhi_uci_class, + MKDEV(MAJOR(uci_ctxt->dev_t), i)); }; - class_destroy(uci_ctxt.mhi_uci_class); -failed_class_add: - unregister_chrdev_region(MAJOR(uci_ctxt.start_ctrl_nr), - MHI_MAX_SOFTWARE_CHANNELS); + + unregister_chrdev_region(MAJOR(uci_ctxt->dev_t), + MHI_SOFTWARE_CLIENT_LIMIT); failed_char_alloc: - return r; -} -static void __exit mhi_uci_exit(void) + return ret_val; +}; + +static int mhi_uci_remove(struct platform_device *pdev) { + struct mhi_uci_ctxt_t *uci_ctxt = platform_get_drvdata(pdev); int i; + for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) { - cdev_del(&uci_ctxt.cdev[i]); - device_destroy(uci_ctxt.mhi_uci_class, - MKDEV(MAJOR(uci_ctxt.start_ctrl_nr), i * 2)); + struct uci_client *uci_client = &uci_ctxt->client_handles[i]; + + uci_client->uci_ctxt = uci_ctxt; + if (uci_client->in_attr.uci_ownership) { + mhi_deregister_channel(uci_client->out_handle); + mhi_deregister_channel(uci_client->in_handle); + cdev_del(&uci_ctxt->cdev[i]); + device_destroy(mhi_uci_drv_ctxt.mhi_uci_class, + MKDEV(MAJOR(uci_ctxt->dev_t), i)); + } } - class_destroy(uci_ctxt.mhi_uci_class); - unregister_chrdev_region(MAJOR(uci_ctxt.start_ctrl_nr), - MHI_MAX_SOFTWARE_CHANNELS); + + unregister_chrdev_region(MAJOR(uci_ctxt->dev_t), + MHI_SOFTWARE_CLIENT_LIMIT); + + mutex_lock(&mhi_uci_drv_ctxt.list_lock); + list_del(&uci_ctxt->node); + mutex_unlock(&mhi_uci_drv_ctxt.list_lock); + return 0; +}; + +static const struct of_device_id mhi_uci_match_table[] = { + {.compatible = "qcom,mhi-uci"}, + {}, +}; + +static struct platform_driver mhi_uci_driver = { + .probe = mhi_uci_probe, + .remove = mhi_uci_remove, + .driver = { + .name = MHI_UCI_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = mhi_uci_match_table, + }, +}; + +static int mhi_uci_init(void) +{ + mhi_uci_drv_ctxt.mhi_uci_ipc_log = + ipc_log_context_create(MHI_UCI_IPC_LOG_PAGES, + "mhi-uci", + 0); + if (mhi_uci_drv_ctxt.mhi_uci_ipc_log == NULL) { + uci_log(NULL, + UCI_DBG_WARNING, + "Failed to create IPC logging context"); + } + + mhi_uci_drv_ctxt.mhi_uci_class = + class_create(THIS_MODULE, DEVICE_NAME); + + if (IS_ERR(mhi_uci_drv_ctxt.mhi_uci_class)) + return -ENODEV; + + mutex_init(&mhi_uci_drv_ctxt.list_lock); + INIT_LIST_HEAD(&mhi_uci_drv_ctxt.head); + + return platform_driver_register(&mhi_uci_driver); +} + +static void __exit mhi_uci_exit(void) +{ + class_destroy(mhi_uci_drv_ctxt.mhi_uci_class); + platform_driver_unregister(&mhi_uci_driver); } module_exit(mhi_uci_exit); diff --git a/drivers/video/fbdev/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c index d74b1432ea71..bb1259e3cfa1 100644 --- a/drivers/video/fbdev/msm/msm_ext_display.c +++ b/drivers/platform/msm/msm_ext_display.c @@ -23,9 +23,6 @@ #include <linux/of_platform.h> #include <linux/msm_ext_display.h> -#include "mdss_hdmi_util.h" -#include "mdss_fb.h" - struct msm_ext_disp_list { struct msm_ext_disp_init_data *data; struct list_head list; @@ -48,7 +45,6 @@ struct msm_ext_disp { static int msm_ext_disp_get_intf_data(struct msm_ext_disp *ext_disp, enum msm_ext_disp_type type, struct msm_ext_disp_init_data **data); -static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack); static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp, enum msm_ext_disp_type type, enum msm_ext_disp_cable_state state, u32 flags); @@ -103,128 +99,6 @@ end: return; } -static void msm_ext_disp_get_pdev_by_name(struct device *dev, - const char *phandle, struct platform_device **pdev) -{ - struct device_node *pd_np; - - if (!dev) { - pr_err("Invalid device\n"); - return; - } - - if (!dev->of_node) { - pr_err("Invalid of_node\n"); - return; - } - - pd_np = of_parse_phandle(dev->of_node, phandle, 0); - if (!pd_np) { - pr_err("Cannot find %s dev\n", phandle); - return; - } - - *pdev = of_find_device_by_node(pd_np); -} - -static void msm_ext_disp_get_fb_pdev(struct device *device, - struct platform_device **fb_pdev) -{ - struct msm_fb_data_type *mfd = NULL; - struct fb_info *fbi = dev_get_drvdata(device); - - if (!fbi) { - pr_err("fb_info is null\n"); - return; - } - - mfd = (struct msm_fb_data_type *)fbi->par; - - *fb_pdev = mfd->pdev; -} -static ssize_t msm_ext_disp_sysfs_wta_audio_cb(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int ack, ret = 0; - ssize_t size = strnlen(buf, PAGE_SIZE); - const char *ext_phandle = "qcom,msm_ext_disp"; - struct platform_device *ext_pdev = NULL; - const char *intf_phandle = "qcom,mdss-intf"; - struct platform_device *intf_pdev = NULL; - struct platform_device *fb_pdev = NULL; - - ret = kstrtoint(buf, 10, &ack); - if (ret) { - pr_err("kstrtoint failed. ret=%d\n", ret); - goto end; - } - - msm_ext_disp_get_fb_pdev(dev, &fb_pdev); - if (!fb_pdev) { - pr_err("failed to get fb pdev\n"); - goto end; - } - - msm_ext_disp_get_pdev_by_name(&fb_pdev->dev, intf_phandle, &intf_pdev); - if (!intf_pdev) { - pr_err("failed to get display intf pdev\n"); - goto end; - } - - msm_ext_disp_get_pdev_by_name(&intf_pdev->dev, ext_phandle, &ext_pdev); - if (!ext_pdev) { - pr_err("failed to get ext_pdev\n"); - goto end; - } - - ret = msm_ext_disp_audio_ack(ext_pdev, ack); - if (ret) - pr_err("Failed to process ack. ret=%d\n", ret); - -end: - return size; -} - -static DEVICE_ATTR(hdmi_audio_cb, S_IWUSR, NULL, - msm_ext_disp_sysfs_wta_audio_cb); - -static struct attribute *msm_ext_disp_fs_attrs[] = { - &dev_attr_hdmi_audio_cb.attr, - NULL, -}; - -static struct attribute_group msm_ext_disp_fs_attrs_group = { - .attrs = msm_ext_disp_fs_attrs, -}; - -static int msm_ext_disp_sysfs_create(struct msm_ext_disp_init_data *data) -{ - int ret = 0; - - if (!data || !data->kobj) { - pr_err("Invalid params\n"); - ret = -EINVAL; - goto end; - } - - ret = sysfs_create_group(data->kobj, &msm_ext_disp_fs_attrs_group); - if (ret) - pr_err("Failed, ret=%d\n", ret); - -end: - return ret; -} - -static void msm_ext_disp_sysfs_remove(struct msm_ext_disp_init_data *data) -{ - if (!data || !data->kobj) { - pr_err("Invalid params\n"); - return; - } - - sysfs_remove_group(data->kobj, &msm_ext_disp_fs_attrs_group); -} - static const char *msm_ext_disp_name(enum msm_ext_disp_type type) { switch (type) { @@ -293,31 +167,6 @@ end: return ret; } -static void msm_ext_disp_remove_intf_data(struct msm_ext_disp *ext_disp, - enum msm_ext_disp_type type) -{ - struct msm_ext_disp_list *node; - struct list_head *position = NULL; - struct list_head *temp = NULL; - - if (!ext_disp) { - pr_err("Invalid params\n"); - return; - } - - list_for_each_safe(position, temp, &ext_disp->display_list) { - node = list_entry(position, struct msm_ext_disp_list, list); - if (node->data->type == type) { - msm_ext_disp_sysfs_remove(node->data); - list_del(&node->list); - pr_debug("Removed display (%s)\n", - msm_ext_disp_name(type)); - kfree(node); - break; - } - } -} - static int msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp, enum msm_ext_disp_cable_state new_state) { @@ -661,6 +510,46 @@ static void msm_ext_disp_teardown_done(struct platform_device *pdev) complete_all(&ext_disp->hpd_comp); } +static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack) +{ + u32 ack_hpd; + int ret = 0; + struct msm_ext_disp *ext_disp = NULL; + + if (!pdev) { + pr_err("Invalid platform device\n"); + return -EINVAL; + } + + ext_disp = platform_get_drvdata(pdev); + if (!ext_disp) { + pr_err("Invalid drvdata\n"); + return -EINVAL; + } + + if (ack & AUDIO_ACK_SET_ENABLE) { + ext_disp->ack_enabled = ack & AUDIO_ACK_ENABLE ? + true : false; + + pr_debug("audio ack feature %s\n", + ext_disp->ack_enabled ? "enabled" : "disabled"); + goto end; + } + + if (!ext_disp->ack_enabled) + goto end; + + ack_hpd = ack & AUDIO_ACK_CONNECT; + + pr_debug("%s acknowledging audio (%d)\n", + msm_ext_disp_name(ext_disp->current_disp), ack_hpd); + + if (!ext_disp->audio_session_on) + complete_all(&ext_disp->hpd_comp); +end: + return ret; +} + static int msm_ext_disp_get_intf_id(struct platform_device *pdev) { int ret = 0; @@ -710,12 +599,14 @@ static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp, ops->cable_status = msm_ext_disp_cable_status; ops->get_intf_id = msm_ext_disp_get_intf_id; ops->teardown_done = msm_ext_disp_teardown_done; + ops->acknowledge = msm_ext_disp_audio_ack; } else { ops->audio_info_setup = NULL; ops->get_audio_edid_blk = NULL; ops->cable_status = NULL; ops->get_intf_id = NULL; ops->teardown_done = NULL; + ops->acknowledge = NULL; } end: return ret; @@ -755,46 +646,6 @@ end: return ret; } -static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack) -{ - u32 ack_hpd; - int ret = 0; - struct msm_ext_disp *ext_disp = NULL; - - if (!pdev) { - pr_err("Invalid platform device\n"); - return -EINVAL; - } - - ext_disp = platform_get_drvdata(pdev); - if (!ext_disp) { - pr_err("Invalid drvdata\n"); - return -EINVAL; - } - - if (ack & AUDIO_ACK_SET_ENABLE) { - ext_disp->ack_enabled = ack & AUDIO_ACK_ENABLE ? - true : false; - - pr_debug("audio ack feature %s\n", - ext_disp->ack_enabled ? "enabled" : "disabled"); - goto end; - } - - if (!ext_disp->ack_enabled) - goto end; - - ack_hpd = ack & AUDIO_ACK_CONNECT; - - pr_debug("%s acknowledging audio (%d)\n", - msm_ext_disp_name(ext_disp->current_disp), ack_hpd); - - if (!ext_disp->audio_session_on) - complete_all(&ext_disp->hpd_comp); -end: - return ret; -} - int msm_hdmi_register_audio_codec(struct platform_device *pdev, struct msm_ext_disp_audio_codec_ops *ops) { @@ -849,11 +700,6 @@ static int msm_ext_disp_validate_intf(struct msm_ext_disp_init_data *init_data) return -EINVAL; } - if (!init_data->kobj) { - pr_err("Invalid display intf kobj\n"); - return -EINVAL; - } - if (!init_data->codec_ops.get_audio_edid_blk || !init_data->codec_ops.cable_status || !init_data->codec_ops.audio_info_setup) { @@ -899,10 +745,6 @@ int msm_ext_disp_register_intf(struct platform_device *pdev, if (ret) goto end; - ret = msm_ext_disp_sysfs_create(init_data); - if (ret) - goto sysfs_failure; - init_data->intf_ops.hpd = msm_ext_disp_hpd; init_data->intf_ops.notify = msm_ext_disp_notify; @@ -913,8 +755,6 @@ int msm_ext_disp_register_intf(struct platform_device *pdev, return ret; -sysfs_failure: - msm_ext_disp_remove_intf_data(ext_disp, init_data->type); end: mutex_unlock(&ext_disp->lock); @@ -1035,7 +875,7 @@ static void __exit msm_ext_disp_exit(void) platform_driver_unregister(&this_driver); } -module_init(msm_ext_disp_init); +subsys_initcall(msm_ext_disp_init); module_exit(msm_ext_disp_exit); MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/toshiba-wmi.c b/drivers/platform/x86/toshiba-wmi.c index feac4576b837..2df07ee8f3c3 100644 --- a/drivers/platform/x86/toshiba-wmi.c +++ b/drivers/platform/x86/toshiba-wmi.c @@ -24,14 +24,15 @@ #include <linux/acpi.h> #include <linux/input.h> #include <linux/input/sparse-keymap.h> +#include <linux/dmi.h> MODULE_AUTHOR("Azael Avalos"); MODULE_DESCRIPTION("Toshiba WMI Hotkey Driver"); MODULE_LICENSE("GPL"); -#define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100" +#define WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100" -MODULE_ALIAS("wmi:"TOSHIBA_WMI_EVENT_GUID); +MODULE_ALIAS("wmi:"WMI_EVENT_GUID); static struct input_dev *toshiba_wmi_input_dev; @@ -63,6 +64,16 @@ static void toshiba_wmi_notify(u32 value, void *context) kfree(response.pointer); } +static struct dmi_system_id toshiba_wmi_dmi_table[] __initdata = { + { + .ident = "Toshiba laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + }, + }, + {} +}; + static int __init toshiba_wmi_input_setup(void) { acpi_status status; @@ -81,7 +92,7 @@ static int __init toshiba_wmi_input_setup(void) if (err) goto err_free_dev; - status = wmi_install_notify_handler(TOSHIBA_WMI_EVENT_GUID, + status = wmi_install_notify_handler(WMI_EVENT_GUID, toshiba_wmi_notify, NULL); if (ACPI_FAILURE(status)) { err = -EIO; @@ -95,7 +106,7 @@ static int __init toshiba_wmi_input_setup(void) return 0; err_remove_notifier: - wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID); + wmi_remove_notify_handler(WMI_EVENT_GUID); err_free_keymap: sparse_keymap_free(toshiba_wmi_input_dev); err_free_dev: @@ -105,7 +116,7 @@ static int __init toshiba_wmi_input_setup(void) static void toshiba_wmi_input_destroy(void) { - wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID); + wmi_remove_notify_handler(WMI_EVENT_GUID); sparse_keymap_free(toshiba_wmi_input_dev); input_unregister_device(toshiba_wmi_input_dev); } @@ -114,7 +125,8 @@ static int __init toshiba_wmi_init(void) { int ret; - if (!wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) + if (!wmi_has_guid(WMI_EVENT_GUID) || + !dmi_check_system(toshiba_wmi_dmi_table)) return -ENODEV; ret = toshiba_wmi_input_setup(); @@ -130,7 +142,7 @@ static int __init toshiba_wmi_init(void) static void __exit toshiba_wmi_exit(void) { - if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) + if (wmi_has_guid(WMI_EVENT_GUID)) toshiba_wmi_input_destroy(); } diff --git a/drivers/power/bq24257_charger.c b/drivers/power/bq24257_charger.c index 1fea2c7ef97f..6fc31bdc639b 100644 --- a/drivers/power/bq24257_charger.c +++ b/drivers/power/bq24257_charger.c @@ -1068,6 +1068,12 @@ static int bq24257_probe(struct i2c_client *client, return ret; } + ret = bq24257_power_supply_init(bq); + if (ret < 0) { + dev_err(dev, "Failed to register power supply\n"); + return ret; + } + ret = devm_request_threaded_irq(dev, client->irq, NULL, bq24257_irq_handler_thread, IRQF_TRIGGER_FALLING | @@ -1078,12 +1084,6 @@ static int bq24257_probe(struct i2c_client *client, return ret; } - ret = bq24257_power_supply_init(bq); - if (ret < 0) { - dev_err(dev, "Failed to register power supply\n"); - return ret; - } - ret = sysfs_create_group(&bq->charger->dev.kobj, &bq24257_attr_group); if (ret < 0) { dev_err(dev, "Can't create sysfs entries\n"); diff --git a/drivers/power/goldfish_battery.c b/drivers/power/goldfish_battery.c index a50bb988c69a..f5c525e4482a 100644 --- a/drivers/power/goldfish_battery.c +++ b/drivers/power/goldfish_battery.c @@ -24,6 +24,7 @@ #include <linux/pci.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/acpi.h> struct goldfish_battery_data { void __iomem *reg_base; @@ -227,11 +228,25 @@ static int goldfish_battery_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_battery_of_match[] = { + { .compatible = "google,goldfish-battery", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_battery_of_match); + +static const struct acpi_device_id goldfish_battery_acpi_match[] = { + { "GFSH0001", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match); + static struct platform_driver goldfish_battery_device = { .probe = goldfish_battery_probe, .remove = goldfish_battery_remove, .driver = { - .name = "goldfish-battery" + .name = "goldfish-battery", + .of_match_table = goldfish_battery_of_match, + .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match), } }; module_platform_driver(goldfish_battery_device); diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 9c65f134d447..da7a75f82489 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -457,13 +457,16 @@ static inline void max17042_write_model_data(struct max17042_chip *chip, } static inline void max17042_read_model_data(struct max17042_chip *chip, - u8 addr, u32 *data, int size) + u8 addr, u16 *data, int size) { struct regmap *map = chip->regmap; int i; + u32 tmp; - for (i = 0; i < size; i++) - regmap_read(map, addr + i, &data[i]); + for (i = 0; i < size; i++) { + regmap_read(map, addr + i, &tmp); + data[i] = (u16)tmp; + } } static inline int max17042_model_data_compare(struct max17042_chip *chip, @@ -486,7 +489,7 @@ static int max17042_init_model(struct max17042_chip *chip) { int ret; int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); - u32 *temp_data; + u16 *temp_data; temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); if (!temp_data) @@ -501,7 +504,7 @@ static int max17042_init_model(struct max17042_chip *chip) ret = max17042_model_data_compare( chip, chip->pdata->config_data->cell_char_tbl, - (u16 *)temp_data, + temp_data, table_size); max10742_lock_model(chip); @@ -514,7 +517,7 @@ static int max17042_verify_model_lock(struct max17042_chip *chip) { int i; int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); - u32 *temp_data; + u16 *temp_data; int ret = 0; temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); diff --git a/drivers/power/qcom/apm.c b/drivers/power/qcom/apm.c index 6181b08a41a5..b5ae7f4ade03 100644 --- a/drivers/power/qcom/apm.c +++ b/drivers/power/qcom/apm.c @@ -27,6 +27,7 @@ #include <linux/string.h> #include <linux/power/qcom/apm.h> #include <soc/qcom/scm.h> +#include <linux/arm-smccc.h> /* * VDD_APCC @@ -79,7 +80,6 @@ #define MSM_APM_DRIVER_NAME "qcom,msm-apm" -asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64); enum { CLOCK_ASSERT_ENABLE, diff --git a/drivers/power/reset/hisi-reboot.c b/drivers/power/reset/hisi-reboot.c index 9ab7f562a83b..f69387e12c1e 100644 --- a/drivers/power/reset/hisi-reboot.c +++ b/drivers/power/reset/hisi-reboot.c @@ -53,13 +53,16 @@ static int hisi_reboot_probe(struct platform_device *pdev) if (of_property_read_u32(np, "reboot-offset", &reboot_offset) < 0) { pr_err("failed to find reboot-offset property\n"); + iounmap(base); return -EINVAL; } err = register_restart_handler(&hisi_restart_nb); - if (err) + if (err) { dev_err(&pdev->dev, "cannot register restart handler (err=%d)\n", err); + iounmap(base); + } return err; } diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig index b919c688e627..47b201738672 100644 --- a/drivers/power/supply/qcom/Kconfig +++ b/drivers/power/supply/qcom/Kconfig @@ -1,23 +1,5 @@ menu "Qualcomm Technologies Inc Charger and Fuel Gauge support" -config QPNP_SMBCHARGER - tristate "QPNP SMB Charger driver" - depends on MFD_SPMI_PMIC - help - Say Y here to enable the dual path switch mode battery charger which - supports USB detection and battery charging up to 3A. - The driver also offers relevant information to userspace via the - power supply framework. - -config QPNP_FG - tristate "QPNP fuel gauge driver" - depends on MFD_SPMI_PMIC - help - Say Y here to enable the Fuel Gauge driver. This adds support for - battery fuel gauging and state of charge of battery connected to the - fuel gauge. The state of charge is reported through a BMS power - supply property and also sends uevents when the capacity is updated. - config QPNP_FG_GEN3 tristate "QPNP GEN3 fuel gauge driver" depends on MFD_SPMI_PMIC diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile index a3aff858c190..dfa83f2304b2 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -1,5 +1,3 @@ -obj-$(CONFIG_QPNP_SMBCHARGER) += qpnp-smbcharger.o batterydata-lib.o pmic-voter.o -obj-$(CONFIG_QPNP_FG) += qpnp-fg.o obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o obj-$(CONFIG_SMB135X_CHARGER) += smb135x-charger.o pmic-voter.o obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.o diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index 4973f91c8af1..5b320ca8e74e 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -35,12 +35,15 @@ #define PARALLEL_PSY_VOTER "PARALLEL_PSY_VOTER" #define PL_HW_ABSENT_VOTER "PL_HW_ABSENT_VOTER" #define PL_VOTER "PL_VOTER" +#define RESTRICT_CHG_VOTER "RESTRICT_CHG_VOTER" struct pl_data { int pl_mode; int slave_pct; int taper_pct; int slave_fcc_ua; + int restricted_current; + bool restricted_charging_enabled; struct votable *fcc_votable; struct votable *fv_votable; struct votable *pl_disable_votable; @@ -80,6 +83,8 @@ module_param_named(debug_mask, debug_mask, int, S_IRUSR | S_IWUSR); enum { VER = 0, SLAVE_PCT, + RESTRICT_CHG_ENABLE, + RESTRICT_CHG_CURRENT, }; /******* @@ -180,10 +185,78 @@ static ssize_t slave_pct_store(struct class *c, struct class_attribute *attr, return count; } +/********************** +* RESTICTED CHARGIGNG * +***********************/ +static ssize_t restrict_chg_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + struct pl_data *chip = container_of(c, struct pl_data, + qcom_batt_class); + + return snprintf(ubuf, PAGE_SIZE, "%d\n", + chip->restricted_charging_enabled); +} + +static ssize_t restrict_chg_store(struct class *c, struct class_attribute *attr, + const char *ubuf, size_t count) +{ + struct pl_data *chip = container_of(c, struct pl_data, + qcom_batt_class); + unsigned long val; + + if (kstrtoul(ubuf, 10, &val)) + return -EINVAL; + + if (chip->restricted_charging_enabled == !!val) + goto no_change; + + chip->restricted_charging_enabled = !!val; + + vote(chip->fcc_votable, RESTRICT_CHG_VOTER, + chip->restricted_charging_enabled, + chip->restricted_current); + +no_change: + return count; +} + +static ssize_t restrict_cur_show(struct class *c, struct class_attribute *attr, + char *ubuf) +{ + struct pl_data *chip = container_of(c, struct pl_data, + qcom_batt_class); + + return snprintf(ubuf, PAGE_SIZE, "%d\n", chip->restricted_current); +} + +static ssize_t restrict_cur_store(struct class *c, struct class_attribute *attr, + const char *ubuf, size_t count) +{ + struct pl_data *chip = container_of(c, struct pl_data, + qcom_batt_class); + unsigned long val; + + if (kstrtoul(ubuf, 10, &val)) + return -EINVAL; + + chip->restricted_current = val; + + vote(chip->fcc_votable, RESTRICT_CHG_VOTER, + chip->restricted_charging_enabled, + chip->restricted_current); + + return count; +} + static struct class_attribute pl_attributes[] = { [VER] = __ATTR_RO(version), [SLAVE_PCT] = __ATTR(parallel_pct, S_IRUGO | S_IWUSR, slave_pct_show, slave_pct_store), + [RESTRICT_CHG_ENABLE] = __ATTR(restricted_charging, S_IRUGO | S_IWUSR, + restrict_chg_show, restrict_chg_store), + [RESTRICT_CHG_CURRENT] = __ATTR(restricted_current, S_IRUGO | S_IWUSR, + restrict_cur_show, restrict_cur_store), __ATTR_NULL, }; @@ -750,6 +823,7 @@ static int pl_determine_initial_status(struct pl_data *chip) return 0; } +#define DEFAULT_RESTRICTED_CURRENT_UA 1000000 static int pl_init(void) { struct pl_data *chip; @@ -759,6 +833,7 @@ static int pl_init(void) if (!chip) return -ENOMEM; chip->slave_pct = 50; + chip->restricted_current = DEFAULT_RESTRICTED_CURRENT_UA; chip->pl_ws = wakeup_source_register("qcom-battery"); if (!chip->pl_ws) diff --git a/drivers/power/supply/qcom/battery_current_limit.c b/drivers/power/supply/qcom/battery_current_limit.c index d2c25bfbf66c..410e64321ba6 100644 --- a/drivers/power/supply/qcom/battery_current_limit.c +++ b/drivers/power/supply/qcom/battery_current_limit.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 @@ -204,6 +204,7 @@ static uint32_t bcl_hotplug_request, bcl_hotplug_mask, bcl_soc_hotplug_mask; static uint32_t bcl_frequency_mask; static struct work_struct bcl_hotplug_work; static DEFINE_MUTEX(bcl_hotplug_mutex); +static DEFINE_MUTEX(bcl_cpufreq_mutex); static bool bcl_hotplug_enabled; static uint32_t battery_soc_val = 100; static uint32_t soc_low_threshold; @@ -251,6 +252,7 @@ static void update_cpu_freq(void) union device_request cpufreq_req; trace_bcl_sw_mitigation_event("Start Frequency Mitigate"); + mutex_lock(&bcl_cpufreq_mutex); cpufreq_req.freq.max_freq = UINT_MAX; cpufreq_req.freq.min_freq = CPUFREQ_MIN_NO_MITIGATION; @@ -275,6 +277,7 @@ static void update_cpu_freq(void) pr_err("Error updating freq for CPU%d. ret:%d\n", cpu, ret); } + mutex_unlock(&bcl_cpufreq_mutex); trace_bcl_sw_mitigation_event("End Frequency Mitigation"); } @@ -285,23 +288,13 @@ static void soc_mitigate(struct work_struct *work) update_cpu_freq(); } -static int power_supply_callback(struct notifier_block *nb, - unsigned long event, void *data) +static int get_and_evaluate_battery_soc(void) { - struct power_supply *psy = data; static struct power_supply *batt_psy; union power_supply_propval ret = {0,}; int battery_percentage; enum bcl_threshold_state prev_soc_state; - if (gbcl->bcl_mode != BCL_DEVICE_ENABLED) { - pr_debug("BCL is not enabled\n"); - return NOTIFY_OK; - } - - if (strcmp(psy->desc->name, "battery")) - return NOTIFY_OK; - if (!batt_psy) batt_psy = power_supply_get_by_name("battery"); if (batt_psy) { @@ -325,6 +318,22 @@ static int power_supply_callback(struct notifier_block *nb, return NOTIFY_OK; } +static int power_supply_callback(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct power_supply *psy = data; + + if (gbcl->bcl_mode != BCL_DEVICE_ENABLED) { + pr_debug("BCL is not enabled\n"); + return NOTIFY_OK; + } + + if (strcmp(psy->desc->name, "battery")) + return NOTIFY_OK; + + return get_and_evaluate_battery_soc(); +} + static int bcl_get_battery_voltage(int *vbatt_mv) { static struct power_supply *psy; @@ -643,7 +652,7 @@ static void bcl_periph_mode_set(enum bcl_device_mode mode) * power state changes. Make sure we read the current SoC * and mitigate. */ - power_supply_callback(&gbcl->psy_nb, 1, gbcl); + get_and_evaluate_battery_soc(); ret = power_supply_reg_notifier(&gbcl->psy_nb); if (ret < 0) { pr_err("Unable to register soc notifier rc = %d\n", diff --git a/drivers/power/supply/qcom/bcl_peripheral.c b/drivers/power/supply/qcom/bcl_peripheral.c index cae4967f1ef4..2d237f27b062 100644 --- a/drivers/power/supply/qcom/bcl_peripheral.c +++ b/drivers/power/supply/qcom/bcl_peripheral.c @@ -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 @@ -482,8 +482,10 @@ static int bcl_access_monitor_enable(bool enable) if (enable == bcl_perph->enabled) goto access_exit; - if ((bcl_perph_version == BCL_PMI8998) && !hw_enabled && enable) + if ((bcl_perph_version == BCL_PMI8998) && !hw_enabled && enable) { bcl_lmh_dcvs_enable(); + hw_enabled = true; + } for (; i < BCL_PARAM_MAX; i++) { perph_data = &bcl_perph->param[i]; diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index c146654e438b..32a25b4e2c7b 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -143,6 +143,7 @@ enum fg_sram_param_id { FG_SRAM_FULL_SOC, FG_SRAM_VOLTAGE_PRED, FG_SRAM_OCV, + FG_SRAM_ESR, FG_SRAM_RSLOW, FG_SRAM_ALG_FLAGS, FG_SRAM_CC_SOC, @@ -233,6 +234,7 @@ struct fg_dt_props { int esr_timer_awake; int esr_timer_asleep; int rconn_mohms; + int esr_clamp_mohms; int cl_start_soc; int cl_max_temp; int cl_min_temp; diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c index d5c4f8ffaac3..f2395b6ba4ab 100644 --- a/drivers/power/supply/qcom/fg-util.c +++ b/drivers/power/supply/qcom/fg-util.c @@ -358,7 +358,7 @@ int fg_write(struct fg_chip *chip, int addr, u8 *val, int len) return -ENXIO; mutex_lock(&chip->bus_lock); - sec_access = (addr & 0xFF00) > 0xD0; + sec_access = (addr & 0x00FF) > 0xD0; if (sec_access) { rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5); if (rc < 0) { @@ -398,7 +398,7 @@ int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val) return -ENXIO; mutex_lock(&chip->bus_lock); - sec_access = (addr & 0xFF00) > 0xD0; + sec_access = (addr & 0x00FF) > 0xD0; if (sec_access) { rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5); if (rc < 0) { diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index 1fd092c550f3..39afc235fbc3 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -99,6 +99,8 @@ #define VOLTAGE_PRED_OFFSET 0 #define OCV_WORD 97 #define OCV_OFFSET 2 +#define ESR_WORD 99 +#define ESR_OFFSET 0 #define RSLOW_WORD 101 #define RSLOW_OFFSET 0 #define ACT_BATT_CAP_WORD 117 @@ -173,6 +175,8 @@ static struct fg_sram_param pmi8998_v1_sram_params[] = { 244141, 0, NULL, fg_decode_voltage_15b), PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 1000, 244141, 0, NULL, fg_decode_voltage_15b), + PARAM(ESR, ESR_WORD, ESR_OFFSET, 2, 1000, 244141, 0, fg_encode_default, + fg_decode_value_16b), PARAM(RSLOW, RSLOW_WORD, RSLOW_OFFSET, 2, 1000, 244141, 0, NULL, fg_decode_value_16b), PARAM(ALG_FLAGS, ALG_FLAGS_WORD, ALG_FLAGS_OFFSET, 1, 1, 1, 0, NULL, @@ -235,6 +239,8 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = { 244141, 0, NULL, fg_decode_voltage_15b), PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 1000, 244141, 0, NULL, fg_decode_voltage_15b), + PARAM(ESR, ESR_WORD, ESR_OFFSET, 2, 1000, 244141, 0, fg_encode_default, + fg_decode_value_16b), PARAM(RSLOW, RSLOW_WORD, RSLOW_OFFSET, 2, 1000, 244141, 0, NULL, fg_decode_value_16b), PARAM(ALG_FLAGS, ALG_FLAGS_WORD, ALG_FLAGS_OFFSET, 1, 1, 1, 0, NULL, @@ -571,38 +577,11 @@ static int fg_get_battery_temp(struct fg_chip *chip, int *val) return 0; } -#define BATT_ESR_NUMR 244141 -#define BATT_ESR_DENR 1000 -static int fg_get_battery_esr(struct fg_chip *chip, int *val) -{ - int rc = 0; - u16 temp = 0; - u8 buf[2]; - - rc = fg_read(chip, BATT_INFO_ESR_LSB(chip), buf, 2); - if (rc < 0) { - pr_err("failed to read addr=0x%04x, rc=%d\n", - BATT_INFO_ESR_LSB(chip), rc); - return rc; - } - - if (chip->wa_flags & PMI8998_V1_REV_WA) - temp = ((buf[0] & ESR_MSB_MASK) << 8) | - (buf[1] & ESR_LSB_MASK); - else - temp = ((buf[1] & ESR_MSB_MASK) << 8) | - (buf[0] & ESR_LSB_MASK); - - pr_debug("buf: %x %x temp: %x\n", buf[0], buf[1], temp); - *val = div_u64((u64)temp * BATT_ESR_NUMR, BATT_ESR_DENR); - return 0; -} - static int fg_get_battery_resistance(struct fg_chip *chip, int *val) { int rc, esr_uohms, rslow_uohms; - rc = fg_get_battery_esr(chip, &esr_uohms); + rc = fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms); if (rc < 0) { pr_err("failed to get ESR, rc=%d\n", rc); return rc; @@ -1631,7 +1610,7 @@ static int fg_rconn_config(struct fg_chip *chip) return 0; } - rc = fg_get_battery_esr(chip, &esr_uohms); + rc = fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms); if (rc < 0) { pr_err("failed to get ESR, rc=%d\n", rc); return rc; @@ -2744,6 +2723,39 @@ out: return rc; } +static int fg_esr_validate(struct fg_chip *chip) +{ + int rc, esr_uohms; + u8 buf[2]; + + if (chip->dt.esr_clamp_mohms <= 0) + return 0; + + rc = fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms); + if (rc < 0) { + pr_err("failed to get ESR, rc=%d\n", rc); + return rc; + } + + if (esr_uohms >= chip->dt.esr_clamp_mohms * 1000) { + pr_debug("ESR %d is > ESR_clamp\n", esr_uohms); + return 0; + } + + esr_uohms = chip->dt.esr_clamp_mohms * 1000; + fg_encode(chip->sp, FG_SRAM_ESR, esr_uohms, buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_ESR].addr_word, + chip->sp[FG_SRAM_ESR].addr_byte, buf, + chip->sp[FG_SRAM_ESR].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing ESR, rc=%d\n", rc); + return rc; + } + + fg_dbg(chip, FG_STATUS, "ESR clamped to %duOhms\n", esr_uohms); + return 0; +} + /* PSY CALLBACKS STAY HERE */ static int fg_psy_get_property(struct power_supply *psy, @@ -3371,6 +3383,10 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) if (rc < 0) pr_err("Error in updating maint_soc, rc=%d\n", rc); + rc = fg_esr_validate(chip); + if (rc < 0) + pr_err("Error in validating ESR, rc=%d\n", rc); + if (batt_psy_initialized(chip)) power_supply_changed(chip->batt_psy); @@ -3659,6 +3675,7 @@ static int fg_parse_ki_coefficients(struct fg_chip *chip) #define DEFAULT_ESR_BROAD_FLT_UPCT 99610 #define DEFAULT_ESR_TIGHT_LT_FLT_UPCT 48829 #define DEFAULT_ESR_BROAD_LT_FLT_UPCT 148438 +#define DEFAULT_ESR_CLAMP_MOHMS 20 static int fg_parse_dt(struct fg_chip *chip) { struct device_node *child, *revid_node, *node = chip->dev->of_node; @@ -3972,6 +3989,12 @@ static int fg_parse_dt(struct fg_chip *chip) if (rc < 0) pr_err("Error in parsing slope limit coeffs, rc=%d\n", rc); + rc = of_property_read_u32(node, "qcom,fg-esr-clamp-mohms", &temp); + if (rc < 0) + chip->dt.esr_clamp_mohms = DEFAULT_ESR_CLAMP_MOHMS; + else + chip->dt.esr_clamp_mohms = temp; + return 0; } diff --git a/drivers/power/supply/qcom/qpnp-fg.c b/drivers/power/supply/qcom/qpnp-fg.c deleted file mode 100644 index cfd2f64a9bb8..000000000000 --- a/drivers/power/supply/qcom/qpnp-fg.c +++ /dev/null @@ -1,7051 +0,0 @@ -/* 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 - * 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) "FG: %s: " fmt, __func__ - -#include <linux/atomic.h> -#include <linux/delay.h> -#include <linux/kernel.h> -#include <linux/regmap.h> -#include <linux/of.h> -#include <linux/rtc.h> -#include <linux/err.h> -#include <linux/debugfs.h> -#include <linux/slab.h> -#include <linux/uaccess.h> -#include <linux/init.h> -#include <linux/spmi.h> -#include <linux/platform_device.h> -#include <linux/of_irq.h> -#include <linux/interrupt.h> -#include <linux/bitops.h> -#include <linux/types.h> -#include <linux/module.h> -#include <linux/ktime.h> -#include <linux/power_supply.h> -#include <linux/of_batterydata.h> -#include <linux/string_helpers.h> -#include <linux/alarmtimer.h> -#include <linux/qpnp/qpnp-revid.h> - -/* Register offsets */ - -/* Interrupt offsets */ -#define INT_RT_STS(base) (base + 0x10) -#define INT_EN_CLR(base) (base + 0x16) - -/* SPMI Register offsets */ -#define SOC_MONOTONIC_SOC 0x09 -#define SOC_BOOT_MOD 0x50 -#define SOC_RESTART 0x51 - -#define REG_OFFSET_PERP_SUBTYPE 0x05 - -/* RAM register offsets */ -#define RAM_OFFSET 0x400 - -/* Bit/Mask definitions */ -#define FULL_PERCENT 0xFF -#define MAX_TRIES_SOC 5 -#define MA_MV_BIT_RES 39 -#define MSB_SIGN BIT(7) -#define IBAT_VBAT_MASK 0x7F -#define NO_OTP_PROF_RELOAD BIT(6) -#define REDO_FIRST_ESTIMATE BIT(3) -#define RESTART_GO BIT(0) -#define THERM_DELAY_MASK 0xE0 - -/* SUBTYPE definitions */ -#define FG_SOC 0x9 -#define FG_BATT 0xA -#define FG_ADC 0xB -#define FG_MEMIF 0xC - -#define QPNP_FG_DEV_NAME "qcom,qpnp-fg" -#define MEM_IF_TIMEOUT_MS 5000 -#define BUCKET_COUNT 8 -#define BUCKET_SOC_PCT (256 / BUCKET_COUNT) - -#define BCL_MA_TO_ADC(_current, _adc_val) { \ - _adc_val = (u8)((_current) * 100 / 976); \ -} - -/* Debug Flag Definitions */ -enum { - FG_SPMI_DEBUG_WRITES = BIT(0), /* Show SPMI writes */ - FG_SPMI_DEBUG_READS = BIT(1), /* Show SPMI reads */ - FG_IRQS = BIT(2), /* Show interrupts */ - FG_MEM_DEBUG_WRITES = BIT(3), /* Show SRAM writes */ - FG_MEM_DEBUG_READS = BIT(4), /* Show SRAM reads */ - FG_POWER_SUPPLY = BIT(5), /* Show POWER_SUPPLY */ - FG_STATUS = BIT(6), /* Show FG status changes */ - FG_AGING = BIT(7), /* Show FG aging algorithm */ -}; - -/* PMIC REVISIONS */ -#define REVID_RESERVED 0 -#define REVID_VARIANT 1 -#define REVID_ANA_MAJOR 2 -#define REVID_DIG_MAJOR 3 - -enum dig_major { - DIG_REV_1 = 0x1, - DIG_REV_2 = 0x2, - DIG_REV_3 = 0x3, -}; - -enum pmic_subtype { - PMI8994 = 10, - PMI8950 = 17, - PMI8996 = 19, - PMI8937 = 55, -}; - -enum wa_flags { - IADC_GAIN_COMP_WA = BIT(0), - USE_CC_SOC_REG = BIT(1), - PULSE_REQUEST_WA = BIT(2), - BCL_HI_POWER_FOR_CHGLED_WA = BIT(3) -}; - -enum current_sense_type { - INTERNAL_CURRENT_SENSE, - EXTERNAL_CURRENT_SENSE, -}; - -struct fg_mem_setting { - u16 address; - u8 offset; - int value; -}; - -struct fg_mem_data { - u16 address; - u8 offset; - unsigned int len; - int value; -}; - -struct fg_learning_data { - int64_t cc_uah; - int64_t learned_cc_uah; - int init_cc_pc_val; - bool active; - bool feedback_on; - struct mutex learning_lock; - ktime_t time_stamp; - /* configuration properties */ - int max_start_soc; - int max_increment; - int max_decrement; - int min_temp; - int max_temp; - int vbat_est_thr_uv; -}; - -struct fg_rslow_data { - u8 rslow_cfg; - u8 rslow_thr; - u8 rs_to_rslow[2]; - u8 rslow_comp[4]; - uint32_t chg_rs_to_rslow; - uint32_t chg_rslow_comp_c1; - uint32_t chg_rslow_comp_c2; - uint32_t chg_rslow_comp_thr; - bool active; - struct mutex lock; -}; - -struct fg_cyc_ctr_data { - bool en; - bool started[BUCKET_COUNT]; - u16 count[BUCKET_COUNT]; - u8 last_soc[BUCKET_COUNT]; - int id; - struct mutex lock; -}; - -struct fg_iadc_comp_data { - u8 dfl_gain_reg[2]; - bool gain_active; - int64_t dfl_gain; -}; - -struct fg_cc_soc_data { - int init_sys_soc; - int init_cc_soc; - int full_capacity; - int delta_soc; -}; - -/* FG_MEMIF setting index */ -enum fg_mem_setting_index { - FG_MEM_SOFT_COLD = 0, - FG_MEM_SOFT_HOT, - FG_MEM_HARD_COLD, - FG_MEM_HARD_HOT, - FG_MEM_RESUME_SOC, - FG_MEM_BCL_LM_THRESHOLD, - FG_MEM_BCL_MH_THRESHOLD, - FG_MEM_TERM_CURRENT, - FG_MEM_CHG_TERM_CURRENT, - FG_MEM_IRQ_VOLT_EMPTY, - FG_MEM_CUTOFF_VOLTAGE, - FG_MEM_VBAT_EST_DIFF, - FG_MEM_DELTA_SOC, - FG_MEM_BATT_LOW, - FG_MEM_THERM_DELAY, - FG_MEM_SETTING_MAX, -}; - -/* FG_MEMIF data index */ -enum fg_mem_data_index { - FG_DATA_BATT_TEMP = 0, - FG_DATA_OCV, - FG_DATA_VOLTAGE, - FG_DATA_CURRENT, - FG_DATA_BATT_ESR, - FG_DATA_BATT_ESR_COUNT, - FG_DATA_BATT_SOC, - FG_DATA_CC_CHARGE, - FG_DATA_VINT_ERR, - FG_DATA_CPRED_VOLTAGE, - /* values below this only gets read once per profile reload */ - FG_DATA_BATT_ID, - FG_DATA_BATT_ID_INFO, - FG_DATA_MAX, -}; - -#define SETTING(_idx, _address, _offset, _value) \ - [FG_MEM_##_idx] = { \ - .address = _address, \ - .offset = _offset, \ - .value = _value, \ - } \ - -static struct fg_mem_setting settings[FG_MEM_SETTING_MAX] = { - /* ID Address, Offset, Value*/ - SETTING(SOFT_COLD, 0x454, 0, 100), - SETTING(SOFT_HOT, 0x454, 1, 400), - SETTING(HARD_COLD, 0x454, 2, 50), - SETTING(HARD_HOT, 0x454, 3, 450), - SETTING(RESUME_SOC, 0x45C, 1, 0), - SETTING(BCL_LM_THRESHOLD, 0x47C, 2, 50), - SETTING(BCL_MH_THRESHOLD, 0x47C, 3, 752), - SETTING(TERM_CURRENT, 0x40C, 2, 250), - SETTING(CHG_TERM_CURRENT, 0x4F8, 2, 250), - SETTING(IRQ_VOLT_EMPTY, 0x458, 3, 3100), - SETTING(CUTOFF_VOLTAGE, 0x40C, 0, 3200), - SETTING(VBAT_EST_DIFF, 0x000, 0, 30), - SETTING(DELTA_SOC, 0x450, 3, 1), - SETTING(BATT_LOW, 0x458, 0, 4200), - SETTING(THERM_DELAY, 0x4AC, 3, 0), -}; - -#define DATA(_idx, _address, _offset, _length, _value) \ - [FG_DATA_##_idx] = { \ - .address = _address, \ - .offset = _offset, \ - .len = _length, \ - .value = _value, \ - } \ - -static struct fg_mem_data fg_data[FG_DATA_MAX] = { - /* ID Address, Offset, Length, Value*/ - DATA(BATT_TEMP, 0x550, 2, 2, -EINVAL), - DATA(OCV, 0x588, 3, 2, -EINVAL), - DATA(VOLTAGE, 0x5CC, 1, 2, -EINVAL), - DATA(CURRENT, 0x5CC, 3, 2, -EINVAL), - DATA(BATT_ESR, 0x554, 2, 2, -EINVAL), - DATA(BATT_ESR_COUNT, 0x558, 2, 2, -EINVAL), - DATA(BATT_SOC, 0x56C, 1, 3, -EINVAL), - DATA(CC_CHARGE, 0x570, 0, 4, -EINVAL), - DATA(VINT_ERR, 0x560, 0, 4, -EINVAL), - DATA(CPRED_VOLTAGE, 0x540, 0, 2, -EINVAL), - DATA(BATT_ID, 0x594, 1, 1, -EINVAL), - DATA(BATT_ID_INFO, 0x594, 3, 1, -EINVAL), -}; - -static int fg_debug_mask; -module_param_named( - debug_mask, fg_debug_mask, int, S_IRUSR | S_IWUSR -); - -static int fg_sense_type = -EINVAL; -static int fg_restart; - -static int fg_est_dump; -module_param_named( - first_est_dump, fg_est_dump, int, S_IRUSR | S_IWUSR -); - -static char *fg_batt_type; -module_param_named( - battery_type, fg_batt_type, charp, S_IRUSR | S_IWUSR -); - -static int fg_sram_update_period_ms = 30000; -module_param_named( - sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR -); - -struct fg_irq { - int irq; - unsigned long disabled; -}; - -enum fg_soc_irq { - HIGH_SOC, - LOW_SOC, - FULL_SOC, - EMPTY_SOC, - DELTA_SOC, - FIRST_EST_DONE, - SW_FALLBK_OCV, - SW_FALLBK_NEW_BATT, - FG_SOC_IRQ_COUNT, -}; - -enum fg_batt_irq { - JEITA_SOFT_COLD, - JEITA_SOFT_HOT, - VBATT_LOW, - BATT_IDENTIFIED, - BATT_ID_REQ, - BATTERY_UNKNOWN, - BATT_MISSING, - BATT_MATCH, - FG_BATT_IRQ_COUNT, -}; - -enum fg_mem_if_irq { - FG_MEM_AVAIL, - TA_RCVRY_SUG, - FG_MEM_IF_IRQ_COUNT, -}; - -enum fg_batt_aging_mode { - FG_AGING_NONE, - FG_AGING_ESR, - FG_AGING_CC, -}; - -enum register_type { - MEM_INTF_CFG, - MEM_INTF_CTL, - MEM_INTF_ADDR_LSB, - MEM_INTF_RD_DATA0, - MEM_INTF_WR_DATA0, - MAX_ADDRESS, -}; - -struct register_offset { - u16 address[MAX_ADDRESS]; -}; - -static struct register_offset offset[] = { - [0] = { - /* CFG CTL LSB RD0 WD0 */ - .address = {0x40, 0x41, 0x42, 0x4C, 0x48}, - }, - [1] = { - /* CFG CTL LSB RD0 WD0 */ - .address = {0x50, 0x51, 0x61, 0x67, 0x63}, - }, -}; - -#define MEM_INTF_CFG(chip) \ - ((chip)->mem_base + (chip)->offset[MEM_INTF_CFG]) -#define MEM_INTF_CTL(chip) \ - ((chip)->mem_base + (chip)->offset[MEM_INTF_CTL]) -#define MEM_INTF_ADDR_LSB(chip) \ - ((chip)->mem_base + (chip)->offset[MEM_INTF_ADDR_LSB]) -#define MEM_INTF_RD_DATA0(chip) \ - ((chip)->mem_base + (chip)->offset[MEM_INTF_RD_DATA0]) -#define MEM_INTF_WR_DATA0(chip) \ - ((chip)->mem_base + (chip)->offset[MEM_INTF_WR_DATA0]) - -struct fg_wakeup_source { - struct wakeup_source source; - unsigned long enabled; -}; - -static void fg_stay_awake(struct fg_wakeup_source *source) -{ - if (!__test_and_set_bit(0, &source->enabled)) { - __pm_stay_awake(&source->source); - pr_debug("enabled source %s\n", source->source.name); - } -} - -static void fg_relax(struct fg_wakeup_source *source) -{ - if (__test_and_clear_bit(0, &source->enabled)) { - __pm_relax(&source->source); - pr_debug("disabled source %s\n", source->source.name); - } -} - -#define THERMAL_COEFF_N_BYTES 6 -struct fg_chip { - struct device *dev; - struct platform_device *pdev; - struct regmap *regmap; - u8 pmic_subtype; - u8 pmic_revision[4]; - u8 revision[4]; - u16 soc_base; - u16 batt_base; - u16 mem_base; - u16 vbat_adc_addr; - u16 ibat_adc_addr; - u16 tp_rev_addr; - u32 wa_flag; - atomic_t memif_user_cnt; - struct fg_irq soc_irq[FG_SOC_IRQ_COUNT]; - struct fg_irq batt_irq[FG_BATT_IRQ_COUNT]; - struct fg_irq mem_irq[FG_MEM_IF_IRQ_COUNT]; - struct completion sram_access_granted; - struct completion sram_access_revoked; - struct completion batt_id_avail; - struct completion first_soc_done; - struct power_supply *bms_psy; - struct power_supply_desc bms_psy_d; - struct mutex rw_lock; - struct mutex sysfs_restart_lock; - struct delayed_work batt_profile_init; - struct work_struct dump_sram; - struct work_struct status_change_work; - struct work_struct cycle_count_work; - struct work_struct battery_age_work; - struct work_struct update_esr_work; - struct work_struct set_resume_soc_work; - struct work_struct rslow_comp_work; - struct work_struct sysfs_restart_work; - struct work_struct init_work; - struct work_struct charge_full_work; - struct work_struct gain_comp_work; - struct work_struct bcl_hi_power_work; - struct power_supply *batt_psy; - struct power_supply *usb_psy; - struct power_supply *dc_psy; - struct fg_wakeup_source memif_wakeup_source; - struct fg_wakeup_source profile_wakeup_source; - struct fg_wakeup_source empty_check_wakeup_source; - struct fg_wakeup_source resume_soc_wakeup_source; - struct fg_wakeup_source gain_comp_wakeup_source; - struct fg_wakeup_source capacity_learning_wakeup_source; - bool first_profile_loaded; - struct fg_wakeup_source update_temp_wakeup_source; - struct fg_wakeup_source update_sram_wakeup_source; - bool fg_restarting; - bool profile_loaded; - bool use_otp_profile; - bool battery_missing; - bool power_supply_registered; - bool sw_rbias_ctrl; - bool use_thermal_coefficients; - bool esr_strict_filter; - bool soc_empty; - bool charge_done; - bool resume_soc_lowered; - bool vbat_low_irq_enabled; - bool charge_full; - bool hold_soc_while_full; - bool input_present; - bool otg_present; - bool safety_timer_expired; - bool bad_batt_detection_en; - bool bcl_lpm_disabled; - bool charging_disabled; - struct delayed_work update_jeita_setting; - struct delayed_work update_sram_data; - struct delayed_work update_temp_work; - struct delayed_work check_empty_work; - char *batt_profile; - u8 thermal_coefficients[THERMAL_COEFF_N_BYTES]; - u32 cc_cv_threshold_mv; - unsigned int batt_profile_len; - unsigned int batt_max_voltage_uv; - const char *batt_type; - const char *batt_psy_name; - unsigned long last_sram_update_time; - unsigned long last_temp_update_time; - int64_t ocv_coeffs[12]; - int64_t cutoff_voltage; - int evaluation_current; - int ocv_junction_p1p2; - int ocv_junction_p2p3; - int nom_cap_uah; - int actual_cap_uah; - int status; - int prev_status; - int health; - enum fg_batt_aging_mode batt_aging_mode; - /* capacity learning */ - struct fg_learning_data learning_data; - struct alarm fg_cap_learning_alarm; - struct work_struct fg_cap_learning_work; - struct fg_cc_soc_data sw_cc_soc_data; - /* rslow compensation */ - struct fg_rslow_data rslow_comp; - /* cycle counter */ - struct fg_cyc_ctr_data cyc_ctr; - /* iadc compensation */ - struct fg_iadc_comp_data iadc_comp_data; - /* interleaved memory access */ - u16 *offset; - bool ima_supported; - bool init_done; - /* jeita hysteresis */ - bool jeita_hysteresis_support; - bool batt_hot; - bool batt_cold; - int cold_hysteresis; - int hot_hysteresis; - /* ESR pulse tuning */ - struct fg_wakeup_source esr_extract_wakeup_source; - struct work_struct esr_extract_config_work; - bool esr_extract_disabled; - bool imptr_pulse_slow_en; - bool esr_pulse_tune_en; -}; - -/* FG_MEMIF DEBUGFS structures */ -#define ADDR_LEN 4 /* 3 byte address + 1 space character */ -#define CHARS_PER_ITEM 3 /* Format is 'XX ' */ -#define ITEMS_PER_LINE 4 /* 4 data items per line */ -#define MAX_LINE_LENGTH (ADDR_LEN + (ITEMS_PER_LINE * CHARS_PER_ITEM) + 1) -#define MAX_REG_PER_TRANSACTION (8) - -static const char *DFS_ROOT_NAME = "fg_memif"; -static const mode_t DFS_MODE = S_IRUSR | S_IWUSR; -static const char *default_batt_type = "Unknown Battery"; -static const char *loading_batt_type = "Loading Battery Data"; -static const char *missing_batt_type = "Disconnected Battery"; - -/* Log buffer */ -struct fg_log_buffer { - size_t rpos; /* Current 'read' position in buffer */ - size_t wpos; /* Current 'write' position in buffer */ - size_t len; /* Length of the buffer */ - char data[0]; /* Log buffer */ -}; - -/* transaction parameters */ -struct fg_trans { - u32 cnt; /* Number of bytes to read */ - u16 addr; /* 12-bit address in SRAM */ - u32 offset; /* Offset of last read data + byte offset */ - struct fg_chip *chip; - struct fg_log_buffer *log; /* log buffer */ - u8 *data; /* fg data that is read */ - struct mutex memif_dfs_lock; /* Prevent thread concurrency */ -}; - -struct fg_dbgfs { - u32 cnt; - u32 addr; - struct fg_chip *chip; - struct dentry *root; - struct mutex lock; - struct debugfs_blob_wrapper help_msg; -}; - -static struct fg_dbgfs dbgfs_data = { - .lock = __MUTEX_INITIALIZER(dbgfs_data.lock), - .help_msg = { - .data = -"FG Debug-FS support\n" -"\n" -"Hierarchy schema:\n" -"/sys/kernel/debug/fg_memif\n" -" /help -- Static help text\n" -" /address -- Starting register address for reads or writes\n" -" /count -- Number of registers to read (only used for reads)\n" -" /data -- Initiates the SRAM read (formatted output)\n" -"\n", - }, -}; - -static const struct of_device_id fg_match_table[] = { - { .compatible = QPNP_FG_DEV_NAME, }, - {} -}; - -static char *fg_supplicants[] = { - "battery", - "bcl", - "fg_adc" -}; - -#define DEBUG_PRINT_BUFFER_SIZE 64 -static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len) -{ - int pos = 0; - int i; - - for (i = 0; i < buf_len; i++) { - pos += scnprintf(str + pos, str_len - pos, "%02X", buf[i]); - if (i < buf_len - 1) - pos += scnprintf(str + pos, str_len - pos, " "); - } -} - -static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len) -{ - int rc = 0; - struct platform_device *pdev = chip->pdev; - char str[DEBUG_PRINT_BUFFER_SIZE]; - - if ((addr & 0xff00) == 0) { - pr_err("addr cannot be zero base=0x%02x sid=0x%02x rc=%d\n", - addr, to_spmi_device(pdev->dev.parent)->usid, rc); - return -EINVAL; - } - - rc = regmap_bulk_write(chip->regmap, addr, val, len); - if (rc) { - pr_err("write failed addr=0x%02x sid=0x%02x rc=%d\n", - addr, to_spmi_device(pdev->dev.parent)->usid, rc); - return rc; - } - - if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_WRITES)) { - str[0] = '\0'; - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len); - pr_info("write(0x%04X), sid=%d, len=%d; %s\n", - addr, to_spmi_device(pdev->dev.parent)->usid, len, - str); - } - - return rc; -} - -static int fg_read(struct fg_chip *chip, u8 *val, u16 addr, int len) -{ - int rc = 0; - struct platform_device *pdev = chip->pdev; - char str[DEBUG_PRINT_BUFFER_SIZE]; - - if ((addr & 0xff00) == 0) { - pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n", - addr, to_spmi_device(pdev->dev.parent)->usid, rc); - return -EINVAL; - } - - rc = regmap_bulk_read(chip->regmap, addr, val, len); - if (rc) { - pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", addr, - to_spmi_device(pdev->dev.parent)->usid, rc); - return rc; - } - - if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_READS)) { - str[0] = '\0'; - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len); - pr_info("read(0x%04x), sid=%d, len=%d; %s\n", - addr, to_spmi_device(pdev->dev.parent)->usid, len, - str); - } - - return rc; -} - -static int fg_masked_write(struct fg_chip *chip, u16 addr, - u8 mask, u8 val, int len) -{ - int rc; - - rc = regmap_update_bits(chip->regmap, addr, mask, val); - if (rc) { - pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc); - return rc; - } - - return rc; -} - -#define RIF_MEM_ACCESS_REQ BIT(7) -static int fg_check_rif_mem_access(struct fg_chip *chip, bool *status) -{ - int rc; - u8 mem_if_sts; - - rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1); - if (rc) { - pr_err("failed to read rif_mem status rc=%d\n", rc); - return rc; - } - - *status = mem_if_sts & RIF_MEM_ACCESS_REQ; - return 0; -} - -static bool fg_check_sram_access(struct fg_chip *chip) -{ - int rc; - u8 mem_if_sts; - bool rif_mem_sts = false; - - rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1); - if (rc) { - pr_err("failed to read mem status rc=%d\n", rc); - return false; - } - - if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0) - return false; - - rc = fg_check_rif_mem_access(chip, &rif_mem_sts); - if (rc) - return false; - - return rif_mem_sts; -} - -static inline int fg_assert_sram_access(struct fg_chip *chip) -{ - int rc; - u8 mem_if_sts; - - rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1); - if (rc) { - pr_err("failed to read mem status rc=%d\n", rc); - return rc; - } - - if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0) { - pr_err("mem_avail not high: %02x\n", mem_if_sts); - return -EINVAL; - } - - rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1); - if (rc) { - pr_err("failed to read mem status rc=%d\n", rc); - return rc; - } - - if ((mem_if_sts & RIF_MEM_ACCESS_REQ) == 0) { - pr_err("mem_avail not high: %02x\n", mem_if_sts); - return -EINVAL; - } - - return 0; -} - -#define INTF_CTL_BURST BIT(7) -#define INTF_CTL_WR_EN BIT(6) -static int fg_config_access(struct fg_chip *chip, bool write, - bool burst) -{ - int rc; - u8 intf_ctl = 0; - - intf_ctl = (write ? INTF_CTL_WR_EN : 0) | (burst ? INTF_CTL_BURST : 0); - - rc = fg_write(chip, &intf_ctl, MEM_INTF_CTL(chip), 1); - if (rc) { - pr_err("failed to set mem access bit\n"); - return -EIO; - } - - return rc; -} - -static int fg_req_and_wait_access(struct fg_chip *chip, int timeout) -{ - int rc = 0, ret = 0; - bool tried_again = false; - - if (!fg_check_sram_access(chip)) { - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), - RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ, 1); - if (rc) { - pr_err("failed to set mem access bit\n"); - return -EIO; - } - fg_stay_awake(&chip->memif_wakeup_source); - } - -wait: - /* Wait for MEM_AVAIL IRQ. */ - ret = wait_for_completion_interruptible_timeout( - &chip->sram_access_granted, - msecs_to_jiffies(timeout)); - /* If we were interrupted wait again one more time. */ - if (ret == -ERESTARTSYS && !tried_again) { - tried_again = true; - goto wait; - } else if (ret <= 0) { - rc = -ETIMEDOUT; - pr_err("transaction timed out rc=%d\n", rc); - return rc; - } - - return rc; -} - -static int fg_release_access(struct fg_chip *chip) -{ - int rc; - - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), - RIF_MEM_ACCESS_REQ, 0, 1); - fg_relax(&chip->memif_wakeup_source); - reinit_completion(&chip->sram_access_granted); - - return rc; -} - -static void fg_release_access_if_necessary(struct fg_chip *chip) -{ - mutex_lock(&chip->rw_lock); - if (atomic_sub_return(1, &chip->memif_user_cnt) <= 0) { - fg_release_access(chip); - } - mutex_unlock(&chip->rw_lock); -} - -/* - * fg_mem_lock disallows the fuel gauge to release access until it has been - * released. - * - * an equal number of calls must be made to fg_mem_release for the fuel gauge - * driver to release the sram access. - */ -static void fg_mem_lock(struct fg_chip *chip) -{ - mutex_lock(&chip->rw_lock); - atomic_add_return(1, &chip->memif_user_cnt); - mutex_unlock(&chip->rw_lock); -} - -static void fg_mem_release(struct fg_chip *chip) -{ - fg_release_access_if_necessary(chip); -} - -static int fg_set_ram_addr(struct fg_chip *chip, u16 *address) -{ - int rc; - - rc = fg_write(chip, (u8 *) address, - chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], 2); - if (rc) { - pr_err("spmi write failed: addr=%03X, rc=%d\n", - chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], rc); - return rc; - } - - return rc; -} - -#define BUF_LEN 4 -static int fg_sub_mem_read(struct fg_chip *chip, u8 *val, u16 address, int len, - int offset) -{ - int rc, total_len; - u8 *rd_data = val; - char str[DEBUG_PRINT_BUFFER_SIZE]; - - rc = fg_config_access(chip, 0, (len > 4)); - if (rc) - return rc; - - rc = fg_set_ram_addr(chip, &address); - if (rc) - return rc; - - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("length %d addr=%02X\n", len, address); - - total_len = len; - while (len > 0) { - if (!offset) { - rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip), - min(len, BUF_LEN)); - } else { - rc = fg_read(chip, rd_data, - MEM_INTF_RD_DATA0(chip) + offset, - min(len, BUF_LEN - offset)); - - /* manually set address to allow continous reads */ - address += BUF_LEN; - - rc = fg_set_ram_addr(chip, &address); - if (rc) - return rc; - } - if (rc) { - pr_err("spmi read failed: addr=%03x, rc=%d\n", - MEM_INTF_RD_DATA0(chip) + offset, rc); - return rc; - } - rd_data += (BUF_LEN - offset); - len -= (BUF_LEN - offset); - offset = 0; - } - - if (fg_debug_mask & FG_MEM_DEBUG_READS) { - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len); - pr_info("data: %s\n", str); - } - return rc; -} - -static int fg_conventional_mem_read(struct fg_chip *chip, u8 *val, u16 address, - int len, int offset, bool keep_access) -{ - int rc = 0, user_cnt = 0, orig_address = address; - - if (offset > 3) { - pr_err("offset too large %d\n", offset); - return -EINVAL; - } - - address = ((orig_address + offset) / 4) * 4; - offset = (orig_address + offset) % 4; - - user_cnt = atomic_add_return(1, &chip->memif_user_cnt); - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("user_cnt %d\n", user_cnt); - mutex_lock(&chip->rw_lock); - if (!fg_check_sram_access(chip)) { - rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS); - if (rc) - goto out; - } - - rc = fg_sub_mem_read(chip, val, address, len, offset); - -out: - user_cnt = atomic_sub_return(1, &chip->memif_user_cnt); - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("user_cnt %d\n", user_cnt); - - fg_assert_sram_access(chip); - - if (!keep_access && (user_cnt == 0) && !rc) { - rc = fg_release_access(chip); - if (rc) { - pr_err("failed to set mem access bit\n"); - rc = -EIO; - } - } - - mutex_unlock(&chip->rw_lock); - return rc; -} - -static int fg_conventional_mem_write(struct fg_chip *chip, u8 *val, u16 address, - int len, int offset, bool keep_access) -{ - int rc = 0, user_cnt = 0, sublen; - bool access_configured = false; - u8 *wr_data = val, word[4]; - char str[DEBUG_PRINT_BUFFER_SIZE]; - - if (address < RAM_OFFSET) - return -EINVAL; - - if (offset > 3) - return -EINVAL; - - address = ((address + offset) / 4) * 4; - offset = (address + offset) % 4; - - user_cnt = atomic_add_return(1, &chip->memif_user_cnt); - if (fg_debug_mask & FG_MEM_DEBUG_WRITES) - pr_info("user_cnt %d\n", user_cnt); - mutex_lock(&chip->rw_lock); - if (!fg_check_sram_access(chip)) { - rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS); - if (rc) - goto out; - } - - if (fg_debug_mask & FG_MEM_DEBUG_WRITES) { - pr_info("length %d addr=%02X offset=%d\n", - len, address, offset); - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, wr_data, len); - pr_info("writing: %s\n", str); - } - - while (len > 0) { - if (offset != 0) { - sublen = min(4 - offset, len); - rc = fg_sub_mem_read(chip, word, address, 4, 0); - if (rc) - goto out; - memcpy(word + offset, wr_data, sublen); - /* configure access as burst if more to write */ - rc = fg_config_access(chip, 1, (len - sublen) > 0); - if (rc) - goto out; - rc = fg_set_ram_addr(chip, &address); - if (rc) - goto out; - offset = 0; - access_configured = true; - } else if (len >= 4) { - if (!access_configured) { - rc = fg_config_access(chip, 1, len > 4); - if (rc) - goto out; - rc = fg_set_ram_addr(chip, &address); - if (rc) - goto out; - access_configured = true; - } - sublen = 4; - memcpy(word, wr_data, 4); - } else if (len > 0 && len < 4) { - sublen = len; - rc = fg_sub_mem_read(chip, word, address, 4, 0); - if (rc) - goto out; - memcpy(word, wr_data, sublen); - rc = fg_config_access(chip, 1, 0); - if (rc) - goto out; - rc = fg_set_ram_addr(chip, &address); - if (rc) - goto out; - access_configured = true; - } else { - pr_err("Invalid length: %d\n", len); - break; - } - rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip), 4); - if (rc) { - pr_err("spmi write failed: addr=%03x, rc=%d\n", - MEM_INTF_WR_DATA0(chip), rc); - goto out; - } - len -= sublen; - wr_data += sublen; - address += 4; - } - -out: - user_cnt = atomic_sub_return(1, &chip->memif_user_cnt); - if (fg_debug_mask & FG_MEM_DEBUG_WRITES) - pr_info("user_cnt %d\n", user_cnt); - - fg_assert_sram_access(chip); - - if (!keep_access && (user_cnt == 0) && !rc) { - rc = fg_release_access(chip); - if (rc) { - pr_err("failed to set mem access bit\n"); - rc = -EIO; - } - } - - mutex_unlock(&chip->rw_lock); - return rc; -} - -#define MEM_INTF_IMA_CFG 0x52 -#define MEM_INTF_IMA_OPR_STS 0x54 -#define MEM_INTF_IMA_ERR_STS 0x5F -#define MEM_INTF_IMA_EXP_STS 0x55 -#define MEM_INTF_IMA_HW_STS 0x56 -#define MEM_INTF_IMA_BYTE_EN 0x60 -#define IMA_ADDR_STBL_ERR BIT(7) -#define IMA_WR_ACS_ERR BIT(6) -#define IMA_RD_ACS_ERR BIT(5) -#define IMA_IACS_CLR BIT(2) -#define IMA_IACS_RDY BIT(1) -static int fg_check_ima_exception(struct fg_chip *chip) -{ - int rc = 0, ret = 0; - u8 err_sts, exp_sts = 0, hw_sts = 0; - - rc = fg_read(chip, &err_sts, - chip->mem_base + MEM_INTF_IMA_ERR_STS, 1); - if (rc) { - pr_err("failed to read beat count rc=%d\n", rc); - return rc; - } - - if (err_sts & (IMA_ADDR_STBL_ERR | IMA_WR_ACS_ERR | IMA_RD_ACS_ERR)) { - u8 temp; - - fg_read(chip, &exp_sts, - chip->mem_base + MEM_INTF_IMA_EXP_STS, 1); - fg_read(chip, &hw_sts, - chip->mem_base + MEM_INTF_IMA_HW_STS, 1); - pr_err("IMA access failed ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n", - err_sts, exp_sts, hw_sts); - rc = err_sts; - - /* clear the error */ - ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG, - IMA_IACS_CLR, IMA_IACS_CLR, 1); - temp = 0x4; - ret |= fg_write(chip, &temp, MEM_INTF_ADDR_LSB(chip) + 1, 1); - temp = 0x0; - ret |= fg_write(chip, &temp, MEM_INTF_WR_DATA0(chip) + 3, 1); - ret |= fg_read(chip, &temp, MEM_INTF_RD_DATA0(chip) + 3, 1); - ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG, - IMA_IACS_CLR, 0, 1); - if (!ret) - return -EAGAIN; - else - pr_err("Error clearing IMA exception ret=%d\n", ret); - } - - return rc; -} - -static int fg_check_iacs_ready(struct fg_chip *chip) -{ - int rc = 0, timeout = 250; - u8 ima_opr_sts = 0; - - /* - * Additional delay to make sure IACS ready bit is set after - * Read/Write operation. - */ - - usleep_range(30, 35); - while (1) { - rc = fg_read(chip, &ima_opr_sts, - chip->mem_base + MEM_INTF_IMA_OPR_STS, 1); - if (!rc && (ima_opr_sts & IMA_IACS_RDY)) { - break; - } else { - if (!(--timeout) || rc) - break; - /* delay for iacs_ready to be asserted */ - usleep_range(5000, 7000); - } - } - - if (!timeout || rc) { - pr_err("IACS_RDY not set\n"); - /* perform IACS_CLR sequence */ - fg_check_ima_exception(chip); - return -EBUSY; - } - - return 0; -} - -#define IACS_SLCT BIT(5) -static int __fg_interleaved_mem_write(struct fg_chip *chip, u8 *val, - u16 address, int offset, int len) -{ - int rc = 0, i; - u8 *word = val, byte_enable = 0, num_bytes = 0; - - if (fg_debug_mask & FG_MEM_DEBUG_WRITES) - pr_info("length %d addr=%02X offset=%d\n", - len, address, offset); - - while (len > 0) { - num_bytes = (offset + len) > BUF_LEN ? - (BUF_LEN - offset) : len; - /* write to byte_enable */ - for (i = offset; i < (offset + num_bytes); i++) - byte_enable |= BIT(i); - - rc = fg_write(chip, &byte_enable, - chip->mem_base + MEM_INTF_IMA_BYTE_EN, 1); - if (rc) { - pr_err("Unable to write to byte_en_reg rc=%d\n", - rc); - return rc; - } - /* write data */ - rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip) + offset, - num_bytes); - if (rc) { - pr_err("spmi write failed: addr=%03x, rc=%d\n", - MEM_INTF_WR_DATA0(chip) + offset, rc); - return rc; - } - /* - * The last-byte WR_DATA3 starts the write transaction. - * Write a dummy value to WR_DATA3 if it does not have - * valid data. This dummy data is not written to the - * SRAM as byte_en for WR_DATA3 is not set. - */ - if (!(byte_enable & BIT(3))) { - u8 dummy_byte = 0x0; - rc = fg_write(chip, &dummy_byte, - MEM_INTF_WR_DATA0(chip) + 3, 1); - if (rc) { - pr_err("Unable to write dummy-data to WR_DATA3 rc=%d\n", - rc); - return rc; - } - } - - rc = fg_check_iacs_ready(chip); - if (rc) { - pr_debug("IACS_RDY failed rc=%d\n", rc); - return rc; - } - - /* check for error condition */ - rc = fg_check_ima_exception(chip); - if (rc) { - pr_err("IMA transaction failed rc=%d", rc); - return rc; - } - - word += num_bytes; - len -= num_bytes; - offset = byte_enable = 0; - } - - return rc; -} - -static int __fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address, - int offset, int len) -{ - int rc = 0, total_len; - u8 *rd_data = val, num_bytes; - char str[DEBUG_PRINT_BUFFER_SIZE]; - - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("length %d addr=%02X\n", len, address); - - total_len = len; - while (len > 0) { - num_bytes = (offset + len) > BUF_LEN ? (BUF_LEN - offset) : len; - rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip) + offset, - num_bytes); - if (rc) { - pr_err("spmi read failed: addr=%03x, rc=%d\n", - MEM_INTF_RD_DATA0(chip) + offset, rc); - return rc; - } - - rd_data += num_bytes; - len -= num_bytes; - offset = 0; - - rc = fg_check_iacs_ready(chip); - if (rc) { - pr_debug("IACS_RDY failed rc=%d\n", rc); - return rc; - } - - /* check for error condition */ - rc = fg_check_ima_exception(chip); - if (rc) { - pr_err("IMA transaction failed rc=%d", rc); - return rc; - } - - if (len && (len + offset) < BUF_LEN) { - /* move to single mode */ - u8 intr_ctl = 0; - - rc = fg_write(chip, &intr_ctl, MEM_INTF_CTL(chip), 1); - if (rc) { - pr_err("failed to move to single mode rc=%d\n", - rc); - return -EIO; - } - } - } - - if (fg_debug_mask & FG_MEM_DEBUG_READS) { - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len); - pr_info("data: %s\n", str); - } - - return rc; -} - -#define IMA_REQ_ACCESS (IACS_SLCT | RIF_MEM_ACCESS_REQ) -static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val, - u16 address, int len, int offset, int op) -{ - int rc = 0; - bool rif_mem_sts = true; - int time_count = 0; - - while (1) { - rc = fg_check_rif_mem_access(chip, &rif_mem_sts); - if (rc) - return rc; - - if (!rif_mem_sts) - break; - - if (fg_debug_mask & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES)) - pr_info("RIF_MEM_ACCESS_REQ is not clear yet for IMA_%s\n", - op ? "write" : "read"); - - /* - * Try this no more than 4 times. If RIF_MEM_ACCESS_REQ is not - * clear, then return an error instead of waiting for it again. - */ - if (time_count > 4) { - pr_err("Waited for 1.5 seconds polling RIF_MEM_ACCESS_REQ\n"); - return -ETIMEDOUT; - } - - /* Wait for 4ms before reading RIF_MEM_ACCESS_REQ again */ - usleep_range(4000, 4100); - time_count++; - } - - /* configure for IMA access */ - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), - IMA_REQ_ACCESS, IMA_REQ_ACCESS, 1); - if (rc) { - pr_err("failed to set mem access bit rc = %d\n", rc); - return rc; - } - - /* configure for the read/write single/burst mode */ - rc = fg_config_access(chip, op, (offset + len) > 4); - if (rc) { - pr_err("failed to set configure memory access rc = %d\n", rc); - return rc; - } - - rc = fg_check_iacs_ready(chip); - if (rc) { - pr_debug("IACS_RDY failed rc=%d\n", rc); - return rc; - } - - /* write addresses to the register */ - rc = fg_set_ram_addr(chip, &address); - if (rc) { - pr_err("failed to set SRAM address rc = %d\n", rc); - return rc; - } - - rc = fg_check_iacs_ready(chip); - if (rc) - pr_debug("IACS_RDY failed rc=%d\n", rc); - - return rc; -} - -#define MEM_INTF_FG_BEAT_COUNT 0x57 -#define BEAT_COUNT_MASK 0x0F -#define RETRY_COUNT 3 -static int fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address, - int len, int offset) -{ - int rc = 0, orig_address = address; - u8 start_beat_count, end_beat_count, count = 0; - bool retry = false; - - if (offset > 3) { - pr_err("offset too large %d\n", offset); - return -EINVAL; - } - - fg_stay_awake(&chip->memif_wakeup_source); - address = ((orig_address + offset) / 4) * 4; - offset = (orig_address + offset) % 4; - - if (address < RAM_OFFSET) { - /* - * OTP memory reads need a conventional memory access, do a - * conventional read when SRAM offset < RAM_OFFSET. - */ - rc = fg_conventional_mem_read(chip, val, address, len, offset, - 0); - if (rc) - pr_err("Failed to read OTP memory %d\n", rc); - goto exit; - } - - mutex_lock(&chip->rw_lock); - -retry: - rc = fg_interleaved_mem_config(chip, val, address, offset, len, 0); - if (rc) { - pr_err("failed to configure SRAM for IMA rc = %d\n", rc); - goto out; - } - - /* read the start beat count */ - rc = fg_read(chip, &start_beat_count, - chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1); - if (rc) { - pr_err("failed to read beat count rc=%d\n", rc); - goto out; - } - - /* read data */ - rc = __fg_interleaved_mem_read(chip, val, address, offset, len); - if (rc) { - if ((rc == -EAGAIN) && (count < RETRY_COUNT)) { - count++; - pr_err("IMA access failed retry_count = %d\n", count); - goto retry; - } else { - pr_err("failed to read SRAM address rc = %d\n", rc); - goto out; - } - } - - /* read the end beat count */ - rc = fg_read(chip, &end_beat_count, - chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1); - if (rc) { - pr_err("failed to read beat count rc=%d\n", rc); - goto out; - } - - start_beat_count &= BEAT_COUNT_MASK; - end_beat_count &= BEAT_COUNT_MASK; - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("Start beat_count = %x End beat_count = %x\n", - start_beat_count, end_beat_count); - if (start_beat_count != end_beat_count) { - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("Beat count do not match - retry transaction\n"); - retry = true; - } -out: - /* Release IMA access */ - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1); - if (rc) - pr_err("failed to reset IMA access bit rc = %d\n", rc); - - if (retry) { - retry = false; - goto retry; - } - mutex_unlock(&chip->rw_lock); - -exit: - fg_relax(&chip->memif_wakeup_source); - return rc; -} - -static int fg_interleaved_mem_write(struct fg_chip *chip, u8 *val, u16 address, - int len, int offset) -{ - int rc = 0, orig_address = address; - u8 count = 0; - - if (address < RAM_OFFSET) - return -EINVAL; - - if (offset > 3) { - pr_err("offset too large %d\n", offset); - return -EINVAL; - } - - fg_stay_awake(&chip->memif_wakeup_source); - address = ((orig_address + offset) / 4) * 4; - offset = (orig_address + offset) % 4; - - mutex_lock(&chip->rw_lock); - -retry: - rc = fg_interleaved_mem_config(chip, val, address, offset, len, 1); - if (rc) { - pr_err("failed to xonfigure SRAM for IMA rc = %d\n", rc); - goto out; - } - - /* write data */ - rc = __fg_interleaved_mem_write(chip, val, address, offset, len); - if (rc) { - if ((rc == -EAGAIN) && (count < RETRY_COUNT)) { - count++; - pr_err("IMA access failed retry_count = %d\n", count); - goto retry; - } else { - pr_err("failed to write SRAM address rc = %d\n", rc); - goto out; - } - } - -out: - /* Release IMA access */ - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1); - if (rc) - pr_err("failed to reset IMA access bit rc = %d\n", rc); - - mutex_unlock(&chip->rw_lock); - fg_relax(&chip->memif_wakeup_source); - return rc; -} - -static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address, - int len, int offset, bool keep_access) -{ - if (chip->ima_supported) - return fg_interleaved_mem_read(chip, val, address, - len, offset); - else - return fg_conventional_mem_read(chip, val, address, - len, offset, keep_access); -} - -static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address, - int len, int offset, bool keep_access) -{ - if (chip->ima_supported) - return fg_interleaved_mem_write(chip, val, address, - len, offset); - else - return fg_conventional_mem_write(chip, val, address, - len, offset, keep_access); -} - -static int fg_mem_masked_write(struct fg_chip *chip, u16 addr, - u8 mask, u8 val, u8 offset) -{ - int rc = 0; - u8 reg[4]; - char str[DEBUG_PRINT_BUFFER_SIZE]; - - rc = fg_mem_read(chip, reg, addr, 4, 0, 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc); - return rc; - } - - reg[offset] &= ~mask; - reg[offset] |= val & mask; - - str[0] = '\0'; - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, reg, 4); - pr_debug("Writing %s address %03x, offset %d\n", str, addr, offset); - - rc = fg_mem_write(chip, reg, addr, 4, 0, 0); - if (rc) { - pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc); - return rc; - } - - return rc; -} - -static int soc_to_setpoint(int soc) -{ - return DIV_ROUND_CLOSEST(soc * 255, 100); -} - -static void batt_to_setpoint_adc(int vbatt_mv, u8 *data) -{ - int val; - /* Battery voltage is an offset from 0 V and LSB is 1/2^15. */ - val = DIV_ROUND_CLOSEST(vbatt_mv * 32768, 5000); - data[0] = val & 0xFF; - data[1] = val >> 8; - return; -} - -static u8 batt_to_setpoint_8b(int vbatt_mv) -{ - int val; - /* Battery voltage is an offset from 2.5 V and LSB is 5/2^9. */ - val = (vbatt_mv - 2500) * 512 / 1000; - return DIV_ROUND_CLOSEST(val, 5); -} - -static u8 therm_delay_to_setpoint(u32 delay_us) -{ - u8 val; - - if (delay_us < 2560) - val = 0; - else if (delay_us > 163840) - val = 7; - else - val = ilog2(delay_us / 10) - 7; - return val << 5; -} - -static int get_current_time(unsigned long *now_tm_sec) -{ - struct rtc_time tm; - struct rtc_device *rtc; - int rc; - - rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); - if (rtc == NULL) { - pr_err("%s: unable to open rtc device (%s)\n", - __FILE__, CONFIG_RTC_HCTOSYS_DEVICE); - return -EINVAL; - } - - rc = rtc_read_time(rtc, &tm); - if (rc) { - pr_err("Error reading rtc device (%s) : %d\n", - CONFIG_RTC_HCTOSYS_DEVICE, rc); - goto close_time; - } - - rc = rtc_valid_tm(&tm); - if (rc) { - pr_err("Invalid RTC time (%s): %d\n", - CONFIG_RTC_HCTOSYS_DEVICE, rc); - goto close_time; - } - rtc_tm_to_time(&tm, now_tm_sec); - -close_time: - rtc_class_close(rtc); - return rc; -} - -#define BATTERY_SOC_REG 0x56C -#define BATTERY_SOC_OFFSET 1 -#define FULL_PERCENT_3B 0xFFFFFF -static int get_battery_soc_raw(struct fg_chip *chip) -{ - int rc; - u8 buffer[3]; - - rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0); - if (rc) { - pr_err("Unable to read battery soc: %d\n", rc); - return 0; - } - return (int)(buffer[2] << 16 | buffer[1] << 8 | buffer[0]); -} - -#define COUNTER_IMPTR_REG 0X558 -#define COUNTER_PULSE_REG 0X55C -#define SOC_FULL_REG 0x564 -#define COUNTER_IMPTR_OFFSET 2 -#define COUNTER_PULSE_OFFSET 0 -#define SOC_FULL_OFFSET 3 -#define ESR_PULSE_RECONFIG_SOC 0xFFF971 -static int fg_configure_soc(struct fg_chip *chip) -{ - u32 batt_soc; - u8 cntr[2] = {0, 0}; - int rc = 0; - - mutex_lock(&chip->rw_lock); - atomic_add_return(1, &chip->memif_user_cnt); - mutex_unlock(&chip->rw_lock); - - /* Read Battery SOC */ - batt_soc = get_battery_soc_raw(chip); - - if (batt_soc > ESR_PULSE_RECONFIG_SOC) { - if (fg_debug_mask & FG_POWER_SUPPLY) - pr_info("Configuring soc registers batt_soc: %x\n", - batt_soc); - batt_soc = ESR_PULSE_RECONFIG_SOC; - rc = fg_mem_write(chip, (u8 *)&batt_soc, BATTERY_SOC_REG, 3, - BATTERY_SOC_OFFSET, 1); - if (rc) { - pr_err("failed to write BATT_SOC rc=%d\n", rc); - goto out; - } - - rc = fg_mem_write(chip, (u8 *)&batt_soc, SOC_FULL_REG, 3, - SOC_FULL_OFFSET, 1); - if (rc) { - pr_err("failed to write SOC_FULL rc=%d\n", rc); - goto out; - } - - rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 2, - COUNTER_IMPTR_OFFSET, 1); - if (rc) { - pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc); - goto out; - } - - rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2, - COUNTER_PULSE_OFFSET, 0); - if (rc) - pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc); - } -out: - fg_release_access_if_necessary(chip); - return rc; -} - -#define SOC_EMPTY BIT(3) -static bool fg_is_batt_empty(struct fg_chip *chip) -{ - u8 fg_soc_sts; - int rc; - - rc = fg_read(chip, &fg_soc_sts, - INT_RT_STS(chip->soc_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->soc_base), rc); - return false; - } - - return (fg_soc_sts & SOC_EMPTY) != 0; -} - -static int get_monotonic_soc_raw(struct fg_chip *chip) -{ - u8 cap[2]; - int rc, tries = 0; - - while (tries < MAX_TRIES_SOC) { - rc = fg_read(chip, cap, - chip->soc_base + SOC_MONOTONIC_SOC, 2); - if (rc) { - pr_err("spmi read failed: addr=%03x, rc=%d\n", - chip->soc_base + SOC_MONOTONIC_SOC, rc); - return rc; - } - - if (cap[0] == cap[1]) - break; - - tries++; - } - - if (tries == MAX_TRIES_SOC) { - pr_err("shadow registers do not match\n"); - return -EINVAL; - } - - if (fg_debug_mask & FG_POWER_SUPPLY) - pr_info_ratelimited("raw: 0x%02x\n", cap[0]); - return cap[0]; -} - -#define EMPTY_CAPACITY 0 -#define DEFAULT_CAPACITY 50 -#define MISSING_CAPACITY 100 -#define FULL_CAPACITY 100 -#define FULL_SOC_RAW 0xFF -static int get_prop_capacity(struct fg_chip *chip) -{ - int msoc; - - if (chip->battery_missing) - return MISSING_CAPACITY; - if (!chip->profile_loaded && !chip->use_otp_profile) - return DEFAULT_CAPACITY; - if (chip->charge_full) - return FULL_CAPACITY; - if (chip->soc_empty) { - if (fg_debug_mask & FG_POWER_SUPPLY) - pr_info_ratelimited("capacity: %d, EMPTY\n", - EMPTY_CAPACITY); - return EMPTY_CAPACITY; - } - msoc = get_monotonic_soc_raw(chip); - if (msoc == 0) - return EMPTY_CAPACITY; - else if (msoc == FULL_SOC_RAW) - return FULL_CAPACITY; - return DIV_ROUND_CLOSEST((msoc - 1) * (FULL_CAPACITY - 2), - FULL_SOC_RAW - 2) + 1; -} - -#define HIGH_BIAS 3 -#define MED_BIAS BIT(1) -#define LOW_BIAS BIT(0) -static u8 bias_ua[] = { - [HIGH_BIAS] = 150, - [MED_BIAS] = 15, - [LOW_BIAS] = 5, -}; - -static int64_t get_batt_id(unsigned int battery_id_uv, u8 bid_info) -{ - u64 battery_id_ohm; - - if ((bid_info & 0x3) == 0) { - pr_err("can't determine battery id 0x%02x\n", bid_info); - return -EINVAL; - } - - battery_id_ohm = div_u64(battery_id_uv, bias_ua[bid_info & 0x3]); - - return battery_id_ohm; -} - -#define DEFAULT_TEMP_DEGC 250 -static int get_sram_prop_now(struct fg_chip *chip, unsigned int type) -{ - if (fg_debug_mask & FG_POWER_SUPPLY) - pr_info("addr 0x%02X, offset %d value %d\n", - fg_data[type].address, fg_data[type].offset, - fg_data[type].value); - - if (type == FG_DATA_BATT_ID) - return get_batt_id(fg_data[type].value, - fg_data[FG_DATA_BATT_ID_INFO].value); - - return fg_data[type].value; -} - -#define MIN_TEMP_DEGC -300 -#define MAX_TEMP_DEGC 970 -static int get_prop_jeita_temp(struct fg_chip *chip, unsigned int type) -{ - if (fg_debug_mask & FG_POWER_SUPPLY) - pr_info("addr 0x%02X, offset %d\n", settings[type].address, - settings[type].offset); - - return settings[type].value; -} - -static int set_prop_jeita_temp(struct fg_chip *chip, - unsigned int type, int decidegc) -{ - int rc = 0; - - if (fg_debug_mask & FG_POWER_SUPPLY) - pr_info("addr 0x%02X, offset %d temp%d\n", - settings[type].address, - settings[type].offset, decidegc); - - settings[type].value = decidegc; - - cancel_delayed_work_sync( - &chip->update_jeita_setting); - schedule_delayed_work( - &chip->update_jeita_setting, 0); - - return rc; -} - -#define EXTERNAL_SENSE_SELECT 0x4AC -#define EXTERNAL_SENSE_OFFSET 0x2 -#define EXTERNAL_SENSE_BIT BIT(2) -static int set_prop_sense_type(struct fg_chip *chip, int ext_sense_type) -{ - int rc; - - rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT, - EXTERNAL_SENSE_BIT, - ext_sense_type ? EXTERNAL_SENSE_BIT : 0, - EXTERNAL_SENSE_OFFSET); - if (rc) { - pr_err("failed to write profile rc=%d\n", rc); - return rc; - } - - return 0; -} - -#define EXPONENT_MASK 0xF800 -#define MANTISSA_MASK 0x3FF -#define SIGN BIT(10) -#define EXPONENT_SHIFT 11 -#define MICRO_UNIT 1000000ULL -static int64_t float_decode(u16 reg) -{ - int64_t final_val, exponent_val, mantissa_val; - int exponent, mantissa, n; - bool sign; - - exponent = (reg & EXPONENT_MASK) >> EXPONENT_SHIFT; - mantissa = (reg & MANTISSA_MASK); - sign = !!(reg & SIGN); - - pr_debug("exponent=%d mantissa=%d sign=%d\n", exponent, mantissa, sign); - - mantissa_val = mantissa * MICRO_UNIT; - - n = exponent - 15; - if (n < 0) - exponent_val = MICRO_UNIT >> -n; - else - exponent_val = MICRO_UNIT << n; - - n = n - 10; - if (n < 0) - mantissa_val >>= -n; - else - mantissa_val <<= n; - - final_val = exponent_val + mantissa_val; - - if (sign) - final_val *= -1; - - return final_val; -} - -#define MIN_HALFFLOAT_EXP_N -15 -#define MAX_HALFFLOAT_EXP_N 16 -static int log2_floor(int64_t uval) -{ - int n = 0; - int64_t i = MICRO_UNIT; - - if (uval > i) { - while (uval > i && n > MIN_HALFFLOAT_EXP_N) { - i <<= 1; - n += 1; - } - if (uval < i) - n -= 1; - } else if (uval < i) { - while (uval < i && n < MAX_HALFFLOAT_EXP_N) { - i >>= 1; - n -= 1; - } - } - - return n; -} - -static int64_t exp2_int(int64_t n) -{ - int p = n - 1; - - if (p > 0) - return (2 * MICRO_UNIT) << p; - else - return (2 * MICRO_UNIT) >> abs(p); -} - -static u16 float_encode(int64_t uval) -{ - int sign = 0, n, exp, mantissa; - u16 half = 0; - - if (uval < 0) { - sign = 1; - uval = abs(uval); - } - n = log2_floor(uval); - exp = n + 15; - mantissa = div_s64(div_s64((uval - exp2_int(n)) * exp2_int(10 - n), - MICRO_UNIT) + MICRO_UNIT / 2, MICRO_UNIT); - - half = (mantissa & MANTISSA_MASK) | ((sign << 10) & SIGN) - | ((exp << 11) & EXPONENT_MASK); - - if (fg_debug_mask & FG_STATUS) - pr_info("uval = %lld, m = 0x%02x, sign = 0x%02x, exp = 0x%02x, half = 0x%04x\n", - uval, mantissa, sign, exp, half); - return half; -} - -#define BATT_IDED BIT(3) -static int fg_is_batt_id_valid(struct fg_chip *chip) -{ - u8 fg_batt_sts; - int rc; - - rc = fg_read(chip, &fg_batt_sts, - INT_RT_STS(chip->batt_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->batt_base), rc); - return rc; - } - - if (fg_debug_mask & FG_IRQS) - pr_info("fg batt sts 0x%x\n", fg_batt_sts); - - return (fg_batt_sts & BATT_IDED) ? 1 : 0; -} - -static int64_t twos_compliment_extend(int64_t val, int nbytes) -{ - int i; - int64_t mask; - - mask = 0x80LL << ((nbytes - 1) * 8); - if (val & mask) { - for (i = 8; i > nbytes; i--) { - mask = 0xFFLL << ((i - 1) * 8); - val |= mask; - } - } - - return val; -} - -#define LSB_24B_NUMRTR 596046 -#define LSB_24B_DENMTR 1000000 -#define LSB_16B_NUMRTR 152587 -#define LSB_16B_DENMTR 1000 -#define LSB_8B 9800 -#define TEMP_LSB_16B 625 -#define DECIKELVIN 2730 -#define SRAM_PERIOD_NO_ID_UPDATE_MS 100 -#define FULL_PERCENT_28BIT 0xFFFFFFF -static void update_sram_data(struct fg_chip *chip, int *resched_ms) -{ - int i, j, rc = 0; - u8 reg[4]; - int64_t temp; - int battid_valid = fg_is_batt_id_valid(chip); - - fg_stay_awake(&chip->update_sram_wakeup_source); - if (chip->fg_restarting) - goto resched; - - fg_mem_lock(chip); - for (i = 1; i < FG_DATA_MAX; i++) { - if (chip->profile_loaded && i >= FG_DATA_BATT_ID) - continue; - rc = fg_mem_read(chip, reg, fg_data[i].address, - fg_data[i].len, fg_data[i].offset, 0); - if (rc) { - pr_err("Failed to update sram data\n"); - break; - } - - temp = 0; - for (j = 0; j < fg_data[i].len; j++) - temp |= reg[j] << (8 * j); - - switch (i) { - case FG_DATA_OCV: - case FG_DATA_VOLTAGE: - case FG_DATA_CPRED_VOLTAGE: - fg_data[i].value = div_u64( - (u64)(u16)temp * LSB_16B_NUMRTR, - LSB_16B_DENMTR); - break; - case FG_DATA_CURRENT: - temp = twos_compliment_extend(temp, fg_data[i].len); - fg_data[i].value = div_s64( - (s64)temp * LSB_16B_NUMRTR, - LSB_16B_DENMTR); - break; - case FG_DATA_BATT_ESR: - fg_data[i].value = float_decode((u16) temp); - break; - case FG_DATA_BATT_ESR_COUNT: - fg_data[i].value = (u16)temp; - break; - case FG_DATA_BATT_ID: - if (battid_valid) - fg_data[i].value = reg[0] * LSB_8B; - break; - case FG_DATA_BATT_ID_INFO: - if (battid_valid) - fg_data[i].value = reg[0]; - break; - case FG_DATA_BATT_SOC: - fg_data[i].value = div64_s64((temp * 10000), - FULL_PERCENT_3B); - break; - case FG_DATA_CC_CHARGE: - temp = twos_compliment_extend(temp, fg_data[i].len); - fg_data[i].value = div64_s64( - temp * (int64_t)chip->nom_cap_uah, - FULL_PERCENT_28BIT); - break; - case FG_DATA_VINT_ERR: - temp = twos_compliment_extend(temp, fg_data[i].len); - fg_data[i].value = div64_s64(temp * chip->nom_cap_uah, - FULL_PERCENT_3B); - break; - }; - - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("%d %lld %d\n", i, temp, fg_data[i].value); - } - fg_mem_release(chip); - - if (!rc) - get_current_time(&chip->last_sram_update_time); - -resched: - if (battid_valid) { - complete_all(&chip->batt_id_avail); - *resched_ms = fg_sram_update_period_ms; - } else { - *resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS; - } - fg_relax(&chip->update_sram_wakeup_source); -} - -#define SRAM_TIMEOUT_MS 3000 -static void update_sram_data_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - update_sram_data.work); - int resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS, ret; - bool tried_again = false; - -wait: - /* Wait for MEMIF access revoked */ - ret = wait_for_completion_interruptible_timeout( - &chip->sram_access_revoked, - msecs_to_jiffies(SRAM_TIMEOUT_MS)); - - /* If we were interrupted wait again one more time. */ - if (ret == -ERESTARTSYS && !tried_again) { - tried_again = true; - goto wait; - } else if (ret <= 0) { - pr_err("transaction timed out ret=%d\n", ret); - goto out; - } - update_sram_data(chip, &resched_ms); - -out: - schedule_delayed_work( - &chip->update_sram_data, - msecs_to_jiffies(resched_ms)); -} - -#define BATT_TEMP_OFFSET 3 -#define BATT_TEMP_CNTRL_MASK 0x17 -#define DISABLE_THERM_BIT BIT(0) -#define TEMP_SENSE_ALWAYS_BIT BIT(1) -#define TEMP_SENSE_CHARGE_BIT BIT(2) -#define FORCE_RBIAS_ON_BIT BIT(4) -#define BATT_TEMP_OFF DISABLE_THERM_BIT -#define BATT_TEMP_ON (FORCE_RBIAS_ON_BIT | TEMP_SENSE_ALWAYS_BIT | \ - TEMP_SENSE_CHARGE_BIT) -#define TEMP_PERIOD_UPDATE_MS 10000 -#define TEMP_PERIOD_TIMEOUT_MS 3000 -static void update_temp_data(struct work_struct *work) -{ - s16 temp; - u8 reg[2]; - bool tried_again = false; - int rc, ret, timeout = TEMP_PERIOD_TIMEOUT_MS; - struct fg_chip *chip = container_of(work, - struct fg_chip, - update_temp_work.work); - - if (chip->fg_restarting) - goto resched; - - fg_stay_awake(&chip->update_temp_wakeup_source); - if (chip->sw_rbias_ctrl) { - rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT, - BATT_TEMP_CNTRL_MASK, - BATT_TEMP_ON, - BATT_TEMP_OFFSET); - if (rc) { - pr_err("failed to write BATT_TEMP_ON rc=%d\n", rc); - goto out; - } - -wait: - /* Wait for MEMIF access revoked */ - ret = wait_for_completion_interruptible_timeout( - &chip->sram_access_revoked, - msecs_to_jiffies(timeout)); - - /* If we were interrupted wait again one more time. */ - if (ret == -ERESTARTSYS && !tried_again) { - tried_again = true; - goto wait; - } else if (ret <= 0) { - rc = -ETIMEDOUT; - pr_err("transaction timed out ret=%d\n", ret); - goto out; - } - } - - /* Read FG_DATA_BATT_TEMP now */ - rc = fg_mem_read(chip, reg, fg_data[0].address, - fg_data[0].len, fg_data[0].offset, - chip->sw_rbias_ctrl ? 1 : 0); - if (rc) { - pr_err("Failed to update temp data\n"); - goto out; - } - - temp = reg[0] | (reg[1] << 8); - fg_data[0].value = (temp * TEMP_LSB_16B / 1000) - - DECIKELVIN; - - if (fg_debug_mask & FG_MEM_DEBUG_READS) - pr_info("BATT_TEMP %d %d\n", temp, fg_data[0].value); - - get_current_time(&chip->last_temp_update_time); - -out: - if (chip->sw_rbias_ctrl) { - rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT, - BATT_TEMP_CNTRL_MASK, - BATT_TEMP_OFF, - BATT_TEMP_OFFSET); - if (rc) - pr_err("failed to write BATT_TEMP_OFF rc=%d\n", rc); - } - fg_relax(&chip->update_temp_wakeup_source); - -resched: - schedule_delayed_work( - &chip->update_temp_work, - msecs_to_jiffies(TEMP_PERIOD_UPDATE_MS)); -} - -static void update_jeita_setting(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - update_jeita_setting.work); - u8 reg[4]; - int i, rc; - - for (i = 0; i < 4; i++) - reg[i] = (settings[FG_MEM_SOFT_COLD + i].value / 10) + 30; - - rc = fg_mem_write(chip, reg, settings[FG_MEM_SOFT_COLD].address, - 4, settings[FG_MEM_SOFT_COLD].offset, 0); - if (rc) - pr_err("failed to update JEITA setting rc=%d\n", rc); -} - -static int fg_set_resume_soc(struct fg_chip *chip, u8 threshold) -{ - u16 address; - int offset, rc; - - address = settings[FG_MEM_RESUME_SOC].address; - offset = settings[FG_MEM_RESUME_SOC].offset; - - rc = fg_mem_masked_write(chip, address, 0xFF, threshold, offset); - - if (rc) - pr_err("write failed rc=%d\n", rc); - else - pr_debug("setting resume-soc to %x\n", threshold); - - return rc; -} - -#define VBATT_LOW_STS_BIT BIT(2) -static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts) -{ - int rc = 0; - u8 fg_batt_sts; - - rc = fg_read(chip, &fg_batt_sts, INT_RT_STS(chip->batt_base), 1); - if (!rc) - *vbatt_low_sts = !!(fg_batt_sts & VBATT_LOW_STS_BIT); - return rc; -} - -#define BATT_CYCLE_NUMBER_REG 0x5E8 -#define BATT_CYCLE_OFFSET 0 -static void restore_cycle_counter(struct fg_chip *chip) -{ - int rc = 0, i, address; - u8 data[2]; - - fg_mem_lock(chip); - for (i = 0; i < BUCKET_COUNT; i++) { - address = BATT_CYCLE_NUMBER_REG + i * 2; - rc = fg_mem_read(chip, (u8 *)&data, address, 2, - BATT_CYCLE_OFFSET, 0); - if (rc) - pr_err("Failed to read BATT_CYCLE_NUMBER[%d] rc: %d\n", - i, rc); - else - chip->cyc_ctr.count[i] = data[0] | data[1] << 8; - } - fg_mem_release(chip); -} - -static void clear_cycle_counter(struct fg_chip *chip) -{ - int rc = 0, len, i; - - if (!chip->cyc_ctr.en) - return; - - len = sizeof(chip->cyc_ctr.count); - memset(chip->cyc_ctr.count, 0, len); - for (i = 0; i < BUCKET_COUNT; i++) { - chip->cyc_ctr.started[i] = false; - chip->cyc_ctr.last_soc[i] = 0; - } - rc = fg_mem_write(chip, (u8 *)&chip->cyc_ctr.count, - BATT_CYCLE_NUMBER_REG, len, - BATT_CYCLE_OFFSET, 0); - if (rc) - pr_err("failed to write BATT_CYCLE_NUMBER rc=%d\n", rc); -} - -static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket) -{ - int rc = 0, address; - u16 cyc_count; - u8 data[2]; - - if (bucket < 0 || (bucket > BUCKET_COUNT - 1)) - return 0; - - cyc_count = chip->cyc_ctr.count[bucket]; - cyc_count++; - data[0] = cyc_count & 0xFF; - data[1] = cyc_count >> 8; - - address = BATT_CYCLE_NUMBER_REG + bucket * 2; - - rc = fg_mem_write(chip, data, address, 2, BATT_CYCLE_OFFSET, 0); - if (rc) - pr_err("failed to write BATT_CYCLE_NUMBER[%d] rc=%d\n", - bucket, rc); - else - chip->cyc_ctr.count[bucket] = cyc_count; - return rc; -} - -static void update_cycle_count(struct work_struct *work) -{ - int rc = 0, bucket, i; - u8 reg[3], batt_soc; - struct fg_chip *chip = container_of(work, - struct fg_chip, - cycle_count_work); - - mutex_lock(&chip->cyc_ctr.lock); - rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3, - BATTERY_SOC_OFFSET, 0); - if (rc) { - pr_err("Failed to read battery soc rc: %d\n", rc); - goto out; - } - batt_soc = reg[2]; - - if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { - /* Find out which bucket the SOC falls in */ - bucket = batt_soc / BUCKET_SOC_PCT; - - if (fg_debug_mask & FG_STATUS) - pr_info("batt_soc: %x bucket: %d\n", reg[2], bucket); - - /* - * If we've started counting for the previous bucket, - * then store the counter for that bucket if the - * counter for current bucket is getting started. - */ - if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] && - !chip->cyc_ctr.started[bucket]) { - rc = fg_inc_store_cycle_ctr(chip, bucket - 1); - if (rc) { - pr_err("Error in storing cycle_ctr rc: %d\n", - rc); - goto out; - } else { - chip->cyc_ctr.started[bucket - 1] = false; - chip->cyc_ctr.last_soc[bucket - 1] = 0; - } - } - if (!chip->cyc_ctr.started[bucket]) { - chip->cyc_ctr.started[bucket] = true; - chip->cyc_ctr.last_soc[bucket] = batt_soc; - } - } else { - for (i = 0; i < BUCKET_COUNT; i++) { - if (chip->cyc_ctr.started[i] && - batt_soc > chip->cyc_ctr.last_soc[i]) { - rc = fg_inc_store_cycle_ctr(chip, i); - if (rc) - pr_err("Error in storing cycle_ctr rc: %d\n", - rc); - chip->cyc_ctr.last_soc[i] = 0; - } - chip->cyc_ctr.started[i] = false; - } - } -out: - mutex_unlock(&chip->cyc_ctr.lock); -} - -static int fg_get_cycle_count(struct fg_chip *chip) -{ - int count; - - if (!chip->cyc_ctr.en) - return 0; - - if ((chip->cyc_ctr.id <= 0) || (chip->cyc_ctr.id > BUCKET_COUNT)) - return -EINVAL; - - mutex_lock(&chip->cyc_ctr.lock); - count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1]; - mutex_unlock(&chip->cyc_ctr.lock); - return count; -} - -static void half_float_to_buffer(int64_t uval, u8 *buffer) -{ - u16 raw; - - raw = float_encode(uval); - buffer[0] = (u8)(raw & 0xFF); - buffer[1] = (u8)((raw >> 8) & 0xFF); -} - -static int64_t half_float(u8 *buffer) -{ - u16 val; - - val = buffer[1] << 8 | buffer[0]; - return float_decode(val); -} - -static int voltage_2b(u8 *buffer) -{ - u16 val; - - val = buffer[1] << 8 | buffer[0]; - /* the range of voltage 2b is [-5V, 5V], so it will fit in an int */ - return (int)div_u64(((u64)val) * LSB_16B_NUMRTR, LSB_16B_DENMTR); -} - -static int bcap_uah_2b(u8 *buffer) -{ - u16 val; - - val = buffer[1] << 8 | buffer[0]; - return ((int)val) * 1000; -} - -static int lookup_ocv_for_soc(struct fg_chip *chip, int soc) -{ - int64_t *coeffs; - - if (soc > chip->ocv_junction_p1p2 * 10) - coeffs = chip->ocv_coeffs; - else if (soc > chip->ocv_junction_p2p3 * 10) - coeffs = chip->ocv_coeffs + 4; - else - coeffs = chip->ocv_coeffs + 8; - /* the range of ocv will fit in a 32 bit int */ - return (int)(coeffs[0] - + div_s64(coeffs[1] * soc, 1000LL) - + div_s64(coeffs[2] * soc * soc, 1000000LL) - + div_s64(coeffs[3] * soc * soc * soc, 1000000000LL)); -} - -static int lookup_soc_for_ocv(struct fg_chip *chip, int ocv) -{ - int64_t val; - int soc = -EINVAL; - /* - * binary search variables representing the valid start and end - * percentages to search - */ - int start = 0, end = 1000, mid; - - if (fg_debug_mask & FG_AGING) - pr_info("target_ocv = %d\n", ocv); - /* do a binary search for the closest soc to match the ocv */ - while (end - start > 1) { - mid = (start + end) / 2; - val = lookup_ocv_for_soc(chip, mid); - if (fg_debug_mask & FG_AGING) - pr_info("start = %d, mid = %d, end = %d, ocv = %lld\n", - start, mid, end, val); - if (ocv < val) { - end = mid; - } else if (ocv > val) { - start = mid; - } else { - soc = mid; - break; - } - } - /* - * if the exact soc was not found and there are two or less values - * remaining, just compare them and see which one is closest to the ocv - */ - if (soc == -EINVAL) { - if (abs(ocv - lookup_ocv_for_soc(chip, start)) - > abs(ocv - lookup_ocv_for_soc(chip, end))) - soc = end; - else - soc = start; - } - if (fg_debug_mask & FG_AGING) - pr_info("closest = %d, target_ocv = %d, ocv_found = %d\n", - soc, ocv, lookup_ocv_for_soc(chip, soc)); - return soc; -} - -#define ESR_ACTUAL_REG 0x554 -#define BATTERY_ESR_REG 0x4F4 -#define TEMP_RS_TO_RSLOW_REG 0x514 -static int estimate_battery_age(struct fg_chip *chip, int *actual_capacity) -{ - int64_t ocv_cutoff_new, ocv_cutoff_aged, temp_rs_to_rslow; - int64_t esr_actual, battery_esr, val; - int soc_cutoff_aged, soc_cutoff_new, rc; - int battery_soc, unusable_soc, batt_temp; - u8 buffer[3]; - - if (chip->batt_aging_mode != FG_AGING_ESR) - return 0; - - if (chip->nom_cap_uah == 0) { - if (fg_debug_mask & FG_AGING) - pr_info("ocv coefficients not loaded, aborting\n"); - return 0; - } - fg_mem_lock(chip); - - batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP); - if (batt_temp < 150 || batt_temp > 400) { - if (fg_debug_mask & FG_AGING) - pr_info("Battery temp (%d) out of range, aborting\n", - (int)batt_temp); - rc = 0; - goto done; - } - - battery_soc = get_battery_soc_raw(chip) * 100 / FULL_PERCENT_3B; - if (battery_soc < 25 || battery_soc > 75) { - if (fg_debug_mask & FG_AGING) - pr_info("Battery SoC (%d) out of range, aborting\n", - (int)battery_soc); - rc = 0; - goto done; - } - - rc = fg_mem_read(chip, buffer, ESR_ACTUAL_REG, 2, 2, 0); - esr_actual = half_float(buffer); - rc |= fg_mem_read(chip, buffer, BATTERY_ESR_REG, 2, 2, 0); - battery_esr = half_float(buffer); - - if (rc) { - goto error_done; - } else if (esr_actual < battery_esr) { - if (fg_debug_mask & FG_AGING) - pr_info("Batt ESR lower than ESR actual, aborting\n"); - rc = 0; - goto done; - } - rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2, 0, 0); - temp_rs_to_rslow = half_float(buffer); - - if (rc) - goto error_done; - - fg_mem_release(chip); - - if (fg_debug_mask & FG_AGING) { - pr_info("batt_soc = %d, cutoff_voltage = %lld, eval current = %d\n", - battery_soc, chip->cutoff_voltage, - chip->evaluation_current); - pr_info("temp_rs_to_rslow = %lld, batt_esr = %lld, esr_actual = %lld\n", - temp_rs_to_rslow, battery_esr, esr_actual); - } - - /* calculate soc_cutoff_new */ - val = (1000000LL + temp_rs_to_rslow) * battery_esr; - do_div(val, 1000000); - ocv_cutoff_new = div64_s64(chip->evaluation_current * val, 1000) - + chip->cutoff_voltage; - - /* calculate soc_cutoff_aged */ - val = (1000000LL + temp_rs_to_rslow) * esr_actual; - do_div(val, 1000000); - ocv_cutoff_aged = div64_s64(chip->evaluation_current * val, 1000) - + chip->cutoff_voltage; - - if (fg_debug_mask & FG_AGING) - pr_info("ocv_cutoff_new = %lld, ocv_cutoff_aged = %lld\n", - ocv_cutoff_new, ocv_cutoff_aged); - - soc_cutoff_new = lookup_soc_for_ocv(chip, ocv_cutoff_new); - soc_cutoff_aged = lookup_soc_for_ocv(chip, ocv_cutoff_aged); - - if (fg_debug_mask & FG_AGING) - pr_info("aged soc = %d, new soc = %d\n", - soc_cutoff_aged, soc_cutoff_new); - unusable_soc = soc_cutoff_aged - soc_cutoff_new; - - *actual_capacity = div64_s64(((int64_t)chip->nom_cap_uah) - * (1000 - unusable_soc), 1000); - if (fg_debug_mask & FG_AGING) - pr_info("nom cap = %d, actual cap = %d\n", - chip->nom_cap_uah, *actual_capacity); - - return rc; - -error_done: - pr_err("some register reads failed: %d\n", rc); -done: - fg_mem_release(chip); - return rc; -} - -static void battery_age_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - battery_age_work); - - estimate_battery_age(chip, &chip->actual_cap_uah); -} - -static enum power_supply_property fg_power_props[] = { - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_RAW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_VOLTAGE_OCV, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, - POWER_SUPPLY_PROP_CHARGE_NOW, - POWER_SUPPLY_PROP_CHARGE_NOW_RAW, - POWER_SUPPLY_PROP_CHARGE_NOW_ERROR, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_COOL_TEMP, - POWER_SUPPLY_PROP_WARM_TEMP, - POWER_SUPPLY_PROP_RESISTANCE, - POWER_SUPPLY_PROP_RESISTANCE_ID, - POWER_SUPPLY_PROP_BATTERY_TYPE, - POWER_SUPPLY_PROP_UPDATE_NOW, - POWER_SUPPLY_PROP_ESR_COUNT, - POWER_SUPPLY_PROP_VOLTAGE_MIN, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_CYCLE_COUNT_ID, - POWER_SUPPLY_PROP_HI_POWER, -}; - -static int fg_power_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct fg_chip *chip = power_supply_get_drvdata(psy); - bool vbatt_low_sts; - - switch (psp) { - case POWER_SUPPLY_PROP_BATTERY_TYPE: - if (chip->battery_missing) - val->strval = missing_batt_type; - else if (chip->fg_restarting) - val->strval = loading_batt_type; - else - val->strval = chip->batt_type; - break; - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = get_prop_capacity(chip); - break; - case POWER_SUPPLY_PROP_CAPACITY_RAW: - val->intval = get_sram_prop_now(chip, FG_DATA_BATT_SOC); - break; - case POWER_SUPPLY_PROP_CHARGE_NOW_ERROR: - val->intval = get_sram_prop_now(chip, FG_DATA_VINT_ERR); - break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - val->intval = get_sram_prop_now(chip, FG_DATA_CURRENT); - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = get_sram_prop_now(chip, FG_DATA_VOLTAGE); - break; - case POWER_SUPPLY_PROP_VOLTAGE_OCV: - val->intval = get_sram_prop_now(chip, FG_DATA_OCV); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = chip->batt_max_voltage_uv; - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = get_sram_prop_now(chip, FG_DATA_BATT_TEMP); - break; - case POWER_SUPPLY_PROP_COOL_TEMP: - val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_COLD); - break; - case POWER_SUPPLY_PROP_WARM_TEMP: - val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_HOT); - break; - case POWER_SUPPLY_PROP_RESISTANCE: - val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR); - break; - case POWER_SUPPLY_PROP_ESR_COUNT: - val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR_COUNT); - break; - case POWER_SUPPLY_PROP_CYCLE_COUNT: - val->intval = fg_get_cycle_count(chip); - break; - case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: - val->intval = chip->cyc_ctr.id; - break; - case POWER_SUPPLY_PROP_RESISTANCE_ID: - val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID); - break; - case POWER_SUPPLY_PROP_UPDATE_NOW: - val->intval = 0; - break; - case POWER_SUPPLY_PROP_VOLTAGE_MIN: - if (!fg_get_vbatt_status(chip, &vbatt_low_sts)) - val->intval = (int)vbatt_low_sts; - else - val->intval = 1; - break; - case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: - val->intval = chip->nom_cap_uah; - break; - case POWER_SUPPLY_PROP_CHARGE_FULL: - val->intval = chip->learning_data.learned_cc_uah; - break; - case POWER_SUPPLY_PROP_CHARGE_NOW: - val->intval = chip->learning_data.cc_uah; - break; - case POWER_SUPPLY_PROP_CHARGE_NOW_RAW: - val->intval = get_sram_prop_now(chip, FG_DATA_CC_CHARGE); - break; - case POWER_SUPPLY_PROP_HI_POWER: - val->intval = !!chip->bcl_lpm_disabled; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int correction_times[] = { - 1470, - 2940, - 4410, - 5880, - 7350, - 8820, - 10290, - 11760, - 13230, - 14700, - 16170, - 17640, - 19110, - 20580, - 22050, - 23520, - 24990, - 26460, - 27930, - 29400, - 30870, - 32340, - 33810, - 35280, - 36750, - 38220, - 39690, - 41160, - 42630, - 44100, - 45570, - 47040, -}; - -static int correction_factors[] = { - 1000000, - 1007874, - 1015789, - 1023745, - 1031742, - 1039780, - 1047859, - 1055979, - 1064140, - 1072342, - 1080584, - 1088868, - 1097193, - 1105558, - 1113964, - 1122411, - 1130899, - 1139427, - 1147996, - 1156606, - 1165256, - 1173947, - 1182678, - 1191450, - 1200263, - 1209115, - 1218008, - 1226942, - 1235915, - 1244929, - 1253983, - 1263076, -}; - -#define FG_CONVERSION_FACTOR (64198531LL) -static int iavg_3b_to_uah(u8 *buffer, int delta_ms) -{ - int64_t val, i_filtered; - int i, correction_factor; - - for (i = 0; i < ARRAY_SIZE(correction_times); i++) { - if (correction_times[i] > delta_ms) - break; - } - if (i >= ARRAY_SIZE(correction_times)) { - if (fg_debug_mask & FG_STATUS) - pr_info("fuel gauge took more than 32 cycles\n"); - i = ARRAY_SIZE(correction_times) - 1; - } - correction_factor = correction_factors[i]; - if (fg_debug_mask & FG_STATUS) - pr_info("delta_ms = %d, cycles = %d, correction = %d\n", - delta_ms, i, correction_factor); - val = buffer[2] << 16 | buffer[1] << 8 | buffer[0]; - /* convert val from signed 24b to signed 64b */ - i_filtered = (val << 40) >> 40; - val = i_filtered * correction_factor; - val = div64_s64(val + FG_CONVERSION_FACTOR / 2, FG_CONVERSION_FACTOR); - if (fg_debug_mask & FG_STATUS) - pr_info("i_filtered = 0x%llx/%lld, cc_uah = %lld\n", - i_filtered, i_filtered, val); - - return val; -} - -static bool fg_is_temperature_ok_for_learning(struct fg_chip *chip) -{ - int batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP); - - if (batt_temp > chip->learning_data.max_temp - || batt_temp < chip->learning_data.min_temp) { - if (fg_debug_mask & FG_AGING) - pr_info("temp (%d) out of range [%d, %d], aborting\n", - batt_temp, - chip->learning_data.min_temp, - chip->learning_data.max_temp); - return false; - } - return true; -} - -static void fg_cap_learning_stop(struct fg_chip *chip) -{ - chip->learning_data.cc_uah = 0; - chip->learning_data.active = false; -} - -#define I_FILTERED_REG 0x584 -static void fg_cap_learning_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - fg_cap_learning_work); - u8 i_filtered[3], data[3]; - int rc, cc_uah, delta_ms; - ktime_t now_kt, delta_kt; - - mutex_lock(&chip->learning_data.learning_lock); - if (!chip->learning_data.active) - goto fail; - if (!fg_is_temperature_ok_for_learning(chip)) { - fg_cap_learning_stop(chip); - goto fail; - } - - if (chip->wa_flag & USE_CC_SOC_REG) { - mutex_unlock(&chip->learning_data.learning_lock); - fg_relax(&chip->capacity_learning_wakeup_source); - return; - } - - fg_mem_lock(chip); - - rc = fg_mem_read(chip, i_filtered, I_FILTERED_REG, 3, 0, 0); - if (rc) { - pr_err("Failed to read i_filtered: %d\n", rc); - fg_mem_release(chip); - goto fail; - } - memset(data, 0, 3); - rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0); - if (rc) { - pr_err("Failed to clear i_filtered: %d\n", rc); - fg_mem_release(chip); - goto fail; - } - fg_mem_release(chip); - - now_kt = ktime_get_boottime(); - delta_kt = ktime_sub(now_kt, chip->learning_data.time_stamp); - chip->learning_data.time_stamp = now_kt; - - delta_ms = (int)div64_s64(ktime_to_ns(delta_kt), 1000000); - - cc_uah = iavg_3b_to_uah(i_filtered, delta_ms); - chip->learning_data.cc_uah -= cc_uah; - if (fg_debug_mask & FG_AGING) - pr_info("total_cc_uah = %lld\n", chip->learning_data.cc_uah); - -fail: - mutex_unlock(&chip->learning_data.learning_lock); - return; - -} - -#define CC_SOC_BASE_REG 0x5BC -#define CC_SOC_OFFSET 3 -#define CC_SOC_MAGNITUDE_MASK 0x1FFFFFFF -#define CC_SOC_NEGATIVE_BIT BIT(29) -static int fg_get_cc_soc(struct fg_chip *chip, int *cc_soc) -{ - int rc; - u8 reg[4]; - unsigned int temp, magnitude; - - rc = fg_mem_read(chip, reg, CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0); - if (rc) { - pr_err("Failed to read CC_SOC_REG rc=%d\n", rc); - return rc; - } - - temp = reg[3] << 24 | reg[2] << 16 | reg[1] << 8 | reg[0]; - magnitude = temp & CC_SOC_MAGNITUDE_MASK; - if (temp & CC_SOC_NEGATIVE_BIT) - *cc_soc = -1 * (~magnitude + 1); - else - *cc_soc = magnitude; - - return 0; -} - -static int fg_cap_learning_process_full_data(struct fg_chip *chip) -{ - int cc_pc_val, rc = -EINVAL; - unsigned int cc_soc_delta_pc; - int64_t delta_cc_uah; - - if (!chip->learning_data.active) - goto fail; - - if (!fg_is_temperature_ok_for_learning(chip)) { - fg_cap_learning_stop(chip); - goto fail; - } - - rc = fg_get_cc_soc(chip, &cc_pc_val); - if (rc) { - pr_err("failed to get CC_SOC, stopping capacity learning\n"); - fg_cap_learning_stop(chip); - goto fail; - } - - cc_soc_delta_pc = DIV_ROUND_CLOSEST( - abs(cc_pc_val - chip->learning_data.init_cc_pc_val) - * 100, FULL_PERCENT_28BIT); - - delta_cc_uah = div64_s64( - chip->learning_data.learned_cc_uah * cc_soc_delta_pc, - 100); - chip->learning_data.cc_uah = delta_cc_uah + chip->learning_data.cc_uah; - - if (fg_debug_mask & FG_AGING) - pr_info("current cc_soc=%d cc_soc_pc=%d total_cc_uah = %lld\n", - cc_pc_val, cc_soc_delta_pc, - chip->learning_data.cc_uah); - - return 0; - -fail: - return rc; -} - -#define FG_CAP_LEARNING_INTERVAL_NS 30000000000 -static enum alarmtimer_restart fg_cap_learning_alarm_cb(struct alarm *alarm, - ktime_t now) -{ - struct fg_chip *chip = container_of(alarm, struct fg_chip, - fg_cap_learning_alarm); - - if (chip->learning_data.active) { - if (fg_debug_mask & FG_AGING) - pr_info("alarm fired\n"); - schedule_work(&chip->fg_cap_learning_work); - alarm_forward_now(alarm, - ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS)); - return ALARMTIMER_RESTART; - } - if (fg_debug_mask & FG_AGING) - pr_info("alarm misfired\n"); - return ALARMTIMER_NORESTART; -} - -#define FG_AGING_STORAGE_REG 0x5E4 -#define ACTUAL_CAPACITY_REG 0x578 -#define MAH_TO_SOC_CONV_REG 0x4A0 -#define CC_SOC_COEFF_OFFSET 0 -#define ACTUAL_CAPACITY_OFFSET 2 -#define MAH_TO_SOC_CONV_CS_OFFSET 0 -static int fg_calc_and_store_cc_soc_coeff(struct fg_chip *chip, int16_t cc_mah) -{ - int rc; - int64_t cc_to_soc_coeff, mah_to_soc; - u8 data[2]; - - rc = fg_mem_write(chip, (u8 *)&cc_mah, ACTUAL_CAPACITY_REG, 2, - ACTUAL_CAPACITY_OFFSET, 0); - if (rc) { - pr_err("Failed to store actual capacity: %d\n", rc); - return rc; - } - - rc = fg_mem_read(chip, (u8 *)&data, MAH_TO_SOC_CONV_REG, 2, - MAH_TO_SOC_CONV_CS_OFFSET, 0); - if (rc) { - pr_err("Failed to read mah_to_soc_conv_cs: %d\n", rc); - } else { - mah_to_soc = data[1] << 8 | data[0]; - mah_to_soc *= MICRO_UNIT; - cc_to_soc_coeff = div64_s64(mah_to_soc, cc_mah); - half_float_to_buffer(cc_to_soc_coeff, data); - rc = fg_mem_write(chip, (u8 *)data, - ACTUAL_CAPACITY_REG, 2, - CC_SOC_COEFF_OFFSET, 0); - if (rc) - pr_err("Failed to write cc_soc_coeff_offset: %d\n", - rc); - else if (fg_debug_mask & FG_AGING) - pr_info("new cc_soc_coeff %lld [%x %x] saved to sram\n", - cc_to_soc_coeff, data[0], data[1]); - } - return rc; -} - -static void fg_cap_learning_load_data(struct fg_chip *chip) -{ - int16_t cc_mah; - int64_t old_cap = chip->learning_data.learned_cc_uah; - int rc; - - rc = fg_mem_read(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0); - if (rc) { - pr_err("Failed to load aged capacity: %d\n", rc); - } else { - chip->learning_data.learned_cc_uah = cc_mah * 1000; - if (fg_debug_mask & FG_AGING) - pr_info("learned capacity %lld-> %lld/%x uah\n", - old_cap, - chip->learning_data.learned_cc_uah, - cc_mah); - } -} - -static void fg_cap_learning_save_data(struct fg_chip *chip) -{ - int16_t cc_mah; - int rc; - - cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000); - - rc = fg_mem_write(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0); - if (rc) - pr_err("Failed to store aged capacity: %d\n", rc); - else if (fg_debug_mask & FG_AGING) - pr_info("learned capacity %lld uah (%d/0x%x uah) saved to sram\n", - chip->learning_data.learned_cc_uah, - cc_mah, cc_mah); - - if (chip->learning_data.feedback_on) { - rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah); - if (rc) - pr_err("Error in storing cc_soc_coeff, rc:%d\n", rc); - } -} - -static void fg_cap_learning_post_process(struct fg_chip *chip) -{ - int64_t max_inc_val, min_dec_val, old_cap; - - max_inc_val = chip->learning_data.learned_cc_uah - * (1000 + chip->learning_data.max_increment); - do_div(max_inc_val, 1000); - - min_dec_val = chip->learning_data.learned_cc_uah - * (1000 - chip->learning_data.max_decrement); - do_div(min_dec_val, 1000); - - old_cap = chip->learning_data.learned_cc_uah; - if (chip->learning_data.cc_uah > max_inc_val) - chip->learning_data.learned_cc_uah = max_inc_val; - else if (chip->learning_data.cc_uah < min_dec_val) - chip->learning_data.learned_cc_uah = min_dec_val; - else - chip->learning_data.learned_cc_uah = - chip->learning_data.cc_uah; - - fg_cap_learning_save_data(chip); - if (fg_debug_mask & FG_AGING) - pr_info("final cc_uah = %lld, learned capacity %lld -> %lld uah\n", - chip->learning_data.cc_uah, - old_cap, chip->learning_data.learned_cc_uah); -} - -static int get_vbat_est_diff(struct fg_chip *chip) -{ - return abs(fg_data[FG_DATA_VOLTAGE].value - - fg_data[FG_DATA_CPRED_VOLTAGE].value); -} - -#define CBITS_INPUT_FILTER_REG 0x4B4 -#define IBATTF_TAU_MASK 0x38 -#define IBATTF_TAU_99_S 0x30 -static int fg_cap_learning_check(struct fg_chip *chip) -{ - u8 data[4]; - int rc = 0, battery_soc, cc_pc_val; - int vbat_est_diff, vbat_est_thr_uv; - unsigned int cc_pc_100 = FULL_PERCENT_28BIT; - - mutex_lock(&chip->learning_data.learning_lock); - if (chip->status == POWER_SUPPLY_STATUS_CHARGING - && !chip->learning_data.active - && chip->batt_aging_mode == FG_AGING_CC) { - if (chip->learning_data.learned_cc_uah == 0) { - if (fg_debug_mask & FG_AGING) - pr_info("no capacity, aborting\n"); - goto fail; - } - - if (!fg_is_temperature_ok_for_learning(chip)) - goto fail; - - fg_mem_lock(chip); - if (!chip->learning_data.feedback_on) { - vbat_est_diff = get_vbat_est_diff(chip); - vbat_est_thr_uv = chip->learning_data.vbat_est_thr_uv; - if (vbat_est_diff >= vbat_est_thr_uv && - vbat_est_thr_uv > 0) { - if (fg_debug_mask & FG_AGING) - pr_info("vbat_est_diff (%d) < threshold (%d)\n", - vbat_est_diff, vbat_est_thr_uv); - fg_mem_release(chip); - fg_cap_learning_stop(chip); - goto fail; - } - } - battery_soc = get_battery_soc_raw(chip); - if (fg_debug_mask & FG_AGING) - pr_info("checking battery soc (%d vs %d)\n", - battery_soc * 100 / FULL_PERCENT_3B, - chip->learning_data.max_start_soc); - /* check if the battery is low enough to start soc learning */ - if (battery_soc * 100 / FULL_PERCENT_3B - > chip->learning_data.max_start_soc) { - if (fg_debug_mask & FG_AGING) - pr_info("battery soc too low (%d < %d), aborting\n", - battery_soc * 100 / FULL_PERCENT_3B, - chip->learning_data.max_start_soc); - fg_mem_release(chip); - fg_cap_learning_stop(chip); - goto fail; - } - - /* set the coulomb counter to a percentage of the capacity */ - chip->learning_data.cc_uah = div64_s64( - (chip->learning_data.learned_cc_uah * battery_soc), - FULL_PERCENT_3B); - - /* Use CC_SOC_REG based capacity learning */ - if (chip->wa_flag & USE_CC_SOC_REG) { - fg_mem_release(chip); - /* SW_CC_SOC based capacity learning */ - if (fg_get_cc_soc(chip, &cc_pc_val)) { - pr_err("failed to get CC_SOC, stop capacity learning\n"); - fg_cap_learning_stop(chip); - goto fail; - } - - chip->learning_data.init_cc_pc_val = cc_pc_val; - chip->learning_data.active = true; - if (fg_debug_mask & FG_AGING) - pr_info("SW_CC_SOC based learning init_CC_SOC=%d\n", - chip->learning_data.init_cc_pc_val); - } else { - rc = fg_mem_masked_write(chip, CBITS_INPUT_FILTER_REG, - IBATTF_TAU_MASK, IBATTF_TAU_99_S, 0); - if (rc) { - pr_err("Failed to write IF IBAT Tau: %d\n", - rc); - fg_mem_release(chip); - fg_cap_learning_stop(chip); - goto fail; - } - - /* clear the i_filtered register */ - memset(data, 0, 4); - rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0); - if (rc) { - pr_err("Failed to clear i_filtered: %d\n", rc); - fg_mem_release(chip); - fg_cap_learning_stop(chip); - goto fail; - } - fg_mem_release(chip); - chip->learning_data.time_stamp = ktime_get_boottime(); - chip->learning_data.active = true; - - if (fg_debug_mask & FG_AGING) - pr_info("cap learning started, soc = %d cc_uah = %lld\n", - battery_soc * 100 / FULL_PERCENT_3B, - chip->learning_data.cc_uah); - alarm_start_relative(&chip->fg_cap_learning_alarm, - ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS)); - } - } else if ((chip->status != POWER_SUPPLY_STATUS_CHARGING) - && chip->learning_data.active) { - if (fg_debug_mask & FG_AGING) - pr_info("capacity learning stopped\n"); - if (!(chip->wa_flag & USE_CC_SOC_REG)) - alarm_try_to_cancel(&chip->fg_cap_learning_alarm); - - if (chip->status == POWER_SUPPLY_STATUS_FULL) { - if (chip->wa_flag & USE_CC_SOC_REG) { - rc = fg_cap_learning_process_full_data(chip); - if (rc) { - fg_cap_learning_stop(chip); - goto fail; - } - /* reset SW_CC_SOC register to 100% */ - rc = fg_mem_write(chip, (u8 *)&cc_pc_100, - CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0); - if (rc) - pr_err("Failed to reset CC_SOC_REG rc=%d\n", - rc); - } - fg_cap_learning_post_process(chip); - } - - fg_cap_learning_stop(chip); - } - -fail: - mutex_unlock(&chip->learning_data.learning_lock); - return rc; -} - -static bool is_usb_present(struct fg_chip *chip) -{ - union power_supply_propval prop = {0,}; - if (!chip->usb_psy) - chip->usb_psy = power_supply_get_by_name("usb"); - - if (chip->usb_psy) - power_supply_get_property(chip->usb_psy, - POWER_SUPPLY_PROP_PRESENT, &prop); - return prop.intval != 0; -} - -static bool is_dc_present(struct fg_chip *chip) -{ - union power_supply_propval prop = {0,}; - if (!chip->dc_psy) - chip->dc_psy = power_supply_get_by_name("dc"); - - if (chip->dc_psy) - power_supply_get_property(chip->dc_psy, - POWER_SUPPLY_PROP_PRESENT, &prop); - return prop.intval != 0; -} - -static bool is_input_present(struct fg_chip *chip) -{ - return is_usb_present(chip) || is_dc_present(chip); -} - -static bool is_otg_present(struct fg_chip *chip) -{ - union power_supply_propval prop = {0,}; - - if (!chip->usb_psy) - chip->usb_psy = power_supply_get_by_name("usb"); - - if (chip->usb_psy) - power_supply_get_property(chip->usb_psy, - POWER_SUPPLY_PROP_USB_OTG, &prop); - return prop.intval != 0; -} - -static bool is_charger_available(struct fg_chip *chip) -{ - if (!chip->batt_psy_name) - return false; - - if (!chip->batt_psy) - chip->batt_psy = power_supply_get_by_name(chip->batt_psy_name); - - if (!chip->batt_psy) - return false; - - return true; -} - -static int set_prop_enable_charging(struct fg_chip *chip, bool enable) -{ - int rc = 0; - union power_supply_propval ret = {enable, }; - - if (!is_charger_available(chip)) { - pr_err("Charger not available yet!\n"); - return -EINVAL; - } - - rc = power_supply_set_property(chip->batt_psy, - POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, - &ret); - if (rc) { - pr_err("couldn't configure batt chg %d\n", rc); - return rc; - } - - chip->charging_disabled = !enable; - if (fg_debug_mask & FG_STATUS) - pr_info("%sabling charging\n", enable ? "en" : "dis"); - - return rc; -} - -#define MAX_BATTERY_CC_SOC_CAPACITY 150 -static void status_change_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - status_change_work); - unsigned long current_time = 0; - int cc_soc, rc, capacity = get_prop_capacity(chip); - - if (chip->esr_pulse_tune_en) { - fg_stay_awake(&chip->esr_extract_wakeup_source); - schedule_work(&chip->esr_extract_config_work); - } - - if (chip->status == POWER_SUPPLY_STATUS_FULL) { - if (capacity >= 99 && chip->hold_soc_while_full - && chip->health == POWER_SUPPLY_HEALTH_GOOD) { - if (fg_debug_mask & FG_STATUS) - pr_info("holding soc at 100\n"); - chip->charge_full = true; - } else if (fg_debug_mask & FG_STATUS) { - pr_info("terminated charging at %d/0x%02x\n", - capacity, get_monotonic_soc_raw(chip)); - } - } - if (chip->status == POWER_SUPPLY_STATUS_FULL || - chip->status == POWER_SUPPLY_STATUS_CHARGING) { - if (!chip->vbat_low_irq_enabled) { - enable_irq(chip->batt_irq[VBATT_LOW].irq); - enable_irq_wake(chip->batt_irq[VBATT_LOW].irq); - chip->vbat_low_irq_enabled = true; - } - if (!!(chip->wa_flag & PULSE_REQUEST_WA) && capacity == 100) - fg_configure_soc(chip); - } else if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) { - if (chip->vbat_low_irq_enabled) { - disable_irq_wake(chip->batt_irq[VBATT_LOW].irq); - disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq); - chip->vbat_low_irq_enabled = false; - } - } - fg_cap_learning_check(chip); - schedule_work(&chip->update_esr_work); - - if (chip->wa_flag & USE_CC_SOC_REG) { - if (fg_get_cc_soc(chip, &cc_soc)) { - pr_err("failed to get CC_SOC\n"); - return; - } - } - - if (chip->prev_status != chip->status && chip->last_sram_update_time) { - get_current_time(¤t_time); - /* - * When charging status changes, update SRAM parameters if it - * was not updated before 5 seconds from now. - */ - if (chip->last_sram_update_time + 5 < current_time) { - cancel_delayed_work(&chip->update_sram_data); - schedule_delayed_work(&chip->update_sram_data, - msecs_to_jiffies(0)); - } - if (chip->cyc_ctr.en) - schedule_work(&chip->cycle_count_work); - if ((chip->wa_flag & USE_CC_SOC_REG) && - chip->bad_batt_detection_en && - chip->status == POWER_SUPPLY_STATUS_CHARGING) { - chip->sw_cc_soc_data.init_sys_soc = capacity; - chip->sw_cc_soc_data.init_cc_soc = cc_soc; - if (fg_debug_mask & FG_STATUS) - pr_info(" Init_sys_soc %d init_cc_soc %d\n", - chip->sw_cc_soc_data.init_sys_soc, - chip->sw_cc_soc_data.init_cc_soc); - } - } - if ((chip->wa_flag & USE_CC_SOC_REG) && chip->bad_batt_detection_en - && chip->safety_timer_expired) { - chip->sw_cc_soc_data.delta_soc = - DIV_ROUND_CLOSEST(abs(cc_soc - - chip->sw_cc_soc_data.init_cc_soc) - * 100, FULL_PERCENT_28BIT); - chip->sw_cc_soc_data.full_capacity = - chip->sw_cc_soc_data.delta_soc + - chip->sw_cc_soc_data.init_sys_soc; - pr_info("Init_sys_soc %d init_cc_soc %d cc_soc %d delta_soc %d full_capacity %d\n", - chip->sw_cc_soc_data.init_sys_soc, - chip->sw_cc_soc_data.init_cc_soc, cc_soc, - chip->sw_cc_soc_data.delta_soc, - chip->sw_cc_soc_data.full_capacity); - /* - * If sw_cc_soc capacity greater than 150, then it's a bad - * battery. else, reset timer and restart charging. - */ - if (chip->sw_cc_soc_data.full_capacity > - MAX_BATTERY_CC_SOC_CAPACITY) { - pr_info("Battery possibly damaged, do not restart charging\n"); - } else { - pr_info("Reset safety-timer and restart charging\n"); - rc = set_prop_enable_charging(chip, false); - if (rc) { - pr_err("failed to disable charging %d\n", rc); - return; - } - - chip->safety_timer_expired = false; - msleep(200); - - rc = set_prop_enable_charging(chip, true); - if (rc) { - pr_err("failed to enable charging %d\n", rc); - return; - } - } - } -} - -/* - * Check for change in the status of input or OTG and schedule - * IADC gain compensation work. - */ -static void check_gain_compensation(struct fg_chip *chip) -{ - bool input_present = is_input_present(chip); - bool otg_present = is_otg_present(chip); - - if ((chip->wa_flag & IADC_GAIN_COMP_WA) - && ((chip->input_present ^ input_present) - || (chip->otg_present ^ otg_present))) { - fg_stay_awake(&chip->gain_comp_wakeup_source); - chip->input_present = input_present; - chip->otg_present = otg_present; - cancel_work_sync(&chip->gain_comp_work); - schedule_work(&chip->gain_comp_work); - } -} - -static void fg_hysteresis_config(struct fg_chip *chip) -{ - int hard_hot = 0, hard_cold = 0; - - hard_hot = get_prop_jeita_temp(chip, FG_MEM_HARD_HOT); - hard_cold = get_prop_jeita_temp(chip, FG_MEM_HARD_COLD); - if (chip->health == POWER_SUPPLY_HEALTH_OVERHEAT && !chip->batt_hot) { - /* turn down the hard hot threshold */ - chip->batt_hot = true; - set_prop_jeita_temp(chip, FG_MEM_HARD_HOT, - hard_hot - chip->hot_hysteresis); - if (fg_debug_mask & FG_STATUS) - pr_info("hard hot hysteresis: old hot=%d, new hot=%d\n", - hard_hot, hard_hot - chip->hot_hysteresis); - } else if (chip->health == POWER_SUPPLY_HEALTH_COLD && - !chip->batt_cold) { - /* turn up the hard cold threshold */ - chip->batt_cold = true; - set_prop_jeita_temp(chip, FG_MEM_HARD_COLD, - hard_cold + chip->cold_hysteresis); - if (fg_debug_mask & FG_STATUS) - pr_info("hard cold hysteresis: old cold=%d, new cold=%d\n", - hard_cold, hard_cold + chip->hot_hysteresis); - } else if (chip->health != POWER_SUPPLY_HEALTH_OVERHEAT && - chip->batt_hot) { - /* restore the hard hot threshold */ - set_prop_jeita_temp(chip, FG_MEM_HARD_HOT, - hard_hot + chip->hot_hysteresis); - chip->batt_hot = !chip->batt_hot; - if (fg_debug_mask & FG_STATUS) - pr_info("restore hard hot threshold: old hot=%d, new hot=%d\n", - hard_hot, - hard_hot + chip->hot_hysteresis); - } else if (chip->health != POWER_SUPPLY_HEALTH_COLD && - chip->batt_cold) { - /* restore the hard cold threshold */ - set_prop_jeita_temp(chip, FG_MEM_HARD_COLD, - hard_cold - chip->cold_hysteresis); - chip->batt_cold = !chip->batt_cold; - if (fg_debug_mask & FG_STATUS) - pr_info("restore hard cold threshold: old cold=%d, new cold=%d\n", - hard_cold, - hard_cold - chip->cold_hysteresis); - } -} - -#define BATT_INFO_STS(base) (base + 0x09) -#define JEITA_HARD_HOT_RT_STS BIT(6) -#define JEITA_HARD_COLD_RT_STS BIT(5) -static int fg_init_batt_temp_state(struct fg_chip *chip) -{ - int rc = 0; - u8 batt_info_sts; - int hard_hot = 0, hard_cold = 0; - - /* - * read the batt_info_sts register to parse battery's - * initial status and do hysteresis config accordingly. - */ - rc = fg_read(chip, &batt_info_sts, - BATT_INFO_STS(chip->batt_base), 1); - if (rc) { - pr_err("failed to read batt info sts, rc=%d\n", rc); - return rc; - } - - hard_hot = get_prop_jeita_temp(chip, FG_MEM_HARD_HOT); - hard_cold = get_prop_jeita_temp(chip, FG_MEM_HARD_COLD); - chip->batt_hot = - (batt_info_sts & JEITA_HARD_HOT_RT_STS) ? true : false; - chip->batt_cold = - (batt_info_sts & JEITA_HARD_COLD_RT_STS) ? true : false; - if (chip->batt_hot || chip->batt_cold) { - if (chip->batt_hot) { - chip->health = POWER_SUPPLY_HEALTH_OVERHEAT; - set_prop_jeita_temp(chip, FG_MEM_HARD_HOT, - hard_hot - chip->hot_hysteresis); - } else { - chip->health = POWER_SUPPLY_HEALTH_COLD; - set_prop_jeita_temp(chip, FG_MEM_HARD_COLD, - hard_cold + chip->cold_hysteresis); - } - } - - return rc; -} - -static int fg_power_set_property(struct power_supply *psy, - enum power_supply_property psp, - const union power_supply_propval *val) -{ - struct fg_chip *chip = power_supply_get_drvdata(psy); - int rc = 0, unused; - - switch (psp) { - case POWER_SUPPLY_PROP_COOL_TEMP: - rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_COLD, val->intval); - break; - case POWER_SUPPLY_PROP_WARM_TEMP: - rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_HOT, val->intval); - break; - case POWER_SUPPLY_PROP_UPDATE_NOW: - if (val->intval) - update_sram_data(chip, &unused); - break; - case POWER_SUPPLY_PROP_STATUS: - chip->prev_status = chip->status; - chip->status = val->intval; - schedule_work(&chip->status_change_work); - check_gain_compensation(chip); - break; - case POWER_SUPPLY_PROP_HEALTH: - chip->health = val->intval; - if (chip->health == POWER_SUPPLY_HEALTH_GOOD) { - fg_stay_awake(&chip->resume_soc_wakeup_source); - schedule_work(&chip->set_resume_soc_work); - } - - if (chip->jeita_hysteresis_support) - fg_hysteresis_config(chip); - break; - case POWER_SUPPLY_PROP_CHARGE_DONE: - chip->charge_done = val->intval; - if (!chip->resume_soc_lowered) { - fg_stay_awake(&chip->resume_soc_wakeup_source); - schedule_work(&chip->set_resume_soc_work); - } - break; - case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: - if ((val->intval > 0) && (val->intval <= BUCKET_COUNT)) { - chip->cyc_ctr.id = val->intval; - } else { - pr_err("rejecting invalid cycle_count_id = %d\n", - val->intval); - rc = -EINVAL; - } - break; - case POWER_SUPPLY_PROP_SAFETY_TIMER_EXPIRED: - chip->safety_timer_expired = val->intval; - schedule_work(&chip->status_change_work); - break; - case POWER_SUPPLY_PROP_HI_POWER: - if (chip->wa_flag & BCL_HI_POWER_FOR_CHGLED_WA) { - chip->bcl_lpm_disabled = !!val->intval; - schedule_work(&chip->bcl_hi_power_work); - } - break; - default: - return -EINVAL; - }; - - return rc; -}; - -static int fg_property_is_writeable(struct power_supply *psy, - enum power_supply_property psp) -{ - switch (psp) { - case POWER_SUPPLY_PROP_COOL_TEMP: - case POWER_SUPPLY_PROP_WARM_TEMP: - case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: - return 1; - default: - break; - } - - return 0; -} - -#define SRAM_DUMP_START 0x400 -#define SRAM_DUMP_LEN 0x200 -static void dump_sram(struct work_struct *work) -{ - int i, rc; - u8 *buffer, rt_sts; - char str[16]; - struct fg_chip *chip = container_of(work, - struct fg_chip, - dump_sram); - - buffer = devm_kzalloc(chip->dev, SRAM_DUMP_LEN, GFP_KERNEL); - if (buffer == NULL) { - pr_err("Can't allocate buffer\n"); - return; - } - - rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->soc_base), 1); - if (rc) - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->soc_base), rc); - else - pr_info("soc rt_sts: 0x%x\n", rt_sts); - - rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->batt_base), 1); - if (rc) - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->batt_base), rc); - else - pr_info("batt rt_sts: 0x%x\n", rt_sts); - - rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->mem_base), 1); - if (rc) - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->mem_base), rc); - else - pr_info("memif rt_sts: 0x%x\n", rt_sts); - - rc = fg_mem_read(chip, buffer, SRAM_DUMP_START, SRAM_DUMP_LEN, 0, 0); - if (rc) { - pr_err("dump failed: rc = %d\n", rc); - return; - } - - for (i = 0; i < SRAM_DUMP_LEN; i += 4) { - str[0] = '\0'; - fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buffer + i, 4); - pr_info("%03X %s\n", SRAM_DUMP_START + i, str); - } - devm_kfree(chip->dev, buffer); -} - -#define MAXRSCHANGE_REG 0x434 -#define ESR_VALUE_OFFSET 1 -#define ESR_STRICT_VALUE 0x4120391F391F3019 -#define ESR_DEFAULT_VALUE 0x58CD4A6761C34A67 -static void update_esr_value(struct work_struct *work) -{ - union power_supply_propval prop = {0, }; - u64 esr_value; - int rc = 0; - struct fg_chip *chip = container_of(work, - struct fg_chip, - update_esr_work); - - if (!is_charger_available(chip)) - return; - - power_supply_get_property(chip->batt_psy, - POWER_SUPPLY_PROP_CHARGE_TYPE, &prop); - - if (!chip->esr_strict_filter) { - if ((prop.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER && - chip->status == POWER_SUPPLY_STATUS_CHARGING) || - (chip->status == POWER_SUPPLY_STATUS_FULL)) { - esr_value = ESR_STRICT_VALUE; - rc = fg_mem_write(chip, (u8 *)&esr_value, - MAXRSCHANGE_REG, 8, - ESR_VALUE_OFFSET, 0); - if (rc) - pr_err("failed to write strict ESR value rc=%d\n", - rc); - else - chip->esr_strict_filter = true; - } - } else if ((prop.intval != POWER_SUPPLY_CHARGE_TYPE_TAPER && - chip->status == POWER_SUPPLY_STATUS_CHARGING) || - (chip->status == POWER_SUPPLY_STATUS_DISCHARGING)) { - esr_value = ESR_DEFAULT_VALUE; - rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8, - ESR_VALUE_OFFSET, 0); - if (rc) - pr_err("failed to write default ESR value rc=%d\n", rc); - else - chip->esr_strict_filter = false; - } -} - -#define TEMP_COUNTER_REG 0x580 -#define VBAT_FILTERED_OFFSET 1 -#define GAIN_REG 0x424 -#define GAIN_OFFSET 1 -#define K_VCOR_REG 0x484 -#define DEF_GAIN_OFFSET 2 -#define PICO_UNIT 0xE8D4A51000LL -#define ATTO_UNIT 0xDE0B6B3A7640000LL -#define VBAT_REF 3800000 - -/* - * IADC Gain compensation steps: - * If Input/OTG absent: - * - read VBAT_FILTERED, KVCOR, GAIN - * - calculate the gain compensation using following formula: - * gain = (1 + gain) * (1 + kvcor * (vbat_filtered - 3800000)) - 1; - * else - * - reset to the default gain compensation - */ -static void iadc_gain_comp_work(struct work_struct *work) -{ - u8 reg[4]; - int rc; - uint64_t vbat_filtered; - int64_t gain, kvcor, temp, numerator; - struct fg_chip *chip = container_of(work, struct fg_chip, - gain_comp_work); - bool input_present = is_input_present(chip); - bool otg_present = is_otg_present(chip); - - if (!chip->init_done) - goto done; - - if (!input_present && !otg_present) { - /* read VBAT_FILTERED */ - rc = fg_mem_read(chip, reg, TEMP_COUNTER_REG, 3, - VBAT_FILTERED_OFFSET, 0); - if (rc) { - pr_err("Failed to read VBAT: rc=%d\n", rc); - goto done; - } - temp = (reg[2] << 16) | (reg[1] << 8) | reg[0]; - vbat_filtered = div_u64((u64)temp * LSB_24B_NUMRTR, - LSB_24B_DENMTR); - - /* read K_VCOR */ - rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, 0, 0); - if (rc) { - pr_err("Failed to KVCOR rc=%d\n", rc); - goto done; - } - kvcor = half_float(reg); - - /* calculate gain */ - numerator = (MICRO_UNIT + chip->iadc_comp_data.dfl_gain) - * (PICO_UNIT + kvcor * (vbat_filtered - VBAT_REF)) - - ATTO_UNIT; - gain = div64_s64(numerator, PICO_UNIT); - - /* write back gain */ - half_float_to_buffer(gain, reg); - rc = fg_mem_write(chip, reg, GAIN_REG, 2, GAIN_OFFSET, 0); - if (rc) { - pr_err("Failed to write gain reg rc=%d\n", rc); - goto done; - } - - if (fg_debug_mask & FG_STATUS) - pr_info("IADC gain update [%x %x]\n", reg[1], reg[0]); - chip->iadc_comp_data.gain_active = true; - } else { - /* reset gain register */ - rc = fg_mem_write(chip, chip->iadc_comp_data.dfl_gain_reg, - GAIN_REG, 2, GAIN_OFFSET, 0); - if (rc) { - pr_err("unable to write gain comp: %d\n", rc); - goto done; - } - - if (fg_debug_mask & FG_STATUS) - pr_info("IADC gain reset [%x %x]\n", - chip->iadc_comp_data.dfl_gain_reg[1], - chip->iadc_comp_data.dfl_gain_reg[0]); - chip->iadc_comp_data.gain_active = false; - } - -done: - fg_relax(&chip->gain_comp_wakeup_source); -} - -#define BATT_MISSING_STS BIT(6) -static bool is_battery_missing(struct fg_chip *chip) -{ - int rc; - u8 fg_batt_sts; - - rc = fg_read(chip, &fg_batt_sts, - INT_RT_STS(chip->batt_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->batt_base), rc); - return false; - } - - return (fg_batt_sts & BATT_MISSING_STS) ? true : false; -} - -#define SOC_FIRST_EST_DONE BIT(5) -static bool is_first_est_done(struct fg_chip *chip) -{ - int rc; - u8 fg_soc_sts; - - rc = fg_read(chip, &fg_soc_sts, - INT_RT_STS(chip->soc_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->soc_base), rc); - return false; - } - - return (fg_soc_sts & SOC_FIRST_EST_DONE) ? true : false; -} - -static irqreturn_t fg_vbatt_low_handler(int irq, void *_chip) -{ - struct fg_chip *chip = _chip; - int rc; - bool vbatt_low_sts; - - if (fg_debug_mask & FG_IRQS) - pr_info("vbatt-low triggered\n"); - - if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { - rc = fg_get_vbatt_status(chip, &vbatt_low_sts); - if (rc) { - pr_err("error in reading vbatt_status, rc:%d\n", rc); - goto out; - } - if (!vbatt_low_sts && chip->vbat_low_irq_enabled) { - if (fg_debug_mask & FG_IRQS) - pr_info("disabling vbatt_low irq\n"); - disable_irq_wake(chip->batt_irq[VBATT_LOW].irq); - disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq); - chip->vbat_low_irq_enabled = false; - } - } - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); -out: - return IRQ_HANDLED; -} - -static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip) -{ - struct fg_chip *chip = _chip; - bool batt_missing = is_battery_missing(chip); - - if (batt_missing) { - chip->battery_missing = true; - chip->profile_loaded = false; - chip->batt_type = default_batt_type; - mutex_lock(&chip->cyc_ctr.lock); - if (fg_debug_mask & FG_IRQS) - pr_info("battery missing, clearing cycle counters\n"); - clear_cycle_counter(chip); - mutex_unlock(&chip->cyc_ctr.lock); - } else { - if (!chip->use_otp_profile) { - reinit_completion(&chip->batt_id_avail); - reinit_completion(&chip->first_soc_done); - schedule_delayed_work(&chip->batt_profile_init, 0); - cancel_delayed_work(&chip->update_sram_data); - schedule_delayed_work( - &chip->update_sram_data, - msecs_to_jiffies(0)); - } else { - chip->battery_missing = false; - } - } - - if (fg_debug_mask & FG_IRQS) - pr_info("batt-missing triggered: %s\n", - batt_missing ? "missing" : "present"); - - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - return IRQ_HANDLED; -} - -static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip) -{ - struct fg_chip *chip = _chip; - u8 mem_if_sts; - int rc; - - rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1); - if (rc) { - pr_err("failed to read mem status rc=%d\n", rc); - return IRQ_HANDLED; - } - - if (fg_check_sram_access(chip)) { - if ((fg_debug_mask & FG_IRQS) - & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES)) - pr_info("sram access granted\n"); - reinit_completion(&chip->sram_access_revoked); - complete_all(&chip->sram_access_granted); - } else { - if ((fg_debug_mask & FG_IRQS) - & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES)) - pr_info("sram access revoked\n"); - complete_all(&chip->sram_access_revoked); - } - - if (!rc && (fg_debug_mask & FG_IRQS) - & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES)) - pr_info("mem_if sts 0x%02x\n", mem_if_sts); - - return IRQ_HANDLED; -} - -static irqreturn_t fg_soc_irq_handler(int irq, void *_chip) -{ - struct fg_chip *chip = _chip; - u8 soc_rt_sts; - int rc; - - rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->soc_base), rc); - } - - if (fg_debug_mask & FG_IRQS) - pr_info("triggered 0x%x\n", soc_rt_sts); - - schedule_work(&chip->battery_age_work); - - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - - if (chip->rslow_comp.chg_rs_to_rslow > 0 && - chip->rslow_comp.chg_rslow_comp_c1 > 0 && - chip->rslow_comp.chg_rslow_comp_c2 > 0) - schedule_work(&chip->rslow_comp_work); - if (chip->cyc_ctr.en) - schedule_work(&chip->cycle_count_work); - schedule_work(&chip->update_esr_work); - if (chip->charge_full) - schedule_work(&chip->charge_full_work); - if (chip->wa_flag & IADC_GAIN_COMP_WA - && chip->iadc_comp_data.gain_active) { - fg_stay_awake(&chip->gain_comp_wakeup_source); - schedule_work(&chip->gain_comp_work); - } - - if (chip->wa_flag & USE_CC_SOC_REG - && chip->learning_data.active) { - fg_stay_awake(&chip->capacity_learning_wakeup_source); - schedule_work(&chip->fg_cap_learning_work); - } - - if (chip->esr_pulse_tune_en) { - fg_stay_awake(&chip->esr_extract_wakeup_source); - schedule_work(&chip->esr_extract_config_work); - } - - return IRQ_HANDLED; -} - -#define FG_EMPTY_DEBOUNCE_MS 1500 -static irqreturn_t fg_empty_soc_irq_handler(int irq, void *_chip) -{ - struct fg_chip *chip = _chip; - u8 soc_rt_sts; - int rc; - - rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->soc_base), rc); - goto done; - } - - if (fg_debug_mask & FG_IRQS) - pr_info("triggered 0x%x\n", soc_rt_sts); - if (fg_is_batt_empty(chip)) { - fg_stay_awake(&chip->empty_check_wakeup_source); - schedule_delayed_work(&chip->check_empty_work, - msecs_to_jiffies(FG_EMPTY_DEBOUNCE_MS)); - } else { - chip->soc_empty = false; - } - -done: - return IRQ_HANDLED; -} - -static irqreturn_t fg_first_soc_irq_handler(int irq, void *_chip) -{ - struct fg_chip *chip = _chip; - - if (fg_debug_mask & FG_IRQS) - pr_info("triggered\n"); - - if (fg_est_dump) - schedule_work(&chip->dump_sram); - - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - - complete_all(&chip->first_soc_done); - - return IRQ_HANDLED; -} - -static void fg_external_power_changed(struct power_supply *psy) -{ - struct fg_chip *chip = power_supply_get_drvdata(psy); - - if (is_input_present(chip) && chip->rslow_comp.active && - chip->rslow_comp.chg_rs_to_rslow > 0 && - chip->rslow_comp.chg_rslow_comp_c1 > 0 && - chip->rslow_comp.chg_rslow_comp_c2 > 0) - schedule_work(&chip->rslow_comp_work); - if (!is_input_present(chip) && chip->resume_soc_lowered) { - fg_stay_awake(&chip->resume_soc_wakeup_source); - schedule_work(&chip->set_resume_soc_work); - } - if (!is_input_present(chip) && chip->charge_full) - schedule_work(&chip->charge_full_work); -} - -static void set_resume_soc_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - set_resume_soc_work); - int rc, resume_soc_raw; - - if (is_input_present(chip) && !chip->resume_soc_lowered) { - if (!chip->charge_done) - goto done; - resume_soc_raw = get_monotonic_soc_raw(chip) - - (0xFF - settings[FG_MEM_RESUME_SOC].value); - if (resume_soc_raw > 0 && resume_soc_raw < FULL_SOC_RAW) { - rc = fg_set_resume_soc(chip, resume_soc_raw); - if (rc) { - pr_err("Couldn't set resume SOC for FG\n"); - goto done; - } - if (fg_debug_mask & FG_STATUS) { - pr_info("resume soc lowered to 0x%02x\n", - resume_soc_raw); - } - } else if (settings[FG_MEM_RESUME_SOC].value > 0) { - pr_err("bad resume soc 0x%02x\n", resume_soc_raw); - } - chip->charge_done = false; - chip->resume_soc_lowered = true; - } else if (chip->resume_soc_lowered && (!is_input_present(chip) - || chip->health == POWER_SUPPLY_HEALTH_GOOD)) { - resume_soc_raw = settings[FG_MEM_RESUME_SOC].value; - if (resume_soc_raw > 0 && resume_soc_raw < FULL_SOC_RAW) { - rc = fg_set_resume_soc(chip, resume_soc_raw); - if (rc) { - pr_err("Couldn't set resume SOC for FG\n"); - goto done; - } - if (fg_debug_mask & FG_STATUS) { - pr_info("resume soc set to 0x%02x\n", - resume_soc_raw); - } - } else if (settings[FG_MEM_RESUME_SOC].value > 0) { - pr_err("bad resume soc 0x%02x\n", resume_soc_raw); - } - chip->resume_soc_lowered = false; - } -done: - fg_relax(&chip->resume_soc_wakeup_source); -} - - -#define OCV_COEFFS_START_REG 0x4C0 -#define OCV_JUNCTION_REG 0x4D8 -#define NOM_CAP_REG 0x4F4 -#define CUTOFF_VOLTAGE_REG 0x40C -#define RSLOW_CFG_REG 0x538 -#define RSLOW_CFG_OFFSET 2 -#define RSLOW_THRESH_REG 0x52C -#define RSLOW_THRESH_OFFSET 0 -#define TEMP_RS_TO_RSLOW_OFFSET 2 -#define RSLOW_COMP_REG 0x528 -#define RSLOW_COMP_C1_OFFSET 0 -#define RSLOW_COMP_C2_OFFSET 2 -static int populate_system_data(struct fg_chip *chip) -{ - u8 buffer[24]; - int rc, i; - int16_t cc_mah; - - fg_mem_lock(chip); - rc = fg_mem_read(chip, buffer, OCV_COEFFS_START_REG, 24, 0, 0); - if (rc) { - pr_err("Failed to read ocv coefficients: %d\n", rc); - goto done; - } - for (i = 0; i < 12; i += 1) - chip->ocv_coeffs[i] = half_float(buffer + (i * 2)); - if (fg_debug_mask & FG_AGING) { - pr_info("coeffs1 = %lld %lld %lld %lld\n", - chip->ocv_coeffs[0], chip->ocv_coeffs[1], - chip->ocv_coeffs[2], chip->ocv_coeffs[3]); - pr_info("coeffs2 = %lld %lld %lld %lld\n", - chip->ocv_coeffs[4], chip->ocv_coeffs[5], - chip->ocv_coeffs[6], chip->ocv_coeffs[7]); - pr_info("coeffs3 = %lld %lld %lld %lld\n", - chip->ocv_coeffs[8], chip->ocv_coeffs[9], - chip->ocv_coeffs[10], chip->ocv_coeffs[11]); - } - rc = fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 0, 0); - chip->ocv_junction_p1p2 = buffer[0] * 100 / 255; - rc |= fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 1, 0); - chip->ocv_junction_p2p3 = buffer[0] * 100 / 255; - if (rc) { - pr_err("Failed to read ocv junctions: %d\n", rc); - goto done; - } - rc = fg_mem_read(chip, buffer, NOM_CAP_REG, 2, 0, 0); - if (rc) { - pr_err("Failed to read nominal capacitance: %d\n", rc); - goto done; - } - chip->nom_cap_uah = bcap_uah_2b(buffer); - chip->actual_cap_uah = chip->nom_cap_uah; - if (chip->learning_data.learned_cc_uah == 0) { - chip->learning_data.learned_cc_uah = chip->nom_cap_uah; - fg_cap_learning_save_data(chip); - } else if (chip->learning_data.feedback_on) { - cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000); - rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah); - if (rc) - pr_err("Error in restoring cc_soc_coeff, rc:%d\n", rc); - } - rc = fg_mem_read(chip, buffer, CUTOFF_VOLTAGE_REG, 2, 0, 0); - if (rc) { - pr_err("Failed to read cutoff voltage: %d\n", rc); - goto done; - } - chip->cutoff_voltage = voltage_2b(buffer); - if (fg_debug_mask & FG_AGING) - pr_info("cutoff_voltage = %lld, nom_cap_uah = %d p1p2 = %d, p2p3 = %d\n", - chip->cutoff_voltage, chip->nom_cap_uah, - chip->ocv_junction_p1p2, - chip->ocv_junction_p2p3); - - rc = fg_mem_read(chip, buffer, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET, 0); - if (rc) { - pr_err("unable to read rslow cfg: %d\n", rc); - goto done; - } - chip->rslow_comp.rslow_cfg = buffer[0]; - rc = fg_mem_read(chip, buffer, RSLOW_THRESH_REG, 1, - RSLOW_THRESH_OFFSET, 0); - if (rc) { - pr_err("unable to read rslow thresh: %d\n", rc); - goto done; - } - chip->rslow_comp.rslow_thr = buffer[0]; - rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2, - RSLOW_THRESH_OFFSET, 0); - if (rc) { - pr_err("unable to read rs to rslow: %d\n", rc); - goto done; - } - memcpy(chip->rslow_comp.rs_to_rslow, buffer, 2); - rc = fg_mem_read(chip, buffer, RSLOW_COMP_REG, 4, - RSLOW_COMP_C1_OFFSET, 0); - if (rc) { - pr_err("unable to read rslow comp: %d\n", rc); - goto done; - } - memcpy(chip->rslow_comp.rslow_comp, buffer, 4); - -done: - fg_mem_release(chip); - return rc; -} - -#define RSLOW_CFG_MASK (BIT(2) | BIT(3) | BIT(4) | BIT(5)) -#define RSLOW_CFG_ON_VAL (BIT(2) | BIT(3)) -#define RSLOW_THRESH_FULL_VAL 0xFF -static int fg_rslow_charge_comp_set(struct fg_chip *chip) -{ - int rc; - u8 buffer[2]; - - mutex_lock(&chip->rslow_comp.lock); - fg_mem_lock(chip); - - rc = fg_mem_masked_write(chip, RSLOW_CFG_REG, - RSLOW_CFG_MASK, RSLOW_CFG_ON_VAL, RSLOW_CFG_OFFSET); - if (rc) { - pr_err("unable to write rslow cfg: %d\n", rc); - goto done; - } - rc = fg_mem_masked_write(chip, RSLOW_THRESH_REG, - 0xFF, RSLOW_THRESH_FULL_VAL, RSLOW_THRESH_OFFSET); - if (rc) { - pr_err("unable to write rslow thresh: %d\n", rc); - goto done; - } - - half_float_to_buffer(chip->rslow_comp.chg_rs_to_rslow, buffer); - rc = fg_mem_write(chip, buffer, - TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0); - if (rc) { - pr_err("unable to write rs to rslow: %d\n", rc); - goto done; - } - half_float_to_buffer(chip->rslow_comp.chg_rslow_comp_c1, buffer); - rc = fg_mem_write(chip, buffer, - RSLOW_COMP_REG, 2, RSLOW_COMP_C1_OFFSET, 0); - if (rc) { - pr_err("unable to write rslow comp: %d\n", rc); - goto done; - } - half_float_to_buffer(chip->rslow_comp.chg_rslow_comp_c2, buffer); - rc = fg_mem_write(chip, buffer, - RSLOW_COMP_REG, 2, RSLOW_COMP_C2_OFFSET, 0); - if (rc) { - pr_err("unable to write rslow comp: %d\n", rc); - goto done; - } - chip->rslow_comp.active = true; - if (fg_debug_mask & FG_STATUS) - pr_info("Activated rslow charge comp values\n"); - -done: - fg_mem_release(chip); - mutex_unlock(&chip->rslow_comp.lock); - return rc; -} - -#define RSLOW_CFG_ORIG_MASK (BIT(4) | BIT(5)) -static int fg_rslow_charge_comp_clear(struct fg_chip *chip) -{ - u8 reg; - int rc; - - mutex_lock(&chip->rslow_comp.lock); - fg_mem_lock(chip); - - reg = chip->rslow_comp.rslow_cfg & RSLOW_CFG_ORIG_MASK; - rc = fg_mem_masked_write(chip, RSLOW_CFG_REG, - RSLOW_CFG_MASK, reg, RSLOW_CFG_OFFSET); - if (rc) { - pr_err("unable to write rslow cfg: %d\n", rc); - goto done; - } - rc = fg_mem_masked_write(chip, RSLOW_THRESH_REG, - 0xFF, chip->rslow_comp.rslow_thr, RSLOW_THRESH_OFFSET); - if (rc) { - pr_err("unable to write rslow thresh: %d\n", rc); - goto done; - } - - rc = fg_mem_write(chip, chip->rslow_comp.rs_to_rslow, - TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0); - if (rc) { - pr_err("unable to write rs to rslow: %d\n", rc); - goto done; - } - rc = fg_mem_write(chip, chip->rslow_comp.rslow_comp, - RSLOW_COMP_REG, 4, RSLOW_COMP_C1_OFFSET, 0); - if (rc) { - pr_err("unable to write rslow comp: %d\n", rc); - goto done; - } - chip->rslow_comp.active = false; - if (fg_debug_mask & FG_STATUS) - pr_info("Cleared rslow charge comp values\n"); - -done: - fg_mem_release(chip); - mutex_unlock(&chip->rslow_comp.lock); - return rc; -} - -static void rslow_comp_work(struct work_struct *work) -{ - int battery_soc_1b; - struct fg_chip *chip = container_of(work, - struct fg_chip, - rslow_comp_work); - - battery_soc_1b = get_battery_soc_raw(chip) >> 16; - if (battery_soc_1b > chip->rslow_comp.chg_rslow_comp_thr - && chip->status == POWER_SUPPLY_STATUS_CHARGING) { - if (!chip->rslow_comp.active) - fg_rslow_charge_comp_set(chip); - } else { - if (chip->rslow_comp.active) - fg_rslow_charge_comp_clear(chip); - } -} - -#define MICROUNITS_TO_ADC_RAW(units) \ - div64_s64(units * LSB_16B_DENMTR, LSB_16B_NUMRTR) -static int update_chg_iterm(struct fg_chip *chip) -{ - u8 data[2]; - u16 converted_current_raw; - s64 current_ma = -settings[FG_MEM_CHG_TERM_CURRENT].value; - - converted_current_raw = (s16)MICROUNITS_TO_ADC_RAW(current_ma * 1000); - data[0] = cpu_to_le16(converted_current_raw) & 0xFF; - data[1] = cpu_to_le16(converted_current_raw) >> 8; - - if (fg_debug_mask & FG_STATUS) - pr_info("current = %lld, converted_raw = %04x, data = %02x %02x\n", - current_ma, converted_current_raw, data[0], data[1]); - return fg_mem_write(chip, data, - settings[FG_MEM_CHG_TERM_CURRENT].address, - 2, settings[FG_MEM_CHG_TERM_CURRENT].offset, 0); -} - -#define CC_CV_SETPOINT_REG 0x4F8 -#define CC_CV_SETPOINT_OFFSET 0 -static void update_cc_cv_setpoint(struct fg_chip *chip) -{ - int rc; - u8 tmp[2]; - - if (!chip->cc_cv_threshold_mv) - return; - batt_to_setpoint_adc(chip->cc_cv_threshold_mv, tmp); - rc = fg_mem_write(chip, tmp, CC_CV_SETPOINT_REG, 2, - CC_CV_SETPOINT_OFFSET, 0); - if (rc) { - pr_err("failed to write CC_CV_VOLT rc=%d\n", rc); - return; - } - if (fg_debug_mask & FG_STATUS) - pr_info("Wrote %x %x to address %x for CC_CV setpoint\n", - tmp[0], tmp[1], CC_CV_SETPOINT_REG); -} - -#define CBITS_INPUT_FILTER_REG 0x4B4 -#define CBITS_RMEAS1_OFFSET 1 -#define CBITS_RMEAS2_OFFSET 2 -#define CBITS_RMEAS1_DEFAULT_VAL 0x65 -#define CBITS_RMEAS2_DEFAULT_VAL 0x65 -#define IMPTR_FAST_TIME_SHIFT 1 -#define IMPTR_LONG_TIME_SHIFT (1 << 4) -#define IMPTR_PULSE_CTR_CHG 1 -#define IMPTR_PULSE_CTR_DISCHG (1 << 4) -static int fg_config_imptr_pulse(struct fg_chip *chip, bool slow) -{ - int rc; - u8 cntr[2] = {0, 0}; - u8 val; - - if (slow == chip->imptr_pulse_slow_en) { - if (fg_debug_mask & FG_STATUS) - pr_info("imptr_pulse_slow is %sabled already\n", - slow ? "en" : "dis"); - return 0; - } - - fg_mem_lock(chip); - - val = slow ? (IMPTR_FAST_TIME_SHIFT | IMPTR_LONG_TIME_SHIFT) : - CBITS_RMEAS1_DEFAULT_VAL; - rc = fg_mem_write(chip, &val, CBITS_INPUT_FILTER_REG, 1, - CBITS_RMEAS1_OFFSET, 0); - if (rc) { - pr_err("unable to write cbits_rmeas1_offset rc=%d\n", rc); - goto done; - } - - val = slow ? (IMPTR_PULSE_CTR_CHG | IMPTR_PULSE_CTR_DISCHG) : - CBITS_RMEAS2_DEFAULT_VAL; - rc = fg_mem_write(chip, &val, CBITS_INPUT_FILTER_REG, 1, - CBITS_RMEAS2_OFFSET, 0); - if (rc) { - pr_err("unable to write cbits_rmeas2_offset rc=%d\n", rc); - goto done; - } - - if (slow) { - rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 4, - COUNTER_IMPTR_OFFSET, 0); - if (rc) { - pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc); - goto done; - } - - rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2, - COUNTER_PULSE_OFFSET, 0); - if (rc) { - pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc); - goto done; - } - } - - chip->imptr_pulse_slow_en = slow; - if (fg_debug_mask & FG_STATUS) - pr_info("imptr_pulse_slow is %sabled\n", slow ? "en" : "dis"); -done: - fg_mem_release(chip); - return rc; -} - -#define CURRENT_DELTA_MIN_REG 0x42C -#define CURRENT_DELTA_MIN_OFFSET 1 -#define SYS_CFG_1_REG 0x4AC -#define SYS_CFG_1_OFFSET 0 -#define CURRENT_DELTA_MIN_DEFAULT 0x16 -#define CURRENT_DELTA_MIN_500MA 0xCD -#define RSLOW_CFG_USE_FIX_RSER_VAL BIT(7) -#define ENABLE_ESR_PULSE_VAL BIT(3) -static int fg_config_esr_extract(struct fg_chip *chip, bool disable) -{ - int rc; - u8 val; - - if (disable == chip->esr_extract_disabled) { - if (fg_debug_mask & FG_STATUS) - pr_info("ESR extract already %sabled\n", - disable ? "dis" : "en"); - return 0; - } - - fg_mem_lock(chip); - - val = disable ? CURRENT_DELTA_MIN_500MA : - CURRENT_DELTA_MIN_DEFAULT; - rc = fg_mem_write(chip, &val, CURRENT_DELTA_MIN_REG, 1, - CURRENT_DELTA_MIN_OFFSET, 0); - if (rc) { - pr_err("unable to write curr_delta_min rc=%d\n", rc); - goto done; - } - - val = disable ? RSLOW_CFG_USE_FIX_RSER_VAL : 0; - rc = fg_mem_masked_write(chip, RSLOW_CFG_REG, - RSLOW_CFG_USE_FIX_RSER_VAL, val, RSLOW_CFG_OFFSET); - if (rc) { - pr_err("unable to write rslow cfg rc= %d\n", rc); - goto done; - } - - val = disable ? 0 : ENABLE_ESR_PULSE_VAL; - rc = fg_mem_masked_write(chip, SYS_CFG_1_REG, - ENABLE_ESR_PULSE_VAL, val, SYS_CFG_1_OFFSET); - if (rc) { - pr_err("unable to write sys_cfg_1 rc= %d\n", rc); - goto done; - } - - chip->esr_extract_disabled = disable; - if (fg_debug_mask & FG_STATUS) - pr_info("ESR extract is %sabled\n", disable ? "dis" : "en"); -done: - fg_mem_release(chip); - return rc; -} - -#define ESR_EXTRACT_STOP_SOC 2 -#define IMPTR_PULSE_CONFIG_SOC 5 -static void esr_extract_config_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, struct fg_chip, - esr_extract_config_work); - bool input_present = is_input_present(chip); - int capacity = get_prop_capacity(chip); - - if (input_present && capacity <= ESR_EXTRACT_STOP_SOC) { - fg_config_esr_extract(chip, true); - } else if (capacity > ESR_EXTRACT_STOP_SOC) { - fg_config_esr_extract(chip, false); - - if (capacity <= IMPTR_PULSE_CONFIG_SOC) - fg_config_imptr_pulse(chip, true); - else - fg_config_imptr_pulse(chip, false); - } - - fg_relax(&chip->esr_extract_wakeup_source); -} - -#define LOW_LATENCY BIT(6) -#define BATT_PROFILE_OFFSET 0x4C0 -#define PROFILE_INTEGRITY_REG 0x53C -#define PROFILE_INTEGRITY_BIT BIT(0) -#define FIRST_EST_DONE_BIT BIT(5) -#define MAX_TRIES_FIRST_EST 3 -#define FIRST_EST_WAIT_MS 2000 -#define PROFILE_LOAD_TIMEOUT_MS 5000 -static int fg_do_restart(struct fg_chip *chip, bool write_profile) -{ - int rc, ibat_ua; - u8 reg = 0; - u8 buf[2]; - bool tried_once = false; - - if (fg_debug_mask & FG_STATUS) - pr_info("restarting fuel gauge...\n"); - -try_again: - if (write_profile) { - if (!chip->charging_disabled) { - pr_err("Charging not yet disabled!\n"); - return -EINVAL; - } - - ibat_ua = get_sram_prop_now(chip, FG_DATA_CURRENT); - if (ibat_ua == -EINVAL) { - pr_err("SRAM not updated yet!\n"); - return ibat_ua; - } - - if (ibat_ua < 0) { - pr_warn("Charging enabled?, ibat_ua: %d\n", ibat_ua); - - if (!tried_once) { - cancel_delayed_work(&chip->update_sram_data); - schedule_delayed_work(&chip->update_sram_data, - msecs_to_jiffies(0)); - msleep(1000); - tried_once = true; - goto try_again; - } - } - } - - chip->fg_restarting = true; - /* - * save the temperature if the sw rbias control is active so that there - * is no gap of time when there is no valid temperature read after the - * restart - */ - if (chip->sw_rbias_ctrl) { - rc = fg_mem_read(chip, buf, - fg_data[FG_DATA_BATT_TEMP].address, - fg_data[FG_DATA_BATT_TEMP].len, - fg_data[FG_DATA_BATT_TEMP].offset, 0); - if (rc) { - pr_err("failed to read batt temp rc=%d\n", rc); - goto sub_and_fail; - } - } - /* - * release the sram access and configure the correct settings - * before re-requesting access. - */ - mutex_lock(&chip->rw_lock); - fg_release_access(chip); - - rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD, - NO_OTP_PROF_RELOAD, 0, 1); - if (rc) { - pr_err("failed to set no otp reload bit\n"); - goto unlock_and_fail; - } - - /* unset the restart bits so the fg doesn't continuously restart */ - reg = REDO_FIRST_ESTIMATE | RESTART_GO; - rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART, - reg, 0, 1); - if (rc) { - pr_err("failed to unset fg restart: %d\n", rc); - goto unlock_and_fail; - } - - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), - LOW_LATENCY, LOW_LATENCY, 1); - if (rc) { - pr_err("failed to set low latency access bit\n"); - goto unlock_and_fail; - } - mutex_unlock(&chip->rw_lock); - - /* read once to get a fg cycle in */ - rc = fg_mem_read(chip, ®, PROFILE_INTEGRITY_REG, 1, 0, 0); - if (rc) { - pr_err("failed to read profile integrity rc=%d\n", rc); - goto fail; - } - - /* - * If this is not the first time a profile has been loaded, sleep for - * 3 seconds to make sure the NO_OTP_RELOAD is cleared in memory - */ - if (chip->first_profile_loaded) - msleep(3000); - - mutex_lock(&chip->rw_lock); - fg_release_access(chip); - rc = fg_masked_write(chip, MEM_INTF_CFG(chip), LOW_LATENCY, 0, 1); - if (rc) { - pr_err("failed to set low latency access bit\n"); - goto unlock_and_fail; - } - - atomic_add_return(1, &chip->memif_user_cnt); - mutex_unlock(&chip->rw_lock); - - if (write_profile) { - /* write the battery profile */ - rc = fg_mem_write(chip, chip->batt_profile, BATT_PROFILE_OFFSET, - chip->batt_profile_len, 0, 1); - if (rc) { - pr_err("failed to write profile rc=%d\n", rc); - goto sub_and_fail; - } - /* write the integrity bits and release access */ - rc = fg_mem_masked_write(chip, PROFILE_INTEGRITY_REG, - PROFILE_INTEGRITY_BIT, - PROFILE_INTEGRITY_BIT, 0); - if (rc) { - pr_err("failed to write profile rc=%d\n", rc); - goto sub_and_fail; - } - } - - /* decrement the user count so that memory access can be released */ - fg_release_access_if_necessary(chip); - - /* - * make sure that the first estimate has completed - * in case of a hotswap - */ - rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done, - msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS)); - if (rc <= 0) { - pr_err("transaction timed out rc=%d\n", rc); - rc = -ETIMEDOUT; - goto fail; - } - - /* - * reinitialize the completion so that the driver knows when the restart - * finishes - */ - reinit_completion(&chip->first_soc_done); - - if (chip->esr_pulse_tune_en) { - fg_stay_awake(&chip->esr_extract_wakeup_source); - schedule_work(&chip->esr_extract_config_work); - } - - /* - * set the restart bits so that the next fg cycle will not reload - * the profile - */ - rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD, - NO_OTP_PROF_RELOAD, NO_OTP_PROF_RELOAD, 1); - if (rc) { - pr_err("failed to set no otp reload bit\n"); - goto fail; - } - - reg = REDO_FIRST_ESTIMATE | RESTART_GO; - rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART, - reg, reg, 1); - if (rc) { - pr_err("failed to set fg restart: %d\n", rc); - goto fail; - } - - /* wait for the first estimate to complete */ - rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done, - msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS)); - if (rc <= 0) { - pr_err("transaction timed out rc=%d\n", rc); - rc = -ETIMEDOUT; - goto fail; - } - rc = fg_read(chip, ®, INT_RT_STS(chip->soc_base), 1); - if (rc) { - pr_err("spmi read failed: addr=%03X, rc=%d\n", - INT_RT_STS(chip->soc_base), rc); - goto fail; - } - if ((reg & FIRST_EST_DONE_BIT) == 0) - pr_err("Battery profile reloading failed, no first estimate\n"); - - rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD, - NO_OTP_PROF_RELOAD, 0, 1); - if (rc) { - pr_err("failed to set no otp reload bit\n"); - goto fail; - } - /* unset the restart bits so the fg doesn't continuously restart */ - reg = REDO_FIRST_ESTIMATE | RESTART_GO; - rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART, - reg, 0, 1); - if (rc) { - pr_err("failed to unset fg restart: %d\n", rc); - goto fail; - } - - /* restore the battery temperature reading here */ - if (chip->sw_rbias_ctrl) { - if (fg_debug_mask & FG_STATUS) - pr_info("reloaded 0x%02x%02x into batt temp", - buf[0], buf[1]); - rc = fg_mem_write(chip, buf, - fg_data[FG_DATA_BATT_TEMP].address, - fg_data[FG_DATA_BATT_TEMP].len, - fg_data[FG_DATA_BATT_TEMP].offset, 0); - if (rc) { - pr_err("failed to write batt temp rc=%d\n", rc); - goto fail; - } - } - - /* Enable charging now as the first estimate is done now */ - if (chip->charging_disabled) { - rc = set_prop_enable_charging(chip, true); - if (rc) - pr_err("Failed to enable charging, rc=%d\n", rc); - else - chip->charging_disabled = false; - } - - chip->fg_restarting = false; - - if (fg_debug_mask & FG_STATUS) - pr_info("done!\n"); - return 0; - -unlock_and_fail: - mutex_unlock(&chip->rw_lock); - goto fail; -sub_and_fail: - fg_release_access_if_necessary(chip); - goto fail; -fail: - chip->fg_restarting = false; - return -EINVAL; -} - -#define FG_PROFILE_LEN 128 -#define PROFILE_COMPARE_LEN 32 -#define THERMAL_COEFF_ADDR 0x444 -#define THERMAL_COEFF_OFFSET 0x2 -#define BATTERY_PSY_WAIT_MS 2000 -static int fg_batt_profile_init(struct fg_chip *chip) -{ - int rc = 0, ret, len, batt_id; - struct device_node *node = chip->pdev->dev.of_node; - struct device_node *batt_node, *profile_node; - const char *data, *batt_type_str; - bool tried_again = false, vbat_in_range, profiles_same; - u8 reg = 0; - -wait: - fg_stay_awake(&chip->profile_wakeup_source); - ret = wait_for_completion_interruptible_timeout(&chip->batt_id_avail, - msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS)); - /* If we were interrupted wait again one more time. */ - if (ret == -ERESTARTSYS && !tried_again) { - tried_again = true; - pr_debug("interrupted, waiting again\n"); - goto wait; - } else if (ret <= 0) { - rc = -ETIMEDOUT; - pr_err("profile loading timed out rc=%d\n", rc); - goto no_profile; - } - - batt_node = of_find_node_by_name(node, "qcom,battery-data"); - if (!batt_node) { - pr_warn("No available batterydata, using OTP defaults\n"); - rc = 0; - goto no_profile; - } - - batt_id = get_sram_prop_now(chip, FG_DATA_BATT_ID); - batt_id /= 1000; - if (fg_debug_mask & FG_STATUS) - pr_info("battery id = %dKOhms\n", batt_id); - - profile_node = of_batterydata_get_best_profile(batt_node, batt_id, - fg_batt_type); - if (IS_ERR_OR_NULL(profile_node)) { - rc = PTR_ERR(profile_node); - pr_err("couldn't find profile handle %d\n", rc); - goto no_profile; - } - - /* read rslow compensation values if they're available */ - rc = of_property_read_u32(profile_node, "qcom,chg-rs-to-rslow", - &chip->rslow_comp.chg_rs_to_rslow); - if (rc) { - chip->rslow_comp.chg_rs_to_rslow = -EINVAL; - if (rc != -EINVAL) - pr_err("Could not read rs to rslow: %d\n", rc); - } - rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-c1", - &chip->rslow_comp.chg_rslow_comp_c1); - if (rc) { - chip->rslow_comp.chg_rslow_comp_c1 = -EINVAL; - if (rc != -EINVAL) - pr_err("Could not read rslow comp c1: %d\n", rc); - } - rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-c2", - &chip->rslow_comp.chg_rslow_comp_c2); - if (rc) { - chip->rslow_comp.chg_rslow_comp_c2 = -EINVAL; - if (rc != -EINVAL) - pr_err("Could not read rslow comp c2: %d\n", rc); - } - rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-thr", - &chip->rslow_comp.chg_rslow_comp_thr); - if (rc) { - chip->rslow_comp.chg_rslow_comp_thr = -EINVAL; - if (rc != -EINVAL) - pr_err("Could not read rslow comp thr: %d\n", rc); - } - - rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv", - &chip->batt_max_voltage_uv); - - if (rc) - pr_warn("couldn't find battery max voltage\n"); - - /* - * Only configure from profile if fg-cc-cv-threshold-mv is not - * defined in the charger device node. - */ - if (!of_find_property(chip->pdev->dev.of_node, - "qcom,fg-cc-cv-threshold-mv", NULL)) { - of_property_read_u32(profile_node, - "qcom,fg-cc-cv-threshold-mv", - &chip->cc_cv_threshold_mv); - } - - data = of_get_property(profile_node, "qcom,fg-profile-data", &len); - if (!data) { - pr_err("no battery profile loaded\n"); - rc = 0; - goto no_profile; - } - - if (len != FG_PROFILE_LEN) { - pr_err("battery profile incorrect size: %d\n", len); - rc = -EINVAL; - goto no_profile; - } - - rc = of_property_read_string(profile_node, "qcom,battery-type", - &batt_type_str); - if (rc) { - pr_err("Could not find battery data type: %d\n", rc); - rc = 0; - goto no_profile; - } - - if (!chip->batt_profile) - chip->batt_profile = devm_kzalloc(chip->dev, - sizeof(char) * len, GFP_KERNEL); - - if (!chip->batt_profile) { - pr_err("out of memory\n"); - rc = -ENOMEM; - goto no_profile; - } - - rc = fg_mem_read(chip, ®, PROFILE_INTEGRITY_REG, 1, 0, 1); - if (rc) { - pr_err("failed to read profile integrity rc=%d\n", rc); - goto no_profile; - } - - rc = fg_mem_read(chip, chip->batt_profile, BATT_PROFILE_OFFSET, - len, 0, 1); - if (rc) { - pr_err("failed to read profile rc=%d\n", rc); - goto no_profile; - } - - /* Check whether the charger is ready */ - if (!is_charger_available(chip)) - goto reschedule; - - /* Disable charging for a FG cycle before calculating vbat_in_range */ - if (!chip->charging_disabled) { - rc = set_prop_enable_charging(chip, false); - if (rc) - pr_err("Failed to disable charging, rc=%d\n", rc); - - goto reschedule; - } - - vbat_in_range = get_vbat_est_diff(chip) - < settings[FG_MEM_VBAT_EST_DIFF].value * 1000; - profiles_same = memcmp(chip->batt_profile, data, - PROFILE_COMPARE_LEN) == 0; - if (reg & PROFILE_INTEGRITY_BIT) { - fg_cap_learning_load_data(chip); - if (vbat_in_range && !fg_is_batt_empty(chip) && profiles_same) { - if (fg_debug_mask & FG_STATUS) - pr_info("Battery profiles same, using default\n"); - if (fg_est_dump) - schedule_work(&chip->dump_sram); - goto done; - } - } else { - pr_info("Battery profile not same, clearing data\n"); - clear_cycle_counter(chip); - chip->learning_data.learned_cc_uah = 0; - } - - if (fg_est_dump) - dump_sram(&chip->dump_sram); - - if ((fg_debug_mask & FG_STATUS) && !vbat_in_range) - pr_info("Vbat out of range: v_current_pred: %d, v:%d\n", - fg_data[FG_DATA_CPRED_VOLTAGE].value, - fg_data[FG_DATA_VOLTAGE].value); - - if ((fg_debug_mask & FG_STATUS) && fg_is_batt_empty(chip)) - pr_info("battery empty\n"); - - if ((fg_debug_mask & FG_STATUS) && !profiles_same) - pr_info("profiles differ\n"); - - if (fg_debug_mask & FG_STATUS) { - pr_info("Using new profile\n"); - print_hex_dump(KERN_INFO, "FG: loaded profile: ", - DUMP_PREFIX_NONE, 16, 1, - chip->batt_profile, len, false); - } - - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - - memcpy(chip->batt_profile, data, len); - - chip->batt_profile_len = len; - - if (fg_debug_mask & FG_STATUS) - print_hex_dump(KERN_INFO, "FG: new profile: ", - DUMP_PREFIX_NONE, 16, 1, chip->batt_profile, - chip->batt_profile_len, false); - - rc = fg_do_restart(chip, true); - if (rc) { - pr_err("restart failed: %d\n", rc); - goto no_profile; - } - - /* - * Only configure from profile if thermal-coefficients is not - * defined in the FG device node. - */ - if (!of_find_property(chip->pdev->dev.of_node, - "qcom,thermal-coefficients", NULL)) { - data = of_get_property(profile_node, - "qcom,thermal-coefficients", &len); - if (data && len == THERMAL_COEFF_N_BYTES) { - memcpy(chip->thermal_coefficients, data, len); - rc = fg_mem_write(chip, chip->thermal_coefficients, - THERMAL_COEFF_ADDR, THERMAL_COEFF_N_BYTES, - THERMAL_COEFF_OFFSET, 0); - if (rc) - pr_err("spmi write failed addr:%03x, ret:%d\n", - THERMAL_COEFF_ADDR, rc); - else if (fg_debug_mask & FG_STATUS) - pr_info("Battery thermal coefficients changed\n"); - } - } - -done: - if (chip->charging_disabled) { - rc = set_prop_enable_charging(chip, true); - if (rc) - pr_err("Failed to enable charging, rc=%d\n", rc); - else - chip->charging_disabled = false; - } - - if (fg_batt_type) - chip->batt_type = fg_batt_type; - else - chip->batt_type = batt_type_str; - chip->first_profile_loaded = true; - chip->profile_loaded = true; - chip->battery_missing = is_battery_missing(chip); - update_chg_iterm(chip); - update_cc_cv_setpoint(chip); - rc = populate_system_data(chip); - if (rc) { - pr_err("failed to read ocv properties=%d\n", rc); - return rc; - } - estimate_battery_age(chip, &chip->actual_cap_uah); - schedule_work(&chip->status_change_work); - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - fg_relax(&chip->profile_wakeup_source); - pr_info("Battery SOC: %d, V: %duV\n", get_prop_capacity(chip), - fg_data[FG_DATA_VOLTAGE].value); - return rc; -no_profile: - if (chip->charging_disabled) { - rc = set_prop_enable_charging(chip, true); - if (rc) - pr_err("Failed to enable charging, rc=%d\n", rc); - else - chip->charging_disabled = false; - } - - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - fg_relax(&chip->profile_wakeup_source); - return rc; -reschedule: - schedule_delayed_work( - &chip->batt_profile_init, - msecs_to_jiffies(BATTERY_PSY_WAIT_MS)); - cancel_delayed_work(&chip->update_sram_data); - schedule_delayed_work( - &chip->update_sram_data, - msecs_to_jiffies(0)); - fg_relax(&chip->profile_wakeup_source); - return 0; -} - -static void check_empty_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - check_empty_work.work); - - if (fg_is_batt_empty(chip)) { - if (fg_debug_mask & FG_STATUS) - pr_info("EMPTY SOC high\n"); - chip->soc_empty = true; - if (chip->power_supply_registered) - power_supply_changed(chip->bms_psy); - } - fg_relax(&chip->empty_check_wakeup_source); -} - -static void batt_profile_init(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - batt_profile_init.work); - - if (fg_batt_profile_init(chip)) - pr_err("failed to initialize profile\n"); -} - -static void sysfs_restart_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - sysfs_restart_work); - int rc; - - rc = fg_do_restart(chip, false); - if (rc) - pr_err("fg restart failed: %d\n", rc); - mutex_lock(&chip->sysfs_restart_lock); - fg_restart = 0; - mutex_unlock(&chip->sysfs_restart_lock); -} - -#define SRAM_MONOTONIC_SOC_REG 0x574 -#define SRAM_MONOTONIC_SOC_OFFSET 2 -#define SRAM_RELEASE_TIMEOUT_MS 500 -static void charge_full_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - charge_full_work); - int rc; - u8 buffer[3]; - int bsoc; - int resume_soc_raw = FULL_SOC_RAW - settings[FG_MEM_RESUME_SOC].value; - bool disable = false; - u8 reg; - - if (chip->status != POWER_SUPPLY_STATUS_FULL) { - if (fg_debug_mask & FG_STATUS) - pr_info("battery not full: %d\n", chip->status); - disable = true; - } - - fg_mem_lock(chip); - rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0); - if (rc) { - pr_err("Unable to read battery soc: %d\n", rc); - goto out; - } - if (buffer[2] <= resume_soc_raw) { - if (fg_debug_mask & FG_STATUS) - pr_info("bsoc = 0x%02x <= resume = 0x%02x\n", - buffer[2], resume_soc_raw); - disable = true; - } - if (!disable) - goto out; - - rc = fg_mem_write(chip, buffer, SOC_FULL_REG, 3, - SOC_FULL_OFFSET, 0); - if (rc) { - pr_err("failed to write SOC_FULL rc=%d\n", rc); - goto out; - } - /* force a full soc value into the monotonic in order to display 100 */ - buffer[0] = 0xFF; - buffer[1] = 0xFF; - rc = fg_mem_write(chip, buffer, SRAM_MONOTONIC_SOC_REG, 2, - SRAM_MONOTONIC_SOC_OFFSET, 0); - if (rc) { - pr_err("failed to write SOC_FULL rc=%d\n", rc); - goto out; - } - if (fg_debug_mask & FG_STATUS) { - bsoc = buffer[0] | buffer[1] << 8 | buffer[2] << 16; - pr_info("wrote %06x into soc full\n", bsoc); - } - fg_mem_release(chip); - /* - * wait one cycle to make sure the soc is updated before clearing - * the soc mask bit - */ - fg_mem_lock(chip); - fg_mem_read(chip, ®, PROFILE_INTEGRITY_REG, 1, 0, 0); -out: - fg_mem_release(chip); - if (disable) - chip->charge_full = false; -} - -static void update_bcl_thresholds(struct fg_chip *chip) -{ - u8 data[4]; - u8 mh_offset = 0, lm_offset = 0; - u16 address = 0; - int ret = 0; - - address = settings[FG_MEM_BCL_MH_THRESHOLD].address; - mh_offset = settings[FG_MEM_BCL_MH_THRESHOLD].offset; - lm_offset = settings[FG_MEM_BCL_LM_THRESHOLD].offset; - ret = fg_mem_read(chip, data, address, 4, 0, 1); - if (ret) - pr_err("Error reading BCL LM & MH threshold rc:%d\n", ret); - else - pr_debug("Old BCL LM threshold:%x MH threshold:%x\n", - data[lm_offset], data[mh_offset]); - BCL_MA_TO_ADC(settings[FG_MEM_BCL_MH_THRESHOLD].value, data[mh_offset]); - BCL_MA_TO_ADC(settings[FG_MEM_BCL_LM_THRESHOLD].value, data[lm_offset]); - - ret = fg_mem_write(chip, data, address, 4, 0, 0); - if (ret) - pr_err("spmi write failed. addr:%03x, ret:%d\n", - address, ret); - else - pr_debug("New BCL LM threshold:%x MH threshold:%x\n", - data[lm_offset], data[mh_offset]); -} - -static int disable_bcl_lpm(struct fg_chip *chip) -{ - u8 data[4]; - u8 lm_offset = 0; - u16 address = 0; - int rc = 0; - - address = settings[FG_MEM_BCL_LM_THRESHOLD].address; - lm_offset = settings[FG_MEM_BCL_LM_THRESHOLD].offset; - rc = fg_mem_read(chip, data, address, 4, 0, 1); - if (rc) { - pr_err("Error reading BCL LM & MH threshold rc:%d\n", rc); - return rc; - } - pr_debug("Old BCL LM threshold:%x\n", data[lm_offset]); - - /* Put BCL always above LPM */ - BCL_MA_TO_ADC(0, data[lm_offset]); - - rc = fg_mem_write(chip, data, address, 4, 0, 0); - if (rc) - pr_err("spmi write failed. addr:%03x, rc:%d\n", - address, rc); - else - pr_debug("New BCL LM threshold:%x\n", data[lm_offset]); - - return rc; -} - -static void bcl_hi_power_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - bcl_hi_power_work); - int rc; - - if (chip->bcl_lpm_disabled) { - rc = disable_bcl_lpm(chip); - if (rc) - pr_err("failed to disable bcl low mode %d\n", - rc); - } else { - update_bcl_thresholds(chip); - } -} - -#define VOLT_UV_TO_VOLTCMP8(volt_uv) \ - ((volt_uv - 2500000) / 9766) -static int update_irq_volt_empty(struct fg_chip *chip) -{ - u8 data; - int volt_mv = settings[FG_MEM_IRQ_VOLT_EMPTY].value; - - data = (u8)VOLT_UV_TO_VOLTCMP8(volt_mv * 1000); - - if (fg_debug_mask & FG_STATUS) - pr_info("voltage = %d, converted_raw = %04x\n", volt_mv, data); - return fg_mem_write(chip, &data, - settings[FG_MEM_IRQ_VOLT_EMPTY].address, 1, - settings[FG_MEM_IRQ_VOLT_EMPTY].offset, 0); -} - -static int update_cutoff_voltage(struct fg_chip *chip) -{ - u8 data[2]; - u16 converted_voltage_raw; - s64 voltage_mv = settings[FG_MEM_CUTOFF_VOLTAGE].value; - - converted_voltage_raw = (s16)MICROUNITS_TO_ADC_RAW(voltage_mv * 1000); - data[0] = cpu_to_le16(converted_voltage_raw) & 0xFF; - data[1] = cpu_to_le16(converted_voltage_raw) >> 8; - - if (fg_debug_mask & FG_STATUS) - pr_info("voltage = %lld, converted_raw = %04x, data = %02x %02x\n", - voltage_mv, converted_voltage_raw, data[0], data[1]); - return fg_mem_write(chip, data, settings[FG_MEM_CUTOFF_VOLTAGE].address, - 2, settings[FG_MEM_CUTOFF_VOLTAGE].offset, 0); -} - -static int update_iterm(struct fg_chip *chip) -{ - u8 data[2]; - u16 converted_current_raw; - s64 current_ma = -settings[FG_MEM_TERM_CURRENT].value; - - converted_current_raw = (s16)MICROUNITS_TO_ADC_RAW(current_ma * 1000); - data[0] = cpu_to_le16(converted_current_raw) & 0xFF; - data[1] = cpu_to_le16(converted_current_raw) >> 8; - - if (fg_debug_mask & FG_STATUS) - pr_info("current = %lld, converted_raw = %04x, data = %02x %02x\n", - current_ma, converted_current_raw, data[0], data[1]); - return fg_mem_write(chip, data, settings[FG_MEM_TERM_CURRENT].address, - 2, settings[FG_MEM_TERM_CURRENT].offset, 0); -} - -#define OF_READ_SETTING(type, qpnp_dt_property, retval, optional) \ -do { \ - if (retval) \ - break; \ - \ - retval = of_property_read_u32(chip->pdev->dev.of_node, \ - "qcom," qpnp_dt_property, \ - &settings[type].value); \ - \ - if ((retval == -EINVAL) && optional) \ - retval = 0; \ - else if (retval) \ - pr_err("Error reading " #qpnp_dt_property \ - " property rc = %d\n", rc); \ -} while (0) - -#define OF_READ_PROPERTY(store, qpnp_dt_property, retval, default_val) \ -do { \ - if (retval) \ - break; \ - \ - retval = of_property_read_u32(chip->pdev->dev.of_node, \ - "qcom," qpnp_dt_property, \ - &store); \ - \ - if (retval == -EINVAL) { \ - retval = 0; \ - store = default_val; \ - } else if (retval) { \ - pr_err("Error reading " #qpnp_dt_property \ - " property rc = %d\n", rc); \ - } \ -} while (0) - -#define DEFAULT_EVALUATION_CURRENT_MA 1000 -static int fg_of_init(struct fg_chip *chip) -{ - int rc = 0, sense_type, len = 0; - const char *data; - struct device_node *node = chip->pdev->dev.of_node; - u32 temp[2] = {0}; - - OF_READ_SETTING(FG_MEM_SOFT_HOT, "warm-bat-decidegc", rc, 1); - OF_READ_SETTING(FG_MEM_SOFT_COLD, "cool-bat-decidegc", rc, 1); - OF_READ_SETTING(FG_MEM_HARD_HOT, "hot-bat-decidegc", rc, 1); - OF_READ_SETTING(FG_MEM_HARD_COLD, "cold-bat-decidegc", rc, 1); - - if (of_find_property(node, "qcom,cold-hot-jeita-hysteresis", NULL)) { - int hard_hot = 0, soft_hot = 0, hard_cold = 0, soft_cold = 0; - - rc = of_property_read_u32_array(node, - "qcom,cold-hot-jeita-hysteresis", temp, 2); - if (rc) { - pr_err("Error reading cold-hot-jeita-hysteresis rc=%d\n", - rc); - return rc; - } - - chip->jeita_hysteresis_support = true; - chip->cold_hysteresis = temp[0]; - chip->hot_hysteresis = temp[1]; - hard_hot = settings[FG_MEM_HARD_HOT].value; - soft_hot = settings[FG_MEM_SOFT_HOT].value; - hard_cold = settings[FG_MEM_HARD_COLD].value; - soft_cold = settings[FG_MEM_SOFT_COLD].value; - if (((hard_hot - chip->hot_hysteresis) < soft_hot) || - ((hard_cold + chip->cold_hysteresis) > soft_cold)) { - chip->jeita_hysteresis_support = false; - pr_err("invalid hysteresis: hot_hysterresis = %d cold_hysteresis = %d\n", - chip->hot_hysteresis, chip->cold_hysteresis); - } else { - pr_debug("cold_hysteresis = %d, hot_hysteresis = %d\n", - chip->cold_hysteresis, chip->hot_hysteresis); - } - } - - OF_READ_SETTING(FG_MEM_BCL_LM_THRESHOLD, "bcl-lm-threshold-ma", - rc, 1); - OF_READ_SETTING(FG_MEM_BCL_MH_THRESHOLD, "bcl-mh-threshold-ma", - rc, 1); - OF_READ_SETTING(FG_MEM_TERM_CURRENT, "fg-iterm-ma", rc, 1); - OF_READ_SETTING(FG_MEM_CHG_TERM_CURRENT, "fg-chg-iterm-ma", rc, 1); - OF_READ_SETTING(FG_MEM_CUTOFF_VOLTAGE, "fg-cutoff-voltage-mv", rc, 1); - data = of_get_property(chip->pdev->dev.of_node, - "qcom,thermal-coefficients", &len); - if (data && len == THERMAL_COEFF_N_BYTES) { - memcpy(chip->thermal_coefficients, data, len); - chip->use_thermal_coefficients = true; - } - OF_READ_SETTING(FG_MEM_RESUME_SOC, "resume-soc", rc, 1); - settings[FG_MEM_RESUME_SOC].value = - DIV_ROUND_CLOSEST(settings[FG_MEM_RESUME_SOC].value - * FULL_SOC_RAW, FULL_CAPACITY); - OF_READ_SETTING(FG_MEM_RESUME_SOC, "resume-soc-raw", rc, 1); - OF_READ_SETTING(FG_MEM_IRQ_VOLT_EMPTY, "irq-volt-empty-mv", rc, 1); - OF_READ_SETTING(FG_MEM_VBAT_EST_DIFF, "vbat-estimate-diff-mv", rc, 1); - OF_READ_SETTING(FG_MEM_DELTA_SOC, "fg-delta-soc", rc, 1); - OF_READ_SETTING(FG_MEM_BATT_LOW, "fg-vbatt-low-threshold", rc, 1); - OF_READ_SETTING(FG_MEM_THERM_DELAY, "fg-therm-delay-us", rc, 1); - OF_READ_PROPERTY(chip->learning_data.max_increment, - "cl-max-increment-deciperc", rc, 5); - OF_READ_PROPERTY(chip->learning_data.max_decrement, - "cl-max-decrement-deciperc", rc, 100); - OF_READ_PROPERTY(chip->learning_data.max_temp, - "cl-max-temp-decidegc", rc, 450); - OF_READ_PROPERTY(chip->learning_data.min_temp, - "cl-min-temp-decidegc", rc, 150); - OF_READ_PROPERTY(chip->learning_data.max_start_soc, - "cl-max-start-capacity", rc, 15); - OF_READ_PROPERTY(chip->learning_data.vbat_est_thr_uv, - "cl-vbat-est-thr-uv", rc, 40000); - OF_READ_PROPERTY(chip->evaluation_current, - "aging-eval-current-ma", rc, - DEFAULT_EVALUATION_CURRENT_MA); - OF_READ_PROPERTY(chip->cc_cv_threshold_mv, - "fg-cc-cv-threshold-mv", rc, 0); - if (of_property_read_bool(chip->pdev->dev.of_node, - "qcom,capacity-learning-on")) - chip->batt_aging_mode = FG_AGING_CC; - else if (of_property_read_bool(chip->pdev->dev.of_node, - "qcom,capacity-estimation-on")) - chip->batt_aging_mode = FG_AGING_ESR; - else - chip->batt_aging_mode = FG_AGING_NONE; - if (chip->batt_aging_mode == FG_AGING_CC) { - chip->learning_data.feedback_on - = of_property_read_bool(chip->pdev->dev.of_node, - "qcom,capacity-learning-feedback"); - } - if (fg_debug_mask & FG_AGING) - pr_info("battery aging mode: %d\n", chip->batt_aging_mode); - - /* Get the use-otp-profile property */ - chip->use_otp_profile = of_property_read_bool(chip->pdev->dev.of_node, - "qcom,use-otp-profile"); - chip->hold_soc_while_full - = of_property_read_bool(chip->pdev->dev.of_node, - "qcom,hold-soc-while-full"); - - sense_type = of_property_read_bool(chip->pdev->dev.of_node, - "qcom,ext-sense-type"); - if (rc == 0) { - if (fg_sense_type < 0) - fg_sense_type = sense_type; - - if (fg_debug_mask & FG_STATUS) { - if (fg_sense_type == INTERNAL_CURRENT_SENSE) - pr_info("Using internal sense\n"); - else if (fg_sense_type == EXTERNAL_CURRENT_SENSE) - pr_info("Using external sense\n"); - else - pr_info("Using default sense\n"); - } - } else { - rc = 0; - } - - chip->bad_batt_detection_en = of_property_read_bool(node, - "qcom,bad-battery-detection-enable"); - - chip->sw_rbias_ctrl = of_property_read_bool(node, - "qcom,sw-rbias-control"); - - chip->cyc_ctr.en = of_property_read_bool(node, - "qcom,cycle-counter-en"); - if (chip->cyc_ctr.en) - chip->cyc_ctr.id = 1; - - chip->esr_pulse_tune_en = of_property_read_bool(node, - "qcom,esr-pulse-tuning-en"); - - return rc; -} - -static int fg_init_irqs(struct fg_chip *chip) -{ - int rc = 0; - unsigned int base; - struct device_node *child; - u8 subtype; - struct platform_device *pdev = chip->pdev; - - if (of_get_available_child_count(pdev->dev.of_node) == 0) { - pr_err("no child nodes\n"); - return -ENXIO; - } - - for_each_available_child_of_node(pdev->dev.of_node, child) { - rc = of_property_read_u32(child, "reg", &base); - if (rc < 0) { - dev_err(&pdev->dev, - "Couldn't find reg in node = %s rc = %d\n", - child->full_name, rc); - return rc; - } - - if ((base == chip->vbat_adc_addr) || - (base == chip->ibat_adc_addr) || - (base == chip->tp_rev_addr)) - continue; - - rc = fg_read(chip, &subtype, - base + REG_OFFSET_PERP_SUBTYPE, 1); - if (rc) { - pr_err("Peripheral subtype read failed rc=%d\n", rc); - return rc; - } - - switch (subtype) { - case FG_SOC: - chip->soc_irq[FULL_SOC].irq = of_irq_get_byname(child, - "full-soc"); - if (chip->soc_irq[FULL_SOC].irq < 0) { - pr_err("Unable to get full-soc irq\n"); - return rc; - } - chip->soc_irq[EMPTY_SOC].irq = of_irq_get_byname(child, - "empty-soc"); - if (chip->soc_irq[EMPTY_SOC].irq < 0) { - pr_err("Unable to get low-soc irq\n"); - return rc; - } - chip->soc_irq[DELTA_SOC].irq = of_irq_get_byname(child, - "delta-soc"); - if (chip->soc_irq[DELTA_SOC].irq < 0) { - pr_err("Unable to get delta-soc irq\n"); - return rc; - } - chip->soc_irq[FIRST_EST_DONE].irq - = of_irq_get_byname(child, "first-est-done"); - if (chip->soc_irq[FIRST_EST_DONE].irq < 0) { - pr_err("Unable to get first-est-done irq\n"); - return rc; - } - - rc = devm_request_irq(chip->dev, - chip->soc_irq[FULL_SOC].irq, - fg_soc_irq_handler, IRQF_TRIGGER_RISING, - "full-soc", chip); - if (rc < 0) { - pr_err("Can't request %d full-soc: %d\n", - chip->soc_irq[FULL_SOC].irq, rc); - return rc; - } - rc = devm_request_irq(chip->dev, - chip->soc_irq[EMPTY_SOC].irq, - fg_empty_soc_irq_handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "empty-soc", chip); - if (rc < 0) { - pr_err("Can't request %d empty-soc: %d\n", - chip->soc_irq[EMPTY_SOC].irq, rc); - return rc; - } - rc = devm_request_irq(chip->dev, - chip->soc_irq[DELTA_SOC].irq, - fg_soc_irq_handler, IRQF_TRIGGER_RISING, - "delta-soc", chip); - if (rc < 0) { - pr_err("Can't request %d delta-soc: %d\n", - chip->soc_irq[DELTA_SOC].irq, rc); - return rc; - } - rc = devm_request_irq(chip->dev, - chip->soc_irq[FIRST_EST_DONE].irq, - fg_first_soc_irq_handler, IRQF_TRIGGER_RISING, - "first-est-done", chip); - if (rc < 0) { - pr_err("Can't request %d delta-soc: %d\n", - chip->soc_irq[FIRST_EST_DONE].irq, rc); - return rc; - } - - enable_irq_wake(chip->soc_irq[DELTA_SOC].irq); - enable_irq_wake(chip->soc_irq[FULL_SOC].irq); - enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq); - break; - case FG_MEMIF: - chip->mem_irq[FG_MEM_AVAIL].irq - = of_irq_get_byname(child, "mem-avail"); - if (chip->mem_irq[FG_MEM_AVAIL].irq < 0) { - pr_err("Unable to get mem-avail irq\n"); - return rc; - } - rc = devm_request_irq(chip->dev, - chip->mem_irq[FG_MEM_AVAIL].irq, - fg_mem_avail_irq_handler, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, - "mem-avail", chip); - if (rc < 0) { - pr_err("Can't request %d mem-avail: %d\n", - chip->mem_irq[FG_MEM_AVAIL].irq, rc); - return rc; - } - break; - case FG_BATT: - chip->batt_irq[BATT_MISSING].irq - = of_irq_get_byname(child, "batt-missing"); - if (chip->batt_irq[BATT_MISSING].irq < 0) { - pr_err("Unable to get batt-missing irq\n"); - rc = -EINVAL; - return rc; - } - rc = devm_request_threaded_irq(chip->dev, - chip->batt_irq[BATT_MISSING].irq, - NULL, - fg_batt_missing_irq_handler, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, - "batt-missing", chip); - if (rc < 0) { - pr_err("Can't request %d batt-missing: %d\n", - chip->batt_irq[BATT_MISSING].irq, rc); - return rc; - } - chip->batt_irq[VBATT_LOW].irq - = of_irq_get_byname(child, "vbatt-low"); - if (chip->batt_irq[VBATT_LOW].irq < 0) { - pr_err("Unable to get vbatt-low irq\n"); - rc = -EINVAL; - return rc; - } - rc = devm_request_irq(chip->dev, - chip->batt_irq[VBATT_LOW].irq, - fg_vbatt_low_handler, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, - "vbatt-low", chip); - if (rc < 0) { - pr_err("Can't request %d vbatt-low: %d\n", - chip->batt_irq[VBATT_LOW].irq, rc); - return rc; - } - disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq); - chip->vbat_low_irq_enabled = false; - break; - case FG_ADC: - break; - default: - pr_err("subtype %d\n", subtype); - return -EINVAL; - } - } - - return rc; -} - -static void fg_cleanup(struct fg_chip *chip) -{ - cancel_delayed_work_sync(&chip->update_sram_data); - cancel_delayed_work_sync(&chip->update_temp_work); - cancel_delayed_work_sync(&chip->update_jeita_setting); - cancel_delayed_work_sync(&chip->check_empty_work); - cancel_delayed_work_sync(&chip->batt_profile_init); - alarm_try_to_cancel(&chip->fg_cap_learning_alarm); - cancel_work_sync(&chip->rslow_comp_work); - cancel_work_sync(&chip->set_resume_soc_work); - cancel_work_sync(&chip->fg_cap_learning_work); - cancel_work_sync(&chip->dump_sram); - cancel_work_sync(&chip->status_change_work); - cancel_work_sync(&chip->cycle_count_work); - cancel_work_sync(&chip->update_esr_work); - cancel_work_sync(&chip->sysfs_restart_work); - cancel_work_sync(&chip->gain_comp_work); - cancel_work_sync(&chip->init_work); - cancel_work_sync(&chip->charge_full_work); - cancel_work_sync(&chip->esr_extract_config_work); - mutex_destroy(&chip->rslow_comp.lock); - mutex_destroy(&chip->rw_lock); - mutex_destroy(&chip->cyc_ctr.lock); - mutex_destroy(&chip->learning_data.learning_lock); - mutex_destroy(&chip->sysfs_restart_lock); - wakeup_source_trash(&chip->resume_soc_wakeup_source.source); - wakeup_source_trash(&chip->empty_check_wakeup_source.source); - wakeup_source_trash(&chip->memif_wakeup_source.source); - wakeup_source_trash(&chip->profile_wakeup_source.source); - wakeup_source_trash(&chip->update_temp_wakeup_source.source); - wakeup_source_trash(&chip->update_sram_wakeup_source.source); - wakeup_source_trash(&chip->gain_comp_wakeup_source.source); - wakeup_source_trash(&chip->capacity_learning_wakeup_source.source); - wakeup_source_trash(&chip->esr_extract_wakeup_source.source); -} - -static int fg_remove(struct platform_device *pdev) -{ - struct fg_chip *chip = dev_get_drvdata(&pdev->dev); - - fg_cleanup(chip); - dev_set_drvdata(&pdev->dev, NULL); - return 0; -} - -static int fg_memif_data_open(struct inode *inode, struct file *file) -{ - struct fg_log_buffer *log; - struct fg_trans *trans; - u8 *data_buf; - - size_t logbufsize = SZ_4K; - size_t databufsize = SZ_4K; - - if (!dbgfs_data.chip) { - pr_err("Not initialized data\n"); - return -EINVAL; - } - - /* Per file "transaction" data */ - trans = kzalloc(sizeof(*trans), GFP_KERNEL); - if (!trans) { - pr_err("Unable to allocate memory for transaction data\n"); - return -ENOMEM; - } - - /* Allocate log buffer */ - log = kzalloc(logbufsize, GFP_KERNEL); - - if (!log) { - kfree(trans); - pr_err("Unable to allocate memory for log buffer\n"); - return -ENOMEM; - } - - log->rpos = 0; - log->wpos = 0; - log->len = logbufsize - sizeof(*log); - - /* Allocate data buffer */ - data_buf = kzalloc(databufsize, GFP_KERNEL); - - if (!data_buf) { - kfree(trans); - kfree(log); - pr_err("Unable to allocate memory for data buffer\n"); - return -ENOMEM; - } - - trans->log = log; - trans->data = data_buf; - trans->cnt = dbgfs_data.cnt; - trans->addr = dbgfs_data.addr; - trans->chip = dbgfs_data.chip; - trans->offset = trans->addr; - mutex_init(&trans->memif_dfs_lock); - - file->private_data = trans; - return 0; -} - -static int fg_memif_dfs_close(struct inode *inode, struct file *file) -{ - struct fg_trans *trans = file->private_data; - - if (trans && trans->log && trans->data) { - file->private_data = NULL; - mutex_destroy(&trans->memif_dfs_lock); - kfree(trans->log); - kfree(trans->data); - kfree(trans); - } - - return 0; -} - -/** - * print_to_log: format a string and place into the log buffer - * @log: The log buffer to place the result into. - * @fmt: The format string to use. - * @...: The arguments for the format string. - * - * The return value is the number of characters written to @log buffer - * not including the trailing '\0'. - */ -static int print_to_log(struct fg_log_buffer *log, const char *fmt, ...) -{ - va_list args; - int cnt; - char *buf = &log->data[log->wpos]; - size_t size = log->len - log->wpos; - - va_start(args, fmt); - cnt = vscnprintf(buf, size, fmt, args); - va_end(args); - - log->wpos += cnt; - return cnt; -} - -/** - * write_next_line_to_log: Writes a single "line" of data into the log buffer - * @trans: Pointer to SRAM transaction data. - * @offset: SRAM address offset to start reading from. - * @pcnt: Pointer to 'cnt' variable. Indicates the number of bytes to read. - * - * The 'offset' is a 12-bit SRAM address. - * - * On a successful read, the pcnt is decremented by the number of data - * bytes read from the SRAM. When the cnt reaches 0, all requested bytes have - * been read. - */ -static int -write_next_line_to_log(struct fg_trans *trans, int offset, size_t *pcnt) -{ - int i, j; - u8 data[ITEMS_PER_LINE]; - struct fg_log_buffer *log = trans->log; - - int cnt = 0; - int padding = offset % ITEMS_PER_LINE; - int items_to_read = min(ARRAY_SIZE(data) - padding, *pcnt); - int items_to_log = min(ITEMS_PER_LINE, padding + items_to_read); - - /* Buffer needs enough space for an entire line */ - if ((log->len - log->wpos) < MAX_LINE_LENGTH) - goto done; - - memcpy(data, trans->data + (offset - trans->addr), items_to_read); - - *pcnt -= items_to_read; - - /* Each line starts with the aligned offset (12-bit address) */ - cnt = print_to_log(log, "%3.3X ", offset & 0xfff); - if (cnt == 0) - goto done; - - /* If the offset is unaligned, add padding to right justify items */ - for (i = 0; i < padding; ++i) { - cnt = print_to_log(log, "-- "); - if (cnt == 0) - goto done; - } - - /* Log the data items */ - for (j = 0; i < items_to_log; ++i, ++j) { - cnt = print_to_log(log, "%2.2X ", data[j]); - if (cnt == 0) - goto done; - } - - /* If the last character was a space, then replace it with a newline */ - if (log->wpos > 0 && log->data[log->wpos - 1] == ' ') - log->data[log->wpos - 1] = '\n'; - -done: - return cnt; -} - -/** - * get_log_data - reads data from SRAM and saves to the log buffer - * @trans: Pointer to SRAM transaction data. - * - * Returns the number of "items" read or SPMI error code for read failures. - */ -static int get_log_data(struct fg_trans *trans) -{ - int cnt, rc; - int last_cnt; - int items_read; - int total_items_read = 0; - u32 offset = trans->offset; - size_t item_cnt = trans->cnt; - struct fg_log_buffer *log = trans->log; - - if (item_cnt == 0) - return 0; - - if (item_cnt > SZ_4K) { - pr_err("Reading too many bytes\n"); - return -EINVAL; - } - - rc = fg_mem_read(trans->chip, trans->data, - trans->addr, trans->cnt, 0, 0); - if (rc) { - pr_err("dump failed: rc = %d\n", rc); - return rc; - } - /* Reset the log buffer 'pointers' */ - log->wpos = log->rpos = 0; - - /* Keep reading data until the log is full */ - do { - last_cnt = item_cnt; - cnt = write_next_line_to_log(trans, offset, &item_cnt); - items_read = last_cnt - item_cnt; - offset += items_read; - total_items_read += items_read; - } while (cnt && item_cnt > 0); - - /* Adjust the transaction offset and count */ - trans->cnt = item_cnt; - trans->offset += total_items_read; - - return total_items_read; -} - -/** - * fg_memif_dfs_reg_read: reads value(s) from SRAM and fills user's buffer a - * byte array (coded as string) - * @file: file pointer - * @buf: where to put the result - * @count: maximum space available in @buf - * @ppos: starting position - * @return number of user bytes read, or negative error value - */ -static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct fg_trans *trans = file->private_data; - struct fg_log_buffer *log = trans->log; - size_t ret; - size_t len; - - mutex_lock(&trans->memif_dfs_lock); - /* Is the the log buffer empty */ - if (log->rpos >= log->wpos) { - if (get_log_data(trans) <= 0) { - len = 0; - goto unlock_mutex; - } - } - - len = min(count, log->wpos - log->rpos); - - ret = copy_to_user(buf, &log->data[log->rpos], len); - if (ret == len) { - pr_err("error copy sram register values to user\n"); - len = -EFAULT; - goto unlock_mutex; - } - - /* 'ret' is the number of bytes not copied */ - len -= ret; - - *ppos += len; - log->rpos += len; - -unlock_mutex: - mutex_unlock(&trans->memif_dfs_lock); - return len; -} - -/** - * fg_memif_dfs_reg_write: write user's byte array (coded as string) to SRAM. - * @file: file pointer - * @buf: user data to be written. - * @count: maximum space available in @buf - * @ppos: starting position - * @return number of user byte written, or negative error value - */ -static ssize_t fg_memif_dfs_reg_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - int bytes_read; - int data; - int pos = 0; - int cnt = 0; - u8 *values; - size_t ret = 0; - char *kbuf; - u32 offset; - - struct fg_trans *trans = file->private_data; - - mutex_lock(&trans->memif_dfs_lock); - offset = trans->offset; - - /* Make a copy of the user data */ - kbuf = kmalloc(count + 1, GFP_KERNEL); - if (!kbuf) { - ret = -ENOMEM; - goto unlock_mutex; - } - - ret = copy_from_user(kbuf, buf, count); - if (ret == count) { - pr_err("failed to copy data from user\n"); - ret = -EFAULT; - goto free_buf; - } - - count -= ret; - *ppos += count; - kbuf[count] = '\0'; - - /* Override the text buffer with the raw data */ - values = kbuf; - - /* Parse the data in the buffer. It should be a string of numbers */ - while ((pos < count) && - sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) { - /* - * We shouldn't be receiving a string of characters that - * exceeds a size of 5 to keep this functionally correct. - * Also, we should make sure that pos never gets overflowed - * beyond the limit. - */ - if (bytes_read > 5 || bytes_read > INT_MAX - pos) { - cnt = 0; - ret = -EINVAL; - break; - } - pos += bytes_read; - values[cnt++] = data & 0xff; - } - - if (!cnt) - goto free_buf; - - pr_info("address %x, count %d\n", offset, cnt); - /* Perform the write(s) */ - - ret = fg_mem_write(trans->chip, values, offset, - cnt, 0, 0); - if (ret) { - pr_err("SPMI write failed, err = %zu\n", ret); - } else { - ret = count; - trans->offset += cnt > 4 ? 4 : cnt; - } - -free_buf: - kfree(kbuf); -unlock_mutex: - mutex_unlock(&trans->memif_dfs_lock); - return ret; -} - -static const struct file_operations fg_memif_dfs_reg_fops = { - .open = fg_memif_data_open, - .release = fg_memif_dfs_close, - .read = fg_memif_dfs_reg_read, - .write = fg_memif_dfs_reg_write, -}; - -/** - * fg_dfs_create_fs: create debugfs file system. - * @return pointer to root directory or NULL if failed to create fs - */ -static struct dentry *fg_dfs_create_fs(void) -{ - struct dentry *root, *file; - - pr_debug("Creating FG_MEM debugfs file-system\n"); - root = debugfs_create_dir(DFS_ROOT_NAME, NULL); - if (IS_ERR_OR_NULL(root)) { - pr_err("Error creating top level directory err:%ld", - (long)root); - if (PTR_ERR(root) == -ENODEV) - pr_err("debugfs is not enabled in the kernel"); - return NULL; - } - - dbgfs_data.help_msg.size = strlen(dbgfs_data.help_msg.data); - - file = debugfs_create_blob("help", S_IRUGO, root, &dbgfs_data.help_msg); - if (!file) { - pr_err("error creating help entry\n"); - goto err_remove_fs; - } - return root; - -err_remove_fs: - debugfs_remove_recursive(root); - return NULL; -} - -/** - * fg_dfs_get_root: return a pointer to FG debugfs root directory. - * @return a pointer to the existing directory, or if no root - * directory exists then create one. Directory is created with file that - * configures SRAM transaction, namely: address, and count. - * @returns valid pointer on success or NULL - */ -struct dentry *fg_dfs_get_root(void) -{ - if (dbgfs_data.root) - return dbgfs_data.root; - - if (mutex_lock_interruptible(&dbgfs_data.lock) < 0) - return NULL; - /* critical section */ - if (!dbgfs_data.root) { /* double checking idiom */ - dbgfs_data.root = fg_dfs_create_fs(); - } - mutex_unlock(&dbgfs_data.lock); - return dbgfs_data.root; -} - -/* - * fg_dfs_create: adds new fg_mem if debugfs entry - * @return zero on success - */ -int fg_dfs_create(struct fg_chip *chip) -{ - struct dentry *root; - struct dentry *file; - - root = fg_dfs_get_root(); - if (!root) - return -ENOENT; - - dbgfs_data.chip = chip; - - file = debugfs_create_u32("count", DFS_MODE, root, &(dbgfs_data.cnt)); - if (!file) { - pr_err("error creating 'count' entry\n"); - goto err_remove_fs; - } - - file = debugfs_create_x32("address", DFS_MODE, - root, &(dbgfs_data.addr)); - if (!file) { - pr_err("error creating 'address' entry\n"); - goto err_remove_fs; - } - - file = debugfs_create_file("data", DFS_MODE, root, &dbgfs_data, - &fg_memif_dfs_reg_fops); - if (!file) { - pr_err("error creating 'data' entry\n"); - goto err_remove_fs; - } - - return 0; - -err_remove_fs: - debugfs_remove_recursive(root); - return -ENOMEM; -} - -#define EXTERNAL_SENSE_OFFSET_REG 0x41C -#define EXT_OFFSET_TRIM_REG 0xF8 -#define SEC_ACCESS_REG 0xD0 -#define SEC_ACCESS_UNLOCK 0xA5 -#define BCL_TRIM_REV_FIXED 12 -static int bcl_trim_workaround(struct fg_chip *chip) -{ - u8 reg, rc; - - if (chip->tp_rev_addr == 0) - return 0; - - rc = fg_read(chip, ®, chip->tp_rev_addr, 1); - if (rc) { - pr_err("Failed to read tp reg, rc = %d\n", rc); - return rc; - } - if (reg >= BCL_TRIM_REV_FIXED) { - if (fg_debug_mask & FG_STATUS) - pr_info("workaround not applied, tp_rev = %d\n", reg); - return 0; - } - - rc = fg_mem_read(chip, ®, EXTERNAL_SENSE_OFFSET_REG, 1, 2, 0); - if (rc) { - pr_err("Failed to read ext sense offset trim, rc = %d\n", rc); - return rc; - } - rc = fg_masked_write(chip, chip->soc_base + SEC_ACCESS_REG, - SEC_ACCESS_UNLOCK, SEC_ACCESS_UNLOCK, 1); - - rc |= fg_masked_write(chip, chip->soc_base + EXT_OFFSET_TRIM_REG, - 0xFF, reg, 1); - if (rc) { - pr_err("Failed to write ext sense offset trim, rc = %d\n", rc); - return rc; - } - - return 0; -} - -#define FG_ALG_SYSCTL_1 0x4B0 -#define SOC_CNFG 0x450 -#define SOC_DELTA_OFFSET 3 -#define DELTA_SOC_PERCENT 1 -#define I_TERM_QUAL_BIT BIT(1) -#define PATCH_NEG_CURRENT_BIT BIT(3) -#define KI_COEFF_PRED_FULL_ADDR 0x408 -#define KI_COEFF_PRED_FULL_4_0_MSB 0x88 -#define KI_COEFF_PRED_FULL_4_0_LSB 0x00 -#define TEMP_FRAC_SHIFT_REG 0x4A4 -#define FG_ADC_CONFIG_REG 0x4B8 -#define FG_BCL_CONFIG_OFFSET 0x3 -#define BCL_FORCED_HPM_IN_CHARGE BIT(2) -static int fg_common_hw_init(struct fg_chip *chip) -{ - int rc; - int resume_soc_raw; - u8 val; - - update_iterm(chip); - update_cutoff_voltage(chip); - update_irq_volt_empty(chip); - update_bcl_thresholds(chip); - - resume_soc_raw = settings[FG_MEM_RESUME_SOC].value; - if (resume_soc_raw > 0) { - rc = fg_set_resume_soc(chip, resume_soc_raw); - if (rc) { - pr_err("Couldn't set resume SOC for FG\n"); - return rc; - } - } else { - pr_info("FG auto recharge threshold not specified in DT\n"); - } - - if (fg_sense_type >= 0) { - rc = set_prop_sense_type(chip, fg_sense_type); - if (rc) { - pr_err("failed to config sense type %d rc=%d\n", - fg_sense_type, rc); - return rc; - } - } - - rc = fg_mem_masked_write(chip, settings[FG_MEM_DELTA_SOC].address, 0xFF, - soc_to_setpoint(settings[FG_MEM_DELTA_SOC].value), - settings[FG_MEM_DELTA_SOC].offset); - if (rc) { - pr_err("failed to write delta soc rc=%d\n", rc); - return rc; - } - - rc = fg_mem_masked_write(chip, settings[FG_MEM_BATT_LOW].address, 0xFF, - batt_to_setpoint_8b(settings[FG_MEM_BATT_LOW].value), - settings[FG_MEM_BATT_LOW].offset); - if (rc) { - pr_err("failed to write Vbatt_low rc=%d\n", rc); - return rc; - } - - rc = fg_mem_masked_write(chip, settings[FG_MEM_THERM_DELAY].address, - THERM_DELAY_MASK, - therm_delay_to_setpoint(settings[FG_MEM_THERM_DELAY].value), - settings[FG_MEM_THERM_DELAY].offset); - if (rc) { - pr_err("failed to write therm_delay rc=%d\n", rc); - return rc; - } - - if (chip->use_thermal_coefficients) { - fg_mem_write(chip, chip->thermal_coefficients, - THERMAL_COEFF_ADDR, THERMAL_COEFF_N_BYTES, - THERMAL_COEFF_OFFSET, 0); - } - - if (!chip->sw_rbias_ctrl) { - rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT, - BATT_TEMP_CNTRL_MASK, - TEMP_SENSE_ALWAYS_BIT, - BATT_TEMP_OFFSET); - if (rc) { - pr_err("failed to write BATT_TEMP_OFFSET rc=%d\n", rc); - return rc; - } - } - - /* Read the cycle counter back from FG SRAM */ - if (chip->cyc_ctr.en) - restore_cycle_counter(chip); - - if (chip->esr_pulse_tune_en) { - rc = fg_mem_read(chip, &val, SYS_CFG_1_REG, 1, SYS_CFG_1_OFFSET, - 0); - if (rc) { - pr_err("unable to read sys_cfg_1: %d\n", rc); - return rc; - } - - if (!(val & ENABLE_ESR_PULSE_VAL)) - chip->esr_extract_disabled = true; - - if (fg_debug_mask & FG_STATUS) - pr_info("ESR extract is %sabled\n", - chip->esr_extract_disabled ? "dis" : "en"); - - rc = fg_mem_read(chip, &val, CBITS_INPUT_FILTER_REG, 1, - CBITS_RMEAS1_OFFSET, 0); - if (rc) { - pr_err("unable to read cbits_input_filter_reg: %d\n", - rc); - return rc; - } - - if (val & (IMPTR_FAST_TIME_SHIFT | IMPTR_LONG_TIME_SHIFT)) - chip->imptr_pulse_slow_en = true; - - if (fg_debug_mask & FG_STATUS) - pr_info("imptr_pulse_slow is %sabled\n", - chip->imptr_pulse_slow_en ? "en" : "dis"); - - rc = fg_mem_read(chip, &val, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET, - 0); - if (rc) { - pr_err("unable to read rslow cfg: %d\n", rc); - return rc; - } - - if (val & RSLOW_CFG_ON_VAL) - chip->rslow_comp.active = true; - - if (fg_debug_mask & FG_STATUS) - pr_info("rslow_comp active is %sabled\n", - chip->rslow_comp.active ? "en" : "dis"); - } - - return 0; -} - -static int fg_8994_hw_init(struct fg_chip *chip) -{ - int rc = 0; - u8 data[4]; - u64 esr_value; - - rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT, - PATCH_NEG_CURRENT_BIT, - PATCH_NEG_CURRENT_BIT, - EXTERNAL_SENSE_OFFSET); - if (rc) { - pr_err("failed to write patch current bit rc=%d\n", rc); - return rc; - } - - rc = bcl_trim_workaround(chip); - if (rc) { - pr_err("failed to redo bcl trim rc=%d\n", rc); - return rc; - } - - rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG, - BCL_FORCED_HPM_IN_CHARGE, - BCL_FORCED_HPM_IN_CHARGE, - FG_BCL_CONFIG_OFFSET); - if (rc) { - pr_err("failed to force hpm in charge rc=%d\n", rc); - return rc; - } - - fg_mem_masked_write(chip, FG_ALG_SYSCTL_1, I_TERM_QUAL_BIT, 0, 0); - - data[0] = 0xA2; - data[1] = 0x12; - - rc = fg_mem_write(chip, data, TEMP_FRAC_SHIFT_REG, 2, 2, 0); - if (rc) { - pr_err("failed to write temp ocv constants rc=%d\n", rc); - return rc; - } - - data[0] = KI_COEFF_PRED_FULL_4_0_LSB; - data[1] = KI_COEFF_PRED_FULL_4_0_MSB; - fg_mem_write(chip, data, KI_COEFF_PRED_FULL_ADDR, 2, 2, 0); - - esr_value = ESR_DEFAULT_VALUE; - rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8, - ESR_VALUE_OFFSET, 0); - if (rc) - pr_err("failed to write default ESR value rc=%d\n", rc); - else - pr_debug("set default value to esr filter\n"); - - return 0; -} - -#define FG_USBID_CONFIG_OFFSET 0x2 -#define DISABLE_USBID_DETECT_BIT BIT(0) -static int fg_8996_hw_init(struct fg_chip *chip) -{ - int rc; - - rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG, - BCL_FORCED_HPM_IN_CHARGE, - BCL_FORCED_HPM_IN_CHARGE, - FG_BCL_CONFIG_OFFSET); - if (rc) { - pr_err("failed to force hpm in charge rc=%d\n", rc); - return rc; - } - - /* enable usbid conversions for PMi8996 V1.0 */ - if (chip->pmic_revision[REVID_DIG_MAJOR] == 1 - && chip->pmic_revision[REVID_ANA_MAJOR] == 0) { - rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG, - DISABLE_USBID_DETECT_BIT, - 0, FG_USBID_CONFIG_OFFSET); - if (rc) { - pr_err("failed to enable usbid conversions: %d\n", rc); - return rc; - } - } - - return rc; -} - -static int fg_8950_hw_init(struct fg_chip *chip) -{ - int rc; - - rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG, - BCL_FORCED_HPM_IN_CHARGE, - BCL_FORCED_HPM_IN_CHARGE, - FG_BCL_CONFIG_OFFSET); - if (rc) - pr_err("failed to force hpm in charge rc=%d\n", rc); - - return rc; -} - -static int fg_hw_init(struct fg_chip *chip) -{ - int rc = 0; - - rc = fg_common_hw_init(chip); - if (rc) { - pr_err("Unable to initialize FG HW rc=%d\n", rc); - return rc; - } - - /* add PMIC specific hw init */ - switch (chip->pmic_subtype) { - case PMI8994: - rc = fg_8994_hw_init(chip); - chip->wa_flag |= PULSE_REQUEST_WA; - break; - case PMI8996: - rc = fg_8996_hw_init(chip); - /* Setup workaround flag based on PMIC type */ - if (fg_sense_type == INTERNAL_CURRENT_SENSE) - chip->wa_flag |= IADC_GAIN_COMP_WA; - if (chip->pmic_revision[REVID_DIG_MAJOR] > 1) - chip->wa_flag |= USE_CC_SOC_REG; - - break; - case PMI8950: - case PMI8937: - rc = fg_8950_hw_init(chip); - /* Setup workaround flag based on PMIC type */ - chip->wa_flag |= BCL_HI_POWER_FOR_CHGLED_WA; - if (fg_sense_type == INTERNAL_CURRENT_SENSE) - chip->wa_flag |= IADC_GAIN_COMP_WA; - if (chip->pmic_revision[REVID_DIG_MAJOR] > 1) - chip->wa_flag |= USE_CC_SOC_REG; - - break; - } - if (rc) - pr_err("Unable to initialize PMIC specific FG HW rc=%d\n", rc); - - pr_debug("wa_flag=0x%x\n", chip->wa_flag); - - return rc; -} - -#define DIG_MINOR 0x0 -#define DIG_MAJOR 0x1 -#define ANA_MINOR 0x2 -#define ANA_MAJOR 0x3 -#define IACS_INTR_SRC_SLCT BIT(3) -static int fg_setup_memif_offset(struct fg_chip *chip) -{ - int rc; - - rc = fg_read(chip, chip->revision, chip->mem_base + DIG_MINOR, 4); - if (rc) { - pr_err("Unable to read FG revision rc=%d\n", rc); - return rc; - } - - switch (chip->revision[DIG_MAJOR]) { - case DIG_REV_1: - case DIG_REV_2: - chip->offset = offset[0].address; - break; - case DIG_REV_3: - chip->offset = offset[1].address; - chip->ima_supported = true; - break; - default: - pr_err("Digital Major rev=%d not supported\n", - chip->revision[DIG_MAJOR]); - return -EINVAL; - } - - if (chip->ima_supported) { - /* - * Change the FG_MEM_INT interrupt to track IACS_READY - * condition instead of end-of-transaction. This makes sure - * that the next transaction starts only after the hw is ready. - */ - rc = fg_masked_write(chip, - chip->mem_base + MEM_INTF_IMA_CFG, IACS_INTR_SRC_SLCT, - IACS_INTR_SRC_SLCT, 1); - if (rc) { - pr_err("failed to configure interrupt source %d\n", rc); - return rc; - } - } - - return 0; -} - -static int fg_detect_pmic_type(struct fg_chip *chip) -{ - struct pmic_revid_data *pmic_rev_id; - struct device_node *revid_dev_node; - - revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node, - "qcom,pmic-revid", 0); - if (!revid_dev_node) { - pr_err("Missing qcom,pmic-revid property - driver failed\n"); - return -EINVAL; - } - - pmic_rev_id = get_revid_data(revid_dev_node); - if (IS_ERR_OR_NULL(pmic_rev_id)) { - pr_err("Unable to get pmic_revid rc=%ld\n", - PTR_ERR(pmic_rev_id)); - /* - * the revid peripheral must be registered, any failure - * here only indicates that the rev-id module has not - * probed yet. - */ - return -EPROBE_DEFER; - } - - switch (pmic_rev_id->pmic_subtype) { - case PMI8994: - case PMI8950: - case PMI8937: - case PMI8996: - chip->pmic_subtype = pmic_rev_id->pmic_subtype; - chip->pmic_revision[REVID_RESERVED] = pmic_rev_id->rev1; - chip->pmic_revision[REVID_VARIANT] = pmic_rev_id->rev2; - chip->pmic_revision[REVID_ANA_MAJOR] = pmic_rev_id->rev3; - chip->pmic_revision[REVID_DIG_MAJOR] = pmic_rev_id->rev4; - break; - default: - pr_err("PMIC subtype %d not supported\n", - pmic_rev_id->pmic_subtype); - return -EINVAL; - } - - return 0; -} - -#define INIT_JEITA_DELAY_MS 1000 - -static void delayed_init_work(struct work_struct *work) -{ - u8 reg[2]; - int rc; - struct fg_chip *chip = container_of(work, - struct fg_chip, - init_work); - - /* hold memory access until initialization finishes */ - fg_mem_lock(chip); - - rc = fg_hw_init(chip); - if (rc) { - pr_err("failed to hw init rc = %d\n", rc); - fg_mem_release(chip); - fg_cleanup(chip); - return; - } - /* release memory access before update_sram_data is called */ - fg_mem_release(chip); - - schedule_delayed_work( - &chip->update_jeita_setting, - msecs_to_jiffies(INIT_JEITA_DELAY_MS)); - - if (chip->last_sram_update_time == 0) - update_sram_data_work(&chip->update_sram_data.work); - - if (chip->last_temp_update_time == 0) - update_temp_data(&chip->update_temp_work.work); - - if (!chip->use_otp_profile) - schedule_delayed_work(&chip->batt_profile_init, 0); - - if (chip->wa_flag & IADC_GAIN_COMP_WA) { - /* read default gain config */ - rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, DEF_GAIN_OFFSET, 0); - if (rc) { - pr_err("Failed to read default gain rc=%d\n", rc); - goto done; - } - - if (reg[1] || reg[0]) { - /* - * Default gain register has valid value: - * - write to gain register. - */ - rc = fg_mem_write(chip, reg, GAIN_REG, 2, - GAIN_OFFSET, 0); - if (rc) { - pr_err("Failed to write gain rc=%d\n", rc); - goto done; - } - } else { - /* - * Default gain register is invalid: - * - read gain register for default gain value - * - write to default gain register. - */ - rc = fg_mem_read(chip, reg, GAIN_REG, 2, - GAIN_OFFSET, 0); - if (rc) { - pr_err("Failed to read gain rc=%d\n", rc); - goto done; - } - rc = fg_mem_write(chip, reg, K_VCOR_REG, 2, - DEF_GAIN_OFFSET, 0); - if (rc) { - pr_err("Failed to write default gain rc=%d\n", - rc); - goto done; - } - } - - chip->iadc_comp_data.dfl_gain_reg[0] = reg[0]; - chip->iadc_comp_data.dfl_gain_reg[1] = reg[1]; - chip->iadc_comp_data.dfl_gain = half_float(reg); - chip->input_present = is_input_present(chip); - chip->otg_present = is_otg_present(chip); - chip->init_done = true; - - pr_debug("IADC gain initial config reg_val 0x%x%x gain %lld\n", - reg[1], reg[0], chip->iadc_comp_data.dfl_gain); - } - - pr_debug("FG: HW_init success\n"); - - return; -done: - fg_cleanup(chip); -} - -static int fg_probe(struct platform_device *pdev) -{ - struct device *dev = &(pdev->dev); - struct fg_chip *chip; - struct device_node *child; - unsigned int base; - u8 subtype, reg; - int rc = 0; - struct power_supply_config bms_psy_cfg; - - if (!pdev) { - pr_err("no valid spmi pointer\n"); - return -ENODEV; - } - - if (!pdev->dev.of_node) { - pr_err("device node missing\n"); - return -ENODEV; - } - - chip = devm_kzalloc(dev, sizeof(struct fg_chip), GFP_KERNEL); - if (chip == NULL) { - pr_err("Can't allocate fg_chip\n"); - return -ENOMEM; - } - chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!chip->regmap) { - dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); - return -EINVAL; - } - - chip->pdev = pdev; - chip->dev = &(pdev->dev); - - wakeup_source_init(&chip->empty_check_wakeup_source.source, - "qpnp_fg_empty_check"); - wakeup_source_init(&chip->memif_wakeup_source.source, - "qpnp_fg_memaccess"); - wakeup_source_init(&chip->profile_wakeup_source.source, - "qpnp_fg_profile"); - wakeup_source_init(&chip->update_temp_wakeup_source.source, - "qpnp_fg_update_temp"); - wakeup_source_init(&chip->update_sram_wakeup_source.source, - "qpnp_fg_update_sram"); - wakeup_source_init(&chip->resume_soc_wakeup_source.source, - "qpnp_fg_set_resume_soc"); - wakeup_source_init(&chip->gain_comp_wakeup_source.source, - "qpnp_fg_gain_comp"); - wakeup_source_init(&chip->capacity_learning_wakeup_source.source, - "qpnp_fg_cap_learning"); - wakeup_source_init(&chip->esr_extract_wakeup_source.source, - "qpnp_fg_esr_extract"); - mutex_init(&chip->rw_lock); - mutex_init(&chip->cyc_ctr.lock); - mutex_init(&chip->learning_data.learning_lock); - mutex_init(&chip->rslow_comp.lock); - mutex_init(&chip->sysfs_restart_lock); - INIT_DELAYED_WORK(&chip->update_jeita_setting, update_jeita_setting); - INIT_DELAYED_WORK(&chip->update_sram_data, update_sram_data_work); - INIT_DELAYED_WORK(&chip->update_temp_work, update_temp_data); - INIT_DELAYED_WORK(&chip->check_empty_work, check_empty_work); - INIT_DELAYED_WORK(&chip->batt_profile_init, batt_profile_init); - INIT_WORK(&chip->rslow_comp_work, rslow_comp_work); - INIT_WORK(&chip->fg_cap_learning_work, fg_cap_learning_work); - INIT_WORK(&chip->dump_sram, dump_sram); - INIT_WORK(&chip->status_change_work, status_change_work); - INIT_WORK(&chip->cycle_count_work, update_cycle_count); - INIT_WORK(&chip->battery_age_work, battery_age_work); - INIT_WORK(&chip->update_esr_work, update_esr_value); - INIT_WORK(&chip->set_resume_soc_work, set_resume_soc_work); - INIT_WORK(&chip->sysfs_restart_work, sysfs_restart_work); - INIT_WORK(&chip->init_work, delayed_init_work); - INIT_WORK(&chip->charge_full_work, charge_full_work); - INIT_WORK(&chip->gain_comp_work, iadc_gain_comp_work); - INIT_WORK(&chip->bcl_hi_power_work, bcl_hi_power_work); - INIT_WORK(&chip->esr_extract_config_work, esr_extract_config_work); - alarm_init(&chip->fg_cap_learning_alarm, ALARM_BOOTTIME, - fg_cap_learning_alarm_cb); - init_completion(&chip->sram_access_granted); - init_completion(&chip->sram_access_revoked); - complete_all(&chip->sram_access_revoked); - init_completion(&chip->batt_id_avail); - init_completion(&chip->first_soc_done); - dev_set_drvdata(&pdev->dev, chip); - - if (of_get_available_child_count(pdev->dev.of_node) == 0) { - pr_err("no child nodes\n"); - rc = -ENXIO; - goto of_init_fail; - } - - for_each_available_child_of_node(pdev->dev.of_node, child) { - rc = of_property_read_u32(child, "reg", &base); - if (rc < 0) { - dev_err(&pdev->dev, - "Couldn't find reg in node = %s rc = %d\n", - child->full_name, rc); - goto of_init_fail; - } - - if (strcmp("qcom,fg-adc-vbat", child->name) == 0) { - chip->vbat_adc_addr = base; - continue; - } else if (strcmp("qcom,fg-adc-ibat", child->name) == 0) { - chip->ibat_adc_addr = base; - continue; - } else if (strcmp("qcom,revid-tp-rev", child->name) == 0) { - chip->tp_rev_addr = base; - continue; - } - - rc = fg_read(chip, &subtype, - base + REG_OFFSET_PERP_SUBTYPE, 1); - if (rc) { - pr_err("Peripheral subtype read failed rc=%d\n", rc); - goto of_init_fail; - } - - switch (subtype) { - case FG_SOC: - chip->soc_base = base; - break; - case FG_MEMIF: - chip->mem_base = base; - break; - case FG_BATT: - chip->batt_base = base; - break; - default: - pr_err("Invalid peripheral subtype=0x%x\n", subtype); - rc = -EINVAL; - } - } - - rc = fg_detect_pmic_type(chip); - if (rc) { - pr_err("Unable to detect PMIC type rc=%d\n", rc); - return rc; - } - - rc = fg_setup_memif_offset(chip); - if (rc) { - pr_err("Unable to setup mem_if offsets rc=%d\n", rc); - goto of_init_fail; - } - - rc = fg_of_init(chip); - if (rc) { - pr_err("failed to parse devicetree rc%d\n", rc); - goto of_init_fail; - } - - if (chip->jeita_hysteresis_support) { - rc = fg_init_batt_temp_state(chip); - if (rc) { - pr_err("failed to get battery status rc%d\n", rc); - goto of_init_fail; - } - } - - /* check if the first estimate is already finished at this time */ - if (is_first_est_done(chip)) - complete_all(&chip->first_soc_done); - - reg = 0xFF; - rc = fg_write(chip, ®, INT_EN_CLR(chip->mem_base), 1); - if (rc) { - pr_err("failed to clear interrupts %d\n", rc); - goto of_init_fail; - } - - rc = fg_init_irqs(chip); - if (rc) { - pr_err("failed to request interrupts %d\n", rc); - goto cancel_work; - } - - chip->batt_type = default_batt_type; - - chip->bms_psy_d.name = "bms"; - chip->bms_psy_d.type = POWER_SUPPLY_TYPE_BMS; - chip->bms_psy_d.properties = fg_power_props; - chip->bms_psy_d.num_properties = ARRAY_SIZE(fg_power_props); - chip->bms_psy_d.get_property = fg_power_get_property; - chip->bms_psy_d.set_property = fg_power_set_property; - chip->bms_psy_d.external_power_changed = fg_external_power_changed; - chip->bms_psy_d.property_is_writeable = fg_property_is_writeable; - - bms_psy_cfg.drv_data = chip; - bms_psy_cfg.supplied_to = fg_supplicants; - bms_psy_cfg.num_supplicants = ARRAY_SIZE(fg_supplicants); - bms_psy_cfg.of_node = NULL; - chip->bms_psy = devm_power_supply_register(chip->dev, - &chip->bms_psy_d, - &bms_psy_cfg); - if (IS_ERR(chip->bms_psy)) { - pr_err("batt failed to register rc = %ld\n", - PTR_ERR(chip->bms_psy)); - goto of_init_fail; - } - chip->power_supply_registered = true; - /* - * Just initialize the batt_psy_name here. Power supply - * will be obtained later. - */ - chip->batt_psy_name = "battery"; - - if (chip->mem_base) { - rc = fg_dfs_create(chip); - if (rc < 0) { - pr_err("failed to create debugfs rc = %d\n", rc); - goto cancel_work; - } - } - - schedule_work(&chip->init_work); - - pr_info("FG Probe success - FG Revision DIG:%d.%d ANA:%d.%d PMIC subtype=%d\n", - chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR], - chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR], - chip->pmic_subtype); - - return rc; - -cancel_work: - cancel_delayed_work_sync(&chip->update_jeita_setting); - cancel_delayed_work_sync(&chip->update_sram_data); - cancel_delayed_work_sync(&chip->update_temp_work); - cancel_delayed_work_sync(&chip->check_empty_work); - cancel_delayed_work_sync(&chip->batt_profile_init); - alarm_try_to_cancel(&chip->fg_cap_learning_alarm); - cancel_work_sync(&chip->set_resume_soc_work); - cancel_work_sync(&chip->fg_cap_learning_work); - cancel_work_sync(&chip->dump_sram); - cancel_work_sync(&chip->status_change_work); - cancel_work_sync(&chip->cycle_count_work); - cancel_work_sync(&chip->update_esr_work); - cancel_work_sync(&chip->rslow_comp_work); - cancel_work_sync(&chip->sysfs_restart_work); - cancel_work_sync(&chip->gain_comp_work); - cancel_work_sync(&chip->init_work); - cancel_work_sync(&chip->charge_full_work); - cancel_work_sync(&chip->bcl_hi_power_work); - cancel_work_sync(&chip->esr_extract_config_work); -of_init_fail: - mutex_destroy(&chip->rslow_comp.lock); - mutex_destroy(&chip->rw_lock); - mutex_destroy(&chip->cyc_ctr.lock); - mutex_destroy(&chip->learning_data.learning_lock); - mutex_destroy(&chip->sysfs_restart_lock); - wakeup_source_trash(&chip->resume_soc_wakeup_source.source); - wakeup_source_trash(&chip->empty_check_wakeup_source.source); - wakeup_source_trash(&chip->memif_wakeup_source.source); - wakeup_source_trash(&chip->profile_wakeup_source.source); - wakeup_source_trash(&chip->update_temp_wakeup_source.source); - wakeup_source_trash(&chip->update_sram_wakeup_source.source); - wakeup_source_trash(&chip->gain_comp_wakeup_source.source); - wakeup_source_trash(&chip->capacity_learning_wakeup_source.source); - wakeup_source_trash(&chip->esr_extract_wakeup_source.source); - return rc; -} - -static void check_and_update_sram_data(struct fg_chip *chip) -{ - unsigned long current_time = 0, next_update_time, time_left; - - get_current_time(¤t_time); - - next_update_time = chip->last_temp_update_time - + (TEMP_PERIOD_UPDATE_MS / 1000); - - if (next_update_time > current_time) - time_left = next_update_time - current_time; - else - time_left = 0; - - schedule_delayed_work( - &chip->update_temp_work, msecs_to_jiffies(time_left * 1000)); - - next_update_time = chip->last_sram_update_time - + (fg_sram_update_period_ms / 1000); - - if (next_update_time > current_time) - time_left = next_update_time - current_time; - else - time_left = 0; - - schedule_delayed_work( - &chip->update_sram_data, msecs_to_jiffies(time_left * 1000)); -} - -static int fg_suspend(struct device *dev) -{ - struct fg_chip *chip = dev_get_drvdata(dev); - - if (!chip->sw_rbias_ctrl) - return 0; - - cancel_delayed_work(&chip->update_temp_work); - cancel_delayed_work(&chip->update_sram_data); - - return 0; -} - -static int fg_resume(struct device *dev) -{ - struct fg_chip *chip = dev_get_drvdata(dev); - - if (!chip->sw_rbias_ctrl) - return 0; - - check_and_update_sram_data(chip); - return 0; -} - -static const struct dev_pm_ops qpnp_fg_pm_ops = { - .suspend = fg_suspend, - .resume = fg_resume, -}; - -static int fg_sense_type_set(const char *val, const struct kernel_param *kp) -{ - int rc; - struct power_supply *bms_psy; - struct fg_chip *chip; - int old_fg_sense_type = fg_sense_type; - - rc = param_set_int(val, kp); - if (rc) { - pr_err("Unable to set fg_sense_type: %d\n", rc); - return rc; - } - - if (fg_sense_type != 0 && fg_sense_type != 1) { - pr_err("Bad value %d\n", fg_sense_type); - fg_sense_type = old_fg_sense_type; - return -EINVAL; - } - - if (fg_debug_mask & FG_STATUS) - pr_info("fg_sense_type set to %d\n", fg_sense_type); - - bms_psy = power_supply_get_by_name("bms"); - if (!bms_psy) { - pr_err("bms psy not found\n"); - return 0; - } - - chip = power_supply_get_drvdata(bms_psy); - rc = set_prop_sense_type(chip, fg_sense_type); - return rc; -} - -static struct kernel_param_ops fg_sense_type_ops = { - .set = fg_sense_type_set, - .get = param_get_int, -}; - -module_param_cb(sense_type, &fg_sense_type_ops, &fg_sense_type, 0644); - -static int fg_restart_set(const char *val, const struct kernel_param *kp) -{ - struct power_supply *bms_psy; - struct fg_chip *chip; - - bms_psy = power_supply_get_by_name("bms"); - if (!bms_psy) { - pr_err("bms psy not found\n"); - return 0; - } - chip = power_supply_get_drvdata(bms_psy); - - mutex_lock(&chip->sysfs_restart_lock); - if (fg_restart != 0) { - mutex_unlock(&chip->sysfs_restart_lock); - return 0; - } - fg_restart = 1; - mutex_unlock(&chip->sysfs_restart_lock); - - if (fg_debug_mask & FG_STATUS) - pr_info("fuel gauge restart initiated from sysfs...\n"); - - schedule_work(&chip->sysfs_restart_work); - return 0; -} - -static struct kernel_param_ops fg_restart_ops = { - .set = fg_restart_set, - .get = param_get_int, -}; - -module_param_cb(restart, &fg_restart_ops, &fg_restart, 0644); - -static struct platform_driver fg_driver = { - .driver = { - .name = QPNP_FG_DEV_NAME, - .of_match_table = fg_match_table, - .pm = &qpnp_fg_pm_ops, - }, - .probe = fg_probe, - .remove = fg_remove, -}; - -static int __init fg_init(void) -{ - return platform_driver_register(&fg_driver); -} - -static void __exit fg_exit(void) -{ - return platform_driver_unregister(&fg_driver); -} - -module_init(fg_init); -module_exit(fg_exit); - -MODULE_DESCRIPTION("QPNP Fuel Gauge Driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" QPNP_FG_DEV_NAME); diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c deleted file mode 100644 index 6c1e58d046e8..000000000000 --- a/drivers/power/supply/qcom/qpnp-smbcharger.c +++ /dev/null @@ -1,8472 +0,0 @@ -/* 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 - * 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) "SMBCHG: %s: " fmt, __func__ - -#include <linux/regmap.h> -#include <linux/spinlock.h> -#include <linux/gpio.h> -#include <linux/errno.h> -#include <linux/delay.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/slab.h> -#include <linux/sched.h> -#include <linux/power_supply.h> -#include <linux/of.h> -#include <linux/of_gpio.h> -#include <linux/of_irq.h> -#include <linux/bitops.h> -#include <linux/regulator/consumer.h> -#include <linux/regulator/driver.h> -#include <linux/regulator/of_regulator.h> -#include <linux/regulator/machine.h> -#include <linux/spmi.h> -#include <linux/platform_device.h> -#include <linux/printk.h> -#include <linux/ratelimit.h> -#include <linux/debugfs.h> -#include <linux/leds.h> -#include <linux/rtc.h> -#include <linux/qpnp/qpnp-adc.h> -#include <linux/batterydata-lib.h> -#include <linux/of_batterydata.h> -#include <linux/msm_bcl.h> -#include <linux/ktime.h> -#include <linux/extcon.h> -#include "pmic-voter.h" - -/* Mask/Bit helpers */ -#define _SMB_MASK(BITS, POS) \ - ((unsigned char)(((1 << (BITS)) - 1) << (POS))) -#define SMB_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \ - _SMB_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \ - (RIGHT_BIT_POS)) -/* Config registers */ -struct smbchg_regulator { - struct regulator_desc rdesc; - struct regulator_dev *rdev; -}; - -struct parallel_usb_cfg { - struct power_supply *psy; - int min_current_thr_ma; - int min_9v_current_thr_ma; - int allowed_lowering_ma; - int current_max_ma; - bool avail; - struct mutex lock; - int initial_aicl_ma; - ktime_t last_disabled; - bool enabled_once; -}; - -struct ilim_entry { - int vmin_uv; - int vmax_uv; - int icl_pt_ma; - int icl_lv_ma; - int icl_hv_ma; -}; - -struct ilim_map { - int num; - struct ilim_entry *entries; -}; - -struct smbchg_version_tables { - const int *dc_ilim_ma_table; - int dc_ilim_ma_len; - const int *usb_ilim_ma_table; - int usb_ilim_ma_len; - const int *iterm_ma_table; - int iterm_ma_len; - const int *fcc_comp_table; - int fcc_comp_len; - const int *aicl_rerun_period_table; - int aicl_rerun_period_len; - int rchg_thr_mv; -}; - -struct smbchg_chip { - struct device *dev; - struct platform_device *pdev; - struct regmap *regmap; - int schg_version; - - /* peripheral register address bases */ - u16 chgr_base; - u16 bat_if_base; - u16 usb_chgpth_base; - u16 dc_chgpth_base; - u16 otg_base; - u16 misc_base; - - int fake_battery_soc; - u8 revision[4]; - - /* configuration parameters */ - int iterm_ma; - int usb_max_current_ma; - int typec_current_ma; - int dc_max_current_ma; - int dc_target_current_ma; - int cfg_fastchg_current_ma; - int fastchg_current_ma; - int vfloat_mv; - int fastchg_current_comp; - int float_voltage_comp; - int resume_delta_mv; - int safety_time; - int prechg_safety_time; - int bmd_pin_src; - int jeita_temp_hard_limit; - int aicl_rerun_period_s; - bool use_vfloat_adjustments; - bool iterm_disabled; - bool bmd_algo_disabled; - bool soft_vfloat_comp_disabled; - bool chg_enabled; - bool charge_unknown_battery; - bool chg_inhibit_en; - bool chg_inhibit_source_fg; - bool low_volt_dcin; - bool cfg_chg_led_support; - bool cfg_chg_led_sw_ctrl; - bool vbat_above_headroom; - bool force_aicl_rerun; - bool hvdcp3_supported; - bool restricted_charging; - bool skip_usb_suspend_for_fake_battery; - bool hvdcp_not_supported; - bool otg_pinctrl; - u8 original_usbin_allowance; - struct parallel_usb_cfg parallel; - struct delayed_work parallel_en_work; - struct dentry *debug_root; - struct smbchg_version_tables tables; - - /* wipower params */ - struct ilim_map wipower_default; - struct ilim_map wipower_pt; - struct ilim_map wipower_div2; - struct qpnp_vadc_chip *vadc_dev; - bool wipower_dyn_icl_avail; - struct ilim_entry current_ilim; - struct mutex wipower_config; - bool wipower_configured; - struct qpnp_adc_tm_btm_param param; - - /* flash current prediction */ - int rpara_uohm; - int rslow_uohm; - int vled_max_uv; - - /* vfloat adjustment */ - int max_vbat_sample; - int n_vbat_samples; - - /* status variables */ - int wake_reasons; - int previous_soc; - int usb_online; - bool dc_present; - bool usb_present; - bool batt_present; - int otg_retries; - ktime_t otg_enable_time; - bool aicl_deglitch_short; - bool safety_timer_en; - bool aicl_complete; - bool usb_ov_det; - bool otg_pulse_skip_dis; - const char *battery_type; - enum power_supply_type usb_supply_type; - bool very_weak_charger; - bool parallel_charger_detected; - bool chg_otg_enabled; - bool flash_triggered; - bool flash_active; - bool icl_disabled; - u32 wa_flags; - int usb_icl_delta; - bool typec_dfp; - unsigned int usb_current_max; - unsigned int usb_health; - - /* jeita and temperature */ - bool batt_hot; - bool batt_cold; - bool batt_warm; - bool batt_cool; - unsigned int thermal_levels; - unsigned int therm_lvl_sel; - unsigned int *thermal_mitigation; - - /* irqs */ - int batt_hot_irq; - int batt_warm_irq; - int batt_cool_irq; - int batt_cold_irq; - int batt_missing_irq; - int vbat_low_irq; - int chg_hot_irq; - int chg_term_irq; - int taper_irq; - bool taper_irq_enabled; - struct mutex taper_irq_lock; - int recharge_irq; - int fastchg_irq; - int wdog_timeout_irq; - int power_ok_irq; - int dcin_uv_irq; - int usbin_uv_irq; - int usbin_ov_irq; - int src_detect_irq; - int otg_fail_irq; - int otg_oc_irq; - int aicl_done_irq; - int usbid_change_irq; - int chg_error_irq; - bool enable_aicl_wake; - - /* psy */ - struct power_supply_desc usb_psy_d; - struct power_supply *usb_psy; - struct power_supply_desc batt_psy_d; - struct power_supply *batt_psy; - struct power_supply_desc dc_psy_d; - struct power_supply *dc_psy; - struct power_supply *bms_psy; - struct power_supply *typec_psy; - int dc_psy_type; - const char *bms_psy_name; - const char *battery_psy_name; - - struct regulator *dpdm_reg; - struct smbchg_regulator otg_vreg; - struct smbchg_regulator ext_otg_vreg; - struct work_struct usb_set_online_work; - struct delayed_work vfloat_adjust_work; - struct delayed_work hvdcp_det_work; - spinlock_t sec_access_lock; - struct mutex therm_lvl_lock; - struct mutex usb_set_online_lock; - struct mutex pm_lock; - /* aicl deglitch workaround */ - unsigned long first_aicl_seconds; - int aicl_irq_count; - struct mutex usb_status_lock; - bool hvdcp_3_det_ignore_uv; - struct completion src_det_lowered; - struct completion src_det_raised; - struct completion usbin_uv_lowered; - struct completion usbin_uv_raised; - int pulse_cnt; - struct led_classdev led_cdev; - bool skip_usb_notification; - u32 vchg_adc_channel; - struct qpnp_vadc_chip *vchg_vadc_dev; - - /* voters */ - struct votable *fcc_votable; - struct votable *usb_icl_votable; - struct votable *dc_icl_votable; - struct votable *usb_suspend_votable; - struct votable *dc_suspend_votable; - struct votable *battchg_suspend_votable; - struct votable *hw_aicl_rerun_disable_votable; - struct votable *hw_aicl_rerun_enable_indirect_votable; - struct votable *aicl_deglitch_short_votable; - - /* extcon for VBUS / ID notification to USB */ - struct extcon_dev *extcon; -}; - -enum qpnp_schg { - QPNP_SCHG, - QPNP_SCHG_LITE, -}; - -static char *version_str[] = { - [QPNP_SCHG] = "SCHG", - [QPNP_SCHG_LITE] = "SCHG_LITE", -}; - -enum pmic_subtype { - PMI8994 = 10, - PMI8950 = 17, - PMI8996 = 19, - PMI8937 = 55, -}; - -enum smbchg_wa { - SMBCHG_AICL_DEGLITCH_WA = BIT(0), - SMBCHG_HVDCP_9V_EN_WA = BIT(1), - SMBCHG_USB100_WA = BIT(2), - SMBCHG_BATT_OV_WA = BIT(3), - SMBCHG_CC_ESR_WA = BIT(4), - SMBCHG_FLASH_ICL_DISABLE_WA = BIT(5), - SMBCHG_RESTART_WA = BIT(6), - SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA = BIT(7), -}; - -enum print_reason { - PR_REGISTER = BIT(0), - PR_INTERRUPT = BIT(1), - PR_STATUS = BIT(2), - PR_DUMP = BIT(3), - PR_PM = BIT(4), - PR_MISC = BIT(5), - PR_WIPOWER = BIT(6), - PR_TYPEC = BIT(7), -}; - -enum wake_reason { - PM_PARALLEL_CHECK = BIT(0), - PM_REASON_VFLOAT_ADJUST = BIT(1), - PM_ESR_PULSE = BIT(2), - PM_PARALLEL_TAPER = BIT(3), - PM_DETECT_HVDCP = BIT(4), -}; - -/* fcc_voters */ -#define ESR_PULSE_FCC_VOTER "ESR_PULSE_FCC_VOTER" -#define BATT_TYPE_FCC_VOTER "BATT_TYPE_FCC_VOTER" -#define RESTRICTED_CHG_FCC_VOTER "RESTRICTED_CHG_FCC_VOTER" - -/* ICL VOTERS */ -#define PSY_ICL_VOTER "PSY_ICL_VOTER" -#define THERMAL_ICL_VOTER "THERMAL_ICL_VOTER" -#define HVDCP_ICL_VOTER "HVDCP_ICL_VOTER" -#define USER_ICL_VOTER "USER_ICL_VOTER" -#define WEAK_CHARGER_ICL_VOTER "WEAK_CHARGER_ICL_VOTER" -#define SW_AICL_ICL_VOTER "SW_AICL_ICL_VOTER" -#define CHG_SUSPEND_WORKAROUND_ICL_VOTER "CHG_SUSPEND_WORKAROUND_ICL_VOTER" - -/* USB SUSPEND VOTERS */ -/* userspace has suspended charging altogether */ -#define USER_EN_VOTER "USER_EN_VOTER" -/* - * this specific path has been suspended through the power supply - * framework - */ -#define POWER_SUPPLY_EN_VOTER "POWER_SUPPLY_EN_VOTER" -/* - * the usb driver has suspended this path by setting a current limit - * of < 2MA - */ -#define USB_EN_VOTER "USB_EN_VOTER" -/* - * the thermal daemon can suspend a charge path when the system - * temperature levels rise - */ -#define THERMAL_EN_VOTER "THERMAL_EN_VOTER" -/* - * an external OTG supply is being used, suspend charge path so the - * charger does not accidentally try to charge from the external supply. - */ -#define OTG_EN_VOTER "OTG_EN_VOTER" -/* - * the charger is very weak, do not draw any current from it - */ -#define WEAK_CHARGER_EN_VOTER "WEAK_CHARGER_EN_VOTER" -/* - * fake battery voter, if battery id-resistance around 7.5 Kohm - */ -#define FAKE_BATTERY_EN_VOTER "FAKE_BATTERY_EN_VOTER" - -/* battchg_enable_voters */ - /* userspace has disabled battery charging */ -#define BATTCHG_USER_EN_VOTER "BATTCHG_USER_EN_VOTER" - /* battery charging disabled while loading battery profiles */ -#define BATTCHG_UNKNOWN_BATTERY_EN_VOTER "BATTCHG_UNKNOWN_BATTERY_EN_VOTER" - -/* hw_aicl_rerun_enable_indirect_voters */ -/* enabled via device tree */ -#define DEFAULT_CONFIG_HW_AICL_VOTER "DEFAULT_CONFIG_HW_AICL_VOTER" -/* Varb workaround voter */ -#define VARB_WORKAROUND_VOTER "VARB_WORKAROUND_VOTER" -/* SHUTDOWN workaround voter */ -#define SHUTDOWN_WORKAROUND_VOTER "SHUTDOWN_WORKAROUND_VOTER" - -/* hw_aicl_rerun_disable_voters */ -/* the results from enabling clients */ -#define HW_AICL_RERUN_ENABLE_INDIRECT_VOTER \ - "HW_AICL_RERUN_ENABLE_INDIRECT_VOTER" -/* Weak charger voter */ -#define WEAK_CHARGER_HW_AICL_VOTER "WEAK_CHARGER_HW_AICL_VOTER" - -/* aicl_short_deglitch_voters */ -/* Varb workaround voter */ -#define VARB_WORKAROUND_SHORT_DEGLITCH_VOTER \ - "VARB_WRKARND_SHORT_DEGLITCH_VOTER" -/* QC 2.0 */ -#define HVDCP_SHORT_DEGLITCH_VOTER "HVDCP_SHORT_DEGLITCH_VOTER" - -static const unsigned int smbchg_extcon_cable[] = { - EXTCON_USB, - EXTCON_USB_HOST, - EXTCON_NONE, -}; - -static int smbchg_debug_mask; -module_param_named( - debug_mask, smbchg_debug_mask, int, S_IRUSR | S_IWUSR -); - -static int smbchg_parallel_en = 1; -module_param_named( - parallel_en, smbchg_parallel_en, int, S_IRUSR | S_IWUSR -); - -static int smbchg_main_chg_fcc_percent = 50; -module_param_named( - main_chg_fcc_percent, smbchg_main_chg_fcc_percent, - int, S_IRUSR | S_IWUSR -); - -static int smbchg_main_chg_icl_percent = 60; -module_param_named( - main_chg_icl_percent, smbchg_main_chg_icl_percent, - int, S_IRUSR | S_IWUSR -); - -static int smbchg_default_hvdcp_icl_ma = 1800; -module_param_named( - default_hvdcp_icl_ma, smbchg_default_hvdcp_icl_ma, - int, S_IRUSR | S_IWUSR -); - -static int smbchg_default_hvdcp3_icl_ma = 3000; -module_param_named( - default_hvdcp3_icl_ma, smbchg_default_hvdcp3_icl_ma, - int, S_IRUSR | S_IWUSR -); - -static int smbchg_default_dcp_icl_ma = 1800; -module_param_named( - default_dcp_icl_ma, smbchg_default_dcp_icl_ma, - int, S_IRUSR | S_IWUSR -); - -static int wipower_dyn_icl_en; -module_param_named( - dynamic_icl_wipower_en, wipower_dyn_icl_en, - int, S_IRUSR | S_IWUSR -); - -static int wipower_dcin_interval = ADC_MEAS1_INTERVAL_2P0MS; -module_param_named( - wipower_dcin_interval, wipower_dcin_interval, - int, S_IRUSR | S_IWUSR -); - -#define WIPOWER_DEFAULT_HYSTERISIS_UV 250000 -static int wipower_dcin_hyst_uv = WIPOWER_DEFAULT_HYSTERISIS_UV; -module_param_named( - wipower_dcin_hyst_uv, wipower_dcin_hyst_uv, - int, S_IRUSR | S_IWUSR -); - -#define pr_smb(reason, fmt, ...) \ - do { \ - if (smbchg_debug_mask & (reason)) \ - pr_info(fmt, ##__VA_ARGS__); \ - else \ - pr_debug(fmt, ##__VA_ARGS__); \ - } while (0) - -#define pr_smb_rt(reason, fmt, ...) \ - do { \ - if (smbchg_debug_mask & (reason)) \ - pr_info_ratelimited(fmt, ##__VA_ARGS__); \ - else \ - pr_debug(fmt, ##__VA_ARGS__); \ - } while (0) - -static int smbchg_read(struct smbchg_chip *chip, u8 *val, - u16 addr, int count) -{ - int rc = 0; - struct platform_device *pdev = chip->pdev; - - if (addr == 0) { - dev_err(chip->dev, "addr cannot be zero addr=0x%02x sid=0x%02x rc=%d\n", - addr, to_spmi_device(pdev->dev.parent)->usid, rc); - return -EINVAL; - } - - rc = regmap_bulk_read(chip->regmap, addr, val, count); - if (rc) { - dev_err(chip->dev, "spmi read failed addr=0x%02x sid=0x%02x rc=%d\n", - addr, to_spmi_device(pdev->dev.parent)->usid, - rc); - return rc; - } - return 0; -} - -/* - * Writes a register to the specified by the base and limited by the bit mask - * - * Do not use this function for register writes if possible. Instead use the - * smbchg_masked_write function. - * - * The sec_access_lock must be held for all register writes and this function - * does not do that. If this function is used, please hold the spinlock or - * random secure access writes may fail. - */ -static int smbchg_masked_write_raw(struct smbchg_chip *chip, u16 base, u8 mask, - u8 val) -{ - int rc; - - rc = regmap_update_bits(chip->regmap, base, mask, val); - if (rc) { - dev_err(chip->dev, "spmi write failed: addr=%03X, rc=%d\n", - base, rc); - return rc; - } - - return 0; -} - -/* - * Writes a register to the specified by the base and limited by the bit mask - * - * This function holds a spin lock to ensure secure access register writes goes - * through. If the secure access unlock register is armed, any old register - * write can unarm the secure access unlock, causing the next write to fail. - * - * Note: do not use this for sec_access registers. Instead use the function - * below: smbchg_sec_masked_write - */ -static int smbchg_masked_write(struct smbchg_chip *chip, u16 base, u8 mask, - u8 val) -{ - unsigned long flags; - int rc; - - spin_lock_irqsave(&chip->sec_access_lock, flags); - rc = smbchg_masked_write_raw(chip, base, mask, val); - spin_unlock_irqrestore(&chip->sec_access_lock, flags); - - return rc; -} - -/* - * Unlocks sec access and writes to the register specified. - * - * This function holds a spin lock to exclude other register writes while - * the two writes are taking place. - */ -#define SEC_ACCESS_OFFSET 0xD0 -#define SEC_ACCESS_VALUE 0xA5 -#define PERIPHERAL_MASK 0xFF -static int smbchg_sec_masked_write(struct smbchg_chip *chip, u16 base, u8 mask, - u8 val) -{ - unsigned long flags; - int rc; - u16 peripheral_base = base & (~PERIPHERAL_MASK); - - spin_lock_irqsave(&chip->sec_access_lock, flags); - - rc = smbchg_masked_write_raw(chip, peripheral_base + SEC_ACCESS_OFFSET, - SEC_ACCESS_VALUE, SEC_ACCESS_VALUE); - if (rc) { - dev_err(chip->dev, "Unable to unlock sec_access: %d", rc); - goto out; - } - - rc = smbchg_masked_write_raw(chip, base, mask, val); - -out: - spin_unlock_irqrestore(&chip->sec_access_lock, flags); - return rc; -} - -static void smbchg_stay_awake(struct smbchg_chip *chip, int reason) -{ - int reasons; - - mutex_lock(&chip->pm_lock); - reasons = chip->wake_reasons | reason; - if (reasons != 0 && chip->wake_reasons == 0) { - pr_smb(PR_PM, "staying awake: 0x%02x (bit %d)\n", - reasons, reason); - pm_stay_awake(chip->dev); - } - chip->wake_reasons = reasons; - mutex_unlock(&chip->pm_lock); -} - -static void smbchg_relax(struct smbchg_chip *chip, int reason) -{ - int reasons; - - mutex_lock(&chip->pm_lock); - reasons = chip->wake_reasons & (~reason); - if (reasons == 0 && chip->wake_reasons != 0) { - pr_smb(PR_PM, "relaxing: 0x%02x (bit %d)\n", - reasons, reason); - pm_relax(chip->dev); - } - chip->wake_reasons = reasons; - mutex_unlock(&chip->pm_lock); -}; - -enum pwr_path_type { - UNKNOWN = 0, - PWR_PATH_BATTERY = 1, - PWR_PATH_USB = 2, - PWR_PATH_DC = 3, -}; - -#define PWR_PATH 0x08 -#define PWR_PATH_MASK 0x03 -static enum pwr_path_type smbchg_get_pwr_path(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + PWR_PATH, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read PWR_PATH rc = %d\n", rc); - return PWR_PATH_BATTERY; - } - - return reg & PWR_PATH_MASK; -} - -#define RID_STS 0xB -#define RID_MASK 0xF -#define IDEV_STS 0x8 -#define RT_STS 0x10 -#define USBID_MSB 0xE -#define USBIN_UV_BIT BIT(0) -#define USBIN_OV_BIT BIT(1) -#define USBIN_SRC_DET_BIT BIT(2) -#define FMB_STS_MASK SMB_MASK(3, 0) -#define USBID_GND_THRESHOLD 0x495 -static bool is_otg_present_schg(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - u8 usbid_reg[2]; - u16 usbid_val; - /* - * After the falling edge of the usbid change interrupt occurs, - * there may still be some time before the ADC conversion for USB RID - * finishes in the fuel gauge. In the worst case, this could be up to - * 15 ms. - * - * Sleep for 20 ms (minimum msleep time) to wait for the conversion to - * finish and the USB RID status register to be updated before trying - * to detect OTG insertions. - */ - - msleep(20); - - /* - * There is a problem with USBID conversions on PMI8994 revisions - * 2.0.0. As a workaround, check that the cable is not - * detected as factory test before enabling OTG. - */ - rc = smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read IDEV_STS rc = %d\n", rc); - return false; - } - - if ((reg & FMB_STS_MASK) != 0) { - pr_smb(PR_STATUS, "IDEV_STS = %02x, not ground\n", reg); - return false; - } - - rc = smbchg_read(chip, usbid_reg, chip->usb_chgpth_base + USBID_MSB, 2); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read USBID rc = %d\n", rc); - return false; - } - usbid_val = (usbid_reg[0] << 8) | usbid_reg[1]; - - if (usbid_val > USBID_GND_THRESHOLD) { - pr_smb(PR_STATUS, "USBID = 0x%04x, too high to be ground\n", - usbid_val); - return false; - } - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RID_STS, 1); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't read usb rid status rc = %d\n", rc); - return false; - } - - pr_smb(PR_STATUS, "RID_STS = %02x\n", reg); - - return (reg & RID_MASK) == 0; -} - -#define RID_GND_DET_STS BIT(2) -static bool is_otg_present_schg_lite(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->otg_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't read otg RT status rc = %d\n", rc); - return false; - } - - return !!(reg & RID_GND_DET_STS); -} - -static bool is_otg_present(struct smbchg_chip *chip) -{ - if (chip->schg_version == QPNP_SCHG_LITE) - return is_otg_present_schg_lite(chip); - - return is_otg_present_schg(chip); -} - -#define USBIN_9V BIT(5) -#define USBIN_UNREG BIT(4) -#define USBIN_LV BIT(3) -#define DCIN_9V BIT(2) -#define DCIN_UNREG BIT(1) -#define DCIN_LV BIT(0) -#define INPUT_STS 0x0D -#define DCIN_UV_BIT BIT(0) -#define DCIN_OV_BIT BIT(1) -static bool is_dc_present(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->dc_chgpth_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read dc status rc = %d\n", rc); - return false; - } - - if ((reg & DCIN_UV_BIT) || (reg & DCIN_OV_BIT)) - return false; - - return true; -} - -static bool is_usb_present(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc); - return false; - } - if (!(reg & USBIN_SRC_DET_BIT) || (reg & USBIN_OV_BIT)) - return false; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + INPUT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc); - return false; - } - - return !!(reg & (USBIN_9V | USBIN_UNREG | USBIN_LV)); -} - -static char *usb_type_str[] = { - "SDP", /* bit 0 */ - "OTHER", /* bit 1 */ - "DCP", /* bit 2 */ - "CDP", /* bit 3 */ - "NONE", /* bit 4 error case */ -}; - -#define N_TYPE_BITS 4 -#define TYPE_BITS_OFFSET 4 - -static int get_type(u8 type_reg) -{ - unsigned long type = type_reg; - type >>= TYPE_BITS_OFFSET; - return find_first_bit(&type, N_TYPE_BITS); -} - -/* helper to return the string of USB type */ -static inline char *get_usb_type_name(int type) -{ - return usb_type_str[type]; -} - -static enum power_supply_type usb_type_enum[] = { - POWER_SUPPLY_TYPE_USB, /* bit 0 */ - POWER_SUPPLY_TYPE_USB_DCP, /* bit 1 */ - POWER_SUPPLY_TYPE_USB_DCP, /* bit 2 */ - POWER_SUPPLY_TYPE_USB_CDP, /* bit 3 */ - POWER_SUPPLY_TYPE_USB_DCP, /* bit 4 error case, report DCP */ -}; - -/* helper to return enum power_supply_type of USB type */ -static inline enum power_supply_type get_usb_supply_type(int type) -{ - return usb_type_enum[type]; -} - -static bool is_src_detect_high(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc); - return false; - } - return reg &= USBIN_SRC_DET_BIT; -} - -static void read_usb_type(struct smbchg_chip *chip, char **usb_type_name, - enum power_supply_type *usb_supply_type) -{ - int rc, type; - u8 reg; - - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "src det low\n"); - *usb_type_name = "Absent"; - *usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN; - return; - } - - rc = smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc); - *usb_type_name = "Other"; - *usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN; - return; - } - type = get_type(reg); - *usb_type_name = get_usb_type_name(type); - *usb_supply_type = get_usb_supply_type(type); -} - -#define CHGR_STS 0x0E -#define BATT_LESS_THAN_2V BIT(4) -#define CHG_HOLD_OFF_BIT BIT(3) -#define CHG_TYPE_MASK SMB_MASK(2, 1) -#define CHG_TYPE_SHIFT 1 -#define BATT_NOT_CHG_VAL 0x0 -#define BATT_PRE_CHG_VAL 0x1 -#define BATT_FAST_CHG_VAL 0x2 -#define BATT_TAPER_CHG_VAL 0x3 -#define CHG_INHIBIT_BIT BIT(1) -#define BAT_TCC_REACHED_BIT BIT(7) -static int get_prop_batt_status(struct smbchg_chip *chip) -{ - int rc, status = POWER_SUPPLY_STATUS_DISCHARGING; - u8 reg = 0, chg_type; - bool charger_present, chg_inhibit; - - charger_present = is_usb_present(chip) | is_dc_present(chip) | - chip->hvdcp_3_det_ignore_uv; - if (!charger_present) - return POWER_SUPPLY_STATUS_DISCHARGING; - - rc = smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc); - return POWER_SUPPLY_STATUS_UNKNOWN; - } - - if (reg & BAT_TCC_REACHED_BIT) - return POWER_SUPPLY_STATUS_FULL; - - chg_inhibit = reg & CHG_INHIBIT_BIT; - if (chg_inhibit) - return POWER_SUPPLY_STATUS_FULL; - - rc = smbchg_read(chip, ®, chip->chgr_base + CHGR_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc); - return POWER_SUPPLY_STATUS_UNKNOWN; - } - - if (reg & CHG_HOLD_OFF_BIT) { - /* - * when chg hold off happens the battery is - * not charging - */ - status = POWER_SUPPLY_STATUS_NOT_CHARGING; - goto out; - } - - chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT; - - if (chg_type == BATT_NOT_CHG_VAL && !chip->hvdcp_3_det_ignore_uv) - status = POWER_SUPPLY_STATUS_DISCHARGING; - else - status = POWER_SUPPLY_STATUS_CHARGING; -out: - pr_smb_rt(PR_MISC, "CHGR_STS = 0x%02x\n", reg); - return status; -} - -#define BAT_PRES_STATUS 0x08 -#define BAT_PRES_BIT BIT(7) -static int get_prop_batt_present(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->bat_if_base + BAT_PRES_STATUS, 1); - if (rc < 0) { - dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc); - return 0; - } - - return !!(reg & BAT_PRES_BIT); -} - -static int get_prop_charge_type(struct smbchg_chip *chip) -{ - int rc; - u8 reg, chg_type; - - rc = smbchg_read(chip, ®, chip->chgr_base + CHGR_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc); - return 0; - } - - chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT; - if (chg_type == BATT_NOT_CHG_VAL) - return POWER_SUPPLY_CHARGE_TYPE_NONE; - else if (chg_type == BATT_TAPER_CHG_VAL) - return POWER_SUPPLY_CHARGE_TYPE_TAPER; - else if (chg_type == BATT_FAST_CHG_VAL) - return POWER_SUPPLY_CHARGE_TYPE_FAST; - else if (chg_type == BATT_PRE_CHG_VAL) - return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; - - return POWER_SUPPLY_CHARGE_TYPE_NONE; -} - -static int set_property_on_fg(struct smbchg_chip *chip, - enum power_supply_property prop, int val) -{ - int rc; - union power_supply_propval ret = {0, }; - - if (!chip->bms_psy && chip->bms_psy_name) - chip->bms_psy = - power_supply_get_by_name((char *)chip->bms_psy_name); - if (!chip->bms_psy) { - pr_smb(PR_STATUS, "no bms psy found\n"); - return -EINVAL; - } - - ret.intval = val; - rc = power_supply_set_property(chip->bms_psy, prop, &ret); - if (rc) - pr_smb(PR_STATUS, - "bms psy does not allow updating prop %d rc = %d\n", - prop, rc); - - return rc; -} - -static int get_property_from_fg(struct smbchg_chip *chip, - enum power_supply_property prop, int *val) -{ - int rc; - union power_supply_propval ret = {0, }; - - if (!chip->bms_psy && chip->bms_psy_name) - chip->bms_psy = - power_supply_get_by_name((char *)chip->bms_psy_name); - if (!chip->bms_psy) { - pr_smb(PR_STATUS, "no bms psy found\n"); - return -EINVAL; - } - - rc = power_supply_get_property(chip->bms_psy, prop, &ret); - if (rc) { - pr_smb(PR_STATUS, - "bms psy doesn't support reading prop %d rc = %d\n", - prop, rc); - return rc; - } - - *val = ret.intval; - return rc; -} - -#define DEFAULT_BATT_CAPACITY 50 -static int get_prop_batt_capacity(struct smbchg_chip *chip) -{ - int capacity, rc; - - if (chip->fake_battery_soc >= 0) - return chip->fake_battery_soc; - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CAPACITY, &capacity); - if (rc) { - pr_smb(PR_STATUS, "Couldn't get capacity rc = %d\n", rc); - capacity = DEFAULT_BATT_CAPACITY; - } - return capacity; -} - -#define DEFAULT_BATT_TEMP 200 -static int get_prop_batt_temp(struct smbchg_chip *chip) -{ - int temp, rc; - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_TEMP, &temp); - if (rc) { - pr_smb(PR_STATUS, "Couldn't get temperature rc = %d\n", rc); - temp = DEFAULT_BATT_TEMP; - } - return temp; -} - -#define DEFAULT_BATT_CURRENT_NOW 0 -static int get_prop_batt_current_now(struct smbchg_chip *chip) -{ - int ua, rc; - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW, &ua); - if (rc) { - pr_smb(PR_STATUS, "Couldn't get current rc = %d\n", rc); - ua = DEFAULT_BATT_CURRENT_NOW; - } - return ua; -} - -#define DEFAULT_BATT_VOLTAGE_NOW 0 -static int get_prop_batt_voltage_now(struct smbchg_chip *chip) -{ - int uv, rc; - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_NOW, &uv); - if (rc) { - pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc); - uv = DEFAULT_BATT_VOLTAGE_NOW; - } - return uv; -} - -#define DEFAULT_BATT_VOLTAGE_MAX_DESIGN 4200000 -static int get_prop_batt_voltage_max_design(struct smbchg_chip *chip) -{ - int uv, rc; - - rc = get_property_from_fg(chip, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, &uv); - if (rc) { - pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc); - uv = DEFAULT_BATT_VOLTAGE_MAX_DESIGN; - } - return uv; -} - -static int get_prop_batt_health(struct smbchg_chip *chip) -{ - if (chip->batt_hot) - return POWER_SUPPLY_HEALTH_OVERHEAT; - else if (chip->batt_cold) - return POWER_SUPPLY_HEALTH_COLD; - else if (chip->batt_warm) - return POWER_SUPPLY_HEALTH_WARM; - else if (chip->batt_cool) - return POWER_SUPPLY_HEALTH_COOL; - else - return POWER_SUPPLY_HEALTH_GOOD; -} - -static void get_property_from_typec(struct smbchg_chip *chip, - enum power_supply_property property, - union power_supply_propval *prop) -{ - int rc; - - rc = power_supply_get_property(chip->typec_psy, - property, prop); - if (rc) - pr_smb(PR_TYPEC, - "typec psy doesn't support reading prop %d rc = %d\n", - property, rc); -} - -static void update_typec_status(struct smbchg_chip *chip) -{ - union power_supply_propval type = {0, }; - union power_supply_propval capability = {0, }; - - get_property_from_typec(chip, POWER_SUPPLY_PROP_TYPE, &type); - if (type.intval != POWER_SUPPLY_TYPE_UNKNOWN) { - get_property_from_typec(chip, - POWER_SUPPLY_PROP_CURRENT_CAPABILITY, - &capability); - chip->typec_current_ma = capability.intval; - pr_smb(PR_TYPEC, "SMB Type-C mode = %d, current=%d\n", - type.intval, capability.intval); - } else { - pr_smb(PR_TYPEC, - "typec detection not completed continuing with USB update\n"); - } -} - -/* - * finds the index of the closest value in the array. If there are two that - * are equally close, the lower index will be returned - */ -static int find_closest_in_array(const int *arr, int len, int val) -{ - int i, closest = 0; - - if (len == 0) - return closest; - for (i = 0; i < len; i++) - if (abs(val - arr[i]) < abs(val - arr[closest])) - closest = i; - - return closest; -} - -/* finds the index of the closest smaller value in the array. */ -static int find_smaller_in_array(const int *table, int val, int len) -{ - int i; - - for (i = len - 1; i >= 0; i--) { - if (val >= table[i]) - break; - } - - return i; -} - -static const int iterm_ma_table_8994[] = { - 300, - 50, - 100, - 150, - 200, - 250, - 500, - 600 -}; - -static const int iterm_ma_table_8996[] = { - 300, - 50, - 100, - 150, - 200, - 250, - 400, - 500 -}; - -static const int usb_ilim_ma_table_8994[] = { - 300, - 400, - 450, - 475, - 500, - 550, - 600, - 650, - 700, - 900, - 950, - 1000, - 1100, - 1200, - 1400, - 1450, - 1500, - 1600, - 1800, - 1850, - 1880, - 1910, - 1930, - 1950, - 1970, - 2000, - 2050, - 2100, - 2300, - 2400, - 2500, - 3000 -}; - -static const int usb_ilim_ma_table_8996[] = { - 300, - 400, - 500, - 600, - 700, - 800, - 900, - 1000, - 1100, - 1200, - 1300, - 1400, - 1450, - 1500, - 1550, - 1600, - 1700, - 1800, - 1900, - 1950, - 2000, - 2050, - 2100, - 2200, - 2300, - 2400, - 2500, - 2600, - 2700, - 2800, - 2900, - 3000 -}; - -static int dc_ilim_ma_table_8994[] = { - 300, - 400, - 450, - 475, - 500, - 550, - 600, - 650, - 700, - 900, - 950, - 1000, - 1100, - 1200, - 1400, - 1450, - 1500, - 1600, - 1800, - 1850, - 1880, - 1910, - 1930, - 1950, - 1970, - 2000, -}; - -static int dc_ilim_ma_table_8996[] = { - 300, - 400, - 500, - 600, - 700, - 800, - 900, - 1000, - 1100, - 1200, - 1300, - 1400, - 1450, - 1500, - 1550, - 1600, - 1700, - 1800, - 1900, - 1950, - 2000, - 2050, - 2100, - 2200, - 2300, - 2400, -}; - -static const int fcc_comp_table_8994[] = { - 250, - 700, - 900, - 1200, -}; - -static const int fcc_comp_table_8996[] = { - 250, - 1100, - 1200, - 1500, -}; - -static const int aicl_rerun_period[] = { - 45, - 90, - 180, - 360, -}; - -static const int aicl_rerun_period_schg_lite[] = { - 3, /* 2.8s */ - 6, /* 5.6s */ - 11, /* 11.3s */ - 23, /* 22.5s */ - 45, - 90, - 180, - 360, -}; - -static void use_pmi8994_tables(struct smbchg_chip *chip) -{ - chip->tables.usb_ilim_ma_table = usb_ilim_ma_table_8994; - chip->tables.usb_ilim_ma_len = ARRAY_SIZE(usb_ilim_ma_table_8994); - chip->tables.dc_ilim_ma_table = dc_ilim_ma_table_8994; - chip->tables.dc_ilim_ma_len = ARRAY_SIZE(dc_ilim_ma_table_8994); - chip->tables.iterm_ma_table = iterm_ma_table_8994; - chip->tables.iterm_ma_len = ARRAY_SIZE(iterm_ma_table_8994); - chip->tables.fcc_comp_table = fcc_comp_table_8994; - chip->tables.fcc_comp_len = ARRAY_SIZE(fcc_comp_table_8994); - chip->tables.rchg_thr_mv = 200; - chip->tables.aicl_rerun_period_table = aicl_rerun_period; - chip->tables.aicl_rerun_period_len = ARRAY_SIZE(aicl_rerun_period); -} - -static void use_pmi8996_tables(struct smbchg_chip *chip) -{ - chip->tables.usb_ilim_ma_table = usb_ilim_ma_table_8996; - chip->tables.usb_ilim_ma_len = ARRAY_SIZE(usb_ilim_ma_table_8996); - chip->tables.dc_ilim_ma_table = dc_ilim_ma_table_8996; - chip->tables.dc_ilim_ma_len = ARRAY_SIZE(dc_ilim_ma_table_8996); - chip->tables.iterm_ma_table = iterm_ma_table_8996; - chip->tables.iterm_ma_len = ARRAY_SIZE(iterm_ma_table_8996); - chip->tables.fcc_comp_table = fcc_comp_table_8996; - chip->tables.fcc_comp_len = ARRAY_SIZE(fcc_comp_table_8996); - chip->tables.rchg_thr_mv = 150; - chip->tables.aicl_rerun_period_table = aicl_rerun_period; - chip->tables.aicl_rerun_period_len = ARRAY_SIZE(aicl_rerun_period); -} - -#define CMD_CHG_REG 0x42 -#define EN_BAT_CHG_BIT BIT(1) -static int smbchg_charging_en(struct smbchg_chip *chip, bool en) -{ - /* The en bit is configured active low */ - return smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG, - EN_BAT_CHG_BIT, en ? 0 : EN_BAT_CHG_BIT); -} - -#define CMD_IL 0x40 -#define USBIN_SUSPEND_BIT BIT(4) -#define CURRENT_100_MA 100 -#define CURRENT_150_MA 150 -#define CURRENT_500_MA 500 -#define CURRENT_900_MA 900 -#define CURRENT_1500_MA 1500 -#define SUSPEND_CURRENT_MA 2 -#define ICL_OVERRIDE_BIT BIT(2) -static int smbchg_usb_suspend(struct smbchg_chip *chip, bool suspend) -{ - int rc; - - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - USBIN_SUSPEND_BIT, suspend ? USBIN_SUSPEND_BIT : 0); - if (rc < 0) - dev_err(chip->dev, "Couldn't set usb suspend rc = %d\n", rc); - return rc; -} - -#define DCIN_SUSPEND_BIT BIT(3) -static int smbchg_dc_suspend(struct smbchg_chip *chip, bool suspend) -{ - int rc = 0; - - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - DCIN_SUSPEND_BIT, suspend ? DCIN_SUSPEND_BIT : 0); - if (rc < 0) - dev_err(chip->dev, "Couldn't set dc suspend rc = %d\n", rc); - return rc; -} - -#define IL_CFG 0xF2 -#define DCIN_INPUT_MASK SMB_MASK(4, 0) -static int smbchg_set_dc_current_max(struct smbchg_chip *chip, int current_ma) -{ - int i; - u8 dc_cur_val; - - i = find_smaller_in_array(chip->tables.dc_ilim_ma_table, - current_ma, chip->tables.dc_ilim_ma_len); - - if (i < 0) { - dev_err(chip->dev, "Cannot find %dma current_table\n", - current_ma); - return -EINVAL; - } - - chip->dc_max_current_ma = chip->tables.dc_ilim_ma_table[i]; - dc_cur_val = i & DCIN_INPUT_MASK; - - pr_smb(PR_STATUS, "dc current set to %d mA\n", - chip->dc_max_current_ma); - return smbchg_sec_masked_write(chip, chip->dc_chgpth_base + IL_CFG, - DCIN_INPUT_MASK, dc_cur_val); -} - -#define AICL_WL_SEL_CFG 0xF5 -#define AICL_WL_SEL_MASK SMB_MASK(1, 0) -#define AICL_WL_SEL_SCHG_LITE_MASK SMB_MASK(2, 0) -static int smbchg_set_aicl_rerun_period_s(struct smbchg_chip *chip, - int period_s) -{ - int i; - u8 reg, mask; - - i = find_smaller_in_array(chip->tables.aicl_rerun_period_table, - period_s, chip->tables.aicl_rerun_period_len); - - if (i < 0) { - dev_err(chip->dev, "Cannot find %ds in aicl rerun period\n", - period_s); - return -EINVAL; - } - - if (chip->schg_version == QPNP_SCHG_LITE) - mask = AICL_WL_SEL_SCHG_LITE_MASK; - else - mask = AICL_WL_SEL_MASK; - - reg = i & mask; - - pr_smb(PR_STATUS, "aicl rerun period set to %ds\n", - chip->tables.aicl_rerun_period_table[i]); - return smbchg_sec_masked_write(chip, - chip->dc_chgpth_base + AICL_WL_SEL_CFG, - mask, reg); -} - -static struct power_supply *get_parallel_psy(struct smbchg_chip *chip) -{ - if (!chip->parallel.avail) - return NULL; - if (chip->parallel.psy) - return chip->parallel.psy; - chip->parallel.psy = power_supply_get_by_name("usb-parallel"); - if (!chip->parallel.psy) - pr_smb(PR_STATUS, "parallel charger not found\n"); - return chip->parallel.psy; -} - -static void smbchg_usb_update_online_work(struct work_struct *work) -{ - struct smbchg_chip *chip = container_of(work, - struct smbchg_chip, - usb_set_online_work); - bool user_enabled = !get_client_vote(chip->usb_suspend_votable, - USER_EN_VOTER); - int online; - - online = user_enabled && chip->usb_present && !chip->very_weak_charger; - - mutex_lock(&chip->usb_set_online_lock); - if (chip->usb_online != online) { - pr_smb(PR_MISC, "setting usb psy online = %d\n", online); - chip->usb_online = online; - power_supply_changed(chip->usb_psy); - } - mutex_unlock(&chip->usb_set_online_lock); -} - -#define CHGPTH_CFG 0xF4 -#define CFG_USB_2_3_SEL_BIT BIT(7) -#define CFG_USB_2 0 -#define CFG_USB_3 BIT(7) -#define USBIN_INPUT_MASK SMB_MASK(4, 0) -#define USBIN_MODE_CHG_BIT BIT(0) -#define USBIN_LIMITED_MODE 0 -#define USBIN_HC_MODE BIT(0) -#define USB51_MODE_BIT BIT(1) -#define USB51_100MA 0 -#define USB51_500MA BIT(1) -static int smbchg_set_high_usb_chg_current(struct smbchg_chip *chip, - int current_ma) -{ - int i, rc; - u8 usb_cur_val; - - if (current_ma == CURRENT_100_MA) { - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - CFG_USB_2_3_SEL_BIT, CFG_USB_2); - if (rc < 0) { - pr_err("Couldn't set CFG_USB_2 rc=%d\n", rc); - return rc; - } - - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT | USB51_MODE_BIT | ICL_OVERRIDE_BIT, - USBIN_LIMITED_MODE | USB51_100MA | ICL_OVERRIDE_BIT); - if (rc < 0) { - pr_err("Couldn't set ICL_OVERRIDE rc=%d\n", rc); - return rc; - } - - pr_smb(PR_STATUS, - "Forcing 100mA current limit\n"); - chip->usb_max_current_ma = CURRENT_100_MA; - return rc; - } - - i = find_smaller_in_array(chip->tables.usb_ilim_ma_table, - current_ma, chip->tables.usb_ilim_ma_len); - if (i < 0) { - dev_err(chip->dev, - "Cannot find %dma current_table using %d\n", - current_ma, CURRENT_150_MA); - - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - CFG_USB_2_3_SEL_BIT, CFG_USB_3); - rc |= smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT | USB51_MODE_BIT, - USBIN_LIMITED_MODE | USB51_100MA); - if (rc < 0) - dev_err(chip->dev, "Couldn't set %dmA rc=%d\n", - CURRENT_150_MA, rc); - else - chip->usb_max_current_ma = 150; - return rc; - } - - usb_cur_val = i & USBIN_INPUT_MASK; - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + IL_CFG, - USBIN_INPUT_MASK, usb_cur_val); - if (rc < 0) { - dev_err(chip->dev, "cannot write to config c rc = %d\n", rc); - return rc; - } - - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT, USBIN_HC_MODE); - if (rc < 0) - dev_err(chip->dev, "Couldn't write cfg 5 rc = %d\n", rc); - chip->usb_max_current_ma = chip->tables.usb_ilim_ma_table[i]; - return rc; -} - -/* if APSD results are used - * if SDP is detected it will look at 500mA setting - * if set it will draw 500mA - * if unset it will draw 100mA - * if CDP/DCP it will look at 0x0C setting - * i.e. values in 0x41[1, 0] does not matter - */ -static int smbchg_set_usb_current_max(struct smbchg_chip *chip, - int current_ma) -{ - int rc = 0; - - /* - * if the battery is not present, do not allow the usb ICL to lower in - * order to avoid browning out the device during a hotswap. - */ - if (!chip->batt_present && current_ma < chip->usb_max_current_ma) { - pr_info_ratelimited("Ignoring usb current->%d, battery is absent\n", - current_ma); - return 0; - } - pr_smb(PR_STATUS, "USB current_ma = %d\n", current_ma); - - if (current_ma <= SUSPEND_CURRENT_MA) { - /* suspend the usb if current <= 2mA */ - rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, true, 0); - chip->usb_max_current_ma = 0; - goto out; - } else { - rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, false, 0); - } - - switch (chip->usb_supply_type) { - case POWER_SUPPLY_TYPE_USB: - if ((current_ma < CURRENT_150_MA) && - (chip->wa_flags & SMBCHG_USB100_WA)) - current_ma = CURRENT_150_MA; - - if (current_ma < CURRENT_150_MA) { - /* force 100mA */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - CFG_USB_2_3_SEL_BIT, CFG_USB_2); - if (rc < 0) { - pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc); - goto out; - } - rc = smbchg_masked_write(chip, - chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT | USB51_MODE_BIT, - USBIN_LIMITED_MODE | USB51_100MA); - if (rc < 0) { - pr_err("Couldn't set CMD_IL rc = %d\n", rc); - goto out; - } - chip->usb_max_current_ma = 100; - } - /* specific current values */ - if (current_ma == CURRENT_150_MA) { - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - CFG_USB_2_3_SEL_BIT, CFG_USB_3); - if (rc < 0) { - pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc); - goto out; - } - rc = smbchg_masked_write(chip, - chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT | USB51_MODE_BIT, - USBIN_LIMITED_MODE | USB51_100MA); - if (rc < 0) { - pr_err("Couldn't set CMD_IL rc = %d\n", rc); - goto out; - } - chip->usb_max_current_ma = 150; - } - if (current_ma == CURRENT_500_MA) { - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - CFG_USB_2_3_SEL_BIT, CFG_USB_2); - if (rc < 0) { - pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc); - goto out; - } - rc = smbchg_masked_write(chip, - chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT | USB51_MODE_BIT, - USBIN_LIMITED_MODE | USB51_500MA); - if (rc < 0) { - pr_err("Couldn't set CMD_IL rc = %d\n", rc); - goto out; - } - chip->usb_max_current_ma = 500; - } - if (current_ma == CURRENT_900_MA) { - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - CFG_USB_2_3_SEL_BIT, CFG_USB_3); - if (rc < 0) { - pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc); - goto out; - } - rc = smbchg_masked_write(chip, - chip->usb_chgpth_base + CMD_IL, - USBIN_MODE_CHG_BIT | USB51_MODE_BIT, - USBIN_LIMITED_MODE | USB51_500MA); - if (rc < 0) { - pr_err("Couldn't set CMD_IL rc = %d\n", rc); - goto out; - } - chip->usb_max_current_ma = 900; - } - break; - case POWER_SUPPLY_TYPE_USB_CDP: - if (current_ma < CURRENT_1500_MA) { - /* use override for CDP */ - rc = smbchg_masked_write(chip, - chip->usb_chgpth_base + CMD_IL, - ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT); - if (rc < 0) - pr_err("Couldn't set override rc = %d\n", rc); - } - /* fall through */ - default: - rc = smbchg_set_high_usb_chg_current(chip, current_ma); - if (rc < 0) - pr_err("Couldn't set %dmA rc = %d\n", current_ma, rc); - break; - } - -out: - pr_smb(PR_STATUS, "usb type = %d current set to %d mA\n", - chip->usb_supply_type, chip->usb_max_current_ma); - return rc; -} - -#define USBIN_HVDCP_STS 0x0C -#define USBIN_HVDCP_SEL_BIT BIT(4) -#define USBIN_HVDCP_SEL_9V_BIT BIT(1) -#define SCHG_LITE_USBIN_HVDCP_SEL_9V_BIT BIT(2) -#define SCHG_LITE_USBIN_HVDCP_SEL_BIT BIT(0) -static int smbchg_get_min_parallel_current_ma(struct smbchg_chip *chip) -{ - int rc; - u8 reg, hvdcp_sel, hvdcp_sel_9v; - - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + USBIN_HVDCP_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc); - return 0; - } - if (chip->schg_version == QPNP_SCHG_LITE) { - hvdcp_sel = SCHG_LITE_USBIN_HVDCP_SEL_BIT; - hvdcp_sel_9v = SCHG_LITE_USBIN_HVDCP_SEL_9V_BIT; - } else { - hvdcp_sel = USBIN_HVDCP_SEL_BIT; - hvdcp_sel_9v = USBIN_HVDCP_SEL_9V_BIT; - } - - if ((reg & hvdcp_sel) && (reg & hvdcp_sel_9v)) - return chip->parallel.min_9v_current_thr_ma; - return chip->parallel.min_current_thr_ma; -} - -static bool is_hvdcp_present(struct smbchg_chip *chip) -{ - int rc; - u8 reg, hvdcp_sel; - - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + USBIN_HVDCP_STS, 1); - if (rc < 0) { - pr_err("Couldn't read hvdcp status rc = %d\n", rc); - return false; - } - - pr_smb(PR_STATUS, "HVDCP_STS = 0x%02x\n", reg); - /* - * If a valid HVDCP is detected, notify it to the usb_psy only - * if USB is still present. - */ - if (chip->schg_version == QPNP_SCHG_LITE) - hvdcp_sel = SCHG_LITE_USBIN_HVDCP_SEL_BIT; - else - hvdcp_sel = USBIN_HVDCP_SEL_BIT; - - if ((reg & hvdcp_sel) && is_usb_present(chip)) - return true; - - return false; -} - -#define FCC_CFG 0xF2 -#define FCC_500MA_VAL 0x4 -#define FCC_MASK SMB_MASK(4, 0) -static int smbchg_set_fastchg_current_raw(struct smbchg_chip *chip, - int current_ma) -{ - int i, rc; - u8 cur_val; - - /* the fcc enumerations are the same as the usb currents */ - i = find_smaller_in_array(chip->tables.usb_ilim_ma_table, - current_ma, chip->tables.usb_ilim_ma_len); - if (i < 0) { - dev_err(chip->dev, - "Cannot find %dma current_table using %d\n", - current_ma, CURRENT_500_MA); - - rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG, - FCC_MASK, - FCC_500MA_VAL); - if (rc < 0) - dev_err(chip->dev, "Couldn't set %dmA rc=%d\n", - CURRENT_500_MA, rc); - else - chip->fastchg_current_ma = 500; - return rc; - } - - if (chip->tables.usb_ilim_ma_table[i] == chip->fastchg_current_ma) { - pr_smb(PR_STATUS, "skipping fastchg current request: %d\n", - chip->fastchg_current_ma); - return 0; - } - - cur_val = i & FCC_MASK; - rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG, - FCC_MASK, cur_val); - if (rc < 0) { - dev_err(chip->dev, "cannot write to fcc cfg rc = %d\n", rc); - return rc; - } - pr_smb(PR_STATUS, "fastcharge current requested %d, set to %d\n", - current_ma, chip->tables.usb_ilim_ma_table[cur_val]); - - chip->fastchg_current_ma = chip->tables.usb_ilim_ma_table[cur_val]; - return rc; -} - -#define ICL_STS_1_REG 0x7 -#define ICL_STS_2_REG 0x9 -#define ICL_STS_MASK 0x1F -#define AICL_SUSP_BIT BIT(6) -#define AICL_STS_BIT BIT(5) -#define USBIN_SUSPEND_STS_BIT BIT(3) -#define USBIN_ACTIVE_PWR_SRC_BIT BIT(1) -#define DCIN_ACTIVE_PWR_SRC_BIT BIT(0) -#define PARALLEL_REENABLE_TIMER_MS 1000 -#define PARALLEL_CHG_THRESHOLD_CURRENT 1800 -static bool smbchg_is_usbin_active_pwr_src(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + ICL_STS_2_REG, 1); - if (rc < 0) { - dev_err(chip->dev, "Could not read usb icl sts 2: %d\n", rc); - return false; - } - - return !(reg & USBIN_SUSPEND_STS_BIT) - && (reg & USBIN_ACTIVE_PWR_SRC_BIT); -} - -static int smbchg_parallel_usb_charging_en(struct smbchg_chip *chip, bool en) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - - if (!parallel_psy || !chip->parallel_charger_detected) - return 0; - - pval.intval = en; - return power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval); -} - -#define ESR_PULSE_CURRENT_DELTA_MA 200 -static int smbchg_sw_esr_pulse_en(struct smbchg_chip *chip, bool en) -{ - int rc, fg_current_now, icl_ma; - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW, - &fg_current_now); - if (rc) { - pr_smb(PR_STATUS, "bms psy does not support OCV\n"); - return 0; - } - - icl_ma = max(chip->iterm_ma + ESR_PULSE_CURRENT_DELTA_MA, - fg_current_now - ESR_PULSE_CURRENT_DELTA_MA); - rc = vote(chip->fcc_votable, ESR_PULSE_FCC_VOTER, en, icl_ma); - if (rc < 0) { - pr_err("Couldn't Vote FCC en = %d rc = %d\n", en, rc); - return rc; - } - rc = smbchg_parallel_usb_charging_en(chip, !en); - return rc; -} - -#define USB_AICL_CFG 0xF3 -#define AICL_EN_BIT BIT(2) -static void smbchg_rerun_aicl(struct smbchg_chip *chip) -{ - pr_smb(PR_STATUS, "Rerunning AICL...\n"); - smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, 0); - /* Add a delay so that AICL successfully clears */ - msleep(50); - smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, AICL_EN_BIT); -} - -static void taper_irq_en(struct smbchg_chip *chip, bool en) -{ - mutex_lock(&chip->taper_irq_lock); - if (en != chip->taper_irq_enabled) { - if (en) { - enable_irq(chip->taper_irq); - enable_irq_wake(chip->taper_irq); - } else { - disable_irq_wake(chip->taper_irq); - disable_irq_nosync(chip->taper_irq); - } - chip->taper_irq_enabled = en; - } - mutex_unlock(&chip->taper_irq_lock); -} - -static int smbchg_get_aicl_level_ma(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + ICL_STS_1_REG, 1); - if (rc < 0) { - dev_err(chip->dev, "Could not read usb icl sts 1: %d\n", rc); - return 0; - } - if (reg & AICL_SUSP_BIT) { - pr_warn("AICL suspended: %02x\n", reg); - return 0; - } - reg &= ICL_STS_MASK; - if (reg >= chip->tables.usb_ilim_ma_len) { - pr_warn("invalid AICL value: %02x\n", reg); - return 0; - } - return chip->tables.usb_ilim_ma_table[reg]; -} - -static void smbchg_parallel_usb_disable(struct smbchg_chip *chip) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - int fcc_ma, usb_icl_ma; - - if (!parallel_psy || !chip->parallel_charger_detected) - return; - pr_smb(PR_STATUS, "disabling parallel charger\n"); - chip->parallel.last_disabled = ktime_get_boottime(); - taper_irq_en(chip, false); - chip->parallel.initial_aicl_ma = 0; - chip->parallel.current_max_ma = 0; - pval.intval = SUSPEND_CURRENT_MA * 1000; - power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_CURRENT_MAX, - &pval); - - pval.intval = false; - power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_PRESENT, - &pval); - - fcc_ma = get_effective_result_locked(chip->fcc_votable); - usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable); - if (fcc_ma < 0) - pr_err("no voters for fcc, skip it\n"); - else - smbchg_set_fastchg_current_raw(chip, fcc_ma); - - if (usb_icl_ma < 0) - pr_err("no voters for usb_icl, skip it\n"); - else - smbchg_set_usb_current_max(chip, usb_icl_ma); - - smbchg_rerun_aicl(chip); -} - -#define PARALLEL_TAPER_MAX_TRIES 3 -#define PARALLEL_FCC_PERCENT_REDUCTION 75 -#define MINIMUM_PARALLEL_FCC_MA 500 -#define CHG_ERROR_BIT BIT(0) -#define BAT_TAPER_MODE_BIT BIT(6) -static void smbchg_parallel_usb_taper(struct smbchg_chip *chip) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - int parallel_fcc_ma, tries = 0; - u8 reg = 0; - - if (!parallel_psy || !chip->parallel_charger_detected) - return; - - smbchg_stay_awake(chip, PM_PARALLEL_TAPER); -try_again: - mutex_lock(&chip->parallel.lock); - if (chip->parallel.current_max_ma == 0) { - pr_smb(PR_STATUS, "Not parallel charging, skipping\n"); - goto done; - } - power_supply_get_property(parallel_psy, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); - tries += 1; - parallel_fcc_ma = pval.intval / 1000; - pr_smb(PR_STATUS, "try #%d parallel charger fcc = %d\n", - tries, parallel_fcc_ma); - if (parallel_fcc_ma < MINIMUM_PARALLEL_FCC_MA - || tries > PARALLEL_TAPER_MAX_TRIES) { - smbchg_parallel_usb_disable(chip); - goto done; - } - pval.intval = ((parallel_fcc_ma - * PARALLEL_FCC_PERCENT_REDUCTION) / 100); - pr_smb(PR_STATUS, "reducing FCC of parallel charger to %d\n", - pval.intval); - /* Change it to uA */ - pval.intval *= 1000; - power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); - /* - * sleep here for 100 ms in order to make sure the charger has a chance - * to go back into constant current charging - */ - mutex_unlock(&chip->parallel.lock); - msleep(100); - - mutex_lock(&chip->parallel.lock); - if (chip->parallel.current_max_ma == 0) { - pr_smb(PR_STATUS, "Not parallel charging, skipping\n"); - goto done; - } - smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1); - if (reg & BAT_TAPER_MODE_BIT) { - mutex_unlock(&chip->parallel.lock); - goto try_again; - } - taper_irq_en(chip, true); -done: - mutex_unlock(&chip->parallel.lock); - smbchg_relax(chip, PM_PARALLEL_TAPER); -} - -static void smbchg_parallel_usb_enable(struct smbchg_chip *chip, - int total_current_ma) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - int new_parallel_cl_ma, set_parallel_cl_ma, new_pmi_cl_ma, rc; - int current_table_index, target_icl_ma; - int fcc_ma, main_fastchg_current_ma; - int target_parallel_fcc_ma, supplied_parallel_fcc_ma; - int parallel_chg_fcc_percent; - - if (!parallel_psy || !chip->parallel_charger_detected) - return; - - pr_smb(PR_STATUS, "Attempting to enable parallel charger\n"); - pval.intval = chip->vfloat_mv + 50; - rc = power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set Vflt on parallel psy rc: %d\n", rc); - return; - } - /* Set USB ICL */ - target_icl_ma = get_effective_result_locked(chip->usb_icl_votable); - if (target_icl_ma < 0) { - pr_err("no voters for usb_icl, skip it\n"); - return; - } - new_parallel_cl_ma = total_current_ma - * (100 - smbchg_main_chg_icl_percent) / 100; - taper_irq_en(chip, true); - - pval.intval = true; - power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_PRESENT, - &pval); - - pval.intval = new_parallel_cl_ma * 1000; - power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_CURRENT_MAX, - &pval); - - /* read back the real amount of current we are getting */ - power_supply_get_property(parallel_psy, - POWER_SUPPLY_PROP_CURRENT_MAX, &pval); - set_parallel_cl_ma = pval.intval / 1000; - chip->parallel.current_max_ma = new_parallel_cl_ma; - pr_smb(PR_MISC, "Requested ICL = %d from parallel, got %d\n", - new_parallel_cl_ma, set_parallel_cl_ma); - new_pmi_cl_ma = max(0, target_icl_ma - set_parallel_cl_ma); - pr_smb(PR_STATUS, "New Total USB current = %d[%d, %d]\n", - total_current_ma, new_pmi_cl_ma, - set_parallel_cl_ma); - smbchg_set_usb_current_max(chip, new_pmi_cl_ma); - - /* begin splitting the fast charge current */ - fcc_ma = get_effective_result_locked(chip->fcc_votable); - if (fcc_ma < 0) { - pr_err("no voters for fcc, skip it\n"); - return; - } - parallel_chg_fcc_percent = 100 - smbchg_main_chg_fcc_percent; - target_parallel_fcc_ma = (fcc_ma * parallel_chg_fcc_percent) / 100; - pval.intval = target_parallel_fcc_ma * 1000; - power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); - /* check how much actual current is supplied by the parallel charger */ - power_supply_get_property(parallel_psy, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); - supplied_parallel_fcc_ma = pval.intval / 1000; - pr_smb(PR_MISC, "Requested FCC = %d from parallel, got %d\n", - target_parallel_fcc_ma, supplied_parallel_fcc_ma); - - /* then for the main charger, use the left over FCC */ - current_table_index = find_smaller_in_array( - chip->tables.usb_ilim_ma_table, - fcc_ma - supplied_parallel_fcc_ma, - chip->tables.usb_ilim_ma_len); - main_fastchg_current_ma = - chip->tables.usb_ilim_ma_table[current_table_index]; - smbchg_set_fastchg_current_raw(chip, main_fastchg_current_ma); - pr_smb(PR_STATUS, "FCC = %d[%d, %d]\n", fcc_ma, main_fastchg_current_ma, - supplied_parallel_fcc_ma); - - chip->parallel.enabled_once = true; - - return; -} - -static bool smbchg_is_parallel_usb_ok(struct smbchg_chip *chip, - int *ret_total_current_ma) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - int min_current_thr_ma, rc, type; - int total_current_ma, current_limit_ma, parallel_cl_ma; - ktime_t kt_since_last_disable; - u8 reg; - int fcc_ma = get_effective_result_locked(chip->fcc_votable); - const char *fcc_voter - = get_effective_client_locked(chip->fcc_votable); - int usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable); - - if (!parallel_psy || !smbchg_parallel_en - || !chip->parallel_charger_detected) { - pr_smb(PR_STATUS, "Parallel charging not enabled\n"); - return false; - } - - if (fcc_ma < 0) { - pr_err("no voters for fcc! Can't enable parallel\n"); - return false; - } - if (usb_icl_ma < 0) { - pr_err("no voters for usb_icl, Can't enable parallel\n"); - return false; - } - - kt_since_last_disable = ktime_sub(ktime_get_boottime(), - chip->parallel.last_disabled); - if (chip->parallel.current_max_ma == 0 - && chip->parallel.enabled_once - && ktime_to_ms(kt_since_last_disable) - < PARALLEL_REENABLE_TIMER_MS) { - pr_smb(PR_STATUS, "Only been %lld since disable, skipping\n", - ktime_to_ms(kt_since_last_disable)); - return false; - } - - /* - * If the battery is not present, try not to change parallel charging - * from OFF to ON or from ON to OFF, as it could cause the device to - * brown out in the instant that the USB settings are changed. - * - * Only allow parallel charging check to report false (thereby turnin - * off parallel charging) if the battery is still there, or if parallel - * charging is disabled in the first place. - */ - if (get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST - && (get_prop_batt_present(chip) - || chip->parallel.current_max_ma == 0)) { - pr_smb(PR_STATUS, "Not in fast charge, skipping\n"); - return false; - } - - if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) { - pr_smb(PR_STATUS, "JEITA active, skipping\n"); - return false; - } - - rc = smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc); - return false; - } - - type = get_type(reg); - if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB_CDP) { - pr_smb(PR_STATUS, "CDP adapter, skipping\n"); - return false; - } - - if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB) { - pr_smb(PR_STATUS, "SDP adapter, skipping\n"); - return false; - } - - /* - * If USBIN is suspended or not the active power source, do not enable - * parallel charging. The device may be charging off of DCIN. - */ - if (!smbchg_is_usbin_active_pwr_src(chip)) { - pr_smb(PR_STATUS, "USB not active power source: %02x\n", reg); - return false; - } - - min_current_thr_ma = smbchg_get_min_parallel_current_ma(chip); - if (min_current_thr_ma <= 0) { - pr_smb(PR_STATUS, "parallel charger unavailable for thr: %d\n", - min_current_thr_ma); - return false; - } - - if (usb_icl_ma < min_current_thr_ma) { - pr_smb(PR_STATUS, "Weak USB chg skip enable: %d < %d\n", - usb_icl_ma, min_current_thr_ma); - return false; - } - - if (!fcc_voter) - return false; - /* - * Suspend the parallel charger if the charging current is < 1800 mA - * and is not because of an ESR pulse. - */ - if ((strcmp(fcc_voter, ESR_PULSE_FCC_VOTER) == 0) - && fcc_ma < PARALLEL_CHG_THRESHOLD_CURRENT) { - pr_smb(PR_STATUS, "FCC %d lower than %d\n", - fcc_ma, - PARALLEL_CHG_THRESHOLD_CURRENT); - return false; - } - - current_limit_ma = smbchg_get_aicl_level_ma(chip); - if (current_limit_ma <= 0) - return false; - - if (chip->parallel.initial_aicl_ma == 0) { - if (current_limit_ma < min_current_thr_ma) { - pr_smb(PR_STATUS, "Initial AICL very low: %d < %d\n", - current_limit_ma, min_current_thr_ma); - return false; - } - chip->parallel.initial_aicl_ma = current_limit_ma; - } - - power_supply_get_property(parallel_psy, - POWER_SUPPLY_PROP_CURRENT_MAX, &pval); - parallel_cl_ma = pval.intval / 1000; - /* - * Read back the real amount of current we are getting - * Treat 2mA as 0 because that is the suspend current setting - */ - if (parallel_cl_ma <= SUSPEND_CURRENT_MA) - parallel_cl_ma = 0; - - /* - * Set the parallel charge path's input current limit (ICL) - * to the total current / 2 - */ - total_current_ma = min(current_limit_ma + parallel_cl_ma, usb_icl_ma); - - if (total_current_ma < chip->parallel.initial_aicl_ma - - chip->parallel.allowed_lowering_ma) { - pr_smb(PR_STATUS, - "Total current reduced a lot: %d (%d + %d) < %d - %d\n", - total_current_ma, - current_limit_ma, parallel_cl_ma, - chip->parallel.initial_aicl_ma, - chip->parallel.allowed_lowering_ma); - return false; - } - - *ret_total_current_ma = total_current_ma; - return true; -} - -#define PARALLEL_CHARGER_EN_DELAY_MS 500 -static void smbchg_parallel_usb_en_work(struct work_struct *work) -{ - struct smbchg_chip *chip = container_of(work, - struct smbchg_chip, - parallel_en_work.work); - int previous_aicl_ma, total_current_ma, aicl_ma; - bool in_progress; - - /* do a check to see if the aicl is stable */ - previous_aicl_ma = smbchg_get_aicl_level_ma(chip); - msleep(PARALLEL_CHARGER_EN_DELAY_MS); - aicl_ma = smbchg_get_aicl_level_ma(chip); - if (previous_aicl_ma == aicl_ma) { - pr_smb(PR_STATUS, "AICL at %d\n", aicl_ma); - } else { - pr_smb(PR_STATUS, - "AICL changed [%d -> %d], recheck %d ms\n", - previous_aicl_ma, aicl_ma, - PARALLEL_CHARGER_EN_DELAY_MS); - goto recheck; - } - - mutex_lock(&chip->parallel.lock); - in_progress = (chip->parallel.current_max_ma != 0); - if (smbchg_is_parallel_usb_ok(chip, &total_current_ma)) { - smbchg_parallel_usb_enable(chip, total_current_ma); - } else { - if (in_progress) { - pr_smb(PR_STATUS, "parallel charging unavailable\n"); - smbchg_parallel_usb_disable(chip); - } - } - mutex_unlock(&chip->parallel.lock); - smbchg_relax(chip, PM_PARALLEL_CHECK); - return; - -recheck: - schedule_delayed_work(&chip->parallel_en_work, 0); -} - -static void smbchg_parallel_usb_check_ok(struct smbchg_chip *chip) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - - if (!parallel_psy || !chip->parallel_charger_detected) - return; - - smbchg_stay_awake(chip, PM_PARALLEL_CHECK); - schedule_delayed_work(&chip->parallel_en_work, 0); -} - -static int charging_suspend_vote_cb(struct votable *votable, void *data, - int suspend, - const char *client) -{ - int rc; - struct smbchg_chip *chip = data; - - if (suspend < 0) { - pr_err("No voters\n"); - suspend = false; - } - - rc = smbchg_charging_en(chip, !suspend); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't configure batt chg: 0x%x rc = %d\n", - !suspend, rc); - } - - return rc; -} - -static int usb_suspend_vote_cb(struct votable *votable, - void *data, - int suspend, - const char *client) -{ - int rc; - struct smbchg_chip *chip = data; - - if (suspend < 0) { - pr_err("No voters\n"); - suspend = false; - } - - rc = smbchg_usb_suspend(chip, suspend); - if (rc < 0) - return rc; - - if ((strcmp(client, THERMAL_EN_VOTER) == 0) - || (strcmp(client, POWER_SUPPLY_EN_VOTER) == 0) - || (strcmp(client, USER_EN_VOTER) == 0) - || (strcmp(client, FAKE_BATTERY_EN_VOTER) == 0)) - smbchg_parallel_usb_check_ok(chip); - - return rc; -} - -static int dc_suspend_vote_cb(struct votable *votable, - void *data, - int suspend, - const char *client) -{ - int rc; - struct smbchg_chip *chip = data; - - if (suspend < 0) { - pr_err("No voters\n"); - suspend = false; - } - - rc = smbchg_dc_suspend(chip, suspend); - if (rc < 0) - return rc; - - if (chip->dc_psy_type != -EINVAL && chip->dc_psy) - power_supply_changed(chip->dc_psy); - - return rc; -} - -static int set_fastchg_current_vote_cb(struct votable *votable, - void *data, - int fcc_ma, - const char *client) -{ - struct smbchg_chip *chip = data; - int rc; - - if (fcc_ma < 0) { - pr_err("No voters\n"); - return 0; - } - - if (chip->parallel.current_max_ma == 0) { - rc = smbchg_set_fastchg_current_raw(chip, fcc_ma); - if (rc < 0) { - pr_err("Can't set FCC fcc_ma=%d rc=%d\n", fcc_ma, rc); - return rc; - } - } - /* - * check if parallel charging can be enabled, and if enabled, - * distribute the fcc - */ - smbchg_parallel_usb_check_ok(chip); - return 0; -} - -static int smbchg_set_fastchg_current_user(struct smbchg_chip *chip, - int current_ma) -{ - int rc = 0; - - pr_smb(PR_STATUS, "User setting FCC to %d\n", current_ma); - - rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true, current_ma); - if (rc < 0) - pr_err("Couldn't vote en rc %d\n", rc); - return rc; -} - -static struct ilim_entry *smbchg_wipower_find_entry(struct smbchg_chip *chip, - struct ilim_map *map, int uv) -{ - int i; - struct ilim_entry *ret = &(chip->wipower_default.entries[0]); - - for (i = 0; i < map->num; i++) { - if (is_between(map->entries[i].vmin_uv, map->entries[i].vmax_uv, - uv)) - ret = &map->entries[i]; - } - return ret; -} - -#define ZIN_ICL_PT 0xFC -#define ZIN_ICL_LV 0xFD -#define ZIN_ICL_HV 0xFE -#define ZIN_ICL_MASK SMB_MASK(4, 0) -static int smbchg_dcin_ilim_config(struct smbchg_chip *chip, int offset, int ma) -{ - int i, rc; - - i = find_smaller_in_array(chip->tables.dc_ilim_ma_table, - ma, chip->tables.dc_ilim_ma_len); - - if (i < 0) - i = 0; - - rc = smbchg_sec_masked_write(chip, chip->bat_if_base + offset, - ZIN_ICL_MASK, i); - if (rc) - dev_err(chip->dev, "Couldn't write bat if offset %d value = %d rc = %d\n", - offset, i, rc); - return rc; -} - -static int smbchg_wipower_ilim_config(struct smbchg_chip *chip, - struct ilim_entry *ilim) -{ - int rc = 0; - - if (chip->current_ilim.icl_pt_ma != ilim->icl_pt_ma) { - rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_PT, ilim->icl_pt_ma); - if (rc) - dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n", - ZIN_ICL_PT, ilim->icl_pt_ma, rc); - else - chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma; - } - - if (chip->current_ilim.icl_lv_ma != ilim->icl_lv_ma) { - rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_LV, ilim->icl_lv_ma); - if (rc) - dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n", - ZIN_ICL_LV, ilim->icl_lv_ma, rc); - else - chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma; - } - - if (chip->current_ilim.icl_hv_ma != ilim->icl_hv_ma) { - rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_HV, ilim->icl_hv_ma); - if (rc) - dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n", - ZIN_ICL_HV, ilim->icl_hv_ma, rc); - else - chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma; - } - return rc; -} - -static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx); -static int smbchg_wipower_dcin_btm_configure(struct smbchg_chip *chip, - struct ilim_entry *ilim) -{ - int rc; - - if (ilim->vmin_uv == chip->current_ilim.vmin_uv - && ilim->vmax_uv == chip->current_ilim.vmax_uv) - return 0; - - chip->param.channel = DCIN; - chip->param.btm_ctx = chip; - if (wipower_dcin_interval < ADC_MEAS1_INTERVAL_0MS) - wipower_dcin_interval = ADC_MEAS1_INTERVAL_0MS; - - if (wipower_dcin_interval > ADC_MEAS1_INTERVAL_16S) - wipower_dcin_interval = ADC_MEAS1_INTERVAL_16S; - - chip->param.timer_interval = wipower_dcin_interval; - chip->param.threshold_notification = &btm_notify_dcin; - chip->param.high_thr = ilim->vmax_uv + wipower_dcin_hyst_uv; - chip->param.low_thr = ilim->vmin_uv - wipower_dcin_hyst_uv; - chip->param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE; - rc = qpnp_vadc_channel_monitor(chip->vadc_dev, &chip->param); - if (rc) { - dev_err(chip->dev, "Couldn't configure btm for dcin rc = %d\n", - rc); - } else { - chip->current_ilim.vmin_uv = ilim->vmin_uv; - chip->current_ilim.vmax_uv = ilim->vmax_uv; - pr_smb(PR_STATUS, "btm ilim = (%duV %duV %dmA %dmA %dmA)\n", - ilim->vmin_uv, ilim->vmax_uv, - ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma); - } - return rc; -} - -static int smbchg_wipower_icl_configure(struct smbchg_chip *chip, - int dcin_uv, bool div2) -{ - int rc = 0; - struct ilim_map *map = div2 ? &chip->wipower_div2 : &chip->wipower_pt; - struct ilim_entry *ilim = smbchg_wipower_find_entry(chip, map, dcin_uv); - - rc = smbchg_wipower_ilim_config(chip, ilim); - if (rc) { - dev_err(chip->dev, "failed to config ilim rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n", - rc, dcin_uv, div2, - ilim->vmin_uv, ilim->vmax_uv, - ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma); - return rc; - } - - rc = smbchg_wipower_dcin_btm_configure(chip, ilim); - if (rc) { - dev_err(chip->dev, "failed to config btm rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n", - rc, dcin_uv, div2, - ilim->vmin_uv, ilim->vmax_uv, - ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma); - return rc; - } - chip->wipower_configured = true; - return 0; -} - -static void smbchg_wipower_icl_deconfigure(struct smbchg_chip *chip) -{ - int rc; - struct ilim_entry *ilim = &(chip->wipower_default.entries[0]); - - if (!chip->wipower_configured) - return; - - rc = smbchg_wipower_ilim_config(chip, ilim); - if (rc) - dev_err(chip->dev, "Couldn't config default ilim rc = %d\n", - rc); - - rc = qpnp_vadc_end_channel_monitor(chip->vadc_dev); - if (rc) - dev_err(chip->dev, "Couldn't de configure btm for dcin rc = %d\n", - rc); - - chip->wipower_configured = false; - chip->current_ilim.vmin_uv = 0; - chip->current_ilim.vmax_uv = 0; - chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma; - chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma; - chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma; - pr_smb(PR_WIPOWER, "De config btm\n"); -} - -#define FV_STS 0x0C -#define DIV2_ACTIVE BIT(7) -static void __smbchg_wipower_check(struct smbchg_chip *chip) -{ - int chg_type; - bool usb_present, dc_present; - int rc; - int dcin_uv; - bool div2; - struct qpnp_vadc_result adc_result; - u8 reg; - - if (!wipower_dyn_icl_en) { - smbchg_wipower_icl_deconfigure(chip); - return; - } - - chg_type = get_prop_charge_type(chip); - usb_present = is_usb_present(chip); - dc_present = is_dc_present(chip); - if (chg_type != POWER_SUPPLY_CHARGE_TYPE_NONE - && !usb_present - && dc_present - && chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER) { - rc = qpnp_vadc_read(chip->vadc_dev, DCIN, &adc_result); - if (rc) { - pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc); - return; - } - dcin_uv = adc_result.physical; - - /* check div_by_2 */ - rc = smbchg_read(chip, ®, chip->chgr_base + FV_STS, 1); - if (rc) { - pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc); - return; - } - div2 = !!(reg & DIV2_ACTIVE); - - pr_smb(PR_WIPOWER, - "config ICL chg_type = %d usb = %d dc = %d dcin_uv(adc_code) = %d (0x%x) div2 = %d\n", - chg_type, usb_present, dc_present, dcin_uv, - adc_result.adc_code, div2); - smbchg_wipower_icl_configure(chip, dcin_uv, div2); - } else { - pr_smb(PR_WIPOWER, - "deconfig ICL chg_type = %d usb = %d dc = %d\n", - chg_type, usb_present, dc_present); - smbchg_wipower_icl_deconfigure(chip); - } -} - -static void smbchg_wipower_check(struct smbchg_chip *chip) -{ - if (!chip->wipower_dyn_icl_avail) - return; - - mutex_lock(&chip->wipower_config); - __smbchg_wipower_check(chip); - mutex_unlock(&chip->wipower_config); -} - -static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx) -{ - struct smbchg_chip *chip = ctx; - - mutex_lock(&chip->wipower_config); - pr_smb(PR_WIPOWER, "%s state\n", - state == ADC_TM_LOW_STATE ? "low" : "high"); - chip->current_ilim.vmin_uv = 0; - chip->current_ilim.vmax_uv = 0; - __smbchg_wipower_check(chip); - mutex_unlock(&chip->wipower_config); -} - -static int force_dcin_icl_write(void *data, u64 val) -{ - struct smbchg_chip *chip = data; - - smbchg_wipower_check(chip); - return 0; -} -DEFINE_SIMPLE_ATTRIBUTE(force_dcin_icl_ops, NULL, - force_dcin_icl_write, "0x%02llx\n"); - -/* - * set the dc charge path's maximum allowed current draw - * that may be limited by the system's thermal level - */ -static int set_dc_current_limit_vote_cb(struct votable *votable, - void *data, - int icl_ma, - const char *client) -{ - struct smbchg_chip *chip = data; - - if (icl_ma < 0) { - pr_err("No voters\n"); - return 0; - } - - return smbchg_set_dc_current_max(chip, icl_ma); -} - -/* - * set the usb charge path's maximum allowed current draw - * that may be limited by the system's thermal level - */ -static int set_usb_current_limit_vote_cb(struct votable *votable, - void *data, - int icl_ma, - const char *client) -{ - struct smbchg_chip *chip = data; - int rc, aicl_ma; - const char *effective_id; - - if (icl_ma < 0) { - pr_err("No voters\n"); - return 0; - } - effective_id = get_effective_client_locked(chip->usb_icl_votable); - - if (!effective_id) - return 0; - - /* disable parallel charging if HVDCP is voting for 300mA */ - if (strcmp(effective_id, HVDCP_ICL_VOTER) == 0) - smbchg_parallel_usb_disable(chip); - - if (chip->parallel.current_max_ma == 0) { - rc = smbchg_set_usb_current_max(chip, icl_ma); - if (rc) { - pr_err("Failed to set usb current max: %d\n", rc); - return rc; - } - } - - /* skip the aicl rerun if hvdcp icl voter is active */ - if (strcmp(effective_id, HVDCP_ICL_VOTER) == 0) - return 0; - - aicl_ma = smbchg_get_aicl_level_ma(chip); - if (icl_ma > aicl_ma) - smbchg_rerun_aicl(chip); - smbchg_parallel_usb_check_ok(chip); - return 0; -} - -static int smbchg_system_temp_level_set(struct smbchg_chip *chip, - int lvl_sel) -{ - int rc = 0; - int prev_therm_lvl; - int thermal_icl_ma; - - if (!chip->thermal_mitigation) { - dev_err(chip->dev, "Thermal mitigation not supported\n"); - return -EINVAL; - } - - if (lvl_sel < 0) { - dev_err(chip->dev, "Unsupported level selected %d\n", lvl_sel); - return -EINVAL; - } - - if (lvl_sel >= chip->thermal_levels) { - dev_err(chip->dev, "Unsupported level selected %d forcing %d\n", - lvl_sel, chip->thermal_levels - 1); - lvl_sel = chip->thermal_levels - 1; - } - - if (lvl_sel == chip->therm_lvl_sel) - return 0; - - mutex_lock(&chip->therm_lvl_lock); - prev_therm_lvl = chip->therm_lvl_sel; - chip->therm_lvl_sel = lvl_sel; - if (chip->therm_lvl_sel == (chip->thermal_levels - 1)) { - /* - * Disable charging if highest value selected by - * setting the DC and USB path in suspend - */ - rc = vote(chip->dc_suspend_votable, THERMAL_EN_VOTER, true, 0); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set dc suspend rc %d\n", rc); - goto out; - } - rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER, true, 0); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set usb suspend rc %d\n", rc); - goto out; - } - goto out; - } - - if (chip->therm_lvl_sel == 0) { - rc = vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't disable USB thermal ICL vote rc=%d\n", - rc); - - rc = vote(chip->dc_icl_votable, THERMAL_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't disable DC thermal ICL vote rc=%d\n", - rc); - } else { - thermal_icl_ma = - (int)chip->thermal_mitigation[chip->therm_lvl_sel]; - rc = vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, true, - thermal_icl_ma); - if (rc < 0) - pr_err("Couldn't vote for USB thermal ICL rc=%d\n", rc); - - rc = vote(chip->dc_icl_votable, THERMAL_ICL_VOTER, true, - thermal_icl_ma); - if (rc < 0) - pr_err("Couldn't vote for DC thermal ICL rc=%d\n", rc); - } - - if (prev_therm_lvl == chip->thermal_levels - 1) { - /* - * If previously highest value was selected charging must have - * been disabed. Enable charging by taking the DC and USB path - * out of suspend. - */ - rc = vote(chip->dc_suspend_votable, THERMAL_EN_VOTER, false, 0); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set dc suspend rc %d\n", rc); - goto out; - } - rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER, - false, 0); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set usb suspend rc %d\n", rc); - goto out; - } - } -out: - mutex_unlock(&chip->therm_lvl_lock); - return rc; -} - -static int smbchg_ibat_ocp_threshold_ua = 4500000; -module_param(smbchg_ibat_ocp_threshold_ua, int, 0644); - -#define UCONV 1000000LL -#define MCONV 1000LL -#define FLASH_V_THRESHOLD 3000000 -#define FLASH_VDIP_MARGIN 100000 -#define VPH_FLASH_VDIP (FLASH_V_THRESHOLD + FLASH_VDIP_MARGIN) -#define BUCK_EFFICIENCY 800LL -static int smbchg_calc_max_flash_current(struct smbchg_chip *chip) -{ - int ocv_uv, esr_uohm, rbatt_uohm, ibat_now, rc; - int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw; - int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv; - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv); - if (rc) { - pr_smb(PR_STATUS, "bms psy does not support OCV\n"); - return 0; - } - - rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_RESISTANCE, - &esr_uohm); - if (rc) { - pr_smb(PR_STATUS, "bms psy does not support resistance\n"); - return 0; - } - - rc = msm_bcl_read(BCL_PARAM_CURRENT, &ibat_now); - if (rc) { - pr_smb(PR_STATUS, "BCL current read failed: %d\n", rc); - return 0; - } - - rbatt_uohm = esr_uohm + chip->rpara_uohm + chip->rslow_uohm; - /* - * Calculate the maximum current that can pulled out of the battery - * before the battery voltage dips below a safe threshold. - */ - ibat_safe_ua = div_s64((ocv_uv - VPH_FLASH_VDIP) * UCONV, - rbatt_uohm); - - if (ibat_safe_ua <= smbchg_ibat_ocp_threshold_ua) { - /* - * If the calculated current is below the OCP threshold, then - * use it as the possible flash current. - */ - ibat_flash_ua = ibat_safe_ua - ibat_now; - vph_flash_uv = VPH_FLASH_VDIP; - } else { - /* - * If the calculated current is above the OCP threshold, then - * use the ocp threshold instead. - * - * Any higher current will be tripping the battery OCP. - */ - ibat_flash_ua = smbchg_ibat_ocp_threshold_ua - ibat_now; - vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm - * smbchg_ibat_ocp_threshold_ua, UCONV); - } - /* Calculate the input voltage of the flash module. */ - vin_flash_uv = max((chip->vled_max_uv + 500000LL), - div64_s64((vph_flash_uv * 1200), 1000)); - /* Calculate the available power for the flash module. */ - avail_flash_power_fw = BUCK_EFFICIENCY * vph_flash_uv * ibat_flash_ua; - /* - * Calculate the available amount of current the flash module can draw - * before collapsing the battery. (available power/ flash input voltage) - */ - avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV); - pr_smb(PR_MISC, - "avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d\n", - avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm); - return (int)avail_flash_ua; -} - -#define FCC_CMP_CFG 0xF3 -#define FCC_COMP_MASK SMB_MASK(1, 0) -static int smbchg_fastchg_current_comp_set(struct smbchg_chip *chip, - int comp_current) -{ - int rc; - u8 i; - - for (i = 0; i < chip->tables.fcc_comp_len; i++) - if (comp_current == chip->tables.fcc_comp_table[i]) - break; - - if (i >= chip->tables.fcc_comp_len) - return -EINVAL; - - rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CMP_CFG, - FCC_COMP_MASK, i); - - if (rc) - dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n", - rc); - - return rc; -} - -#define CFG_TCC_REG 0xF9 -#define CHG_ITERM_MASK SMB_MASK(2, 0) -static int smbchg_iterm_set(struct smbchg_chip *chip, int iterm_ma) -{ - int rc; - u8 reg; - - reg = find_closest_in_array( - chip->tables.iterm_ma_table, - chip->tables.iterm_ma_len, - iterm_ma); - - rc = smbchg_sec_masked_write(chip, - chip->chgr_base + CFG_TCC_REG, - CHG_ITERM_MASK, reg); - if (rc) { - dev_err(chip->dev, - "Couldn't set iterm rc = %d\n", rc); - return rc; - } - pr_smb(PR_STATUS, "set tcc (%d) to 0x%02x\n", - iterm_ma, reg); - chip->iterm_ma = iterm_ma; - - return 0; -} - -#define FV_CMP_CFG 0xF5 -#define FV_COMP_MASK SMB_MASK(5, 0) -static int smbchg_float_voltage_comp_set(struct smbchg_chip *chip, int code) -{ - int rc; - u8 val; - - val = code & FV_COMP_MASK; - rc = smbchg_sec_masked_write(chip, chip->chgr_base + FV_CMP_CFG, - FV_COMP_MASK, val); - - if (rc) - dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n", - rc); - - return rc; -} - -#define VFLOAT_CFG_REG 0xF4 -#define MIN_FLOAT_MV 3600 -#define MAX_FLOAT_MV 4500 -#define VFLOAT_MASK SMB_MASK(5, 0) - -#define MID_RANGE_FLOAT_MV_MIN 3600 -#define MID_RANGE_FLOAT_MIN_VAL 0x05 -#define MID_RANGE_FLOAT_STEP_MV 20 - -#define HIGH_RANGE_FLOAT_MIN_MV 4340 -#define HIGH_RANGE_FLOAT_MIN_VAL 0x2A -#define HIGH_RANGE_FLOAT_STEP_MV 10 - -#define VHIGH_RANGE_FLOAT_MIN_MV 4360 -#define VHIGH_RANGE_FLOAT_MIN_VAL 0x2C -#define VHIGH_RANGE_FLOAT_STEP_MV 20 -static int smbchg_float_voltage_set(struct smbchg_chip *chip, int vfloat_mv) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval prop; - int rc, delta; - u8 temp; - - if ((vfloat_mv < MIN_FLOAT_MV) || (vfloat_mv > MAX_FLOAT_MV)) { - dev_err(chip->dev, "bad float voltage mv =%d asked to set\n", - vfloat_mv); - return -EINVAL; - } - - if (vfloat_mv <= HIGH_RANGE_FLOAT_MIN_MV) { - /* mid range */ - delta = vfloat_mv - MID_RANGE_FLOAT_MV_MIN; - temp = MID_RANGE_FLOAT_MIN_VAL + delta - / MID_RANGE_FLOAT_STEP_MV; - vfloat_mv -= delta % MID_RANGE_FLOAT_STEP_MV; - } else if (vfloat_mv <= VHIGH_RANGE_FLOAT_MIN_MV) { - /* high range */ - delta = vfloat_mv - HIGH_RANGE_FLOAT_MIN_MV; - temp = HIGH_RANGE_FLOAT_MIN_VAL + delta - / HIGH_RANGE_FLOAT_STEP_MV; - vfloat_mv -= delta % HIGH_RANGE_FLOAT_STEP_MV; - } else { - /* very high range */ - delta = vfloat_mv - VHIGH_RANGE_FLOAT_MIN_MV; - temp = VHIGH_RANGE_FLOAT_MIN_VAL + delta - / VHIGH_RANGE_FLOAT_STEP_MV; - vfloat_mv -= delta % VHIGH_RANGE_FLOAT_STEP_MV; - } - - if (parallel_psy) { - prop.intval = vfloat_mv + 50; - rc = power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop); - if (rc) - dev_err(chip->dev, "Couldn't set float voltage on parallel psy rc: %d\n", - rc); - } - - rc = smbchg_sec_masked_write(chip, chip->chgr_base + VFLOAT_CFG_REG, - VFLOAT_MASK, temp); - - if (rc) - dev_err(chip->dev, "Couldn't set float voltage rc = %d\n", rc); - else - chip->vfloat_mv = vfloat_mv; - - return rc; -} - -static int smbchg_float_voltage_get(struct smbchg_chip *chip) -{ - return chip->vfloat_mv; -} - -#define SFT_CFG 0xFD -#define SFT_EN_MASK SMB_MASK(5, 4) -#define SFT_TO_MASK SMB_MASK(3, 2) -#define PRECHG_SFT_TO_MASK SMB_MASK(1, 0) -#define SFT_TIMER_DISABLE_BIT BIT(5) -#define PRECHG_SFT_TIMER_DISABLE_BIT BIT(4) -#define SAFETY_TIME_MINUTES_SHIFT 2 -static int smbchg_safety_timer_enable(struct smbchg_chip *chip, bool enable) -{ - int rc; - u8 reg; - - if (enable == chip->safety_timer_en) - return 0; - - if (enable) - reg = 0; - else - reg = SFT_TIMER_DISABLE_BIT | PRECHG_SFT_TIMER_DISABLE_BIT; - - rc = smbchg_sec_masked_write(chip, chip->chgr_base + SFT_CFG, - SFT_EN_MASK, reg); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't %s safety timer rc = %d\n", - enable ? "enable" : "disable", rc); - return rc; - } - chip->safety_timer_en = enable; - return 0; -} - -enum skip_reason { - REASON_OTG_ENABLED = BIT(0), - REASON_FLASH_ENABLED = BIT(1) -}; - -#define BAT_IF_TRIM7_REG 0xF7 -#define CFG_750KHZ_BIT BIT(1) -#define MISC_CFG_NTC_VOUT_REG 0xF3 -#define CFG_NTC_VOUT_FSW_BIT BIT(0) -static int smbchg_switch_buck_frequency(struct smbchg_chip *chip, - bool flash_active) -{ - int rc; - - if (!(chip->wa_flags & SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA)) - return 0; - - if (chip->flash_active == flash_active) { - pr_smb(PR_STATUS, "Fsw not changed, flash_active: %d\n", - flash_active); - return 0; - } - - /* - * As per the systems team recommendation, before the flash fires, - * buck switching frequency(Fsw) needs to be increased to 1MHz. Once the - * flash is disabled, Fsw needs to be set back to 750KHz. - */ - rc = smbchg_sec_masked_write(chip, chip->misc_base + - MISC_CFG_NTC_VOUT_REG, CFG_NTC_VOUT_FSW_BIT, - flash_active ? CFG_NTC_VOUT_FSW_BIT : 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set switching frequency multiplier rc=%d\n", - rc); - return rc; - } - - rc = smbchg_sec_masked_write(chip, chip->bat_if_base + BAT_IF_TRIM7_REG, - CFG_750KHZ_BIT, flash_active ? 0 : CFG_750KHZ_BIT); - if (rc < 0) { - dev_err(chip->dev, "Cannot set switching freq: %d\n", rc); - return rc; - } - - pr_smb(PR_STATUS, "Fsw @ %sHz\n", flash_active ? "1M" : "750K"); - chip->flash_active = flash_active; - return 0; -} - -#define OTG_TRIM6 0xF6 -#define TR_ENB_SKIP_BIT BIT(2) -#define OTG_EN_BIT BIT(0) -static int smbchg_otg_pulse_skip_disable(struct smbchg_chip *chip, - enum skip_reason reason, bool disable) -{ - int rc; - bool disabled; - - disabled = !!chip->otg_pulse_skip_dis; - pr_smb(PR_STATUS, "%s pulse skip, reason %d\n", - disable ? "disabling" : "enabling", reason); - if (disable) - chip->otg_pulse_skip_dis |= reason; - else - chip->otg_pulse_skip_dis &= ~reason; - if (disabled == !!chip->otg_pulse_skip_dis) - return 0; - disabled = !!chip->otg_pulse_skip_dis; - - rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_TRIM6, - TR_ENB_SKIP_BIT, disabled ? TR_ENB_SKIP_BIT : 0); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't %s otg pulse skip rc = %d\n", - disabled ? "disable" : "enable", rc); - return rc; - } - pr_smb(PR_STATUS, "%s pulse skip\n", disabled ? "disabled" : "enabled"); - return 0; -} - -#define LOW_PWR_OPTIONS_REG 0xFF -#define FORCE_TLIM_BIT BIT(4) -static int smbchg_force_tlim_en(struct smbchg_chip *chip, bool enable) -{ - int rc; - - rc = smbchg_sec_masked_write(chip, chip->otg_base + LOW_PWR_OPTIONS_REG, - FORCE_TLIM_BIT, enable ? FORCE_TLIM_BIT : 0); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't %s otg force tlim rc = %d\n", - enable ? "enable" : "disable", rc); - return rc; - } - return rc; -} - -static void smbchg_vfloat_adjust_check(struct smbchg_chip *chip) -{ - if (!chip->use_vfloat_adjustments) - return; - - smbchg_stay_awake(chip, PM_REASON_VFLOAT_ADJUST); - pr_smb(PR_STATUS, "Starting vfloat adjustments\n"); - schedule_delayed_work(&chip->vfloat_adjust_work, 0); -} - -#define FV_STS_REG 0xC -#define AICL_INPUT_STS_BIT BIT(6) -static bool smbchg_is_input_current_limited(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->chgr_base + FV_STS_REG, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read FV_STS rc=%d\n", rc); - return false; - } - - return !!(reg & AICL_INPUT_STS_BIT); -} - -#define SW_ESR_PULSE_MS 1500 -static void smbchg_cc_esr_wa_check(struct smbchg_chip *chip) -{ - int rc, esr_count; - - if (!(chip->wa_flags & SMBCHG_CC_ESR_WA)) - return; - - if (!is_usb_present(chip) && !is_dc_present(chip)) { - pr_smb(PR_STATUS, "No inputs present, skipping\n"); - return; - } - - if (get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST) { - pr_smb(PR_STATUS, "Not in fast charge, skipping\n"); - return; - } - - if (!smbchg_is_input_current_limited(chip)) { - pr_smb(PR_STATUS, "Not input current limited, skipping\n"); - return; - } - - set_property_on_fg(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1); - rc = get_property_from_fg(chip, - POWER_SUPPLY_PROP_ESR_COUNT, &esr_count); - if (rc) { - pr_smb(PR_STATUS, - "could not read ESR counter rc = %d\n", rc); - return; - } - - /* - * The esr_count is counting down the number of fuel gauge cycles - * before a ESR pulse is needed. - * - * After a successful ESR pulse, this count is reset to some - * high number like 28. If this reaches 0, then the fuel gauge - * hardware should force a ESR pulse. - * - * However, if the device is in constant current charge mode while - * being input current limited, the ESR pulse will not affect the - * battery current, so the measurement will fail. - * - * As a failsafe, force a manual ESR pulse if this value is read as - * 0. - */ - if (esr_count != 0) { - pr_smb(PR_STATUS, "ESR count is not zero, skipping\n"); - return; - } - - pr_smb(PR_STATUS, "Lowering charge current for ESR pulse\n"); - smbchg_stay_awake(chip, PM_ESR_PULSE); - smbchg_sw_esr_pulse_en(chip, true); - msleep(SW_ESR_PULSE_MS); - pr_smb(PR_STATUS, "Raising charge current for ESR pulse\n"); - smbchg_relax(chip, PM_ESR_PULSE); - smbchg_sw_esr_pulse_en(chip, false); -} - -static void smbchg_soc_changed(struct smbchg_chip *chip) -{ - smbchg_cc_esr_wa_check(chip); -} - -#define DC_AICL_CFG 0xF3 -#define MISC_TRIM_OPT_15_8 0xF5 -#define USB_AICL_DEGLITCH_MASK (BIT(5) | BIT(4) | BIT(3)) -#define USB_AICL_DEGLITCH_SHORT (BIT(5) | BIT(4) | BIT(3)) -#define USB_AICL_DEGLITCH_LONG 0 -#define DC_AICL_DEGLITCH_MASK (BIT(5) | BIT(4) | BIT(3)) -#define DC_AICL_DEGLITCH_SHORT (BIT(5) | BIT(4) | BIT(3)) -#define DC_AICL_DEGLITCH_LONG 0 -#define AICL_RERUN_MASK (BIT(5) | BIT(4)) -#define AICL_RERUN_ON (BIT(5) | BIT(4)) -#define AICL_RERUN_OFF 0 - -static int smbchg_hw_aicl_rerun_enable_indirect_cb(struct votable *votable, - void *data, - int enable, - const char *client) -{ - int rc = 0; - struct smbchg_chip *chip = data; - - if (enable < 0) { - pr_err("No voters\n"); - enable = 0; - } - /* - * If the indirect voting result of all the clients is to enable hw aicl - * rerun, then remove our vote to disable hw aicl rerun - */ - rc = vote(chip->hw_aicl_rerun_disable_votable, - HW_AICL_RERUN_ENABLE_INDIRECT_VOTER, !enable, 0); - if (rc < 0) { - pr_err("Couldn't vote for hw rerun rc= %d\n", rc); - return rc; - } - - return rc; -} - -static int smbchg_hw_aicl_rerun_disable_cb(struct votable *votable, void *data, - int disable, - const char *client) -{ - int rc = 0; - struct smbchg_chip *chip = data; - - if (disable < 0) { - pr_err("No voters\n"); - disable = 0; - } - - rc = smbchg_sec_masked_write(chip, - chip->misc_base + MISC_TRIM_OPT_15_8, - AICL_RERUN_MASK, disable ? AICL_RERUN_OFF : AICL_RERUN_ON); - if (rc < 0) - pr_err("Couldn't write to MISC_TRIM_OPTIONS_15_8 rc=%d\n", rc); - - return rc; -} - -static int smbchg_aicl_deglitch_config_cb(struct votable *votable, void *data, - int shorter, - const char *client) -{ - int rc = 0; - struct smbchg_chip *chip = data; - - if (shorter < 0) { - pr_err("No voters\n"); - shorter = 0; - } - - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + USB_AICL_CFG, - USB_AICL_DEGLITCH_MASK, - shorter ? USB_AICL_DEGLITCH_SHORT : USB_AICL_DEGLITCH_LONG); - if (rc < 0) { - pr_err("Couldn't write to USB_AICL_CFG rc=%d\n", rc); - return rc; - } - rc = smbchg_sec_masked_write(chip, - chip->dc_chgpth_base + DC_AICL_CFG, - DC_AICL_DEGLITCH_MASK, - shorter ? DC_AICL_DEGLITCH_SHORT : DC_AICL_DEGLITCH_LONG); - if (rc < 0) { - pr_err("Couldn't write to DC_AICL_CFG rc=%d\n", rc); - return rc; - } - return rc; -} - -static void smbchg_aicl_deglitch_wa_en(struct smbchg_chip *chip, bool en) -{ - int rc; - - rc = vote(chip->aicl_deglitch_short_votable, - VARB_WORKAROUND_VOTER, en, 0); - if (rc < 0) { - pr_err("Couldn't vote %s deglitch rc=%d\n", - en ? "short" : "long", rc); - return; - } - pr_smb(PR_STATUS, "AICL deglitch set to %s\n", en ? "short" : "long"); - - rc = vote(chip->hw_aicl_rerun_enable_indirect_votable, - VARB_WORKAROUND_VOTER, en, 0); - if (rc < 0) { - pr_err("Couldn't vote hw aicl rerun rc= %d\n", rc); - return; - } - chip->aicl_deglitch_short = en; -} - -static void smbchg_aicl_deglitch_wa_check(struct smbchg_chip *chip) -{ - union power_supply_propval prop = {0,}; - int rc; - bool low_volt_chgr = true; - - if (!(chip->wa_flags & SMBCHG_AICL_DEGLITCH_WA)) - return; - - if (!is_usb_present(chip) && !is_dc_present(chip)) { - pr_smb(PR_STATUS, "Charger removed\n"); - smbchg_aicl_deglitch_wa_en(chip, false); - return; - } - - if (!chip->bms_psy) - return; - - if (is_usb_present(chip)) { - if (is_hvdcp_present(chip)) - low_volt_chgr = false; - } else if (is_dc_present(chip)) { - if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER) - low_volt_chgr = false; - else - low_volt_chgr = chip->low_volt_dcin; - } - - if (!low_volt_chgr) { - pr_smb(PR_STATUS, "High volt charger! Don't set deglitch\n"); - smbchg_aicl_deglitch_wa_en(chip, false); - return; - } - - /* It is possible that battery voltage went high above threshold - * when the charger is inserted and can go low because of system - * load. We shouldn't be reconfiguring AICL deglitch when this - * happens as it will lead to oscillation again which is being - * fixed here. Do it once when the battery voltage crosses the - * threshold (e.g. 4.2 V) and clear it only when the charger - * is removed. - */ - if (!chip->vbat_above_headroom) { - rc = power_supply_get_property(chip->bms_psy, - POWER_SUPPLY_PROP_VOLTAGE_MIN, &prop); - if (rc < 0) { - pr_err("could not read voltage_min, rc=%d\n", rc); - return; - } - chip->vbat_above_headroom = !prop.intval; - } - smbchg_aicl_deglitch_wa_en(chip, chip->vbat_above_headroom); -} - -#define MISC_TEST_REG 0xE2 -#define BB_LOOP_DISABLE_ICL BIT(2) -static int smbchg_icl_loop_disable_check(struct smbchg_chip *chip) -{ - bool icl_disabled = !chip->chg_otg_enabled && chip->flash_triggered; - int rc = 0; - - if ((chip->wa_flags & SMBCHG_FLASH_ICL_DISABLE_WA) - && icl_disabled != chip->icl_disabled) { - rc = smbchg_sec_masked_write(chip, - chip->misc_base + MISC_TEST_REG, - BB_LOOP_DISABLE_ICL, - icl_disabled ? BB_LOOP_DISABLE_ICL : 0); - chip->icl_disabled = icl_disabled; - } - - return rc; -} - -#define UNKNOWN_BATT_TYPE "Unknown Battery" -#define LOADING_BATT_TYPE "Loading Battery Data" -static int smbchg_config_chg_battery_type(struct smbchg_chip *chip) -{ - int rc = 0, max_voltage_uv = 0, fastchg_ma = 0, ret = 0, iterm_ua = 0; - struct device_node *batt_node, *profile_node; - struct device_node *node = chip->pdev->dev.of_node; - union power_supply_propval prop = {0,}; - - rc = power_supply_get_property(chip->bms_psy, - POWER_SUPPLY_PROP_BATTERY_TYPE, &prop); - if (rc) { - pr_smb(PR_STATUS, "Unable to read battery-type rc=%d\n", rc); - return 0; - } - if (!strcmp(prop.strval, UNKNOWN_BATT_TYPE) || - !strcmp(prop.strval, LOADING_BATT_TYPE)) { - pr_smb(PR_MISC, "Battery-type not identified\n"); - return 0; - } - /* quit if there is no change in the battery-type from previous */ - if (chip->battery_type && !strcmp(prop.strval, chip->battery_type)) - return 0; - - chip->battery_type = prop.strval; - batt_node = of_parse_phandle(node, "qcom,battery-data", 0); - if (!batt_node) { - pr_smb(PR_MISC, "No batterydata available\n"); - return 0; - } - - rc = power_supply_get_property(chip->bms_psy, - POWER_SUPPLY_PROP_RESISTANCE_ID, &prop); - if (rc < 0) { - pr_smb(PR_STATUS, "Unable to read battery-id rc=%d\n", rc); - return 0; - } - - profile_node = of_batterydata_get_best_profile(batt_node, - prop.intval / 1000, NULL); - if (IS_ERR_OR_NULL(profile_node)) { - rc = PTR_ERR(profile_node); - pr_err("couldn't find profile handle %d\n", rc); - return rc; - } - - /* change vfloat */ - rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv", - &max_voltage_uv); - if (rc) { - pr_warn("couldn't find battery max voltage rc=%d\n", rc); - ret = rc; - } else { - if (chip->vfloat_mv != (max_voltage_uv / 1000)) { - pr_info("Vfloat changed from %dmV to %dmV for battery-type %s\n", - chip->vfloat_mv, (max_voltage_uv / 1000), - chip->battery_type); - rc = smbchg_float_voltage_set(chip, - (max_voltage_uv / 1000)); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set float voltage rc = %d\n", rc); - return rc; - } - } - } - - /* change chg term */ - rc = of_property_read_u32(profile_node, "qcom,chg-term-ua", - &iterm_ua); - if (rc && rc != -EINVAL) { - pr_warn("couldn't read battery term current=%d\n", rc); - ret = rc; - } else if (!rc) { - if (chip->iterm_ma != (iterm_ua / 1000) - && !chip->iterm_disabled) { - pr_info("Term current changed from %dmA to %dmA for battery-type %s\n", - chip->iterm_ma, (iterm_ua / 1000), - chip->battery_type); - rc = smbchg_iterm_set(chip, - (iterm_ua / 1000)); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set iterm rc = %d\n", rc); - return rc; - } - } - chip->iterm_ma = iterm_ua / 1000; - } - - /* - * Only configure from profile if fastchg-ma is not defined in the - * charger device node. - */ - if (!of_find_property(chip->pdev->dev.of_node, - "qcom,fastchg-current-ma", NULL)) { - rc = of_property_read_u32(profile_node, - "qcom,fastchg-current-ma", &fastchg_ma); - if (rc) { - ret = rc; - } else { - pr_smb(PR_MISC, - "fastchg-ma changed from to %dma for battery-type %s\n", - fastchg_ma, chip->battery_type); - rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true, - fastchg_ma); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't vote for fastchg current rc=%d\n", - rc); - return rc; - } - } - } - - return ret; -} - -#define MAX_INV_BATT_ID 7700 -#define MIN_INV_BATT_ID 7300 -static void check_battery_type(struct smbchg_chip *chip) -{ - union power_supply_propval prop = {0,}; - bool en; - - if (!chip->bms_psy && chip->bms_psy_name) - chip->bms_psy = - power_supply_get_by_name((char *)chip->bms_psy_name); - if (chip->bms_psy) { - power_supply_get_property(chip->bms_psy, - POWER_SUPPLY_PROP_BATTERY_TYPE, &prop); - en = (strcmp(prop.strval, UNKNOWN_BATT_TYPE) != 0 - || chip->charge_unknown_battery) - && (strcmp(prop.strval, LOADING_BATT_TYPE) != 0); - vote(chip->battchg_suspend_votable, - BATTCHG_UNKNOWN_BATTERY_EN_VOTER, !en, 0); - - if (!chip->skip_usb_suspend_for_fake_battery) { - power_supply_get_property(chip->bms_psy, - POWER_SUPPLY_PROP_RESISTANCE_ID, &prop); - /* suspend USB path for invalid battery-id */ - en = (prop.intval <= MAX_INV_BATT_ID && - prop.intval >= MIN_INV_BATT_ID) ? 1 : 0; - vote(chip->usb_suspend_votable, FAKE_BATTERY_EN_VOTER, - en, 0); - } - } -} - -static void smbchg_external_power_changed(struct power_supply *psy) -{ - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - union power_supply_propval prop = {0,}; - int rc, current_limit = 0, soc; - enum power_supply_type usb_supply_type; - char *usb_type_name = "null"; - - if (chip->bms_psy_name) - chip->bms_psy = - power_supply_get_by_name((char *)chip->bms_psy_name); - - smbchg_aicl_deglitch_wa_check(chip); - if (chip->bms_psy) { - check_battery_type(chip); - soc = get_prop_batt_capacity(chip); - if (chip->previous_soc != soc) { - chip->previous_soc = soc; - smbchg_soc_changed(chip); - } - - rc = smbchg_config_chg_battery_type(chip); - if (rc) - pr_smb(PR_MISC, - "Couldn't update charger configuration rc=%d\n", - rc); - } - - rc = power_supply_get_property(chip->usb_psy, - POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); - if (rc == 0) - vote(chip->usb_suspend_votable, POWER_SUPPLY_EN_VOTER, - !prop.intval, 0); - - current_limit = chip->usb_current_max / 1000; - - /* Override if type-c charger used */ - if (chip->typec_current_ma > 500 && - current_limit < chip->typec_current_ma) - current_limit = chip->typec_current_ma; - - read_usb_type(chip, &usb_type_name, &usb_supply_type); - - if (usb_supply_type != POWER_SUPPLY_TYPE_USB) - goto skip_current_for_non_sdp; - - pr_smb(PR_MISC, "usb type = %s current_limit = %d\n", - usb_type_name, current_limit); - - rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true, - current_limit); - if (rc < 0) - pr_err("Couldn't update USB PSY ICL vote rc=%d\n", rc); - -skip_current_for_non_sdp: - smbchg_vfloat_adjust_check(chip); - - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); -} - -static int smbchg_otg_regulator_enable(struct regulator_dev *rdev) -{ - int rc = 0; - struct smbchg_chip *chip = rdev_get_drvdata(rdev); - - chip->otg_retries = 0; - chip->chg_otg_enabled = true; - smbchg_icl_loop_disable_check(chip); - smbchg_otg_pulse_skip_disable(chip, REASON_OTG_ENABLED, true); - - /* If pin control mode then return from here */ - if (chip->otg_pinctrl) - return rc; - - /* sleep to make sure the pulse skip is actually disabled */ - msleep(20); - rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG, - OTG_EN_BIT, OTG_EN_BIT); - if (rc < 0) - dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", rc); - else - chip->otg_enable_time = ktime_get(); - pr_smb(PR_STATUS, "Enabling OTG Boost\n"); - return rc; -} - -static int smbchg_otg_regulator_disable(struct regulator_dev *rdev) -{ - int rc = 0; - struct smbchg_chip *chip = rdev_get_drvdata(rdev); - - if (!chip->otg_pinctrl) { - rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG, - OTG_EN_BIT, 0); - if (rc < 0) - dev_err(chip->dev, "Couldn't disable OTG mode rc=%d\n", - rc); - } - - chip->chg_otg_enabled = false; - smbchg_otg_pulse_skip_disable(chip, REASON_OTG_ENABLED, false); - smbchg_icl_loop_disable_check(chip); - pr_smb(PR_STATUS, "Disabling OTG Boost\n"); - return rc; -} - -static int smbchg_otg_regulator_is_enable(struct regulator_dev *rdev) -{ - int rc = 0; - u8 reg = 0; - struct smbchg_chip *chip = rdev_get_drvdata(rdev); - - rc = smbchg_read(chip, ®, chip->bat_if_base + CMD_CHG_REG, 1); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't read OTG enable bit rc=%d\n", rc); - return rc; - } - - return (reg & OTG_EN_BIT) ? 1 : 0; -} - -struct regulator_ops smbchg_otg_reg_ops = { - .enable = smbchg_otg_regulator_enable, - .disable = smbchg_otg_regulator_disable, - .is_enabled = smbchg_otg_regulator_is_enable, -}; - -#define USBIN_CHGR_CFG 0xF1 -#define ADAPTER_ALLOWANCE_MASK 0x7 -#define USBIN_ADAPTER_9V 0x3 -#define USBIN_ADAPTER_5V_9V_CONT 0x2 -#define USBIN_ADAPTER_5V_UNREGULATED_9V 0x5 -#define HVDCP_EN_BIT BIT(3) -static int smbchg_external_otg_regulator_enable(struct regulator_dev *rdev) -{ - int rc = 0; - struct smbchg_chip *chip = rdev_get_drvdata(rdev); - - rc = vote(chip->usb_suspend_votable, OTG_EN_VOTER, true, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't suspend charger rc=%d\n", rc); - return rc; - } - - rc = smbchg_read(chip, &chip->original_usbin_allowance, - chip->usb_chgpth_base + USBIN_CHGR_CFG, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc); - return rc; - } - - /* - * To disallow source detect and usbin_uv interrupts, set the adapter - * allowance to 9V, so that the audio boost operating in reverse never - * gets detected as a valid input - */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_EN_BIT, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't disable HVDCP rc=%d\n", rc); - return rc; - } - - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + USBIN_CHGR_CFG, - 0xFF, USBIN_ADAPTER_9V); - if (rc < 0) { - dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc); - return rc; - } - - pr_smb(PR_STATUS, "Enabling OTG Boost\n"); - return rc; -} - -static int smbchg_external_otg_regulator_disable(struct regulator_dev *rdev) -{ - int rc = 0; - struct smbchg_chip *chip = rdev_get_drvdata(rdev); - - rc = vote(chip->usb_suspend_votable, OTG_EN_VOTER, false, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't unsuspend charger rc=%d\n", rc); - return rc; - } - - /* - * Reenable HVDCP and set the adapter allowance back to the original - * value in order to allow normal USBs to be recognized as a valid - * input. - */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_EN_BIT, HVDCP_EN_BIT); - if (rc < 0) { - dev_err(chip->dev, "Couldn't enable HVDCP rc=%d\n", rc); - return rc; - } - - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + USBIN_CHGR_CFG, - 0xFF, chip->original_usbin_allowance); - if (rc < 0) { - dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc); - return rc; - } - - pr_smb(PR_STATUS, "Disabling OTG Boost\n"); - return rc; -} - -static int smbchg_external_otg_regulator_is_enable(struct regulator_dev *rdev) -{ - struct smbchg_chip *chip = rdev_get_drvdata(rdev); - - return get_client_vote(chip->usb_suspend_votable, OTG_EN_VOTER); -} - -struct regulator_ops smbchg_external_otg_reg_ops = { - .enable = smbchg_external_otg_regulator_enable, - .disable = smbchg_external_otg_regulator_disable, - .is_enabled = smbchg_external_otg_regulator_is_enable, -}; - -static int smbchg_regulator_init(struct smbchg_chip *chip) -{ - int rc = 0; - struct regulator_config cfg = {}; - struct device_node *regulator_node; - - cfg.dev = chip->dev; - cfg.driver_data = chip; - - chip->otg_vreg.rdesc.owner = THIS_MODULE; - chip->otg_vreg.rdesc.type = REGULATOR_VOLTAGE; - chip->otg_vreg.rdesc.ops = &smbchg_otg_reg_ops; - chip->otg_vreg.rdesc.of_match = "qcom,smbcharger-boost-otg"; - chip->otg_vreg.rdesc.name = "qcom,smbcharger-boost-otg"; - - chip->otg_vreg.rdev = devm_regulator_register(chip->dev, - &chip->otg_vreg.rdesc, &cfg); - if (IS_ERR(chip->otg_vreg.rdev)) { - rc = PTR_ERR(chip->otg_vreg.rdev); - chip->otg_vreg.rdev = NULL; - if (rc != -EPROBE_DEFER) - dev_err(chip->dev, - "OTG reg failed, rc=%d\n", rc); - } - if (rc) - return rc; - - regulator_node = of_get_child_by_name(chip->dev->of_node, - "qcom,smbcharger-external-otg"); - if (!regulator_node) { - dev_dbg(chip->dev, "external-otg node absent\n"); - return 0; - } - - chip->ext_otg_vreg.rdesc.owner = THIS_MODULE; - chip->ext_otg_vreg.rdesc.type = REGULATOR_VOLTAGE; - chip->ext_otg_vreg.rdesc.ops = &smbchg_external_otg_reg_ops; - chip->ext_otg_vreg.rdesc.of_match = "qcom,smbcharger-external-otg"; - chip->ext_otg_vreg.rdesc.name = "qcom,smbcharger-external-otg"; - if (of_get_property(chip->dev->of_node, "otg-parent-supply", NULL)) - chip->ext_otg_vreg.rdesc.supply_name = "otg-parent"; - cfg.dev = chip->dev; - cfg.driver_data = chip; - - chip->ext_otg_vreg.rdev = devm_regulator_register(chip->dev, - &chip->ext_otg_vreg.rdesc, - &cfg); - if (IS_ERR(chip->ext_otg_vreg.rdev)) { - rc = PTR_ERR(chip->ext_otg_vreg.rdev); - chip->ext_otg_vreg.rdev = NULL; - if (rc != -EPROBE_DEFER) - dev_err(chip->dev, - "external OTG reg failed, rc=%d\n", rc); - } - - return rc; -} - -#define CMD_CHG_LED_REG 0x43 -#define CHG_LED_CTRL_BIT BIT(0) -#define LED_SW_CTRL_BIT 0x1 -#define LED_CHG_CTRL_BIT 0x0 -#define CHG_LED_ON 0x03 -#define CHG_LED_OFF 0x00 -#define LED_BLINKING_PATTERN1 0x01 -#define LED_BLINKING_PATTERN2 0x02 -#define LED_BLINKING_CFG_MASK SMB_MASK(2, 1) -#define CHG_LED_SHIFT 1 -static int smbchg_chg_led_controls(struct smbchg_chip *chip) -{ - u8 reg, mask; - int rc; - - if (chip->cfg_chg_led_sw_ctrl) { - /* turn-off LED by default for software control */ - mask = CHG_LED_CTRL_BIT | LED_BLINKING_CFG_MASK; - reg = LED_SW_CTRL_BIT; - } else { - mask = CHG_LED_CTRL_BIT; - reg = LED_CHG_CTRL_BIT; - } - - rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_LED_REG, - mask, reg); - if (rc < 0) - dev_err(chip->dev, - "Couldn't write LED_CTRL_BIT rc=%d\n", rc); - return rc; -} - -static void smbchg_chg_led_brightness_set(struct led_classdev *cdev, - enum led_brightness value) -{ - struct smbchg_chip *chip = container_of(cdev, - struct smbchg_chip, led_cdev); - union power_supply_propval pval = {0, }; - u8 reg; - int rc; - - reg = (value > LED_OFF) ? CHG_LED_ON << CHG_LED_SHIFT : - CHG_LED_OFF << CHG_LED_SHIFT; - pval.intval = value > LED_OFF ? 1 : 0; - power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_HI_POWER, - &pval); - pr_smb(PR_STATUS, - "set the charger led brightness to value=%d\n", - value); - rc = smbchg_sec_masked_write(chip, - chip->bat_if_base + CMD_CHG_LED_REG, - LED_BLINKING_CFG_MASK, reg); - if (rc) - dev_err(chip->dev, "Couldn't write CHG_LED rc=%d\n", - rc); -} - -static enum -led_brightness smbchg_chg_led_brightness_get(struct led_classdev *cdev) -{ - struct smbchg_chip *chip = container_of(cdev, - struct smbchg_chip, led_cdev); - u8 reg_val, chg_led_sts; - int rc; - - rc = smbchg_read(chip, ®_val, chip->bat_if_base + CMD_CHG_LED_REG, - 1); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't read CHG_LED_REG sts rc=%d\n", - rc); - return rc; - } - - chg_led_sts = (reg_val & LED_BLINKING_CFG_MASK) >> CHG_LED_SHIFT; - - pr_smb(PR_STATUS, "chg_led_sts = %02x\n", chg_led_sts); - - return (chg_led_sts == CHG_LED_OFF) ? LED_OFF : LED_FULL; -} - -static void smbchg_chg_led_blink_set(struct smbchg_chip *chip, - unsigned long blinking) -{ - union power_supply_propval pval = {0, }; - u8 reg; - int rc; - - pval.intval = (blinking == 0) ? 0 : 1; - power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_HI_POWER, - &pval); - - if (blinking == 0) { - reg = CHG_LED_OFF << CHG_LED_SHIFT; - } else { - if (blinking == 1) - reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT; - else if (blinking == 2) - reg = LED_BLINKING_PATTERN2 << CHG_LED_SHIFT; - else - reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT; - } - - rc = smbchg_sec_masked_write(chip, - chip->bat_if_base + CMD_CHG_LED_REG, - LED_BLINKING_CFG_MASK, reg); - if (rc) - dev_err(chip->dev, "Couldn't write CHG_LED rc=%d\n", - rc); -} - -static ssize_t smbchg_chg_led_blink_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t len) -{ - struct led_classdev *cdev = dev_get_drvdata(dev); - struct smbchg_chip *chip = container_of(cdev, struct smbchg_chip, - led_cdev); - unsigned long blinking; - ssize_t rc = -EINVAL; - - rc = kstrtoul(buf, 10, &blinking); - if (rc) - return rc; - - smbchg_chg_led_blink_set(chip, blinking); - - return len; -} - -static DEVICE_ATTR(blink, 0664, NULL, smbchg_chg_led_blink_store); - -static struct attribute *led_blink_attributes[] = { - &dev_attr_blink.attr, - NULL, -}; - -static struct attribute_group smbchg_led_attr_group = { - .attrs = led_blink_attributes -}; - -static int smbchg_register_chg_led(struct smbchg_chip *chip) -{ - int rc; - - chip->led_cdev.name = "red"; - chip->led_cdev.brightness_set = smbchg_chg_led_brightness_set; - chip->led_cdev.brightness_get = smbchg_chg_led_brightness_get; - - rc = led_classdev_register(chip->dev, &chip->led_cdev); - if (rc) { - dev_err(chip->dev, "unable to register charger led, rc=%d\n", - rc); - return rc; - } - - rc = sysfs_create_group(&chip->led_cdev.dev->kobj, - &smbchg_led_attr_group); - if (rc) { - dev_err(chip->dev, "led sysfs rc: %d\n", rc); - return rc; - } - - return rc; -} - -static int vf_adjust_low_threshold = 5; -module_param(vf_adjust_low_threshold, int, 0644); - -static int vf_adjust_high_threshold = 7; -module_param(vf_adjust_high_threshold, int, 0644); - -static int vf_adjust_n_samples = 10; -module_param(vf_adjust_n_samples, int, 0644); - -static int vf_adjust_max_delta_mv = 40; -module_param(vf_adjust_max_delta_mv, int, 0644); - -static int vf_adjust_trim_steps_per_adjust = 1; -module_param(vf_adjust_trim_steps_per_adjust, int, 0644); - -#define CENTER_TRIM_CODE 7 -#define MAX_LIN_CODE 14 -#define MAX_TRIM_CODE 15 -#define SCALE_SHIFT 4 -#define VF_TRIM_OFFSET_MASK SMB_MASK(3, 0) -#define VF_STEP_SIZE_MV 10 -#define SCALE_LSB_MV 17 -static int smbchg_trim_add_steps(int prev_trim, int delta_steps) -{ - int scale_steps; - int linear_offset, linear_scale; - int offset_code = prev_trim & VF_TRIM_OFFSET_MASK; - int scale_code = (prev_trim & ~VF_TRIM_OFFSET_MASK) >> SCALE_SHIFT; - - if (abs(delta_steps) > 1) { - pr_smb(PR_STATUS, - "Cant trim multiple steps delta_steps = %d\n", - delta_steps); - return prev_trim; - } - if (offset_code <= CENTER_TRIM_CODE) - linear_offset = offset_code + CENTER_TRIM_CODE; - else if (offset_code > CENTER_TRIM_CODE) - linear_offset = MAX_TRIM_CODE - offset_code; - - if (scale_code <= CENTER_TRIM_CODE) - linear_scale = scale_code + CENTER_TRIM_CODE; - else if (scale_code > CENTER_TRIM_CODE) - linear_scale = scale_code - (CENTER_TRIM_CODE + 1); - - /* check if we can accomodate delta steps with just the offset */ - if (linear_offset + delta_steps >= 0 - && linear_offset + delta_steps <= MAX_LIN_CODE) { - linear_offset += delta_steps; - - if (linear_offset > CENTER_TRIM_CODE) - offset_code = linear_offset - CENTER_TRIM_CODE; - else - offset_code = MAX_TRIM_CODE - linear_offset; - - return (prev_trim & ~VF_TRIM_OFFSET_MASK) | offset_code; - } - - /* changing offset cannot satisfy delta steps, change the scale bits */ - scale_steps = delta_steps > 0 ? 1 : -1; - - if (linear_scale + scale_steps < 0 - || linear_scale + scale_steps > MAX_LIN_CODE) { - pr_smb(PR_STATUS, - "Cant trim scale_steps = %d delta_steps = %d\n", - scale_steps, delta_steps); - return prev_trim; - } - - linear_scale += scale_steps; - - if (linear_scale > CENTER_TRIM_CODE) - scale_code = linear_scale - CENTER_TRIM_CODE; - else - scale_code = linear_scale + (CENTER_TRIM_CODE + 1); - prev_trim = (prev_trim & VF_TRIM_OFFSET_MASK) - | scale_code << SCALE_SHIFT; - - /* - * now that we have changed scale which is a 17mV jump, change the - * offset bits (10mV) too so the effective change is just 7mV - */ - delta_steps = -1 * delta_steps; - - linear_offset = clamp(linear_offset + delta_steps, 0, MAX_LIN_CODE); - if (linear_offset > CENTER_TRIM_CODE) - offset_code = linear_offset - CENTER_TRIM_CODE; - else - offset_code = MAX_TRIM_CODE - linear_offset; - - return (prev_trim & ~VF_TRIM_OFFSET_MASK) | offset_code; -} - -#define TRIM_14 0xFE -#define VF_TRIM_MASK 0xFF -static int smbchg_adjust_vfloat_mv_trim(struct smbchg_chip *chip, - int delta_mv) -{ - int sign, delta_steps, rc = 0; - u8 prev_trim, new_trim; - int i; - - sign = delta_mv > 0 ? 1 : -1; - delta_steps = (delta_mv + sign * VF_STEP_SIZE_MV / 2) - / VF_STEP_SIZE_MV; - - rc = smbchg_read(chip, &prev_trim, chip->misc_base + TRIM_14, 1); - if (rc) { - dev_err(chip->dev, "Unable to read trim 14: %d\n", rc); - return rc; - } - - for (i = 1; i <= abs(delta_steps) - && i <= vf_adjust_trim_steps_per_adjust; i++) { - new_trim = (u8)smbchg_trim_add_steps(prev_trim, - delta_steps > 0 ? 1 : -1); - if (new_trim == prev_trim) { - pr_smb(PR_STATUS, - "VFloat trim unchanged from %02x\n", prev_trim); - /* treat no trim change as an error */ - return -EINVAL; - } - - rc = smbchg_sec_masked_write(chip, chip->misc_base + TRIM_14, - VF_TRIM_MASK, new_trim); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't change vfloat trim rc=%d\n", rc); - } - pr_smb(PR_STATUS, - "VFlt trim %02x to %02x, delta steps: %d\n", - prev_trim, new_trim, delta_steps); - prev_trim = new_trim; - } - - return rc; -} - -#define VFLOAT_RESAMPLE_DELAY_MS 10000 -static void smbchg_vfloat_adjust_work(struct work_struct *work) -{ - struct smbchg_chip *chip = container_of(work, - struct smbchg_chip, - vfloat_adjust_work.work); - int vbat_uv, vbat_mv, ibat_ua, rc, delta_vfloat_mv; - bool taper, enable; - - smbchg_stay_awake(chip, PM_REASON_VFLOAT_ADJUST); - taper = (get_prop_charge_type(chip) - == POWER_SUPPLY_CHARGE_TYPE_TAPER); - enable = taper && (chip->parallel.current_max_ma == 0); - - if (!enable) { - pr_smb(PR_MISC, - "Stopping vfloat adj taper=%d parallel_ma = %d\n", - taper, chip->parallel.current_max_ma); - goto stop; - } - - if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) { - pr_smb(PR_STATUS, "JEITA active, skipping\n"); - goto stop; - } - - set_property_on_fg(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1); - rc = get_property_from_fg(chip, - POWER_SUPPLY_PROP_VOLTAGE_NOW, &vbat_uv); - if (rc) { - pr_smb(PR_STATUS, - "bms psy does not support voltage rc = %d\n", rc); - goto stop; - } - vbat_mv = vbat_uv / 1000; - - if ((vbat_mv - chip->vfloat_mv) < -1 * vf_adjust_max_delta_mv) { - pr_smb(PR_STATUS, "Skip vbat out of range: %d\n", vbat_mv); - goto reschedule; - } - - rc = get_property_from_fg(chip, - POWER_SUPPLY_PROP_CURRENT_NOW, &ibat_ua); - if (rc) { - pr_smb(PR_STATUS, - "bms psy does not support current_now rc = %d\n", rc); - goto stop; - } - - if (ibat_ua / 1000 > -chip->iterm_ma) { - pr_smb(PR_STATUS, "Skip ibat too high: %d\n", ibat_ua); - goto reschedule; - } - - pr_smb(PR_STATUS, "sample number = %d vbat_mv = %d ibat_ua = %d\n", - chip->n_vbat_samples, - vbat_mv, - ibat_ua); - - chip->max_vbat_sample = max(chip->max_vbat_sample, vbat_mv); - chip->n_vbat_samples += 1; - if (chip->n_vbat_samples < vf_adjust_n_samples) { - pr_smb(PR_STATUS, "Skip %d samples; max = %d\n", - chip->n_vbat_samples, chip->max_vbat_sample); - goto reschedule; - } - /* if max vbat > target vfloat, delta_vfloat_mv could be negative */ - delta_vfloat_mv = chip->vfloat_mv - chip->max_vbat_sample; - pr_smb(PR_STATUS, "delta_vfloat_mv = %d, samples = %d, mvbat = %d\n", - delta_vfloat_mv, chip->n_vbat_samples, chip->max_vbat_sample); - /* - * enough valid samples has been collected, adjust trim codes - * based on maximum of collected vbat samples if necessary - */ - if (delta_vfloat_mv > vf_adjust_high_threshold - || delta_vfloat_mv < -1 * vf_adjust_low_threshold) { - rc = smbchg_adjust_vfloat_mv_trim(chip, delta_vfloat_mv); - if (rc) { - pr_smb(PR_STATUS, - "Stopping vfloat adj after trim adj rc = %d\n", - rc); - goto stop; - } - chip->max_vbat_sample = 0; - chip->n_vbat_samples = 0; - goto reschedule; - } - -stop: - chip->max_vbat_sample = 0; - chip->n_vbat_samples = 0; - smbchg_relax(chip, PM_REASON_VFLOAT_ADJUST); - return; - -reschedule: - schedule_delayed_work(&chip->vfloat_adjust_work, - msecs_to_jiffies(VFLOAT_RESAMPLE_DELAY_MS)); - return; -} - -static int smbchg_charging_status_change(struct smbchg_chip *chip) -{ - smbchg_vfloat_adjust_check(chip); - set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS, - get_prop_batt_status(chip)); - return 0; -} - -#define BB_CLMP_SEL 0xF8 -#define BB_CLMP_MASK SMB_MASK(1, 0) -#define BB_CLMP_VFIX_3338MV 0x1 -#define BB_CLMP_VFIX_3512MV 0x2 -static int smbchg_set_optimal_charging_mode(struct smbchg_chip *chip, int type) -{ - int rc; - bool hvdcp2 = (type == POWER_SUPPLY_TYPE_USB_HVDCP - && smbchg_is_usbin_active_pwr_src(chip)); - - /* - * Set the charger switching freq to 1MHZ if HVDCP 2.0, - * or 750KHZ otherwise - */ - rc = smbchg_sec_masked_write(chip, - chip->bat_if_base + BAT_IF_TRIM7_REG, - CFG_750KHZ_BIT, hvdcp2 ? 0 : CFG_750KHZ_BIT); - if (rc) { - dev_err(chip->dev, "Cannot set switching freq: %d\n", rc); - return rc; - } - - /* - * Set the charger switch frequency clamp voltage threshold to 3.338V - * if HVDCP 2.0, or 3.512V otherwise. - */ - rc = smbchg_sec_masked_write(chip, chip->bat_if_base + BB_CLMP_SEL, - BB_CLMP_MASK, - hvdcp2 ? BB_CLMP_VFIX_3338MV : BB_CLMP_VFIX_3512MV); - if (rc) { - dev_err(chip->dev, "Cannot set switching freq: %d\n", rc); - return rc; - } - - return 0; -} - -#define DEFAULT_SDP_MA 100 -#define DEFAULT_CDP_MA 1500 -static int smbchg_change_usb_supply_type(struct smbchg_chip *chip, - enum power_supply_type type) -{ - int rc, current_limit_ma; - - /* - * if the type is not unknown, set the type before changing ICL vote - * in order to ensure that the correct current limit registers are - * used - */ - if (type != POWER_SUPPLY_TYPE_UNKNOWN) - chip->usb_supply_type = type; - - /* - * Type-C only supports STD(900), MEDIUM(1500) and HIGH(3000) current - * modes, skip all BC 1.2 current if external typec is supported. - * Note: for SDP supporting current based on USB notifications. - */ - if (chip->typec_psy && (type != POWER_SUPPLY_TYPE_USB)) - current_limit_ma = chip->typec_current_ma; - else if (type == POWER_SUPPLY_TYPE_USB) - current_limit_ma = DEFAULT_SDP_MA; - else if (type == POWER_SUPPLY_TYPE_USB_CDP) - current_limit_ma = DEFAULT_CDP_MA; - else if (type == POWER_SUPPLY_TYPE_USB_HVDCP) - current_limit_ma = smbchg_default_hvdcp_icl_ma; - else if (type == POWER_SUPPLY_TYPE_USB_HVDCP_3) - current_limit_ma = smbchg_default_hvdcp3_icl_ma; - else - current_limit_ma = smbchg_default_dcp_icl_ma; - - pr_smb(PR_STATUS, "Type %d: setting mA = %d\n", - type, current_limit_ma); - rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true, - current_limit_ma); - if (rc < 0) { - pr_err("Couldn't vote for new USB ICL rc=%d\n", rc); - goto out; - } - - /* otherwise if it is unknown, set type after the vote */ - if (type == POWER_SUPPLY_TYPE_UNKNOWN) - chip->usb_supply_type = type; - - if (!chip->skip_usb_notification) - power_supply_changed(chip->usb_psy); - - /* set the correct buck switching frequency */ - rc = smbchg_set_optimal_charging_mode(chip, type); - if (rc < 0) - pr_err("Couldn't set charger optimal mode rc=%d\n", rc); - -out: - return rc; -} - -#define HVDCP_ADAPTER_SEL_MASK SMB_MASK(5, 4) -#define HVDCP_5V 0x00 -#define HVDCP_9V 0x10 -#define USB_CMD_HVDCP_1 0x42 -#define FORCE_HVDCP_2p0 BIT(3) - -static int force_9v_hvdcp(struct smbchg_chip *chip) -{ - int rc; - - /* Force 5V HVDCP */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_5V); - if (rc) { - pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", rc); - return rc; - } - - /* Force QC2.0 */ - rc = smbchg_masked_write(chip, - chip->usb_chgpth_base + USB_CMD_HVDCP_1, - FORCE_HVDCP_2p0, FORCE_HVDCP_2p0); - rc |= smbchg_masked_write(chip, - chip->usb_chgpth_base + USB_CMD_HVDCP_1, - FORCE_HVDCP_2p0, 0); - if (rc < 0) { - pr_err("Couldn't force QC2.0 rc=%d\n", rc); - return rc; - } - - /* Delay to switch into HVDCP 2.0 and avoid UV */ - msleep(500); - - /* Force 9V HVDCP */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_9V); - if (rc) - pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", rc); - - return rc; -} - -static void smbchg_hvdcp_det_work(struct work_struct *work) -{ - struct smbchg_chip *chip = container_of(work, - struct smbchg_chip, - hvdcp_det_work.work); - int rc; - - if (is_hvdcp_present(chip)) { - if (!chip->hvdcp3_supported && - (chip->wa_flags & SMBCHG_HVDCP_9V_EN_WA)) { - /* force HVDCP 2.0 */ - rc = force_9v_hvdcp(chip); - if (rc) - pr_err("could not force 9V HVDCP continuing rc=%d\n", - rc); - } - smbchg_change_usb_supply_type(chip, - POWER_SUPPLY_TYPE_USB_HVDCP); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_aicl_deglitch_wa_check(chip); - } - smbchg_relax(chip, PM_DETECT_HVDCP); -} - -static int set_usb_psy_dp_dm(struct smbchg_chip *chip, int state) -{ - int rc; - u8 reg; - union power_supply_propval pval = {0, }; - - /* - * ensure that we are not in the middle of an insertion where usbin_uv - * is low and src_detect hasnt gone high. If so force dp=F dm=F - * which guarantees proper type detection - */ - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1); - if (!rc && !(reg & USBIN_UV_BIT) && !(reg & USBIN_SRC_DET_BIT)) { - pr_smb(PR_MISC, "overwriting state = %d with %d\n", - state, POWER_SUPPLY_DP_DM_DPF_DMF); - if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg)) - return regulator_enable(chip->dpdm_reg); - } - pr_smb(PR_MISC, "setting usb psy dp dm = %d\n", state); - pval.intval = state; - return power_supply_set_property(chip->usb_psy, - POWER_SUPPLY_PROP_DP_DM, &pval); -} - -#define APSD_CFG 0xF5 -#define AUTO_SRC_DETECT_EN_BIT BIT(0) -#define APSD_TIMEOUT_MS 1500 -static void restore_from_hvdcp_detection(struct smbchg_chip *chip) -{ - int rc; - - pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n"); - rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc); - - /* switch to 9V HVDCP */ - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_9V); - if (rc < 0) - pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc); - - /* enable HVDCP */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_EN_BIT, HVDCP_EN_BIT); - if (rc < 0) - pr_err("Couldn't enable HVDCP rc=%d\n", rc); - - /* enable APSD */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + APSD_CFG, - AUTO_SRC_DETECT_EN_BIT, AUTO_SRC_DETECT_EN_BIT); - if (rc < 0) - pr_err("Couldn't enable APSD rc=%d\n", rc); - - /* Reset back to 5V unregulated */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + USBIN_CHGR_CFG, - ADAPTER_ALLOWANCE_MASK, USBIN_ADAPTER_5V_UNREGULATED_9V); - if (rc < 0) - pr_err("Couldn't write usb allowance rc=%d\n", rc); - - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, AICL_EN_BIT); - if (rc < 0) - pr_err("Couldn't enable AICL rc=%d\n", rc); - - chip->hvdcp_3_det_ignore_uv = false; - chip->pulse_cnt = 0; -} - -#define RESTRICTED_CHG_FCC_PERCENT 50 -static int smbchg_restricted_charging(struct smbchg_chip *chip, bool enable) -{ - int current_table_index, fastchg_current; - int rc = 0; - - /* If enable, set the fcc to the set point closest - * to 50% of the configured fcc while remaining below it - */ - current_table_index = find_smaller_in_array( - chip->tables.usb_ilim_ma_table, - chip->cfg_fastchg_current_ma - * RESTRICTED_CHG_FCC_PERCENT / 100, - chip->tables.usb_ilim_ma_len); - fastchg_current = - chip->tables.usb_ilim_ma_table[current_table_index]; - rc = vote(chip->fcc_votable, RESTRICTED_CHG_FCC_VOTER, enable, - fastchg_current); - - pr_smb(PR_STATUS, "restricted_charging set to %d\n", enable); - chip->restricted_charging = enable; - - return rc; -} - -static void handle_usb_removal(struct smbchg_chip *chip) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - int rc; - - pr_smb(PR_STATUS, "triggered\n"); - smbchg_aicl_deglitch_wa_check(chip); - /* Clear the OV detected status set before */ - if (chip->usb_ov_det) - chip->usb_ov_det = false; - /* Clear typec current status */ - if (chip->typec_psy) - chip->typec_current_ma = 0; - smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN); - extcon_set_cable_state_(chip->extcon, EXTCON_USB, chip->usb_present); - if (chip->dpdm_reg) - regulator_disable(chip->dpdm_reg); - schedule_work(&chip->usb_set_online_work); - - pr_smb(PR_MISC, "setting usb psy health UNKNOWN\n"); - chip->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; - power_supply_changed(chip->usb_psy); - - if (parallel_psy && chip->parallel_charger_detected) { - pval.intval = false; - power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_PRESENT, &pval); - } - if (chip->parallel.avail && chip->aicl_done_irq - && chip->enable_aicl_wake) { - disable_irq_wake(chip->aicl_done_irq); - chip->enable_aicl_wake = false; - } - chip->parallel.enabled_once = false; - chip->vbat_above_headroom = false; - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - ICL_OVERRIDE_BIT, 0); - if (rc < 0) - pr_err("Couldn't set override rc = %d\n", rc); - - vote(chip->usb_icl_votable, WEAK_CHARGER_ICL_VOTER, false, 0); - chip->usb_icl_delta = 0; - vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, false, 0); - vote(chip->aicl_deglitch_short_votable, - HVDCP_SHORT_DEGLITCH_VOTER, false, 0); - if (!chip->hvdcp_not_supported) - restore_from_hvdcp_detection(chip); -} - -static bool is_usbin_uv_high(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc); - return false; - } - return reg &= USBIN_UV_BIT; -} - -#define HVDCP_NOTIFY_MS 2500 -static void handle_usb_insertion(struct smbchg_chip *chip) -{ - struct power_supply *parallel_psy = get_parallel_psy(chip); - union power_supply_propval pval = {0, }; - enum power_supply_type usb_supply_type; - int rc; - char *usb_type_name = "null"; - - pr_smb(PR_STATUS, "triggered\n"); - /* usb inserted */ - read_usb_type(chip, &usb_type_name, &usb_supply_type); - pr_smb(PR_STATUS, - "inserted type = %d (%s)", usb_supply_type, usb_type_name); - - smbchg_aicl_deglitch_wa_check(chip); - if (chip->typec_psy) - update_typec_status(chip); - smbchg_change_usb_supply_type(chip, usb_supply_type); - - /* Only notify USB if it's not a charger */ - if (usb_supply_type == POWER_SUPPLY_TYPE_USB || - usb_supply_type == POWER_SUPPLY_TYPE_USB_CDP) - extcon_set_cable_state_(chip->extcon, EXTCON_USB, - chip->usb_present); - - /* Notify the USB psy if OV condition is not present */ - if (!chip->usb_ov_det) { - /* - * Note that this could still be a very weak charger - * if the handle_usb_insertion was triggered from - * the falling edge of an USBIN_OV interrupt - */ - pr_smb(PR_MISC, "setting usb psy health %s\n", - chip->very_weak_charger - ? "UNSPEC_FAILURE" : "GOOD"); - chip->usb_health = chip->very_weak_charger - ? POWER_SUPPLY_HEALTH_UNSPEC_FAILURE - : POWER_SUPPLY_HEALTH_GOOD; - power_supply_changed(chip->usb_psy); - } - schedule_work(&chip->usb_set_online_work); - - if (!chip->hvdcp_not_supported && - (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP)) { - cancel_delayed_work_sync(&chip->hvdcp_det_work); - smbchg_stay_awake(chip, PM_DETECT_HVDCP); - schedule_delayed_work(&chip->hvdcp_det_work, - msecs_to_jiffies(HVDCP_NOTIFY_MS)); - } - - if (parallel_psy) { - pval.intval = true; - rc = power_supply_set_property(parallel_psy, - POWER_SUPPLY_PROP_PRESENT, &pval); - chip->parallel_charger_detected = rc ? false : true; - if (rc) - pr_debug("parallel-charger absent rc=%d\n", rc); - } - - if (chip->parallel.avail && chip->aicl_done_irq - && !chip->enable_aicl_wake) { - rc = enable_irq_wake(chip->aicl_done_irq); - chip->enable_aicl_wake = true; - } -} - -void update_usb_status(struct smbchg_chip *chip, bool usb_present, bool force) -{ - mutex_lock(&chip->usb_status_lock); - if (force) { - chip->usb_present = usb_present; - chip->usb_present ? handle_usb_insertion(chip) - : handle_usb_removal(chip); - goto unlock; - } - if (!chip->usb_present && usb_present) { - chip->usb_present = usb_present; - handle_usb_insertion(chip); - } else if (chip->usb_present && !usb_present) { - chip->usb_present = usb_present; - handle_usb_removal(chip); - } - - /* update FG */ - set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS, - get_prop_batt_status(chip)); -unlock: - mutex_unlock(&chip->usb_status_lock); -} - -static int otg_oc_reset(struct smbchg_chip *chip) -{ - int rc; - - rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG, - OTG_EN_BIT, 0); - if (rc) - pr_err("Failed to disable OTG rc=%d\n", rc); - - msleep(20); - - /* - * There is a possibility that an USBID interrupt might have - * occurred notifying USB power supply to disable OTG. We - * should not enable OTG in such cases. - */ - if (!is_otg_present(chip)) { - pr_smb(PR_STATUS, - "OTG is not present, not enabling OTG_EN_BIT\n"); - goto out; - } - - rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG, - OTG_EN_BIT, OTG_EN_BIT); - if (rc) - pr_err("Failed to re-enable OTG rc=%d\n", rc); - -out: - return rc; -} - -static int get_current_time(unsigned long *now_tm_sec) -{ - struct rtc_time tm; - struct rtc_device *rtc; - int rc; - - rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); - if (rtc == NULL) { - pr_err("%s: unable to open rtc device (%s)\n", - __FILE__, CONFIG_RTC_HCTOSYS_DEVICE); - return -EINVAL; - } - - rc = rtc_read_time(rtc, &tm); - if (rc) { - pr_err("Error reading rtc device (%s) : %d\n", - CONFIG_RTC_HCTOSYS_DEVICE, rc); - goto close_time; - } - - rc = rtc_valid_tm(&tm); - if (rc) { - pr_err("Invalid RTC time (%s): %d\n", - CONFIG_RTC_HCTOSYS_DEVICE, rc); - goto close_time; - } - rtc_tm_to_time(&tm, now_tm_sec); - -close_time: - rtc_class_close(rtc); - return rc; -} - -#define AICL_IRQ_LIMIT_SECONDS 60 -#define AICL_IRQ_LIMIT_COUNT 25 -static void increment_aicl_count(struct smbchg_chip *chip) -{ - bool bad_charger = false; - int max_aicl_count, rc; - u8 reg; - long elapsed_seconds; - unsigned long now_seconds; - - pr_smb(PR_INTERRUPT, "aicl count c:%d dgltch:%d first:%ld\n", - chip->aicl_irq_count, chip->aicl_deglitch_short, - chip->first_aicl_seconds); - - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + ICL_STS_1_REG, 1); - if (!rc) - chip->aicl_complete = reg & AICL_STS_BIT; - else - chip->aicl_complete = false; - - if (chip->aicl_deglitch_short || chip->force_aicl_rerun) { - if (!chip->aicl_irq_count) - get_current_time(&chip->first_aicl_seconds); - get_current_time(&now_seconds); - elapsed_seconds = now_seconds - - chip->first_aicl_seconds; - - if (elapsed_seconds > AICL_IRQ_LIMIT_SECONDS) { - pr_smb(PR_INTERRUPT, - "resetting: elp:%ld first:%ld now:%ld c=%d\n", - elapsed_seconds, chip->first_aicl_seconds, - now_seconds, chip->aicl_irq_count); - chip->aicl_irq_count = 1; - get_current_time(&chip->first_aicl_seconds); - return; - } - /* - * Double the amount of AICLs allowed if parallel charging is - * enabled. - */ - max_aicl_count = AICL_IRQ_LIMIT_COUNT - * (chip->parallel.avail ? 2 : 1); - chip->aicl_irq_count++; - - if (chip->aicl_irq_count > max_aicl_count) { - pr_smb(PR_INTERRUPT, "elp:%ld first:%ld now:%ld c=%d\n", - elapsed_seconds, chip->first_aicl_seconds, - now_seconds, chip->aicl_irq_count); - pr_smb(PR_INTERRUPT, "Disable AICL rerun\n"); - chip->very_weak_charger = true; - bad_charger = true; - - /* - * Disable AICL rerun since many interrupts were - * triggered in a short time - */ - /* disable hw aicl */ - rc = vote(chip->hw_aicl_rerun_disable_votable, - WEAK_CHARGER_HW_AICL_VOTER, true, 0); - if (rc < 0) { - pr_err("Couldn't disable hw aicl rerun rc=%d\n", - rc); - return; - } - - /* Vote 100mA current limit */ - rc = vote(chip->usb_icl_votable, WEAK_CHARGER_ICL_VOTER, - true, CURRENT_100_MA); - if (rc < 0) { - pr_err("Can't vote %d current limit rc=%d\n", - CURRENT_100_MA, rc); - } - - chip->aicl_irq_count = 0; - } else if ((get_prop_charge_type(chip) == - POWER_SUPPLY_CHARGE_TYPE_FAST) && - (reg & AICL_SUSP_BIT)) { - /* - * If the AICL_SUSP_BIT is on, then AICL reruns have - * already been disabled. Set the very weak charger - * flag so that the driver reports a bad charger - * and does not reenable AICL reruns. - */ - chip->very_weak_charger = true; - bad_charger = true; - } - if (bad_charger) { - pr_smb(PR_MISC, - "setting usb psy health UNSPEC_FAILURE\n"); - chip->usb_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; - power_supply_changed(chip->usb_psy); - schedule_work(&chip->usb_set_online_work); - } - } -} - -static int wait_for_usbin_uv(struct smbchg_chip *chip, bool high) -{ - int rc; - int tries = 3; - struct completion *completion = &chip->usbin_uv_lowered; - bool usbin_uv; - - if (high) - completion = &chip->usbin_uv_raised; - - while (tries--) { - rc = wait_for_completion_interruptible_timeout( - completion, - msecs_to_jiffies(APSD_TIMEOUT_MS)); - if (rc >= 0) - break; - } - - usbin_uv = is_usbin_uv_high(chip); - - if (high == usbin_uv) - return 0; - - pr_err("usbin uv didnt go to a %s state, still at %s, tries = %d, rc = %d\n", - high ? "risen" : "lowered", - usbin_uv ? "high" : "low", - tries, rc); - return -EINVAL; -} - -static int wait_for_src_detect(struct smbchg_chip *chip, bool high) -{ - int rc; - int tries = 3; - struct completion *completion = &chip->src_det_lowered; - bool src_detect; - - if (high) - completion = &chip->src_det_raised; - - while (tries--) { - rc = wait_for_completion_interruptible_timeout( - completion, - msecs_to_jiffies(APSD_TIMEOUT_MS)); - if (rc >= 0) - break; - } - - src_detect = is_src_detect_high(chip); - - if (high == src_detect) - return 0; - - pr_err("src detect didnt go to a %s state, still at %s, tries = %d, rc = %d\n", - high ? "risen" : "lowered", - src_detect ? "high" : "low", - tries, rc); - return -EINVAL; -} - -static int fake_insertion_removal(struct smbchg_chip *chip, bool insertion) -{ - int rc; - bool src_detect; - bool usbin_uv; - - if (insertion) { - reinit_completion(&chip->src_det_raised); - reinit_completion(&chip->usbin_uv_lowered); - } else { - reinit_completion(&chip->src_det_lowered); - reinit_completion(&chip->usbin_uv_raised); - } - - /* ensure that usbin uv real time status is in the right state */ - usbin_uv = is_usbin_uv_high(chip); - if (usbin_uv != insertion) { - pr_err("Skip faking, usbin uv is already %d\n", usbin_uv); - return -EINVAL; - } - - /* ensure that src_detect real time status is in the right state */ - src_detect = is_src_detect_high(chip); - if (src_detect == insertion) { - pr_err("Skip faking, src detect is already %d\n", src_detect); - return -EINVAL; - } - - pr_smb(PR_MISC, "Allow only %s charger\n", - insertion ? "5-9V" : "9V only"); - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + USBIN_CHGR_CFG, - ADAPTER_ALLOWANCE_MASK, - insertion ? - USBIN_ADAPTER_5V_9V_CONT : USBIN_ADAPTER_9V); - if (rc < 0) { - pr_err("Couldn't write usb allowance rc=%d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Waiting on %s usbin uv\n", - insertion ? "falling" : "rising"); - rc = wait_for_usbin_uv(chip, !insertion); - if (rc < 0) { - pr_err("wait for usbin uv failed rc = %d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Waiting on %s src det\n", - insertion ? "rising" : "falling"); - rc = wait_for_src_detect(chip, insertion); - if (rc < 0) { - pr_err("wait for src detect failed rc = %d\n", rc); - return rc; - } - - return 0; -} - -static int smbchg_prepare_for_pulsing(struct smbchg_chip *chip) -{ - int rc = 0; - u8 reg; - - /* switch to 5V HVDCP */ - pr_smb(PR_MISC, "Switch to 5V HVDCP\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_5V); - if (rc < 0) { - pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc); - goto out; - } - - /* wait for HVDCP to lower to 5V */ - msleep(500); - /* - * Check if the same hvdcp session is in progress. src_det should be - * high and that we are still in 5V hvdcp - */ - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "src det low after 500mS sleep\n"); - goto out; - } - - /* disable HVDCP */ - pr_smb(PR_MISC, "Disable HVDCP\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_EN_BIT, 0); - if (rc < 0) { - pr_err("Couldn't disable HVDCP rc=%d\n", rc); - goto out; - } - - pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n"); - rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300); - if (rc < 0) { - pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc); - goto out; - } - - pr_smb(PR_MISC, "Disable AICL\n"); - smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, 0); - - chip->hvdcp_3_det_ignore_uv = true; - /* fake a removal */ - pr_smb(PR_MISC, "Faking Removal\n"); - rc = fake_insertion_removal(chip, false); - if (rc < 0) { - pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc); - goto handle_removal; - } - - /* disable APSD */ - pr_smb(PR_MISC, "Disabling APSD\n"); - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + APSD_CFG, - AUTO_SRC_DETECT_EN_BIT, 0); - if (rc < 0) { - pr_err("Couldn't disable APSD rc=%d\n", rc); - goto out; - } - - /* fake an insertion */ - pr_smb(PR_MISC, "Faking Insertion\n"); - rc = fake_insertion_removal(chip, true); - if (rc < 0) { - pr_err("Couldn't fake insertion rc=%d\n", rc); - goto handle_removal; - } - chip->hvdcp_3_det_ignore_uv = false; - - pr_smb(PR_MISC, "Enable AICL\n"); - smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, AICL_EN_BIT); - - set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DMF); - /* - * DCP will switch to HVDCP in this time by removing the short - * between DP DM - */ - msleep(HVDCP_NOTIFY_MS); - /* - * Check if the same hvdcp session is in progress. src_det should be - * high and the usb type should be none since APSD was disabled - */ - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "src det low after 2s sleep\n"); - rc = -EINVAL; - goto out; - } - - smbchg_read(chip, ®, chip->misc_base + IDEV_STS, 1); - if ((reg >> TYPE_BITS_OFFSET) != 0) { - pr_smb(PR_MISC, "type bits set after 2s sleep - abort\n"); - rc = -EINVAL; - goto out; - } - - set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DM3P3); - /* Wait 60mS after entering continuous mode */ - msleep(60); - - return 0; -out: - chip->hvdcp_3_det_ignore_uv = false; - restore_from_hvdcp_detection(chip); - return rc; -handle_removal: - chip->hvdcp_3_det_ignore_uv = false; - update_usb_status(chip, 0, 0); - return rc; -} - -static int smbchg_unprepare_for_pulsing(struct smbchg_chip *chip) -{ - int rc = 0; - - if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg)) - rc = regulator_enable(chip->dpdm_reg); - if (rc < 0) { - pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc); - return rc; - } - - /* switch to 9V HVDCP */ - pr_smb(PR_MISC, "Switch to 9V HVDCP\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_9V); - if (rc < 0) { - pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc); - return rc; - } - - /* enable HVDCP */ - pr_smb(PR_MISC, "Enable HVDCP\n"); - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_EN_BIT, HVDCP_EN_BIT); - if (rc < 0) { - pr_err("Couldn't enable HVDCP rc=%d\n", rc); - return rc; - } - - /* enable APSD */ - pr_smb(PR_MISC, "Enabling APSD\n"); - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + APSD_CFG, - AUTO_SRC_DETECT_EN_BIT, AUTO_SRC_DETECT_EN_BIT); - if (rc < 0) { - pr_err("Couldn't enable APSD rc=%d\n", rc); - return rc; - } - - /* Disable AICL */ - pr_smb(PR_MISC, "Disable AICL\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, 0); - if (rc < 0) { - pr_err("Couldn't disable AICL rc=%d\n", rc); - return rc; - } - - /* fake a removal */ - chip->hvdcp_3_det_ignore_uv = true; - pr_smb(PR_MISC, "Faking Removal\n"); - rc = fake_insertion_removal(chip, false); - if (rc < 0) { - pr_err("Couldn't fake removal rc=%d\n", rc); - goto out; - } - - /* - * reset the enabled once flag for parallel charging so - * parallel charging can immediately restart after the HVDCP pulsing - * is complete - */ - chip->parallel.enabled_once = false; - - /* fake an insertion */ - pr_smb(PR_MISC, "Faking Insertion\n"); - rc = fake_insertion_removal(chip, true); - if (rc < 0) { - pr_err("Couldn't fake insertion rc=%d\n", rc); - goto out; - } - chip->hvdcp_3_det_ignore_uv = false; - - /* Enable AICL */ - pr_smb(PR_MISC, "Enable AICL\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, 0); - if (rc < 0) { - pr_err("Couldn't enable AICL rc=%d\n", rc); - return rc; - } - -out: - /* - * There are many QC 2.0 chargers that collapse before the aicl deglitch - * timer can mitigate. Hence set the aicl deglitch time to a shorter - * period. - */ - - rc = vote(chip->aicl_deglitch_short_votable, - HVDCP_SHORT_DEGLITCH_VOTER, true, 0); - if (rc < 0) - pr_err("Couldn't reduce aicl deglitch rc=%d\n", rc); - - pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n"); - rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc); - - chip->hvdcp_3_det_ignore_uv = false; - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "HVDCP removed\n"); - update_usb_status(chip, 0, 0); - } - return rc; -} - -#define USB_CMD_APSD 0x41 -#define APSD_RERUN BIT(0) -static int rerun_apsd(struct smbchg_chip *chip) -{ - int rc; - - reinit_completion(&chip->src_det_raised); - reinit_completion(&chip->usbin_uv_lowered); - reinit_completion(&chip->src_det_lowered); - reinit_completion(&chip->usbin_uv_raised); - - /* re-run APSD */ - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + USB_CMD_APSD, - APSD_RERUN, APSD_RERUN); - if (rc) { - pr_err("Couldn't re-run APSD rc=%d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Waiting on rising usbin uv\n"); - rc = wait_for_usbin_uv(chip, true); - if (rc < 0) { - pr_err("wait for usbin uv failed rc = %d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Waiting on falling src det\n"); - rc = wait_for_src_detect(chip, false); - if (rc < 0) { - pr_err("wait for src detect failed rc = %d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Waiting on falling usbin uv\n"); - rc = wait_for_usbin_uv(chip, false); - if (rc < 0) { - pr_err("wait for usbin uv failed rc = %d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Waiting on rising src det\n"); - rc = wait_for_src_detect(chip, true); - if (rc < 0) { - pr_err("wait for src detect failed rc = %d\n", rc); - return rc; - } - - return rc; -} - -#define SCHG_LITE_USBIN_HVDCP_5_9V 0x8 -#define SCHG_LITE_USBIN_HVDCP_5_9V_SEL_MASK 0x38 -#define SCHG_LITE_USBIN_HVDCP_SEL_IDLE BIT(3) -static bool is_hvdcp_5v_cont_mode(struct smbchg_chip *chip) -{ - int rc; - u8 reg = 0; - - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + USBIN_HVDCP_STS, 1); - if (rc) { - pr_err("Unable to read HVDCP status rc=%d\n", rc); - return false; - } - - pr_smb(PR_STATUS, "HVDCP status = %x\n", reg); - - if (reg & SCHG_LITE_USBIN_HVDCP_SEL_IDLE) { - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + INPUT_STS, 1); - if (rc) { - pr_err("Unable to read INPUT status rc=%d\n", rc); - return false; - } - pr_smb(PR_STATUS, "INPUT status = %x\n", reg); - if ((reg & SCHG_LITE_USBIN_HVDCP_5_9V_SEL_MASK) == - SCHG_LITE_USBIN_HVDCP_5_9V) - return true; - } - return false; -} - -static int smbchg_prepare_for_pulsing_lite(struct smbchg_chip *chip) -{ - int rc = 0; - - /* check if HVDCP is already in 5V continuous mode */ - if (is_hvdcp_5v_cont_mode(chip)) { - pr_smb(PR_MISC, "HVDCP by default is in 5V continuous mode\n"); - return 0; - } - - /* switch to 5V HVDCP */ - pr_smb(PR_MISC, "Switch to 5V HVDCP\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_5V); - if (rc < 0) { - pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc); - goto out; - } - - /* wait for HVDCP to lower to 5V */ - msleep(500); - /* - * Check if the same hvdcp session is in progress. src_det should be - * high and that we are still in 5V hvdcp - */ - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "src det low after 500mS sleep\n"); - goto out; - } - - pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n"); - rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300); - if (rc < 0) { - pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc); - goto out; - } - - pr_smb(PR_MISC, "Disable AICL\n"); - smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, 0); - - chip->hvdcp_3_det_ignore_uv = true; - - /* re-run APSD */ - rc = rerun_apsd(chip); - if (rc) { - pr_err("APSD rerun failed\n"); - goto out; - } - - chip->hvdcp_3_det_ignore_uv = false; - - pr_smb(PR_MISC, "Enable AICL\n"); - smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, - AICL_EN_BIT, AICL_EN_BIT); - /* - * DCP will switch to HVDCP in this time by removing the short - * between DP DM - */ - msleep(HVDCP_NOTIFY_MS); - /* - * Check if the same hvdcp session is in progress. src_det should be - * high and the usb type should be none since APSD was disabled - */ - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "src det low after 2s sleep\n"); - rc = -EINVAL; - goto out; - } - - /* We are set if HVDCP in 5V continuous mode */ - if (!is_hvdcp_5v_cont_mode(chip)) { - pr_err("HVDCP could not be set in 5V continuous mode\n"); - goto out; - } - - return 0; -out: - chip->hvdcp_3_det_ignore_uv = false; - restore_from_hvdcp_detection(chip); - return rc; -} - -static int smbchg_unprepare_for_pulsing_lite(struct smbchg_chip *chip) -{ - int rc = 0; - - pr_smb(PR_MISC, "Forcing 9V HVDCP 2.0\n"); - rc = force_9v_hvdcp(chip); - if (rc) { - pr_err("Failed to force 9V HVDCP=%d\n", rc); - return rc; - } - - pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n"); - rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc); - - return rc; -} - -#define CMD_HVDCP_2 0x43 -#define SINGLE_INCREMENT BIT(0) -#define SINGLE_DECREMENT BIT(1) -static int smbchg_dp_pulse_lite(struct smbchg_chip *chip) -{ - int rc = 0; - - pr_smb(PR_MISC, "Increment DP\n"); - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_HVDCP_2, - SINGLE_INCREMENT, SINGLE_INCREMENT); - if (rc) - pr_err("Single-increment failed rc=%d\n", rc); - - return rc; -} - -static int smbchg_dm_pulse_lite(struct smbchg_chip *chip) -{ - int rc = 0; - - pr_smb(PR_MISC, "Decrement DM\n"); - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_HVDCP_2, - SINGLE_DECREMENT, SINGLE_DECREMENT); - if (rc) - pr_err("Single-decrement failed rc=%d\n", rc); - - return rc; -} - -static int smbchg_hvdcp3_confirmed(struct smbchg_chip *chip) -{ - int rc = 0; - - /* - * reset the enabled once flag for parallel charging because this is - * effectively a new insertion. - */ - chip->parallel.enabled_once = false; - - pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n"); - rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc); - - smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_USB_HVDCP_3); - - return rc; -} - -static int smbchg_dp_dm(struct smbchg_chip *chip, int val) -{ - int rc = 0; - int target_icl_vote_ma; - - switch (val) { - case POWER_SUPPLY_DP_DM_PREPARE: - if (!is_hvdcp_present(chip)) { - pr_err("No pulsing unless HVDCP\n"); - return -ENODEV; - } - if (chip->schg_version == QPNP_SCHG_LITE) - rc = smbchg_prepare_for_pulsing_lite(chip); - else - rc = smbchg_prepare_for_pulsing(chip); - break; - case POWER_SUPPLY_DP_DM_UNPREPARE: - if (chip->schg_version == QPNP_SCHG_LITE) - rc = smbchg_unprepare_for_pulsing_lite(chip); - else - rc = smbchg_unprepare_for_pulsing(chip); - break; - case POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3: - rc = smbchg_hvdcp3_confirmed(chip); - break; - case POWER_SUPPLY_DP_DM_DP_PULSE: - if (chip->schg_version == QPNP_SCHG) - rc = set_usb_psy_dp_dm(chip, - POWER_SUPPLY_DP_DM_DP_PULSE); - else - rc = smbchg_dp_pulse_lite(chip); - if (!rc) - chip->pulse_cnt++; - pr_smb(PR_MISC, "pulse_cnt = %d\n", chip->pulse_cnt); - break; - case POWER_SUPPLY_DP_DM_DM_PULSE: - if (chip->schg_version == QPNP_SCHG) - rc = set_usb_psy_dp_dm(chip, - POWER_SUPPLY_DP_DM_DM_PULSE); - else - rc = smbchg_dm_pulse_lite(chip); - if (!rc && chip->pulse_cnt) - chip->pulse_cnt--; - pr_smb(PR_MISC, "pulse_cnt = %d\n", chip->pulse_cnt); - break; - case POWER_SUPPLY_DP_DM_HVDCP3_SUPPORTED: - chip->hvdcp3_supported = true; - pr_smb(PR_MISC, "HVDCP3 supported\n"); - break; - case POWER_SUPPLY_DP_DM_ICL_DOWN: - chip->usb_icl_delta -= 100; - target_icl_vote_ma = get_client_vote(chip->usb_icl_votable, - PSY_ICL_VOTER); - vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, true, - target_icl_vote_ma + chip->usb_icl_delta); - break; - case POWER_SUPPLY_DP_DM_ICL_UP: - chip->usb_icl_delta += 100; - target_icl_vote_ma = get_client_vote(chip->usb_icl_votable, - PSY_ICL_VOTER); - vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, true, - target_icl_vote_ma + chip->usb_icl_delta); - break; - default: - break; - } - - return rc; -} - -static void update_typec_capability_status(struct smbchg_chip *chip, - const union power_supply_propval *val) -{ - pr_smb(PR_TYPEC, "typec capability = %dma\n", val->intval); - - pr_debug("changing ICL from %dma to %dma\n", chip->typec_current_ma, - val->intval); - chip->typec_current_ma = val->intval; - smbchg_change_usb_supply_type(chip, chip->usb_supply_type); -} - -static void update_typec_otg_status(struct smbchg_chip *chip, int mode, - bool force) -{ - union power_supply_propval pval = {0, }; - pr_smb(PR_TYPEC, "typec mode = %d\n", mode); - - if (mode == POWER_SUPPLY_TYPE_DFP) { - chip->typec_dfp = true; - pval.intval = 1; - extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST, - chip->typec_dfp); - /* update FG */ - set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS, - get_prop_batt_status(chip)); - } else if (force || chip->typec_dfp) { - chip->typec_dfp = false; - pval.intval = 0; - extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST, - chip->typec_dfp); - /* update FG */ - set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS, - get_prop_batt_status(chip)); - } -} - -static int smbchg_usb_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - - switch (psp) { - case POWER_SUPPLY_PROP_CURRENT_MAX: - val->intval = chip->usb_current_max; - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = chip->usb_present; - break; - case POWER_SUPPLY_PROP_ONLINE: - val->intval = chip->usb_online; - break; - case POWER_SUPPLY_PROP_TYPE: - val->intval = chip->usb_supply_type; - break; - case POWER_SUPPLY_PROP_HEALTH: - val->intval = chip->usb_health; - break; - default: - return -EINVAL; - } - return 0; -} - -static int smbchg_usb_set_property(struct power_supply *psy, - enum power_supply_property psp, - const union power_supply_propval *val) -{ - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - - switch (psp) { - case POWER_SUPPLY_PROP_CURRENT_MAX: - chip->usb_current_max = val->intval; - break; - case POWER_SUPPLY_PROP_ONLINE: - chip->usb_online = val->intval; - break; - default: - return -EINVAL; - } - - power_supply_changed(psy); - return 0; -} - -static int -smbchg_usb_is_writeable(struct power_supply *psy, enum power_supply_property psp) -{ - switch (psp) { - case POWER_SUPPLY_PROP_CURRENT_MAX: - return 1; - default: - break; - } - - return 0; -} - - -static char *smbchg_usb_supplicants[] = { - "battery", - "bms", -}; - -static enum power_supply_property smbchg_usb_properties[] = { - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_CURRENT_MAX, - POWER_SUPPLY_PROP_TYPE, - POWER_SUPPLY_PROP_HEALTH, -}; - -#define CHARGE_OUTPUT_VTG_RATIO 840 -static int smbchg_get_iusb(struct smbchg_chip *chip) -{ - int rc, iusb_ua = -EINVAL; - struct qpnp_vadc_result adc_result; - - if (!is_usb_present(chip) && !is_dc_present(chip)) - return 0; - - if (chip->vchg_vadc_dev && chip->vchg_adc_channel != -EINVAL) { - rc = qpnp_vadc_read(chip->vchg_vadc_dev, - chip->vchg_adc_channel, &adc_result); - if (rc) { - pr_smb(PR_STATUS, - "error in VCHG (channel-%d) read rc = %d\n", - chip->vchg_adc_channel, rc); - return 0; - } - iusb_ua = div_s64(adc_result.physical * 1000, - CHARGE_OUTPUT_VTG_RATIO); - } - - return iusb_ua; -} - -static enum power_supply_property smbchg_battery_properties[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, - POWER_SUPPLY_PROP_CHARGING_ENABLED, - POWER_SUPPLY_PROP_CHARGE_TYPE, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, - POWER_SUPPLY_PROP_FLASH_CURRENT_MAX, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, - POWER_SUPPLY_PROP_VOLTAGE_MAX, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE, - POWER_SUPPLY_PROP_INPUT_CURRENT_MAX, - POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, - POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, - POWER_SUPPLY_PROP_FLASH_ACTIVE, - POWER_SUPPLY_PROP_FLASH_TRIGGER, - POWER_SUPPLY_PROP_DP_DM, - POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, - POWER_SUPPLY_PROP_RERUN_AICL, - POWER_SUPPLY_PROP_RESTRICTED_CHARGING, -}; - -static int smbchg_battery_set_property(struct power_supply *psy, - enum power_supply_property prop, - const union power_supply_propval *val) -{ - int rc = 0; - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - - switch (prop) { - case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: - vote(chip->battchg_suspend_votable, BATTCHG_USER_EN_VOTER, - !val->intval, 0); - break; - case POWER_SUPPLY_PROP_CHARGING_ENABLED: - rc = vote(chip->usb_suspend_votable, USER_EN_VOTER, - !val->intval, 0); - rc = vote(chip->dc_suspend_votable, USER_EN_VOTER, - !val->intval, 0); - chip->chg_enabled = val->intval; - schedule_work(&chip->usb_set_online_work); - break; - case POWER_SUPPLY_PROP_CAPACITY: - chip->fake_battery_soc = val->intval; - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - break; - case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: - smbchg_system_temp_level_set(chip, val->intval); - break; - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: - rc = smbchg_set_fastchg_current_user(chip, val->intval / 1000); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - rc = smbchg_float_voltage_set(chip, val->intval); - break; - case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE: - rc = smbchg_safety_timer_enable(chip, val->intval); - break; - case POWER_SUPPLY_PROP_FLASH_ACTIVE: - rc = smbchg_switch_buck_frequency(chip, val->intval); - if (rc) { - pr_err("Couldn't switch buck frequency, rc=%d\n", rc); - /* - * Trigger a panic if there is an error while switching - * buck frequency. This will prevent LS FET damage. - */ - BUG_ON(1); - } - - rc = smbchg_otg_pulse_skip_disable(chip, - REASON_FLASH_ENABLED, val->intval); - break; - case POWER_SUPPLY_PROP_FLASH_TRIGGER: - chip->flash_triggered = !!val->intval; - smbchg_icl_loop_disable_check(chip); - break; - case POWER_SUPPLY_PROP_FORCE_TLIM: - rc = smbchg_force_tlim_en(chip, val->intval); - break; - case POWER_SUPPLY_PROP_DP_DM: - rc = smbchg_dp_dm(chip, val->intval); - break; - case POWER_SUPPLY_PROP_RERUN_AICL: - smbchg_rerun_aicl(chip); - break; - case POWER_SUPPLY_PROP_RESTRICTED_CHARGING: - rc = smbchg_restricted_charging(chip, val->intval); - break; - case POWER_SUPPLY_PROP_CURRENT_CAPABILITY: - if (chip->typec_psy) - update_typec_capability_status(chip, val); - break; - case POWER_SUPPLY_PROP_TYPEC_MODE: - if (chip->typec_psy) - update_typec_otg_status(chip, val->intval, false); - break; - default: - return -EINVAL; - } - - return rc; -} - -static int smbchg_battery_is_writeable(struct power_supply *psy, - enum power_supply_property prop) -{ - int rc; - - switch (prop) { - case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: - case POWER_SUPPLY_PROP_CHARGING_ENABLED: - case POWER_SUPPLY_PROP_CAPACITY: - case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE: - case POWER_SUPPLY_PROP_DP_DM: - case POWER_SUPPLY_PROP_RERUN_AICL: - case POWER_SUPPLY_PROP_RESTRICTED_CHARGING: - rc = 1; - break; - default: - rc = 0; - break; - } - return rc; -} - -static int smbchg_battery_get_property(struct power_supply *psy, - enum power_supply_property prop, - union power_supply_propval *val) -{ - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - - switch (prop) { - case POWER_SUPPLY_PROP_STATUS: - val->intval = get_prop_batt_status(chip); - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = get_prop_batt_present(chip); - break; - case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: - val->intval - = get_effective_result(chip->battchg_suspend_votable); - if (val->intval < 0) /* no votes */ - val->intval = 1; - else - val->intval = !val->intval; - break; - case POWER_SUPPLY_PROP_CHARGING_ENABLED: - val->intval = chip->chg_enabled; - break; - case POWER_SUPPLY_PROP_CHARGE_TYPE: - val->intval = get_prop_charge_type(chip); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - val->intval = smbchg_float_voltage_get(chip); - break; - case POWER_SUPPLY_PROP_HEALTH: - val->intval = get_prop_batt_health(chip); - break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; - break; - case POWER_SUPPLY_PROP_FLASH_CURRENT_MAX: - val->intval = smbchg_calc_max_flash_current(chip); - break; - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: - val->intval = chip->fastchg_current_ma * 1000; - break; - case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: - val->intval = chip->therm_lvl_sel; - break; - case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX: - val->intval = smbchg_get_aicl_level_ma(chip) * 1000; - break; - case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED: - val->intval = (int)chip->aicl_complete; - break; - case POWER_SUPPLY_PROP_RESTRICTED_CHARGING: - val->intval = (int)chip->restricted_charging; - break; - /* properties from fg */ - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = get_prop_batt_capacity(chip); - break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - val->intval = get_prop_batt_current_now(chip); - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = get_prop_batt_voltage_now(chip); - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = get_prop_batt_temp(chip); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = get_prop_batt_voltage_max_design(chip); - break; - case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE: - val->intval = chip->safety_timer_en; - break; - case POWER_SUPPLY_PROP_FLASH_ACTIVE: - val->intval = chip->otg_pulse_skip_dis; - break; - case POWER_SUPPLY_PROP_FLASH_TRIGGER: - val->intval = chip->flash_triggered; - break; - case POWER_SUPPLY_PROP_DP_DM: - val->intval = chip->pulse_cnt; - break; - case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: - val->intval = smbchg_is_input_current_limited(chip); - break; - case POWER_SUPPLY_PROP_RERUN_AICL: - val->intval = 0; - break; - case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW: - val->intval = smbchg_get_iusb(chip); - break; - default: - return -EINVAL; - } - return 0; -} - -static char *smbchg_dc_supplicants[] = { - "bms", -}; - -static enum power_supply_property smbchg_dc_properties[] = { - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_CHARGING_ENABLED, - POWER_SUPPLY_PROP_CURRENT_MAX, -}; - -static int smbchg_dc_set_property(struct power_supply *psy, - enum power_supply_property prop, - const union power_supply_propval *val) -{ - int rc = 0; - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - - switch (prop) { - case POWER_SUPPLY_PROP_CHARGING_ENABLED: - rc = vote(chip->dc_suspend_votable, POWER_SUPPLY_EN_VOTER, - !val->intval, 0); - break; - case POWER_SUPPLY_PROP_CURRENT_MAX: - rc = vote(chip->dc_icl_votable, USER_ICL_VOTER, true, - val->intval / 1000); - break; - default: - return -EINVAL; - } - - return rc; -} - -static int smbchg_dc_get_property(struct power_supply *psy, - enum power_supply_property prop, - union power_supply_propval *val) -{ - struct smbchg_chip *chip = power_supply_get_drvdata(psy); - - switch (prop) { - case POWER_SUPPLY_PROP_PRESENT: - val->intval = is_dc_present(chip); - break; - case POWER_SUPPLY_PROP_CHARGING_ENABLED: - val->intval = get_effective_result(chip->dc_suspend_votable); - if (val->intval < 0) /* no votes */ - val->intval = 1; - else - val->intval = !val->intval; - break; - case POWER_SUPPLY_PROP_ONLINE: - /* return if dc is charging the battery */ - val->intval = (smbchg_get_pwr_path(chip) == PWR_PATH_DC) - && (get_prop_batt_status(chip) - == POWER_SUPPLY_STATUS_CHARGING); - break; - case POWER_SUPPLY_PROP_CURRENT_MAX: - val->intval = chip->dc_max_current_ma * 1000; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int smbchg_dc_is_writeable(struct power_supply *psy, - enum power_supply_property prop) -{ - int rc; - - switch (prop) { - case POWER_SUPPLY_PROP_CHARGING_ENABLED: - case POWER_SUPPLY_PROP_CURRENT_MAX: - rc = 1; - break; - default: - rc = 0; - break; - } - return rc; -} - -#define HOT_BAT_HARD_BIT BIT(0) -#define HOT_BAT_SOFT_BIT BIT(1) -#define COLD_BAT_HARD_BIT BIT(2) -#define COLD_BAT_SOFT_BIT BIT(3) -#define BAT_OV_BIT BIT(4) -#define BAT_LOW_BIT BIT(5) -#define BAT_MISSING_BIT BIT(6) -#define BAT_TERM_MISSING_BIT BIT(7) -static irqreturn_t batt_hot_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1); - chip->batt_hot = !!(reg & HOT_BAT_HARD_BIT); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - smbchg_wipower_check(chip); - set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH, - get_prop_batt_health(chip)); - return IRQ_HANDLED; -} - -static irqreturn_t batt_cold_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1); - chip->batt_cold = !!(reg & COLD_BAT_HARD_BIT); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - smbchg_wipower_check(chip); - set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH, - get_prop_batt_health(chip)); - return IRQ_HANDLED; -} - -static irqreturn_t batt_warm_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1); - chip->batt_warm = !!(reg & HOT_BAT_SOFT_BIT); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH, - get_prop_batt_health(chip)); - return IRQ_HANDLED; -} - -static irqreturn_t batt_cool_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1); - chip->batt_cool = !!(reg & COLD_BAT_SOFT_BIT); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH, - get_prop_batt_health(chip)); - return IRQ_HANDLED; -} - -static irqreturn_t batt_pres_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1); - chip->batt_present = !(reg & BAT_MISSING_BIT); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH, - get_prop_batt_health(chip)); - return IRQ_HANDLED; -} - -static irqreturn_t vbat_low_handler(int irq, void *_chip) -{ - pr_warn_ratelimited("vbat low\n"); - return IRQ_HANDLED; -} - -#define CHG_COMP_SFT_BIT BIT(3) -static irqreturn_t chg_error_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - int rc = 0; - u8 reg; - - pr_smb(PR_INTERRUPT, "chg-error triggered\n"); - - rc = smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc); - } else { - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - if (reg & CHG_COMP_SFT_BIT) - set_property_on_fg(chip, - POWER_SUPPLY_PROP_SAFETY_TIMER_EXPIRED, - 1); - } - - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - smbchg_wipower_check(chip); - return IRQ_HANDLED; -} - -static irqreturn_t fastchg_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - - pr_smb(PR_INTERRUPT, "p2f triggered\n"); - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - smbchg_wipower_check(chip); - return IRQ_HANDLED; -} - -static irqreturn_t chg_hot_handler(int irq, void *_chip) -{ - pr_warn_ratelimited("chg hot\n"); - smbchg_wipower_check(_chip); - return IRQ_HANDLED; -} - -static irqreturn_t chg_term_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - - pr_smb(PR_INTERRUPT, "tcc triggered\n"); - /* - * Charge termination is a pulse and not level triggered. That means, - * TCC bit in RT_STS can get cleared by the time this interrupt is - * handled. Instead of relying on that to determine whether the - * charge termination had happened, we've to simply notify the FG - * about this as long as the interrupt is handled. - */ - set_property_on_fg(chip, POWER_SUPPLY_PROP_CHARGE_DONE, 1); - - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - - return IRQ_HANDLED; -} - -static irqreturn_t taper_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - taper_irq_en(chip, false); - smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - smbchg_parallel_usb_taper(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - smbchg_wipower_check(chip); - return IRQ_HANDLED; -} - -static irqreturn_t recharge_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->chgr_base + RT_STS, 1); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - smbchg_parallel_usb_check_ok(chip); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - return IRQ_HANDLED; -} - -static irqreturn_t wdog_timeout_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->misc_base + RT_STS, 1); - pr_warn_ratelimited("wdog timeout rt_stat = 0x%02x\n", reg); - if (chip->batt_psy) - power_supply_changed(chip->batt_psy); - smbchg_charging_status_change(chip); - return IRQ_HANDLED; -} - -/** - * power_ok_handler() - called when the switcher turns on or turns off - * @chip: pointer to smbchg_chip - * @rt_stat: the status bit indicating switcher turning on or off - */ -static irqreturn_t power_ok_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - u8 reg = 0; - - smbchg_read(chip, ®, chip->misc_base + RT_STS, 1); - pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg); - return IRQ_HANDLED; -} - -/** - * dcin_uv_handler() - called when the dc voltage crosses the uv threshold - * @chip: pointer to smbchg_chip - * @rt_stat: the status bit indicating whether dc voltage is uv - */ -#define DCIN_UNSUSPEND_DELAY_MS 1000 -static irqreturn_t dcin_uv_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - bool dc_present = is_dc_present(chip); - - pr_smb(PR_STATUS, "chip->dc_present = %d dc_present = %d\n", - chip->dc_present, dc_present); - - if (chip->dc_present != dc_present) { - /* dc changed */ - chip->dc_present = dc_present; - if (chip->dc_psy_type != -EINVAL && chip->batt_psy) - power_supply_changed(chip->dc_psy); - smbchg_charging_status_change(chip); - smbchg_aicl_deglitch_wa_check(chip); - chip->vbat_above_headroom = false; - } - - smbchg_wipower_check(chip); - return IRQ_HANDLED; -} - -/** - * usbin_ov_handler() - this is called when an overvoltage condition occurs - * @chip: pointer to smbchg_chip chip - */ -static irqreturn_t usbin_ov_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - int rc; - u8 reg; - bool usb_present; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc); - goto out; - } - - /* OV condition is detected. Notify it to USB psy */ - if (reg & USBIN_OV_BIT) { - chip->usb_ov_det = true; - pr_smb(PR_MISC, "setting usb psy health OV\n"); - chip->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; - power_supply_changed(chip->usb_psy); - } else { - chip->usb_ov_det = false; - /* If USB is present, then handle the USB insertion */ - usb_present = is_usb_present(chip); - if (usb_present) - update_usb_status(chip, usb_present, false); - } -out: - return IRQ_HANDLED; -} - -/** - * usbin_uv_handler() - this is called when USB charger is removed - * @chip: pointer to smbchg_chip chip - * @rt_stat: the status bit indicating chg insertion/removal - */ -#define ICL_MODE_MASK SMB_MASK(5, 4) -#define ICL_MODE_HIGH_CURRENT 0 -static irqreturn_t usbin_uv_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - int aicl_level = smbchg_get_aicl_level_ma(chip); - int rc; - u8 reg; - - rc = smbchg_read(chip, ®, chip->usb_chgpth_base + RT_STS, 1); - if (rc) { - pr_err("could not read rt sts: %d", rc); - goto out; - } - - pr_smb(PR_STATUS, - "%s chip->usb_present = %d rt_sts = 0x%02x hvdcp_3_det_ignore_uv = %d aicl = %d\n", - chip->hvdcp_3_det_ignore_uv ? "Ignoring":"", - chip->usb_present, reg, chip->hvdcp_3_det_ignore_uv, - aicl_level); - - /* - * set usb_psy's dp=f dm=f if this is a new insertion, i.e. it is - * not already src_detected and usbin_uv is seen falling - */ - if (!(reg & USBIN_UV_BIT) && !(reg & USBIN_SRC_DET_BIT)) { - pr_smb(PR_MISC, "setting usb dp=f dm=f\n"); - if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg)) - rc = regulator_enable(chip->dpdm_reg); - if (rc < 0) { - pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc); - return rc; - } - } - - if (reg & USBIN_UV_BIT) - complete_all(&chip->usbin_uv_raised); - else - complete_all(&chip->usbin_uv_lowered); - - if (chip->hvdcp_3_det_ignore_uv) - goto out; - - if ((reg & USBIN_UV_BIT) && (reg & USBIN_SRC_DET_BIT)) { - pr_smb(PR_STATUS, "Very weak charger detected\n"); - chip->very_weak_charger = true; - rc = smbchg_read(chip, ®, - chip->usb_chgpth_base + ICL_STS_2_REG, 1); - if (rc) { - dev_err(chip->dev, "Could not read usb icl sts 2: %d\n", - rc); - goto out; - } - if ((reg & ICL_MODE_MASK) != ICL_MODE_HIGH_CURRENT) { - /* - * If AICL is not even enabled, this is either an - * SDP or a grossly out of spec charger. Do not - * draw any current from it. - */ - rc = vote(chip->usb_suspend_votable, - WEAK_CHARGER_EN_VOTER, true, 0); - if (rc < 0) - pr_err("could not disable charger: %d", rc); - } else if (aicl_level == chip->tables.usb_ilim_ma_table[0]) { - /* - * we are in a situation where the adapter is not able - * to supply even 300mA. Disable hw aicl reruns else it - * is only a matter of time when we get back here again - */ - rc = vote(chip->hw_aicl_rerun_disable_votable, - WEAK_CHARGER_HW_AICL_VOTER, true, 0); - if (rc < 0) - pr_err("Couldn't disable hw aicl rerun rc=%d\n", - rc); - } - pr_smb(PR_MISC, "setting usb psy health UNSPEC_FAILURE\n"); - chip->usb_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; - power_supply_changed(chip->usb_psy); - schedule_work(&chip->usb_set_online_work); - } - - smbchg_wipower_check(chip); -out: - return IRQ_HANDLED; -} - -/** - * src_detect_handler() - this is called on rising edge when USB charger type - * is detected and on falling edge when USB voltage falls - * below the coarse detect voltage(1V), use it for - * handling USB charger insertion and removal. - * @chip: pointer to smbchg_chip - * @rt_stat: the status bit indicating chg insertion/removal - */ -static irqreturn_t src_detect_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - bool usb_present = is_usb_present(chip); - bool src_detect = is_src_detect_high(chip); - int rc; - - pr_smb(PR_STATUS, - "%s chip->usb_present = %d usb_present = %d src_detect = %d hvdcp_3_det_ignore_uv=%d\n", - chip->hvdcp_3_det_ignore_uv ? "Ignoring":"", - chip->usb_present, usb_present, src_detect, - chip->hvdcp_3_det_ignore_uv); - - if (src_detect) - complete_all(&chip->src_det_raised); - else - complete_all(&chip->src_det_lowered); - - if (chip->hvdcp_3_det_ignore_uv) - goto out; - - /* - * When VBAT is above the AICL threshold (4.25V) - 180mV (4.07V), - * an input collapse due to AICL will actually cause an USBIN_UV - * interrupt to fire as well. - * - * Handle USB insertions and removals in the source detect handler - * instead of the USBIN_UV handler since the latter is untrustworthy - * when the battery voltage is high. - */ - chip->very_weak_charger = false; - /* - * a src detect marks a new insertion or a real removal, - * vote for enable aicl hw reruns - */ - rc = vote(chip->hw_aicl_rerun_disable_votable, - WEAK_CHARGER_HW_AICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't enable hw aicl rerun rc=%d\n", rc); - - rc = vote(chip->usb_suspend_votable, WEAK_CHARGER_EN_VOTER, false, 0); - if (rc < 0) - pr_err("could not enable charger: %d\n", rc); - - if (src_detect) { - update_usb_status(chip, usb_present, 0); - } else { - update_usb_status(chip, 0, false); - chip->aicl_irq_count = 0; - } -out: - return IRQ_HANDLED; -} - -/** - * otg_oc_handler() - called when the usb otg goes over current - */ -#define NUM_OTG_RETRIES 5 -#define OTG_OC_RETRY_DELAY_US 50000 -static irqreturn_t otg_oc_handler(int irq, void *_chip) -{ - int rc; - struct smbchg_chip *chip = _chip; - s64 elapsed_us = ktime_us_delta(ktime_get(), chip->otg_enable_time); - - pr_smb(PR_INTERRUPT, "triggered\n"); - - if (chip->schg_version == QPNP_SCHG_LITE) { - pr_warn("OTG OC triggered - OTG disabled\n"); - return IRQ_HANDLED; - } - - if (elapsed_us > OTG_OC_RETRY_DELAY_US) - chip->otg_retries = 0; - - /* - * Due to a HW bug in the PMI8994 charger, the current inrush that - * occurs when connecting certain OTG devices can cause the OTG - * overcurrent protection to trip. - * - * The work around is to try reenabling the OTG when getting an - * overcurrent interrupt once. - */ - if (chip->otg_retries < NUM_OTG_RETRIES) { - chip->otg_retries += 1; - pr_smb(PR_STATUS, - "Retrying OTG enable. Try #%d, elapsed_us %lld\n", - chip->otg_retries, elapsed_us); - rc = otg_oc_reset(chip); - if (rc) - pr_err("Failed to reset OTG OC state rc=%d\n", rc); - chip->otg_enable_time = ktime_get(); - } - return IRQ_HANDLED; -} - -/** - * otg_fail_handler() - called when the usb otg fails - * (when vbat < OTG UVLO threshold) - */ -static irqreturn_t otg_fail_handler(int irq, void *_chip) -{ - pr_smb(PR_INTERRUPT, "triggered\n"); - return IRQ_HANDLED; -} - -/** - * aicl_done_handler() - called when the usb AICL algorithm is finished - * and a current is set. - */ -static irqreturn_t aicl_done_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - bool usb_present = is_usb_present(chip); - int aicl_level = smbchg_get_aicl_level_ma(chip); - - pr_smb(PR_INTERRUPT, "triggered, aicl: %d\n", aicl_level); - - increment_aicl_count(chip); - - if (usb_present) - smbchg_parallel_usb_check_ok(chip); - - if (chip->aicl_complete && chip->batt_psy) - power_supply_changed(chip->batt_psy); - - return IRQ_HANDLED; -} - -/** - * usbid_change_handler() - called when the usb RID changes. - * This is used mostly for detecting OTG - */ -static irqreturn_t usbid_change_handler(int irq, void *_chip) -{ - struct smbchg_chip *chip = _chip; - bool otg_present; - - pr_smb(PR_INTERRUPT, "triggered\n"); - - otg_present = is_otg_present(chip); - pr_smb(PR_MISC, "setting usb psy OTG = %d\n", - otg_present ? 1 : 0); - - extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST, otg_present); - - if (otg_present) - pr_smb(PR_STATUS, "OTG detected\n"); - - /* update FG */ - set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS, - get_prop_batt_status(chip)); - - return IRQ_HANDLED; -} - -static int determine_initial_status(struct smbchg_chip *chip) -{ - union power_supply_propval type = {0, }; - - /* - * It is okay to read the interrupt status here since - * interrupts aren't requested. reading interrupt status - * clears the interrupt so be careful to read interrupt - * status only in interrupt handling code - */ - - batt_pres_handler(0, chip); - batt_hot_handler(0, chip); - batt_warm_handler(0, chip); - batt_cool_handler(0, chip); - batt_cold_handler(0, chip); - if (chip->typec_psy) { - get_property_from_typec(chip, POWER_SUPPLY_PROP_TYPE, &type); - update_typec_otg_status(chip, type.intval, true); - } else { - usbid_change_handler(0, chip); - } - src_detect_handler(0, chip); - - chip->usb_present = is_usb_present(chip); - chip->dc_present = is_dc_present(chip); - - if (chip->usb_present) { - int rc = 0; - pr_smb(PR_MISC, "setting usb dp=f dm=f\n"); - if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg)) - rc = regulator_enable(chip->dpdm_reg); - if (rc < 0) { - pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc); - return rc; - } - handle_usb_insertion(chip); - } else { - handle_usb_removal(chip); - } - - return 0; -} - -static int prechg_time[] = { - 24, - 48, - 96, - 192, -}; -static int chg_time[] = { - 192, - 384, - 768, - 1536, -}; - -enum bpd_type { - BPD_TYPE_BAT_NONE, - BPD_TYPE_BAT_ID, - BPD_TYPE_BAT_THM, - BPD_TYPE_BAT_THM_BAT_ID, - BPD_TYPE_DEFAULT, -}; - -static const char * const bpd_label[] = { - [BPD_TYPE_BAT_NONE] = "bpd_none", - [BPD_TYPE_BAT_ID] = "bpd_id", - [BPD_TYPE_BAT_THM] = "bpd_thm", - [BPD_TYPE_BAT_THM_BAT_ID] = "bpd_thm_id", -}; - -static inline int get_bpd(const char *name) -{ - int i = 0; - for (i = 0; i < ARRAY_SIZE(bpd_label); i++) { - if (strcmp(bpd_label[i], name) == 0) - return i; - } - return -EINVAL; -} - -#define REVISION1_REG 0x0 -#define DIG_MINOR 0 -#define DIG_MAJOR 1 -#define ANA_MINOR 2 -#define ANA_MAJOR 3 -#define CHGR_CFG1 0xFB -#define RECHG_THRESHOLD_SRC_BIT BIT(1) -#define TERM_I_SRC_BIT BIT(2) -#define TERM_SRC_FG BIT(2) -#define CHG_INHIB_CFG_REG 0xF7 -#define CHG_INHIBIT_50MV_VAL 0x00 -#define CHG_INHIBIT_100MV_VAL 0x01 -#define CHG_INHIBIT_200MV_VAL 0x02 -#define CHG_INHIBIT_300MV_VAL 0x03 -#define CHG_INHIBIT_MASK 0x03 -#define USE_REGISTER_FOR_CURRENT BIT(2) -#define CHGR_CFG2 0xFC -#define CHG_EN_SRC_BIT BIT(7) -#define CHG_EN_POLARITY_BIT BIT(6) -#define P2F_CHG_TRAN BIT(5) -#define CHG_BAT_OV_ECC BIT(4) -#define I_TERM_BIT BIT(3) -#define AUTO_RECHG_BIT BIT(2) -#define CHARGER_INHIBIT_BIT BIT(0) -#define USB51_COMMAND_POL BIT(2) -#define USB51AC_CTRL BIT(1) -#define TR_8OR32B 0xFE -#define BUCK_8_16_FREQ_BIT BIT(0) -#define BM_CFG 0xF3 -#define BATT_MISSING_ALGO_BIT BIT(2) -#define BMD_PIN_SRC_MASK SMB_MASK(1, 0) -#define PIN_SRC_SHIFT 0 -#define CHGR_CFG 0xFF -#define RCHG_LVL_BIT BIT(0) -#define VCHG_EN_BIT BIT(1) -#define VCHG_INPUT_CURRENT_BIT BIT(3) -#define CFG_AFVC 0xF6 -#define VFLOAT_COMP_ENABLE_MASK SMB_MASK(2, 0) -#define TR_RID_REG 0xFA -#define FG_INPUT_FET_DELAY_BIT BIT(3) -#define TRIM_OPTIONS_7_0 0xF6 -#define INPUT_MISSING_POLLER_EN_BIT BIT(3) -#define CHGR_CCMP_CFG 0xFA -#define JEITA_TEMP_HARD_LIMIT_BIT BIT(5) -#define HVDCP_ADAPTER_SEL_MASK SMB_MASK(5, 4) -#define HVDCP_ADAPTER_SEL_9V_BIT BIT(4) -#define HVDCP_AUTH_ALG_EN_BIT BIT(6) -#define CMD_APSD 0x41 -#define APSD_RERUN_BIT BIT(0) -#define OTG_CFG 0xF1 -#define HICCUP_ENABLED_BIT BIT(6) -#define OTG_PIN_POLARITY_BIT BIT(4) -#define OTG_PIN_ACTIVE_LOW BIT(4) -#define OTG_EN_CTRL_MASK SMB_MASK(3, 2) -#define OTG_PIN_CTRL_RID_DIS 0x04 -#define OTG_CMD_CTRL_RID_EN 0x08 -#define AICL_ADC_BIT BIT(6) -static void batt_ov_wa_check(struct smbchg_chip *chip) -{ - int rc; - u8 reg; - - /* disable-'battery OV disables charging' feature */ - rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG2, - CHG_BAT_OV_ECC, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc); - return; - } - - /* - * if battery OV is set: - * restart charging by disable/enable charging - */ - rc = smbchg_read(chip, ®, chip->bat_if_base + RT_STS, 1); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't read Battery RT status rc = %d\n", rc); - return; - } - - if (reg & BAT_OV_BIT) { - rc = smbchg_charging_en(chip, false); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't disable charging: rc = %d\n", rc); - return; - } - - /* delay for charging-disable to take affect */ - msleep(200); - - rc = smbchg_charging_en(chip, true); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't enable charging: rc = %d\n", rc); - return; - } - } -} - -static int smbchg_hw_init(struct smbchg_chip *chip) -{ - int rc, i; - u8 reg, mask; - - rc = smbchg_read(chip, chip->revision, - chip->misc_base + REVISION1_REG, 4); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read revision rc=%d\n", - rc); - return rc; - } - pr_smb(PR_STATUS, "Charger Revision DIG: %d.%d; ANA: %d.%d\n", - chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR], - chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]); - - /* Setup 9V HVDCP */ - if (!chip->hvdcp_not_supported) { - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_9V); - if (rc < 0) { - pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", - rc); - return rc; - } - } - - if (chip->aicl_rerun_period_s > 0) { - rc = smbchg_set_aicl_rerun_period_s(chip, - chip->aicl_rerun_period_s); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set AICL rerun timer rc=%d\n", - rc); - return rc; - } - } - - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + TR_RID_REG, - FG_INPUT_FET_DELAY_BIT, FG_INPUT_FET_DELAY_BIT); - if (rc < 0) { - dev_err(chip->dev, "Couldn't disable fg input fet delay rc=%d\n", - rc); - return rc; - } - - rc = smbchg_sec_masked_write(chip, chip->misc_base + TRIM_OPTIONS_7_0, - INPUT_MISSING_POLLER_EN_BIT, - INPUT_MISSING_POLLER_EN_BIT); - if (rc < 0) { - dev_err(chip->dev, "Couldn't enable input missing poller rc=%d\n", - rc); - return rc; - } - - /* - * Do not force using current from the register i.e. use auto - * power source detect (APSD) mA ratings for the initial current values. - * - * If this is set, AICL will not rerun at 9V for HVDCPs - */ - rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL, - USE_REGISTER_FOR_CURRENT, 0); - - if (rc < 0) { - dev_err(chip->dev, "Couldn't set input limit cmd rc=%d\n", rc); - return rc; - } - - /* - * set chg en by cmd register, set chg en by writing bit 1, - * enable auto pre to fast, enable auto recharge by default. - * enable current termination and charge inhibition based on - * the device tree configuration. - */ - rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG2, - CHG_EN_SRC_BIT | CHG_EN_POLARITY_BIT | P2F_CHG_TRAN - | I_TERM_BIT | AUTO_RECHG_BIT | CHARGER_INHIBIT_BIT, - CHG_EN_POLARITY_BIT - | (chip->chg_inhibit_en ? CHARGER_INHIBIT_BIT : 0) - | (chip->iterm_disabled ? I_TERM_BIT : 0)); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc); - return rc; - } - - /* - * enable battery charging to make sure it hasn't been changed earlier - * by the bootloader. - */ - rc = smbchg_charging_en(chip, true); - if (rc < 0) { - dev_err(chip->dev, "Couldn't enable battery charging=%d\n", rc); - return rc; - } - - /* - * Based on the configuration, use the analog sensors or the fuelgauge - * adc for recharge threshold source. - */ - - if (chip->chg_inhibit_source_fg) - rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1, - TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT, - TERM_SRC_FG | RECHG_THRESHOLD_SRC_BIT); - else - rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1, - TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT, 0); - - if (rc < 0) { - dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc); - return rc; - } - - /* - * control USB suspend via command bits and set correct 100/500mA - * polarity on the usb current - */ - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - USB51_COMMAND_POL | USB51AC_CTRL, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set usb_chgpth cfg rc=%d\n", rc); - return rc; - } - - check_battery_type(chip); - - /* set the float voltage */ - if (chip->vfloat_mv != -EINVAL) { - rc = smbchg_float_voltage_set(chip, chip->vfloat_mv); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set float voltage rc = %d\n", rc); - return rc; - } - pr_smb(PR_STATUS, "set vfloat to %d\n", chip->vfloat_mv); - } - - /* set the fast charge current compensation */ - if (chip->fastchg_current_comp != -EINVAL) { - rc = smbchg_fastchg_current_comp_set(chip, - chip->fastchg_current_comp); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n", - rc); - return rc; - } - pr_smb(PR_STATUS, "set fastchg current comp to %d\n", - chip->fastchg_current_comp); - } - - /* set the float voltage compensation */ - if (chip->float_voltage_comp != -EINVAL) { - rc = smbchg_float_voltage_comp_set(chip, - chip->float_voltage_comp); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n", - rc); - return rc; - } - pr_smb(PR_STATUS, "set float voltage comp to %d\n", - chip->float_voltage_comp); - } - - /* set iterm */ - if (chip->iterm_ma != -EINVAL) { - if (chip->iterm_disabled) { - dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n"); - return -EINVAL; - } else { - smbchg_iterm_set(chip, chip->iterm_ma); - } - } - - /* set the safety time voltage */ - if (chip->safety_time != -EINVAL) { - reg = (chip->safety_time > 0 ? 0 : SFT_TIMER_DISABLE_BIT) | - (chip->prechg_safety_time > 0 - ? 0 : PRECHG_SFT_TIMER_DISABLE_BIT); - - for (i = 0; i < ARRAY_SIZE(chg_time); i++) { - if (chip->safety_time <= chg_time[i]) { - reg |= i << SAFETY_TIME_MINUTES_SHIFT; - break; - } - } - for (i = 0; i < ARRAY_SIZE(prechg_time); i++) { - if (chip->prechg_safety_time <= prechg_time[i]) { - reg |= i; - break; - } - } - - rc = smbchg_sec_masked_write(chip, - chip->chgr_base + SFT_CFG, - SFT_EN_MASK | SFT_TO_MASK | - (chip->prechg_safety_time > 0 - ? PRECHG_SFT_TO_MASK : 0), reg); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set safety timer rc = %d\n", - rc); - return rc; - } - chip->safety_timer_en = true; - } else { - rc = smbchg_read(chip, ®, chip->chgr_base + SFT_CFG, 1); - if (rc < 0) - dev_err(chip->dev, "Unable to read SFT_CFG rc = %d\n", - rc); - else if (!(reg & SFT_EN_MASK)) - chip->safety_timer_en = true; - } - - /* configure jeita temperature hard limit */ - if (chip->jeita_temp_hard_limit >= 0) { - rc = smbchg_sec_masked_write(chip, - chip->chgr_base + CHGR_CCMP_CFG, - JEITA_TEMP_HARD_LIMIT_BIT, - chip->jeita_temp_hard_limit - ? 0 : JEITA_TEMP_HARD_LIMIT_BIT); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't set jeita temp hard limit rc = %d\n", - rc); - return rc; - } - } - - /* make the buck switch faster to prevent some vbus oscillation */ - rc = smbchg_sec_masked_write(chip, - chip->usb_chgpth_base + TR_8OR32B, - BUCK_8_16_FREQ_BIT, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set buck frequency rc = %d\n", rc); - return rc; - } - - /* battery missing detection */ - mask = BATT_MISSING_ALGO_BIT; - reg = chip->bmd_algo_disabled ? 0 : BATT_MISSING_ALGO_BIT; - if (chip->bmd_pin_src < BPD_TYPE_DEFAULT) { - mask |= BMD_PIN_SRC_MASK; - reg |= chip->bmd_pin_src << PIN_SRC_SHIFT; - } - rc = smbchg_sec_masked_write(chip, - chip->bat_if_base + BM_CFG, mask, reg); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set batt_missing config = %d\n", - rc); - return rc; - } - - if (chip->vchg_adc_channel != -EINVAL) { - /* configure and enable VCHG */ - rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG, - VCHG_INPUT_CURRENT_BIT | VCHG_EN_BIT, - VCHG_INPUT_CURRENT_BIT | VCHG_EN_BIT); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set recharge rc = %d\n", - rc); - return rc; - } - } - - smbchg_charging_status_change(chip); - - vote(chip->usb_suspend_votable, USER_EN_VOTER, !chip->chg_enabled, 0); - vote(chip->dc_suspend_votable, USER_EN_VOTER, !chip->chg_enabled, 0); - /* resume threshold */ - if (chip->resume_delta_mv != -EINVAL) { - - /* - * Configure only if the recharge threshold source is not - * fuel gauge ADC. - */ - if (!chip->chg_inhibit_source_fg) { - if (chip->resume_delta_mv < 100) - reg = CHG_INHIBIT_50MV_VAL; - else if (chip->resume_delta_mv < 200) - reg = CHG_INHIBIT_100MV_VAL; - else if (chip->resume_delta_mv < 300) - reg = CHG_INHIBIT_200MV_VAL; - else - reg = CHG_INHIBIT_300MV_VAL; - - rc = smbchg_sec_masked_write(chip, - chip->chgr_base + CHG_INHIB_CFG_REG, - CHG_INHIBIT_MASK, reg); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set inhibit val rc = %d\n", - rc); - return rc; - } - } - - rc = smbchg_sec_masked_write(chip, - chip->chgr_base + CHGR_CFG, - RCHG_LVL_BIT, - (chip->resume_delta_mv - < chip->tables.rchg_thr_mv) - ? 0 : RCHG_LVL_BIT); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set recharge rc = %d\n", - rc); - return rc; - } - } - - /* DC path current settings */ - if (chip->dc_psy_type != -EINVAL) { - rc = vote(chip->dc_icl_votable, PSY_ICL_VOTER, true, - chip->dc_target_current_ma); - if (rc < 0) { - dev_err(chip->dev, - "Couldn't vote for initial DC ICL rc=%d\n", rc); - return rc; - } - } - - - /* - * on some devices the battery is powered via external sources which - * could raise its voltage above the float voltage. smbchargers go - * in to reverse boost in such a situation and the workaround is to - * disable float voltage compensation (note that the battery will appear - * hot/cold when powered via external source). - */ - if (chip->soft_vfloat_comp_disabled) { - rc = smbchg_sec_masked_write(chip, chip->chgr_base + CFG_AFVC, - VFLOAT_COMP_ENABLE_MASK, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't disable soft vfloat rc = %d\n", - rc); - return rc; - } - } - - rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true, - chip->cfg_fastchg_current_ma); - if (rc < 0) { - dev_err(chip->dev, "Couldn't vote fastchg ma rc = %d\n", rc); - return rc; - } - - rc = smbchg_read(chip, &chip->original_usbin_allowance, - chip->usb_chgpth_base + USBIN_CHGR_CFG, 1); - if (rc < 0) - dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc); - - if (chip->wipower_dyn_icl_avail) { - rc = smbchg_wipower_ilim_config(chip, - &(chip->wipower_default.entries[0])); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set default wipower ilim = %d\n", - rc); - return rc; - } - } - /* unsuspend dc path, it could be suspended by the bootloader */ - rc = smbchg_dc_suspend(chip, 0); - if (rc < 0) { - dev_err(chip->dev, "Couldn't unsuspend dc path= %d\n", rc); - return rc; - } - - if (chip->force_aicl_rerun) { - /* vote to enable hw aicl */ - rc = vote(chip->hw_aicl_rerun_enable_indirect_votable, - DEFAULT_CONFIG_HW_AICL_VOTER, true, 0); - if (rc < 0) { - pr_err("Couldn't vote enable hw aicl rerun rc=%d\n", - rc); - return rc; - } - } - - if (chip->schg_version == QPNP_SCHG_LITE) { - /* enable OTG hiccup mode */ - rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_CFG, - HICCUP_ENABLED_BIT, HICCUP_ENABLED_BIT); - if (rc < 0) - dev_err(chip->dev, "Couldn't set OTG OC config rc = %d\n", - rc); - } - - if (chip->otg_pinctrl) { - /* configure OTG enable to pin control active low */ - rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_CFG, - OTG_PIN_POLARITY_BIT | OTG_EN_CTRL_MASK, - OTG_PIN_ACTIVE_LOW | OTG_PIN_CTRL_RID_DIS); - if (rc < 0) { - dev_err(chip->dev, "Couldn't set OTG EN config rc = %d\n", - rc); - return rc; - } - } - - if (chip->wa_flags & SMBCHG_BATT_OV_WA) - batt_ov_wa_check(chip); - - /* turn off AICL adc for improved accuracy */ - rc = smbchg_sec_masked_write(chip, - chip->misc_base + MISC_TRIM_OPT_15_8, AICL_ADC_BIT, 0); - if (rc) - pr_err("Couldn't write to MISC_TRIM_OPTIONS_15_8 rc=%d\n", - rc); - - return rc; -} - -static struct of_device_id smbchg_match_table[] = { - { - .compatible = "qcom,qpnp-smbcharger", - }, - { }, -}; - -#define DC_MA_MIN 300 -#define DC_MA_MAX 2000 -#define OF_PROP_READ(chip, prop, dt_property, retval, optional) \ -do { \ - if (retval) \ - break; \ - if (optional) \ - prop = -EINVAL; \ - \ - retval = of_property_read_u32(chip->pdev->dev.of_node, \ - "qcom," dt_property , \ - &prop); \ - \ - if ((retval == -EINVAL) && optional) \ - retval = 0; \ - else if (retval) \ - dev_err(chip->dev, "Error reading " #dt_property \ - " property rc = %d\n", rc); \ -} while (0) - -#define ILIM_ENTRIES 3 -#define VOLTAGE_RANGE_ENTRIES 2 -#define RANGE_ENTRY (ILIM_ENTRIES + VOLTAGE_RANGE_ENTRIES) -static int smb_parse_wipower_map_dt(struct smbchg_chip *chip, - struct ilim_map *map, char *property) -{ - struct device_node *node = chip->dev->of_node; - int total_elements, size; - struct property *prop; - const __be32 *data; - int num, i; - - prop = of_find_property(node, property, &size); - if (!prop) { - dev_err(chip->dev, "%s missing\n", property); - return -EINVAL; - } - - total_elements = size / sizeof(int); - if (total_elements % RANGE_ENTRY) { - dev_err(chip->dev, "%s table not in multiple of %d, total elements = %d\n", - property, RANGE_ENTRY, total_elements); - return -EINVAL; - } - - data = prop->value; - num = total_elements / RANGE_ENTRY; - map->entries = devm_kzalloc(chip->dev, - num * sizeof(struct ilim_entry), GFP_KERNEL); - if (!map->entries) { - dev_err(chip->dev, "kzalloc failed for default ilim\n"); - return -ENOMEM; - } - for (i = 0; i < num; i++) { - map->entries[i].vmin_uv = be32_to_cpup(data++); - map->entries[i].vmax_uv = be32_to_cpup(data++); - map->entries[i].icl_pt_ma = be32_to_cpup(data++); - map->entries[i].icl_lv_ma = be32_to_cpup(data++); - map->entries[i].icl_hv_ma = be32_to_cpup(data++); - } - map->num = num; - return 0; -} - -static int smb_parse_wipower_dt(struct smbchg_chip *chip) -{ - int rc = 0; - - chip->wipower_dyn_icl_avail = false; - - if (!chip->vadc_dev) - goto err; - - rc = smb_parse_wipower_map_dt(chip, &chip->wipower_default, - "qcom,wipower-default-ilim-map"); - if (rc) { - dev_err(chip->dev, "failed to parse wipower-pt-ilim-map rc = %d\n", - rc); - goto err; - } - - rc = smb_parse_wipower_map_dt(chip, &chip->wipower_pt, - "qcom,wipower-pt-ilim-map"); - if (rc) { - dev_err(chip->dev, "failed to parse wipower-pt-ilim-map rc = %d\n", - rc); - goto err; - } - - rc = smb_parse_wipower_map_dt(chip, &chip->wipower_div2, - "qcom,wipower-div2-ilim-map"); - if (rc) { - dev_err(chip->dev, "failed to parse wipower-div2-ilim-map rc = %d\n", - rc); - goto err; - } - chip->wipower_dyn_icl_avail = true; - return 0; -err: - chip->wipower_default.num = 0; - chip->wipower_pt.num = 0; - chip->wipower_default.num = 0; - if (chip->wipower_default.entries) - devm_kfree(chip->dev, chip->wipower_default.entries); - if (chip->wipower_pt.entries) - devm_kfree(chip->dev, chip->wipower_pt.entries); - if (chip->wipower_div2.entries) - devm_kfree(chip->dev, chip->wipower_div2.entries); - chip->wipower_default.entries = NULL; - chip->wipower_pt.entries = NULL; - chip->wipower_div2.entries = NULL; - chip->vadc_dev = NULL; - return rc; -} - -#define DEFAULT_VLED_MAX_UV 3500000 -#define DEFAULT_FCC_MA 2000 -static int smb_parse_dt(struct smbchg_chip *chip) -{ - int rc = 0, ocp_thresh = -EINVAL; - struct device_node *node = chip->dev->of_node; - const char *dc_psy_type, *bpd; - - if (!node) { - dev_err(chip->dev, "device tree info. missing\n"); - return -EINVAL; - } - - /* read optional u32 properties */ - OF_PROP_READ(chip, ocp_thresh, - "ibat-ocp-threshold-ua", rc, 1); - if (ocp_thresh >= 0) - smbchg_ibat_ocp_threshold_ua = ocp_thresh; - OF_PROP_READ(chip, chip->iterm_ma, "iterm-ma", rc, 1); - OF_PROP_READ(chip, chip->cfg_fastchg_current_ma, - "fastchg-current-ma", rc, 1); - if (chip->cfg_fastchg_current_ma == -EINVAL) - chip->cfg_fastchg_current_ma = DEFAULT_FCC_MA; - OF_PROP_READ(chip, chip->vfloat_mv, "float-voltage-mv", rc, 1); - OF_PROP_READ(chip, chip->safety_time, "charging-timeout-mins", rc, 1); - OF_PROP_READ(chip, chip->vled_max_uv, "vled-max-uv", rc, 1); - if (chip->vled_max_uv < 0) - chip->vled_max_uv = DEFAULT_VLED_MAX_UV; - OF_PROP_READ(chip, chip->rpara_uohm, "rparasitic-uohm", rc, 1); - if (chip->rpara_uohm < 0) - chip->rpara_uohm = 0; - OF_PROP_READ(chip, chip->prechg_safety_time, "precharging-timeout-mins", - rc, 1); - OF_PROP_READ(chip, chip->fastchg_current_comp, "fastchg-current-comp", - rc, 1); - OF_PROP_READ(chip, chip->float_voltage_comp, "float-voltage-comp", - rc, 1); - if (chip->safety_time != -EINVAL && - (chip->safety_time > chg_time[ARRAY_SIZE(chg_time) - 1])) { - dev_err(chip->dev, "Bad charging-timeout-mins %d\n", - chip->safety_time); - return -EINVAL; - } - if (chip->prechg_safety_time != -EINVAL && - (chip->prechg_safety_time > - prechg_time[ARRAY_SIZE(prechg_time) - 1])) { - dev_err(chip->dev, "Bad precharging-timeout-mins %d\n", - chip->prechg_safety_time); - return -EINVAL; - } - OF_PROP_READ(chip, chip->resume_delta_mv, "resume-delta-mv", rc, 1); - OF_PROP_READ(chip, chip->parallel.min_current_thr_ma, - "parallel-usb-min-current-ma", rc, 1); - OF_PROP_READ(chip, chip->parallel.min_9v_current_thr_ma, - "parallel-usb-9v-min-current-ma", rc, 1); - OF_PROP_READ(chip, chip->parallel.allowed_lowering_ma, - "parallel-allowed-lowering-ma", rc, 1); - if (chip->parallel.min_current_thr_ma != -EINVAL - && chip->parallel.min_9v_current_thr_ma != -EINVAL) - chip->parallel.avail = true; - /* - * use the dt values if they exist, otherwise do not touch the params - */ - of_property_read_u32(node, "qcom,parallel-main-chg-fcc-percent", - &smbchg_main_chg_fcc_percent); - of_property_read_u32(node, "qcom,parallel-main-chg-icl-percent", - &smbchg_main_chg_icl_percent); - pr_smb(PR_STATUS, "parallel usb thr: %d, 9v thr: %d\n", - chip->parallel.min_current_thr_ma, - chip->parallel.min_9v_current_thr_ma); - OF_PROP_READ(chip, chip->jeita_temp_hard_limit, - "jeita-temp-hard-limit", rc, 1); - OF_PROP_READ(chip, chip->aicl_rerun_period_s, - "aicl-rerun-period-s", rc, 1); - OF_PROP_READ(chip, chip->vchg_adc_channel, - "vchg-adc-channel-id", rc, 1); - - /* read boolean configuration properties */ - chip->use_vfloat_adjustments = of_property_read_bool(node, - "qcom,autoadjust-vfloat"); - chip->bmd_algo_disabled = of_property_read_bool(node, - "qcom,bmd-algo-disabled"); - chip->iterm_disabled = of_property_read_bool(node, - "qcom,iterm-disabled"); - chip->soft_vfloat_comp_disabled = of_property_read_bool(node, - "qcom,soft-vfloat-comp-disabled"); - chip->chg_enabled = !(of_property_read_bool(node, - "qcom,charging-disabled")); - chip->charge_unknown_battery = of_property_read_bool(node, - "qcom,charge-unknown-battery"); - chip->chg_inhibit_en = of_property_read_bool(node, - "qcom,chg-inhibit-en"); - chip->chg_inhibit_source_fg = of_property_read_bool(node, - "qcom,chg-inhibit-fg"); - chip->low_volt_dcin = of_property_read_bool(node, - "qcom,low-volt-dcin"); - chip->force_aicl_rerun = of_property_read_bool(node, - "qcom,force-aicl-rerun"); - chip->skip_usb_suspend_for_fake_battery = of_property_read_bool(node, - "qcom,skip-usb-suspend-for-fake-battery"); - - /* parse the battery missing detection pin source */ - rc = of_property_read_string(chip->pdev->dev.of_node, - "qcom,bmd-pin-src", &bpd); - if (rc) { - /* Select BAT_THM as default BPD scheme */ - chip->bmd_pin_src = BPD_TYPE_DEFAULT; - rc = 0; - } else { - chip->bmd_pin_src = get_bpd(bpd); - if (chip->bmd_pin_src < 0) { - dev_err(chip->dev, - "failed to determine bpd schema %d\n", rc); - return rc; - } - } - - /* parse the dc power supply configuration */ - rc = of_property_read_string(node, "qcom,dc-psy-type", &dc_psy_type); - if (rc) { - chip->dc_psy_type = -EINVAL; - rc = 0; - } else { - if (strcmp(dc_psy_type, "Mains") == 0) - chip->dc_psy_type = POWER_SUPPLY_TYPE_MAINS; - else if (strcmp(dc_psy_type, "Wireless") == 0) - chip->dc_psy_type = POWER_SUPPLY_TYPE_WIRELESS; - else if (strcmp(dc_psy_type, "Wipower") == 0) - chip->dc_psy_type = POWER_SUPPLY_TYPE_WIPOWER; - } - if (chip->dc_psy_type != -EINVAL) { - OF_PROP_READ(chip, chip->dc_target_current_ma, - "dc-psy-ma", rc, 0); - if (rc) - return rc; - if (chip->dc_target_current_ma < DC_MA_MIN - || chip->dc_target_current_ma > DC_MA_MAX) { - dev_err(chip->dev, "Bad dc mA %d\n", - chip->dc_target_current_ma); - return -EINVAL; - } - } - - if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER) - smb_parse_wipower_dt(chip); - - /* read the bms power supply name */ - rc = of_property_read_string(node, "qcom,bms-psy-name", - &chip->bms_psy_name); - if (rc) - chip->bms_psy_name = NULL; - - /* read the battery power supply name */ - rc = of_property_read_string(node, "qcom,battery-psy-name", - &chip->battery_psy_name); - if (rc) - chip->battery_psy_name = "battery"; - - /* Get the charger led support property */ - chip->cfg_chg_led_sw_ctrl = - of_property_read_bool(node, "qcom,chg-led-sw-controls"); - chip->cfg_chg_led_support = - of_property_read_bool(node, "qcom,chg-led-support"); - - if (of_find_property(node, "qcom,thermal-mitigation", - &chip->thermal_levels)) { - chip->thermal_mitigation = devm_kzalloc(chip->dev, - chip->thermal_levels, - GFP_KERNEL); - - if (chip->thermal_mitigation == NULL) { - dev_err(chip->dev, "thermal mitigation kzalloc() failed.\n"); - return -ENOMEM; - } - - chip->thermal_levels /= sizeof(int); - rc = of_property_read_u32_array(node, - "qcom,thermal-mitigation", - chip->thermal_mitigation, chip->thermal_levels); - if (rc) { - dev_err(chip->dev, - "Couldn't read threm limits rc = %d\n", rc); - return rc; - } - } - - chip->skip_usb_notification - = of_property_read_bool(node, - "qcom,skip-usb-notification"); - - chip->otg_pinctrl = of_property_read_bool(node, "qcom,otg-pinctrl"); - - return 0; -} - -#define SUBTYPE_REG 0x5 -#define SMBCHG_CHGR_SUBTYPE 0x1 -#define SMBCHG_OTG_SUBTYPE 0x8 -#define SMBCHG_BAT_IF_SUBTYPE 0x3 -#define SMBCHG_USB_CHGPTH_SUBTYPE 0x4 -#define SMBCHG_DC_CHGPTH_SUBTYPE 0x5 -#define SMBCHG_MISC_SUBTYPE 0x7 -#define SMBCHG_LITE_CHGR_SUBTYPE 0x51 -#define SMBCHG_LITE_OTG_SUBTYPE 0x58 -#define SMBCHG_LITE_BAT_IF_SUBTYPE 0x53 -#define SMBCHG_LITE_USB_CHGPTH_SUBTYPE 0x54 -#define SMBCHG_LITE_DC_CHGPTH_SUBTYPE 0x55 -#define SMBCHG_LITE_MISC_SUBTYPE 0x57 -static int smbchg_request_irq(struct smbchg_chip *chip, - struct device_node *child, - int irq_num, char *irq_name, - irqreturn_t (irq_handler)(int irq, void *_chip), - int flags) -{ - int rc; - - irq_num = of_irq_get_byname(child, irq_name); - if (irq_num < 0) { - dev_err(chip->dev, "Unable to get %s irqn", irq_name); - rc = -ENXIO; - } - rc = devm_request_threaded_irq(chip->dev, - irq_num, NULL, irq_handler, flags, irq_name, - chip); - if (rc < 0) { - dev_err(chip->dev, "Unable to request %s irq: %dn", - irq_name, rc); - rc = -ENXIO; - } - return 0; -} - -static int smbchg_request_irqs(struct smbchg_chip *chip) -{ - int rc = 0; - unsigned int base; - struct device_node *child; - u8 subtype; - unsigned long flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING - | IRQF_ONESHOT; - - if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) { - pr_err("no child nodes\n"); - return -ENXIO; - } - - for_each_available_child_of_node(chip->pdev->dev.of_node, child) { - rc = of_property_read_u32(child, "reg", &base); - if (rc < 0) { - rc = 0; - continue; - } - - rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1); - if (rc) { - dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n", - rc); - return rc; - } - - switch (subtype) { - case SMBCHG_CHGR_SUBTYPE: - case SMBCHG_LITE_CHGR_SUBTYPE: - rc = smbchg_request_irq(chip, child, - chip->chg_error_irq, "chg-error", - chg_error_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, chip->taper_irq, - "chg-taper-thr", taper_handler, - (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - disable_irq_nosync(chip->taper_irq); - rc = smbchg_request_irq(chip, child, chip->chg_term_irq, - "chg-tcc-thr", chg_term_handler, - (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, chip->recharge_irq, - "chg-rechg-thr", recharge_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, chip->fastchg_irq, - "chg-p2f-thr", fastchg_handler, flags); - if (rc < 0) - return rc; - enable_irq_wake(chip->chg_term_irq); - enable_irq_wake(chip->chg_error_irq); - enable_irq_wake(chip->fastchg_irq); - break; - case SMBCHG_BAT_IF_SUBTYPE: - case SMBCHG_LITE_BAT_IF_SUBTYPE: - rc = smbchg_request_irq(chip, child, chip->batt_hot_irq, - "batt-hot", batt_hot_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->batt_warm_irq, - "batt-warm", batt_warm_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->batt_cool_irq, - "batt-cool", batt_cool_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->batt_cold_irq, - "batt-cold", batt_cold_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->batt_missing_irq, - "batt-missing", batt_pres_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->vbat_low_irq, - "batt-low", vbat_low_handler, flags); - if (rc < 0) - return rc; - - enable_irq_wake(chip->batt_hot_irq); - enable_irq_wake(chip->batt_warm_irq); - enable_irq_wake(chip->batt_cool_irq); - enable_irq_wake(chip->batt_cold_irq); - enable_irq_wake(chip->batt_missing_irq); - enable_irq_wake(chip->vbat_low_irq); - break; - case SMBCHG_USB_CHGPTH_SUBTYPE: - case SMBCHG_LITE_USB_CHGPTH_SUBTYPE: - rc = smbchg_request_irq(chip, child, - chip->usbin_uv_irq, - "usbin-uv", usbin_uv_handler, - flags | IRQF_EARLY_RESUME); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->usbin_ov_irq, - "usbin-ov", usbin_ov_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->src_detect_irq, - "usbin-src-det", - src_detect_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->aicl_done_irq, - "aicl-done", - aicl_done_handler, - (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - - if (chip->schg_version != QPNP_SCHG_LITE) { - rc = smbchg_request_irq(chip, child, - chip->otg_fail_irq, "otg-fail", - otg_fail_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->otg_oc_irq, "otg-oc", - otg_oc_handler, - (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->usbid_change_irq, "usbid-change", - usbid_change_handler, - (IRQF_TRIGGER_FALLING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - enable_irq_wake(chip->otg_oc_irq); - enable_irq_wake(chip->usbid_change_irq); - enable_irq_wake(chip->otg_fail_irq); - } - enable_irq_wake(chip->usbin_uv_irq); - enable_irq_wake(chip->usbin_ov_irq); - enable_irq_wake(chip->src_detect_irq); - if (chip->parallel.avail && chip->usb_present) { - rc = enable_irq_wake(chip->aicl_done_irq); - chip->enable_aicl_wake = true; - } - break; - case SMBCHG_DC_CHGPTH_SUBTYPE: - case SMBCHG_LITE_DC_CHGPTH_SUBTYPE: - rc = smbchg_request_irq(chip, child, chip->dcin_uv_irq, - "dcin-uv", dcin_uv_handler, flags); - if (rc < 0) - return rc; - enable_irq_wake(chip->dcin_uv_irq); - break; - case SMBCHG_MISC_SUBTYPE: - case SMBCHG_LITE_MISC_SUBTYPE: - rc = smbchg_request_irq(chip, child, chip->power_ok_irq, - "power-ok", power_ok_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, chip->chg_hot_irq, - "temp-shutdown", chg_hot_handler, flags); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, chip->wdog_timeout_irq, - "wdog-timeout", - wdog_timeout_handler, flags); - if (rc < 0) - return rc; - enable_irq_wake(chip->chg_hot_irq); - enable_irq_wake(chip->wdog_timeout_irq); - break; - case SMBCHG_OTG_SUBTYPE: - break; - case SMBCHG_LITE_OTG_SUBTYPE: - rc = smbchg_request_irq(chip, child, - chip->usbid_change_irq, "usbid-change", - usbid_change_handler, - (IRQF_TRIGGER_FALLING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->otg_oc_irq, "otg-oc", - otg_oc_handler, - (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); - if (rc < 0) - return rc; - rc = smbchg_request_irq(chip, child, - chip->otg_fail_irq, "otg-fail", - otg_fail_handler, flags); - if (rc < 0) - return rc; - enable_irq_wake(chip->usbid_change_irq); - enable_irq_wake(chip->otg_oc_irq); - enable_irq_wake(chip->otg_fail_irq); - break; - } - } - - return rc; -} - -#define REQUIRE_BASE(chip, base, rc) \ -do { \ - if (!rc && !chip->base) { \ - dev_err(chip->dev, "Missing " #base "\n"); \ - rc = -EINVAL; \ - } \ -} while (0) - -static int smbchg_parse_peripherals(struct smbchg_chip *chip) -{ - int rc = 0; - unsigned int base; - struct device_node *child; - u8 subtype; - - if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) { - pr_err("no child nodes\n"); - return -ENXIO; - } - - for_each_available_child_of_node(chip->pdev->dev.of_node, child) { - rc = of_property_read_u32(child, "reg", &base); - if (rc < 0) { - rc = 0; - continue; - } - - rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1); - if (rc) { - dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n", - rc); - return rc; - } - - switch (subtype) { - case SMBCHG_CHGR_SUBTYPE: - case SMBCHG_LITE_CHGR_SUBTYPE: - chip->chgr_base = base; - break; - case SMBCHG_BAT_IF_SUBTYPE: - case SMBCHG_LITE_BAT_IF_SUBTYPE: - chip->bat_if_base = base; - break; - case SMBCHG_USB_CHGPTH_SUBTYPE: - case SMBCHG_LITE_USB_CHGPTH_SUBTYPE: - chip->usb_chgpth_base = base; - break; - case SMBCHG_DC_CHGPTH_SUBTYPE: - case SMBCHG_LITE_DC_CHGPTH_SUBTYPE: - chip->dc_chgpth_base = base; - break; - case SMBCHG_MISC_SUBTYPE: - case SMBCHG_LITE_MISC_SUBTYPE: - chip->misc_base = base; - break; - case SMBCHG_OTG_SUBTYPE: - case SMBCHG_LITE_OTG_SUBTYPE: - chip->otg_base = base; - break; - } - } - - REQUIRE_BASE(chip, chgr_base, rc); - REQUIRE_BASE(chip, bat_if_base, rc); - REQUIRE_BASE(chip, usb_chgpth_base, rc); - REQUIRE_BASE(chip, dc_chgpth_base, rc); - REQUIRE_BASE(chip, misc_base, rc); - - return rc; -} - -static inline void dump_reg(struct smbchg_chip *chip, u16 addr, - const char *name) -{ - u8 reg; - - smbchg_read(chip, ®, addr, 1); - pr_smb(PR_DUMP, "%s - %04X = %02X\n", name, addr, reg); -} - -/* dumps useful registers for debug */ -static void dump_regs(struct smbchg_chip *chip) -{ - u16 addr; - - /* charger peripheral */ - for (addr = 0xB; addr <= 0x10; addr++) - dump_reg(chip, chip->chgr_base + addr, "CHGR Status"); - for (addr = 0xF0; addr <= 0xFF; addr++) - dump_reg(chip, chip->chgr_base + addr, "CHGR Config"); - /* battery interface peripheral */ - dump_reg(chip, chip->bat_if_base + RT_STS, "BAT_IF Status"); - dump_reg(chip, chip->bat_if_base + CMD_CHG_REG, "BAT_IF Command"); - for (addr = 0xF0; addr <= 0xFB; addr++) - dump_reg(chip, chip->bat_if_base + addr, "BAT_IF Config"); - /* usb charge path peripheral */ - for (addr = 0x7; addr <= 0x10; addr++) - dump_reg(chip, chip->usb_chgpth_base + addr, "USB Status"); - dump_reg(chip, chip->usb_chgpth_base + CMD_IL, "USB Command"); - for (addr = 0xF0; addr <= 0xF5; addr++) - dump_reg(chip, chip->usb_chgpth_base + addr, "USB Config"); - /* dc charge path peripheral */ - dump_reg(chip, chip->dc_chgpth_base + RT_STS, "DC Status"); - for (addr = 0xF0; addr <= 0xF6; addr++) - dump_reg(chip, chip->dc_chgpth_base + addr, "DC Config"); - /* misc peripheral */ - dump_reg(chip, chip->misc_base + IDEV_STS, "MISC Status"); - dump_reg(chip, chip->misc_base + RT_STS, "MISC Status"); - for (addr = 0xF0; addr <= 0xF3; addr++) - dump_reg(chip, chip->misc_base + addr, "MISC CFG"); -} - -static int create_debugfs_entries(struct smbchg_chip *chip) -{ - struct dentry *ent; - - chip->debug_root = debugfs_create_dir("qpnp-smbcharger", NULL); - if (!chip->debug_root) { - dev_err(chip->dev, "Couldn't create debug dir\n"); - return -EINVAL; - } - - ent = debugfs_create_file("force_dcin_icl_check", - S_IFREG | S_IWUSR | S_IRUGO, - chip->debug_root, chip, - &force_dcin_icl_ops); - if (!ent) { - dev_err(chip->dev, - "Couldn't create force dcin icl check file\n"); - return -EINVAL; - } - return 0; -} - -static int smbchg_check_chg_version(struct smbchg_chip *chip) -{ - struct pmic_revid_data *pmic_rev_id; - struct device_node *revid_dev_node; - int rc; - - revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node, - "qcom,pmic-revid", 0); - if (!revid_dev_node) { - pr_err("Missing qcom,pmic-revid property - driver failed\n"); - return -EINVAL; - } - - pmic_rev_id = get_revid_data(revid_dev_node); - if (IS_ERR(pmic_rev_id)) { - rc = PTR_ERR(revid_dev_node); - if (rc != -EPROBE_DEFER) - pr_err("Unable to get pmic_revid rc=%d\n", rc); - return rc; - } - - switch (pmic_rev_id->pmic_subtype) { - case PMI8994: - chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA - | SMBCHG_BATT_OV_WA - | SMBCHG_CC_ESR_WA - | SMBCHG_RESTART_WA; - use_pmi8994_tables(chip); - chip->schg_version = QPNP_SCHG; - break; - case PMI8950: - case PMI8937: - chip->wa_flags |= SMBCHG_BATT_OV_WA; - if (pmic_rev_id->rev4 < 2) /* PMI8950 1.0 */ { - chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA; - } else { /* rev > PMI8950 v1.0 */ - chip->wa_flags |= SMBCHG_HVDCP_9V_EN_WA - | SMBCHG_USB100_WA; - } - use_pmi8994_tables(chip); - chip->tables.aicl_rerun_period_table = - aicl_rerun_period_schg_lite; - chip->tables.aicl_rerun_period_len = - ARRAY_SIZE(aicl_rerun_period_schg_lite); - - chip->schg_version = QPNP_SCHG_LITE; - if (pmic_rev_id->pmic_subtype == PMI8937) - chip->hvdcp_not_supported = true; - break; - case PMI8996: - chip->wa_flags |= SMBCHG_CC_ESR_WA - | SMBCHG_FLASH_ICL_DISABLE_WA - | SMBCHG_RESTART_WA - | SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA; - use_pmi8996_tables(chip); - chip->schg_version = QPNP_SCHG; - break; - default: - pr_err("PMIC subtype %d not supported, WA flags not set\n", - pmic_rev_id->pmic_subtype); - } - - pr_smb(PR_STATUS, "pmic=%s, wa_flags=0x%x, hvdcp_supported=%s\n", - pmic_rev_id->pmic_name, chip->wa_flags, - chip->hvdcp_not_supported ? "false" : "true"); - - return 0; -} - -static void rerun_hvdcp_det_if_necessary(struct smbchg_chip *chip) -{ - enum power_supply_type usb_supply_type; - char *usb_type_name; - int rc; - - if (!(chip->wa_flags & SMBCHG_RESTART_WA)) - return; - - read_usb_type(chip, &usb_type_name, &usb_supply_type); - if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP - && !is_hvdcp_present(chip)) { - pr_smb(PR_STATUS, "DCP found rerunning APSD\n"); - rc = vote(chip->usb_icl_votable, - CHG_SUSPEND_WORKAROUND_ICL_VOTER, true, 300); - if (rc < 0) - pr_err("Couldn't vote for 300mA for suspend wa, going ahead rc=%d\n", - rc); - - pr_smb(PR_STATUS, "Faking Removal\n"); - fake_insertion_removal(chip, false); - msleep(500); - pr_smb(PR_STATUS, "Faking Insertion\n"); - fake_insertion_removal(chip, true); - - read_usb_type(chip, &usb_type_name, &usb_supply_type); - if (usb_supply_type != POWER_SUPPLY_TYPE_USB_DCP) { - msleep(500); - pr_smb(PR_STATUS, "Fake Removal again as type!=DCP\n"); - fake_insertion_removal(chip, false); - msleep(500); - pr_smb(PR_STATUS, "Fake Insert again as type!=DCP\n"); - fake_insertion_removal(chip, true); - } - - rc = vote(chip->usb_icl_votable, - CHG_SUSPEND_WORKAROUND_ICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't vote for 0 for suspend wa, going ahead rc=%d\n", - rc); - } -} - -static int smbchg_probe(struct platform_device *pdev) -{ - int rc; - struct smbchg_chip *chip; - struct power_supply *typec_psy = NULL; - struct qpnp_vadc_chip *vadc_dev, *vchg_vadc_dev; - const char *typec_psy_name; - struct power_supply_config usb_psy_cfg = {}; - struct power_supply_config batt_psy_cfg = {}; - struct power_supply_config dc_psy_cfg = {}; - - if (of_property_read_bool(pdev->dev.of_node, "qcom,external-typec")) { - /* read the type power supply name */ - rc = of_property_read_string(pdev->dev.of_node, - "qcom,typec-psy-name", &typec_psy_name); - if (rc) { - pr_err("failed to get prop typec-psy-name rc=%d\n", - rc); - return rc; - } - - typec_psy = power_supply_get_by_name(typec_psy_name); - if (!typec_psy) { - pr_smb(PR_STATUS, - "Type-C supply not found, deferring probe\n"); - return -EPROBE_DEFER; - } - } - - vadc_dev = NULL; - if (of_find_property(pdev->dev.of_node, "qcom,dcin-vadc", NULL)) { - vadc_dev = qpnp_get_vadc(&pdev->dev, "dcin"); - if (IS_ERR(vadc_dev)) { - rc = PTR_ERR(vadc_dev); - if (rc != -EPROBE_DEFER) - dev_err(&pdev->dev, - "Couldn't get vadc rc=%d\n", - rc); - return rc; - } - } - - vchg_vadc_dev = NULL; - if (of_find_property(pdev->dev.of_node, "qcom,vchg_sns-vadc", NULL)) { - vchg_vadc_dev = qpnp_get_vadc(&pdev->dev, "vchg_sns"); - if (IS_ERR(vchg_vadc_dev)) { - rc = PTR_ERR(vchg_vadc_dev); - if (rc != -EPROBE_DEFER) - dev_err(&pdev->dev, "Couldn't get vadc 'vchg' rc=%d\n", - rc); - return rc; - } - } - - - chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!chip->regmap) { - dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); - return -EINVAL; - } - - chip->fcc_votable = create_votable("BATT_FCC", - VOTE_MIN, - set_fastchg_current_vote_cb, chip); - if (IS_ERR(chip->fcc_votable)) { - rc = PTR_ERR(chip->fcc_votable); - goto votables_cleanup; - } - - chip->usb_icl_votable = create_votable("USB_ICL", - VOTE_MIN, - set_usb_current_limit_vote_cb, chip); - if (IS_ERR(chip->usb_icl_votable)) { - rc = PTR_ERR(chip->usb_icl_votable); - goto votables_cleanup; - } - - chip->dc_icl_votable = create_votable("DCIN_ICL", - VOTE_MIN, - set_dc_current_limit_vote_cb, chip); - if (IS_ERR(chip->dc_icl_votable)) { - rc = PTR_ERR(chip->dc_icl_votable); - goto votables_cleanup; - } - - chip->usb_suspend_votable = create_votable("USB_SUSPEND", - VOTE_SET_ANY, - usb_suspend_vote_cb, chip); - if (IS_ERR(chip->usb_suspend_votable)) { - rc = PTR_ERR(chip->usb_suspend_votable); - goto votables_cleanup; - } - - chip->dc_suspend_votable = create_votable("DC_SUSPEND", - VOTE_SET_ANY, - dc_suspend_vote_cb, chip); - if (IS_ERR(chip->dc_suspend_votable)) { - rc = PTR_ERR(chip->dc_suspend_votable); - goto votables_cleanup; - } - - chip->battchg_suspend_votable = create_votable("BATTCHG_SUSPEND", - VOTE_SET_ANY, - charging_suspend_vote_cb, chip); - if (IS_ERR(chip->battchg_suspend_votable)) { - rc = PTR_ERR(chip->battchg_suspend_votable); - goto votables_cleanup; - } - - chip->hw_aicl_rerun_disable_votable = create_votable("HWAICL_DISABLE", - VOTE_SET_ANY, - smbchg_hw_aicl_rerun_disable_cb, chip); - if (IS_ERR(chip->hw_aicl_rerun_disable_votable)) { - rc = PTR_ERR(chip->hw_aicl_rerun_disable_votable); - goto votables_cleanup; - } - - chip->hw_aicl_rerun_enable_indirect_votable = create_votable( - "HWAICL_ENABLE_INDIRECT", - VOTE_SET_ANY, - smbchg_hw_aicl_rerun_enable_indirect_cb, chip); - if (IS_ERR(chip->hw_aicl_rerun_enable_indirect_votable)) { - rc = PTR_ERR(chip->hw_aicl_rerun_enable_indirect_votable); - goto votables_cleanup; - } - - chip->aicl_deglitch_short_votable = create_votable( - "HWAICL_SHORT_DEGLITCH", - VOTE_SET_ANY, - smbchg_aicl_deglitch_config_cb, chip); - if (IS_ERR(chip->aicl_deglitch_short_votable)) { - rc = PTR_ERR(chip->aicl_deglitch_short_votable); - goto votables_cleanup; - } - - INIT_WORK(&chip->usb_set_online_work, smbchg_usb_update_online_work); - INIT_DELAYED_WORK(&chip->parallel_en_work, - smbchg_parallel_usb_en_work); - INIT_DELAYED_WORK(&chip->vfloat_adjust_work, smbchg_vfloat_adjust_work); - INIT_DELAYED_WORK(&chip->hvdcp_det_work, smbchg_hvdcp_det_work); - init_completion(&chip->src_det_lowered); - init_completion(&chip->src_det_raised); - init_completion(&chip->usbin_uv_lowered); - init_completion(&chip->usbin_uv_raised); - chip->vadc_dev = vadc_dev; - chip->vchg_vadc_dev = vchg_vadc_dev; - chip->pdev = pdev; - chip->dev = &pdev->dev; - - chip->typec_psy = typec_psy; - chip->fake_battery_soc = -EINVAL; - chip->usb_online = -EINVAL; - dev_set_drvdata(&pdev->dev, chip); - - spin_lock_init(&chip->sec_access_lock); - mutex_init(&chip->therm_lvl_lock); - mutex_init(&chip->usb_set_online_lock); - mutex_init(&chip->parallel.lock); - mutex_init(&chip->taper_irq_lock); - mutex_init(&chip->pm_lock); - mutex_init(&chip->wipower_config); - mutex_init(&chip->usb_status_lock); - device_init_wakeup(chip->dev, true); - - rc = smbchg_parse_peripherals(chip); - if (rc) { - dev_err(chip->dev, "Error parsing DT peripherals: %d\n", rc); - goto votables_cleanup; - } - - rc = smbchg_check_chg_version(chip); - if (rc) { - pr_err("Unable to check schg version rc=%d\n", rc); - goto votables_cleanup; - } - - rc = smb_parse_dt(chip); - if (rc < 0) { - dev_err(&pdev->dev, "Unable to parse DT nodes: %d\n", rc); - goto votables_cleanup; - } - - rc = smbchg_regulator_init(chip); - if (rc) { - dev_err(&pdev->dev, - "Couldn't initialize regulator rc=%d\n", rc); - goto votables_cleanup; - } - - chip->extcon = devm_extcon_dev_allocate(chip->dev, smbchg_extcon_cable); - if (IS_ERR(chip->extcon)) { - dev_err(chip->dev, "failed to allocate extcon device\n"); - rc = PTR_ERR(chip->extcon); - goto votables_cleanup; - } - - rc = devm_extcon_dev_register(chip->dev, chip->extcon); - if (rc) { - dev_err(chip->dev, "failed to register extcon device\n"); - goto votables_cleanup; - } - - chip->usb_psy_d.name = "usb"; - chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB; - chip->usb_psy_d.get_property = smbchg_usb_get_property; - chip->usb_psy_d.set_property = smbchg_usb_set_property; - chip->usb_psy_d.properties = smbchg_usb_properties; - chip->usb_psy_d.num_properties = ARRAY_SIZE(smbchg_usb_properties); - chip->usb_psy_d.property_is_writeable = smbchg_usb_is_writeable; - - usb_psy_cfg.drv_data = chip; - usb_psy_cfg.supplied_to = smbchg_usb_supplicants; - usb_psy_cfg.num_supplicants = ARRAY_SIZE(smbchg_usb_supplicants); - - chip->usb_psy = devm_power_supply_register(chip->dev, - &chip->usb_psy_d, &usb_psy_cfg); - if (IS_ERR(chip->usb_psy)) { - dev_err(&pdev->dev, "Unable to register usb_psy rc = %ld\n", - PTR_ERR(chip->usb_psy)); - rc = PTR_ERR(chip->usb_psy); - goto votables_cleanup; - } - - if (of_find_property(chip->dev->of_node, "dpdm-supply", NULL)) { - chip->dpdm_reg = devm_regulator_get(chip->dev, "dpdm"); - if (IS_ERR(chip->dpdm_reg)) { - rc = PTR_ERR(chip->dpdm_reg); - goto votables_cleanup; - } - } - - rc = smbchg_hw_init(chip); - if (rc < 0) { - dev_err(&pdev->dev, - "Unable to intialize hardware rc = %d\n", rc); - goto out; - } - - rc = determine_initial_status(chip); - if (rc < 0) { - dev_err(&pdev->dev, - "Unable to determine init status rc = %d\n", rc); - goto out; - } - - chip->previous_soc = -EINVAL; - chip->batt_psy_d.name = chip->battery_psy_name; - chip->batt_psy_d.type = POWER_SUPPLY_TYPE_BATTERY; - chip->batt_psy_d.get_property = smbchg_battery_get_property; - chip->batt_psy_d.set_property = smbchg_battery_set_property; - chip->batt_psy_d.properties = smbchg_battery_properties; - chip->batt_psy_d.num_properties = ARRAY_SIZE(smbchg_battery_properties); - chip->batt_psy_d.external_power_changed = smbchg_external_power_changed; - chip->batt_psy_d.property_is_writeable = smbchg_battery_is_writeable; - - batt_psy_cfg.drv_data = chip; - batt_psy_cfg.num_supplicants = 0; - chip->batt_psy = devm_power_supply_register(chip->dev, - &chip->batt_psy_d, - &batt_psy_cfg); - if (IS_ERR(chip->batt_psy)) { - dev_err(&pdev->dev, - "Unable to register batt_psy rc = %ld\n", - PTR_ERR(chip->batt_psy)); - goto out; - } - - if (chip->dc_psy_type != -EINVAL) { - chip->dc_psy_d.name = "dc"; - chip->dc_psy_d.type = chip->dc_psy_type; - chip->dc_psy_d.get_property = smbchg_dc_get_property; - chip->dc_psy_d.set_property = smbchg_dc_set_property; - chip->dc_psy_d.property_is_writeable = smbchg_dc_is_writeable; - chip->dc_psy_d.properties = smbchg_dc_properties; - chip->dc_psy_d.num_properties - = ARRAY_SIZE(smbchg_dc_properties); - - dc_psy_cfg.drv_data = chip; - dc_psy_cfg.num_supplicants - = ARRAY_SIZE(smbchg_dc_supplicants); - dc_psy_cfg.supplied_to = smbchg_dc_supplicants; - - chip->dc_psy = devm_power_supply_register(chip->dev, - &chip->dc_psy_d, - &dc_psy_cfg); - if (IS_ERR(chip->dc_psy)) { - dev_err(&pdev->dev, - "Unable to register dc_psy rc = %ld\n", - PTR_ERR(chip->dc_psy)); - goto out; - } - } - - if (chip->cfg_chg_led_support && - chip->schg_version == QPNP_SCHG_LITE) { - rc = smbchg_register_chg_led(chip); - if (rc) { - dev_err(chip->dev, - "Unable to register charger led: %d\n", - rc); - goto out; - } - - rc = smbchg_chg_led_controls(chip); - if (rc) { - dev_err(chip->dev, - "Failed to set charger led controld bit: %d\n", - rc); - goto unregister_led_class; - } - } - - rc = smbchg_request_irqs(chip); - if (rc < 0) { - dev_err(&pdev->dev, "Unable to request irqs rc = %d\n", rc); - goto unregister_led_class; - } - - rerun_hvdcp_det_if_necessary(chip); - - dump_regs(chip); - create_debugfs_entries(chip); - dev_info(chip->dev, - "SMBCHG successfully probe Charger version=%s Revision DIG:%d.%d ANA:%d.%d batt=%d dc=%d usb=%d\n", - version_str[chip->schg_version], - chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR], - chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR], - get_prop_batt_present(chip), - chip->dc_present, chip->usb_present); - return 0; - -unregister_led_class: - if (chip->cfg_chg_led_support && chip->schg_version == QPNP_SCHG_LITE) - led_classdev_unregister(&chip->led_cdev); -out: - handle_usb_removal(chip); -votables_cleanup: - if (chip->aicl_deglitch_short_votable) - destroy_votable(chip->aicl_deglitch_short_votable); - if (chip->hw_aicl_rerun_enable_indirect_votable) - destroy_votable(chip->hw_aicl_rerun_enable_indirect_votable); - if (chip->hw_aicl_rerun_disable_votable) - destroy_votable(chip->hw_aicl_rerun_disable_votable); - if (chip->battchg_suspend_votable) - destroy_votable(chip->battchg_suspend_votable); - if (chip->dc_suspend_votable) - destroy_votable(chip->dc_suspend_votable); - if (chip->usb_suspend_votable) - destroy_votable(chip->usb_suspend_votable); - if (chip->dc_icl_votable) - destroy_votable(chip->dc_icl_votable); - if (chip->usb_icl_votable) - destroy_votable(chip->usb_icl_votable); - if (chip->fcc_votable) - destroy_votable(chip->fcc_votable); - return rc; -} - -static int smbchg_remove(struct platform_device *pdev) -{ - struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev); - - debugfs_remove_recursive(chip->debug_root); - - destroy_votable(chip->aicl_deglitch_short_votable); - destroy_votable(chip->hw_aicl_rerun_enable_indirect_votable); - destroy_votable(chip->hw_aicl_rerun_disable_votable); - destroy_votable(chip->battchg_suspend_votable); - destroy_votable(chip->dc_suspend_votable); - destroy_votable(chip->usb_suspend_votable); - destroy_votable(chip->dc_icl_votable); - destroy_votable(chip->usb_icl_votable); - destroy_votable(chip->fcc_votable); - - return 0; -} - -static void smbchg_shutdown(struct platform_device *pdev) -{ - struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev); - int rc; - - if (!(chip->wa_flags & SMBCHG_RESTART_WA)) - return; - - if (!is_hvdcp_present(chip)) - return; - - pr_smb(PR_MISC, "Disable Parallel\n"); - mutex_lock(&chip->parallel.lock); - smbchg_parallel_en = 0; - smbchg_parallel_usb_disable(chip); - mutex_unlock(&chip->parallel.lock); - - pr_smb(PR_MISC, "Disable all interrupts\n"); - disable_irq(chip->aicl_done_irq); - disable_irq(chip->batt_cold_irq); - disable_irq(chip->batt_cool_irq); - disable_irq(chip->batt_hot_irq); - disable_irq(chip->batt_missing_irq); - disable_irq(chip->batt_warm_irq); - disable_irq(chip->chg_error_irq); - disable_irq(chip->chg_hot_irq); - disable_irq(chip->chg_term_irq); - disable_irq(chip->dcin_uv_irq); - disable_irq(chip->fastchg_irq); - disable_irq(chip->otg_fail_irq); - disable_irq(chip->otg_oc_irq); - disable_irq(chip->power_ok_irq); - disable_irq(chip->recharge_irq); - disable_irq(chip->src_detect_irq); - disable_irq(chip->taper_irq); - disable_irq(chip->usbid_change_irq); - disable_irq(chip->usbin_ov_irq); - disable_irq(chip->usbin_uv_irq); - disable_irq(chip->vbat_low_irq); - disable_irq(chip->wdog_timeout_irq); - - /* remove all votes for short deglitch */ - vote(chip->aicl_deglitch_short_votable, - VARB_WORKAROUND_SHORT_DEGLITCH_VOTER, false, 0); - vote(chip->aicl_deglitch_short_votable, - HVDCP_SHORT_DEGLITCH_VOTER, false, 0); - - /* vote to ensure AICL rerun is enabled */ - rc = vote(chip->hw_aicl_rerun_enable_indirect_votable, - SHUTDOWN_WORKAROUND_VOTER, true, 0); - if (rc < 0) - pr_err("Couldn't vote to enable indirect AICL rerun\n"); - rc = vote(chip->hw_aicl_rerun_disable_votable, - WEAK_CHARGER_HW_AICL_VOTER, false, 0); - if (rc < 0) - pr_err("Couldn't vote to enable AICL rerun\n"); - - /* switch to 5V HVDCP */ - pr_smb(PR_MISC, "Switch to 5V HVDCP\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_ADAPTER_SEL_MASK, HVDCP_5V); - if (rc < 0) { - pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc); - return; - } - - pr_smb(PR_MISC, "Wait 500mS to lower to 5V\n"); - /* wait for HVDCP to lower to 5V */ - msleep(500); - /* - * Check if the same hvdcp session is in progress. src_det should be - * high and that we are still in 5V hvdcp - */ - if (!is_src_detect_high(chip)) { - pr_smb(PR_MISC, "src det low after 500mS sleep\n"); - return; - } - - /* disable HVDCP */ - pr_smb(PR_MISC, "Disable HVDCP\n"); - rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG, - HVDCP_EN_BIT, 0); - if (rc < 0) - pr_err("Couldn't disable HVDCP rc=%d\n", rc); - - chip->hvdcp_3_det_ignore_uv = true; - /* fake a removal */ - pr_smb(PR_MISC, "Faking Removal\n"); - rc = fake_insertion_removal(chip, false); - if (rc < 0) - pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc); - - /* fake an insertion */ - pr_smb(PR_MISC, "Faking Insertion\n"); - rc = fake_insertion_removal(chip, true); - if (rc < 0) - pr_err("Couldn't fake insertion rc=%d\n", rc); - - pr_smb(PR_MISC, "Wait 1S to settle\n"); - msleep(1000); - chip->hvdcp_3_det_ignore_uv = false; - - pr_smb(PR_STATUS, "wrote power off configurations\n"); -} - -static const struct dev_pm_ops smbchg_pm_ops = { -}; - -MODULE_DEVICE_TABLE(spmi, smbchg_id); - -static struct platform_driver smbchg_driver = { - .driver = { - .name = "qpnp-smbcharger", - .owner = THIS_MODULE, - .of_match_table = smbchg_match_table, - .pm = &smbchg_pm_ops, - }, - .probe = smbchg_probe, - .remove = smbchg_remove, - .shutdown = smbchg_shutdown, -}; - -static int __init smbchg_init(void) -{ - return platform_driver_register(&smbchg_driver); -} - -static void __exit smbchg_exit(void) -{ - return platform_driver_unregister(&smbchg_driver); -} - -module_init(smbchg_init); -module_exit(smbchg_exit); - -MODULE_DESCRIPTION("QPNP SMB Charger"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:qpnp-smbcharger"); diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index f9784630c327..a8427c5e3107 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -381,13 +381,13 @@ static int smblib_set_opt_freq_buck(struct smb_charger *chg, int fsw_khz) if (chg->mode == PARALLEL_MASTER && chg->pl.psy) { pval.intval = fsw_khz; - rc = power_supply_set_property(chg->pl.psy, + /* + * Some parallel charging implementations may not have + * PROP_BUCK_FREQ property - they could be running + * with a fixed frequency + */ + power_supply_set_property(chg->pl.psy, POWER_SUPPLY_PROP_BUCK_FREQ, &pval); - if (rc < 0) { - dev_err(chg->dev, - "Could not set parallel buck_freq rc=%d\n", rc); - return rc; - } } return rc; @@ -474,6 +474,36 @@ int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend) return rc; } +static int smblib_set_adapter_allowance(struct smb_charger *chg, + u8 allowed_voltage) +{ + int rc = 0; + + switch (allowed_voltage) { + case USBIN_ADAPTER_ALLOW_12V: + case USBIN_ADAPTER_ALLOW_5V_OR_12V: + case USBIN_ADAPTER_ALLOW_9V_TO_12V: + case USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V: + case USBIN_ADAPTER_ALLOW_5V_TO_12V: + /* PM660 only support max. 9V */ + if (chg->smb_version == PM660_SUBTYPE) { + smblib_dbg(chg, PR_MISC, "voltage not supported=%d\n", + allowed_voltage); + allowed_voltage = USBIN_ADAPTER_ALLOW_5V_TO_9V; + } + break; + } + + rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, allowed_voltage); + if (rc < 0) { + smblib_err(chg, "Couldn't write 0x%02x to USBIN_ADAPTER_ALLOW_CFG rc=%d\n", + allowed_voltage, rc); + return rc; + } + + return rc; +} + #define MICRO_5V 5000000 #define MICRO_9V 9000000 #define MICRO_12V 12000000 @@ -504,10 +534,10 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg, return -EINVAL; } - rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, allowed_voltage); + rc = smblib_set_adapter_allowance(chg, allowed_voltage); if (rc < 0) { - smblib_err(chg, "Couldn't write 0x%02x to USBIN_ADAPTER_ALLOW_CFG rc=%d\n", - allowed_voltage, rc); + smblib_err(chg, "Couldn't configure adapter allowance rc=%d\n", + rc); return rc; } @@ -650,8 +680,8 @@ static void smblib_uusb_removal(struct smb_charger *chg) } /* reconfigure allowed voltage for HVDCP */ - rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, - USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); + rc = smblib_set_adapter_allowance(chg, + USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); if (rc < 0) smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n", rc); @@ -3405,8 +3435,8 @@ static void typec_source_removal(struct smb_charger *chg) } /* reconfigure allowed voltage for HVDCP */ - rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, - USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); + rc = smblib_set_adapter_allowance(chg, + USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); if (rc < 0) smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n", rc); diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index 37dc15494761..1c7c1e78699f 100644 --- a/drivers/power/supply/qcom/smb138x-charger.c +++ b/drivers/power/supply/qcom/smb138x-charger.c @@ -44,6 +44,8 @@ #define SMB2CHG_DC_TM_SREFGEN (DCIN_BASE + 0xE2) #define STACKED_DIODE_EN_BIT BIT(2) +#define TDIE_AVG_COUNT 10 + enum { OOB_COMP_WA_BIT = BIT(0), }; @@ -118,6 +120,27 @@ irqreturn_t smb138x_handle_slave_chg_state_change(int irq, void *data) return IRQ_HANDLED; } +static int smb138x_get_prop_charger_temp(struct smb138x *chip, + union power_supply_propval *val) +{ + union power_supply_propval pval; + int rc = 0, avg = 0, i; + struct smb_charger *chg = &chip->chg; + + for (i = 0; i < TDIE_AVG_COUNT; i++) { + pval.intval = 0; + rc = smblib_get_prop_charger_temp(chg, &pval); + if (rc < 0) { + pr_err("Couldnt read chg temp at %dth iteration rc = %d\n", + i + 1, rc); + return rc; + } + avg += pval.intval; + } + val->intval = avg / TDIE_AVG_COUNT; + return rc; +} + static int smb138x_parse_dt(struct smb138x *chip) { struct smb_charger *chg = &chip->chg; @@ -343,7 +366,7 @@ static int smb138x_batt_get_prop(struct power_supply *psy, rc = smblib_get_prop_batt_capacity(chg, val); break; case POWER_SUPPLY_PROP_CHARGER_TEMP: - rc = smblib_get_prop_charger_temp(chg, val); + rc = smb138x_get_prop_charger_temp(chip, val); break; case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX: rc = smblib_get_prop_charger_temp_max(chg, val); @@ -547,7 +570,7 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, rc = smblib_get_prop_slave_current_now(chg, val); break; case POWER_SUPPLY_PROP_CHARGER_TEMP: - rc = smblib_get_prop_charger_temp(chg, val); + rc = smb138x_get_prop_charger_temp(chip, val); break; case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX: rc = smblib_get_prop_charger_temp_max(chg, val); @@ -621,10 +644,6 @@ static int smb138x_parallel_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: rc = smblib_set_charge_param(chg, &chg->param.fcc, val->intval); break; - case POWER_SUPPLY_PROP_BUCK_FREQ: - rc = smblib_set_charge_param(chg, &chg->param.freq_buck, - val->intval); - break; case POWER_SUPPLY_PROP_SET_SHIP_MODE: /* Not in ship mode as long as the device is active */ if (!val->intval) @@ -632,7 +651,7 @@ static int smb138x_parallel_set_prop(struct power_supply *psy, rc = smblib_set_prop_ship_mode(chg, val); break; default: - pr_err("parallel power supply set prop %d not supported\n", + pr_debug("parallel power supply set prop %d not supported\n", prop); return -EINVAL; } @@ -911,6 +930,13 @@ static int smb138x_init_hw(struct smb138x *chip) chg->dcp_icl_ua = chip->dt.usb_icl_ua; + /* configure to a fixed 700khz freq to avoid tdie errors */ + rc = smblib_set_charge_param(chg, &chg->param.freq_buck, 700); + if (rc < 0) { + pr_err("Couldn't configure 700Khz switch freq rc=%d\n", rc); + return rc; + } + /* configure charge enable for software control; active high */ rc = smblib_masked_write(chg, CHGR_CFG2_REG, CHG_EN_POLARITY_BIT | CHG_EN_SRC_BIT, 0); diff --git a/drivers/power/tps65217_charger.c b/drivers/power/tps65217_charger.c index d9f56730c735..040a40b4b173 100644 --- a/drivers/power/tps65217_charger.c +++ b/drivers/power/tps65217_charger.c @@ -205,6 +205,7 @@ static int tps65217_charger_probe(struct platform_device *pdev) if (!charger) return -ENOMEM; + platform_set_drvdata(pdev, charger); charger->tps = tps; charger->dev = &pdev->dev; diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index d24ca5f281b4..ec84ff8ad1b4 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -321,6 +321,8 @@ int pwmchip_remove(struct pwm_chip *chip) unsigned int i; int ret = 0; + pwmchip_sysfs_unexport_children(chip); + mutex_lock(&pwm_lock); for (i = 0; i < chip->npwm; i++) { @@ -889,7 +891,7 @@ EXPORT_SYMBOL_GPL(devm_pwm_put); */ bool pwm_can_sleep(struct pwm_device *pwm) { - return pwm->chip->can_sleep; + return true; } EXPORT_SYMBOL_GPL(pwm_can_sleep); diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c index 9c90886f4123..375008e2be20 100644 --- a/drivers/pwm/sysfs.c +++ b/drivers/pwm/sysfs.c @@ -350,6 +350,26 @@ void pwmchip_sysfs_unexport(struct pwm_chip *chip) } } +void pwmchip_sysfs_unexport_children(struct pwm_chip *chip) +{ + struct device *parent; + unsigned int i; + + parent = class_find_device(&pwm_class, NULL, chip, + pwmchip_sysfs_match); + if (!parent) + return; + + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; + + if (test_bit(PWMF_EXPORTED, &pwm->flags)) + pwm_unexport_child(parent, pwm); + } + + put_device(parent); +} + static int __init pwm_sysfs_init(void) { return class_register(&pwm_class); diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c index 6fa0c7d13290..4bda998afdef 100644 --- a/drivers/regulator/qcom_smd-regulator.c +++ b/drivers/regulator/qcom_smd-regulator.c @@ -166,29 +166,30 @@ static const struct regulator_desc pm8x41_hfsmps = { static const struct regulator_desc pm8841_ftsmps = { .linear_ranges = (struct regulator_linear_range[]) { REGULATOR_LINEAR_RANGE(350000, 0, 184, 5000), - REGULATOR_LINEAR_RANGE(700000, 185, 339, 10000), + REGULATOR_LINEAR_RANGE(1280000, 185, 261, 10000), }, .n_linear_ranges = 2, - .n_voltages = 340, + .n_voltages = 262, .ops = &rpm_smps_ldo_ops, }; static const struct regulator_desc pm8941_boost = { .linear_ranges = (struct regulator_linear_range[]) { - REGULATOR_LINEAR_RANGE(4000000, 0, 15, 100000), + REGULATOR_LINEAR_RANGE(4000000, 0, 30, 50000), }, .n_linear_ranges = 1, - .n_voltages = 16, + .n_voltages = 31, .ops = &rpm_smps_ldo_ops, }; static const struct regulator_desc pm8941_pldo = { .linear_ranges = (struct regulator_linear_range[]) { - REGULATOR_LINEAR_RANGE( 750000, 0, 30, 25000), - REGULATOR_LINEAR_RANGE(1500000, 31, 99, 50000), + REGULATOR_LINEAR_RANGE( 750000, 0, 63, 12500), + REGULATOR_LINEAR_RANGE(1550000, 64, 126, 25000), + REGULATOR_LINEAR_RANGE(3100000, 127, 163, 50000), }, - .n_linear_ranges = 2, - .n_voltages = 100, + .n_linear_ranges = 3, + .n_voltages = 164, .ops = &rpm_smps_ldo_ops, }; diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c index 88a5dc88badc..fee6457e3111 100644 --- a/drivers/regulator/qcom_spmi-regulator.c +++ b/drivers/regulator/qcom_spmi-regulator.c @@ -1050,6 +1050,8 @@ static struct regulator_ops spmi_vs_ops = { .set_pull_down = spmi_regulator_common_set_pull_down, .set_soft_start = spmi_regulator_common_set_soft_start, .set_over_current_protection = spmi_regulator_vs_ocp, + .set_mode = spmi_regulator_common_set_mode, + .get_mode = spmi_regulator_common_get_mode, }; static struct regulator_ops spmi_boost_ops = { @@ -1440,6 +1442,7 @@ static const struct spmi_regulator_data pm8941_regulators[] = { { "s1", 0x1400, "vdd_s1", }, { "s2", 0x1700, "vdd_s2", }, { "s3", 0x1a00, "vdd_s3", }, + { "s4", 0xa000, }, { "l1", 0x4000, "vdd_l1_l3", }, { "l2", 0x4100, "vdd_l2_lvs_1_2_3", }, { "l3", 0x4200, "vdd_l1_l3", }, @@ -1467,8 +1470,8 @@ static const struct spmi_regulator_data pm8941_regulators[] = { { "lvs1", 0x8000, "vdd_l2_lvs_1_2_3", }, { "lvs2", 0x8100, "vdd_l2_lvs_1_2_3", }, { "lvs3", 0x8200, "vdd_l2_lvs_1_2_3", }, - { "mvs1", 0x8300, "vin_5vs", }, - { "mvs2", 0x8400, "vin_5vs", }, + { "5vs1", 0x8300, "vin_5vs", "ocp-5vs1", }, + { "5vs2", 0x8400, "vin_5vs", "ocp-5vs2", }, { } }; diff --git a/drivers/regulator/qpnp-oledb-regulator.c b/drivers/regulator/qpnp-oledb-regulator.c index 8d017fb55a3f..c012f373e80e 100644 --- a/drivers/regulator/qpnp-oledb-regulator.c +++ b/drivers/regulator/qpnp-oledb-regulator.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/notifier.h> #include <linux/of.h> #include <linux/regmap.h> #include <linux/spmi.h> @@ -24,6 +25,8 @@ #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/of_regulator.h> +#include <linux/regulator/qpnp-labibb-regulator.h> +#include <linux/qpnp/qpnp-pbs.h> #define QPNP_OLEDB_REGULATOR_DRIVER_NAME "qcom,qpnp-oledb-regulator" #define OLEDB_VOUT_STEP_MV 100 @@ -91,6 +94,12 @@ #define OLEDB_ENABLE_NLIMIT_BIT_SHIFT 7 #define OLEDB_NLIMIT_PGM_MASK GENMASK(1, 0) +#define OLEDB_SPARE_CTL 0xE9 +#define OLEDB_FORCE_PD_CTL_SPARE_BIT BIT(7) + +#define OLEDB_PD_PBS_TRIGGER_BIT BIT(0) + +#define OLEDB_SEC_UNLOCK_CODE 0xA5 #define OLEDB_PSM_HYS_CTRL_MIN 13 #define OLEDB_PSM_HYS_CTRL_MAX 26 @@ -150,6 +159,9 @@ struct qpnp_oledb { struct qpnp_oledb_psm_ctl psm_ctl; struct qpnp_oledb_pfm_ctl pfm_ctl; struct qpnp_oledb_fast_precharge_ctl fast_prechg_ctl; + struct notifier_block oledb_nb; + struct mutex bus_lock; + struct device_node *pbs_dev_node; u32 base; u8 mod_enable; @@ -168,6 +180,7 @@ struct qpnp_oledb { bool ext_pin_control; bool dynamic_ext_pinctl_config; bool pbs_control; + bool force_pd_control; }; static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400}; @@ -184,11 +197,13 @@ static int qpnp_oledb_read(struct qpnp_oledb *oledb, u32 address, int rc = 0; struct platform_device *pdev = oledb->pdev; + mutex_lock(&oledb->bus_lock); rc = regmap_bulk_read(oledb->regmap, address, val, count); if (rc) pr_err("Failed to read address=0x%02x sid=0x%02x rc=%d\n", address, to_spmi_device(pdev->dev.parent)->usid, rc); + mutex_unlock(&oledb->bus_lock); return rc; } @@ -197,6 +212,7 @@ static int qpnp_oledb_masked_write(struct qpnp_oledb *oledb, { int rc; + mutex_lock(&oledb->bus_lock); rc = regmap_update_bits(oledb->regmap, address, mask, val); if (rc < 0) pr_err("Failed to write address 0x%04X, rc = %d\n", @@ -205,6 +221,31 @@ static int qpnp_oledb_masked_write(struct qpnp_oledb *oledb, pr_debug("Wrote 0x%02X to addr 0x%04X\n", val, address); + mutex_unlock(&oledb->bus_lock); + return rc; +} + +#define OLEDB_SEC_ACCESS 0xD0 +static int qpnp_oledb_sec_masked_write(struct qpnp_oledb *oledb, u16 address, + u8 mask, u8 val) +{ + int rc = 0; + u8 sec_val = OLEDB_SEC_UNLOCK_CODE; + u16 sec_reg_addr = (address & 0xFF00) | OLEDB_SEC_ACCESS; + + mutex_lock(&oledb->bus_lock); + rc = regmap_write(oledb->regmap, sec_reg_addr, sec_val); + if (rc < 0) { + pr_err("register %x failed rc = %d\n", sec_reg_addr, rc); + goto error; + } + + rc = regmap_update_bits(oledb->regmap, address, mask, val); + if (rc < 0) + pr_err("spmi write failed: addr=%03X, rc=%d\n", address, rc); + +error: + mutex_unlock(&oledb->bus_lock); return rc; } @@ -214,6 +255,7 @@ static int qpnp_oledb_write(struct qpnp_oledb *oledb, u16 address, u8 *val, int rc = 0; struct platform_device *pdev = oledb->pdev; + mutex_lock(&oledb->bus_lock); rc = regmap_bulk_write(oledb->regmap, address, val, count); if (rc) pr_err("Failed to write address=0x%02x sid=0x%02x rc=%d\n", @@ -222,7 +264,8 @@ static int qpnp_oledb_write(struct qpnp_oledb *oledb, u16 address, u8 *val, pr_debug("Wrote 0x%02X to addr 0x%04X\n", *val, address); - return 0; + mutex_unlock(&oledb->bus_lock); + return rc; } static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev) @@ -285,6 +328,8 @@ static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev) static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev) { int rc = 0; + u8 trigger_bitmap = OLEDB_PD_PBS_TRIGGER_BIT; + u8 val; struct qpnp_oledb *oledb = rdev_get_drvdata(rdev); @@ -314,6 +359,27 @@ static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev) pr_debug("Register-control mode, module disabled\n"); } + if (oledb->force_pd_control) { + rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_SPARE_CTL, + &val, 1); + if (rc < 0) { + pr_err("Failed to read OLEDB_SPARE_CTL rc=%d\n", rc); + return rc; + } + + if (val & OLEDB_FORCE_PD_CTL_SPARE_BIT) { + rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node, + trigger_bitmap); + if (rc < 0) { + pr_err("Failed to trigger the PBS sequence\n"); + return rc; + } + pr_debug("PBS event triggered\n"); + } else { + pr_debug("OLEDB_SPARE_CTL register bit not set\n"); + } + } + oledb->mod_enable = false; return rc; @@ -1034,6 +1100,18 @@ static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb) oledb->pbs_control = of_property_read_bool(of_node, "qcom,pbs-control"); + oledb->force_pd_control = + of_property_read_bool(of_node, "qcom,force-pd-control"); + + if (oledb->force_pd_control) { + oledb->pbs_dev_node = of_parse_phandle(of_node, + "qcom,pbs-client", 0); + if (!oledb->pbs_dev_node) { + pr_err("Missing qcom,pbs-client property\n"); + return -EINVAL; + } + } + oledb->current_voltage = -EINVAL; rc = of_property_read_u32(of_node, "qcom,oledb-init-voltage-mv", &oledb->current_voltage); @@ -1116,6 +1194,52 @@ static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb) return rc; } +static int qpnp_oledb_force_pulldown_config(struct qpnp_oledb *oledb) +{ + int rc = 0; + u8 val; + + rc = qpnp_oledb_sec_masked_write(oledb, oledb->base + + OLEDB_SPARE_CTL, OLEDB_FORCE_PD_CTL_SPARE_BIT, 0); + if (rc < 0) { + pr_err("Failed to write SPARE_CTL rc=%d\n", rc); + return rc; + } + + val = 1; + rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_PD_CTL, + &val, 1); + if (rc < 0) { + pr_err("Failed to write PD_CTL rc=%d\n", rc); + return rc; + } + + rc = qpnp_oledb_masked_write(oledb, oledb->base + + OLEDB_SWIRE_CONTROL, OLEDB_EN_SWIRE_PD_UPD_BIT, 0); + if (rc < 0) + pr_err("Failed to write SWIRE_CTL for pbs mode rc=%d\n", + rc); + + return rc; +} + +static int qpnp_labibb_notifier_cb(struct notifier_block *nb, + unsigned long action, void *data) +{ + int rc = 0; + struct qpnp_oledb *oledb = container_of(nb, struct qpnp_oledb, + oledb_nb); + + if (action == LAB_VREG_OK) { + /* Disable SWIRE pull down control and enable via spmi mode */ + rc = qpnp_oledb_force_pulldown_config(oledb); + if (rc < 0) + return NOTIFY_STOP; + } + + return NOTIFY_OK; +} + static int qpnp_oledb_regulator_probe(struct platform_device *pdev) { int rc = 0; @@ -1143,6 +1267,7 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev) return rc; } + mutex_init(&(oledb->bus_lock)); oledb->base = val; rc = qpnp_oledb_parse_dt(oledb); if (rc < 0) { @@ -1156,18 +1281,47 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev) return rc; } + if (oledb->force_pd_control) { + oledb->oledb_nb.notifier_call = qpnp_labibb_notifier_cb; + rc = qpnp_labibb_notifier_register(&oledb->oledb_nb); + if (rc < 0) { + pr_err("Failed to register qpnp_labibb_notifier_cb\n"); + return rc; + } + } + rc = qpnp_oledb_register_regulator(oledb); - if (!rc) - pr_info("OLEDB registered successfully, ext_pin_en=%d mod_en=%d cuurent_voltage=%d mV\n", + if (rc < 0) { + pr_err("Failed to register regulator rc=%d\n", rc); + goto out; + } + pr_info("OLEDB registered successfully, ext_pin_en=%d mod_en=%d current_voltage=%d mV\n", oledb->ext_pin_control, oledb->mod_enable, oledb->current_voltage); + return 0; + +out: + if (oledb->force_pd_control) { + rc = qpnp_labibb_notifier_unregister(&oledb->oledb_nb); + if (rc < 0) + pr_err("Failed to unregister lab_vreg_ok notifier\n"); + } return rc; } static int qpnp_oledb_regulator_remove(struct platform_device *pdev) { - return 0; + int rc = 0; + struct qpnp_oledb *oledb = platform_get_drvdata(pdev); + + if (oledb->force_pd_control) { + rc = qpnp_labibb_notifier_unregister(&oledb->oledb_nb); + if (rc < 0) + pr_err("Failed to unregister lab_vreg_ok notifier\n"); + } + + return rc; } const struct of_device_id qpnp_oledb_regulator_match_table[] = { diff --git a/drivers/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c index 6893e52f6bdd..c75beec59a79 100644 --- a/drivers/regulator/spm-regulator.c +++ b/drivers/regulator/spm-regulator.c @@ -29,9 +29,9 @@ #include <linux/regulator/of_regulator.h> #include <linux/regulator/spm-regulator.h> #include <soc/qcom/spm.h> +#include <linux/arm-smccc.h> #if defined(CONFIG_ARM64) || (defined(CONFIG_ARM) && defined(CONFIG_ARM_PSCI)) - asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64); #else #define __invoke_psci_fn_smc(a, b, c, d) 0 #endif diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index fb991ec76423..696116ebdf50 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -1111,6 +1111,12 @@ static int tps65910_probe(struct platform_device *pdev) pmic->num_regulators = ARRAY_SIZE(tps65910_regs); pmic->ext_sleep_control = tps65910_ext_sleep_control; info = tps65910_regs; + /* Work around silicon erratum SWCZ010: output programmed + * voltage level can go higher than expected or crash + * Workaround: use no synchronization of DCDC clocks + */ + tps65910_reg_clear_bits(pmic->mfd, TPS65910_DCDCCTRL, + DCDCCTRL_DCDCCKSYNC_MASK); break; case TPS65911: pmic->get_ctrl_reg = &tps65911_get_ctrl_register; diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index ec2e9c5fb993..22394fe30579 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -109,6 +109,7 @@ /* OMAP_RTC_OSC_REG bit fields: */ #define OMAP_RTC_OSC_32KCLK_EN BIT(6) #define OMAP_RTC_OSC_SEL_32KCLK_SRC BIT(3) +#define OMAP_RTC_OSC_OSC32K_GZ_DISABLE BIT(4) /* OMAP_RTC_IRQWAKEEN bit fields: */ #define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1) @@ -646,8 +647,9 @@ static int omap_rtc_probe(struct platform_device *pdev) */ if (rtc->has_ext_clk) { reg = rtc_read(rtc, OMAP_RTC_OSC_REG); - rtc_write(rtc, OMAP_RTC_OSC_REG, - reg | OMAP_RTC_OSC_SEL_32KCLK_SRC); + reg &= ~OMAP_RTC_OSC_OSC32K_GZ_DISABLE; + reg |= OMAP_RTC_OSC_32KCLK_EN | OMAP_RTC_OSC_SEL_32KCLK_SRC; + rtc_writel(rtc, OMAP_RTC_OSC_REG, reg); } rtc->type->lock(rtc); diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 7c511add5aa7..bae98521c808 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -124,7 +124,12 @@ con3270_create_status(struct con3270 *cp) static void con3270_update_string(struct con3270 *cp, struct string *s, int nr) { - if (s->len >= cp->view.cols - 5) + if (s->len < 4) { + /* This indicates a bug, but printing a warning would + * cause a deadlock. */ + return; + } + if (s->string[s->len - 4] != TO_RA) return; raw3270_buffer_address(cp->view.dev, s->string + s->len - 3, cp->view.cols * (nr + 1)); @@ -461,11 +466,11 @@ con3270_cline_end(struct con3270 *cp) cp->cline->len + 4 : cp->view.cols; s = con3270_alloc_string(cp, size); memcpy(s->string, cp->cline->string, cp->cline->len); - if (s->len < cp->view.cols - 5) { + if (cp->cline->len < cp->view.cols - 5) { s->string[s->len - 4] = TO_RA; s->string[s->len - 1] = 0; } else { - while (--size > cp->cline->len) + while (--size >= cp->cline->len) s->string[size] = cp->view.ascebc[' ']; } /* Replace cline with allocated line s and reset cline. */ diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index c424c0c7367e..1e16331891a9 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -95,12 +95,13 @@ struct chsc_ssd_area { int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) { struct chsc_ssd_area *ssd_area; + unsigned long flags; int ccode; int ret; int i; int mask; - spin_lock_irq(&chsc_page_lock); + spin_lock_irqsave(&chsc_page_lock, flags); memset(chsc_page, 0, PAGE_SIZE); ssd_area = chsc_page; ssd_area->request.length = 0x0010; @@ -144,7 +145,7 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) ssd->fla[i] = ssd_area->fla[i]; } out: - spin_unlock_irq(&chsc_page_lock); + spin_unlock_irqrestore(&chsc_page_lock, flags); return ret; } @@ -832,9 +833,10 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable) u32 fmt : 4; u32 : 16; } __attribute__ ((packed)) *secm_area; + unsigned long flags; int ret, ccode; - spin_lock_irq(&chsc_page_lock); + spin_lock_irqsave(&chsc_page_lock, flags); memset(chsc_page, 0, PAGE_SIZE); secm_area = chsc_page; secm_area->request.length = 0x0050; @@ -864,7 +866,7 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable) CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n", secm_area->response.code); out: - spin_unlock_irq(&chsc_page_lock); + spin_unlock_irqrestore(&chsc_page_lock, flags); return ret; } @@ -993,6 +995,7 @@ chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, int chsc_get_channel_measurement_chars(struct channel_path *chp) { + unsigned long flags; int ccode, ret; struct { @@ -1022,7 +1025,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp) if (!css_chsc_characteristics.scmc || !css_chsc_characteristics.secm) return 0; - spin_lock_irq(&chsc_page_lock); + spin_lock_irqsave(&chsc_page_lock, flags); memset(chsc_page, 0, PAGE_SIZE); scmc_area = chsc_page; scmc_area->request.length = 0x0010; @@ -1054,7 +1057,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp) chsc_initialize_cmg_chars(chp, scmc_area->cmcv, (struct cmg_chars *) &scmc_area->data); out: - spin_unlock_irq(&chsc_page_lock); + spin_unlock_irqrestore(&chsc_page_lock, flags); return ret; } @@ -1135,6 +1138,7 @@ struct css_chsc_char css_chsc_characteristics; int __init chsc_determine_css_characteristics(void) { + unsigned long flags; int result; struct { struct chsc_header request; @@ -1147,7 +1151,7 @@ chsc_determine_css_characteristics(void) u32 chsc_char[508]; } __attribute__ ((packed)) *scsc_area; - spin_lock_irq(&chsc_page_lock); + spin_lock_irqsave(&chsc_page_lock, flags); memset(chsc_page, 0, PAGE_SIZE); scsc_area = chsc_page; scsc_area->request.length = 0x0010; @@ -1169,7 +1173,7 @@ chsc_determine_css_characteristics(void) CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n", scsc_area->response.code); exit: - spin_unlock_irq(&chsc_page_lock); + spin_unlock_irqrestore(&chsc_page_lock, flags); return result; } diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 5d7fbe4e907e..581001989937 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -3,7 +3,7 @@ * * Debug traces for zfcp. * - * Copyright IBM Corp. 2002, 2013 + * Copyright IBM Corp. 2002, 2016 */ #define KMSG_COMPONENT "zfcp" @@ -65,7 +65,7 @@ void zfcp_dbf_pl_write(struct zfcp_dbf *dbf, void *data, u16 length, char *area, * @tag: tag indicating which kind of unsolicited status has been received * @req: request for which a response was received */ -void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req) +void zfcp_dbf_hba_fsf_res(char *tag, int level, struct zfcp_fsf_req *req) { struct zfcp_dbf *dbf = req->adapter->dbf; struct fsf_qtcb_prefix *q_pref = &req->qtcb->prefix; @@ -85,6 +85,8 @@ void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req) rec->u.res.req_issued = req->issued; rec->u.res.prot_status = q_pref->prot_status; rec->u.res.fsf_status = q_head->fsf_status; + rec->u.res.port_handle = q_head->port_handle; + rec->u.res.lun_handle = q_head->lun_handle; memcpy(rec->u.res.prot_status_qual, &q_pref->prot_status_qual, FSF_PROT_STATUS_QUAL_SIZE); @@ -97,7 +99,7 @@ void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req) rec->pl_len, "fsf_res", req->req_id); } - debug_event(dbf->hba, 1, rec, sizeof(*rec)); + debug_event(dbf->hba, level, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->hba_lock, flags); } @@ -241,7 +243,8 @@ static void zfcp_dbf_set_common(struct zfcp_dbf_rec *rec, if (sdev) { rec->lun_status = atomic_read(&sdev_to_zfcp(sdev)->status); rec->lun = zfcp_scsi_dev_lun(sdev); - } + } else + rec->lun = ZFCP_DBF_INVALID_LUN; } /** @@ -320,13 +323,48 @@ void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp) spin_unlock_irqrestore(&dbf->rec_lock, flags); } +/** + * zfcp_dbf_rec_run_wka - trace wka port event with info like running recovery + * @tag: identifier for event + * @wka_port: well known address port + * @req_id: request ID to correlate with potential HBA trace record + */ +void zfcp_dbf_rec_run_wka(char *tag, struct zfcp_fc_wka_port *wka_port, + u64 req_id) +{ + struct zfcp_dbf *dbf = wka_port->adapter->dbf; + struct zfcp_dbf_rec *rec = &dbf->rec_buf; + unsigned long flags; + + spin_lock_irqsave(&dbf->rec_lock, flags); + memset(rec, 0, sizeof(*rec)); + + rec->id = ZFCP_DBF_REC_RUN; + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->port_status = wka_port->status; + rec->d_id = wka_port->d_id; + rec->lun = ZFCP_DBF_INVALID_LUN; + + rec->u.run.fsf_req_id = req_id; + rec->u.run.rec_status = ~0; + rec->u.run.rec_step = ~0; + rec->u.run.rec_action = ~0; + rec->u.run.rec_count = ~0; + + debug_event(dbf->rec, 1, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->rec_lock, flags); +} + static inline -void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len, - u64 req_id, u32 d_id) +void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, + char *paytag, struct scatterlist *sg, u8 id, u16 len, + u64 req_id, u32 d_id, u16 cap_len) { struct zfcp_dbf_san *rec = &dbf->san_buf; u16 rec_len; unsigned long flags; + struct zfcp_dbf_pay *payload = &dbf->pay_buf; + u16 pay_sum = 0; spin_lock_irqsave(&dbf->san_lock, flags); memset(rec, 0, sizeof(*rec)); @@ -334,10 +372,41 @@ void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len, rec->id = id; rec->fsf_req_id = req_id; rec->d_id = d_id; - rec_len = min(len, (u16)ZFCP_DBF_SAN_MAX_PAYLOAD); - memcpy(rec->payload, data, rec_len); memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->pl_len = len; /* full length even if we cap pay below */ + if (!sg) + goto out; + rec_len = min_t(unsigned int, sg->length, ZFCP_DBF_SAN_MAX_PAYLOAD); + memcpy(rec->payload, sg_virt(sg), rec_len); /* part of 1st sg entry */ + if (len <= rec_len) + goto out; /* skip pay record if full content in rec->payload */ + + /* if (len > rec_len): + * dump data up to cap_len ignoring small duplicate in rec->payload + */ + spin_lock(&dbf->pay_lock); + memset(payload, 0, sizeof(*payload)); + memcpy(payload->area, paytag, ZFCP_DBF_TAG_LEN); + payload->fsf_req_id = req_id; + payload->counter = 0; + for (; sg && pay_sum < cap_len; sg = sg_next(sg)) { + u16 pay_len, offset = 0; + + while (offset < sg->length && pay_sum < cap_len) { + pay_len = min((u16)ZFCP_DBF_PAY_MAX_REC, + (u16)(sg->length - offset)); + /* cap_len <= pay_sum < cap_len+ZFCP_DBF_PAY_MAX_REC */ + memcpy(payload->data, sg_virt(sg) + offset, pay_len); + debug_event(dbf->pay, 1, payload, + zfcp_dbf_plen(pay_len)); + payload->counter++; + offset += pay_len; + pay_sum += pay_len; + } + } + spin_unlock(&dbf->pay_lock); +out: debug_event(dbf->san, 1, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->san_lock, flags); } @@ -354,9 +423,62 @@ void zfcp_dbf_san_req(char *tag, struct zfcp_fsf_req *fsf, u32 d_id) struct zfcp_fsf_ct_els *ct_els = fsf->data; u16 length; - length = (u16)(ct_els->req->length + FC_CT_HDR_LEN); - zfcp_dbf_san(tag, dbf, sg_virt(ct_els->req), ZFCP_DBF_SAN_REQ, length, - fsf->req_id, d_id); + length = (u16)zfcp_qdio_real_bytes(ct_els->req); + zfcp_dbf_san(tag, dbf, "san_req", ct_els->req, ZFCP_DBF_SAN_REQ, + length, fsf->req_id, d_id, length); +} + +static u16 zfcp_dbf_san_res_cap_len_if_gpn_ft(char *tag, + struct zfcp_fsf_req *fsf, + u16 len) +{ + struct zfcp_fsf_ct_els *ct_els = fsf->data; + struct fc_ct_hdr *reqh = sg_virt(ct_els->req); + struct fc_ns_gid_ft *reqn = (struct fc_ns_gid_ft *)(reqh + 1); + struct scatterlist *resp_entry = ct_els->resp; + struct fc_gpn_ft_resp *acc; + int max_entries, x, last = 0; + + if (!(memcmp(tag, "fsscth2", 7) == 0 + && ct_els->d_id == FC_FID_DIR_SERV + && reqh->ct_rev == FC_CT_REV + && reqh->ct_in_id[0] == 0 + && reqh->ct_in_id[1] == 0 + && reqh->ct_in_id[2] == 0 + && reqh->ct_fs_type == FC_FST_DIR + && reqh->ct_fs_subtype == FC_NS_SUBTYPE + && reqh->ct_options == 0 + && reqh->_ct_resvd1 == 0 + && reqh->ct_cmd == FC_NS_GPN_FT + /* reqh->ct_mr_size can vary so do not match but read below */ + && reqh->_ct_resvd2 == 0 + && reqh->ct_reason == 0 + && reqh->ct_explan == 0 + && reqh->ct_vendor == 0 + && reqn->fn_resvd == 0 + && reqn->fn_domain_id_scope == 0 + && reqn->fn_area_id_scope == 0 + && reqn->fn_fc4_type == FC_TYPE_FCP)) + return len; /* not GPN_FT response so do not cap */ + + acc = sg_virt(resp_entry); + max_entries = (reqh->ct_mr_size * 4 / sizeof(struct fc_gpn_ft_resp)) + + 1 /* zfcp_fc_scan_ports: bytes correct, entries off-by-one + * to account for header as 1st pseudo "entry" */; + + /* the basic CT_IU preamble is the same size as one entry in the GPN_FT + * response, allowing us to skip special handling for it - just skip it + */ + for (x = 1; x < max_entries && !last; x++) { + if (x % (ZFCP_FC_GPN_FT_ENT_PAGE + 1)) + acc++; + else + acc = sg_virt(++resp_entry); + + last = acc->fp_flags & FC_NS_FID_LAST; + } + len = min(len, (u16)(x * sizeof(struct fc_gpn_ft_resp))); + return len; /* cap after last entry */ } /** @@ -370,9 +492,10 @@ void zfcp_dbf_san_res(char *tag, struct zfcp_fsf_req *fsf) struct zfcp_fsf_ct_els *ct_els = fsf->data; u16 length; - length = (u16)(ct_els->resp->length + FC_CT_HDR_LEN); - zfcp_dbf_san(tag, dbf, sg_virt(ct_els->resp), ZFCP_DBF_SAN_RES, length, - fsf->req_id, 0); + length = (u16)zfcp_qdio_real_bytes(ct_els->resp); + zfcp_dbf_san(tag, dbf, "san_res", ct_els->resp, ZFCP_DBF_SAN_RES, + length, fsf->req_id, ct_els->d_id, + zfcp_dbf_san_res_cap_len_if_gpn_ft(tag, fsf, length)); } /** @@ -386,11 +509,13 @@ void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf) struct fsf_status_read_buffer *srb = (struct fsf_status_read_buffer *) fsf->data; u16 length; + struct scatterlist sg; length = (u16)(srb->length - offsetof(struct fsf_status_read_buffer, payload)); - zfcp_dbf_san(tag, dbf, srb->payload.data, ZFCP_DBF_SAN_ELS, length, - fsf->req_id, ntoh24(srb->d_id)); + sg_init_one(&sg, srb->payload.data, length); + zfcp_dbf_san(tag, dbf, "san_els", &sg, ZFCP_DBF_SAN_ELS, length, + fsf->req_id, ntoh24(srb->d_id), length); } /** @@ -399,7 +524,8 @@ void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf) * @sc: pointer to struct scsi_cmnd * @fsf: pointer to struct zfcp_fsf_req */ -void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf) +void zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *sc, + struct zfcp_fsf_req *fsf) { struct zfcp_adapter *adapter = (struct zfcp_adapter *) sc->device->host->hostdata[0]; @@ -442,7 +568,7 @@ void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf) } } - debug_event(dbf->scsi, 1, rec, sizeof(*rec)); + debug_event(dbf->scsi, level, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->scsi_lock, flags); } diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index 0be3d48681ae..36d07584271d 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, 2010 + * Copyright IBM Corp. 2008, 2015 */ #ifndef ZFCP_DBF_H @@ -17,6 +17,11 @@ #define ZFCP_DBF_INVALID_LUN 0xFFFFFFFFFFFFFFFFull +enum zfcp_dbf_pseudo_erp_act_type { + ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD = 0xff, + ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL = 0xfe, +}; + /** * struct zfcp_dbf_rec_trigger - trace record for triggered recovery action * @ready: number of ready recovery actions @@ -110,6 +115,7 @@ struct zfcp_dbf_san { u32 d_id; #define ZFCP_DBF_SAN_MAX_PAYLOAD (FC_CT_HDR_LEN + 32) char payload[ZFCP_DBF_SAN_MAX_PAYLOAD]; + u16 pl_len; } __packed; /** @@ -126,6 +132,8 @@ struct zfcp_dbf_hba_res { u8 prot_status_qual[FSF_PROT_STATUS_QUAL_SIZE]; u32 fsf_status; u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE]; + u32 port_handle; + u32 lun_handle; } __packed; /** @@ -279,7 +287,7 @@ static inline void zfcp_dbf_hba_fsf_resp(char *tag, int level, struct zfcp_fsf_req *req) { if (debug_level_enabled(req->adapter->dbf->hba, level)) - zfcp_dbf_hba_fsf_res(tag, req); + zfcp_dbf_hba_fsf_res(tag, level, req); } /** @@ -318,7 +326,7 @@ void _zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *scmd, scmd->device->host->hostdata[0]; if (debug_level_enabled(adapter->dbf->scsi, level)) - zfcp_dbf_scsi(tag, scmd, req); + zfcp_dbf_scsi(tag, level, scmd, req); } /** diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 3fb410977014..a59d678125bd 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, 2010 + * Copyright IBM Corp. 2002, 2015 */ #define KMSG_COMPONENT "zfcp" @@ -1217,8 +1217,14 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) break; case ZFCP_ERP_ACTION_REOPEN_PORT: - if (result == ZFCP_ERP_SUCCEEDED) - zfcp_scsi_schedule_rport_register(port); + /* This switch case might also happen after a forced reopen + * was successfully done and thus overwritten with a new + * non-forced reopen at `ersfs_2'. In this case, we must not + * do the clean-up of the non-forced version. + */ + if (act->step != ZFCP_ERP_STEP_UNINITIALIZED) + if (result == ZFCP_ERP_SUCCEEDED) + zfcp_scsi_schedule_rport_register(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 5b500652572b..c8fed9fa1cca 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, 2010 + * Copyright IBM Corp. 2002, 2015 */ #ifndef ZFCP_EXT_H @@ -35,8 +35,9 @@ 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_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 *, struct zfcp_fsf_req *); +extern void zfcp_dbf_hba_fsf_res(char *, int, struct zfcp_fsf_req *); extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *); extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *); extern void zfcp_dbf_hba_def_err(struct zfcp_adapter *, u64, u16, void **); @@ -44,7 +45,8 @@ extern void zfcp_dbf_hba_basic(char *, struct zfcp_adapter *); extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32); extern void zfcp_dbf_san_res(char *, struct zfcp_fsf_req *); extern void zfcp_dbf_san_in_els(char *, struct zfcp_fsf_req *); -extern void zfcp_dbf_scsi(char *, struct scsi_cmnd *, struct zfcp_fsf_req *); +extern void zfcp_dbf_scsi(char *, int, struct scsi_cmnd *, + struct zfcp_fsf_req *); /* zfcp_erp.c */ extern void zfcp_erp_set_adapter_status(struct zfcp_adapter *, u32); diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 522a633c866a..75f820ca17b7 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -3,7 +3,7 @@ * * Implementation of FSF commands. * - * Copyright IBM Corp. 2002, 2013 + * Copyright IBM Corp. 2002, 2015 */ #define KMSG_COMPONENT "zfcp" @@ -508,7 +508,10 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req) fc_host_port_type(shost) = FC_PORTTYPE_PTP; break; case FSF_TOPO_FABRIC: - fc_host_port_type(shost) = FC_PORTTYPE_NPORT; + if (bottom->connection_features & FSF_FEATURE_NPIV_MODE) + fc_host_port_type(shost) = FC_PORTTYPE_NPIV; + else + fc_host_port_type(shost) = FC_PORTTYPE_NPORT; break; case FSF_TOPO_AL: fc_host_port_type(shost) = FC_PORTTYPE_NLPORT; @@ -613,7 +616,6 @@ static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req) if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) { fc_host_permanent_port_name(shost) = bottom->wwpn; - fc_host_port_type(shost) = FC_PORTTYPE_NPIV; } else fc_host_permanent_port_name(shost) = fc_host_port_name(shost); fc_host_maxframe_size(shost) = bottom->maximum_frame_size; @@ -982,8 +984,12 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req, if (zfcp_adapter_multi_buffer_active(adapter)) { if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_req)) return -EIO; + qtcb->bottom.support.req_buf_length = + zfcp_qdio_real_bytes(sg_req); if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_resp)) return -EIO; + qtcb->bottom.support.resp_buf_length = + zfcp_qdio_real_bytes(sg_resp); zfcp_qdio_set_data_div(qdio, &req->qdio_req, zfcp_qdio_sbale_count(sg_req)); @@ -1073,6 +1079,7 @@ int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port, req->handler = zfcp_fsf_send_ct_handler; req->qtcb->header.port_handle = wka_port->handle; + ct->d_id = wka_port->d_id; req->data = ct; zfcp_dbf_san_req("fssct_1", req, wka_port->d_id); @@ -1169,6 +1176,7 @@ int zfcp_fsf_send_els(struct zfcp_adapter *adapter, u32 d_id, hton24(req->qtcb->bottom.support.d_id, d_id); req->handler = zfcp_fsf_send_els_handler; + els->d_id = d_id; req->data = els; zfcp_dbf_san_req("fssels1", req, d_id); @@ -1575,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; + struct zfcp_fsf_req *req = NULL; int retval = -EIO; spin_lock_irq(&qdio->req_q_lock); @@ -1604,6 +1612,8 @@ 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)) + zfcp_dbf_rec_run_wka("fsowp_1", wka_port, req->req_id); return retval; } @@ -1628,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; + struct zfcp_fsf_req *req = NULL; int retval = -EIO; spin_lock_irq(&qdio->req_q_lock); @@ -1657,6 +1667,8 @@ 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)) + 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 57ae3ae1046d..be1c04b334c5 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, 2010 + * Copyright IBM Corp. 2002, 2015 */ #ifndef FSF_H @@ -436,6 +436,7 @@ struct zfcp_blk_drv_data { * @handler_data: data passed to handler function * @port: Optional pointer to port for zfcp internal ELS (only test link ADISC) * @status: used to pass error status to calling function + * @d_id: Destination ID of either open WKA port for CT or of D_ID for ELS */ struct zfcp_fsf_ct_els { struct scatterlist *req; @@ -444,6 +445,7 @@ struct zfcp_fsf_ct_els { void *handler_data; struct zfcp_port *port; int status; + u32 d_id; }; #endif /* FSF_H */ diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index b3c6ff49103b..9069f98a1817 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, 2013 + * Copyright IBM Corp. 2002, 2015 */ #define KMSG_COMPONENT "zfcp" @@ -556,6 +556,9 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port) ids.port_id = port->d_id; ids.roles = FC_RPORT_ROLE_FCP_TARGET; + zfcp_dbf_rec_trig("scpaddy", port->adapter, port, NULL, + ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD, + ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD); rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids); if (!rport) { dev_err(&port->adapter->ccw_device->dev, @@ -577,6 +580,9 @@ static void zfcp_scsi_rport_block(struct zfcp_port *port) struct fc_rport *rport = port->rport; if (rport) { + zfcp_dbf_rec_trig("scpdely", port->adapter, port, NULL, + ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL, + ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL); fc_remote_port_delete(rport); port->rport = NULL; } diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 41f9a00e4f74..7aa01c1960ea 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -2297,15 +2297,23 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, } case ARCMSR_MESSAGE_WRITE_WQBUFFER: { unsigned char *ver_addr; - int32_t user_len, cnt2end; + uint32_t user_len; + int32_t cnt2end; uint8_t *pQbuffer, *ptmpuserbuffer; + + user_len = pcmdmessagefld->cmdmessage.Length; + if (user_len > ARCMSR_API_DATA_BUFLEN) { + retvalue = ARCMSR_MESSAGE_FAIL; + goto message_out; + } + ver_addr = kmalloc(ARCMSR_API_DATA_BUFLEN, GFP_ATOMIC); if (!ver_addr) { retvalue = ARCMSR_MESSAGE_FAIL; goto message_out; } ptmpuserbuffer = ver_addr; - user_len = pcmdmessagefld->cmdmessage.Length; + memcpy(ptmpuserbuffer, pcmdmessagefld->messagedatabuffer, user_len); spin_lock_irqsave(&acb->wqbuffer_lock, flags); @@ -2537,18 +2545,9 @@ static int arcmsr_queue_command_lck(struct scsi_cmnd *cmd, struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata; struct CommandControlBlock *ccb; int target = cmd->device->id; - int lun = cmd->device->lun; - uint8_t scsicmd = cmd->cmnd[0]; cmd->scsi_done = done; cmd->host_scribble = NULL; cmd->result = 0; - if ((scsicmd == SYNCHRONIZE_CACHE) ||(scsicmd == SEND_DIAGNOSTIC)){ - if(acb->devstate[target][lun] == ARECA_RAID_GONE) { - cmd->result = (DID_NO_CONNECT << 16); - } - cmd->scsi_done(cmd); - return 0; - } if (target == 16) { /* virtual device for iop message transfer */ arcmsr_handle_virtual_command(acb, cmd); diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c index 67669a9e73c1..f3a33312a9a6 100644 --- a/drivers/scsi/fnic/fnic_fcs.c +++ b/drivers/scsi/fnic/fnic_fcs.c @@ -954,8 +954,8 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq) skb_put(skb, len); pa = pci_map_single(fnic->pdev, skb->data, len, PCI_DMA_FROMDEVICE); - r = pci_dma_mapping_error(fnic->pdev, pa); - if (r) { + if (pci_dma_mapping_error(fnic->pdev, pa)) { + r = -ENOMEM; printk(KERN_ERR "PCI mapping failed with error %d\n", r); goto free_skb; } @@ -1093,8 +1093,8 @@ static int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp) pa = pci_map_single(fnic->pdev, eth_hdr, tot_len, PCI_DMA_TODEVICE); - ret = pci_dma_mapping_error(fnic->pdev, pa); - if (ret) { + if (pci_dma_mapping_error(fnic->pdev, pa)) { + ret = -ENOMEM; printk(KERN_ERR "DMA map failed with error %d\n", ret); goto free_skb_on_err; } diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index a3860367b568..e9ce74afd13f 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -3930,6 +3930,70 @@ static int hpsa_set_local_logical_count(struct ctlr_info *h, return rc; } +static bool hpsa_is_disk_spare(struct ctlr_info *h, u8 *lunaddrbytes) +{ + struct bmic_identify_physical_device *id_phys; + bool is_spare = false; + int rc; + + id_phys = kzalloc(sizeof(*id_phys), GFP_KERNEL); + if (!id_phys) + return false; + + rc = hpsa_bmic_id_physical_device(h, + lunaddrbytes, + GET_BMIC_DRIVE_NUMBER(lunaddrbytes), + id_phys, sizeof(*id_phys)); + if (rc == 0) + is_spare = (id_phys->more_flags >> 6) & 0x01; + + kfree(id_phys); + return is_spare; +} + +#define RPL_DEV_FLAG_NON_DISK 0x1 +#define RPL_DEV_FLAG_UNCONFIG_DISK_REPORTING_SUPPORTED 0x2 +#define RPL_DEV_FLAG_UNCONFIG_DISK 0x4 + +#define BMIC_DEVICE_TYPE_ENCLOSURE 6 + +static bool hpsa_skip_device(struct ctlr_info *h, u8 *lunaddrbytes, + struct ext_report_lun_entry *rle) +{ + u8 device_flags; + u8 device_type; + + if (!MASKED_DEVICE(lunaddrbytes)) + return false; + + device_flags = rle->device_flags; + device_type = rle->device_type; + + if (device_flags & RPL_DEV_FLAG_NON_DISK) { + if (device_type == BMIC_DEVICE_TYPE_ENCLOSURE) + return false; + return true; + } + + if (!(device_flags & RPL_DEV_FLAG_UNCONFIG_DISK_REPORTING_SUPPORTED)) + return false; + + if (device_flags & RPL_DEV_FLAG_UNCONFIG_DISK) + return false; + + /* + * Spares may be spun down, we do not want to + * do an Inquiry to a RAID set spare drive as + * that would have them spun up, that is a + * performance hit because I/O to the RAID device + * stops while the spin up occurs which can take + * over 50 seconds. + */ + if (hpsa_is_disk_spare(h, lunaddrbytes)) + return true; + + return false; +} static void hpsa_update_scsi_devices(struct ctlr_info *h) { @@ -4023,6 +4087,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h) u8 *lunaddrbytes, is_OBDR = 0; int rc = 0; int phys_dev_index = i - (raid_ctlr_position == 0); + bool skip_device = false; physical_device = i < nphysicals + (raid_ctlr_position == 0); @@ -4030,10 +4095,15 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h) lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position, i, nphysicals, nlogicals, physdev_list, logdev_list); - /* skip masked non-disk devices */ - if (MASKED_DEVICE(lunaddrbytes) && physical_device && - (physdev_list->LUN[phys_dev_index].device_flags & 0x01)) - continue; + /* + * Skip over some devices such as a spare. + */ + if (!tmpdevice->external && physical_device) { + skip_device = hpsa_skip_device(h, lunaddrbytes, + &physdev_list->LUN[phys_dev_index]); + if (skip_device) + continue; + } /* Get device type, vendor, model, device id */ rc = hpsa_update_device_info(h, lunaddrbytes, tmpdevice, diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 6aa317c303e2..1f9f9e5af207 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -717,7 +717,6 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) spin_lock_irqsave(vhost->host->host_lock, flags); vhost->state = IBMVFC_NO_CRQ; vhost->logged_in = 0; - ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); /* Clean out the queue */ memset(crq->msgs, 0, PAGE_SIZE); diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index ef4ff03242ea..aaf7da07a358 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -1923,7 +1923,7 @@ struct megasas_instance_template { }; #define MEGASAS_IS_LOGICAL(scp) \ - (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1 + ((scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1) #define MEGASAS_DEV_INDEX(scp) \ (((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \ diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 3f8d357b1bac..17c440b9d086 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -1688,16 +1688,13 @@ megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd) goto out_done; } - switch (scmd->cmnd[0]) { - case SYNCHRONIZE_CACHE: - /* - * FW takes care of flush cache on its own - * No need to send it down - */ + /* + * FW takes care of flush cache on its own for Virtual Disk. + * No need to send it down for VD. For JBOD send SYNCHRONIZE_CACHE to FW. + */ + if ((scmd->cmnd[0] == SYNCHRONIZE_CACHE) && MEGASAS_IS_LOGICAL(scmd)) { scmd->result = DID_OK << 16; goto out_done; - default: - break; } if (instance->instancet->build_and_issue_cmd(instance, scmd)) { @@ -5941,11 +5938,11 @@ static void megasas_detach_one(struct pci_dev *pdev) if (fusion->ld_drv_map[i]) free_pages((ulong)fusion->ld_drv_map[i], fusion->drv_map_pages); - if (fusion->pd_seq_sync) - dma_free_coherent(&instance->pdev->dev, - pd_seq_map_sz, - fusion->pd_seq_sync[i], - fusion->pd_seq_phys[i]); + if (fusion->pd_seq_sync[i]) + dma_free_coherent(&instance->pdev->dev, + pd_seq_map_sz, + fusion->pd_seq_sync[i], + fusion->pd_seq_phys[i]); } free_pages((ulong)instance->ctrl_context, instance->ctrl_context_pages); diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 6180f7970bbf..8cead04f26d6 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -1275,9 +1275,9 @@ scsih_target_alloc(struct scsi_target *starget) sas_target_priv_data->handle = raid_device->handle; sas_target_priv_data->sas_address = raid_device->wwid; sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME; - sas_target_priv_data->raid_device = raid_device; if (ioc->is_warpdrive) - raid_device->starget = starget; + sas_target_priv_data->raid_device = raid_device; + raid_device->starget = starget; } spin_unlock_irqrestore(&ioc->raid_device_lock, flags); return 0; @@ -3706,6 +3706,11 @@ _scsih_temp_threshold_events(struct MPT3SAS_ADAPTER *ioc, } } +static inline bool ata_12_16_cmd(struct scsi_cmnd *scmd) +{ + return (scmd->cmnd[0] == ATA_12 || scmd->cmnd[0] == ATA_16); +} + /** * _scsih_flush_running_cmds - completing outstanding commands. * @ioc: per adapter object @@ -3727,6 +3732,9 @@ _scsih_flush_running_cmds(struct MPT3SAS_ADAPTER *ioc) if (!scmd) continue; count++; + if (ata_12_16_cmd(scmd)) + scsi_internal_device_unblock(scmd->device, + SDEV_RUNNING); mpt3sas_base_free_smid(ioc, smid); scsi_dma_unmap(scmd); if (ioc->pci_error_recovery) @@ -3831,8 +3839,6 @@ _scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status) SAM_STAT_CHECK_CONDITION; } - - /** * scsih_qcmd - main scsi request entry point * @scmd: pointer to scsi command object @@ -3859,6 +3865,13 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) if (ioc->logging_level & MPT_DEBUG_SCSI) scsi_print_command(scmd); + /* + * Lock the device for any subsequent command until command is + * done. + */ + if (ata_12_16_cmd(scmd)) + scsi_internal_device_block(scmd->device); + sas_device_priv_data = scmd->device->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { scmd->result = DID_NO_CONNECT << 16; @@ -4431,6 +4444,9 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) if (scmd == NULL) return 1; + if (ata_12_16_cmd(scmd)) + scsi_internal_device_unblock(scmd->device, SDEV_RUNNING); + mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); if (mpi_reply == NULL) { @@ -4510,7 +4526,7 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) le16_to_cpu(mpi_reply->DevHandle)); mpt3sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq); - if (!(ioc->logging_level & MPT_DEBUG_REPLY) && + if ((ioc->logging_level & MPT_DEBUG_REPLY) && ((scmd->sense_buffer[2] == UNIT_ATTENTION) || (scmd->sense_buffer[2] == MEDIUM_ERROR) || (scmd->sense_buffer[2] == HARDWARE_ERROR))) diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index fc6674db4f2d..c44cbf46221c 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -2257,6 +2257,8 @@ qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time) { scsi_qla_host_t *vha = shost_priv(shost); + if (test_bit(UNLOADING, &vha->dpc_flags)) + return 1; if (!vha->host) return 1; if (time > vha->hw->loop_reset_delay * HZ) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index d09d60293c27..e357a393d56e 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -4981,6 +4981,7 @@ static void __exit scsi_debug_exit(void) bus_unregister(&pseudo_lld_bus); root_device_unregister(pseudo_primary); + vfree(map_storep); vfree(dif_storep); vfree(fake_storep); } diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 87b35b78d879..95fa74203b99 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1458,12 +1458,12 @@ retry: out_err: kfree(lun_data); out: - scsi_device_put(sdev); if (scsi_device_created(sdev)) /* * the sdev we used didn't appear in the report luns scan */ __scsi_remove_device(sdev); + scsi_device_put(sdev); return ret; } diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 5d81bcc1dc75..d5c00951cf93 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2806,10 +2806,10 @@ static int sd_revalidate_disk(struct gendisk *disk) if (sdkp->opt_xfer_blocks && sdkp->opt_xfer_blocks <= dev_max && sdkp->opt_xfer_blocks <= SD_DEF_XFER_BLOCKS && - sdkp->opt_xfer_blocks * sdp->sector_size >= PAGE_CACHE_SIZE) - rw_max = q->limits.io_opt = - sdkp->opt_xfer_blocks * sdp->sector_size; - else + logical_to_bytes(sdp, sdkp->opt_xfer_blocks) >= PAGE_CACHE_SIZE) { + q->limits.io_opt = logical_to_bytes(sdp, sdkp->opt_xfer_blocks); + rw_max = logical_to_sectors(sdp, sdkp->opt_xfer_blocks); + } else rw_max = BLK_DEF_MAX_SECTORS; /* Combine with controller limits */ diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 654630bb7d0e..765a6f1ac1b7 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -151,6 +151,11 @@ static inline sector_t logical_to_sectors(struct scsi_device *sdev, sector_t blo return blocks << (ilog2(sdev->sector_size) - 9); } +static inline unsigned int logical_to_bytes(struct scsi_device *sdev, sector_t blocks) +{ + return blocks * sdev->sector_size; +} + /* * A DIF-capable target device can be formatted with different * protection schemes. Currently 0 through 3 are defined: diff --git a/drivers/sensors/sensors_ssc.c b/drivers/sensors/sensors_ssc.c index 0910ef34e777..0e299efece94 100644 --- a/drivers/sensors/sensors_ssc.c +++ b/drivers/sensors/sensors_ssc.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 @@ -26,6 +26,8 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/sysfs.h> +#include <linux/workqueue.h> + #include <soc/qcom/subsystem_restart.h> #define IMAGE_LOAD_CMD 1 @@ -64,10 +66,11 @@ static struct attribute *attrs[] = { }; static struct platform_device *slpi_private; +static struct work_struct slpi_ldr_work; -static void slpi_loader_do(struct platform_device *pdev) +static void slpi_load_fw(struct work_struct *slpi_ldr_work) { - + struct platform_device *pdev = slpi_private; struct slpi_loader_private *priv = NULL; int ret; const char *firmware_name = NULL; @@ -111,6 +114,12 @@ fail: dev_err(&pdev->dev, "%s: SLPI image loading failed\n", __func__); } +static void slpi_loader_do(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s: scheduling work to load SLPI fw\n", __func__); + schedule_work(&slpi_ldr_work); +} + static void slpi_loader_unload(struct platform_device *pdev) { struct slpi_loader_private *priv = NULL; @@ -336,6 +345,8 @@ static int sensors_ssc_probe(struct platform_device *pdev) goto cdev_add_err; } + INIT_WORK(&slpi_ldr_work, slpi_load_fw); + return 0; cdev_add_err: diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index d3f1050499f3..75bebf66376d 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -46,6 +46,15 @@ config QPNP_HAPTIC module provides haptic feedback for user actions such as a long press on the touch screen. It uses the Android timed-output framework. +config QPNP_PBS + tristate "PBS trigger support for QPNP PMIC" + depends on SPMI + help + This driver supports configuring software PBS trigger event through PBS + RAM on Qualcomm Technologies, Inc. QPNP PMICs. This module provides + the APIs to the client drivers that wants to send the PBS trigger + event to the PBS RAM. + config MSM_SMD depends on MSM_SMEM bool "MSM Shared Memory Driver (SMD)" diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 2605107e2dbd..fa350d122384 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -52,6 +52,7 @@ endif obj-$(CONFIG_QPNP_HAPTIC) += qpnp-haptic.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_CPUSS_DUMP) += cpuss_dump.o +obj-$(CONFIG_QPNP_PBS) += qpnp-pbs.o obj-$(CONFIG_QCOM_PM) += spm.o obj-$(CONFIG_QCOM_SMD) += smd.o obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 768871ffa9a7..4e541773c805 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -195,6 +195,38 @@ struct ce_irq_list { irqreturn_t (*handler)(int, void *); }; +struct icnss_vreg_info { + struct regulator *reg; + const char *name; + u32 min_v; + u32 max_v; + u32 load_ua; + unsigned long settle_delay; + bool required; +}; + +struct icnss_clk_info { + struct clk *handle; + const char *name; + u32 freq; + bool required; +}; + +static struct icnss_vreg_info icnss_vreg_info[] = { + {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, true}, + {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false}, + {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false}, + {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false}, +}; + +#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info) + +static struct icnss_clk_info icnss_clk_info[] = { + {NULL, "cxo_ref_clk_pin", 0, false}, +}; + +#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info) + struct icnss_stats { struct { uint32_t posted; @@ -248,6 +280,7 @@ struct icnss_stats { uint32_t rejuvenate_ack_req; uint32_t rejuvenate_ack_resp; uint32_t rejuvenate_ack_err; + uint32_t trigger_recovery; }; #define MAX_NO_OF_MAC_ADDR 4 @@ -267,6 +300,8 @@ static struct icnss_priv { struct platform_device *pdev; struct icnss_driver_ops *ops; struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS]; + struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE]; + struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE]; u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS]; phys_addr_t mem_base_pa; void __iomem *mem_base_va; @@ -664,41 +699,220 @@ out: return ret; } +static int icnss_vreg_on(struct icnss_priv *priv) +{ + int ret = 0; + struct icnss_vreg_info *vreg_info; + int i; + + for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) { + vreg_info = &priv->vreg_info[i]; + + if (!vreg_info->reg) + continue; + + icnss_pr_dbg("Regulator %s being enabled\n", vreg_info->name); + + ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v, + vreg_info->max_v); + if (ret) { + icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n", + vreg_info->name, vreg_info->min_v, + vreg_info->max_v, ret); + break; + } + + if (vreg_info->load_ua) { + ret = regulator_set_load(vreg_info->reg, + vreg_info->load_ua); + if (ret < 0) { + icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n", + vreg_info->name, + vreg_info->load_ua, ret); + break; + } + } + + ret = regulator_enable(vreg_info->reg); + if (ret) { + icnss_pr_err("Regulator %s, can't enable: %d\n", + vreg_info->name, ret); + break; + } + + if (vreg_info->settle_delay) + udelay(vreg_info->settle_delay); + } + + if (!ret) + return 0; + + for (; i >= 0; i--) { + vreg_info = &priv->vreg_info[i]; + + if (!vreg_info->reg) + continue; + + regulator_disable(vreg_info->reg); + regulator_set_load(vreg_info->reg, 0); + regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v); + } + + return ret; +} + +static int icnss_vreg_off(struct icnss_priv *priv) +{ + int ret = 0; + struct icnss_vreg_info *vreg_info; + int i; + + for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) { + vreg_info = &priv->vreg_info[i]; + + if (!vreg_info->reg) + continue; + + icnss_pr_dbg("Regulator %s being disabled\n", vreg_info->name); + + ret = regulator_disable(vreg_info->reg); + if (ret) + icnss_pr_err("Regulator %s, can't disable: %d\n", + vreg_info->name, ret); + + ret = regulator_set_load(vreg_info->reg, 0); + if (ret < 0) + icnss_pr_err("Regulator %s, can't set load: %d\n", + vreg_info->name, ret); + + ret = regulator_set_voltage(vreg_info->reg, 0, + vreg_info->max_v); + if (ret) + icnss_pr_err("Regulator %s, can't set voltage: %d\n", + vreg_info->name, ret); + } + + return ret; +} + +static int icnss_clk_init(struct icnss_priv *priv) +{ + struct icnss_clk_info *clk_info; + int i; + int ret = 0; + + for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) { + clk_info = &priv->clk_info[i]; + + if (!clk_info->handle) + continue; + + icnss_pr_dbg("Clock %s being enabled\n", clk_info->name); + + if (clk_info->freq) { + ret = clk_set_rate(clk_info->handle, clk_info->freq); + + if (ret) { + icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n", + clk_info->name, clk_info->freq, + ret); + break; + } + } + + ret = clk_prepare_enable(clk_info->handle); + if (ret) { + icnss_pr_err("Clock %s, can't enable: %d\n", + clk_info->name, ret); + break; + } + } + + if (ret == 0) + return 0; + + for (; i >= 0; i--) { + clk_info = &priv->clk_info[i]; + + if (!clk_info->handle) + continue; + + clk_disable_unprepare(clk_info->handle); + } + + return ret; +} + +static int icnss_clk_deinit(struct icnss_priv *priv) +{ + struct icnss_clk_info *clk_info; + int i; + + for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) { + clk_info = &priv->clk_info[i]; + + if (!clk_info->handle) + continue; + + icnss_pr_dbg("Clock %s being disabled\n", clk_info->name); + + clk_disable_unprepare(clk_info->handle); + } + + return 0; +} + static int icnss_hw_power_on(struct icnss_priv *priv) { int ret = 0; - unsigned long flags; icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state); - spin_lock_irqsave(&priv->on_off_lock, flags); + spin_lock(&priv->on_off_lock); if (test_bit(ICNSS_POWER_ON, &priv->state)) { - spin_unlock_irqrestore(&priv->on_off_lock, flags); + spin_unlock(&priv->on_off_lock); return ret; } set_bit(ICNSS_POWER_ON, &priv->state); - spin_unlock_irqrestore(&priv->on_off_lock, flags); + spin_unlock(&priv->on_off_lock); + + ret = icnss_vreg_on(priv); + if (ret) + goto out; + + ret = icnss_clk_init(priv); + if (ret) + goto vreg_off; + + return ret; +vreg_off: + icnss_vreg_off(priv); +out: + clear_bit(ICNSS_POWER_ON, &priv->state); return ret; } static int icnss_hw_power_off(struct icnss_priv *priv) { int ret = 0; - unsigned long flags; if (test_bit(HW_ALWAYS_ON, &quirks)) return 0; icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state); - spin_lock_irqsave(&priv->on_off_lock, flags); + spin_lock(&priv->on_off_lock); if (!test_bit(ICNSS_POWER_ON, &priv->state)) { - spin_unlock_irqrestore(&priv->on_off_lock, flags); + spin_unlock(&priv->on_off_lock); return ret; } clear_bit(ICNSS_POWER_ON, &priv->state); - spin_unlock_irqrestore(&priv->on_off_lock, flags); + spin_unlock(&priv->on_off_lock); + + icnss_clk_deinit(priv); + + ret = icnss_vreg_off(priv); return ret; } @@ -1895,6 +2109,8 @@ static int icnss_call_driver_remove(struct icnss_priv *priv) clear_bit(ICNSS_DRIVER_PROBED, &priv->state); + icnss_hw_power_off(penv); + return 0; } @@ -1947,8 +2163,6 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, icnss_call_driver_remove(priv); out: - ret = icnss_hw_power_off(priv); - kfree(data); return ret; @@ -2922,12 +3136,24 @@ 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", + priv->state); + ret = -EOPNOTSUPP; + goto out; + } + if (!priv->service_notifier[0].handle) { - icnss_pr_err("Invalid handle during recovery\n"); + 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); + priv->stats.trigger_recovery++; + /* * Initiate PDR, required only for the first instance */ @@ -3005,6 +3231,114 @@ static void icnss_smmu_deinit(struct icnss_priv *priv) priv->smmu_mapping = NULL; } +static int icnss_get_vreg_info(struct device *dev, + struct icnss_vreg_info *vreg_info) +{ + int ret = 0; + char prop_name[MAX_PROP_SIZE]; + struct regulator *reg; + const __be32 *prop; + int len = 0; + int i; + + reg = devm_regulator_get_optional(dev, vreg_info->name); + if (PTR_ERR(reg) == -EPROBE_DEFER) { + icnss_pr_err("EPROBE_DEFER for regulator: %s\n", + vreg_info->name); + ret = PTR_ERR(reg); + goto out; + } + + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + + if (vreg_info->required) { + icnss_pr_err("Regulator %s doesn't exist: %d\n", + vreg_info->name, ret); + goto out; + } else { + icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n", + vreg_info->name, ret); + goto done; + } + } + + vreg_info->reg = reg; + + snprintf(prop_name, MAX_PROP_SIZE, + "qcom,%s-config", vreg_info->name); + + prop = of_get_property(dev->of_node, prop_name, &len); + + icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n", + prop_name, len); + + if (!prop || len < (2 * sizeof(__be32))) { + icnss_pr_dbg("Property %s %s\n", prop_name, + prop ? "invalid format" : "doesn't exist"); + goto done; + } + + for (i = 0; (i * sizeof(__be32)) < len; i++) { + switch (i) { + case 0: + vreg_info->min_v = be32_to_cpup(&prop[0]); + break; + case 1: + vreg_info->max_v = be32_to_cpup(&prop[1]); + break; + case 2: + vreg_info->load_ua = be32_to_cpup(&prop[2]); + break; + case 3: + vreg_info->settle_delay = be32_to_cpup(&prop[3]); + break; + default: + icnss_pr_dbg("Property %s, ignoring value at %d\n", + prop_name, i); + break; + } + } + +done: + icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n", + vreg_info->name, vreg_info->min_v, vreg_info->max_v, + vreg_info->load_ua, vreg_info->settle_delay); + + return 0; + +out: + return ret; +} + +static int icnss_get_clk_info(struct device *dev, + struct icnss_clk_info *clk_info) +{ + struct clk *handle; + int ret = 0; + + handle = devm_clk_get(dev, clk_info->name); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + if (clk_info->required) { + icnss_pr_err("Clock %s isn't available: %d\n", + clk_info->name, ret); + goto out; + } else { + icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name, + ret); + ret = 0; + goto out; + } + } + + icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq); + + clk_info->handle = handle; +out: + return ret; +} + static int icnss_fw_debug_show(struct seq_file *s, void *data) { struct icnss_priv *priv = s->private; @@ -3370,6 +3704,7 @@ static int icnss_stats_show(struct seq_file *s, void *data) ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req); ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp); ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err); + ICNSS_STATS_DUMP(s, priv, trigger_recovery); seq_puts(s, "\n<------------------ PM stats ------------------->\n"); ICNSS_STATS_DUMP(s, priv, pm_suspend); @@ -3715,6 +4050,21 @@ static int icnss_probe(struct platform_device *pdev) if (ret == -EPROBE_DEFER) goto out; + memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info)); + for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) { + ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]); + + if (ret) + goto out; + } + + memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info)); + for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) { + ret = icnss_get_clk_info(dev, &priv->clk_info[i]); + if (ret) + goto out; + } + if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass")) priv->bypass_s1_smmu = true; diff --git a/drivers/soc/qcom/qdsp6v2/adsp-loader.c b/drivers/soc/qcom/qdsp6v2/adsp-loader.c index 51539a36a74f..b45eac88ed12 100644 --- a/drivers/soc/qcom/qdsp6v2/adsp-loader.c +++ b/drivers/soc/qcom/qdsp6v2/adsp-loader.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-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 @@ -20,6 +20,8 @@ #include <linux/qdsp6v2/apr.h> #include <linux/of_device.h> #include <linux/sysfs.h> +#include <linux/workqueue.h> + #include <soc/qcom/subsystem_restart.h> #define Q6_PIL_GET_DELAY_MS 100 @@ -44,12 +46,13 @@ static struct attribute *attrs[] = { NULL, }; +static struct work_struct adsp_ldr_work; static struct platform_device *adsp_private; static void adsp_loader_unload(struct platform_device *pdev); -static void adsp_loader_do(struct platform_device *pdev) +static void adsp_load_fw(struct work_struct *adsp_ldr_work) { - + struct platform_device *pdev = adsp_private; struct adsp_loader_private *priv = NULL; const char *adsp_dt = "qcom,adsp-state"; @@ -146,6 +149,11 @@ fail: return; } +static void adsp_loader_do(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s: scheduling work to load ADSP fw\n", __func__); + schedule_work(&adsp_ldr_work); +} static ssize_t adsp_boot_store(struct kobject *kobj, struct kobj_attribute *attr, @@ -270,6 +278,8 @@ static int adsp_loader_probe(struct platform_device *pdev) return ret; } + INIT_WORK(&adsp_ldr_work, adsp_load_fw); + return 0; } diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c index 19974b61ec1c..d11ffdde23be 100644 --- a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c +++ b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c @@ -218,6 +218,7 @@ static void apr_tal_notify_remote_rx_intent(void *handle, const void *priv, */ pr_debug("%s: remote queued an intent\n", __func__); apr_ch->if_remote_intent_ready = true; + wake_up(&apr_ch->wait); } void apr_tal_notify_state(void *handle, const void *priv, unsigned event) diff --git a/drivers/soc/qcom/qdsp6v2/cdsp-loader.c b/drivers/soc/qcom/qdsp6v2/cdsp-loader.c index 0b801c5cd7dd..cae26e3913d6 100644 --- a/drivers/soc/qcom/qdsp6v2/cdsp-loader.c +++ b/drivers/soc/qcom/qdsp6v2/cdsp-loader.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014,2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-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,6 +19,8 @@ #include <linux/platform_device.h> #include <linux/of_device.h> #include <linux/sysfs.h> +#include <linux/workqueue.h> + #include <soc/qcom/subsystem_restart.h> #define BOOT_CMD 1 @@ -47,11 +49,12 @@ static struct attribute *attrs[] = { static u32 cdsp_state = CDSP_SUBSYS_DOWN; static struct platform_device *cdsp_private; +static struct work_struct cdsp_ldr_work; static void cdsp_loader_unload(struct platform_device *pdev); -static int cdsp_loader_do(struct platform_device *pdev) +static void cdsp_load_fw(struct work_struct *cdsp_ldr_work) { - + struct platform_device *pdev = cdsp_private; struct cdsp_loader_private *priv = NULL; int rc = 0; @@ -101,14 +104,19 @@ static int cdsp_loader_do(struct platform_device *pdev) } dev_dbg(&pdev->dev, "%s: CDSP image is loaded\n", __func__); - return rc; + return; } fail: dev_err(&pdev->dev, "%s: CDSP image loading failed\n", __func__); - return rc; + return; } +static void cdsp_loader_do(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s: scheduling work to load CDSP fw\n", __func__); + schedule_work(&cdsp_ldr_work); +} static ssize_t cdsp_boot_store(struct kobject *kobj, struct kobj_attribute *attr, @@ -126,7 +134,7 @@ static ssize_t cdsp_boot_store(struct kobject *kobj, pr_debug("%s: going to call cdsp_loader_do\n", __func__); cdsp_loader_do(cdsp_private); } else if (boot == IMAGE_UNLOAD_CMD) { - pr_debug("%s: going to call adsp_unloader\n", __func__); + pr_debug("%s: going to call cdsp_unloader\n", __func__); cdsp_loader_unload(cdsp_private); } return count; @@ -238,6 +246,8 @@ static int cdsp_loader_probe(struct platform_device *pdev) return ret; } + INIT_WORK(&cdsp_ldr_work, cdsp_load_fw); + return 0; } diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c index 39070561d7e4..cf0b7ff25201 100644 --- a/drivers/soc/qcom/qpnp-haptic.c +++ b/drivers/soc/qcom/qpnp-haptic.c @@ -141,9 +141,7 @@ #define QPNP_HAP_CYCLS 5 #define QPNP_TEST_TIMER_MS 5 -#define AUTO_RES_ENABLE_TIMEOUT 20000 -#define AUTO_RES_ERR_CAPTURE_RES 5 -#define AUTO_RES_ERR_MAX 15 +#define QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN 20000 #define MISC_TRIM_ERROR_RC19P2_CLK 0x09F5 #define MISC_SEC_ACCESS 0x09D0 @@ -152,8 +150,22 @@ #define POLL_TIME_AUTO_RES_ERR_NS (5 * NSEC_PER_MSEC) -#define LRA_POS_FREQ_COUNT 6 -int lra_play_rate_code[LRA_POS_FREQ_COUNT]; +#define MAX_POSITIVE_VARIATION_LRA_FREQ 30 +#define MAX_NEGATIVE_VARIATION_LRA_FREQ -30 +#define FREQ_VARIATION_STEP 5 +#define AUTO_RES_ERROR_CAPTURE_RES 5 +#define AUTO_RES_ERROR_MAX 30 +#define ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE \ + ((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) +#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) + +u32 adjusted_lra_play_rate_code[ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE]; /* haptic debug register set */ static u8 qpnp_hap_dbg_regs[] = { @@ -246,10 +258,21 @@ struct qpnp_pwm_info { * @ pwm_info - pwm info * @ lock - mutex lock * @ wf_lock - mutex lock for waveform + * @ init_drive_period_code - the initial lra drive period code + * @ drive_period_code_max_limit_percent_variation - maximum limit of + percentage variation of drive period code + * @ drive_period_code_min_limit_percent_variation - minimum limit og + percentage variation of drive period code + * @ drive_period_code_max_limit - calculated drive period code with + percentage variation on the higher side. + * @ drive_period_code_min_limit - calculated drive period code with + percentage variation on the lower side * @ play_mode - play mode * @ auto_res_mode - auto resonace mode * @ lra_high_z - high z option line * @ timeout_ms - max timeout in ms + * @ time_required_to_generate_back_emf_us - the time required for sufficient + back-emf to be generated for auto resonance to be successful * @ vmax_mv - max voltage in mv * @ ilim_ma - limiting current in ma * @ sc_deb_cycles - short circuit debounce cycles @@ -280,6 +303,8 @@ struct qpnp_pwm_info { * @ 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 + * @ perform_lra_auto_resonance_search - whether lra auto resonance search + * algorithm should be performed or not. */ struct qpnp_hap { struct platform_device *pdev; @@ -300,7 +325,9 @@ struct qpnp_hap { enum qpnp_hap_mode play_mode; enum qpnp_hap_auto_res_mode auto_res_mode; enum qpnp_hap_high_z lra_high_z; + u32 init_drive_period_code; u32 timeout_ms; + u32 time_required_to_generate_back_emf_us; u32 vmax_mv; u32 ilim_ma; u32 sc_deb_cycles; @@ -312,16 +339,21 @@ struct qpnp_hap { u32 play_irq; u32 sc_irq; u16 base; + u16 drive_period_code_max_limit; + u16 drive_period_code_min_limit; + u8 drive_period_code_max_limit_percent_variation; + u8 drive_period_code_min_limit_percent_variation; u8 act_type; u8 wave_shape; - u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN]; - u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN]; - u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN]; + u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN]; + u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN]; + u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN]; u8 reg_en_ctl; u8 reg_play; u8 lra_res_cal_period; u8 sc_duration; u8 ext_pwm_dtest_line; + bool vcc_pon_enabled; bool state; bool use_play_irq; bool use_sc_irq; @@ -333,6 +365,7 @@ struct qpnp_hap { 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; @@ -1314,29 +1347,62 @@ static struct device_attribute qpnp_hap_attrs[] = { qpnp_hap_min_max_test_data_store), }; -static void calculate_lra_code(struct qpnp_hap *hap) +static int calculate_lra_code(struct qpnp_hap *hap) { - u8 play_rate_code_lo, play_rate_code_hi; - int play_rate_code, neg_idx = 0, pos_idx = LRA_POS_FREQ_COUNT-1; - int lra_init_freq, freq_variation, start_variation = AUTO_RES_ERR_MAX; + u8 lra_drive_period_code_lo = 0, lra_drive_period_code_hi = 0; + u32 lra_drive_period_code, lra_drive_frequency_hz, freq_variation; + u8 start_variation = AUTO_RES_ERROR_MAX, i; + u8 neg_idx = 0, pos_idx = ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE - 1; + int rc = 0; - qpnp_hap_read_reg(hap, &play_rate_code_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); - qpnp_hap_read_reg(hap, &play_rate_code_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_lo, + QPNP_HAP_RATE_CFG1_REG(hap->base)); + if (rc) { + dev_err(&hap->pdev->dev, + "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)); + if (rc) { + dev_err(&hap->pdev->dev, + "Error while reading RATE_CFG2 register\n"); + return rc; + } - play_rate_code = (play_rate_code_hi << 8) | (play_rate_code_lo & 0xff); + 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"); + return -EINVAL; + } - lra_init_freq = 200000 / play_rate_code; + lra_drive_period_code = + (lra_drive_period_code_hi << 8) | (lra_drive_period_code_lo & 0xff); + lra_drive_frequency_hz = 200000 / lra_drive_period_code; - while (start_variation >= AUTO_RES_ERR_CAPTURE_RES) { - freq_variation = (lra_init_freq * start_variation) / 100; - lra_play_rate_code[neg_idx++] = 200000 / (lra_init_freq - - freq_variation); - lra_play_rate_code[pos_idx--] = 200000 / (lra_init_freq + - freq_variation); - start_variation -= AUTO_RES_ERR_CAPTURE_RES; + while (start_variation >= AUTO_RES_ERROR_CAPTURE_RES) { + freq_variation = + (lra_drive_frequency_hz * start_variation) / 100; + adjusted_lra_play_rate_code[neg_idx++] = + 200000 / (lra_drive_frequency_hz - freq_variation); + adjusted_lra_play_rate_code[pos_idx--] = + 200000 / (lra_drive_frequency_hz + freq_variation); + 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" + "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]); + + return 0; } static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) @@ -1369,20 +1435,37 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) static void update_lra_frequency(struct qpnp_hap *hap) { u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0; + u32 play_rate_code; 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)); - if (lra_auto_res_lo && lra_auto_res_hi) { - qpnp_hap_write_reg(hap, &lra_auto_res_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); + play_rate_code = + (lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF); - 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)); - } + dev_dbg(&hap->pdev->dev, + "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); + + /* + * If the drive period code read from AUTO RES_LO and AUTO_RES_HI + * registers is more than the max limit percent variation read from + * DT or less than the min limit percent variation read from DT, then + * RATE_CFG registers are not uptdated. + */ + + if ((play_rate_code <= hap->drive_period_code_min_limit) || + (play_rate_code >= hap->drive_period_code_max_limit)) + return; + + qpnp_hap_write_reg(hap, &lra_auto_res_lo, + QPNP_HAP_RATE_CFG1_REG(hap->base)); + + 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)); } static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) @@ -1412,36 +1495,38 @@ static void correct_auto_res_error(struct work_struct *auto_res_err_work) struct qpnp_hap, auto_res_err_work); u8 lra_code_lo, lra_code_hi, disable_hap = 0x00; - static int lra_freq_index; - ktime_t currtime, remaining_time; - int temp, rem = 0, index = lra_freq_index % LRA_POS_FREQ_COUNT; + static u8 lra_freq_index; + ktime_t currtime = ktime_set(0, 0), remaining_time = ktime_set(0, 0); - if (hrtimer_active(&hap->hap_timer)) { + if (hrtimer_active(&hap->hap_timer)) remaining_time = hrtimer_get_remaining(&hap->hap_timer); - rem = (int)ktime_to_us(remaining_time); - } qpnp_hap_play(hap, 0); qpnp_hap_write_reg(hap, &disable_hap, QPNP_HAP_EN_CTL_REG(hap->base)); - lra_code_lo = lra_play_rate_code[index] & QPNP_HAP_RATE_CFG1_MASK; - qpnp_hap_write_reg(hap, &lra_code_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); + if (hap->perform_lra_auto_resonance_search) { + lra_code_lo = + adjusted_lra_play_rate_code[lra_freq_index] + & QPNP_HAP_RATE_CFG1_MASK; - qpnp_hap_read_reg(hap, &lra_code_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + qpnp_hap_write_reg(hap, &lra_code_lo, + QPNP_HAP_RATE_CFG1_REG(hap->base)); - lra_code_hi &= QPNP_HAP_RATE_CFG2_MASK; - temp = lra_play_rate_code[index] >> QPNP_HAP_RATE_CFG2_SHFT; - lra_code_hi |= temp; + lra_code_hi = adjusted_lra_play_rate_code[lra_freq_index] + >> QPNP_HAP_RATE_CFG2_SHFT; - qpnp_hap_write_reg(hap, &lra_code_hi, + qpnp_hap_write_reg(hap, &lra_code_hi, QPNP_HAP_RATE_CFG2_REG(hap->base)); - lra_freq_index++; + lra_freq_index = (lra_freq_index+1) % + ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE; + } - if (rem > 0) { + dev_dbg(&hap->pdev->dev, "Remaining time is %lld\n", + ktime_to_us(remaining_time)); + + if ((ktime_to_us(remaining_time)) > 0) { currtime = ktime_get(); hap->state = 1; hrtimer_forward(&hap->hap_timer, currtime, remaining_time); @@ -1455,6 +1540,7 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) int rc = 0; u8 val = 0; unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS; + u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us; if (hap->play_mode == QPNP_HAP_PWM) { if (on) @@ -1464,8 +1550,21 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) } else if (hap->play_mode == QPNP_HAP_BUFFER || hap->play_mode == QPNP_HAP_DIRECT) { if (on) { - if (hap->correct_lra_drive_freq || - hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) + /* + * For auto resonance detection to work properly, + * sufficient back-emf has to be generated. In general, + * back-emf takes some time to build up. When the auto + * resonance mode is chosen as QWD, high-z will be + * applied for every LRA cycle and hence there won't be + * enough back-emf at the start-up. Hence, the motor + * needs to vibrate for few LRA cycles after the PLAY + * bit is asserted. So disable the auto resonance here + * and enable it after the sleep of + * 'time_required_to_generate_back_emf_us' is completed. + */ + if ((hap->act_type == QPNP_HAP_LRA) && + (hap->correct_lra_drive_freq || + hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) qpnp_hap_auto_res_enable(hap, 0); rc = qpnp_hap_mod_enable(hap, on); @@ -1474,17 +1573,18 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) rc = qpnp_hap_play(hap, on); - if ((hap->act_type == QPNP_HAP_LRA && - hap->correct_lra_drive_freq) || - hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) { - usleep_range(AUTO_RES_ENABLE_TIMEOUT, - (AUTO_RES_ENABLE_TIMEOUT + 1)); + if ((hap->act_type == QPNP_HAP_LRA) && + (hap->correct_lra_drive_freq || + hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) { + usleep_range(back_emf_delay_us, + (back_emf_delay_us + 1)); rc = qpnp_hap_auto_res_enable(hap, 1); if (rc < 0) return rc; } - if (hap->correct_lra_drive_freq) { + if (hap->act_type == QPNP_HAP_LRA && + hap->correct_lra_drive_freq) { /* * Start timer to poll Auto Resonance error bit */ @@ -1500,7 +1600,8 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) if (rc < 0) return rc; - if (hap->correct_lra_drive_freq) { + if (hap->act_type == QPNP_HAP_LRA && + hap->correct_lra_drive_freq) { rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); if (!(val & AUTO_RES_ERR_BIT)) @@ -1511,7 +1612,6 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) { hrtimer_cancel(&hap->auto_res_err_poll_timer); - calculate_lra_code(hap); } } } @@ -1619,13 +1719,15 @@ static void qpnp_hap_worker(struct work_struct *work) struct qpnp_hap *hap = container_of(work, struct qpnp_hap, work); u8 val = 0x00; - int rc, reg_en = 0; + int rc; - if (hap->vcc_pon) { - reg_en = regulator_enable(hap->vcc_pon); - if (reg_en) - pr_err("%s: could not enable vcc_pon regulator\n", - __func__); + 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); + else + hap->vcc_pon_enabled = true; } /* Disable haptics module if the duration of short circuit @@ -1640,11 +1742,13 @@ static void qpnp_hap_worker(struct work_struct *work) qpnp_hap_set(hap, hap->state); } - if (hap->vcc_pon && !reg_en) { + 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\n", - __func__); + pr_err("%s: could not disable vcc_pon regulator rc=%d\n", + __func__, rc); + else + hap->vcc_pon_enabled = false; } } @@ -1706,10 +1810,16 @@ 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, error_value; - int rc, i, temp; + u8 reg = 0, unlock_val; + 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) @@ -1838,16 +1948,22 @@ static int qpnp_hap_config(struct qpnp_hap *hap) else if (hap->wave_play_rate_us > QPNP_HAP_WAV_PLAY_RATE_US_MAX) hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MAX; - temp = hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US; + hap->init_drive_period_code = + hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US; /* - * The frequency of 19.2Mzhz RC clock is subject to variation. - * In PMI8950, TRIM_ERROR_RC19P2_CLK register in MISC module - * holds the frequency error in 19.2Mhz RC clock + * 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. */ 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, @@ -1855,36 +1971,69 @@ static int qpnp_hap_config(struct qpnp_hap *hap) regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK, &error_code); + dev_dbg(&hap->pdev->dev, "TRIM register = 0x%x\n", error_code); - error_value = (error_code & 0x0F) * 7; + /* + * 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; - if (error_code & 0x80) - temp = (temp * (1000 - error_value)) / 1000; + /* + * 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. + * + * The adjusted play rate code is calculated as follows: + * LRA drive period code (RATE_CFG) = + * 200KHz * 1 / LRA drive frequency * ( 1 + %error/ 100) + * + * This can be rewritten as: + * LRA drive period code (RATE_CFG) = + * 200KHz * 1/LRA drive frequency *( 1 + %error * 10/1000) + * + * Since 200KHz * 1/LRA drive frequency is already calculated + * above we only do rest of the scaling here. + */ + if (error_code >= 128) + LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent_x10); else - temp = (temp * (1000 + error_value)) / 1000; + LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent_x10); } - reg = temp & QPNP_HAP_RATE_CFG1_MASK; + dev_dbg(&hap->pdev->dev, + "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)); if (rc) return rc; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_RATE_CFG2_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_RATE_CFG2_MASK; - temp = temp >> QPNP_HAP_RATE_CFG2_SHFT; - reg |= temp; + 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)); if (rc) return rc; - if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq) + if (hap->act_type == QPNP_HAP_LRA && + hap->perform_lra_auto_resonance_search) calculate_lra_code(hap); + if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) { + hap->drive_period_code_max_limit = + (hap->init_drive_period_code * 100) / + (100 - hap->drive_period_code_max_limit_percent_variation); + hap->drive_period_code_min_limit = + (hap->init_drive_period_code * 100) / + (100 + hap->drive_period_code_min_limit_percent_variation); + 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); + } + /* Configure BRAKE register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); if (rc < 0) @@ -2031,13 +2180,44 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) return rc; } + hap->perform_lra_auto_resonance_search = + of_property_read_bool(pdev->dev.of_node, + "qcom,perform-lra-auto-resonance-search"); + hap->correct_lra_drive_freq = of_property_read_bool(pdev->dev.of_node, "qcom,correct-lra-drive-freq"); + hap->drive_period_code_max_limit_percent_variation = 25; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,drive-period-code-max-limit-percent-variation", &temp); + if (!rc) + hap->drive_period_code_max_limit_percent_variation = + (u8) temp; + + hap->drive_period_code_min_limit_percent_variation = 25; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,drive-period-code-min-limit-percent-variation", &temp); + if (!rc) + 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; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,time-required-to-generate-back-emf-us", + &temp); + if (!rc) + hap->time_required_to_generate_back_emf_us = + temp; + } else { + hap->time_required_to_generate_back_emf_us = 0; + } } rc = of_property_read_string(pdev->dev.of_node, diff --git a/drivers/soc/qcom/qpnp-pbs.c b/drivers/soc/qcom/qpnp-pbs.c new file mode 100644 index 000000000000..287c8a25b391 --- /dev/null +++ b/drivers/soc/qcom/qpnp-pbs.c @@ -0,0 +1,361 @@ +/* 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) "PBS: %s: " fmt, __func__ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spmi.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/qpnp/qpnp-pbs.h> + +#define QPNP_PBS_DEV_NAME "qcom,qpnp-pbs" + +#define PBS_CLIENT_TRIG_CTL 0x42 +#define PBS_CLIENT_SW_TRIG_BIT BIT(7) +#define PBS_CLIENT_SCRATCH1 0x50 +#define PBS_CLIENT_SCRATCH2 0x51 + +static LIST_HEAD(pbs_dev_list); +static DEFINE_MUTEX(pbs_list_lock); + +struct qpnp_pbs { + struct platform_device *pdev; + struct device *dev; + struct device_node *dev_node; + struct regmap *regmap; + struct mutex pbs_lock; + struct list_head link; + + u32 base; +}; + +static int qpnp_pbs_read(struct qpnp_pbs *pbs, u32 address, + u8 *val, int count) +{ + int rc = 0; + struct platform_device *pdev = pbs->pdev; + + rc = regmap_bulk_read(pbs->regmap, address, val, count); + if (rc) + pr_err("Failed to read address=0x%02x sid=0x%02x rc=%d\n", + address, to_spmi_device(pdev->dev.parent)->usid, rc); + + return rc; +} + +static int qpnp_pbs_write(struct qpnp_pbs *pbs, u16 address, + u8 *val, int count) +{ + int rc = 0; + struct platform_device *pdev = pbs->pdev; + + rc = regmap_bulk_write(pbs->regmap, address, val, count); + if (rc < 0) + pr_err("Failed to write address =0x%02x sid=0x%02x rc=%d\n", + address, to_spmi_device(pdev->dev.parent)->usid, rc); + else + pr_debug("Wrote 0x%02X to addr 0x%04x\n", *val, address); + + return rc; +} + +static int qpnp_pbs_masked_write(struct qpnp_pbs *pbs, u16 address, + u8 mask, u8 val) +{ + int rc; + + rc = regmap_update_bits(pbs->regmap, address, mask, val); + if (rc < 0) + pr_err("Failed to write address 0x%04X, rc = %d\n", + address, rc); + else + pr_debug("Wrote 0x%02X to addr 0x%04X\n", + val, address); + + return rc; +} + +static struct qpnp_pbs *get_pbs_client_node(struct device_node *dev_node) +{ + struct qpnp_pbs *pbs; + + mutex_lock(&pbs_list_lock); + list_for_each_entry(pbs, &pbs_dev_list, link) { + if (dev_node == pbs->dev_node) { + mutex_unlock(&pbs_list_lock); + return pbs; + } + } + + mutex_unlock(&pbs_list_lock); + return ERR_PTR(-EINVAL); +} + +static int qpnp_pbs_wait_for_ack(struct qpnp_pbs *pbs, u8 bit_pos) +{ + int rc = 0; + u16 retries = 2000, dly = 1000; + u8 val; + + while (retries--) { + rc = qpnp_pbs_read(pbs, pbs->base + + PBS_CLIENT_SCRATCH2, &val, 1); + if (rc < 0) { + pr_err("Failed to read register %x rc = %d\n", + PBS_CLIENT_SCRATCH2, rc); + return rc; + } + + if (val == 0xFF) { + /* PBS error - clear SCRATCH2 register */ + rc = qpnp_pbs_write(pbs, pbs->base + + PBS_CLIENT_SCRATCH2, 0, 1); + if (rc < 0) { + pr_err("Failed to clear register %x rc=%d\n", + PBS_CLIENT_SCRATCH2, rc); + return rc; + } + + pr_err("NACK from PBS for bit %d\n", bit_pos); + return -EINVAL; + } + + if (val & BIT(bit_pos)) { + pr_debug("PBS sequence for bit %d executed!\n", + bit_pos); + break; + } + + usleep_range(dly, dly + 100); + } + + if (!retries) { + pr_err("Timeout for PBS ACK/NACK for bit %d\n", bit_pos); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * qpnp_pbs_trigger_event - Trigger the PBS RAM sequence + * + * Returns = 0 If the PBS RAM sequence executed successfully. + * + * Returns < 0 for errors. + * + * This function is used to trigger the PBS RAM sequence to be + * executed by the client driver. + * + * The PBS trigger sequence involves + * 1. setting the PBS sequence bit in PBS_CLIENT_SCRATCH1 + * 2. Initiating the SW PBS trigger + * 3. Checking the equivalent bit in PBS_CLIENT_SCRATCH2 for the + * completion of the sequence. + * 4. If PBS_CLIENT_SCRATCH2 == 0xFF, the PBS sequence failed to execute + */ +int qpnp_pbs_trigger_event(struct device_node *dev_node, u8 bitmap) +{ + struct qpnp_pbs *pbs; + int rc = 0; + u16 bit_pos = 0; + u8 val, mask = 0; + + if (!dev_node) + return -EINVAL; + + if (!bitmap) { + pr_err("Invalid bitmap passed by client\n"); + return -EINVAL; + } + + pbs = get_pbs_client_node(dev_node); + if (IS_ERR_OR_NULL(pbs)) { + pr_err("Unable to find the PBS dev_node\n"); + return -EINVAL; + } + + mutex_lock(&pbs->pbs_lock); + rc = qpnp_pbs_read(pbs, pbs->base + PBS_CLIENT_SCRATCH2, &val, 1); + if (rc < 0) { + pr_err("read register %x failed rc = %d\n", + PBS_CLIENT_SCRATCH2, rc); + goto out; + } + + if (val == 0xFF) { + /* PBS error - clear SCRATCH2 register */ + rc = qpnp_pbs_write(pbs, pbs->base + PBS_CLIENT_SCRATCH2, 0, 1); + if (rc < 0) { + pr_err("Failed to clear register %x rc=%d\n", + PBS_CLIENT_SCRATCH2, rc); + goto out; + } + } + + for (bit_pos = 0; bit_pos < 8; bit_pos++) { + if (bitmap & BIT(bit_pos)) { + /* + * Clear the PBS sequence bit position in + * PBS_CLIENT_SCRATCH2 mask register. + */ + rc = qpnp_pbs_masked_write(pbs, pbs->base + + PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0); + if (rc < 0) { + pr_err("Failed to clear %x reg bit rc=%d\n", + PBS_CLIENT_SCRATCH2, rc); + goto error; + } + + /* + * Set the PBS sequence bit position in + * PBS_CLIENT_SCRATCH1 register. + */ + val = mask = BIT(bit_pos); + rc = qpnp_pbs_masked_write(pbs, pbs->base + + PBS_CLIENT_SCRATCH1, mask, val); + if (rc < 0) { + pr_err("Failed to set %x reg bit rc=%d\n", + PBS_CLIENT_SCRATCH1, rc); + goto error; + } + + /* Initiate the SW trigger */ + val = mask = PBS_CLIENT_SW_TRIG_BIT; + rc = qpnp_pbs_masked_write(pbs, pbs->base + + PBS_CLIENT_TRIG_CTL, mask, val); + if (rc < 0) { + pr_err("Failed to write register %x rc=%d\n", + PBS_CLIENT_TRIG_CTL, rc); + goto error; + } + + rc = qpnp_pbs_wait_for_ack(pbs, bit_pos); + if (rc < 0) { + pr_err("Error during wait_for_ack\n"); + goto error; + } + + /* + * Clear the PBS sequence bit position in + * PBS_CLIENT_SCRATCH1 register. + */ + rc = qpnp_pbs_masked_write(pbs, pbs->base + + PBS_CLIENT_SCRATCH1, BIT(bit_pos), 0); + if (rc < 0) { + pr_err("Failed to clear %x reg bit rc=%d\n", + PBS_CLIENT_SCRATCH1, rc); + goto error; + } + + /* + * Clear the PBS sequence bit position in + * PBS_CLIENT_SCRATCH2 mask register. + */ + rc = qpnp_pbs_masked_write(pbs, pbs->base + + PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0); + if (rc < 0) { + pr_err("Failed to clear %x reg bit rc=%d\n", + PBS_CLIENT_SCRATCH2, rc); + goto error; + } + + } + } + +error: + /* Clear all the requested bitmap */ + rc = qpnp_pbs_masked_write(pbs, pbs->base + PBS_CLIENT_SCRATCH1, + bitmap, 0); + if (rc < 0) + pr_err("Failed to clear %x reg bit rc=%d\n", + PBS_CLIENT_SCRATCH1, rc); +out: + mutex_unlock(&pbs->pbs_lock); + + return rc; +} +EXPORT_SYMBOL(qpnp_pbs_trigger_event); + +static int qpnp_pbs_probe(struct platform_device *pdev) +{ + int rc = 0; + u32 val = 0; + struct qpnp_pbs *pbs; + + pbs = devm_kzalloc(&pdev->dev, sizeof(*pbs), GFP_KERNEL); + if (!pbs) + return -ENOMEM; + + pbs->pdev = pdev; + pbs->dev = &pdev->dev; + pbs->dev_node = pdev->dev.of_node; + pbs->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!pbs->regmap) { + dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); + return -EINVAL; + } + + rc = of_property_read_u32(pdev->dev.of_node, "reg", &val); + if (rc < 0) { + dev_err(&pdev->dev, + "Couldn't find reg in node = %s rc = %d\n", + pdev->dev.of_node->full_name, rc); + return rc; + } + + pbs->base = val; + mutex_init(&pbs->pbs_lock); + + dev_set_drvdata(&pdev->dev, pbs); + + mutex_lock(&pbs_list_lock); + list_add(&pbs->link, &pbs_dev_list); + mutex_unlock(&pbs_list_lock); + + return 0; +} + +static const struct of_device_id qpnp_pbs_match_table[] = { + { .compatible = QPNP_PBS_DEV_NAME }, + {} +}; + +static struct platform_driver qpnp_pbs_driver = { + .driver = { + .name = QPNP_PBS_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = qpnp_pbs_match_table, + }, + .probe = qpnp_pbs_probe, +}; + +static int __init qpnp_pbs_init(void) +{ + return platform_driver_register(&qpnp_pbs_driver); +} +arch_initcall(qpnp_pbs_init); + +static void __exit qpnp_pbs_exit(void) +{ + return platform_driver_unregister(&qpnp_pbs_driver); +} +module_exit(qpnp_pbs_exit); + +MODULE_DESCRIPTION("QPNP PBS DRIVER"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" QPNP_PBS_DEV_NAME); diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index 2b708732760f..8581ed587ead 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -1,5 +1,5 @@ /* - * 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 @@ -111,6 +111,7 @@ static void service_locator_svc_arrive(struct work_struct *work) qmi_handle_create(service_locator_clnt_notify, NULL); if (!service_locator.clnt_handle) { service_locator.clnt_handle = NULL; + complete_all(&service_locator.service_available); mutex_unlock(&service_locator.service_mutex); pr_err("Service locator QMI client handle alloc failed!\n"); return; @@ -123,6 +124,7 @@ static void service_locator_svc_arrive(struct work_struct *work) if (rc) { qmi_handle_destroy(service_locator.clnt_handle); service_locator.clnt_handle = NULL; + complete_all(&service_locator.service_available); mutex_unlock(&service_locator.service_mutex); pr_err("Unable to connnect to service rc:%d\n", rc); return; @@ -138,6 +140,7 @@ static void service_locator_svc_exit(struct work_struct *work) mutex_lock(&service_locator.service_mutex); qmi_handle_destroy(service_locator.clnt_handle); service_locator.clnt_handle = NULL; + complete_all(&service_locator.service_available); mutex_unlock(&service_locator.service_mutex); pr_info("Connection with service locator lost\n"); } diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c index 85ff81ff475c..c1c65cd25558 100644 --- a/drivers/soc/qcom/service-notifier.c +++ b/drivers/soc/qcom/service-notifier.c @@ -635,7 +635,13 @@ static int send_pd_restart_req(const char *service_path, return rc; } - /* Check the response */ + /* Check response if PDR is disabled */ + if (QMI_RESP_BIT_SHIFT(resp.resp.result) == QMI_ERR_DISABLED_V01) { + pr_err("PD restart is disabled 0x%x\n", + QMI_RESP_BIT_SHIFT(resp.resp.error)); + return -EOPNOTSUPP; + } + /* Check the response for other error case*/ if (QMI_RESP_BIT_SHIFT(resp.resp.result) != QMI_RESULT_SUCCESS_V01) { pr_err("QMI request for PD restart failed 0x%x\n", QMI_RESP_BIT_SHIFT(resp.resp.error)); diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index 6a60e3624420..07610877f140 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -886,10 +886,11 @@ static int spcom_rx(struct spcom_channel *ch, if (timeleft == 0) { pr_err("rx_done timeout [%d] msec expired.\n", timeout_msec); - goto exit_err; + mutex_unlock(&ch->lock); + return -ETIMEDOUT; } else if (ch->rx_abort) { - pr_err("rx aborted.\n"); - goto exit_err; + mutex_unlock(&ch->lock); + return -ERESTART; /* probably SSR */ } else if (ch->actual_rx_size) { pr_debug("actual_rx_size is [%d].\n", ch->actual_rx_size); } else { @@ -1976,7 +1977,8 @@ static int spcom_handle_read_req_resp(struct spcom_channel *ch, ret = spcom_rx(ch, rx_buf, rx_buf_size, timeout_msec); if (ret < 0) { pr_err("rx error %d.\n", ret); - goto exit_err; + kfree(rx_buf); + return ret; } else { size = ret; /* actual_rx_size */ } @@ -2269,8 +2271,14 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, if (buf == NULL) return -ENOMEM; - actual_size = spcom_handle_read(ch, buf, size); - if ((actual_size <= 0) || (actual_size > size)) { + ret = spcom_handle_read(ch, buf, size); + if (ret < 0) { + pr_err("read error [%d].\n", ret); + kfree(buf); + return ret; + } + actual_size = ret; + if ((actual_size == 0) || (actual_size > size)) { pr_err("invalid actual_size [%d].\n", actual_size); kfree(buf); return -EFAULT; diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index b04b05a0904e..5548a31e1a39 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -116,7 +116,7 @@ static const struct spm_reg_data spm_reg_8064_cpu = { static DEFINE_PER_CPU(struct spm_driver_data *, cpu_spm_drv); -typedef int (*idle_fn)(int); +typedef int (*idle_fn)(void); static DEFINE_PER_CPU(idle_fn*, qcom_idle_ops); static inline void spm_register_write(struct spm_driver_data *drv, @@ -179,10 +179,10 @@ static int qcom_pm_collapse(unsigned long int unused) return -1; } -static int qcom_cpu_spc(int cpu) +static int qcom_cpu_spc(void) { int ret; - struct spm_driver_data *drv = per_cpu(cpu_spm_drv, cpu); + struct spm_driver_data *drv = __this_cpu_read(cpu_spm_drv); spm_set_low_power_mode(drv, PM_SLEEP_MODE_SPC); ret = cpu_suspend(0, qcom_pm_collapse); @@ -197,9 +197,9 @@ static int qcom_cpu_spc(int cpu) return ret; } -static int qcom_idle_enter(int cpu, unsigned long index) +static int qcom_idle_enter(unsigned long index) { - return per_cpu(qcom_idle_ops, cpu)[index](cpu); + return __this_cpu_read(qcom_idle_ops)[index](); } static const struct of_device_id qcom_idle_state_match[] __initconst = { @@ -288,7 +288,7 @@ static struct spm_driver_data *spm_get_drv(struct platform_device *pdev, struct spm_driver_data *drv = NULL; struct device_node *cpu_node, *saw_node; int cpu; - bool found; + bool found = 0; for_each_possible_cpu(cpu) { cpu_node = of_cpu_device_node_get(cpu); diff --git a/drivers/soc/qcom/sysmon-qmi.c b/drivers/soc/qcom/sysmon-qmi.c index 7ef69b527ef8..1063b96d8d83 100644 --- a/drivers/soc/qcom/sysmon-qmi.c +++ b/drivers/soc/qcom/sysmon-qmi.c @@ -1,5 +1,5 @@ /* - * 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 @@ -153,10 +153,12 @@ static void sysmon_clnt_svc_arrive(struct work_struct *work) struct sysmon_qmi_data *data = container_of(work, struct sysmon_qmi_data, svc_arrive); + mutex_lock(&sysmon_lock); /* Create a Local client port for QMI communication */ data->clnt_handle = qmi_handle_create(sysmon_clnt_notify, work); if (!data->clnt_handle) { pr_err("QMI client handle alloc failed for %s\n", data->name); + mutex_unlock(&sysmon_lock); return; } @@ -167,6 +169,7 @@ static void sysmon_clnt_svc_arrive(struct work_struct *work) data->name); qmi_handle_destroy(data->clnt_handle); data->clnt_handle = NULL; + mutex_unlock(&sysmon_lock); return; } pr_info("Connection established between QMI handle and %s's SSCTL service\n" @@ -177,6 +180,7 @@ static void sysmon_clnt_svc_arrive(struct work_struct *work) if (rc < 0) pr_warn("%s: Could not register the indication callback\n", data->name); + mutex_unlock(&sysmon_lock); } static void sysmon_clnt_svc_exit(struct work_struct *work) @@ -184,8 +188,10 @@ static void sysmon_clnt_svc_exit(struct work_struct *work) struct sysmon_qmi_data *data = container_of(work, struct sysmon_qmi_data, svc_exit); + mutex_lock(&sysmon_lock); qmi_handle_destroy(data->clnt_handle); data->clnt_handle = NULL; + mutex_unlock(&sysmon_lock); } static void sysmon_clnt_recv_msg(struct work_struct *work) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 39412c9097c6..a3965cac1b34 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -753,7 +753,6 @@ static int dspi_remove(struct platform_device *pdev) /* Disconnect from the SPI framework */ clk_disable_unprepare(dspi->clk); spi_unregister_master(dspi->master); - spi_master_put(dspi->master); return 0; } diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index a7934ab00b96..d22de4c8c399 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -263,6 +263,9 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, for (k = 0; k < ARRAY_SIZE(sh_msiof_spi_div_table); k++) { brps = DIV_ROUND_UP(div, sh_msiof_spi_div_table[k].div); + /* SCR_BRDV_DIV_1 is valid only if BRPS is x 1/1 or x 1/2 */ + if (sh_msiof_spi_div_table[k].div == 1 && brps > 2) + continue; if (brps <= 32) /* max of brdv is 32 */ break; } diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c b/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c index 99c6584fcfa5..97246bcbcd62 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c @@ -197,6 +197,6 @@ void fiq_debugger_dump_stacktrace(struct fiq_debugger_output *output, frame.sp = regs->sp; frame.pc = regs->pc; output->printf(output, "\n"); - walk_stackframe(&frame, report_trace, &sts); + walk_stackframe(current, &frame, report_trace, &sts); } } diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c index b1e45161eefc..18c2b6daf588 100644 --- a/drivers/staging/fbtft/fbtft-core.c +++ b/drivers/staging/fbtft/fbtft-core.c @@ -392,11 +392,11 @@ static void fbtft_update_display(struct fbtft_par *par, unsigned start_line, if (unlikely(timeit)) { ts_end = ktime_get(); - if (ktime_to_ns(par->update_time)) + if (!ktime_to_ns(par->update_time)) par->update_time = ts_start; - par->update_time = ts_start; fps = ktime_us_delta(ts_start, par->update_time); + par->update_time = ts_start; fps = fps ? 1000000 / fps : 0; throughput = ktime_us_delta(ts_end, ts_start); diff --git a/drivers/staging/goldfish/Kconfig b/drivers/staging/goldfish/Kconfig index 4e094602437c..c579141a7bed 100644 --- a/drivers/staging/goldfish/Kconfig +++ b/drivers/staging/goldfish/Kconfig @@ -4,6 +4,12 @@ config GOLDFISH_AUDIO ---help--- Emulated audio channel for the Goldfish Android Virtual Device +config GOLDFISH_SYNC + tristate "Goldfish AVD Sync Driver" + depends on GOLDFISH + ---help--- + Emulated sync fences for the Goldfish Android Virtual Device + config MTD_GOLDFISH_NAND tristate "Goldfish NAND device" depends on GOLDFISH diff --git a/drivers/staging/goldfish/Makefile b/drivers/staging/goldfish/Makefile index dec34ad58162..0cf525588210 100644 --- a/drivers/staging/goldfish/Makefile +++ b/drivers/staging/goldfish/Makefile @@ -4,3 +4,8 @@ obj-$(CONFIG_GOLDFISH_AUDIO) += goldfish_audio.o obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o + +# and sync + +ccflags-y := -Idrivers/staging/android +obj-$(CONFIG_GOLDFISH_SYNC) += goldfish_sync.o diff --git a/drivers/staging/goldfish/goldfish_audio.c b/drivers/staging/goldfish/goldfish_audio.c index b0927e49d0a8..63b79c09b41b 100644 --- a/drivers/staging/goldfish/goldfish_audio.c +++ b/drivers/staging/goldfish/goldfish_audio.c @@ -26,7 +26,9 @@ #include <linux/sched.h> #include <linux/dma-mapping.h> #include <linux/uaccess.h> +#include <linux/slab.h> #include <linux/goldfish.h> +#include <linux/acpi.h> MODULE_AUTHOR("Google, Inc."); MODULE_DESCRIPTION("Android QEMU Audio Driver"); @@ -115,6 +117,7 @@ static ssize_t goldfish_audio_read(struct file *fp, char __user *buf, size_t count, loff_t *pos) { struct goldfish_audio *data = fp->private_data; + unsigned long irq_flags; int length; int result = 0; @@ -128,6 +131,10 @@ static ssize_t goldfish_audio_read(struct file *fp, char __user *buf, wait_event_interruptible(data->wait, data->buffer_status & AUDIO_INT_READ_BUFFER_FULL); + spin_lock_irqsave(&data->lock, irq_flags); + data->buffer_status &= ~AUDIO_INT_READ_BUFFER_FULL; + spin_unlock_irqrestore(&data->lock, irq_flags); + length = AUDIO_READ(data, AUDIO_READ_BUFFER_AVAILABLE); /* copy data to user space */ @@ -344,11 +351,25 @@ static int goldfish_audio_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_audio_of_match[] = { + { .compatible = "google,goldfish-audio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_audio_of_match); + +static const struct acpi_device_id goldfish_audio_acpi_match[] = { + { "GFSH0005", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goldfish_audio_acpi_match); + static struct platform_driver goldfish_audio_driver = { .probe = goldfish_audio_probe, .remove = goldfish_audio_remove, .driver = { - .name = "goldfish_audio" + .name = "goldfish_audio", + .of_match_table = goldfish_audio_of_match, + .acpi_match_table = ACPI_PTR(goldfish_audio_acpi_match), } }; diff --git a/drivers/staging/goldfish/goldfish_sync.c b/drivers/staging/goldfish/goldfish_sync.c new file mode 100644 index 000000000000..ba8def29901e --- /dev/null +++ b/drivers/staging/goldfish/goldfish_sync.c @@ -0,0 +1,987 @@ +/* + * Copyright (C) 2016 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/fdtable.h> +#include <linux/file.h> +#include <linux/init.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> + +#include <linux/interrupt.h> +#include <linux/kref.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/acpi.h> + +#include <linux/string.h> +#include <linux/syscalls.h> + +#include "sw_sync.h" +#include "sync.h" + +#define ERR(...) printk(KERN_ERR __VA_ARGS__); + +#define INFO(...) printk(KERN_INFO __VA_ARGS__); + +#define DPRINT(...) pr_debug(__VA_ARGS__); + +#define DTRACE() DPRINT("%s: enter", __func__) + +/* The Goldfish sync driver is designed to provide a interface + * between the underlying host's sync device and the kernel's + * sw_sync. + * The purpose of the device/driver is to enable lightweight + * creation and signaling of timelines and fences + * in order to synchronize the guest with host-side graphics events. + * + * Each time the interrupt trips, the driver + * may perform a sw_sync operation. + */ + +/* The operations are: */ + +/* Ready signal - used to mark when irq should lower */ +#define CMD_SYNC_READY 0 + +/* Create a new timeline. writes timeline handle */ +#define CMD_CREATE_SYNC_TIMELINE 1 + +/* Create a fence object. reads timeline handle and time argument. + * Writes fence fd to the SYNC_REG_HANDLE register. */ +#define CMD_CREATE_SYNC_FENCE 2 + +/* Increments timeline. reads timeline handle and time argument */ +#define CMD_SYNC_TIMELINE_INC 3 + +/* Destroys a timeline. reads timeline handle */ +#define CMD_DESTROY_SYNC_TIMELINE 4 + +/* Starts a wait on the host with + * the given glsync object and sync thread handle. */ +#define CMD_TRIGGER_HOST_WAIT 5 + +/* The register layout is: */ + +#define SYNC_REG_BATCH_COMMAND 0x00 /* host->guest batch commands */ +#define SYNC_REG_BATCH_GUESTCOMMAND 0x04 /* guest->host batch commands */ +#define SYNC_REG_BATCH_COMMAND_ADDR 0x08 /* communicate physical address of host->guest batch commands */ +#define SYNC_REG_BATCH_COMMAND_ADDR_HIGH 0x0c /* 64-bit part */ +#define SYNC_REG_BATCH_GUESTCOMMAND_ADDR 0x10 /* communicate physical address of guest->host commands */ +#define SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH 0x14 /* 64-bit part */ +#define SYNC_REG_INIT 0x18 /* signals that the device has been probed */ + +/* There is an ioctl associated with goldfish sync driver. + * Make it conflict with ioctls that are not likely to be used + * in the emulator. + * + * '@' 00-0F linux/radeonfb.h conflict! + * '@' 00-0F drivers/video/aty/aty128fb.c conflict! + */ +#define GOLDFISH_SYNC_IOC_MAGIC '@' + +#define GOLDFISH_SYNC_IOC_QUEUE_WORK _IOWR(GOLDFISH_SYNC_IOC_MAGIC, 0, struct goldfish_sync_ioctl_info) + +/* The above definitions (command codes, register layout, ioctl definitions) + * need to be in sync with the following files: + * + * Host-side (emulator): + * external/qemu/android/emulation/goldfish_sync.h + * external/qemu-android/hw/misc/goldfish_sync.c + * + * Guest-side (system image): + * device/generic/goldfish-opengl/system/egl/goldfish_sync.h + * device/generic/goldfish/ueventd.ranchu.rc + * platform/build/target/board/generic/sepolicy/file_contexts + */ +struct goldfish_sync_hostcmd { + /* sorted for alignment */ + uint64_t handle; + uint64_t hostcmd_handle; + uint32_t cmd; + uint32_t time_arg; +}; + +struct goldfish_sync_guestcmd { + uint64_t host_command; /* uint64_t for alignment */ + uint64_t glsync_handle; + uint64_t thread_handle; + uint64_t guest_timeline_handle; +}; + +#define GOLDFISH_SYNC_MAX_CMDS 64 + +struct goldfish_sync_state { + char __iomem *reg_base; + int irq; + + /* Spinlock protects |to_do| / |to_do_end|. */ + spinlock_t lock; + /* |mutex_lock| protects all concurrent access + * to timelines for both kernel and user space. */ + struct mutex mutex_lock; + + /* Buffer holding commands issued from host. */ + struct goldfish_sync_hostcmd to_do[GOLDFISH_SYNC_MAX_CMDS]; + uint32_t to_do_end; + + /* Addresses for the reading or writing + * of individual commands. The host can directly write + * to |batch_hostcmd| (and then this driver immediately + * copies contents to |to_do|). This driver either replies + * through |batch_hostcmd| or simply issues a + * guest->host command through |batch_guestcmd|. + */ + struct goldfish_sync_hostcmd *batch_hostcmd; + struct goldfish_sync_guestcmd *batch_guestcmd; + + /* Used to give this struct itself to a work queue + * function for executing actual sync commands. */ + struct work_struct work_item; +}; + +static struct goldfish_sync_state global_sync_state[1]; + +struct goldfish_sync_timeline_obj { + struct sw_sync_timeline *sw_sync_tl; + uint32_t current_time; + /* We need to be careful about when we deallocate + * this |goldfish_sync_timeline_obj| struct. + * In order to ensure proper cleanup, we need to + * consider the triggered host-side wait that may + * still be in flight when the guest close()'s a + * goldfish_sync device's sync context fd (and + * destroys the |sw_sync_tl| field above). + * The host-side wait may raise IRQ + * and tell the kernel to increment the timeline _after_ + * the |sw_sync_tl| has already been set to null. + * + * From observations on OpenGL apps and CTS tests, this + * happens at some very low probability upon context + * destruction or process close, but it does happen + * and it needs to be handled properly. Otherwise, + * if we clean up the surrounding |goldfish_sync_timeline_obj| + * too early, any |handle| field of any host->guest command + * might not even point to a null |sw_sync_tl| field, + * but to garbage memory or even a reclaimed |sw_sync_tl|. + * If we do not count such "pending waits" and kfree the object + * immediately upon |goldfish_sync_timeline_destroy|, + * we might get mysterous RCU stalls after running a long + * time because the garbage memory that is being read + * happens to be interpretable as a |spinlock_t| struct + * that is currently in the locked state. + * + * To track when to free the |goldfish_sync_timeline_obj| + * itself, we maintain a kref. + * The kref essentially counts the timeline itself plus + * the number of waits in flight. kref_init/kref_put + * are issued on + * |goldfish_sync_timeline_create|/|goldfish_sync_timeline_destroy| + * and kref_get/kref_put are issued on + * |goldfish_sync_fence_create|/|goldfish_sync_timeline_inc|. + * + * The timeline is destroyed after reference count + * reaches zero, which would happen after + * |goldfish_sync_timeline_destroy| and all pending + * |goldfish_sync_timeline_inc|'s are fulfilled. + * + * NOTE (1): We assume that |fence_create| and + * |timeline_inc| calls are 1:1, otherwise the kref scheme + * will not work. This is a valid assumption as long + * as the host-side virtual device implementation + * does not insert any timeline increments + * that we did not trigger from here. + * + * NOTE (2): The use of kref by itself requires no locks, + * but this does not mean everything works without locks. + * Related timeline operations do require a lock of some sort, + * or at least are not proven to work without it. + * In particualr, we assume that all the operations + * done on the |kref| field above are done in contexts where + * |global_sync_state->mutex_lock| is held. Do not + * remove that lock until everything is proven to work + * without it!!! */ + struct kref kref; +}; + +/* We will call |delete_timeline_obj| when the last reference count + * of the kref is decremented. This deletes the sw_sync + * timeline object along with the wrapper itself. */ +static void delete_timeline_obj(struct kref* kref) { + struct goldfish_sync_timeline_obj* obj = + container_of(kref, struct goldfish_sync_timeline_obj, kref); + + sync_timeline_destroy(&obj->sw_sync_tl->obj); + obj->sw_sync_tl = NULL; + kfree(obj); +} + +static uint64_t gensym_ctr; +static void gensym(char *dst) +{ + sprintf(dst, "goldfish_sync:gensym:%llu", gensym_ctr); + gensym_ctr++; +} + +/* |goldfish_sync_timeline_create| assumes that |global_sync_state->mutex_lock| + * is held. */ +static struct goldfish_sync_timeline_obj* +goldfish_sync_timeline_create(void) +{ + + char timeline_name[256]; + struct sw_sync_timeline *res_sync_tl = NULL; + struct goldfish_sync_timeline_obj *res; + + DTRACE(); + + gensym(timeline_name); + + res_sync_tl = sw_sync_timeline_create(timeline_name); + if (!res_sync_tl) { + ERR("Failed to create sw_sync timeline."); + return NULL; + } + + res = kzalloc(sizeof(struct goldfish_sync_timeline_obj), GFP_KERNEL); + res->sw_sync_tl = res_sync_tl; + res->current_time = 0; + kref_init(&res->kref); + + DPRINT("new timeline_obj=0x%p", res); + return res; +} + +/* |goldfish_sync_fence_create| assumes that |global_sync_state->mutex_lock| + * is held. */ +static int +goldfish_sync_fence_create(struct goldfish_sync_timeline_obj *obj, + uint32_t val) +{ + + int fd; + char fence_name[256]; + struct sync_pt *syncpt = NULL; + struct sync_fence *sync_obj = NULL; + struct sw_sync_timeline *tl; + + DTRACE(); + + if (!obj) return -1; + + tl = obj->sw_sync_tl; + + syncpt = sw_sync_pt_create(tl, val); + if (!syncpt) { + ERR("could not create sync point! " + "sync_timeline=0x%p val=%d", + tl, val); + return -1; + } + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + ERR("could not get unused fd for sync fence. " + "errno=%d", fd); + goto err_cleanup_pt; + } + + gensym(fence_name); + + sync_obj = sync_fence_create(fence_name, syncpt); + if (!sync_obj) { + ERR("could not create sync fence! " + "sync_timeline=0x%p val=%d sync_pt=0x%p", + tl, val, syncpt); + goto err_cleanup_fd_pt; + } + + DPRINT("installing sync fence into fd %d sync_obj=0x%p", fd, sync_obj); + sync_fence_install(sync_obj, fd); + kref_get(&obj->kref); + + return fd; + +err_cleanup_fd_pt: + put_unused_fd(fd); +err_cleanup_pt: + sync_pt_free(syncpt); + return -1; +} + +/* |goldfish_sync_timeline_inc| assumes that |global_sync_state->mutex_lock| + * is held. */ +static void +goldfish_sync_timeline_inc(struct goldfish_sync_timeline_obj *obj, uint32_t inc) +{ + DTRACE(); + /* Just give up if someone else nuked the timeline. + * Whoever it was won't care that it doesn't get signaled. */ + if (!obj) return; + + DPRINT("timeline_obj=0x%p", obj); + sw_sync_timeline_inc(obj->sw_sync_tl, inc); + DPRINT("incremented timeline. increment max_time"); + obj->current_time += inc; + + /* Here, we will end up deleting the timeline object if it + * turns out that this call was a pending increment after + * |goldfish_sync_timeline_destroy| was called. */ + kref_put(&obj->kref, delete_timeline_obj); + DPRINT("done"); +} + +/* |goldfish_sync_timeline_destroy| assumes + * that |global_sync_state->mutex_lock| is held. */ +static void +goldfish_sync_timeline_destroy(struct goldfish_sync_timeline_obj *obj) +{ + DTRACE(); + /* See description of |goldfish_sync_timeline_obj| for why we + * should not immediately destroy |obj| */ + kref_put(&obj->kref, delete_timeline_obj); +} + +static inline void +goldfish_sync_cmd_queue(struct goldfish_sync_state *sync_state, + uint32_t cmd, + uint64_t handle, + uint32_t time_arg, + uint64_t hostcmd_handle) +{ + struct goldfish_sync_hostcmd *to_add; + + DTRACE(); + + BUG_ON(sync_state->to_do_end == GOLDFISH_SYNC_MAX_CMDS); + + to_add = &sync_state->to_do[sync_state->to_do_end]; + + to_add->cmd = cmd; + to_add->handle = handle; + to_add->time_arg = time_arg; + to_add->hostcmd_handle = hostcmd_handle; + + sync_state->to_do_end += 1; +} + +static inline void +goldfish_sync_hostcmd_reply(struct goldfish_sync_state *sync_state, + uint32_t cmd, + uint64_t handle, + uint32_t time_arg, + uint64_t hostcmd_handle) +{ + unsigned long irq_flags; + struct goldfish_sync_hostcmd *batch_hostcmd = + sync_state->batch_hostcmd; + + DTRACE(); + + spin_lock_irqsave(&sync_state->lock, irq_flags); + + batch_hostcmd->cmd = cmd; + batch_hostcmd->handle = handle; + batch_hostcmd->time_arg = time_arg; + batch_hostcmd->hostcmd_handle = hostcmd_handle; + writel(0, sync_state->reg_base + SYNC_REG_BATCH_COMMAND); + + spin_unlock_irqrestore(&sync_state->lock, irq_flags); +} + +static inline void +goldfish_sync_send_guestcmd(struct goldfish_sync_state *sync_state, + uint32_t cmd, + uint64_t glsync_handle, + uint64_t thread_handle, + uint64_t timeline_handle) +{ + unsigned long irq_flags; + struct goldfish_sync_guestcmd *batch_guestcmd = + sync_state->batch_guestcmd; + + DTRACE(); + + spin_lock_irqsave(&sync_state->lock, irq_flags); + + batch_guestcmd->host_command = (uint64_t)cmd; + batch_guestcmd->glsync_handle = (uint64_t)glsync_handle; + batch_guestcmd->thread_handle = (uint64_t)thread_handle; + batch_guestcmd->guest_timeline_handle = (uint64_t)timeline_handle; + writel(0, sync_state->reg_base + SYNC_REG_BATCH_GUESTCOMMAND); + + spin_unlock_irqrestore(&sync_state->lock, irq_flags); +} + +/* |goldfish_sync_interrupt| handles IRQ raises from the virtual device. + * In the context of OpenGL, this interrupt will fire whenever we need + * to signal a fence fd in the guest, with the command + * |CMD_SYNC_TIMELINE_INC|. + * However, because this function will be called in an interrupt context, + * it is necessary to do the actual work of signaling off of interrupt context. + * The shared work queue is used for this purpose. At the end when + * all pending commands are intercepted by the interrupt handler, + * we call |schedule_work|, which will later run the actual + * desired sync command in |goldfish_sync_work_item_fn|. + */ +static irqreturn_t goldfish_sync_interrupt(int irq, void *dev_id) +{ + + struct goldfish_sync_state *sync_state = dev_id; + + uint32_t nextcmd; + uint32_t command_r; + uint64_t handle_rw; + uint32_t time_r; + uint64_t hostcmd_handle_rw; + + int count = 0; + + DTRACE(); + + sync_state = dev_id; + + spin_lock(&sync_state->lock); + + for (;;) { + + readl(sync_state->reg_base + SYNC_REG_BATCH_COMMAND); + nextcmd = sync_state->batch_hostcmd->cmd; + + if (nextcmd == 0) + break; + + command_r = nextcmd; + handle_rw = sync_state->batch_hostcmd->handle; + time_r = sync_state->batch_hostcmd->time_arg; + hostcmd_handle_rw = sync_state->batch_hostcmd->hostcmd_handle; + + goldfish_sync_cmd_queue( + sync_state, + command_r, + handle_rw, + time_r, + hostcmd_handle_rw); + + count++; + } + + spin_unlock(&sync_state->lock); + + schedule_work(&sync_state->work_item); + + return (count == 0) ? IRQ_NONE : IRQ_HANDLED; +} + +/* |goldfish_sync_work_item_fn| does the actual work of servicing + * host->guest sync commands. This function is triggered whenever + * the IRQ for the goldfish sync device is raised. Once it starts + * running, it grabs the contents of the buffer containing the + * commands it needs to execute (there may be multiple, because + * our IRQ is active high and not edge triggered), and then + * runs all of them one after the other. + */ +static void goldfish_sync_work_item_fn(struct work_struct *input) +{ + + struct goldfish_sync_state *sync_state; + int sync_fence_fd; + + struct goldfish_sync_timeline_obj *timeline; + uint64_t timeline_ptr; + + uint64_t hostcmd_handle; + + uint32_t cmd; + uint64_t handle; + uint32_t time_arg; + + struct goldfish_sync_hostcmd *todo; + uint32_t todo_end; + + unsigned long irq_flags; + + struct goldfish_sync_hostcmd to_run[GOLDFISH_SYNC_MAX_CMDS]; + uint32_t i = 0; + + sync_state = container_of(input, struct goldfish_sync_state, work_item); + + mutex_lock(&sync_state->mutex_lock); + + spin_lock_irqsave(&sync_state->lock, irq_flags); { + + todo_end = sync_state->to_do_end; + + DPRINT("num sync todos: %u", sync_state->to_do_end); + + for (i = 0; i < todo_end; i++) + to_run[i] = sync_state->to_do[i]; + + /* We expect that commands will come in at a slow enough rate + * so that incoming items will not be more than + * GOLDFISH_SYNC_MAX_CMDS. + * + * This is because the way the sync device is used, + * it's only for managing buffer data transfers per frame, + * with a sequential dependency between putting things in + * to_do and taking them out. Once a set of commands is + * queued up in to_do, the user of the device waits for + * them to be processed before queuing additional commands, + * which limits the rate at which commands come in + * to the rate at which we take them out here. + * + * We also don't expect more than MAX_CMDS to be issued + * at once; there is a correspondence between + * which buffers need swapping to the (display / buffer queue) + * to particular commands, and we don't expect there to be + * enough display or buffer queues in operation at once + * to overrun GOLDFISH_SYNC_MAX_CMDS. + */ + sync_state->to_do_end = 0; + + } spin_unlock_irqrestore(&sync_state->lock, irq_flags); + + for (i = 0; i < todo_end; i++) { + DPRINT("todo index: %u", i); + + todo = &to_run[i]; + + cmd = todo->cmd; + + handle = (uint64_t)todo->handle; + time_arg = todo->time_arg; + hostcmd_handle = (uint64_t)todo->hostcmd_handle; + + DTRACE(); + + timeline = (struct goldfish_sync_timeline_obj *)(uintptr_t)handle; + + switch (cmd) { + case CMD_SYNC_READY: + break; + case CMD_CREATE_SYNC_TIMELINE: + DPRINT("exec CMD_CREATE_SYNC_TIMELINE: " + "handle=0x%llx time_arg=%d", + handle, time_arg); + timeline = goldfish_sync_timeline_create(); + timeline_ptr = (uintptr_t)timeline; + goldfish_sync_hostcmd_reply(sync_state, CMD_CREATE_SYNC_TIMELINE, + timeline_ptr, + 0, + hostcmd_handle); + DPRINT("sync timeline created: %p", timeline); + break; + case CMD_CREATE_SYNC_FENCE: + DPRINT("exec CMD_CREATE_SYNC_FENCE: " + "handle=0x%llx time_arg=%d", + handle, time_arg); + sync_fence_fd = goldfish_sync_fence_create(timeline, time_arg); + goldfish_sync_hostcmd_reply(sync_state, CMD_CREATE_SYNC_FENCE, + sync_fence_fd, + 0, + hostcmd_handle); + break; + case CMD_SYNC_TIMELINE_INC: + DPRINT("exec CMD_SYNC_TIMELINE_INC: " + "handle=0x%llx time_arg=%d", + handle, time_arg); + goldfish_sync_timeline_inc(timeline, time_arg); + break; + case CMD_DESTROY_SYNC_TIMELINE: + DPRINT("exec CMD_DESTROY_SYNC_TIMELINE: " + "handle=0x%llx time_arg=%d", + handle, time_arg); + goldfish_sync_timeline_destroy(timeline); + break; + } + DPRINT("Done executing sync command"); + } + mutex_unlock(&sync_state->mutex_lock); +} + +/* Guest-side interface: file operations */ + +/* Goldfish sync context and ioctl info. + * + * When a sync context is created by open()-ing the goldfish sync device, we + * create a sync context (|goldfish_sync_context|). + * + * Currently, the only data required to track is the sync timeline itself + * along with the current time, which are all packed up in the + * |goldfish_sync_timeline_obj| field. We use a |goldfish_sync_context| + * as the filp->private_data. + * + * Next, when a sync context user requests that work be queued and a fence + * fd provided, we use the |goldfish_sync_ioctl_info| struct, which holds + * information about which host handles to touch for this particular + * queue-work operation. We need to know about the host-side sync thread + * and the particular host-side GLsync object. We also possibly write out + * a file descriptor. + */ +struct goldfish_sync_context { + struct goldfish_sync_timeline_obj *timeline; +}; + +struct goldfish_sync_ioctl_info { + uint64_t host_glsync_handle_in; + uint64_t host_syncthread_handle_in; + int fence_fd_out; +}; + +static int goldfish_sync_open(struct inode *inode, struct file *file) +{ + + struct goldfish_sync_context *sync_context; + + DTRACE(); + + mutex_lock(&global_sync_state->mutex_lock); + + sync_context = kzalloc(sizeof(struct goldfish_sync_context), GFP_KERNEL); + + if (sync_context == NULL) { + ERR("Creation of goldfish sync context failed!"); + mutex_unlock(&global_sync_state->mutex_lock); + return -ENOMEM; + } + + sync_context->timeline = NULL; + + file->private_data = sync_context; + + DPRINT("successfully create a sync context @0x%p", sync_context); + + mutex_unlock(&global_sync_state->mutex_lock); + + return 0; +} + +static int goldfish_sync_release(struct inode *inode, struct file *file) +{ + + struct goldfish_sync_context *sync_context; + + DTRACE(); + + mutex_lock(&global_sync_state->mutex_lock); + + sync_context = file->private_data; + + if (sync_context->timeline) + goldfish_sync_timeline_destroy(sync_context->timeline); + + sync_context->timeline = NULL; + + kfree(sync_context); + + mutex_unlock(&global_sync_state->mutex_lock); + + return 0; +} + +/* |goldfish_sync_ioctl| is the guest-facing interface of goldfish sync + * and is used in conjunction with eglCreateSyncKHR to queue up the + * actual work of waiting for the EGL sync command to complete, + * possibly returning a fence fd to the guest. + */ +static long goldfish_sync_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct goldfish_sync_context *sync_context_data; + struct goldfish_sync_timeline_obj *timeline; + int fd_out; + struct goldfish_sync_ioctl_info ioctl_data; + + DTRACE(); + + sync_context_data = file->private_data; + fd_out = -1; + + switch (cmd) { + case GOLDFISH_SYNC_IOC_QUEUE_WORK: + + DPRINT("exec GOLDFISH_SYNC_IOC_QUEUE_WORK"); + + mutex_lock(&global_sync_state->mutex_lock); + + if (copy_from_user(&ioctl_data, + (void __user *)arg, + sizeof(ioctl_data))) { + ERR("Failed to copy memory for ioctl_data from user."); + mutex_unlock(&global_sync_state->mutex_lock); + return -EFAULT; + } + + if (ioctl_data.host_syncthread_handle_in == 0) { + DPRINT("Error: zero host syncthread handle!!!"); + mutex_unlock(&global_sync_state->mutex_lock); + return -EFAULT; + } + + if (!sync_context_data->timeline) { + DPRINT("no timeline yet, create one."); + sync_context_data->timeline = goldfish_sync_timeline_create(); + DPRINT("timeline: 0x%p", &sync_context_data->timeline); + } + + timeline = sync_context_data->timeline; + fd_out = goldfish_sync_fence_create(timeline, + timeline->current_time + 1); + DPRINT("Created fence with fd %d and current time %u (timeline: 0x%p)", + fd_out, + sync_context_data->timeline->current_time + 1, + sync_context_data->timeline); + + ioctl_data.fence_fd_out = fd_out; + + if (copy_to_user((void __user *)arg, + &ioctl_data, + sizeof(ioctl_data))) { + DPRINT("Error, could not copy to user!!!"); + + sys_close(fd_out); + /* We won't be doing an increment, kref_put immediately. */ + kref_put(&timeline->kref, delete_timeline_obj); + mutex_unlock(&global_sync_state->mutex_lock); + return -EFAULT; + } + + /* We are now about to trigger a host-side wait; + * accumulate on |pending_waits|. */ + goldfish_sync_send_guestcmd(global_sync_state, + CMD_TRIGGER_HOST_WAIT, + ioctl_data.host_glsync_handle_in, + ioctl_data.host_syncthread_handle_in, + (uint64_t)(uintptr_t)(sync_context_data->timeline)); + + mutex_unlock(&global_sync_state->mutex_lock); + return 0; + default: + return -ENOTTY; + } +} + +static const struct file_operations goldfish_sync_fops = { + .owner = THIS_MODULE, + .open = goldfish_sync_open, + .release = goldfish_sync_release, + .unlocked_ioctl = goldfish_sync_ioctl, + .compat_ioctl = goldfish_sync_ioctl, +}; + +static struct miscdevice goldfish_sync_device = { + .name = "goldfish_sync", + .fops = &goldfish_sync_fops, +}; + + +static bool setup_verify_batch_cmd_addr(struct goldfish_sync_state *sync_state, + void *batch_addr, + uint32_t addr_offset, + uint32_t addr_offset_high) +{ + uint64_t batch_addr_phys; + uint32_t batch_addr_phys_test_lo; + uint32_t batch_addr_phys_test_hi; + + if (!batch_addr) { + ERR("Could not use batch command address!"); + return false; + } + + batch_addr_phys = virt_to_phys(batch_addr); + writel((uint32_t)(batch_addr_phys), + sync_state->reg_base + addr_offset); + writel((uint32_t)(batch_addr_phys >> 32), + sync_state->reg_base + addr_offset_high); + + batch_addr_phys_test_lo = + readl(sync_state->reg_base + addr_offset); + batch_addr_phys_test_hi = + readl(sync_state->reg_base + addr_offset_high); + + if (virt_to_phys(batch_addr) != + (((uint64_t)batch_addr_phys_test_hi << 32) | + batch_addr_phys_test_lo)) { + ERR("Invalid batch command address!"); + return false; + } + + return true; +} + +int goldfish_sync_probe(struct platform_device *pdev) +{ + struct resource *ioresource; + struct goldfish_sync_state *sync_state = global_sync_state; + int status; + + DTRACE(); + + sync_state->to_do_end = 0; + + spin_lock_init(&sync_state->lock); + mutex_init(&sync_state->mutex_lock); + + platform_set_drvdata(pdev, sync_state); + + ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (ioresource == NULL) { + ERR("platform_get_resource failed"); + return -ENODEV; + } + + sync_state->reg_base = devm_ioremap(&pdev->dev, ioresource->start, PAGE_SIZE); + if (sync_state->reg_base == NULL) { + ERR("Could not ioremap"); + return -ENOMEM; + } + + sync_state->irq = platform_get_irq(pdev, 0); + if (sync_state->irq < 0) { + ERR("Could not platform_get_irq"); + return -ENODEV; + } + + status = devm_request_irq(&pdev->dev, + sync_state->irq, + goldfish_sync_interrupt, + IRQF_SHARED, + pdev->name, + sync_state); + if (status) { + ERR("request_irq failed"); + return -ENODEV; + } + + INIT_WORK(&sync_state->work_item, + goldfish_sync_work_item_fn); + + misc_register(&goldfish_sync_device); + + /* Obtain addresses for batch send/recv of commands. */ + { + struct goldfish_sync_hostcmd *batch_addr_hostcmd; + struct goldfish_sync_guestcmd *batch_addr_guestcmd; + + batch_addr_hostcmd = devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_hostcmd), + GFP_KERNEL); + batch_addr_guestcmd = devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_guestcmd), + GFP_KERNEL); + + if (!setup_verify_batch_cmd_addr(sync_state, + batch_addr_hostcmd, + SYNC_REG_BATCH_COMMAND_ADDR, + SYNC_REG_BATCH_COMMAND_ADDR_HIGH)) { + ERR("goldfish_sync: Could not setup batch command address"); + return -ENODEV; + } + + if (!setup_verify_batch_cmd_addr(sync_state, + batch_addr_guestcmd, + SYNC_REG_BATCH_GUESTCOMMAND_ADDR, + SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH)) { + ERR("goldfish_sync: Could not setup batch guest command address"); + return -ENODEV; + } + + sync_state->batch_hostcmd = batch_addr_hostcmd; + sync_state->batch_guestcmd = batch_addr_guestcmd; + } + + INFO("goldfish_sync: Initialized goldfish sync device"); + + writel(0, sync_state->reg_base + SYNC_REG_INIT); + + return 0; +} + +static int goldfish_sync_remove(struct platform_device *pdev) +{ + struct goldfish_sync_state *sync_state = global_sync_state; + + DTRACE(); + + misc_deregister(&goldfish_sync_device); + memset(sync_state, 0, sizeof(struct goldfish_sync_state)); + return 0; +} + +static const struct of_device_id goldfish_sync_of_match[] = { + { .compatible = "google,goldfish-sync", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_sync_of_match); + +static const struct acpi_device_id goldfish_sync_acpi_match[] = { + { "GFSH0006", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(acpi, goldfish_sync_acpi_match); + +static struct platform_driver goldfish_sync = { + .probe = goldfish_sync_probe, + .remove = goldfish_sync_remove, + .driver = { + .name = "goldfish_sync", + .of_match_table = goldfish_sync_of_match, + .acpi_match_table = ACPI_PTR(goldfish_sync_acpi_match), + } +}; + +module_platform_driver(goldfish_sync); + +MODULE_AUTHOR("Google, Inc."); +MODULE_DESCRIPTION("Android QEMU Sync Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); + +/* This function is only to run a basic test of sync framework. + * It creates a timeline and fence object whose signal point is at 1. + * The timeline is incremented, and we use the sync framework's + * sync_fence_wait on that fence object. If everything works out, + * we should not hang in the wait and return immediately. + * There is no way to explicitly run this test yet, but it + * can be used by inserting it at the end of goldfish_sync_probe. + */ +void test_kernel_sync(void) +{ + struct goldfish_sync_timeline_obj *test_timeline; + int test_fence_fd; + + DTRACE(); + + DPRINT("test sw_sync"); + + test_timeline = goldfish_sync_timeline_create(); + DPRINT("sw_sync_timeline_create -> 0x%p", test_timeline); + + test_fence_fd = goldfish_sync_fence_create(test_timeline, 1); + DPRINT("sync_fence_create -> %d", test_fence_fd); + + DPRINT("incrementing test timeline"); + goldfish_sync_timeline_inc(test_timeline, 1); + + DPRINT("test waiting (should NOT hang)"); + sync_fence_wait( + sync_fence_fdget(test_fence_fd), -1); + + DPRINT("test waiting (afterward)"); +} diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c index bb40f3728742..20314ff08be0 100644 --- a/drivers/staging/iio/adc/ad7192.c +++ b/drivers/staging/iio/adc/ad7192.c @@ -236,7 +236,7 @@ static int ad7192_setup(struct ad7192_state *st, st->mclk = pdata->ext_clk_hz; else st->mclk = AD7192_INT_FREQ_MHZ; - break; + break; default: ret = -EINVAL; goto out; diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index 10c43dda0f5a..196da09e20a1 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -647,6 +647,7 @@ static void ad5933_work(struct work_struct *work) __be16 buf[2]; int val[2]; unsigned char status; + int ret; mutex_lock(&indio_dev->mlock); if (st->state == AD5933_CTRL_INIT_START_FREQ) { @@ -654,19 +655,22 @@ static void ad5933_work(struct work_struct *work) ad5933_cmd(st, AD5933_CTRL_START_SWEEP); st->state = AD5933_CTRL_START_SWEEP; schedule_delayed_work(&st->work, st->poll_time_jiffies); - mutex_unlock(&indio_dev->mlock); - return; + goto out; } - ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status); + ret = ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status); + if (ret) + goto out; if (status & AD5933_STAT_DATA_VALID) { int scan_count = bitmap_weight(indio_dev->active_scan_mask, indio_dev->masklength); - ad5933_i2c_read(st->client, + ret = ad5933_i2c_read(st->client, test_bit(1, indio_dev->active_scan_mask) ? AD5933_REG_REAL_DATA : AD5933_REG_IMAG_DATA, scan_count * 2, (u8 *)buf); + if (ret) + goto out; if (scan_count == 2) { val[0] = be16_to_cpu(buf[0]); @@ -678,8 +682,7 @@ static void ad5933_work(struct work_struct *work) } else { /* no data available - try again later */ schedule_delayed_work(&st->work, st->poll_time_jiffies); - mutex_unlock(&indio_dev->mlock); - return; + goto out; } if (status & AD5933_STAT_SWEEP_DONE) { @@ -691,7 +694,7 @@ static void ad5933_work(struct work_struct *work) ad5933_cmd(st, AD5933_CTRL_INC_FREQ); schedule_delayed_work(&st->work, st->poll_time_jiffies); } - +out: mutex_unlock(&indio_dev->mlock); } diff --git a/drivers/staging/nvec/nvec_ps2.c b/drivers/staging/nvec/nvec_ps2.c index 0922dd3a08d3..196f6b0a288f 100644 --- a/drivers/staging/nvec/nvec_ps2.c +++ b/drivers/staging/nvec/nvec_ps2.c @@ -106,13 +106,12 @@ static int nvec_mouse_probe(struct platform_device *pdev) { struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent); struct serio *ser_dev; - char mouse_reset[] = { NVEC_PS2, SEND_COMMAND, PSMOUSE_RST, 3 }; - ser_dev = devm_kzalloc(&pdev->dev, sizeof(struct serio), GFP_KERNEL); + ser_dev = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!ser_dev) return -ENOMEM; - ser_dev->id.type = SERIO_PS_PSTHRU; + ser_dev->id.type = SERIO_8042; ser_dev->write = ps2_sendcommand; ser_dev->start = ps2_startstreaming; ser_dev->stop = ps2_stopstreaming; @@ -127,9 +126,6 @@ static int nvec_mouse_probe(struct platform_device *pdev) serio_register_port(ser_dev); - /* mouse reset */ - nvec_write_async(nvec, mouse_reset, sizeof(mouse_reset)); - return 0; } diff --git a/drivers/staging/rtl8188eu/core/rtw_cmd.c b/drivers/staging/rtl8188eu/core/rtw_cmd.c index 9b7026e7d55b..45d0a87f55d2 100644 --- a/drivers/staging/rtl8188eu/core/rtw_cmd.c +++ b/drivers/staging/rtl8188eu/core/rtw_cmd.c @@ -718,13 +718,13 @@ u8 rtw_addbareq_cmd(struct adapter *padapter, u8 tid, u8 *addr) u8 res = _SUCCESS; - ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ph2c == NULL) { res = _FAIL; goto exit; } - paddbareq_parm = kzalloc(sizeof(struct addBaReq_parm), GFP_KERNEL); + paddbareq_parm = kzalloc(sizeof(struct addBaReq_parm), GFP_ATOMIC); if (paddbareq_parm == NULL) { kfree(ph2c); res = _FAIL; diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 7bc3778a1ac9..2a67af4e2e13 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1680,6 +1680,7 @@ void transport_generic_request_failure(struct se_cmd *cmd, case TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED: case TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED: case TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED: + case TCM_COPY_TARGET_DEVICE_NOT_REACHABLE: break; case TCM_OUT_OF_RESOURCES: sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; @@ -2509,8 +2510,10 @@ int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref) * fabric acknowledgement that requires two target_put_sess_cmd() * invocations before se_cmd descriptor release. */ - if (ack_kref) + if (ack_kref) { kref_get(&se_cmd->cmd_kref); + se_cmd->se_cmd_flags |= SCF_ACK_KREF; + } spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); if (se_sess->sess_tearing_down) { @@ -2833,6 +2836,12 @@ static const struct sense_info sense_info_table[] = { .ascq = 0x03, /* LOGICAL BLOCK REFERENCE TAG CHECK FAILED */ .add_sector_info = true, }, + [TCM_COPY_TARGET_DEVICE_NOT_REACHABLE] = { + .key = COPY_ABORTED, + .asc = 0x0d, + .ascq = 0x02, /* COPY TARGET DEVICE NOT REACHABLE */ + + }, [TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE] = { /* * Returning ILLEGAL REQUEST would cause immediate IO errors on diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c index 47fe94ee10b8..153a6f255b6d 100644 --- a/drivers/target/target_core_xcopy.c +++ b/drivers/target/target_core_xcopy.c @@ -104,7 +104,7 @@ static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op } mutex_unlock(&g_device_mutex); - pr_err("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n"); + pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n"); return -EINVAL; } @@ -185,7 +185,7 @@ static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd, struct xcopy_op *xop, unsigned char *p, - unsigned short tdll) + unsigned short tdll, sense_reason_t *sense_ret) { struct se_device *local_dev = se_cmd->se_dev; unsigned char *desc = p; @@ -193,6 +193,8 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd, unsigned short start = 0; bool src = true; + *sense_ret = TCM_INVALID_PARAMETER_LIST; + if (offset != 0) { pr_err("XCOPY target descriptor list length is not" " multiple of %d\n", XCOPY_TARGET_DESC_LEN); @@ -243,9 +245,16 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd, rc = target_xcopy_locate_se_dev_e4(se_cmd, xop, true); else rc = target_xcopy_locate_se_dev_e4(se_cmd, xop, false); - - if (rc < 0) + /* + * If a matching IEEE NAA 0x83 descriptor for the requested device + * is not located on this node, return COPY_ABORTED with ASQ/ASQC + * 0x0d/0x02 - COPY_TARGET_DEVICE_NOT_REACHABLE to request the + * initiator to fall back to normal copy method. + */ + if (rc < 0) { + *sense_ret = TCM_COPY_TARGET_DEVICE_NOT_REACHABLE; goto out; + } pr_debug("XCOPY TGT desc: Source dev: %p NAA IEEE WWN: 0x%16phN\n", xop->src_dev, &xop->src_tid_wwn[0]); @@ -653,6 +662,7 @@ static int target_xcopy_read_source( rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, src_dev, &cdb[0], remote_port, true); if (rc < 0) { + ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status; transport_generic_free_cmd(se_cmd, 0); return rc; } @@ -664,6 +674,7 @@ static int target_xcopy_read_source( rc = target_xcopy_issue_pt_cmd(xpt_cmd); if (rc < 0) { + ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status; transport_generic_free_cmd(se_cmd, 0); return rc; } @@ -714,6 +725,7 @@ static int target_xcopy_write_destination( remote_port, false); if (rc < 0) { struct se_cmd *src_cmd = &xop->src_pt_cmd->se_cmd; + ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status; /* * If the failure happened before the t_mem_list hand-off in * target_xcopy_setup_pt_cmd(), Reset memory + clear flag so that @@ -729,6 +741,7 @@ static int target_xcopy_write_destination( rc = target_xcopy_issue_pt_cmd(xpt_cmd); if (rc < 0) { + ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status; se_cmd->se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC; transport_generic_free_cmd(se_cmd, 0); return rc; @@ -815,9 +828,14 @@ static void target_xcopy_do_work(struct work_struct *work) out: xcopy_pt_undepend_remotedev(xop); kfree(xop); - - pr_warn("target_xcopy_do_work: Setting X-COPY CHECK_CONDITION -> sending response\n"); - ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION; + /* + * Don't override an error scsi status if it has already been set + */ + if (ec_cmd->scsi_status == SAM_STAT_GOOD) { + pr_warn_ratelimited("target_xcopy_do_work: rc: %d, Setting X-COPY" + " CHECK_CONDITION -> sending response\n", rc); + ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION; + } target_complete_cmd(ec_cmd, SAM_STAT_CHECK_CONDITION); } @@ -875,7 +893,7 @@ sense_reason_t target_do_xcopy(struct se_cmd *se_cmd) " tdll: %hu sdll: %u inline_dl: %u\n", list_id, list_id_usage, tdll, sdll, inline_dl); - rc = target_xcopy_parse_target_descriptors(se_cmd, xop, &p[16], tdll); + rc = target_xcopy_parse_target_descriptors(se_cmd, xop, &p[16], tdll, &ret); if (rc <= 0) goto out; diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 0f82c0b146f6..1e332855b933 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -68,8 +68,7 @@ static void goldfish_tty_do_write(int line, const char *buf, unsigned count) static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) { - struct platform_device *pdev = dev_id; - struct goldfish_tty *qtty = &goldfish_ttys[pdev->id]; + struct goldfish_tty *qtty = dev_id; void __iomem *base = qtty->base; unsigned long irq_flags; unsigned char *buf; @@ -233,6 +232,7 @@ static int goldfish_tty_probe(struct platform_device *pdev) struct device *ttydev; void __iomem *base; u32 irq; + unsigned int line; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) @@ -248,10 +248,16 @@ static int goldfish_tty_probe(struct platform_device *pdev) irq = r->start; - if (pdev->id >= goldfish_tty_line_count) - goto err_unmap; - mutex_lock(&goldfish_tty_lock); + + if (pdev->id == PLATFORM_DEVID_NONE) + line = goldfish_tty_current_line_count; + else + line = pdev->id; + + if (line >= goldfish_tty_line_count) + goto err_create_driver_failed; + if (goldfish_tty_current_line_count == 0) { ret = goldfish_tty_create_driver(); if (ret) @@ -259,7 +265,7 @@ static int goldfish_tty_probe(struct platform_device *pdev) } goldfish_tty_current_line_count++; - qtty = &goldfish_ttys[pdev->id]; + qtty = &goldfish_ttys[line]; spin_lock_init(&qtty->lock); tty_port_init(&qtty->port); qtty->port.ops = &goldfish_port_ops; @@ -269,13 +275,13 @@ static int goldfish_tty_probe(struct platform_device *pdev) writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD); ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, - "goldfish_tty", pdev); + "goldfish_tty", qtty); if (ret) goto err_request_irq_failed; ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver, - pdev->id, &pdev->dev); + line, &pdev->dev); if (IS_ERR(ttydev)) { ret = PTR_ERR(ttydev); goto err_tty_register_device_failed; @@ -286,8 +292,9 @@ static int goldfish_tty_probe(struct platform_device *pdev) qtty->console.device = goldfish_tty_console_device; qtty->console.setup = goldfish_tty_console_setup; qtty->console.flags = CON_PRINTBUFFER; - qtty->console.index = pdev->id; + qtty->console.index = line; register_console(&qtty->console); + platform_set_drvdata(pdev, qtty); mutex_unlock(&goldfish_tty_lock); return 0; @@ -307,13 +314,12 @@ err_unmap: static int goldfish_tty_remove(struct platform_device *pdev) { - struct goldfish_tty *qtty; + struct goldfish_tty *qtty = platform_get_drvdata(pdev); mutex_lock(&goldfish_tty_lock); - qtty = &goldfish_ttys[pdev->id]; unregister_console(&qtty->console); - tty_unregister_device(goldfish_tty_driver, pdev->id); + tty_unregister_device(goldfish_tty_driver, qtty->console.index); iounmap(qtty->base); qtty->base = NULL; free_irq(qtty->irq, pdev); @@ -324,11 +330,19 @@ static int goldfish_tty_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_tty_of_match[] = { + { .compatible = "google,goldfish-tty", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, goldfish_tty_of_match); + static struct platform_driver goldfish_tty_platform_driver = { .probe = goldfish_tty_probe, .remove = goldfish_tty_remove, .driver = { - .name = "goldfish_tty" + .name = "goldfish_tty", + .of_match_table = goldfish_tty_of_match, } }; diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index a5d319e4aae6..8435c3f204c1 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -440,7 +440,7 @@ static int dw8250_probe(struct platform_device *pdev) } data->pclk = devm_clk_get(&pdev->dev, "apb_pclk"); - if (IS_ERR(data->clk) && PTR_ERR(data->clk) == -EPROBE_DEFER) { + if (IS_ERR(data->pclk) && PTR_ERR(data->pclk) == -EPROBE_DEFER) { err = -EPROBE_DEFER; goto err_clk; } diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index ed489880e62b..83b3988eb6b2 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -149,6 +149,9 @@ static void mid8250_set_termios(struct uart_port *p, unsigned long w = BIT(24) - 1; unsigned long mul, div; + /* Gracefully handle the B0 case: fall back to B9600 */ + fuart = fuart ? fuart : 9600 * 16; + if (mid->board->freq < fuart) { /* Find prescaler value that satisfies Fuart < Fref */ if (mid->board->freq > baud) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index c1d4a8fa9be8..029de3f99752 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1952,6 +1952,43 @@ pci_wch_ch38x_setup(struct serial_private *priv, #define PCI_DEVICE_ID_PERICOM_PI7C9X7954 0x7954 #define PCI_DEVICE_ID_PERICOM_PI7C9X7958 0x7958 +#define PCI_VENDOR_ID_ACCESIO 0x494f +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SDB 0x1051 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2S 0x1053 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB 0x105C +#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S 0x105E +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_2DB 0x1091 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_2 0x1093 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB 0x1099 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4 0x109B +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SMDB 0x10D1 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2SM 0x10D3 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB 0x10DA +#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM 0x10DC +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_1 0x1108 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_2 0x1110 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_2 0x1111 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4 0x1118 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4 0x1119 +#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2S 0x1152 +#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S 0x115A +#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_2 0x1190 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_2 0x1191 +#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4 0x1198 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4 0x1199 +#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2SM 0x11D0 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4 0x105A +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4 0x105B +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM422_8 0x106A +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM485_8 0x106B +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4 0x1098 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_8 0x10A9 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM 0x10D9 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM 0x10E9 +#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM 0x11D8 + + + /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 #define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588 @@ -5120,6 +5157,108 @@ static struct pci_device_id serial_pci_tbl[] = { 0, 0, pbn_pericom_PI7C9X7958 }, /* + * ACCES I/O Products quad + */ + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SDB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2S, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_2DB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SMDB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_1, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2S, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM422_8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM485_8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + /* * Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke) */ { PCI_VENDOR_ID_TOPIC, PCI_DEVICE_ID_TOPIC_TP560, diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 7b5462eb8388..e0b89b961e1b 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2075,6 +2075,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state, static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned long flags; unsigned int old_mode, mode, imr, quot, baud; @@ -2178,11 +2179,29 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, mode |= ATMEL_US_USMODE_RS485; } else if (termios->c_cflag & CRTSCTS) { /* RS232 with hardware handshake (RTS/CTS) */ - if (atmel_use_dma_rx(port) && !atmel_use_fifo(port)) { - dev_info(port->dev, "not enabling hardware flow control because DMA is used"); - termios->c_cflag &= ~CRTSCTS; - } else { + if (atmel_use_fifo(port) && + !mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS)) { + /* + * with ATMEL_US_USMODE_HWHS set, the controller will + * be able to drive the RTS pin high/low when the RX + * FIFO is above RXFTHRES/below RXFTHRES2. + * It will also disable the transmitter when the CTS + * pin is high. + * This mode is not activated if CTS pin is a GPIO + * because in this case, the transmitter is always + * disabled (there must be an internal pull-up + * responsible for this behaviour). + * If the RTS pin is a GPIO, the controller won't be + * able to drive it according to the FIFO thresholds, + * but it will be handled by the driver. + */ mode |= ATMEL_US_USMODE_HWHS; + } else { + /* + * For platforms without FIFO, the flow control is + * handled by the driver. + */ + mode |= ATMEL_US_USMODE_NORMAL; } } else { /* RS232 without hadware handshake */ diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index ca0d3802f2af..4e603d060e80 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -490,12 +490,6 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig locked = spin_trylock_irqsave(&port->lock, flags); else spin_lock_irqsave(&port->lock, flags); - if (port->sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&port->lock); - } else - spin_lock(&port->lock); for (i = 0; i < n; i++) { if (*s == '\n') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 136ebaaa9cc0..5ab54ef4f304 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -872,10 +872,15 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, if (new_cols == vc->vc_cols && new_rows == vc->vc_rows) return 0; + if (new_screen_size > (4 << 20)) + return -EINVAL; newscreen = kmalloc(new_screen_size, GFP_USER); if (!newscreen) return -ENOMEM; + if (vc == sel_cons) + clear_selection(); + old_rows = vc->vc_rows; old_row_size = vc->vc_size_row; @@ -1173,7 +1178,7 @@ static void csi_J(struct vc_data *vc, int vpar) break; case 3: /* erase scroll-back buffer (and whole display) */ scr_memsetw(vc->vc_screenbuf, vc->vc_video_erase_char, - vc->vc_screenbuf_size >> 1); + vc->vc_screenbuf_size); set_origin(vc); if (CON_IS_VISIBLE(vc)) update_screen(vc); diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index 915facbf552e..e1134a4d97f3 100644 --- a/drivers/uio/uio_dmem_genirq.c +++ b/drivers/uio/uio_dmem_genirq.c @@ -229,7 +229,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) ++uiomem; } - priv->dmem_region_start = i; + priv->dmem_region_start = uiomem - &uioinfo->mem[0]; priv->num_dmem_regions = pdata->num_dynamic_regions; for (i = 0; i < pdata->num_dynamic_regions; ++i) { diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 965d0e240dcb..ba4a2a1eb3ff 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -926,6 +926,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (!ci) return -ENOMEM; + spin_lock_init(&ci->lock); ci->dev = dev; ci->platdata = dev_get_platdata(dev); ci->imx28_write_fix = !!(ci->platdata->flags & diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index ca367b05e440..d8a045fc1fdb 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -939,6 +939,15 @@ static int isr_setup_status_phase(struct ci_hdrc *ci) int retval; struct ci_hw_ep *hwep; + /* + * Unexpected USB controller behavior, caused by bad signal integrity + * or ground reference problems, can lead to isr_setup_status_phase + * being called with ci->status equal to NULL. + * If this situation occurs, you should review your USB hardware design. + */ + if (WARN_ON_ONCE(!ci->status)) + return -EPIPE; + hwep = (ci->ep0_dir == TX) ? ci->ep0out : ci->ep0in; ci->status->context = ci; ci->status->complete = isr_setup_status_complete; @@ -1875,8 +1884,6 @@ static int udc_start(struct ci_hdrc *ci) struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps; int retval = 0; - spin_lock_init(&ci->lock); - ci->gadget.ops = &usb_gadget_ops; ci->gadget.speed = USB_SPEED_UNKNOWN; ci->gadget.max_speed = USB_SPEED_HIGH; diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 7f374369e539..4d77745f439f 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -877,8 +877,6 @@ static int wait_serial_change(struct acm *acm, unsigned long arg) DECLARE_WAITQUEUE(wait, current); struct async_icount old, new; - if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD )) - return -EINVAL; do { spin_lock_irq(&acm->read_lock); old = acm->oldcount; diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 7a11a8263171..deaddb950c20 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -121,6 +121,7 @@ static void usbtmc_delete(struct kref *kref) struct usbtmc_device_data *data = to_usbtmc_data(kref); usb_put_dev(data->usb_dev); + kfree(data); } static int usbtmc_open(struct inode *inode, struct file *filp) @@ -1104,7 +1105,7 @@ static int usbtmc_probe(struct usb_interface *intf, dev_dbg(&intf->dev, "%s called\n", __func__); - data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); + data = kmalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 80c8d90d8b75..ff44cfa26af8 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -211,8 +211,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, memcpy(&endpoint->desc, d, n); INIT_LIST_HEAD(&endpoint->urb_list); - /* Fix up bInterval values outside the legal range. Use 32 ms if no - * proper value can be guessed. */ + /* + * Fix up bInterval values outside the legal range. + * Use 10 or 8 ms if no proper value can be guessed. + */ i = 0; /* i = min, j = max, n = default */ j = 255; if (usb_endpoint_xfer_int(d)) { @@ -221,13 +223,15 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, case USB_SPEED_SUPER_PLUS: case USB_SPEED_SUPER: case USB_SPEED_HIGH: - /* Many device manufacturers are using full-speed + /* + * Many device manufacturers are using full-speed * bInterval values in high-speed interrupt endpoint - * descriptors. Try to fix those and fall back to a - * 32 ms default value otherwise. */ + * descriptors. Try to fix those and fall back to an + * 8-ms default value otherwise. + */ n = fls(d->bInterval*8); if (n == 0) - n = 9; /* 32 ms = 2^(9-1) uframes */ + n = 7; /* 8 ms = 2^(7-1) uframes */ j = 16; /* @@ -242,10 +246,12 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, } break; default: /* USB_SPEED_FULL or _LOW */ - /* For low-speed, 10 ms is the official minimum. + /* + * For low-speed, 10 ms is the official minimum. * But some "overclocked" devices might want faster - * polling so we'll allow it. */ - n = 32; + * polling so we'll allow it. + */ + n = 10; break; } } else if (usb_endpoint_xfer_isoc(d)) { @@ -253,10 +259,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, j = 16; switch (to_usb_device(ddev)->speed) { case USB_SPEED_HIGH: - n = 9; /* 32 ms = 2^(9-1) uframes */ + n = 7; /* 8 ms = 2^(7-1) uframes */ break; default: /* USB_SPEED_FULL */ - n = 6; /* 32 ms = 2^(6-1) frames */ + n = 4; /* 8 ms = 2^(4-1) frames */ break; } } diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 068b03a35bd5..20ac60d6b6a8 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -978,22 +978,30 @@ void dbg_print_reg(const char *name, int reg) static ssize_t dwc3_store_events(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - unsigned tty; + int ret; + u8 tty; if (buf == NULL) { pr_err("[%s] EINVAL\n", __func__); - goto done; + ret = -EINVAL; + return ret; } - if (sscanf(buf, "%u", &tty) != 1 || tty > 1) { + ret = kstrtou8_from_user(buf, count, 0, &tty); + if (ret < 0) { + pr_err("can't get enter value.\n"); + return ret; + } + + if (tty > 1) { pr_err("<1|0>: enable|disable console log\n"); - goto done; + ret = -EINVAL; + return ret; } dbg_dwc3_data.tty = tty; pr_info("tty = %u", dbg_dwc3_data.tty); - done: return count; } @@ -1034,21 +1042,30 @@ const struct file_operations dwc3_gadget_dbg_data_fops = { static ssize_t dwc3_store_int_events(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { - int clear_stats, i; + int i, ret; unsigned long flags; struct seq_file *s = file->private_data; struct dwc3 *dwc = s->private; struct dwc3_ep *dep; struct timespec ts; + u8 clear_stats; if (ubuf == NULL) { pr_err("[%s] EINVAL\n", __func__); - goto done; + ret = -EINVAL; + return ret; + } + + ret = kstrtou8_from_user(ubuf, count, 0, &clear_stats); + if (ret < 0) { + pr_err("can't get enter value.\n"); + return ret; } - if (sscanf(ubuf, "%u", &clear_stats) != 1 || clear_stats != 0) { + if (clear_stats != 0) { pr_err("Wrong value. To clear stats, enter value as 0.\n"); - goto done; + ret = -EINVAL; + return ret; } spin_lock_irqsave(&dwc->lock, flags); @@ -1065,7 +1082,6 @@ static ssize_t dwc3_store_int_events(struct file *file, spin_unlock_irqrestore(&dwc->lock, flags); -done: return count; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 88350e61f3bd..7a279db521ca 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3582,7 +3582,7 @@ err3: kfree(dwc->setup_buf); err2: - dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2, dwc->ep0_trb, dwc->ep0_trb_addr); err1: @@ -3611,7 +3611,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc) kfree(dwc->setup_buf); - dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2, dwc->ep0_trb, dwc->ep0_trb_addr); dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index a53b23789d7a..9622514e3df9 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1942,7 +1942,9 @@ unknown: if (value < 0) { DBG(cdev, "ep_queue --> %d\n", value); req->status = 0; - composite_setup_complete(gadget->ep0, req); + if (value != -ESHUTDOWN) + composite_setup_complete(gadget->ep0, + req); } return value; } @@ -2031,7 +2033,8 @@ try_fun_setup: if (value < 0) { DBG(cdev, "ep_queue --> %d\n", value); req->status = 0; - composite_setup_complete(gadget->ep0, req); + if (value != -ESHUTDOWN) + composite_setup_complete(gadget->ep0, req); } } else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) { WARN(cdev, @@ -2461,6 +2464,11 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev) spin_lock_irqsave(&cdev->lock, flags); if (cdev->delayed_status == 0) { + if (!cdev->config) { + spin_unlock_irqrestore(&cdev->lock, flags); + return; + } + spin_unlock_irqrestore(&cdev->lock, flags); WARN(cdev, "%s: Unexpected call\n", __func__); } else if (--cdev->delayed_status == 0) { diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 4964bb1a24b1..ed0ff7b1fc15 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -426,11 +426,6 @@ static int config_usb_cfg_link( } f = usb_get_function(fi); - if (f == NULL) { - /* Are we trying to symlink PTP without MTP function? */ - ret = -EINVAL; /* Invalid Configuration */ - goto out; - } if (IS_ERR(f)) { ret = PTR_ERR(f); goto out; diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index 61057befc136..cd096fb9078f 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -255,6 +255,7 @@ static inline struct acc_dev *func_to_dev(struct usb_function *f) static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size) { struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) return NULL; @@ -1079,6 +1080,7 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f) static void acc_start_work(struct work_struct *data) { char *envp[2] = { "ACCESSORY=START", NULL }; + kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); } diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c index bcd817439dbf..db7903d19c43 100644 --- a/drivers/usb/gadget/function/f_audio_source.c +++ b/drivers/usb/gadget/function/f_audio_source.c @@ -310,6 +310,7 @@ static struct device_attribute *audio_source_function_attributes[] = { static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size) { struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) return NULL; @@ -377,10 +378,9 @@ static void audio_send(struct audio_dev *audio) /* compute number of frames to send */ now = ktime_get(); - msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time); - do_div(msecs, 1000000); - frames = msecs * SAMPLE_RATE; - do_div(frames, 1000); + msecs = div_s64((ktime_to_ns(now) - ktime_to_ns(audio->start_time)), + 1000000); + frames = div_s64((msecs * SAMPLE_RATE), 1000); /* Readjust our frames_sent if we fall too far behind. * If we get too far behind it is better to drop some frames than diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 739cf9790cd4..ab44bd316217 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1581,6 +1581,8 @@ static int functionfs_init(void) pr_err("failed registering file system (%d)\n", ret); ffs_ipc_log = ipc_log_context_create(NUM_PAGES, "f_fs", 0); + if (IS_ERR_OR_NULL(ffs_ipc_log)) + ffs_ipc_log = NULL; return ret; } @@ -1591,6 +1593,11 @@ static void functionfs_cleanup(void) pr_info("unloading\n"); unregister_filesystem(&ffs_fs_type); + + if (ffs_ipc_log) { + ipc_log_context_destroy(ffs_ipc_log); + ffs_ipc_log = NULL; + } } diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 2f08a6c9d476..e46edc83430c 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -40,7 +40,6 @@ MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data"); static struct workqueue_struct *ipa_usb_wq; -static void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis); static void ipa_disconnect_handler(struct gsi_data_port *d_port); static int gsi_ctrl_send_notification(struct f_gsi *gsi); static int gsi_alloc_trb_buffer(struct f_gsi *gsi); @@ -48,6 +47,20 @@ static void gsi_free_trb_buffer(struct f_gsi *gsi); static struct gsi_ctrl_pkt *gsi_ctrl_pkt_alloc(unsigned len, gfp_t flags); static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt); +static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f) +{ + bool remote_wakeup_allowed; + + if (f->config->cdev->gadget->speed == USB_SPEED_SUPER) + remote_wakeup_allowed = f->func_wakeup_allowed; + else + remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; + + log_event_dbg("%s: remote_wakeup_allowed:%s", __func__, + remote_wakeup_allowed ? "true" : "false"); + return remote_wakeup_allowed; +} + void post_event(struct gsi_data_port *port, u8 event) { unsigned long flags; @@ -477,16 +490,22 @@ static void ipa_disconnect_handler(struct gsi_data_port *d_port) log_event_dbg("%s: EP Disable for data", __func__); - /* Block doorbell to GSI to avoid USB wrapper from - * ringing doorbell in case IPA clocks are OFF - */ - usb_gsi_ep_op(d_port->in_ep, (void *)&block_db, + if (gsi->d_port.in_ep) { + /* + * Block doorbell to GSI to avoid USB wrapper from + * ringing doorbell in case IPA clocks are OFF. + */ + usb_gsi_ep_op(d_port->in_ep, (void *)&block_db, GSI_EP_OP_SET_CLR_BLOCK_DBL); + gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc; + usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE); + } - usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE); - - if (gsi->d_port.out_ep) + if (gsi->d_port.out_ep) { + gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc; usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_DISABLE); + } + gsi->d_port.net_ready_trigger = false; } @@ -523,19 +542,21 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port) int ret = 0; bool block_db, f_suspend; struct f_gsi *gsi = d_port_to_gsi(d_port); + struct usb_function *f = &gsi->function; + + f_suspend = f->func_wakeup_allowed; + log_event_dbg("%s: f_suspend:%d", __func__, f_suspend); - f_suspend = gsi->function.func_wakeup_allowed; if (!usb_gsi_ep_op(gsi->d_port.in_ep, (void *) &f_suspend, GSI_EP_OP_CHECK_FOR_SUSPEND)) { ret = -EFAULT; goto done; } - log_event_dbg("%s: Calling xdci_suspend", __func__); + log_event_dbg("%s: Calling xdci_suspend", __func__); ret = ipa_usb_xdci_suspend(gsi->d_port.out_channel_handle, gsi->d_port.in_channel_handle, gsi->prot_id, - true); - + usb_gsi_remote_wakeup_allowed(f)); if (!ret) { d_port->sm_state = STATE_SUSPENDED; log_event_dbg("%s: STATE SUSPENDED", __func__); @@ -553,10 +574,8 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port) log_event_err("%s: Error %d for %d", __func__, ret, gsi->prot_id); } - - log_event_dbg("%s: xdci_suspend ret %d", __func__, ret); - done: + log_event_dbg("%s: xdci_suspend ret %d", __func__, ret); return ret; } @@ -591,7 +610,6 @@ static void ipa_work_handler(struct work_struct *w) struct device *dev; struct device *gad_dev; struct f_gsi *gsi; - bool block_db; event = read_event(d_port); @@ -653,6 +671,29 @@ static void ipa_work_handler(struct work_struct *w) __func__); break; } + + /* + * Update desc and reconfigure USB GSI OUT and IN + * endpoint for RNDIS Adaptor enable case. + */ + if (d_port->out_ep && !d_port->out_ep->desc && + gsi->out_ep_desc_backup) { + d_port->out_ep->desc = gsi->out_ep_desc_backup; + d_port->out_ep->ep_intr_num = 1; + log_event_dbg("%s: OUT ep_op_config", __func__); + usb_gsi_ep_op(d_port->out_ep, + &d_port->out_request, GSI_EP_OP_CONFIG); + } + + if (d_port->in_ep && !d_port->in_ep->desc && + gsi->in_ep_desc_backup) { + d_port->in_ep->desc = gsi->in_ep_desc_backup; + d_port->in_ep->ep_intr_num = 2; + log_event_dbg("%s: IN ep_op_config", __func__); + usb_gsi_ep_op(d_port->in_ep, + &d_port->in_request, GSI_EP_OP_CONFIG); + } + ipa_connect_channels(d_port); ipa_data_path_enable(d_port); d_port->sm_state = STATE_CONNECTED; @@ -714,15 +755,7 @@ static void ipa_work_handler(struct work_struct *w) if (event == EVT_HOST_NRDY) { log_event_dbg("%s: ST_CON_HOST_NRDY\n", __func__); - block_db = true; - /* stop USB ringing doorbell to GSI(OUT_EP) */ - usb_gsi_ep_op(d_port->in_ep, (void *)&block_db, - GSI_EP_OP_SET_CLR_BLOCK_DBL); - gsi_rndis_ipa_reset_trigger(gsi); - usb_gsi_ep_op(d_port->in_ep, NULL, - GSI_EP_OP_ENDXFER); - usb_gsi_ep_op(d_port->out_ep, NULL, - GSI_EP_OP_ENDXFER); + ipa_disconnect_handler(d_port); } ipa_disconnect_work_handler(d_port); @@ -1086,9 +1119,9 @@ static ssize_t gsi_ctrl_dev_write(struct file *fp, const char __user *buf, list_add_tail(&cpkt->list, &c_port->cpkt_resp_q); spin_unlock_irqrestore(&c_port->lock, flags); - ret = gsi_ctrl_send_notification(gsi); + if (!gsi_ctrl_send_notification(gsi)) + c_port->modem_to_host++; - c_port->modem_to_host++; log_event_dbg("Exit %zu", count); return ret ? ret : count; @@ -1345,26 +1378,6 @@ static void gsi_rndis_open(struct f_gsi *rndis) rndis_signal_connect(rndis->params); } -static void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis) -{ - unsigned long flags; - - if (!rndis) { - log_event_err("%s: gsi prot ctx is %pK", __func__, rndis); - return; - } - - spin_lock_irqsave(&rndis->d_port.lock, flags); - if (!rndis) { - log_event_err("%s: No RNDIS instance", __func__); - spin_unlock_irqrestore(&rndis->d_port.lock, flags); - return; - } - - rndis->d_port.net_ready_trigger = false; - spin_unlock_irqrestore(&rndis->d_port.lock, flags); -} - void gsi_rndis_flow_ctrl_enable(bool enable, struct rndis_params *param) { struct f_gsi *rndis = param->v; @@ -1392,33 +1405,18 @@ static int queue_notification_request(struct f_gsi *gsi) { int ret; unsigned long flags; - struct usb_cdc_notification *event; - struct gsi_ctrl_pkt *cpkt; ret = usb_func_ep_queue(&gsi->function, gsi->c_port.notify, gsi->c_port.notify_req, GFP_ATOMIC); - if (ret == -ENOTSUPP || (ret < 0 && ret != -EAGAIN)) { + if (ret < 0) { spin_lock_irqsave(&gsi->c_port.lock, flags); gsi->c_port.notify_req_queued = false; - /* check if device disconnected while we dropped lock */ - if (atomic_read(&gsi->connected) && - !list_empty(&gsi->c_port.cpkt_resp_q)) { - cpkt = list_first_entry(&gsi->c_port.cpkt_resp_q, - struct gsi_ctrl_pkt, list); - list_del(&cpkt->list); - log_event_err("%s: drop ctrl pkt of len %d error %d", - __func__, cpkt->len, ret); - gsi_ctrl_pkt_free(cpkt); - } - gsi->c_port.cpkt_drop_cnt++; spin_unlock_irqrestore(&gsi->c_port.lock, flags); - } else { - ret = 0; - event = gsi->c_port.notify_req->buf; - log_event_dbg("%s: Queued Notify type %02x", __func__, - event->bNotificationType); } + log_event_dbg("%s: ret:%d req_queued:%d", + __func__, ret, gsi->c_port.notify_req_queued); + return ret; } @@ -2130,7 +2128,6 @@ static void gsi_suspend(struct usb_function *f) { bool block_db; struct f_gsi *gsi = func_to_gsi(f); - bool remote_wakeup_allowed; /* Check if function is already suspended in gsi_func_suspend() */ if (f->func_is_suspended) { @@ -2138,49 +2135,17 @@ static void gsi_suspend(struct usb_function *f) return; } - if (f->config->cdev->gadget->speed == USB_SPEED_SUPER) - remote_wakeup_allowed = f->func_wakeup_allowed; - else - remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; - - log_event_info("%s: remote_wakeup_allowed %d", - __func__, remote_wakeup_allowed); - - if (!remote_wakeup_allowed) { - if (gsi->prot_id == IPA_USB_RNDIS) - rndis_flow_control(gsi->params, true); - /* - * When remote wakeup is disabled, IPA is disconnected - * because it cannot send new data until the USB bus is - * resumed. Endpoint descriptors info is saved before it - * gets reset by the BAM disconnect API. This lets us - * restore this info when the USB bus is resumed. - */ - if (gsi->d_port.in_ep) - gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc; - if (gsi->d_port.out_ep) - gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc; - - ipa_disconnect_handler(&gsi->d_port); - - post_event(&gsi->d_port, EVT_DISCONNECTED); - queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); - log_event_dbg("%s: Disconnecting", __func__); - } else { - block_db = true; - usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db, - GSI_EP_OP_SET_CLR_BLOCK_DBL); - post_event(&gsi->d_port, EVT_SUSPEND); - queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); - } - + block_db = true; + usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db, + GSI_EP_OP_SET_CLR_BLOCK_DBL); + post_event(&gsi->d_port, EVT_SUSPEND); + queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); log_event_dbg("gsi suspended"); } static void gsi_resume(struct usb_function *f) { struct f_gsi *gsi = func_to_gsi(f); - bool remote_wakeup_allowed; struct usb_composite_dev *cdev = f->config->cdev; log_event_dbg("%s", __func__); @@ -2193,49 +2158,24 @@ static void gsi_resume(struct usb_function *f) f->func_is_suspended) return; - if (f->config->cdev->gadget->speed == USB_SPEED_SUPER) - remote_wakeup_allowed = f->func_wakeup_allowed; - else - remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; - if (gsi->c_port.notify && !gsi->c_port.notify->desc) config_ep_by_speed(cdev->gadget, f, gsi->c_port.notify); /* Check any pending cpkt, and queue immediately on resume */ gsi_ctrl_send_notification(gsi); - if (!remote_wakeup_allowed) { - - /* Configure EPs for GSI */ - if (gsi->d_port.out_ep) { - gsi->d_port.out_ep->desc = gsi->out_ep_desc_backup; - gsi->d_port.out_ep->ep_intr_num = 1; - usb_gsi_ep_op(gsi->d_port.out_ep, - &gsi->d_port.out_request, GSI_EP_OP_CONFIG); - } - gsi->d_port.in_ep->desc = gsi->in_ep_desc_backup; - if (gsi->prot_id != IPA_USB_DIAG) - gsi->d_port.in_ep->ep_intr_num = 2; - else - gsi->d_port.in_ep->ep_intr_num = 3; - - usb_gsi_ep_op(gsi->d_port.in_ep, &gsi->d_port.in_request, - GSI_EP_OP_CONFIG); - post_event(&gsi->d_port, EVT_CONNECT_IN_PROGRESS); - - /* - * Linux host does not send RNDIS_MSG_INIT or non-zero - * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume. - * Trigger state machine explicitly on resume. - */ - if (gsi->prot_id == IPA_USB_RNDIS) - rndis_flow_control(gsi->params, false); - } else - post_event(&gsi->d_port, EVT_RESUMED); + /* + * Linux host does not send RNDIS_MSG_INIT or non-zero + * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume. + * Trigger state machine explicitly on resume. + */ + if (gsi->prot_id == IPA_USB_RNDIS && + !usb_gsi_remote_wakeup_allowed(f)) + rndis_flow_control(gsi->params, false); + post_event(&gsi->d_port, EVT_RESUMED); queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); - log_event_dbg("%s: completed", __func__); } @@ -3132,7 +3072,7 @@ MODULE_DESCRIPTION("GSI function driver"); static int fgsi_init(void) { ipa_usb_wq = alloc_workqueue("k_ipa_usb", - WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE, 1); if (!ipa_usb_wq) { log_event_err("Failed to create workqueue for IPA"); return -ENOMEM; diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 97d86b6ac69b..e309dec68a75 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -2289,8 +2289,11 @@ reset: } common->running = 0; - if (!new_fsg || rc) + if (!new_fsg || rc) { + /* allow usb LPM after eps are disabled */ + usb_gadget_autopm_put_async(common->gadget); return rc; + } common->fsg = new_fsg; fsg = common->fsg; @@ -2333,6 +2336,9 @@ reset: bh->outreq->complete = bulk_out_complete; } + /* prevents usb LPM until thread runs to completion */ + usb_gadget_autopm_get_noresume(common->gadget); + common->running = 1; for (i = 0; i < ARRAY_SIZE(common->luns); ++i) if (common->luns[i]) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 8919cc26b98e..5bcff5d2cd8d 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1160,7 +1160,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) card = midi->card; midi->card = NULL; if (card) - snd_card_free(card); + snd_card_free_when_closed(card); usb_free_all_descriptors(f); } diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index 972ea68b16e4..bf7460f25e61 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -137,6 +137,7 @@ struct mtp_dev { unsigned dbg_read_index; unsigned dbg_write_index; bool is_ptp; + struct mutex read_mutex; }; static struct usb_interface_descriptor mtp_interface_desc = { @@ -412,6 +413,7 @@ static inline struct mtp_dev *func_to_mtp(struct usb_function *f) static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size) { struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) return NULL; @@ -640,11 +642,18 @@ static ssize_t mtp_read(struct file *fp, char __user *buf, dev->state = STATE_BUSY; spin_unlock_irq(&dev->lock); + mutex_lock(&dev->read_mutex); + if (dev->state == STATE_OFFLINE) { + r = -EIO; + mutex_unlock(&dev->read_mutex); + goto done; + } requeue_req: /* queue a request */ req = dev->rx_req[0]; req->length = len; dev->rx_done = 0; + mutex_unlock(&dev->read_mutex); ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); if (ret < 0) { r = -EIO; @@ -670,6 +679,7 @@ requeue_req: usb_ep_dequeue(dev->ep_out, req); goto done; } + mutex_lock(&dev->read_mutex); if (dev->state == STATE_BUSY) { /* If we got a 0-len packet, throw it back and try again. */ if (req->actual == 0) @@ -683,6 +693,7 @@ requeue_req: } else r = -EIO; + mutex_unlock(&dev->read_mutex); done: spin_lock_irq(&dev->lock); if (dev->state == STATE_CANCELED) @@ -937,6 +948,12 @@ static void receive_file_work(struct work_struct *data) while (count > 0 || write_req) { if (count > 0) { + mutex_lock(&dev->read_mutex); + if (dev->state == STATE_OFFLINE) { + r = -EIO; + mutex_unlock(&dev->read_mutex); + break; + } /* queue a request */ read_req = dev->rx_req[cur_buf]; cur_buf = (cur_buf + 1) % RX_REQ_MAX; @@ -945,6 +962,7 @@ static void receive_file_work(struct work_struct *data) read_req->length = mtp_rx_req_len; dev->rx_done = 0; + mutex_unlock(&dev->read_mutex); ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL); if (ret < 0) { r = -EIO; @@ -957,15 +975,23 @@ static void receive_file_work(struct work_struct *data) if (write_req) { DBG(cdev, "rx %pK %d\n", write_req, write_req->actual); start_time = ktime_get(); + mutex_lock(&dev->read_mutex); + if (dev->state == STATE_OFFLINE) { + r = -EIO; + mutex_unlock(&dev->read_mutex); + break; + } ret = vfs_write(filp, write_req->buf, write_req->actual, &offset); DBG(cdev, "vfs_write %d\n", ret); if (ret != write_req->actual) { r = -EIO; + mutex_unlock(&dev->read_mutex); if (dev->state != STATE_OFFLINE) dev->state = STATE_ERROR; break; } + mutex_unlock(&dev->read_mutex); dev->perf[dev->dbg_write_index].vfs_wtime = ktime_to_us(ktime_sub(ktime_get(), start_time)); dev->perf[dev->dbg_write_index].vfs_wbytes = ret; @@ -989,6 +1015,12 @@ static void receive_file_work(struct work_struct *data) break; } + mutex_lock(&dev->read_mutex); + if (dev->state == STATE_OFFLINE) { + r = -EIO; + mutex_unlock(&dev->read_mutex); + break; + } /* Check if we aligned the size due to MTU constraint */ if (count < read_req->length) read_req->actual = (read_req->actual > count ? @@ -1009,6 +1041,7 @@ static void receive_file_work(struct work_struct *data) write_req = read_req; read_req = NULL; + mutex_unlock(&dev->read_mutex); } } @@ -1352,6 +1385,7 @@ static int mtp_ctrlrequest(struct usb_composite_dev *cdev, } else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS && w_index == 0 && w_value == 0) { struct mtp_device_status *status = cdev->req->buf; + status->wLength = __constant_cpu_to_le16(sizeof(*status)); @@ -1374,6 +1408,7 @@ static int mtp_ctrlrequest(struct usb_composite_dev *cdev, /* respond with data transfer or status phase? */ if (value >= 0) { int rc; + cdev->req->zero = value < w_length; cdev->req->length = value; rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); @@ -1469,12 +1504,14 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) struct usb_request *req; int i; + mutex_lock(&dev->read_mutex); while ((req = mtp_req_get(dev, &dev->tx_idle))) mtp_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) mtp_request_free(dev->rx_req[i], dev->ep_out); while ((req = mtp_req_get(dev, &dev->intr_idle))) mtp_request_free(req, dev->ep_intr); + mutex_unlock(&dev->read_mutex); dev->state = STATE_OFFLINE; dev->is_ptp = false; kfree(f->os_desc_table); @@ -1733,6 +1770,7 @@ static struct mtp_instance *to_mtp_instance(struct config_item *item) static void mtp_attr_release(struct config_item *item) { struct mtp_instance *fi_mtp = to_mtp_instance(item); + usb_put_function_instance(&fi_mtp->func_inst); } @@ -1853,7 +1891,7 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi, pr_err("\t2: Create MTP function\n"); pr_err("\t3: Create and symlink PTP function" " with a gadget configuration\n"); - return NULL; + return ERR_PTR(-EINVAL); /* Invalid Configuration */ } dev = fi_mtp->dev; @@ -1877,6 +1915,7 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi, dev->is_ptp = !mtp_config; fi->f = &dev->function; + mutex_init(&dev->read_mutex); return &dev->function; } EXPORT_SYMBOL_GPL(function_alloc_mtp_ptp); diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c index 4975ba474f97..5718f71bcdea 100644 --- a/drivers/usb/gadget/function/u_data_ipa.c +++ b/drivers/usb/gadget/function/u_data_ipa.c @@ -428,6 +428,7 @@ static void ipa_data_connect_work(struct work_struct *w) if (!gadget) { spin_unlock_irqrestore(&port->port_lock, flags); + usb_gadget_autopm_put_async(port->gadget); pr_err("%s: gport is NULL.\n", __func__); return; } @@ -691,6 +692,8 @@ disconnect_usb_bam_ipa_out: usb_bam_disconnect_ipa(port->usb_bam_type, &port->ipa_params); is_ipa_disconnected = true; } + if (port->func_type == USB_IPA_FUNC_RMNET) + teth_bridge_disconnect(port->ipa_params.src_client); unconfig_msm_ep_in: spin_lock_irqsave(&port->port_lock, flags); /* check if USB cable is disconnected or not */ @@ -713,6 +716,7 @@ out: spin_lock_irqsave(&port->port_lock, flags); port->is_connected = false; spin_unlock_irqrestore(&port->port_lock, flags); + usb_gadget_autopm_put_async(port->gadget); } /** diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 81ce22e91883..43e054666b68 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -743,7 +743,8 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, /* throttle highspeed IRQ rate back slightly */ if (gadget_is_dualspeed(dev->gadget) && - (dev->gadget->speed == USB_SPEED_HIGH)) { + (dev->gadget->speed == USB_SPEED_HIGH) && + !list_empty(&dev->tx_reqs)) { dev->tx_qlen++; if (dev->tx_qlen == (dev->qmult/2)) { req->no_interrupt = 0; diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c index 389c1f3d0fee..b13f839e7368 100644 --- a/drivers/usb/gadget/functions.c +++ b/drivers/usb/gadget/functions.c @@ -58,7 +58,7 @@ struct usb_function *usb_get_function(struct usb_function_instance *fi) struct usb_function *f; f = fi->fd->alloc_func(fi); - if ((f == NULL) || IS_ERR(f)) + if (IS_ERR(f)) return f; f->fi = fi; return f; diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index c73689b72f95..b38a33584d4a 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -1878,11 +1878,8 @@ static int qe_get_frame(struct usb_gadget *gadget) tmp = in_be16(&udc->usb_param->frame_n); if (tmp & 0x8000) - tmp = tmp & 0x07ff; - else - tmp = -EINVAL; - - return (int)tmp; + return tmp & 0x07ff; + return -EINVAL; } static int fsl_qe_start(struct usb_gadget *gadget, diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 9094bca1bac6..c0ce3db6a7c0 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -72,7 +72,7 @@ static const char hcd_name [] = "ohci_hcd"; #define STATECHANGE_DELAY msecs_to_jiffies(300) -#define IO_WATCHDOG_DELAY msecs_to_jiffies(250) +#define IO_WATCHDOG_DELAY msecs_to_jiffies(275) #include "ohci.h" #include "pci-quirks.h" diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 963867c2c1d5..cf147ccac7d3 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -45,6 +45,7 @@ #define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 +#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI 0x9cb1 #define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5 #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f @@ -154,7 +155,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_SPURIOUS_REBOOT; } if (pdev->vendor == PCI_VENDOR_ID_INTEL && - pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) { + (pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI)) { xhci->quirks |= XHCI_SPURIOUS_REBOOT; xhci->quirks |= XHCI_SPURIOUS_WAKEUP; } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index fe529f9f7d28..98d01acc8b11 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -846,6 +846,10 @@ 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 " @@ -899,7 +903,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) spin_unlock_irqrestore(&xhci->lock, flags); xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Calling usb_hc_died()"); - usb_hc_died(xhci_to_hcd(xhci)->primary_hcd); + usb_hc_died(xhci_to_hcd(xhci)); xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "xHCI host controller is dead."); } diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 7771be3ac178..4dd531ac5a7f 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -898,24 +898,6 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval; dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval; - /* we can register the device now, as it is ready */ - usb_set_intfdata (interface, dev); - - retval = usb_register_dev (interface, &tower_class); - - if (retval) { - /* something prevented us from registering this driver */ - dev_err(idev, "Not able to get a minor for this device.\n"); - usb_set_intfdata (interface, NULL); - goto error; - } - dev->minor = interface->minor; - - /* let the user know what node this device is now attached to */ - dev_info(&interface->dev, "LEGO USB Tower #%d now attached to major " - "%d minor %d\n", (dev->minor - LEGO_USB_TOWER_MINOR_BASE), - USB_MAJOR, dev->minor); - /* get the firmware version and log it */ result = usb_control_msg (udev, usb_rcvctrlpipe(udev, 0), @@ -936,6 +918,23 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device get_version_reply.minor, le16_to_cpu(get_version_reply.build_no)); + /* we can register the device now, as it is ready */ + usb_set_intfdata (interface, dev); + + retval = usb_register_dev (interface, &tower_class); + + if (retval) { + /* something prevented us from registering this driver */ + dev_err(idev, "Not able to get a minor for this device.\n"); + usb_set_intfdata (interface, NULL); + goto error; + } + dev->minor = interface->minor; + + /* let the user know what node this device is now attached to */ + dev_info(&interface->dev, "LEGO USB Tower #%d now attached to major " + "%d minor %d\n", (dev->minor - LEGO_USB_TOWER_MINOR_BASE), + USB_MAJOR, dev->minor); exit: return retval; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 59a63a0b7985..e0a083f6ab68 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -661,7 +661,7 @@ static int musb_tx_dma_set_mode_mentor(struct dma_controller *dma, csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE); csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */ } - channel->desired_mode = mode; + channel->desired_mode = *mode; musb_writew(epio, MUSB_TXCSR, csr); return 0; @@ -2008,10 +2008,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) qh->offset, urb->transfer_buffer_length); - done = musb_rx_dma_in_inventra_cppi41(c, hw_ep, qh, - urb, xfer_len, - iso_err); - if (done) + if (musb_rx_dma_in_inventra_cppi41(c, hw_ep, qh, urb, + xfer_len, iso_err)) goto finish; else dev_err(musb->controller, "error: rx_dma failed\n"); diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index ea278781440c..935bd0778bfb 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -665,7 +665,7 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, rx_msg->type = PD_MSG_HDR_TYPE(header); rx_msg->len = PD_MSG_HDR_COUNT(header); - memcpy(&rx_msg->payload, buf, len); + memcpy(&rx_msg->payload, buf, min(len, sizeof(rx_msg->payload))); spin_lock_irqsave(&pd->rx_lock, flags); list_add_tail(&rx_msg->entry, &pd->rx_q); diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index d4be5d594896..28965ef4f824 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -282,9 +282,16 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) if (usbhs_mod_is_host(priv)) usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); - usbhs_write(priv, BRDYSTS, ~irq_state.brdysts); + /* + * The driver should not clear the xxxSTS after the line of + * "call irq callback functions" because each "if" statement is + * possible to call the callback function for avoiding any side effects. + */ + if (irq_state.intsts0 & BRDY) + usbhs_write(priv, BRDYSTS, ~irq_state.brdysts); usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts); - usbhs_write(priv, BEMPSTS, ~irq_state.bempsts); + if (irq_state.intsts0 & BEMP) + usbhs_write(priv, BEMPSTS, ~irq_state.bempsts); /* * call irq callback functions diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index a2b43a6e7fa7..fe7452f0f38a 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -117,6 +117,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */ { USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */ { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ + { USB_DEVICE(0x10C4, 0x8470) }, /* Juniper Networks BX Series System Console */ { USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */ { USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */ { USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */ @@ -129,6 +130,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */ { USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */ { USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */ + { USB_DEVICE(0x10C4, 0x8962) }, /* Brim Brothers charging dock */ { USB_DEVICE(0x10C4, 0x8977) }, /* CEL MeshWorks DevKit Device */ { USB_DEVICE(0x10C4, 0x8998) }, /* KCF Technologies PRN */ { USB_DEVICE(0x10C4, 0x8A2A) }, /* HubZ dual ZigBee and Z-Wave dongle */ @@ -784,7 +786,7 @@ static void cp210x_set_termios(struct tty_struct *tty, } else { modem_ctl[0] &= ~0x7B; modem_ctl[0] |= 0x01; - modem_ctl[1] |= 0x40; + modem_ctl[1] = 0x40; dev_dbg(dev, "%s - flow control = NONE\n", __func__); } @@ -844,7 +846,9 @@ static int cp210x_tiocmget(struct tty_struct *tty) unsigned int control; int result; - cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1); + result = cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1); + if (result) + return result; result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0) |((control & CONTROL_RTS) ? TIOCM_RTS : 0) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 8c48c9d83d48..d3d6ec455151 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -986,7 +986,8 @@ static const struct usb_device_id id_table_combined[] = { /* ekey Devices */ { USB_DEVICE(FTDI_VID, FTDI_EKEY_CONV_USB_PID) }, /* Infineon Devices */ - { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_TC1798_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_TC2X7_PID, 1) }, /* GE Healthcare devices */ { USB_DEVICE(GE_HEALTHCARE_VID, GE_HEALTHCARE_NEMO_TRACKER_PID) }, /* Active Research (Actisense) devices */ @@ -1011,6 +1012,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(ICPDAS_VID, ICPDAS_I7561U_PID) }, { USB_DEVICE(ICPDAS_VID, ICPDAS_I7563U_PID) }, { USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) }, + { USB_DEVICE(TI_VID, TI_CC3200_LAUNCHPAD_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index f87a938cf005..48ee04c94a75 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -596,6 +596,12 @@ #define STK541_PID 0x2109 /* Zigbee Controller */ /* + * Texas Instruments + */ +#define TI_VID 0x0451 +#define TI_CC3200_LAUNCHPAD_PID 0xC32A /* SimpleLink Wi-Fi CC3200 LaunchPad */ + +/* * Blackfin gnICE JTAG * http://docs.blackfin.uclinux.org/doku.php?id=hw:jtag:gnice */ @@ -626,8 +632,9 @@ /* * Infineon Technologies */ -#define INFINEON_VID 0x058b -#define INFINEON_TRIBOARD_PID 0x0028 /* DAS JTAG TriBoard TC1798 V1.0 */ +#define INFINEON_VID 0x058b +#define INFINEON_TRIBOARD_TC1798_PID 0x0028 /* DAS JTAG TriBoard TC1798 V1.0 */ +#define INFINEON_TRIBOARD_TC2X7_PID 0x0043 /* DAS JTAG TriBoard TC2X7 V1.0 */ /* * Acton Research Corp. diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c index a204782ae530..e98b6e57b703 100644 --- a/drivers/usb/serial/usb-serial-simple.c +++ b/drivers/usb/serial/usb-serial-simple.c @@ -54,7 +54,8 @@ DEVICE(funsoft, FUNSOFT_IDS); /* Infineon Flashloader driver */ #define FLASHLOADER_IDS() \ { USB_DEVICE_INTERFACE_CLASS(0x058b, 0x0041, USB_CLASS_CDC_DATA) }, \ - { USB_DEVICE(0x8087, 0x0716) } + { USB_DEVICE(0x8087, 0x0716) }, \ + { USB_DEVICE(0x8087, 0x0801) } DEVICE(flashloader, FLASHLOADER_IDS); /* Google Serial USB SubClass */ diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index a0ca291bc07f..e7e29c797824 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1077,7 +1077,8 @@ static int usb_serial_probe(struct usb_interface *interface, serial->disconnected = 0; - usb_serial_console_init(serial->port[0]->minor); + if (num_ports > 0) + usb_serial_console_init(serial->port[0]->minor); exit: module_put(type->driver.owner); return 0; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 5e67f63b2e46..02f86dd1a340 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -919,10 +919,15 @@ int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us) /* COMMAND STAGE */ /* let's send the command via the control pipe */ + /* + * Command is sometime (f.e. after scsi_eh_prep_cmnd) on the stack. + * Stack may be vmallocated. So no DMA for us. Make a copy. + */ + memcpy(us->iobuf, srb->cmnd, srb->cmd_len); result = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, - us->ifnum, srb->cmnd, srb->cmd_len); + us->ifnum, us->iobuf, srb->cmd_len); /* check the return code for the command */ usb_stor_dbg(us, "Call to usb_stor_ctrl_transfer() returned %d\n", diff --git a/drivers/uwb/lc-rc.c b/drivers/uwb/lc-rc.c index d059ad4d0dbd..97ee1b46db69 100644 --- a/drivers/uwb/lc-rc.c +++ b/drivers/uwb/lc-rc.c @@ -56,8 +56,11 @@ static struct uwb_rc *uwb_rc_find_by_index(int index) struct uwb_rc *rc = NULL; dev = class_find_device(&uwb_rc_class, NULL, &index, uwb_rc_index_match); - if (dev) + if (dev) { rc = dev_get_drvdata(dev); + put_device(dev); + } + return rc; } @@ -467,7 +470,9 @@ struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *target_rc) if (dev) { rc = dev_get_drvdata(dev); __uwb_rc_get(rc); + put_device(dev); } + return rc; } EXPORT_SYMBOL_GPL(__uwb_rc_try_get); @@ -520,8 +525,11 @@ struct uwb_rc *uwb_rc_get_by_grandpa(const struct device *grandpa_dev) dev = class_find_device(&uwb_rc_class, NULL, grandpa_dev, find_rc_grandpa); - if (dev) + if (dev) { rc = dev_get_drvdata(dev); + put_device(dev); + } + return rc; } EXPORT_SYMBOL_GPL(uwb_rc_get_by_grandpa); @@ -553,8 +561,10 @@ struct uwb_rc *uwb_rc_get_by_dev(const struct uwb_dev_addr *addr) struct uwb_rc *rc = NULL; dev = class_find_device(&uwb_rc_class, NULL, addr, find_rc_dev); - if (dev) + if (dev) { rc = dev_get_drvdata(dev); + put_device(dev); + } return rc; } diff --git a/drivers/uwb/pal.c b/drivers/uwb/pal.c index c1304b8d4985..678e93741ae1 100644 --- a/drivers/uwb/pal.c +++ b/drivers/uwb/pal.c @@ -97,6 +97,8 @@ static bool uwb_rc_class_device_exists(struct uwb_rc *target_rc) dev = class_find_device(&uwb_rc_class, NULL, target_rc, find_rc); + put_device(dev); + return (dev != NULL); } diff --git a/drivers/video/adf/adf_fops.c b/drivers/video/adf/adf_fops.c index 8726617f73ab..705411bfaebb 100644 --- a/drivers/video/adf/adf_fops.c +++ b/drivers/video/adf/adf_fops.c @@ -132,7 +132,7 @@ static int adf_eng_get_data(struct adf_overlay_engine *eng, eng->ops->n_supported_formats)); mutex_lock(&dev->client_lock); - ret = adf_obj_copy_custom_data_to_user(&eng->base, arg->custom_data, + ret = adf_obj_copy_custom_data_to_user(&eng->base, data.custom_data, &data.custom_data_size); mutex_unlock(&dev->client_lock); @@ -144,7 +144,7 @@ static int adf_eng_get_data(struct adf_overlay_engine *eng, goto done; } - if (supported_formats && copy_to_user(arg->supported_formats, + if (supported_formats && copy_to_user(data.supported_formats, supported_formats, n_supported_formats * sizeof(supported_formats[0]))) ret = -EFAULT; @@ -220,56 +220,45 @@ static int adf_device_post_config(struct adf_device *dev, int complete_fence_fd; struct adf_buffer *bufs = NULL; struct adf_interface **intfs = NULL; - size_t n_intfs, n_bufs, i; + struct adf_post_config data; + size_t i; void *custom_data = NULL; - size_t custom_data_size; int ret = 0; + if (copy_from_user(&data, arg, sizeof(data))) + return -EFAULT; + complete_fence_fd = get_unused_fd_flags(O_CLOEXEC); if (complete_fence_fd < 0) return complete_fence_fd; - if (get_user(n_intfs, &arg->n_interfaces)) { - ret = -EFAULT; - goto err_get_user; - } - - if (n_intfs > ADF_MAX_INTERFACES) { + if (data.n_interfaces > ADF_MAX_INTERFACES) { ret = -EINVAL; goto err_get_user; } - if (get_user(n_bufs, &arg->n_bufs)) { - ret = -EFAULT; - goto err_get_user; - } - - if (n_bufs > ADF_MAX_BUFFERS) { + if (data.n_bufs > ADF_MAX_BUFFERS) { ret = -EINVAL; goto err_get_user; } - if (get_user(custom_data_size, &arg->custom_data_size)) { - ret = -EFAULT; - goto err_get_user; - } - - if (custom_data_size > ADF_MAX_CUSTOM_DATA_SIZE) { + if (data.custom_data_size > ADF_MAX_CUSTOM_DATA_SIZE) { ret = -EINVAL; goto err_get_user; } - if (n_intfs) { - intfs = kmalloc(sizeof(intfs[0]) * n_intfs, GFP_KERNEL); + if (data.n_interfaces) { + intfs = kmalloc(sizeof(intfs[0]) * data.n_interfaces, + GFP_KERNEL); if (!intfs) { ret = -ENOMEM; goto err_get_user; } } - for (i = 0; i < n_intfs; i++) { + for (i = 0; i < data.n_interfaces; i++) { u32 intf_id; - if (get_user(intf_id, &arg->interfaces[i])) { + if (get_user(intf_id, &data.interfaces[i])) { ret = -EFAULT; goto err_get_user; } @@ -281,31 +270,31 @@ static int adf_device_post_config(struct adf_device *dev, } } - if (n_bufs) { - bufs = kzalloc(sizeof(bufs[0]) * n_bufs, GFP_KERNEL); + if (data.n_bufs) { + bufs = kzalloc(sizeof(bufs[0]) * data.n_bufs, GFP_KERNEL); if (!bufs) { ret = -ENOMEM; goto err_get_user; } } - for (i = 0; i < n_bufs; i++) { - ret = adf_buffer_import(dev, &arg->bufs[i], &bufs[i]); + for (i = 0; i < data.n_bufs; i++) { + ret = adf_buffer_import(dev, &data.bufs[i], &bufs[i]); if (ret < 0) { memset(&bufs[i], 0, sizeof(bufs[i])); goto err_import; } } - if (custom_data_size) { - custom_data = kzalloc(custom_data_size, GFP_KERNEL); + if (data.custom_data_size) { + custom_data = kzalloc(data.custom_data_size, GFP_KERNEL); if (!custom_data) { ret = -ENOMEM; goto err_import; } - if (copy_from_user(custom_data, arg->custom_data, - custom_data_size)) { + if (copy_from_user(custom_data, data.custom_data, + data.custom_data_size)) { ret = -EFAULT; goto err_import; } @@ -316,8 +305,8 @@ static int adf_device_post_config(struct adf_device *dev, goto err_import; } - complete_fence = adf_device_post_nocopy(dev, intfs, n_intfs, bufs, - n_bufs, custom_data, custom_data_size); + complete_fence = adf_device_post_nocopy(dev, intfs, data.n_interfaces, + bufs, data.n_bufs, custom_data, data.custom_data_size); if (IS_ERR(complete_fence)) { ret = PTR_ERR(complete_fence); goto err_import; @@ -327,7 +316,7 @@ static int adf_device_post_config(struct adf_device *dev, return 0; err_import: - for (i = 0; i < n_bufs; i++) + for (i = 0; i < data.n_bufs; i++) adf_buffer_cleanup(&bufs[i]); err_get_user: @@ -481,19 +470,19 @@ static int adf_device_get_data(struct adf_device *dev, data.n_allowed_attachments); mutex_lock(&dev->client_lock); - ret = adf_obj_copy_custom_data_to_user(&dev->base, arg->custom_data, + ret = adf_obj_copy_custom_data_to_user(&dev->base, data.custom_data, &data.custom_data_size); mutex_unlock(&dev->client_lock); if (ret < 0) goto done; - ret = adf_copy_attachment_list_to_user(arg->attachments, + ret = adf_copy_attachment_list_to_user(data.attachments, data.n_attachments, attach, n_attach); if (ret < 0) goto done; - ret = adf_copy_attachment_list_to_user(arg->allowed_attachments, + ret = adf_copy_attachment_list_to_user(data.allowed_attachments, data.n_allowed_attachments, allowed_attach, n_allowed_attach); if (ret < 0) @@ -592,7 +581,7 @@ static int adf_intf_get_data(struct adf_interface *intf, data.n_available_modes = intf->n_modes; read_unlock_irqrestore(&intf->hotplug_modelist_lock, flags); - if (copy_to_user(arg->available_modes, modelist, modelist_size)) { + if (copy_to_user(data.available_modes, modelist, modelist_size)) { ret = -EFAULT; goto done; } @@ -601,7 +590,7 @@ static int adf_intf_get_data(struct adf_interface *intf, memcpy(&data.current_mode, &intf->current_mode, sizeof(intf->current_mode)); - ret = adf_obj_copy_custom_data_to_user(&intf->base, arg->custom_data, + ret = adf_obj_copy_custom_data_to_user(&intf->base, data.custom_data, &data.custom_data_size); done: mutex_unlock(&dev->client_lock); diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c index 95d293b7445a..dc2fcda54d53 100644 --- a/drivers/video/fbdev/efifb.c +++ b/drivers/video/fbdev/efifb.c @@ -52,9 +52,9 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green, return 1; if (regno < 16) { - red >>= 8; - green >>= 8; - blue >>= 8; + red >>= 16 - info->var.red.length; + green >>= 16 - info->var.green.length; + blue >>= 16 - info->var.blue.length; ((u32 *)(info->pseudo_palette))[regno] = (red << info->var.red.offset) | (green << info->var.green.offset) | diff --git a/drivers/video/fbdev/goldfishfb.c b/drivers/video/fbdev/goldfishfb.c index 7f6c9e6cfc6c..1e56b50e4082 100644 --- a/drivers/video/fbdev/goldfishfb.c +++ b/drivers/video/fbdev/goldfishfb.c @@ -26,6 +26,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/platform_device.h> +#include <linux/acpi.h> enum { FB_GET_WIDTH = 0x00, @@ -234,7 +235,7 @@ static int goldfish_fb_probe(struct platform_device *pdev) fb->fb.var.activate = FB_ACTIVATE_NOW; fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT); fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH); - fb->fb.var.pixclock = 10000; + fb->fb.var.pixclock = 0; fb->fb.var.red.offset = 11; fb->fb.var.red.length = 5; @@ -304,12 +305,25 @@ static int goldfish_fb_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_fb_of_match[] = { + { .compatible = "google,goldfish-fb", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_fb_of_match); + +static const struct acpi_device_id goldfish_fb_acpi_match[] = { + { "GFSH0004", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goldfish_fb_acpi_match); static struct platform_driver goldfish_fb_driver = { .probe = goldfish_fb_probe, .remove = goldfish_fb_remove, .driver = { - .name = "goldfish_fb" + .name = "goldfish_fb", + .of_match_table = goldfish_fb_of_match, + .acpi_match_table = ACPI_PTR(goldfish_fb_acpi_match), } }; diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig index ef5c96214c19..03ee89ad0d99 100644 --- a/drivers/video/fbdev/msm/Kconfig +++ b/drivers/video/fbdev/msm/Kconfig @@ -63,6 +63,7 @@ config FB_MSM_MDSS_WRITEBACK config FB_MSM_MDSS_HDMI_PANEL depends on FB_MSM_MDSS + select MSM_EXT_DISPLAY bool "MDSS HDMI Tx Panel" default n ---help--- @@ -98,6 +99,7 @@ config FB_MSM_MDSS_DSI_CTRL_STATUS config FB_MSM_MDSS_DP_PANEL depends on FB_MSM_MDSS + select MSM_EXT_DISPLAY bool "MDSS DP Panel" ---help--- The MDSS DP Panel provides support for DP host controller driver diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index b905c0e855dd..e101b873f361 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -49,7 +49,6 @@ obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_aux.o obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_hdcp2p2.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o -obj-$(CONFIG_FB_MSM_MDSS) += msm_ext_display.o obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_panel.o obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_hdcp2p2.o diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index bf4dc39f57ee..d9a4bd91f3eb 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -27,6 +27,7 @@ #include <linux/msm-bus.h> #include <linux/file.h> #include <linux/dma-direction.h> +#include <soc/qcom/cx_ipeak.h> #include "mdss_panel.h" @@ -535,6 +536,7 @@ struct mdss_data_type { u32 sec_cam_en; u32 sec_session_cnt; wait_queue_head_t secure_waitq; + struct cx_ipeak_client *mdss_cx_ipeak; }; extern struct mdss_data_type *mdss_res; diff --git a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c index 3bcacf945761..5a677dfe7484 100644 --- a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c @@ -449,7 +449,7 @@ static struct attribute *dp_hdcp2p2_fs_attrs[] = { }; static struct attribute_group dp_hdcp2p2_fs_attr_group = { - .name = "dp_hdcp2p2", + .name = "hdcp2p2", .attrs = dp_hdcp2p2_fs_attrs, }; diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index c66d9f3b3a65..4aa14422899f 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -629,6 +629,7 @@ struct buf_data { char *string_buf; /* cmd buf as string, 3 bytes per number */ int sblen; /* string buffer length */ int sync_flag; + struct mutex dbg_mutex; /* mutex to synchronize read/write/flush */ }; struct mdss_dsi_debugfs_info { @@ -718,6 +719,7 @@ static ssize_t mdss_dsi_cmd_read(struct file *file, char __user *buf, char *bp; ssize_t ret = 0; + mutex_lock(&pcmds->dbg_mutex); if (*ppos == 0) { kfree(pcmds->string_buf); pcmds->string_buf = NULL; @@ -736,6 +738,7 @@ static ssize_t mdss_dsi_cmd_read(struct file *file, char __user *buf, buffer = kmalloc(bsize, GFP_KERNEL); if (!buffer) { pr_err("%s: Failed to allocate memory\n", __func__); + mutex_unlock(&pcmds->dbg_mutex); return -ENOMEM; } @@ -771,10 +774,12 @@ static ssize_t mdss_dsi_cmd_read(struct file *file, char __user *buf, kfree(pcmds->string_buf); pcmds->string_buf = NULL; pcmds->sblen = 0; + mutex_unlock(&pcmds->dbg_mutex); return 0; /* the end */ } ret = simple_read_from_buffer(buf, count, ppos, pcmds->string_buf, pcmds->sblen); + mutex_unlock(&pcmds->dbg_mutex); return ret; } @@ -786,6 +791,7 @@ static ssize_t mdss_dsi_cmd_write(struct file *file, const char __user *p, int blen = 0; char *string_buf; + mutex_lock(&pcmds->dbg_mutex); if (*ppos == 0) { kfree(pcmds->string_buf); pcmds->string_buf = NULL; @@ -797,6 +803,7 @@ static ssize_t mdss_dsi_cmd_write(struct file *file, const char __user *p, string_buf = krealloc(pcmds->string_buf, blen + 1, GFP_KERNEL); if (!string_buf) { pr_err("%s: Failed to allocate memory\n", __func__); + mutex_unlock(&pcmds->dbg_mutex); return -ENOMEM; } @@ -806,6 +813,7 @@ static ssize_t mdss_dsi_cmd_write(struct file *file, const char __user *p, string_buf[blen] = '\0'; pcmds->string_buf = string_buf; pcmds->sblen = blen; + mutex_unlock(&pcmds->dbg_mutex); return ret; } @@ -816,8 +824,12 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id) char *buf, *bufp, *bp; struct dsi_ctrl_hdr *dchdr; - if (!pcmds->string_buf) + mutex_lock(&pcmds->dbg_mutex); + + if (!pcmds->string_buf) { + mutex_unlock(&pcmds->dbg_mutex); return 0; + } /* * Allocate memory for command buffer @@ -830,6 +842,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id) kfree(pcmds->string_buf); pcmds->string_buf = NULL; pcmds->sblen = 0; + mutex_unlock(&pcmds->dbg_mutex); return -ENOMEM; } @@ -854,6 +867,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id) pr_err("%s: dtsi cmd=%x error, len=%d\n", __func__, dchdr->dtype, dchdr->dlen); kfree(buf); + mutex_unlock(&pcmds->dbg_mutex); return -EINVAL; } bp += sizeof(*dchdr); @@ -865,6 +879,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id) pr_err("%s: dcs_cmd=%x len=%d error!\n", __func__, bp[0], len); kfree(buf); + mutex_unlock(&pcmds->dbg_mutex); return -EINVAL; } @@ -877,6 +892,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id) pcmds->buf = buf; pcmds->blen = blen; } + mutex_unlock(&pcmds->dbg_mutex); return 0; } @@ -891,6 +907,7 @@ struct dentry *dsi_debugfs_create_dcs_cmd(const char *name, umode_t mode, struct dentry *parent, struct buf_data *cmd, struct dsi_panel_cmds ctrl_cmds) { + mutex_init(&cmd->dbg_mutex); cmd->buf = ctrl_cmds.buf; cmd->blen = ctrl_cmds.blen; cmd->string_buf = NULL; diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index d57558a1b52d..734d3bee8fd0 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 CEIL(x, y) (((x) + ((y)-1)) / (y)) + struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX]; struct mdss_hw mdss_dsi0_hw = { @@ -1423,6 +1425,35 @@ void mdss_dsi_ctrl_setup(struct mdss_dsi_ctrl_pdata *ctrl) mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode, pdata); } +static int mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl) +{ + int ret = 0; + u32 v_total = 0, v_blank = 0, sleep_ms = 0, fps = 0; + struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; + + if (ctrl->panel_mode == DSI_CMD_MODE) + return ret; + + 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; + if (pinfo->dynamic_fps && pinfo->current_fps) + fps = pinfo->current_fps; + else + fps = pinfo->mipi.frame_rate; + + sleep_ms = CEIL((v_blank * 1000), (v_total * fps)) + 1; + /* delay sleep_ms to skip BLLP */ + if (sleep_ms) + udelay(sleep_ms * 1000); + ret = 1; + } + + return ret; +} + /** * mdss_dsi_bta_status_check() - Check dsi panel status through bta check * @ctrl_pdata: pointer to the dsi controller structure @@ -1463,6 +1494,7 @@ 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); + mdss_dsi_wait4video_eng_busy(ctrl_pdata); /* mask out overflow errors */ if (ignore_underflow) mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0x0f0000); @@ -2348,15 +2380,20 @@ void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl) /* DSI_INTL_CTRL */ data = MIPI_INP((ctrl->ctrl_base) + 0x0110); data &= DSI_INTR_TOTAL_MASK; - data |= DSI_INTR_VIDEO_DONE_MASK; - MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data); + /* clear previous VIDEO_DONE interrupt */ + MIPI_OUTP((ctrl->ctrl_base) + 0x0110, (data | DSI_INTR_VIDEO_DONE)); + wmb(); /* ensure interrupt is cleared */ spin_lock_irqsave(&ctrl->mdp_lock, flag); reinit_completion(&ctrl->video_comp); mdss_dsi_enable_irq(ctrl, DSI_VIDEO_TERM); spin_unlock_irqrestore(&ctrl->mdp_lock, flag); + data |= DSI_INTR_VIDEO_DONE_MASK; + MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data); + wmb(); /* ensure interrupt is enabled */ + wait_for_completion_timeout(&ctrl->video_comp, msecs_to_jiffies(VSYNC_PERIOD * 4)); @@ -2366,23 +2403,6 @@ void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl) MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data); } -static int mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl) -{ - int ret = 0; - - if (ctrl->panel_mode == DSI_CMD_MODE) - return ret; - - if (ctrl->ctrl_state & CTRL_STATE_MDP_ACTIVE) { - mdss_dsi_wait4video_done(ctrl); - /* delay 4 ms to skip BLLP */ - usleep_range(4000, 4000); - ret = 1; - } - - return ret; -} - void mdss_dsi_cmd_mdp_start(struct mdss_dsi_ctrl_pdata *ctrl) { unsigned long flag; diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index f4c4c509410a..db27842eaccc 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -892,6 +892,12 @@ static ssize_t mdss_fb_get_persist_mode(struct device *dev, return ret; } +static ssize_t mdss_fb_idle_pc_notify(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "idle power collapsed\n"); +} + static DEVICE_ATTR(msm_fb_type, S_IRUGO, mdss_fb_get_type, NULL); static DEVICE_ATTR(msm_fb_split, S_IRUGO | S_IWUSR, mdss_fb_show_split, mdss_fb_store_split); @@ -912,6 +918,8 @@ static DEVICE_ATTR(measured_fps, S_IRUGO | S_IWUSR | S_IWGRP, mdss_fb_get_fps_info, NULL); static DEVICE_ATTR(msm_fb_persist_mode, S_IRUGO | S_IWUSR, mdss_fb_get_persist_mode, mdss_fb_change_persist_mode); +static DEVICE_ATTR(idle_power_collapse, S_IRUGO, mdss_fb_idle_pc_notify, NULL); + static struct attribute *mdss_fb_attrs[] = { &dev_attr_msm_fb_type.attr, &dev_attr_msm_fb_split.attr, @@ -925,6 +933,7 @@ static struct attribute *mdss_fb_attrs[] = { &dev_attr_msm_fb_dfps_mode.attr, &dev_attr_measured_fps.attr, &dev_attr_msm_fb_persist_mode.attr, + &dev_attr_idle_power_collapse.attr, NULL, }; @@ -4470,7 +4479,7 @@ err: static int __mdss_fb_copy_destscaler_data(struct fb_info *info, struct mdp_layer_commit *commit) { - int i; + int i = 0; int ret = 0; u32 data_size; struct mdp_destination_scaler_data __user *ds_data_user; @@ -4543,6 +4552,7 @@ static int __mdss_fb_copy_destscaler_data(struct fb_info *info, data_size); if (ret) { pr_err("scale data copy from user failed\n"); + kfree(scale_data); goto err; } } @@ -4552,7 +4562,7 @@ static int __mdss_fb_copy_destscaler_data(struct fb_info *info, err: if (ds_data) { - for (i = 0; i < commit->commit_v1.dest_scaler_cnt; i++) { + for (i--; i >= 0; i--) { scale_data = to_user_ptr(ds_data[i].scale); kfree(scale_data); } @@ -5176,3 +5186,16 @@ void mdss_fb_calc_fps(struct msm_fb_data_type *mfd) mfd->fps_info.frame_count = 0; } } + +void mdss_fb_idle_pc(struct msm_fb_data_type *mfd) +{ + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + + if (mdss_fb_is_power_off(mfd)) + return; + + if ((mfd->panel_info->type == MIPI_CMD_PANEL) && mdp5_data) { + pr_debug("Notify fb%d idle power collapsed\n", mfd->index); + sysfs_notify(&mfd->fbi->dev->kobj, NULL, "idle_power_collapse"); + } +} diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index 111d7cfc7c9a..d64580a35775 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -468,4 +468,5 @@ void mdss_fb_report_panel_dead(struct msm_fb_data_type *mfd); void mdss_panelinfo_to_fb_var(struct mdss_panel_info *pinfo, struct fb_var_screeninfo *var); void mdss_fb_calc_fps(struct msm_fb_data_type *mfd); +void mdss_fb_idle_pc(struct msm_fb_data_type *mfd); #endif /* MDSS_FB_H */ diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index d8d11f21f3b2..3cadfa4ecef7 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -1173,6 +1173,31 @@ irqreturn_t mdss_mdp_isr(int irq, void *ptr) return IRQ_HANDLED; } +static void mdss_mdp_cxipeak_vote(bool set_vote, unsigned long new_rate, + unsigned long prev_rate) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + int ret = 0; + + if (!mdata->mdss_cx_ipeak) + return; + + /* fmax threshold for mdp in sdm660 is max MDP clk */ + if (set_vote) { + if ((new_rate >= mdata->max_mdp_clk_rate) && + (prev_rate < mdata->max_mdp_clk_rate)) + ret = cx_ipeak_update(mdata->mdss_cx_ipeak, true); + } else { + if ((new_rate < mdata->max_mdp_clk_rate) && + (prev_rate >= mdata->max_mdp_clk_rate)) + ret = cx_ipeak_update(mdata->mdss_cx_ipeak, false); + } + if (ret) { + pr_err("cxipeak api fail ret:%d set_vote :%d new_rate:%lu prev_rate:%lu\n", + ret, (int)set_vote, new_rate, prev_rate); + } +} + static int mdss_mdp_clk_update(u32 clk_idx, u32 enable) { int ret = -ENODEV; @@ -1234,7 +1259,7 @@ void mdss_mdp_set_clk_rate(unsigned long rate, bool locked) struct mdss_data_type *mdata = mdss_res; unsigned long clk_rate; struct clk *clk = mdss_mdp_get_clk(MDSS_CLK_MDP_CORE); - unsigned long min_clk_rate; + unsigned long min_clk_rate, curr_clk_rate; min_clk_rate = max(rate, mdata->perf_tune.min_mdp_clk); @@ -1246,15 +1271,20 @@ void mdss_mdp_set_clk_rate(unsigned long rate, bool locked) clk_rate = clk_round_rate(clk, min_clk_rate); else clk_rate = mdata->max_mdp_clk_rate; + + curr_clk_rate = clk_get_rate(clk); if (IS_ERR_VALUE(clk_rate)) { pr_err("unable to round rate err=%ld\n", clk_rate); - } else if (clk_rate != clk_get_rate(clk)) { - + } else if (clk_rate != curr_clk_rate) { + mdss_mdp_cxipeak_vote(true, clk_rate, curr_clk_rate); mdata->mdp_clk_rate = clk_rate; - if (IS_ERR_VALUE(clk_set_rate(clk, clk_rate))) + if (IS_ERR_VALUE(clk_set_rate(clk, clk_rate))) { pr_err("clk_set_rate failed\n"); - else + } else { + mdss_mdp_cxipeak_vote(false, clk_rate, + curr_clk_rate); pr_debug("mdp clk rate=%lu\n", clk_rate); + } } if (!locked) mutex_unlock(&mdp_clk_lock); @@ -1355,6 +1385,68 @@ static inline void __mdss_mdp_reg_access_clk_enable( } } +/* + * __mdss_mdp_clk_control - Overall MDSS clock control for power on/off + */ +static void __mdss_mdp_clk_control(struct mdss_data_type *mdata, bool enable) +{ + int rc = 0; + unsigned long flags; + + if (enable) { + pm_runtime_get_sync(&mdata->pdev->dev); + + mdss_update_reg_bus_vote(mdata->reg_bus_clt, + VOTE_INDEX_LOW); + + rc = mdss_iommu_ctrl(1); + if (IS_ERR_VALUE(rc)) + pr_err("IOMMU attach failed\n"); + + /* Active+Sleep */ + msm_bus_scale_client_update_context(mdata->bus_hdl, + false, mdata->curr_bw_uc_idx); + + spin_lock_irqsave(&mdp_lock, flags); + mdata->clk_ena = enable; + spin_unlock_irqrestore(&mdp_lock, flags); + + mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, 1); + mdss_mdp_clk_update(MDSS_CLK_AHB, 1); + mdss_mdp_clk_update(MDSS_CLK_AXI, 1); + mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, 1); + mdss_mdp_clk_update(MDSS_CLK_MDP_LUT, 1); + if (mdata->vsync_ena) + mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, 1); + } else { + spin_lock_irqsave(&mdp_lock, flags); + mdata->clk_ena = enable; + spin_unlock_irqrestore(&mdp_lock, flags); + + if (mdata->vsync_ena) + mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, 0); + + mdss_mdp_clk_update(MDSS_CLK_MDP_LUT, 0); + mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, 0); + mdss_mdp_clk_update(MDSS_CLK_AXI, 0); + mdss_mdp_clk_update(MDSS_CLK_AHB, 0); + mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, 0); + + /* release iommu control */ + mdss_iommu_ctrl(0); + + /* Active-Only */ + msm_bus_scale_client_update_context(mdata->bus_hdl, + true, mdata->ao_bw_uc_idx); + + mdss_update_reg_bus_vote(mdata->reg_bus_clt, + VOTE_INDEX_DISABLE); + + pm_runtime_mark_last_busy(&mdata->pdev->dev); + pm_runtime_put_autosuspend(&mdata->pdev->dev); + } +} + int __mdss_mdp_vbif_halt(struct mdss_data_type *mdata, bool is_nrt) { int rc = 0; @@ -1646,9 +1738,7 @@ void mdss_mdp_clk_ctrl(int enable) { struct mdss_data_type *mdata = mdss_mdp_get_mdata(); static int mdp_clk_cnt; - unsigned long flags; int changed = 0; - int rc = 0; mutex_lock(&mdp_clk_lock); if (enable) { @@ -1672,49 +1762,8 @@ void mdss_mdp_clk_ctrl(int enable) __builtin_return_address(0), current->group_leader->comm, mdata->bus_ref_cnt, changed, enable); - if (changed) { - if (enable) { - pm_runtime_get_sync(&mdata->pdev->dev); - - mdss_update_reg_bus_vote(mdata->reg_bus_clt, - VOTE_INDEX_LOW); - - rc = mdss_iommu_ctrl(1); - if (IS_ERR_VALUE(rc)) - pr_err("IOMMU attach failed\n"); - - /* Active+Sleep */ - msm_bus_scale_client_update_context(mdata->bus_hdl, - false, mdata->curr_bw_uc_idx); - } - - spin_lock_irqsave(&mdp_lock, flags); - mdata->clk_ena = enable; - spin_unlock_irqrestore(&mdp_lock, flags); - - mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, enable); - mdss_mdp_clk_update(MDSS_CLK_AHB, enable); - mdss_mdp_clk_update(MDSS_CLK_AXI, enable); - mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, enable); - mdss_mdp_clk_update(MDSS_CLK_MDP_LUT, enable); - if (mdata->vsync_ena) - mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, enable); - - if (!enable) { - /* release iommu control */ - mdss_iommu_ctrl(0); - - /* Active-Only */ - msm_bus_scale_client_update_context(mdata->bus_hdl, - true, mdata->ao_bw_uc_idx); - - mdss_update_reg_bus_vote(mdata->reg_bus_clt, - VOTE_INDEX_DISABLE); - - pm_runtime_mark_last_busy(&mdata->pdev->dev); - pm_runtime_put_autosuspend(&mdata->pdev->dev); - } - } + if (changed) + __mdss_mdp_clk_control(mdata, enable); if (enable && changed) mdss_mdp_idle_pc_restore(); @@ -4519,6 +4568,10 @@ static int mdss_mdp_parse_dt_misc(struct platform_device *pdev) pr_debug("max pipe width not specified. Using default value\n"); mdata->max_pipe_width = DEFAULT_MDP_PIPE_WIDTH; } + + if (of_find_property(pdev->dev.of_node, "qcom,mdss-cx-ipeak", NULL)) + mdata->mdss_cx_ipeak = cx_ipeak_register(pdev->dev.of_node, + "qcom,mdss-cx-ipeak"); return 0; } @@ -5092,6 +5145,22 @@ vreg_set_voltage_fail: } /** + * mdss_mdp_notify_idle_pc() - Notify fb driver of idle power collapse + * @mdata: MDP private data + * + * This function is called if there are active overlays. + */ +static void mdss_mdp_notify_idle_pc(struct mdss_data_type *mdata) +{ + int i; + + for (i = 0; i < mdata->nctl; i++) + if ((mdata->ctl_off[i].ref_cnt) && + !mdss_mdp_ctl_is_power_off(&mdata->ctl_off[i])) + mdss_fb_idle_pc(mdata->ctl_off[i].mfd); +} + +/** * mdss_mdp_footswitch_ctrl() - Disable/enable MDSS GDSC and CX/Batfet rails * @mdata: MDP private data * @on: 1 to turn on footswitch, 0 to turn off footswitch @@ -5155,6 +5224,7 @@ static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on) mdss_mdp_memory_retention_ctrl(MEM_RETAIN_ON, PERIPH_RETAIN_OFF); mdata->idle_pc = true; + mdss_mdp_notify_idle_pc(mdata); pr_debug("idle pc. active overlays=%d\n", active_cnt); } else { @@ -5487,6 +5557,8 @@ static int mdss_mdp_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); mdss_mdp_pp_term(&pdev->dev); mdss_mdp_bus_scale_unregister(mdata); + if (mdata->mdss_cx_ipeak) + cx_ipeak_unregister(mdata->mdss_cx_ipeak); mdss_debugfs_remove(mdata); if (mdata->regulator_notif_register) regulator_unregister_notifier(mdata->fs, &(mdata->gdsc_cb)); diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 5e98de043e55..36a866685f21 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -1842,7 +1842,7 @@ int mdss_mdp_calib_mode(struct msm_fb_data_type *mfd, int mdss_mdp_pipe_handoff(struct mdss_mdp_pipe *pipe); int mdss_mdp_smp_handoff(struct mdss_data_type *mdata); struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer, - u32 type, struct mdss_mdp_pipe *left_blend_pipe); + u32 off, u32 type, struct mdss_mdp_pipe *left_blend_pipe); struct mdss_mdp_pipe *mdss_mdp_pipe_get(u32 ndx, enum mdss_mdp_pipe_rect rect_num); struct mdss_mdp_pipe *mdss_mdp_pipe_search(struct mdss_data_type *mdata, diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index e258f258aeca..7b0207de101a 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -892,7 +892,7 @@ static u32 __calc_prefill_line_time_us(struct mdss_mdp_ctl *ctl) static u32 __get_min_prefill_line_time_us(struct mdss_mdp_ctl *ctl) { - u32 vbp_min = 0; + u32 vbp_min = UINT_MAX; int i; struct mdss_data_type *mdata; @@ -914,6 +914,9 @@ static u32 __get_min_prefill_line_time_us(struct mdss_mdp_ctl *ctl) } } + if (vbp_min == UINT_MAX) + vbp_min = 0; + return vbp_min; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index 663d63092ebf..97be2fd728c8 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -449,7 +449,7 @@ static void mdss_mdp_video_avr_vtotal_setup(struct mdss_mdp_ctl *ctl, if (sctl) sctx = (struct mdss_mdp_video_ctx *) sctl->intf_ctx[MASTER_CTX]; - mdss_mdp_video_timegen_flush(ctl, ctx); + mdss_mdp_video_timegen_flush(ctl, sctx); MDSS_XLOG(pinfo->min_fps, pinfo->default_fps, avr_vtotal); } @@ -1671,7 +1671,7 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, struct mdss_panel_data *pdata; int i, ret = 0, off; u32 data, flush; - struct mdss_mdp_video_ctx *ctx; + struct mdss_mdp_video_ctx *ctx, *sctx = NULL; struct mdss_mdp_ctl *sctl; if (!ctl) { @@ -1695,10 +1695,13 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, pdata->panel_info.cont_splash_enabled = 0; sctl = mdss_mdp_get_split_ctl(ctl); - if (sctl) + if (sctl) { sctl->panel_data->panel_info.cont_splash_enabled = 0; - else if (ctl->panel_data->next && is_pingpong_split(ctl->mfd)) + sctx = (struct mdss_mdp_video_ctx *) sctl->intf_ctx[MASTER_CTX]; + } else if (ctl->panel_data->next && is_pingpong_split(ctl->mfd)) { ctl->panel_data->next->panel_info.cont_splash_enabled = 0; + sctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[SLAVE_CTX]; + } if (!handoff) { ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN, @@ -1724,6 +1727,8 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush); mdp_video_write(ctx, 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 */ msleep(20); @@ -1896,7 +1901,6 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, struct mdss_mdp_format_params *fmt; struct mdss_data_type *mdata = ctl->mdata; struct dsc_desc *dsc = NULL; - u32 hdmi_dp_core; ctx->ctl = ctl; ctx->intf_type = ctl->intf_type; @@ -2033,10 +2037,19 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, mdp_video_write(ctx, MDSS_MDP_REG_INTF_PANEL_FORMAT, ctl->dst_format); - hdmi_dp_core = (ctx->intf_type == MDSS_INTF_EDP) ? 1 : 0; - - writel_relaxed(hdmi_dp_core, mdata->mdp_base + + /* select HDMI or DP core usage */ + switch (ctx->intf_type) { + case MDSS_INTF_EDP: + writel_relaxed(0x1, mdata->mdp_base + + MDSS_MDP_HDMI_DP_CORE_SELECT); + break; + case MDSS_INTF_HDMI: + writel_relaxed(0x0, mdata->mdp_base + MDSS_MDP_HDMI_DP_CORE_SELECT); + break; + default: + break; + } return 0; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index da08917d334b..9e295815da77 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -519,6 +519,102 @@ static int __mdss_mdp_validate_pxl_extn(struct mdss_mdp_pipe *pipe) return 0; } +static int __mdss_mdp_validate_qseed3_cfg(struct mdss_mdp_pipe *pipe) +{ + int plane; + + for (plane = 0; plane < MAX_PLANES; plane++) { + u32 hor_req_pixels, hor_fetch_pixels; + u32 hor_ov_fetch, vert_ov_fetch; + u32 vert_req_pixels, vert_fetch_pixels; + u32 src_w = DECIMATED_DIMENSION(pipe->src.w, pipe->horz_deci); + u32 src_h = DECIMATED_DIMENSION(pipe->src.h, pipe->vert_deci); + + /* + * plane 1 and 2 are for chroma and are same. While configuring + * HW, programming only one of the chroma components is + * sufficient. + */ + if (plane == 2) + continue; + + /* + * For chroma plane, width is half for the following sub sampled + * formats. Except in case of decimation, where hardware avoids + * 1 line of decimation instead of downsampling. + */ + if (plane == 1 && !pipe->horz_deci && + ((pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420) || + (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_H2V1))) { + src_w >>= 1; + } + + if (plane == 1 && !pipe->vert_deci && + ((pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420) || + (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_H1V2))) + src_h >>= 1; + + hor_req_pixels = pipe->scaler.num_ext_pxls_left[plane]; + + hor_fetch_pixels = src_w + + (pipe->scaler.left_ftch[plane] >> pipe->horz_deci) + + pipe->scaler.left_rpt[plane] + + (pipe->scaler.right_ftch[plane] >> pipe->horz_deci) + + pipe->scaler.right_rpt[plane]; + + hor_ov_fetch = src_w + + (pipe->scaler.left_ftch[plane] >> pipe->horz_deci) + + (pipe->scaler.right_ftch[plane] >> pipe->horz_deci); + + vert_req_pixels = pipe->scaler.num_ext_pxls_top[plane]; + + vert_fetch_pixels = src_h + + (pipe->scaler.top_ftch[plane] >> pipe->vert_deci) + + pipe->scaler.top_rpt[plane] + + (pipe->scaler.btm_ftch[plane] >> pipe->vert_deci) + + pipe->scaler.btm_rpt[plane]; + + vert_ov_fetch = src_h + + (pipe->scaler.top_ftch[plane] >> pipe->vert_deci) + + (pipe->scaler.btm_ftch[plane] >> pipe->vert_deci); + + if ((hor_req_pixels != hor_fetch_pixels) || + (hor_ov_fetch > pipe->img_width) || + (vert_req_pixels != vert_fetch_pixels) || + (vert_ov_fetch > pipe->img_height)) { + pr_err("err: plane=%d h_req:%d h_fetch:%d v_req:%d v_fetch:%d src_img[%d %d] ov_fetch[%d %d]\n", + + plane, + hor_req_pixels, hor_fetch_pixels, + vert_req_pixels, vert_fetch_pixels, + pipe->img_width, pipe->img_height, + hor_ov_fetch, vert_ov_fetch); + pipe->scaler.enable = 0; + return -EINVAL; + } + /* + * alpha plane can only be scaled using bilinear or pixel + * repeat/drop, src_width and src_height are only specified + * for Y and UV plane + */ + if (plane != 3) { + if ((hor_req_pixels != + pipe->scaler.src_width[plane]) || + (vert_req_pixels != + pipe->scaler.src_height[plane])) { + pr_err("roi_w[%d]=%d, scaler:[%d, %d], src_img:[%d, %d]\n", + plane, pipe->scaler.roi_w[plane], + pipe->scaler.src_width[plane], + pipe->scaler.src_height[plane], + pipe->img_width, pipe->img_height); + pipe->scaler.enable = 0; + return -EINVAL; + } + } + } + + return 0; +} int mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe) { @@ -528,8 +624,11 @@ int mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe) mdata = mdss_mdp_get_mdata(); if (pipe->scaler.enable) { - if (!test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map)) + if (test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map)) + rc = __mdss_mdp_validate_qseed3_cfg(pipe); + else rc = __mdss_mdp_validate_pxl_extn(pipe); + return rc; } @@ -609,6 +708,7 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, bool is_vig_needed = false; u32 left_lm_w = left_lm_w_from_mfd(mfd); u32 flags = 0; + u32 off = 0; if (mdp5_data->ctl == NULL) return -ENODEV; @@ -692,18 +792,29 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, break; case PIPE_TYPE_AUTO: default: - if (req->flags & MDP_OV_PIPE_FORCE_DMA) + if (req->flags & MDP_OV_PIPE_FORCE_DMA) { pipe_type = MDSS_MDP_PIPE_TYPE_DMA; - else if (fmt->is_yuv || + /* + * For paths using legacy API's for pipe + * allocation, use offset of 2 for allocating + * right pipe for pipe type DMA. This is + * because from SDM 3.x.x. onwards one DMA + * pipe has two instances for multirect. + */ + off = (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) + ? 2 : 0; + } else if (fmt->is_yuv || (req->flags & MDP_OV_PIPE_SHARE) || - is_vig_needed) + is_vig_needed) { pipe_type = MDSS_MDP_PIPE_TYPE_VIG; - else + } else { pipe_type = MDSS_MDP_PIPE_TYPE_RGB; + } break; } - pipe = mdss_mdp_pipe_alloc(mixer, pipe_type, left_blend_pipe); + pipe = mdss_mdp_pipe_alloc(mixer, off, + pipe_type, left_blend_pipe); /* RGB pipes can be used instead of DMA */ if (IS_ERR_OR_NULL(pipe) && @@ -712,7 +823,7 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, pr_debug("giving RGB pipe for fb%d. flags:0x%x\n", mfd->index, req->flags); pipe_type = MDSS_MDP_PIPE_TYPE_RGB; - pipe = mdss_mdp_pipe_alloc(mixer, pipe_type, + pipe = mdss_mdp_pipe_alloc(mixer, off, pipe_type, left_blend_pipe); } @@ -723,7 +834,7 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, pr_debug("giving ViG pipe for fb%d. flags:0x%x\n", mfd->index, req->flags); pipe_type = MDSS_MDP_PIPE_TYPE_VIG; - pipe = mdss_mdp_pipe_alloc(mixer, pipe_type, + pipe = mdss_mdp_pipe_alloc(mixer, off, pipe_type, left_blend_pipe); } diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c index 965a6533dfcb..8d7bd60318ad 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c @@ -1250,11 +1250,12 @@ cursor_done: } struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer, - u32 type, struct mdss_mdp_pipe *left_blend_pipe) + u32 off, u32 type, struct mdss_mdp_pipe *left_blend_pipe) { struct mdss_mdp_pipe *pipe; + mutex_lock(&mdss_mdp_sspp_lock); - pipe = mdss_mdp_pipe_init(mixer, type, 0, left_blend_pipe); + pipe = mdss_mdp_pipe_init(mixer, type, off, left_blend_pipe); mutex_unlock(&mdss_mdp_sspp_lock); return pipe; } diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index dc2b94142f53..a01a41a41269 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -548,7 +548,8 @@ void virtqueue_disable_cb(struct virtqueue *_vq) if (!(vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT)) { vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT; - vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow); + if (!vq->event) + vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow); } } @@ -580,7 +581,8 @@ unsigned virtqueue_enable_cb_prepare(struct virtqueue *_vq) * entry. Always do both to keep code simple. */ if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) { vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT; - vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow); + if (!vq->event) + vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow); } vring_used_event(&vq->vring) = cpu_to_virtio16(_vq->vdev, last_used_idx = vq->last_used_idx); END_USE(vq); @@ -648,10 +650,11 @@ bool virtqueue_enable_cb_delayed(struct virtqueue *_vq) * more to do. */ /* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to * either clear the flags bit or point the event index at the next - * entry. Always do both to keep code simple. */ + * entry. Always update the event index to keep code simple. */ if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) { vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT; - vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow); + if (!vq->event) + vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow); } /* TODO: tune this threshold */ bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) * 3 / 4; @@ -770,7 +773,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int index, /* No callback? Tell other side not to bother us. */ if (!callback) { vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT; - vq->vring.avail->flags = cpu_to_virtio16(vdev, vq->avail_flags_shadow); + if (!vq->event) + vq->vring.avail->flags = cpu_to_virtio16(vdev, vq->avail_flags_shadow); } /* Put everything in free lists. */ diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c index 531e76474983..0e0eb10f82a0 100644 --- a/drivers/xen/xenbus/xenbus_dev_frontend.c +++ b/drivers/xen/xenbus/xenbus_dev_frontend.c @@ -316,7 +316,7 @@ static int xenbus_write_transaction(unsigned msg_type, rc = -ENOMEM; goto out; } - } else { + } else if (msg_type == XS_TRANSACTION_END) { list_for_each_entry(trans, &u->transactions, list) if (trans->handle.id == u->u.msg.tx_id) break; |
