diff options
author | Michael Bestas <mkbestas@lineageos.org> | 2021-02-06 01:33:49 +0200 |
---|---|---|
committer | Michael Bestas <mkbestas@lineageos.org> | 2021-02-06 01:33:49 +0200 |
commit | 4d2544c30eab39bfe0eed6027fe4059f58ee91ad (patch) | |
tree | cdb839bc415cbac2a2342c8f82ffe963e40faa48 | |
parent | 141849eac5defb4bb6cf6e6f1381cb24ffcfdba5 (diff) | |
parent | c3cd6d0f0f73c7f0b0b5173ac7ad6753fb3b8ce1 (diff) |
Merge tag 'LA.UM.9.2.r1-02000-SDMxx0.0' of https://source.codeaurora.org/quic/la/kernel/msm-4.4 into lineage-18.1-caf-msm8998
* tag 'LA.UM.9.2.r1-02000-SDMxx0.0' of https://source.codeaurora.org/quic/la/kernel/msm-4.4:
net: ipv6: Use passed in table for nexthop lookups
ipv6: addrconf: use stable address generator for ARPHRD_NONE
msm: kgsl: Protect the memdesc->gpuaddr in SVM use cases
msm: kgsl: Stop using memdesc->usermem
msm: kgsl: Correct the refcount on current process PID
HID: sony: Update hid_have_special_driver
UPSTREAM: HID: input: map digitizer battery usage
HID: input: ignore the battery in OKLICK Laser BTmouse
defconfig: Enable CONFIG_HID_NINTENDO for msm8998
iio: qcom-rradc: Update logic to monitor health of RRADC peripheral
qcom: fg-memif: Correct timeout condition for memory grant
power: qpnp-fg-gen3: Add a property to reset FG BCL device
msm: ipa3: Add check to validate rule_cnt
power_supply: add FG_RESET_CLOCK property
defconfig: Enable CONFIG_HID_NINTENDO for SDM660
FROMLIST: HID: nintendo: add nintendo switch controller driver
defconfig: Enable UTS_NS for sdm660
Conflicts:
drivers/hid/hid-input.c
drivers/power/power_supply_sysfs.c
include/linux/power_supply.h
Change-Id: I577e4b1d9410887224dbdb192c6eea1f2de6aded
24 files changed, 502 insertions, 75 deletions
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 96b24176c388..2170a329d5bf 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2020, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2021, 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 @@ -266,6 +266,7 @@ kgsl_mem_entry_create(void) /* put this ref in the caller functions after init */ kref_get(&entry->refcount); } + atomic_set(&entry->map_count, 0); return entry; } #ifdef CONFIG_DMA_SHARED_BUFFER @@ -916,17 +917,24 @@ static struct kgsl_process_private *kgsl_process_private_new( list_for_each_entry(private, &kgsl_driver.process_list, list) { if (private->pid == cur_pid) { if (!kgsl_process_private_get(private)) { - put_pid(cur_pid); private = ERR_PTR(-EINVAL); } + /* + * We need to hold only one reference to the PID for + * each process struct to avoid overflowing the + * reference counter which can lead to use-after-free. + */ + put_pid(cur_pid); return private; } } /* Create a new object */ private = kzalloc(sizeof(struct kgsl_process_private), GFP_KERNEL); - if (private == NULL) + if (private == NULL) { + put_pid(cur_pid); return ERR_PTR(-ENOMEM); + } kref_init(&private->refcount); @@ -2131,7 +2139,7 @@ static int check_vma(unsigned long hostptr, u64 size) return true; } -static int memdesc_sg_virt(struct kgsl_memdesc *memdesc) +static int memdesc_sg_virt(struct kgsl_memdesc *memdesc, unsigned long useraddr) { int ret = 0; long npages = 0, i; @@ -2153,13 +2161,13 @@ static int memdesc_sg_virt(struct kgsl_memdesc *memdesc) } down_read(¤t->mm->mmap_sem); - if (!check_vma(memdesc->useraddr, memdesc->size)) { + if (!check_vma(useraddr, memdesc->size)) { up_read(¤t->mm->mmap_sem); ret = ~EFAULT; goto out; } - npages = get_user_pages(current, current->mm, memdesc->useraddr, + npages = get_user_pages(current, current->mm, useraddr, sglen, write ? FOLL_WRITE : 0, pages, NULL); up_read(¤t->mm->mmap_sem); @@ -2198,7 +2206,6 @@ static int kgsl_setup_anon_useraddr(struct kgsl_pagetable *pagetable, entry->memdesc.pagetable = pagetable; entry->memdesc.size = (uint64_t) size; - entry->memdesc.useraddr = hostptr; entry->memdesc.flags |= KGSL_MEMFLAGS_USERMEM_ADDR; if (kgsl_memdesc_use_cpu_map(&entry->memdesc)) { @@ -2206,15 +2213,15 @@ static int kgsl_setup_anon_useraddr(struct kgsl_pagetable *pagetable, /* Register the address in the database */ ret = kgsl_mmu_set_svm_region(pagetable, - (uint64_t) entry->memdesc.useraddr, (uint64_t) size); + (uint64_t) hostptr, (uint64_t) size); if (ret) return ret; - entry->memdesc.gpuaddr = (uint64_t) entry->memdesc.useraddr; + entry->memdesc.gpuaddr = (uint64_t) hostptr; } - return memdesc_sg_virt(&entry->memdesc); + return memdesc_sg_virt(&entry->memdesc, hostptr); } static int match_file(const void *p, struct file *file, unsigned int fd) @@ -2299,8 +2306,8 @@ static int kgsl_setup_dmabuf_useraddr(struct kgsl_device *device, return ret; } - /* Setup the user addr/cache mode for cache operations */ - entry->memdesc.useraddr = hostptr; + /* Setup the cache mode for cache operations */ + _setup_cache_mode(entry, vma); up_read(¤t->mm->mmap_sem); return 0; @@ -2434,6 +2441,8 @@ long kgsl_ioctl_gpuobj_import(struct kgsl_device_private *dev_priv, if (entry == NULL) return -ENOMEM; + spin_lock_init(&entry->memdesc.lock); + param->flags &= KGSL_MEMFLAGS_GPUREADONLY | KGSL_MEMTYPE_MASK | KGSL_MEMALIGN_MASK @@ -2707,6 +2716,8 @@ long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, if (entry == NULL) return -ENOMEM; + spin_lock_init(&entry->memdesc.lock); + /* * Convert from enum value to KGSL_MEM_ENTRY value, so that * we can use the latter consistently everywhere. @@ -3318,7 +3329,12 @@ long kgsl_ioctl_gpumem_get_info(struct kgsl_device_private *dev_priv, param->flags = (unsigned int) entry->memdesc.flags; param->size = (size_t) entry->memdesc.size; param->mmapsize = (size_t) kgsl_memdesc_footprint(&entry->memdesc); - param->useraddr = entry->memdesc.useraddr; + /* + * Entries can have multiple user mappings so thre isn't any one address + * we can report. Plus, the user should already know their mappings, so + * there isn't any value in reporting it back to them. + */ + param->useraddr = 0; kgsl_mem_entry_put(entry); return result; @@ -3482,6 +3498,8 @@ long kgsl_ioctl_sparse_virt_alloc(struct kgsl_device_private *dev_priv, if (entry == NULL) return -ENOMEM; + spin_lock_init(&entry->memdesc.lock); + entry->memdesc.flags = KGSL_MEMFLAGS_SPARSE_VIRT; entry->memdesc.size = param->size; entry->memdesc.cur_bindings = 0; @@ -3784,9 +3802,6 @@ static int _sparse_bind(struct kgsl_process_private *process, if (memdesc->gpuaddr) return -EINVAL; - if (memdesc->useraddr != 0) - return -EINVAL; - pagetable = memdesc->pagetable; /* Clear out any mappings */ @@ -4066,7 +4081,12 @@ long kgsl_ioctl_gpuobj_info(struct kgsl_device_private *dev_priv, param->flags = entry->memdesc.flags; param->size = entry->memdesc.size; param->va_len = kgsl_memdesc_footprint(&entry->memdesc); - param->va_addr = (uint64_t) entry->memdesc.useraddr; + /* + * Entries can have multiple user mappings so thre isn't any one address + * we can report. Plus, the user should already know their mappings, so + * there isn't any value in reporting it back to them. + */ + param->va_addr = 0; kgsl_mem_entry_put(entry); return 0; @@ -4234,6 +4254,8 @@ static void kgsl_gpumem_vm_open(struct vm_area_struct *vma) if (kgsl_mem_entry_get(entry) == 0) vma->vm_private_data = NULL; + + atomic_inc(&entry->map_count); } static int @@ -4257,7 +4279,8 @@ kgsl_gpumem_vm_close(struct vm_area_struct *vma) if (!entry) return; - entry->memdesc.useraddr = 0; + atomic_dec(&entry->map_count); + kgsl_mem_entry_put(entry); } @@ -4296,7 +4319,8 @@ get_mmap_entry(struct kgsl_process_private *private, } } - if (entry->memdesc.useraddr != 0) { + /* Don't allow ourselves to remap user memory */ + if (entry->memdesc.flags & KGSL_MEMFLAGS_USERMEM_ADDR) { ret = -EBUSY; goto err_put; } @@ -4329,19 +4353,34 @@ static unsigned long _gpu_set_svm_region(struct kgsl_process_private *private, { int ret; + /* + * Protect access to the gpuaddr here to prevent multiple vmas from + * trying to map a SVM region at the same time + */ + spin_lock(&entry->memdesc.lock); + + if (entry->memdesc.gpuaddr) { + spin_unlock(&entry->memdesc.lock); + return (unsigned long) -EBUSY; + } + ret = kgsl_mmu_set_svm_region(private->pagetable, (uint64_t) addr, (uint64_t) size); - if (ret != 0) - return ret; + if (ret != 0) { + spin_unlock(&entry->memdesc.lock); + return (unsigned long) ret; + } entry->memdesc.gpuaddr = (uint64_t) addr; + spin_unlock(&entry->memdesc.lock); + entry->memdesc.pagetable = private->pagetable; ret = kgsl_mmu_map(private->pagetable, &entry->memdesc); if (ret) { kgsl_mmu_put_gpuaddr(&entry->memdesc); - return ret; + return (unsigned long) ret; } kgsl_memfree_purge(private->pagetable, entry->memdesc.gpuaddr, @@ -4404,6 +4443,14 @@ static unsigned long _search_range(struct kgsl_process_private *private, result = _gpu_set_svm_region(private, entry, cpu, len); if (!IS_ERR_VALUE(result)) break; + /* + * _gpu_set_svm_region will return -EBUSY if we tried to set up + * SVM on an object that already has a GPU address. If + * that happens don't bother walking the rest of the + * region + */ + if ((long) result == -EBUSY) + return -EBUSY; trace_kgsl_mem_unmapped_area_collision(entry, cpu, len); @@ -4620,9 +4667,9 @@ static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) vma->vm_file = file; - entry->memdesc.useraddr = vma->vm_start; + atomic_inc(&entry->map_count); - trace_kgsl_mem_mmap(entry); + trace_kgsl_mem_mmap(entry, vma->vm_start); return 0; } diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 6b8ef82d340f..31257d291c7e 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2016,2018-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2016,2018-2019,2021, 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 @@ -197,11 +197,9 @@ struct kgsl_memdesc_ops { * @pagetable: Pointer to the pagetable that the object is mapped in * @hostptr: Kernel virtual address * @hostptr_count: Number of threads using hostptr - * @useraddr: User virtual address (if applicable) * @gpuaddr: GPU virtual address * @physaddr: Physical address of the memory object * @size: Size of the memory object - * @mapsize: Size of memory mapped in userspace * @priv: Internal flags and settings * @sgt: Scatter gather table for allocated pages * @ops: Function hooks for the memdesc memory type @@ -216,11 +214,9 @@ struct kgsl_memdesc { struct kgsl_pagetable *pagetable; void *hostptr; unsigned int hostptr_count; - unsigned long useraddr; uint64_t gpuaddr; phys_addr_t physaddr; uint64_t size; - uint64_t mapsize; unsigned int priv; struct sg_table *sgt; struct kgsl_memdesc_ops *ops; @@ -230,6 +226,11 @@ struct kgsl_memdesc { struct page **pages; unsigned int page_count; unsigned int cur_bindings; + /* + * @lock: Spinlock to protect the gpuaddr from being accessed by + * multiple entities trying to map the same SVM region at once + */ + spinlock_t lock; }; /* @@ -278,6 +279,11 @@ struct kgsl_mem_entry { struct work_struct work; spinlock_t bind_lock; struct rb_root bind_tree; + /* + * @map_count: Count how many vmas this object is mapped in - used for + * debugfs accounting + */ + atomic_t map_count; }; struct kgsl_device_private; diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c index 30769de41a70..36bb4d801df9 100644 --- a/drivers/gpu/msm/kgsl_debugfs.c +++ b/drivers/gpu/msm/kgsl_debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2017,2020, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2008-2017,2020-2021, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -149,7 +149,11 @@ static int print_mem_entry(void *data, void *ptr) flags[3] = get_alignflag(m); flags[4] = get_cacheflag(m); flags[5] = kgsl_memdesc_use_cpu_map(m) ? 'p' : '-'; - flags[6] = (m->useraddr) ? 'Y' : 'N'; + /* + * Show Y if at least one vma has this entry + * mapped (could be multiple) + */ + flags[6] = atomic_read(&entry->map_count) ? 'Y' : 'N'; flags[7] = kgsl_memdesc_is_secured(m) ? 's' : '-'; flags[8] = m->flags & KGSL_MEMFLAGS_SPARSE_PHYS ? 'P' : '-'; flags[9] = '\0'; @@ -160,12 +164,16 @@ static int print_mem_entry(void *data, void *ptr) kgsl_get_egl_counts(entry, &egl_surface_count, &egl_image_count); - seq_printf(s, "%pK %pK %16llu %5d %9s %10s %16s %5d %16llu %6d %6d", + seq_printf(s, "%pK %d %16llu %5d %9s %10s %16s %5d %16d %6d %6d", (uint64_t *)(uintptr_t) m->gpuaddr, - (unsigned long *) m->useraddr, - m->size, entry->id, flags, + /* + * Show zero for the useraddr - we can't reliably track + * that value for multiple vmas anyway + */ + 0, m->size, entry->id, flags, memtype_str(usermem_type), - usage, (m->sgt ? m->sgt->nents : 0), m->mapsize, + usage, (m->sgt ? m->sgt->nents : 0), + atomic_read(&entry->map_count), egl_surface_count, egl_image_count); if (entry->metadata[0] != 0) @@ -235,7 +243,7 @@ static int process_mem_seq_show(struct seq_file *s, void *ptr) if (ptr == SEQ_START_TOKEN) { seq_printf(s, "%16s %16s %16s %5s %9s %10s %16s %5s %16s %6s %6s\n", "gpuaddr", "useraddr", "size", "id", "flags", "type", - "usage", "sglen", "mapsize", "eglsrf", "eglimg"); + "usage", "sglen", "mapcount", "eglsrf", "eglimg"); return 0; } else return print_mem_entry(s, ptr); diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index aff54105474a..08f5c6d9d50b 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2020, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2021, 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 @@ -270,6 +270,7 @@ static void kgsl_setup_qdss_desc(struct kgsl_device *device) return; } + spin_lock_init(&gpu_qdss_desc.lock); gpu_qdss_desc.flags = 0; gpu_qdss_desc.priv = 0; gpu_qdss_desc.physaddr = gpu_qdss_entry[0]; @@ -315,6 +316,7 @@ static void kgsl_setup_qtimer_desc(struct kgsl_device *device) return; } + spin_lock_init(&gpu_qtimer_desc.lock); gpu_qtimer_desc.flags = 0; gpu_qtimer_desc.priv = 0; gpu_qtimer_desc.physaddr = gpu_qtimer_entry[0]; @@ -1499,6 +1501,7 @@ static int _setstate_alloc(struct kgsl_device *device, { int ret; + spin_lock_init(&iommu->setstate.lock); ret = kgsl_sharedmem_alloc_contig(device, &iommu->setstate, PAGE_SIZE); if (!ret) { @@ -2477,6 +2480,11 @@ static int kgsl_iommu_get_gpuaddr(struct kgsl_pagetable *pagetable, goto out; } + /* + * This path is only called in a non-SVM path with locks so we can be + * sure we aren't racing with anybody so we don't need to worry about + * taking the lock + */ ret = _insert_gpuaddr(pagetable, addr, size); if (ret == 0) { memdesc->gpuaddr = addr; diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index aa7157e882ac..2303e8ee0721 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2017,2021, 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 @@ -443,10 +443,16 @@ void kgsl_mmu_put_gpuaddr(struct kgsl_memdesc *memdesc) if (PT_OP_VALID(pagetable, put_gpuaddr) && (unmap_fail == 0)) pagetable->pt_ops->put_gpuaddr(memdesc); + memdesc->pagetable = NULL; + + /* + * If SVM tries to take a GPU address it will lose the race until the + * gpuaddr returns to zero so we shouldn't need to worry about taking a + * lock here + */ if (!kgsl_memdesc_is_global(memdesc)) memdesc->gpuaddr = 0; - memdesc->pagetable = NULL; } EXPORT_SYMBOL(kgsl_mmu_put_gpuaddr); diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index b233211620da..dfbea53c306b 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2017,2020, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2017,2020-2021, 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 @@ -342,6 +342,7 @@ int kgsl_allocate_user(struct kgsl_device *device, int ret; memdesc->flags = flags; + spin_lock_init(&memdesc->lock); if (kgsl_mmu_get_mmutype(device) == KGSL_MMU_TYPE_NONE) ret = kgsl_sharedmem_alloc_contig(device, memdesc, size); @@ -373,8 +374,6 @@ static int kgsl_page_alloc_vmfault(struct kgsl_memdesc *memdesc, get_page(page); vmf->page = page; - memdesc->mapsize += PAGE_SIZE; - return 0; } @@ -504,8 +503,6 @@ static int kgsl_contiguous_vmfault(struct kgsl_memdesc *memdesc, else if (ret == -EFAULT) return VM_FAULT_SIGBUS; - memdesc->mapsize += PAGE_SIZE; - return VM_FAULT_NOPAGE; } diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index e5da594b77b8..ee89cfb808d7 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2017,2021, 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 @@ -283,6 +283,7 @@ static inline int kgsl_allocate_global(struct kgsl_device *device, memdesc->flags = flags; memdesc->priv = priv; + spin_lock_init(&memdesc->lock); if (((memdesc->priv & KGSL_MEMDESC_CONTIG) != 0) || (kgsl_mmu_get_mmutype(device) == KGSL_MMU_TYPE_NONE)) diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h index 2d9bca7e7d7f..5f325729f71e 100644 --- a/drivers/gpu/msm/kgsl_trace.h +++ b/drivers/gpu/msm/kgsl_trace.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016,2020, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2016,2020-2021, 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 @@ -433,9 +433,9 @@ TRACE_EVENT(kgsl_mem_alloc, TRACE_EVENT(kgsl_mem_mmap, - TP_PROTO(struct kgsl_mem_entry *mem_entry), + TP_PROTO(struct kgsl_mem_entry *mem_entry, unsigned long useraddr), - TP_ARGS(mem_entry), + TP_ARGS(mem_entry, useraddr), TP_STRUCT__entry( __field(unsigned long, useraddr) @@ -447,7 +447,7 @@ TRACE_EVENT(kgsl_mem_mmap, ), TP_fast_assign( - __entry->useraddr = mem_entry->memdesc.useraddr; + __entry->useraddr = useraddr; __entry->gpuaddr = mem_entry->memdesc.gpuaddr; __entry->size = mem_entry->memdesc.size; kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage), diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c index b3aa73f1a5a1..b9e12f795c79 100644 --- a/drivers/iio/adc/qcom-rradc.c +++ b/drivers/iio/adc/qcom-rradc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 2020, 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 @@ -193,7 +193,8 @@ #define FG_RR_ADC_STS_CHANNEL_READING_MASK 0x3 #define FG_RR_ADC_STS_CHANNEL_STS 0x2 -#define FG_RR_CONV_CONTINUOUS_TIME_MIN_MS 50 +#define FG_RR_CONV_CONTINUOUS_TIME_MIN_MS 50 +#define FG_RR_CONV_CONT_CBK_TIME_MIN_MS 10 #define FG_RR_CONV_MAX_RETRY_CNT 50 #define FG_RR_TP_REV_VERSION1 21 #define FG_RR_TP_REV_VERSION2 29 @@ -236,6 +237,11 @@ struct rradc_chip { struct pmic_revid_data *pmic_fab_id; int volt; struct power_supply *usb_trig; + struct power_supply *batt_psy; + struct power_supply *bms_psy; + struct notifier_block nb; + bool conv_cbk; + struct work_struct psy_notify_work; }; struct rradc_channels { @@ -680,6 +686,28 @@ static const struct rradc_channels rradc_chans[] = { FG_ADC_RR_AUX_THERM_STS) }; +static bool rradc_is_batt_psy_available(struct rradc_chip *chip) +{ + if (!chip->batt_psy) + chip->batt_psy = power_supply_get_by_name("battery"); + + if (!chip->batt_psy) + return false; + + return true; +} + +static bool rradc_is_bms_psy_available(struct rradc_chip *chip) +{ + if (!chip->bms_psy) + chip->bms_psy = power_supply_get_by_name("bms"); + + if (!chip->bms_psy) + return false; + + return true; +} + static int rradc_enable_continuous_mode(struct rradc_chip *chip) { int rc = 0; @@ -749,6 +777,7 @@ static int rradc_check_status_ready_with_retry(struct rradc_chip *chip, struct rradc_chan_prop *prop, u8 *buf, u16 status) { int rc = 0, retry_cnt = 0, mask = 0; + union power_supply_propval pval = {0, }; switch (prop->channel) { case RR_ADC_BATT_ID: @@ -775,7 +804,11 @@ static int rradc_check_status_ready_with_retry(struct rradc_chip *chip, break; } - msleep(FG_RR_CONV_CONTINUOUS_TIME_MIN_MS); + if ((chip->conv_cbk) && (prop->channel == RR_ADC_USBIN_V)) + msleep(FG_RR_CONV_CONT_CBK_TIME_MIN_MS); + else + msleep(FG_RR_CONV_CONTINUOUS_TIME_MIN_MS); + retry_cnt++; rc = rradc_read(chip, status, buf, 1); if (rc < 0) { @@ -784,8 +817,26 @@ static int rradc_check_status_ready_with_retry(struct rradc_chip *chip, } } - if (retry_cnt >= FG_RR_CONV_MAX_RETRY_CNT) - rc = -ENODATA; + if ((retry_cnt >= FG_RR_CONV_MAX_RETRY_CNT) && + ((prop->channel != RR_ADC_DCIN_V) || + (prop->channel != RR_ADC_DCIN_I))) { + pr_err("rradc is hung, Proceed to recovery\n"); + if (rradc_is_bms_psy_available(chip)) { + rc = power_supply_set_property(chip->bms_psy, + POWER_SUPPLY_PROP_FG_RESET_CLOCK, + &pval); + if (rc < 0) { + pr_err("Couldn't reset FG clock rc=%d\n", rc); + return rc; + } + } else { + pr_err("Error obtaining bms power supply\n"); + rc = -EINVAL; + } + } else { + if (retry_cnt >= FG_RR_CONV_MAX_RETRY_CNT) + rc = -ENODATA; + } return rc; } @@ -1073,6 +1124,67 @@ static int rradc_read_raw(struct iio_dev *indio_dev, return rc; } +static void psy_notify_work(struct work_struct *work) +{ + struct rradc_chip *chip = container_of(work, + struct rradc_chip, psy_notify_work); + + struct rradc_chan_prop *prop; + union power_supply_propval pval = {0, }; + u16 adc_code; + int rc = 0; + + if (rradc_is_batt_psy_available(chip)) { + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_STATUS, &pval); + if (rc < 0) + pr_err("Error obtaining battery status, rc=%d\n", rc); + + if (pval.intval == POWER_SUPPLY_STATUS_CHARGING) { + chip->conv_cbk = true; + prop = &chip->chan_props[RR_ADC_USBIN_V]; + rc = rradc_do_conversion(chip, prop, &adc_code); + if (rc == -ENODATA) { + pr_err("rradc is hung, Proceed to recovery\n"); + if (rradc_is_bms_psy_available(chip)) { + rc = power_supply_set_property + (chip->bms_psy, + POWER_SUPPLY_PROP_FG_RESET_CLOCK, + &pval); + if (rc < 0) + pr_err("Couldn't reset FG clock rc=%d\n", + rc); + prop = &chip->chan_props[RR_ADC_BATT_ID]; + rc = rradc_do_conversion(chip, prop, + &adc_code); + if (rc == -ENODATA) + pr_err("RRADC read failed after reset"); + } else { + pr_err("Error obtaining bms power supply"); + } + } + } + } else { + pr_err("Error obtaining battery power supply"); + } + chip->conv_cbk = false; + pm_relax(chip->dev); +} + +static int rradc_psy_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct power_supply *psy = data; + struct rradc_chip *chip = container_of(nb, struct rradc_chip, nb); + + if (strcmp(psy->desc->name, "battery") == 0) { + pm_stay_awake(chip->dev); + schedule_work(&chip->psy_notify_work); + } + + return NOTIFY_OK; +} + static const struct iio_info rradc_info = { .read_raw = &rradc_read_raw, .driver_module = THIS_MODULE, @@ -1184,6 +1296,20 @@ static int rradc_probe(struct platform_device *pdev) if (!chip->usb_trig) pr_debug("Error obtaining usb power supply\n"); + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + pr_debug("Error obtaining battery power supply\n"); + + chip->bms_psy = power_supply_get_by_name("bms"); + if (!chip->bms_psy) + pr_debug("Error obtaining bms power supply\n"); + + chip->nb.notifier_call = rradc_psy_notifier_cb; + rc = power_supply_reg_notifier(&chip->nb); + if (rc < 0) + pr_err("Error registering psy notifier rc = %d\n", rc); + INIT_WORK(&chip->psy_notify_work, psy_notify_work); + return devm_iio_device_register(dev, indio_dev); } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c index c29cbdf95057..b2e876c26749 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 2020, 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 @@ -1065,7 +1065,10 @@ static int __ipa_add_flt_rule(struct ipa_flt_tbl *tbl, enum ipa_ip_type ip, } else { list_add(&entry->link, &tbl->head_flt_rule_list); } - tbl->rule_cnt++; + if (tbl->rule_cnt < IPA_RULE_CNT_MAX) + tbl->rule_cnt++; + else + return -EINVAL; if (entry->rt_tbl) entry->rt_tbl->ref_cnt++; id = ipa_id_alloc(entry); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h index 1c9eeb50d1cd..bf0b069f92bd 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 2020, 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 @@ -201,6 +201,7 @@ #define IPA2_ACTIVE_CLIENTS_LOG_LINE_LEN 96 #define IPA2_ACTIVE_CLIENTS_LOG_HASHTABLE_SIZE 50 #define IPA2_ACTIVE_CLIENTS_LOG_NAME_LEN 40 +#define IPA_RULE_CNT_MAX 512 struct ipa2_active_client_htable_entry { struct hlist_node list; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index 007f92bcee13..abb7947b2a06 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2020, 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 @@ -1086,7 +1086,10 @@ static int __ipa_add_rt_rule(enum ipa_ip_type ip, const char *name, list_add_tail(&entry->link, &tbl->head_rt_rule_list); else list_add(&entry->link, &tbl->head_rt_rule_list); - tbl->rule_cnt++; + if (tbl->rule_cnt < IPA_RULE_CNT_MAX) + tbl->rule_cnt++; + else + return -EINVAL; if (entry->hdr) entry->hdr->ref_cnt++; else if (entry->proc_ctx) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c index 060b40a3acc6..f36687b44b8d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2020, 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 @@ -824,7 +824,10 @@ static int __ipa_finish_flt_rule_add(struct ipa3_flt_tbl *tbl, { int id; - tbl->rule_cnt++; + if (tbl->rule_cnt < IPA_RULE_CNT_MAX) + tbl->rule_cnt++; + else + return -EINVAL; if (entry->rt_tbl) entry->rt_tbl->ref_cnt++; id = ipa3_id_alloc(entry); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 7691aa93d544..5c1e49435631 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2020, 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 @@ -190,6 +190,8 @@ #define IPA3_ACTIVE_CLIENTS_LOG_HASHTABLE_SIZE 50 #define IPA3_ACTIVE_CLIENTS_LOG_NAME_LEN 40 +#define IPA_RULE_CNT_MAX 512 + struct ipa3_active_client_htable_entry { struct hlist_node list; char id_string[IPA3_ACTIVE_CLIENTS_LOG_NAME_LEN]; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index d8afb0c3becc..473618e9bd6a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -1000,7 +1000,10 @@ static int __ipa_finish_rt_rule_add(struct ipa3_rt_entry *entry, u32 *rule_hdl, { int id; - tbl->rule_cnt++; + if (tbl->rule_cnt < IPA_RULE_CNT_MAX) + tbl->rule_cnt++; + else + return -EINVAL; if (entry->hdr) entry->hdr->ref_cnt++; else if (entry->proc_ctx) diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 96853c18bc43..2907291dfa09 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -315,6 +315,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(parallel_batfet_mode), POWER_SUPPLY_ATTR(parallel_fcc_max), POWER_SUPPLY_ATTR(min_icl), + POWER_SUPPLY_ATTR(fg_reset_clock), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index 076cd49e6dd5..f596f85a5b50 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 2020, 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 @@ -522,4 +522,5 @@ extern void fg_circ_buf_clr(struct fg_circ_buf *); extern int fg_circ_buf_avg(struct fg_circ_buf *, int *); extern int fg_circ_buf_median(struct fg_circ_buf *, int *); extern int fg_lerp(const struct fg_pt *, size_t, s32, s32 *); +extern int fg_dma_mem_req(struct fg_chip *, bool); #endif diff --git a/drivers/power/supply/qcom/fg-memif.c b/drivers/power/supply/qcom/fg-memif.c index 8a949bfe61d0..694e8f769516 100644 --- a/drivers/power/supply/qcom/fg-memif.c +++ b/drivers/power/supply/qcom/fg-memif.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 2020, 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 @@ -746,6 +746,64 @@ out: return rc; } +int fg_dma_mem_req(struct fg_chip *chip, bool request) +{ + int ret, rc = 0, retry_count = RETRY_COUNT; + u8 val; + + if (request) { + /* configure for DMA access */ + rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, + MEM_ACCESS_REQ_BIT); + if (rc < 0) { + pr_err("failed to set mem_access bit rc=%d\n", rc); + return rc; + } + + rc = fg_masked_write(chip, MEM_IF_MEM_ARB_CFG(chip), + MEM_IF_ARB_REQ_BIT, MEM_IF_ARB_REQ_BIT); + if (rc < 0) { + pr_err("failed to set mem_arb bit rc=%d\n", rc); + goto release_mem; + } + + while (retry_count--) { + rc = fg_read(chip, MEM_IF_INT_RT_STS(chip), &val, 1); + if (rc < 0) { + pr_err("failed to set ima_rt_sts rc=%d\n", rc); + goto release_mem; + } + if (val & MEM_GNT_BIT) + break; + msleep(20); + } + if ((retry_count < 0) && !(val & MEM_GNT_BIT)) { + pr_err("failed to get memory access\n"); + rc = -ETIMEDOUT; + goto release_mem; + } + + return 0; + } + +release_mem: + /* Release access */ + rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0); + if (rc < 0) + pr_err("failed to reset mem_access bit rc = %d\n", rc); + + ret = fg_masked_write(chip, MEM_IF_MEM_ARB_CFG(chip), + MEM_IF_ARB_REQ_BIT, 0); + if (ret < 0) { + pr_err("failed to release mem_arb bit rc=%d\n", ret); + return ret; + } + + return rc; +} + int fg_ima_init(struct fg_chip *chip) { int rc; diff --git a/drivers/power/supply/qcom/fg-reg.h b/drivers/power/supply/qcom/fg-reg.h index cd0b2fb4391f..906792e1ed79 100644 --- a/drivers/power/supply/qcom/fg-reg.h +++ b/drivers/power/supply/qcom/fg-reg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,6 +29,7 @@ #define BATT_SOC_STS_CLR(chip) (chip->batt_soc_base + 0x4A) #define BATT_SOC_LOW_PWR_CFG(chip) (chip->batt_soc_base + 0x52) #define BATT_SOC_LOW_PWR_STS(chip) (chip->batt_soc_base + 0x56) +#define BATT_SOC_RST_CTRL0(chip) (chip->batt_soc_base + 0xBA) /* BATT_SOC_INT_RT_STS */ #define MSOC_EMPTY_BIT BIT(5) @@ -39,6 +40,9 @@ /* BATT_SOC_RESTART */ #define RESTART_GO_BIT BIT(0) +/* BCL_RESET */ +#define BCL_RESET_BIT BIT(2) + /* FG_BATT_INFO register definitions */ #define BATT_INFO_BATT_TEMP_STS(chip) (chip->batt_info_base + 0x06) #define BATT_INFO_SYS_BATT(chip) (chip->batt_info_base + 0x07) @@ -58,7 +62,6 @@ #define BATT_INFO_JEITA_COLD(chip) (chip->batt_info_base + 0x63) #define BATT_INFO_JEITA_HOT(chip) (chip->batt_info_base + 0x64) #define BATT_INFO_JEITA_TOO_HOT(chip) (chip->batt_info_base + 0x65) - /* only for v1.1 */ #define BATT_INFO_ESR_CFG(chip) (chip->batt_info_base + 0x69) /* starting from v2.0 */ @@ -95,6 +98,8 @@ #define BATT_INFO_IADC_MSB(chip) (chip->batt_info_base + 0xAF) #define BATT_INFO_TM_MISC(chip) (chip->batt_info_base + 0xE5) #define BATT_INFO_TM_MISC1(chip) (chip->batt_info_base + 0xE6) +#define BATT_INFO_PEEK_MUX1(chip) (chip->batt_info_base + 0xEB) +#define BATT_INFO_RDBACK(chip) (chip->batt_info_base + 0xEF) /* BATT_INFO_BATT_TEMP_STS */ #define JEITA_TOO_HOT_STS_BIT BIT(7) @@ -264,8 +269,12 @@ #define ESR_REQ_CTL_BIT BIT(1) #define ESR_REQ_CTL_EN_BIT BIT(0) +/* BATT_INFO_PEEK_MUX1 */ +#define PEEK_MUX1_BIT BIT(0) + /* FG_MEM_IF register and bit definitions */ #define MEM_IF_INT_RT_STS(chip) ((chip->mem_if_base) + 0x10) +#define MEM_IF_MEM_ARB_CFG(chip) ((chip->mem_if_base) + 0x40) #define MEM_IF_MEM_INTF_CFG(chip) ((chip->mem_if_base) + 0x50) #define MEM_IF_IMA_CTL(chip) ((chip->mem_if_base) + 0x51) #define MEM_IF_IMA_CFG(chip) ((chip->mem_if_base) + 0x52) @@ -286,6 +295,7 @@ /* MEM_IF_INT_RT_STS */ #define MEM_XCP_BIT BIT(1) +#define MEM_GNT_BIT BIT(2) /* MEM_IF_MEM_INTF_CFG */ #define MEM_ACCESS_REQ_BIT BIT(7) @@ -326,4 +336,7 @@ /* MEM_IF_DMA_CTL */ #define DMA_CLEAR_LOG_BIT BIT(0) + +/* MEM_IF_REQ */ +#define MEM_IF_ARB_REQ_BIT BIT(0) #endif diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c index 23dd9131d402..f074ffe6c274 100644 --- a/drivers/power/supply/qcom/fg-util.c +++ b/drivers/power/supply/qcom/fg-util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 2020, 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 @@ -420,7 +420,7 @@ int fg_write(struct fg_chip *chip, int addr, u8 *val, int len) return -ENXIO; mutex_lock(&chip->bus_lock); - sec_access = (addr & 0x00FF) > 0xD0; + sec_access = (addr & 0x00FF) >= 0xBA; if (sec_access) { rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5); if (rc < 0) { @@ -460,7 +460,7 @@ int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val) return -ENXIO; mutex_lock(&chip->bus_lock); - sec_access = (addr & 0x00FF) > 0xD0; + sec_access = (addr & 0x00FF) >= 0xBA; if (sec_access) { rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5); if (rc < 0) { diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index 32fb0538cc8c..43b48d4878fd 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -3850,6 +3850,9 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CC_STEP_SEL: pval->intval = chip->ttf.cc_step.sel; break; + case POWER_SUPPLY_PROP_FG_RESET_CLOCK: + pval->intval = 0; + break; default: pr_err("unsupported property %d\n", psp); rc = -EINVAL; @@ -3862,6 +3865,100 @@ static int fg_psy_get_property(struct power_supply *psy, return 0; } +#define BCL_RESET_RETRY_COUNT 4 +static int fg_bcl_reset(struct fg_chip *chip) +{ + int i, ret, rc = 0; + u8 val, peek_mux; + bool success = false; + + /* Read initial value of peek mux1 */ + rc = fg_read(chip, BATT_INFO_PEEK_MUX1(chip), &peek_mux, 1); + if (rc < 0) { + pr_err("Error in writing peek mux1, rc=%d\n", rc); + return rc; + } + + val = 0x83; + rc = fg_write(chip, BATT_INFO_PEEK_MUX1(chip), &val, 1); + if (rc < 0) { + pr_err("Error in writing peek mux1, rc=%d\n", rc); + return rc; + } + + mutex_lock(&chip->sram_rw_lock); + for (i = 0; i < BCL_RESET_RETRY_COUNT; i++) { + rc = fg_dma_mem_req(chip, true); + if (rc < 0) { + pr_err("Error in locking memory, rc=%d\n", rc); + goto unlock; + } + + rc = fg_read(chip, BATT_INFO_RDBACK(chip), &val, 1); + if (rc < 0) { + pr_err("Error in reading rdback, rc=%d\n", rc); + goto release_mem; + } + + if (val & PEEK_MUX1_BIT) { + rc = fg_masked_write(chip, BATT_SOC_RST_CTRL0(chip), + BCL_RESET_BIT, BCL_RESET_BIT); + if (rc < 0) { + pr_err("Error in writing RST_CTRL0, rc=%d\n", + rc); + goto release_mem; + } + + rc = fg_dma_mem_req(chip, false); + if (rc < 0) + pr_err("Error in unlocking memory, rc=%d\n", + rc); + + /* Delay of 2ms */ + usleep_range(2000, 3000); + ret = fg_masked_write(chip, BATT_SOC_RST_CTRL0(chip), + BCL_RESET_BIT, 0); + if (ret < 0) + pr_err("Error in writing RST_CTRL0, rc=%d\n", + rc); + if (!rc && !ret) + success = true; + + goto unlock; + } else { + rc = fg_dma_mem_req(chip, false); + if (rc < 0) { + pr_err("Error in unlocking memory, rc=%d\n", + rc); + goto unlock; + } + success = false; + pr_err_ratelimited("PEEK_MUX1 not set retrying...\n"); + msleep(1000); + } + } + +release_mem: + rc = fg_dma_mem_req(chip, false); + if (rc < 0) + pr_err("Error in unlocking memory, rc=%d\n", rc); + +unlock: + ret = fg_write(chip, BATT_INFO_PEEK_MUX1(chip), &peek_mux, 1); + if (ret < 0) { + pr_err("Error in writing peek mux1, rc=%d\n", rc); + mutex_unlock(&chip->sram_rw_lock); + return ret; + } + + mutex_unlock(&chip->sram_rw_lock); + + if (!success) + return -EAGAIN; + else + return rc; +} + static int fg_psy_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *pval) @@ -3950,6 +4047,13 @@ static int fg_psy_set_property(struct power_supply *psy, return rc; } break; + case POWER_SUPPLY_PROP_FG_RESET_CLOCK: + rc = fg_bcl_reset(chip); + if (rc < 0) { + pr_err("Error in resetting BCL clock, rc=%d\n", rc); + return rc; + } + break; default: break; } @@ -4047,6 +4151,7 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CC_STEP, POWER_SUPPLY_PROP_CC_STEP_SEL, + POWER_SUPPLY_PROP_FG_RESET_CLOCK, }; static const struct power_supply_desc fg_psy_desc = { diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 2ebd54c98e61..4d2e20415071 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -275,6 +275,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_PARALLEL_BATFET_MODE, POWER_SUPPLY_PROP_PARALLEL_FCC_MAX, POWER_SUPPLY_PROP_MIN_ICL, + POWER_SUPPLY_PROP_FG_RESET_CLOCK, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 5ad57375a99f..7eb9178e3666 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -218,6 +218,7 @@ enum in6_addr_gen_mode { IN6_ADDR_GEN_MODE_EUI64, IN6_ADDR_GEN_MODE_NONE, IN6_ADDR_GEN_MODE_STABLE_PRIVACY, + IN6_ADDR_GEN_MODE_RANDOM, }; /* Bridge section */ diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 441f6519e2e7..7061b435072b 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2396,6 +2396,12 @@ static void manage_tempaddrs(struct inet6_dev *idev, } } +static bool is_addr_mode_generate_stable(struct inet6_dev *idev) +{ + return idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY || + idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM; +} + void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) { struct prefix_info *pinfo; @@ -2512,8 +2518,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) in6_dev->token.s6_addr + 8, 8); read_unlock_bh(&in6_dev->lock); tokenized = true; - } else if (in6_dev->addr_gen_mode == - IN6_ADDR_GEN_MODE_STABLE_PRIVACY && + } else if (is_addr_mode_generate_stable(in6_dev) && !ipv6_generate_stable_address(&addr, 0, in6_dev)) { addr_flags |= IFA_F_STABLE_PRIVACY; @@ -3113,6 +3118,17 @@ retry: return 0; } +static void ipv6_gen_mode_random_init(struct inet6_dev *idev) +{ + struct ipv6_stable_secret *s = &idev->cnf.stable_secret; + + if (s->initialized) + return; + s = &idev->cnf.stable_secret; + get_random_bytes(&s->secret, sizeof(s->secret)); + s->initialized = true; +} + static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) { struct in6_addr addr; @@ -3123,13 +3139,18 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); - if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { + switch (idev->addr_gen_mode) { + case IN6_ADDR_GEN_MODE_RANDOM: + ipv6_gen_mode_random_init(idev); + /* fallthrough */ + case IN6_ADDR_GEN_MODE_STABLE_PRIVACY: if (!ipv6_generate_stable_address(&addr, 0, idev)) addrconf_add_linklocal(idev, &addr, IFA_F_STABLE_PRIVACY); else if (prefix_route) addrconf_prefix_route(&addr, 64, idev->dev, 0, 0); - } else if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) { + break; + case IN6_ADDR_GEN_MODE_EUI64: /* addrconf_add_linklocal also adds a prefix_route and we * only need to care about prefix routes if ipv6_generate_eui64 * couldn't generate one. @@ -3138,6 +3159,11 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) addrconf_add_linklocal(idev, &addr, 0); else if (prefix_route) addrconf_prefix_route(&addr, 64, idev->dev, 0, 0); + break; + case IN6_ADDR_GEN_MODE_NONE: + default: + /* will not add any link local address */ + break; } } @@ -3155,6 +3181,7 @@ static void addrconf_dev_config(struct net_device *dev) (dev->type != ARPHRD_IEEE1394) && (dev->type != ARPHRD_TUNNEL6) && (dev->type != ARPHRD_6LOWPAN) && + (dev->type != ARPHRD_NONE) && (dev->type != ARPHRD_RAWIP) && (dev->type != ARPHRD_INFINIBAND)) { /* Alas, we support only Ethernet autoconfiguration. */ @@ -3165,6 +3192,11 @@ static void addrconf_dev_config(struct net_device *dev) if (IS_ERR(idev)) return; + /* this device type has no EUI support */ + if (dev->type == ARPHRD_NONE && + idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) + idev->addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM; + addrconf_addr_gen(idev, false); } @@ -5041,7 +5073,8 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla) if (mode != IN6_ADDR_GEN_MODE_EUI64 && mode != IN6_ADDR_GEN_MODE_NONE && - mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) + mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY && + mode != IN6_ADDR_GEN_MODE_RANDOM) return -EINVAL; if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY && |