diff options
Diffstat (limited to 'drivers')
52 files changed, 2162 insertions, 545 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index fcd4ce6f78d5..1c2b846c5776 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -200,6 +200,7 @@ static int acpi_power_get_list_state(struct list_head *list, int *state) return -EINVAL; /* The state of the list is 'on' IFF all resources are 'on'. */ + cur_state = 0; list_for_each_entry(entry, list, node) { struct acpi_power_resource *resource = entry->resource; acpi_handle handle = resource->device.handle; diff --git a/drivers/bluetooth/btfm_slim.h b/drivers/bluetooth/btfm_slim.h index 5d105fba2193..e67c6964ee65 100644 --- a/drivers/bluetooth/btfm_slim.h +++ b/drivers/bluetooth/btfm_slim.h @@ -13,7 +13,7 @@ #define BTFM_SLIM_H #include <linux/slimbus/slimbus.h> -#define BTFMSLIM_DBG(fmt, arg...) pr_debug(fmt "\n", ## arg) +#define BTFMSLIM_DBG(fmt, arg...) pr_debug("%s: " fmt "\n", __func__, ## arg) #define BTFMSLIM_INFO(fmt, arg...) pr_info("%s: " fmt "\n", __func__, ## arg) #define BTFMSLIM_ERR(fmt, arg...) pr_err("%s: " fmt "\n", __func__, ## arg) diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c index c93b29281e35..a451ff33103c 100644 --- a/drivers/bluetooth/btfm_slim_wcn3990.c +++ b/drivers/bluetooth/btfm_slim_wcn3990.c @@ -83,19 +83,34 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, { int ret = 0; uint8_t reg_val = 0; + uint8_t port_bit = 0; uint16_t reg; BTFMSLIM_DBG("port(%d) enable(%d)", port_num, enable); if (rxport) { + if (enable) { + /* For SCO Rx, A2DP Rx */ + reg_val = 0x1; + port_bit = port_num - 0x10; + reg = CHRK_SB_PGD_RX_PORTn_MULTI_CHNL_0(port_bit); + BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)", + reg_val, reg); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", + ret, reg); + goto error; + } + } /* Port enable */ reg = CHRK_SB_PGD_PORT_RX_CFGN(port_num - 0x10); goto enable_disable_rxport; } - /* txport */ if (!enable) goto enable_disable_txport; - /* Multiple Channel Setting - only for FM Tx */ + /* txport */ + /* Multiple Channel Setting */ if (is_fm_port(port_num)) { reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) | (0x1 << CHRK_SB_PGD_PORT_TX2_FM); @@ -105,6 +120,18 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); goto error; } + } else if (port_num == CHRK_SB_PGD_PORT_TX_SCO) { + /* SCO Tx */ + reg_val = 0x1 << CHRK_SB_PGD_PORT_TX_SCO; + reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); + BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)", + reg_val, reg); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", + ret, reg); + goto error; + } } /* Enable Tx port hw auto recovery for underrun or overrun error */ diff --git a/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c b/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c index 5a2edb0ea518..690e6f546e60 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c @@ -733,6 +733,35 @@ static void a5xx_snapshot_indexed_registers(struct msm_gpu *gpu, } } +static void a5xx_snapshot_preemption(struct msm_gpu *gpu, struct msm_snapshot + *snapshot) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); + struct msm_snapshot_gpu_object header = { + .type = SNAPSHOT_GPU_OBJECT_GLOBAL, + .size = A5XX_PREEMPT_RECORD_SIZE >> 2, + .pt_base = 0, + }; + int index; + + if (gpu->nr_rings <= 1) + return; + + for (index = 0; index < gpu->nr_rings; index++) { + + header.gpuaddr = a5xx_gpu->preempt_iova[index]; + + if (!SNAPSHOT_HEADER(snapshot, header, + SNAPSHOT_SECTION_GPU_OBJECT_V2, + A5XX_PREEMPT_RECORD_SIZE >> 2)) + return; + + SNAPSHOT_MEMCPY(snapshot, a5xx_gpu->preempt[index], + A5XX_PREEMPT_RECORD_SIZE); + } +} + int a5xx_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot) { struct crashdump crashdump = { 0 }; @@ -787,6 +816,9 @@ int a5xx_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot) /* CP MERCIU */ a5xx_snapshot_cp_merciu(gpu, snapshot); + /* Preemption records*/ + a5xx_snapshot_preemption(gpu, snapshot); + crashdump_destroy(gpu, &crashdump); snapshot->priv = NULL; diff --git a/drivers/gpu/drm/msm/msm_snapshot.h b/drivers/gpu/drm/msm/msm_snapshot.h index 247e1358c885..fd560b2129f1 100644 --- a/drivers/gpu/drm/msm/msm_snapshot.h +++ b/drivers/gpu/drm/msm/msm_snapshot.h @@ -71,8 +71,8 @@ static inline bool _snapshot_header(struct msm_snapshot *snapshot, */ #define SNAPSHOT_HEADER(_snapshot, _header, _id, _dwords) \ _snapshot_header((_snapshot), \ - (struct msm_snapshot_section_header *) &(header), \ - sizeof(header), (_dwords) << 2, (_id)) + (struct msm_snapshot_section_header *) &(_header), \ + sizeof(_header), (_dwords) << 2, (_id)) struct msm_gpu; diff --git a/drivers/gpu/drm/msm/msm_snapshot_api.h b/drivers/gpu/drm/msm/msm_snapshot_api.h index 9f0adb9ee784..7ad6f0498423 100644 --- a/drivers/gpu/drm/msm/msm_snapshot_api.h +++ b/drivers/gpu/drm/msm/msm_snapshot_api.h @@ -118,4 +118,17 @@ struct msm_snapshot_shader { __u32 size; } __packed; +#define SNAPSHOT_GPU_OBJECT_SHADER 1 +#define SNAPSHOT_GPU_OBJECT_IB 2 +#define SNAPSHOT_GPU_OBJECT_GENERIC 3 +#define SNAPSHOT_GPU_OBJECT_DRAW 4 +#define SNAPSHOT_GPU_OBJECT_GLOBAL 5 + +struct msm_snapshot_gpu_object { + struct msm_snapshot_section_header header; + __u32 type; + __u64 gpuaddr; + __u64 pt_base; + __u64 size; +} __packed; #endif diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 1ef37c727572..d037454fe7b8 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -73,7 +73,6 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, void *in, *out; unsigned long flags; int ret, err = 0; - unsigned long t; struct page *page; spin_lock_irqsave(&newchannel->lock, flags); @@ -183,11 +182,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, goto error1; } - t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ); - if (t == 0) { - err = -ETIMEDOUT; - goto error1; - } + wait_for_completion(&open_info->waitevent); spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); list_del(&open_info->msglistentry); @@ -375,7 +370,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, struct vmbus_channel_gpadl_header *gpadlmsg; struct vmbus_channel_gpadl_body *gpadl_body; struct vmbus_channel_msginfo *msginfo = NULL; - struct vmbus_channel_msginfo *submsginfo; + struct vmbus_channel_msginfo *submsginfo, *tmp; u32 msgcount; struct list_head *curr; u32 next_gpadl_handle; @@ -437,6 +432,13 @@ cleanup: list_del(&msginfo->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + if (msgcount > 1) { + list_for_each_entry_safe(submsginfo, tmp, &msginfo->submsglist, + msglistentry) { + kfree(submsginfo); + } + } + kfree(msginfo); return ret; } diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 4fc2e8836e60..2bbc53025549 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -429,7 +429,7 @@ int vmbus_post_msg(void *buffer, size_t buflen) union hv_connection_id conn_id; int ret = 0; int retries = 0; - u32 msec = 1; + u32 usec = 1; conn_id.asu32 = 0; conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID; @@ -462,9 +462,9 @@ int vmbus_post_msg(void *buffer, size_t buflen) } retries++; - msleep(msec); - if (msec < 2048) - msec *= 2; + udelay(usec); + if (usec < 2048) + usec *= 2; } return ret; } diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index ddbf7e7e0d98..8ce1f2e22912 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -305,9 +305,10 @@ void hv_cleanup(bool crash) hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); - if (!crash) + if (!crash) { vfree(hv_context.tsc_page); - hv_context.tsc_page = NULL; + hv_context.tsc_page = NULL; + } } #endif } diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 43af91362be5..354da7f207b7 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -430,16 +430,27 @@ struct dm_info_msg { * currently hot added. We hot add in multiples of 128M * chunks; it is possible that we may not be able to bring * online all the pages in the region. The range - * covered_end_pfn defines the pages that can + * covered_start_pfn:covered_end_pfn defines the pages that can * be brough online. */ struct hv_hotadd_state { struct list_head list; unsigned long start_pfn; + unsigned long covered_start_pfn; unsigned long covered_end_pfn; unsigned long ha_end_pfn; unsigned long end_pfn; + /* + * A list of gaps. + */ + struct list_head gap_list; +}; + +struct hv_hotadd_gap { + struct list_head list; + unsigned long start_pfn; + unsigned long end_pfn; }; struct balloon_state { @@ -595,18 +606,46 @@ static struct notifier_block hv_memory_nb = { .priority = 0 }; +/* Check if the particular page is backed and can be onlined and online it. */ +static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg) +{ + unsigned long cur_start_pgp; + unsigned long cur_end_pgp; + struct hv_hotadd_gap *gap; + + cur_start_pgp = (unsigned long)pfn_to_page(has->covered_start_pfn); + cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn); + + /* The page is not backed. */ + if (((unsigned long)pg < cur_start_pgp) || + ((unsigned long)pg >= cur_end_pgp)) + return; + + /* Check for gaps. */ + list_for_each_entry(gap, &has->gap_list, list) { + cur_start_pgp = (unsigned long) + pfn_to_page(gap->start_pfn); + cur_end_pgp = (unsigned long) + pfn_to_page(gap->end_pfn); + if (((unsigned long)pg >= cur_start_pgp) && + ((unsigned long)pg < cur_end_pgp)) { + return; + } + } -static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size) + /* This frame is currently backed; online the page. */ + __online_page_set_limits(pg); + __online_page_increment_counters(pg); + __online_page_free(pg); +} + +static void hv_bring_pgs_online(struct hv_hotadd_state *has, + unsigned long start_pfn, unsigned long size) { int i; - for (i = 0; i < size; i++) { - struct page *pg; - pg = pfn_to_page(start_pfn + i); - __online_page_set_limits(pg); - __online_page_increment_counters(pg); - __online_page_free(pg); - } + for (i = 0; i < size; i++) + hv_page_online_one(has, pfn_to_page(start_pfn + i)); } static void hv_mem_hot_add(unsigned long start, unsigned long size, @@ -682,26 +721,25 @@ static void hv_online_page(struct page *pg) list_for_each(cur, &dm_device.ha_region_list) { has = list_entry(cur, struct hv_hotadd_state, list); - cur_start_pgp = (unsigned long)pfn_to_page(has->start_pfn); - cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn); + cur_start_pgp = (unsigned long) + pfn_to_page(has->start_pfn); + cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn); - if (((unsigned long)pg >= cur_start_pgp) && - ((unsigned long)pg < cur_end_pgp)) { - /* - * This frame is currently backed; online the - * page. - */ - __online_page_set_limits(pg); - __online_page_increment_counters(pg); - __online_page_free(pg); - } + /* The page belongs to a different HAS. */ + if (((unsigned long)pg < cur_start_pgp) || + ((unsigned long)pg >= cur_end_pgp)) + continue; + + hv_page_online_one(has, pg); + break; } } -static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) +static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) { struct list_head *cur; struct hv_hotadd_state *has; + struct hv_hotadd_gap *gap; unsigned long residual, new_inc; if (list_empty(&dm_device.ha_region_list)) @@ -716,6 +754,24 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) */ if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn) continue; + + /* + * If the current start pfn is not where the covered_end + * is, create a gap and update covered_end_pfn. + */ + if (has->covered_end_pfn != start_pfn) { + gap = kzalloc(sizeof(struct hv_hotadd_gap), GFP_ATOMIC); + if (!gap) + return -ENOMEM; + + INIT_LIST_HEAD(&gap->list); + gap->start_pfn = has->covered_end_pfn; + gap->end_pfn = start_pfn; + list_add_tail(&gap->list, &has->gap_list); + + has->covered_end_pfn = start_pfn; + } + /* * If the current hot add-request extends beyond * our current limit; extend it. @@ -732,19 +788,10 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) has->end_pfn += new_inc; } - /* - * If the current start pfn is not where the covered_end - * is, update it. - */ - - if (has->covered_end_pfn != start_pfn) - has->covered_end_pfn = start_pfn; - - return true; - + return 1; } - return false; + return 0; } static unsigned long handle_pg_range(unsigned long pg_start, @@ -783,6 +830,8 @@ static unsigned long handle_pg_range(unsigned long pg_start, if (pgs_ol > pfn_cnt) pgs_ol = pfn_cnt; + has->covered_end_pfn += pgs_ol; + pfn_cnt -= pgs_ol; /* * Check if the corresponding memory block is already * online by checking its last previously backed page. @@ -791,10 +840,8 @@ static unsigned long handle_pg_range(unsigned long pg_start, */ if (start_pfn > has->start_pfn && !PageReserved(pfn_to_page(start_pfn - 1))) - hv_bring_pgs_online(start_pfn, pgs_ol); + hv_bring_pgs_online(has, start_pfn, pgs_ol); - has->covered_end_pfn += pgs_ol; - pfn_cnt -= pgs_ol; } if ((has->ha_end_pfn < has->end_pfn) && (pfn_cnt > 0)) { @@ -832,13 +879,19 @@ static unsigned long process_hot_add(unsigned long pg_start, unsigned long rg_size) { struct hv_hotadd_state *ha_region = NULL; + int covered; if (pfn_cnt == 0) return 0; - if (!dm_device.host_specified_ha_region) - if (pfn_covered(pg_start, pfn_cnt)) + if (!dm_device.host_specified_ha_region) { + covered = pfn_covered(pg_start, pfn_cnt); + if (covered < 0) + return 0; + + if (covered) goto do_pg_range; + } /* * If the host has specified a hot-add range; deal with it first. @@ -850,10 +903,12 @@ static unsigned long process_hot_add(unsigned long pg_start, return 0; INIT_LIST_HEAD(&ha_region->list); + INIT_LIST_HEAD(&ha_region->gap_list); list_add_tail(&ha_region->list, &dm_device.ha_region_list); ha_region->start_pfn = rg_start; ha_region->ha_end_pfn = rg_start; + ha_region->covered_start_pfn = pg_start; ha_region->covered_end_pfn = pg_start; ha_region->end_pfn = rg_start + rg_size; } @@ -1581,6 +1636,7 @@ static int balloon_remove(struct hv_device *dev) struct hv_dynmem_device *dm = hv_get_drvdata(dev); struct list_head *cur, *tmp; struct hv_hotadd_state *has; + struct hv_hotadd_gap *gap, *tmp_gap; if (dm->num_pages_ballooned != 0) pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned); @@ -1597,6 +1653,10 @@ static int balloon_remove(struct hv_device *dev) #endif list_for_each_safe(cur, tmp, &dm->ha_region_list) { has = list_entry(cur, struct hv_hotadd_state, list); + list_for_each_entry_safe(gap, tmp_gap, &has->gap_list, list) { + list_del(&gap->list); + kfree(gap); + } list_del(&has->list); kfree(has); } diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 43482ae1e049..1a2b2620421e 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1122,6 +1122,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, * Asus UX32VD 0x361f02 00, 15, 0e clickpad * Avatar AVIU-145A2 0x361f00 ? clickpad * Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons + * Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons * Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons * Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**) @@ -1528,6 +1529,13 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { }, }, { + /* Fujitsu LIFEBOOK E547 does not work with crc_enabled == 0 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E547"), + }, + }, + { /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 7f39ea93e110..dc44b40a85f3 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -522,6 +522,8 @@ static bool arm_smmu_is_slave_side_secure(struct arm_smmu_domain *smmu_domain); static bool arm_smmu_has_secure_vmid(struct arm_smmu_domain *smmu_domain); static bool arm_smmu_is_iova_coherent(struct iommu_domain *domain, dma_addr_t iova); +static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain, + dma_addr_t iova); static int arm_smmu_enable_s1_translations(struct arm_smmu_domain *smmu_domain); @@ -2536,6 +2538,23 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, return ret; } +static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain, + dma_addr_t iova) +{ + uint64_t ret; + unsigned long flags; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); + struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops; + + if (!ops) + return 0; + + flags = arm_smmu_pgtbl_lock(smmu_domain); + ret = ops->iova_to_pte(ops, iova); + arm_smmu_pgtbl_unlock(smmu_domain, flags); + return ret; +} + static size_t arm_smmu_map_sg(struct iommu_domain *domain, unsigned long iova, struct scatterlist *sg, unsigned int nents, int prot) { @@ -3437,6 +3456,7 @@ static struct iommu_ops arm_smmu_ops = { .enable_config_clocks = arm_smmu_enable_config_clocks, .disable_config_clocks = arm_smmu_disable_config_clocks, .is_iova_coherent = arm_smmu_is_iova_coherent, + .iova_to_pte = arm_smmu_iova_to_pte, }; static void arm_smmu_device_reset(struct arm_smmu_device *smmu) diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 6a8a9492c771..2d2583c78bdb 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -903,6 +903,19 @@ found_translation: return 0; } +static uint64_t arm_lpae_iova_get_pte(struct io_pgtable_ops *ops, + unsigned long iova) +{ + struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); + arm_lpae_iopte pte; + int lvl; + + if (!arm_lpae_iova_to_pte(data, iova, &lvl, &pte)) + return pte; + + return 0; +} + static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, unsigned long iova) { @@ -1033,6 +1046,7 @@ arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg) .unmap = arm_lpae_unmap, .iova_to_phys = arm_lpae_iova_to_phys, .is_iova_coherent = arm_lpae_is_iova_coherent, + .iova_to_pte = arm_lpae_iova_get_pte, }; return data; diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h index e6939c2212d4..2cf213514221 100644 --- a/drivers/iommu/io-pgtable.h +++ b/drivers/iommu/io-pgtable.h @@ -124,6 +124,8 @@ struct io_pgtable_ops { unsigned long iova); bool (*is_iova_coherent)(struct io_pgtable_ops *ops, unsigned long iova); + uint64_t (*iova_to_pte)(struct io_pgtable_ops *ops, + unsigned long iova); }; diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c index 28a817aba3fc..776e06facc11 100644 --- a/drivers/iommu/iommu-debug.c +++ b/drivers/iommu/iommu-debug.c @@ -470,6 +470,7 @@ static inline void iommu_debug_destroy_tracking(void) { } static LIST_HEAD(iommu_debug_devices); static struct dentry *debugfs_tests_dir; static u32 iters_per_op = 1; +static void *virt_addr; struct iommu_debug_device { struct device *dev; @@ -1537,6 +1538,68 @@ out_domain_free: return -EIO; } +static ssize_t __iommu_debug_dma_attach_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *offset) +{ + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + struct dma_iommu_mapping *dma_mapping; + ssize_t retval = -EINVAL; + int val; + + if (kstrtoint_from_user(ubuf, count, 0, &val)) { + pr_err("Invalid format. Expected a hex or decimal integer"); + retval = -EFAULT; + goto out; + } + + if (val) { + if (dev->archdata.mapping) + if (dev->archdata.mapping->domain) { + pr_err("Already attached.\n"); + retval = -EINVAL; + goto out; + } + if (WARN(dev->archdata.iommu, + "Attachment tracking out of sync with device\n")) { + retval = -EINVAL; + goto out; + } + + dma_mapping = arm_iommu_create_mapping(&platform_bus_type, 0, + (SZ_1G * 4ULL)); + + if (!dma_mapping) + goto out; + + if (arm_iommu_attach_device(dev, dma_mapping)) + goto out_release_mapping; + pr_err("Attached\n"); + } else { + if (!dev->archdata.mapping) { + pr_err("No mapping. Did you already attach?\n"); + retval = -EINVAL; + goto out; + } + if (!dev->archdata.mapping->domain) { + pr_err("No domain. Did you already attach?\n"); + retval = -EINVAL; + goto out; + } + arm_iommu_detach_device(dev); + arm_iommu_release_mapping(dev->archdata.mapping); + pr_err("Detached\n"); + } + retval = count; + return retval; + +out_release_mapping: + arm_iommu_release_mapping(dma_mapping); +out: + return retval; +} + static ssize_t __iommu_debug_attach_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset, @@ -1585,6 +1648,79 @@ out: return retval; } +static ssize_t iommu_debug_dma_attach_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *offset) +{ + return __iommu_debug_dma_attach_write(file, ubuf, count, offset); + +} + +static ssize_t iommu_debug_dma_attach_read(struct file *file, char __user *ubuf, + size_t count, loff_t *offset) +{ + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + char c[2]; + + if (*offset) + return 0; + + if (!dev->archdata.mapping) + c[0] = '0'; + else + c[0] = dev->archdata.mapping->domain ? '1' : '0'; + + c[1] = '\n'; + if (copy_to_user(ubuf, &c, 2)) { + pr_err("copy_to_user failed\n"); + return -EFAULT; + } + *offset = 1; /* non-zero means we're done */ + + return 2; +} + +static const struct file_operations iommu_debug_dma_attach_fops = { + .open = simple_open, + .write = iommu_debug_dma_attach_write, + .read = iommu_debug_dma_attach_read, +}; + +static ssize_t iommu_debug_virt_addr_read(struct file *file, char __user *ubuf, + size_t count, loff_t *offset) +{ + char buf[100]; + ssize_t retval; + size_t buflen; + + if (*offset) + return 0; + + memset(buf, 0, 100); + + if (!virt_addr) + strlcpy(buf, "FAIL\n", 100); + else + snprintf(buf, 100, "0x%pK\n", virt_addr); + + buflen = strlen(buf); + if (copy_to_user(ubuf, buf, buflen)) { + pr_err("Couldn't copy_to_user\n"); + retval = -EFAULT; + } else { + *offset = 1; /* non-zero means we're done */ + retval = buflen; + } + + return retval; +} + +static const struct file_operations iommu_debug_virt_addr_fops = { + .open = simple_open, + .read = iommu_debug_virt_addr_read, +}; + static ssize_t iommu_debug_attach_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) @@ -1635,6 +1771,75 @@ static const struct file_operations iommu_debug_secure_attach_fops = { .read = iommu_debug_attach_read, }; +static ssize_t iommu_debug_pte_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *offset) +{ + struct iommu_debug_device *ddev = file->private_data; + dma_addr_t iova; + + if (kstrtox_from_user(ubuf, count, 0, &iova)) { + pr_err("Invalid format for iova\n"); + ddev->iova = 0; + return -EINVAL; + } + + ddev->iova = iova; + pr_err("Saved iova=%pa for future PTE commands\n", &iova); + return count; +} + + +static ssize_t iommu_debug_pte_read(struct file *file, char __user *ubuf, + size_t count, loff_t *offset) +{ + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + uint64_t pte; + char buf[100]; + ssize_t retval; + size_t buflen; + + if (!dev->archdata.mapping) { + pr_err("No mapping. Did you already attach?\n"); + return -EINVAL; + } + if (!dev->archdata.mapping->domain) { + pr_err("No domain. Did you already attach?\n"); + return -EINVAL; + } + + if (*offset) + return 0; + + memset(buf, 0, 100); + + pte = iommu_iova_to_pte(dev->archdata.mapping->domain, + ddev->iova); + + if (!pte) + strlcpy(buf, "FAIL\n", 100); + else + snprintf(buf, 100, "pte=%016llx\n", pte); + + buflen = strlen(buf); + if (copy_to_user(ubuf, buf, buflen)) { + pr_err("Couldn't copy_to_user\n"); + retval = -EFAULT; + } else { + *offset = 1; /* non-zero means we're done */ + retval = buflen; + } + + return retval; +} + +static const struct file_operations iommu_debug_pte_fops = { + .open = simple_open, + .write = iommu_debug_pte_write, + .read = iommu_debug_pte_read, +}; + static ssize_t iommu_debug_atos_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) @@ -1700,6 +1905,55 @@ static const struct file_operations iommu_debug_atos_fops = { .read = iommu_debug_atos_read, }; +static ssize_t iommu_debug_dma_atos_read(struct file *file, char __user *ubuf, + size_t count, loff_t *offset) +{ + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + phys_addr_t phys; + char buf[100]; + ssize_t retval; + size_t buflen; + + if (!dev->archdata.mapping) { + pr_err("No mapping. Did you already attach?\n"); + return -EINVAL; + } + if (!dev->archdata.mapping->domain) { + pr_err("No domain. Did you already attach?\n"); + return -EINVAL; + } + + if (*offset) + return 0; + + memset(buf, 0, 100); + + phys = iommu_iova_to_phys_hard(dev->archdata.mapping->domain, + ddev->iova); + if (!phys) + strlcpy(buf, "FAIL\n", 100); + else + snprintf(buf, 100, "%pa\n", &phys); + + buflen = strlen(buf); + if (copy_to_user(ubuf, buf, buflen)) { + pr_err("Couldn't copy_to_user\n"); + retval = -EFAULT; + } else { + *offset = 1; /* non-zero means we're done */ + retval = buflen; + } + + return retval; +} + +static const struct file_operations iommu_debug_dma_atos_fops = { + .open = simple_open, + .write = iommu_debug_atos_write, + .read = iommu_debug_dma_atos_read, +}; + static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { @@ -1780,6 +2034,152 @@ static const struct file_operations iommu_debug_map_fops = { .write = iommu_debug_map_write, }; +static ssize_t iommu_debug_dma_map_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *offset) +{ + ssize_t retval = -EINVAL; + int ret; + char *comma1, *comma2; + char buf[100]; + unsigned long addr; + void *v_addr; + dma_addr_t iova; + size_t size; + unsigned int attr; + struct dma_attrs coherent_attr; + struct dma_attrs *dma_attrs = &coherent_attr; + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + + init_dma_attrs(dma_attrs); + + if (count >= 100) { + pr_err("Value too large\n"); + return -EINVAL; + } + + if (!dev->archdata.mapping) { + pr_err("No mapping. Did you already attach?\n"); + retval = -EINVAL; + goto out; + } + if (!dev->archdata.mapping->domain) { + pr_err("No domain. Did you already attach?\n"); + retval = -EINVAL; + goto out; + } + + memset(buf, 0, 100); + + if (copy_from_user(buf, ubuf, count)) { + pr_err("Couldn't copy from user\n"); + retval = -EFAULT; + goto out; + } + + comma1 = strnchr(buf, count, ','); + if (!comma1) + goto invalid_format; + + comma2 = strnchr(comma1 + 1, count, ','); + if (!comma2) + goto invalid_format; + + *comma1 = *comma2 = '\0'; + + if (kstrtoul(buf, 0, &addr)) + goto invalid_format; + v_addr = (void *)addr; + + if (kstrtosize_t(comma1 + 1, 0, &size)) + goto invalid_format; + + if (kstrtouint(comma2 + 1, 0, &attr)) + goto invalid_format; + + if (v_addr < virt_addr || v_addr > (virt_addr + SZ_1M - 1)) + goto invalid_addr; + + if (attr == 0) + dma_attrs = NULL; + else if (attr == 1) + dma_set_attr(DMA_ATTR_FORCE_COHERENT, dma_attrs); + else if (attr == 2) + dma_set_attr(DMA_ATTR_FORCE_NON_COHERENT, dma_attrs); + else + goto invalid_format; + + iova = dma_map_single_attrs(dev, v_addr, size, + DMA_TO_DEVICE, dma_attrs); + + if (dma_mapping_error(dev, iova)) { + pr_err("Failed to perform dma_map_single\n"); + ret = -EINVAL; + goto out; + } + + retval = count; + pr_err("Mapped 0x%p to %pa (len=0x%zx)\n", + v_addr, &iova, size); + ddev->iova = iova; + pr_err("Saved iova=%pa for future PTE commands\n", &iova); +out: + return retval; + +invalid_format: + pr_err("Invalid format. Expected: addr,len,dma attr where 'dma attr' is\n0: normal mapping\n1: force coherent\n2: force non-cohernet\n"); + return retval; + +invalid_addr: + pr_err("Invalid addr given! Address should be within 1MB size from start addr returned by doing 'cat virt_addr'.\n"); + return retval; +} + +static ssize_t iommu_debug_dma_map_read(struct file *file, char __user *ubuf, + size_t count, loff_t *offset) +{ + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + char buf[100]; + ssize_t retval; + size_t buflen; + dma_addr_t iova; + + if (!dev->archdata.mapping) { + pr_err("No mapping. Did you already attach?\n"); + return -EINVAL; + } + if (!dev->archdata.mapping->domain) { + pr_err("No domain. Did you already attach?\n"); + return -EINVAL; + } + + if (*offset) + return 0; + + memset(buf, 0, 100); + + iova = ddev->iova; + snprintf(buf, 100, "%pa\n", &iova); + + buflen = strlen(buf); + if (copy_to_user(ubuf, buf, buflen)) { + pr_err("Couldn't copy_to_user\n"); + retval = -EFAULT; + } else { + *offset = 1; /* non-zero means we're done */ + retval = buflen; + } + + return retval; +} + +static const struct file_operations iommu_debug_dma_map_fops = { + .open = simple_open, + .write = iommu_debug_dma_map_write, + .read = iommu_debug_dma_map_read, +}; + static ssize_t iommu_debug_unmap_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) @@ -1845,6 +2245,92 @@ static const struct file_operations iommu_debug_unmap_fops = { .write = iommu_debug_unmap_write, }; +static ssize_t iommu_debug_dma_unmap_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *offset) +{ + ssize_t retval = 0; + char *comma1, *comma2; + char buf[100]; + size_t size; + unsigned int attr; + dma_addr_t iova; + struct dma_attrs coherent_attr; + struct dma_attrs *dma_attrs = &coherent_attr; + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + + init_dma_attrs(dma_attrs); + + if (count >= 100) { + pr_err("Value too large\n"); + return -EINVAL; + } + + if (!dev->archdata.mapping) { + pr_err("No mapping. Did you already attach?\n"); + retval = -EINVAL; + goto out; + } + if (!dev->archdata.mapping->domain) { + pr_err("No domain. Did you already attach?\n"); + retval = -EINVAL; + goto out; + } + + memset(buf, 0, 100); + + if (copy_from_user(buf, ubuf, count)) { + pr_err("Couldn't copy from user\n"); + retval = -EFAULT; + goto out; + } + + comma1 = strnchr(buf, count, ','); + if (!comma1) + goto invalid_format; + + comma2 = strnchr(comma1 + 1, count, ','); + if (!comma2) + goto invalid_format; + + *comma1 = *comma2 = '\0'; + + if (kstrtoux(buf, 0, &iova)) + goto invalid_format; + + if (kstrtosize_t(comma1 + 1, 0, &size)) + goto invalid_format; + + if (kstrtouint(comma2 + 1, 0, &attr)) + goto invalid_format; + + if (attr == 0) + dma_attrs = NULL; + else if (attr == 1) + dma_set_attr(DMA_ATTR_FORCE_COHERENT, dma_attrs); + else if (attr == 2) + dma_set_attr(DMA_ATTR_FORCE_NON_COHERENT, dma_attrs); + else + goto invalid_format; + + dma_unmap_single_attrs(dev, iova, size, DMA_TO_DEVICE, dma_attrs); + + retval = count; + pr_err("Unmapped %pa (len=0x%zx)\n", &iova, size); +out: + return retval; + +invalid_format: + pr_err("Invalid format. Expected: iova,len, dma attr\n"); + return retval; +} + +static const struct file_operations iommu_debug_dma_unmap_fops = { + .open = simple_open, + .write = iommu_debug_dma_unmap_write, +}; + static ssize_t iommu_debug_config_clocks_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) @@ -1923,6 +2409,13 @@ static int snarf_iommu_devices(struct device *dev, const char *name) goto err_rmdir; } + if (!debugfs_create_file("virt_addr", S_IRUSR, dir, ddev, + &iommu_debug_virt_addr_fops)) { + pr_err("Couldn't create iommu/devices/%s/virt_addr debugfs file\n", + name); + goto err_rmdir; + } + if (!debugfs_create_file("profiling", S_IRUSR, dir, ddev, &iommu_debug_profiling_fops)) { pr_err("Couldn't create iommu/devices/%s/profiling debugfs file\n", @@ -1965,6 +2458,13 @@ static int snarf_iommu_devices(struct device *dev, const char *name) goto err_rmdir; } + if (!debugfs_create_file("dma_attach", S_IRUSR, dir, ddev, + &iommu_debug_dma_attach_fops)) { + pr_err("Couldn't create iommu/devices/%s/dma_attach debugfs file\n", + name); + goto err_rmdir; + } + if (!debugfs_create_file("attach", S_IRUSR, dir, ddev, &iommu_debug_attach_fops)) { pr_err("Couldn't create iommu/devices/%s/attach debugfs file\n", @@ -1986,6 +2486,13 @@ static int snarf_iommu_devices(struct device *dev, const char *name) goto err_rmdir; } + if (!debugfs_create_file("dma_atos", S_IWUSR, dir, ddev, + &iommu_debug_dma_atos_fops)) { + pr_err("Couldn't create iommu/devices/%s/dma_atos debugfs file\n", + name); + goto err_rmdir; + } + if (!debugfs_create_file("map", S_IWUSR, dir, ddev, &iommu_debug_map_fops)) { pr_err("Couldn't create iommu/devices/%s/map debugfs file\n", @@ -1993,6 +2500,13 @@ static int snarf_iommu_devices(struct device *dev, const char *name) goto err_rmdir; } + if (!debugfs_create_file("dma_map", S_IWUSR, dir, ddev, + &iommu_debug_dma_map_fops)) { + pr_err("Couldn't create iommu/devices/%s/dma_map debugfs file\n", + name); + goto err_rmdir; + } + if (!debugfs_create_file("unmap", S_IWUSR, dir, ddev, &iommu_debug_unmap_fops)) { pr_err("Couldn't create iommu/devices/%s/unmap debugfs file\n", @@ -2000,6 +2514,20 @@ static int snarf_iommu_devices(struct device *dev, const char *name) goto err_rmdir; } + if (!debugfs_create_file("dma_unmap", S_IWUSR, dir, ddev, + &iommu_debug_dma_unmap_fops)) { + pr_err("Couldn't create iommu/devices/%s/dma_unmap debugfs file\n", + name); + goto err_rmdir; + } + + if (!debugfs_create_file("pte", S_IWUSR, dir, ddev, + &iommu_debug_pte_fops)) { + pr_err("Couldn't create iommu/devices/%s/pte debugfs file\n", + name); + goto err_rmdir; + } + if (!debugfs_create_file("config_clocks", S_IWUSR, dir, ddev, &iommu_debug_config_clocks_fops)) { pr_err("Couldn't create iommu/devices/%s/config_clocks debugfs file\n", @@ -2054,6 +2582,11 @@ static int iommu_debug_init_tests(void) return -ENODEV; } + virt_addr = kzalloc(SZ_1M, GFP_KERNEL); + + if (!virt_addr) + return -ENOMEM; + return iommu_debug_populate_devices(); } diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index a77a45088b9d..b831796b5b7d 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1336,6 +1336,15 @@ phys_addr_t iommu_iova_to_phys_hard(struct iommu_domain *domain, return domain->ops->iova_to_phys_hard(domain, iova); } +uint64_t iommu_iova_to_pte(struct iommu_domain *domain, + dma_addr_t iova) +{ + if (unlikely(domain->ops->iova_to_pte == NULL)) + return 0; + + return domain->ops->iova_to_pte(domain, iova); +} + bool iommu_is_iova_coherent(struct iommu_domain *domain, dma_addr_t iova) { if (unlikely(domain->ops->is_iova_coherent == NULL)) diff --git a/drivers/md/Makefile b/drivers/md/Makefile index eef9097c60b3..41ba86576d04 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -71,10 +71,6 @@ ifeq ($(CONFIG_DM_VERITY_FEC),y) dm-verity-objs += dm-verity-fec.o endif -ifeq ($(CONFIG_DM_ANDROID_VERITY),y) -dm-verity-objs += dm-android-verity.o -endif - ifeq ($(CONFIG_DM_VERITY_AVB),y) dm-verity-objs += dm-verity-avb.o endif 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 caf6639f5151..24e3223a79d0 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -67,6 +67,10 @@ static int pix_overflow_error_count[VFE_MAX] = { 0 }; #define CDBG(fmt, args...) #endif +/* Backward interface compatibility for 3D THRESHOLD calculation */ +#define ISPIF_USE_DEFAULT_THRESHOLD (0) +#define ISPIF_CALCULATE_THRESHOLD (1) + static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable); static int ispif_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); static long msm_ispif_subdev_ioctl_unlocked(struct v4l2_subdev *sd, @@ -452,7 +456,7 @@ static int msm_ispif_reset_hw(struct ispif_device *ispif) /* This is set when device is 8974 */ ispif->clk_idx = 1; } - + memset(ispif->stereo_configured, 0, sizeof(ispif->stereo_configured)); atomic_set(&ispif->reset_trig[VFE0], 1); /* initiate reset of ISPIF */ msm_camera_io_w(ISPIF_RST_CMD_MASK, @@ -1009,21 +1013,29 @@ static int msm_ispif_config(struct ispif_device *ispif, } static void msm_ispif_config_stereo(struct ispif_device *ispif, - struct msm_ispif_param_data_ext *params) { + struct msm_ispif_param_data_ext *params, int use_line_width) { int i; enum msm_ispif_vfe_intf vfe_intf; + uint32_t stereo_3d_threshold = STEREO_DEFAULT_3D_THRESHOLD; for (i = 0; i < params->num; i++) { + vfe_intf = params->entries[i].vfe_intf; if (params->entries[i].intftype == PIX0 && - params->stereo_enable && - params->right_entries[i].csid < CSID_MAX) { - vfe_intf = params->entries[i].vfe_intf; + params->stereo_enable && + params->right_entries[i].csid < CSID_MAX && + !ispif->stereo_configured[vfe_intf]) { msm_camera_io_w_mb(0x3, ispif->base + ISPIF_VFE_m_OUTPUT_SEL(vfe_intf)); - msm_camera_io_w_mb(STEREO_DEFAULT_3D_THRESHOLD, + if (use_line_width && + (params->line_width[vfe_intf] > 0)) + stereo_3d_threshold = + (params->line_width[vfe_intf] + + 2 * 6 - 1) / (2 * 6); + msm_camera_io_w_mb(stereo_3d_threshold, ispif->base + ISPIF_VFE_m_3D_THRESHOLD(vfe_intf)); + ispif->stereo_configured[vfe_intf] = 1; } } } @@ -1132,6 +1144,8 @@ static int msm_ispif_stop_immediately(struct ispif_device *ispif, msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype, cid_mask, params->entries[i].vfe_intf, 0); if (params->stereo_enable) { + ispif->stereo_configured[ + params->entries[i].vfe_intf] = 0; cid_mask = msm_ispif_get_right_cids_mask_from_cfg( ¶ms->right_entries[i], params->entries[i].num_cids); @@ -1162,7 +1176,8 @@ static int msm_ispif_start_frame_boundary(struct ispif_device *ispif, rc = -EINVAL; return rc; } - msm_ispif_config_stereo(ispif, params); + + msm_ispif_config_stereo(ispif, params, ISPIF_USE_DEFAULT_THRESHOLD); msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params); return rc; @@ -1392,6 +1407,8 @@ static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif, if (rc < 0) goto end; if (cid_right_mask) { + ispif->stereo_configured[ + params->entries[i].vfe_intf] = 0; intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1); rc = readl_poll_timeout(ispif->base + intf_addr, stop_flag, @@ -1807,6 +1824,10 @@ static long msm_ispif_dispatch_cmd(enum ispif_cfg_type_t cmd, rc = msm_ispif_config2(ispif, params); msm_ispif_io_dump_reg(ispif); break; + case ISPIF_CFG_STEREO: + msm_ispif_config_stereo(ispif, params, + ISPIF_CALCULATE_THRESHOLD); + break; default: pr_err("%s: invalid cfg_type\n", __func__); rc = -EINVAL; diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h index 61e8f1dd7aff..3e6680c63ee5 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -77,5 +77,6 @@ struct ispif_device { int ispif_vdd_count; struct regulator *vfe_vdd[ISPIF_VFE_VDD_INFO_MAX]; int vfe_vdd_count; + int stereo_configured[VFE_MAX]; }; #endif diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index 8596366a70f0..f95cc37f5c2c 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -1015,11 +1015,9 @@ static int msm_open(struct file *filep) BUG_ON(!pvdev); /* !!! only ONE open is allowed !!! */ - if (atomic_read(&pvdev->opened)) + if (atomic_cmpxchg(&pvdev->opened, 0, 1)) return -EBUSY; - atomic_set(&pvdev->opened, 1); - spin_lock_irqsave(&msm_pid_lock, flags); msm_pid = get_pid(task_pid(current)); spin_unlock_irqrestore(&msm_pid_lock, flags); diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index 8402e31364b9..b7feb126f707 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c @@ -2542,9 +2542,29 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev, return -EINVAL; } - if (stripe_base == UINT_MAX || new_frame->num_strips > - (UINT_MAX - 1 - stripe_base) / stripe_size) { - pr_err("Invalid frame message,num_strips %d is large\n", + /* Stripe index starts at zero */ + if ((!new_frame->num_strips) || + (new_frame->first_stripe_index >= new_frame->num_strips) || + (new_frame->last_stripe_index >= new_frame->num_strips) || + (new_frame->first_stripe_index > + new_frame->last_stripe_index)) { + pr_err("Invalid frame message, #stripes=%d, stripe indices=[%d,%d]\n", + new_frame->num_strips, + new_frame->first_stripe_index, + new_frame->last_stripe_index); + return -EINVAL; + } + + if (!stripe_size) { + pr_err("Invalid frame message, invalid stripe_size (%d)!\n", + stripe_size); + return -EINVAL; + } + + if ((stripe_base == UINT_MAX) || + (new_frame->num_strips > + (UINT_MAX - 1 - stripe_base) / stripe_size)) { + pr_err("Invalid frame message, num_strips %d is large\n", new_frame->num_strips); return -EINVAL; } @@ -2785,13 +2805,14 @@ static int msm_cpp_cfg(struct cpp_device *cpp_dev, struct msm_cpp_frame_info_t *frame = NULL; struct msm_cpp_frame_info_t k_frame_info; int32_t rc = 0; - int32_t i = 0; - int32_t num_buff = sizeof(k_frame_info.output_buffer_info)/ + uint32_t i = 0; + uint32_t num_buff = sizeof(k_frame_info.output_buffer_info) / sizeof(struct msm_cpp_buffer_info_t); + if (copy_from_user(&k_frame_info, (void __user *)ioctl_ptr->ioctl_ptr, sizeof(k_frame_info))) - return -EFAULT; + return -EFAULT; frame = msm_cpp_get_frame(ioctl_ptr); if (!frame) { @@ -2953,8 +2974,9 @@ static int msm_cpp_validate_input(unsigned int cmd, void *arg, } *ioctl_ptr = arg; - if ((*ioctl_ptr == NULL) || - ((*ioctl_ptr)->ioctl_ptr == NULL)) { + if (((*ioctl_ptr) == NULL) || + ((*ioctl_ptr)->ioctl_ptr == NULL) || + ((*ioctl_ptr)->len == 0)) { pr_err("Error invalid ioctl argument cmd %u", cmd); return -EINVAL; } @@ -3503,13 +3525,18 @@ STREAM_BUFF_END: if (cpp_dev->iommu_state == CPP_IOMMU_STATE_DETACHED) { struct msm_camera_smmu_attach_type cpp_attach_info; + if (ioctl_ptr->len != + sizeof(struct msm_camera_smmu_attach_type)) { + rc = -EINVAL; + break; + } + memset(&cpp_attach_info, 0, sizeof(cpp_attach_info)); rc = msm_cpp_copy_from_ioctl_ptr(&cpp_attach_info, ioctl_ptr); if (rc < 0) { pr_err("CPP_IOMMU_ATTACH copy from user fail"); - ERR_COPY_FROM_USER(); - return -EINVAL; + break; } cpp_dev->security_mode = cpp_attach_info.attach; @@ -3538,16 +3565,20 @@ STREAM_BUFF_END: case VIDIOC_MSM_CPP_IOMMU_DETACH: { if ((cpp_dev->iommu_state == CPP_IOMMU_STATE_ATTACHED) && (cpp_dev->stream_cnt == 0)) { - struct msm_camera_smmu_attach_type cpp_attach_info; + if (ioctl_ptr->len != + sizeof(struct msm_camera_smmu_attach_type)) { + rc = -EINVAL; + break; + } + memset(&cpp_attach_info, 0, sizeof(cpp_attach_info)); rc = msm_cpp_copy_from_ioctl_ptr(&cpp_attach_info, ioctl_ptr); if (rc < 0) { pr_err("CPP_IOMMU_DETTACH copy from user fail"); - ERR_COPY_FROM_USER(); - return -EINVAL; + break; } cpp_dev->security_mode = cpp_attach_info.attach; @@ -3568,6 +3599,7 @@ STREAM_BUFF_END: } else { pr_err("%s:%d IOMMMU attach triggered in invalid state\n", __func__, __LINE__); + rc = -EINVAL; } break; } @@ -3883,6 +3915,7 @@ static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, struct msm_cpp_stream_buff_info_t k_cpp_buff_info; struct msm_cpp_frame_info32_t k32_frame_info; struct msm_cpp_frame_info_t k64_frame_info; + struct msm_camera_smmu_attach_type kb_cpp_smmu_attach_info; uint32_t identity_k = 0; bool is_copytouser_req = true; void __user *up = (void __user *)arg; @@ -4187,11 +4220,23 @@ static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, break; } case VIDIOC_MSM_CPP_IOMMU_ATTACH32: - cmd = VIDIOC_MSM_CPP_IOMMU_ATTACH; - break; case VIDIOC_MSM_CPP_IOMMU_DETACH32: - cmd = VIDIOC_MSM_CPP_IOMMU_DETACH; + { + if ((kp_ioctl.len != sizeof(struct msm_camera_smmu_attach_type)) + || (copy_from_user(&kb_cpp_smmu_attach_info, + (void __user *)kp_ioctl.ioctl_ptr, + sizeof(kb_cpp_smmu_attach_info)))) { + mutex_unlock(&cpp_dev->mutex); + return -EINVAL; + } + + kp_ioctl.ioctl_ptr = (void *)&kb_cpp_smmu_attach_info; + is_copytouser_req = false; + cmd = (cmd == VIDIOC_MSM_CPP_IOMMU_ATTACH32) ? + VIDIOC_MSM_CPP_IOMMU_ATTACH : + VIDIOC_MSM_CPP_IOMMU_DETACH; break; + } case MSM_SD_NOTIFY_FREEZE: break; case MSM_SD_UNNOTIFY_FREEZE: @@ -4202,7 +4247,8 @@ static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, default: pr_err_ratelimited("%s: unsupported compat type :%x LOAD %lu\n", __func__, cmd, VIDIOC_MSM_CPP_LOAD_FIRMWARE); - break; + mutex_unlock(&cpp_dev->mutex); + return -EINVAL; } mutex_unlock(&cpp_dev->mutex); @@ -4233,7 +4279,7 @@ static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, default: pr_err_ratelimited("%s: unsupported compat type :%d\n", __func__, cmd); - break; + return -EINVAL; } if (is_copytouser_req) { diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c index b7af80854420..e3f23caac5b8 100644 --- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c +++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c @@ -851,6 +851,7 @@ static long audio_aio_process_event_req_compat(struct q6audio_aio *audio, long rc; struct msm_audio_event32 usr_evt_32; struct msm_audio_event usr_evt; + memset(&usr_evt, 0, sizeof(struct msm_audio_event)); if (copy_from_user(&usr_evt_32, arg, sizeof(struct msm_audio_event32))) { @@ -860,6 +861,11 @@ static long audio_aio_process_event_req_compat(struct q6audio_aio *audio, usr_evt.timeout_ms = usr_evt_32.timeout_ms; rc = audio_aio_process_event_req_common(audio, &usr_evt); + if (rc < 0) { + pr_err("%s: audio process event failed, rc = %ld", + __func__, rc); + return rc; + } usr_evt_32.event_type = usr_evt.event_type; switch (usr_evt_32.event_type) { diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 618617f3e867..ad21276c8d9e 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -95,9 +95,11 @@ static int uid_cputime_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; struct task_struct *task, *temp; + struct user_namespace *user_ns = current_user_ns(); cputime_t utime; cputime_t stime; unsigned long bkt; + uid_t uid; rt_mutex_lock(&uid_lock); @@ -108,14 +110,13 @@ static int uid_cputime_show(struct seq_file *m, void *v) read_lock(&tasklist_lock); do_each_thread(temp, task) { - uid_entry = find_or_register_uid(from_kuid_munged( - current_user_ns(), task_uid(task))); + uid = from_kuid_munged(user_ns, task_uid(task)); + uid_entry = find_or_register_uid(uid); if (!uid_entry) { read_unlock(&tasklist_lock); rt_mutex_unlock(&uid_lock); pr_err("%s: failed to find the uid_entry for uid %d\n", - __func__, from_kuid_munged(current_user_ns(), - task_uid(task))); + __func__, uid); return -ENOMEM; } task_cputime_adjusted(task, &utime, &stime); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 41f0935440fd..c462eee4a5f7 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -3944,12 +3944,10 @@ static void mmc_hw_reset_for_init(struct mmc_host *host) */ int mmc_cmdq_hw_reset(struct mmc_host *host) { - if (!host->bus_ops->power_restore) - return -EOPNOTSUPP; + if (!host->bus_ops->reset) + return -EOPNOTSUPP; - mmc_power_cycle(host, host->ocr_avail); - mmc_select_voltage(host, host->card->ocr); - return host->bus_ops->power_restore(host); + return host->bus_ops->reset(host); } EXPORT_SYMBOL(mmc_cmdq_hw_reset); @@ -3969,8 +3967,9 @@ int mmc_hw_reset(struct mmc_host *host) ret = host->bus_ops->reset(host); mmc_bus_put(host); - if (ret != -EOPNOTSUPP) - pr_warn("%s: tried to reset card\n", mmc_hostname(host)); + if (ret) + pr_warn("%s: tried to reset card, got error %d\n", + mmc_hostname(host), ret); return ret; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index afdf70000651..691287125895 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -2892,23 +2892,42 @@ EXPORT_SYMBOL(mmc_can_reset); static int mmc_reset(struct mmc_host *host) { struct mmc_card *card = host->card; + int ret; + + if ((host->caps & MMC_CAP_HW_RESET) && host->ops->hw_reset && + mmc_can_reset(card)) { + /* If the card accept RST_n signal, send it. */ + mmc_set_clock(host, host->f_init); + host->ops->hw_reset(host); + /* Set initial state and call mmc_set_ios */ + mmc_set_initial_state(host); + } else { + /* Do a brute force power cycle */ + mmc_power_cycle(host, card->ocr); + } - if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) - return -EOPNOTSUPP; - - if (!mmc_can_reset(card)) - return -EOPNOTSUPP; + /* Suspend clk scaling to avoid switching frequencies intermittently */ - mmc_host_clk_hold(host); - mmc_set_clock(host, host->f_init); + ret = mmc_suspend_clk_scaling(host); + if (ret) { + pr_err("%s: %s: fail to suspend clock scaling (%d)\n", + mmc_hostname(host), __func__, ret); + return ret; + } - host->ops->hw_reset(host); + ret = mmc_init_card(host, host->card->ocr, host->card); + if (ret) { + pr_err("%s: %s: mmc_init_card failed (%d)\n", + mmc_hostname(host), __func__, ret); + return ret; + } - /* Set initial state and call mmc_set_ios */ - mmc_set_initial_state(host); - mmc_host_clk_release(host); + ret = mmc_resume_clk_scaling(host); + if (ret) + pr_err("%s: %s: fail to resume clock scaling (%d)\n", + mmc_hostname(host), __func__, ret); - return mmc_init_card(host, card->ocr, card); + return ret; } static const struct mmc_bus_ops mmc_ops = { diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 1f1582f6cccb..8d838779fd1b 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -804,6 +804,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host, switch (uhs) { case MMC_TIMING_UHS_SDR50: + case MMC_TIMING_UHS_DDR50: pinctrl = imx_data->pins_100mhz; break; case MMC_TIMING_UHS_SDR104: diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index 0134ba32a057..39712560b4c1 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -148,11 +148,11 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, return err; } - if (bytes == 0) { - err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL); - if (err) - return err; + err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL); + if (err) + return err; + if (bytes == 0) { err = clear_update_marker(ubi, vol, 0); if (err) return err; diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index 6df3ee561d52..515aa3f993f3 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -836,25 +836,30 @@ static int hfa384x_get_rid(struct net_device *dev, u16 rid, void *buf, int len, spin_lock_bh(&local->baplock); res = hfa384x_setup_bap(dev, BAP0, rid, 0); - if (!res) - res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec)); + if (res) + goto unlock; + + res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec)); + if (res) + goto unlock; if (le16_to_cpu(rec.len) == 0) { /* RID not available */ res = -ENODATA; + goto unlock; } rlen = (le16_to_cpu(rec.len) - 1) * 2; - if (!res && exact_len && rlen != len) { + if (exact_len && rlen != len) { printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: " "rid=0x%04x, len=%d (expected %d)\n", dev->name, rid, rlen, len); res = -ENODATA; } - if (!res) - res = hfa384x_from_bap(dev, BAP0, buf, len); + res = hfa384x_from_bap(dev, BAP0, buf, len); +unlock: spin_unlock_bh(&local->baplock); mutex_unlock(&local->rid_bap_mtx); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c index 57fa2465c9ea..e49bdc8c7083 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c @@ -1205,6 +1205,8 @@ int ipa3_connect_wdi_pipe(struct ipa_wdi_in_params *in, IPADBG("Skipping endpoint configuration.\n"); } + ipa3_enable_data_path(ipa_ep_idx); + out->clnt_hdl = ipa_ep_idx; if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(in->sys.client)) diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index 8d038ba0770d..acea4f213484 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -497,13 +497,13 @@ static size_t store_dload_mode(struct kobject *kobj, struct attribute *attr, if (sysfs_streq(buf, "full")) { dload_type = SCM_DLOAD_FULLDUMP; } else if (sysfs_streq(buf, "mini")) { - if (!msm_minidump_enabled()) { - pr_info("Minidump is not enabled\n"); + if (!minidump_enabled) { + pr_err("Minidump is not enabled\n"); return -ENODEV; } dload_type = SCM_DLOAD_MINIDUMP; } else { - pr_info("Invalid value. Use 'full' or 'mini'\n"); + pr_err("Invalid value. Use 'full' or 'mini'\n"); return -EINVAL; } diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index 024c968e484a..94b9e9c4d912 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -427,6 +427,7 @@ static enum power_supply_property smb2_usb_props[] = { POWER_SUPPLY_PROP_PE_START, POWER_SUPPLY_PROP_CTM_CURRENT_MAX, POWER_SUPPLY_PROP_HW_CURRENT_MAX, + POWER_SUPPLY_PROP_REAL_TYPE, }; static int smb2_usb_get_prop(struct power_supply *psy, @@ -446,6 +447,16 @@ static int smb2_usb_get_prop(struct power_supply *psy, break; case POWER_SUPPLY_PROP_ONLINE: rc = smblib_get_prop_usb_online(chg, val); + if (!val->intval) + break; + + rc = smblib_get_prop_typec_mode(chg, val); + if ((val->intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || + chg->micro_usb_mode) && + chg->real_charger_type == POWER_SUPPLY_TYPE_USB) + val->intval = 0; + else + val->intval = 1; break; case POWER_SUPPLY_PROP_VOLTAGE_MIN: val->intval = chg->voltage_min_uv; @@ -463,10 +474,13 @@ static int smb2_usb_get_prop(struct power_supply *psy, rc = smblib_get_prop_usb_current_max(chg, val); break; case POWER_SUPPLY_PROP_TYPE: + val->intval = POWER_SUPPLY_TYPE_USB_PD; + break; + case POWER_SUPPLY_PROP_REAL_TYPE: if (chip->bad_part) - val->intval = POWER_SUPPLY_TYPE_USB; + val->intval = POWER_SUPPLY_TYPE_USB_PD; else - val->intval = chg->usb_psy_desc.type; + val->intval = chg->real_charger_type; break; case POWER_SUPPLY_PROP_TYPEC_MODE: if (chg->micro_usb_mode) @@ -608,7 +622,7 @@ static int smb2_init_usb_psy(struct smb2 *chip) struct smb_charger *chg = &chip->chg; chg->usb_psy_desc.name = "usb"; - chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN; + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD; chg->usb_psy_desc.properties = smb2_usb_props; chg->usb_psy_desc.num_properties = ARRAY_SIZE(smb2_usb_props); chg->usb_psy_desc.get_property = smb2_usb_get_prop; @@ -628,6 +642,97 @@ static int smb2_init_usb_psy(struct smb2 *chip) return 0; } +/******************************** + * USB PC_PORT PSY REGISTRATION * + ********************************/ +static enum power_supply_property smb2_usb_port_props[] = { + POWER_SUPPLY_PROP_TYPE, + POWER_SUPPLY_PROP_ONLINE, +}; + +static int smb2_usb_port_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct smb2 *chip = power_supply_get_drvdata(psy); + struct smb_charger *chg = &chip->chg; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_TYPE: + val->intval = POWER_SUPPLY_TYPE_USB; + break; + case POWER_SUPPLY_PROP_ONLINE: + rc = smblib_get_prop_usb_online(chg, val); + if (!val->intval) + break; + + rc = smblib_get_prop_typec_mode(chg, val); + if ((val->intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || + chg->micro_usb_mode) && + chg->real_charger_type == POWER_SUPPLY_TYPE_USB) + val->intval = 1; + else + val->intval = 0; + break; + default: + pr_err_ratelimited("Get prop %d is not supported in pc_port\n", + psp); + return -EINVAL; + } + + if (rc < 0) { + pr_debug("Couldn't get prop %d rc = %d\n", psp, rc); + return -ENODATA; + } + + return 0; +} + +static int smb2_usb_port_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + int rc = 0; + + switch (psp) { + default: + pr_err_ratelimited("Set prop %d is not supported in pc_port\n", + psp); + rc = -EINVAL; + break; + } + + return rc; +} + +static const struct power_supply_desc usb_port_psy_desc = { + .name = "pc_port", + .type = POWER_SUPPLY_TYPE_USB, + .properties = smb2_usb_port_props, + .num_properties = ARRAY_SIZE(smb2_usb_port_props), + .get_property = smb2_usb_port_get_prop, + .set_property = smb2_usb_port_set_prop, +}; + +static int smb2_init_usb_port_psy(struct smb2 *chip) +{ + struct power_supply_config usb_port_cfg = {}; + struct smb_charger *chg = &chip->chg; + + usb_port_cfg.drv_data = chip; + usb_port_cfg.of_node = chg->dev->of_node; + chg->usb_port_psy = power_supply_register(chg->dev, + &usb_port_psy_desc, + &usb_port_cfg); + if (IS_ERR(chg->usb_port_psy)) { + pr_err("Couldn't register USB pc_port power supply\n"); + return PTR_ERR(chg->usb_port_psy); + } + + return 0; +} + /***************************** * USB MAIN PSY REGISTRATION * *****************************/ @@ -1061,6 +1166,9 @@ static int smb2_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_DP_DM: rc = smblib_dp_dm(chg, val->intval); break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: + rc = smblib_set_prop_input_current_limited(chg, val); + break; default: rc = -EINVAL; } @@ -1078,6 +1186,7 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_PARALLEL_DISABLE: case POWER_SUPPLY_PROP_DP_DM: case POWER_SUPPLY_PROP_RERUN_AICL: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: return 1; default: break; @@ -2266,7 +2375,13 @@ static int smb2_probe(struct platform_device *pdev) rc = smb2_init_usb_main_psy(chip); if (rc < 0) { - pr_err("Couldn't initialize usb psy rc=%d\n", rc); + pr_err("Couldn't initialize usb main psy rc=%d\n", rc); + goto cleanup; + } + + rc = smb2_init_usb_port_psy(chip); + if (rc < 0) { + pr_err("Couldn't initialize usb pc_port psy rc=%d\n", rc); goto cleanup; } @@ -2324,7 +2439,7 @@ static int smb2_probe(struct platform_device *pdev) device_init_wakeup(chg->dev, true); pr_info("QPNP SMB2 probed successfully usb:present=%d type=%d batt:present = %d health = %d charge = %d\n", - usb_present, chg->usb_psy_desc.type, + usb_present, chg->real_charger_type, batt_present, batt_health, batt_charge_type); return rc; @@ -2336,6 +2451,8 @@ cleanup: power_supply_unregister(chg->usb_main_psy); if (chg->usb_psy) power_supply_unregister(chg->usb_psy); + if (chg->usb_port_psy) + power_supply_unregister(chg->usb_port_psy); if (chg->dc_psy) power_supply_unregister(chg->dc_psy); if (chg->vconn_vreg && chg->vconn_vreg->rdev) @@ -2356,6 +2473,7 @@ static int smb2_remove(struct platform_device *pdev) power_supply_unregister(chg->batt_psy); power_supply_unregister(chg->usb_psy); + power_supply_unregister(chg->usb_port_psy); regulator_unregister(chg->vconn_vreg->rdev); regulator_unregister(chg->vbus_vreg->rdev); diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index ecfb532e5b3c..a191391a3904 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -550,9 +550,9 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) /* if PD is active, APSD is disabled so won't have a valid result */ if (chg->pd_active) - chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD; + chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD; else - chg->usb_psy_desc.type = apsd_result->pst; + chg->real_charger_type = apsd_result->pst; smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n", apsd_result->name, chg->pd_active); @@ -858,7 +858,7 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) /* configure current */ if (pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT - && (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB)) { + && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) { rc = set_sdp_current(chg, icl_ua); if (rc < 0) { smblib_err(chg, "Couldn't set SDP ICL rc=%d\n", rc); @@ -879,10 +879,10 @@ override_suspend_config: /* remove override if no voters - hw defaults is desired */ override = false; } else if (pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { - if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB) + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB) /* For std cable with type = SDP never override */ override = false; - else if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_CDP + else if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_CDP && icl_ua == 1500000) /* * For std cable with type = CDP override only if @@ -1707,6 +1707,11 @@ int smblib_get_prop_input_current_limited(struct smb_charger *chg, u8 stat; int rc; + if (chg->fake_input_current_limited >= 0) { + val->intval = chg->fake_input_current_limited; + return 0; + } + rc = smblib_read(chg, AICL_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read AICL_STATUS rc=%d\n", rc); @@ -1898,6 +1903,13 @@ int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg, return rc; } +int smblib_set_prop_input_current_limited(struct smb_charger *chg, + const union power_supply_propval *val) +{ + chg->fake_input_current_limited = val->intval; + return 0; +} + int smblib_rerun_aicl(struct smb_charger *chg) { int rc, settled_icl_ua; @@ -3314,7 +3326,7 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg) int pulses; power_supply_changed(chg->usb_main_psy); - if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_HVDCP) { + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) { rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, @@ -3342,7 +3354,7 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg) } } - if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_HVDCP_3) { + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3) { rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, @@ -4471,6 +4483,7 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work); chg->fake_capacity = -EINVAL; + chg->fake_input_current_limited = -EINVAL; switch (chg->mode) { case PARALLEL_MASTER: diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index d14ea86bf0c6..5dc60ecb9436 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -244,6 +244,8 @@ struct smb_charger { struct power_supply *bms_psy; struct power_supply_desc usb_psy_desc; struct power_supply *usb_main_psy; + struct power_supply *usb_port_psy; + enum power_supply_type real_charger_type; /* notifiers */ struct notifier_block nb; @@ -316,6 +318,7 @@ struct smb_charger { bool typec_present; u8 typec_status[5]; bool typec_legacy_valid; + int fake_input_current_limited; /* workaround flag */ u32 wa_flags; @@ -419,6 +422,8 @@ int smblib_set_prop_batt_capacity(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_system_temp_level(struct smb_charger *chg, const union power_supply_propval *val); +int smblib_set_prop_input_current_limited(struct smb_charger *chg, + const union power_supply_propval *val); int smblib_get_prop_dc_present(struct smb_charger *chg, union power_supply_propval *val); diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h index 3f260a407721..167666a8c548 100644 --- a/drivers/power/supply/qcom/smb-reg.h +++ b/drivers/power/supply/qcom/smb-reg.h @@ -1025,14 +1025,4 @@ enum { /* CHGR FREQ Peripheral registers */ #define FREQ_CLK_DIV_REG (CHGR_FREQ_BASE + 0x50) -/* SMB1355 specific registers */ -#define SMB1355_TEMP_COMP_STATUS_REG (MISC_BASE + 0x07) -#define SKIN_TEMP_RST_HOT_BIT BIT(6) -#define SKIN_TEMP_UB_HOT_BIT BIT(5) -#define SKIN_TEMP_LB_HOT_BIT BIT(4) -#define DIE_TEMP_TSD_HOT_BIT BIT(3) -#define DIE_TEMP_RST_HOT_BIT BIT(2) -#define DIE_TEMP_UB_HOT_BIT BIT(1) -#define DIE_TEMP_LB_HOT_BIT BIT(0) - #endif /* __SMB2_CHARGER_REG_H */ diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index e4a876126444..694591c3ec56 100644 --- a/drivers/power/supply/qcom/smb138x-charger.c +++ b/drivers/power/supply/qcom/smb138x-charger.c @@ -104,8 +104,6 @@ struct smb138x { struct smb_dt_props dt; struct power_supply *parallel_psy; u32 wa_flags; - struct pmic_revid_data *pmic_rev_id; - char *name; }; static int __debug_mask; @@ -169,14 +167,6 @@ static int smb138x_parse_dt(struct smb138x *chip) if (rc < 0) chip->dt.pl_mode = POWER_SUPPLY_PL_USBMID_USBMID; - /* check that smb1355 is configured to run in mid-mid mode */ - if (chip->pmic_rev_id->pmic_subtype == SMB1355_SUBTYPE - && chip->dt.pl_mode != POWER_SUPPLY_PL_USBMID_USBMID) { - pr_err("Smb1355 can only run in MID-MID mode, saw = %d mode\n", - chip->dt.pl_mode); - return -EINVAL; - } - chip->dt.suspend_input = of_property_read_bool(node, "qcom,suspend-input"); @@ -489,30 +479,6 @@ static int smb138x_init_batt_psy(struct smb138x *chip) * PARALLEL PSY REGISTRATION * *****************************/ -static int smb1355_get_prop_connector_health(struct smb138x *chip) -{ - struct smb_charger *chg = &chip->chg; - u8 temp; - int rc; - - rc = smblib_read(chg, SMB1355_TEMP_COMP_STATUS_REG, &temp); - if (rc < 0) { - pr_err("Couldn't read comp stat reg rc = %d\n", rc); - return POWER_SUPPLY_HEALTH_UNKNOWN; - } - - if (temp & SKIN_TEMP_RST_HOT_BIT) - return POWER_SUPPLY_HEALTH_OVERHEAT; - - if (temp & SKIN_TEMP_UB_HOT_BIT) - return POWER_SUPPLY_HEALTH_HOT; - - if (temp & SKIN_TEMP_LB_HOT_BIT) - return POWER_SUPPLY_HEALTH_WARM; - - return POWER_SUPPLY_HEALTH_COOL; -} - static int smb138x_get_prop_connector_health(struct smb138x *chip) { struct smb_charger *chg = &chip->chg; @@ -570,54 +536,18 @@ static enum power_supply_property smb138x_parallel_props[] = { POWER_SUPPLY_PROP_PIN_ENABLED, POWER_SUPPLY_PROP_INPUT_SUSPEND, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, + POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, - POWER_SUPPLY_PROP_MODEL_NAME, - POWER_SUPPLY_PROP_PARALLEL_MODE, - POWER_SUPPLY_PROP_CONNECTOR_HEALTH, - POWER_SUPPLY_PROP_SET_SHIP_MODE, + POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CHARGER_TEMP, POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CURRENT_MAX, -}; - -static enum power_supply_property smb1355_parallel_props[] = { - POWER_SUPPLY_PROP_CHARGE_TYPE, - POWER_SUPPLY_PROP_CHARGING_ENABLED, - POWER_SUPPLY_PROP_PIN_ENABLED, - POWER_SUPPLY_PROP_INPUT_SUSPEND, - POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, - POWER_SUPPLY_PROP_VOLTAGE_MAX, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_PARALLEL_MODE, POWER_SUPPLY_PROP_CONNECTOR_HEALTH, POWER_SUPPLY_PROP_SET_SHIP_MODE, - POWER_SUPPLY_PROP_CHARGER_TEMP, - POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, }; -static int smb138x_get_parallel_charging(struct smb138x *chip, int *disabled) -{ - struct smb_charger *chg = &chip->chg; - int rc = 0; - u8 cfg2; - - rc = smblib_read(chg, CHGR_CFG2_REG, &cfg2); - if (rc < 0) { - pr_err("Couldn't read en_cmg_reg rc=%d\n", rc); - return rc; - } - - if (cfg2 & CHG_EN_SRC_BIT) - *disabled = 0; - else - *disabled = 1; - - return 0; -} - static int smb138x_parallel_get_prop(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) @@ -644,7 +574,7 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, val->intval = !(temp & DISABLE_CHARGING_BIT); break; case POWER_SUPPLY_PROP_INPUT_SUSPEND: - rc = smb138x_get_parallel_charging(chip, &val->intval); + rc = smblib_get_usb_suspend(chg, &val->intval); break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) @@ -653,6 +583,14 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, else val->intval = 0; break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) + rc = smblib_get_charge_param(chg, &chg->param.usb_icl, + &val->intval); + else + val->intval = 0; + break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: rc = smblib_get_charge_param(chg, &chg->param.fv, &val->intval); break; @@ -660,46 +598,28 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, rc = smblib_get_charge_param(chg, &chg->param.fcc, &val->intval); break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + rc = smblib_get_prop_slave_current_now(chg, val); + break; + case POWER_SUPPLY_PROP_CHARGER_TEMP: + 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); + break; case POWER_SUPPLY_PROP_MODEL_NAME: - val->strval = chip->name; + val->strval = "smb138x"; break; case POWER_SUPPLY_PROP_PARALLEL_MODE: val->intval = chip->dt.pl_mode; break; case POWER_SUPPLY_PROP_CONNECTOR_HEALTH: - if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE) - val->intval = smb138x_get_prop_connector_health(chip); - else - val->intval = smb1355_get_prop_connector_health(chip); + val->intval = smb138x_get_prop_connector_health(chip); break; case POWER_SUPPLY_PROP_SET_SHIP_MODE: /* Not in ship mode as long as device is active */ val->intval = 0; break; - case POWER_SUPPLY_PROP_CHARGER_TEMP: - if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE) - rc = smb138x_get_prop_charger_temp(chip, val); - else - rc = smblib_get_prop_charger_temp(chg, val); - break; - case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX: - rc = smblib_get_prop_charger_temp_max(chg, val); - break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE) - rc = smblib_get_prop_slave_current_now(chg, val); - else - rc = -ENODATA; - break; - case POWER_SUPPLY_PROP_CURRENT_MAX: - if ((chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE) - && ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) - || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))) - rc = smblib_get_charge_param(chg, &chg->param.usb_icl, - &val->intval); - else - rc = -ENODATA; - break; default: pr_err("parallel power supply get prop %d not supported\n", prop); @@ -714,33 +634,28 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, return rc; } -static int smb138x_set_parallel_charging(struct smb138x *chip, bool disable) +static int smb138x_set_parallel_suspend(struct smb138x *chip, bool suspend) { struct smb_charger *chg = &chip->chg; int rc = 0; rc = smblib_masked_write(chg, WD_CFG_REG, WDOG_TIMER_EN_BIT, - disable ? 0 : WDOG_TIMER_EN_BIT); + suspend ? 0 : WDOG_TIMER_EN_BIT); if (rc < 0) { pr_err("Couldn't %s watchdog rc=%d\n", - disable ? "disable" : "enable", rc); - disable = true; + suspend ? "disable" : "enable", rc); + suspend = true; } - /* - * Configure charge enable for high polarity and - * When disabling charging set it to cmd register control(cmd bit=0) - * When enabling charging set it to pin control - */ - rc = smblib_masked_write(chg, CHGR_CFG2_REG, - CHG_EN_POLARITY_BIT | CHG_EN_SRC_BIT, - disable ? 0 : CHG_EN_SRC_BIT); + rc = smblib_masked_write(chg, USBIN_CMD_IL_REG, USBIN_SUSPEND_BIT, + suspend ? USBIN_SUSPEND_BIT : 0); if (rc < 0) { - pr_err("Couldn't configure charge enable source rc=%d\n", rc); + pr_err("Couldn't %s parallel charger rc=%d\n", + suspend ? "suspend" : "resume", rc); return rc; } - return 0; + return rc; } static int smb138x_parallel_set_prop(struct power_supply *psy, @@ -753,7 +668,7 @@ static int smb138x_parallel_set_prop(struct power_supply *psy, switch (prop) { case POWER_SUPPLY_PROP_INPUT_SUSPEND: - rc = smb138x_set_parallel_charging(chip, (bool)val->intval); + rc = smb138x_set_parallel_suspend(chip, (bool)val->intval); break; case POWER_SUPPLY_PROP_CURRENT_MAX: if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) @@ -788,7 +703,7 @@ static int smb138x_parallel_prop_is_writeable(struct power_supply *psy, return 0; } -static struct power_supply_desc parallel_psy_desc = { +static const struct power_supply_desc parallel_psy_desc = { .name = "parallel", .type = POWER_SUPPLY_TYPE_PARALLEL, .properties = smb138x_parallel_props, @@ -816,28 +731,6 @@ static int smb138x_init_parallel_psy(struct smb138x *chip) return 0; } -static int smb1355_init_parallel_psy(struct smb138x *chip) -{ - struct power_supply_config parallel_cfg = {}; - struct smb_charger *chg = &chip->chg; - - parallel_cfg.drv_data = chip; - parallel_cfg.of_node = chg->dev->of_node; - - /* change to smb1355's property list */ - parallel_psy_desc.properties = smb1355_parallel_props; - parallel_psy_desc.num_properties = ARRAY_SIZE(smb1355_parallel_props); - chip->parallel_psy = devm_power_supply_register(chg->dev, - ¶llel_psy_desc, - ¶llel_cfg); - if (IS_ERR(chip->parallel_psy)) { - pr_err("Couldn't register parallel power supply\n"); - return PTR_ERR(chip->parallel_psy); - } - - return 0; -} - /****************************** * VBUS REGULATOR REGISTRATION * ******************************/ @@ -971,32 +864,35 @@ static int smb138x_init_slave_hw(struct smb138x *chip) return rc; } - /* disable the charging path when under s/w control */ - rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG, - CHARGING_ENABLE_CMD_BIT, 0); + /* suspend parallel charging */ + rc = smb138x_set_parallel_suspend(chip, true); if (rc < 0) { - pr_err("Couldn't disable charging rc=%d\n", rc); + pr_err("Couldn't suspend parallel charging rc=%d\n", rc); return rc; } - /* disable parallel charging path */ - rc = smb138x_set_parallel_charging(chip, true); + /* initialize FCC to 0 */ + rc = smblib_set_charge_param(chg, &chg->param.fcc, 0); if (rc < 0) { - pr_err("Couldn't disable parallel path rc=%d\n", rc); + pr_err("Couldn't set 0 FCC rc=%d\n", rc); return rc; } - /* unsuspend parallel charging */ - rc = smblib_masked_write(chg, USBIN_CMD_IL_REG, USBIN_SUSPEND_BIT, 0); + /* enable the charging path */ + rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT, + CHARGING_ENABLE_CMD_BIT); if (rc < 0) { - pr_err("Couldn't unsuspend parallel charging rc=%d\n", rc); + pr_err("Couldn't enable charging rc=%d\n", rc); return rc; } - /* initialize FCC to 0 */ - rc = smblib_set_charge_param(chg, &chg->param.fcc, 0); + /* 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); if (rc < 0) { - pr_err("Couldn't set 0 FCC rc=%d\n", rc); + pr_err("Couldn't configure charge enable source rc=%d\n", + rc); return rc; } @@ -1154,6 +1050,7 @@ static int smb138x_init_hw(struct smb138x *chip) static int smb138x_setup_wa_flags(struct smb138x *chip) { + struct pmic_revid_data *pmic_rev_id; struct device_node *revid_dev_node; revid_dev_node = of_parse_phandle(chip->chg.dev->of_node, @@ -1163,8 +1060,8 @@ static int smb138x_setup_wa_flags(struct smb138x *chip) return -EINVAL; } - chip->pmic_rev_id = get_revid_data(revid_dev_node); - if (IS_ERR_OR_NULL(chip->pmic_rev_id)) { + pmic_rev_id = get_revid_data(revid_dev_node); + if (IS_ERR_OR_NULL(pmic_rev_id)) { /* * the revid peripheral must be registered, any failure * here only indicates that the rev-id module has not @@ -1173,14 +1070,14 @@ static int smb138x_setup_wa_flags(struct smb138x *chip) return -EPROBE_DEFER; } - switch (chip->pmic_rev_id->pmic_subtype) { + switch (pmic_rev_id->pmic_subtype) { case SMB1381_SUBTYPE: - if (chip->pmic_rev_id->rev4 < 2) /* SMB1381 rev 1.0 */ + if (pmic_rev_id->rev4 < 2) /* SMB1381 rev 1.0 */ chip->wa_flags |= OOB_COMP_WA_BIT; break; default: pr_err("PMIC subtype %d not supported\n", - chip->pmic_rev_id->pmic_subtype); + pmic_rev_id->pmic_subtype); return -EINVAL; } @@ -1478,7 +1375,6 @@ static int smb138x_master_probe(struct smb138x *chip) chg->param = v1_params; - chip->name = "smb1381"; rc = smblib_init(chg); if (rc < 0) { pr_err("Couldn't initialize smblib rc=%d\n", rc); @@ -1539,7 +1435,7 @@ static int smb138x_master_probe(struct smb138x *chip) return rc; } -static int smb1355_slave_probe(struct smb138x *chip) +static int smb138x_slave_probe(struct smb138x *chip) { struct smb_charger *chg = &chip->chg; int rc = 0; @@ -1552,55 +1448,6 @@ static int smb1355_slave_probe(struct smb138x *chip) goto cleanup; } - rc = smb138x_parse_dt(chip); - if (rc < 0) { - pr_err("Couldn't parse device tree rc=%d\n", rc); - goto cleanup; - } - - rc = smb138x_init_slave_hw(chip); - if (rc < 0) { - pr_err("Couldn't initialize hardware rc=%d\n", rc); - goto cleanup; - } - - rc = smb1355_init_parallel_psy(chip); - if (rc < 0) { - pr_err("Couldn't initialize parallel psy rc=%d\n", rc); - goto cleanup; - } - - rc = smb138x_determine_initial_slave_status(chip); - if (rc < 0) { - pr_err("Couldn't determine initial status rc=%d\n", rc); - goto cleanup; - } - - rc = smb138x_request_interrupts(chip); - if (rc < 0) { - pr_err("Couldn't request interrupts rc=%d\n", rc); - goto cleanup; - } - - return 0; - -cleanup: - smblib_deinit(chg); - return rc; -} - -static int smb1381_slave_probe(struct smb138x *chip) -{ - struct smb_charger *chg = &chip->chg; - int rc = 0; - - chg->param = v1_params; - - rc = smblib_init(chg); - if (rc < 0) { - pr_err("Couldn't initialize smblib rc=%d\n", rc); - goto cleanup; - } chg->iio.temp_max_chan = iio_channel_get(chg->dev, "charger_temp_max"); if (IS_ERR(chg->iio.temp_max_chan)) { rc = PTR_ERR(chg->iio.temp_max_chan); @@ -1668,71 +1515,25 @@ static int smb1381_slave_probe(struct smb138x *chip) goto cleanup; } - return 0; + return rc; cleanup: smblib_deinit(chg); + if (chip->parallel_psy) + power_supply_unregister(chip->parallel_psy); + if (chg->vbus_vreg && chg->vbus_vreg->rdev) + regulator_unregister(chg->vbus_vreg->rdev); return rc; } -static int slave_probe(struct smb138x *chip) -{ - struct device_node *revid_dev_node; - int rc = 0; - - revid_dev_node = of_parse_phandle(chip->chg.dev->of_node, - "qcom,pmic-revid", 0); - if (!revid_dev_node) { - pr_err("Missing qcom,pmic-revid property\n"); - return -EINVAL; - } - - chip->pmic_rev_id = get_revid_data(revid_dev_node); - if (IS_ERR_OR_NULL(chip->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 (chip->pmic_rev_id->pmic_subtype) { - case SMB1355_SUBTYPE: - chip->name = "smb1355"; - rc = smb1355_slave_probe(chip); - break; - case SMB1381_SUBTYPE: - chip->name = "smb1381"; - rc = smb1381_slave_probe(chip); - break; - default: - pr_err("Unsupported pmic subtype = 0x%02x\n", - chip->pmic_rev_id->pmic_subtype); - rc = -EINVAL; - } - - if (rc < 0) { - if (rc != -EPROBE_DEFER) - pr_err("Couldn't probe SMB138X rc=%d\n", rc); - return rc; - } - - return 0; -} - static const struct of_device_id match_table[] = { { - .compatible = "qcom,smb138x-charger", - .data = (void *) PARALLEL_MASTER, - }, - { - .compatible = "qcom,smb138x-parallel-slave", - .data = (void *) PARALLEL_SLAVE, + .compatible = "qcom,smb138x-charger", + .data = (void *) PARALLEL_MASTER }, { - .compatible = "qcom,smb1355-parallel-slave", - .data = (void *) PARALLEL_SLAVE, + .compatible = "qcom,smb138x-parallel-slave", + .data = (void *) PARALLEL_SLAVE }, { }, }; @@ -1779,7 +1580,7 @@ static int smb138x_probe(struct platform_device *pdev) rc = smb138x_master_probe(chip); break; case PARALLEL_SLAVE: - rc = slave_probe(chip); + rc = smb138x_slave_probe(chip); break; default: pr_err("Couldn't find a matching mode %d\n", chip->chg.mode); @@ -1793,8 +1594,7 @@ static int smb138x_probe(struct platform_device *pdev) goto cleanup; } - pr_info("%s probed successfully mode=%d pl_mode = %d\n", - chip->name, chip->chg.mode, chip->dt.pl_mode); + pr_info("SMB138X probed successfully mode=%d\n", chip->chg.mode); return rc; cleanup: diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index e5ba63171eba..88956d3ba674 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -472,6 +472,15 @@ config REGULATOR_MT6397 This driver supports the control of different power rails of device through regulator interface. +config REGULATOR_ONSEMI_NCP6335D + tristate "OnSemi NCP6335D regulator support" + depends on I2C + help + This driver supports the OnSemi NCP6335D switching voltage regulator + (buck converter). The regulator is controlled using an I2C interface + and supports a programmable voltage range from 0.6V to 1.4V in steps + of 6.25mV. + config REGULATOR_PALMAS tristate "TI Palmas PMIC Regulators" depends on MFD_PALMAS diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 20cf304a4714..e345f10f94af 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o +obj-$(CONFIG_REGULATOR_ONSEMI_NCP6335D) += onsemi-ncp6335d.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o diff --git a/drivers/regulator/onsemi-ncp6335d.c b/drivers/regulator/onsemi-ncp6335d.c new file mode 100644 index 000000000000..3b4ca4f81527 --- /dev/null +++ b/drivers/regulator/onsemi-ncp6335d.c @@ -0,0 +1,775 @@ +/* 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 + * 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/err.h> +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/log2.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <linux/debugfs.h> +#include <linux/regulator/onsemi-ncp6335d.h> +#include <linux/string.h> + +/* registers */ +#define REG_NCP6335D_PID 0x03 +#define REG_NCP6335D_PROGVSEL1 0x10 +#define REG_NCP6335D_PROGVSEL0 0x11 +#define REG_NCP6335D_PGOOD 0x12 +#define REG_NCP6335D_TIMING 0x13 +#define REG_NCP6335D_COMMAND 0x14 + +/* constraints */ +#define NCP6335D_MIN_VOLTAGE_UV 600000 +#define NCP6335D_STEP_VOLTAGE_UV 6250 +#define NCP6335D_VOLTAGE_STEPS 128 +#define NCP6335D_MIN_SLEW_NS 166 +#define NCP6335D_MAX_SLEW_NS 1333 + +/* bits */ +#define NCP6335D_ENABLE BIT(7) +#define NCP6335D_DVS_PWM_MODE BIT(5) +#define NCP6335D_PWM_MODE1 BIT(6) +#define NCP6335D_PWM_MODE0 BIT(7) +#define NCP6335D_PGOOD_DISCHG BIT(4) +#define NCP6335D_SLEEP_MODE BIT(4) + +#define NCP6335D_VOUT_SEL_MASK 0x7F +#define NCP6335D_SLEW_MASK 0x18 +#define NCP6335D_SLEW_SHIFT 0x3 + +struct ncp6335d_info { + struct regulator_dev *regulator; + struct regulator_init_data *init_data; + struct regmap *regmap; + struct device *dev; + unsigned int vsel_reg; + unsigned int vsel_backup_reg; + unsigned int mode_bit; + int curr_voltage; + int slew_rate; + + unsigned int step_size; + unsigned int min_voltage; + unsigned int min_slew_ns; + unsigned int max_slew_ns; + unsigned int peek_poke_address; + + struct dentry *debug_root; +}; + +static int delay_array[] = {10, 20, 30, 40, 50}; + +static int ncp6335x_read(struct ncp6335d_info *dd, unsigned int reg, + unsigned int *val) +{ + int i = 0, rc = 0; + + rc = regmap_read(dd->regmap, reg, val); + for (i = 0; rc && i < ARRAY_SIZE(delay_array); i++) { + pr_debug("Failed reading reg=%u - retry(%d)\n", reg, i); + msleep(delay_array[i]); + rc = regmap_read(dd->regmap, reg, val); + } + + if (rc) + pr_err("Failed reading reg=%u rc=%d\n", reg, rc); + + return rc; +} + +static int ncp6335x_write(struct ncp6335d_info *dd, unsigned int reg, + unsigned int val) +{ + int i = 0, rc = 0; + + rc = regmap_write(dd->regmap, reg, val); + for (i = 0; rc && i < ARRAY_SIZE(delay_array); i++) { + pr_debug("Failed writing reg=%u - retry(%d)\n", reg, i); + msleep(delay_array[i]); + rc = regmap_write(dd->regmap, reg, val); + } + + if (rc) + pr_err("Failed writing reg=%u rc=%d\n", reg, rc); + + return rc; +} + +static int ncp6335x_update_bits(struct ncp6335d_info *dd, unsigned int reg, + unsigned int mask, unsigned int val) +{ + int i = 0, rc = 0; + + rc = regmap_update_bits(dd->regmap, reg, mask, val); + for (i = 0; rc && i < ARRAY_SIZE(delay_array); i++) { + pr_debug("Failed updating reg=%u- retry(%d)\n", reg, i); + msleep(delay_array[i]); + rc = regmap_update_bits(dd->regmap, reg, mask, val); + } + + if (rc) + pr_err("Failed updating reg=%u rc=%d\n", reg, rc); + + return rc; +} + +static void dump_registers(struct ncp6335d_info *dd, + unsigned int reg, const char *func) +{ + unsigned int val = 0; + + ncp6335x_read(dd, reg, &val); + dev_dbg(dd->dev, "%s: NCP6335D: Reg = %x, Val = %x\n", func, reg, val); +} + +static void ncp633d_slew_delay(struct ncp6335d_info *dd, + int prev_uV, int new_uV) +{ + u8 val; + int delay; + + val = abs(prev_uV - new_uV) / dd->step_size; + delay = ((val * dd->slew_rate) / 1000) + 1; + + dev_dbg(dd->dev, "Slew Delay = %d\n", delay); + + udelay(delay); +} + +static int ncp6335d_is_enabled(struct regulator_dev *rdev) +{ + int rc, val = 0; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + rc = ncp6335x_read(dd, dd->vsel_reg, &val); + if (rc) + dev_err(dd->dev, "Unable to read enable register rc(%d)", rc); + + dump_registers(dd, dd->vsel_reg, __func__); + + return ((val & NCP6335D_ENABLE) ? 1 : 0); +} + +static int ncp6335d_enable(struct regulator_dev *rdev) +{ + int rc; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + rc = ncp6335x_update_bits(dd, dd->vsel_reg, + NCP6335D_ENABLE, NCP6335D_ENABLE); + if (rc) + dev_err(dd->dev, "Unable to enable regualtor rc(%d)", rc); + + dump_registers(dd, dd->vsel_reg, __func__); + + return rc; +} + +static int ncp6335d_disable(struct regulator_dev *rdev) +{ + int rc; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + rc = ncp6335x_update_bits(dd, dd->vsel_reg, + NCP6335D_ENABLE, 0); + if (rc) + dev_err(dd->dev, "Unable to disable regualtor rc(%d)", rc); + + dump_registers(dd, dd->vsel_reg, __func__); + + return rc; +} + +static int ncp6335d_get_voltage(struct regulator_dev *rdev) +{ + unsigned int val; + int rc; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + rc = ncp6335x_read(dd, dd->vsel_reg, &val); + if (rc) { + dev_err(dd->dev, "Unable to get volatge rc(%d)", rc); + return rc; + } + dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) * dd->step_size) + + dd->min_voltage; + + dump_registers(dd, dd->vsel_reg, __func__); + + return dd->curr_voltage; +} + +static int ncp6335d_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + int rc, set_val, new_uV; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + set_val = DIV_ROUND_UP(min_uV - dd->min_voltage, dd->step_size); + new_uV = (set_val * dd->step_size) + dd->min_voltage; + if (new_uV > max_uV) { + dev_err(dd->dev, "Unable to set volatge (%d %d)\n", + min_uV, max_uV); + return -EINVAL; + } + + rc = ncp6335x_update_bits(dd, dd->vsel_reg, + NCP6335D_VOUT_SEL_MASK, (set_val & NCP6335D_VOUT_SEL_MASK)); + if (rc) { + dev_err(dd->dev, "Unable to set volatge (%d %d)\n", + min_uV, max_uV); + } else { + ncp633d_slew_delay(dd, dd->curr_voltage, new_uV); + dd->curr_voltage = new_uV; + } + + dump_registers(dd, dd->vsel_reg, __func__); + + return rc; +} + +static int ncp6335d_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + if (selector >= NCP6335D_VOLTAGE_STEPS) + return 0; + + return selector * dd->step_size + dd->min_voltage; +} + +static int ncp6335d_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + int rc; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + /* only FAST and NORMAL mode types are supported */ + if (mode != REGULATOR_MODE_FAST && mode != REGULATOR_MODE_NORMAL) { + dev_err(dd->dev, "Mode %d not supported\n", mode); + return -EINVAL; + } + + rc = ncp6335x_update_bits(dd, REG_NCP6335D_COMMAND, dd->mode_bit, + (mode == REGULATOR_MODE_FAST) ? dd->mode_bit : 0); + if (rc) { + dev_err(dd->dev, "Unable to set operating mode rc(%d)", rc); + return rc; + } + + rc = ncp6335x_update_bits(dd, REG_NCP6335D_COMMAND, + NCP6335D_DVS_PWM_MODE, + (mode == REGULATOR_MODE_FAST) ? + NCP6335D_DVS_PWM_MODE : 0); + if (rc) + dev_err(dd->dev, "Unable to set DVS trans. mode rc(%d)", rc); + + dump_registers(dd, REG_NCP6335D_COMMAND, __func__); + + return rc; +} + +static unsigned int ncp6335d_get_mode(struct regulator_dev *rdev) +{ + unsigned int val; + int rc; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + rc = ncp6335x_read(dd, REG_NCP6335D_COMMAND, &val); + if (rc) { + dev_err(dd->dev, "Unable to get regulator mode rc(%d)\n", rc); + return rc; + } + + dump_registers(dd, REG_NCP6335D_COMMAND, __func__); + + if (val & dd->mode_bit) + return REGULATOR_MODE_FAST; + + return REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops ncp6335d_ops = { + .set_voltage = ncp6335d_set_voltage, + .get_voltage = ncp6335d_get_voltage, + .list_voltage = ncp6335d_list_voltage, + .is_enabled = ncp6335d_is_enabled, + .enable = ncp6335d_enable, + .disable = ncp6335d_disable, + .set_mode = ncp6335d_set_mode, + .get_mode = ncp6335d_get_mode, +}; + +static struct regulator_desc rdesc = { + .name = "ncp6335d", + .supply_name = "vin", + .owner = THIS_MODULE, + .n_voltages = NCP6335D_VOLTAGE_STEPS, + .ops = &ncp6335d_ops, +}; + +static int ncp6335d_restore_working_reg(struct device_node *node, + struct ncp6335d_info *dd) +{ + int ret; + unsigned int val; + + /* Restore register from back up register */ + ret = ncp6335x_read(dd, dd->vsel_backup_reg, &val); + if (ret < 0) { + dev_err(dd->dev, "Failed to get backup data from reg %d, ret = %d\n", + dd->vsel_backup_reg, ret); + return ret; + } + + ret = ncp6335x_update_bits(dd, dd->vsel_reg, + NCP6335D_VOUT_SEL_MASK, val); + if (ret < 0) { + dev_err(dd->dev, "Failed to update working reg %d, ret = %d\n", + dd->vsel_reg, ret); + return ret; + } + + return ret; +} + +static int ncp6335d_parse_gpio(struct device_node *node, + struct ncp6335d_info *dd) +{ + int ret = 0, gpio; + enum of_gpio_flags flags; + + if (!of_find_property(node, "onnn,vsel-gpio", NULL)) + return ret; + + /* Get GPIO connected to vsel and set its output */ + gpio = of_get_named_gpio_flags(node, + "onnn,vsel-gpio", 0, &flags); + if (!gpio_is_valid(gpio)) { + if (gpio != -EPROBE_DEFER) + dev_err(dd->dev, "Could not get vsel, ret = %d\n", + gpio); + return gpio; + } + + ret = devm_gpio_request(dd->dev, gpio, "ncp6335d_vsel"); + if (ret) { + dev_err(dd->dev, "Failed to obtain gpio %d ret = %d\n", + gpio, ret); + return ret; + } + + ret = gpio_direction_output(gpio, flags & OF_GPIO_ACTIVE_LOW ? 0 : 1); + if (ret) { + dev_err(dd->dev, "Failed to set GPIO %d to: %s, ret = %d", + gpio, flags & OF_GPIO_ACTIVE_LOW ? + "GPIO_LOW" : "GPIO_HIGH", ret); + return ret; + } + + return ret; +} + +static int ncp6335d_init(struct i2c_client *client, struct ncp6335d_info *dd, + const struct ncp6335d_platform_data *pdata) +{ + int rc; + unsigned int val; + + switch (pdata->default_vsel) { + case NCP6335D_VSEL0: + dd->vsel_reg = REG_NCP6335D_PROGVSEL0; + dd->vsel_backup_reg = REG_NCP6335D_PROGVSEL1; + dd->mode_bit = NCP6335D_PWM_MODE0; + break; + case NCP6335D_VSEL1: + dd->vsel_reg = REG_NCP6335D_PROGVSEL1; + dd->vsel_backup_reg = REG_NCP6335D_PROGVSEL0; + dd->mode_bit = NCP6335D_PWM_MODE1; + break; + default: + dev_err(dd->dev, "Invalid VSEL ID %d\n", pdata->default_vsel); + return -EINVAL; + } + + if (of_property_read_bool(client->dev.of_node, "onnn,restore-reg")) { + rc = ncp6335d_restore_working_reg(client->dev.of_node, dd); + if (rc) + return rc; + } + + rc = ncp6335d_parse_gpio(client->dev.of_node, dd); + if (rc) + return rc; + + /* get the current programmed voltage */ + rc = ncp6335x_read(dd, dd->vsel_reg, &val); + if (rc) { + dev_err(dd->dev, "Unable to get volatge rc(%d)", rc); + return rc; + } + dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) * + dd->step_size) + dd->min_voltage; + + /* set discharge */ + rc = ncp6335x_update_bits(dd, REG_NCP6335D_PGOOD, + NCP6335D_PGOOD_DISCHG, + (pdata->discharge_enable ? + NCP6335D_PGOOD_DISCHG : 0)); + if (rc) { + dev_err(dd->dev, "Unable to set Active Discharge rc(%d)\n", rc); + return -EINVAL; + } + + /* set slew rate */ + if (pdata->slew_rate_ns < dd->min_slew_ns || + pdata->slew_rate_ns > dd->max_slew_ns) { + dev_err(dd->dev, "Invalid slew rate %d\n", pdata->slew_rate_ns); + return -EINVAL; + } + + dd->slew_rate = pdata->slew_rate_ns; + val = DIV_ROUND_UP(pdata->slew_rate_ns, dd->min_slew_ns); + val = ilog2(val); + + rc = ncp6335x_update_bits(dd, REG_NCP6335D_TIMING, + NCP6335D_SLEW_MASK, val << NCP6335D_SLEW_SHIFT); + if (rc) + dev_err(dd->dev, "Unable to set slew rate rc(%d)\n", rc); + + /* Set Sleep mode bit */ + rc = ncp6335x_update_bits(dd, REG_NCP6335D_COMMAND, + NCP6335D_SLEEP_MODE, pdata->sleep_enable ? + NCP6335D_SLEEP_MODE : 0); + if (rc) + dev_err(dd->dev, "Unable to set sleep mode (%d)\n", rc); + + dump_registers(dd, REG_NCP6335D_COMMAND, __func__); + dump_registers(dd, REG_NCP6335D_PROGVSEL0, __func__); + dump_registers(dd, REG_NCP6335D_TIMING, __func__); + dump_registers(dd, REG_NCP6335D_PGOOD, __func__); + + return rc; +} + +static struct regmap_config ncp6335d_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int ncp6335d_parse_dt(struct i2c_client *client, + struct ncp6335d_info *dd) +{ + int rc; + + rc = of_property_read_u32(client->dev.of_node, + "onnn,step-size", &dd->step_size); + if (rc < 0) { + dev_err(&client->dev, "step size missing: rc = %d.\n", rc); + return rc; + } + + rc = of_property_read_u32(client->dev.of_node, + "onnn,min-slew-ns", &dd->min_slew_ns); + if (rc < 0) { + dev_err(&client->dev, "min slew us missing: rc = %d.\n", rc); + return rc; + } + + rc = of_property_read_u32(client->dev.of_node, + "onnn,max-slew-ns", &dd->max_slew_ns); + if (rc < 0) { + dev_err(&client->dev, "max slew us missing: rc = %d.\n", rc); + return rc; + } + + rc = of_property_read_u32(client->dev.of_node, + "onnn,min-setpoint", &dd->min_voltage); + if (rc < 0) { + dev_err(&client->dev, "min set point missing: rc = %d.\n", rc); + return rc; + } + + return rc; +} + +static struct ncp6335d_platform_data * + ncp6335d_get_of_platform_data(struct i2c_client *client) +{ + struct ncp6335d_platform_data *pdata = NULL; + struct regulator_init_data *init_data; + const char *mode_name; + int rc; + + init_data = of_get_regulator_init_data(&client->dev, + client->dev.of_node, &rdesc); + if (!init_data) { + dev_err(&client->dev, "regulator init data is missing\n"); + return pdata; + } + + pdata = devm_kzalloc(&client->dev, + sizeof(struct ncp6335d_platform_data), GFP_KERNEL); + if (!pdata) + return pdata; + + rc = of_property_read_u32(client->dev.of_node, + "onnn,vsel", &pdata->default_vsel); + if (rc < 0) { + dev_err(&client->dev, "onnn,vsel property missing: rc = %d.\n", + rc); + return NULL; + } + + rc = of_property_read_u32(client->dev.of_node, + "onnn,slew-ns", &pdata->slew_rate_ns); + if (rc < 0) { + dev_err(&client->dev, "onnn,slew-ns property missing: rc = %d.\n", + rc); + return NULL; + } + + pdata->discharge_enable = of_property_read_bool(client->dev.of_node, + "onnn,discharge-enable"); + + pdata->sleep_enable = of_property_read_bool(client->dev.of_node, + "onnn,sleep-enable"); + + pdata->init_data = init_data; + + init_data->constraints.input_uV = init_data->constraints.max_uV; + init_data->constraints.valid_ops_mask = + REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_MODE; + init_data->constraints.valid_modes_mask = + REGULATOR_MODE_NORMAL | + REGULATOR_MODE_FAST; + + rc = of_property_read_string(client->dev.of_node, "onnn,mode", + &mode_name); + if (!rc) { + if (strcmp("pwm", mode_name) == 0) { + init_data->constraints.initial_mode = + REGULATOR_MODE_FAST; + } else if (strcmp("auto", mode_name) == 0) { + init_data->constraints.initial_mode = + REGULATOR_MODE_NORMAL; + } else { + dev_err(&client->dev, "onnn,mode, unknown regulator mode: %s\n", + mode_name); + return NULL; + } + } + + return pdata; +} + +static int get_reg(void *data, u64 *val) +{ + struct ncp6335d_info *dd = data; + int rc; + unsigned int temp = 0; + + rc = ncp6335x_read(dd, dd->peek_poke_address, &temp); + if (rc < 0) + dev_err(dd->dev, "Couldn't read reg %x rc = %d\n", + dd->peek_poke_address, rc); + else + *val = temp; + + return rc; +} + +static int set_reg(void *data, u64 val) +{ + struct ncp6335d_info *dd = data; + int rc; + unsigned int temp = 0; + + temp = (unsigned int) val; + rc = ncp6335x_write(dd, dd->peek_poke_address, temp); + if (rc < 0) + dev_err(dd->dev, "Couldn't write 0x%02x to 0x%02x rc= %d\n", + dd->peek_poke_address, temp, rc); + + return rc; +} +DEFINE_SIMPLE_ATTRIBUTE(poke_poke_debug_ops, get_reg, set_reg, "0x%02llx\n"); + +static int ncp6335d_regulator_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc; + unsigned int val = 0; + struct ncp6335d_info *dd; + const struct ncp6335d_platform_data *pdata; + struct regulator_config config = { }; + + if (client->dev.of_node) + pdata = ncp6335d_get_of_platform_data(client); + else + pdata = client->dev.platform_data; + + if (!pdata) { + dev_err(&client->dev, "Platform data not specified\n"); + return -EINVAL; + } + + dd = devm_kzalloc(&client->dev, sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + + if (client->dev.of_node) { + rc = ncp6335d_parse_dt(client, dd); + if (rc) + return rc; + } else { + dd->step_size = NCP6335D_STEP_VOLTAGE_UV; + dd->min_voltage = NCP6335D_MIN_VOLTAGE_UV; + dd->min_slew_ns = NCP6335D_MIN_SLEW_NS; + dd->max_slew_ns = NCP6335D_MAX_SLEW_NS; + } + + dd->regmap = devm_regmap_init_i2c(client, &ncp6335d_regmap_config); + if (IS_ERR(dd->regmap)) { + dev_err(&client->dev, "Error allocating regmap\n"); + return PTR_ERR(dd->regmap); + } + + rc = ncp6335x_read(dd, REG_NCP6335D_PID, &val); + if (rc) { + dev_err(&client->dev, "Unable to identify NCP6335D, rc(%d)\n", + rc); + return rc; + } + dev_info(&client->dev, "Detected Regulator NCP6335D PID = %d\n", val); + + dd->init_data = pdata->init_data; + dd->dev = &client->dev; + i2c_set_clientdata(client, dd); + + rc = ncp6335d_init(client, dd, pdata); + if (rc) { + dev_err(&client->dev, "Unable to initialize the regulator\n"); + return -EINVAL; + } + + config.dev = &client->dev; + config.init_data = dd->init_data; + config.regmap = dd->regmap; + config.driver_data = dd; + config.of_node = client->dev.of_node; + + dd->regulator = regulator_register(&rdesc, &config); + + if (IS_ERR(dd->regulator)) { + dev_err(&client->dev, "Unable to register regulator rc(%ld)", + PTR_ERR(dd->regulator)); + + return PTR_ERR(dd->regulator); + } + + dd->debug_root = debugfs_create_dir("ncp6335x", NULL); + if (!dd->debug_root) + dev_err(&client->dev, "Couldn't create debug dir\n"); + + if (dd->debug_root) { + struct dentry *ent; + + ent = debugfs_create_x32("address", S_IFREG | S_IWUSR | S_IRUGO, + dd->debug_root, + &(dd->peek_poke_address)); + if (!ent) + dev_err(&client->dev, "Couldn't create address debug file rc = %d\n", + rc); + + ent = debugfs_create_file("data", S_IFREG | S_IWUSR | S_IRUGO, + dd->debug_root, dd, + &poke_poke_debug_ops); + if (!ent) + dev_err(&client->dev, "Couldn't create data debug file rc = %d\n", + rc); + } + + return 0; +} + +static int ncp6335d_regulator_remove(struct i2c_client *client) +{ + struct ncp6335d_info *dd = i2c_get_clientdata(client); + + regulator_unregister(dd->regulator); + + debugfs_remove_recursive(dd->debug_root); + + return 0; +} + +static const struct of_device_id ncp6335d_match_table[] = { + { .compatible = "onnn,ncp6335d-regulator", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ncp6335d_match_table); + +static const struct i2c_device_id ncp6335d_id[] = { + {"ncp6335d", -1}, + { }, +}; + +static struct i2c_driver ncp6335d_regulator_driver = { + .driver = { + .name = "ncp6335d-regulator", + .owner = THIS_MODULE, + .of_match_table = ncp6335d_match_table, + }, + .probe = ncp6335d_regulator_probe, + .remove = ncp6335d_regulator_remove, + .id_table = ncp6335d_id, +}; + +/** + * ncp6335d_regulator_init() - initialized ncp6335d regulator driver + * This function registers the ncp6335d regulator platform driver. + * + * Returns 0 on success or errno on failure. + */ +int __init ncp6335d_regulator_init(void) +{ + static bool initialized; + + if (initialized) + return 0; + + initialized = true; + + return i2c_add_driver(&ncp6335d_regulator_driver); +} +EXPORT_SYMBOL(ncp6335d_regulator_init); +subsys_initcall(ncp6335d_regulator_init); + +static void __exit ncp6335d_regulator_exit(void) +{ + i2c_del_driver(&ncp6335d_regulator_driver); +} +module_exit(ncp6335d_regulator_exit); +MODULE_DESCRIPTION("OnSemi-NCP6335D regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 69e0ebc78f6f..8c242bc7a702 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -691,6 +691,8 @@ static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len) goto out; } + memset(&ind_msg, 0, sizeof(ind_msg)); + ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01; ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN; ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei; diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c index 092b1c1af44b..924c826208dd 100644 --- a/drivers/soc/qcom/memory_dump_v2.c +++ b/drivers/soc/qcom/memory_dump_v2.c @@ -95,7 +95,7 @@ int msm_dump_data_add_minidump(struct msm_dump_entry *entry) data = (struct msm_dump_data *)(phys_to_virt(entry->addr)); if (!strcmp(data->name, "")) { - pr_info("Entry name is NULL, Use ID %d for minidump\n", + pr_debug("Entry name is NULL, Use ID %d for minidump\n", entry->id); snprintf(md_entry.name, sizeof(md_entry.name), "KMDT0x%X", entry->id); @@ -133,7 +133,7 @@ int msm_dump_data_register(enum msm_dump_table_ids id, dmac_flush_range(table, (void *)table + sizeof(struct msm_dump_table)); if (msm_dump_data_add_minidump(entry)) - pr_info("Failed to add entry in Minidump table\n"); + pr_err("Failed to add entry in Minidump table\n"); return 0; } diff --git a/drivers/soc/qcom/msm_minidump.c b/drivers/soc/qcom/msm_minidump.c index 1cb36bf98555..300233085161 100644 --- a/drivers/soc/qcom/msm_minidump.c +++ b/drivers/soc/qcom/msm_minidump.c @@ -62,24 +62,31 @@ struct md_table { struct md_region entry[MAX_NUM_ENTRIES]; }; +/* + * md_elfhdr: Minidump table elf header + * @md_ehdr: elf main header + * @shdr: Section header + * @phdr: Program header + * @elf_offset: section offset in elf + * @strtable_idx: string table current index position + */ +struct md_elfhdr { + struct elfhdr *md_ehdr; + struct elf_shdr *shdr; + struct elf_phdr *phdr; + u64 elf_offset; + u64 strtable_idx; +}; + /* Protect elfheader and smem table from deferred calls contention */ static DEFINE_SPINLOCK(mdt_lock); -static bool minidump_enabled; -static struct md_table minidump_table; +static struct md_table minidump_table; +static struct md_elfhdr minidump_elfheader; + +bool minidump_enabled; static unsigned int pendings; static unsigned int region_idx = 1; /* First entry is ELF header*/ -/* ELF Header */ -static struct elfhdr *md_ehdr; -/* ELF Program header */ -static struct elf_phdr *phdr; -/* ELF Section header */ -static struct elf_shdr *shdr; -/* Section offset in elf image */ -static u64 elf_offset; -/* String table index, first byte must be '\0' */ -static unsigned int stringtable_idx = 1; - static inline struct elf_shdr *elf_sheader(struct elfhdr *hdr) { return (struct elf_shdr *)((size_t)hdr + (size_t)hdr->e_shoff); @@ -90,6 +97,16 @@ static inline struct elf_shdr *elf_section(struct elfhdr *hdr, int idx) return &elf_sheader(hdr)[idx]; } +static inline struct elf_phdr *elf_pheader(struct elfhdr *hdr) +{ + return (struct elf_phdr *)((size_t)hdr + (size_t)hdr->e_phoff); +} + +static inline struct elf_phdr *elf_program(struct elfhdr *hdr, int idx) +{ + return &elf_pheader(hdr)[idx]; +} + static inline char *elf_str_table(struct elfhdr *hdr) { if (hdr->e_shstrndx == SHN_UNDEF) @@ -101,23 +118,24 @@ static inline char *elf_lookup_string(struct elfhdr *hdr, int offset) { char *strtab = elf_str_table(hdr); - if ((strtab == NULL) | (stringtable_idx < offset)) + if ((strtab == NULL) || (minidump_elfheader.strtable_idx < offset)) return NULL; return strtab + offset; } static inline unsigned int set_section_name(const char *name) { - char *strtab = elf_str_table(md_ehdr); + char *strtab = elf_str_table(minidump_elfheader.md_ehdr); + int idx = minidump_elfheader.strtable_idx; int ret = 0; - if ((strtab == NULL) | (name == NULL)) + if ((strtab == NULL) || (name == NULL)) return 0; - ret = stringtable_idx; - stringtable_idx += strlcpy((strtab + stringtable_idx), - name, MAX_NAME_LENGTH); - stringtable_idx += 1; + ret = idx; + idx += strlcpy((strtab + idx), name, MAX_NAME_LENGTH); + minidump_elfheader.strtable_idx = idx + 1; + return ret; } @@ -137,11 +155,9 @@ static inline bool md_check_name(const char *name) static int md_update_smem_table(const struct md_region *entry) { struct md_smem_region *mdr; - - if (!minidump_enabled) { - pr_info("Table in smem is not setup\n"); - return -ENODEV; - } + struct elfhdr *hdr = minidump_elfheader.md_ehdr; + struct elf_shdr *shdr = elf_section(hdr, hdr->e_shnum++); + struct elf_phdr *phdr = elf_program(hdr, hdr->e_phnum++); mdr = &minidump_table.region[region_idx++]; @@ -155,36 +171,21 @@ static int md_update_smem_table(const struct md_region *entry) shdr->sh_addr = (elf_addr_t)entry->virt_addr; shdr->sh_size = mdr->size; shdr->sh_flags = SHF_WRITE; - shdr->sh_offset = elf_offset; + shdr->sh_offset = minidump_elfheader.elf_offset; shdr->sh_entsize = 0; phdr->p_type = PT_LOAD; - phdr->p_offset = elf_offset; + phdr->p_offset = minidump_elfheader.elf_offset; phdr->p_vaddr = entry->virt_addr; phdr->p_paddr = entry->phys_addr; phdr->p_filesz = phdr->p_memsz = mdr->size; phdr->p_flags = PF_R | PF_W; - md_ehdr->e_shnum += 1; - md_ehdr->e_phnum += 1; - elf_offset += shdr->sh_size; - shdr++; - phdr++; + minidump_elfheader.elf_offset += shdr->sh_size; return 0; } -bool msm_minidump_enabled(void) -{ - bool ret; - - spin_lock(&mdt_lock); - ret = minidump_enabled; - spin_unlock(&mdt_lock); - return ret; -} -EXPORT_SYMBOL(msm_minidump_enabled); - int msm_minidump_add_region(const struct md_region *entry) { u32 entries; @@ -196,19 +197,19 @@ int msm_minidump_add_region(const struct md_region *entry) if (((strlen(entry->name) > MAX_NAME_LENGTH) || md_check_name(entry->name)) && !entry->virt_addr) { - pr_info("Invalid entry details\n"); + pr_err("Invalid entry details\n"); return -EINVAL; } if (!IS_ALIGNED(entry->size, 4)) { - pr_info("size should be 4 byte aligned\n"); + pr_err("size should be 4 byte aligned\n"); return -EINVAL; } spin_lock(&mdt_lock); entries = minidump_table.num_regions; if (entries >= MAX_NUM_ENTRIES) { - pr_info("Maximum entries reached.\n"); + pr_err("Maximum entries reached.\n"); spin_unlock(&mdt_lock); return -ENOMEM; } @@ -238,23 +239,32 @@ EXPORT_SYMBOL(msm_minidump_add_region); static int msm_minidump_add_header(void) { struct md_smem_region *mdreg = &minidump_table.region[0]; - char *banner; + struct elfhdr *md_ehdr; + struct elf_shdr *shdr; + struct elf_phdr *phdr; unsigned int strtbl_off, elfh_size, phdr_off; + char *banner; + /* Header buffer contains: + * elf header, MAX_NUM_ENTRIES+1 of section and program elf headers, + * string table section and linux banner. + */ elfh_size = sizeof(*md_ehdr) + MAX_STRTBL_SIZE + MAX_MEM_LENGTH + ((sizeof(*shdr) + sizeof(*phdr)) * (MAX_NUM_ENTRIES + 1)); - md_ehdr = kzalloc(elfh_size, GFP_KERNEL); - if (!md_ehdr) + minidump_elfheader.md_ehdr = kzalloc(elfh_size, GFP_KERNEL); + if (!minidump_elfheader.md_ehdr) return -ENOMEM; strlcpy(mdreg->name, "KELF_HEADER", sizeof(mdreg->name)); - mdreg->address = virt_to_phys(md_ehdr); + mdreg->address = virt_to_phys(minidump_elfheader.md_ehdr); mdreg->size = elfh_size; - /* Section headers*/ - shdr = (struct elf_shdr *)(md_ehdr + 1); - phdr = (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES); + md_ehdr = minidump_elfheader.md_ehdr; + /* Assign section/program headers offset */ + minidump_elfheader.shdr = shdr = (struct elf_shdr *)(md_ehdr + 1); + minidump_elfheader.phdr = phdr = + (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES); phdr_off = sizeof(*md_ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES); memcpy(md_ehdr->e_ident, ELFMAG, SELFMAG); @@ -268,18 +278,19 @@ static int msm_minidump_add_header(void) md_ehdr->e_ehsize = sizeof(*md_ehdr); md_ehdr->e_phoff = phdr_off; md_ehdr->e_phentsize = sizeof(*phdr); - md_ehdr->e_phnum = 1; md_ehdr->e_shoff = sizeof(*md_ehdr); md_ehdr->e_shentsize = sizeof(*shdr); - md_ehdr->e_shnum = 3; /* NULL, STR TABLE, Linux banner */ md_ehdr->e_shstrndx = 1; - elf_offset = elfh_size; + minidump_elfheader.elf_offset = elfh_size; + + /* + * First section header should be NULL, + * 2nd section is string table. + */ + minidump_elfheader.strtable_idx = 1; strtbl_off = sizeof(*md_ehdr) + ((sizeof(*phdr) + sizeof(*shdr)) * MAX_NUM_ENTRIES); - /* First section header should be NULL - * 2nd entry for string table - */ shdr++; shdr->sh_type = SHT_STRTAB; shdr->sh_offset = (elf_addr_t)strtbl_off; @@ -289,7 +300,15 @@ static int msm_minidump_add_header(void) shdr->sh_name = set_section_name("STR_TBL"); shdr++; - /* 3rd entry for linux banner */ + /* 3rd section is for minidump_table VA, used by parsers */ + shdr->sh_type = SHT_PROGBITS; + shdr->sh_entsize = 0; + shdr->sh_flags = 0; + shdr->sh_addr = (elf_addr_t)&minidump_table; + shdr->sh_name = set_section_name("minidump_table"); + shdr++; + + /* 4th section is linux banner */ banner = (char *)md_ehdr + strtbl_off + MAX_STRTBL_SIZE; strlcpy(banner, linux_banner, MAX_MEM_LENGTH); @@ -300,7 +319,6 @@ static int msm_minidump_add_header(void) shdr->sh_entsize = 0; shdr->sh_flags = SHF_WRITE; shdr->sh_name = set_section_name("linux_banner"); - shdr++; phdr->p_type = PT_LOAD; phdr->p_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE); @@ -309,8 +327,9 @@ static int msm_minidump_add_header(void) phdr->p_filesz = phdr->p_memsz = strlen(linux_banner) + 1; phdr->p_flags = PF_R | PF_W; - md_ehdr->e_phnum += 1; - phdr++; + /* Update headers count*/ + md_ehdr->e_phnum = 1; + md_ehdr->e_shnum = 4; return 0; } @@ -325,13 +344,13 @@ static int __init msm_minidump_init(void) smem_table = smem_get_entry(SMEM_MINIDUMP_TABLE_ID, &size, 0, SMEM_ANY_HOST_FLAG); if (IS_ERR_OR_NULL(smem_table)) { - pr_info("SMEM is not initialized.\n"); + pr_err("SMEM is not initialized.\n"); return -ENODEV; } if ((smem_table->next_avail_offset + MAX_MEM_LENGTH) > smem_table->smem_length) { - pr_info("SMEM memory not available.\n"); + pr_err("SMEM memory not available.\n"); return -ENOMEM; } @@ -353,10 +372,10 @@ static int __init msm_minidump_init(void) for (i = 0; i < pendings; i++) { mdr = &minidump_table.entry[i]; if (md_update_smem_table(mdr)) { - pr_info("Unable to add entry %s to smem table\n", + pr_err("Unable to add entry %s to smem table\n", mdr->name); spin_unlock(&mdt_lock); - return -ENODEV; + return -ENOENT; } } diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c index 4ba92436bd06..6e7d34ac9163 100644 --- a/drivers/soc/qcom/qbt1000.c +++ b/drivers/soc/qcom/qbt1000.c @@ -377,6 +377,12 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) drvdata = file->private_data; + if (IS_ERR(priv_arg)) { + dev_err(drvdata->dev, "%s: invalid user space pointer %lu\n", + __func__, arg); + return -EINVAL; + } + mutex_lock(&drvdata->mutex); pr_debug("qbt1000_ioctl %d\n", cmd); @@ -401,6 +407,13 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) goto end; } + if (strcmp(app.name, FP_APP_NAME)) { + dev_err(drvdata->dev, "%s: Invalid app name\n", + __func__); + rc = -EINVAL; + goto end; + } + if (drvdata->app_handle) { dev_err(drvdata->dev, "%s: LOAD app already loaded, unloading first\n", __func__); @@ -414,6 +427,7 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) } pr_debug("app %s load before\n", app.name); + app.name[MAX_NAME_SIZE - 1] = '\0'; /* start the TZ app */ rc = qseecom_start_app( @@ -427,7 +441,8 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) pr_err("App %s failed to set bw\n", app.name); } } else { - pr_err("app %s failed to load\n", app.name); + dev_err(drvdata->dev, "%s: Fingerprint Trusted App failed to load\n", + __func__); goto end; } @@ -447,9 +462,7 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) pr_debug("app %s load after\n", app.name); - if (!strcmp(app.name, FP_APP_NAME)) - drvdata->fp_app_handle = drvdata->app_handle; - + drvdata->fp_app_handle = drvdata->app_handle; break; } case QBT1000_UNLOAD_APP: diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 58bf3d2f52bd..bfb7dd2d920d 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -3,7 +3,7 @@ * drivers/staging/android/ion/ion.c * * Copyright (C) 2011 Google, Inc. - * Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -122,8 +122,6 @@ struct ion_handle { int id; }; -static struct ion_device *ion_dev; - bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer) { return (buffer->flags & ION_FLAG_CACHED) && @@ -844,32 +842,45 @@ void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle) } EXPORT_SYMBOL(ion_unmap_kernel); -static int ion_debug_client_show(struct seq_file *s, void *unused) +static struct mutex debugfs_mutex; +static struct rb_root *ion_root_client; +static int is_client_alive(struct ion_client *client) { - struct ion_client *client = s->private; - struct rb_node *n, *cnode; - bool found = false; - - down_write(&ion_dev->lock); + struct rb_node *node; + struct ion_client *tmp; + struct ion_device *dev; - if (!client || (client->dev != ion_dev)) { - up_write(&ion_dev->lock); - return -EINVAL; - } + node = ion_root_client->rb_node; + dev = container_of(ion_root_client, struct ion_device, clients); - cnode = rb_first(&ion_dev->clients); - for ( ; cnode; cnode = rb_next(cnode)) { - struct ion_client *c = rb_entry(cnode, - struct ion_client, node); - if (client == c) { - found = true; - break; + down_read(&dev->lock); + while (node) { + tmp = rb_entry(node, struct ion_client, node); + if (client < tmp) { + node = node->rb_left; + } else if (client > tmp) { + node = node->rb_right; + } else { + up_read(&dev->lock); + return 1; } } - if (!found) { - up_write(&ion_dev->lock); - return -EINVAL; + up_read(&dev->lock); + return 0; +} + +static int ion_debug_client_show(struct seq_file *s, void *unused) +{ + struct ion_client *client = s->private; + struct rb_node *n; + + mutex_lock(&debugfs_mutex); + if (!is_client_alive(client)) { + seq_printf(s, "ion_client 0x%pK dead, can't dump its buffers\n", + client); + mutex_unlock(&debugfs_mutex); + return 0; } seq_printf(s, "%16.16s: %16.16s : %16.16s : %12.12s\n", @@ -890,7 +901,7 @@ static int ion_debug_client_show(struct seq_file *s, void *unused) seq_printf(s, "\n"); } mutex_unlock(&client->lock); - up_write(&ion_dev->lock); + mutex_unlock(&debugfs_mutex); return 0; } @@ -1021,7 +1032,7 @@ void ion_client_destroy(struct ion_client *client) struct rb_node *n; pr_debug("%s: %d\n", __func__, __LINE__); - mutex_lock(&client->lock); + mutex_lock(&debugfs_mutex); while ((n = rb_first(&client->handles))) { struct ion_handle *handle = rb_entry(n, struct ion_handle, node); @@ -1029,7 +1040,6 @@ void ion_client_destroy(struct ion_client *client) } idr_destroy(&client->idr); - mutex_unlock(&client->lock); down_write(&dev->lock); if (client->task) @@ -1042,6 +1052,7 @@ void ion_client_destroy(struct ion_client *client) kfree(client->display_name); kfree(client->name); kfree(client); + mutex_unlock(&debugfs_mutex); } EXPORT_SYMBOL(ion_client_destroy); @@ -1838,7 +1849,7 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) seq_printf(s, "%16s %16s %16s\n", "client", "pid", "size"); seq_puts(s, "----------------------------------------------------\n"); - down_read(&dev->lock); + mutex_lock(&debugfs_mutex); for (n = rb_first(&dev->clients); n; n = rb_next(n)) { struct ion_client *client = rb_entry(n, struct ion_client, node); @@ -1857,7 +1868,8 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) client->pid, size); } } - up_read(&dev->lock); + mutex_unlock(&debugfs_mutex); + seq_puts(s, "----------------------------------------------------\n"); seq_puts(s, "orphaned allocations (info is from last known client):\n"); mutex_lock(&dev->buffer_lock); @@ -2095,7 +2107,8 @@ debugfs_done: init_rwsem(&idev->lock); plist_head_init(&idev->heaps); idev->clients = RB_ROOT; - ion_dev = idev; + ion_root_client = &idev->clients; + mutex_init(&debugfs_mutex); return idev; } EXPORT_SYMBOL(ion_device_create); diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 80f9de907563..5cc80b80c82b 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -823,7 +823,7 @@ static int receive_data(enum port_type index, struct nozomi *dc) struct tty_struct *tty = tty_port_tty_get(&port->port); int i, ret; - read_mem32((u32 *) &size, addr, 4); + size = __le32_to_cpu(readl(addr)); /* DBG1( "%d bytes port: %d", size, index); */ if (tty && test_bit(TTY_THROTTLED, &tty->flags)) { diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 343a32a4bad3..efaac5eb6592 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1170,26 +1170,17 @@ static int msm_startup(struct uart_port *port) snprintf(msm_port->name, sizeof(msm_port->name), "msm_serial%d", port->line); - ret = request_irq(port->irq, msm_uart_irq, IRQF_TRIGGER_HIGH, - msm_port->name, port); - if (unlikely(ret)) - return ret; - /* * UART clk must be kept enabled to * avoid losing received character */ ret = clk_prepare_enable(msm_port->clk); - if (ret) { - goto err_clk; + if (ret) return ret; - } ret = clk_prepare_enable(msm_port->pclk); - if (ret) { + if (ret) goto err_pclk; - return ret; - } msm_serial_set_mnd_regs(port); @@ -1217,12 +1208,21 @@ static int msm_startup(struct uart_port *port) msm_request_rx_dma(msm_port, msm_port->uart.mapbase); } + ret = request_irq(port->irq, msm_uart_irq, IRQF_TRIGGER_HIGH, + msm_port->name, port); + if (unlikely(ret)) + goto err_irq; + return 0; +err_irq: + if (msm_port->is_uartdm) + msm_release_dma(msm_port); + + clk_disable_unprepare(msm_port->pclk); + err_pclk: clk_disable_unprepare(msm_port->clk); -err_clk: - free_irq(port->irq, port); return ret; } diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index e394f123f918..ad9d6cc4e23f 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -3597,7 +3597,8 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA) } } - power_supply_get_property(mdwc->usb_psy, POWER_SUPPLY_PROP_TYPE, &pval); + power_supply_get_property(mdwc->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, &pval); if (pval.intval != POWER_SUPPLY_TYPE_USB) return 0; diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index c76ca5a94557..055c6203577a 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -2365,7 +2365,7 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) pd->vbus_present = val.intval; ret = power_supply_get_property(pd->usb_psy, - POWER_SUPPLY_PROP_TYPE, &val); + POWER_SUPPLY_PROP_REAL_TYPE, &val); if (ret) { usbpd_err(&pd->dev, "Unable to read USB TYPE: %d\n", ret); return ret; diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 9982cb176ce8..830e2fd47642 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -562,8 +562,9 @@ static long vfio_pci_ioctl(void *device_data, } else if (cmd == VFIO_DEVICE_SET_IRQS) { struct vfio_irq_set hdr; + size_t size; u8 *data = NULL; - int ret = 0; + int max, ret = 0; minsz = offsetofend(struct vfio_irq_set, count); @@ -571,23 +572,31 @@ static long vfio_pci_ioctl(void *device_data, return -EFAULT; if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS || + hdr.count >= (U32_MAX - hdr.start) || hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK | VFIO_IRQ_SET_ACTION_TYPE_MASK)) return -EINVAL; - if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) { - size_t size; - int max = vfio_pci_get_irq_count(vdev, hdr.index); + max = vfio_pci_get_irq_count(vdev, hdr.index); + if (hdr.start >= max || hdr.start + hdr.count > max) + return -EINVAL; - if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL) - size = sizeof(uint8_t); - else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD) - size = sizeof(int32_t); - else - return -EINVAL; + switch (hdr.flags & VFIO_IRQ_SET_DATA_TYPE_MASK) { + case VFIO_IRQ_SET_DATA_NONE: + size = 0; + break; + case VFIO_IRQ_SET_DATA_BOOL: + size = sizeof(uint8_t); + break; + case VFIO_IRQ_SET_DATA_EVENTFD: + size = sizeof(int32_t); + break; + default: + return -EINVAL; + } - if (hdr.argsz - minsz < hdr.count * size || - hdr.start >= max || hdr.start + hdr.count > max) + if (size) { + if (hdr.argsz - minsz < hdr.count * size) return -EINVAL; data = memdup_user((void __user *)(arg + minsz), diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c index 20e9a86d2dcf..5c8f767b6368 100644 --- a/drivers/vfio/pci/vfio_pci_intrs.c +++ b/drivers/vfio/pci/vfio_pci_intrs.c @@ -255,7 +255,7 @@ static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix) if (!is_irq_none(vdev)) return -EINVAL; - vdev->ctx = kzalloc(nvec * sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); + vdev->ctx = kcalloc(nvec, sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); if (!vdev->ctx) return -ENOMEM; diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index 09a34223c2a5..5e96a08b77ed 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -3035,6 +3035,12 @@ int mdss_mdp_layer_pre_commit_wfd(struct msm_fb_data_type *mfd, wfd = mdp5_data->wfd; output_layer = commit->output_layer; + if (output_layer->buffer.plane_count > MAX_PLANES) { + pr_err("Output buffer plane_count exceeds MAX_PLANES limit:%d\n", + output_layer->buffer.plane_count); + return -EINVAL; + } + data = mdss_mdp_wfd_add_data(wfd, output_layer); if (IS_ERR_OR_NULL(data)) return PTR_ERR(data); diff --git a/drivers/video/fbdev/msm/mdss_mdp_wfd.c b/drivers/video/fbdev/msm/mdss_mdp_wfd.c index 71a07f6b7d39..7868dc0f1999 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_wfd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_wfd.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 @@ -322,6 +322,12 @@ int mdss_mdp_wb_import_data(struct device *device, if (wfd_data->layer.flags & MDP_LAYER_SECURE_SESSION) flags = MDP_SECURE_OVERLAY_SESSION; + if (buffer->plane_count > MAX_PLANES) { + pr_err("buffer plane_count exceeds MAX_PLANES limit:%d", + buffer->plane_count); + return -EINVAL; + } + memset(planes, 0, sizeof(planes)); for (i = 0; i < buffer->plane_count; i++) { diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c index fdd1c0153ce0..399a12e3dcc8 100644 --- a/drivers/video/fbdev/msm/mdss_rotator.c +++ b/drivers/video/fbdev/msm/mdss_rotator.c @@ -501,6 +501,12 @@ static int mdss_rotator_import_buffer(struct mdp_layer_buffer *buffer, memset(planes, 0, sizeof(planes)); + if (buffer->plane_count > MAX_PLANES) { + pr_err("buffer plane_count exceeds MAX_PLANES limit:%d\n", + buffer->plane_count); + return -EINVAL; + } + for (i = 0; i < buffer->plane_count; i++) { planes[i].memory_id = buffer->planes[i].fd; planes[i].offset = buffer->planes[i].offset; @@ -2104,6 +2110,20 @@ struct mdss_rot_entry_container *mdss_rotator_req_init( struct mdss_rot_entry_container *req; int size, i; + /* + * Check input and output plane_count from each given item + * are within the MAX_PLANES limit + */ + for (i = 0 ; i < count; i++) { + if ((items[i].input.plane_count > MAX_PLANES) || + (items[i].output.plane_count > MAX_PLANES)) { + pr_err("Input/Output plane_count exceeds MAX_PLANES limit, input:%d, output:%d\n", + items[i].input.plane_count, + items[i].output.plane_count); + return ERR_PTR(-EINVAL); + } + } + size = sizeof(struct mdss_rot_entry_container); size += sizeof(struct mdss_rot_entry) * count; req = devm_kzalloc(&mgr->pdev->dev, size, GFP_KERNEL); |
