diff options
Diffstat (limited to 'drivers')
228 files changed, 9505 insertions, 4289 deletions
diff --git a/drivers/android/binder.c b/drivers/android/binder.c index a4b98e95ab85..76052006bd87 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1971,7 +1971,7 @@ static bool binder_validate_fixup(struct binder_proc *proc, * struct binder_task_work_cb - for deferred close * * @twork: callback_head for task work - * @fd: fd to close + * @file: file to close * * Structure to pass task work to be handled after * returning from binder_ioctl() via task_work_add(). @@ -3800,13 +3800,13 @@ err_invalid_target_handle: } binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, - "%d:%d transaction %s to %d:%d failed %d/%d/%d, size %lld-%lld line %d\n", + "%d:%d transaction %s to %d:%d failed %d/%d/%d, code %u size %lld-%lld line %d\n", proc->pid, thread->pid, reply ? "reply" : (tr->flags & TF_ONE_WAY ? "async" : "call"), target_proc ? target_proc->pid : 0, target_thread ? target_thread->pid : 0, t_debug_id, return_error, return_error_param, - (u64)tr->data_size, (u64)tr->offsets_size, + tr->code, (u64)tr->data_size, (u64)tr->offsets_size, return_error_line); if (target_thread) @@ -6373,7 +6373,7 @@ static void print_binder_transaction_ilocked(struct seq_file *m, seq_printf(m, " node %d", buffer->target_node->debug_id); seq_printf(m, " size %zd:%zd offset %lx\n", buffer->data_size, buffer->offsets_size, - proc->alloc.buffer - buffer->user_data); + proc->alloc.vm_start - buffer->user_data); } static void print_binder_work_ilocked(struct seq_file *m, @@ -6927,6 +6927,11 @@ const struct binder_debugfs_entry binder_debugfs_entries[] = { {} /* terminator */ }; +void binder_add_device(struct binder_device *device) +{ + hlist_add_head(&device->hlist, &binder_devices); +} + static int __init init_binder_device(const char *name) { int ret; diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index a738e7745865..fcfaf1b899c8 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -61,7 +61,7 @@ static size_t binder_alloc_buffer_size(struct binder_alloc *alloc, struct binder_buffer *buffer) { if (list_is_last(&buffer->entry, &alloc->buffers)) - return alloc->buffer + alloc->buffer_size - buffer->user_data; + return alloc->vm_start + alloc->buffer_size - buffer->user_data; return binder_buffer_next(buffer)->user_data - buffer->user_data; } @@ -169,32 +169,33 @@ struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc, { struct binder_buffer *buffer; - spin_lock(&alloc->lock); + mutex_lock(&alloc->mutex); buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr); - spin_unlock(&alloc->lock); + mutex_unlock(&alloc->mutex); return buffer; } static inline void -binder_set_installed_page(struct binder_lru_page *lru_page, +binder_set_installed_page(struct binder_alloc *alloc, + unsigned long index, struct page *page) { /* Pairs with acquire in binder_get_installed_page() */ - smp_store_release(&lru_page->page_ptr, page); + smp_store_release(&alloc->pages[index], page); } static inline struct page * -binder_get_installed_page(struct binder_lru_page *lru_page) +binder_get_installed_page(struct binder_alloc *alloc, unsigned long index) { /* Pairs with release in binder_set_installed_page() */ - return smp_load_acquire(&lru_page->page_ptr); + return smp_load_acquire(&alloc->pages[index]); } static void binder_lru_freelist_add(struct binder_alloc *alloc, unsigned long start, unsigned long end) { - struct binder_lru_page *page; unsigned long page_addr; + struct page *page; trace_binder_update_page_range(alloc, false, start, end); @@ -202,65 +203,159 @@ static void binder_lru_freelist_add(struct binder_alloc *alloc, size_t index; int ret; - index = (page_addr - alloc->buffer) / PAGE_SIZE; - page = &alloc->pages[index]; - - if (!binder_get_installed_page(page)) + index = (page_addr - alloc->vm_start) / PAGE_SIZE; + page = binder_get_installed_page(alloc, index); + if (!page) continue; trace_binder_free_lru_start(alloc, index); - ret = list_lru_add_obj(&binder_freelist, &page->lru); + ret = list_lru_add(&binder_freelist, + page_to_lru(page), + page_to_nid(page), + NULL); WARN_ON(!ret); trace_binder_free_lru_end(alloc, index); } } -static int binder_install_single_page(struct binder_alloc *alloc, - struct binder_lru_page *lru_page, - unsigned long addr) +static inline +void binder_alloc_set_mapped(struct binder_alloc *alloc, bool state) { - struct page *page; - int ret = 0; + /* pairs with smp_load_acquire in binder_alloc_is_mapped() */ + smp_store_release(&alloc->mapped, state); +} - if (!mmget_not_zero(alloc->mm)) - return -ESRCH; +static inline bool binder_alloc_is_mapped(struct binder_alloc *alloc) +{ + /* pairs with smp_store_release in binder_alloc_set_mapped() */ + return smp_load_acquire(&alloc->mapped); +} + +static struct page *binder_page_lookup(struct binder_alloc *alloc, + unsigned long addr) +{ + struct mm_struct *mm = alloc->mm; + struct page *page; + long npages = 0; /* - * Protected with mmap_sem in write mode as multiple tasks - * might race to install the same page. + * Find an existing page in the remote mm. If missing, + * don't attempt to fault-in just propagate an error. */ - mmap_write_lock(alloc->mm); - if (binder_get_installed_page(lru_page)) - goto out; + mmap_read_lock(mm); + if (binder_alloc_is_mapped(alloc)) + npages = get_user_pages_remote(mm, addr, 1, FOLL_NOFAULT, + &page, NULL); + mmap_read_unlock(mm); - if (!alloc->vma) { - pr_err("%d: %s failed, no vma\n", alloc->pid, __func__); - ret = -ESRCH; - goto out; + return npages > 0 ? page : NULL; +} + +static int binder_page_insert(struct binder_alloc *alloc, + unsigned long addr, + struct page *page) +{ + struct mm_struct *mm = alloc->mm; + struct vm_area_struct *vma; + int ret = -ESRCH; + + /* attempt per-vma lock first */ + vma = lock_vma_under_rcu(mm, addr); + if (vma) { + if (binder_alloc_is_mapped(alloc)) + ret = vm_insert_page(vma, addr, page); + vma_end_read(vma); + return ret; } + /* fall back to mmap_lock */ + mmap_read_lock(mm); + vma = vma_lookup(mm, addr); + if (vma && binder_alloc_is_mapped(alloc)) + ret = vm_insert_page(vma, addr, page); + mmap_read_unlock(mm); + + return ret; +} + +static struct page *binder_page_alloc(struct binder_alloc *alloc, + unsigned long index) +{ + struct binder_shrinker_mdata *mdata; + struct page *page; + page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); + if (!page) + return NULL; + + /* allocate and install shrinker metadata under page->private */ + mdata = kzalloc(sizeof(*mdata), GFP_KERNEL); + if (!mdata) { + __free_page(page); + return NULL; + } + + mdata->alloc = alloc; + mdata->page_index = index; + INIT_LIST_HEAD(&mdata->lru); + set_page_private(page, (unsigned long)mdata); + + return page; +} + +static void binder_free_page(struct page *page) +{ + kfree((struct binder_shrinker_mdata *)page_private(page)); + __free_page(page); +} + +static int binder_install_single_page(struct binder_alloc *alloc, + unsigned long index, + unsigned long addr) +{ + struct page *page; + int ret; + + if (!mmget_not_zero(alloc->mm)) + return -ESRCH; + + page = binder_page_alloc(alloc, index); if (!page) { - pr_err("%d: failed to allocate page\n", alloc->pid); ret = -ENOMEM; goto out; } - ret = vm_insert_page(alloc->vma, addr, page); - if (ret) { + ret = binder_page_insert(alloc, addr, page); + switch (ret) { + case -EBUSY: + /* + * EBUSY is ok. Someone installed the pte first but the + * alloc->pages[index] has not been updated yet. Discard + * our page and look up the one already installed. + */ + ret = 0; + binder_free_page(page); + page = binder_page_lookup(alloc, addr); + if (!page) { + pr_err("%d: failed to find page at offset %lx\n", + alloc->pid, addr - alloc->vm_start); + ret = -ESRCH; + break; + } + fallthrough; + case 0: + /* Mark page installation complete and safe to use */ + binder_set_installed_page(alloc, index, page); + break; + default: + binder_free_page(page); pr_err("%d: %s failed to insert page at offset %lx with %d\n", - alloc->pid, __func__, addr - alloc->buffer, ret); - __free_page(page); - ret = -ENOMEM; - goto out; + alloc->pid, __func__, addr - alloc->vm_start, ret); + break; } - - /* Mark page installation complete and safe to use */ - binder_set_installed_page(lru_page, page); out: - mmap_write_unlock(alloc->mm); mmput_async(alloc->mm); return ret; } @@ -269,7 +364,6 @@ static int binder_install_buffer_pages(struct binder_alloc *alloc, struct binder_buffer *buffer, size_t size) { - struct binder_lru_page *page; unsigned long start, final; unsigned long page_addr; @@ -280,15 +374,13 @@ static int binder_install_buffer_pages(struct binder_alloc *alloc, unsigned long index; int ret; - index = (page_addr - alloc->buffer) / PAGE_SIZE; - page = &alloc->pages[index]; - - if (binder_get_installed_page(page)) + index = (page_addr - alloc->vm_start) / PAGE_SIZE; + if (binder_get_installed_page(alloc, index)) continue; trace_binder_alloc_page_start(alloc, index); - ret = binder_install_single_page(alloc, page, page_addr); + ret = binder_install_single_page(alloc, index, page_addr); if (ret) return ret; @@ -302,8 +394,8 @@ static int binder_install_buffer_pages(struct binder_alloc *alloc, static void binder_lru_freelist_del(struct binder_alloc *alloc, unsigned long start, unsigned long end) { - struct binder_lru_page *page; unsigned long page_addr; + struct page *page; trace_binder_update_page_range(alloc, true, start, end); @@ -311,13 +403,16 @@ static void binder_lru_freelist_del(struct binder_alloc *alloc, unsigned long index; bool on_lru; - index = (page_addr - alloc->buffer) / PAGE_SIZE; - page = &alloc->pages[index]; + index = (page_addr - alloc->vm_start) / PAGE_SIZE; + page = binder_get_installed_page(alloc, index); - if (page->page_ptr) { + if (page) { trace_binder_alloc_lru_start(alloc, index); - on_lru = list_lru_del_obj(&binder_freelist, &page->lru); + on_lru = list_lru_del(&binder_freelist, + page_to_lru(page), + page_to_nid(page), + NULL); WARN_ON(!on_lru); trace_binder_alloc_lru_end(alloc, index); @@ -329,20 +424,6 @@ static void binder_lru_freelist_del(struct binder_alloc *alloc, } } -static inline void binder_alloc_set_vma(struct binder_alloc *alloc, - struct vm_area_struct *vma) -{ - /* pairs with smp_load_acquire in binder_alloc_get_vma() */ - smp_store_release(&alloc->vma, vma); -} - -static inline struct vm_area_struct *binder_alloc_get_vma( - struct binder_alloc *alloc) -{ - /* pairs with smp_store_release in binder_alloc_set_vma() */ - return smp_load_acquire(&alloc->vma); -} - static void debug_no_space_locked(struct binder_alloc *alloc) { size_t largest_alloc_size = 0; @@ -576,7 +657,7 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, int ret; /* Check binder_alloc is fully initialized */ - if (!binder_alloc_get_vma(alloc)) { + if (!binder_alloc_is_mapped(alloc)) { binder_alloc_debug(BINDER_DEBUG_USER_ERROR, "%d: binder_alloc_buf, no vma\n", alloc->pid); @@ -597,10 +678,10 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, if (!next) return ERR_PTR(-ENOMEM); - spin_lock(&alloc->lock); + mutex_lock(&alloc->mutex); buffer = binder_alloc_new_buf_locked(alloc, next, size, is_async); if (IS_ERR(buffer)) { - spin_unlock(&alloc->lock); + mutex_unlock(&alloc->mutex); goto out; } @@ -608,7 +689,7 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, buffer->offsets_size = offsets_size; buffer->extra_buffers_size = extra_buffers_size; buffer->pid = current->tgid; - spin_unlock(&alloc->lock); + mutex_unlock(&alloc->mutex); ret = binder_install_buffer_pages(alloc, buffer, size); if (ret) { @@ -674,8 +755,8 @@ static void binder_free_buf_locked(struct binder_alloc *alloc, BUG_ON(buffer->free); BUG_ON(size > buffer_size); BUG_ON(buffer->transaction != NULL); - BUG_ON(buffer->user_data < alloc->buffer); - BUG_ON(buffer->user_data > alloc->buffer + alloc->buffer_size); + BUG_ON(buffer->user_data < alloc->vm_start); + BUG_ON(buffer->user_data > alloc->vm_start + alloc->buffer_size); if (buffer->async_transaction) { alloc->free_async_space += buffer_size; @@ -734,14 +815,13 @@ static struct page *binder_alloc_get_page(struct binder_alloc *alloc, pgoff_t *pgoffp) { binder_size_t buffer_space_offset = buffer_offset + - (buffer->user_data - alloc->buffer); + (buffer->user_data - alloc->vm_start); pgoff_t pgoff = buffer_space_offset & ~PAGE_MASK; size_t index = buffer_space_offset >> PAGE_SHIFT; - struct binder_lru_page *lru_page; - lru_page = &alloc->pages[index]; *pgoffp = pgoff; - return lru_page->page_ptr; + + return alloc->pages[index]; } /** @@ -785,17 +865,17 @@ void binder_alloc_free_buf(struct binder_alloc *alloc, * We could eliminate the call to binder_alloc_clear_buf() * from binder_alloc_deferred_release() by moving this to * binder_free_buf_locked(). However, that could - * increase contention for the alloc->lock if clear_on_free - * is used frequently for large buffers. This lock is not + * increase contention for the alloc mutex if clear_on_free + * is used frequently for large buffers. The mutex is not * needed for correctness here. */ if (buffer->clear_on_free) { binder_alloc_clear_buf(alloc, buffer); buffer->clear_on_free = false; } - spin_lock(&alloc->lock); + mutex_lock(&alloc->mutex); binder_free_buf_locked(alloc, buffer); - spin_unlock(&alloc->lock); + mutex_unlock(&alloc->mutex); } /** @@ -816,7 +896,7 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, { struct binder_buffer *buffer; const char *failure_string; - int ret, i; + int ret; if (unlikely(vma->vm_mm != alloc->mm)) { ret = -EINVAL; @@ -834,22 +914,17 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, SZ_4M); mutex_unlock(&binder_alloc_mmap_lock); - alloc->buffer = vma->vm_start; + alloc->vm_start = vma->vm_start; alloc->pages = kvcalloc(alloc->buffer_size / PAGE_SIZE, sizeof(alloc->pages[0]), GFP_KERNEL); - if (alloc->pages == NULL) { + if (!alloc->pages) { ret = -ENOMEM; failure_string = "alloc page array"; goto err_alloc_pages_failed; } - for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { - alloc->pages[i].alloc = alloc; - INIT_LIST_HEAD(&alloc->pages[i].lru); - } - buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); if (!buffer) { ret = -ENOMEM; @@ -857,14 +932,14 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, goto err_alloc_buf_struct_failed; } - buffer->user_data = alloc->buffer; + buffer->user_data = alloc->vm_start; list_add(&buffer->entry, &alloc->buffers); buffer->free = 1; binder_insert_free_buffer(alloc, buffer); alloc->free_async_space = alloc->buffer_size / 2; /* Signal binder_alloc is fully initialized */ - binder_alloc_set_vma(alloc, vma); + binder_alloc_set_mapped(alloc, true); return 0; @@ -872,7 +947,7 @@ err_alloc_buf_struct_failed: kvfree(alloc->pages); alloc->pages = NULL; err_alloc_pages_failed: - alloc->buffer = 0; + alloc->vm_start = 0; mutex_lock(&binder_alloc_mmap_lock); alloc->buffer_size = 0; err_already_mapped: @@ -893,8 +968,8 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) struct binder_buffer *buffer; buffers = 0; - spin_lock(&alloc->lock); - BUG_ON(alloc->vma); + mutex_lock(&alloc->mutex); + BUG_ON(alloc->mapped); while ((n = rb_first(&alloc->allocated_buffers))) { buffer = rb_entry(n, struct binder_buffer, rb_node); @@ -925,22 +1000,26 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) int i; for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { + struct page *page; bool on_lru; - if (!alloc->pages[i].page_ptr) + page = binder_get_installed_page(alloc, i); + if (!page) continue; - on_lru = list_lru_del_obj(&binder_freelist, - &alloc->pages[i].lru); + on_lru = list_lru_del(&binder_freelist, + page_to_lru(page), + page_to_nid(page), + NULL); binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, "%s: %d: page %d %s\n", __func__, alloc->pid, i, on_lru ? "on lru" : "active"); - __free_page(alloc->pages[i].page_ptr); + binder_free_page(page); page_count++; } } - spin_unlock(&alloc->lock); + mutex_unlock(&alloc->mutex); kvfree(alloc->pages); if (alloc->mm) mmdrop(alloc->mm); @@ -964,17 +1043,17 @@ void binder_alloc_print_allocated(struct seq_file *m, struct binder_buffer *buffer; struct rb_node *n; - spin_lock(&alloc->lock); + mutex_lock(&alloc->mutex); for (n = rb_first(&alloc->allocated_buffers); n; n = rb_next(n)) { buffer = rb_entry(n, struct binder_buffer, rb_node); seq_printf(m, " buffer %d: %lx size %zd:%zd:%zd %s\n", buffer->debug_id, - buffer->user_data - alloc->buffer, + buffer->user_data - alloc->vm_start, buffer->data_size, buffer->offsets_size, buffer->extra_buffers_size, buffer->transaction ? "active" : "delivered"); } - spin_unlock(&alloc->lock); + mutex_unlock(&alloc->mutex); } /** @@ -985,29 +1064,29 @@ void binder_alloc_print_allocated(struct seq_file *m, void binder_alloc_print_pages(struct seq_file *m, struct binder_alloc *alloc) { - struct binder_lru_page *page; + struct page *page; int i; int active = 0; int lru = 0; int free = 0; - spin_lock(&alloc->lock); + mutex_lock(&alloc->mutex); /* * Make sure the binder_alloc is fully initialized, otherwise we might * read inconsistent state. */ - if (binder_alloc_get_vma(alloc) != NULL) { + if (binder_alloc_is_mapped(alloc)) { for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { - page = &alloc->pages[i]; - if (!page->page_ptr) + page = binder_get_installed_page(alloc, i); + if (!page) free++; - else if (list_empty(&page->lru)) + else if (list_empty(page_to_lru(page))) active++; else lru++; } } - spin_unlock(&alloc->lock); + mutex_unlock(&alloc->mutex); seq_printf(m, " pages: %d:%d:%d\n", active, lru, free); seq_printf(m, " pages high watermark: %zu\n", alloc->pages_high); } @@ -1023,10 +1102,10 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc) struct rb_node *n; int count = 0; - spin_lock(&alloc->lock); + mutex_lock(&alloc->mutex); for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n)) count++; - spin_unlock(&alloc->lock); + mutex_unlock(&alloc->mutex); return count; } @@ -1036,12 +1115,12 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc) * @alloc: binder_alloc for this proc * * Called from binder_vma_close() when releasing address space. - * Clears alloc->vma to prevent new incoming transactions from + * Clears alloc->mapped to prevent new incoming transactions from * allocating more buffers. */ void binder_alloc_vma_close(struct binder_alloc *alloc) { - binder_alloc_set_vma(alloc, NULL); + binder_alloc_set_mapped(alloc, false); } /** @@ -1058,39 +1137,50 @@ enum lru_status binder_alloc_free_page(struct list_head *item, void *cb_arg) __must_hold(&lru->lock) { - struct binder_lru_page *page = container_of(item, typeof(*page), lru); - struct binder_alloc *alloc = page->alloc; + struct binder_shrinker_mdata *mdata = container_of(item, typeof(*mdata), lru); + struct binder_alloc *alloc = mdata->alloc; struct mm_struct *mm = alloc->mm; struct vm_area_struct *vma; struct page *page_to_free; unsigned long page_addr; + int mm_locked = 0; size_t index; if (!mmget_not_zero(mm)) goto err_mmget; - if (!mmap_read_trylock(mm)) - goto err_mmap_read_lock_failed; - if (!spin_trylock(&alloc->lock)) - goto err_get_alloc_lock_failed; - if (!page->page_ptr) - goto err_page_already_freed; - - index = page - alloc->pages; - page_addr = alloc->buffer + index * PAGE_SIZE; - - vma = vma_lookup(mm, page_addr); - if (vma && vma != binder_alloc_get_vma(alloc)) + + index = mdata->page_index; + page_addr = alloc->vm_start + index * PAGE_SIZE; + + /* attempt per-vma lock first */ + vma = lock_vma_under_rcu(mm, page_addr); + if (!vma) { + /* fall back to mmap_lock */ + if (!mmap_read_trylock(mm)) + goto err_mmap_read_lock_failed; + mm_locked = 1; + vma = vma_lookup(mm, page_addr); + } + + if (!mutex_trylock(&alloc->mutex)) + goto err_get_alloc_mutex_failed; + + /* + * Since a binder_alloc can only be mapped once, we ensure + * the vma corresponds to this mapping by checking whether + * the binder_alloc is still mapped. + */ + if (vma && !binder_alloc_is_mapped(alloc)) goto err_invalid_vma; trace_binder_unmap_kernel_start(alloc, index); - page_to_free = page->page_ptr; - page->page_ptr = NULL; + page_to_free = alloc->pages[index]; + binder_set_installed_page(alloc, index, NULL); trace_binder_unmap_kernel_end(alloc, index); list_lru_isolate(lru, item); - spin_unlock(&alloc->lock); spin_unlock(&lru->lock); if (vma) { @@ -1101,17 +1191,23 @@ enum lru_status binder_alloc_free_page(struct list_head *item, trace_binder_unmap_user_end(alloc, index); } - mmap_read_unlock(mm); + mutex_unlock(&alloc->mutex); + if (mm_locked) + mmap_read_unlock(mm); + else + vma_end_read(vma); mmput_async(mm); - __free_page(page_to_free); + binder_free_page(page_to_free); return LRU_REMOVED_RETRY; err_invalid_vma: -err_page_already_freed: - spin_unlock(&alloc->lock); -err_get_alloc_lock_failed: - mmap_read_unlock(mm); + mutex_unlock(&alloc->mutex); +err_get_alloc_mutex_failed: + if (mm_locked) + mmap_read_unlock(mm); + else + vma_end_read(vma); err_mmap_read_lock_failed: mmput_async(mm); err_mmget: @@ -1145,7 +1241,7 @@ void binder_alloc_init(struct binder_alloc *alloc) alloc->pid = current->group_leader->pid; alloc->mm = current->mm; mmgrab(alloc->mm); - spin_lock_init(&alloc->lock); + mutex_init(&alloc->mutex); INIT_LIST_HEAD(&alloc->buffers); } diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index c02c8ebcb466..feecd7414241 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -9,7 +9,7 @@ #include <linux/rbtree.h> #include <linux/list.h> #include <linux/mm.h> -#include <linux/spinlock.h> +#include <linux/rtmutex.h> #include <linux/vmalloc.h> #include <linux/slab.h> #include <linux/list_lru.h> @@ -59,34 +59,43 @@ struct binder_buffer { }; /** - * struct binder_lru_page - page object used for binder shrinker - * @page_ptr: pointer to physical page in mmap'd space - * @lru: entry in binder_freelist - * @alloc: binder_alloc for a proc + * struct binder_shrinker_mdata - binder metadata used to reclaim pages + * @lru: LRU entry in binder_freelist + * @alloc: binder_alloc owning the page to reclaim + * @page_index: offset in @alloc->pages[] into the page to reclaim */ -struct binder_lru_page { +struct binder_shrinker_mdata { struct list_head lru; - struct page *page_ptr; struct binder_alloc *alloc; + unsigned long page_index; }; +static inline struct list_head *page_to_lru(struct page *p) +{ + struct binder_shrinker_mdata *mdata; + + mdata = (struct binder_shrinker_mdata *)page_private(p); + + return &mdata->lru; +} + /** * struct binder_alloc - per-binder proc state for binder allocator - * @lock: protects binder_alloc fields - * @vma: vm_area_struct passed to mmap_handler - * (invariant after mmap) + * @mutex: protects binder_alloc fields * @mm: copy of task->mm (invariant after open) - * @buffer: base of per-proc address space mapped via mmap + * @vm_start: base of per-proc address space mapped via mmap * @buffers: list of all buffers for this proc * @free_buffers: rb tree of buffers available for allocation * sorted by size * @allocated_buffers: rb tree of allocated buffers sorted by address * @free_async_space: VA space available for async buffers. This is * initialized at mmap time to 1/2 the full VA space - * @pages: array of binder_lru_page + * @pages: array of struct page * * @buffer_size: size of address space specified via mmap * @pid: pid for associated binder_proc (invariant after init) * @pages_high: high watermark of offset in @pages + * @mapped: whether the vm area is mapped, each binder instance is + * allowed a single mapping throughout its lifetime * @oneway_spam_detected: %true if oneway spam detection fired, clear that * flag once the async buffer has returned to a healthy state * @@ -96,18 +105,18 @@ struct binder_lru_page { * struct binder_buffer objects used to track the user buffers */ struct binder_alloc { - spinlock_t lock; - struct vm_area_struct *vma; + struct mutex mutex; struct mm_struct *mm; - unsigned long buffer; + unsigned long vm_start; struct list_head buffers; struct rb_root free_buffers; struct rb_root allocated_buffers; size_t free_async_space; - struct binder_lru_page *pages; + struct page **pages; size_t buffer_size; int pid; size_t pages_high; + bool mapped; bool oneway_spam_detected; }; @@ -153,9 +162,9 @@ binder_alloc_get_free_async_space(struct binder_alloc *alloc) { size_t free_async_space; - spin_lock(&alloc->lock); + mutex_lock(&alloc->mutex); free_async_space = alloc->free_async_space; - spin_unlock(&alloc->lock); + mutex_unlock(&alloc->mutex); return free_async_space; } diff --git a/drivers/android/binder_alloc_selftest.c b/drivers/android/binder_alloc_selftest.c index 81442fe20a69..c88735c54848 100644 --- a/drivers/android/binder_alloc_selftest.c +++ b/drivers/android/binder_alloc_selftest.c @@ -104,11 +104,11 @@ static bool check_buffer_pages_allocated(struct binder_alloc *alloc, end = PAGE_ALIGN(buffer->user_data + size); page_addr = buffer->user_data; for (; page_addr < end; page_addr += PAGE_SIZE) { - page_index = (page_addr - alloc->buffer) / PAGE_SIZE; - if (!alloc->pages[page_index].page_ptr || - !list_empty(&alloc->pages[page_index].lru)) { + page_index = (page_addr - alloc->vm_start) / PAGE_SIZE; + if (!alloc->pages[page_index] || + !list_empty(page_to_lru(alloc->pages[page_index]))) { pr_err("expect alloc but is %s at page index %d\n", - alloc->pages[page_index].page_ptr ? + alloc->pages[page_index] ? "lru" : "free", page_index); return false; } @@ -148,10 +148,10 @@ static void binder_selftest_free_buf(struct binder_alloc *alloc, * if binder shrinker ran during binder_alloc_free_buf * calls above. */ - if (list_empty(&alloc->pages[i].lru)) { + if (list_empty(page_to_lru(alloc->pages[i]))) { pr_err_size_seq(sizes, seq); pr_err("expect lru but is %s at page index %d\n", - alloc->pages[i].page_ptr ? "alloc" : "free", i); + alloc->pages[i] ? "alloc" : "free", i); binder_selftest_failures++; } } @@ -168,9 +168,9 @@ static void binder_selftest_free_page(struct binder_alloc *alloc) } for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) { - if (alloc->pages[i].page_ptr) { + if (alloc->pages[i]) { pr_err("expect free but is %s at page index %d\n", - list_empty(&alloc->pages[i].lru) ? + list_empty(page_to_lru(alloc->pages[i])) ? "alloc" : "lru", i); binder_selftest_failures++; } @@ -291,7 +291,7 @@ void binder_selftest_alloc(struct binder_alloc *alloc) if (!binder_selftest_run) return; mutex_lock(&binder_selftest_lock); - if (!binder_selftest_run || !alloc->vma) + if (!binder_selftest_run || !alloc->mapped) goto done; pr_info("STARTED\n"); binder_selftest_alloc_offset(alloc, end_offset, 0); diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index f8d6be682f23..e4eb8357989c 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -25,8 +25,7 @@ struct binder_context { /** * struct binder_device - information about a binder device node - * @hlist: list of binder devices (only used for devices requested via - * CONFIG_ANDROID_BINDER_DEVICES) + * @hlist: list of binder devices * @miscdev: information about a binder character device node * @context: binder context information * @binderfs_inode: This is the inode of the root dentry of the super block @@ -582,4 +581,12 @@ struct binder_object { }; }; +/** + * Add a binder device to binder_devices + * @device: the new binder device to add to the global list + * + * Not reentrant as the list is not protected by any locks + */ +void binder_add_device(struct binder_device *device); + #endif /* _LINUX_BINDER_INTERNAL_H */ diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h index fe38c6fc65d0..16de1b9e72f7 100644 --- a/drivers/android/binder_trace.h +++ b/drivers/android/binder_trace.h @@ -328,7 +328,7 @@ TRACE_EVENT(binder_update_page_range, TP_fast_assign( __entry->proc = alloc->pid; __entry->allocate = allocate; - __entry->offset = start - alloc->buffer; + __entry->offset = start - alloc->vm_start; __entry->size = end - start; ), TP_printk("proc=%d allocate=%d offset=%zu size=%zu", diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index ad1fa7abc323..bc6bae76ccaf 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -207,6 +207,8 @@ static int binderfs_binder_device_create(struct inode *ref_inode, fsnotify_create(root->d_inode, dentry); inode_unlock(d_inode(root)); + binder_add_device(device); + return 0; err: diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c index e8c92972f9df..9dcc7184817d 100644 --- a/drivers/bus/mhi/host/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -357,6 +357,7 @@ error_alloc_segment: for (--i, --mhi_buf; i >= 0; i--, mhi_buf--) dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len, mhi_buf->buf, mhi_buf->dma_addr); + kfree(img_info->mhi_buf); error_alloc_mhi_buf: kfree(img_info); diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 56ba4192c89c..c41119b9079f 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -245,6 +245,58 @@ struct mhi_pci_dev_info { .channel = ch_num, \ } +static const struct mhi_channel_config mhi_qcom_qdu100_channels[] = { + MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 2), + MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 2), + MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 128, 1), + MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 128, 1), + MHI_CHANNEL_CONFIG_UL(4, "DIAG", 64, 3), + MHI_CHANNEL_CONFIG_DL(5, "DIAG", 64, 3), + MHI_CHANNEL_CONFIG_UL(9, "QDSS", 64, 3), + MHI_CHANNEL_CONFIG_UL(14, "NMEA", 32, 4), + MHI_CHANNEL_CONFIG_DL(15, "NMEA", 32, 4), + MHI_CHANNEL_CONFIG_UL(16, "CSM_CTRL", 32, 4), + MHI_CHANNEL_CONFIG_DL(17, "CSM_CTRL", 32, 4), + MHI_CHANNEL_CONFIG_UL(40, "MHI_PHC", 32, 4), + MHI_CHANNEL_CONFIG_DL(41, "MHI_PHC", 32, 4), + MHI_CHANNEL_CONFIG_UL(46, "IP_SW0", 256, 5), + MHI_CHANNEL_CONFIG_DL(47, "IP_SW0", 256, 5), +}; + +static struct mhi_event_config mhi_qcom_qdu100_events[] = { + /* first ring is control+data ring */ + MHI_EVENT_CONFIG_CTRL(0, 64), + /* SAHARA dedicated event ring */ + MHI_EVENT_CONFIG_SW_DATA(1, 256), + /* Software channels dedicated event ring */ + MHI_EVENT_CONFIG_SW_DATA(2, 64), + MHI_EVENT_CONFIG_SW_DATA(3, 256), + MHI_EVENT_CONFIG_SW_DATA(4, 256), + /* Software IP channels dedicated event ring */ + MHI_EVENT_CONFIG_SW_DATA(5, 512), + MHI_EVENT_CONFIG_SW_DATA(6, 512), + MHI_EVENT_CONFIG_SW_DATA(7, 512), +}; + +static const struct mhi_controller_config mhi_qcom_qdu100_config = { + .max_channels = 128, + .timeout_ms = 120000, + .num_channels = ARRAY_SIZE(mhi_qcom_qdu100_channels), + .ch_cfg = mhi_qcom_qdu100_channels, + .num_events = ARRAY_SIZE(mhi_qcom_qdu100_events), + .event_cfg = mhi_qcom_qdu100_events, +}; + +static const struct mhi_pci_dev_info mhi_qcom_qdu100_info = { + .name = "qcom-qdu100", + .fw = "qcom/qdu100/xbl_s.melf", + .edl_trigger = true, + .config = &mhi_qcom_qdu100_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .sideband_wake = false, +}; + static const struct mhi_channel_config modem_qcom_v1_mhi_channels[] = { MHI_CHANNEL_CONFIG_UL(4, "DIAG", 16, 1), MHI_CHANNEL_CONFIG_DL(5, "DIAG", 16, 1), @@ -742,6 +794,9 @@ static const struct pci_device_id mhi_pci_id_table[] = { .driver_data = (kernel_ulong_t) &mhi_qcom_sdx65_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0309), .driver_data = (kernel_ulong_t) &mhi_qcom_sdx75_info }, + /* QDU100, x100-DU */ + { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0601), + .driver_data = (kernel_ulong_t) &mhi_qcom_qdu100_info }, { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1001), /* EM120R-GL (sdx24) */ .driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info }, { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1002), /* EM160R-GL (sdx24) */ @@ -949,7 +1004,7 @@ static int mhi_pci_get_irqs(struct mhi_controller *mhi_cntrl, */ mhi_cntrl->nr_irqs = 1 + mhi_cntrl_config->num_events; - nr_vectors = pci_alloc_irq_vectors(pdev, 1, mhi_cntrl->nr_irqs, PCI_IRQ_MSI); + nr_vectors = pci_alloc_irq_vectors(pdev, 1, mhi_cntrl->nr_irqs, PCI_IRQ_MSIX | PCI_IRQ_MSI); if (nr_vectors < 0) { dev_err(&pdev->dev, "Error allocating MSI vectors %d\n", nr_vectors); diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index 76eac3653b1c..c573ed2ee71a 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -338,7 +338,10 @@ static void cdx_shutdown(struct device *dev) { struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver); struct cdx_device *cdx_dev = to_cdx_device(dev); + struct cdx_controller *cdx = cdx_dev->cdx; + if (cdx_dev->is_bus && cdx_dev->enabled && cdx->ops->bus_disable) + cdx->ops->bus_disable(cdx, cdx_dev->bus_num); if (cdx_drv && cdx_drv->shutdown) cdx_drv->shutdown(cdx_dev); } diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 541edc26ec89..2cf595d2e10b 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -63,16 +63,30 @@ static DEFINE_MUTEX(misc_mtx); #define DYNAMIC_MINORS 128 /* like dynamic majors */ static DEFINE_IDA(misc_minors_ida); -static int misc_minor_alloc(void) +static int misc_minor_alloc(int minor) { - int ret; - - ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL); - if (ret >= 0) { - ret = DYNAMIC_MINORS - ret - 1; + int ret = 0; + + if (minor == MISC_DYNAMIC_MINOR) { + /* allocate free id */ + ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL); + if (ret >= 0) { + ret = DYNAMIC_MINORS - ret - 1; + } else { + ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1, + MINORMASK, GFP_KERNEL); + } } else { - ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1, - MINORMASK, GFP_KERNEL); + /* specific minor, check if it is in dynamic or misc dynamic range */ + if (minor < DYNAMIC_MINORS) { + minor = DYNAMIC_MINORS - minor - 1; + ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL); + } else if (minor > MISC_DYNAMIC_MINOR) { + ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL); + } else { + /* case of non-dynamic minors, no need to allocate id */ + ret = 0; + } } return ret; } @@ -219,7 +233,7 @@ int misc_register(struct miscdevice *misc) mutex_lock(&misc_mtx); if (is_dynamic) { - int i = misc_minor_alloc(); + int i = misc_minor_alloc(misc->minor); if (i < 0) { err = -EBUSY; @@ -228,6 +242,7 @@ int misc_register(struct miscdevice *misc) misc->minor = i; } else { struct miscdevice *c; + int i; list_for_each_entry(c, &misc_list, list) { if (c->minor == misc->minor) { @@ -235,6 +250,12 @@ int misc_register(struct miscdevice *misc) goto out; } } + + i = misc_minor_alloc(misc->minor); + if (i < 0) { + err = -EBUSY; + goto out; + } } dev = MKDEV(MISC_MAJOR, misc->minor); diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index c62b208b42f1..24442485e73e 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -883,9 +883,9 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf, if (len + offset > PAGE_SIZE) len = PAGE_SIZE - offset; - src = kmap_atomic(buf->page); + src = kmap_local_page(buf->page); memcpy(page_address(page) + offset, src + buf->offset, len); - kunmap_atomic(src); + kunmap_local(src); sg_set_page(&(sgl->sg[sgl->n]), page, len, offset); } diff --git a/drivers/extcon/extcon-fsa9480.c b/drivers/extcon/extcon-fsa9480.c index e458ce0c45ab..b11b43171063 100644 --- a/drivers/extcon/extcon-fsa9480.c +++ b/drivers/extcon/extcon-fsa9480.c @@ -350,7 +350,7 @@ static const struct dev_pm_ops fsa9480_pm_ops = { }; static const struct i2c_device_id fsa9480_id[] = { - { "fsa9480", 0 }, + { "fsa9480" }, {} }; MODULE_DEVICE_TABLE(i2c, fsa9480_id); diff --git a/drivers/extcon/extcon-ptn5150.c b/drivers/extcon/extcon-ptn5150.c index 4616da7e5430..78ad86c4a3be 100644 --- a/drivers/extcon/extcon-ptn5150.c +++ b/drivers/extcon/extcon-ptn5150.c @@ -338,7 +338,7 @@ static const struct of_device_id ptn5150_dt_match[] = { MODULE_DEVICE_TABLE(of, ptn5150_dt_match); static const struct i2c_device_id ptn5150_i2c_id[] = { - { "ptn5150", 0 }, + { "ptn5150" }, { } }; MODULE_DEVICE_TABLE(i2c, ptn5150_i2c_id); diff --git a/drivers/extcon/extcon-rtk-type-c.c b/drivers/extcon/extcon-rtk-type-c.c index bdc2b7b3a246..82b60b927e41 100644 --- a/drivers/extcon/extcon-rtk-type-c.c +++ b/drivers/extcon/extcon-rtk-type-c.c @@ -1369,6 +1369,8 @@ static int extcon_rtk_type_c_probe(struct platform_device *pdev) } type_c->type_c_cfg = devm_kzalloc(dev, sizeof(*type_c_cfg), GFP_KERNEL); + if (!type_c->type_c_cfg) + return -ENOMEM; memcpy(type_c->type_c_cfg, type_c_cfg, sizeof(*type_c_cfg)); diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index c5c78b869561..3c52cb73237a 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -967,18 +967,15 @@ int stratix10_svc_send(struct stratix10_svc_chan *chan, void *msg) /* first client will create kernel thread */ if (!chan->ctrl->task) { chan->ctrl->task = - kthread_create_on_node(svc_normal_to_secure_thread, - (void *)chan->ctrl, - cpu_to_node(cpu), - "svc_smc_hvc_thread"); + kthread_run_on_cpu(svc_normal_to_secure_thread, + (void *)chan->ctrl, + cpu, "svc_smc_hvc_thread"); if (IS_ERR(chan->ctrl->task)) { dev_err(chan->ctrl->dev, "failed to create svc_smc_hvc_thread\n"); kfree(p_data); return -EINVAL; } - kthread_bind(chan->ctrl->task, cpu); - wake_up_process(chan->ctrl->task); } pr_debug("%s: sent P-va=%p, P-com=%x, P-size=%u\n", __func__, diff --git a/drivers/fpga/dfl-afu-dma-region.c b/drivers/fpga/dfl-afu-dma-region.c index 02b60fde0430..5aa7b8884374 100644 --- a/drivers/fpga/dfl-afu-dma-region.c +++ b/drivers/fpga/dfl-afu-dma-region.c @@ -16,26 +16,26 @@ #include "dfl-afu.h" -void afu_dma_region_init(struct dfl_feature_platform_data *pdata) +void afu_dma_region_init(struct dfl_feature_dev_data *fdata) { - struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata); + struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata); afu->dma_regions = RB_ROOT; } /** * afu_dma_pin_pages - pin pages of given dma memory region - * @pdata: feature device platform data + * @fdata: feature dev data * @region: dma memory region to be pinned * * Pin all the pages of given dfl_afu_dma_region. * Return 0 for success or negative error code. */ -static int afu_dma_pin_pages(struct dfl_feature_platform_data *pdata, +static int afu_dma_pin_pages(struct dfl_feature_dev_data *fdata, struct dfl_afu_dma_region *region) { int npages = region->length >> PAGE_SHIFT; - struct device *dev = &pdata->dev->dev; + struct device *dev = &fdata->dev->dev; int ret, pinned; ret = account_locked_vm(current->mm, npages, true); @@ -73,17 +73,17 @@ unlock_vm: /** * afu_dma_unpin_pages - unpin pages of given dma memory region - * @pdata: feature device platform data + * @fdata: feature dev data * @region: dma memory region to be unpinned * * Unpin all the pages of given dfl_afu_dma_region. * Return 0 for success or negative error code. */ -static void afu_dma_unpin_pages(struct dfl_feature_platform_data *pdata, +static void afu_dma_unpin_pages(struct dfl_feature_dev_data *fdata, struct dfl_afu_dma_region *region) { long npages = region->length >> PAGE_SHIFT; - struct device *dev = &pdata->dev->dev; + struct device *dev = &fdata->dev->dev; unpin_user_pages(region->pages, npages); kfree(region->pages); @@ -133,20 +133,20 @@ static bool dma_region_check_iova(struct dfl_afu_dma_region *region, /** * afu_dma_region_add - add given dma region to rbtree - * @pdata: feature device platform data + * @fdata: feature dev data * @region: dma region to be added * * Return 0 for success, -EEXIST if dma region has already been added. * - * Needs to be called with pdata->lock heold. + * Needs to be called with fdata->lock held. */ -static int afu_dma_region_add(struct dfl_feature_platform_data *pdata, +static int afu_dma_region_add(struct dfl_feature_dev_data *fdata, struct dfl_afu_dma_region *region) { - struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata); + struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata); struct rb_node **new, *parent = NULL; - dev_dbg(&pdata->dev->dev, "add region (iova = %llx)\n", + dev_dbg(&fdata->dev->dev, "add region (iova = %llx)\n", (unsigned long long)region->iova); new = &afu->dma_regions.rb_node; @@ -177,50 +177,50 @@ static int afu_dma_region_add(struct dfl_feature_platform_data *pdata, /** * afu_dma_region_remove - remove given dma region from rbtree - * @pdata: feature device platform data + * @fdata: feature dev data * @region: dma region to be removed * - * Needs to be called with pdata->lock heold. + * Needs to be called with fdata->lock held. */ -static void afu_dma_region_remove(struct dfl_feature_platform_data *pdata, +static void afu_dma_region_remove(struct dfl_feature_dev_data *fdata, struct dfl_afu_dma_region *region) { struct dfl_afu *afu; - dev_dbg(&pdata->dev->dev, "del region (iova = %llx)\n", + dev_dbg(&fdata->dev->dev, "del region (iova = %llx)\n", (unsigned long long)region->iova); - afu = dfl_fpga_pdata_get_private(pdata); + afu = dfl_fpga_fdata_get_private(fdata); rb_erase(®ion->node, &afu->dma_regions); } /** * afu_dma_region_destroy - destroy all regions in rbtree - * @pdata: feature device platform data + * @fdata: feature dev data * - * Needs to be called with pdata->lock heold. + * Needs to be called with fdata->lock held. */ -void afu_dma_region_destroy(struct dfl_feature_platform_data *pdata) +void afu_dma_region_destroy(struct dfl_feature_dev_data *fdata) { - struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata); + struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata); struct rb_node *node = rb_first(&afu->dma_regions); struct dfl_afu_dma_region *region; while (node) { region = container_of(node, struct dfl_afu_dma_region, node); - dev_dbg(&pdata->dev->dev, "del region (iova = %llx)\n", + dev_dbg(&fdata->dev->dev, "del region (iova = %llx)\n", (unsigned long long)region->iova); rb_erase(node, &afu->dma_regions); if (region->iova) - dma_unmap_page(dfl_fpga_pdata_to_parent(pdata), + dma_unmap_page(dfl_fpga_fdata_to_parent(fdata), region->iova, region->length, DMA_BIDIRECTIONAL); if (region->pages) - afu_dma_unpin_pages(pdata, region); + afu_dma_unpin_pages(fdata, region); node = rb_next(node); kfree(region); @@ -229,7 +229,7 @@ void afu_dma_region_destroy(struct dfl_feature_platform_data *pdata) /** * afu_dma_region_find - find the dma region from rbtree based on iova and size - * @pdata: feature device platform data + * @fdata: feature dev data * @iova: address of the dma memory area * @size: size of the dma memory area * @@ -239,14 +239,14 @@ void afu_dma_region_destroy(struct dfl_feature_platform_data *pdata) * [@iova, @iova+size) * If nothing is matched returns NULL. * - * Needs to be called with pdata->lock held. + * Needs to be called with fdata->lock held. */ struct dfl_afu_dma_region * -afu_dma_region_find(struct dfl_feature_platform_data *pdata, u64 iova, u64 size) +afu_dma_region_find(struct dfl_feature_dev_data *fdata, u64 iova, u64 size) { - struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata); + struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata); struct rb_node *node = afu->dma_regions.rb_node; - struct device *dev = &pdata->dev->dev; + struct device *dev = &fdata->dev->dev; while (node) { struct dfl_afu_dma_region *region; @@ -276,20 +276,20 @@ afu_dma_region_find(struct dfl_feature_platform_data *pdata, u64 iova, u64 size) /** * afu_dma_region_find_iova - find the dma region from rbtree by iova - * @pdata: feature device platform data + * @fdata: feature dev data * @iova: address of the dma region * - * Needs to be called with pdata->lock held. + * Needs to be called with fdata->lock held. */ static struct dfl_afu_dma_region * -afu_dma_region_find_iova(struct dfl_feature_platform_data *pdata, u64 iova) +afu_dma_region_find_iova(struct dfl_feature_dev_data *fdata, u64 iova) { - return afu_dma_region_find(pdata, iova, 0); + return afu_dma_region_find(fdata, iova, 0); } /** * afu_dma_map_region - map memory region for dma - * @pdata: feature device platform data + * @fdata: feature dev data * @user_addr: address of the memory region * @length: size of the memory region * @iova: pointer of iova address @@ -298,9 +298,10 @@ afu_dma_region_find_iova(struct dfl_feature_platform_data *pdata, u64 iova) * of the memory region via @iova. * Return 0 for success, otherwise error code. */ -int afu_dma_map_region(struct dfl_feature_platform_data *pdata, +int afu_dma_map_region(struct dfl_feature_dev_data *fdata, u64 user_addr, u64 length, u64 *iova) { + struct device *dev = &fdata->dev->dev; struct dfl_afu_dma_region *region; int ret; @@ -323,47 +324,47 @@ int afu_dma_map_region(struct dfl_feature_platform_data *pdata, region->length = length; /* Pin the user memory region */ - ret = afu_dma_pin_pages(pdata, region); + ret = afu_dma_pin_pages(fdata, region); if (ret) { - dev_err(&pdata->dev->dev, "failed to pin memory region\n"); + dev_err(dev, "failed to pin memory region\n"); goto free_region; } /* Only accept continuous pages, return error else */ if (!afu_dma_check_continuous_pages(region)) { - dev_err(&pdata->dev->dev, "pages are not continuous\n"); + dev_err(dev, "pages are not continuous\n"); ret = -EINVAL; goto unpin_pages; } /* As pages are continuous then start to do DMA mapping */ - region->iova = dma_map_page(dfl_fpga_pdata_to_parent(pdata), + region->iova = dma_map_page(dfl_fpga_fdata_to_parent(fdata), region->pages[0], 0, region->length, DMA_BIDIRECTIONAL); - if (dma_mapping_error(dfl_fpga_pdata_to_parent(pdata), region->iova)) { - dev_err(&pdata->dev->dev, "failed to map for dma\n"); + if (dma_mapping_error(dfl_fpga_fdata_to_parent(fdata), region->iova)) { + dev_err(dev, "failed to map for dma\n"); ret = -EFAULT; goto unpin_pages; } *iova = region->iova; - mutex_lock(&pdata->lock); - ret = afu_dma_region_add(pdata, region); - mutex_unlock(&pdata->lock); + mutex_lock(&fdata->lock); + ret = afu_dma_region_add(fdata, region); + mutex_unlock(&fdata->lock); if (ret) { - dev_err(&pdata->dev->dev, "failed to add dma region\n"); + dev_err(dev, "failed to add dma region\n"); goto unmap_dma; } return 0; unmap_dma: - dma_unmap_page(dfl_fpga_pdata_to_parent(pdata), + dma_unmap_page(dfl_fpga_fdata_to_parent(fdata), region->iova, region->length, DMA_BIDIRECTIONAL); unpin_pages: - afu_dma_unpin_pages(pdata, region); + afu_dma_unpin_pages(fdata, region); free_region: kfree(region); return ret; @@ -371,34 +372,34 @@ free_region: /** * afu_dma_unmap_region - unmap dma memory region - * @pdata: feature device platform data + * @fdata: feature dev data * @iova: dma address of the region * * Unmap dma memory region based on @iova. * Return 0 for success, otherwise error code. */ -int afu_dma_unmap_region(struct dfl_feature_platform_data *pdata, u64 iova) +int afu_dma_unmap_region(struct dfl_feature_dev_data *fdata, u64 iova) { struct dfl_afu_dma_region *region; - mutex_lock(&pdata->lock); - region = afu_dma_region_find_iova(pdata, iova); + mutex_lock(&fdata->lock); + region = afu_dma_region_find_iova(fdata, iova); if (!region) { - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return -EINVAL; } if (region->in_use) { - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return -EBUSY; } - afu_dma_region_remove(pdata, region); - mutex_unlock(&pdata->lock); + afu_dma_region_remove(fdata, region); + mutex_unlock(&fdata->lock); - dma_unmap_page(dfl_fpga_pdata_to_parent(pdata), + dma_unmap_page(dfl_fpga_fdata_to_parent(fdata), region->iova, region->length, DMA_BIDIRECTIONAL); - afu_dma_unpin_pages(pdata, region); + afu_dma_unpin_pages(fdata, region); kfree(region); return 0; diff --git a/drivers/fpga/dfl-afu-error.c b/drivers/fpga/dfl-afu-error.c index ab7be6217368..0f392d1f6d45 100644 --- a/drivers/fpga/dfl-afu-error.c +++ b/drivers/fpga/dfl-afu-error.c @@ -28,37 +28,36 @@ #define ERROR_MASK GENMASK_ULL(63, 0) /* mask or unmask port errors by the error mask register. */ -static void __afu_port_err_mask(struct device *dev, bool mask) +static void __afu_port_err_mask(struct dfl_feature_dev_data *fdata, bool mask) { void __iomem *base; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_ERROR); writeq(mask ? ERROR_MASK : 0, base + PORT_ERROR_MASK); } static void afu_port_err_mask(struct device *dev, bool mask) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); - mutex_lock(&pdata->lock); - __afu_port_err_mask(dev, mask); - mutex_unlock(&pdata->lock); + mutex_lock(&fdata->lock); + __afu_port_err_mask(fdata, mask); + mutex_unlock(&fdata->lock); } /* clear port errors. */ static int afu_port_err_clear(struct device *dev, u64 err) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); - struct platform_device *pdev = to_platform_device(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base_err, *base_hdr; int enable_ret = 0, ret = -EBUSY; u64 v; - base_err = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR); - base_hdr = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + base_err = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_ERROR); + base_hdr = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); /* * clear Port Errors @@ -80,12 +79,12 @@ static int afu_port_err_clear(struct device *dev, u64 err) } /* Halt Port by keeping Port in reset */ - ret = __afu_port_disable(pdev); + ret = __afu_port_disable(fdata); if (ret) goto done; /* Mask all errors */ - __afu_port_err_mask(dev, true); + __afu_port_err_mask(fdata, true); /* Clear errors if err input matches with current port errors.*/ v = readq(base_err + PORT_ERROR); @@ -102,28 +101,28 @@ static int afu_port_err_clear(struct device *dev, u64 err) } /* Clear mask */ - __afu_port_err_mask(dev, false); + __afu_port_err_mask(fdata, false); /* Enable the Port by clearing the reset */ - enable_ret = __afu_port_enable(pdev); + enable_ret = __afu_port_enable(fdata); done: - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return enable_ret ? enable_ret : ret; } static ssize_t errors_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 error; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_ERROR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); error = readq(base + PORT_ERROR); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "0x%llx\n", (unsigned long long)error); } @@ -146,15 +145,15 @@ static DEVICE_ATTR_RW(errors); static ssize_t first_error_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 error; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_ERROR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); error = readq(base + PORT_FIRST_ERROR); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "0x%llx\n", (unsigned long long)error); } @@ -164,16 +163,16 @@ static ssize_t first_malformed_req_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 req0, req1; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_ERROR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); req0 = readq(base + PORT_MALFORMED_REQ0); req1 = readq(base + PORT_MALFORMED_REQ1); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "0x%016llx%016llx\n", (unsigned long long)req1, (unsigned long long)req0); @@ -191,12 +190,14 @@ static umode_t port_err_attrs_visible(struct kobject *kobj, struct attribute *attr, int n) { struct device *dev = kobj_to_dev(kobj); + struct dfl_feature_dev_data *fdata; + fdata = to_dfl_feature_dev_data(dev); /* * sysfs entries are visible only if related private feature is * enumerated. */ - if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_ERROR)) + if (!dfl_get_feature_by_id(fdata, PORT_FEATURE_ID_ERROR)) return 0; return attr->mode; diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c index 2fd4f07ed081..3bf8e7338dbe 100644 --- a/drivers/fpga/dfl-afu-main.c +++ b/drivers/fpga/dfl-afu-main.c @@ -26,7 +26,7 @@ /** * __afu_port_enable - enable a port by clear reset - * @pdev: port platform device. + * @fdata: port feature dev data. * * Enable Port by clear the port soft reset bit, which is set by default. * The AFU is unable to respond to any MMIO access while in reset. @@ -35,18 +35,17 @@ * * The caller needs to hold lock for protection. */ -int __afu_port_enable(struct platform_device *pdev) +int __afu_port_enable(struct dfl_feature_dev_data *fdata) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); void __iomem *base; u64 v; - WARN_ON(!pdata->disable_count); + WARN_ON(!fdata->disable_count); - if (--pdata->disable_count != 0) + if (--fdata->disable_count != 0) return 0; - base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); /* Clear port soft reset */ v = readq(base + PORT_HDR_CTRL); @@ -60,7 +59,8 @@ int __afu_port_enable(struct platform_device *pdev) if (readq_poll_timeout(base + PORT_HDR_CTRL, v, !(v & PORT_CTRL_SFTRST_ACK), RST_POLL_INVL, RST_POLL_TIMEOUT)) { - dev_err(&pdev->dev, "timeout, failure to enable device\n"); + dev_err(fdata->dfl_cdev->parent, + "timeout, failure to enable device\n"); return -ETIMEDOUT; } @@ -69,22 +69,21 @@ int __afu_port_enable(struct platform_device *pdev) /** * __afu_port_disable - disable a port by hold reset - * @pdev: port platform device. + * @fdata: port feature dev data. * * Disable Port by setting the port soft reset bit, it puts the port into reset. * * The caller needs to hold lock for protection. */ -int __afu_port_disable(struct platform_device *pdev) +int __afu_port_disable(struct dfl_feature_dev_data *fdata) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); void __iomem *base; u64 v; - if (pdata->disable_count++ != 0) + if (fdata->disable_count++ != 0) return 0; - base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); /* Set port soft reset */ v = readq(base + PORT_HDR_CTRL); @@ -99,7 +98,8 @@ int __afu_port_disable(struct platform_device *pdev) if (readq_poll_timeout(base + PORT_HDR_CTRL, v, v & PORT_CTRL_SFTRST_ACK, RST_POLL_INVL, RST_POLL_TIMEOUT)) { - dev_err(&pdev->dev, "timeout, failure to disable device\n"); + dev_err(fdata->dfl_cdev->parent, + "timeout, failure to disable device\n"); return -ETIMEDOUT; } @@ -118,34 +118,34 @@ int __afu_port_disable(struct platform_device *pdev) * (disabled). Any attempts on MMIO access to AFU while in reset, will * result errors reported via port error reporting sub feature (if present). */ -static int __port_reset(struct platform_device *pdev) +static int __port_reset(struct dfl_feature_dev_data *fdata) { int ret; - ret = __afu_port_disable(pdev); + ret = __afu_port_disable(fdata); if (ret) return ret; - return __afu_port_enable(pdev); + return __afu_port_enable(fdata); } static int port_reset(struct platform_device *pdev) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); int ret; - mutex_lock(&pdata->lock); - ret = __port_reset(pdev); - mutex_unlock(&pdata->lock); + mutex_lock(&fdata->lock); + ret = __port_reset(fdata); + mutex_unlock(&fdata->lock); return ret; } -static int port_get_id(struct platform_device *pdev) +static int port_get_id(struct dfl_feature_dev_data *fdata) { void __iomem *base; - base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + PORT_HDR_CAP)); } @@ -153,7 +153,8 @@ static int port_get_id(struct platform_device *pdev) static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf) { - int id = port_get_id(to_platform_device(dev)); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); + int id = port_get_id(fdata); return scnprintf(buf, PAGE_SIZE, "%d\n", id); } @@ -162,15 +163,15 @@ static DEVICE_ATTR_RO(id); static ssize_t ltr_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 v; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); v = readq(base + PORT_HDR_CTRL); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_CTRL_LATENCY, v)); } @@ -179,7 +180,7 @@ static ssize_t ltr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; bool ltr; u64 v; @@ -187,14 +188,14 @@ ltr_store(struct device *dev, struct device_attribute *attr, if (kstrtobool(buf, <r)) return -EINVAL; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); v = readq(base + PORT_HDR_CTRL); v &= ~PORT_CTRL_LATENCY; v |= FIELD_PREP(PORT_CTRL_LATENCY, ltr ? 1 : 0); writeq(v, base + PORT_HDR_CTRL); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return count; } @@ -203,15 +204,15 @@ static DEVICE_ATTR_RW(ltr); static ssize_t ap1_event_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 v; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); v = readq(base + PORT_HDR_STS); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP1_EVT, v)); } @@ -220,18 +221,18 @@ static ssize_t ap1_event_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; bool clear; if (kstrtobool(buf, &clear) || !clear) return -EINVAL; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); writeq(PORT_STS_AP1_EVT, base + PORT_HDR_STS); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return count; } @@ -241,15 +242,15 @@ static ssize_t ap2_event_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 v; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); v = readq(base + PORT_HDR_STS); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP2_EVT, v)); } @@ -258,18 +259,18 @@ static ssize_t ap2_event_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; bool clear; if (kstrtobool(buf, &clear) || !clear) return -EINVAL; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); writeq(PORT_STS_AP2_EVT, base + PORT_HDR_STS); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return count; } @@ -278,15 +279,15 @@ static DEVICE_ATTR_RW(ap2_event); static ssize_t power_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 v; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); v = readq(base + PORT_HDR_STS); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "0x%x\n", (u8)FIELD_GET(PORT_STS_PWR_STATE, v)); } @@ -296,18 +297,18 @@ static ssize_t userclk_freqcmd_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); u64 userclk_freq_cmd; void __iomem *base; if (kstrtou64(buf, 0, &userclk_freq_cmd)) return -EINVAL; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); writeq(userclk_freq_cmd, base + PORT_HDR_USRCLK_CMD0); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return count; } @@ -317,18 +318,18 @@ static ssize_t userclk_freqcntrcmd_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); u64 userclk_freqcntr_cmd; void __iomem *base; if (kstrtou64(buf, 0, &userclk_freqcntr_cmd)) return -EINVAL; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); writeq(userclk_freqcntr_cmd, base + PORT_HDR_USRCLK_CMD1); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return count; } @@ -338,15 +339,15 @@ static ssize_t userclk_freqsts_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); u64 userclk_freqsts; void __iomem *base; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); userclk_freqsts = readq(base + PORT_HDR_USRCLK_STS0); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "0x%llx\n", (unsigned long long)userclk_freqsts); } @@ -356,15 +357,15 @@ static ssize_t userclk_freqcntrsts_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); u64 userclk_freqcntrsts; void __iomem *base; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); userclk_freqcntrsts = readq(base + PORT_HDR_USRCLK_STS1); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "0x%llx\n", (unsigned long long)userclk_freqcntrsts); @@ -388,10 +389,12 @@ static umode_t port_hdr_attrs_visible(struct kobject *kobj, struct attribute *attr, int n) { struct device *dev = kobj_to_dev(kobj); + struct dfl_feature_dev_data *fdata; umode_t mode = attr->mode; void __iomem *base; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + fdata = to_dfl_feature_dev_data(dev); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER); if (dfl_feature_revision(base) > 0) { /* @@ -456,21 +459,21 @@ static const struct dfl_feature_ops port_hdr_ops = { static ssize_t afu_id_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 guidl, guidh; - base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_AFU); + base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_AFU); - mutex_lock(&pdata->lock); - if (pdata->disable_count) { - mutex_unlock(&pdata->lock); + mutex_lock(&fdata->lock); + if (fdata->disable_count) { + mutex_unlock(&fdata->lock); return -EBUSY; } guidl = readq(base + GUID_L); guidh = readq(base + GUID_H); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return scnprintf(buf, PAGE_SIZE, "%016llx%016llx\n", guidh, guidl); } @@ -485,12 +488,14 @@ static umode_t port_afu_attrs_visible(struct kobject *kobj, struct attribute *attr, int n) { struct device *dev = kobj_to_dev(kobj); + struct dfl_feature_dev_data *fdata; + fdata = to_dfl_feature_dev_data(dev); /* * sysfs entries are visible only if related private feature is * enumerated. */ - if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_AFU)) + if (!dfl_get_feature_by_id(fdata, PORT_FEATURE_ID_AFU)) return 0; return attr->mode; @@ -504,9 +509,10 @@ static const struct attribute_group port_afu_group = { static int port_afu_init(struct platform_device *pdev, struct dfl_feature *feature) { + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); struct resource *res = &pdev->resource[feature->resource_index]; - return afu_mmio_region_add(dev_get_platdata(&pdev->dev), + return afu_mmio_region_add(fdata, DFL_PORT_REGION_INDEX_AFU, resource_size(res), res->start, DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ | @@ -525,9 +531,10 @@ static const struct dfl_feature_ops port_afu_ops = { static int port_stp_init(struct platform_device *pdev, struct dfl_feature *feature) { + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); struct resource *res = &pdev->resource[feature->resource_index]; - return afu_mmio_region_add(dev_get_platdata(&pdev->dev), + return afu_mmio_region_add(fdata, DFL_PORT_REGION_INDEX_STP, resource_size(res), res->start, DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ | @@ -595,22 +602,18 @@ static struct dfl_feature_driver port_feature_drvs[] = { static int afu_open(struct inode *inode, struct file *filp) { - struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode); - struct dfl_feature_platform_data *pdata; + struct dfl_feature_dev_data *fdata = dfl_fpga_inode_to_feature_dev_data(inode); + struct platform_device *fdev = fdata->dev; int ret; - pdata = dev_get_platdata(&fdev->dev); - if (WARN_ON(!pdata)) - return -ENODEV; - - mutex_lock(&pdata->lock); - ret = dfl_feature_dev_use_begin(pdata, filp->f_flags & O_EXCL); + mutex_lock(&fdata->lock); + ret = dfl_feature_dev_use_begin(fdata, filp->f_flags & O_EXCL); if (!ret) { dev_dbg(&fdev->dev, "Device File Opened %d Times\n", - dfl_feature_dev_use_count(pdata)); + dfl_feature_dev_use_count(fdata)); filp->private_data = fdev; } - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return ret; } @@ -618,29 +621,29 @@ static int afu_open(struct inode *inode, struct file *filp) static int afu_release(struct inode *inode, struct file *filp) { struct platform_device *pdev = filp->private_data; - struct dfl_feature_platform_data *pdata; + struct dfl_feature_dev_data *fdata; struct dfl_feature *feature; dev_dbg(&pdev->dev, "Device File Release\n"); - pdata = dev_get_platdata(&pdev->dev); + fdata = to_dfl_feature_dev_data(&pdev->dev); - mutex_lock(&pdata->lock); - dfl_feature_dev_use_end(pdata); + mutex_lock(&fdata->lock); + dfl_feature_dev_use_end(fdata); - if (!dfl_feature_dev_use_count(pdata)) { - dfl_fpga_dev_for_each_feature(pdata, feature) + if (!dfl_feature_dev_use_count(fdata)) { + dfl_fpga_dev_for_each_feature(fdata, feature) dfl_fpga_set_irq_triggers(feature, 0, feature->nr_irqs, NULL); - __port_reset(pdev); - afu_dma_region_destroy(pdata); + __port_reset(fdata); + afu_dma_region_destroy(fdata); } - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return 0; } -static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata, +static long afu_ioctl_check_extension(struct dfl_feature_dev_data *fdata, unsigned long arg) { /* No extension support for now */ @@ -648,7 +651,7 @@ static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata, } static long -afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg) +afu_ioctl_get_info(struct dfl_feature_dev_data *fdata, void __user *arg) { struct dfl_fpga_port_info info; struct dfl_afu *afu; @@ -662,12 +665,12 @@ afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg) if (info.argsz < minsz) return -EINVAL; - mutex_lock(&pdata->lock); - afu = dfl_fpga_pdata_get_private(pdata); + mutex_lock(&fdata->lock); + afu = dfl_fpga_fdata_get_private(fdata); info.flags = 0; info.num_regions = afu->num_regions; info.num_umsgs = afu->num_umsgs; - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; @@ -675,7 +678,7 @@ afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg) return 0; } -static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata, +static long afu_ioctl_get_region_info(struct dfl_feature_dev_data *fdata, void __user *arg) { struct dfl_fpga_port_region_info rinfo; @@ -691,7 +694,7 @@ static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata, if (rinfo.argsz < minsz || rinfo.padding) return -EINVAL; - ret = afu_mmio_region_get_by_index(pdata, rinfo.index, ®ion); + ret = afu_mmio_region_get_by_index(fdata, rinfo.index, ®ion); if (ret) return ret; @@ -706,7 +709,7 @@ static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata, } static long -afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg) +afu_ioctl_dma_map(struct dfl_feature_dev_data *fdata, void __user *arg) { struct dfl_fpga_port_dma_map map; unsigned long minsz; @@ -720,16 +723,16 @@ afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg) if (map.argsz < minsz || map.flags) return -EINVAL; - ret = afu_dma_map_region(pdata, map.user_addr, map.length, &map.iova); + ret = afu_dma_map_region(fdata, map.user_addr, map.length, &map.iova); if (ret) return ret; if (copy_to_user(arg, &map, sizeof(map))) { - afu_dma_unmap_region(pdata, map.iova); + afu_dma_unmap_region(fdata, map.iova); return -EFAULT; } - dev_dbg(&pdata->dev->dev, "dma map: ua=%llx, len=%llx, iova=%llx\n", + dev_dbg(&fdata->dev->dev, "dma map: ua=%llx, len=%llx, iova=%llx\n", (unsigned long long)map.user_addr, (unsigned long long)map.length, (unsigned long long)map.iova); @@ -738,7 +741,7 @@ afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg) } static long -afu_ioctl_dma_unmap(struct dfl_feature_platform_data *pdata, void __user *arg) +afu_ioctl_dma_unmap(struct dfl_feature_dev_data *fdata, void __user *arg) { struct dfl_fpga_port_dma_unmap unmap; unsigned long minsz; @@ -751,33 +754,33 @@ afu_ioctl_dma_unmap(struct dfl_feature_platform_data *pdata, void __user *arg) if (unmap.argsz < minsz || unmap.flags) return -EINVAL; - return afu_dma_unmap_region(pdata, unmap.iova); + return afu_dma_unmap_region(fdata, unmap.iova); } static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct platform_device *pdev = filp->private_data; - struct dfl_feature_platform_data *pdata; + struct dfl_feature_dev_data *fdata; struct dfl_feature *f; long ret; dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd); - pdata = dev_get_platdata(&pdev->dev); + fdata = to_dfl_feature_dev_data(&pdev->dev); switch (cmd) { case DFL_FPGA_GET_API_VERSION: return DFL_FPGA_API_VERSION; case DFL_FPGA_CHECK_EXTENSION: - return afu_ioctl_check_extension(pdata, arg); + return afu_ioctl_check_extension(fdata, arg); case DFL_FPGA_PORT_GET_INFO: - return afu_ioctl_get_info(pdata, (void __user *)arg); + return afu_ioctl_get_info(fdata, (void __user *)arg); case DFL_FPGA_PORT_GET_REGION_INFO: - return afu_ioctl_get_region_info(pdata, (void __user *)arg); + return afu_ioctl_get_region_info(fdata, (void __user *)arg); case DFL_FPGA_PORT_DMA_MAP: - return afu_ioctl_dma_map(pdata, (void __user *)arg); + return afu_ioctl_dma_map(fdata, (void __user *)arg); case DFL_FPGA_PORT_DMA_UNMAP: - return afu_ioctl_dma_unmap(pdata, (void __user *)arg); + return afu_ioctl_dma_unmap(fdata, (void __user *)arg); default: /* * Let sub-feature's ioctl function to handle the cmd @@ -785,7 +788,7 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) * handled in this sub feature, and returns 0 and other * error code if cmd is handled. */ - dfl_fpga_dev_for_each_feature(pdata, f) + dfl_fpga_dev_for_each_feature(fdata, f) if (f->ops && f->ops->ioctl) { ret = f->ops->ioctl(pdev, f, cmd, arg); if (ret != -ENODEV) @@ -805,8 +808,8 @@ static const struct vm_operations_struct afu_vma_ops = { static int afu_mmap(struct file *filp, struct vm_area_struct *vma) { struct platform_device *pdev = filp->private_data; - struct dfl_feature_platform_data *pdata; u64 size = vma->vm_end - vma->vm_start; + struct dfl_feature_dev_data *fdata; struct dfl_afu_mmio_region region; u64 offset; int ret; @@ -814,10 +817,10 @@ static int afu_mmap(struct file *filp, struct vm_area_struct *vma) if (!(vma->vm_flags & VM_SHARED)) return -EINVAL; - pdata = dev_get_platdata(&pdev->dev); + fdata = to_dfl_feature_dev_data(&pdev->dev); offset = vma->vm_pgoff << PAGE_SHIFT; - ret = afu_mmio_region_get_by_offset(pdata, offset, size, ®ion); + ret = afu_mmio_region_get_by_offset(fdata, offset, size, ®ion); if (ret) return ret; @@ -851,46 +854,45 @@ static const struct file_operations afu_fops = { static int afu_dev_init(struct platform_device *pdev) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); struct dfl_afu *afu; afu = devm_kzalloc(&pdev->dev, sizeof(*afu), GFP_KERNEL); if (!afu) return -ENOMEM; - mutex_lock(&pdata->lock); - dfl_fpga_pdata_set_private(pdata, afu); - afu_mmio_region_init(pdata); - afu_dma_region_init(pdata); - mutex_unlock(&pdata->lock); + mutex_lock(&fdata->lock); + dfl_fpga_fdata_set_private(fdata, afu); + afu_mmio_region_init(fdata); + afu_dma_region_init(fdata); + mutex_unlock(&fdata->lock); return 0; } static int afu_dev_destroy(struct platform_device *pdev) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); - mutex_lock(&pdata->lock); - afu_mmio_region_destroy(pdata); - afu_dma_region_destroy(pdata); - dfl_fpga_pdata_set_private(pdata, NULL); - mutex_unlock(&pdata->lock); + mutex_lock(&fdata->lock); + afu_mmio_region_destroy(fdata); + afu_dma_region_destroy(fdata); + dfl_fpga_fdata_set_private(fdata, NULL); + mutex_unlock(&fdata->lock); return 0; } -static int port_enable_set(struct platform_device *pdev, bool enable) +static int port_enable_set(struct dfl_feature_dev_data *fdata, bool enable) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret; - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); if (enable) - ret = __afu_port_enable(pdev); + ret = __afu_port_enable(fdata); else - ret = __afu_port_disable(pdev); - mutex_unlock(&pdata->lock); + ret = __afu_port_disable(fdata); + mutex_unlock(&fdata->lock); return ret; } diff --git a/drivers/fpga/dfl-afu-region.c b/drivers/fpga/dfl-afu-region.c index 2e7b41629406..b11a5b21e666 100644 --- a/drivers/fpga/dfl-afu-region.c +++ b/drivers/fpga/dfl-afu-region.c @@ -12,11 +12,11 @@ /** * afu_mmio_region_init - init function for afu mmio region support - * @pdata: afu platform device's pdata. + * @fdata: afu feature dev data */ -void afu_mmio_region_init(struct dfl_feature_platform_data *pdata) +void afu_mmio_region_init(struct dfl_feature_dev_data *fdata) { - struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata); + struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata); INIT_LIST_HEAD(&afu->regions); } @@ -39,7 +39,7 @@ static struct dfl_afu_mmio_region *get_region_by_index(struct dfl_afu *afu, /** * afu_mmio_region_add - add a mmio region to given feature dev. * - * @pdata: afu platform device's pdata. + * @fdata: afu feature dev data * @region_index: region index. * @region_size: region size. * @phys: region's physical address of this region. @@ -47,14 +47,15 @@ static struct dfl_afu_mmio_region *get_region_by_index(struct dfl_afu *afu, * * Return: 0 on success, negative error code otherwise. */ -int afu_mmio_region_add(struct dfl_feature_platform_data *pdata, +int afu_mmio_region_add(struct dfl_feature_dev_data *fdata, u32 region_index, u64 region_size, u64 phys, u32 flags) { + struct device *dev = &fdata->dev->dev; struct dfl_afu_mmio_region *region; struct dfl_afu *afu; int ret = 0; - region = devm_kzalloc(&pdata->dev->dev, sizeof(*region), GFP_KERNEL); + region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL); if (!region) return -ENOMEM; @@ -63,13 +64,13 @@ int afu_mmio_region_add(struct dfl_feature_platform_data *pdata, region->phys = phys; region->flags = flags; - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); - afu = dfl_fpga_pdata_get_private(pdata); + afu = dfl_fpga_fdata_get_private(fdata); /* check if @index already exists */ if (get_region_by_index(afu, region_index)) { - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); ret = -EEXIST; goto exit; } @@ -80,37 +81,37 @@ int afu_mmio_region_add(struct dfl_feature_platform_data *pdata, afu->region_cur_offset += region_size; afu->num_regions++; - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return 0; exit: - devm_kfree(&pdata->dev->dev, region); + devm_kfree(dev, region); return ret; } /** * afu_mmio_region_destroy - destroy all mmio regions under given feature dev. - * @pdata: afu platform device's pdata. + * @fdata: afu feature dev data */ -void afu_mmio_region_destroy(struct dfl_feature_platform_data *pdata) +void afu_mmio_region_destroy(struct dfl_feature_dev_data *fdata) { - struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata); + struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata); struct dfl_afu_mmio_region *tmp, *region; list_for_each_entry_safe(region, tmp, &afu->regions, node) - devm_kfree(&pdata->dev->dev, region); + devm_kfree(&fdata->dev->dev, region); } /** * afu_mmio_region_get_by_index - find an afu region by index. - * @pdata: afu platform device's pdata. + * @fdata: afu feature dev data * @region_index: region index. * @pregion: ptr to region for result. * * Return: 0 on success, negative error code otherwise. */ -int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata, +int afu_mmio_region_get_by_index(struct dfl_feature_dev_data *fdata, u32 region_index, struct dfl_afu_mmio_region *pregion) { @@ -118,8 +119,8 @@ int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata, struct dfl_afu *afu; int ret = 0; - mutex_lock(&pdata->lock); - afu = dfl_fpga_pdata_get_private(pdata); + mutex_lock(&fdata->lock); + afu = dfl_fpga_fdata_get_private(fdata); region = get_region_by_index(afu, region_index); if (!region) { ret = -EINVAL; @@ -127,14 +128,14 @@ int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata, } *pregion = *region; exit: - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return ret; } /** * afu_mmio_region_get_by_offset - find an afu mmio region by offset and size * - * @pdata: afu platform device's pdata. + * @fdata: afu feature dev data * @offset: region offset from start of the device fd. * @size: region size. * @pregion: ptr to region for result. @@ -144,7 +145,7 @@ exit: * * Return: 0 on success, negative error code otherwise. */ -int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata, +int afu_mmio_region_get_by_offset(struct dfl_feature_dev_data *fdata, u64 offset, u64 size, struct dfl_afu_mmio_region *pregion) { @@ -152,8 +153,8 @@ int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata, struct dfl_afu *afu; int ret = 0; - mutex_lock(&pdata->lock); - afu = dfl_fpga_pdata_get_private(pdata); + mutex_lock(&fdata->lock); + afu = dfl_fpga_fdata_get_private(fdata); for_each_region(region, afu) if (region->offset <= offset && region->offset + region->size >= offset + size) { @@ -162,6 +163,6 @@ int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata, } ret = -EINVAL; exit: - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return ret; } diff --git a/drivers/fpga/dfl-afu.h b/drivers/fpga/dfl-afu.h index 7bef3e300aa2..03be4f0969c7 100644 --- a/drivers/fpga/dfl-afu.h +++ b/drivers/fpga/dfl-afu.h @@ -76,27 +76,27 @@ struct dfl_afu { struct rb_root dma_regions; }; -/* hold pdata->lock when call __afu_port_enable/disable */ -int __afu_port_enable(struct platform_device *pdev); -int __afu_port_disable(struct platform_device *pdev); +/* hold fdata->lock when call __afu_port_enable/disable */ +int __afu_port_enable(struct dfl_feature_dev_data *fdata); +int __afu_port_disable(struct dfl_feature_dev_data *fdata); -void afu_mmio_region_init(struct dfl_feature_platform_data *pdata); -int afu_mmio_region_add(struct dfl_feature_platform_data *pdata, +void afu_mmio_region_init(struct dfl_feature_dev_data *fdata); +int afu_mmio_region_add(struct dfl_feature_dev_data *fdata, u32 region_index, u64 region_size, u64 phys, u32 flags); -void afu_mmio_region_destroy(struct dfl_feature_platform_data *pdata); -int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata, +void afu_mmio_region_destroy(struct dfl_feature_dev_data *fdata); +int afu_mmio_region_get_by_index(struct dfl_feature_dev_data *fdata, u32 region_index, struct dfl_afu_mmio_region *pregion); -int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata, +int afu_mmio_region_get_by_offset(struct dfl_feature_dev_data *fdata, u64 offset, u64 size, struct dfl_afu_mmio_region *pregion); -void afu_dma_region_init(struct dfl_feature_platform_data *pdata); -void afu_dma_region_destroy(struct dfl_feature_platform_data *pdata); -int afu_dma_map_region(struct dfl_feature_platform_data *pdata, +void afu_dma_region_init(struct dfl_feature_dev_data *fdata); +void afu_dma_region_destroy(struct dfl_feature_dev_data *fdata); +int afu_dma_map_region(struct dfl_feature_dev_data *fdata, u64 user_addr, u64 length, u64 *iova); -int afu_dma_unmap_region(struct dfl_feature_platform_data *pdata, u64 iova); +int afu_dma_unmap_region(struct dfl_feature_dev_data *fdata, u64 iova); struct dfl_afu_dma_region * -afu_dma_region_find(struct dfl_feature_platform_data *pdata, +afu_dma_region_find(struct dfl_feature_dev_data *fdata, u64 iova, u64 size); extern const struct dfl_feature_ops port_err_ops; diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c index 950c606c59d4..28b0f9d062ac 100644 --- a/drivers/fpga/dfl-fme-br.c +++ b/drivers/fpga/dfl-fme-br.c @@ -22,34 +22,34 @@ struct fme_br_priv { struct dfl_fme_br_pdata *pdata; struct dfl_fpga_port_ops *port_ops; - struct platform_device *port_pdev; + struct dfl_feature_dev_data *port_fdata; }; static int fme_bridge_enable_set(struct fpga_bridge *bridge, bool enable) { struct fme_br_priv *priv = bridge->priv; - struct platform_device *port_pdev; + struct dfl_feature_dev_data *port_fdata; struct dfl_fpga_port_ops *ops; - if (!priv->port_pdev) { - port_pdev = dfl_fpga_cdev_find_port(priv->pdata->cdev, - &priv->pdata->port_id, - dfl_fpga_check_port_id); - if (!port_pdev) + if (!priv->port_fdata) { + port_fdata = dfl_fpga_cdev_find_port_data(priv->pdata->cdev, + &priv->pdata->port_id, + dfl_fpga_check_port_id); + if (!port_fdata) return -ENODEV; - priv->port_pdev = port_pdev; + priv->port_fdata = port_fdata; } - if (priv->port_pdev && !priv->port_ops) { - ops = dfl_fpga_port_ops_get(priv->port_pdev); + if (priv->port_fdata && !priv->port_ops) { + ops = dfl_fpga_port_ops_get(priv->port_fdata); if (!ops || !ops->enable_set) return -ENOENT; priv->port_ops = ops; } - return priv->port_ops->enable_set(priv->port_pdev, enable); + return priv->port_ops->enable_set(priv->port_fdata, enable); } static const struct fpga_bridge_ops fme_bridge_ops = { @@ -85,8 +85,6 @@ static void fme_br_remove(struct platform_device *pdev) fpga_bridge_unregister(br); - if (priv->port_pdev) - put_device(&priv->port_pdev->dev); if (priv->port_ops) dfl_fpga_port_ops_put(priv->port_ops); } diff --git a/drivers/fpga/dfl-fme-error.c b/drivers/fpga/dfl-fme-error.c index 51c2892ec06d..f00d949efe69 100644 --- a/drivers/fpga/dfl-fme-error.c +++ b/drivers/fpga/dfl-fme-error.c @@ -42,15 +42,15 @@ static ssize_t pcie0_errors_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 value; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); value = readq(base + PCIE0_ERROR); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "0x%llx\n", (unsigned long long)value); } @@ -59,7 +59,7 @@ static ssize_t pcie0_errors_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; int ret = 0; u64 v, val; @@ -67,9 +67,9 @@ static ssize_t pcie0_errors_store(struct device *dev, if (kstrtou64(buf, 0, &val)) return -EINVAL; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); writeq(GENMASK_ULL(63, 0), base + PCIE0_ERROR_MASK); v = readq(base + PCIE0_ERROR); @@ -79,7 +79,7 @@ static ssize_t pcie0_errors_store(struct device *dev, ret = -EINVAL; writeq(0ULL, base + PCIE0_ERROR_MASK); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return ret ? ret : count; } static DEVICE_ATTR_RW(pcie0_errors); @@ -87,15 +87,15 @@ static DEVICE_ATTR_RW(pcie0_errors); static ssize_t pcie1_errors_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 value; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); value = readq(base + PCIE1_ERROR); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "0x%llx\n", (unsigned long long)value); } @@ -104,7 +104,7 @@ static ssize_t pcie1_errors_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; int ret = 0; u64 v, val; @@ -112,9 +112,9 @@ static ssize_t pcie1_errors_store(struct device *dev, if (kstrtou64(buf, 0, &val)) return -EINVAL; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); writeq(GENMASK_ULL(63, 0), base + PCIE1_ERROR_MASK); v = readq(base + PCIE1_ERROR); @@ -124,7 +124,7 @@ static ssize_t pcie1_errors_store(struct device *dev, ret = -EINVAL; writeq(0ULL, base + PCIE1_ERROR_MASK); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return ret ? ret : count; } static DEVICE_ATTR_RW(pcie1_errors); @@ -132,9 +132,10 @@ static DEVICE_ATTR_RW(pcie1_errors); static ssize_t nonfatal_errors_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); return sprintf(buf, "0x%llx\n", (unsigned long long)readq(base + RAS_NONFAT_ERROR)); @@ -144,9 +145,10 @@ static DEVICE_ATTR_RO(nonfatal_errors); static ssize_t catfatal_errors_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); return sprintf(buf, "0x%llx\n", (unsigned long long)readq(base + RAS_CATFAT_ERROR)); @@ -156,15 +158,15 @@ static DEVICE_ATTR_RO(catfatal_errors); static ssize_t inject_errors_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 v; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); v = readq(base + RAS_ERROR_INJECT); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "0x%llx\n", (unsigned long long)FIELD_GET(INJECT_ERROR_MASK, v)); @@ -174,7 +176,7 @@ static ssize_t inject_errors_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u8 inject_error; u64 v; @@ -185,14 +187,14 @@ static ssize_t inject_errors_store(struct device *dev, if (inject_error & ~INJECT_ERROR_MASK) return -EINVAL; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); v = readq(base + RAS_ERROR_INJECT); v &= ~INJECT_ERROR_MASK; v |= FIELD_PREP(INJECT_ERROR_MASK, inject_error); writeq(v, base + RAS_ERROR_INJECT); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return count; } @@ -201,15 +203,15 @@ static DEVICE_ATTR_RW(inject_errors); static ssize_t fme_errors_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 value; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); value = readq(base + FME_ERROR); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "0x%llx\n", (unsigned long long)value); } @@ -218,7 +220,7 @@ static ssize_t fme_errors_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 v, val; int ret = 0; @@ -226,9 +228,9 @@ static ssize_t fme_errors_store(struct device *dev, if (kstrtou64(buf, 0, &val)) return -EINVAL; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); writeq(GENMASK_ULL(63, 0), base + FME_ERROR_MASK); v = readq(base + FME_ERROR); @@ -240,7 +242,7 @@ static ssize_t fme_errors_store(struct device *dev, /* Workaround: disable MBP_ERROR if feature revision is 0 */ writeq(dfl_feature_revision(base) ? 0ULL : MBP_ERROR, base + FME_ERROR_MASK); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return ret ? ret : count; } static DEVICE_ATTR_RW(fme_errors); @@ -248,15 +250,15 @@ static DEVICE_ATTR_RW(fme_errors); static ssize_t first_error_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 value; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); value = readq(base + FME_FIRST_ERROR); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "0x%llx\n", (unsigned long long)value); } @@ -265,15 +267,15 @@ static DEVICE_ATTR_RO(first_error); static ssize_t next_error_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 value; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); value = readq(base + FME_NEXT_ERROR); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return sprintf(buf, "0x%llx\n", (unsigned long long)value); } @@ -295,12 +297,14 @@ static umode_t fme_global_err_attrs_visible(struct kobject *kobj, struct attribute *attr, int n) { struct device *dev = kobj_to_dev(kobj); + struct dfl_feature_dev_data *fdata; + fdata = to_dfl_feature_dev_data(dev); /* * sysfs entries are visible only if related private feature is * enumerated. */ - if (!dfl_get_feature_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR)) + if (!dfl_get_feature_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR)) return 0; return attr->mode; @@ -314,12 +318,12 @@ const struct attribute_group fme_global_err_group = { static void fme_err_mask(struct device *dev, bool mask) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); /* Workaround: keep MBP_ERROR always masked if revision is 0 */ if (dfl_feature_revision(base)) @@ -332,7 +336,7 @@ static void fme_err_mask(struct device *dev, bool mask) writeq(mask ? ERROR_MASK : 0, base + RAS_NONFAT_ERROR_MASK); writeq(mask ? ERROR_MASK : 0, base + RAS_CATFAT_ERROR_MASK); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); } static int fme_global_err_init(struct platform_device *pdev, diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c index f8d89a4a6ccb..8aca2fb20e87 100644 --- a/drivers/fpga/dfl-fme-main.c +++ b/drivers/fpga/dfl-fme-main.c @@ -28,10 +28,11 @@ static ssize_t ports_num_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 v; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER); v = readq(base + FME_HDR_CAP); @@ -47,10 +48,11 @@ static DEVICE_ATTR_RO(ports_num); static ssize_t bitstream_id_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 v; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER); v = readq(base + FME_HDR_BITSTREAM_ID); @@ -65,10 +67,11 @@ static DEVICE_ATTR_RO(bitstream_id); static ssize_t bitstream_metadata_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 v; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER); v = readq(base + FME_HDR_BITSTREAM_MD); @@ -79,10 +82,11 @@ static DEVICE_ATTR_RO(bitstream_metadata); static ssize_t cache_size_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 v; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER); v = readq(base + FME_HDR_CAP); @@ -94,10 +98,11 @@ static DEVICE_ATTR_RO(cache_size); static ssize_t fabric_version_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 v; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER); v = readq(base + FME_HDR_CAP); @@ -109,10 +114,11 @@ static DEVICE_ATTR_RO(fabric_version); static ssize_t socket_id_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); void __iomem *base; u64 v; - base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER); v = readq(base + FME_HDR_CAP); @@ -135,10 +141,10 @@ static const struct attribute_group fme_hdr_group = { .attrs = fme_hdr_attrs, }; -static long fme_hdr_ioctl_release_port(struct dfl_feature_platform_data *pdata, +static long fme_hdr_ioctl_release_port(struct dfl_feature_dev_data *fdata, unsigned long arg) { - struct dfl_fpga_cdev *cdev = pdata->dfl_cdev; + struct dfl_fpga_cdev *cdev = fdata->dfl_cdev; int port_id; if (get_user(port_id, (int __user *)arg)) @@ -147,10 +153,10 @@ static long fme_hdr_ioctl_release_port(struct dfl_feature_platform_data *pdata, return dfl_fpga_cdev_release_port(cdev, port_id); } -static long fme_hdr_ioctl_assign_port(struct dfl_feature_platform_data *pdata, +static long fme_hdr_ioctl_assign_port(struct dfl_feature_dev_data *fdata, unsigned long arg) { - struct dfl_fpga_cdev *cdev = pdata->dfl_cdev; + struct dfl_fpga_cdev *cdev = fdata->dfl_cdev; int port_id; if (get_user(port_id, (int __user *)arg)) @@ -163,13 +169,13 @@ static long fme_hdr_ioctl(struct platform_device *pdev, struct dfl_feature *feature, unsigned int cmd, unsigned long arg) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); switch (cmd) { case DFL_FPGA_FME_PORT_RELEASE: - return fme_hdr_ioctl_release_port(pdata, arg); + return fme_hdr_ioctl_release_port(fdata, arg); case DFL_FPGA_FME_PORT_ASSIGN: - return fme_hdr_ioctl_assign_port(pdata, arg); + return fme_hdr_ioctl_assign_port(fdata, arg); } return -ENODEV; @@ -411,14 +417,14 @@ static int power_hwmon_read(struct device *dev, enum hwmon_sensor_types type, static int power_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev->parent); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev->parent); struct dfl_feature *feature = dev_get_drvdata(dev); int ret = 0; u64 v; val = clamp_val(val / MICRO, 0, PWR_THRESHOLD_MAX); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); switch (attr) { case hwmon_power_max: @@ -438,7 +444,7 @@ static int power_hwmon_write(struct device *dev, enum hwmon_sensor_types type, break; } - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return ret; } @@ -589,7 +595,7 @@ static struct dfl_feature_driver fme_feature_drvs[] = { }, }; -static long fme_ioctl_check_extension(struct dfl_feature_platform_data *pdata, +static long fme_ioctl_check_extension(struct dfl_feature_dev_data *fdata, unsigned long arg) { /* No extension support for now */ @@ -598,49 +604,46 @@ static long fme_ioctl_check_extension(struct dfl_feature_platform_data *pdata, static int fme_open(struct inode *inode, struct file *filp) { - struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode); - struct dfl_feature_platform_data *pdata = dev_get_platdata(&fdev->dev); + struct dfl_feature_dev_data *fdata = dfl_fpga_inode_to_feature_dev_data(inode); + struct platform_device *fdev = fdata->dev; int ret; - if (WARN_ON(!pdata)) - return -ENODEV; - - mutex_lock(&pdata->lock); - ret = dfl_feature_dev_use_begin(pdata, filp->f_flags & O_EXCL); + mutex_lock(&fdata->lock); + ret = dfl_feature_dev_use_begin(fdata, filp->f_flags & O_EXCL); if (!ret) { dev_dbg(&fdev->dev, "Device File Opened %d Times\n", - dfl_feature_dev_use_count(pdata)); - filp->private_data = pdata; + dfl_feature_dev_use_count(fdata)); + filp->private_data = fdata; } - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return ret; } static int fme_release(struct inode *inode, struct file *filp) { - struct dfl_feature_platform_data *pdata = filp->private_data; - struct platform_device *pdev = pdata->dev; + struct dfl_feature_dev_data *fdata = filp->private_data; + struct platform_device *pdev = fdata->dev; struct dfl_feature *feature; dev_dbg(&pdev->dev, "Device File Release\n"); - mutex_lock(&pdata->lock); - dfl_feature_dev_use_end(pdata); + mutex_lock(&fdata->lock); + dfl_feature_dev_use_end(fdata); - if (!dfl_feature_dev_use_count(pdata)) - dfl_fpga_dev_for_each_feature(pdata, feature) + if (!dfl_feature_dev_use_count(fdata)) + dfl_fpga_dev_for_each_feature(fdata, feature) dfl_fpga_set_irq_triggers(feature, 0, feature->nr_irqs, NULL); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return 0; } static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct dfl_feature_platform_data *pdata = filp->private_data; - struct platform_device *pdev = pdata->dev; + struct dfl_feature_dev_data *fdata = filp->private_data; + struct platform_device *pdev = fdata->dev; struct dfl_feature *f; long ret; @@ -650,7 +653,7 @@ static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case DFL_FPGA_GET_API_VERSION: return DFL_FPGA_API_VERSION; case DFL_FPGA_CHECK_EXTENSION: - return fme_ioctl_check_extension(pdata, arg); + return fme_ioctl_check_extension(fdata, arg); default: /* * Let sub-feature's ioctl function to handle the cmd. @@ -658,7 +661,7 @@ static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) * handled in this sub feature, and returns 0 or other * error code if cmd is handled. */ - dfl_fpga_dev_for_each_feature(pdata, f) { + dfl_fpga_dev_for_each_feature(fdata, f) { if (f->ops && f->ops->ioctl) { ret = f->ops->ioctl(pdev, f, cmd, arg); if (ret != -ENODEV) @@ -672,27 +675,27 @@ static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) static int fme_dev_init(struct platform_device *pdev) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); struct dfl_fme *fme; fme = devm_kzalloc(&pdev->dev, sizeof(*fme), GFP_KERNEL); if (!fme) return -ENOMEM; - mutex_lock(&pdata->lock); - dfl_fpga_pdata_set_private(pdata, fme); - mutex_unlock(&pdata->lock); + mutex_lock(&fdata->lock); + dfl_fpga_fdata_set_private(fdata, fme); + mutex_unlock(&fdata->lock); return 0; } static void fme_dev_destroy(struct platform_device *pdev) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); - mutex_lock(&pdata->lock); - dfl_fpga_pdata_set_private(pdata, NULL); - mutex_unlock(&pdata->lock); + mutex_lock(&fdata->lock); + dfl_fpga_fdata_set_private(fdata, NULL); + mutex_unlock(&fdata->lock); } static const struct file_operations fme_fops = { diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c index cdcf6dea4cc9..b878b260af38 100644 --- a/drivers/fpga/dfl-fme-pr.c +++ b/drivers/fpga/dfl-fme-pr.c @@ -65,7 +65,7 @@ static struct fpga_region *dfl_fme_region_find(struct dfl_fme *fme, int port_id) static int fme_pr(struct platform_device *pdev, unsigned long arg) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); void __user *argp = (void __user *)arg; struct dfl_fpga_fme_port_pr port_pr; struct fpga_image_info *info; @@ -87,8 +87,7 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg) return -EINVAL; /* get fme header region */ - fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev, - FME_FEATURE_ID_HEADER); + fme_hdr = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER); /* check port id */ v = readq(fme_hdr + FME_HDR_CAP); @@ -123,8 +122,8 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg) info->flags |= FPGA_MGR_PARTIAL_RECONFIG; - mutex_lock(&pdata->lock); - fme = dfl_fpga_pdata_get_private(pdata); + mutex_lock(&fdata->lock); + fme = dfl_fpga_fdata_get_private(fdata); /* fme device has been unregistered. */ if (!fme) { ret = -EINVAL; @@ -156,7 +155,7 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg) put_device(®ion->dev); unlock_exit: - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); free_exit: vfree(buf); return ret; @@ -164,16 +163,16 @@ free_exit: /** * dfl_fme_create_mgr - create fpga mgr platform device as child device + * @fdata: fme feature dev data * @feature: sub feature info - * @pdata: fme platform_device's pdata * * Return: mgr platform device if successful, and error code otherwise. */ static struct platform_device * -dfl_fme_create_mgr(struct dfl_feature_platform_data *pdata, +dfl_fme_create_mgr(struct dfl_feature_dev_data *fdata, struct dfl_feature *feature) { - struct platform_device *mgr, *fme = pdata->dev; + struct platform_device *mgr, *fme = fdata->dev; struct dfl_fme_mgr_pdata mgr_pdata; int ret = -ENOMEM; @@ -209,11 +208,11 @@ create_mgr_err: /** * dfl_fme_destroy_mgr - destroy fpga mgr platform device - * @pdata: fme platform device's pdata + * @fdata: fme feature dev data */ -static void dfl_fme_destroy_mgr(struct dfl_feature_platform_data *pdata) +static void dfl_fme_destroy_mgr(struct dfl_feature_dev_data *fdata) { - struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata); + struct dfl_fme *priv = dfl_fpga_fdata_get_private(fdata); platform_device_unregister(priv->mgr); } @@ -221,15 +220,15 @@ static void dfl_fme_destroy_mgr(struct dfl_feature_platform_data *pdata) /** * dfl_fme_create_bridge - create fme fpga bridge platform device as child * - * @pdata: fme platform device's pdata + * @fdata: fme feature dev data * @port_id: port id for the bridge to be created. * * Return: bridge platform device if successful, and error code otherwise. */ static struct dfl_fme_bridge * -dfl_fme_create_bridge(struct dfl_feature_platform_data *pdata, int port_id) +dfl_fme_create_bridge(struct dfl_feature_dev_data *fdata, int port_id) { - struct device *dev = &pdata->dev->dev; + struct device *dev = &fdata->dev->dev; struct dfl_fme_br_pdata br_pdata; struct dfl_fme_bridge *fme_br; int ret = -ENOMEM; @@ -238,7 +237,7 @@ dfl_fme_create_bridge(struct dfl_feature_platform_data *pdata, int port_id) if (!fme_br) return ERR_PTR(ret); - br_pdata.cdev = pdata->dfl_cdev; + br_pdata.cdev = fdata->dfl_cdev; br_pdata.port_id = port_id; fme_br->br = platform_device_alloc(DFL_FPGA_FME_BRIDGE, @@ -274,11 +273,11 @@ static void dfl_fme_destroy_bridge(struct dfl_fme_bridge *fme_br) /** * dfl_fme_destroy_bridges - destroy all fpga bridge platform device - * @pdata: fme platform device's pdata + * @fdata: fme feature dev data */ -static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata) +static void dfl_fme_destroy_bridges(struct dfl_feature_dev_data *fdata) { - struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata); + struct dfl_fme *priv = dfl_fpga_fdata_get_private(fdata); struct dfl_fme_bridge *fbridge, *tmp; list_for_each_entry_safe(fbridge, tmp, &priv->bridge_list, node) { @@ -290,7 +289,7 @@ static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata) /** * dfl_fme_create_region - create fpga region platform device as child * - * @pdata: fme platform device's pdata + * @fdata: fme feature dev data * @mgr: mgr platform device needed for region * @br: br platform device needed for region * @port_id: port id @@ -298,12 +297,12 @@ static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata) * Return: fme region if successful, and error code otherwise. */ static struct dfl_fme_region * -dfl_fme_create_region(struct dfl_feature_platform_data *pdata, +dfl_fme_create_region(struct dfl_feature_dev_data *fdata, struct platform_device *mgr, struct platform_device *br, int port_id) { struct dfl_fme_region_pdata region_pdata; - struct device *dev = &pdata->dev->dev; + struct device *dev = &fdata->dev->dev; struct dfl_fme_region *fme_region; int ret = -ENOMEM; @@ -353,11 +352,11 @@ static void dfl_fme_destroy_region(struct dfl_fme_region *fme_region) /** * dfl_fme_destroy_regions - destroy all fme regions - * @pdata: fme platform device's pdata + * @fdata: fme feature dev data */ -static void dfl_fme_destroy_regions(struct dfl_feature_platform_data *pdata) +static void dfl_fme_destroy_regions(struct dfl_feature_dev_data *fdata) { - struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata); + struct dfl_fme *priv = dfl_fpga_fdata_get_private(fdata); struct dfl_fme_region *fme_region, *tmp; list_for_each_entry_safe(fme_region, tmp, &priv->region_list, node) { @@ -369,7 +368,7 @@ static void dfl_fme_destroy_regions(struct dfl_feature_platform_data *pdata) static int pr_mgmt_init(struct platform_device *pdev, struct dfl_feature *feature) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); struct dfl_fme_region *fme_region; struct dfl_fme_bridge *fme_br; struct platform_device *mgr; @@ -378,18 +377,17 @@ static int pr_mgmt_init(struct platform_device *pdev, int ret = -ENODEV, i = 0; u64 fme_cap, port_offset; - fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev, - FME_FEATURE_ID_HEADER); + fme_hdr = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER); - mutex_lock(&pdata->lock); - priv = dfl_fpga_pdata_get_private(pdata); + mutex_lock(&fdata->lock); + priv = dfl_fpga_fdata_get_private(fdata); /* Initialize the region and bridge sub device list */ INIT_LIST_HEAD(&priv->region_list); INIT_LIST_HEAD(&priv->bridge_list); /* Create fpga mgr platform device */ - mgr = dfl_fme_create_mgr(pdata, feature); + mgr = dfl_fme_create_mgr(fdata, feature); if (IS_ERR(mgr)) { dev_err(&pdev->dev, "fail to create fpga mgr pdev\n"); goto unlock; @@ -405,7 +403,7 @@ static int pr_mgmt_init(struct platform_device *pdev, continue; /* Create bridge for each port */ - fme_br = dfl_fme_create_bridge(pdata, i); + fme_br = dfl_fme_create_bridge(fdata, i); if (IS_ERR(fme_br)) { ret = PTR_ERR(fme_br); goto destroy_region; @@ -414,7 +412,7 @@ static int pr_mgmt_init(struct platform_device *pdev, list_add(&fme_br->node, &priv->bridge_list); /* Create region for each port */ - fme_region = dfl_fme_create_region(pdata, mgr, + fme_region = dfl_fme_create_region(fdata, mgr, fme_br->br, i); if (IS_ERR(fme_region)) { ret = PTR_ERR(fme_region); @@ -423,30 +421,30 @@ static int pr_mgmt_init(struct platform_device *pdev, list_add(&fme_region->node, &priv->region_list); } - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return 0; destroy_region: - dfl_fme_destroy_regions(pdata); - dfl_fme_destroy_bridges(pdata); - dfl_fme_destroy_mgr(pdata); + dfl_fme_destroy_regions(fdata); + dfl_fme_destroy_bridges(fdata); + dfl_fme_destroy_mgr(fdata); unlock: - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); return ret; } static void pr_mgmt_uinit(struct platform_device *pdev, struct dfl_feature *feature) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); - dfl_fme_destroy_regions(pdata); - dfl_fme_destroy_bridges(pdata); - dfl_fme_destroy_mgr(pdata); - mutex_unlock(&pdata->lock); + dfl_fme_destroy_regions(fdata); + dfl_fme_destroy_bridges(fdata); + dfl_fme_destroy_mgr(fdata); + mutex_unlock(&fdata->lock); } static long fme_pr_ioctl(struct platform_device *pdev, diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index c406b949026f..7022657243c0 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -119,17 +119,6 @@ static void dfl_id_free(enum dfl_id_type type, int id) mutex_unlock(&dfl_id_mutex); } -static enum dfl_id_type feature_dev_id_type(struct platform_device *pdev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dfl_devs); i++) - if (!strcmp(dfl_devs[i].name, pdev->name)) - return i; - - return DFL_ID_MAX; -} - static enum dfl_id_type dfh_id_to_type(u16 id) { int i; @@ -156,12 +145,12 @@ static LIST_HEAD(dfl_port_ops_list); /** * dfl_fpga_port_ops_get - get matched port ops from the global list - * @pdev: platform device to match with associated port ops. + * @fdata: feature dev data to match with associated port ops. * Return: matched port ops on success, NULL otherwise. * * Please note that must dfl_fpga_port_ops_put after use the port_ops. */ -struct dfl_fpga_port_ops *dfl_fpga_port_ops_get(struct platform_device *pdev) +struct dfl_fpga_port_ops *dfl_fpga_port_ops_get(struct dfl_feature_dev_data *fdata) { struct dfl_fpga_port_ops *ops = NULL; @@ -171,7 +160,7 @@ struct dfl_fpga_port_ops *dfl_fpga_port_ops_get(struct platform_device *pdev) list_for_each_entry(ops, &dfl_port_ops_list, node) { /* match port_ops using the name of platform device */ - if (!strcmp(pdev->name, ops->name)) { + if (!strcmp(fdata->pdev_name, ops->name)) { if (!try_module_get(ops->owner)) ops = NULL; goto done; @@ -222,27 +211,26 @@ EXPORT_SYMBOL_GPL(dfl_fpga_port_ops_del); /** * dfl_fpga_check_port_id - check the port id - * @pdev: port platform device. + * @fdata: port feature dev data. * @pport_id: port id to compare. * * Return: 1 if port device matches with given port id, otherwise 0. */ -int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id) +int dfl_fpga_check_port_id(struct dfl_feature_dev_data *fdata, void *pport_id) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); struct dfl_fpga_port_ops *port_ops; - if (pdata->id != FEATURE_DEV_ID_UNUSED) - return pdata->id == *(int *)pport_id; + if (fdata->id != FEATURE_DEV_ID_UNUSED) + return fdata->id == *(int *)pport_id; - port_ops = dfl_fpga_port_ops_get(pdev); + port_ops = dfl_fpga_port_ops_get(fdata); if (!port_ops || !port_ops->get_id) return 0; - pdata->id = port_ops->get_id(pdev); + fdata->id = port_ops->get_id(fdata); dfl_fpga_port_ops_put(port_ops); - return pdata->id == *(int *)pport_id; + return fdata->id == *(int *)pport_id; } EXPORT_SYMBOL_GPL(dfl_fpga_check_port_id); @@ -351,10 +339,10 @@ static void release_dfl_dev(struct device *dev) } static struct dfl_device * -dfl_dev_add(struct dfl_feature_platform_data *pdata, +dfl_dev_add(struct dfl_feature_dev_data *fdata, struct dfl_feature *feature) { - struct platform_device *pdev = pdata->dev; + struct platform_device *pdev = fdata->dev; struct resource *parent_res; struct dfl_device *ddev; int id, i, ret; @@ -380,11 +368,11 @@ dfl_dev_add(struct dfl_feature_platform_data *pdata, if (ret) goto put_dev; - ddev->type = feature_dev_id_type(pdev); + ddev->type = fdata->type; ddev->feature_id = feature->id; ddev->revision = feature->revision; ddev->dfh_version = feature->dfh_version; - ddev->cdev = pdata->dfl_cdev; + ddev->cdev = fdata->dfl_cdev; if (feature->param_size) { ddev->params = kmemdup(feature->params, feature->param_size, GFP_KERNEL); if (!ddev->params) { @@ -435,11 +423,11 @@ put_dev: return ERR_PTR(ret); } -static void dfl_devs_remove(struct dfl_feature_platform_data *pdata) +static void dfl_devs_remove(struct dfl_feature_dev_data *fdata) { struct dfl_feature *feature; - dfl_fpga_dev_for_each_feature(pdata, feature) { + dfl_fpga_dev_for_each_feature(fdata, feature) { if (feature->ddev) { device_unregister(&feature->ddev->dev); feature->ddev = NULL; @@ -447,13 +435,13 @@ static void dfl_devs_remove(struct dfl_feature_platform_data *pdata) } } -static int dfl_devs_add(struct dfl_feature_platform_data *pdata) +static int dfl_devs_add(struct dfl_feature_dev_data *fdata) { struct dfl_feature *feature; struct dfl_device *ddev; int ret; - dfl_fpga_dev_for_each_feature(pdata, feature) { + dfl_fpga_dev_for_each_feature(fdata, feature) { if (feature->ioaddr) continue; @@ -462,7 +450,7 @@ static int dfl_devs_add(struct dfl_feature_platform_data *pdata) goto err; } - ddev = dfl_dev_add(pdata, feature); + ddev = dfl_dev_add(fdata, feature); if (IS_ERR(ddev)) { ret = PTR_ERR(ddev); goto err; @@ -474,7 +462,7 @@ static int dfl_devs_add(struct dfl_feature_platform_data *pdata) return 0; err: - dfl_devs_remove(pdata); + dfl_devs_remove(fdata); return ret; } @@ -504,12 +492,12 @@ EXPORT_SYMBOL(dfl_driver_unregister); */ void dfl_fpga_dev_feature_uinit(struct platform_device *pdev) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); struct dfl_feature *feature; - dfl_devs_remove(pdata); + dfl_devs_remove(fdata); - dfl_fpga_dev_for_each_feature(pdata, feature) { + dfl_fpga_dev_for_each_feature(fdata, feature) { if (feature->ops) { if (feature->ops->uinit) feature->ops->uinit(pdev, feature); @@ -520,7 +508,6 @@ void dfl_fpga_dev_feature_uinit(struct platform_device *pdev) EXPORT_SYMBOL_GPL(dfl_fpga_dev_feature_uinit); static int dfl_feature_instance_init(struct platform_device *pdev, - struct dfl_feature_platform_data *pdata, struct dfl_feature *feature, struct dfl_feature_driver *drv) { @@ -579,16 +566,15 @@ static bool dfl_feature_drv_match(struct dfl_feature *feature, int dfl_fpga_dev_feature_init(struct platform_device *pdev, struct dfl_feature_driver *feature_drvs) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); struct dfl_feature_driver *drv = feature_drvs; struct dfl_feature *feature; int ret; while (drv->ops) { - dfl_fpga_dev_for_each_feature(pdata, feature) { + dfl_fpga_dev_for_each_feature(fdata, feature) { if (dfl_feature_drv_match(feature, drv)) { - ret = dfl_feature_instance_init(pdev, pdata, - feature, drv); + ret = dfl_feature_instance_init(pdev, feature, drv); if (ret) goto exit; } @@ -596,7 +582,7 @@ int dfl_fpga_dev_feature_init(struct platform_device *pdev, drv++; } - ret = dfl_devs_add(pdata); + ret = dfl_devs_add(fdata); if (ret) goto exit; @@ -695,7 +681,7 @@ EXPORT_SYMBOL_GPL(dfl_fpga_dev_ops_unregister); * @nr_irqs: number of irqs for all feature devices. * @irq_table: Linux IRQ numbers for all irqs, indexed by local irq index of * this device. - * @feature_dev: current feature device. + * @type: the current FIU type. * @ioaddr: header register region address of current FIU in enumeration. * @start: register resource start of current FIU. * @len: max register resource length of current FIU. @@ -708,7 +694,7 @@ struct build_feature_devs_info { unsigned int nr_irqs; int *irq_table; - struct platform_device *feature_dev; + enum dfl_id_type type; void __iomem *ioaddr; resource_size_t start; resource_size_t len; @@ -743,50 +729,62 @@ struct dfl_feature_info { u64 params[]; }; -static void dfl_fpga_cdev_add_port_dev(struct dfl_fpga_cdev *cdev, - struct platform_device *port) +static void dfl_fpga_cdev_add_port_data(struct dfl_fpga_cdev *cdev, + struct dfl_feature_dev_data *fdata) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&port->dev); - mutex_lock(&cdev->lock); - list_add(&pdata->node, &cdev->port_dev_list); - get_device(&pdata->dev->dev); + list_add(&fdata->node, &cdev->port_dev_list); mutex_unlock(&cdev->lock); } -/* - * register current feature device, it is called when we need to switch to - * another feature parsing or we have parsed all features on given device - * feature list. - */ -static int build_info_commit_dev(struct build_feature_devs_info *binfo) +static void dfl_id_free_action(void *arg) +{ + struct dfl_feature_dev_data *fdata = arg; + + dfl_id_free(fdata->type, fdata->pdev_id); +} + +static struct dfl_feature_dev_data * +binfo_create_feature_dev_data(struct build_feature_devs_info *binfo) { - struct platform_device *fdev = binfo->feature_dev; - struct dfl_feature_platform_data *pdata; + enum dfl_id_type type = binfo->type; struct dfl_feature_info *finfo, *p; - enum dfl_id_type type; + struct dfl_feature_dev_data *fdata; int ret, index = 0, res_idx = 0; - type = feature_dev_id_type(fdev); if (WARN_ON_ONCE(type >= DFL_ID_MAX)) - return -EINVAL; + return ERR_PTR(-EINVAL); - /* - * we do not need to care for the memory which is associated with - * the platform device. After calling platform_device_unregister(), - * it will be automatically freed by device's release() callback, - * platform_device_release(). - */ - pdata = kzalloc(struct_size(pdata, features, binfo->feature_num), GFP_KERNEL); - if (!pdata) - return -ENOMEM; + fdata = devm_kzalloc(binfo->dev, sizeof(*fdata), GFP_KERNEL); + if (!fdata) + return ERR_PTR(-ENOMEM); + + fdata->features = devm_kcalloc(binfo->dev, binfo->feature_num, + sizeof(*fdata->features), GFP_KERNEL); + if (!fdata->features) + return ERR_PTR(-ENOMEM); + + fdata->resources = devm_kcalloc(binfo->dev, binfo->feature_num, + sizeof(*fdata->resources), GFP_KERNEL); + if (!fdata->resources) + return ERR_PTR(-ENOMEM); + + fdata->type = type; - pdata->dev = fdev; - pdata->num = binfo->feature_num; - pdata->dfl_cdev = binfo->cdev; - pdata->id = FEATURE_DEV_ID_UNUSED; - mutex_init(&pdata->lock); - lockdep_set_class_and_name(&pdata->lock, &dfl_pdata_keys[type], + fdata->pdev_id = dfl_id_alloc(type, binfo->dev); + if (fdata->pdev_id < 0) + return ERR_PTR(fdata->pdev_id); + + ret = devm_add_action_or_reset(binfo->dev, dfl_id_free_action, fdata); + if (ret) + return ERR_PTR(ret); + + fdata->pdev_name = dfl_devs[type].name; + fdata->num = binfo->feature_num; + fdata->dfl_cdev = binfo->cdev; + fdata->id = FEATURE_DEV_ID_UNUSED; + mutex_init(&fdata->lock); + lockdep_set_class_and_name(&fdata->lock, &dfl_pdata_keys[type], dfl_pdata_key_strings[type]); /* @@ -795,25 +793,15 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) * works properly for port device. * and it should always be 0 for fme device. */ - WARN_ON(pdata->disable_count); - - fdev->dev.platform_data = pdata; - - /* each sub feature has one MMIO resource */ - fdev->num_resources = binfo->feature_num; - fdev->resource = kcalloc(binfo->feature_num, sizeof(*fdev->resource), - GFP_KERNEL); - if (!fdev->resource) - return -ENOMEM; + WARN_ON(fdata->disable_count); /* fill features and resource information for feature dev */ list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) { - struct dfl_feature *feature = &pdata->features[index++]; + struct dfl_feature *feature = &fdata->features[index++]; struct dfl_feature_irq_ctx *ctx; unsigned int i; /* save resource information for each feature */ - feature->dev = fdev; feature->id = finfo->fid; feature->revision = finfo->revision; feature->dfh_version = finfo->dfh_version; @@ -823,7 +811,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) finfo->params, finfo->param_size, GFP_KERNEL); if (!feature->params) - return -ENOMEM; + return ERR_PTR(-ENOMEM); feature->param_size = finfo->param_size; } @@ -840,17 +828,17 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) devm_ioremap_resource(binfo->dev, &finfo->mmio_res); if (IS_ERR(feature->ioaddr)) - return PTR_ERR(feature->ioaddr); + return ERR_CAST(feature->ioaddr); } else { feature->resource_index = res_idx; - fdev->resource[res_idx++] = finfo->mmio_res; + fdata->resources[res_idx++] = finfo->mmio_res; } if (finfo->nr_irqs) { ctx = devm_kcalloc(binfo->dev, finfo->nr_irqs, sizeof(*ctx), GFP_KERNEL); if (!ctx) - return -ENOMEM; + return ERR_PTR(-ENOMEM); for (i = 0; i < finfo->nr_irqs; i++) ctx[i].irq = @@ -864,55 +852,94 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) kfree(finfo); } - ret = platform_device_add(binfo->feature_dev); - if (!ret) { - if (type == PORT_ID) - dfl_fpga_cdev_add_port_dev(binfo->cdev, - binfo->feature_dev); - else - binfo->cdev->fme_dev = - get_device(&binfo->feature_dev->dev); - /* - * reset it to avoid build_info_free() freeing their resource. - * - * The resource of successfully registered feature devices - * will be freed by platform_device_unregister(). See the - * comments in build_info_create_dev(). - */ - binfo->feature_dev = NULL; - } + fdata->resource_num = res_idx; - return ret; + return fdata; } -static int -build_info_create_dev(struct build_feature_devs_info *binfo, - enum dfl_id_type type) +/* + * register current feature device, it is called when we need to switch to + * another feature parsing or we have parsed all features on given device + * feature list. + */ +static int feature_dev_register(struct dfl_feature_dev_data *fdata) { + struct dfl_feature_platform_data pdata = {}; struct platform_device *fdev; + struct dfl_feature *feature; + int ret; - if (type >= DFL_ID_MAX) - return -EINVAL; - - /* - * we use -ENODEV as the initialization indicator which indicates - * whether the id need to be reclaimed - */ - fdev = platform_device_alloc(dfl_devs[type].name, -ENODEV); + fdev = platform_device_alloc(fdata->pdev_name, fdata->pdev_id); if (!fdev) return -ENOMEM; - binfo->feature_dev = fdev; - binfo->feature_num = 0; + fdata->dev = fdev; - INIT_LIST_HEAD(&binfo->sub_features); + fdev->dev.parent = &fdata->dfl_cdev->region->dev; + fdev->dev.devt = dfl_get_devt(dfl_devs[fdata->type].devt_type, fdev->id); - fdev->id = dfl_id_alloc(type, &fdev->dev); - if (fdev->id < 0) - return fdev->id; + dfl_fpga_dev_for_each_feature(fdata, feature) + feature->dev = fdev; + + ret = platform_device_add_resources(fdev, fdata->resources, + fdata->resource_num); + if (ret) + goto err_put_dev; + + pdata.fdata = fdata; + ret = platform_device_add_data(fdev, &pdata, sizeof(pdata)); + if (ret) + goto err_put_dev; + + ret = platform_device_add(fdev); + if (ret) + goto err_put_dev; + + return 0; + +err_put_dev: + platform_device_put(fdev); + + fdata->dev = NULL; + + dfl_fpga_dev_for_each_feature(fdata, feature) + feature->dev = NULL; + + return ret; +} + +static void feature_dev_unregister(struct dfl_feature_dev_data *fdata) +{ + struct dfl_feature *feature; + + platform_device_unregister(fdata->dev); + + fdata->dev = NULL; + + dfl_fpga_dev_for_each_feature(fdata, feature) + feature->dev = NULL; +} + +static int build_info_commit_dev(struct build_feature_devs_info *binfo) +{ + struct dfl_feature_dev_data *fdata; + int ret; + + fdata = binfo_create_feature_dev_data(binfo); + if (IS_ERR(fdata)) + return PTR_ERR(fdata); + + ret = feature_dev_register(fdata); + if (ret) + return ret; + + if (binfo->type == PORT_ID) + dfl_fpga_cdev_add_port_data(binfo->cdev, fdata); + else + binfo->cdev->fme_dev = get_device(&fdata->dev->dev); - fdev->dev.parent = &binfo->cdev->region->dev; - fdev->dev.devt = dfl_get_devt(dfl_devs[type].devt_type, fdev->id); + /* reset the binfo for next FIU */ + binfo->type = DFL_ID_MAX; return 0; } @@ -921,22 +948,11 @@ static void build_info_free(struct build_feature_devs_info *binfo) { struct dfl_feature_info *finfo, *p; - /* - * it is a valid id, free it. See comments in - * build_info_create_dev() - */ - if (binfo->feature_dev && binfo->feature_dev->id >= 0) { - dfl_id_free(feature_dev_id_type(binfo->feature_dev), - binfo->feature_dev->id); - - list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) { - list_del(&finfo->node); - kfree(finfo); - } + list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) { + list_del(&finfo->node); + kfree(finfo); } - platform_device_put(binfo->feature_dev); - devm_kfree(binfo->dev, binfo); } @@ -1025,7 +1041,7 @@ static int parse_feature_irqs(struct build_feature_devs_info *binfo, * Instead, features with interrupt functionality provide * the information in feature specific registers. */ - type = feature_dev_id_type(binfo->feature_dev); + type = binfo->type; if (type == PORT_ID) { switch (fid) { case PORT_FEATURE_ID_UINT: @@ -1217,7 +1233,7 @@ static int parse_feature_port_afu(struct build_feature_devs_info *binfo, return create_feature_instance(binfo, ofst, size, FEATURE_ID_AFU); } -#define is_feature_dev_detected(binfo) (!!(binfo)->feature_dev) +#define is_feature_dev_detected(binfo) ((binfo)->type != DFL_ID_MAX) static int parse_feature_afu(struct build_feature_devs_info *binfo, resource_size_t ofst) @@ -1227,12 +1243,11 @@ static int parse_feature_afu(struct build_feature_devs_info *binfo, return -EINVAL; } - switch (feature_dev_id_type(binfo->feature_dev)) { + switch (binfo->type) { case PORT_ID: return parse_feature_port_afu(binfo, ofst); default: - dev_info(binfo->dev, "AFU belonging to FIU %s is not supported yet.\n", - binfo->feature_dev->name); + dev_info(binfo->dev, "AFU belonging to FIU is not supported yet.\n"); } return 0; @@ -1273,6 +1288,7 @@ static void build_info_complete(struct build_feature_devs_info *binfo) static int parse_feature_fiu(struct build_feature_devs_info *binfo, resource_size_t ofst) { + enum dfl_id_type type; int ret = 0; u32 offset; u16 id; @@ -1294,10 +1310,13 @@ static int parse_feature_fiu(struct build_feature_devs_info *binfo, v = readq(binfo->ioaddr + DFH); id = FIELD_GET(DFH_ID, v); - /* create platform device for dfl feature dev */ - ret = build_info_create_dev(binfo, dfh_id_to_type(id)); - if (ret) - return ret; + type = dfh_id_to_type(id); + if (type >= DFL_ID_MAX) + return -EINVAL; + + binfo->type = type; + binfo->feature_num = 0; + INIT_LIST_HEAD(&binfo->sub_features); ret = create_feature_instance(binfo, 0, 0, 0); if (ret) @@ -1515,13 +1534,9 @@ EXPORT_SYMBOL_GPL(dfl_fpga_enum_info_add_irq); static int remove_feature_dev(struct device *dev, void *data) { - struct platform_device *pdev = to_platform_device(dev); - enum dfl_id_type type = feature_dev_id_type(pdev); - int id = pdev->id; - - platform_device_unregister(pdev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev); - dfl_id_free(type, id); + feature_dev_unregister(fdata); return 0; } @@ -1573,6 +1588,7 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info) goto unregister_region_exit; } + binfo->type = DFL_ID_MAX; binfo->dev = info->dev; binfo->cdev = cdev; @@ -1614,25 +1630,10 @@ EXPORT_SYMBOL_GPL(dfl_fpga_feature_devs_enumerate); */ void dfl_fpga_feature_devs_remove(struct dfl_fpga_cdev *cdev) { - struct dfl_feature_platform_data *pdata, *ptmp; - mutex_lock(&cdev->lock); if (cdev->fme_dev) put_device(cdev->fme_dev); - list_for_each_entry_safe(pdata, ptmp, &cdev->port_dev_list, node) { - struct platform_device *port_dev = pdata->dev; - - /* remove released ports */ - if (!device_is_registered(&port_dev->dev)) { - dfl_id_free(feature_dev_id_type(port_dev), - port_dev->id); - platform_device_put(port_dev); - } - - list_del(&pdata->node); - put_device(&port_dev->dev); - } mutex_unlock(&cdev->lock); remove_feature_devs(cdev); @@ -1643,7 +1644,7 @@ void dfl_fpga_feature_devs_remove(struct dfl_fpga_cdev *cdev) EXPORT_SYMBOL_GPL(dfl_fpga_feature_devs_remove); /** - * __dfl_fpga_cdev_find_port - find a port under given container device + * __dfl_fpga_cdev_find_port_data - find a port under given container device * * @cdev: container device * @data: data passed to match function @@ -1656,23 +1657,20 @@ EXPORT_SYMBOL_GPL(dfl_fpga_feature_devs_remove); * * NOTE: you will need to drop the device reference with put_device() after use. */ -struct platform_device * -__dfl_fpga_cdev_find_port(struct dfl_fpga_cdev *cdev, void *data, - int (*match)(struct platform_device *, void *)) +struct dfl_feature_dev_data * +__dfl_fpga_cdev_find_port_data(struct dfl_fpga_cdev *cdev, void *data, + int (*match)(struct dfl_feature_dev_data *, void *)) { - struct dfl_feature_platform_data *pdata; - struct platform_device *port_dev; - - list_for_each_entry(pdata, &cdev->port_dev_list, node) { - port_dev = pdata->dev; + struct dfl_feature_dev_data *fdata; - if (match(port_dev, data) && get_device(&port_dev->dev)) - return port_dev; + list_for_each_entry(fdata, &cdev->port_dev_list, node) { + if (match(fdata, data)) + return fdata; } return NULL; } -EXPORT_SYMBOL_GPL(__dfl_fpga_cdev_find_port); +EXPORT_SYMBOL_GPL(__dfl_fpga_cdev_find_port_data); static int __init dfl_fpga_init(void) { @@ -1706,33 +1704,28 @@ static int __init dfl_fpga_init(void) */ int dfl_fpga_cdev_release_port(struct dfl_fpga_cdev *cdev, int port_id) { - struct dfl_feature_platform_data *pdata; - struct platform_device *port_pdev; + struct dfl_feature_dev_data *fdata; int ret = -ENODEV; mutex_lock(&cdev->lock); - port_pdev = __dfl_fpga_cdev_find_port(cdev, &port_id, - dfl_fpga_check_port_id); - if (!port_pdev) + fdata = __dfl_fpga_cdev_find_port_data(cdev, &port_id, + dfl_fpga_check_port_id); + if (!fdata) goto unlock_exit; - if (!device_is_registered(&port_pdev->dev)) { + if (!fdata->dev) { ret = -EBUSY; - goto put_dev_exit; + goto unlock_exit; } - pdata = dev_get_platdata(&port_pdev->dev); - - mutex_lock(&pdata->lock); - ret = dfl_feature_dev_use_begin(pdata, true); - mutex_unlock(&pdata->lock); + mutex_lock(&fdata->lock); + ret = dfl_feature_dev_use_begin(fdata, true); + mutex_unlock(&fdata->lock); if (ret) - goto put_dev_exit; + goto unlock_exit; - platform_device_del(port_pdev); + feature_dev_unregister(fdata); cdev->released_port_num++; -put_dev_exit: - put_device(&port_pdev->dev); unlock_exit: mutex_unlock(&cdev->lock); return ret; @@ -1752,34 +1745,29 @@ EXPORT_SYMBOL_GPL(dfl_fpga_cdev_release_port); */ int dfl_fpga_cdev_assign_port(struct dfl_fpga_cdev *cdev, int port_id) { - struct dfl_feature_platform_data *pdata; - struct platform_device *port_pdev; + struct dfl_feature_dev_data *fdata; int ret = -ENODEV; mutex_lock(&cdev->lock); - port_pdev = __dfl_fpga_cdev_find_port(cdev, &port_id, - dfl_fpga_check_port_id); - if (!port_pdev) + fdata = __dfl_fpga_cdev_find_port_data(cdev, &port_id, + dfl_fpga_check_port_id); + if (!fdata) goto unlock_exit; - if (device_is_registered(&port_pdev->dev)) { + if (fdata->dev) { ret = -EBUSY; - goto put_dev_exit; + goto unlock_exit; } - ret = platform_device_add(port_pdev); + ret = feature_dev_register(fdata); if (ret) - goto put_dev_exit; - - pdata = dev_get_platdata(&port_pdev->dev); + goto unlock_exit; - mutex_lock(&pdata->lock); - dfl_feature_dev_use_end(pdata); - mutex_unlock(&pdata->lock); + mutex_lock(&fdata->lock); + dfl_feature_dev_use_end(fdata); + mutex_unlock(&fdata->lock); cdev->released_port_num--; -put_dev_exit: - put_device(&port_pdev->dev); unlock_exit: mutex_unlock(&cdev->lock); return ret; @@ -1789,10 +1777,11 @@ EXPORT_SYMBOL_GPL(dfl_fpga_cdev_assign_port); static void config_port_access_mode(struct device *fme_dev, int port_id, bool is_vf) { + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(fme_dev); void __iomem *base; u64 v; - base = dfl_get_feature_ioaddr_by_id(fme_dev, FME_FEATURE_ID_HEADER); + base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER); v = readq(base + FME_HDR_PORT_OFST(port_id)); @@ -1816,14 +1805,14 @@ static void config_port_access_mode(struct device *fme_dev, int port_id, */ void dfl_fpga_cdev_config_ports_pf(struct dfl_fpga_cdev *cdev) { - struct dfl_feature_platform_data *pdata; + struct dfl_feature_dev_data *fdata; mutex_lock(&cdev->lock); - list_for_each_entry(pdata, &cdev->port_dev_list, node) { - if (device_is_registered(&pdata->dev->dev)) + list_for_each_entry(fdata, &cdev->port_dev_list, node) { + if (fdata->dev) continue; - config_port_pf_mode(cdev->fme_dev, pdata->id); + config_port_pf_mode(cdev->fme_dev, fdata->id); } mutex_unlock(&cdev->lock); } @@ -1842,7 +1831,7 @@ EXPORT_SYMBOL_GPL(dfl_fpga_cdev_config_ports_pf); */ int dfl_fpga_cdev_config_ports_vf(struct dfl_fpga_cdev *cdev, int num_vfs) { - struct dfl_feature_platform_data *pdata; + struct dfl_feature_dev_data *fdata; int ret = 0; mutex_lock(&cdev->lock); @@ -1856,11 +1845,11 @@ int dfl_fpga_cdev_config_ports_vf(struct dfl_fpga_cdev *cdev, int num_vfs) goto done; } - list_for_each_entry(pdata, &cdev->port_dev_list, node) { - if (device_is_registered(&pdata->dev->dev)) + list_for_each_entry(fdata, &cdev->port_dev_list, node) { + if (fdata->dev) continue; - config_port_vf_mode(cdev->fme_dev, pdata->id); + config_port_vf_mode(cdev->fme_dev, fdata->id); } done: mutex_unlock(&cdev->lock); @@ -1993,7 +1982,7 @@ long dfl_feature_ioctl_set_irq(struct platform_device *pdev, struct dfl_feature *feature, unsigned long arg) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev); struct dfl_fpga_irq_set hdr; s32 *fds; long ret; @@ -2013,9 +2002,9 @@ long dfl_feature_ioctl_set_irq(struct platform_device *pdev, if (IS_ERR(fds)) return PTR_ERR(fds); - mutex_lock(&pdata->lock); + mutex_lock(&fdata->lock); ret = dfl_fpga_set_irq_triggers(feature, hdr.start, hdr.count, fds); - mutex_unlock(&pdata->lock); + mutex_unlock(&fdata->lock); kfree(fds); return ret; diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h index 5063d73b0d82..95539f1213cb 100644 --- a/drivers/fpga/dfl.h +++ b/drivers/fpga/dfl.h @@ -17,6 +17,7 @@ #include <linux/bitfield.h> #include <linux/cdev.h> #include <linux/delay.h> +#include <linux/dfl.h> #include <linux/eventfd.h> #include <linux/fs.h> #include <linux/interrupt.h> @@ -206,6 +207,8 @@ #define PORT_UINT_CAP_INT_NUM GENMASK_ULL(11, 0) /* Interrupts num */ #define PORT_UINT_CAP_FST_VECT GENMASK_ULL(23, 12) /* First Vector */ +struct dfl_feature_dev_data; + /** * struct dfl_fpga_port_ops - port ops * @@ -219,15 +222,15 @@ struct dfl_fpga_port_ops { const char *name; struct module *owner; struct list_head node; - int (*get_id)(struct platform_device *pdev); - int (*enable_set)(struct platform_device *pdev, bool enable); + int (*get_id)(struct dfl_feature_dev_data *fdata); + int (*enable_set)(struct dfl_feature_dev_data *fdata, bool enable); }; void dfl_fpga_port_ops_add(struct dfl_fpga_port_ops *ops); void dfl_fpga_port_ops_del(struct dfl_fpga_port_ops *ops); -struct dfl_fpga_port_ops *dfl_fpga_port_ops_get(struct platform_device *pdev); +struct dfl_fpga_port_ops *dfl_fpga_port_ops_get(struct dfl_feature_dev_data *fdata); void dfl_fpga_port_ops_put(struct dfl_fpga_port_ops *ops); -int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id); +int dfl_fpga_check_port_id(struct dfl_feature_dev_data *fdata, void *pport_id); /** * struct dfl_feature_id - dfl private feature id @@ -300,26 +303,32 @@ struct dfl_feature { #define FEATURE_DEV_ID_UNUSED (-1) /** - * struct dfl_feature_platform_data - platform data for feature devices + * struct dfl_feature_dev_data - dfl enumeration data for dfl feature dev. * - * @node: node to link feature devs to container device's port_dev_list. - * @lock: mutex to protect platform data. - * @cdev: cdev of feature dev. - * @dev: ptr to platform device linked with this platform data. + * @node: node to link the data structure to container device's port_dev_list. + * @lock: mutex to protect feature dev data. + * @dev: ptr to the feature's platform device linked with this structure. + * @type: type of DFL FIU for the feature dev. See enum dfl_id_type. + * @pdev_id: platform device id for the feature dev. + * @pdev_name: platform device name for the feature dev. * @dfl_cdev: ptr to container device. - * @id: id used for this feature device. + * @id: id used for the feature device. * @disable_count: count for port disable. * @excl_open: set on feature device exclusive open. * @open_count: count for feature device open. * @num: number for sub features. * @private: ptr to feature dev private data. - * @features: sub features of this feature dev. + * @features: sub features for the feature dev. + * @resource_num: number of resources for the feature dev. + * @resources: resources for the feature dev. */ -struct dfl_feature_platform_data { +struct dfl_feature_dev_data { struct list_head node; struct mutex lock; - struct cdev cdev; struct platform_device *dev; + enum dfl_id_type type; + int pdev_id; + const char *pdev_name; struct dfl_fpga_cdev *dfl_cdev; int id; unsigned int disable_count; @@ -327,55 +336,68 @@ struct dfl_feature_platform_data { int open_count; void *private; int num; - struct dfl_feature features[]; + struct dfl_feature *features; + int resource_num; + struct resource *resources; +}; + +/** + * struct dfl_feature_platform_data - platform data for feature devices + * + * @cdev: cdev of feature dev. + * @fdata: dfl enumeration data for the dfl feature device. + */ +struct dfl_feature_platform_data { + struct cdev cdev; + struct dfl_feature_dev_data *fdata; }; static inline -int dfl_feature_dev_use_begin(struct dfl_feature_platform_data *pdata, +int dfl_feature_dev_use_begin(struct dfl_feature_dev_data *fdata, bool excl) { - if (pdata->excl_open) + if (fdata->excl_open) return -EBUSY; if (excl) { - if (pdata->open_count) + if (fdata->open_count) return -EBUSY; - pdata->excl_open = true; + fdata->excl_open = true; } - pdata->open_count++; + fdata->open_count++; return 0; } static inline -void dfl_feature_dev_use_end(struct dfl_feature_platform_data *pdata) +void dfl_feature_dev_use_end(struct dfl_feature_dev_data *fdata) { - pdata->excl_open = false; + fdata->excl_open = false; - if (WARN_ON(pdata->open_count <= 0)) + if (WARN_ON(fdata->open_count <= 0)) return; - pdata->open_count--; + fdata->open_count--; } static inline -int dfl_feature_dev_use_count(struct dfl_feature_platform_data *pdata) +int dfl_feature_dev_use_count(struct dfl_feature_dev_data *fdata) { - return pdata->open_count; + return fdata->open_count; } static inline -void dfl_fpga_pdata_set_private(struct dfl_feature_platform_data *pdata, +void dfl_fpga_fdata_set_private(struct dfl_feature_dev_data *fdata, void *private) { - pdata->private = private; + fdata->private = private; } static inline -void *dfl_fpga_pdata_get_private(struct dfl_feature_platform_data *pdata) +void *dfl_fpga_fdata_get_private(struct dfl_feature_dev_data *fdata) { - return pdata->private; + return fdata->private; } struct dfl_feature_ops { @@ -398,37 +420,36 @@ int dfl_fpga_dev_ops_register(struct platform_device *pdev, struct module *owner); void dfl_fpga_dev_ops_unregister(struct platform_device *pdev); -static inline -struct platform_device *dfl_fpga_inode_to_feature_dev(struct inode *inode) +static inline struct dfl_feature_dev_data * +dfl_fpga_inode_to_feature_dev_data(struct inode *inode) { struct dfl_feature_platform_data *pdata; pdata = container_of(inode->i_cdev, struct dfl_feature_platform_data, cdev); - return pdata->dev; + return pdata->fdata; } -#define dfl_fpga_dev_for_each_feature(pdata, feature) \ - for ((feature) = (pdata)->features; \ - (feature) < (pdata)->features + (pdata)->num; (feature)++) +#define dfl_fpga_dev_for_each_feature(fdata, feature) \ + for ((feature) = (fdata)->features; \ + (feature) < (fdata)->features + (fdata)->num; (feature)++) -static inline -struct dfl_feature *dfl_get_feature_by_id(struct device *dev, u16 id) +static inline struct dfl_feature * +dfl_get_feature_by_id(struct dfl_feature_dev_data *fdata, u16 id) { - struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); struct dfl_feature *feature; - dfl_fpga_dev_for_each_feature(pdata, feature) + dfl_fpga_dev_for_each_feature(fdata, feature) if (feature->id == id) return feature; return NULL; } -static inline -void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u16 id) +static inline void __iomem * +dfl_get_feature_ioaddr_by_id(struct dfl_feature_dev_data *fdata, u16 id) { - struct dfl_feature *feature = dfl_get_feature_by_id(dev, id); + struct dfl_feature *feature = dfl_get_feature_by_id(fdata, id); if (feature && feature->ioaddr) return feature->ioaddr; @@ -437,10 +458,18 @@ void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u16 id) return NULL; } +static inline struct dfl_feature_dev_data * +to_dfl_feature_dev_data(struct device *dev) +{ + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); + + return pdata->fdata; +} + static inline -struct device *dfl_fpga_pdata_to_parent(struct dfl_feature_platform_data *pdata) +struct device *dfl_fpga_fdata_to_parent(struct dfl_feature_dev_data *fdata) { - return pdata->dev->dev.parent->parent; + return fdata->dev->dev.parent->parent; } static inline bool dfl_feature_is_fme(void __iomem *base) @@ -522,26 +551,21 @@ struct dfl_fpga_cdev * dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info); void dfl_fpga_feature_devs_remove(struct dfl_fpga_cdev *cdev); -/* - * need to drop the device reference with put_device() after use port platform - * device returned by __dfl_fpga_cdev_find_port and dfl_fpga_cdev_find_port - * functions. - */ -struct platform_device * -__dfl_fpga_cdev_find_port(struct dfl_fpga_cdev *cdev, void *data, - int (*match)(struct platform_device *, void *)); +struct dfl_feature_dev_data * +__dfl_fpga_cdev_find_port_data(struct dfl_fpga_cdev *cdev, void *data, + int (*match)(struct dfl_feature_dev_data *, void *)); -static inline struct platform_device * -dfl_fpga_cdev_find_port(struct dfl_fpga_cdev *cdev, void *data, - int (*match)(struct platform_device *, void *)) +static inline struct dfl_feature_dev_data * +dfl_fpga_cdev_find_port_data(struct dfl_fpga_cdev *cdev, void *data, + int (*match)(struct dfl_feature_dev_data *, void *)) { - struct platform_device *pdev; + struct dfl_feature_dev_data *fdata; mutex_lock(&cdev->lock); - pdev = __dfl_fpga_cdev_find_port(cdev, data, match); + fdata = __dfl_fpga_cdev_find_port_data(cdev, data, match); mutex_unlock(&cdev->lock); - return pdev; + return fdata; } int dfl_fpga_cdev_release_port(struct dfl_fpga_cdev *cdev, int port_id); diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index ea38ecf26fcb..0a9380350fb5 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -75,22 +75,54 @@ struct coresight_device *coresight_get_percpu_sink(int cpu) } EXPORT_SYMBOL_GPL(coresight_get_percpu_sink); +static struct coresight_device *coresight_get_source(struct list_head *path) +{ + struct coresight_device *csdev; + + if (!path) + return NULL; + + csdev = list_first_entry(path, struct coresight_node, link)->csdev; + if (!coresight_is_device_source(csdev)) + return NULL; + + return csdev; +} + +/** + * coresight_blocks_source - checks whether the connection matches the source + * of path if connection is bound to specific source. + * @src: The source device of the trace path + * @conn: The connection of one outport + * + * Return false if the connection doesn't have a source binded or source of the + * path matches the source binds to connection. + */ +static bool coresight_blocks_source(struct coresight_device *src, + struct coresight_connection *conn) +{ + return conn->filter_src_fwnode && (conn->filter_src_dev != src); +} + static struct coresight_connection * -coresight_find_out_connection(struct coresight_device *src_dev, - struct coresight_device *dest_dev) +coresight_find_out_connection(struct coresight_device *csdev, + struct coresight_device *out_dev, + struct coresight_device *trace_src) { int i; struct coresight_connection *conn; - for (i = 0; i < src_dev->pdata->nr_outconns; i++) { - conn = src_dev->pdata->out_conns[i]; - if (conn->dest_dev == dest_dev) + for (i = 0; i < csdev->pdata->nr_outconns; i++) { + conn = csdev->pdata->out_conns[i]; + if (coresight_blocks_source(trace_src, conn)) + continue; + if (conn->dest_dev == out_dev) return conn; } - dev_err(&src_dev->dev, - "couldn't find output connection, src_dev: %s, dest_dev: %s\n", - dev_name(&src_dev->dev), dev_name(&dest_dev->dev)); + dev_err(&csdev->dev, + "couldn't find output connection, csdev: %s, out_dev: %s\n", + dev_name(&csdev->dev), dev_name(&out_dev->dev)); return ERR_PTR(-ENODEV); } @@ -251,7 +283,8 @@ static void coresight_disable_sink(struct coresight_device *csdev) static int coresight_enable_link(struct coresight_device *csdev, struct coresight_device *parent, - struct coresight_device *child) + struct coresight_device *child, + struct coresight_device *source) { int link_subtype; struct coresight_connection *inconn, *outconn; @@ -259,8 +292,8 @@ static int coresight_enable_link(struct coresight_device *csdev, if (!parent || !child) return -EINVAL; - inconn = coresight_find_out_connection(parent, csdev); - outconn = coresight_find_out_connection(csdev, child); + inconn = coresight_find_out_connection(parent, csdev, source); + outconn = coresight_find_out_connection(csdev, child, source); link_subtype = csdev->subtype.link_subtype; if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG && IS_ERR(inconn)) @@ -273,15 +306,16 @@ static int coresight_enable_link(struct coresight_device *csdev, static void coresight_disable_link(struct coresight_device *csdev, struct coresight_device *parent, - struct coresight_device *child) + struct coresight_device *child, + struct coresight_device *source) { struct coresight_connection *inconn, *outconn; if (!parent || !child) return; - inconn = coresight_find_out_connection(parent, csdev); - outconn = coresight_find_out_connection(csdev, child); + inconn = coresight_find_out_connection(parent, csdev, source); + outconn = coresight_find_out_connection(csdev, child, source); link_ops(csdev)->disable(csdev, inconn, outconn); } @@ -375,7 +409,8 @@ static void coresight_disable_path_from(struct list_head *path, case CORESIGHT_DEV_TYPE_LINK: parent = list_prev_entry(nd, link)->csdev; child = list_next_entry(nd, link)->csdev; - coresight_disable_link(csdev, parent, child); + coresight_disable_link(csdev, parent, child, + coresight_get_source(path)); break; default: break; @@ -418,7 +453,9 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode, u32 type; struct coresight_node *nd; struct coresight_device *csdev, *parent, *child; + struct coresight_device *source; + source = coresight_get_source(path); list_for_each_entry_reverse(nd, path, link) { csdev = nd->csdev; type = csdev->type; @@ -456,7 +493,7 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode, case CORESIGHT_DEV_TYPE_LINK: parent = list_prev_entry(nd, link)->csdev; child = list_next_entry(nd, link)->csdev; - ret = coresight_enable_link(csdev, parent, child); + ret = coresight_enable_link(csdev, parent, child, source); if (ret) goto err; break; @@ -619,6 +656,7 @@ static void coresight_drop_device(struct coresight_device *csdev) /** * _coresight_build_path - recursively build a path from a @csdev to a sink. * @csdev: The device to start from. + * @source: The trace source device of the path. * @sink: The final sink we want in this path. * @path: The list to add devices to. * @@ -628,6 +666,7 @@ static void coresight_drop_device(struct coresight_device *csdev) * the source is the first device and the sink the last one. */ static int _coresight_build_path(struct coresight_device *csdev, + struct coresight_device *source, struct coresight_device *sink, struct list_head *path) { @@ -641,7 +680,7 @@ static int _coresight_build_path(struct coresight_device *csdev, if (coresight_is_percpu_source(csdev) && coresight_is_percpu_sink(sink) && sink == per_cpu(csdev_sink, source_ops(csdev)->cpu_id(csdev))) { - if (_coresight_build_path(sink, sink, path) == 0) { + if (_coresight_build_path(sink, source, sink, path) == 0) { found = true; goto out; } @@ -652,8 +691,12 @@ static int _coresight_build_path(struct coresight_device *csdev, struct coresight_device *child_dev; child_dev = csdev->pdata->out_conns[i]->dest_dev; + + if (coresight_blocks_source(source, csdev->pdata->out_conns[i])) + continue; + if (child_dev && - _coresight_build_path(child_dev, sink, path) == 0) { + _coresight_build_path(child_dev, source, sink, path) == 0) { found = true; break; } @@ -698,7 +741,7 @@ struct list_head *coresight_build_path(struct coresight_device *source, INIT_LIST_HEAD(path); - rc = _coresight_build_path(source, sink, path); + rc = _coresight_build_path(source, source, sink, path); if (rc) { kfree(path); return ERR_PTR(rc); @@ -927,6 +970,16 @@ static int coresight_orphan_match(struct device *dev, void *data) for (i = 0; i < src_csdev->pdata->nr_outconns; i++) { conn = src_csdev->pdata->out_conns[i]; + /* Fix filter source device before skip the port */ + if (conn->filter_src_fwnode && !conn->filter_src_dev) { + if (dst_csdev && + (conn->filter_src_fwnode == dst_csdev->dev.fwnode) && + !WARN_ON_ONCE(!coresight_is_device_source(dst_csdev))) + conn->filter_src_dev = dst_csdev; + else + still_orphan = true; + } + /* Skip the port if it's already connected. */ if (conn->dest_dev) continue; @@ -977,18 +1030,40 @@ static int coresight_fixup_orphan_conns(struct coresight_device *csdev) csdev, coresight_orphan_match); } +static int coresight_clear_filter_source(struct device *dev, void *data) +{ + int i; + struct coresight_device *source = data; + struct coresight_device *csdev = to_coresight_device(dev); + + for (i = 0; i < csdev->pdata->nr_outconns; ++i) { + if (csdev->pdata->out_conns[i]->filter_src_dev == source) + csdev->pdata->out_conns[i]->filter_src_dev = NULL; + } + return 0; +} + /* coresight_remove_conns - Remove other device's references to this device */ static void coresight_remove_conns(struct coresight_device *csdev) { int i, j; struct coresight_connection *conn; + if (coresight_is_device_source(csdev)) + bus_for_each_dev(&coresight_bustype, NULL, csdev, + coresight_clear_filter_source); + /* * Remove the input connection references from the destination device * for each output connection. */ for (i = 0; i < csdev->pdata->nr_outconns; i++) { conn = csdev->pdata->out_conns[i]; + if (conn->filter_src_fwnode) { + conn->filter_src_dev = NULL; + fwnode_handle_put(conn->filter_src_fwnode); + } + if (!conn->dest_dev) continue; diff --git a/drivers/hwtracing/coresight/coresight-dummy.c b/drivers/hwtracing/coresight/coresight-dummy.c index 02ef2b945a0c..9be53be8964b 100644 --- a/drivers/hwtracing/coresight/coresight-dummy.c +++ b/drivers/hwtracing/coresight/coresight-dummy.c @@ -11,10 +11,12 @@ #include <linux/pm_runtime.h> #include "coresight-priv.h" +#include "coresight-trace-id.h" struct dummy_drvdata { struct device *dev; struct coresight_device *csdev; + u8 traceid; }; DEFINE_CORESIGHT_DEVLIST(source_devs, "dummy_source"); @@ -72,6 +74,32 @@ static const struct coresight_ops dummy_sink_cs_ops = { .sink_ops = &dummy_sink_ops, }; +/* User can get the trace id of dummy source from this node. */ +static ssize_t traceid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct dummy_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->traceid; + return sysfs_emit(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(traceid); + +static struct attribute *coresight_dummy_attrs[] = { + &dev_attr_traceid.attr, + NULL, +}; + +static const struct attribute_group coresight_dummy_group = { + .attrs = coresight_dummy_attrs, +}; + +static const struct attribute_group *coresight_dummy_groups[] = { + &coresight_dummy_group, + NULL, +}; + static int dummy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -79,6 +107,11 @@ static int dummy_probe(struct platform_device *pdev) struct coresight_platform_data *pdata; struct dummy_drvdata *drvdata; struct coresight_desc desc = { 0 }; + int ret = 0, trace_id = 0; + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; if (of_device_is_compatible(node, "arm,coresight-dummy-source")) { @@ -90,6 +123,26 @@ static int dummy_probe(struct platform_device *pdev) desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS; desc.ops = &dummy_source_cs_ops; + desc.groups = coresight_dummy_groups; + + ret = coresight_get_static_trace_id(dev, &trace_id); + if (!ret) { + /* Get the static id if id is set in device tree. */ + ret = coresight_trace_id_get_static_system_id(trace_id); + if (ret < 0) { + dev_err(dev, "Fail to get static id.\n"); + return ret; + } + } else { + /* Get next available id if id is not set in device tree. */ + trace_id = coresight_trace_id_get_system_id(); + if (trace_id < 0) { + ret = trace_id; + return ret; + } + } + drvdata->traceid = (u8)trace_id; + } else if (of_device_is_compatible(node, "arm,coresight-dummy-sink")) { desc.name = coresight_alloc_device_name(&sink_devs, dev); if (!desc.name) @@ -104,27 +157,35 @@ static int dummy_probe(struct platform_device *pdev) } pdata = coresight_get_platform_data(dev); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto free_id; + } pdev->dev.platform_data = pdata; - drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); - if (!drvdata) - return -ENOMEM; - drvdata->dev = &pdev->dev; platform_set_drvdata(pdev, drvdata); desc.pdata = pdev->dev.platform_data; desc.dev = &pdev->dev; drvdata->csdev = coresight_register(&desc); - if (IS_ERR(drvdata->csdev)) - return PTR_ERR(drvdata->csdev); + if (IS_ERR(drvdata->csdev)) { + ret = PTR_ERR(drvdata->csdev); + goto free_id; + } pm_runtime_enable(dev); dev_dbg(dev, "Dummy device initialized\n"); - return 0; + ret = 0; + goto out; + +free_id: + if (IS_VALID_CS_TRACE_ID(drvdata->traceid)) + coresight_trace_id_put_system_id(drvdata->traceid); + +out: + return ret; } static void dummy_remove(struct platform_device *pdev) @@ -132,6 +193,8 @@ static void dummy_remove(struct platform_device *pdev) struct dummy_drvdata *drvdata = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + if (IS_VALID_CS_TRACE_ID(drvdata->traceid)) + coresight_trace_id_put_system_id(drvdata->traceid); pm_runtime_disable(dev); coresight_unregister(drvdata->csdev); } diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 33efe1acbef7..8faf51469bb8 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -86,14 +86,14 @@ static int funnel_enable(struct coresight_device *csdev, bool first_enable = false; spin_lock_irqsave(&drvdata->spinlock, flags); - if (atomic_read(&in->dest_refcnt) == 0) { + if (in->dest_refcnt == 0) { if (drvdata->base) rc = dynamic_funnel_enable_hw(drvdata, in->dest_port); if (!rc) first_enable = true; } if (!rc) - atomic_inc(&in->dest_refcnt); + in->dest_refcnt++; spin_unlock_irqrestore(&drvdata->spinlock, flags); if (first_enable) @@ -130,7 +130,7 @@ static void funnel_disable(struct coresight_device *csdev, bool last_disable = false; spin_lock_irqsave(&drvdata->spinlock, flags); - if (atomic_dec_return(&in->dest_refcnt) == 0) { + if (--in->dest_refcnt == 0) { if (drvdata->base) dynamic_funnel_disable_hw(drvdata, in->dest_port); last_disable = true; diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c index 64e171eaad82..8192ba3279f0 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -243,6 +243,27 @@ static int of_coresight_parse_endpoint(struct device *dev, conn.dest_fwnode = fwnode_handle_get(rdev_fwnode); conn.dest_port = rendpoint.port; + /* + * Get the firmware node of the filter source through the + * reference. This could be used to filter the source in + * building path. + */ + conn.filter_src_fwnode = + fwnode_find_reference(&ep->fwnode, "filter-source", 0); + if (IS_ERR(conn.filter_src_fwnode)) { + conn.filter_src_fwnode = NULL; + } else { + conn.filter_src_dev = + coresight_find_csdev_by_fwnode(conn.filter_src_fwnode); + if (conn.filter_src_dev && + !coresight_is_device_source(conn.filter_src_dev)) { + dev_warn(dev, "port %d: Filter handle is not a trace source : %s\n", + conn.src_port, dev_name(&conn.filter_src_dev->dev)); + conn.filter_src_dev = NULL; + conn.filter_src_fwnode = NULL; + } + } + new_conn = coresight_add_out_conn(dev, pdata, &conn); if (IS_ERR_VALUE(new_conn)) { fwnode_handle_put(conn.dest_fwnode); @@ -796,6 +817,12 @@ int coresight_get_cpu(struct device *dev) } EXPORT_SYMBOL_GPL(coresight_get_cpu); +int coresight_get_static_trace_id(struct device *dev, u32 *id) +{ + return fwnode_property_read_u32(dev_fwnode(dev), "arm,static-trace-id", id); +} +EXPORT_SYMBOL_GPL(coresight_get_static_trace_id); + struct coresight_platform_data * coresight_get_platform_data(struct device *dev) { diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 0fba87de6d1a..a1181c9048c0 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -126,7 +126,7 @@ static int replicator_enable(struct coresight_device *csdev, bool first_enable = false; spin_lock_irqsave(&drvdata->spinlock, flags); - if (atomic_read(&out->src_refcnt) == 0) { + if (out->src_refcnt == 0) { if (drvdata->base) rc = dynamic_replicator_enable(drvdata, in->dest_port, out->src_port); @@ -134,7 +134,7 @@ static int replicator_enable(struct coresight_device *csdev, first_enable = true; } if (!rc) - atomic_inc(&out->src_refcnt); + out->src_refcnt++; spin_unlock_irqrestore(&drvdata->spinlock, flags); if (first_enable) @@ -180,7 +180,7 @@ static void replicator_disable(struct coresight_device *csdev, bool last_disable = false; spin_lock_irqsave(&drvdata->spinlock, flags); - if (atomic_dec_return(&out->src_refcnt) == 0) { + if (--out->src_refcnt == 0) { if (drvdata->base) dynamic_replicator_disable(drvdata, in->dest_port, out->src_port); diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c index bfca103f9f84..189a4abc2561 100644 --- a/drivers/hwtracing/coresight/coresight-tpda.c +++ b/drivers/hwtracing/coresight/coresight-tpda.c @@ -24,7 +24,7 @@ DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda"); static bool coresight_device_is_tpdm(struct coresight_device *csdev) { - return (csdev->type == CORESIGHT_DEV_TYPE_SOURCE) && + return (coresight_is_device_source(csdev)) && (csdev->subtype.source_subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM); } @@ -110,6 +110,16 @@ static int tpda_get_element_size(struct tpda_drvdata *drvdata, csdev->pdata->in_conns[i]->dest_port != inport) continue; + /* + * If this port has a hardcoded filter, use the source + * device directly. + */ + if (csdev->pdata->in_conns[i]->filter_src_fwnode) { + in = csdev->pdata->in_conns[i]->filter_src_dev; + if (!in) + continue; + } + if (coresight_device_is_tpdm(in)) { if (drvdata->dsb_esize || drvdata->cmb_esize) return -EEXIST; @@ -124,7 +134,6 @@ static int tpda_get_element_size(struct tpda_drvdata *drvdata, } } - return rc; } @@ -190,10 +199,10 @@ static int tpda_enable(struct coresight_device *csdev, int ret = 0; spin_lock(&drvdata->spinlock); - if (atomic_read(&in->dest_refcnt) == 0) { + if (in->dest_refcnt == 0) { ret = __tpda_enable(drvdata, in->dest_port); if (!ret) { - atomic_inc(&in->dest_refcnt); + in->dest_refcnt++; csdev->refcnt++; dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port); } @@ -223,7 +232,7 @@ static void tpda_disable(struct coresight_device *csdev, struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); spin_lock(&drvdata->spinlock); - if (atomic_dec_return(&in->dest_refcnt) == 0) { + if (--in->dest_refcnt == 0) { __tpda_disable(drvdata, in->dest_port); csdev->refcnt--; } diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index b7d99e91ab84..c38f9701665e 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -640,8 +640,7 @@ static ssize_t dsb_mode_store(struct device *dev, struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val; - if ((kstrtoul(buf, 0, &val)) || (val < 0) || - (val & ~TPDM_DSB_MODE_MASK)) + if ((kstrtoul(buf, 0, &val)) || (val & ~TPDM_DSB_MODE_MASK)) return -EINVAL; spin_lock(&drvdata->spinlock); @@ -1308,8 +1307,8 @@ static void tpdm_remove(struct amba_device *adev) */ static struct amba_id tpdm_ids[] = { { - .id = 0x000f0e00, - .mask = 0x000fff00, + .id = 0x001f0e00, + .mask = 0x00ffff00, }, { 0, 0, NULL }, }; diff --git a/drivers/hwtracing/coresight/coresight-trace-id.c b/drivers/hwtracing/coresight/coresight-trace-id.c index d98e12cb30ec..378af743be45 100644 --- a/drivers/hwtracing/coresight/coresight-trace-id.c +++ b/drivers/hwtracing/coresight/coresight-trace-id.c @@ -12,6 +12,12 @@ #include "coresight-trace-id.h" +enum trace_id_flags { + TRACE_ID_ANY = 0x0, + TRACE_ID_PREFER_ODD = 0x1, + TRACE_ID_REQ_STATIC = 0x2, +}; + /* Default trace ID map. Used in sysfs mode and for system sources */ static DEFINE_PER_CPU(atomic_t, id_map_default_cpu_ids) = ATOMIC_INIT(0); static struct coresight_trace_id_map id_map_default = { @@ -74,21 +80,25 @@ static int coresight_trace_id_find_odd_id(struct coresight_trace_id_map *id_map) * Otherwise allocate next available ID. */ static int coresight_trace_id_alloc_new_id(struct coresight_trace_id_map *id_map, - int preferred_id, bool prefer_odd_id) + int preferred_id, unsigned int flags) { int id = 0; /* for backwards compatibility, cpu IDs may use preferred value */ - if (IS_VALID_CS_TRACE_ID(preferred_id) && - !test_bit(preferred_id, id_map->used_ids)) { - id = preferred_id; - goto trace_id_allocated; - } else if (prefer_odd_id) { + if (IS_VALID_CS_TRACE_ID(preferred_id)) { + if (!test_bit(preferred_id, id_map->used_ids)) { + id = preferred_id; + goto trace_id_allocated; + } else if (flags & TRACE_ID_REQ_STATIC) + return -EBUSY; + } else if (flags & TRACE_ID_PREFER_ODD) { /* may use odd ids to avoid preferred legacy cpu IDs */ id = coresight_trace_id_find_odd_id(id_map); if (id) goto trace_id_allocated; - } + } else if (!IS_VALID_CS_TRACE_ID(preferred_id) && + (flags & TRACE_ID_REQ_STATIC)) + return -EINVAL; /* * skip reserved bit 0, look at bitmap length of @@ -153,7 +163,7 @@ static int _coresight_trace_id_get_cpu_id(int cpu, struct coresight_trace_id_map */ id = coresight_trace_id_alloc_new_id(id_map, CORESIGHT_LEGACY_CPU_TRACE_ID(cpu), - false); + TRACE_ID_ANY); if (!IS_VALID_CS_TRACE_ID(id)) goto get_cpu_id_out_unlock; @@ -188,14 +198,14 @@ static void _coresight_trace_id_put_cpu_id(int cpu, struct coresight_trace_id_ma DUMP_ID_MAP(id_map); } -static int coresight_trace_id_map_get_system_id(struct coresight_trace_id_map *id_map) +static int coresight_trace_id_map_get_system_id(struct coresight_trace_id_map *id_map, + int preferred_id, unsigned int traceid_flags) { unsigned long flags; int id; spin_lock_irqsave(&id_map->lock, flags); - /* prefer odd IDs for system components to avoid legacy CPU IDS */ - id = coresight_trace_id_alloc_new_id(id_map, 0, true); + id = coresight_trace_id_alloc_new_id(id_map, preferred_id, traceid_flags); spin_unlock_irqrestore(&id_map->lock, flags); DUMP_ID(id); @@ -255,10 +265,19 @@ EXPORT_SYMBOL_GPL(coresight_trace_id_read_cpu_id_map); int coresight_trace_id_get_system_id(void) { - return coresight_trace_id_map_get_system_id(&id_map_default); + /* prefer odd IDs for system components to avoid legacy CPU IDS */ + return coresight_trace_id_map_get_system_id(&id_map_default, 0, + TRACE_ID_PREFER_ODD); } EXPORT_SYMBOL_GPL(coresight_trace_id_get_system_id); +int coresight_trace_id_get_static_system_id(int trace_id) +{ + return coresight_trace_id_map_get_system_id(&id_map_default, + trace_id, TRACE_ID_REQ_STATIC); +} +EXPORT_SYMBOL_GPL(coresight_trace_id_get_static_system_id); + void coresight_trace_id_put_system_id(int id) { coresight_trace_id_map_put_system_id(&id_map_default, id); diff --git a/drivers/hwtracing/coresight/coresight-trace-id.h b/drivers/hwtracing/coresight/coresight-trace-id.h index 9aae50a553ca..db68e1ec56b6 100644 --- a/drivers/hwtracing/coresight/coresight-trace-id.h +++ b/drivers/hwtracing/coresight/coresight-trace-id.h @@ -117,6 +117,15 @@ int coresight_trace_id_read_cpu_id_map(int cpu, struct coresight_trace_id_map *i int coresight_trace_id_get_system_id(void); /** + * Allocate a CoreSight static trace ID for a system component. + * + * Used to allocate static IDs for system trace sources such as dummy source. + * + * return: Trace ID or -EINVAL if allocation is impossible. + */ +int coresight_trace_id_get_static_system_id(int id); + +/** * Release an allocated system trace ID. * * Unconditionally release a trace ID allocated to a system component. diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index d72993355473..47d9e6c3bac0 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -857,8 +857,9 @@ static irqreturn_t intel_th_irq(int irq, void *data) /** * intel_th_alloc() - allocate a new Intel TH device and its subdevices * @dev: parent device + * @drvdata: data private to the driver * @devres: resources indexed by th_mmio_idx - * @irq: irq number + * @ndevres: number of entries in the @devres resources */ struct intel_th * intel_th_alloc(struct device *dev, const struct intel_th_drvdata *drvdata, diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h index 3d5c8719db3d..517e494ba555 100644 --- a/drivers/iio/accel/adxl345.h +++ b/drivers/iio/accel/adxl345.h @@ -9,37 +9,93 @@ #define _ADXL345_H_ #define ADXL345_REG_DEVID 0x00 +#define ADXL345_REG_THRESH_TAP 0x1D #define ADXL345_REG_OFSX 0x1E #define ADXL345_REG_OFSY 0x1F #define ADXL345_REG_OFSZ 0x20 #define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index)) + +/* Tap duration */ +#define ADXL345_REG_DUR 0x21 +/* Tap latency */ +#define ADXL345_REG_LATENT 0x22 +/* Tap window */ +#define ADXL345_REG_WINDOW 0x23 +/* Activity threshold */ +#define ADXL345_REG_THRESH_ACT 0x24 +/* Inactivity threshold */ +#define ADXL345_REG_THRESH_INACT 0x25 +/* Inactivity time */ +#define ADXL345_REG_TIME_INACT 0x26 +/* Axis enable control for activity and inactivity detection */ +#define ADXL345_REG_ACT_INACT_CTRL 0x27 +/* Free-fall threshold */ +#define ADXL345_REG_THRESH_FF 0x28 +/* Free-fall time */ +#define ADXL345_REG_TIME_FF 0x29 +/* Axis control for single tap or double tap */ +#define ADXL345_REG_TAP_AXIS 0x2A +/* Source of single tap or double tap */ +#define ADXL345_REG_ACT_TAP_STATUS 0x2B +/* Data rate and power mode control */ #define ADXL345_REG_BW_RATE 0x2C #define ADXL345_REG_POWER_CTL 0x2D +#define ADXL345_REG_INT_ENABLE 0x2E +#define ADXL345_REG_INT_MAP 0x2F +#define ADXL345_REG_INT_SOURCE 0x30 +#define ADXL345_REG_INT_SOURCE_MSK 0xFF #define ADXL345_REG_DATA_FORMAT 0x31 -#define ADXL345_REG_DATAX0 0x32 -#define ADXL345_REG_DATAY0 0x34 -#define ADXL345_REG_DATAZ0 0x36 -#define ADXL345_REG_DATA_AXIS(index) \ - (ADXL345_REG_DATAX0 + (index) * sizeof(__le16)) +#define ADXL345_REG_XYZ_BASE 0x32 +#define ADXL345_REG_DATA_AXIS(index) \ + (ADXL345_REG_XYZ_BASE + (index) * sizeof(__le16)) + +#define ADXL345_REG_FIFO_CTL 0x38 +#define ADXL345_FIFO_CTL_SAMPLES_MSK GENMASK(4, 0) +/* 0: INT1, 1: INT2 */ +#define ADXL345_FIFO_CTL_TRIGGER_MSK BIT(5) +#define ADXL345_FIFO_CTL_MODE_MSK GENMASK(7, 6) +#define ADXL345_REG_FIFO_STATUS 0x39 +#define ADXL345_REG_FIFO_STATUS_MSK 0x3F +#define ADXL345_INT_OVERRUN BIT(0) +#define ADXL345_INT_WATERMARK BIT(1) +#define ADXL345_INT_FREE_FALL BIT(2) +#define ADXL345_INT_INACTIVITY BIT(3) +#define ADXL345_INT_ACTIVITY BIT(4) +#define ADXL345_INT_DOUBLE_TAP BIT(5) +#define ADXL345_INT_SINGLE_TAP BIT(6) +#define ADXL345_INT_DATA_READY BIT(7) + +/* + * BW_RATE bits - Bandwidth and output data rate. The default value is + * 0x0A, which translates to a 100 Hz output data rate + */ #define ADXL345_BW_RATE GENMASK(3, 0) +#define ADXL345_BW_LOW_POWER BIT(4) #define ADXL345_BASE_RATE_NANO_HZ 97656250LL -#define ADXL345_POWER_CTL_MEASURE BIT(3) #define ADXL345_POWER_CTL_STANDBY 0x00 +#define ADXL345_POWER_CTL_WAKEUP GENMASK(1, 0) +#define ADXL345_POWER_CTL_SLEEP BIT(2) +#define ADXL345_POWER_CTL_MEASURE BIT(3) +#define ADXL345_POWER_CTL_AUTO_SLEEP BIT(4) +#define ADXL345_POWER_CTL_LINK BIT(5) -#define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) /* Set the g range */ -#define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) /* Left-justified (MSB) mode */ -#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */ -#define ADXL345_DATA_FORMAT_SPI_3WIRE BIT(6) /* 3-wire SPI mode */ -#define ADXL345_DATA_FORMAT_SELF_TEST BIT(7) /* Enable a self test */ - +/* Set the g range */ +#define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) +/* Data is left justified */ +#define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) +/* Up to 13-bits resolution */ +#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) +#define ADXL345_DATA_FORMAT_SPI_3WIRE BIT(6) +#define ADXL345_DATA_FORMAT_SELF_TEST BIT(7) #define ADXL345_DATA_FORMAT_2G 0 #define ADXL345_DATA_FORMAT_4G 1 #define ADXL345_DATA_FORMAT_8G 2 #define ADXL345_DATA_FORMAT_16G 3 #define ADXL345_DEVID 0xE5 +#define ADXL345_FIFO_SIZE 32 /* * In full-resolution mode, scale factor is maintained at ~4 mg/LSB @@ -62,6 +118,7 @@ struct adxl345_chip_info { }; int adxl345_core_probe(struct device *dev, struct regmap *regmap, + bool fifo_delay_default, int (*setup)(struct device*, struct regmap*)); #endif /* _ADXL345_H_ */ diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index b1efab0f6404..d1b2d3985a40 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -7,6 +7,8 @@ * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf */ +#include <linux/bitfield.h> +#include <linux/interrupt.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regmap.h> @@ -14,36 +16,92 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/kfifo_buf.h> #include "adxl345.h" -struct adxl345_data { +#define ADXL345_FIFO_BYPASS 0 +#define ADXL345_FIFO_FIFO 1 +#define ADXL345_FIFO_STREAM 2 + +#define ADXL345_DIRS 3 + +#define ADXL345_INT_NONE 0xff +#define ADXL345_INT1 0 +#define ADXL345_INT2 1 + +struct adxl345_state { const struct adxl345_chip_info *info; struct regmap *regmap; + bool fifo_delay; /* delay: delay is needed for SPI */ + int irq; + u8 intio; + u8 int_map; + u8 watermark; + u8 fifo_mode; + __le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN); }; -#define ADXL345_CHANNEL(index, axis) { \ +#define ADXL345_CHANNEL(index, reg, axis) { \ .type = IIO_ACCEL, \ .modified = 1, \ .channel2 = IIO_MOD_##axis, \ - .address = index, \ + .address = (reg), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (index), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 13, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ } +enum adxl345_chans { + chan_x, chan_y, chan_z, +}; + static const struct iio_chan_spec adxl345_channels[] = { - ADXL345_CHANNEL(0, X), - ADXL345_CHANNEL(1, Y), - ADXL345_CHANNEL(2, Z), + ADXL345_CHANNEL(0, chan_x, X), + ADXL345_CHANNEL(1, chan_y, Y), + ADXL345_CHANNEL(2, chan_z, Z), }; +static const unsigned long adxl345_scan_masks[] = { + BIT(chan_x) | BIT(chan_y) | BIT(chan_z), + 0 +}; + +static int adxl345_set_interrupts(struct adxl345_state *st) +{ + int ret; + unsigned int int_enable = st->int_map; + unsigned int int_map; + + /* + * Any bits set to 0 in the INT map register send their respective + * interrupts to the INT1 pin, whereas bits set to 1 send their respective + * interrupts to the INT2 pin. The intio shall convert this accordingly. + */ + int_map = FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, + st->intio ? st->int_map : ~st->int_map); + + ret = regmap_write(st->regmap, ADXL345_REG_INT_MAP, int_map); + if (ret) + return ret; + + return regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, int_enable); +} + static int adxl345_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - struct adxl345_data *data = iio_priv(indio_dev); + struct adxl345_state *st = iio_priv(indio_dev); __le16 accel; long long samp_freq_nhz; unsigned int regval; @@ -56,7 +114,7 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, * ADXL345_REG_DATA(X0/Y0/Z0) contain the least significant byte * and ADXL345_REG_DATA(X0/Y0/Z0) + 1 the most significant byte */ - ret = regmap_bulk_read(data->regmap, + ret = regmap_bulk_read(st->regmap, ADXL345_REG_DATA_AXIS(chan->address), &accel, sizeof(accel)); if (ret < 0) @@ -66,10 +124,10 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 0; - *val2 = data->info->uscale; + *val2 = st->info->uscale; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_CALIBBIAS: - ret = regmap_read(data->regmap, + ret = regmap_read(st->regmap, ADXL345_REG_OFS_AXIS(chan->address), ®val); if (ret < 0) return ret; @@ -81,7 +139,7 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SAMP_FREQ: - ret = regmap_read(data->regmap, ADXL345_REG_BW_RATE, ®val); + ret = regmap_read(st->regmap, ADXL345_REG_BW_RATE, ®val); if (ret < 0) return ret; @@ -99,7 +157,7 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - struct adxl345_data *data = iio_priv(indio_dev); + struct adxl345_state *st = iio_priv(indio_dev); s64 n; switch (mask) { @@ -108,14 +166,14 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, * 8-bit resolution at +/- 2g, that is 4x accel data scale * factor */ - return regmap_write(data->regmap, + return regmap_write(st->regmap, ADXL345_REG_OFS_AXIS(chan->address), val / 4); case IIO_CHAN_INFO_SAMP_FREQ: n = div_s64(val * NANOHZ_PER_HZ + val2, ADXL345_BASE_RATE_NANO_HZ); - return regmap_update_bits(data->regmap, ADXL345_REG_BW_RATE, + return regmap_update_bits(st->regmap, ADXL345_REG_BW_RATE, ADXL345_BW_RATE, clamp_val(ilog2(n), 0, ADXL345_BW_RATE)); @@ -124,6 +182,24 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, return -EINVAL; } +static int adxl345_set_watermark(struct iio_dev *indio_dev, unsigned int value) +{ + struct adxl345_state *st = iio_priv(indio_dev); + unsigned int fifo_mask = 0x1F; + int ret; + + value = min(value, ADXL345_FIFO_SIZE - 1); + + ret = regmap_update_bits(st->regmap, ADXL345_REG_FIFO_CTL, fifo_mask, value); + if (ret) + return ret; + + st->watermark = value; + st->int_map |= ADXL345_INT_WATERMARK; + + return 0; +} + static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) @@ -138,6 +214,33 @@ static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev, } } +/** + * adxl345_set_measure_en() - Enable and disable measuring. + * + * @st: The device data. + * @en: Enable measurements, else standby mode. + * + * For lowest power operation, standby mode can be used. In standby mode, + * current consumption is supposed to be reduced to 0.1uA (typical). In this + * mode no measurements are made. Placing the device into standby mode + * preserves the contents of FIFO. + * + * Return: Returns 0 if successful, or a negative error value. + */ +static int adxl345_set_measure_en(struct adxl345_state *st, bool en) +{ + unsigned int val = en ? ADXL345_POWER_CTL_MEASURE : ADXL345_POWER_CTL_STANDBY; + + return regmap_write(st->regmap, ADXL345_REG_POWER_CTL, val); +} + +static void adxl345_powerdown(void *ptr) +{ + struct adxl345_state *st = ptr; + + adxl345_set_measure_en(st, false); +} + static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( "0.09765625 0.1953125 0.390625 0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600 3200" ); @@ -151,37 +254,244 @@ static const struct attribute_group adxl345_attrs_group = { .attrs = adxl345_attrs, }; -static const struct iio_info adxl345_info = { - .attrs = &adxl345_attrs_group, - .read_raw = adxl345_read_raw, - .write_raw = adxl345_write_raw, - .write_raw_get_fmt = adxl345_write_raw_get_fmt, +static int adxl345_set_fifo(struct adxl345_state *st) +{ + int ret; + + /* FIFO should only be configured while in standby mode */ + ret = adxl345_set_measure_en(st, false); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, ADXL345_REG_FIFO_CTL, + FIELD_PREP(ADXL345_FIFO_CTL_SAMPLES_MSK, + st->watermark) | + FIELD_PREP(ADXL345_FIFO_CTL_TRIGGER_MSK, + st->intio) | + FIELD_PREP(ADXL345_FIFO_CTL_MODE_MSK, + st->fifo_mode)); + if (ret < 0) + return ret; + + return adxl345_set_measure_en(st, true); +} + +/** + * adxl345_get_samples() - Read number of FIFO entries. + * @st: The initialized state instance of this driver. + * + * The sensor does not support treating any axis individually, or exclude them + * from measuring. + * + * Return: negative error, or value. + */ +static int adxl345_get_samples(struct adxl345_state *st) +{ + unsigned int regval = 0; + int ret; + + ret = regmap_read(st->regmap, ADXL345_REG_FIFO_STATUS, ®val); + if (ret < 0) + return ret; + + return FIELD_GET(ADXL345_REG_FIFO_STATUS_MSK, regval); +} + +/** + * adxl345_fifo_transfer() - Read samples number of elements. + * @st: The instance of the state object of this sensor. + * @samples: The number of lines in the FIFO referred to as fifo_entry. + * + * It is recommended that a multiple-byte read of all registers be performed to + * prevent a change in data between reads of sequential registers. That is to + * read out the data registers X0, X1, Y0, Y1, Z0, Z1, i.e. 6 bytes at once. + * + * Return: 0 or error value. + */ +static int adxl345_fifo_transfer(struct adxl345_state *st, int samples) +{ + size_t count; + int i, ret = 0; + + /* count is the 3x the fifo_buf element size, hence 6B */ + count = sizeof(st->fifo_buf[0]) * ADXL345_DIRS; + for (i = 0; i < samples; i++) { + /* read 3x 2 byte elements from base address into next fifo_buf position */ + ret = regmap_bulk_read(st->regmap, ADXL345_REG_XYZ_BASE, + st->fifo_buf + (i * count / 2), count); + if (ret < 0) + return ret; + + /* + * To ensure that the FIFO has completely popped, there must be at least 5 + * us between the end of reading the data registers, signified by the + * transition to register 0x38 from 0x37 or the CS pin going high, and the + * start of new reads of the FIFO or reading the FIFO_STATUS register. For + * SPI operation at 1.5 MHz or lower, the register addressing portion of the + * transmission is sufficient delay to ensure the FIFO has completely + * popped. It is necessary for SPI operation greater than 1.5 MHz to + * de-assert the CS pin to ensure a total of 5 us, which is at most 3.4 us + * at 5 MHz operation. + */ + if (st->fifo_delay && samples > 1) + udelay(3); + } + return ret; +} + +/** + * adxl345_fifo_reset() - Empty the FIFO in error condition. + * @st: The instance to the state object of the sensor. + * + * Read all elements of the FIFO. Reading the interrupt source register + * resets the sensor. + */ +static void adxl345_fifo_reset(struct adxl345_state *st) +{ + int regval; + int samples; + + adxl345_set_measure_en(st, false); + + samples = adxl345_get_samples(st); + if (samples > 0) + adxl345_fifo_transfer(st, samples); + + regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, ®val); + + adxl345_set_measure_en(st, true); +} + +static int adxl345_buffer_postenable(struct iio_dev *indio_dev) +{ + struct adxl345_state *st = iio_priv(indio_dev); + int ret; + + ret = adxl345_set_interrupts(st); + if (ret < 0) + return ret; + + st->fifo_mode = ADXL345_FIFO_STREAM; + return adxl345_set_fifo(st); +} + +static int adxl345_buffer_predisable(struct iio_dev *indio_dev) +{ + struct adxl345_state *st = iio_priv(indio_dev); + int ret; + + st->fifo_mode = ADXL345_FIFO_BYPASS; + ret = adxl345_set_fifo(st); + if (ret < 0) + return ret; + + st->int_map = 0x00; + return adxl345_set_interrupts(st); +} + +static const struct iio_buffer_setup_ops adxl345_buffer_ops = { + .postenable = adxl345_buffer_postenable, + .predisable = adxl345_buffer_predisable, }; -static int adxl345_powerup(void *regmap) +static int adxl345_get_status(struct adxl345_state *st) { - return regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_MEASURE); + int ret; + unsigned int regval; + + ret = regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, ®val); + if (ret < 0) + return ret; + + return FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, regval); } -static void adxl345_powerdown(void *regmap) +static int adxl345_fifo_push(struct iio_dev *indio_dev, + int samples) { - regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_STANDBY); + struct adxl345_state *st = iio_priv(indio_dev); + int i, ret; + + if (samples <= 0) + return -EINVAL; + + ret = adxl345_fifo_transfer(st, samples); + if (ret) + return ret; + + for (i = 0; i < ADXL345_DIRS * samples; i += ADXL345_DIRS) + iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); + + return 0; +} + +/** + * adxl345_irq_handler() - Handle irqs of the ADXL345. + * @irq: The irq being handled. + * @p: The struct iio_device pointer for the device. + * + * Return: The interrupt was handled. + */ +static irqreturn_t adxl345_irq_handler(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct adxl345_state *st = iio_priv(indio_dev); + int int_stat; + int samples; + + int_stat = adxl345_get_status(st); + if (int_stat <= 0) + return IRQ_NONE; + + if (int_stat & ADXL345_INT_OVERRUN) + goto err; + + if (int_stat & ADXL345_INT_WATERMARK) { + samples = adxl345_get_samples(st); + if (samples < 0) + goto err; + + if (adxl345_fifo_push(indio_dev, samples) < 0) + goto err; + } + return IRQ_HANDLED; + +err: + adxl345_fifo_reset(st); + + return IRQ_HANDLED; } +static const struct iio_info adxl345_info = { + .attrs = &adxl345_attrs_group, + .read_raw = adxl345_read_raw, + .write_raw = adxl345_write_raw, + .write_raw_get_fmt = adxl345_write_raw_get_fmt, + .hwfifo_set_watermark = adxl345_set_watermark, +}; + /** - * adxl345_core_probe() - probe and setup for the adxl345 accelerometer, - * also covers the adlx375 accelerometer + * adxl345_core_probe() - Probe and setup for the accelerometer. * @dev: Driver model representation of the device * @regmap: Regmap instance for the device + * @fifo_delay_default: Using FIFO with SPI needs delay * @setup: Setup routine to be executed right before the standard device * setup * + * For SPI operation greater than 1.6 MHz, it is necessary to deassert the CS + * pin to ensure a total delay of 5 us; otherwise, the delay is not sufficient. + * The total delay necessary for 5 MHz operation is at most 3.4 us. This is not + * a concern when using I2C mode because the communication rate is low enough + * to ensure a sufficient delay between FIFO reads. + * Ref: "Retrieving Data from FIFO", p. 21 of 36, Data Sheet ADXL345 Rev. G + * * Return: 0 on success, negative errno on error */ int adxl345_core_probe(struct device *dev, struct regmap *regmap, + bool fifo_delay_default, int (*setup)(struct device*, struct regmap*)) { - struct adxl345_data *data; + struct adxl345_state *st; struct iio_dev *indio_dev; u32 regval; unsigned int data_format_mask = (ADXL345_DATA_FORMAT_RANGE | @@ -190,30 +500,32 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, ADXL345_DATA_FORMAT_SELF_TEST); int ret; - indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; - data = iio_priv(indio_dev); - data->regmap = regmap; - data->info = device_get_match_data(dev); - if (!data->info) + st = iio_priv(indio_dev); + st->regmap = regmap; + st->info = device_get_match_data(dev); + if (!st->info) return -ENODEV; + st->fifo_delay = fifo_delay_default; - indio_dev->name = data->info->name; + indio_dev->name = st->info->name; indio_dev->info = &adxl345_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = adxl345_channels; indio_dev->num_channels = ARRAY_SIZE(adxl345_channels); + indio_dev->available_scan_masks = adxl345_scan_masks; if (setup) { /* Perform optional initial bus specific configuration */ - ret = setup(dev, data->regmap); + ret = setup(dev, st->regmap); if (ret) return ret; /* Enable full-resolution mode */ - ret = regmap_update_bits(data->regmap, ADXL345_REG_DATA_FORMAT, + ret = regmap_update_bits(st->regmap, ADXL345_REG_DATA_FORMAT, data_format_mask, ADXL345_DATA_FORMAT_FULL_RES); if (ret) @@ -222,14 +534,14 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, } else { /* Enable full-resolution mode (init all data_format bits) */ - ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT, + ret = regmap_write(st->regmap, ADXL345_REG_DATA_FORMAT, ADXL345_DATA_FORMAT_FULL_RES); if (ret) return dev_err_probe(dev, ret, "Failed to set data range\n"); } - ret = regmap_read(data->regmap, ADXL345_REG_DEVID, ®val); + ret = regmap_read(st->regmap, ADXL345_REG_DEVID, ®val); if (ret < 0) return dev_err_probe(dev, ret, "Error reading device ID\n"); @@ -238,14 +550,43 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, regval, ADXL345_DEVID); /* Enable measurement mode */ - ret = adxl345_powerup(data->regmap); + ret = adxl345_set_measure_en(st, true); if (ret < 0) return dev_err_probe(dev, ret, "Failed to enable measurement mode\n"); - ret = devm_add_action_or_reset(dev, adxl345_powerdown, data->regmap); + ret = devm_add_action_or_reset(dev, adxl345_powerdown, st); if (ret < 0) return ret; + st->intio = ADXL345_INT1; + st->irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1"); + if (st->irq < 0) { + st->intio = ADXL345_INT2; + st->irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2"); + if (st->irq < 0) + st->intio = ADXL345_INT_NONE; + } + + if (st->intio != ADXL345_INT_NONE) { + /* FIFO_STREAM mode is going to be activated later */ + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &adxl345_buffer_ops); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, st->irq, NULL, + &adxl345_irq_handler, + IRQF_SHARED | IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return ret; + } else { + ret = regmap_write(st->regmap, ADXL345_REG_FIFO_CTL, + FIELD_PREP(ADXL345_FIFO_CTL_MODE_MSK, + ADXL345_FIFO_BYPASS)); + if (ret < 0) + return ret; + } + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(adxl345_core_probe, "IIO_ADXL345"); diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c index cb23fb11fcd7..8c385dd6c01d 100644 --- a/drivers/iio/accel/adxl345_i2c.c +++ b/drivers/iio/accel/adxl345_i2c.c @@ -27,7 +27,7 @@ static int adxl345_i2c_probe(struct i2c_client *client) if (IS_ERR(regmap)) return dev_err_probe(&client->dev, PTR_ERR(regmap), "Error initializing regmap\n"); - return adxl345_core_probe(&client->dev, regmap, NULL); + return adxl345_core_probe(&client->dev, regmap, false, NULL); } static const struct adxl345_chip_info adxl345_i2c_info = { diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c index 968e7b390d4b..7e518aea17bf 100644 --- a/drivers/iio/accel/adxl345_spi.c +++ b/drivers/iio/accel/adxl345_spi.c @@ -12,6 +12,7 @@ #include "adxl345.h" #define ADXL345_MAX_SPI_FREQ_HZ 5000000 +#define ADXL345_MAX_FREQ_NO_FIFO_DELAY 1500000 static const struct regmap_config adxl345_spi_regmap_config = { .reg_bits = 8, @@ -28,6 +29,7 @@ static int adxl345_spi_setup(struct device *dev, struct regmap *regmap) static int adxl345_spi_probe(struct spi_device *spi) { struct regmap *regmap; + bool needs_delay; /* Bail out if max_speed_hz exceeds 5 MHz */ if (spi->max_speed_hz > ADXL345_MAX_SPI_FREQ_HZ) @@ -38,10 +40,11 @@ static int adxl345_spi_probe(struct spi_device *spi) if (IS_ERR(regmap)) return dev_err_probe(&spi->dev, PTR_ERR(regmap), "Error initializing regmap\n"); + needs_delay = spi->max_speed_hz > ADXL345_MAX_FREQ_NO_FIFO_DELAY; if (spi->mode & SPI_3WIRE) - return adxl345_core_probe(&spi->dev, regmap, adxl345_spi_setup); + return adxl345_core_probe(&spi->dev, regmap, needs_delay, adxl345_spi_setup); else - return adxl345_core_probe(&spi->dev, regmap, NULL); + return adxl345_core_probe(&spi->dev, regmap, needs_delay, NULL); } static const struct adxl345_chip_info adxl345_spi_info = { diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index 009e6243c6cb..96ba028157ee 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -66,7 +66,7 @@ struct bma220_data { struct { s8 chans[3]; /* Ensure timestamp is naturally aligned. */ - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; u8 tx_buf[2] __aligned(IIO_DMA_MINALIGN); }; diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index 65aac60f1245..987212a7c038 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -129,6 +129,8 @@ #define FXLS8962AF_DEVICE_ID 0x62 #define FXLS8964AF_DEVICE_ID 0x84 +#define FXLS8974CF_DEVICE_ID 0x86 +#define FXLS8967AF_DEVICE_ID 0x87 /* Raw temp channel offset */ #define FXLS8962AF_TEMP_CENTER_VAL 25 @@ -766,6 +768,18 @@ static const struct fxls8962af_chip_info fxls_chip_info_table[] = { .channels = fxls8962af_channels, .num_channels = ARRAY_SIZE(fxls8962af_channels), }, + [fxls8967af] = { + .chip_id = FXLS8967AF_DEVICE_ID, + .name = "fxls8967af", + .channels = fxls8962af_channels, + .num_channels = ARRAY_SIZE(fxls8962af_channels), + }, + [fxls8974cf] = { + .chip_id = FXLS8974CF_DEVICE_ID, + .name = "fxls8974cf", + .channels = fxls8962af_channels, + .num_channels = ARRAY_SIZE(fxls8962af_channels), + }, }; static const struct iio_info fxls8962af_info = { diff --git a/drivers/iio/accel/fxls8962af-i2c.c b/drivers/iio/accel/fxls8962af-i2c.c index 2e1bb43ef2a1..1b9156b6b2e3 100644 --- a/drivers/iio/accel/fxls8962af-i2c.c +++ b/drivers/iio/accel/fxls8962af-i2c.c @@ -30,6 +30,8 @@ static int fxls8962af_probe(struct i2c_client *client) static const struct i2c_device_id fxls8962af_id[] = { { "fxls8962af", fxls8962af }, { "fxls8964af", fxls8964af }, + { "fxls8967af", fxls8967af }, + { "fxls8974cf", fxls8974cf }, {} }; MODULE_DEVICE_TABLE(i2c, fxls8962af_id); diff --git a/drivers/iio/accel/fxls8962af.h b/drivers/iio/accel/fxls8962af.h index 6eaa2803b26f..1c9adfc8c0dc 100644 --- a/drivers/iio/accel/fxls8962af.h +++ b/drivers/iio/accel/fxls8962af.h @@ -11,6 +11,8 @@ struct device; enum { fxls8962af, fxls8964af, + fxls8967af, + fxls8974cf, }; int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq); diff --git a/drivers/iio/accel/kionix-kx022a-i2c.c b/drivers/iio/accel/kionix-kx022a-i2c.c index b39a43ecadff..42388636ca31 100644 --- a/drivers/iio/accel/kionix-kx022a-i2c.c +++ b/drivers/iio/accel/kionix-kx022a-i2c.c @@ -38,7 +38,9 @@ static int kx022a_i2c_probe(struct i2c_client *i2c) static const struct i2c_device_id kx022a_i2c_id[] = { { .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info }, { .name = "kx132-1211", .driver_data = (kernel_ulong_t)&kx132_chip_info }, + { .name = "kx134-1211", .driver_data = (kernel_ulong_t)&kx134_chip_info }, { .name = "kx132acr-lbz", .driver_data = (kernel_ulong_t)&kx132acr_chip_info }, + { .name = "kx134acr-lbz", .driver_data = (kernel_ulong_t)&kx134acr_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, kx022a_i2c_id); @@ -46,7 +48,9 @@ MODULE_DEVICE_TABLE(i2c, kx022a_i2c_id); static const struct of_device_id kx022a_of_match[] = { { .compatible = "kionix,kx022a", .data = &kx022a_chip_info }, { .compatible = "kionix,kx132-1211", .data = &kx132_chip_info }, + { .compatible = "kionix,kx134-1211", .data = &kx134_chip_info }, { .compatible = "rohm,kx132acr-lbz", .data = &kx132acr_chip_info }, + { .compatible = "rohm,kx134acr-lbz", .data = &kx134acr_chip_info }, { } }; MODULE_DEVICE_TABLE(of, kx022a_of_match); diff --git a/drivers/iio/accel/kionix-kx022a-spi.c b/drivers/iio/accel/kionix-kx022a-spi.c index c38a47806a00..e30d21083dc8 100644 --- a/drivers/iio/accel/kionix-kx022a-spi.c +++ b/drivers/iio/accel/kionix-kx022a-spi.c @@ -38,7 +38,9 @@ static int kx022a_spi_probe(struct spi_device *spi) static const struct spi_device_id kx022a_id[] = { { .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info }, { .name = "kx132-1211", .driver_data = (kernel_ulong_t)&kx132_chip_info }, + { .name = "kx134-1211", .driver_data = (kernel_ulong_t)&kx134_chip_info }, { .name = "kx132acr-lbz", .driver_data = (kernel_ulong_t)&kx132acr_chip_info }, + { .name = "kx134acr-lbz", .driver_data = (kernel_ulong_t)&kx134acr_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, kx022a_id); @@ -46,7 +48,9 @@ MODULE_DEVICE_TABLE(spi, kx022a_id); static const struct of_device_id kx022a_of_match[] = { { .compatible = "kionix,kx022a", .data = &kx022a_chip_info }, { .compatible = "kionix,kx132-1211", .data = &kx132_chip_info }, + { .compatible = "kionix,kx134-1211", .data = &kx134_chip_info }, { .compatible = "rohm,kx132acr-lbz", .data = &kx132acr_chip_info }, + { .compatible = "rohm,kx134acr-lbz", .data = &kx134acr_chip_info }, { } }; MODULE_DEVICE_TABLE(of, kx022a_of_match); diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 670bac21965b..5aeb3b951ac5 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -5,6 +5,7 @@ * ROHM/KIONIX accelerometer driver */ +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/interrupt.h> @@ -407,11 +408,21 @@ static const int kx022a_scale_table[][2] = { { 0, 4788403 }, }; +/* KX134ACR-LBZ ranges are (+/-) 8, 16, 32, 64 G */ +static const int kx134acr_lbz_scale_table[][2] = { + { 0, 2394202 }, + { 0, 4788403 }, + { 0, 9576807 }, + { 0, 19153613 }, +}; + static int kx022a_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, const int **vals, int *type, int *length, long mask) { + struct kx022a_data *data = iio_priv(indio_dev); + switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: *vals = (const int *)kx022a_accel_samp_freq_table; @@ -420,9 +431,8 @@ static int kx022a_read_avail(struct iio_dev *indio_dev, *type = IIO_VAL_INT_PLUS_MICRO; return IIO_AVAIL_LIST; case IIO_CHAN_INFO_SCALE: - *vals = (const int *)kx022a_scale_table; - *length = ARRAY_SIZE(kx022a_scale_table) * - ARRAY_SIZE(kx022a_scale_table[0]); + *vals = (const int *)data->chip_info->scale_table; + *length = data->chip_info->scale_table_size; *type = IIO_VAL_INT_PLUS_NANO; return IIO_AVAIL_LIST; default: @@ -438,17 +448,17 @@ static void kx022a_reg2freq(unsigned int val, int *val1, int *val2) *val2 = kx022a_accel_samp_freq_table[val & KX022A_MASK_ODR][1]; } -static void kx022a_reg2scale(unsigned int val, unsigned int *val1, - unsigned int *val2) +static void kx022a_reg2scale(struct kx022a_data *data, unsigned int val, + unsigned int *val1, unsigned int *val2) { val &= KX022A_MASK_GSEL; val >>= KX022A_GSEL_SHIFT; - *val1 = kx022a_scale_table[val][0]; - *val2 = kx022a_scale_table[val][1]; + *val1 = data->chip_info->scale_table[val][0]; + *val2 = data->chip_info->scale_table[val][1]; } -static int kx022a_turn_on_off_unlocked(struct kx022a_data *data, bool on) +static int __kx022a_turn_on_off(struct kx022a_data *data, bool on) { int ret; @@ -469,7 +479,7 @@ static int kx022a_turn_off_lock(struct kx022a_data *data) int ret; mutex_lock(&data->mutex); - ret = kx022a_turn_on_off_unlocked(data, false); + ret = __kx022a_turn_on_off(data, false); if (ret) mutex_unlock(&data->mutex); @@ -480,7 +490,7 @@ static int kx022a_turn_on_unlock(struct kx022a_data *data) { int ret; - ret = kx022a_turn_on_off_unlocked(data, true); + ret = __kx022a_turn_on_off(data, true); mutex_unlock(&data->mutex); return ret; @@ -543,11 +553,11 @@ static int kx022a_write_raw(struct iio_dev *idev, kx022a_turn_on_unlock(data); break; case IIO_CHAN_INFO_SCALE: - n = ARRAY_SIZE(kx022a_scale_table); + n = data->chip_info->scale_table_size / 2; while (n-- > 0) - if (val == kx022a_scale_table[n][0] && - val2 == kx022a_scale_table[n][1]) + if (val == data->chip_info->scale_table[n][0] && + val2 == data->chip_info->scale_table[n][1]) break; if (n < 0) { ret = -EINVAL; @@ -642,7 +652,7 @@ static int kx022a_read_raw(struct iio_dev *idev, if (ret < 0) return ret; - kx022a_reg2scale(regval, val, val2); + kx022a_reg2scale(data, regval, val, val2); return IIO_VAL_INT_PLUS_NANO; } @@ -912,18 +922,19 @@ static int kx022a_fifo_disable(struct kx022a_data *data) { int ret = 0; - ret = kx022a_turn_off_lock(data); + guard(mutex)(&data->mutex); + ret = __kx022a_turn_on_off(data, false); if (ret) return ret; ret = regmap_clear_bits(data->regmap, data->ien_reg, KX022A_MASK_WMI); if (ret) - goto unlock_out; + return ret; ret = regmap_clear_bits(data->regmap, data->chip_info->buf_cntl2, KX022A_MASK_BUF_EN); if (ret) - goto unlock_out; + return ret; data->state &= ~KX022A_STATE_FIFO; @@ -931,12 +942,7 @@ static int kx022a_fifo_disable(struct kx022a_data *data) kfree(data->fifo_buffer); - return kx022a_turn_on_unlock(data); - -unlock_out: - mutex_unlock(&data->mutex); - - return ret; + return __kx022a_turn_on_off(data, true); } static int kx022a_buffer_predisable(struct iio_dev *idev) @@ -959,33 +965,29 @@ static int kx022a_fifo_enable(struct kx022a_data *data) if (!data->fifo_buffer) return -ENOMEM; - ret = kx022a_turn_off_lock(data); + guard(mutex)(&data->mutex); + ret = __kx022a_turn_on_off(data, false); if (ret) return ret; /* Update watermark to HW */ ret = kx022a_fifo_set_wmi(data); if (ret) - goto unlock_out; + return ret; /* Enable buffer */ ret = regmap_set_bits(data->regmap, data->chip_info->buf_cntl2, KX022A_MASK_BUF_EN); if (ret) - goto unlock_out; + return ret; data->state |= KX022A_STATE_FIFO; ret = regmap_set_bits(data->regmap, data->ien_reg, KX022A_MASK_WMI); if (ret) - goto unlock_out; - - return kx022a_turn_on_unlock(data); - -unlock_out: - mutex_unlock(&data->mutex); + return ret; - return ret; + return __kx022a_turn_on_off(data, true); } static int kx022a_buffer_postenable(struct iio_dev *idev) @@ -1053,7 +1055,7 @@ static irqreturn_t kx022a_irq_thread_handler(int irq, void *private) struct kx022a_data *data = iio_priv(idev); irqreturn_t ret = IRQ_NONE; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (data->trigger_enabled) { iio_trigger_poll_nested(data->trig); @@ -1068,8 +1070,6 @@ static irqreturn_t kx022a_irq_thread_handler(int irq, void *private) ret = IRQ_HANDLED; } - mutex_unlock(&data->mutex); - return ret; } @@ -1079,32 +1079,26 @@ static int kx022a_trigger_set_state(struct iio_trigger *trig, struct kx022a_data *data = iio_trigger_get_drvdata(trig); int ret = 0; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (data->trigger_enabled == state) - goto unlock_out; + return 0; if (data->state & KX022A_STATE_FIFO) { dev_warn(data->dev, "Can't set trigger when FIFO enabled\n"); - ret = -EBUSY; - goto unlock_out; + return -EBUSY; } - ret = kx022a_turn_on_off_unlocked(data, false); + ret = __kx022a_turn_on_off(data, false); if (ret) - goto unlock_out; + return ret; data->trigger_enabled = state; ret = kx022a_set_drdy_irq(data, state); if (ret) - goto unlock_out; - - ret = kx022a_turn_on_off_unlocked(data, true); - -unlock_out: - mutex_unlock(&data->mutex); + return ret; - return ret; + return __kx022a_turn_on_off(data, true); } static const struct iio_trigger_ops kx022a_trigger_ops = { @@ -1121,10 +1115,15 @@ static int kx022a_chip_init(struct kx022a_data *data) return ret; /* - * I've seen I2C read failures if we poll too fast after the sensor - * reset. Slight delay gives I2C block the time to recover. + * According to the power-on procedure documents, there is (at least) + * 2ms delay required after the software reset. This should be same for + * all, KX022ACR-Z, KX132-1211, KX132ACR-LBZ and KX134ACR-LBZ. + * + * https://fscdn.rohm.com/kionix/en/document/AN010_KX022ACR-Z_Power-on_Procedure_E.pdf + * https://fscdn.rohm.com/kionix/en/document/TN027-Power-On-Procedure.pdf + * https://fscdn.rohm.com/kionix/en/document/AN011_KX134ACR-LBZ_Power-on_Procedure_E.pdf */ - msleep(1); + msleep(2); ret = regmap_read_poll_timeout(data->regmap, data->chip_info->cntl2, val, !(val & KX022A_MASK_SRST), @@ -1158,6 +1157,9 @@ const struct kx022a_chip_info kx022a_chip_info = { .regmap_config = &kx022a_regmap_config, .channels = kx022a_channels, .num_channels = ARRAY_SIZE(kx022a_channels), + .scale_table = kx022a_scale_table, + .scale_table_size = ARRAY_SIZE(kx022a_scale_table) * + ARRAY_SIZE(kx022a_scale_table[0]), .fifo_length = KX022A_FIFO_LENGTH, .who = KX022A_REG_WHO, .id = KX022A_ID, @@ -1183,6 +1185,9 @@ const struct kx022a_chip_info kx132_chip_info = { .regmap_config = &kx132_regmap_config, .channels = kx132_channels, .num_channels = ARRAY_SIZE(kx132_channels), + .scale_table = kx022a_scale_table, + .scale_table_size = ARRAY_SIZE(kx022a_scale_table) * + ARRAY_SIZE(kx022a_scale_table[0]), .fifo_length = KX132_FIFO_LENGTH, .who = KX132_REG_WHO, .id = KX132_ID, @@ -1204,6 +1209,35 @@ const struct kx022a_chip_info kx132_chip_info = { }; EXPORT_SYMBOL_NS_GPL(kx132_chip_info, "IIO_KX022A"); +const struct kx022a_chip_info kx134_chip_info = { + .name = "kx134-1211", + .regmap_config = &kx132_regmap_config, + .channels = kx132_channels, + .num_channels = ARRAY_SIZE(kx132_channels), + .scale_table = kx134acr_lbz_scale_table, + .scale_table_size = ARRAY_SIZE(kx134acr_lbz_scale_table) * + ARRAY_SIZE(kx134acr_lbz_scale_table[0]), + .fifo_length = KX132_FIFO_LENGTH, + .who = KX132_REG_WHO, + .id = KX134_1211_ID, + .cntl = KX132_REG_CNTL, + .cntl2 = KX132_REG_CNTL2, + .odcntl = KX132_REG_ODCNTL, + .buf_cntl1 = KX132_REG_BUF_CNTL1, + .buf_cntl2 = KX132_REG_BUF_CNTL2, + .buf_clear = KX132_REG_BUF_CLEAR, + .buf_status1 = KX132_REG_BUF_STATUS_1, + .buf_smp_lvl_mask = KX132_MASK_BUF_SMP_LVL, + .buf_read = KX132_REG_BUF_READ, + .inc1 = KX132_REG_INC1, + .inc4 = KX132_REG_INC4, + .inc5 = KX132_REG_INC5, + .inc6 = KX132_REG_INC6, + .xout_l = KX132_REG_XOUT_L, + .get_fifo_bytes_available = kx132_get_fifo_bytes_available, +}; +EXPORT_SYMBOL_NS_GPL(kx134_chip_info, "IIO_KX022A"); + /* * Despite the naming, KX132ACR-LBZ is not similar to KX132-1211 but it is * exact subset of KX022A. KX132ACR-LBZ is meant to be used for industrial @@ -1216,6 +1250,9 @@ const struct kx022a_chip_info kx132acr_chip_info = { .regmap_config = &kx022a_regmap_config, .channels = kx022a_channels, .num_channels = ARRAY_SIZE(kx022a_channels), + .scale_table = kx022a_scale_table, + .scale_table_size = ARRAY_SIZE(kx022a_scale_table) * + ARRAY_SIZE(kx022a_scale_table[0]), .fifo_length = KX022A_FIFO_LENGTH, .who = KX022A_REG_WHO, .id = KX132ACR_LBZ_ID, @@ -1236,6 +1273,34 @@ const struct kx022a_chip_info kx132acr_chip_info = { }; EXPORT_SYMBOL_NS_GPL(kx132acr_chip_info, "IIO_KX022A"); +const struct kx022a_chip_info kx134acr_chip_info = { + .name = "kx134acr-lbz", + .regmap_config = &kx022a_regmap_config, + .channels = kx022a_channels, + .num_channels = ARRAY_SIZE(kx022a_channels), + .scale_table = kx134acr_lbz_scale_table, + .scale_table_size = ARRAY_SIZE(kx134acr_lbz_scale_table) * + ARRAY_SIZE(kx134acr_lbz_scale_table[0]), + .fifo_length = KX022A_FIFO_LENGTH, + .who = KX022A_REG_WHO, + .id = KX134ACR_LBZ_ID, + .cntl = KX022A_REG_CNTL, + .cntl2 = KX022A_REG_CNTL2, + .odcntl = KX022A_REG_ODCNTL, + .buf_cntl1 = KX022A_REG_BUF_CNTL1, + .buf_cntl2 = KX022A_REG_BUF_CNTL2, + .buf_clear = KX022A_REG_BUF_CLEAR, + .buf_status1 = KX022A_REG_BUF_STATUS_1, + .buf_read = KX022A_REG_BUF_READ, + .inc1 = KX022A_REG_INC1, + .inc4 = KX022A_REG_INC4, + .inc5 = KX022A_REG_INC5, + .inc6 = KX022A_REG_INC6, + .xout_l = KX022A_REG_XOUT_L, + .get_fifo_bytes_available = kx022a_get_fifo_bytes_available, +}; +EXPORT_SYMBOL_NS_GPL(kx134acr_chip_info, "IIO_KX022A"); + int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chip_info) { static const char * const regulator_names[] = {"io-vdd", "vdd"}; diff --git a/drivers/iio/accel/kionix-kx022a.h b/drivers/iio/accel/kionix-kx022a.h index 7060438ad88c..0ed54f584223 100644 --- a/drivers/iio/accel/kionix-kx022a.h +++ b/drivers/iio/accel/kionix-kx022a.h @@ -14,6 +14,7 @@ #define KX022A_REG_WHO 0x0f #define KX022A_ID 0xc8 #define KX132ACR_LBZ_ID 0xd8 +#define KX134ACR_LBZ_ID 0xcc #define KX022A_REG_CNTL2 0x19 #define KX022A_MASK_SRST BIT(7) @@ -77,6 +78,7 @@ #define KX132_REG_WHO 0x13 #define KX132_ID 0x3d +#define KX134_1211_ID 0x46 #define KX132_FIFO_LENGTH 86 @@ -135,6 +137,14 @@ struct kx022a_data; * * @name: name of the device * @regmap_config: pointer to register map configuration + * @scale_table: An array of tables of scaling factors for + * a supported acceleration measurement range. + * Each table containing a single scaling + * factor consisting of two integers. The first + * value in a table is the integer part, and + * the second value is the fractional part as + * parts per billion. + * @scale_table_size: Amount of values in tables. * @channels: pointer to iio_chan_spec array * @num_channels: number of iio_chan_spec channels * @fifo_length: number of 16-bit samples in a full buffer @@ -161,6 +171,8 @@ struct kx022a_data; struct kx022a_chip_info { const char *name; const struct regmap_config *regmap_config; + const int (*scale_table)[2]; + const int scale_table_size; const struct iio_chan_spec *channels; unsigned int num_channels; unsigned int fifo_length; @@ -187,6 +199,8 @@ int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chi extern const struct kx022a_chip_info kx022a_chip_info; extern const struct kx022a_chip_info kx132_chip_info; +extern const struct kx022a_chip_info kx134_chip_info; extern const struct kx022a_chip_info kx132acr_chip_info; +extern const struct kx022a_chip_info kx134acr_chip_info; #endif diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c index b3b82535f5c1..1d556a842a68 100644 --- a/drivers/iio/adc/ad4000.c +++ b/drivers/iio/adc/ad4000.c @@ -35,10 +35,6 @@ #define AD4000_SCALE_OPTIONS 2 -#define AD4000_TQUIET1_NS 190 -#define AD4000_TQUIET2_NS 60 -#define AD4000_TCONV_NS 320 - #define __AD4000_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access) \ { \ .type = IIO_VOLTAGE, \ @@ -49,6 +45,7 @@ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\ + .scan_index = 0, \ .scan_type = { \ .sign = _sign, \ .realbits = _real_bits, \ @@ -62,6 +59,12 @@ __AD4000_DIFF_CHANNEL((_sign), (_real_bits), \ ((_real_bits) > 16 ? 32 : 16), (_reg_access)) +#define AD4000_DIFF_CHANNELS(_sign, _real_bits, _reg_access) \ +{ \ + AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ +} + #define __AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access)\ { \ .type = IIO_VOLTAGE, \ @@ -71,6 +74,7 @@ BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_OFFSET), \ .info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\ + .scan_index = 0, \ .scan_type = { \ .sign = _sign, \ .realbits = _real_bits, \ @@ -84,6 +88,12 @@ __AD4000_PSEUDO_DIFF_CHANNEL((_sign), (_real_bits), \ ((_real_bits) > 16 ? 32 : 16), (_reg_access)) +#define AD4000_PSEUDO_DIFF_CHANNELS(_sign, _real_bits, _reg_access) \ +{ \ + AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ +} + static const char * const ad4000_power_supplies[] = { "vdd", "vio" }; @@ -108,111 +118,280 @@ static const int ad4000_gains[] = { 454, 909, 1000, 1900, }; +struct ad4000_time_spec { + int t_conv_ns; + int t_quiet2_ns; +}; + +/* + * Same timing specifications for all of AD4000, AD4001, ..., AD4008, AD4010, + * ADAQ4001, and ADAQ4003. + */ +static const struct ad4000_time_spec ad4000_t_spec = { + .t_conv_ns = 320, + .t_quiet2_ns = 60, +}; + +/* AD4020, AD4021, AD4022 */ +static const struct ad4000_time_spec ad4020_t_spec = { + .t_conv_ns = 350, + .t_quiet2_ns = 60, +}; + +/* AD7983, AD7984 */ +static const struct ad4000_time_spec ad7983_t_spec = { + .t_conv_ns = 500, + .t_quiet2_ns = 0, +}; + +/* AD7980, AD7982 */ +static const struct ad4000_time_spec ad7980_t_spec = { + .t_conv_ns = 800, + .t_quiet2_ns = 0, +}; + +/* AD7946, AD7686, AD7688, AD7988-5, AD7693 */ +static const struct ad4000_time_spec ad7686_t_spec = { + .t_conv_ns = 1600, + .t_quiet2_ns = 0, +}; + +/* AD7690 */ +static const struct ad4000_time_spec ad7690_t_spec = { + .t_conv_ns = 2100, + .t_quiet2_ns = 0, +}; + +/* AD7942, AD7685, AD7687 */ +static const struct ad4000_time_spec ad7687_t_spec = { + .t_conv_ns = 3200, + .t_quiet2_ns = 0, +}; + +/* AD7691 */ +static const struct ad4000_time_spec ad7691_t_spec = { + .t_conv_ns = 3700, + .t_quiet2_ns = 0, +}; + +/* AD7988-1 */ +static const struct ad4000_time_spec ad7988_1_t_spec = { + .t_conv_ns = 9500, + .t_quiet2_ns = 0, +}; + struct ad4000_chip_info { const char *dev_name; - struct iio_chan_spec chan_spec; - struct iio_chan_spec reg_access_chan_spec; + struct iio_chan_spec chan_spec[2]; + struct iio_chan_spec reg_access_chan_spec[2]; + const struct ad4000_time_spec *time_spec; bool has_hardware_gain; }; static const struct ad4000_chip_info ad4000_chip_info = { .dev_name = "ad4000", - .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0), - .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1), + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1), + .time_spec = &ad4000_t_spec, }; static const struct ad4000_chip_info ad4001_chip_info = { .dev_name = "ad4001", - .chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1), + .time_spec = &ad4000_t_spec, }; static const struct ad4000_chip_info ad4002_chip_info = { .dev_name = "ad4002", - .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0), - .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1), + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1), + .time_spec = &ad4000_t_spec, }; static const struct ad4000_chip_info ad4003_chip_info = { .dev_name = "ad4003", - .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1), + .time_spec = &ad4000_t_spec, }; static const struct ad4000_chip_info ad4004_chip_info = { .dev_name = "ad4004", - .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0), - .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1), + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1), + .time_spec = &ad4000_t_spec, }; static const struct ad4000_chip_info ad4005_chip_info = { .dev_name = "ad4005", - .chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1), + .time_spec = &ad4000_t_spec, }; static const struct ad4000_chip_info ad4006_chip_info = { .dev_name = "ad4006", - .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0), - .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1), + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1), + .time_spec = &ad4000_t_spec, }; static const struct ad4000_chip_info ad4007_chip_info = { .dev_name = "ad4007", - .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1), + .time_spec = &ad4000_t_spec, }; static const struct ad4000_chip_info ad4008_chip_info = { .dev_name = "ad4008", - .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0), - .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1), + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1), + .time_spec = &ad4000_t_spec, }; static const struct ad4000_chip_info ad4010_chip_info = { .dev_name = "ad4010", - .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0), - .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1), + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1), + .time_spec = &ad4000_t_spec, }; static const struct ad4000_chip_info ad4011_chip_info = { .dev_name = "ad4011", - .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1), + .time_spec = &ad4000_t_spec, }; static const struct ad4000_chip_info ad4020_chip_info = { .dev_name = "ad4020", - .chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1), + .time_spec = &ad4020_t_spec, }; static const struct ad4000_chip_info ad4021_chip_info = { .dev_name = "ad4021", - .chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1), + .time_spec = &ad4020_t_spec, }; static const struct ad4000_chip_info ad4022_chip_info = { .dev_name = "ad4022", - .chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1), + .time_spec = &ad4020_t_spec, }; static const struct ad4000_chip_info adaq4001_chip_info = { .dev_name = "adaq4001", - .chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1), + .time_spec = &ad4000_t_spec, .has_hardware_gain = true, }; static const struct ad4000_chip_info adaq4003_chip_info = { .dev_name = "adaq4003", - .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1), + .time_spec = &ad4000_t_spec, .has_hardware_gain = true, }; +static const struct ad4000_chip_info ad7685_chip_info = { + .dev_name = "ad7685", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .time_spec = &ad7687_t_spec, +}; + +static const struct ad4000_chip_info ad7686_chip_info = { + .dev_name = "ad7686", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .time_spec = &ad7686_t_spec, +}; + +static const struct ad4000_chip_info ad7687_chip_info = { + .dev_name = "ad7687", + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .time_spec = &ad7687_t_spec, +}; + +static const struct ad4000_chip_info ad7688_chip_info = { + .dev_name = "ad7688", + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .time_spec = &ad7686_t_spec, +}; + +static const struct ad4000_chip_info ad7690_chip_info = { + .dev_name = "ad7690", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .time_spec = &ad7690_t_spec, +}; + +static const struct ad4000_chip_info ad7691_chip_info = { + .dev_name = "ad7691", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .time_spec = &ad7691_t_spec, +}; + +static const struct ad4000_chip_info ad7693_chip_info = { + .dev_name = "ad7693", + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .time_spec = &ad7686_t_spec, +}; + +static const struct ad4000_chip_info ad7942_chip_info = { + .dev_name = "ad7942", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 14, 0), + .time_spec = &ad7687_t_spec, +}; + +static const struct ad4000_chip_info ad7946_chip_info = { + .dev_name = "ad7946", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 14, 0), + .time_spec = &ad7686_t_spec, +}; + +static const struct ad4000_chip_info ad7980_chip_info = { + .dev_name = "ad7980", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .time_spec = &ad7980_t_spec, +}; + +static const struct ad4000_chip_info ad7982_chip_info = { + .dev_name = "ad7982", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .time_spec = &ad7980_t_spec, +}; + +static const struct ad4000_chip_info ad7983_chip_info = { + .dev_name = "ad7983", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .time_spec = &ad7983_t_spec, +}; + +static const struct ad4000_chip_info ad7984_chip_info = { + .dev_name = "ad7984", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .time_spec = &ad7983_t_spec, +}; + +static const struct ad4000_chip_info ad7988_1_chip_info = { + .dev_name = "ad7988-1", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .time_spec = &ad7988_1_t_spec, +}; + +static const struct ad4000_chip_info ad7988_5_chip_info = { + .dev_name = "ad7988-5", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .time_spec = &ad7686_t_spec, +}; + struct ad4000_state { struct spi_device *spi; struct gpio_desc *cnv_gpio; @@ -224,6 +403,7 @@ struct ad4000_state { bool span_comp; u16 gain_milli; int scale_tbl[AD4000_SCALE_OPTIONS][2]; + const struct ad4000_time_spec *time_spec; /* * DMA (thus cache coherency maintenance) requires the transfer buffers @@ -234,7 +414,7 @@ struct ad4000_state { __be16 sample_buf16; __be32 sample_buf32; } data; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan __aligned(IIO_DMA_MINALIGN); u8 tx_buf[2]; u8 rx_buf[2]; @@ -488,16 +668,15 @@ static const struct iio_info ad4000_info = { static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st, const struct iio_chan_spec *chan) { - unsigned int cnv_pulse_time = AD4000_TCONV_NS; struct spi_transfer *xfers = st->xfers; xfers[0].cs_change = 1; - xfers[0].cs_change_delay.value = cnv_pulse_time; + xfers[0].cs_change_delay.value = st->time_spec->t_conv_ns; xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; xfers[1].rx_buf = &st->scan.data; xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits); - xfers[1].delay.value = AD4000_TQUIET2_NS; + xfers[1].delay.value = st->time_spec->t_quiet2_ns; xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS; spi_message_init_with_transfers(&st->msg, st->xfers, 2); @@ -515,7 +694,6 @@ static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st, static int ad4000_prepare_4wire_mode_message(struct ad4000_state *st, const struct iio_chan_spec *chan) { - unsigned int cnv_to_sdi_time = AD4000_TCONV_NS; struct spi_transfer *xfers = st->xfers; /* @@ -523,7 +701,7 @@ static int ad4000_prepare_4wire_mode_message(struct ad4000_state *st, * going low. */ xfers[0].cs_off = 1; - xfers[0].delay.value = cnv_to_sdi_time; + xfers[0].delay.value = st->time_spec->t_conv_ns; xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; xfers[1].rx_buf = &st->scan.data; @@ -562,6 +740,7 @@ static int ad4000_probe(struct spi_device *spi) st = iio_priv(indio_dev); st->spi = spi; + st->time_spec = chip->time_spec; ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4000_power_supplies), ad4000_power_supplies); @@ -591,7 +770,7 @@ static int ad4000_probe(struct spi_device *spi) switch (st->sdi_pin) { case AD4000_SDI_MOSI: indio_dev->info = &ad4000_reg_access_info; - indio_dev->channels = &chip->reg_access_chan_spec; + indio_dev->channels = chip->reg_access_chan_spec; /* * In "3-wire mode", the ADC SDI line must be kept high when @@ -603,7 +782,7 @@ static int ad4000_probe(struct spi_device *spi) if (ret < 0) return ret; - ret = ad4000_prepare_3wire_mode_message(st, indio_dev->channels); + ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]); if (ret) return ret; @@ -614,16 +793,16 @@ static int ad4000_probe(struct spi_device *spi) break; case AD4000_SDI_VIO: indio_dev->info = &ad4000_info; - indio_dev->channels = &chip->chan_spec; - ret = ad4000_prepare_3wire_mode_message(st, indio_dev->channels); + indio_dev->channels = chip->chan_spec; + ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]); if (ret) return ret; break; case AD4000_SDI_CS: indio_dev->info = &ad4000_info; - indio_dev->channels = &chip->chan_spec; - ret = ad4000_prepare_4wire_mode_message(st, indio_dev->channels); + indio_dev->channels = chip->chan_spec; + ret = ad4000_prepare_4wire_mode_message(st, &indio_dev->channels[0]); if (ret) return ret; @@ -637,7 +816,7 @@ static int ad4000_probe(struct spi_device *spi) } indio_dev->name = chip->dev_name; - indio_dev->num_channels = 1; + indio_dev->num_channels = 2; ret = devm_mutex_init(dev, &st->lock); if (ret) @@ -658,7 +837,7 @@ static int ad4000_probe(struct spi_device *spi) } } - ad4000_fill_scale_tbl(st, indio_dev->channels); + ad4000_fill_scale_tbl(st, &indio_dev->channels[0]); ret = devm_iio_triggered_buffer_setup(dev, indio_dev, &iio_pollfunc_store_time, @@ -686,6 +865,21 @@ static const struct spi_device_id ad4000_id[] = { { "ad4022", (kernel_ulong_t)&ad4022_chip_info }, { "adaq4001", (kernel_ulong_t)&adaq4001_chip_info }, { "adaq4003", (kernel_ulong_t)&adaq4003_chip_info }, + { "ad7685", (kernel_ulong_t)&ad7685_chip_info }, + { "ad7686", (kernel_ulong_t)&ad7686_chip_info }, + { "ad7687", (kernel_ulong_t)&ad7687_chip_info }, + { "ad7688", (kernel_ulong_t)&ad7688_chip_info }, + { "ad7690", (kernel_ulong_t)&ad7690_chip_info }, + { "ad7691", (kernel_ulong_t)&ad7691_chip_info }, + { "ad7693", (kernel_ulong_t)&ad7693_chip_info }, + { "ad7942", (kernel_ulong_t)&ad7942_chip_info }, + { "ad7946", (kernel_ulong_t)&ad7946_chip_info }, + { "ad7980", (kernel_ulong_t)&ad7980_chip_info }, + { "ad7982", (kernel_ulong_t)&ad7982_chip_info }, + { "ad7983", (kernel_ulong_t)&ad7983_chip_info }, + { "ad7984", (kernel_ulong_t)&ad7984_chip_info }, + { "ad7988-1", (kernel_ulong_t)&ad7988_1_chip_info }, + { "ad7988-5", (kernel_ulong_t)&ad7988_5_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4000_id); @@ -707,6 +901,21 @@ static const struct of_device_id ad4000_of_match[] = { { .compatible = "adi,ad4022", .data = &ad4022_chip_info }, { .compatible = "adi,adaq4001", .data = &adaq4001_chip_info }, { .compatible = "adi,adaq4003", .data = &adaq4003_chip_info }, + { .compatible = "adi,ad7685", .data = &ad7685_chip_info }, + { .compatible = "adi,ad7686", .data = &ad7686_chip_info }, + { .compatible = "adi,ad7687", .data = &ad7687_chip_info }, + { .compatible = "adi,ad7688", .data = &ad7688_chip_info }, + { .compatible = "adi,ad7690", .data = &ad7690_chip_info }, + { .compatible = "adi,ad7691", .data = &ad7691_chip_info }, + { .compatible = "adi,ad7693", .data = &ad7693_chip_info }, + { .compatible = "adi,ad7942", .data = &ad7942_chip_info }, + { .compatible = "adi,ad7946", .data = &ad7946_chip_info }, + { .compatible = "adi,ad7980", .data = &ad7980_chip_info }, + { .compatible = "adi,ad7982", .data = &ad7982_chip_info }, + { .compatible = "adi,ad7983", .data = &ad7983_chip_info }, + { .compatible = "adi,ad7984", .data = &ad7984_chip_info }, + { .compatible = "adi,ad7988-1", .data = &ad7988_1_chip_info }, + { .compatible = "adi,ad7988-5", .data = &ad7988_5_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad4000_of_match); diff --git a/drivers/iio/adc/ad4695.c b/drivers/iio/adc/ad4695.c index 0146aed9069f..b79d135a5471 100644 --- a/drivers/iio/adc/ad4695.c +++ b/drivers/iio/adc/ad4695.c @@ -30,7 +30,7 @@ #include <linux/spi/spi.h> #include <linux/units.h> -#include <dt-bindings/iio/adi,ad4695.h> +#include <dt-bindings/iio/adc/adi,ad4695.h> /* AD4695 registers */ #define AD4695_REG_SPI_CONFIG_A 0x0000 diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 3d678c420cbf..6ae27cdd3250 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -95,6 +95,10 @@ #define AD7124_MAX_CONFIGS 8 #define AD7124_MAX_CHANNELS 16 +/* AD7124 input sources */ +#define AD7124_INPUT_TEMPSENSOR 16 +#define AD7124_INPUT_AVSS 17 + enum ad7124_ids { ID_AD7124_4, ID_AD7124_8, @@ -360,20 +364,21 @@ static int ad7124_find_free_config_slot(struct ad7124_state *st) return free_cfg_slot; } +/* Only called during probe, so dev_err_probe() can be used */ static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channel_config *cfg) { + struct device *dev = &st->sd.spi->dev; unsigned int refsel = cfg->refsel; switch (refsel) { case AD7124_REFIN1: case AD7124_REFIN2: case AD7124_AVDD_REF: - if (IS_ERR(st->vref[refsel])) { - dev_err(&st->sd.spi->dev, - "Error, trying to use external voltage reference without a %s regulator.\n", - ad7124_ref_names[refsel]); - return PTR_ERR(st->vref[refsel]); - } + if (IS_ERR(st->vref[refsel])) + return dev_err_probe(dev, PTR_ERR(st->vref[refsel]), + "Error, trying to use external voltage reference without a %s regulator.\n", + ad7124_ref_names[refsel]); + cfg->vref_mv = regulator_get_voltage(st->vref[refsel]); /* Conversion from uV to mV */ cfg->vref_mv /= 1000; @@ -384,8 +389,7 @@ static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channe st->adc_control |= AD7124_ADC_CTRL_REF_EN(1); return 0; default: - dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel); - return -EINVAL; + return dev_err_probe(dev, -EINVAL, "Invalid reference %d\n", refsel); } } @@ -571,6 +575,7 @@ static const struct ad_sigma_delta_info ad7124_sigma_delta_info = { .data_reg = AD7124_DATA, .num_slots = 8, .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 64, }; static int ad7124_read_raw(struct iio_dev *indio_dev, @@ -588,26 +593,59 @@ static int ad7124_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - mutex_lock(&st->cfgs_lock); + switch (chan->type) { + case IIO_VOLTAGE: + mutex_lock(&st->cfgs_lock); - idx = st->channels[chan->address].cfg.pga_bits; - *val = st->channels[chan->address].cfg.vref_mv; - if (st->channels[chan->address].cfg.bipolar) - *val2 = chan->scan_type.realbits - 1 + idx; - else - *val2 = chan->scan_type.realbits + idx; + idx = st->channels[chan->address].cfg.pga_bits; + *val = st->channels[chan->address].cfg.vref_mv; + if (st->channels[chan->address].cfg.bipolar) + *val2 = chan->scan_type.realbits - 1 + idx; + else + *val2 = chan->scan_type.realbits + idx; + + mutex_unlock(&st->cfgs_lock); + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_TEMP: + /* + * According to the data sheet + * Temperature (°C) + * = ((Conversion − 0x800000)/13584) − 272.5 + * = (Conversion − 0x800000 - 13584 * 272.5) / 13584 + * = (Conversion − 12090248) / 13584 + * So scale with 1000/13584 to yield °mC. Reduce by 8 to + * 125/1698. + */ + *val = 125; + *val2 = 1698; + return IIO_VAL_FRACTIONAL; + + default: + return -EINVAL; + } - mutex_unlock(&st->cfgs_lock); - return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_OFFSET: - mutex_lock(&st->cfgs_lock); - if (st->channels[chan->address].cfg.bipolar) - *val = -(1 << (chan->scan_type.realbits - 1)); - else - *val = 0; + switch (chan->type) { + case IIO_VOLTAGE: + mutex_lock(&st->cfgs_lock); + if (st->channels[chan->address].cfg.bipolar) + *val = -(1 << (chan->scan_type.realbits - 1)); + else + *val = 0; + + mutex_unlock(&st->cfgs_lock); + return IIO_VAL_INT; + + case IIO_TEMP: + /* see calculation above */ + *val = -12090248; + return IIO_VAL_INT; + + default: + return -EINVAL; + } - mutex_unlock(&st->cfgs_lock); - return IIO_VAL_INT; case IIO_CHAN_INFO_SAMP_FREQ: mutex_lock(&st->cfgs_lock); *val = st->channels[chan->address].cfg.odr; @@ -751,12 +789,14 @@ static const struct iio_info ad7124_info = { .attrs = &ad7124_attrs_group, }; +/* Only called during probe, so dev_err_probe() can be used */ static int ad7124_soft_reset(struct ad7124_state *st) { + struct device *dev = &st->sd.spi->dev; unsigned int readval, timeout; int ret; - ret = ad_sd_reset(&st->sd, 64); + ret = ad_sd_reset(&st->sd); if (ret < 0) return ret; @@ -765,7 +805,7 @@ static int ad7124_soft_reset(struct ad7124_state *st) do { ret = ad_sd_read_reg(&st->sd, AD7124_STATUS, 1, &readval); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, "Error reading status register\n"); if (!(readval & AD7124_STATUS_POR_FLAG_MSK)) return 0; @@ -774,39 +814,47 @@ static int ad7124_soft_reset(struct ad7124_state *st) usleep_range(100, 2000); } while (--timeout); - dev_err(&st->sd.spi->dev, "Soft reset failed\n"); - - return -EIO; + return dev_err_probe(dev, -EIO, "Soft reset failed\n"); } static int ad7124_check_chip_id(struct ad7124_state *st) { + struct device *dev = &st->sd.spi->dev; unsigned int readval, chip_id, silicon_rev; int ret; ret = ad_sd_read_reg(&st->sd, AD7124_ID, 1, &readval); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, "Failure to read ID register\n"); chip_id = AD7124_DEVICE_ID_GET(readval); silicon_rev = AD7124_SILICON_REV_GET(readval); - if (chip_id != st->chip_info->chip_id) { - dev_err(&st->sd.spi->dev, - "Chip ID mismatch: expected %u, got %u\n", - st->chip_info->chip_id, chip_id); - return -ENODEV; - } + if (chip_id != st->chip_info->chip_id) + return dev_err_probe(dev, -ENODEV, + "Chip ID mismatch: expected %u, got %u\n", + st->chip_info->chip_id, chip_id); - if (silicon_rev == 0) { - dev_err(&st->sd.spi->dev, - "Silicon revision empty. Chip may not be present\n"); - return -ENODEV; - } + if (silicon_rev == 0) + return dev_err_probe(dev, -ENODEV, + "Silicon revision empty. Chip may not be present\n"); return 0; } +/* + * Input specifiers 8 - 15 are explicitly reserved for ad7124-4 + * while they are fine for ad7124-8. Values above 31 don't fit + * into the register field and so are invalid for sure. + */ +static bool ad7124_valid_input_select(unsigned int ain, const struct ad7124_chip_info *info) +{ + if (ain >= info->num_inputs && ain < 16) + return false; + + return ain <= FIELD_MAX(AD7124_CHANNEL_AINM_MSK); +} + static int ad7124_parse_channel_config(struct iio_dev *indio_dev, struct device *dev) { @@ -815,11 +863,23 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev, struct ad7124_channel *channels; struct iio_chan_spec *chan; unsigned int ain[2], channel = 0, tmp; + unsigned int num_channels; int ret; - st->num_channels = device_get_child_node_count(dev); - if (!st->num_channels) - return dev_err_probe(dev, -ENODEV, "no channel children\n"); + num_channels = device_get_child_node_count(dev); + + /* + * The driver assigns each logical channel defined in the device tree + * statically one channel register. So only accept 16 such logical + * channels to not treat CONFIG_0 (i.e. the register following + * CHANNEL_15) as an additional channel register. The driver could be + * improved to lift this limitation. + */ + if (num_channels > AD7124_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, "Too many channels defined\n"); + + /* Add one for temperature */ + st->num_channels = min(num_channels + 1, AD7124_MAX_CHANNELS); chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels, sizeof(*chan), GFP_KERNEL); @@ -838,16 +898,23 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev, device_for_each_child_node_scoped(dev, child) { ret = fwnode_property_read_u32(child, "reg", &channel); if (ret) - return ret; + return dev_err_probe(dev, ret, + "Failed to parse reg property of %pfwP\n", child); - if (channel >= indio_dev->num_channels) + if (channel >= num_channels) return dev_err_probe(dev, -EINVAL, - "Channel index >= number of channels\n"); + "Channel index >= number of channels in %pfwP\n", child); ret = fwnode_property_read_u32_array(child, "diff-channels", ain, 2); if (ret) - return ret; + return dev_err_probe(dev, ret, + "Failed to parse diff-channels property of %pfwP\n", child); + + if (!ad7124_valid_input_select(ain[0], st->chip_info) || + !ad7124_valid_input_select(ain[1], st->chip_info)) + return dev_err_probe(dev, -EINVAL, + "diff-channels property of %pfwP contains invalid data\n", child); st->channels[channel].nr = channel; st->channels[channel].ain = AD7124_CHANNEL_AINP(ain[0]) | @@ -874,17 +941,49 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev, chan[channel].channel2 = ain[1]; } + if (num_channels < AD7124_MAX_CHANNELS) { + st->channels[num_channels] = (struct ad7124_channel) { + .nr = num_channels, + .ain = AD7124_CHANNEL_AINP(AD7124_INPUT_TEMPSENSOR) | + AD7124_CHANNEL_AINM(AD7124_INPUT_AVSS), + .cfg = { + .bipolar = true, + }, + }; + + chan[num_channels] = (struct iio_chan_spec) { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + /* + * You might find it strange that a bipolar + * measurement yields an unsigned value, but + * this matches the device's manual. + */ + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + .address = num_channels, + .scan_index = num_channels, + }; + } + return 0; } static int ad7124_setup(struct ad7124_state *st) { + struct device *dev = &st->sd.spi->dev; unsigned int fclk, power_mode; int i, ret; fclk = clk_get_rate(st->mclk); if (!fclk) - return -EINVAL; + return dev_err_probe(dev, -EINVAL, "Failed to get mclk rate\n"); /* The power mode changes the master clock frequency */ power_mode = ad7124_find_closest_match(ad7124_master_clk_freq_hz, @@ -893,7 +992,7 @@ static int ad7124_setup(struct ad7124_state *st) if (fclk != ad7124_master_clk_freq_hz[power_mode]) { ret = clk_set_rate(st->mclk, fclk); if (ret) - return ret; + return dev_err_probe(dev, ret, "Failed to set mclk rate\n"); } /* Set the power mode */ @@ -924,7 +1023,7 @@ static int ad7124_setup(struct ad7124_state *st) ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, "Failed to setup CONTROL register\n"); return ret; } @@ -937,13 +1036,14 @@ static void ad7124_reg_disable(void *r) static int ad7124_probe(struct spi_device *spi) { const struct ad7124_chip_info *info; + struct device *dev = &spi->dev; struct ad7124_state *st; struct iio_dev *indio_dev; int i, ret; info = spi_get_device_match_data(spi); if (!info) - return -ENODEV; + return dev_err_probe(dev, -ENODEV, "Failed to get match data\n"); indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (!indio_dev) @@ -978,17 +1078,17 @@ static int ad7124_probe(struct spi_device *spi) ret = regulator_enable(st->vref[i]); if (ret) - return ret; + return dev_err_probe(dev, ret, "Failed to enable regulator #%d\n", i); ret = devm_add_action_or_reset(&spi->dev, ad7124_reg_disable, st->vref[i]); if (ret) - return ret; + return dev_err_probe(dev, ret, "Failed to register disable handler for regulator #%d\n", i); } st->mclk = devm_clk_get_enabled(&spi->dev, "mclk"); if (IS_ERR(st->mclk)) - return PTR_ERR(st->mclk); + return dev_err_probe(dev, PTR_ERR(st->mclk), "Failed to get mclk\n"); ret = ad7124_soft_reset(st); if (ret < 0) @@ -1004,10 +1104,13 @@ static int ad7124_probe(struct spi_device *spi) ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, "Failed to setup triggers\n"); - return devm_iio_device_register(&spi->dev, indio_dev); + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to register iio device\n"); + return 0; } static const struct of_device_id ad7124_of_match[] = { diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 8b03c1e5567e..6c4ed10ae580 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -150,6 +150,11 @@ #define AD7173_FILTER_ODR0_MASK GENMASK(5, 0) #define AD7173_MAX_CONFIGS 8 +#define AD7173_MODE_CAL_INT_ZERO 0x4 /* Internal Zero-Scale Calibration */ +#define AD7173_MODE_CAL_INT_FULL 0x5 /* Internal Full-Scale Calibration */ +#define AD7173_MODE_CAL_SYS_ZERO 0x6 /* System Zero-Scale Calibration */ +#define AD7173_MODE_CAL_SYS_FULL 0x7 /* System Full-Scale Calibration */ + struct ad7173_device_info { const unsigned int *sinc5_data_rates; unsigned int num_sinc5_data_rates; @@ -175,6 +180,7 @@ struct ad7173_device_info { bool has_input_buf; bool has_int_ref; bool has_ref2; + bool has_internal_fs_calibration; bool higher_gpio_bits; u8 num_gpios; }; @@ -193,9 +199,9 @@ struct ad7173_channel_config { }; struct ad7173_channel { - unsigned int chan_reg; unsigned int ain; struct ad7173_channel_config cfg; + u8 syscalib_mode; }; struct ad7173_state { @@ -273,6 +279,7 @@ static const struct ad7173_device_info ad4111_device_info = { .has_input_buf = true, .has_current_inputs = true, .has_int_ref = true, + .has_internal_fs_calibration = true, .clock = 2 * HZ_PER_MHZ, .sinc5_data_rates = ad7173_sinc5_data_rates, .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), @@ -292,6 +299,7 @@ static const struct ad7173_device_info ad4112_device_info = { .has_input_buf = true, .has_current_inputs = true, .has_int_ref = true, + .has_internal_fs_calibration = true, .clock = 2 * HZ_PER_MHZ, .sinc5_data_rates = ad7173_sinc5_data_rates, .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), @@ -327,6 +335,7 @@ static const struct ad7173_device_info ad4114_device_info = { .has_temp = true, .has_input_buf = true, .has_int_ref = true, + .has_internal_fs_calibration = true, .clock = 2 * HZ_PER_MHZ, .sinc5_data_rates = ad7173_sinc5_data_rates, .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), @@ -344,6 +353,7 @@ static const struct ad7173_device_info ad4115_device_info = { .has_temp = true, .has_input_buf = true, .has_int_ref = true, + .has_internal_fs_calibration = true, .clock = 8 * HZ_PER_MHZ, .sinc5_data_rates = ad4115_sinc5_data_rates, .num_sinc5_data_rates = ARRAY_SIZE(ad4115_sinc5_data_rates), @@ -361,6 +371,7 @@ static const struct ad7173_device_info ad4116_device_info = { .has_temp = true, .has_input_buf = true, .has_int_ref = true, + .has_internal_fs_calibration = true, .clock = 4 * HZ_PER_MHZ, .sinc5_data_rates = ad4116_sinc5_data_rates, .num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates), @@ -506,6 +517,105 @@ static const struct regmap_config ad7173_regmap_config = { .read_flag_mask = BIT(6), }; +enum { + AD7173_SYSCALIB_ZERO_SCALE, + AD7173_SYSCALIB_FULL_SCALE, +}; + +static const char * const ad7173_syscalib_modes[] = { + [AD7173_SYSCALIB_ZERO_SCALE] = "zero_scale", + [AD7173_SYSCALIB_FULL_SCALE] = "full_scale", +}; + +static int ad7173_set_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad7173_state *st = iio_priv(indio_dev); + + st->channels[chan->channel].syscalib_mode = mode; + + return 0; +} + +static int ad7173_get_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7173_state *st = iio_priv(indio_dev); + + return st->channels[chan->channel].syscalib_mode; +} + +static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7173_state *st = iio_priv(indio_dev); + bool sys_calib; + int ret, mode; + + ret = kstrtobool(buf, &sys_calib); + if (ret) + return ret; + + mode = st->channels[chan->channel].syscalib_mode; + if (sys_calib) { + if (mode == AD7173_SYSCALIB_ZERO_SCALE) + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_ZERO, + chan->address); + else + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_FULL, + chan->address); + } + + return ret ? : len; +} + +static const struct iio_enum ad7173_syscalib_mode_enum = { + .items = ad7173_syscalib_modes, + .num_items = ARRAY_SIZE(ad7173_syscalib_modes), + .set = ad7173_set_syscalib_mode, + .get = ad7173_get_syscalib_mode +}; + +static const struct iio_chan_spec_ext_info ad7173_calibsys_ext_info[] = { + { + .name = "sys_calibration", + .write = ad7173_write_syscalib, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("sys_calibration_mode", IIO_SEPARATE, + &ad7173_syscalib_mode_enum), + IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE, + &ad7173_syscalib_mode_enum), + { } +}; + +static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_dev) +{ + int ret; + int i; + + for (i = 0; i < st->num_channels; i++) { + if (indio_dev->channels[i].type != IIO_VOLTAGE) + continue; + + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, st->channels[i].ain); + if (ret < 0) + return ret; + + if (st->info->has_internal_fs_calibration) { + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL, + st->channels[i].ain); + if (ret < 0) + return ret; + } + } + + return 0; +} + static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base, unsigned int offset, unsigned int *reg, unsigned int *mask) @@ -765,6 +875,7 @@ static const struct ad_sigma_delta_info ad7173_sigma_delta_info = { .read_mask = BIT(6), .status_ch_mask = GENMASK(3, 0), .data_reg = AD7173_REG_DATA, + .num_resetclks = 64, }; static int ad7173_setup(struct iio_dev *indio_dev) @@ -802,6 +913,10 @@ static int ad7173_setup(struct iio_dev *indio_dev) if (!st->config_cnts) return -ENOMEM; + ret = ad7173_calibrate_all(st, indio_dev); + if (ret) + return ret; + /* All channels are enabled by default after a reset */ return ad7173_disable_all(&st->sd); } @@ -1024,6 +1139,7 @@ static const struct iio_chan_spec ad7173_channel_template = { .storagebits = 32, .endianness = IIO_BE, }, + .ext_info = ad7173_calibsys_ext_info, }; static const struct iio_chan_spec ad7173_temp_iio_channel_template = { @@ -1317,7 +1433,6 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan->address = chan_index; chan->scan_index = chan_index; chan->channel = ain[0]; - chan_st_priv->chan_reg = chan_index; chan_st_priv->cfg.input_buf = st->info->has_input_buf; chan_st_priv->cfg.odr = 0; diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 1c87db0e0460..e96a5ae92375 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -361,6 +361,7 @@ static const struct ad_sigma_delta_info ad7192_sigma_delta_info = { .status_ch_mask = GENMASK(3, 0), .num_slots = 4, .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 40, }; static const struct ad_sigma_delta_info ad7194_sigma_delta_info = { @@ -373,6 +374,7 @@ static const struct ad_sigma_delta_info ad7194_sigma_delta_info = { .read_mask = BIT(6), .status_ch_mask = GENMASK(3, 0), .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 40, }; static const struct ad_sd_calib_data ad7192_calib_arr[8] = { @@ -565,7 +567,7 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev) int i, ret, id; /* reset the serial interface */ - ret = ad_sd_reset(&st->sd, 48); + ret = ad_sd_reset(&st->sd); if (ret < 0) return ret; usleep_range(500, 1000); /* Wait for at least 500us */ diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index e35d55d03d86..d8e3c7a43678 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -175,17 +175,17 @@ static const struct iio_chan_spec ad7616_channels[] = { AD7606_CHANNEL(15, 16), }; -static int ad7606c_18bit_chan_scale_setup(struct ad7606_state *st, +static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch); -static int ad7606c_16bit_chan_scale_setup(struct ad7606_state *st, +static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch); -static int ad7606_16bit_chan_scale_setup(struct ad7606_state *st, +static int ad7606_16bit_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch); -static int ad7607_chan_scale_setup(struct ad7606_state *st, +static int ad7607_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch); -static int ad7608_chan_scale_setup(struct ad7606_state *st, +static int ad7608_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch); -static int ad7609_chan_scale_setup(struct ad7606_state *st, +static int ad7609_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch); const struct ad7606_chip_info ad7605_4_info = { @@ -323,9 +323,10 @@ int ad7606_reset(struct ad7606_state *st) } EXPORT_SYMBOL_NS_GPL(ad7606_reset, "IIO_AD7606"); -static int ad7606_16bit_chan_scale_setup(struct ad7606_state *st, +static int ad7606_16bit_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch) { + struct ad7606_state *st = iio_priv(indio_dev); struct ad7606_chan_scale *cs = &st->chan_scales[ch]; if (!st->sw_mode_en) { @@ -345,10 +346,12 @@ static int ad7606_16bit_chan_scale_setup(struct ad7606_state *st, return 0; } -static int ad7606_get_chan_config(struct ad7606_state *st, int ch, +static int ad7606_get_chan_config(struct iio_dev *indio_dev, int ch, bool *bipolar, bool *differential) { - unsigned int num_channels = st->chip_info->num_channels - 1; + struct ad7606_state *st = iio_priv(indio_dev); + unsigned int num_channels = st->chip_info->num_adc_channels; + unsigned int offset = indio_dev->num_channels - st->chip_info->num_adc_channels; struct device *dev = st->dev; int ret; @@ -364,7 +367,7 @@ static int ad7606_get_chan_config(struct ad7606_state *st, int ch, continue; /* channel number (here) is from 1 to num_channels */ - if (reg == 0 || reg > num_channels) { + if (reg < offset || reg > num_channels) { dev_warn(dev, "Invalid channel number (ignoring): %d\n", reg); continue; @@ -399,9 +402,10 @@ static int ad7606_get_chan_config(struct ad7606_state *st, int ch, return 0; } -static int ad7606c_18bit_chan_scale_setup(struct ad7606_state *st, +static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch) { + struct ad7606_state *st = iio_priv(indio_dev); struct ad7606_chan_scale *cs = &st->chan_scales[ch]; bool bipolar, differential; int ret; @@ -413,7 +417,7 @@ static int ad7606c_18bit_chan_scale_setup(struct ad7606_state *st, return 0; } - ret = ad7606_get_chan_config(st, ch, &bipolar, &differential); + ret = ad7606_get_chan_config(indio_dev, ch, &bipolar, &differential); if (ret) return ret; @@ -455,9 +459,10 @@ static int ad7606c_18bit_chan_scale_setup(struct ad7606_state *st, return 0; } -static int ad7606c_16bit_chan_scale_setup(struct ad7606_state *st, +static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch) { + struct ad7606_state *st = iio_priv(indio_dev); struct ad7606_chan_scale *cs = &st->chan_scales[ch]; bool bipolar, differential; int ret; @@ -469,7 +474,7 @@ static int ad7606c_16bit_chan_scale_setup(struct ad7606_state *st, return 0; } - ret = ad7606_get_chan_config(st, ch, &bipolar, &differential); + ret = ad7606_get_chan_config(indio_dev, ch, &bipolar, &differential); if (ret) return ret; @@ -512,9 +517,10 @@ static int ad7606c_16bit_chan_scale_setup(struct ad7606_state *st, return 0; } -static int ad7607_chan_scale_setup(struct ad7606_state *st, +static int ad7607_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch) { + struct ad7606_state *st = iio_priv(indio_dev); struct ad7606_chan_scale *cs = &st->chan_scales[ch]; cs->range = 0; @@ -523,9 +529,10 @@ static int ad7607_chan_scale_setup(struct ad7606_state *st, return 0; } -static int ad7608_chan_scale_setup(struct ad7606_state *st, +static int ad7608_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch) { + struct ad7606_state *st = iio_priv(indio_dev); struct ad7606_chan_scale *cs = &st->chan_scales[ch]; cs->range = 0; @@ -534,9 +541,10 @@ static int ad7608_chan_scale_setup(struct ad7606_state *st, return 0; } -static int ad7609_chan_scale_setup(struct ad7606_state *st, +static int ad7609_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch) { + struct ad7606_state *st = iio_priv(indio_dev); struct ad7606_chan_scale *cs = &st->chan_scales[ch]; cs->range = 0; @@ -1146,8 +1154,8 @@ static int ad7606_sw_mode_setup(struct iio_dev *indio_dev) static int ad7606_chan_scales_setup(struct iio_dev *indio_dev) { - unsigned int num_channels = indio_dev->num_channels - 1; struct ad7606_state *st = iio_priv(indio_dev); + unsigned int offset = indio_dev->num_channels - st->chip_info->num_adc_channels; struct iio_chan_spec *chans; size_t size; int ch, ret; @@ -1161,8 +1169,8 @@ static int ad7606_chan_scales_setup(struct iio_dev *indio_dev) memcpy(chans, indio_dev->channels, size); indio_dev->channels = chans; - for (ch = 0; ch < num_channels; ch++) { - ret = st->chip_info->scale_setup_cb(st, &chans[ch + 1], ch); + for (ch = 0; ch < st->chip_info->num_adc_channels; ch++) { + ret = st->chip_info->scale_setup_cb(indio_dev, &chans[ch + offset], ch); if (ret) return ret; } diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h index 998814a92b82..8778ffe515b3 100644 --- a/drivers/iio/adc/ad7606.h +++ b/drivers/iio/adc/ad7606.h @@ -69,7 +69,7 @@ struct ad7606_state; -typedef int (*ad7606_scale_setup_cb_t)(struct ad7606_state *st, +typedef int (*ad7606_scale_setup_cb_t)(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch); /** diff --git a/drivers/iio/adc/ad7625.c b/drivers/iio/adc/ad7625.c index aefe3bf75c91..afa9bf4ddf3c 100644 --- a/drivers/iio/adc/ad7625.c +++ b/drivers/iio/adc/ad7625.c @@ -477,12 +477,12 @@ static int devm_ad7625_pwm_get(struct device *dev, ref_clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(ref_clk)) return dev_err_probe(dev, PTR_ERR(ref_clk), - "failed to get ref_clk"); + "failed to get ref_clk\n"); ref_clk_rate_hz = clk_get_rate(ref_clk); if (!ref_clk_rate_hz) return dev_err_probe(dev, -EINVAL, - "failed to get ref_clk rate"); + "failed to get ref_clk rate\n"); st->ref_clk_rate_hz = ref_clk_rate_hz; @@ -533,7 +533,7 @@ static int devm_ad7625_regulator_setup(struct device *dev, if (!st->info->has_internal_vref && !st->have_refin && !ref_mv) return dev_err_probe(dev, -EINVAL, - "Need either REFIN or REF"); + "Need either REFIN or REF\n"); if (st->have_refin && ref_mv) return dev_err_probe(dev, -EINVAL, @@ -623,7 +623,7 @@ static int ad7625_probe(struct platform_device *pdev) st->back = devm_iio_backend_get(dev, NULL); if (IS_ERR(st->back)) return dev_err_probe(dev, PTR_ERR(st->back), - "failed to get IIO backend"); + "failed to get IIO backend\n"); ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); if (ret) diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index e1bf13fe2cd7..76118fe22db8 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -254,6 +254,7 @@ static const struct ad_sigma_delta_info ad7791_sigma_delta_info = { .addr_shift = 4, .read_mask = BIT(3), .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 32, }; static int ad7791_read_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index d55c71566707..1b50d9643a63 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -206,6 +206,7 @@ static const struct ad_sigma_delta_info ad7793_sigma_delta_info = { .addr_shift = 3, .read_mask = BIT(6), .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 32, }; static const struct ad_sd_calib_data ad7793_calib_arr[6] = { @@ -265,7 +266,7 @@ static int ad7793_setup(struct iio_dev *indio_dev, return ret; /* reset the serial interface */ - ret = ad_sd_reset(&st->sd, 32); + ret = ad_sd_reset(&st->sd); if (ret < 0) goto out; usleep_range(500, 2000); /* Wait for at least 500us */ diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c index a5aea4e9f1a7..0ec9cda10f5f 100644 --- a/drivers/iio/adc/ad7944.c +++ b/drivers/iio/adc/ad7944.c @@ -75,7 +75,7 @@ struct ad7944_adc { u16 u16; u32 u32; } raw; - u64 timestamp __aligned(8); + aligned_s64 timestamp; } sample __aligned(IIO_DMA_MINALIGN); }; diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 3fd200b34161..d5d81581ab34 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -29,8 +29,11 @@ #define AD_SD_COMM_CHAN_MASK 0x3 #define AD_SD_REG_COMM 0x00 +#define AD_SD_REG_STATUS 0x00 #define AD_SD_REG_DATA 0x03 +#define AD_SD_REG_STATUS_RDY 0x80 + /** * ad_sd_set_comm() - Set communications register * @@ -109,7 +112,7 @@ static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, }, { .rx_buf = val, .len = size, - .cs_change = sigma_delta->bus_locked, + .cs_change = sigma_delta->keep_cs_asserted, }, }; struct spi_message m; @@ -178,13 +181,12 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, "IIO_AD_SIGMA_DELTA"); * ad_sd_reset() - Reset the serial interface * * @sigma_delta: The sigma delta device - * @reset_length: Number of SCLKs with DIN = 1 * * Returns 0 on success, an error code otherwise. **/ -int ad_sd_reset(struct ad_sigma_delta *sigma_delta, - unsigned int reset_length) +int ad_sd_reset(struct ad_sigma_delta *sigma_delta) { + unsigned int reset_length = sigma_delta->info->num_resetclks; uint8_t *buf; unsigned int size; int ret; @@ -202,6 +204,107 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta, } EXPORT_SYMBOL_NS_GPL(ad_sd_reset, "IIO_AD_SIGMA_DELTA"); +static bool ad_sd_disable_irq(struct ad_sigma_delta *sigma_delta) +{ + guard(spinlock_irqsave)(&sigma_delta->irq_lock); + + /* It's already off, return false to indicate nothing was changed */ + if (sigma_delta->irq_dis) + return false; + + sigma_delta->irq_dis = true; + disable_irq_nosync(sigma_delta->irq_line); + return true; +} + +static void ad_sd_enable_irq(struct ad_sigma_delta *sigma_delta) +{ + guard(spinlock_irqsave)(&sigma_delta->irq_lock); + + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->irq_line); +} + +#define AD_SD_CLEAR_DATA_BUFLEN 9 + +/* Called with `sigma_delta->bus_locked == true` only. */ +static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta) +{ + bool pending_event; + unsigned int data_read_len = BITS_TO_BYTES(sigma_delta->info->num_resetclks); + u8 *data; + struct spi_transfer t[] = { + { + .len = 1, + }, { + .len = data_read_len, + } + }; + struct spi_message m; + int ret; + + /* + * Read RÌ…DÌ…YÌ… pin (if possible) or status register to check if there is an + * old event. + */ + if (sigma_delta->rdy_gpiod) { + pending_event = gpiod_get_value(sigma_delta->rdy_gpiod); + } else { + unsigned int status_reg; + + ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_STATUS, 1, &status_reg); + if (ret) + return ret; + + pending_event = !(status_reg & AD_SD_REG_STATUS_RDY); + } + + if (!pending_event) + return 0; + + /* + * In general the size of the data register is unknown. It varies from + * device to device, might be one byte longer if CONTROL.DATA_STATUS is + * set and even varies on some devices depending on which input is + * selected. So send one byte to start reading the data register and + * then just clock for some bytes with DIN (aka MOSI) high to not + * confuse the register access state machine after the data register was + * completely read. Note however that the sequence length must be + * shorter than the reset procedure. + */ + + data = kzalloc(data_read_len + 1, GFP_KERNEL); + if (!data) + return -ENOMEM; + + spi_message_init(&m); + if (sigma_delta->info->has_registers) { + unsigned int data_reg = sigma_delta->info->data_reg ?: AD_SD_REG_DATA; + + data[0] = data_reg << sigma_delta->info->addr_shift; + data[0] |= sigma_delta->info->read_mask; + data[0] |= sigma_delta->comm; + t[0].tx_buf = data; + spi_message_add_tail(&t[0], &m); + } + + /* + * The first transferred byte is part of the real data register, + * so this doesn't need to be 0xff. In the remaining + * `data_read_len - 1` bytes are less than $num_resetclks ones. + */ + t[1].tx_buf = data + 1; + data[1] = 0x00; + memset(data + 2, 0xff, data_read_len - 1); + spi_message_add_tail(&t[1], &m); + + ret = spi_sync_locked(sigma_delta->spi, &m); + + kfree(data); + + return ret; +} + int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, unsigned int mode, unsigned int channel) { @@ -217,16 +320,18 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, sigma_delta->keep_cs_asserted = true; reinit_completion(&sigma_delta->completion); + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto out; + ret = ad_sigma_delta_set_mode(sigma_delta, mode); if (ret < 0) goto out; - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); time_left = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ); if (time_left == 0) { - sigma_delta->irq_dis = true; - disable_irq_nosync(sigma_delta->irq_line); + ad_sd_disable_irq(sigma_delta); ret = -EIO; } else { ret = 0; @@ -292,10 +397,13 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, sigma_delta->keep_cs_asserted = true; reinit_completion(&sigma_delta->completion); + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto out_unlock; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE); - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); ret = wait_for_completion_interruptible_timeout( &sigma_delta->completion, HZ); @@ -314,14 +422,13 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, &raw_sample); out: - if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->irq_line); - sigma_delta->irq_dis = true; - } + ad_sd_disable_irq(sigma_delta); - sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); ad_sigma_delta_disable_one(sigma_delta, chan->address); + +out_unlock: + sigma_delta->keep_cs_asserted = false; sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->controller); iio_device_release_direct_mode(indio_dev); @@ -392,12 +499,15 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) sigma_delta->bus_locked = true; sigma_delta->keep_cs_asserted = true; + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto err_unlock; + ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS); if (ret) goto err_unlock; - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); return 0; @@ -414,10 +524,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) reinit_completion(&sigma_delta->completion); wait_for_completion_timeout(&sigma_delta->completion, HZ); - if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->irq_line); - sigma_delta->irq_dis = true; - } + ad_sd_disable_irq(sigma_delta); sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); @@ -516,8 +623,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) irq_handled: iio_trigger_notify_done(indio_dev->trig); - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); return IRQ_HANDLED; } @@ -539,12 +645,31 @@ static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private) { struct ad_sigma_delta *sigma_delta = private; - complete(&sigma_delta->completion); - disable_irq_nosync(irq); - sigma_delta->irq_dis = true; - iio_trigger_poll(sigma_delta->trig); + /* + * AD7124 and a few others use the same physical line for interrupt + * reporting (RÌ…DÌ…YÌ…) and MISO. + * As MISO toggles when reading a register, this likely results in a + * pending interrupt. This has two consequences: a) The irq might + * trigger immediately after it's enabled even though the conversion + * isn't done yet; and b) checking the STATUS register's RÌ…DÌ…YÌ… flag is + * off-limits as reading that would trigger another irq event. + * + * So read the MOSI line as GPIO (if available) and only trigger the irq + * if the line is active. Without such a GPIO assume this is a valid + * interrupt. + * + * Also as disable_irq_nosync() is used to disable the irq, only act if + * the irq wasn't disabled before. + */ + if ((!sigma_delta->rdy_gpiod || gpiod_get_value(sigma_delta->rdy_gpiod)) && + ad_sd_disable_irq(sigma_delta)) { + complete(&sigma_delta->completion); + iio_trigger_poll(sigma_delta->trig); - return IRQ_HANDLED; + return IRQ_HANDLED; + } + + return IRQ_NONE; } /** @@ -674,11 +799,24 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, } } + spin_lock_init(&sigma_delta->irq_lock); + if (info->irq_line) sigma_delta->irq_line = info->irq_line; else sigma_delta->irq_line = spi->irq; + sigma_delta->rdy_gpiod = devm_gpiod_get_optional(&spi->dev, "rdy", GPIOD_IN); + if (IS_ERR(sigma_delta->rdy_gpiod)) + return dev_err_probe(&spi->dev, PTR_ERR(sigma_delta->rdy_gpiod), + "Failed to find rdy gpio\n"); + + if (sigma_delta->rdy_gpiod && !sigma_delta->irq_line) { + sigma_delta->irq_line = gpiod_to_irq(sigma_delta->rdy_gpiod); + if (sigma_delta->irq_line < 0) + return sigma_delta->irq_line; + } + iio_device_set_drvdata(indio_dev, sigma_delta); return 0; diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c index 30328626d9be..221a5fdc1eaa 100644 --- a/drivers/iio/adc/dln2-adc.c +++ b/drivers/iio/adc/dln2-adc.c @@ -66,8 +66,6 @@ struct dln2_adc { /* Demux table */ unsigned int demux_count; struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS]; - /* Precomputed timestamp padding offset and length */ - unsigned int ts_pad_offset, ts_pad_length; }; struct dln2_adc_port_chan { @@ -111,8 +109,6 @@ static void dln2_adc_update_demux(struct dln2_adc *dln2) if (iio_get_masklength(indio_dev) && (*indio_dev->active_scan_mask & 0xff) == 0xff) { dln2_adc_add_demux(dln2, 0, 0, 16); - dln2->ts_pad_offset = 0; - dln2->ts_pad_length = 0; return; } @@ -127,16 +123,6 @@ static void dln2_adc_update_demux(struct dln2_adc *dln2) out_loc += 2; in_loc += 2; } - - if (indio_dev->scan_timestamp) { - size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1; - - dln2->ts_pad_offset = out_loc; - dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc; - } else { - dln2->ts_pad_offset = 0; - dln2->ts_pad_length = 0; - } } static int dln2_adc_get_chan_count(struct dln2_adc *dln2) @@ -494,6 +480,8 @@ static irqreturn_t dln2_adc_trigger_h(int irq, void *p) if (ret < 0) goto done; + memset(&data, 0, sizeof(data)); + /* Demux operation */ for (i = 0; i < dln2->demux_count; ++i) { t = &dln2->demux[i]; @@ -501,11 +489,6 @@ static irqreturn_t dln2_adc_trigger_h(int irq, void *p) (void *)dev_data.values + t->from, t->length); } - /* Zero padding space between values and timestamp */ - if (dln2->ts_pad_length) - memset((void *)data.values + dln2->ts_pad_offset, - 0, dln2->ts_pad_length); - iio_push_to_buffers_with_timestamp(indio_dev, &data, iio_get_time_ns(indio_dev)); diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 48c95e12e791..40d14faa71c5 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -150,7 +150,7 @@ struct ina2xx_chip_info { /* data buffer needs space for channel data and timestamp */ struct { u16 chan[4]; - u64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c index 3d0a7d0eb7ee..565ca2e21c0c 100644 --- a/drivers/iio/adc/max1118.c +++ b/drivers/iio/adc/max1118.c @@ -39,7 +39,7 @@ struct max1118 { /* Ensure natural alignment of buffer elements */ struct { u8 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; u8 data __aligned(IIO_DMA_MINALIGN); diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index f0dc4b460903..76abafd47404 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -143,7 +143,7 @@ struct max11410_state { int irq; struct { u32 data __aligned(IIO_DMA_MINALIGN); - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 9a0baea08ab6..e8d731bc34e0 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -161,6 +161,7 @@ struct max1363_chip_info { * @vref_uv: Actual (external or internal) reference voltage * @send: function used to send data to the chip * @recv: function used to receive data from the chip + * @data: buffer to store channel data and timestamp */ struct max1363_state { struct i2c_client *client; @@ -186,6 +187,10 @@ struct max1363_state { const char *buf, int count); int (*recv)(const struct i2c_client *client, char *buf, int count); + struct { + u8 buf[MAX1363_MAX_CHANNELS * 2]; + aligned_s64 ts; + } data; }; #define MAX1363_MODE_SINGLE(_num, _mask) { \ @@ -1462,22 +1467,10 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct max1363_state *st = iio_priv(indio_dev); - __u8 *rxbuf; int b_sent; - size_t d_size; unsigned long numvals = bitmap_weight(st->current_mode->modemask, MAX1363_MAX_CHANNELS); - /* Ensure the timestamp is 8 byte aligned */ - if (st->chip_info->bits != 8) - d_size = numvals*2; - else - d_size = numvals; - if (indio_dev->scan_timestamp) { - d_size += sizeof(s64); - if (d_size % sizeof(s64)) - d_size += sizeof(s64) - (d_size % sizeof(s64)); - } /* Monitor mode prevents reading. Whilst not currently implemented * might as well have this test in here in the meantime as it does * no harm. @@ -1485,21 +1478,16 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p) if (numvals == 0) goto done; - rxbuf = kmalloc(d_size, GFP_KERNEL); - if (rxbuf == NULL) - goto done; if (st->chip_info->bits != 8) - b_sent = st->recv(st->client, rxbuf, numvals * 2); + b_sent = st->recv(st->client, st->data.buf, numvals * 2); else - b_sent = st->recv(st->client, rxbuf, numvals); + b_sent = st->recv(st->client, st->data.buf, numvals); if (b_sent < 0) - goto done_free; + goto done; - iio_push_to_buffers_with_timestamp(indio_dev, rxbuf, + iio_push_to_buffers_with_timestamp(indio_dev, &st->data, iio_get_time_ns(indio_dev)); -done_free: - kfree(rxbuf); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index b097f04172c8..6748b44d568d 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -122,7 +122,7 @@ struct mcp3911 { const struct mcp3911_chip_info *chip; struct { u32 channels[MCP39XX_MAX_NUM_CHANNELS]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; u8 tx_buf __aligned(IIO_DMA_MINALIGN); diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 2d475b43e717..997def4a4d2f 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -155,10 +155,10 @@ */ #define MESON_SAR_ADC_REG11 0x2c #define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13) - #define MESON_SAR_ADC_REG11_CMV_SEL BIT(6) - #define MESON_SAR_ADC_REG11_VREF_VOLTAGE BIT(5) - #define MESON_SAR_ADC_REG11_EOC BIT(1) - #define MESON_SAR_ADC_REG11_VREF_SEL BIT(0) + #define MESON_SAR_ADC_REG11_CMV_SEL BIT(6) + #define MESON_SAR_ADC_REG11_VREF_VOLTAGE BIT(5) + #define MESON_SAR_ADC_REG11_EOC BIT(1) + #define MESON_SAR_ADC_REG11_VREF_SEL BIT(0) #define MESON_SAR_ADC_REG13 0x34 #define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8) @@ -315,19 +315,17 @@ static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = { struct meson_sar_adc_param { bool has_bl30_integration; unsigned long clock_rate; - u32 bandgap_reg; unsigned int resolution; const struct regmap_config *regmap_config; u8 temperature_trimming_bits; unsigned int temperature_multiplier; unsigned int temperature_divider; u8 disable_ring_counter; - bool has_reg11; bool has_vref_select; u8 vref_select; u8 cmv_select; u8 adc_eoc; - enum meson_sar_adc_vref_sel vref_volatge; + enum meson_sar_adc_vref_sel vref_voltage; }; struct meson_sar_adc_data { @@ -976,7 +974,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN, regval); - if (priv->param->has_reg11) { + if (priv->param->regmap_config->max_register >= MESON_SAR_ADC_REG11) { regval = FIELD_PREP(MESON_SAR_ADC_REG11_EOC, priv->param->adc_eoc); regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, MESON_SAR_ADC_REG11_EOC, regval); @@ -989,7 +987,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) } regval = FIELD_PREP(MESON_SAR_ADC_REG11_VREF_VOLTAGE, - priv->param->vref_volatge); + priv->param->vref_voltage); regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, MESON_SAR_ADC_REG11_VREF_VOLTAGE, regval); @@ -1013,16 +1011,15 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) static void meson_sar_adc_set_bandgap(struct iio_dev *indio_dev, bool on_off) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); - const struct meson_sar_adc_param *param = priv->param; - u32 enable_mask; - if (param->bandgap_reg == MESON_SAR_ADC_REG11) - enable_mask = MESON_SAR_ADC_REG11_BANDGAP_EN; + if (priv->param->regmap_config->max_register >= MESON_SAR_ADC_REG11) + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_BANDGAP_EN, + on_off ? MESON_SAR_ADC_REG11_BANDGAP_EN : 0); else - enable_mask = MESON_SAR_ADC_DELTA_10_TS_VBG_EN; - - regmap_update_bits(priv->regmap, param->bandgap_reg, enable_mask, - on_off ? enable_mask : 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_VBG_EN, + on_off ? MESON_SAR_ADC_DELTA_10_TS_VBG_EN : 0); } static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev) @@ -1186,7 +1183,6 @@ static const struct iio_info meson_sar_adc_iio_info = { static const struct meson_sar_adc_param meson_sar_adc_meson8_param = { .has_bl30_integration = false, .clock_rate = 1150000, - .bandgap_reg = MESON_SAR_ADC_DELTA_10, .regmap_config = &meson_sar_adc_regmap_config_meson8, .resolution = 10, .temperature_trimming_bits = 4, @@ -1197,7 +1193,6 @@ static const struct meson_sar_adc_param meson_sar_adc_meson8_param = { static const struct meson_sar_adc_param meson_sar_adc_meson8b_param = { .has_bl30_integration = false, .clock_rate = 1150000, - .bandgap_reg = MESON_SAR_ADC_DELTA_10, .regmap_config = &meson_sar_adc_regmap_config_meson8, .resolution = 10, .temperature_trimming_bits = 5, @@ -1208,35 +1203,29 @@ static const struct meson_sar_adc_param meson_sar_adc_meson8b_param = { static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = { .has_bl30_integration = true, .clock_rate = 1200000, - .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 10, - .has_reg11 = true, - .vref_volatge = 1, + .vref_voltage = 1, .cmv_select = 1, }; static const struct meson_sar_adc_param meson_sar_adc_gxl_param = { .has_bl30_integration = true, .clock_rate = 1200000, - .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 12, .disable_ring_counter = 1, - .has_reg11 = true, - .vref_volatge = 1, + .vref_voltage = 1, .cmv_select = 1, }; static const struct meson_sar_adc_param meson_sar_adc_axg_param = { .has_bl30_integration = true, .clock_rate = 1200000, - .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 12, .disable_ring_counter = 1, - .has_reg11 = true, - .vref_volatge = 1, + .vref_voltage = 1, .has_vref_select = true, .vref_select = VREF_VDDA, .cmv_select = 1, @@ -1245,11 +1234,9 @@ static const struct meson_sar_adc_param meson_sar_adc_axg_param = { static const struct meson_sar_adc_param meson_sar_adc_g12a_param = { .has_bl30_integration = false, .clock_rate = 1200000, - .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 12, .disable_ring_counter = 1, - .has_reg11 = true, .adc_eoc = 1, .has_vref_select = true, .vref_select = VREF_VDDA, diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c index b0f6727cfe38..90f61c47b1c4 100644 --- a/drivers/iio/adc/pac1921.c +++ b/drivers/iio/adc/pac1921.c @@ -12,6 +12,7 @@ #include <linux/iio/iio.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> +#include <linux/limits.h> #include <linux/regmap.h> #include <linux/units.h> @@ -67,6 +68,14 @@ enum pac1921_mxsl { #define PAC1921_DEFAULT_DI_GAIN 0 /* 2^(value): 1x gain (HW default) */ #define PAC1921_DEFAULT_NUM_SAMPLES 0 /* 2^(value): 1 sample (HW default) */ +#define PAC1921_ACPI_GET_uOHMS_VALS 0 +#define PAC1921_ACPI_GET_LABEL 1 + +/* f7bb9932-86ee-4516-a236-7a7a742e55cb */ +static const guid_t pac1921_guid = + GUID_INIT(0xf7bb9932, 0x86ee, 0x4516, 0xa2, + 0x36, 0x7a, 0x7a, 0x74, 0x2e, 0x55, 0xcb); + /* * Pre-computed scale factors for BUS voltage * format: IIO_VAL_INT_PLUS_NANO @@ -200,7 +209,7 @@ struct pac1921_priv { struct { u16 chan[PAC1921_NUM_MEAS_CHANS]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; @@ -782,7 +791,7 @@ static ssize_t pac1921_write_shunt_resistor(struct iio_dev *indio_dev, const char *buf, size_t len) { struct pac1921_priv *priv = iio_priv(indio_dev); - u64 rshunt_uohm; + u32 rshunt_uohm; int val, val_fract; int ret; @@ -793,10 +802,17 @@ static ssize_t pac1921_write_shunt_resistor(struct iio_dev *indio_dev, if (ret) return ret; - rshunt_uohm = val * MICRO + val_fract; - if (rshunt_uohm == 0 || rshunt_uohm > INT_MAX) + /* + * This check validates the shunt is not zero and does not surpass + * INT_MAX. The check is done before calculating in order to avoid + * val * MICRO overflowing. + */ + if ((!val && !val_fract) || val > INT_MAX / MICRO || + (val == INT_MAX / MICRO && val_fract > INT_MAX % MICRO)) return -EINVAL; + rshunt_uohm = val * MICRO + val_fract; + guard(mutex)(&priv->lock); priv->rshunt_uohm = rshunt_uohm; @@ -1151,6 +1167,61 @@ static void pac1921_regulator_disable(void *data) regulator_disable(regulator); } +/* + * Documentation related to the ACPI device definition + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC193X-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf + */ +static int pac1921_match_acpi_device(struct iio_dev *indio_dev) +{ + acpi_handle handle; + union acpi_object *status; + char *label; + struct pac1921_priv *priv = iio_priv(indio_dev); + struct device *dev = &priv->client->dev; + + handle = ACPI_HANDLE(dev); + + status = acpi_evaluate_dsm(handle, &pac1921_guid, 1, + PAC1921_ACPI_GET_uOHMS_VALS, NULL); + if (!status) + return dev_err_probe(dev, -EINVAL, + "Could not read shunt from ACPI table\n"); + + priv->rshunt_uohm = status->package.elements[0].integer.value; + ACPI_FREE(status); + + status = acpi_evaluate_dsm(handle, &pac1921_guid, 1, + PAC1921_ACPI_GET_LABEL, NULL); + if (!status) + return dev_err_probe(dev, -EINVAL, + "Could not read label from ACPI table\n"); + + label = devm_kstrdup(dev, status->package.elements[0].string.pointer, + GFP_KERNEL); + if (!label) + return -ENOMEM; + + indio_dev->label = label; + ACPI_FREE(status); + + return 0; +} + +static int pac1921_parse_of_fw(struct iio_dev *indio_dev) +{ + int ret; + struct pac1921_priv *priv = iio_priv(indio_dev); + struct device *dev = &priv->client->dev; + + ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", + &priv->rshunt_uohm); + if (ret) + return dev_err_probe(dev, ret, + "Cannot read shunt resistor property\n"); + + return 0; +} + static int pac1921_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1179,11 +1250,14 @@ static int pac1921_probe(struct i2c_client *client) priv->di_gain = PAC1921_DEFAULT_DI_GAIN; priv->n_samples = PAC1921_DEFAULT_NUM_SAMPLES; - ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", - &priv->rshunt_uohm); + if (is_acpi_device_node(dev->fwnode)) + ret = pac1921_match_acpi_device(indio_dev); + else + ret = pac1921_parse_of_fw(indio_dev); if (ret) return dev_err_probe(dev, ret, - "Cannot read shunt resistor property\n"); + "Parameter parsing error\n"); + if (priv->rshunt_uohm == 0 || priv->rshunt_uohm > INT_MAX) return dev_err_probe(dev, -EINVAL, "Invalid shunt resistor: %u\n", @@ -1246,11 +1320,18 @@ static const struct of_device_id pac1921_of_match[] = { }; MODULE_DEVICE_TABLE(of, pac1921_of_match); +static const struct acpi_device_id pac1921_acpi_match[] = { + { "MCHP1921" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, pac1921_acpi_match); + static struct i2c_driver pac1921_driver = { .driver = { .name = "pac1921", .pm = pm_sleep_ptr(&pac1921_pm_ops), .of_match_table = pac1921_of_match, + .acpi_match_table = pac1921_acpi_match, }, .probe = pac1921_probe, .id_table = pac1921_id, diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index dfd47a6e1f4a..a29e54754c8f 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -363,7 +363,7 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p) */ struct { u16 values[SARADC_MAX_CHANNELS]; - int64_t timestamp; + aligned_s64 timestamp; } data; int ret; int i, j = 0; diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c index 56ed948a8ae1..337bc8b31b2c 100644 --- a/drivers/iio/adc/rtq6056.c +++ b/drivers/iio/adc/rtq6056.c @@ -634,7 +634,7 @@ static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p) struct device *dev = priv->dev; struct { u16 vals[RTQ6056_MAX_CHANNEL]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } data; unsigned int raw; int i = 0, bit, ret; diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c index cd3a7e46ea53..883c167c0670 100644 --- a/drivers/iio/adc/rzg2l_adc.c +++ b/drivers/iio/adc/rzg2l_adc.c @@ -8,12 +8,13 @@ */ #include <linux/bitfield.h> -#include <linux/clk.h> +#include <linux/cleanup.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/iio/iio.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -32,20 +33,15 @@ #define RZG2L_ADM1_MS BIT(2) #define RZG2L_ADM1_BS BIT(4) #define RZG2L_ADM1_EGA_MASK GENMASK(13, 12) -#define RZG2L_ADM2_CHSEL_MASK GENMASK(7, 0) #define RZG2L_ADM3_ADIL_MASK GENMASK(31, 24) #define RZG2L_ADM3_ADCMP_MASK GENMASK(23, 16) -#define RZG2L_ADM3_ADCMP_E FIELD_PREP(RZG2L_ADM3_ADCMP_MASK, 0xe) -#define RZG2L_ADM3_ADSMP_MASK GENMASK(15, 0) #define RZG2L_ADINT 0x20 -#define RZG2L_ADINT_INTEN_MASK GENMASK(7, 0) #define RZG2L_ADINT_CSEEN BIT(16) #define RZG2L_ADINT_INTS BIT(31) #define RZG2L_ADSTS 0x24 #define RZG2L_ADSTS_CSEST BIT(16) -#define RZG2L_ADSTS_INTST_MASK GENMASK(7, 0) #define RZG2L_ADIVC 0x28 #define RZG2L_ADIVC_DIVADC_MASK GENMASK(8, 0) @@ -56,12 +52,28 @@ #define RZG2L_ADCR(n) (0x30 + ((n) * 0x4)) #define RZG2L_ADCR_AD_MASK GENMASK(11, 0) -#define RZG2L_ADSMP_DEFAULT_SAMPLING 0x578 - -#define RZG2L_ADC_MAX_CHANNELS 8 -#define RZG2L_ADC_CHN_MASK 0x7 +#define RZG2L_ADC_MAX_CHANNELS 9 #define RZG2L_ADC_TIMEOUT usecs_to_jiffies(1 * 4) +/** + * struct rzg2l_adc_hw_params - ADC hardware specific parameters + * @default_adsmp: default ADC sampling period (see ADM3 register); index 0 is + * used for voltage channels, index 1 is used for temperature channel + * @adsmp_mask: ADC sampling period mask (see ADM3 register) + * @adint_inten_mask: conversion end interrupt mask (see ADINT register) + * @default_adcmp: default ADC cmp (see ADM3 register) + * @num_channels: number of supported channels + * @adivc: specifies if ADVIC register is available + */ +struct rzg2l_adc_hw_params { + u16 default_adsmp[2]; + u16 adsmp_mask; + u16 adint_inten_mask; + u8 default_adcmp; + u8 num_channels; + bool adivc; +}; + struct rzg2l_adc_data { const struct iio_chan_spec *channels; u8 num_channels; @@ -69,25 +81,36 @@ struct rzg2l_adc_data { struct rzg2l_adc { void __iomem *base; - struct clk *pclk; - struct clk *adclk; struct reset_control *presetn; struct reset_control *adrstn; - struct completion completion; const struct rzg2l_adc_data *data; + const struct rzg2l_adc_hw_params *hw_params; + struct completion completion; struct mutex lock; u16 last_val[RZG2L_ADC_MAX_CHANNELS]; + bool was_rpm_active; +}; + +/** + * struct rzg2l_adc_channel - ADC channel descriptor + * @name: ADC channel name + * @type: ADC channel type + */ +struct rzg2l_adc_channel { + const char * const name; + enum iio_chan_type type; }; -static const char * const rzg2l_adc_channel_name[] = { - "adc0", - "adc1", - "adc2", - "adc3", - "adc4", - "adc5", - "adc6", - "adc7", +static const struct rzg2l_adc_channel rzg2l_adc_channels[] = { + { "adc0", IIO_VOLTAGE }, + { "adc1", IIO_VOLTAGE }, + { "adc2", IIO_VOLTAGE }, + { "adc3", IIO_VOLTAGE }, + { "adc4", IIO_VOLTAGE }, + { "adc5", IIO_VOLTAGE }, + { "adc6", IIO_VOLTAGE }, + { "adc7", IIO_VOLTAGE }, + { "adc8", IIO_TEMP }, }; static unsigned int rzg2l_adc_readl(struct rzg2l_adc *adc, u32 reg) @@ -115,7 +138,7 @@ static void rzg2l_adc_pwr(struct rzg2l_adc *adc, bool on) static void rzg2l_adc_start_stop(struct rzg2l_adc *adc, bool start) { - int timeout = 5; + int ret; u32 reg; reg = rzg2l_adc_readl(adc, RZG2L_ADM(0)); @@ -128,15 +151,10 @@ static void rzg2l_adc_start_stop(struct rzg2l_adc *adc, bool start) if (start) return; - do { - usleep_range(100, 200); - reg = rzg2l_adc_readl(adc, RZG2L_ADM(0)); - timeout--; - if (!timeout) { - pr_err("%s stopping ADC timed out\n", __func__); - break; - } - } while (((reg & RZG2L_ADM0_ADBSY) || (reg & RZG2L_ADM0_ADCE))); + ret = read_poll_timeout(rzg2l_adc_readl, reg, !(reg & (RZG2L_ADM0_ADBSY | RZG2L_ADM0_ADCE)), + 200, 1000, true, adc, RZG2L_ADM(0)); + if (ret) + pr_err("%s stopping ADC timed out\n", __func__); } static void rzg2l_set_trigger(struct rzg2l_adc *adc) @@ -158,8 +176,18 @@ static void rzg2l_set_trigger(struct rzg2l_adc *adc) rzg2l_adc_writel(adc, RZG2L_ADM(1), reg); } +static u8 rzg2l_adc_ch_to_adsmp_index(u8 ch) +{ + if (rzg2l_adc_channels[ch].type == IIO_VOLTAGE) + return 0; + + return 1; +} + static int rzg2l_adc_conversion_setup(struct rzg2l_adc *adc, u8 ch) { + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; + u8 index = rzg2l_adc_ch_to_adsmp_index(ch); u32 reg; if (rzg2l_adc_readl(adc, RZG2L_ADM(0)) & RZG2L_ADM0_ADBSY) @@ -169,10 +197,15 @@ static int rzg2l_adc_conversion_setup(struct rzg2l_adc *adc, u8 ch) /* Select analog input channel subjected to conversion. */ reg = rzg2l_adc_readl(adc, RZG2L_ADM(2)); - reg &= ~RZG2L_ADM2_CHSEL_MASK; + reg &= ~GENMASK(hw_params->num_channels - 1, 0); reg |= BIT(ch); rzg2l_adc_writel(adc, RZG2L_ADM(2), reg); + reg = rzg2l_adc_readl(adc, RZG2L_ADM(3)); + reg &= ~hw_params->adsmp_mask; + reg |= hw_params->default_adsmp[index]; + rzg2l_adc_writel(adc, RZG2L_ADM(3), reg); + /* * Setup ADINT * INTS[31] - Select pulse signal @@ -181,36 +214,26 @@ static int rzg2l_adc_conversion_setup(struct rzg2l_adc *adc, u8 ch) */ reg = rzg2l_adc_readl(adc, RZG2L_ADINT); reg &= ~RZG2L_ADINT_INTS; - reg &= ~RZG2L_ADINT_INTEN_MASK; + reg &= ~hw_params->adint_inten_mask; reg |= (RZG2L_ADINT_CSEEN | BIT(ch)); rzg2l_adc_writel(adc, RZG2L_ADINT, reg); return 0; } -static int rzg2l_adc_set_power(struct iio_dev *indio_dev, bool on) -{ - struct device *dev = indio_dev->dev.parent; - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_sync(dev); -} - static int rzg2l_adc_conversion(struct iio_dev *indio_dev, struct rzg2l_adc *adc, u8 ch) { + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; + struct device *dev = indio_dev->dev.parent; int ret; - ret = rzg2l_adc_set_power(indio_dev, true); + ret = pm_runtime_resume_and_get(dev); if (ret) return ret; ret = rzg2l_adc_conversion_setup(adc, ch); - if (ret) { - rzg2l_adc_set_power(indio_dev, false); - return ret; - } + if (ret) + goto rpm_put; reinit_completion(&adc->completion); @@ -218,13 +241,16 @@ static int rzg2l_adc_conversion(struct iio_dev *indio_dev, struct rzg2l_adc *adc if (!wait_for_completion_timeout(&adc->completion, RZG2L_ADC_TIMEOUT)) { rzg2l_adc_writel(adc, RZG2L_ADINT, - rzg2l_adc_readl(adc, RZG2L_ADINT) & ~RZG2L_ADINT_INTEN_MASK); - rzg2l_adc_start_stop(adc, false); - rzg2l_adc_set_power(indio_dev, false); - return -ETIMEDOUT; + rzg2l_adc_readl(adc, RZG2L_ADINT) & ~hw_params->adint_inten_mask); + ret = -ETIMEDOUT; } - return rzg2l_adc_set_power(indio_dev, false); + rzg2l_adc_start_stop(adc, false); + +rpm_put: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return ret; } static int rzg2l_adc_read_raw(struct iio_dev *indio_dev, @@ -233,24 +259,22 @@ static int rzg2l_adc_read_raw(struct iio_dev *indio_dev, { struct rzg2l_adc *adc = iio_priv(indio_dev); int ret; - u8 ch; switch (mask) { - case IIO_CHAN_INFO_RAW: - if (chan->type != IIO_VOLTAGE) + case IIO_CHAN_INFO_RAW: { + if (chan->type != IIO_VOLTAGE && chan->type != IIO_TEMP) return -EINVAL; - mutex_lock(&adc->lock); - ch = chan->channel & RZG2L_ADC_CHN_MASK; - ret = rzg2l_adc_conversion(indio_dev, adc, ch); - if (ret) { - mutex_unlock(&adc->lock); + guard(mutex)(&adc->lock); + + ret = rzg2l_adc_conversion(indio_dev, adc, chan->channel); + if (ret) return ret; - } - *val = adc->last_val[ch]; - mutex_unlock(&adc->lock); + + *val = adc->last_val[chan->channel]; return IIO_VAL_INT; + } default: return -EINVAL; @@ -261,7 +285,7 @@ static int rzg2l_adc_read_label(struct iio_dev *iio_dev, const struct iio_chan_spec *chan, char *label) { - return sysfs_emit(label, "%s\n", rzg2l_adc_channel_name[chan->channel]); + return sysfs_emit(label, "%s\n", rzg2l_adc_channels[chan->channel].name); } static const struct iio_info rzg2l_adc_iio_info = { @@ -272,6 +296,7 @@ static const struct iio_info rzg2l_adc_iio_info = { static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id) { struct rzg2l_adc *adc = dev_id; + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; unsigned long intst; u32 reg; int ch; @@ -284,11 +309,11 @@ static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id) return IRQ_HANDLED; } - intst = reg & RZG2L_ADSTS_INTST_MASK; + intst = reg & GENMASK(hw_params->num_channels - 1, 0); if (!intst) return IRQ_NONE; - for_each_set_bit(ch, &intst, RZG2L_ADC_MAX_CHANNELS) + for_each_set_bit(ch, &intst, hw_params->num_channels) adc->last_val[ch] = rzg2l_adc_readl(adc, RZG2L_ADCR(ch)) & RZG2L_ADCR_AD_MASK; /* clear the channel interrupt */ @@ -301,6 +326,7 @@ static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id) static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l_adc *adc) { + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; struct iio_chan_spec *chan_array; struct rzg2l_adc_data *data; unsigned int channel; @@ -313,15 +339,12 @@ static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l return -ENOMEM; num_channels = device_get_child_node_count(&pdev->dev); - if (!num_channels) { - dev_err(&pdev->dev, "no channel children\n"); - return -ENODEV; - } + if (!num_channels) + return dev_err_probe(&pdev->dev, -ENODEV, "no channel children\n"); - if (num_channels > RZG2L_ADC_MAX_CHANNELS) { - dev_err(&pdev->dev, "num of channel children out of range\n"); - return -EINVAL; - } + if (num_channels > hw_params->num_channels) + return dev_err_probe(&pdev->dev, -EINVAL, + "num of channel children out of range\n"); chan_array = devm_kcalloc(&pdev->dev, num_channels, sizeof(*chan_array), GFP_KERNEL); @@ -334,14 +357,14 @@ static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l if (ret) return ret; - if (channel >= RZG2L_ADC_MAX_CHANNELS) + if (channel >= hw_params->num_channels) return -EINVAL; - chan_array[i].type = IIO_VOLTAGE; + chan_array[i].type = rzg2l_adc_channels[channel].type; chan_array[i].indexed = 1; chan_array[i].channel = channel; chan_array[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW); - chan_array[i].datasheet_name = rzg2l_adc_channel_name[channel]; + chan_array[i].datasheet_name = rzg2l_adc_channels[channel].name; i++; } @@ -352,13 +375,13 @@ static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l return 0; } -static int rzg2l_adc_hw_init(struct rzg2l_adc *adc) +static int rzg2l_adc_hw_init(struct device *dev, struct rzg2l_adc *adc) { - int timeout = 5; + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; u32 reg; int ret; - ret = clk_prepare_enable(adc->pclk); + ret = pm_runtime_resume_and_get(dev); if (ret) return ret; @@ -367,21 +390,19 @@ static int rzg2l_adc_hw_init(struct rzg2l_adc *adc) reg |= RZG2L_ADM0_SRESB; rzg2l_adc_writel(adc, RZG2L_ADM(0), reg); - while (!(rzg2l_adc_readl(adc, RZG2L_ADM(0)) & RZG2L_ADM0_SRESB)) { - if (!timeout) { - ret = -EBUSY; - goto exit_hw_init; - } - timeout--; - usleep_range(100, 200); + ret = read_poll_timeout(rzg2l_adc_readl, reg, reg & RZG2L_ADM0_SRESB, + 200, 1000, false, adc, RZG2L_ADM(0)); + if (ret) + goto exit_hw_init; + + if (hw_params->adivc) { + /* Only division by 4 can be set */ + reg = rzg2l_adc_readl(adc, RZG2L_ADIVC); + reg &= ~RZG2L_ADIVC_DIVADC_MASK; + reg |= RZG2L_ADIVC_DIVADC_4; + rzg2l_adc_writel(adc, RZG2L_ADIVC, reg); } - /* Only division by 4 can be set */ - reg = rzg2l_adc_readl(adc, RZG2L_ADIVC); - reg &= ~RZG2L_ADIVC_DIVADC_MASK; - reg |= RZG2L_ADIVC_DIVADC_4; - rzg2l_adc_writel(adc, RZG2L_ADIVC, reg); - /* * Setup AMD3 * ADIL[31:24] - Should be always set to 0 @@ -391,35 +412,18 @@ static int rzg2l_adc_hw_init(struct rzg2l_adc *adc) reg = rzg2l_adc_readl(adc, RZG2L_ADM(3)); reg &= ~RZG2L_ADM3_ADIL_MASK; reg &= ~RZG2L_ADM3_ADCMP_MASK; - reg &= ~RZG2L_ADM3_ADSMP_MASK; - reg |= (RZG2L_ADM3_ADCMP_E | RZG2L_ADSMP_DEFAULT_SAMPLING); + reg &= ~hw_params->adsmp_mask; + reg |= FIELD_PREP(RZG2L_ADM3_ADCMP_MASK, hw_params->default_adcmp) | + hw_params->default_adsmp[0]; + rzg2l_adc_writel(adc, RZG2L_ADM(3), reg); exit_hw_init: - clk_disable_unprepare(adc->pclk); - + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); return ret; } -static void rzg2l_adc_pm_runtime_disable(void *data) -{ - struct device *dev = data; - - pm_runtime_disable(dev->parent); -} - -static void rzg2l_adc_pm_runtime_set_suspended(void *data) -{ - struct device *dev = data; - - pm_runtime_set_suspended(dev->parent); -} - -static void rzg2l_adc_reset_assert(void *data) -{ - reset_control_assert(data); -} - static int rzg2l_adc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -434,6 +438,10 @@ static int rzg2l_adc_probe(struct platform_device *pdev) adc = iio_priv(indio_dev); + adc->hw_params = device_get_match_data(dev); + if (!adc->hw_params || adc->hw_params->num_channels > RZG2L_ADC_MAX_CHANNELS) + return -EINVAL; + ret = rzg2l_adc_parse_properties(pdev, adc); if (ret) return ret; @@ -444,63 +452,28 @@ static int rzg2l_adc_probe(struct platform_device *pdev) if (IS_ERR(adc->base)) return PTR_ERR(adc->base); - adc->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(adc->pclk)) { - dev_err(dev, "Failed to get pclk"); - return PTR_ERR(adc->pclk); - } + adc->adrstn = devm_reset_control_get_exclusive_deasserted(dev, "adrst-n"); + if (IS_ERR(adc->adrstn)) + return dev_err_probe(dev, PTR_ERR(adc->adrstn), + "failed to get/deassert adrst-n\n"); - adc->adclk = devm_clk_get(dev, "adclk"); - if (IS_ERR(adc->adclk)) { - dev_err(dev, "Failed to get adclk"); - return PTR_ERR(adc->adclk); - } + adc->presetn = devm_reset_control_get_exclusive_deasserted(dev, "presetn"); + if (IS_ERR(adc->presetn)) + return dev_err_probe(dev, PTR_ERR(adc->presetn), + "failed to get/deassert presetn\n"); - adc->adrstn = devm_reset_control_get_exclusive(dev, "adrst-n"); - if (IS_ERR(adc->adrstn)) { - dev_err(dev, "failed to get adrstn\n"); - return PTR_ERR(adc->adrstn); - } - - adc->presetn = devm_reset_control_get_exclusive(dev, "presetn"); - if (IS_ERR(adc->presetn)) { - dev_err(dev, "failed to get presetn\n"); - return PTR_ERR(adc->presetn); - } - - ret = reset_control_deassert(adc->adrstn); - if (ret) { - dev_err(&pdev->dev, "failed to deassert adrstn pin, %d\n", ret); - return ret; - } - - ret = devm_add_action_or_reset(&pdev->dev, - rzg2l_adc_reset_assert, adc->adrstn); - if (ret) { - dev_err(&pdev->dev, "failed to register adrstn assert devm action, %d\n", - ret); + pm_runtime_set_autosuspend_delay(dev, 300); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) return ret; - } - ret = reset_control_deassert(adc->presetn); - if (ret) { - dev_err(&pdev->dev, "failed to deassert presetn pin, %d\n", ret); - return ret; - } - - ret = devm_add_action_or_reset(&pdev->dev, - rzg2l_adc_reset_assert, adc->presetn); - if (ret) { - dev_err(&pdev->dev, "failed to register presetn assert devm action, %d\n", - ret); - return ret; - } + platform_set_drvdata(pdev, indio_dev); - ret = rzg2l_adc_hw_init(adc); - if (ret) { - dev_err(&pdev->dev, "failed to initialize ADC HW, %d\n", ret); - return ret; - } + ret = rzg2l_adc_hw_init(dev, adc); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to initialize ADC HW\n"); irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -513,72 +486,130 @@ static int rzg2l_adc_probe(struct platform_device *pdev) init_completion(&adc->completion); - platform_set_drvdata(pdev, indio_dev); - indio_dev->name = DRIVER_NAME; indio_dev->info = &rzg2l_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = adc->data->channels; indio_dev->num_channels = adc->data->num_channels; - pm_runtime_set_suspended(dev); - ret = devm_add_action_or_reset(&pdev->dev, - rzg2l_adc_pm_runtime_set_suspended, &indio_dev->dev); - if (ret) - return ret; - - pm_runtime_enable(dev); - ret = devm_add_action_or_reset(&pdev->dev, - rzg2l_adc_pm_runtime_disable, &indio_dev->dev); - if (ret) - return ret; - return devm_iio_device_register(dev, indio_dev); } +static const struct rzg2l_adc_hw_params rzg2l_hw_params = { + .num_channels = 8, + .default_adcmp = 0xe, + .default_adsmp = { 0x578 }, + .adsmp_mask = GENMASK(15, 0), + .adint_inten_mask = GENMASK(7, 0), + .adivc = true +}; + +static const struct rzg2l_adc_hw_params rzg3s_hw_params = { + .num_channels = 9, + .default_adcmp = 0x1d, + .default_adsmp = { 0x7f, 0xff }, + .adsmp_mask = GENMASK(7, 0), + .adint_inten_mask = GENMASK(11, 0), +}; + static const struct of_device_id rzg2l_adc_match[] = { - { .compatible = "renesas,rzg2l-adc",}, + { .compatible = "renesas,r9a08g045-adc", .data = &rzg3s_hw_params }, + { .compatible = "renesas,rzg2l-adc", .data = &rzg2l_hw_params }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rzg2l_adc_match); -static int __maybe_unused rzg2l_adc_pm_runtime_suspend(struct device *dev) +static int rzg2l_adc_pm_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct rzg2l_adc *adc = iio_priv(indio_dev); rzg2l_adc_pwr(adc, false); - clk_disable_unprepare(adc->adclk); - clk_disable_unprepare(adc->pclk); return 0; } -static int __maybe_unused rzg2l_adc_pm_runtime_resume(struct device *dev) +static int rzg2l_adc_pm_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rzg2l_adc *adc = iio_priv(indio_dev); + + rzg2l_adc_pwr(adc, true); + + return 0; +} + +static int rzg2l_adc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct rzg2l_adc *adc = iio_priv(indio_dev); + struct reset_control_bulk_data resets[] = { + { .rstc = adc->presetn }, + { .rstc = adc->adrstn }, + }; int ret; - ret = clk_prepare_enable(adc->pclk); + if (pm_runtime_suspended(dev)) { + adc->was_rpm_active = false; + } else { + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + adc->was_rpm_active = true; + } + + ret = reset_control_bulk_assert(ARRAY_SIZE(resets), resets); if (ret) - return ret; + goto rpm_restore; + + return 0; + +rpm_restore: + if (adc->was_rpm_active) + pm_runtime_force_resume(dev); + + return ret; +} + +static int rzg2l_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rzg2l_adc *adc = iio_priv(indio_dev); + struct reset_control_bulk_data resets[] = { + { .rstc = adc->adrstn }, + { .rstc = adc->presetn }, + }; + int ret; - ret = clk_prepare_enable(adc->adclk); - if (ret) { - clk_disable_unprepare(adc->pclk); + ret = reset_control_bulk_deassert(ARRAY_SIZE(resets), resets); + if (ret) return ret; + + if (adc->was_rpm_active) { + ret = pm_runtime_force_resume(dev); + if (ret) + goto resets_restore; } - rzg2l_adc_pwr(adc, true); + ret = rzg2l_adc_hw_init(dev, adc); + if (ret) + goto rpm_restore; return 0; + +rpm_restore: + if (adc->was_rpm_active) { + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } +resets_restore: + reset_control_bulk_assert(ARRAY_SIZE(resets), resets); + return ret; } static const struct dev_pm_ops rzg2l_adc_pm_ops = { - SET_RUNTIME_PM_OPS(rzg2l_adc_pm_runtime_suspend, - rzg2l_adc_pm_runtime_resume, - NULL) + RUNTIME_PM_OPS(rzg2l_adc_pm_runtime_suspend, rzg2l_adc_pm_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rzg2l_adc_suspend, rzg2l_adc_resume) }; static struct platform_driver rzg2l_adc_driver = { @@ -586,7 +617,7 @@ static struct platform_driver rzg2l_adc_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = rzg2l_adc_match, - .pm = &rzg2l_adc_pm_ops, + .pm = pm_ptr(&rzg2l_adc_pm_ops), }, }; diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index 6c2cb3dabbbf..1af9be071d8d 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -37,7 +37,7 @@ struct adc081c { /* Ensure natural alignment of buffer elements */ struct { u16 channel; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index bf98f9bf942a..da16876c32ae 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -29,7 +29,7 @@ struct adc084s021 { /* Buffer used to align data */ struct { __be16 channels[4]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; /* * DMA (thus cache coherency maintenance) may require the diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 47fe8e16aee4..4355726b373a 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -448,7 +448,7 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p) /* Ensure natural alignment of timestamp */ struct { s16 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; int chan, ret, res; diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index c268e27eec12..de019b3faa48 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -501,7 +501,7 @@ static irqreturn_t ads1119_trigger_handler(int irq, void *private) struct ads1119_state *st = iio_priv(indio_dev); struct { s16 sample; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; unsigned int index; int ret; diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c index 31f1f229d97a..91a79ebc4bde 100644 --- a/drivers/iio/adc/ti-ads131e08.c +++ b/drivers/iio/adc/ti-ads131e08.c @@ -102,7 +102,7 @@ struct ads131e08_state { struct completion completion; struct { u8 data[ADS131E08_NUM_DATA_BYTES_MAX]; - s64 ts __aligned(8); + aligned_s64 ts; } tmp_buf; u8 tx_buf[3] __aligned(IIO_DMA_MINALIGN); diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c index 169e3591320b..1e4a78677fe5 100644 --- a/drivers/iio/adc/ti-lmp92064.c +++ b/drivers/iio/adc/ti-lmp92064.c @@ -199,7 +199,7 @@ static irqreturn_t lmp92064_trigger_handler(int irq, void *p) struct lmp92064_adc_priv *priv = iio_priv(indio_dev); struct { u16 values[2]; - int64_t timestamp __aligned(8); + aligned_s64 timestamp; } data; int ret; diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c index b56f2503f14c..7dde5713973f 100644 --- a/drivers/iio/adc/ti-tsc2046.c +++ b/drivers/iio/adc/ti-tsc2046.c @@ -157,7 +157,7 @@ struct tsc2046_adc_priv { /* Scan data for each channel */ u16 data[TI_TSC2046_MAX_CHAN]; /* Timestamp */ - s64 ts __aligned(8); + aligned_s64 ts; } scan_buf; /* diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 4d83c12975c5..513365d42aa5 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -173,10 +173,14 @@ struct vf610_adc { /* Ensure the timestamp is naturally aligned */ struct { u16 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; +struct vf610_chip_info { + u8 num_channels; +}; + static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 }; static const u32 vf610_lst_adder[] = { 3, 5, 7, 9, 13, 17, 21, 25 }; @@ -808,14 +812,31 @@ static const struct iio_info vf610_adc_iio_info = { .attrs = &vf610_attribute_group, }; +static const struct vf610_chip_info vf610_chip_info = { + .num_channels = ARRAY_SIZE(vf610_adc_iio_channels), +}; + +static const struct vf610_chip_info imx6sx_chip_info = { + .num_channels = 4, +}; + static const struct of_device_id vf610_adc_match[] = { - { .compatible = "fsl,vf610-adc", }, + { .compatible = "fsl,imx6sx-adc", .data = &imx6sx_chip_info}, + { .compatible = "fsl,vf610-adc", .data = &vf610_chip_info}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, vf610_adc_match); +static void vf610_adc_action_remove(void *d) +{ + struct vf610_adc *info = d; + + regulator_disable(info->vref); +} + static int vf610_adc_probe(struct platform_device *pdev) { + const struct vf610_chip_info *chip_info; struct device *dev = &pdev->dev; struct vf610_adc *info; struct iio_dev *indio_dev; @@ -823,10 +844,8 @@ static int vf610_adc_probe(struct platform_device *pdev) int ret; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct vf610_adc)); - if (!indio_dev) { - dev_err(&pdev->dev, "Failed allocating iio device\n"); - return -ENOMEM; - } + if (!indio_dev) + return dev_err_probe(&pdev->dev, -ENOMEM, "Failed allocating iio device\n"); info = iio_priv(indio_dev); info->dev = &pdev->dev; @@ -835,6 +854,8 @@ static int vf610_adc_probe(struct platform_device *pdev) if (IS_ERR(info->regs)) return PTR_ERR(info->regs); + chip_info = device_get_match_data(dev); + irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -842,17 +863,12 @@ static int vf610_adc_probe(struct platform_device *pdev) ret = devm_request_irq(info->dev, irq, vf610_adc_isr, 0, dev_name(&pdev->dev), indio_dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed requesting irq, irq = %d\n", irq); - info->clk = devm_clk_get(&pdev->dev, "adc"); - if (IS_ERR(info->clk)) { - dev_err(&pdev->dev, "failed getting clock, err = %ld\n", - PTR_ERR(info->clk)); - return PTR_ERR(info->clk); - } + info->clk = devm_clk_get_enabled(&pdev->dev, "adc"); + if (IS_ERR(info->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->clk), "failed getting clock\n"); info->vref = devm_regulator_get(&pdev->dev, "vref"); if (IS_ERR(info->vref)) @@ -862,6 +878,10 @@ static int vf610_adc_probe(struct platform_device *pdev) if (ret) return ret; + ret = devm_add_action_or_reset(&pdev->dev, vf610_adc_action_remove, info); + if (ret) + return ret; + info->vref_uv = regulator_get_voltage(info->vref); device_property_read_u32_array(dev, "fsl,adck-max-frequency", info->max_adck_rate, 3); @@ -877,54 +897,23 @@ static int vf610_adc_probe(struct platform_device *pdev) indio_dev->info = &vf610_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = vf610_adc_iio_channels; - indio_dev->num_channels = ARRAY_SIZE(vf610_adc_iio_channels); - - ret = clk_prepare_enable(info->clk); - if (ret) { - dev_err(&pdev->dev, - "Could not prepare or enable the clock.\n"); - goto error_adc_clk_enable; - } + indio_dev->num_channels = chip_info->num_channels; vf610_adc_cfg_init(info); vf610_adc_hw_init(info); - ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, - NULL, &iio_triggered_buffer_setup_ops); - if (ret < 0) { - dev_err(&pdev->dev, "Couldn't initialise the buffer\n"); - goto error_iio_device_register; - } + ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev, &iio_pollfunc_store_time, + NULL, &iio_triggered_buffer_setup_ops); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Couldn't initialise the buffer\n"); mutex_init(&info->lock); - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "Couldn't register the device.\n"); - goto error_adc_buffer_init; - } + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Couldn't register the device.\n"); return 0; - -error_adc_buffer_init: - iio_triggered_buffer_cleanup(indio_dev); -error_iio_device_register: - clk_disable_unprepare(info->clk); -error_adc_clk_enable: - regulator_disable(info->vref); - - return ret; -} - -static void vf610_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct vf610_adc *info = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - regulator_disable(info->vref); - clk_disable_unprepare(info->clk); } static int vf610_adc_suspend(struct device *dev) @@ -972,7 +961,6 @@ static DEFINE_SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend, static struct platform_driver vf610_adc_driver = { .probe = vf610_adc_probe, - .remove = vf610_adc_remove, .driver = { .name = DRIVER_NAME, .of_match_table = vf610_adc_match, diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index d2e1529ad8fd..614e1c4189a9 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -206,7 +206,7 @@ static const struct iio_dev_attr *iio_dmaengine_buffer_attrs[] = { /** * iio_dmaengine_buffer_alloc() - Allocate new buffer which uses DMAengine - * @dev: Parent device for the buffer + * @dev: DMA channel consumer device * @channel: DMA channel name, typically "rx". * * This allocates a new IIO buffer which internally uses the DMAengine framework @@ -288,6 +288,21 @@ void iio_dmaengine_buffer_free(struct iio_buffer *buffer) } EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_free, "IIO_DMAENGINE_BUFFER"); +/** + * iio_dmaengine_buffer_setup_ext() - Setup a DMA buffer for an IIO device + * @dev: DMA channel consumer device + * @indio_dev: IIO device to which to attach this buffer. + * @channel: DMA channel name, typically "rx". + * @dir: Direction of buffer (in or out) + * + * This allocates a new IIO buffer with devm_iio_dmaengine_buffer_alloc() + * and attaches it to an IIO device with iio_device_attach_buffer(). + * It also appends the INDIO_BUFFER_HARDWARE mode to the supported modes of the + * IIO device. + * + * Once done using the buffer iio_dmaengine_buffer_free() should be used to + * release it. + */ struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev, struct iio_dev *indio_dev, const char *channel, @@ -321,7 +336,7 @@ static void __devm_iio_dmaengine_buffer_free(void *buffer) /** * devm_iio_dmaengine_buffer_setup_ext() - Setup a DMA buffer for an IIO device - * @dev: Parent device for the buffer + * @dev: Device for devm ownership and DMA channel consumer device * @indio_dev: IIO device to which to attach this buffer. * @channel: DMA channel name, typically "rx". * @dir: Direction of buffer (in or out) diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h index 00ab89b3138b..7d86ed8b02e6 100644 --- a/drivers/iio/chemical/bme680.h +++ b/drivers/iio/chemical/bme680.h @@ -2,6 +2,7 @@ #ifndef BME680_H_ #define BME680_H_ +#include <linux/pm.h> #include <linux/regmap.h> #define BME680_REG_CHIP_ID 0xD0 @@ -80,6 +81,7 @@ #define BME680_CALIB_RANGE_3_LEN 5 extern const struct regmap_config bme680_regmap_config; +extern const struct dev_pm_ops bme680_dev_pm_ops; int bme680_core_probe(struct device *dev, struct regmap *regmap, const char *name); diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index d12270409c8a..9d73fd2cf52c 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -14,7 +14,10 @@ #include <linux/device.h> #include <linux/log2.h> #include <linux/module.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/iio/buffer.h> #include <linux/iio/iio.h> @@ -111,6 +114,8 @@ enum bme680_scan { BME680_GAS, }; +static const char *const bme680_supply_names[] = { "vdd", "vddio" }; + struct bme680_data { struct regmap *regmap; struct bme680_calib bme680; @@ -817,9 +822,9 @@ static int bme680_read_gas(struct bme680_data *data, int *comp_gas_res) return 0; } -static int bme680_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) +static int __bme680_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct bme680_data *data = iio_priv(indio_dev); int chan_val, ret; @@ -874,11 +879,11 @@ static int bme680_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: switch (chan->type) { case IIO_TEMP: - ret = bme680_read_temp(data, (s16 *)&chan_val); + ret = bme680_read_temp(data, &temp_chan_val); if (ret) return ret; - *val = chan_val; + *val = temp_chan_val; return IIO_VAL_INT; case IIO_PRESSURE: ret = bme680_read_press(data, &chan_val); @@ -932,14 +937,33 @@ static int bme680_read_raw(struct iio_dev *indio_dev, } } +static int bme680_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bme680_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = __bme680_read_raw(indio_dev, chan, val, val2, mask); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + static bool bme680_is_valid_oversampling(int rate) { return (rate > 0 && rate <= 16 && is_power_of_2(rate)); } -static int bme680_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) +static int __bme680_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct bme680_data *data = iio_priv(indio_dev); @@ -984,6 +1008,25 @@ static int bme680_write_raw(struct iio_dev *indio_dev, } } +static int bme680_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bme680_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = __bme680_write_raw(indio_dev, chan, val, val2, mask); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + static const char bme680_oversampling_ratio_show[] = "1 2 4 8 16"; static IIO_CONST_ATTR(oversampling_ratio_available, @@ -1084,6 +1127,29 @@ out: return IRQ_HANDLED; } +static int bme680_buffer_preenable(struct iio_dev *indio_dev) +{ + struct bme680_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + + return pm_runtime_resume_and_get(dev); +} + +static int bme680_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct bme680_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; +} + +static const struct iio_buffer_setup_ops bme680_buffer_setup_ops = { + .preenable = bme680_buffer_preenable, + .postdisable = bme680_buffer_postdisable, +}; + int bme680_core_probe(struct device *dev, struct regmap *regmap, const char *name) { @@ -1114,6 +1180,14 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap, data->heater_dur = 150; /* milliseconds */ data->preheat_curr_mA = 0; + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(bme680_supply_names), + bme680_supply_names); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable supplies.\n"); + + fsleep(BME680_STARTUP_TIME_US); + ret = regmap_write(regmap, BME680_REG_SOFT_RESET, BME680_CMD_SOFTRESET); if (ret < 0) return dev_err_probe(dev, ret, "Failed to reset chip\n"); @@ -1149,15 +1223,47 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap, ret = devm_iio_triggered_buffer_setup(dev, indio_dev, iio_pollfunc_store_time, bme680_trigger_handler, - NULL); + &bme680_buffer_setup_ops); if (ret) return dev_err_probe(dev, ret, "iio triggered buffer setup failed\n"); + /* Enable runtime PM */ + pm_runtime_set_autosuspend_delay(dev, BME680_STARTUP_TIME_US); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(bme680_core_probe, "IIO_BME680"); +static int bme680_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bme680_data *data = iio_priv(indio_dev); + + return bme680_set_mode(data, BME680_MODE_SLEEP); +} + +static int bme680_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bme680_data *data = iio_priv(indio_dev); + int ret; + + ret = bme680_chip_config(data); + if (ret) + return ret; + + return bme680_gas_config(data); +} + +EXPORT_RUNTIME_DEV_PM_OPS(bme680_dev_pm_ops, bme680_runtime_suspend, + bme680_runtime_resume, NULL); + MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>"); MODULE_DESCRIPTION("Bosch BME680 Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c index 7a949228b4a6..ac7763f98a6a 100644 --- a/drivers/iio/chemical/bme680_i2c.c +++ b/drivers/iio/chemical/bme680_i2c.c @@ -51,6 +51,7 @@ static struct i2c_driver bme680_i2c_driver = { .driver = { .name = "bme680_i2c", .of_match_table = bme680_of_i2c_match, + .pm = pm_ptr(&bme680_dev_pm_ops), }, .probe = bme680_i2c_probe, .id_table = bme680_i2c_id, diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index 3916a51ba68e..ecb24ba0ebc9 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -154,6 +154,7 @@ static struct spi_driver bme680_spi_driver = { .driver = { .name = "bme680_spi", .of_match_table = bme680_of_spi_match, + .pm = pm_ptr(&bme680_dev_pm_ops), }, .probe = bme680_spi_probe, .id_table = bme680_spi_id, diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c index 17d1bc518bf2..451fb65dbe60 100644 --- a/drivers/iio/chemical/ccs811.c +++ b/drivers/iio/chemical/ccs811.c @@ -81,7 +81,7 @@ struct ccs811_data { /* Ensures correct alignment of timestamp if present */ struct { s16 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/chemical/ens160_core.c b/drivers/iio/chemical/ens160_core.c index 4a89cd5894d9..48d5ad2075b6 100644 --- a/drivers/iio/chemical/ens160_core.c +++ b/drivers/iio/chemical/ens160_core.c @@ -60,7 +60,7 @@ struct ens160_data { struct mutex mutex; struct { __le16 chans[2]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan __aligned(IIO_DMA_MINALIGN); u8 fw_version[3]; __le16 buf; diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index ac3080929f0b..d613c54cb28d 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -594,7 +594,7 @@ static irqreturn_t scd30_trigger_handler(int irq, void *p) struct scd30_state *state = iio_priv(indio_dev); struct { int data[SCD30_MEAS_COUNT]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; int ret; diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c index 52cad54e8572..50e3ac44422b 100644 --- a/drivers/iio/chemical/scd4x.c +++ b/drivers/iio/chemical/scd4x.c @@ -665,7 +665,7 @@ static irqreturn_t scd4x_trigger_handler(int irq, void *p) struct scd4x_state *state = iio_priv(indio_dev); struct { uint16_t data[3]; - int64_t ts __aligned(8); + aligned_s64 ts; } scan; int ret; diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c index c081b5caa475..97526ba87b93 100644 --- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c @@ -109,8 +109,8 @@ static bool inv_update_chip_period(struct inv_sensors_timestamp *ts, static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts) { - const int64_t period_min = ts->min_period * ts->mult; - const int64_t period_max = ts->max_period * ts->mult; + const int64_t period_min = (int64_t)ts->min_period * ts->mult; + const int64_t period_max = (int64_t)ts->max_period * ts->mult; int64_t add_max, sub_max; int64_t delta, jitter; int64_t adjust; diff --git a/drivers/iio/common/ssp_sensors/ssp_iio.c b/drivers/iio/common/ssp_sensors/ssp_iio.c index caa404edd9d0..78ac689de2fe 100644 --- a/drivers/iio/common/ssp_sensors/ssp_iio.c +++ b/drivers/iio/common/ssp_sensors/ssp_iio.c @@ -8,6 +8,8 @@ #include <linux/iio/kfifo_buf.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/unaligned.h> +#include <linux/units.h> #include "ssp_iio_sensor.h" /** @@ -70,8 +72,7 @@ EXPORT_SYMBOL_NS(ssp_common_buffer_postdisable, "IIO_SSP_SENSORS"); int ssp_common_process_data(struct iio_dev *indio_dev, void *buf, unsigned int len, int64_t timestamp) { - __le32 time; - int64_t calculated_time = 0; + int64_t calculated_time; struct ssp_sensor_data *spd = iio_priv(indio_dev); if (indio_dev->scan_bytes == 0) @@ -82,11 +83,8 @@ int ssp_common_process_data(struct iio_dev *indio_dev, void *buf, */ memcpy(spd->buffer, buf, len); - if (indio_dev->scan_timestamp) { - memcpy(&time, &((char *)buf)[len], SSP_TIME_SIZE); - calculated_time = - timestamp + (int64_t)le32_to_cpu(time) * 1000000; - } + calculated_time = timestamp + + (int64_t)get_unaligned_le32(buf + len) * MEGA; return iio_push_to_buffers_with_timestamp(indio_dev, spd->buffer, calculated_time); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 5d01ba4edbf3..5690a37267d8 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -80,7 +80,7 @@ config AD5421 depends on SPI help Say yes here to build support for Analog Devices AD5421 loop-powered - digital-to-analog convertors (DAC). + digital-to-analog converters (DAC). To compile this driver as module choose M here: the module will be called ad5421. @@ -348,6 +348,14 @@ config AD8801 To compile this driver as a module choose M here: the module will be called ad8801. +config BD79703 + tristate "ROHM Semiconductor BD79703 DAC driver" + depends on SPI + select REGMAP_SPI + help + Say yes here to build support for ROHM Semiconductor BD79703 Digital + to Analog Converter (DAC). + config CIO_DAC tristate "Measurement Computing CIO-DAC IIO driver" depends on X86 && (ISA_BUS || PC104) diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 414c152be779..8dd6cce81ed1 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_AD8460) += ad8460.o obj-$(CONFIG_AD8801) += ad8801.o obj-$(CONFIG_AD9739A) += ad9739a.o obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o +obj-$(CONFIG_BD79703) += rohm-bd79703.o obj-$(CONFIG_CIO_DAC) += cio-dac.o obj-$(CONFIG_DPOT_DAC) += dpot-dac.o obj-$(CONFIG_DS4424) += ds4424.o diff --git a/drivers/iio/dac/ad3552r-common.c b/drivers/iio/dac/ad3552r-common.c index 0f495df2e5ce..03e0864f5084 100644 --- a/drivers/iio/dac/ad3552r-common.c +++ b/drivers/iio/dac/ad3552r-common.c @@ -22,11 +22,10 @@ EXPORT_SYMBOL_NS_GPL(ad3552r_ch_ranges, "IIO_AD3552R"); const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2] = { [AD3542R_CH_OUTPUT_RANGE_0__2P5V] = { 0, 2500 }, - [AD3542R_CH_OUTPUT_RANGE_0__3V] = { 0, 3000 }, [AD3542R_CH_OUTPUT_RANGE_0__5V] = { 0, 5000 }, [AD3542R_CH_OUTPUT_RANGE_0__10V] = { 0, 10000 }, - [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = { -2500, 7500 }, - [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V] = { -5000, 5000 } + [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V] = { -5000, 5000 }, + [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = { -2500, 7500 } }; EXPORT_SYMBOL_NS_GPL(ad3542r_ch_ranges, "IIO_AD3552R"); diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c index 216c634f3eaf..8974df625670 100644 --- a/drivers/iio/dac/ad3552r-hs.c +++ b/drivers/iio/dac/ad3552r-hs.c @@ -329,6 +329,12 @@ static int ad3552r_hs_setup(struct ad3552r_hs_state *st) dev_info(st->dev, "Chip ID error. Expected 0x%x, Read 0x%x\n", AD3552R_ID, id); + /* Clear reset error flag, see ad3552r manual, rev B table 38. */ + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_ERR_STATUS, + AD3552R_MASK_RESET_STATUS, 1); + if (ret) + return ret; + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SH_REFERENCE_CONFIG, 0, 1); diff --git a/drivers/iio/dac/ad3552r.h b/drivers/iio/dac/ad3552r.h index fd5a3dfd1d1c..4b5581039ae9 100644 --- a/drivers/iio/dac/ad3552r.h +++ b/drivers/iio/dac/ad3552r.h @@ -131,7 +131,7 @@ #define AD3552R_CH1_ACTIVE BIT(1) #define AD3552R_MAX_RANGES 5 -#define AD3542R_MAX_RANGES 6 +#define AD3542R_MAX_RANGES 5 #define AD3552R_QUAD_SPI 2 extern const s32 ad3552r_ch_ranges[AD3552R_MAX_RANGES][2]; @@ -189,16 +189,14 @@ enum ad3552r_ch_vref_select { enum ad3542r_ch_output_range { /* Range from 0 V to 2.5 V. Requires Rfb1x connection */ AD3542R_CH_OUTPUT_RANGE_0__2P5V, - /* Range from 0 V to 3 V. Requires Rfb1x connection */ - AD3542R_CH_OUTPUT_RANGE_0__3V, /* Range from 0 V to 5 V. Requires Rfb1x connection */ AD3542R_CH_OUTPUT_RANGE_0__5V, /* Range from 0 V to 10 V. Requires Rfb2x connection */ AD3542R_CH_OUTPUT_RANGE_0__10V, - /* Range from -2.5 V to 7.5 V. Requires Rfb2x connection */ - AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V, /* Range from -5 V to 5 V. Requires Rfb2x connection */ AD3542R_CH_OUTPUT_RANGE_NEG_5__5V, + /* Range from -2.5 V to 7.5 V. Requires Rfb2x connection */ + AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V, }; enum ad3552r_ch_output_range { diff --git a/drivers/iio/dac/ad5624r.h b/drivers/iio/dac/ad5624r.h index 14a439b06eb6..098fb5a7683d 100644 --- a/drivers/iio/dac/ad5624r.h +++ b/drivers/iio/dac/ad5624r.h @@ -41,11 +41,9 @@ struct ad5624r_chip_info { }; /** - * struct ad5446_state - driver instance specific data - * @indio_dev: the industrial I/O device + * struct ad5624r_state - driver instance specific data * @us: spi_device * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator * @vref_mv: actual reference voltage used * @pwr_down_mask power down mask * @pwr_down_mode current power down mode diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 39b5dad0d6a5..9c727aa6ea18 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -95,11 +95,6 @@ static int ad5686_spi_probe(struct spi_device *spi) ad5686_spi_write, ad5686_spi_read); } -static void ad5686_spi_remove(struct spi_device *spi) -{ - ad5686_remove(&spi->dev); -} - static const struct spi_device_id ad5686_spi_id[] = { {"ad5310r", ID_AD5310R}, {"ad5672r", ID_AD5672R}, @@ -126,7 +121,6 @@ static struct spi_driver ad5686_spi_driver = { .name = "ad5686", }, .probe = ad5686_spi_probe, - .remove = ad5686_spi_remove, .id_table = ad5686_spi_id, }; diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 8dc578b08784..763af690c444 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -455,39 +455,28 @@ int ad5686_probe(struct device *dev, struct ad5686_state *st; struct iio_dev *indio_dev; unsigned int val, ref_bit_msk; + bool has_external_vref; u8 cmd; - int ret, i, voltage_uv = 0; + int ret, i; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; st = iio_priv(indio_dev); - dev_set_drvdata(dev, indio_dev); st->dev = dev; st->write = write; st->read = read; - st->reg = devm_regulator_get_optional(dev, "vcc"); - if (!IS_ERR(st->reg)) { - ret = regulator_enable(st->reg); - if (ret) - return ret; - - ret = regulator_get_voltage(st->reg); - if (ret < 0) - goto error_disable_reg; - - voltage_uv = ret; - } - st->chip_info = &ad5686_chip_info_tbl[chip_type]; - if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else - st->vref_mv = st->chip_info->int_vref_mv; + ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); + if (ret < 0 && ret != -ENODEV) + return ret; + + has_external_vref = ret != -ENODEV; + st->vref_mv = has_external_vref ? ret / 1000 : st->chip_info->int_vref_mv; /* Set all the power down mode for all channels to 1K pulldown */ for (i = 0; i < st->chip_info->num_channels; i++) @@ -505,12 +494,12 @@ int ad5686_probe(struct device *dev, case AD5310_REGMAP: cmd = AD5686_CMD_CONTROL_REG; ref_bit_msk = AD5310_REF_BIT_MSK; - st->use_internal_vref = !voltage_uv; + st->use_internal_vref = !has_external_vref; break; case AD5683_REGMAP: cmd = AD5686_CMD_CONTROL_REG; ref_bit_msk = AD5683_REF_BIT_MSK; - st->use_internal_vref = !voltage_uv; + st->use_internal_vref = !has_external_vref; break; case AD5686_REGMAP: cmd = AD5686_CMD_INTERNAL_REFER_SETUP; @@ -519,43 +508,22 @@ int ad5686_probe(struct device *dev, case AD5693_REGMAP: cmd = AD5686_CMD_CONTROL_REG; ref_bit_msk = AD5693_REF_BIT_MSK; - st->use_internal_vref = !voltage_uv; + st->use_internal_vref = !has_external_vref; break; default: - ret = -EINVAL; - goto error_disable_reg; + return -EINVAL; } - val = (voltage_uv | ref_bit_msk); + val = (has_external_vref | ref_bit_msk); ret = st->write(st, cmd, 0, !!val); if (ret) - goto error_disable_reg; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; + return ret; -error_disable_reg: - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); - return ret; + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(ad5686_probe, "IIO_AD5686"); -void ad5686_remove(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct ad5686_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); -} -EXPORT_SYMBOL_NS_GPL(ad5686_remove, "IIO_AD5686"); - MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index 760f852911df..e7d36bae3e59 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -115,10 +115,9 @@ struct ad5686_chip_info { }; /** - * struct ad5446_state - driver instance specific data + * struct ad5686_state - driver instance specific data * @spi: spi_device * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator * @vref_mv: actual reference voltage used * @pwr_down_mask: power down mask * @pwr_down_mode: current power down mode @@ -130,7 +129,6 @@ struct ad5686_chip_info { struct ad5686_state { struct device *dev; const struct ad5686_chip_info *chip_info; - struct regulator *reg; unsigned short vref_mv; unsigned int pwr_down_mask; unsigned int pwr_down_mode; @@ -157,7 +155,5 @@ int ad5686_probe(struct device *dev, const char *name, ad5686_write_func write, ad5686_read_func read); -void ad5686_remove(struct device *dev); - #endif /* __DRIVERS_IIO_DAC_AD5686_H__ */ diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index bbcda246c547..0156f32c12c8 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -65,11 +65,6 @@ static int ad5686_i2c_probe(struct i2c_client *i2c) ad5686_i2c_write, ad5686_i2c_read); } -static void ad5686_i2c_remove(struct i2c_client *i2c) -{ - ad5686_remove(&i2c->dev); -} - static const struct i2c_device_id ad5686_i2c_id[] = { {"ad5311r", ID_AD5311R}, {"ad5337r", ID_AD5337R}, @@ -116,7 +111,6 @@ static struct i2c_driver ad5686_i2c_driver = { .of_match_table = ad5686_of_match, }, .probe = ad5686_i2c_probe, - .remove = ad5686_i2c_remove, .id_table = ad5686_i2c_id, }; diff --git a/drivers/iio/dac/ad7293.c b/drivers/iio/dac/ad7293.c index 1d4032670482..d3f49b5337d2 100644 --- a/drivers/iio/dac/ad7293.c +++ b/drivers/iio/dac/ad7293.c @@ -141,8 +141,6 @@ struct ad7293_state { /* Protect against concurrent accesses to the device, page selection and data content */ struct mutex lock; struct gpio_desc *gpio_reset; - struct regulator *reg_avdd; - struct regulator *reg_vdrive; u8 page_select; u8 data[3] __aligned(IIO_DMA_MINALIGN); }; @@ -777,6 +775,15 @@ static int ad7293_reset(struct ad7293_state *st) static int ad7293_properties_parse(struct ad7293_state *st) { struct spi_device *spi = st->spi; + int ret; + + ret = devm_regulator_get_enable(&spi->dev, "avdd"); + if (ret) + return dev_err_probe(&spi->dev, ret, "failed to enable AVDD\n"); + + ret = devm_regulator_get_enable(&spi->dev, "vdrive"); + if (ret) + return dev_err_probe(&spi->dev, ret, "failed to enable VDRIVE\n"); st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", GPIOD_OUT_HIGH); @@ -784,24 +791,9 @@ static int ad7293_properties_parse(struct ad7293_state *st) return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_reset), "failed to get the reset GPIO\n"); - st->reg_avdd = devm_regulator_get(&spi->dev, "avdd"); - if (IS_ERR(st->reg_avdd)) - return dev_err_probe(&spi->dev, PTR_ERR(st->reg_avdd), - "failed to get the AVDD voltage\n"); - - st->reg_vdrive = devm_regulator_get(&spi->dev, "vdrive"); - if (IS_ERR(st->reg_vdrive)) - return dev_err_probe(&spi->dev, PTR_ERR(st->reg_vdrive), - "failed to get the VDRIVE voltage\n"); - return 0; } -static void ad7293_reg_disable(void *data) -{ - regulator_disable(data); -} - static int ad7293_init(struct ad7293_state *st) { int ret; @@ -816,48 +808,6 @@ static int ad7293_init(struct ad7293_state *st) if (ret) return ret; - ret = regulator_enable(st->reg_avdd); - if (ret) { - dev_err(&spi->dev, - "Failed to enable specified AVDD Voltage!\n"); - return ret; - } - - ret = devm_add_action_or_reset(&spi->dev, ad7293_reg_disable, - st->reg_avdd); - if (ret) - return ret; - - ret = regulator_enable(st->reg_vdrive); - if (ret) { - dev_err(&spi->dev, - "Failed to enable specified VDRIVE Voltage!\n"); - return ret; - } - - ret = devm_add_action_or_reset(&spi->dev, ad7293_reg_disable, - st->reg_vdrive); - if (ret) - return ret; - - ret = regulator_get_voltage(st->reg_avdd); - if (ret < 0) { - dev_err(&spi->dev, "Failed to read avdd regulator: %d\n", ret); - return ret; - } - - if (ret > 5500000 || ret < 4500000) - return -EINVAL; - - ret = regulator_get_voltage(st->reg_vdrive); - if (ret < 0) { - dev_err(&spi->dev, - "Failed to read vdrive regulator: %d\n", ret); - return ret; - } - if (ret > 5500000 || ret < 1700000) - return -EINVAL; - /* Check Chip ID */ ret = __ad7293_spi_read(st, AD7293_REG_DEVICE_ID, &chip_id); if (ret) diff --git a/drivers/iio/dac/ad8801.c b/drivers/iio/dac/ad8801.c index 919e8c880697..8a362fae2eca 100644 --- a/drivers/iio/dac/ad8801.c +++ b/drivers/iio/dac/ad8801.c @@ -23,8 +23,6 @@ struct ad8801_state { unsigned char dac_cache[8]; /* Value write on each channel */ unsigned int vrefh_mv; unsigned int vrefl_mv; - struct regulator *vrefh_reg; - struct regulator *vrefl_reg; __be16 data __aligned(IIO_DMA_MINALIGN); }; @@ -122,86 +120,34 @@ static int ad8801_probe(struct spi_device *spi) state->spi = spi; id = spi_get_device_id(spi); - state->vrefh_reg = devm_regulator_get(&spi->dev, "vrefh"); - if (IS_ERR(state->vrefh_reg)) - return dev_err_probe(&spi->dev, PTR_ERR(state->vrefh_reg), - "Vrefh regulator not specified\n"); + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vrefh"); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "failed to get Vrefh voltage\n"); - ret = regulator_enable(state->vrefh_reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable vrefh regulator: %d\n", - ret); - return ret; - } - - ret = regulator_get_voltage(state->vrefh_reg); - if (ret < 0) { - dev_err(&spi->dev, "Failed to read vrefh regulator: %d\n", - ret); - goto error_disable_vrefh_reg; - } state->vrefh_mv = ret / 1000; if (id->driver_data == ID_AD8803) { - state->vrefl_reg = devm_regulator_get(&spi->dev, "vrefl"); - if (IS_ERR(state->vrefl_reg)) { - ret = dev_err_probe(&spi->dev, PTR_ERR(state->vrefl_reg), - "Vrefl regulator not specified\n"); - goto error_disable_vrefh_reg; - } - - ret = regulator_enable(state->vrefl_reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable vrefl regulator: %d\n", - ret); - goto error_disable_vrefh_reg; - } - - ret = regulator_get_voltage(state->vrefl_reg); - if (ret < 0) { - dev_err(&spi->dev, "Failed to read vrefl regulator: %d\n", - ret); - goto error_disable_vrefl_reg; - } + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vrefl"); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "failed to get Vrefl voltage\n"); + state->vrefl_mv = ret / 1000; - } else { - state->vrefl_mv = 0; - state->vrefl_reg = NULL; } - spi_set_drvdata(spi, indio_dev); indio_dev->info = &ad8801_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ad8801_channels; indio_dev->num_channels = ARRAY_SIZE(ad8801_channels); indio_dev->name = id->name; - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&spi->dev, "Failed to register iio device: %d\n", - ret); - goto error_disable_vrefl_reg; - } + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to register iio device\n"); return 0; - -error_disable_vrefl_reg: - if (state->vrefl_reg) - regulator_disable(state->vrefl_reg); -error_disable_vrefh_reg: - regulator_disable(state->vrefh_reg); - return ret; -} - -static void ad8801_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad8801_state *state = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (state->vrefl_reg) - regulator_disable(state->vrefl_reg); - regulator_disable(state->vrefh_reg); } static const struct spi_device_id ad8801_ids[] = { @@ -216,7 +162,6 @@ static struct spi_driver ad8801_driver = { .name = "ad8801", }, .probe = ad8801_probe, - .remove = ad8801_remove, .id_table = ad8801_ids, }; module_spi_driver(ad8801_driver); diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index a4fb2509c950..999348836d87 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -41,13 +41,11 @@ struct ltc2632_chip_info { * @spi_dev: pointer to the spi_device struct * @powerdown_cache_mask: used to show current channel powerdown state * @vref_mv: used reference voltage (internal or external) - * @vref_reg: regulator for the reference voltage */ struct ltc2632_state { struct spi_device *spi_dev; unsigned int powerdown_cache_mask; int vref_mv; - struct regulator *vref_reg; }; enum ltc2632_supported_device_ids { @@ -310,6 +308,7 @@ static int ltc2632_probe(struct spi_device *spi) struct ltc2632_state *st; struct iio_dev *indio_dev; struct ltc2632_chip_info *chip_info; + bool has_external_vref; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); @@ -318,49 +317,31 @@ static int ltc2632_probe(struct spi_device *spi) st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); st->spi_dev = spi; chip_info = (struct ltc2632_chip_info *) spi_get_device_id(spi)->driver_data; - st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); - if (PTR_ERR(st->vref_reg) == -ENODEV) { - /* use internal reference voltage */ - st->vref_reg = NULL; - st->vref_mv = chip_info->vref_mv; + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(&spi->dev, ret, + "Failed to get vref regulator voltage\n"); - ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER, - 0, 0, 0); - if (ret) { - dev_err(&spi->dev, - "Set internal reference command failed, %d\n", - ret); - return ret; - } - } else if (IS_ERR(st->vref_reg)) { - dev_err(&spi->dev, - "Error getting voltage reference regulator\n"); - return PTR_ERR(st->vref_reg); - } else { - /* use external reference voltage */ - ret = regulator_enable(st->vref_reg); - if (ret) { - dev_err(&spi->dev, - "enable reference regulator failed, %d\n", - ret); - return ret; - } - st->vref_mv = regulator_get_voltage(st->vref_reg) / 1000; + has_external_vref = ret != -ENODEV; + st->vref_mv = has_external_vref ? ret / 1000 : chip_info->vref_mv; + if (has_external_vref) { ret = ltc2632_spi_write(spi, LTC2632_CMD_EXTERNAL_REFER, - 0, 0, 0); - if (ret) { - dev_err(&spi->dev, - "Set external reference command failed, %d\n", - ret); - return ret; - } + 0, 0, 0); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Set external reference command failed\n"); + } else { + ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER, + 0, 0, 0); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Set internal reference command failed\n"); } indio_dev->name = fwnode_get_name(dev_fwnode(&spi->dev)) ?: spi_get_device_id(spi)->name; @@ -369,18 +350,7 @@ static int ltc2632_probe(struct spi_device *spi) indio_dev->channels = chip_info->channels; indio_dev->num_channels = chip_info->num_channels; - return iio_device_register(indio_dev); -} - -static void ltc2632_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ltc2632_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (st->vref_reg) - regulator_disable(st->vref_reg); + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ltc2632_id[] = { @@ -472,7 +442,6 @@ static struct spi_driver ltc2632_driver = { .of_match_table = ltc2632_of_match, }, .probe = ltc2632_probe, - .remove = ltc2632_remove, .id_table = ltc2632_id, }; module_spi_driver(ltc2632_driver); diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c index 376dca163c91..bdc857c7fa6d 100644 --- a/drivers/iio/dac/ltc2688.c +++ b/drivers/iio/dac/ltc2688.c @@ -842,7 +842,7 @@ static int ltc2688_channel_config(struct ltc2688_state *st) return 0; } -static int ltc2688_setup(struct ltc2688_state *st, struct regulator *vref) +static int ltc2688_setup(struct ltc2688_state *st, bool has_external_vref) { struct device *dev = &st->spi->dev; struct gpio_desc *gpio; @@ -881,18 +881,13 @@ static int ltc2688_setup(struct ltc2688_state *st, struct regulator *vref) if (ret) return ret; - if (!vref) + if (!has_external_vref) return 0; return regmap_set_bits(st->regmap, LTC2688_CMD_CONFIG, LTC2688_CONFIG_EXT_REF); } -static void ltc2688_disable_regulator(void *regulator) -{ - regulator_disable(regulator); -} - static bool ltc2688_reg_readable(struct device *dev, unsigned int reg) { switch (reg) { @@ -947,8 +942,8 @@ static int ltc2688_probe(struct spi_device *spi) static const char * const regulators[] = { "vcc", "iovcc" }; struct ltc2688_state *st; struct iio_dev *indio_dev; - struct regulator *vref_reg; struct device *dev = &spi->dev; + bool has_external_vref; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); @@ -973,34 +968,15 @@ static int ltc2688_probe(struct spi_device *spi) if (ret) return dev_err_probe(dev, ret, "Failed to enable regulators\n"); - vref_reg = devm_regulator_get_optional(dev, "vref"); - if (IS_ERR(vref_reg)) { - if (PTR_ERR(vref_reg) != -ENODEV) - return dev_err_probe(dev, PTR_ERR(vref_reg), - "Failed to get vref regulator"); - - vref_reg = NULL; - /* internal reference */ - st->vref = 4096; - } else { - ret = regulator_enable(vref_reg); - if (ret) - return dev_err_probe(dev, ret, - "Failed to enable vref regulators\n"); - - ret = devm_add_action_or_reset(dev, ltc2688_disable_regulator, - vref_reg); - if (ret) - return ret; - - ret = regulator_get_voltage(vref_reg); - if (ret < 0) - return dev_err_probe(dev, ret, "Failed to get vref\n"); + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, + "Failed to get vref regulator voltage\n"); - st->vref = ret / 1000; - } + has_external_vref = ret != -ENODEV; + st->vref = has_external_vref ? ret / 1000 : 0; - ret = ltc2688_setup(st, vref_reg); + ret = ltc2688_setup(st, has_external_vref); if (ret) return ret; diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index 18ba3eaaad75..b062a18be5e7 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -32,7 +32,6 @@ enum max5821_device_ids { struct max5821_data { struct i2c_client *client; - struct regulator *vref_reg; unsigned short vref_mv; bool powerdown[MAX5821_MAX_DAC_CHANNELS]; u8 powerdown_mode[MAX5821_MAX_DAC_CHANNELS]; @@ -295,11 +294,6 @@ static const struct iio_info max5821_info = { .write_raw = max5821_write_raw, }; -static void max5821_regulator_disable(void *reg) -{ - regulator_disable(reg); -} - static int max5821_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); @@ -321,32 +315,10 @@ static int max5821_probe(struct i2c_client *client) data->powerdown_mode[tmp] = MAX5821_100KOHM_TO_GND; } - data->vref_reg = devm_regulator_get(&client->dev, "vref"); - if (IS_ERR(data->vref_reg)) - return dev_err_probe(&client->dev, PTR_ERR(data->vref_reg), - "Failed to get vref regulator\n"); - - ret = regulator_enable(data->vref_reg); - if (ret) { - dev_err(&client->dev, - "Failed to enable vref regulator: %d\n", ret); - return ret; - } - - ret = devm_add_action_or_reset(&client->dev, max5821_regulator_disable, - data->vref_reg); - if (ret) { - dev_err(&client->dev, - "Failed to add action to managed regulator: %d\n", ret); - return ret; - } - - ret = regulator_get_voltage(data->vref_reg); - if (ret < 0) { - dev_err(&client->dev, - "Failed to get voltage on regulator: %d\n", ret); - return ret; - } + ret = devm_regulator_get_enable_read_voltage(&client->dev, "vref"); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to get vref regulator voltage\n"); data->vref_mv = ret / 1000; diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 25bb1c0490af..1337fb02ccf5 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -379,7 +379,7 @@ static int mcp4725_probe_dt(struct device *dev, struct mcp4725_platform_data *pdata) { /* check if is the vref-supply defined */ - pdata->use_vref = device_property_read_bool(dev, "vref-supply"); + pdata->use_vref = device_property_present(dev, "vref-supply"); pdata->vref_buffered = device_property_read_bool(dev, "microchip,vref-buffered"); diff --git a/drivers/iio/dac/rohm-bd79703.c b/drivers/iio/dac/rohm-bd79703.c new file mode 100644 index 000000000000..e998ab51052e --- /dev/null +++ b/drivers/iio/dac/rohm-bd79703.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * BD79703 ROHM Digital to Analog converter + * + * Copyright (c) 2024, ROHM Semiconductor. + */ + +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> + +#define BD79703_MAX_REGISTER 0xf +#define BD79703_DAC_BITS 8 +#define BD79703_REG_OUT_ALL GENMASK(2, 0) + +/* + * The BD79703 uses 12-bit SPI commands. First four bits (high bits) define + * channel(s) which are operated on, and also the mode. The mode can be to set + * a DAC word only, or set DAC word and output. The data-sheet is not very + * specific on how a previously set DAC word can be 'taken in to use'. Thus + * this driver only uses the 'set DAC and output it' -mode. + * + * The BD79703 latches last 12-bits when the chip-select is toggled. Thus we + * can use 16-bit transfers which should be widely supported. To simplify this + * further, we treat the last 8 bits as a value, and first 8 bits as an + * address. This allows us to separate channels/mode by address and treat the + * 8-bit register value as DAC word. The highest 4 bits of address will be + * discarded when the transfer is latched. + */ +static const struct regmap_config bd79703_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = BD79703_MAX_REGISTER, + .cache_type = REGCACHE_RBTREE, +}; + +struct bd79703_data { + struct regmap *regmap; + int vfs; +}; + +static int bd79703_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct bd79703_data *data = iio_priv(idev); + + if (mask != IIO_CHAN_INFO_SCALE) + return -EINVAL; + + *val = data->vfs / 1000; + *val2 = BD79703_DAC_BITS; + + return IIO_VAL_FRACTIONAL_LOG2; +} + +static int bd79703_write_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct bd79703_data *data = iio_priv(idev); + + if (val < 0 || val >= 1 << BD79703_DAC_BITS) + return -EINVAL; + + return regmap_write(data->regmap, chan->channel + 1, val); +}; + +static const struct iio_info bd79703_info = { + .read_raw = bd79703_read_raw, + .write_raw = bd79703_write_raw, +}; + +#define BD79703_CHAN(_chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (_chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = (_chan), \ +} + +static const struct iio_chan_spec bd79703_channels[] = { + BD79703_CHAN(0), + BD79703_CHAN(1), + BD79703_CHAN(2), + BD79703_CHAN(3), + BD79703_CHAN(4), + BD79703_CHAN(5), +}; + +static int bd79703_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct bd79703_data *data; + struct iio_dev *idev; + int ret; + + idev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!idev) + return -ENOMEM; + + data = iio_priv(idev); + + data->regmap = devm_regmap_init_spi(spi, &bd79703_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "Failed to initialize Regmap\n"); + + ret = devm_regulator_get_enable(dev, "vcc"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable VCC\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "vfs"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get Vfs\n"); + + data->vfs = ret; + idev->channels = bd79703_channels; + idev->num_channels = ARRAY_SIZE(bd79703_channels); + idev->modes = INDIO_DIRECT_MODE; + idev->info = &bd79703_info; + idev->name = "bd79703"; + + /* Initialize all to output zero */ + ret = regmap_write(data->regmap, BD79703_REG_OUT_ALL, 0); + if (ret) + return ret; + + return devm_iio_device_register(dev, idev); +} + +static const struct spi_device_id bd79703_id[] = { + { "bd79703", }, + { } +}; +MODULE_DEVICE_TABLE(spi, bd79703_id); + +static const struct of_device_id bd79703_of_match[] = { + { .compatible = "rohm,bd79703", }, + { } +}; +MODULE_DEVICE_TABLE(of, bd79703_of_match); + +static struct spi_driver bd79703_driver = { + .driver = { + .name = "bd79703", + .of_match_table = bd79703_of_match, + }, + .probe = bd79703_probe, + .id_table = bd79703_id, +}; +module_spi_driver(bd79703_driver); + +MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>"); +MODULE_DESCRIPTION("ROHM BD79703 DAC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c index 600e9725da78..223fc181109c 100644 --- a/drivers/iio/gyro/adxrs290.c +++ b/drivers/iio/gyro/adxrs290.c @@ -75,7 +75,7 @@ struct adxrs290_state { /* Ensure correct alignment of timestamp when present */ struct { s16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } buffer; }; diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index ba877d067afb..deb3c6459dde 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -99,7 +99,7 @@ struct bmg160_data { /* Ensure naturally aligned timestamp */ struct { s16 chans[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; u32 dps_range; int ev_enable_state; diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c index 4cfa0d439560..a624400a239c 100644 --- a/drivers/iio/gyro/itg3200_buffer.c +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -52,7 +52,7 @@ static irqreturn_t itg3200_trigger_handler(int irq, void *p) */ struct { __be16 buf[ITG3200_SCAN_ELEMENTS]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; int ret = itg3200_read_all_channels(st->i2c, scan.buf); diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index b6883e8b2a8b..d66224bed8e3 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -474,7 +474,7 @@ static irqreturn_t mpu3050_trigger_handler(int irq, void *p) int ret; struct { __be16 chans[4]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; s64 timestamp; unsigned int datums_from_fifo = 0; diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c index 6b0aa3a3f025..2323974b805c 100644 --- a/drivers/iio/humidity/am2315.c +++ b/drivers/iio/humidity/am2315.c @@ -35,7 +35,7 @@ struct am2315_data { /* Ensure timestamp is naturally aligned */ struct { s16 chans[2]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c index 9b355380c9bf..a303f704b7ed 100644 --- a/drivers/iio/humidity/hdc100x.c +++ b/drivers/iio/humidity/hdc100x.c @@ -44,7 +44,7 @@ struct hdc100x_data { /* Ensure natural alignment of timestamp */ struct { __be16 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/humidity/hts221.h b/drivers/iio/humidity/hts221.h index 721359e226cb..0215f11fc35e 100644 --- a/drivers/iio/humidity/hts221.h +++ b/drivers/iio/humidity/hts221.h @@ -40,7 +40,7 @@ struct hts221_hw { /* Ensure natural alignment of timestamp */ struct { __le16 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index 0a5d13d2240e..727e0a11eac1 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -878,11 +878,32 @@ static const struct iio_chan_spec adis16545_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(17), }; +static const struct iio_chan_spec adis16489_channels[] = { + ADIS16480_GYRO_CHANNEL(X), + ADIS16480_GYRO_CHANNEL(Y), + ADIS16480_GYRO_CHANNEL(Z), + ADIS16480_ACCEL_CHANNEL(X), + ADIS16480_ACCEL_CHANNEL(Y), + ADIS16480_ACCEL_CHANNEL(Z), + ADIS16480_PRESSURE_CHANNEL(), + ADIS16480_TEMP_CHANNEL(), + IIO_CHAN_SOFT_TIMESTAMP(8), + ADIS16480_DELTANG_CHANNEL_NO_SCAN(X), + ADIS16480_DELTANG_CHANNEL_NO_SCAN(Y), + ADIS16480_DELTANG_CHANNEL_NO_SCAN(Z), + ADIS16480_DELTVEL_CHANNEL_NO_SCAN(X), + ADIS16480_DELTVEL_CHANNEL_NO_SCAN(Y), + ADIS16480_DELTVEL_CHANNEL_NO_SCAN(Z), +}; + enum adis16480_variant { ADIS16375, ADIS16480, ADIS16485, + ADIS16486, + ADIS16487, ADIS16488, + ADIS16489, ADIS16490, ADIS16495_1, ADIS16495_2, @@ -1038,6 +1059,38 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .filter_freqs = adis16480_def_filter_freqs, .adis_data = ADIS16480_DATA(16485, &adis16485_timeouts, 0, 0), }, + [ADIS16486] = { + .channels = adis16485_channels, + .num_channels = ARRAY_SIZE(adis16485_channels), + .gyro_max_val = 22500 << 16, + .gyro_max_scale = IIO_DEGREE_TO_RAD(450), + .accel_max_val = IIO_M_S_2_TO_G(20000 << 16), + .accel_max_scale = 18, + .temp_scale = 5650, /* 5.65 milli degree Celsius */ + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 200, + .int_clk = 2460000, + .max_dec_rate = 2048, + .has_sleep_cnt = true, + .filter_freqs = adis16480_def_filter_freqs, + .adis_data = ADIS16480_DATA(16486, &adis16480_timeouts, 0, 0), + }, + [ADIS16487] = { + .channels = adis16485_channels, + .num_channels = ARRAY_SIZE(adis16485_channels), + .gyro_max_val = 22500 << 16, + .gyro_max_scale = IIO_DEGREE_TO_RAD(450), + .accel_max_val = IIO_M_S_2_TO_G(20000 << 16), + .accel_max_scale = 5, + .temp_scale = 5650, /* 5.65 milli degree Celsius */ + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 50, + .int_clk = 2460000, + .max_dec_rate = 2048, + .has_sleep_cnt = true, + .filter_freqs = adis16480_def_filter_freqs, + .adis_data = ADIS16480_DATA(16487, &adis16485_timeouts, 0, 0), + }, [ADIS16488] = { .channels = adis16480_channels, .num_channels = ARRAY_SIZE(adis16480_channels), @@ -1054,6 +1107,22 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .filter_freqs = adis16480_def_filter_freqs, .adis_data = ADIS16480_DATA(16488, &adis16485_timeouts, 0, 0), }, + [ADIS16489] = { + .channels = adis16489_channels, + .num_channels = ARRAY_SIZE(adis16489_channels), + .gyro_max_val = 22500 << 16, + .gyro_max_scale = IIO_DEGREE_TO_RAD(450), + .accel_max_val = IIO_M_S_2_TO_G(20000 << 16), + .accel_max_scale = 18, + .temp_scale = 5650, /* 5.65 milli degree Celsius */ + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 200, + .int_clk = 2460000, + .max_dec_rate = 2048, + .has_sleep_cnt = true, + .filter_freqs = adis16480_def_filter_freqs, + .adis_data = ADIS16480_DATA(16489, &adis16480_timeouts, 0, 0), + }, [ADIS16490] = { .channels = adis16485_channels, .num_channels = ARRAY_SIZE(adis16485_channels), @@ -1741,7 +1810,10 @@ static const struct spi_device_id adis16480_ids[] = { { "adis16375", ADIS16375 }, { "adis16480", ADIS16480 }, { "adis16485", ADIS16485 }, + { "adis16486", ADIS16486 }, + { "adis16487", ADIS16487 }, { "adis16488", ADIS16488 }, + { "adis16489", ADIS16489 }, { "adis16490", ADIS16490 }, { "adis16495-1", ADIS16495_1 }, { "adis16495-2", ADIS16495_2 }, @@ -1763,7 +1835,10 @@ static const struct of_device_id adis16480_of_match[] = { { .compatible = "adi,adis16375" }, { .compatible = "adi,adis16480" }, { .compatible = "adi,adis16485" }, + { .compatible = "adi,adis16486" }, + { .compatible = "adi,adis16487" }, { .compatible = "adi,adis16488" }, + { .compatible = "adi,adis16489" }, { .compatible = "adi,adis16490" }, { .compatible = "adi,adis16495-1" }, { .compatible = "adi,adis16495-2" }, diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c index f7d7f4442e65..7f386c5e58b4 100644 --- a/drivers/iio/imu/bmi323/bmi323_core.c +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -174,7 +174,7 @@ struct bmi323_data { __le16 fifo_buff[BMI323_FIFO_FULL_IN_WORDS] __aligned(IIO_DMA_MINALIGN); struct { __le16 channels[BMI323_CHAN_MAX]; - s64 ts __aligned(8); + aligned_s64 ts; } buffer; __le16 steps_count[BMI323_STEP_LEN]; }; diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c index 0728d38260a1..597c402b98de 100644 --- a/drivers/iio/imu/bno055/bno055.c +++ b/drivers/iio/imu/bno055/bno055.c @@ -207,7 +207,7 @@ struct bno055_priv { bool sw_reset; struct { __le16 chans[BNO055_SCAN_CH_COUNT]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } buf; struct dentry *debugfs; }; @@ -1193,7 +1193,7 @@ static ssize_t serialnumber_show(struct device *dev, } static ssize_t calibration_data_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { struct bno055_priv *priv = iio_priv(dev_to_iio_dev(kobj_to_dev(kobj))); @@ -1348,16 +1348,16 @@ static struct attribute *bno055_attrs[] = { NULL }; -static BIN_ATTR_RO(calibration_data, BNO055_CALDATA_LEN); +static const BIN_ATTR_RO(calibration_data, BNO055_CALDATA_LEN); -static struct bin_attribute *bno055_bin_attrs[] = { +static const struct bin_attribute *const bno055_bin_attrs[] = { &bin_attr_calibration_data, NULL }; static const struct attribute_group bno055_attrs_group = { .attrs = bno055_attrs, - .bin_attrs = bno055_bin_attrs, + .bin_attrs_new = bno055_bin_attrs, }; static const struct iio_info bno055_info = { diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 7968aa27f9fd..388520ec60b5 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -178,7 +178,7 @@ static const struct iio_chan_spec inv_icm42600_accel_channels[] = { struct inv_icm42600_accel_buffer { struct inv_icm42600_fifo_sensor_data accel; int16_t temp; - int64_t timestamp __aligned(8); + aligned_s64 timestamp; }; #define INV_ICM42600_SCAN_MASK_ACCEL_3AXIS \ diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index c6bb68bf5e14..591ed78a55bb 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -78,7 +78,7 @@ static const struct iio_chan_spec inv_icm42600_gyro_channels[] = { struct inv_icm42600_gyro_buffer { struct inv_icm42600_fifo_sensor_data gyro; int16_t temp; - int64_t timestamp __aligned(8); + aligned_s64 timestamp; }; #define INV_ICM42600_SCAN_MASK_GYRO_3AXIS \ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 844b611b825a..5bcd5e797046 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -277,6 +277,14 @@ static const struct inv_mpu6050_hw hw_info[] = { .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, .startup_time = {INV_ICM20690_GYRO_STARTUP_TIME, INV_ICM20690_ACCEL_STARTUP_TIME}, }, + { .whoami = INV_IAM20380_WHOAMI_VALUE, + .name = "IAM20380", + .reg = ®_set_6500, + .config = &chip_config_6500, + .fifo_size = 512, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, + .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME}, + }, { .whoami = INV_IAM20680_WHOAMI_VALUE, .name = "IAM20680", @@ -1519,6 +1527,14 @@ static const struct iio_chan_spec inv_mpu6050_channels[] = { INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z), }; +static const struct iio_chan_spec inv_iam20380_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP), + + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z), +}; + static const struct iio_chan_spec inv_mpu6500_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP), @@ -1623,6 +1639,10 @@ static const struct iio_chan_spec inv_mpu9250_channels[] = { | BIT(INV_MPU9X50_SCAN_MAGN_Y) \ | BIT(INV_MPU9X50_SCAN_MAGN_Z)) +static const unsigned long inv_iam20380_scan_masks[] = { + INV_MPU6050_SCAN_MASK_3AXIS_GYRO, +}; + static const unsigned long inv_mpu9x50_scan_masks[] = { /* 3-axis accel */ INV_MPU6050_SCAN_MASK_3AXIS_ACCEL, @@ -2026,6 +2046,11 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels); indio_dev->available_scan_masks = inv_mpu9x50_scan_masks; break; + case INV_IAM20380: + indio_dev->channels = inv_iam20380_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_iam20380_channels); + indio_dev->available_scan_masks = inv_iam20380_scan_masks; + break; case INV_ICM20600: case INV_ICM20602: indio_dev->channels = inv_mpu6500_channels; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 307a06f4df2e..91d77f94d204 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -34,6 +34,7 @@ static bool inv_mpu_i2c_aux_bus(struct device *dev) case INV_ICM20689: case INV_ICM20600: case INV_ICM20602: + case INV_IAM20380: case INV_IAM20680: /* no i2c auxiliary bus on the chip */ return false; @@ -187,6 +188,7 @@ static const struct i2c_device_id inv_mpu_id[] = { {"icm20600", INV_ICM20600}, {"icm20602", INV_ICM20602}, {"icm20690", INV_ICM20690}, + {"iam20380", INV_IAM20380}, {"iam20680", INV_IAM20680}, {"iam20680hp", INV_IAM20680HP}, {"iam20680ht", INV_IAM20680HT}, @@ -253,6 +255,10 @@ static const struct of_device_id inv_of_match[] = { .data = (void *)INV_ICM20690 }, { + .compatible = "invensense,iam20380", + .data = (void *)INV_IAM20380 + }, + { .compatible = "invensense,iam20680", .data = (void *)INV_IAM20680 }, diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index a6862cf42639..211901f8b8eb 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -84,6 +84,7 @@ enum inv_devices { INV_ICM20600, INV_ICM20602, INV_ICM20690, + INV_IAM20380, INV_IAM20680, INV_IAM20680HP, INV_IAM20680HT, @@ -425,6 +426,7 @@ struct inv_mpu6050_state { #define INV_ICM20600_WHOAMI_VALUE 0x11 #define INV_ICM20602_WHOAMI_VALUE 0x12 #define INV_ICM20690_WHOAMI_VALUE 0x20 +#define INV_IAM20380_WHOAMI_VALUE 0xB5 #define INV_IAM20680_WHOAMI_VALUE 0xA9 #define INV_IAM20680HP_WHOAMI_VALUE 0xF8 #define INV_IAM20680HT_WHOAMI_VALUE 0xFA diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index ab415874d699..20de6eb5cd35 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -79,6 +79,7 @@ static const struct spi_device_id inv_mpu_id[] = { {"icm20600", INV_ICM20600}, {"icm20602", INV_ICM20602}, {"icm20690", INV_ICM20690}, + {"iam20380", INV_IAM20380}, {"iam20680", INV_IAM20680}, {"iam20680hp", INV_IAM20680HP}, {"iam20680ht", INV_IAM20680HT}, @@ -141,6 +142,10 @@ static const struct of_device_id inv_of_match[] = { .data = (void *)INV_ICM20690 }, { + .compatible = "invensense,iam20380", + .data = (void *)INV_IAM20380 + }, + { .compatible = "invensense,iam20680", .data = (void *)INV_IAM20680 }, diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig index 89d687ec3099..3cabec3b152d 100644 --- a/drivers/iio/imu/st_lsm6dsx/Kconfig +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -6,9 +6,6 @@ config IIO_ST_LSM6DSX select IIO_BUFFER select IIO_TRIGGERED_BUFFER select IIO_KFIFO_BUF - select IIO_ST_LSM6DSX_I2C if (I2C) - select IIO_ST_LSM6DSX_SPI if (SPI_MASTER) - select IIO_ST_LSM6DSX_I3C if (I3C) help Say yes here to build support for STMicroelectronics LSM6DSx imu sensor. @@ -42,16 +39,19 @@ config IIO_ST_LSM6DSX will be called st_lsm6dsx. config IIO_ST_LSM6DSX_I2C - tristate - depends on IIO_ST_LSM6DSX + tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors I2C Interface" + depends on I2C && IIO_ST_LSM6DSX + default I2C && IIO_ST_LSM6DSX select REGMAP_I2C config IIO_ST_LSM6DSX_SPI - tristate - depends on IIO_ST_LSM6DSX + tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors SPI Interface" + depends on SPI_MASTER && IIO_ST_LSM6DSX + default SPI_MASTER && IIO_ST_LSM6DSX select REGMAP_SPI config IIO_ST_LSM6DSX_I3C - tristate - depends on IIO_ST_LSM6DSX + tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors I3C Interface" + depends on I3C && IIO_ST_LSM6DSX + default I3C && IIO_ST_LSM6DSX select REGMAP_I3C diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c index 6952d901316f..f968f32890d1 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c @@ -9,7 +9,6 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/i3c/device.h> -#include <linux/i3c/master.h> #include <linux/slab.h> #include <linux/regmap.h> @@ -30,15 +29,16 @@ static int st_lsm6dsx_i3c_probe(struct i3c_device *i3cdev) }; const struct i3c_device_id *id = i3c_device_match_id(i3cdev, st_lsm6dsx_i3c_ids); + struct device *dev = i3cdev_to_dev(i3cdev); struct regmap *regmap; regmap = devm_regmap_init_i3c(i3cdev, &st_lsm6dsx_i3c_regmap_config); if (IS_ERR(regmap)) { - dev_err(&i3cdev->dev, "Failed to register i3c regmap %ld\n", PTR_ERR(regmap)); + dev_err(dev, "Failed to register i3c regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } - return st_lsm6dsx_probe(&i3cdev->dev, 0, (uintptr_t)id->data, regmap); + return st_lsm6dsx_probe(dev, 0, (uintptr_t)id->data, regmap); } static struct i3c_driver st_lsm6dsx_driver = { diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 2708f87df719..a80f7cc25a27 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1137,7 +1137,7 @@ static int iio_enable_buffers(struct iio_dev *indio_dev, int ret; indio_dev->active_scan_mask = config->scan_mask; - indio_dev->scan_timestamp = config->scan_timestamp; + ACCESS_PRIVATE(indio_dev, scan_timestamp) = config->scan_timestamp; indio_dev->scan_bytes = config->scan_bytes; iio_dev_opaque->currentmode = config->mode; diff --git a/drivers/iio/industrialio-gts-helper.c b/drivers/iio/industrialio-gts-helper.c index 3b5a99815062..d70ebe3bf774 100644 --- a/drivers/iio/industrialio-gts-helper.c +++ b/drivers/iio/industrialio-gts-helper.c @@ -915,6 +915,41 @@ int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel } EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_sel_for_scale_using_time, "IIO_GTS_HELPER"); +/** + * iio_gts_find_gain_time_sel_for_scale - Fetch gain and time selectors for scale + * @gts: Gain time scale descriptor + * @scale_int: Integral part of the scale (typically val1) + * @scale_nano: Fractional part of the scale (nano or ppb) + * @gain_sel: Pointer to value where gain selector is stored. + * @time_sel: Pointer to value where time selector is stored. + * + * Wrapper around iio_gts_find_gain_for_scale_using_time() to fetch the + * gain and time selectors for a given scale. + * + * Return: 0 on success and -EINVAL on error. + */ +int iio_gts_find_gain_time_sel_for_scale(struct iio_gts *gts, int scale_int, + int scale_nano, int *gain_sel, + int *time_sel) +{ + int i, ret; + + for (i = 0; i < gts->num_itime; i++) { + *time_sel = gts->itime_table[i].sel; + ret = iio_gts_find_gain_sel_for_scale_using_time(gts, *time_sel, + scale_int, + scale_nano, + gain_sel); + if (ret) + continue; + + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_time_sel_for_scale, "IIO_GTS_HELPER"); + static int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time) { const struct iio_itime_sel_mul *itime; @@ -1086,6 +1121,48 @@ int iio_gts_find_new_gain_by_old_gain_time(struct iio_gts *gts, int old_gain, } EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_by_old_gain_time, "IIO_GTS_HELPER"); +/** + * iio_gts_find_new_gain_by_gain_time_min - compensate for time change + * @gts: Gain time scale descriptor + * @old_gain: Previously set gain + * @old_time: Selector corresponding previously set time + * @new_time: Selector corresponding new time to be set + * @new_gain: Pointer to value where new gain is to be written + * @in_range: Indicate if the @new_gain was in the range of + * supported gains. + * + * Wrapper around iio_gts_find_new_gain_by_old_gain_time() that tries to + * set an optimal value if no exact match was found, defaulting to the + * minimum gain to avoid saturations if the optimal value is not in the + * range of supported gains. + * + * Return: 0 on success and a negative value if no gain was found. + */ +int iio_gts_find_new_gain_by_gain_time_min(struct iio_gts *gts, int old_gain, + int old_time, int new_time, + int *new_gain, bool *in_range) +{ + int ret; + + *in_range = true; + ret = iio_gts_find_new_gain_by_old_gain_time(gts, old_gain, old_time, + new_time, new_gain); + if (*new_gain < 0) + return -EINVAL; + + if (ret) { + *new_gain = iio_find_closest_gain_low(gts, *new_gain, in_range); + if (*new_gain < 0) { + *new_gain = iio_gts_get_min_gain(gts); + if (*new_gain < 0) + return -EINVAL; + } + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_by_gain_time_min, "IIO_GTS_HELPER"); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>"); MODULE_DESCRIPTION("IIO light sensor gain-time-scale helpers"); diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 9050a59129e6..c174ebb7d5e6 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -7,6 +7,7 @@ #include <linux/err.h> #include <linux/export.h> #include <linux/minmax.h> +#include <linux/mm.h> #include <linux/mutex.h> #include <linux/property.h> #include <linux/slab.h> @@ -989,6 +990,11 @@ ssize_t iio_read_channel_ext_info(struct iio_channel *chan, { const struct iio_chan_spec_ext_info *ext_info; + if (!buf || offset_in_page(buf)) { + pr_err("iio: invalid ext_info read buffer\n"); + return -EINVAL; + } + ext_info = iio_lookup_ext_info(chan, attr); if (!ext_info) return -EINVAL; @@ -1014,6 +1020,11 @@ EXPORT_SYMBOL_GPL(iio_write_channel_ext_info); ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf) { + if (!buf || offset_in_page(buf)) { + pr_err("iio: invalid label read buffer\n"); + return -EINVAL; + } + return do_iio_read_channel_label(chan->indio_dev, chan->channel, buf); } EXPORT_SYMBOL_GPL(iio_read_channel_label); diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 29ffa8491927..e34e551eef3e 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -329,22 +329,6 @@ config JSA1212 To compile this driver as a module, choose M here: the module will be called jsa1212. -config ROHM_BU27008 - tristate "ROHM BU27008 color (RGB+C/IR) sensor" - depends on I2C - select REGMAP_I2C - select IIO_GTS_HELPER - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - Enable support for the ROHM BU27008 color sensor. - The ROHM BU27008 is a sensor with 5 photodiodes (red, green, - blue, clear and IR) with four configurable channels. Red and - green being always available and two out of the rest three - (blue, clear, IR) can be selected to be simultaneously measured. - Typical application is adjusting LCD backlight of TVs, - mobile phones and tablet PCs. - config ROHM_BU27034 tristate "ROHM BU27034 ambient light sensor" depends on I2C @@ -491,6 +475,19 @@ config OPT4001 If built as a dynamically linked module, it will be called opt4001. +config OPT4060 + tristate "Texas Instruments OPT4060 RGBW Color Sensor" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say Y or M here, you get support for Texas Instruments + OPT4060 RGBW Color Sensor. + + If built as a dynamically linked module, it will be called + opt4060. + config PA12203001 tristate "TXC PA12203001 light and proximity sensor" depends on I2C @@ -672,6 +669,7 @@ config VCNL4035 config VEML3235 tristate "VEML3235 ambient light sensor" select REGMAP_I2C + select IIO_GTS_HELPER depends on I2C help Say Y here if you want to build a driver for the Vishay VEML3235 @@ -683,6 +681,8 @@ config VEML3235 config VEML6030 tristate "VEML6030 and VEML6035 ambient light sensors" select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER depends on I2C help Say Y here if you want to build a driver for the Vishay VEML6030 diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index f14a37442712..11a4041b918a 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -42,8 +42,8 @@ obj-$(CONFIG_MAX44009) += max44009.o obj-$(CONFIG_NOA1305) += noa1305.o obj-$(CONFIG_OPT3001) += opt3001.o obj-$(CONFIG_OPT4001) += opt4001.o +obj-$(CONFIG_OPT4060) += opt4060.o obj-$(CONFIG_PA12203001) += pa12203001.o -obj-$(CONFIG_ROHM_BU27008) += rohm-bu27008.o obj-$(CONFIG_ROHM_BU27034) += rohm-bu27034.o obj-$(CONFIG_RPR0521) += rpr0521.o obj-$(CONFIG_SI1133) += si1133.o diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index c1b43053fbc7..cf96e3dd8bc6 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -56,7 +56,7 @@ struct adjd_s311_data { struct i2c_client *client; struct { s16 chans[4]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/light/as73211.c b/drivers/iio/light/as73211.c index be0068081ebb..37fffce35dd1 100644 --- a/drivers/iio/light/as73211.c +++ b/drivers/iio/light/as73211.c @@ -177,6 +177,12 @@ struct as73211_data { BIT(AS73211_SCAN_INDEX_TEMP) | \ AS73211_SCAN_MASK_COLOR) +static const unsigned long as73211_scan_masks[] = { + AS73211_SCAN_MASK_COLOR, + AS73211_SCAN_MASK_ALL, + 0 +}; + static const struct iio_chan_spec as73211_channels[] = { { .type = IIO_TEMP, @@ -636,7 +642,7 @@ static irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p) struct as73211_data *data = iio_priv(indio_dev); struct { __le16 chan[4]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; int data_result, ret; @@ -672,9 +678,12 @@ static irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p) /* AS73211 starts reading at address 2 */ ret = i2c_master_recv(data->client, - (char *)&scan.chan[1], 3 * sizeof(scan.chan[1])); + (char *)&scan.chan[0], 3 * sizeof(scan.chan[0])); if (ret < 0) goto done; + + /* Avoid pushing uninitialized data */ + scan.chan[3] = 0; } if (data_result) { @@ -682,9 +691,15 @@ static irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p) * Saturate all channels (in case of overflows). Temperature channel * is not affected by overflows. */ - scan.chan[1] = cpu_to_le16(U16_MAX); - scan.chan[2] = cpu_to_le16(U16_MAX); - scan.chan[3] = cpu_to_le16(U16_MAX); + if (*indio_dev->active_scan_mask == AS73211_SCAN_MASK_ALL) { + scan.chan[1] = cpu_to_le16(U16_MAX); + scan.chan[2] = cpu_to_le16(U16_MAX); + scan.chan[3] = cpu_to_le16(U16_MAX); + } else { + scan.chan[0] = cpu_to_le16(U16_MAX); + scan.chan[1] = cpu_to_le16(U16_MAX); + scan.chan[2] = cpu_to_le16(U16_MAX); + } } iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); @@ -758,6 +773,7 @@ static int as73211_probe(struct i2c_client *client) indio_dev->channels = data->spec_dev->channels; indio_dev->num_channels = data->spec_dev->num_channels; indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->available_scan_masks = as73211_scan_masks; ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR); if (ret < 0) diff --git a/drivers/iio/light/bh1745.c b/drivers/iio/light/bh1745.c index 63bf729df517..3b4056be54a0 100644 --- a/drivers/iio/light/bh1745.c +++ b/drivers/iio/light/bh1745.c @@ -739,7 +739,7 @@ static irqreturn_t bh1745_trigger_handler(int interrupt, void *p) struct bh1745_data *data = iio_priv(indio_dev); struct { u16 chans[4]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; u16 value; int ret; diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c index b6288dd25bbf..5b00ad2a014e 100644 --- a/drivers/iio/light/cm3232.c +++ b/drivers/iio/light/cm3232.c @@ -89,6 +89,15 @@ static int cm3232_reg_init(struct cm3232_chip *chip) chip->als_info = &cm3232_als_info_default; + /* Disable and reset device */ + chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET; + ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, + chip->regs_cmd); + if (ret < 0) { + dev_err(&chip->client->dev, "Error writing reg_cmd\n"); + return ret; + } + /* Identify device */ ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID); if (ret < 0) { @@ -99,15 +108,6 @@ static int cm3232_reg_init(struct cm3232_chip *chip) if ((ret & 0xFF) != chip->als_info->hw_id) return -ENODEV; - /* Disable and reset device */ - chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET; - ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, - chip->regs_cmd); - if (ret < 0) { - dev_err(&chip->client->dev, "Error writing reg_cmd\n"); - return ret; - } - /* Register default value */ chip->regs_cmd = chip->als_info->regs_cmd_default; diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index c83acbd78275..7ab64f5c623c 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -94,6 +94,7 @@ static int prox_read_raw(struct iio_dev *indio_dev, *val2 = 0; switch (mask) { case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: if (chan->scan_index >= prox_state->num_channels) return -EINVAL; address = prox_state->channel2usage[chan->scan_index]; diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c index b176bf4c884b..326dc39e7929 100644 --- a/drivers/iio/light/isl29125.c +++ b/drivers/iio/light/isl29125.c @@ -54,7 +54,7 @@ struct isl29125_data { /* Ensure timestamp is naturally aligned */ struct { u16 chans[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 604f5f900a2e..669da0840eba 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1280,7 +1280,7 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p) struct ltr501_data *data = iio_priv(indio_dev); struct { u16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; __le16 als_buf[2]; u8 mask = 0; diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c index b935976871a6..e8b767680133 100644 --- a/drivers/iio/light/max44000.c +++ b/drivers/iio/light/max44000.c @@ -78,7 +78,7 @@ struct max44000_data { /* Ensure naturally aligned timestamp */ struct { u16 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c new file mode 100644 index 000000000000..ab55f8d2ea0c --- /dev/null +++ b/drivers/iio/light/opt4060.c @@ -0,0 +1,1343 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 Axis Communications AB + * + * Datasheet: https://www.ti.com/lit/gpn/opt4060 + * + * Device driver for the Texas Instruments OPT4060 RGBW Color Sensor. + */ + +#include <linux/bitfield.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/math64.h> +#include <linux/units.h> +#include <linux/limits.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/mutex.h> +#include <linux/regulator/consumer.h> +#include <linux/iio/events.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +/* OPT4060 register set */ +#define OPT4060_RED_MSB 0x00 +#define OPT4060_RED_LSB 0x01 +#define OPT4060_GREEN_MSB 0x02 +#define OPT4060_GREEN_LSB 0x03 +#define OPT4060_BLUE_MSB 0x04 +#define OPT4060_BLUE_LSB 0x05 +#define OPT4060_CLEAR_MSB 0x06 +#define OPT4060_CLEAR_LSB 0x07 +#define OPT4060_THRESHOLD_LOW 0x08 +#define OPT4060_THRESHOLD_HIGH 0x09 +#define OPT4060_CTRL 0x0a +#define OPT4060_INT_CTRL 0x0b +#define OPT4060_RES_CTRL 0x0c +#define OPT4060_DEVICE_ID 0x11 + +/* OPT4060 register mask */ +#define OPT4060_EXPONENT_MASK GENMASK(15, 12) +#define OPT4060_MSB_MASK GENMASK(11, 0) +#define OPT4060_LSB_MASK GENMASK(15, 8) +#define OPT4060_COUNTER_MASK GENMASK(7, 4) +#define OPT4060_CRC_MASK GENMASK(3, 0) + +/* OPT4060 device id mask */ +#define OPT4060_DEVICE_ID_MASK GENMASK(11, 0) + +/* OPT4060 control register masks */ +#define OPT4060_CTRL_QWAKE_MASK BIT(15) +#define OPT4060_CTRL_RANGE_MASK GENMASK(13, 10) +#define OPT4060_CTRL_CONV_TIME_MASK GENMASK(9, 6) +#define OPT4060_CTRL_OPER_MODE_MASK GENMASK(5, 4) +#define OPT4060_CTRL_LATCH_MASK BIT(3) +#define OPT4060_CTRL_INT_POL_MASK BIT(2) +#define OPT4060_CTRL_FAULT_COUNT_MASK GENMASK(1, 0) + +/* OPT4060 interrupt control register masks */ +#define OPT4060_INT_CTRL_THRESH_SEL GENMASK(6, 5) +#define OPT4060_INT_CTRL_OUTPUT BIT(4) +#define OPT4060_INT_CTRL_INT_CFG GENMASK(3, 2) +#define OPT4060_INT_CTRL_THRESHOLD 0x0 +#define OPT4060_INT_CTRL_NEXT_CH 0x1 +#define OPT4060_INT_CTRL_ALL_CH 0x3 + +/* OPT4060 result control register masks */ +#define OPT4060_RES_CTRL_OVERLOAD BIT(3) +#define OPT4060_RES_CTRL_CONV_READY BIT(2) +#define OPT4060_RES_CTRL_FLAG_H BIT(1) +#define OPT4060_RES_CTRL_FLAG_L BIT(0) + +/* OPT4060 constants */ +#define OPT4060_DEVICE_ID_VAL 0x821 + +/* OPT4060 operating modes */ +#define OPT4060_CTRL_OPER_MODE_OFF 0x0 +#define OPT4060_CTRL_OPER_MODE_FORCED 0x1 +#define OPT4060_CTRL_OPER_MODE_ONE_SHOT 0x2 +#define OPT4060_CTRL_OPER_MODE_CONTINUOUS 0x3 + +/* OPT4060 conversion control register definitions */ +#define OPT4060_CTRL_CONVERSION_0_6MS 0x0 +#define OPT4060_CTRL_CONVERSION_1MS 0x1 +#define OPT4060_CTRL_CONVERSION_1_8MS 0x2 +#define OPT4060_CTRL_CONVERSION_3_4MS 0x3 +#define OPT4060_CTRL_CONVERSION_6_5MS 0x4 +#define OPT4060_CTRL_CONVERSION_12_7MS 0x5 +#define OPT4060_CTRL_CONVERSION_25MS 0x6 +#define OPT4060_CTRL_CONVERSION_50MS 0x7 +#define OPT4060_CTRL_CONVERSION_100MS 0x8 +#define OPT4060_CTRL_CONVERSION_200MS 0x9 +#define OPT4060_CTRL_CONVERSION_400MS 0xa +#define OPT4060_CTRL_CONVERSION_800MS 0xb + +/* OPT4060 fault count control register definitions */ +#define OPT4060_CTRL_FAULT_COUNT_1 0x0 +#define OPT4060_CTRL_FAULT_COUNT_2 0x1 +#define OPT4060_CTRL_FAULT_COUNT_4 0x2 +#define OPT4060_CTRL_FAULT_COUNT_8 0x3 + +/* OPT4060 scale light level range definitions */ +#define OPT4060_CTRL_LIGHT_SCALE_AUTO 12 + +/* OPT4060 default values */ +#define OPT4060_DEFAULT_CONVERSION_TIME OPT4060_CTRL_CONVERSION_50MS + +/* + * enum opt4060_chan_type - OPT4060 channel types + * @OPT4060_RED: Red channel. + * @OPT4060_GREEN: Green channel. + * @OPT4060_BLUE: Blue channel. + * @OPT4060_CLEAR: Clear (white) channel. + * @OPT4060_ILLUM: Calculated illuminance channel. + * @OPT4060_NUM_CHANS: Number of channel types. + */ +enum opt4060_chan_type { + OPT4060_RED, + OPT4060_GREEN, + OPT4060_BLUE, + OPT4060_CLEAR, + OPT4060_ILLUM, + OPT4060_NUM_CHANS +}; + +struct opt4060_chip { + struct regmap *regmap; + struct device *dev; + struct iio_trigger *trig; + u8 int_time; + int irq; + /* + * Mutex for protecting sensor irq settings. Switching between interrupt + * on each sample and on thresholds needs to be synchronized. + */ + struct mutex irq_setting_lock; + /* + * Mutex for protecting event enabling. + */ + struct mutex event_enabling_lock; + struct completion completion; + bool thresh_event_lo_active; + bool thresh_event_hi_active; +}; + +struct opt4060_channel_factor { + u32 mul; + u32 div; +}; + +static const int opt4060_int_time_available[][2] = { + { 0, 600 }, + { 0, 1000 }, + { 0, 1800 }, + { 0, 3400 }, + { 0, 6500 }, + { 0, 12700 }, + { 0, 25000 }, + { 0, 50000 }, + { 0, 100000 }, + { 0, 200000 }, + { 0, 400000 }, + { 0, 800000 }, +}; + +/* + * Conversion time is integration time + time to set register + * this is used as integration time. + */ +static const int opt4060_int_time_reg[][2] = { + { 600, OPT4060_CTRL_CONVERSION_0_6MS }, + { 1000, OPT4060_CTRL_CONVERSION_1MS }, + { 1800, OPT4060_CTRL_CONVERSION_1_8MS }, + { 3400, OPT4060_CTRL_CONVERSION_3_4MS }, + { 6500, OPT4060_CTRL_CONVERSION_6_5MS }, + { 12700, OPT4060_CTRL_CONVERSION_12_7MS }, + { 25000, OPT4060_CTRL_CONVERSION_25MS }, + { 50000, OPT4060_CTRL_CONVERSION_50MS }, + { 100000, OPT4060_CTRL_CONVERSION_100MS }, + { 200000, OPT4060_CTRL_CONVERSION_200MS }, + { 400000, OPT4060_CTRL_CONVERSION_400MS }, + { 800000, OPT4060_CTRL_CONVERSION_800MS }, +}; + +static int opt4060_als_time_to_index(const u32 als_integration_time) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(opt4060_int_time_available); i++) { + if (als_integration_time == opt4060_int_time_available[i][1]) + return i; + } + + return -EINVAL; +} + +static u8 opt4060_calculate_crc(u8 exp, u32 mantissa, u8 count) +{ + u8 crc; + + /* + * Calculates a 4-bit CRC from a 20-bit mantissa, 4-bit exponent and a 4-bit counter. + * crc[0] = XOR(mantissa[19:0], exp[3:0], count[3:0]) + * crc[1] = XOR(mantissa[1,3,5,7,9,11,13,15,17,19], exp[1,3], count[1,3]) + * crc[2] = XOR(mantissa[3,7,11,15,19], exp[3], count[3]) + * crc[3] = XOR(mantissa[3,11,19]) + */ + crc = (hweight32(mantissa) + hweight32(exp) + hweight32(count)) % 2; + crc |= ((hweight32(mantissa & 0xAAAAA) + hweight32(exp & 0xA) + + hweight32(count & 0xA)) % 2) << 1; + crc |= ((hweight32(mantissa & 0x88888) + hweight32(exp & 0x8) + + hweight32(count & 0x8)) % 2) << 2; + crc |= (hweight32(mantissa & 0x80808) % 2) << 3; + + return crc; +} + +static int opt4060_set_int_state(struct opt4060_chip *chip, u32 state) +{ + int ret; + unsigned int regval; + + guard(mutex)(&chip->irq_setting_lock); + + regval = FIELD_PREP(OPT4060_INT_CTRL_INT_CFG, state); + ret = regmap_update_bits(chip->regmap, OPT4060_INT_CTRL, + OPT4060_INT_CTRL_INT_CFG, regval); + if (ret) + dev_err(chip->dev, "Failed to set interrupt config\n"); + return ret; +} + +static int opt4060_set_sampling_mode(struct opt4060_chip *chip, + bool continuous) +{ + unsigned int reg; + int ret; + + ret = regmap_read(chip->regmap, OPT4060_CTRL, ®); + if (ret < 0) { + dev_err(chip->dev, "Failed to read ctrl register\n"); + return ret; + } + reg &= ~OPT4060_CTRL_OPER_MODE_MASK; + if (continuous) + reg |= FIELD_PREP(OPT4060_CTRL_OPER_MODE_MASK, + OPT4060_CTRL_OPER_MODE_CONTINUOUS); + else + reg |= FIELD_PREP(OPT4060_CTRL_OPER_MODE_MASK, + OPT4060_CTRL_OPER_MODE_ONE_SHOT); + + /* + * Trigger a new conversions by writing to CRTL register. It is not + * possible to use regmap_update_bits() since that will only write when + * data is modified. + */ + ret = regmap_write(chip->regmap, OPT4060_CTRL, reg); + if (ret) + dev_err(chip->dev, "Failed to set ctrl register\n"); + return ret; +} + +static bool opt4060_event_active(struct opt4060_chip *chip) +{ + return chip->thresh_event_lo_active || chip->thresh_event_hi_active; +} + +static int opt4060_set_state_common(struct opt4060_chip *chip, + bool continuous_sampling, + bool continuous_irq) +{ + int ret = 0; + + /* It is important to setup irq before sampling to avoid missing samples. */ + if (continuous_irq) + ret = opt4060_set_int_state(chip, OPT4060_INT_CTRL_ALL_CH); + else + ret = opt4060_set_int_state(chip, OPT4060_INT_CTRL_THRESHOLD); + if (ret) { + dev_err(chip->dev, "Failed to set irq state.\n"); + return ret; + } + + if (continuous_sampling || opt4060_event_active(chip)) + ret = opt4060_set_sampling_mode(chip, true); + else + ret = opt4060_set_sampling_mode(chip, false); + if (ret) + dev_err(chip->dev, "Failed to set sampling state.\n"); + return ret; +} + +/* + * Function for setting the driver state for sampling and irq. Either direct + * mode of buffer mode will be claimed during the transition to prevent races + * between sysfs read, buffer or events. + */ +static int opt4060_set_driver_state(struct iio_dev *indio_dev, + bool continuous_sampling, + bool continuous_irq) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + int ret = 0; +any_mode_retry: + if (iio_device_claim_buffer_mode(indio_dev)) { + /* + * This one is a *bit* hacky. If we cannot claim buffer mode, + * then try direct mode so that we make sure things cannot + * concurrently change. And we just keep trying until we get one + * of the modes... + */ + if (iio_device_claim_direct_mode(indio_dev)) + goto any_mode_retry; + /* + * This path means that we managed to claim direct mode. In + * this case the buffer isn't enabled and it's okay to leave + * continuous mode for sampling and/or irq. + */ + ret = opt4060_set_state_common(chip, continuous_sampling, + continuous_irq); + iio_device_release_direct_mode(indio_dev); + } else { + /* + * This path means that we managed to claim buffer mode. In + * this case the buffer is enabled and irq and sampling must go + * to or remain continuous, but only if the trigger is from this + * device. + */ + if (!iio_trigger_validate_own_device(indio_dev->trig, indio_dev)) + ret = opt4060_set_state_common(chip, true, true); + else + ret = opt4060_set_state_common(chip, continuous_sampling, + continuous_irq); + iio_device_release_buffer_mode(indio_dev); + } + return ret; +} + +/* + * This function is called with framework mutex locked. + */ +static int opt4060_trigger_set_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct opt4060_chip *chip = iio_priv(indio_dev); + + return opt4060_set_state_common(chip, state, state); +} + +static int opt4060_read_raw_value(struct opt4060_chip *chip, + unsigned long address, u32 *raw) +{ + int ret; + u16 result[2]; + u32 mantissa_raw; + u16 msb, lsb; + u8 exp, count, crc, calc_crc; + + ret = regmap_bulk_read(chip->regmap, address, result, 2); + if (ret) { + dev_err(chip->dev, "Reading channel data failed\n"); + return ret; + } + exp = FIELD_GET(OPT4060_EXPONENT_MASK, result[0]); + msb = FIELD_GET(OPT4060_MSB_MASK, result[0]); + count = FIELD_GET(OPT4060_COUNTER_MASK, result[1]); + crc = FIELD_GET(OPT4060_CRC_MASK, result[1]); + lsb = FIELD_GET(OPT4060_LSB_MASK, result[1]); + mantissa_raw = (msb << 8) + lsb; + calc_crc = opt4060_calculate_crc(exp, mantissa_raw, count); + if (calc_crc != crc) + return -EIO; + *raw = mantissa_raw << exp; + return 0; +} + +static int opt4060_trigger_new_samples(struct iio_dev *indio_dev) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + int ret; + + /* + * The conversion time should be 500us startup time plus the integration time + * times the number of channels. An exact timeout isn't critical, it's better + * not to get incorrect errors in the log. Setting the timeout to double the + * theoretical time plus and extra 100ms margin. + */ + unsigned int timeout_us = (500 + OPT4060_NUM_CHANS * + opt4060_int_time_reg[chip->int_time][0]) * 2 + 100000; + + /* Setting the state in one shot mode with irq on each sample. */ + ret = opt4060_set_driver_state(indio_dev, false, true); + if (ret) + return ret; + + if (chip->irq) { + guard(mutex)(&chip->irq_setting_lock); + reinit_completion(&chip->completion); + if (wait_for_completion_timeout(&chip->completion, + usecs_to_jiffies(timeout_us)) == 0) { + dev_err(chip->dev, "Completion timed out.\n"); + return -ETIME; + } + } else { + unsigned int ready; + + ret = regmap_read_poll_timeout(chip->regmap, OPT4060_RES_CTRL, + ready, (ready & OPT4060_RES_CTRL_CONV_READY), + 1000, timeout_us); + if (ret) + dev_err(chip->dev, "Conversion ready did not finish within timeout.\n"); + } + /* Setting the state in one shot mode with irq on thresholds. */ + return opt4060_set_driver_state(indio_dev, false, false); +} + +static int opt4060_read_chan_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + u32 adc_raw; + int ret; + + ret = opt4060_trigger_new_samples(indio_dev); + if (ret) { + dev_err(chip->dev, "Failed to trigger new samples.\n"); + return ret; + } + + ret = opt4060_read_raw_value(chip, chan->address, &adc_raw); + if (ret) { + dev_err(chip->dev, "Reading raw channel data failed.\n"); + return ret; + } + *val = adc_raw; + return IIO_VAL_INT; +} + +/* + * Returns the scale values used for red, green and blue. Scales the raw value + * so that for a particular test light source, typically white, the measurement + * intensity is the same across different color channels. + */ +static int opt4060_get_chan_scale(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + + switch (chan->scan_index) { + case OPT4060_RED: + /* 2.4 */ + *val = 2; + *val2 = 400000; + break; + case OPT4060_GREEN: + /* 1.0 */ + *val = 1; + *val2 = 0; + break; + case OPT4060_BLUE: + /* 1.3 */ + *val = 1; + *val2 = 300000; + break; + default: + dev_err(chip->dev, "Unexpected channel index.\n"); + return -EINVAL; + } + return IIO_VAL_INT_PLUS_MICRO; +} + +static int opt4060_calc_illuminance(struct opt4060_chip *chip, int *val) +{ + u32 lux_raw; + int ret; + + /* The green wide spectral channel is used for illuminance. */ + ret = opt4060_read_raw_value(chip, OPT4060_GREEN_MSB, &lux_raw); + if (ret) { + dev_err(chip->dev, "Reading raw channel data failed\n"); + return ret; + } + + /* Illuminance is calculated by ADC_RAW * 2.15e-3. */ + *val = DIV_U64_ROUND_CLOSEST((u64)(lux_raw * 215), 1000); + return ret; +} + +static int opt4060_read_illuminance(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + int ret; + + ret = opt4060_trigger_new_samples(indio_dev); + if (ret) { + dev_err(chip->dev, "Failed to trigger new samples.\n"); + return ret; + } + ret = opt4060_calc_illuminance(chip, val); + if (ret) { + dev_err(chip->dev, "Failed to calculate illuminance.\n"); + return ret; + } + + return IIO_VAL_INT; +} + +static int opt4060_set_int_time(struct opt4060_chip *chip) +{ + unsigned int regval; + int ret; + + regval = FIELD_PREP(OPT4060_CTRL_CONV_TIME_MASK, chip->int_time); + ret = regmap_update_bits(chip->regmap, OPT4060_CTRL, + OPT4060_CTRL_CONV_TIME_MASK, regval); + if (ret) + dev_err(chip->dev, "Failed to set integration time.\n"); + + return ret; +} + +static int opt4060_power_down(struct opt4060_chip *chip) +{ + int ret; + + ret = regmap_clear_bits(chip->regmap, OPT4060_CTRL, OPT4060_CTRL_OPER_MODE_MASK); + if (ret) + dev_err(chip->dev, "Failed to power down\n"); + + return ret; +} + +static void opt4060_chip_off_action(void *chip) +{ + opt4060_power_down(chip); +} + +#define _OPT4060_COLOR_CHANNEL(_color, _mask, _ev_spec, _num_ev_spec) \ +{ \ + .type = IIO_INTENSITY, \ + .modified = 1, \ + .channel2 = IIO_MOD_LIGHT_##_color, \ + .info_mask_separate = _mask, \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), \ + .address = OPT4060_##_color##_MSB, \ + .scan_index = OPT4060_##_color, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_CPU, \ + }, \ + .event_spec = _ev_spec, \ + .num_event_specs = _num_ev_spec, \ +} + +#define OPT4060_COLOR_CHANNEL(_color, _mask) \ + _OPT4060_COLOR_CHANNEL(_color, _mask, opt4060_event_spec, \ + ARRAY_SIZE(opt4060_event_spec)) \ + +#define OPT4060_COLOR_CHANNEL_NO_EVENTS(_color, _mask) \ + _OPT4060_COLOR_CHANNEL(_color, _mask, NULL, 0) \ + +#define OPT4060_LIGHT_CHANNEL(_channel) \ +{ \ + .type = IIO_LIGHT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), \ + .scan_index = OPT4060_##_channel, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_CPU, \ + }, \ +} + +static const struct iio_event_spec opt4060_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_PERIOD), + }, +}; + +static const struct iio_chan_spec opt4060_channels[] = { + OPT4060_COLOR_CHANNEL(RED, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)), + OPT4060_COLOR_CHANNEL(GREEN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)), + OPT4060_COLOR_CHANNEL(BLUE, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)), + OPT4060_COLOR_CHANNEL(CLEAR, BIT(IIO_CHAN_INFO_RAW)), + OPT4060_LIGHT_CHANNEL(ILLUM), + IIO_CHAN_SOFT_TIMESTAMP(OPT4060_NUM_CHANS), +}; + +static const struct iio_chan_spec opt4060_channels_no_events[] = { + OPT4060_COLOR_CHANNEL_NO_EVENTS(RED, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)), + OPT4060_COLOR_CHANNEL_NO_EVENTS(GREEN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)), + OPT4060_COLOR_CHANNEL_NO_EVENTS(BLUE, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)), + OPT4060_COLOR_CHANNEL_NO_EVENTS(CLEAR, BIT(IIO_CHAN_INFO_RAW)), + OPT4060_LIGHT_CHANNEL(ILLUM), + IIO_CHAN_SOFT_TIMESTAMP(OPT4060_NUM_CHANS), +}; + +static int opt4060_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return opt4060_read_chan_raw(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + return opt4060_get_chan_scale(indio_dev, chan, val, val2); + case IIO_CHAN_INFO_PROCESSED: + return opt4060_read_illuminance(indio_dev, chan, val); + case IIO_CHAN_INFO_INT_TIME: + *val = 0; + *val2 = opt4060_int_time_reg[chip->int_time][0]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int opt4060_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + int int_time; + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + int_time = opt4060_als_time_to_index(val2); + if (int_time < 0) + return int_time; + chip->int_time = int_time; + return opt4060_set_int_time(chip); + default: + return -EINVAL; + } +} + +static int opt4060_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static u32 opt4060_calc_th_reg(u32 adc_val) +{ + u32 th_val, th_exp, bits; + /* + * The threshold registers take 4 bits of exponent and 12 bits of data + * ADC = TH_VAL << (8 + TH_EXP) + */ + bits = fls(adc_val); + + if (bits > 31) + th_exp = 11; /* Maximum exponent */ + else if (bits > 20) + th_exp = bits - 20; + else + th_exp = 0; + th_val = (adc_val >> (8 + th_exp)) & 0xfff; + + return (th_exp << 12) + th_val; +} + +static u32 opt4060_calc_val_from_th_reg(u32 th_reg) +{ + /* + * The threshold registers take 4 bits of exponent and 12 bits of data + * ADC = TH_VAL << (8 + TH_EXP) + */ + u32 th_val, th_exp; + + th_exp = (th_reg >> 12) & 0xf; + th_val = th_reg & 0xfff; + + return th_val << (8 + th_exp); +} + +static int opt4060_read_available(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + *length = ARRAY_SIZE(opt4060_int_time_available) * 2; + *vals = (const int *)opt4060_int_time_available; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + + default: + return -EINVAL; + } +} + +static ssize_t opt4060_read_ev_period(struct opt4060_chip *chip, int *val, + int *val2) +{ + int ret, pers, fault_count, int_time; + u64 uval; + + int_time = opt4060_int_time_reg[chip->int_time][0]; + + ret = regmap_read(chip->regmap, OPT4060_CTRL, &fault_count); + if (ret < 0) + return ret; + + fault_count = fault_count & OPT4060_CTRL_FAULT_COUNT_MASK; + switch (fault_count) { + case OPT4060_CTRL_FAULT_COUNT_2: + pers = 2; + break; + case OPT4060_CTRL_FAULT_COUNT_4: + pers = 4; + break; + case OPT4060_CTRL_FAULT_COUNT_8: + pers = 8; + break; + + default: + pers = 1; + break; + } + + uval = mul_u32_u32(int_time, pers); + *val = div_u64_rem(uval, MICRO, val2); + + return IIO_VAL_INT_PLUS_MICRO; +} + +static ssize_t opt4060_write_ev_period(struct opt4060_chip *chip, int val, + int val2) +{ + u64 uval, int_time; + unsigned int regval, fault_count_val; + + uval = mul_u32_u32(val, MICRO) + val2; + int_time = opt4060_int_time_reg[chip->int_time][0]; + + /* Check if the period is closest to 1, 2, 4 or 8 times integration time.*/ + if (uval <= int_time) + fault_count_val = OPT4060_CTRL_FAULT_COUNT_1; + else if (uval <= int_time * 2) + fault_count_val = OPT4060_CTRL_FAULT_COUNT_2; + else if (uval <= int_time * 4) + fault_count_val = OPT4060_CTRL_FAULT_COUNT_4; + else + fault_count_val = OPT4060_CTRL_FAULT_COUNT_8; + + regval = FIELD_PREP(OPT4060_CTRL_FAULT_COUNT_MASK, fault_count_val); + return regmap_update_bits(chip->regmap, OPT4060_CTRL, + OPT4060_CTRL_FAULT_COUNT_MASK, regval); +} + +static int opt4060_get_channel_sel(struct opt4060_chip *chip, int *ch_sel) +{ + int ret; + u32 regval; + + ret = regmap_read(chip->regmap, OPT4060_INT_CTRL, ®val); + if (ret) { + dev_err(chip->dev, "Failed to get channel selection.\n"); + return ret; + } + *ch_sel = FIELD_GET(OPT4060_INT_CTRL_THRESH_SEL, regval); + return ret; +} + +static int opt4060_set_channel_sel(struct opt4060_chip *chip, int ch_sel) +{ + int ret; + u32 regval; + + regval = FIELD_PREP(OPT4060_INT_CTRL_THRESH_SEL, ch_sel); + ret = regmap_update_bits(chip->regmap, OPT4060_INT_CTRL, + OPT4060_INT_CTRL_THRESH_SEL, regval); + if (ret) + dev_err(chip->dev, "Failed to set channel selection.\n"); + return ret; +} + +static int opt4060_get_thresholds(struct opt4060_chip *chip, u32 *th_lo, u32 *th_hi) +{ + int ret; + u32 regval; + + ret = regmap_read(chip->regmap, OPT4060_THRESHOLD_LOW, ®val); + if (ret) { + dev_err(chip->dev, "Failed to read THRESHOLD_LOW.\n"); + return ret; + } + *th_lo = opt4060_calc_val_from_th_reg(regval); + + ret = regmap_read(chip->regmap, OPT4060_THRESHOLD_HIGH, ®val); + if (ret) { + dev_err(chip->dev, "Failed to read THRESHOLD_LOW.\n"); + return ret; + } + *th_hi = opt4060_calc_val_from_th_reg(regval); + + return ret; +} + +static int opt4060_set_thresholds(struct opt4060_chip *chip, u32 th_lo, u32 th_hi) +{ + int ret; + + ret = regmap_write(chip->regmap, OPT4060_THRESHOLD_LOW, opt4060_calc_th_reg(th_lo)); + if (ret) { + dev_err(chip->dev, "Failed to write THRESHOLD_LOW.\n"); + return ret; + } + + ret = regmap_write(chip->regmap, OPT4060_THRESHOLD_HIGH, opt4060_calc_th_reg(th_hi)); + if (ret) + dev_err(chip->dev, "Failed to write THRESHOLD_HIGH.\n"); + + return ret; +} + +static int opt4060_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + u32 th_lo, th_hi; + int ret; + + if (chan->type != IIO_INTENSITY) + return -EINVAL; + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + ret = opt4060_get_thresholds(chip, &th_lo, &th_hi); + if (ret) + return ret; + if (dir == IIO_EV_DIR_FALLING) { + *val = th_lo; + ret = IIO_VAL_INT; + } else if (dir == IIO_EV_DIR_RISING) { + *val = th_hi; + ret = IIO_VAL_INT; + } + return ret; + case IIO_EV_INFO_PERIOD: + return opt4060_read_ev_period(chip, val, val2); + default: + return -EINVAL; + } +} + +static int opt4060_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + u32 th_lo, th_hi; + int ret; + + if (chan->type != IIO_INTENSITY) + return -EINVAL; + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + ret = opt4060_get_thresholds(chip, &th_lo, &th_hi); + if (ret) + return ret; + if (dir == IIO_EV_DIR_FALLING) + th_lo = val; + else if (dir == IIO_EV_DIR_RISING) + th_hi = val; + return opt4060_set_thresholds(chip, th_lo, th_hi); + case IIO_EV_INFO_PERIOD: + return opt4060_write_ev_period(chip, val, val2); + default: + return -EINVAL; + } +} + +static int opt4060_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + int ch_sel, ch_idx = chan->scan_index; + struct opt4060_chip *chip = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_INTENSITY) + return -EINVAL; + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + ret = opt4060_get_channel_sel(chip, &ch_sel); + if (ret) + return ret; + + if (((dir == IIO_EV_DIR_FALLING) && chip->thresh_event_lo_active) || + ((dir == IIO_EV_DIR_RISING) && chip->thresh_event_hi_active)) + return ch_sel == ch_idx; + + return ret; +} + +static int opt4060_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + int ch_sel, ch_idx = chan->scan_index; + struct opt4060_chip *chip = iio_priv(indio_dev); + int ret; + + guard(mutex)(&chip->event_enabling_lock); + + if (chan->type != IIO_INTENSITY) + return -EINVAL; + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + ret = opt4060_get_channel_sel(chip, &ch_sel); + if (ret) + return ret; + + if (state) { + /* Only one channel can be active at the same time */ + if ((chip->thresh_event_lo_active || chip->thresh_event_hi_active) && + (ch_idx != ch_sel)) + return -EBUSY; + if (dir == IIO_EV_DIR_FALLING) + chip->thresh_event_lo_active = true; + else if (dir == IIO_EV_DIR_RISING) + chip->thresh_event_hi_active = true; + ret = opt4060_set_channel_sel(chip, ch_idx); + if (ret) + return ret; + } else { + if (ch_idx == ch_sel) { + if (dir == IIO_EV_DIR_FALLING) + chip->thresh_event_lo_active = false; + else if (dir == IIO_EV_DIR_RISING) + chip->thresh_event_hi_active = false; + } + } + + return opt4060_set_driver_state(indio_dev, + chip->thresh_event_hi_active | + chip->thresh_event_lo_active, + false); +} + +static const struct iio_info opt4060_info = { + .read_raw = opt4060_read_raw, + .write_raw = opt4060_write_raw, + .write_raw_get_fmt = opt4060_write_raw_get_fmt, + .read_avail = opt4060_read_available, + .read_event_value = opt4060_read_event, + .write_event_value = opt4060_write_event, + .read_event_config = opt4060_read_event_config, + .write_event_config = opt4060_write_event_config, +}; + +static const struct iio_info opt4060_info_no_irq = { + .read_raw = opt4060_read_raw, + .write_raw = opt4060_write_raw, + .write_raw_get_fmt = opt4060_write_raw_get_fmt, + .read_avail = opt4060_read_available, +}; + +static int opt4060_load_defaults(struct opt4060_chip *chip) +{ + u16 reg; + int ret; + + chip->int_time = OPT4060_DEFAULT_CONVERSION_TIME; + + /* Set initial MIN/MAX thresholds */ + ret = opt4060_set_thresholds(chip, 0, UINT_MAX); + if (ret) + return ret; + + /* + * Setting auto-range, latched window for thresholds, one-shot conversion + * and quick wake-up mode as default. + */ + reg = FIELD_PREP(OPT4060_CTRL_RANGE_MASK, + OPT4060_CTRL_LIGHT_SCALE_AUTO); + reg |= FIELD_PREP(OPT4060_CTRL_CONV_TIME_MASK, chip->int_time); + reg |= FIELD_PREP(OPT4060_CTRL_OPER_MODE_MASK, + OPT4060_CTRL_OPER_MODE_ONE_SHOT); + reg |= OPT4060_CTRL_QWAKE_MASK | OPT4060_CTRL_LATCH_MASK; + + ret = regmap_write(chip->regmap, OPT4060_CTRL, reg); + if (ret) + dev_err(chip->dev, "Failed to set configuration\n"); + + return ret; +} + +static bool opt4060_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg <= OPT4060_CLEAR_LSB || reg == OPT4060_RES_CTRL; +} + +static bool opt4060_writable_reg(struct device *dev, unsigned int reg) +{ + return reg >= OPT4060_THRESHOLD_LOW || reg >= OPT4060_INT_CTRL; +} + +static bool opt4060_readonly_reg(struct device *dev, unsigned int reg) +{ + return reg == OPT4060_DEVICE_ID; +} + +static bool opt4060_readable_reg(struct device *dev, unsigned int reg) +{ + /* Volatile, writable and read-only registers are readable. */ + return opt4060_volatile_reg(dev, reg) || opt4060_writable_reg(dev, reg) || + opt4060_readonly_reg(dev, reg); +} + +static const struct regmap_config opt4060_regmap_config = { + .name = "opt4060", + .reg_bits = 8, + .val_bits = 16, + .cache_type = REGCACHE_RBTREE, + .max_register = OPT4060_DEVICE_ID, + .readable_reg = opt4060_readable_reg, + .writeable_reg = opt4060_writable_reg, + .volatile_reg = opt4060_volatile_reg, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + +static const struct iio_trigger_ops opt4060_trigger_ops = { + .set_trigger_state = opt4060_trigger_set_state, +}; + +static irqreturn_t opt4060_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *idev = pf->indio_dev; + struct opt4060_chip *chip = iio_priv(idev); + struct { + u32 chan[OPT4060_NUM_CHANS]; + aligned_s64 ts; + } raw; + int i = 0; + int chan, ret; + + /* If the trigger is not from this driver, a new sample is needed.*/ + if (iio_trigger_validate_own_device(idev->trig, idev)) + opt4060_trigger_new_samples(idev); + + memset(&raw, 0, sizeof(raw)); + + iio_for_each_active_channel(idev, chan) { + if (chan == OPT4060_ILLUM) + ret = opt4060_calc_illuminance(chip, &raw.chan[i++]); + else + ret = opt4060_read_raw_value(chip, + idev->channels[chan].address, + &raw.chan[i++]); + if (ret) { + dev_err(chip->dev, "Reading channel data failed\n"); + goto err_read; + } + } + + iio_push_to_buffers_with_timestamp(idev, &raw, pf->timestamp); +err_read: + iio_trigger_notify_done(idev->trig); + return IRQ_HANDLED; +} + +static irqreturn_t opt4060_irq_thread(int irq, void *private) +{ + struct iio_dev *idev = private; + struct opt4060_chip *chip = iio_priv(idev); + int ret, dummy; + unsigned int int_res; + + ret = regmap_read(chip->regmap, OPT4060_RES_CTRL, &int_res); + if (ret < 0) { + dev_err(chip->dev, "Failed to read interrupt reasons.\n"); + return IRQ_NONE; + } + + /* Read OPT4060_CTRL to clear interrupt */ + ret = regmap_read(chip->regmap, OPT4060_CTRL, &dummy); + if (ret < 0) { + dev_err(chip->dev, "Failed to clear interrupt\n"); + return IRQ_NONE; + } + + /* Handle events */ + if (int_res & (OPT4060_RES_CTRL_FLAG_H | OPT4060_RES_CTRL_FLAG_L)) { + u64 code; + int chan = 0; + + ret = opt4060_get_channel_sel(chip, &chan); + if (ret) { + dev_err(chip->dev, "Failed to read threshold channel.\n"); + return IRQ_NONE; + } + + /* Check if the interrupt is from the lower threshold */ + if (int_res & OPT4060_RES_CTRL_FLAG_L) { + code = IIO_MOD_EVENT_CODE(IIO_INTENSITY, + chan, + idev->channels[chan].channel2, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING); + iio_push_event(idev, code, iio_get_time_ns(idev)); + } + /* Check if the interrupt is from the upper threshold */ + if (int_res & OPT4060_RES_CTRL_FLAG_H) { + code = IIO_MOD_EVENT_CODE(IIO_INTENSITY, + chan, + idev->channels[chan].channel2, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING); + iio_push_event(idev, code, iio_get_time_ns(idev)); + } + } + + /* Handle conversion ready */ + if (int_res & OPT4060_RES_CTRL_CONV_READY) { + /* Signal completion for potentially waiting reads */ + complete(&chip->completion); + + /* Handle data ready triggers */ + if (iio_buffer_enabled(idev)) + iio_trigger_poll_nested(chip->trig); + } + return IRQ_HANDLED; +} + +static int opt4060_setup_buffer(struct opt4060_chip *chip, struct iio_dev *idev) +{ + int ret; + + ret = devm_iio_triggered_buffer_setup(chip->dev, idev, + &iio_pollfunc_store_time, + opt4060_trigger_handler, NULL); + if (ret) + return dev_err_probe(chip->dev, ret, + "Buffer setup failed.\n"); + return ret; +} + +static int opt4060_setup_trigger(struct opt4060_chip *chip, struct iio_dev *idev) +{ + struct iio_trigger *data_trigger; + char *name; + int ret; + + data_trigger = devm_iio_trigger_alloc(chip->dev, "%s-data-ready-dev%d", + idev->name, iio_device_id(idev)); + if (!data_trigger) + return -ENOMEM; + + /* + * The data trigger allows for sample capture on each new conversion + * ready interrupt. + */ + chip->trig = data_trigger; + data_trigger->ops = &opt4060_trigger_ops; + iio_trigger_set_drvdata(data_trigger, idev); + ret = devm_iio_trigger_register(chip->dev, data_trigger); + if (ret) + return dev_err_probe(chip->dev, ret, + "Data ready trigger registration failed\n"); + + name = devm_kasprintf(chip->dev, GFP_KERNEL, "%s-opt4060", + dev_name(chip->dev)); + if (!name) + return dev_err_probe(chip->dev, -ENOMEM, "Failed to alloc chip name\n"); + + ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL, opt4060_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + name, idev); + if (ret) + return dev_err_probe(chip->dev, ret, "Could not request IRQ\n"); + + init_completion(&chip->completion); + + ret = devm_mutex_init(chip->dev, &chip->irq_setting_lock); + if (ret) + return ret; + + ret = devm_mutex_init(chip->dev, &chip->event_enabling_lock); + if (ret) + return ret; + + ret = regmap_write_bits(chip->regmap, OPT4060_INT_CTRL, + OPT4060_INT_CTRL_OUTPUT, + OPT4060_INT_CTRL_OUTPUT); + if (ret) + return dev_err_probe(chip->dev, ret, + "Failed to set interrupt as output\n"); + + return 0; +} + +static int opt4060_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct opt4060_chip *chip; + struct iio_dev *indio_dev; + int ret; + unsigned int regval, dev_id; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable vdd supply\n"); + + chip->regmap = devm_regmap_init_i2c(client, &opt4060_regmap_config); + if (IS_ERR(chip->regmap)) + return dev_err_probe(dev, PTR_ERR(chip->regmap), + "regmap initialization failed\n"); + + chip->dev = dev; + chip->irq = client->irq; + + ret = regmap_reinit_cache(chip->regmap, &opt4060_regmap_config); + if (ret) + return dev_err_probe(dev, ret, + "failed to reinit regmap cache\n"); + + ret = regmap_read(chip->regmap, OPT4060_DEVICE_ID, ®val); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to read the device ID register\n"); + + dev_id = FIELD_GET(OPT4060_DEVICE_ID_MASK, regval); + if (dev_id != OPT4060_DEVICE_ID_VAL) + dev_info(dev, "Device ID: %#04x unknown\n", dev_id); + + if (chip->irq) { + indio_dev->info = &opt4060_info; + indio_dev->channels = opt4060_channels; + indio_dev->num_channels = ARRAY_SIZE(opt4060_channels); + } else { + indio_dev->info = &opt4060_info_no_irq; + indio_dev->channels = opt4060_channels_no_events; + indio_dev->num_channels = ARRAY_SIZE(opt4060_channels_no_events); + } + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = "opt4060"; + + ret = opt4060_load_defaults(chip); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to set sensor defaults\n"); + + ret = devm_add_action_or_reset(dev, opt4060_chip_off_action, chip); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to setup power off action\n"); + + ret = opt4060_setup_buffer(chip, indio_dev); + if (ret) + return ret; + + if (chip->irq) { + ret = opt4060_setup_trigger(chip, indio_dev); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct i2c_device_id opt4060_id[] = { + { "opt4060", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, opt4060_id); + +static const struct of_device_id opt4060_of_match[] = { + { .compatible = "ti,opt4060" }, + { } +}; +MODULE_DEVICE_TABLE(of, opt4060_of_match); + +static struct i2c_driver opt4060_driver = { + .driver = { + .name = "opt4060", + .of_match_table = opt4060_of_match, + }, + .probe = opt4060_probe, + .id_table = opt4060_id, +}; +module_i2c_driver(opt4060_driver); + +MODULE_AUTHOR("Per-Daniel Olsson <perdaniel.olsson@axis.com>"); +MODULE_DESCRIPTION("Texas Instruments OPT4060 RGBW color sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/rohm-bu27008.c b/drivers/iio/light/rohm-bu27008.c deleted file mode 100644 index fa35dd32700c..000000000000 --- a/drivers/iio/light/rohm-bu27008.c +++ /dev/null @@ -1,1635 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * ROHM Colour Sensor driver for - * - BU27008 RGBC sensor - * - BU27010 RGBC + Flickering sensor - * - * Copyright (c) 2023, ROHM Semiconductor. - */ - -#include <linux/bitfield.h> -#include <linux/bitops.h> -#include <linux/device.h> -#include <linux/i2c.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/property.h> -#include <linux/regmap.h> -#include <linux/regulator/consumer.h> -#include <linux/units.h> - -#include <linux/iio/iio.h> -#include <linux/iio/iio-gts-helper.h> -#include <linux/iio/trigger.h> -#include <linux/iio/trigger_consumer.h> -#include <linux/iio/triggered_buffer.h> - -/* - * A word about register address and mask definitions. - * - * At a quick glance to the data-sheet register tables, the BU27010 has all the - * registers that the BU27008 has. On top of that the BU27010 adds couple of new - * ones. - * - * So, all definitions BU27008_REG_* are there also for BU27010 but none of the - * BU27010_REG_* are present on BU27008. This makes sense as BU27010 just adds - * some features (Flicker FIFO, more power control) on top of the BU27008. - * - * Unfortunately, some of the wheel has been re-invented. Even though the names - * of the registers have stayed the same, pretty much all of the functionality - * provided by the registers has changed place. Contents of all MODE_CONTROL - * registers on BU27008 and BU27010 are different. - * - * Chip-specific mapping from register addresses/bits to functionality is done - * in bu27_chip_data structures. - */ -#define BU27008_REG_SYSTEM_CONTROL 0x40 -#define BU27008_MASK_SW_RESET BIT(7) -#define BU27008_MASK_PART_ID GENMASK(5, 0) -#define BU27008_ID 0x1a -#define BU27008_REG_MODE_CONTROL1 0x41 -#define BU27008_MASK_MEAS_MODE GENMASK(2, 0) -#define BU27008_MASK_CHAN_SEL GENMASK(3, 2) - -#define BU27008_REG_MODE_CONTROL2 0x42 -#define BU27008_MASK_RGBC_GAIN GENMASK(7, 3) -#define BU27008_MASK_IR_GAIN_LO GENMASK(2, 0) -#define BU27008_SHIFT_IR_GAIN 3 - -#define BU27008_REG_MODE_CONTROL3 0x43 -#define BU27008_MASK_VALID BIT(7) -#define BU27008_MASK_INT_EN BIT(1) -#define BU27008_INT_EN BU27008_MASK_INT_EN -#define BU27008_INT_DIS 0 -#define BU27008_MASK_MEAS_EN BIT(0) -#define BU27008_MEAS_EN BIT(0) -#define BU27008_MEAS_DIS 0 - -#define BU27008_REG_DATA0_LO 0x50 -#define BU27008_REG_DATA1_LO 0x52 -#define BU27008_REG_DATA2_LO 0x54 -#define BU27008_REG_DATA3_LO 0x56 -#define BU27008_REG_DATA3_HI 0x57 -#define BU27008_REG_MANUFACTURER_ID 0x92 -#define BU27008_REG_MAX BU27008_REG_MANUFACTURER_ID - -/* BU27010 specific definitions */ - -#define BU27010_MASK_SW_RESET BIT(7) -#define BU27010_ID 0x1b -#define BU27010_REG_POWER 0x3e -#define BU27010_MASK_POWER BIT(0) - -#define BU27010_REG_RESET 0x3f -#define BU27010_MASK_RESET BIT(0) -#define BU27010_RESET_RELEASE BU27010_MASK_RESET - -#define BU27010_MASK_MEAS_EN BIT(1) - -#define BU27010_MASK_CHAN_SEL GENMASK(7, 6) -#define BU27010_MASK_MEAS_MODE GENMASK(5, 4) -#define BU27010_MASK_RGBC_GAIN GENMASK(3, 0) - -#define BU27010_MASK_DATA3_GAIN GENMASK(7, 6) -#define BU27010_MASK_DATA2_GAIN GENMASK(5, 4) -#define BU27010_MASK_DATA1_GAIN GENMASK(3, 2) -#define BU27010_MASK_DATA0_GAIN GENMASK(1, 0) - -#define BU27010_MASK_FLC_MODE BIT(7) -#define BU27010_MASK_FLC_GAIN GENMASK(4, 0) - -#define BU27010_REG_MODE_CONTROL4 0x44 -/* If flicker is ever to be supported the IRQ must be handled as a field */ -#define BU27010_IRQ_DIS_ALL GENMASK(1, 0) -#define BU27010_DRDY_EN BIT(0) -#define BU27010_MASK_INT_SEL GENMASK(1, 0) - -#define BU27010_REG_MODE_CONTROL5 0x45 -#define BU27010_MASK_RGB_VALID BIT(7) -#define BU27010_MASK_FLC_VALID BIT(6) -#define BU27010_MASK_WAIT_EN BIT(3) -#define BU27010_MASK_FIFO_EN BIT(2) -#define BU27010_MASK_RGB_EN BIT(1) -#define BU27010_MASK_FLC_EN BIT(0) - -#define BU27010_REG_DATA_FLICKER_LO 0x56 -#define BU27010_MASK_DATA_FLICKER_HI GENMASK(2, 0) -#define BU27010_REG_FLICKER_COUNT 0x5a -#define BU27010_REG_FIFO_LEVEL_LO 0x5b -#define BU27010_MASK_FIFO_LEVEL_HI BIT(0) -#define BU27010_REG_FIFO_DATA_LO 0x5d -#define BU27010_REG_FIFO_DATA_HI 0x5e -#define BU27010_MASK_FIFO_DATA_HI GENMASK(2, 0) -#define BU27010_REG_MANUFACTURER_ID 0x92 -#define BU27010_REG_MAX BU27010_REG_MANUFACTURER_ID - -/** - * enum bu27008_chan_type - BU27008 channel types - * @BU27008_RED: Red channel. Always via data0. - * @BU27008_GREEN: Green channel. Always via data1. - * @BU27008_BLUE: Blue channel. Via data2 (when used). - * @BU27008_CLEAR: Clear channel. Via data2 or data3 (when used). - * @BU27008_IR: IR channel. Via data3 (when used). - * @BU27008_LUX: Illuminance channel, computed using RGB and IR. - * @BU27008_NUM_CHANS: Number of channel types. - */ -enum bu27008_chan_type { - BU27008_RED, - BU27008_GREEN, - BU27008_BLUE, - BU27008_CLEAR, - BU27008_IR, - BU27008_LUX, - BU27008_NUM_CHANS -}; - -/** - * enum bu27008_chan - BU27008 physical data channel - * @BU27008_DATA0: Always red. - * @BU27008_DATA1: Always green. - * @BU27008_DATA2: Blue or clear. - * @BU27008_DATA3: IR or clear. - * @BU27008_NUM_HW_CHANS: Number of physical channels - */ -enum bu27008_chan { - BU27008_DATA0, - BU27008_DATA1, - BU27008_DATA2, - BU27008_DATA3, - BU27008_NUM_HW_CHANS -}; - -/* We can always measure red and green at same time */ -#define ALWAYS_SCANNABLE (BIT(BU27008_RED) | BIT(BU27008_GREEN)) - -/* We use these data channel configs. Ensure scan_masks below follow them too */ -#define BU27008_BLUE2_CLEAR3 0x0 /* buffer is R, G, B, C */ -#define BU27008_CLEAR2_IR3 0x1 /* buffer is R, G, C, IR */ -#define BU27008_BLUE2_IR3 0x2 /* buffer is R, G, B, IR */ - -static const unsigned long bu27008_scan_masks[] = { - /* buffer is R, G, B, C */ - ALWAYS_SCANNABLE | BIT(BU27008_BLUE) | BIT(BU27008_CLEAR), - /* buffer is R, G, C, IR */ - ALWAYS_SCANNABLE | BIT(BU27008_CLEAR) | BIT(BU27008_IR), - /* buffer is R, G, B, IR */ - ALWAYS_SCANNABLE | BIT(BU27008_BLUE) | BIT(BU27008_IR), - /* buffer is R, G, B, IR, LUX */ - ALWAYS_SCANNABLE | BIT(BU27008_BLUE) | BIT(BU27008_IR) | BIT(BU27008_LUX), - 0 -}; - -/* - * Available scales with gain 1x - 1024x, timings 55, 100, 200, 400 mS - * Time impacts to gain: 1x, 2x, 4x, 8x. - * - * => Max total gain is HWGAIN * gain by integration time (8 * 1024) = 8192 - * - * Max amplification is (HWGAIN * MAX integration-time multiplier) 1024 * 8 - * = 8192. With NANO scale we get rid of accuracy loss when we start with the - * scale 16.0 for HWGAIN1, INT-TIME 55 mS. This way the nano scale for MAX - * total gain 8192 will be 1953125 - */ -#define BU27008_SCALE_1X 16 - -/* - * On BU27010 available scales with gain 1x - 4096x, - * timings 55, 100, 200, 400 mS. Time impacts to gain: 1x, 2x, 4x, 8x. - * - * => Max total gain is HWGAIN * gain by integration time (8 * 4096) - * - * Using NANO precision for scale we must use scale 64x corresponding gain 1x - * to avoid precision loss. - */ -#define BU27010_SCALE_1X 64 - -/* See the data sheet for the "Gain Setting" table */ -#define BU27008_GSEL_1X 0x00 -#define BU27008_GSEL_4X 0x08 -#define BU27008_GSEL_8X 0x09 -#define BU27008_GSEL_16X 0x0a -#define BU27008_GSEL_32X 0x0b -#define BU27008_GSEL_64X 0x0c -#define BU27008_GSEL_256X 0x18 -#define BU27008_GSEL_512X 0x19 -#define BU27008_GSEL_1024X 0x1a - -static const struct iio_gain_sel_pair bu27008_gains[] = { - GAIN_SCALE_GAIN(1, BU27008_GSEL_1X), - GAIN_SCALE_GAIN(4, BU27008_GSEL_4X), - GAIN_SCALE_GAIN(8, BU27008_GSEL_8X), - GAIN_SCALE_GAIN(16, BU27008_GSEL_16X), - GAIN_SCALE_GAIN(32, BU27008_GSEL_32X), - GAIN_SCALE_GAIN(64, BU27008_GSEL_64X), - GAIN_SCALE_GAIN(256, BU27008_GSEL_256X), - GAIN_SCALE_GAIN(512, BU27008_GSEL_512X), - GAIN_SCALE_GAIN(1024, BU27008_GSEL_1024X), -}; - -static const struct iio_gain_sel_pair bu27008_gains_ir[] = { - GAIN_SCALE_GAIN(2, BU27008_GSEL_1X), - GAIN_SCALE_GAIN(4, BU27008_GSEL_4X), - GAIN_SCALE_GAIN(8, BU27008_GSEL_8X), - GAIN_SCALE_GAIN(16, BU27008_GSEL_16X), - GAIN_SCALE_GAIN(32, BU27008_GSEL_32X), - GAIN_SCALE_GAIN(64, BU27008_GSEL_64X), - GAIN_SCALE_GAIN(256, BU27008_GSEL_256X), - GAIN_SCALE_GAIN(512, BU27008_GSEL_512X), - GAIN_SCALE_GAIN(1024, BU27008_GSEL_1024X), -}; - -#define BU27010_GSEL_1X 0x00 /* 000000 */ -#define BU27010_GSEL_4X 0x08 /* 001000 */ -#define BU27010_GSEL_16X 0x09 /* 001001 */ -#define BU27010_GSEL_64X 0x0e /* 001110 */ -#define BU27010_GSEL_256X 0x1e /* 011110 */ -#define BU27010_GSEL_1024X 0x2e /* 101110 */ -#define BU27010_GSEL_4096X 0x3f /* 111111 */ - -static const struct iio_gain_sel_pair bu27010_gains[] = { - GAIN_SCALE_GAIN(1, BU27010_GSEL_1X), - GAIN_SCALE_GAIN(4, BU27010_GSEL_4X), - GAIN_SCALE_GAIN(16, BU27010_GSEL_16X), - GAIN_SCALE_GAIN(64, BU27010_GSEL_64X), - GAIN_SCALE_GAIN(256, BU27010_GSEL_256X), - GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X), - GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X), -}; - -static const struct iio_gain_sel_pair bu27010_gains_ir[] = { - GAIN_SCALE_GAIN(2, BU27010_GSEL_1X), - GAIN_SCALE_GAIN(4, BU27010_GSEL_4X), - GAIN_SCALE_GAIN(16, BU27010_GSEL_16X), - GAIN_SCALE_GAIN(64, BU27010_GSEL_64X), - GAIN_SCALE_GAIN(256, BU27010_GSEL_256X), - GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X), - GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X), -}; - -#define BU27008_MEAS_MODE_100MS 0x00 -#define BU27008_MEAS_MODE_55MS 0x01 -#define BU27008_MEAS_MODE_200MS 0x02 -#define BU27008_MEAS_MODE_400MS 0x04 - -#define BU27010_MEAS_MODE_100MS 0x00 -#define BU27010_MEAS_MODE_55MS 0x03 -#define BU27010_MEAS_MODE_200MS 0x01 -#define BU27010_MEAS_MODE_400MS 0x02 - -#define BU27008_MEAS_TIME_MAX_MS 400 - -static const struct iio_itime_sel_mul bu27008_itimes[] = { - GAIN_SCALE_ITIME_US(400000, BU27008_MEAS_MODE_400MS, 8), - GAIN_SCALE_ITIME_US(200000, BU27008_MEAS_MODE_200MS, 4), - GAIN_SCALE_ITIME_US(100000, BU27008_MEAS_MODE_100MS, 2), - GAIN_SCALE_ITIME_US(55000, BU27008_MEAS_MODE_55MS, 1), -}; - -static const struct iio_itime_sel_mul bu27010_itimes[] = { - GAIN_SCALE_ITIME_US(400000, BU27010_MEAS_MODE_400MS, 8), - GAIN_SCALE_ITIME_US(200000, BU27010_MEAS_MODE_200MS, 4), - GAIN_SCALE_ITIME_US(100000, BU27010_MEAS_MODE_100MS, 2), - GAIN_SCALE_ITIME_US(55000, BU27010_MEAS_MODE_55MS, 1), -}; - -/* - * All the RGBC channels share the same gain. - * IR gain can be fine-tuned from the gain set for the RGBC by 2 bit, but this - * would yield quite complex gain setting. Especially since not all bit - * compinations are supported. And in any case setting GAIN for RGBC will - * always also change the IR-gain. - * - * On top of this, the selector '0' which corresponds to hw-gain 1X on RGBC, - * corresponds to gain 2X on IR. Rest of the selctors correspond to same gains - * though. This, however, makes it not possible to use shared gain for all - * RGBC and IR settings even though they are all changed at the one go. - */ -#define BU27008_CHAN(color, data, separate_avail) \ -{ \ - .type = IIO_INTENSITY, \ - .modified = 1, \ - .channel2 = IIO_MOD_LIGHT_##color, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_SCALE), \ - .info_mask_separate_available = (separate_avail), \ - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ - .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), \ - .address = BU27008_REG_##data##_LO, \ - .scan_index = BU27008_##color, \ - .scan_type = { \ - .sign = 'u', \ - .realbits = 16, \ - .storagebits = 16, \ - .endianness = IIO_LE, \ - }, \ -} - -/* For raw reads we always configure DATA3 for CLEAR */ -static const struct iio_chan_spec bu27008_channels[] = { - BU27008_CHAN(RED, DATA0, BIT(IIO_CHAN_INFO_SCALE)), - BU27008_CHAN(GREEN, DATA1, BIT(IIO_CHAN_INFO_SCALE)), - BU27008_CHAN(BLUE, DATA2, BIT(IIO_CHAN_INFO_SCALE)), - BU27008_CHAN(CLEAR, DATA2, BIT(IIO_CHAN_INFO_SCALE)), - /* - * We don't allow setting scale for IR (because of shared gain bits). - * Hence we don't advertise available ones either. - */ - BU27008_CHAN(IR, DATA3, 0), - { - .type = IIO_LIGHT, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE), - .channel = BU27008_LUX, - .scan_index = BU27008_LUX, - .scan_type = { - .sign = 'u', - .realbits = 64, - .storagebits = 64, - .endianness = IIO_CPU, - }, - }, - IIO_CHAN_SOFT_TIMESTAMP(BU27008_NUM_CHANS), -}; - -struct bu27008_data; - -struct bu27_chip_data { - const char *name; - int (*chip_init)(struct bu27008_data *data); - int (*get_gain_sel)(struct bu27008_data *data, int *sel); - int (*write_gain_sel)(struct bu27008_data *data, int sel); - const struct regmap_config *regmap_cfg; - const struct iio_gain_sel_pair *gains; - const struct iio_gain_sel_pair *gains_ir; - const struct iio_itime_sel_mul *itimes; - int num_gains; - int num_gains_ir; - int num_itimes; - int scale1x; - - int drdy_en_reg; - int drdy_en_mask; - int meas_en_reg; - int meas_en_mask; - int valid_reg; - int chan_sel_reg; - int chan_sel_mask; - int int_time_mask; - u8 part_id; -}; - -struct bu27008_data { - const struct bu27_chip_data *cd; - struct regmap *regmap; - struct iio_trigger *trig; - struct device *dev; - struct iio_gts gts; - struct iio_gts gts_ir; - int irq; - - /* - * Prevent changing gain/time config when scale is read/written. - * Similarly, protect the integration_time read/change sequence. - * Prevent changing gain/time when data is read. - */ - struct mutex mutex; -}; - -static const struct regmap_range bu27008_volatile_ranges[] = { - { - .range_min = BU27008_REG_SYSTEM_CONTROL, /* SWRESET */ - .range_max = BU27008_REG_SYSTEM_CONTROL, - }, { - .range_min = BU27008_REG_MODE_CONTROL3, /* VALID */ - .range_max = BU27008_REG_MODE_CONTROL3, - }, { - .range_min = BU27008_REG_DATA0_LO, /* DATA */ - .range_max = BU27008_REG_DATA3_HI, - }, -}; - -static const struct regmap_range bu27010_volatile_ranges[] = { - { - .range_min = BU27010_REG_RESET, /* RSTB */ - .range_max = BU27008_REG_SYSTEM_CONTROL, /* RESET */ - }, { - .range_min = BU27010_REG_MODE_CONTROL5, /* VALID bits */ - .range_max = BU27010_REG_MODE_CONTROL5, - }, { - .range_min = BU27008_REG_DATA0_LO, - .range_max = BU27010_REG_FIFO_DATA_HI, - }, -}; - -static const struct regmap_access_table bu27008_volatile_regs = { - .yes_ranges = &bu27008_volatile_ranges[0], - .n_yes_ranges = ARRAY_SIZE(bu27008_volatile_ranges), -}; - -static const struct regmap_access_table bu27010_volatile_regs = { - .yes_ranges = &bu27010_volatile_ranges[0], - .n_yes_ranges = ARRAY_SIZE(bu27010_volatile_ranges), -}; - -static const struct regmap_range bu27008_read_only_ranges[] = { - { - .range_min = BU27008_REG_DATA0_LO, - .range_max = BU27008_REG_DATA3_HI, - }, { - .range_min = BU27008_REG_MANUFACTURER_ID, - .range_max = BU27008_REG_MANUFACTURER_ID, - }, -}; - -static const struct regmap_range bu27010_read_only_ranges[] = { - { - .range_min = BU27008_REG_DATA0_LO, - .range_max = BU27010_REG_FIFO_DATA_HI, - }, { - .range_min = BU27010_REG_MANUFACTURER_ID, - .range_max = BU27010_REG_MANUFACTURER_ID, - } -}; - -static const struct regmap_access_table bu27008_ro_regs = { - .no_ranges = &bu27008_read_only_ranges[0], - .n_no_ranges = ARRAY_SIZE(bu27008_read_only_ranges), -}; - -static const struct regmap_access_table bu27010_ro_regs = { - .no_ranges = &bu27010_read_only_ranges[0], - .n_no_ranges = ARRAY_SIZE(bu27010_read_only_ranges), -}; - -static const struct regmap_config bu27008_regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = BU27008_REG_MAX, - .cache_type = REGCACHE_RBTREE, - .volatile_table = &bu27008_volatile_regs, - .wr_table = &bu27008_ro_regs, - /* - * All register writes are serialized by the mutex which protects the - * scale setting/getting. This is needed because scale is combined by - * gain and integration time settings and we need to ensure those are - * not read / written when scale is being computed. - * - * As a result of this serializing, we don't need regmap locking. Note, - * this is not true if we add any configurations which are not - * serialized by the mutex and which may need for example a protected - * read-modify-write cycle (eg. regmap_update_bits()). Please, revise - * this when adding features to the driver. - */ - .disable_locking = true, -}; - -static const struct regmap_config bu27010_regmap = { - .reg_bits = 8, - .val_bits = 8, - - .max_register = BU27010_REG_MAX, - .cache_type = REGCACHE_RBTREE, - .volatile_table = &bu27010_volatile_regs, - .wr_table = &bu27010_ro_regs, - .disable_locking = true, -}; - -static int bu27008_write_gain_sel(struct bu27008_data *data, int sel) -{ - int regval; - - regval = FIELD_PREP(BU27008_MASK_RGBC_GAIN, sel); - - /* - * We do always set also the LOW bits of IR-gain because othervice we - * would risk resulting an invalid GAIN register value. - * - * We could allow setting separate gains for RGBC and IR when the - * values were such that HW could support both gain settings. - * Eg, when the shared bits were same for both gain values. - * - * This, however, has a negligible benefit compared to the increased - * software complexity when we would need to go through the gains - * for both channels separately when the integration time changes. - * This would end up with nasty logic for computing gain values for - * both channels - and rejecting them if shared bits changed. - * - * We should then build the logic by guessing what a user prefers. - * RGBC or IR gains correctly set while other jumps to odd value? - * Maybe look-up a value where both gains are somehow optimized - * <what this somehow is, is ATM unknown to us>. Or maybe user would - * expect us to reject changes when optimal gains can't be set to both - * channels w/given integration time. At best that would result - * solution that works well for a very specific subset of - * configurations but causes unexpected corner-cases. - * - * So, we keep it simple. Always set same selector to IR and RGBC. - * We disallow setting IR (as I expect that most of the users are - * interested in RGBC). This way we can show the user that the scales - * for RGBC and IR channels are different (1X Vs 2X with sel 0) while - * still keeping the operation deterministic. - */ - regval |= FIELD_PREP(BU27008_MASK_IR_GAIN_LO, sel); - - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL2, - BU27008_MASK_RGBC_GAIN, regval); -} - -static int bu27010_write_gain_sel(struct bu27008_data *data, int sel) -{ - unsigned int regval; - int ret, chan_selector; - - /* - * Gain 'selector' is composed of two registers. Selector is 6bit value, - * 4 high bits being the RGBC gain fieild in MODE_CONTROL1 register and - * two low bits being the channel specific gain in MODE_CONTROL2. - * - * Let's take the 4 high bits of whole 6 bit selector, and prepare - * the MODE_CONTROL1 value (RGBC gain part). - */ - regval = FIELD_PREP(BU27010_MASK_RGBC_GAIN, (sel >> 2)); - - ret = regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1, - BU27010_MASK_RGBC_GAIN, regval); - if (ret) - return ret; - - /* - * Two low two bits of the selector must be written for all 4 - * channels in the MODE_CONTROL2 register. Copy these two bits for - * all channels. - */ - chan_selector = sel & GENMASK(1, 0); - - regval = FIELD_PREP(BU27010_MASK_DATA0_GAIN, chan_selector); - regval |= FIELD_PREP(BU27010_MASK_DATA1_GAIN, chan_selector); - regval |= FIELD_PREP(BU27010_MASK_DATA2_GAIN, chan_selector); - regval |= FIELD_PREP(BU27010_MASK_DATA3_GAIN, chan_selector); - - return regmap_write(data->regmap, BU27008_REG_MODE_CONTROL2, regval); -} - -static int bu27008_get_gain_sel(struct bu27008_data *data, int *sel) -{ - int ret; - - /* - * If we always "lock" the gain selectors for all channels to prevent - * unsupported configs, then it does not matter which channel is used - * we can just return selector from any of them. - * - * This, however is not true if we decide to support only 4X and 16X - * and then individual gains for channels. Currently this is not the - * case. - * - * If we some day decide to support individual gains, then we need to - * have channel information here. - */ - - ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel); - if (ret) - return ret; - - *sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, *sel); - - return 0; -} - -static int bu27010_get_gain_sel(struct bu27008_data *data, int *sel) -{ - int ret, tmp; - - /* - * We always "lock" the gain selectors for all channels to prevent - * unsupported configs. It does not matter which channel is used - * we can just return selector from any of them. - * - * Read the channel0 gain. - */ - ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel); - if (ret) - return ret; - - *sel = FIELD_GET(BU27010_MASK_DATA0_GAIN, *sel); - - /* Read the shared gain */ - ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &tmp); - if (ret) - return ret; - - /* - * The gain selector is made as a combination of common RGBC gain and - * the channel specific gain. The channel specific gain forms the low - * bits of selector and RGBC gain is appended right after it. - * - * Compose the selector from channel0 gain and shared RGBC gain. - */ - *sel |= FIELD_GET(BU27010_MASK_RGBC_GAIN, tmp) << fls(BU27010_MASK_DATA0_GAIN); - - return ret; -} - -static int bu27008_chip_init(struct bu27008_data *data) -{ - int ret; - - ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL, - BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET); - if (ret) - return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); - - /* - * The data-sheet does not tell how long performing the IC reset takes. - * However, the data-sheet says the minimum time it takes the IC to be - * able to take inputs after power is applied, is 100 uS. I'd assume - * > 1 mS is enough. - */ - msleep(1); - - ret = regmap_reinit_cache(data->regmap, data->cd->regmap_cfg); - if (ret) - dev_err(data->dev, "Failed to reinit reg cache\n"); - - return ret; -} - -static int bu27010_chip_init(struct bu27008_data *data) -{ - int ret; - - ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL, - BU27010_MASK_SW_RESET, BU27010_MASK_SW_RESET); - if (ret) - return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); - - msleep(1); - - /* Power ON*/ - ret = regmap_write_bits(data->regmap, BU27010_REG_POWER, - BU27010_MASK_POWER, BU27010_MASK_POWER); - if (ret) - return dev_err_probe(data->dev, ret, "Sensor power-on failed\n"); - - msleep(1); - - /* Release blocks from reset */ - ret = regmap_write_bits(data->regmap, BU27010_REG_RESET, - BU27010_MASK_RESET, BU27010_RESET_RELEASE); - if (ret) - return dev_err_probe(data->dev, ret, "Sensor powering failed\n"); - - msleep(1); - - /* - * The IRQ enabling on BU27010 is done in a peculiar way. The IRQ - * enabling is not a bit mask where individual IRQs could be enabled but - * a field which values are: - * 00 => IRQs disabled - * 01 => Data-ready (RGBC/IR) - * 10 => Data-ready (flicker) - * 11 => Flicker FIFO - * - * So, only one IRQ can be enabled at a time and enabling for example - * flicker FIFO would automagically disable data-ready IRQ. - * - * Currently the driver does not support the flicker. Hence, we can - * just treat the RGBC data-ready as single bit which can be enabled / - * disabled. This works for as long as the second bit in the field - * stays zero. Here we ensure it gets zeroed. - */ - return regmap_clear_bits(data->regmap, BU27010_REG_MODE_CONTROL4, - BU27010_IRQ_DIS_ALL); -} - -static const struct bu27_chip_data bu27010_chip = { - .name = "bu27010", - .chip_init = bu27010_chip_init, - .get_gain_sel = bu27010_get_gain_sel, - .write_gain_sel = bu27010_write_gain_sel, - .regmap_cfg = &bu27010_regmap, - .gains = &bu27010_gains[0], - .gains_ir = &bu27010_gains_ir[0], - .itimes = &bu27010_itimes[0], - .num_gains = ARRAY_SIZE(bu27010_gains), - .num_gains_ir = ARRAY_SIZE(bu27010_gains_ir), - .num_itimes = ARRAY_SIZE(bu27010_itimes), - .scale1x = BU27010_SCALE_1X, - .drdy_en_reg = BU27010_REG_MODE_CONTROL4, - .drdy_en_mask = BU27010_DRDY_EN, - .meas_en_reg = BU27010_REG_MODE_CONTROL5, - .meas_en_mask = BU27010_MASK_MEAS_EN, - .valid_reg = BU27010_REG_MODE_CONTROL5, - .chan_sel_reg = BU27008_REG_MODE_CONTROL1, - .chan_sel_mask = BU27010_MASK_CHAN_SEL, - .int_time_mask = BU27010_MASK_MEAS_MODE, - .part_id = BU27010_ID, -}; - -static const struct bu27_chip_data bu27008_chip = { - .name = "bu27008", - .chip_init = bu27008_chip_init, - .get_gain_sel = bu27008_get_gain_sel, - .write_gain_sel = bu27008_write_gain_sel, - .regmap_cfg = &bu27008_regmap, - .gains = &bu27008_gains[0], - .gains_ir = &bu27008_gains_ir[0], - .itimes = &bu27008_itimes[0], - .num_gains = ARRAY_SIZE(bu27008_gains), - .num_gains_ir = ARRAY_SIZE(bu27008_gains_ir), - .num_itimes = ARRAY_SIZE(bu27008_itimes), - .scale1x = BU27008_SCALE_1X, - .drdy_en_reg = BU27008_REG_MODE_CONTROL3, - .drdy_en_mask = BU27008_MASK_INT_EN, - .valid_reg = BU27008_REG_MODE_CONTROL3, - .meas_en_reg = BU27008_REG_MODE_CONTROL3, - .meas_en_mask = BU27008_MASK_MEAS_EN, - .chan_sel_reg = BU27008_REG_MODE_CONTROL3, - .chan_sel_mask = BU27008_MASK_CHAN_SEL, - .int_time_mask = BU27008_MASK_MEAS_MODE, - .part_id = BU27008_ID, -}; - -#define BU27008_MAX_VALID_RESULT_WAIT_US 50000 -#define BU27008_VALID_RESULT_WAIT_QUANTA_US 1000 - -static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val) -{ - int ret, valid; - __le16 tmp; - - ret = regmap_read_poll_timeout(data->regmap, data->cd->valid_reg, - valid, (valid & BU27008_MASK_VALID), - BU27008_VALID_RESULT_WAIT_QUANTA_US, - BU27008_MAX_VALID_RESULT_WAIT_US); - if (ret) - return ret; - - ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp)); - if (ret) - dev_err(data->dev, "Reading channel data failed\n"); - - *val = le16_to_cpu(tmp); - - return ret; -} - -static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain) -{ - int ret, sel; - - ret = data->cd->get_gain_sel(data, &sel); - if (ret) - return ret; - - ret = iio_gts_find_gain_by_sel(gts, sel); - if (ret < 0) { - dev_err(data->dev, "unknown gain value 0x%x\n", sel); - return ret; - } - - *gain = ret; - - return 0; -} - -static int bu27008_set_gain(struct bu27008_data *data, int gain) -{ - int ret; - - ret = iio_gts_find_sel_by_gain(&data->gts, gain); - if (ret < 0) - return ret; - - return data->cd->write_gain_sel(data, ret); -} - -static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel) -{ - int ret, val; - - ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &val); - if (ret) - return ret; - - val &= data->cd->int_time_mask; - val >>= ffs(data->cd->int_time_mask) - 1; - - *sel = val; - - return 0; -} - -static int bu27008_set_int_time_sel(struct bu27008_data *data, int sel) -{ - sel <<= ffs(data->cd->int_time_mask) - 1; - - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1, - data->cd->int_time_mask, sel); -} - -static int bu27008_get_int_time_us(struct bu27008_data *data) -{ - int ret, sel; - - ret = bu27008_get_int_time_sel(data, &sel); - if (ret) - return ret; - - return iio_gts_find_int_time_by_sel(&data->gts, sel); -} - -static int _bu27008_get_scale(struct bu27008_data *data, bool ir, int *val, - int *val2) -{ - struct iio_gts *gts; - int gain, ret; - - if (ir) - gts = &data->gts_ir; - else - gts = &data->gts; - - ret = bu27008_get_gain(data, gts, &gain); - if (ret) - return ret; - - ret = bu27008_get_int_time_us(data); - if (ret < 0) - return ret; - - return iio_gts_get_scale(gts, gain, ret, val, val2); -} - -static int bu27008_get_scale(struct bu27008_data *data, bool ir, int *val, - int *val2) -{ - int ret; - - mutex_lock(&data->mutex); - ret = _bu27008_get_scale(data, ir, val, val2); - mutex_unlock(&data->mutex); - - return ret; -} - -static int bu27008_set_int_time(struct bu27008_data *data, int time) -{ - int ret; - - ret = iio_gts_find_sel_by_int_time(&data->gts, time); - if (ret < 0) - return ret; - - return bu27008_set_int_time_sel(data, ret); -} - -/* Try to change the time so that the scale is maintained */ -static int bu27008_try_set_int_time(struct bu27008_data *data, int int_time_new) -{ - int ret, old_time_sel, new_time_sel, old_gain, new_gain; - - mutex_lock(&data->mutex); - - ret = bu27008_get_int_time_sel(data, &old_time_sel); - if (ret < 0) - goto unlock_out; - - if (!iio_gts_valid_time(&data->gts, int_time_new)) { - dev_dbg(data->dev, "Unsupported integration time %u\n", - int_time_new); - - ret = -EINVAL; - goto unlock_out; - } - - /* If we already use requested time, then we're done */ - new_time_sel = iio_gts_find_sel_by_int_time(&data->gts, int_time_new); - if (new_time_sel == old_time_sel) - goto unlock_out; - - ret = bu27008_get_gain(data, &data->gts, &old_gain); - if (ret) - goto unlock_out; - - ret = iio_gts_find_new_gain_sel_by_old_gain_time(&data->gts, old_gain, - old_time_sel, new_time_sel, &new_gain); - if (ret) { - int scale1, scale2; - bool ok; - - _bu27008_get_scale(data, false, &scale1, &scale2); - dev_dbg(data->dev, - "Can't support time %u with current scale %u %u\n", - int_time_new, scale1, scale2); - - if (new_gain < 0) - goto unlock_out; - - /* - * If caller requests for integration time change and we - * can't support the scale - then the caller should be - * prepared to 'pick up the pieces and deal with the - * fact that the scale changed'. - */ - ret = iio_find_closest_gain_low(&data->gts, new_gain, &ok); - if (!ok) - dev_dbg(data->dev, "optimal gain out of range\n"); - - if (ret < 0) { - dev_dbg(data->dev, - "Total gain increase. Risk of saturation"); - ret = iio_gts_get_min_gain(&data->gts); - if (ret < 0) - goto unlock_out; - } - new_gain = ret; - dev_dbg(data->dev, "scale changed, new gain %u\n", new_gain); - } - - ret = bu27008_set_gain(data, new_gain); - if (ret) - goto unlock_out; - - ret = bu27008_set_int_time(data, int_time_new); - -unlock_out: - mutex_unlock(&data->mutex); - - return ret; -} - -static int bu27008_meas_set(struct bu27008_data *data, bool enable) -{ - if (enable) - return regmap_set_bits(data->regmap, data->cd->meas_en_reg, - data->cd->meas_en_mask); - return regmap_clear_bits(data->regmap, data->cd->meas_en_reg, - data->cd->meas_en_mask); -} - -static int bu27008_chan_cfg(struct bu27008_data *data, - struct iio_chan_spec const *chan) -{ - int chan_sel; - - if (chan->scan_index == BU27008_BLUE) - chan_sel = BU27008_BLUE2_CLEAR3; - else - chan_sel = BU27008_CLEAR2_IR3; - - /* - * prepare bitfield for channel sel. The FIELD_PREP works only when - * mask is constant. In our case the mask is assigned based on the - * chip type. Hence the open-coded FIELD_PREP here. We don't bother - * zeroing the irrelevant bits though - update_bits takes care of that. - */ - chan_sel <<= ffs(data->cd->chan_sel_mask) - 1; - - return regmap_update_bits(data->regmap, data->cd->chan_sel_reg, - BU27008_MASK_CHAN_SEL, chan_sel); -} - -static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev, - struct iio_chan_spec const *chan, int *val, int *val2) -{ - int ret, int_time; - - ret = bu27008_chan_cfg(data, chan); - if (ret) - return ret; - - ret = bu27008_meas_set(data, true); - if (ret) - return ret; - - ret = bu27008_get_int_time_us(data); - if (ret < 0) - int_time = BU27008_MEAS_TIME_MAX_MS; - else - int_time = ret / USEC_PER_MSEC; - - msleep(int_time); - - ret = bu27008_chan_read_data(data, chan->address, val); - if (!ret) - ret = IIO_VAL_INT; - - if (bu27008_meas_set(data, false)) - dev_warn(data->dev, "measurement disabling failed\n"); - - return ret; -} - -#define BU27008_LUX_DATA_RED 0 -#define BU27008_LUX_DATA_GREEN 1 -#define BU27008_LUX_DATA_BLUE 2 -#define BU27008_LUX_DATA_IR 3 -#define LUX_DATA_SIZE (BU27008_NUM_HW_CHANS * sizeof(__le16)) - -static int bu27008_read_lux_chans(struct bu27008_data *data, unsigned int time, - __le16 *chan_data) -{ - int ret, chan_sel, tmpret, valid; - - chan_sel = BU27008_BLUE2_IR3 << (ffs(data->cd->chan_sel_mask) - 1); - - ret = regmap_update_bits(data->regmap, data->cd->chan_sel_reg, - data->cd->chan_sel_mask, chan_sel); - if (ret) - return ret; - - ret = bu27008_meas_set(data, true); - if (ret) - return ret; - - msleep(time / USEC_PER_MSEC); - - ret = regmap_read_poll_timeout(data->regmap, data->cd->valid_reg, - valid, (valid & BU27008_MASK_VALID), - BU27008_VALID_RESULT_WAIT_QUANTA_US, - BU27008_MAX_VALID_RESULT_WAIT_US); - if (ret) - goto out; - - ret = regmap_bulk_read(data->regmap, BU27008_REG_DATA0_LO, chan_data, - LUX_DATA_SIZE); - if (ret) - goto out; -out: - tmpret = bu27008_meas_set(data, false); - if (tmpret) - dev_warn(data->dev, "Stopping measurement failed\n"); - - return ret; -} - -/* - * Following equation for computing lux out of register values was given by - * ROHM HW colleagues; - * - * Red = RedData*1024 / Gain * 20 / meas_mode - * Green = GreenData* 1024 / Gain * 20 / meas_mode - * Blue = BlueData* 1024 / Gain * 20 / meas_mode - * IR = IrData* 1024 / Gain * 20 / meas_mode - * - * where meas_mode is the integration time in mS / 10 - * - * IRratio = (IR > 0.18 * Green) ? 0 : 1 - * - * Lx = max(c1*Red + c2*Green + c3*Blue,0) - * - * for - * IRratio 0: c1 = -0.00002237, c2 = 0.0003219, c3 = -0.000120371 - * IRratio 1: c1 = -0.00001074, c2 = 0.000305415, c3 = -0.000129367 - */ - -/* - * The max chan data is 0xffff. When we multiply it by 1024 * 20, we'll get - * 0x4FFFB000 which still fits in 32-bit integer. This won't overflow. - */ -#define NORM_CHAN_DATA_FOR_LX_CALC(chan, gain, time) (le16_to_cpu(chan) * \ - 1024 * 20 / (gain) / (time)) -static u64 bu27008_calc_nlux(struct bu27008_data *data, __le16 *lux_data, - unsigned int gain, unsigned int gain_ir, unsigned int time) -{ - unsigned int red, green, blue, ir; - s64 c1, c2, c3, nlux; - - time /= 10000; - ir = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_IR], gain_ir, time); - red = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_RED], gain, time); - green = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_GREEN], gain, time); - blue = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_BLUE], gain, time); - - if ((u64)ir * 100LLU > (u64)green * 18LLU) { - c1 = -22370; - c2 = 321900; - c3 = -120371; - } else { - c1 = -10740; - c2 = 305415; - c3 = -129367; - } - nlux = c1 * red + c2 * green + c3 * blue; - - return max_t(s64, 0, nlux); -} - -static int bu27008_get_time_n_gains(struct bu27008_data *data, - unsigned int *gain, unsigned int *gain_ir, unsigned int *time) -{ - int ret; - - ret = bu27008_get_gain(data, &data->gts, gain); - if (ret < 0) - return ret; - - ret = bu27008_get_gain(data, &data->gts_ir, gain_ir); - if (ret < 0) - return ret; - - ret = bu27008_get_int_time_us(data); - if (ret < 0) - return ret; - - /* Max integration time is 400000. Fits in signed int. */ - *time = ret; - - return 0; -} - -struct bu27008_buf { - __le16 chan[BU27008_NUM_HW_CHANS]; - u64 lux __aligned(8); - s64 ts __aligned(8); -}; - -static int bu27008_buffer_fill_lux(struct bu27008_data *data, - struct bu27008_buf *raw) -{ - unsigned int gain, gain_ir, time; - int ret; - - ret = bu27008_get_time_n_gains(data, &gain, &gain_ir, &time); - if (ret) - return ret; - - raw->lux = bu27008_calc_nlux(data, raw->chan, gain, gain_ir, time); - - return 0; -} - -static int bu27008_read_lux(struct bu27008_data *data, struct iio_dev *idev, - struct iio_chan_spec const *chan, - int *val, int *val2) -{ - __le16 lux_data[BU27008_NUM_HW_CHANS]; - unsigned int gain, gain_ir, time; - u64 nlux; - int ret; - - ret = bu27008_get_time_n_gains(data, &gain, &gain_ir, &time); - if (ret) - return ret; - - ret = bu27008_read_lux_chans(data, time, lux_data); - if (ret) - return ret; - - nlux = bu27008_calc_nlux(data, lux_data, gain, gain_ir, time); - *val = (int)nlux; - *val2 = nlux >> 32LLU; - - return IIO_VAL_INT_64; -} - -static int bu27008_read_raw(struct iio_dev *idev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) -{ - struct bu27008_data *data = iio_priv(idev); - int busy, ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - busy = iio_device_claim_direct_mode(idev); - if (busy) - return -EBUSY; - - mutex_lock(&data->mutex); - if (chan->type == IIO_LIGHT) - ret = bu27008_read_lux(data, idev, chan, val, val2); - else - ret = bu27008_read_one(data, idev, chan, val, val2); - mutex_unlock(&data->mutex); - - iio_device_release_direct_mode(idev); - - return ret; - - case IIO_CHAN_INFO_SCALE: - if (chan->type == IIO_LIGHT) { - *val = 0; - *val2 = 1; - return IIO_VAL_INT_PLUS_NANO; - } - ret = bu27008_get_scale(data, chan->scan_index == BU27008_IR, - val, val2); - if (ret) - return ret; - - return IIO_VAL_INT_PLUS_NANO; - - case IIO_CHAN_INFO_INT_TIME: - ret = bu27008_get_int_time_us(data); - if (ret < 0) - return ret; - - *val = 0; - *val2 = ret; - - return IIO_VAL_INT_PLUS_MICRO; - - default: - return -EINVAL; - } -} - -/* Called if the new scale could not be supported with existing int-time */ -static int bu27008_try_find_new_time_gain(struct bu27008_data *data, int val, - int val2, int *gain_sel) -{ - int i, ret, new_time_sel; - - for (i = 0; i < data->gts.num_itime; i++) { - new_time_sel = data->gts.itime_table[i].sel; - ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, - new_time_sel, val, val2, gain_sel); - if (!ret) - break; - } - if (i == data->gts.num_itime) { - dev_err(data->dev, "Can't support scale %u %u\n", val, val2); - - return -EINVAL; - } - - return bu27008_set_int_time_sel(data, new_time_sel); -} - -static int bu27008_set_scale(struct bu27008_data *data, - struct iio_chan_spec const *chan, - int val, int val2) -{ - int ret, gain_sel, time_sel; - - if (chan->scan_index == BU27008_IR) - return -EINVAL; - - mutex_lock(&data->mutex); - - ret = bu27008_get_int_time_sel(data, &time_sel); - if (ret < 0) - goto unlock_out; - - ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel, - val, val2, &gain_sel); - if (ret) { - ret = bu27008_try_find_new_time_gain(data, val, val2, &gain_sel); - if (ret) - goto unlock_out; - - } - ret = data->cd->write_gain_sel(data, gain_sel); - -unlock_out: - mutex_unlock(&data->mutex); - - return ret; -} - -static int bu27008_write_raw_get_fmt(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - long mask) -{ - - switch (mask) { - case IIO_CHAN_INFO_SCALE: - return IIO_VAL_INT_PLUS_NANO; - case IIO_CHAN_INFO_INT_TIME: - return IIO_VAL_INT_PLUS_MICRO; - default: - return -EINVAL; - } -} - -static int bu27008_write_raw(struct iio_dev *idev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) -{ - struct bu27008_data *data = iio_priv(idev); - int ret; - - /* - * Do not allow changing scale when measurement is ongoing as doing so - * could make values in the buffer inconsistent. - */ - ret = iio_device_claim_direct_mode(idev); - if (ret) - return ret; - - switch (mask) { - case IIO_CHAN_INFO_SCALE: - ret = bu27008_set_scale(data, chan, val, val2); - break; - case IIO_CHAN_INFO_INT_TIME: - if (val) { - ret = -EINVAL; - break; - } - ret = bu27008_try_set_int_time(data, val2); - break; - default: - ret = -EINVAL; - break; - } - iio_device_release_direct_mode(idev); - - return ret; -} - -static int bu27008_read_avail(struct iio_dev *idev, - struct iio_chan_spec const *chan, const int **vals, - int *type, int *length, long mask) -{ - struct bu27008_data *data = iio_priv(idev); - - switch (mask) { - case IIO_CHAN_INFO_INT_TIME: - return iio_gts_avail_times(&data->gts, vals, type, length); - case IIO_CHAN_INFO_SCALE: - if (chan->channel2 == IIO_MOD_LIGHT_IR) - return iio_gts_all_avail_scales(&data->gts_ir, vals, - type, length); - return iio_gts_all_avail_scales(&data->gts, vals, type, length); - default: - return -EINVAL; - } -} - -static int bu27008_update_scan_mode(struct iio_dev *idev, - const unsigned long *scan_mask) -{ - struct bu27008_data *data = iio_priv(idev); - int chan_sel; - - /* Configure channel selection */ - if (test_bit(BU27008_BLUE, idev->active_scan_mask)) { - if (test_bit(BU27008_CLEAR, idev->active_scan_mask)) - chan_sel = BU27008_BLUE2_CLEAR3; - else - chan_sel = BU27008_BLUE2_IR3; - } else { - chan_sel = BU27008_CLEAR2_IR3; - } - - chan_sel <<= ffs(data->cd->chan_sel_mask) - 1; - - return regmap_update_bits(data->regmap, data->cd->chan_sel_reg, - data->cd->chan_sel_mask, chan_sel); -} - -static const struct iio_info bu27008_info = { - .read_raw = &bu27008_read_raw, - .write_raw = &bu27008_write_raw, - .write_raw_get_fmt = &bu27008_write_raw_get_fmt, - .read_avail = &bu27008_read_avail, - .update_scan_mode = bu27008_update_scan_mode, - .validate_trigger = iio_validate_own_trigger, -}; - -static int bu27008_trigger_set_state(struct iio_trigger *trig, bool state) -{ - struct bu27008_data *data = iio_trigger_get_drvdata(trig); - int ret; - - - if (state) - ret = regmap_set_bits(data->regmap, data->cd->drdy_en_reg, - data->cd->drdy_en_mask); - else - ret = regmap_clear_bits(data->regmap, data->cd->drdy_en_reg, - data->cd->drdy_en_mask); - if (ret) - dev_err(data->dev, "Failed to set trigger state\n"); - - return ret; -} - -static void bu27008_trigger_reenable(struct iio_trigger *trig) -{ - struct bu27008_data *data = iio_trigger_get_drvdata(trig); - - enable_irq(data->irq); -} - -static const struct iio_trigger_ops bu27008_trigger_ops = { - .set_trigger_state = bu27008_trigger_set_state, - .reenable = bu27008_trigger_reenable, -}; - -static irqreturn_t bu27008_trigger_handler(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *idev = pf->indio_dev; - struct bu27008_data *data = iio_priv(idev); - struct bu27008_buf raw; - int ret, dummy; - - memset(&raw, 0, sizeof(raw)); - - /* - * After some measurements, it seems reading the - * BU27008_REG_MODE_CONTROL3 debounces the IRQ line - */ - ret = regmap_read(data->regmap, data->cd->valid_reg, &dummy); - if (ret < 0) - goto err_read; - - ret = regmap_bulk_read(data->regmap, BU27008_REG_DATA0_LO, &raw.chan, - sizeof(raw.chan)); - if (ret < 0) - goto err_read; - - if (test_bit(BU27008_LUX, idev->active_scan_mask)) { - ret = bu27008_buffer_fill_lux(data, &raw); - if (ret) - goto err_read; - } - - iio_push_to_buffers_with_timestamp(idev, &raw, pf->timestamp); -err_read: - iio_trigger_notify_done(idev->trig); - - return IRQ_HANDLED; -} - -static int bu27008_buffer_preenable(struct iio_dev *idev) -{ - struct bu27008_data *data = iio_priv(idev); - - return bu27008_meas_set(data, true); -} - -static int bu27008_buffer_postdisable(struct iio_dev *idev) -{ - struct bu27008_data *data = iio_priv(idev); - - return bu27008_meas_set(data, false); -} - -static const struct iio_buffer_setup_ops bu27008_buffer_ops = { - .preenable = bu27008_buffer_preenable, - .postdisable = bu27008_buffer_postdisable, -}; - -static irqreturn_t bu27008_data_rdy_poll(int irq, void *private) -{ - /* - * The BU27008 keeps IRQ asserted until we read the VALID bit from - * a register. We need to keep the IRQ disabled until then. - */ - disable_irq_nosync(irq); - iio_trigger_poll(private); - - return IRQ_HANDLED; -} - -static int bu27008_setup_trigger(struct bu27008_data *data, struct iio_dev *idev) -{ - struct iio_trigger *itrig; - char *name; - int ret; - - ret = devm_iio_triggered_buffer_setup(data->dev, idev, - &iio_pollfunc_store_time, - bu27008_trigger_handler, - &bu27008_buffer_ops); - if (ret) - return dev_err_probe(data->dev, ret, - "iio_triggered_buffer_setup_ext FAIL\n"); - - itrig = devm_iio_trigger_alloc(data->dev, "%sdata-rdy-dev%d", - idev->name, iio_device_id(idev)); - if (!itrig) - return -ENOMEM; - - data->trig = itrig; - - itrig->ops = &bu27008_trigger_ops; - iio_trigger_set_drvdata(itrig, data); - - name = devm_kasprintf(data->dev, GFP_KERNEL, "%s-bu27008", - dev_name(data->dev)); - - ret = devm_request_irq(data->dev, data->irq, - &bu27008_data_rdy_poll, - 0, name, itrig); - if (ret) - return dev_err_probe(data->dev, ret, "Could not request IRQ\n"); - - ret = devm_iio_trigger_register(data->dev, itrig); - if (ret) - return dev_err_probe(data->dev, ret, - "Trigger registration failed\n"); - - /* set default trigger */ - idev->trig = iio_trigger_get(itrig); - - return 0; -} - -static int bu27008_probe(struct i2c_client *i2c) -{ - struct device *dev = &i2c->dev; - struct bu27008_data *data; - struct regmap *regmap; - unsigned int part_id, reg; - struct iio_dev *idev; - int ret; - - idev = devm_iio_device_alloc(dev, sizeof(*data)); - if (!idev) - return -ENOMEM; - - ret = devm_regulator_get_enable(dev, "vdd"); - if (ret) - return dev_err_probe(dev, ret, "Failed to get regulator\n"); - - data = iio_priv(idev); - - data->cd = device_get_match_data(&i2c->dev); - if (!data->cd) - return -ENODEV; - - regmap = devm_regmap_init_i2c(i2c, data->cd->regmap_cfg); - if (IS_ERR(regmap)) - return dev_err_probe(dev, PTR_ERR(regmap), - "Failed to initialize Regmap\n"); - - - ret = regmap_read(regmap, BU27008_REG_SYSTEM_CONTROL, ®); - if (ret) - return dev_err_probe(dev, ret, "Failed to access sensor\n"); - - part_id = FIELD_GET(BU27008_MASK_PART_ID, reg); - - if (part_id != data->cd->part_id) - dev_warn(dev, "unknown device 0x%x\n", part_id); - - ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains, - data->cd->num_gains, data->cd->itimes, - data->cd->num_itimes, &data->gts); - if (ret) - return ret; - - ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains_ir, - data->cd->num_gains_ir, data->cd->itimes, - data->cd->num_itimes, &data->gts_ir); - if (ret) - return ret; - - mutex_init(&data->mutex); - data->regmap = regmap; - data->dev = dev; - data->irq = i2c->irq; - - idev->channels = bu27008_channels; - idev->num_channels = ARRAY_SIZE(bu27008_channels); - idev->name = data->cd->name; - idev->info = &bu27008_info; - idev->modes = INDIO_DIRECT_MODE; - idev->available_scan_masks = bu27008_scan_masks; - - ret = data->cd->chip_init(data); - if (ret) - return ret; - - if (i2c->irq) { - ret = bu27008_setup_trigger(data, idev); - if (ret) - return ret; - } else { - dev_info(dev, "No IRQ, buffered mode disabled\n"); - } - - ret = devm_iio_device_register(dev, idev); - if (ret) - return dev_err_probe(dev, ret, - "Unable to register iio device\n"); - - return 0; -} - -static const struct of_device_id bu27008_of_match[] = { - { .compatible = "rohm,bu27008", .data = &bu27008_chip }, - { .compatible = "rohm,bu27010", .data = &bu27010_chip }, - { } -}; -MODULE_DEVICE_TABLE(of, bu27008_of_match); - -static struct i2c_driver bu27008_i2c_driver = { - .driver = { - .name = "bu27008", - .of_match_table = bu27008_of_match, - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - }, - .probe = bu27008_probe, -}; -module_i2c_driver(bu27008_i2c_driver); - -MODULE_DESCRIPTION("ROHM BU27008 and BU27010 colour sensor driver"); -MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); -MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS("IIO_GTS_HELPER"); diff --git a/drivers/iio/light/rohm-bu27034.c b/drivers/iio/light/rohm-bu27034.c index 4f591c2278f2..cc25596cb248 100644 --- a/drivers/iio/light/rohm-bu27034.c +++ b/drivers/iio/light/rohm-bu27034.c @@ -7,6 +7,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/i2c.h> #include <linux/module.h> @@ -205,7 +206,7 @@ struct bu27034_data { struct { u32 mlux; __le16 channels[BU27034_NUM_HW_DATA_CHANS]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -395,30 +396,26 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) int numg = ARRAY_SIZE(gains); int ret, int_time_old, i; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = bu27034_get_int_time(data); if (ret < 0) - goto unlock_out; + return ret; int_time_old = ret; if (!iio_gts_valid_time(&data->gts, time_us)) { dev_err(data->dev, "Unsupported integration time %u\n", time_us); - ret = -EINVAL; - - goto unlock_out; + return -EINVAL; } - if (time_us == int_time_old) { - ret = 0; - goto unlock_out; - } + if (time_us == int_time_old) + return 0; for (i = 0; i < numg; i++) { ret = bu27034_get_gain(data, gains[i].chan, &gains[i].old_gain); if (ret) - goto unlock_out; + return 0; ret = iio_gts_find_new_gain_by_old_gain_time(&data->gts, gains[i].old_gain, @@ -434,7 +431,7 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) gains[i].chan, time_us, scale1, scale2); if (gains[i].new_gain < 0) - goto unlock_out; + return ret; /* * If caller requests for integration time change and we @@ -455,7 +452,7 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) "Total gain increase. Risk of saturation"); ret = iio_gts_get_min_gain(&data->gts); if (ret < 0) - goto unlock_out; + return ret; } dev_dbg(data->dev, "chan %u scale changed\n", gains[i].chan); @@ -468,15 +465,10 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) for (i = 0; i < numg; i++) { ret = bu27034_set_gain(data, gains[i].chan, gains[i].new_gain); if (ret) - goto unlock_out; + return ret; } - ret = bu27034_set_int_time(data, time_us); - -unlock_out: - mutex_unlock(&data->mutex); - - return ret; + return bu27034_set_int_time(data, time_us); } static int bu27034_set_scale(struct bu27034_data *data, int chan, @@ -492,10 +484,10 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, return -EINVAL; } - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &time_sel); if (ret) - goto unlock_out; + return ret; ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel, val, val2, &gain_sel); @@ -518,7 +510,7 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, ret = bu27034_get_gain(data, gain.chan, &gain.old_gain); if (ret) - goto unlock_out; + return ret; /* * Iterate through all the times to see if we find one which @@ -551,26 +543,20 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, if (!found) { dev_dbg(data->dev, "Can't set scale maintaining other channel\n"); - ret = -EINVAL; - - goto unlock_out; + return -EINVAL; } ret = bu27034_set_gain(data, gain.chan, gain.new_gain); if (ret) - goto unlock_out; + return ret; ret = regmap_update_bits(data->regmap, BU27034_REG_MODE_CONTROL1, BU27034_MASK_MEAS_MODE, new_time_sel); if (ret) - goto unlock_out; + return ret; } - ret = bu27034_write_gain_sel(data, chan, gain_sel); -unlock_out: - mutex_unlock(&data->mutex); - - return ret; + return bu27034_write_gain_sel(data, chan, gain_sel); } /* @@ -1221,42 +1207,33 @@ static int bu27034_buffer_enable(struct iio_dev *idev) struct task_struct *task; int ret; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = bu27034_meas_set(data, true); if (ret) - goto unlock_out; + return ret; task = kthread_run(bu27034_buffer_thread, idev, "bu27034-buffering-%u", iio_device_id(idev)); - if (IS_ERR(task)) { - ret = PTR_ERR(task); - goto unlock_out; - } + if (IS_ERR(task)) + return PTR_ERR(task); data->task = task; -unlock_out: - mutex_unlock(&data->mutex); - - return ret; + return 0; } static int bu27034_buffer_disable(struct iio_dev *idev) { struct bu27034_data *data = iio_priv(idev); - int ret; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (data->task) { kthread_stop(data->task); data->task = NULL; } - ret = bu27034_meas_set(data, false); - mutex_unlock(&data->mutex); - - return ret; + return bu27034_meas_set(data, false); } static const struct iio_buffer_setup_ops bu27034_buffer_ops = { diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 56f5fbbf79ac..2ba917c5c138 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -203,7 +203,7 @@ struct rpr0521_data { struct { __le16 channels[3]; u8 garbage; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/light/st_uvis25.h b/drivers/iio/light/st_uvis25.h index 283086887caf..1f93e3dc45c2 100644 --- a/drivers/iio/light/st_uvis25.h +++ b/drivers/iio/light/st_uvis25.h @@ -30,7 +30,7 @@ struct st_uvis25_hw { /* Ensure timestamp is naturally aligned */ struct { u8 chan; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c index 4fecdf10aeb1..884e43e4cda4 100644 --- a/drivers/iio/light/tcs3414.c +++ b/drivers/iio/light/tcs3414.c @@ -56,7 +56,7 @@ struct tcs3414_data { /* Ensure timestamp is naturally aligned */ struct { u16 chans[4]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 4186aac04902..2bd36a344ea5 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -67,7 +67,7 @@ struct tcs3472_data { /* Ensure timestamp is naturally aligned */ struct { u16 chans[4]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; diff --git a/drivers/iio/light/veml3235.c b/drivers/iio/light/veml3235.c index 66361c3012a3..77c9ae17ed47 100644 --- a/drivers/iio/light/veml3235.c +++ b/drivers/iio/light/veml3235.c @@ -11,6 +11,7 @@ #include <linux/err.h> #include <linux/i2c.h> #include <linux/iio/iio.h> +#include <linux/iio/iio-gts-helper.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> @@ -35,17 +36,33 @@ struct veml3235_data { struct device *dev; struct regmap *regmap; struct veml3235_rf rf; + struct iio_gts gts; }; -static const int veml3235_it_times[][2] = { - { 0, 50000 }, - { 0, 100000 }, - { 0, 200000 }, - { 0, 400000 }, - { 0, 800000 }, +static const struct iio_itime_sel_mul veml3235_it_sel[] = { + GAIN_SCALE_ITIME_US(50000, 0, 1), + GAIN_SCALE_ITIME_US(100000, 1, 2), + GAIN_SCALE_ITIME_US(200000, 2, 4), + GAIN_SCALE_ITIME_US(400000, 3, 8), + GAIN_SCALE_ITIME_US(800000, 4, 16), }; -static const int veml3235_scale_vals[] = { 1, 2, 4, 8 }; +/* + * The MSB (DG) doubles the value of the rest of the field, which leads to + * two possible combinations to obtain gain = 2 and gain = 4. The gain + * handling can be simplified by restricting DG = 1 to the only gain that + * really requires it, gain = 8. Note that "X10" is a reserved value. + */ +#define VEML3235_SEL_GAIN_X1 0 +#define VEML3235_SEL_GAIN_X2 1 +#define VEML3235_SEL_GAIN_X4 3 +#define VEML3235_SEL_GAIN_X8 7 +static const struct iio_gain_sel_pair veml3235_gain_sel[] = { + GAIN_SCALE_GAIN(1, VEML3235_SEL_GAIN_X1), + GAIN_SCALE_GAIN(2, VEML3235_SEL_GAIN_X2), + GAIN_SCALE_GAIN(4, VEML3235_SEL_GAIN_X4), + GAIN_SCALE_GAIN(8, VEML3235_SEL_GAIN_X8), +}; static int veml3235_power_on(struct veml3235_data *data) { @@ -101,42 +118,58 @@ static const struct iio_chan_spec veml3235_channels[] = { }, }; +static const struct regmap_range veml3235_readable_ranges[] = { + regmap_reg_range(VEML3235_REG_CONF, VEML3235_REG_ID), +}; + +static const struct regmap_access_table veml3235_readable_table = { + .yes_ranges = veml3235_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(veml3235_readable_ranges), +}; + +static const struct regmap_range veml3235_writable_ranges[] = { + regmap_reg_range(VEML3235_REG_CONF, VEML3235_REG_CONF), +}; + +static const struct regmap_access_table veml3235_writable_table = { + .yes_ranges = veml3235_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(veml3235_writable_ranges), +}; + +static const struct regmap_range veml3235_volatile_ranges[] = { + regmap_reg_range(VEML3235_REG_WH_DATA, VEML3235_REG_ALS_DATA), +}; + +static const struct regmap_access_table veml3235_volatile_table = { + .yes_ranges = veml3235_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(veml3235_volatile_ranges), +}; + static const struct regmap_config veml3235_regmap_config = { .name = "veml3235_regmap", .reg_bits = 8, .val_bits = 16, .max_register = VEML3235_REG_ID, .val_format_endian = REGMAP_ENDIAN_LITTLE, + .rd_table = &veml3235_readable_table, + .wr_table = &veml3235_writable_table, + .volatile_table = &veml3235_volatile_table, + .cache_type = REGCACHE_RBTREE, }; static int veml3235_get_it(struct veml3235_data *data, int *val, int *val2) { - int ret, reg; + int ret, it_idx; - ret = regmap_field_read(data->rf.it, ®); + ret = regmap_field_read(data->rf.it, &it_idx); if (ret) return ret; - switch (reg) { - case 0: - *val2 = 50000; - break; - case 1: - *val2 = 100000; - break; - case 2: - *val2 = 200000; - break; - case 3: - *val2 = 400000; - break; - case 4: - *val2 = 800000; - break; - default: - return -EINVAL; - } + ret = iio_gts_find_int_time_by_sel(&data->gts, it_idx); + if (ret < 0) + return ret; + *val2 = ret; *val = 0; return IIO_VAL_INT_PLUS_MICRO; @@ -145,78 +178,78 @@ static int veml3235_get_it(struct veml3235_data *data, int *val, int *val2) static int veml3235_set_it(struct iio_dev *indio_dev, int val, int val2) { struct veml3235_data *data = iio_priv(indio_dev); - int ret, new_it; + int ret, gain_idx, it_idx, new_gain, prev_gain, prev_it; + bool in_range; - if (val) + if (val || !iio_gts_valid_time(&data->gts, val2)) return -EINVAL; - switch (val2) { - case 50000: - new_it = 0x00; - break; - case 100000: - new_it = 0x01; - break; - case 200000: - new_it = 0x02; - break; - case 400000: - new_it = 0x03; - break; - case 800000: - new_it = 0x04; - break; - default: - return -EINVAL; - } + ret = regmap_field_read(data->rf.it, &it_idx); + if (ret) + return ret; - ret = regmap_field_write(data->rf.it, new_it); - if (ret) { - dev_err(data->dev, - "failed to update integration time: %d\n", ret); + ret = regmap_field_read(data->rf.gain, &gain_idx); + if (ret) return ret; - } - return 0; + prev_it = iio_gts_find_int_time_by_sel(&data->gts, it_idx); + if (prev_it < 0) + return prev_it; + + if (prev_it == val2) + return 0; + + prev_gain = iio_gts_find_gain_by_sel(&data->gts, gain_idx); + if (prev_gain < 0) + return prev_gain; + + ret = iio_gts_find_new_gain_by_gain_time_min(&data->gts, prev_gain, prev_it, + val2, &new_gain, &in_range); + if (ret) + return ret; + + if (!in_range) + dev_dbg(data->dev, "Optimal gain out of range\n"); + + ret = iio_gts_find_sel_by_int_time(&data->gts, val2); + if (ret < 0) + return ret; + + ret = regmap_field_write(data->rf.it, ret); + if (ret) + return ret; + + ret = iio_gts_find_sel_by_gain(&data->gts, new_gain); + if (ret < 0) + return ret; + + return regmap_field_write(data->rf.gain, ret); } -static int veml3235_set_gain(struct iio_dev *indio_dev, int val, int val2) +static int veml3235_set_scale(struct iio_dev *indio_dev, int val, int val2) { struct veml3235_data *data = iio_priv(indio_dev); - int ret, new_gain; + int ret, it_idx, gain_sel, time_sel; - if (val2 != 0) - return -EINVAL; + ret = regmap_field_read(data->rf.it, &it_idx); + if (ret) + return ret; - switch (val) { - case 1: - new_gain = 0x00; - break; - case 2: - new_gain = 0x01; - break; - case 4: - new_gain = 0x03; - break; - case 8: - new_gain = 0x07; - break; - default: - return -EINVAL; - } + ret = iio_gts_find_gain_time_sel_for_scale(&data->gts, val, val2, + &gain_sel, &time_sel); + if (ret) + return ret; - ret = regmap_field_write(data->rf.gain, new_gain); - if (ret) { - dev_err(data->dev, "failed to set gain: %d\n", ret); + ret = regmap_field_write(data->rf.it, time_sel); + if (ret) return ret; - } - return 0; + return regmap_field_write(data->rf.gain, gain_sel); } -static int veml3235_get_gain(struct veml3235_data *data, int *val) +static int veml3235_get_scale(struct veml3235_data *data, int *val, int *val2) { - int ret, reg; + int gain, it, reg, ret; ret = regmap_field_read(data->rf.gain, ®); if (ret) { @@ -224,25 +257,25 @@ static int veml3235_get_gain(struct veml3235_data *data, int *val) return ret; } - switch (reg & 0x03) { - case 0: - *val = 1; - break; - case 1: - *val = 2; - break; - case 3: - *val = 4; - break; - default: - return -EINVAL; + gain = iio_gts_find_gain_by_sel(&data->gts, reg); + if (gain < 0) + return gain; + + ret = regmap_field_read(data->rf.it, ®); + if (ret) { + dev_err(data->dev, "failed to read integration time %d\n", ret); + return ret; } - /* Double gain */ - if (reg & 0x04) - *val *= 2; + it = iio_gts_find_int_time_by_sel(&data->gts, reg); + if (it < 0) + return it; + + ret = iio_gts_get_scale(&data->gts, gain, it, val, val2); + if (ret) + return ret; - return IIO_VAL_INT; + return IIO_VAL_INT_PLUS_NANO; } static int veml3235_read_raw(struct iio_dev *indio_dev, @@ -276,7 +309,7 @@ static int veml3235_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_INT_TIME: return veml3235_get_it(data, val, val2); case IIO_CHAN_INFO_SCALE: - return veml3235_get_gain(data, val); + return veml3235_get_scale(data, val, val2); default: return -EINVAL; } @@ -287,17 +320,27 @@ static int veml3235_read_avail(struct iio_dev *indio_dev, const int **vals, int *type, int *length, long mask) { + struct veml3235_data *data = iio_priv(indio_dev); + switch (mask) { case IIO_CHAN_INFO_INT_TIME: - *vals = (int *)&veml3235_it_times; - *length = 2 * ARRAY_SIZE(veml3235_it_times); - *type = IIO_VAL_INT_PLUS_MICRO; - return IIO_AVAIL_LIST; + return iio_gts_avail_times(&data->gts, vals, type, length); + case IIO_CHAN_INFO_SCALE: + return iio_gts_all_avail_scales(&data->gts, vals, type, length); + default: + return -EINVAL; + } +} + +static int veml3235_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { case IIO_CHAN_INFO_SCALE: - *vals = (int *)&veml3235_scale_vals; - *length = ARRAY_SIZE(veml3235_scale_vals); - *type = IIO_VAL_INT; - return IIO_AVAIL_LIST; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_INT_TIME: + return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } @@ -311,7 +354,7 @@ static int veml3235_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_INT_TIME: return veml3235_set_it(indio_dev, val, val2); case IIO_CHAN_INFO_SCALE: - return veml3235_set_gain(indio_dev, val, val2); + return veml3235_set_scale(indio_dev, val, val2); } return -EINVAL; @@ -321,7 +364,7 @@ static void veml3235_read_id(struct veml3235_data *data) { int ret, reg; - ret = regmap_field_read(data->rf.id, ®); + ret = regmap_field_read(data->rf.id, ®); if (ret) { dev_info(data->dev, "failed to read ID\n"); return; @@ -371,6 +414,13 @@ static int veml3235_hw_init(struct iio_dev *indio_dev) struct device *dev = data->dev; int ret; + ret = devm_iio_init_iio_gts(data->dev, 0, 272640000, + veml3235_gain_sel, ARRAY_SIZE(veml3235_gain_sel), + veml3235_it_sel, ARRAY_SIZE(veml3235_it_sel), + &data->gts); + if (ret) + return dev_err_probe(data->dev, ret, "failed to init iio gts\n"); + /* Set gain to 1 and integration time to 100 ms */ ret = regmap_field_write(data->rf.gain, 0x00); if (ret) @@ -389,9 +439,10 @@ static int veml3235_hw_init(struct iio_dev *indio_dev) } static const struct iio_info veml3235_info = { - .read_raw = veml3235_read_raw, - .read_avail = veml3235_read_avail, + .read_raw = veml3235_read_raw, + .read_avail = veml3235_read_avail, .write_raw = veml3235_write_raw, + .write_raw_get_fmt = veml3235_write_raw_get_fmt, }; static int veml3235_probe(struct i2c_client *client) @@ -493,3 +544,4 @@ module_i2c_driver(veml3235_driver); MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>"); MODULE_DESCRIPTION("VEML3235 Ambient Light Sensor"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_GTS_HELPER"); diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index ccb43dfd5cf7..9b71825eea9b 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -28,6 +28,8 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/iio/events.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> /* Device registers */ #define VEML6030_REG_ALS_CONF 0x00 @@ -37,6 +39,7 @@ #define VEML6030_REG_ALS_DATA 0x04 #define VEML6030_REG_WH_DATA 0x05 #define VEML6030_REG_ALS_INT 0x06 +#define VEML6030_REG_DATA(ch) (VEML6030_REG_ALS_DATA + (ch)) /* Bit masks for specific functionality */ #define VEML6030_ALS_IT GENMASK(9, 6) @@ -56,6 +59,12 @@ #define VEML6035_INT_CHAN BIT(3) #define VEML6035_CHAN_EN BIT(2) +enum veml6030_scan { + VEML6030_SCAN_ALS, + VEML6030_SCAN_WH, + VEML6030_SCAN_TIMESTAMP, +}; + struct veml603x_chip { const char *name; const int(*scale_vals)[][2]; @@ -242,6 +251,13 @@ static const struct iio_chan_spec veml6030_channels[] = { BIT(IIO_CHAN_INFO_SCALE), .event_spec = veml6030_event_spec, .num_event_specs = ARRAY_SIZE(veml6030_event_spec), + .scan_index = VEML6030_SCAN_ALS, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, }, { .type = IIO_INTENSITY, @@ -253,7 +269,15 @@ static const struct iio_chan_spec veml6030_channels[] = { BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = VEML6030_SCAN_WH, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, }, + IIO_CHAN_SOFT_TIMESTAMP(VEML6030_SCAN_TIMESTAMP), }; static const struct iio_chan_spec veml7700_channels[] = { @@ -266,6 +290,13 @@ static const struct iio_chan_spec veml7700_channels[] = { BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = VEML6030_SCAN_ALS, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, }, { .type = IIO_INTENSITY, @@ -277,7 +308,15 @@ static const struct iio_chan_spec veml7700_channels[] = { BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = VEML6030_SCAN_WH, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, }, + IIO_CHAN_SOFT_TIMESTAMP(VEML6030_SCAN_TIMESTAMP), }; static const struct regmap_config veml6030_regmap_config = { @@ -889,6 +928,37 @@ static irqreturn_t veml6030_event_handler(int irq, void *private) return IRQ_HANDLED; } +static irqreturn_t veml6030_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *iio = pf->indio_dev; + struct veml6030_data *data = iio_priv(iio); + unsigned int reg; + int ch, ret, i = 0; + struct { + u16 chans[2]; + aligned_s64 timestamp; + } scan; + + memset(&scan, 0, sizeof(scan)); + + iio_for_each_active_channel(iio, ch) { + ret = regmap_read(data->regmap, VEML6030_REG_DATA(ch), + ®); + if (ret) + goto done; + + scan.chans[i++] = reg; + } + + iio_push_to_buffers_with_timestamp(iio, &scan, pf->timestamp); + +done: + iio_trigger_notify_done(iio->trig); + + return IRQ_HANDLED; +} + static int veml6030_set_info(struct iio_dev *indio_dev) { struct veml6030_data *data = iio_priv(indio_dev); @@ -1077,6 +1147,12 @@ static int veml6030_probe(struct i2c_client *client) if (ret < 0) return ret; + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + veml6030_trigger_handler, NULL); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to register triggered buffer"); + return devm_iio_device_register(&client->dev, indio_dev); } diff --git a/drivers/iio/magnetometer/af8133j.c b/drivers/iio/magnetometer/af8133j.c index acd291f3e792..a70bf8a3c73b 100644 --- a/drivers/iio/magnetometer/af8133j.c +++ b/drivers/iio/magnetometer/af8133j.c @@ -360,7 +360,7 @@ static irqreturn_t af8133j_trigger_handler(int irq, void *p) s64 timestamp = iio_get_time_ns(indio_dev); struct { __le16 values[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } sample; int ret; diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index 8306a18706ac..08975c60e325 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -197,7 +197,7 @@ struct ak8974 { /* Ensure timestamp is naturally aligned */ struct { __le16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 18077fb463a9..ef1363126cc2 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -426,7 +426,7 @@ struct ak8975_data { /* Ensure natural alignment of timestamp */ struct { s16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index 7f545740178e..88bb673e40d8 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -140,7 +140,7 @@ struct bmc150_magn_data { /* Ensure timestamp is naturally aligned */ struct { s32 chans[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; struct iio_trigger *dready_trig; bool dready_trigger_on; diff --git a/drivers/iio/magnetometer/hmc5843.h b/drivers/iio/magnetometer/hmc5843.h index 60fbb5431c88..ffd669b1ee7c 100644 --- a/drivers/iio/magnetometer/hmc5843.h +++ b/drivers/iio/magnetometer/hmc5843.h @@ -44,7 +44,7 @@ struct hmc5843_data { struct iio_mount_matrix orientation; struct { __be16 chans[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index 5295dc0100e4..2fe8e97f2cf8 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -60,7 +60,7 @@ struct mag3110_data { struct { __be16 channels[3]; u8 temperature; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index c55a38650c0d..28012b20c64f 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -236,7 +236,7 @@ struct yas5xx { */ struct { s32 channels[4]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c index 2953403bef53..c309d991490c 100644 --- a/drivers/iio/multiplexer/iio-mux.c +++ b/drivers/iio/multiplexer/iio-mux.c @@ -7,6 +7,7 @@ * Author: Peter Rosin <peda@axentia.se> */ +#include <linux/cleanup.h> #include <linux/err.h> #include <linux/iio/consumer.h> #include <linux/iio/iio.h> @@ -237,49 +238,18 @@ static ssize_t mux_write_ext_info(struct iio_dev *indio_dev, uintptr_t private, return ret; } -static int mux_configure_channel(struct device *dev, struct mux *mux, - u32 state, const char *label, int idx) +static int mux_configure_chan_ext_info(struct device *dev, struct mux *mux, + int idx, int num_ext_info) { struct mux_child *child = &mux->child[idx]; - struct iio_chan_spec *chan = &mux->chan[idx]; struct iio_chan_spec const *pchan = mux->parent->channel; - char *page = NULL; - int num_ext_info; int i; int ret; - chan->indexed = 1; - chan->output = pchan->output; - chan->datasheet_name = label; - chan->ext_info = mux->ext_info; - - ret = iio_get_channel_type(mux->parent, &chan->type); - if (ret < 0) { - dev_err(dev, "failed to get parent channel type\n"); - return ret; - } - - if (iio_channel_has_info(pchan, IIO_CHAN_INFO_RAW)) - chan->info_mask_separate |= BIT(IIO_CHAN_INFO_RAW); - if (iio_channel_has_info(pchan, IIO_CHAN_INFO_SCALE)) - chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE); - - if (iio_channel_has_available(pchan, IIO_CHAN_INFO_RAW)) - chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW); - - if (state >= mux_control_states(mux->control)) { - dev_err(dev, "too many channels\n"); - return -EINVAL; - } - - chan->channel = state; + char *page __free(kfree) = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!page) + return -ENOMEM; - num_ext_info = iio_get_channel_ext_info_count(mux->parent); - if (num_ext_info) { - page = devm_kzalloc(dev, PAGE_SIZE, GFP_KERNEL); - if (!page) - return -ENOMEM; - } child->ext_info_cache = devm_kcalloc(dev, num_ext_info, sizeof(*child->ext_info_cache), @@ -318,8 +288,46 @@ static int mux_configure_channel(struct device *dev, struct mux *mux, child->ext_info_cache[i].size = ret; } - if (page) - devm_kfree(dev, page); + return 0; +} + +static int mux_configure_channel(struct device *dev, struct mux *mux, u32 state, + const char *label, int idx) +{ + struct iio_chan_spec *chan = &mux->chan[idx]; + struct iio_chan_spec const *pchan = mux->parent->channel; + int num_ext_info; + int ret; + + chan->indexed = 1; + chan->output = pchan->output; + chan->datasheet_name = label; + chan->ext_info = mux->ext_info; + + ret = iio_get_channel_type(mux->parent, &chan->type); + if (ret < 0) { + dev_err(dev, "failed to get parent channel type\n"); + return ret; + } + + if (iio_channel_has_info(pchan, IIO_CHAN_INFO_RAW)) + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_RAW); + if (iio_channel_has_info(pchan, IIO_CHAN_INFO_SCALE)) + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE); + + if (iio_channel_has_available(pchan, IIO_CHAN_INFO_RAW)) + chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW); + + if (state >= mux_control_states(mux->control)) { + dev_err(dev, "too many channels\n"); + return -EINVAL; + } + + chan->channel = state; + + num_ext_info = iio_get_channel_ext_info_count(mux->parent); + if (num_ext_info) + return mux_configure_chan_ext_info(dev, mux, idx, num_ext_info); return 0; } diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 5376605b69b4..d44ab65c94cb 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -1002,7 +1002,7 @@ static int bmp280_preinit(struct bmp280_data *data) * after resetting, the device uses the complete power-on sequence so * it needs to wait for the defined start-up time. */ - fsleep(data->start_up_time); + fsleep(data->start_up_time_us); ret = regmap_read(data->regmap, BMP280_REG_STATUS, ®); if (ret) @@ -1161,7 +1161,7 @@ const struct bmp280_chip_info bmp280_chip_info = { .chip_id = bmp280_chip_ids, .num_chip_id = ARRAY_SIZE(bmp280_chip_ids), .regmap_config = &bmp280_regmap_config, - .start_up_time = 2000, + .start_up_time_us = 2000, .channels = bmp280_channels, .num_channels = ARRAY_SIZE(bmp280_channels), .avail_scan_masks = bmp280_avail_scan_masks, @@ -1347,7 +1347,7 @@ const struct bmp280_chip_info bme280_chip_info = { .chip_id = bme280_chip_ids, .num_chip_id = ARRAY_SIZE(bme280_chip_ids), .regmap_config = &bme280_regmap_config, - .start_up_time = 2000, + .start_up_time_us = 2000, .channels = bme280_channels, .num_channels = ARRAY_SIZE(bme280_channels), .avail_scan_masks = bme280_avail_scan_masks, @@ -1414,7 +1414,7 @@ static int bmp380_cmd(struct bmp280_data *data, u8 cmd) return ret; } /* Wait for 2ms for command to be processed */ - usleep_range(data->start_up_time, data->start_up_time + 100); + fsleep(data->start_up_time_us); /* Check for command processing error */ ret = regmap_read(data->regmap, BMP380_REG_ERROR, ®); if (ret) { @@ -1806,7 +1806,7 @@ static int bmp380_chip_config(struct bmp280_data *data) * formula in datasheet section 3.9.2 with an offset of ~+15% * as it seen as well in table 3.9.1. */ - msleep(150); + fsleep(150 * USEC_PER_MSEC); /* Check config error flag */ ret = regmap_read(data->regmap, BMP380_REG_ERROR, &tmp); @@ -1957,7 +1957,7 @@ const struct bmp280_chip_info bmp380_chip_info = { .num_chip_id = ARRAY_SIZE(bmp380_chip_ids), .regmap_config = &bmp380_regmap_config, .spi_read_extra_byte = true, - .start_up_time = 2000, + .start_up_time_us = 2000, .channels = bmp380_channels, .num_channels = ARRAY_SIZE(bmp380_channels), .avail_scan_masks = bmp280_avail_scan_masks, @@ -2006,7 +2006,8 @@ static int bmp580_soft_reset(struct bmp280_data *data) dev_err(data->dev, "failed to send reset command to device\n"); return ret; } - usleep_range(2000, 2500); + /* From datasheet's table 4: electrical characteristics */ + fsleep(2000); /* Dummy read of chip_id */ ret = regmap_read(data->regmap, BMP580_REG_CHIP_ID, ®); @@ -2208,7 +2209,7 @@ static int bmp580_nvmem_read_impl(void *priv, unsigned int offset, void *val, goto exit; } /* Wait standby transition time */ - usleep_range(2500, 3000); + fsleep(2500); while (bytes >= sizeof(*dst)) { addr = bmp580_nvmem_addrs[offset / sizeof(*dst)]; @@ -2274,7 +2275,7 @@ static int bmp580_nvmem_write_impl(void *priv, unsigned int offset, void *val, goto exit; } /* Wait standby transition time */ - usleep_range(2500, 3000); + fsleep(2500); while (bytes >= sizeof(*buf)) { addr = bmp580_nvmem_addrs[offset / sizeof(*buf)]; @@ -2458,7 +2459,7 @@ static int bmp580_chip_config(struct bmp280_data *data) return ret; } /* From datasheet's table 4: electrical characteristics */ - usleep_range(2500, 3000); + fsleep(2500); /* Set default DSP mode settings */ reg_val = FIELD_PREP(BMP580_DSP_COMP_MASK, BMP580_DSP_PRESS_TEMP_COMP_EN) | @@ -2649,7 +2650,7 @@ const struct bmp280_chip_info bmp580_chip_info = { .chip_id = bmp580_chip_ids, .num_chip_id = ARRAY_SIZE(bmp580_chip_ids), .regmap_config = &bmp580_regmap_config, - .start_up_time = 2000, + .start_up_time_us = 2000, .channels = bmp580_channels, .num_channels = ARRAY_SIZE(bmp580_channels), .avail_scan_masks = bmp280_avail_scan_masks, @@ -2720,7 +2721,7 @@ static int bmp180_wait_for_eoc(struct bmp280_data *data, u8 ctrl_meas) delay_us = conversion_time_max[data->oversampling_press]; - usleep_range(delay_us, delay_us + 1000); + fsleep(delay_us); } ret = regmap_read(data->regmap, BMP280_REG_CTRL_MEAS, &ctrl); @@ -2988,7 +2989,7 @@ const struct bmp280_chip_info bmp180_chip_info = { .chip_id = bmp180_chip_ids, .num_chip_id = ARRAY_SIZE(bmp180_chip_ids), .regmap_config = &bmp180_regmap_config, - .start_up_time = 2000, + .start_up_time_us = 2000, .channels = bmp280_channels, .num_channels = ARRAY_SIZE(bmp280_channels), .avail_scan_masks = bmp280_avail_scan_masks, @@ -3066,7 +3067,7 @@ const struct bmp280_chip_info bmp085_chip_info = { .chip_id = bmp180_chip_ids, .num_chip_id = ARRAY_SIZE(bmp180_chip_ids), .regmap_config = &bmp180_regmap_config, - .start_up_time = 2000, + .start_up_time_us = 2000, .channels = bmp280_channels, .num_channels = ARRAY_SIZE(bmp280_channels), .avail_scan_masks = bmp280_avail_scan_masks, @@ -3175,7 +3176,7 @@ int bmp280_common_probe(struct device *dev, data->oversampling_temp = chip_info->oversampling_temp_default; data->iir_filter_coeff = chip_info->iir_filter_coeff_default; data->sampling_freq = chip_info->sampling_freq_default; - data->start_up_time = chip_info->start_up_time; + data->start_up_time_us = chip_info->start_up_time_us; /* Bring up regulators */ regulator_bulk_set_supply_names(data->supplies, @@ -3201,7 +3202,7 @@ int bmp280_common_probe(struct device *dev, return ret; /* Wait to make sure we started up properly */ - usleep_range(data->start_up_time, data->start_up_time + 100); + fsleep(data->start_up_time_us); /* Bring chip out of reset if there is an assigned GPIO line */ gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); @@ -3287,7 +3288,7 @@ int bmp280_common_probe(struct device *dev, * Set autosuspend to two orders of magnitude larger than the * start-up time. */ - pm_runtime_set_autosuspend_delay(dev, data->start_up_time / 10); + pm_runtime_set_autosuspend_delay(dev, data->start_up_time_us / 10); pm_runtime_use_autosuspend(dev); pm_runtime_put(dev); @@ -3306,7 +3307,7 @@ static int bmp280_runtime_suspend(struct device *dev) data->chip_info->set_mode(data, BMP280_SLEEP); - fsleep(data->start_up_time); + fsleep(data->start_up_time_us); return regulator_bulk_disable(BMP280_NUM_SUPPLIES, data->supplies); } @@ -3320,7 +3321,7 @@ static int bmp280_runtime_resume(struct device *dev) if (ret) return ret; - usleep_range(data->start_up_time, data->start_up_time + 100); + fsleep(data->start_up_time_us); ret = data->chip_info->chip_config(data); if (ret) diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index 2df1175b6b85..5b2ee1d0ee46 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -434,7 +434,7 @@ struct bmp280_data { struct bmp380_calib bmp380; } calib; struct regulator_bulk_data supplies[BMP280_NUM_SUPPLIES]; - unsigned int start_up_time; /* in microseconds */ + unsigned int start_up_time_us; /* log of base 2 of oversampling rate */ u8 oversampling_press; @@ -470,8 +470,8 @@ struct bmp280_data { /* Sensor data buffer */ u8 buf[BME280_BURST_READ_BYTES]; /* Calibration data buffers */ - __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; - __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; + __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / sizeof(__le16)]; + __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / sizeof(__be16)]; u8 bme280_humid_cal_buf[BME280_CONTIGUOUS_CALIB_REGS]; u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT]; /* Miscellaneous, endianness-aware data buffers */ @@ -490,7 +490,7 @@ struct bmp280_chip_info { const struct iio_chan_spec *channels; int num_channels; - unsigned int start_up_time; + unsigned int start_up_time_us; const unsigned long *avail_scan_masks; const int *oversampling_temp_avail; diff --git a/drivers/iio/pressure/hsc030pa.h b/drivers/iio/pressure/hsc030pa.h index 9b40f46f575f..5db46784f4c6 100644 --- a/drivers/iio/pressure/hsc030pa.h +++ b/drivers/iio/pressure/hsc030pa.h @@ -58,7 +58,7 @@ struct hsc_data { s32 p_offset_dec; struct { __be16 chan[2]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; u8 buffer[HSC_REG_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN); }; diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index 056c8271c49d..00c077b2a2a4 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -213,7 +213,7 @@ static irqreturn_t ms5611_trigger_handler(int irq, void *p) /* Ensure buffer elements are naturally aligned */ struct { s32 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; int ret; diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index f24d9f927681..9c1197f0e742 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -8,6 +8,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/i2c.h> #include <linux/module.h> @@ -138,7 +139,7 @@ enum { struct bm1390_data_buf { u32 pressure; __be16 temp; - s64 ts __aligned(8); + aligned_s64 ts; }; /* BM1390 has FIFO for 4 pressure samples */ @@ -263,14 +264,14 @@ static int bm1390_read_data(struct bm1390_data *data, { int ret, warn; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); /* * We use 'continuous mode' even for raw read because according to the * data-sheet an one-shot mode can't be used with IIR filter. */ ret = bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS); if (ret) - goto unlock_out; + return ret; switch (chan->type) { case IIO_PRESSURE: @@ -287,10 +288,8 @@ static int bm1390_read_data(struct bm1390_data *data, warn = bm1390_meas_set(data, BM1390_MEAS_MODE_STOP); if (warn) dev_warn(data->dev, "Failed to stop measurement (%d)\n", warn); -unlock_out: - mutex_unlock(&data->mutex); - return ret; + return 0; } static int bm1390_read_raw(struct iio_dev *idev, @@ -543,38 +542,33 @@ static int bm1390_fifo_enable(struct iio_dev *idev) if (data->irq <= 0) return -EINVAL; - mutex_lock(&data->mutex); - if (data->trigger_enabled) { - ret = -EBUSY; - goto unlock_out; - } + guard(mutex)(&data->mutex); + + if (data->trigger_enabled) + return -EBUSY; /* Update watermark to HW */ ret = bm1390_fifo_set_wmi(data); if (ret) - goto unlock_out; + return ret; /* Enable WMI_IRQ */ ret = regmap_set_bits(data->regmap, BM1390_REG_MODE_CTRL, BM1390_MASK_WMI_EN); if (ret) - goto unlock_out; + return ret; /* Enable FIFO */ ret = regmap_set_bits(data->regmap, BM1390_REG_FIFO_CTRL, BM1390_MASK_FIFO_EN); if (ret) - goto unlock_out; + return ret; data->state = BM1390_STATE_FIFO; data->old_timestamp = iio_get_time_ns(idev); - ret = bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS); -unlock_out: - mutex_unlock(&data->mutex); - - return ret; + return bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS); } static int bm1390_fifo_disable(struct iio_dev *idev) @@ -584,27 +578,22 @@ static int bm1390_fifo_disable(struct iio_dev *idev) msleep(1); - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = bm1390_meas_set(data, BM1390_MEAS_MODE_STOP); if (ret) - goto unlock_out; + return ret; /* Disable FIFO */ ret = regmap_clear_bits(data->regmap, BM1390_REG_FIFO_CTRL, BM1390_MASK_FIFO_EN); if (ret) - goto unlock_out; + return ret; data->state = BM1390_STATE_SAMPLE; /* Disable WMI_IRQ */ - ret = regmap_clear_bits(data->regmap, BM1390_REG_MODE_CTRL, + return regmap_clear_bits(data->regmap, BM1390_REG_MODE_CTRL, BM1390_MASK_WMI_EN); - -unlock_out: - mutex_unlock(&data->mutex); - - return ret; } static int bm1390_buffer_postenable(struct iio_dev *idev) @@ -688,25 +677,24 @@ static irqreturn_t bm1390_irq_thread_handler(int irq, void *private) { struct iio_dev *idev = private; struct bm1390_data *data = iio_priv(idev); - int ret = IRQ_NONE; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (data->trigger_enabled) { iio_trigger_poll_nested(data->trig); - ret = IRQ_HANDLED; - } else if (data->state == BM1390_STATE_FIFO) { + return IRQ_HANDLED; + } + + if (data->state == BM1390_STATE_FIFO) { int ok; ok = __bm1390_fifo_flush(idev, BM1390_FIFO_LENGTH, data->timestamp); if (ok > 0) - ret = IRQ_HANDLED; + return IRQ_HANDLED; } - mutex_unlock(&data->mutex); - - return ret; + return IRQ_NONE; } static int bm1390_set_drdy_irq(struct bm1390_data *data, bool en) @@ -722,17 +710,16 @@ static int bm1390_trigger_set_state(struct iio_trigger *trig, bool state) { struct bm1390_data *data = iio_trigger_get_drvdata(trig); - int ret = 0; + int ret; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (data->trigger_enabled == state) - goto unlock_out; + return 0; if (data->state == BM1390_STATE_FIFO) { dev_warn(data->dev, "Can't set trigger when FIFO enabled\n"); - ret = -EBUSY; - goto unlock_out; + return -EBUSY; } data->trigger_enabled = state; @@ -740,13 +727,13 @@ static int bm1390_trigger_set_state(struct iio_trigger *trig, if (state) { ret = bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS); if (ret) - goto unlock_out; + return ret; } else { int dummy; ret = bm1390_meas_set(data, BM1390_MEAS_MODE_STOP); if (ret) - goto unlock_out; + return ret; /* * We need to read the status register in order to ACK the @@ -758,12 +745,7 @@ static int bm1390_trigger_set_state(struct iio_trigger *trig, dev_warn(data->dev, "status read failed\n"); } - ret = bm1390_set_drdy_irq(data, state); - -unlock_out: - mutex_unlock(&data->mutex); - - return ret; + return bm1390_set_drdy_irq(data, state); } static const struct iio_trigger_ops bm1390_trigger_ops = { diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index 96fa97451cbf..9d3caf2bef18 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -63,7 +63,7 @@ struct as3935_state { /* Ensure timestamp is naturally aligned */ struct { u8 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; u8 buf[2] __aligned(IIO_DMA_MINALIGN); }; diff --git a/drivers/iio/proximity/aw96103.c b/drivers/iio/proximity/aw96103.c index cdd254da9e50..3472a2c36e44 100644 --- a/drivers/iio/proximity/aw96103.c +++ b/drivers/iio/proximity/aw96103.c @@ -433,7 +433,7 @@ static int aw96103_write_event_config(struct iio_dev *indio_dev, state ? BIT(chan->channel) : 0); } -static struct iio_info iio_info = { +static const struct iio_info iio_info = { .read_raw = aw96103_read_raw, .read_event_value = aw96103_read_event_val, .write_event_value = aw96103_write_event_val, diff --git a/drivers/iio/proximity/hx9023s.c b/drivers/iio/proximity/hx9023s.c index 4021feb7a7ac..e092a935dbac 100644 --- a/drivers/iio/proximity/hx9023s.c +++ b/drivers/iio/proximity/hx9023s.c @@ -14,6 +14,7 @@ #include <linux/cleanup.h> #include <linux/device.h> #include <linux/errno.h> +#include <linux/firmware.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/irqreturn.h> @@ -100,6 +101,17 @@ #define HX9023S_INTERRUPT_MASK GENMASK(9, 0) #define HX9023S_PROX_DEBOUNCE_MASK GENMASK(3, 0) +#define FW_VER_OFFSET 2 +#define FW_REG_CNT_OFFSET 3 +#define FW_DATA_OFFSET 16 + +struct hx9023s_bin { + u16 reg_count; + u16 fw_size; + u8 fw_ver; + u8 data[] __counted_by(fw_size); +}; + struct hx9023s_ch_data { s16 raw; /* Raw Data*/ s16 lp; /* Low Pass Filter Data*/ @@ -134,7 +146,7 @@ struct hx9023s_data { struct { __le16 channels[HX9023S_CH_NUM]; - s64 ts __aligned(8); + aligned_s64 ts; } buffer; /* @@ -998,6 +1010,77 @@ static int hx9023s_id_check(struct iio_dev *indio_dev) return 0; } +static int hx9023s_bin_load(struct hx9023s_data *data, struct hx9023s_bin *bin) +{ + u8 *cfg_start = bin->data + FW_DATA_OFFSET; + u8 addr, val; + u16 i; + int ret; + + for (i = 0; i < bin->reg_count; i++) { + addr = cfg_start[i * 2]; + val = cfg_start[i * 2 + 1]; + ret = regmap_write(data->regmap, addr, val); + if (ret < 0) + return ret; + } + + return 0; +} + +static int hx9023s_send_cfg(const struct firmware *fw, struct hx9023s_data *data) +{ + struct hx9023s_bin *bin __free(kfree) = + kzalloc(fw->size + sizeof(*bin), GFP_KERNEL); + if (!bin) + return -ENOMEM; + + memcpy(bin->data, fw->data, fw->size); + release_firmware(fw); + + bin->fw_size = fw->size; + bin->fw_ver = bin->data[FW_VER_OFFSET]; + bin->reg_count = get_unaligned_le16(bin->data + FW_REG_CNT_OFFSET); + + return hx9023s_bin_load(data, bin); +} + +static void hx9023s_cfg_update(const struct firmware *fw, void *context) +{ + struct hx9023s_data *data = context; + struct device *dev = regmap_get_device(data->regmap); + int ret; + + if (!fw || !fw->data) { + dev_warn(dev, "No firmware\n"); + goto no_fw; + } + + ret = hx9023s_send_cfg(fw, data); + if (ret) { + dev_warn(dev, "Firmware update failed: %d\n", ret); + goto no_fw; + } + + ret = regcache_sync(data->regmap); + if (ret) + dev_err(dev, "regcache sync failed\n"); + + return; + +no_fw: + ret = regmap_multi_reg_write(data->regmap, hx9023s_reg_init_list, + ARRAY_SIZE(hx9023s_reg_init_list)); + if (ret) { + dev_err(dev, "Error loading default configuration\n"); + return; + } + + ret = regcache_sync(data->regmap); + if (ret) + dev_err(dev, "regcache sync failed\n"); +} + static int hx9023s_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1036,18 +1119,14 @@ static int hx9023s_probe(struct i2c_client *client) indio_dev->modes = INDIO_DIRECT_MODE; i2c_set_clientdata(client, indio_dev); - ret = regmap_multi_reg_write(data->regmap, hx9023s_reg_init_list, - ARRAY_SIZE(hx9023s_reg_init_list)); - if (ret) - return dev_err_probe(dev, ret, "device init failed\n"); - ret = hx9023s_ch_cfg(data); if (ret) return dev_err_probe(dev, ret, "channel config failed\n"); - ret = regcache_sync(data->regmap); + ret = request_firmware_nowait(THIS_MODULE, true, "hx9023s.bin", dev, + GFP_KERNEL, data, hx9023s_cfg_update); if (ret) - return dev_err_probe(dev, ret, "regcache sync failed\n"); + return dev_err_probe(dev, ret, "reg config failed\n"); if (client->irq) { ret = devm_request_threaded_irq(dev, client->irq, diff --git a/drivers/iio/proximity/mb1232.c b/drivers/iio/proximity/mb1232.c index 614e65cb9d42..cfc75d001f20 100644 --- a/drivers/iio/proximity/mb1232.c +++ b/drivers/iio/proximity/mb1232.c @@ -45,7 +45,7 @@ struct mb1232_data { /* Ensure correct alignment of data to push to IIO buffer */ struct { s16 distance; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index 5c959730aecd..f3d054b06b4c 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -47,7 +47,7 @@ struct lidar_data { /* Ensure timestamp is naturally aligned */ struct { u16 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c index a75ea5042876..86cab113ef3d 100644 --- a/drivers/iio/proximity/srf08.c +++ b/drivers/iio/proximity/srf08.c @@ -66,7 +66,7 @@ struct srf08_data { /* Ensure timestamp is naturally aligned */ struct { s16 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; /* Sensor-Type */ diff --git a/drivers/iio/proximity/sx_common.h b/drivers/iio/proximity/sx_common.h index fb14e6f06a6d..259b5c695233 100644 --- a/drivers/iio/proximity/sx_common.h +++ b/drivers/iio/proximity/sx_common.h @@ -125,7 +125,7 @@ struct sx_common_data { /* Ensure correct alignment of timestamp when present. */ struct { __be16 channels[SX_COMMON_MAX_NUM_CHANNELS]; - s64 ts __aligned(8); + aligned_s64 ts; } buffer; unsigned int suspend_ctrl; diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index a414eef12e5e..b681129a99b6 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -164,7 +164,7 @@ struct ad2s1210_state { struct { __be16 chan[2]; /* Ensure timestamp is naturally aligned. */ - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; /** SPI transmit buffer. */ u8 rx[2]; diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c index 02b27f471baa..1998047a1f24 100644 --- a/drivers/iio/temperature/tmp006.c +++ b/drivers/iio/temperature/tmp006.c @@ -248,7 +248,7 @@ static irqreturn_t tmp006_trigger_handler(int irq, void *p) struct tmp006_data *data = iio_priv(indio_dev); struct { s16 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; s32 ret; diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index bb60b2d7b2ec..e41cb741253b 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -38,6 +38,9 @@ static const void *triggers_table[][MAX_TRIGGERS] = { { TIM15_TRGO,}, { TIM16_OC1,}, { TIM17_OC1,}, + { }, /* timer 18 */ + { }, /* timer 19 */ + { TIM20_TRGO, TIM20_TRGO2, TIM20_OC1, TIM20_OC2, TIM20_OC3, }, }; /* List the triggers accepted by each timer */ @@ -119,7 +122,7 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, unsigned int frequency) { unsigned long long prd, div; - int prescaler = 0; + int prescaler = 0, ret; u32 ccer; /* Period and prescaler values depends of clock rate */ @@ -150,10 +153,12 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, if (ccer & TIM_CCER_CCXE) return -EBUSY; - mutex_lock(&priv->lock); + guard(mutex)(&priv->lock); if (!priv->enabled) { priv->enabled = true; - clk_enable(priv->clk); + ret = clk_enable(priv->clk); + if (ret) + return ret; } regmap_write(priv->regmap, TIM_PSC, prescaler); @@ -173,7 +178,6 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, /* Enable controller */ regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); - mutex_unlock(&priv->lock); return 0; } @@ -307,7 +311,7 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev, struct stm32_timer_trigger *priv = dev_get_drvdata(dev); struct iio_trigger *trig = to_iio_trigger(dev); u32 mask, shift, master_mode_max; - int i; + int i, ret; if (stm32_timer_is_trgo2_name(trig->name)) { mask = TIM_CR2_MMS2; @@ -322,15 +326,16 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev, for (i = 0; i <= master_mode_max; i++) { if (!strncmp(master_mode_table[i], buf, strlen(master_mode_table[i]))) { - mutex_lock(&priv->lock); + guard(mutex)(&priv->lock); if (!priv->enabled) { /* Clock should be enabled first */ priv->enabled = true; - clk_enable(priv->clk); + ret = clk_enable(priv->clk); + if (ret) + return ret; } regmap_update_bits(priv->regmap, TIM_CR2, mask, i << shift); - mutex_unlock(&priv->lock); return len; } } @@ -482,6 +487,7 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -491,12 +497,14 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, /* fixed scale */ return -EINVAL; - case IIO_CHAN_INFO_ENABLE: - mutex_lock(&priv->lock); + case IIO_CHAN_INFO_ENABLE: { + guard(mutex)(&priv->lock); if (val) { if (!priv->enabled) { priv->enabled = true; - clk_enable(priv->clk); + ret = clk_enable(priv->clk); + if (ret) + return ret; } regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); } else { @@ -506,11 +514,12 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, clk_disable(priv->clk); } } - mutex_unlock(&priv->lock); + return 0; } - - return -EINVAL; + default: + return -EINVAL; + } } static int stm32_counter_validate_trigger(struct iio_dev *indio_dev, @@ -602,6 +611,7 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev, { struct stm32_timer_trigger *priv = iio_priv(indio_dev); int sms = stm32_enable_mode2sms(mode); + int ret; if (sms < 0) return sms; @@ -609,12 +619,15 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev, * Triggered mode sets CEN bit automatically by hardware. So, first * enable counter clock, so it can use it. Keeps it in sync with CEN. */ - mutex_lock(&priv->lock); - if (sms == 6 && !priv->enabled) { - clk_enable(priv->clk); - priv->enabled = true; + scoped_guard(mutex, &priv->lock) { + if (sms == 6 && !priv->enabled) { + ret = clk_enable(priv->clk); + if (ret) + return ret; + + priv->enabled = true; + } } - mutex_unlock(&priv->lock); regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms); @@ -781,7 +794,7 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) return -EINVAL; /* Create an IIO device only if we have triggers to be validated */ - if (*cfg->valids_table[index]) + if (cfg->valids_table && *cfg->valids_table[index]) priv = stm32_setup_counter_device(dev); else priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -794,7 +807,8 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) priv->clk = ddata->clk; priv->max_arr = ddata->max_arr; priv->triggers = triggers_table[index]; - priv->valids = cfg->valids_table[index]; + if (cfg->valids_table && *cfg->valids_table[index]) + priv->valids = cfg->valids_table[index]; stm32_timer_detect_trgo2(priv); mutex_init(&priv->lock); @@ -886,6 +900,16 @@ static const struct stm32_timer_trigger_cfg stm32h7_timer_trg_cfg = { .num_valids_table = ARRAY_SIZE(stm32h7_valids_table), }; +static const struct stm32_timer_trigger_cfg stm32mp25_timer_trg_cfg = { + /* + * valids_table not used: counter framework is now superseding the deprecated IIO + * counter interface (IIO_COUNT), so don't support it. num_valids_table is only + * kept here to register the IIO HW triggers. valids_table should be moved at some + * point to the stm32-timer-cnt driver instead. + */ + .num_valids_table = ARRAY_SIZE(triggers_table), +}; + static const struct of_device_id stm32_trig_of_match[] = { { .compatible = "st,stm32-timer-trigger", @@ -893,6 +917,9 @@ static const struct of_device_id stm32_trig_of_match[] = { }, { .compatible = "st,stm32h7-timer-trigger", .data = (void *)&stm32h7_timer_trg_cfg, + }, { + .compatible = "st,stm32mp25-timer-trigger", + .data = (void *)&stm32mp25_timer_trg_cfg, }, { /* end node */ }, }; diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 362fb9b0a198..1219f4f23d40 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -337,6 +337,15 @@ config INTERCONNECT_QCOM_SM8650 This is a driver for the Qualcomm Network-on-Chip on SM8650-based platforms. +config INTERCONNECT_QCOM_SM8750 + tristate "Qualcomm SM8750 interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Network-on-Chip on SM8750-based + platforms. + config INTERCONNECT_QCOM_X1E80100 tristate "Qualcomm X1E80100 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 9997728c02bf..7887b1e8d69b 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -40,6 +40,7 @@ qnoc-sm8350-objs := sm8350.o qnoc-sm8450-objs := sm8450.o qnoc-sm8550-objs := sm8550.o qnoc-sm8650-objs := sm8650.o +qnoc-sm8750-objs := sm8750.o qnoc-x1e80100-objs := x1e80100.o icc-smd-rpm-objs := smd-rpm.o icc-rpm.o icc-rpm-clocks.o @@ -80,5 +81,6 @@ obj-$(CONFIG_INTERCONNECT_QCOM_SM8350) += qnoc-sm8350.o obj-$(CONFIG_INTERCONNECT_QCOM_SM8450) += qnoc-sm8450.o obj-$(CONFIG_INTERCONNECT_QCOM_SM8550) += qnoc-sm8550.o obj-$(CONFIG_INTERCONNECT_QCOM_SM8650) += qnoc-sm8650.o +obj-$(CONFIG_INTERCONNECT_QCOM_SM8750) += qnoc-sm8750.o obj-$(CONFIG_INTERCONNECT_QCOM_X1E80100) += qnoc-x1e80100.o obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o diff --git a/drivers/interconnect/qcom/sm8750.c b/drivers/interconnect/qcom/sm8750.c new file mode 100644 index 000000000000..69bc22222075 --- /dev/null +++ b/drivers/interconnect/qcom/sm8750.c @@ -0,0 +1,1705 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + * + */ + +#include <linux/device.h> +#include <linux/interconnect.h> +#include <linux/interconnect-provider.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <dt-bindings/interconnect/qcom,sm8750-rpmh.h> + +#include "bcm-voter.h" +#include "icc-rpmh.h" + +#define SM8750_MASTER_GPU_TCU 0 +#define SM8750_MASTER_SYS_TCU 1 +#define SM8750_MASTER_APPSS_PROC 2 +#define SM8750_MASTER_LLCC 3 +#define SM8750_MASTER_QDSS_BAM 4 +#define SM8750_MASTER_QSPI_0 5 +#define SM8750_MASTER_QUP_1 6 +#define SM8750_MASTER_QUP_2 7 +#define SM8750_MASTER_A1NOC_SNOC 8 +#define SM8750_MASTER_A2NOC_SNOC 9 +#define SM8750_MASTER_CAMNOC_HF 10 +#define SM8750_MASTER_CAMNOC_NRT_ICP_SF 11 +#define SM8750_MASTER_CAMNOC_RT_CDM_SF 12 +#define SM8750_MASTER_CAMNOC_SF 13 +#define SM8750_MASTER_GEM_NOC_CNOC 14 +#define SM8750_MASTER_GEM_NOC_PCIE_SNOC 15 +#define SM8750_MASTER_GFX3D 16 +#define SM8750_MASTER_LPASS_GEM_NOC 17 +#define SM8750_MASTER_LPASS_LPINOC 18 +#define SM8750_MASTER_LPIAON_NOC 19 +#define SM8750_MASTER_LPASS_PROC 20 +#define SM8750_MASTER_MDP 21 +#define SM8750_MASTER_MSS_PROC 22 +#define SM8750_MASTER_MNOC_HF_MEM_NOC 23 +#define SM8750_MASTER_MNOC_SF_MEM_NOC 24 +#define SM8750_MASTER_CDSP_PROC 25 +#define SM8750_MASTER_COMPUTE_NOC 26 +#define SM8750_MASTER_ANOC_PCIE_GEM_NOC 27 +#define SM8750_MASTER_SNOC_SF_MEM_NOC 28 +#define SM8750_MASTER_UBWC_P 29 +#define SM8750_MASTER_CDSP_HCP 30 +#define SM8750_MASTER_VIDEO_CV_PROC 31 +#define SM8750_MASTER_VIDEO_EVA 32 +#define SM8750_MASTER_VIDEO_MVP 33 +#define SM8750_MASTER_VIDEO_V_PROC 34 +#define SM8750_MASTER_CNOC_CFG 35 +#define SM8750_MASTER_CNOC_MNOC_CFG 36 +#define SM8750_MASTER_PCIE_ANOC_CFG 37 +#define SM8750_MASTER_QUP_CORE_0 38 +#define SM8750_MASTER_QUP_CORE_1 39 +#define SM8750_MASTER_QUP_CORE_2 40 +#define SM8750_MASTER_CRYPTO 41 +#define SM8750_MASTER_IPA 42 +#define SM8750_MASTER_QUP_3 43 +#define SM8750_MASTER_SOCCP_AGGR_NOC 44 +#define SM8750_MASTER_SP 45 +#define SM8750_MASTER_GIC 46 +#define SM8750_MASTER_PCIE_0 47 +#define SM8750_MASTER_QDSS_ETR 48 +#define SM8750_MASTER_QDSS_ETR_1 49 +#define SM8750_MASTER_SDCC_2 50 +#define SM8750_MASTER_SDCC_4 51 +#define SM8750_MASTER_UFS_MEM 52 +#define SM8750_MASTER_USB3_0 53 +#define SM8750_SLAVE_UBWC_P 54 +#define SM8750_SLAVE_EBI1 55 +#define SM8750_SLAVE_AHB2PHY_SOUTH 56 +#define SM8750_SLAVE_AHB2PHY_NORTH 57 +#define SM8750_SLAVE_AOSS 58 +#define SM8750_SLAVE_CAMERA_CFG 59 +#define SM8750_SLAVE_CLK_CTL 60 +#define SM8750_SLAVE_CRYPTO_0_CFG 61 +#define SM8750_SLAVE_DISPLAY_CFG 62 +#define SM8750_SLAVE_EVA_CFG 63 +#define SM8750_SLAVE_GFX3D_CFG 64 +#define SM8750_SLAVE_I2C 65 +#define SM8750_SLAVE_I3C_IBI0_CFG 66 +#define SM8750_SLAVE_I3C_IBI1_CFG 67 +#define SM8750_SLAVE_IMEM_CFG 68 +#define SM8750_SLAVE_IPA_CFG 69 +#define SM8750_SLAVE_IPC_ROUTER_CFG 70 +#define SM8750_SLAVE_CNOC_MSS 71 +#define SM8750_SLAVE_PCIE_CFG 72 +#define SM8750_SLAVE_PRNG 73 +#define SM8750_SLAVE_QDSS_CFG 74 +#define SM8750_SLAVE_QSPI_0 75 +#define SM8750_SLAVE_QUP_3 76 +#define SM8750_SLAVE_QUP_1 77 +#define SM8750_SLAVE_QUP_2 78 +#define SM8750_SLAVE_SDCC_2 79 +#define SM8750_SLAVE_SDCC_4 80 +#define SM8750_SLAVE_SOCCP 81 +#define SM8750_SLAVE_SPSS_CFG 82 +#define SM8750_SLAVE_TCSR 83 +#define SM8750_SLAVE_TLMM 84 +#define SM8750_SLAVE_TME_CFG 85 +#define SM8750_SLAVE_UFS_MEM_CFG 86 +#define SM8750_SLAVE_USB3_0 87 +#define SM8750_SLAVE_VENUS_CFG 88 +#define SM8750_SLAVE_VSENSE_CTRL_CFG 89 +#define SM8750_SLAVE_A1NOC_SNOC 90 +#define SM8750_SLAVE_A2NOC_SNOC 91 +#define SM8750_SLAVE_APPSS 92 +#define SM8750_SLAVE_GEM_NOC_CNOC 93 +#define SM8750_SLAVE_SNOC_GEM_NOC_SF 94 +#define SM8750_SLAVE_LLCC 95 +#define SM8750_SLAVE_LPASS_GEM_NOC 96 +#define SM8750_SLAVE_LPIAON_NOC_LPASS_AG_NOC 97 +#define SM8750_SLAVE_LPICX_NOC_LPIAON_NOC 98 +#define SM8750_SLAVE_MNOC_HF_MEM_NOC 99 +#define SM8750_SLAVE_MNOC_SF_MEM_NOC 100 +#define SM8750_SLAVE_CDSP_MEM_NOC 101 +#define SM8750_SLAVE_MEM_NOC_PCIE_SNOC 102 +#define SM8750_SLAVE_ANOC_PCIE_GEM_NOC 103 +#define SM8750_SLAVE_CNOC_CFG 104 +#define SM8750_SLAVE_DDRSS_CFG 105 +#define SM8750_SLAVE_CNOC_MNOC_CFG 106 +#define SM8750_SLAVE_PCIE_ANOC_CFG 107 +#define SM8750_SLAVE_QUP_CORE_0 108 +#define SM8750_SLAVE_QUP_CORE_1 109 +#define SM8750_SLAVE_QUP_CORE_2 110 +#define SM8750_SLAVE_BOOT_IMEM 111 +#define SM8750_SLAVE_IMEM 112 +#define SM8750_SLAVE_BOOT_IMEM_2 113 +#define SM8750_SLAVE_SERVICE_CNOC 114 +#define SM8750_SLAVE_SERVICE_MNOC 115 +#define SM8750_SLAVE_SERVICE_PCIE_ANOC 116 +#define SM8750_SLAVE_PCIE_0 117 +#define SM8750_SLAVE_QDSS_STM 118 +#define SM8750_SLAVE_TCU 119 + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .id = SM8750_MASTER_QSPI_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup1 = { + .name = "qhm_qup1", + .id = SM8750_MASTER_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_qup02 = { + .name = "qxm_qup02", + .id = SM8750_MASTER_QUP_3, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_sdc4 = { + .name = "xm_sdc4", + .id = SM8750_MASTER_SDCC_4, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .id = SM8750_MASTER_UFS_MEM, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .id = SM8750_MASTER_USB3_0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qdss_bam = { + .name = "qhm_qdss_bam", + .id = SM8750_MASTER_QDSS_BAM, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup2 = { + .name = "qhm_qup2", + .id = SM8750_MASTER_QUP_2, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .id = SM8750_MASTER_CRYPTO, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .id = SM8750_MASTER_IPA, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_soccp = { + .name = "qxm_soccp", + .id = SM8750_MASTER_SOCCP_AGGR_NOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_sp = { + .name = "qxm_sp", + .id = SM8750_MASTER_SP, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .id = SM8750_MASTER_QDSS_ETR, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .id = SM8750_MASTER_QDSS_ETR_1, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .id = SM8750_MASTER_SDCC_2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .id = SM8750_MASTER_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_SLAVE_QUP_CORE_0 }, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .id = SM8750_MASTER_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_SLAVE_QUP_CORE_1 }, +}; + +static struct qcom_icc_node qup2_core_master = { + .name = "qup2_core_master", + .id = SM8750_MASTER_QUP_CORE_2, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_SLAVE_QUP_CORE_2 }, +}; + +static struct qcom_icc_node qsm_cfg = { + .name = "qsm_cfg", + .id = SM8750_MASTER_CNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 33, + .links = { SM8750_SLAVE_AHB2PHY_SOUTH, SM8750_SLAVE_AHB2PHY_NORTH, + SM8750_SLAVE_CAMERA_CFG, SM8750_SLAVE_CLK_CTL, + SM8750_SLAVE_CRYPTO_0_CFG, SM8750_SLAVE_DISPLAY_CFG, + SM8750_SLAVE_EVA_CFG, SM8750_SLAVE_GFX3D_CFG, + SM8750_SLAVE_I2C, SM8750_SLAVE_I3C_IBI0_CFG, + SM8750_SLAVE_I3C_IBI1_CFG, SM8750_SLAVE_IMEM_CFG, + SM8750_SLAVE_CNOC_MSS, SM8750_SLAVE_PCIE_CFG, + SM8750_SLAVE_PRNG, SM8750_SLAVE_QDSS_CFG, + SM8750_SLAVE_QSPI_0, SM8750_SLAVE_QUP_3, + SM8750_SLAVE_QUP_1, SM8750_SLAVE_QUP_2, + SM8750_SLAVE_SDCC_2, SM8750_SLAVE_SDCC_4, + SM8750_SLAVE_SPSS_CFG, SM8750_SLAVE_TCSR, + SM8750_SLAVE_TLMM, SM8750_SLAVE_UFS_MEM_CFG, + SM8750_SLAVE_USB3_0, SM8750_SLAVE_VENUS_CFG, + SM8750_SLAVE_VSENSE_CTRL_CFG, SM8750_SLAVE_CNOC_MNOC_CFG, + SM8750_SLAVE_PCIE_ANOC_CFG, SM8750_SLAVE_QDSS_STM, + SM8750_SLAVE_TCU }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .id = SM8750_MASTER_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 12, + .links = { SM8750_SLAVE_AOSS, SM8750_SLAVE_IPA_CFG, + SM8750_SLAVE_IPC_ROUTER_CFG, SM8750_SLAVE_SOCCP, + SM8750_SLAVE_TME_CFG, SM8750_SLAVE_APPSS, + SM8750_SLAVE_CNOC_CFG, SM8750_SLAVE_DDRSS_CFG, + SM8750_SLAVE_BOOT_IMEM, SM8750_SLAVE_IMEM, + SM8750_SLAVE_BOOT_IMEM_2, SM8750_SLAVE_SERVICE_CNOC }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .id = SM8750_MASTER_GEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_PCIE_0 }, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .id = SM8750_MASTER_GPU_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SM8750_SLAVE_GEM_NOC_CNOC, SM8750_SLAVE_LLCC }, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .id = SM8750_MASTER_SYS_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SM8750_SLAVE_GEM_NOC_CNOC, SM8750_SLAVE_LLCC }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .id = SM8750_MASTER_APPSS_PROC, + .channels = 4, + .buswidth = 32, + .num_links = 4, + .links = { SM8750_SLAVE_UBWC_P, SM8750_SLAVE_GEM_NOC_CNOC, + SM8750_SLAVE_LLCC, SM8750_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .id = SM8750_MASTER_GFX3D, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { SM8750_SLAVE_GEM_NOC_CNOC, SM8750_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_lpass_gemnoc = { + .name = "qnm_lpass_gemnoc", + .id = SM8750_MASTER_LPASS_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 3, + .links = { SM8750_SLAVE_GEM_NOC_CNOC, SM8750_SLAVE_LLCC, + SM8750_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_mdsp = { + .name = "qnm_mdsp", + .id = SM8750_MASTER_MSS_PROC, + .channels = 1, + .buswidth = 16, + .num_links = 3, + .links = { SM8750_SLAVE_GEM_NOC_CNOC, SM8750_SLAVE_LLCC, + SM8750_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .id = SM8750_MASTER_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { SM8750_SLAVE_GEM_NOC_CNOC, SM8750_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .id = SM8750_MASTER_MNOC_SF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { SM8750_SLAVE_GEM_NOC_CNOC, SM8750_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_nsp_gemnoc = { + .name = "qnm_nsp_gemnoc", + .id = SM8750_MASTER_COMPUTE_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 3, + .links = { SM8750_SLAVE_GEM_NOC_CNOC, SM8750_SLAVE_LLCC, + SM8750_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .id = SM8750_MASTER_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SM8750_SLAVE_GEM_NOC_CNOC, SM8750_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .id = SM8750_MASTER_SNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 3, + .links = { SM8750_SLAVE_GEM_NOC_CNOC, SM8750_SLAVE_LLCC, + SM8750_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_ubwc_p = { + .name = "qnm_ubwc_p", + .id = SM8750_MASTER_UBWC_P, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SM8750_SLAVE_LLCC }, +}; + +static struct qcom_icc_node xm_gic = { + .name = "xm_gic", + .id = SM8750_MASTER_GIC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc = { + .name = "qnm_lpiaon_noc", + .id = SM8750_MASTER_LPIAON_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_SLAVE_LPASS_GEM_NOC }, +}; + +static struct qcom_icc_node qnm_lpass_lpinoc = { + .name = "qnm_lpass_lpinoc", + .id = SM8750_MASTER_LPASS_LPINOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_SLAVE_LPIAON_NOC_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node qnm_lpinoc_dsp_qns4m = { + .name = "qnm_lpinoc_dsp_qns4m", + .id = SM8750_MASTER_LPASS_PROC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_SLAVE_LPICX_NOC_LPIAON_NOC }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .id = SM8750_MASTER_LLCC, + .channels = 4, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_SLAVE_EBI1 }, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .id = SM8750_MASTER_CAMNOC_HF, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8750_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_camnoc_nrt_icp_sf = { + .name = "qnm_camnoc_nrt_icp_sf", + .id = SM8750_MASTER_CAMNOC_NRT_ICP_SF, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_camnoc_rt_cdm_sf = { + .name = "qnm_camnoc_rt_cdm_sf", + .id = SM8750_MASTER_CAMNOC_RT_CDM_SF, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .id = SM8750_MASTER_CAMNOC_SF, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8750_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_mdp = { + .name = "qnm_mdp", + .id = SM8750_MASTER_MDP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8750_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_vapss_hcp = { + .name = "qnm_vapss_hcp", + .id = SM8750_MASTER_CDSP_HCP, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SM8750_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_cv_cpu = { + .name = "qnm_video_cv_cpu", + .id = SM8750_MASTER_VIDEO_CV_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_eva = { + .name = "qnm_video_eva", + .id = SM8750_MASTER_VIDEO_EVA, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8750_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_mvp = { + .name = "qnm_video_mvp", + .id = SM8750_MASTER_VIDEO_MVP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8750_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_v_cpu = { + .name = "qnm_video_v_cpu", + .id = SM8750_MASTER_VIDEO_V_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qsm_mnoc_cfg = { + .name = "qsm_mnoc_cfg", + .id = SM8750_MASTER_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_SLAVE_SERVICE_MNOC }, +}; + +static struct qcom_icc_node qnm_nsp = { + .name = "qnm_nsp", + .id = SM8750_MASTER_CDSP_PROC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8750_SLAVE_CDSP_MEM_NOC }, +}; + +static struct qcom_icc_node qsm_pcie_anoc_cfg = { + .name = "qsm_pcie_anoc_cfg", + .id = SM8750_MASTER_PCIE_ANOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_SLAVE_SERVICE_PCIE_ANOC }, +}; + +static struct qcom_icc_node xm_pcie3 = { + .name = "xm_pcie3", + .id = SM8750_MASTER_PCIE_0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node qnm_aggre1_noc = { + .name = "qnm_aggre1_noc", + .id = SM8750_MASTER_A1NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_aggre2_noc = { + .name = "qnm_aggre2_noc", + .id = SM8750_MASTER_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .id = SM8750_SLAVE_A1NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_MASTER_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qns_a2noc_snoc = { + .name = "qns_a2noc_snoc", + .id = SM8750_SLAVE_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_MASTER_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .id = SM8750_SLAVE_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .id = SM8750_SLAVE_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup2_core_slave = { + .name = "qup2_core_slave", + .id = SM8750_SLAVE_QUP_CORE_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .id = SM8750_SLAVE_AHB2PHY_SOUTH, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy1 = { + .name = "qhs_ahb2phy1", + .id = SM8750_SLAVE_AHB2PHY_NORTH, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .id = SM8750_SLAVE_CAMERA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .id = SM8750_SLAVE_CLK_CTL, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .id = SM8750_SLAVE_CRYPTO_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .id = SM8750_SLAVE_DISPLAY_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_eva_cfg = { + .name = "qhs_eva_cfg", + .id = SM8750_SLAVE_EVA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .id = SM8750_SLAVE_GFX3D_CFG, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_i2c = { + .name = "qhs_i2c", + .id = SM8750_SLAVE_I2C, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_i3c_ibi0_cfg = { + .name = "qhs_i3c_ibi0_cfg", + .id = SM8750_SLAVE_I3C_IBI0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_i3c_ibi1_cfg = { + .name = "qhs_i3c_ibi1_cfg", + .id = SM8750_SLAVE_I3C_IBI1_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .id = SM8750_SLAVE_IMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mss_cfg = { + .name = "qhs_mss_cfg", + .id = SM8750_SLAVE_CNOC_MSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie_cfg = { + .name = "qhs_pcie_cfg", + .id = SM8750_SLAVE_PCIE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .id = SM8750_SLAVE_PRNG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .id = SM8750_SLAVE_QDSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .id = SM8750_SLAVE_QSPI_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup02 = { + .name = "qhs_qup02", + .id = SM8750_SLAVE_QUP_3, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .id = SM8750_SLAVE_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup2 = { + .name = "qhs_qup2", + .id = SM8750_SLAVE_QUP_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .id = SM8750_SLAVE_SDCC_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc4 = { + .name = "qhs_sdc4", + .id = SM8750_SLAVE_SDCC_4, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_spss_cfg = { + .name = "qhs_spss_cfg", + .id = SM8750_SLAVE_SPSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .id = SM8750_SLAVE_TCSR, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .id = SM8750_SLAVE_TLMM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .id = SM8750_SLAVE_UFS_MEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb3_0 = { + .name = "qhs_usb3_0", + .id = SM8750_SLAVE_USB3_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .id = SM8750_SLAVE_VENUS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .name = "qhs_vsense_ctrl_cfg", + .id = SM8750_SLAVE_VSENSE_CTRL_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qss_mnoc_cfg = { + .name = "qss_mnoc_cfg", + .id = SM8750_SLAVE_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_MASTER_CNOC_MNOC_CFG }, +}; + +static struct qcom_icc_node qss_pcie_anoc_cfg = { + .name = "qss_pcie_anoc_cfg", + .id = SM8750_SLAVE_PCIE_ANOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_MASTER_PCIE_ANOC_CFG }, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .id = SM8750_SLAVE_QDSS_STM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .id = SM8750_SLAVE_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .id = SM8750_SLAVE_AOSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .id = SM8750_SLAVE_IPA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .id = SM8750_SLAVE_IPC_ROUTER_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_soccp = { + .name = "qhs_soccp", + .id = SM8750_SLAVE_SOCCP, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tme_cfg = { + .name = "qhs_tme_cfg", + .id = SM8750_SLAVE_TME_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_apss = { + .name = "qns_apss", + .id = SM8750_SLAVE_APPSS, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qss_cfg = { + .name = "qss_cfg", + .id = SM8750_SLAVE_CNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8750_MASTER_CNOC_CFG }, +}; + +static struct qcom_icc_node qss_ddrss_cfg = { + .name = "qss_ddrss_cfg", + .id = SM8750_SLAVE_DDRSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_boot_imem = { + .name = "qxs_boot_imem", + .id = SM8750_SLAVE_BOOT_IMEM, + .channels = 1, + .buswidth = 16, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .id = SM8750_SLAVE_IMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_modem_boot_imem = { + .name = "qxs_modem_boot_imem", + .id = SM8750_SLAVE_BOOT_IMEM_2, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_cnoc_main = { + .name = "srvc_cnoc_main", + .id = SM8750_SLAVE_SERVICE_CNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie = { + .name = "xs_pcie", + .id = SM8750_SLAVE_PCIE_0, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node chs_ubwc_p = { + .name = "chs_ubwc_p", + .id = SM8750_SLAVE_UBWC_P, + .channels = 1, + .buswidth = 32, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .id = SM8750_SLAVE_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_MASTER_GEM_NOC_CNOC }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .id = SM8750_SLAVE_LLCC, + .channels = 4, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_MASTER_LLCC }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .id = SM8750_SLAVE_MEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_MASTER_GEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qns_lpass_ag_noc_gemnoc = { + .name = "qns_lpass_ag_noc_gemnoc", + .id = SM8750_SLAVE_LPASS_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_MASTER_LPASS_GEM_NOC }, +}; + +static struct qcom_icc_node qns_lpass_aggnoc = { + .name = "qns_lpass_aggnoc", + .id = SM8750_SLAVE_LPIAON_NOC_LPASS_AG_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_MASTER_LPIAON_NOC }, +}; + +static struct qcom_icc_node qns_lpi_aon_noc = { + .name = "qns_lpi_aon_noc", + .id = SM8750_SLAVE_LPICX_NOC_LPIAON_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_MASTER_LPASS_LPINOC }, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .id = SM8750_SLAVE_EBI1, + .channels = 4, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .id = SM8750_SLAVE_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8750_MASTER_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .id = SM8750_SLAVE_MNOC_SF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8750_MASTER_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node srvc_mnoc = { + .name = "srvc_mnoc", + .id = SM8750_SLAVE_SERVICE_MNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .id = SM8750_SLAVE_CDSP_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8750_MASTER_COMPUTE_NOC }, +}; + +static struct qcom_icc_node qns_pcie_mem_noc = { + .name = "qns_pcie_mem_noc", + .id = SM8750_SLAVE_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8750_MASTER_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node srvc_pcie_aggre_noc = { + .name = "srvc_pcie_aggre_noc", + .id = SM8750_SLAVE_SERVICE_PCIE_ANOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .id = SM8750_SLAVE_SNOC_GEM_NOC_SF, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8750_MASTER_SNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .enable_mask = BIT(0), + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .enable_mask = BIT(0), + .keepalive = true, + .num_nodes = 44, + .nodes = { &qsm_cfg, &qhs_ahb2phy0, + &qhs_ahb2phy1, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_crypto0_cfg, + &qhs_eva_cfg, &qhs_gpuss_cfg, + &qhs_i3c_ibi0_cfg, &qhs_i3c_ibi1_cfg, + &qhs_imem_cfg, &qhs_mss_cfg, + &qhs_pcie_cfg, &qhs_prng, + &qhs_qdss_cfg, &qhs_qspi, + &qhs_sdc2, &qhs_sdc4, + &qhs_spss_cfg, &qhs_tcsr, + &qhs_tlmm, &qhs_ufs_mem_cfg, + &qhs_usb3_0, &qhs_venus_cfg, + &qhs_vsense_ctrl_cfg, &qss_mnoc_cfg, + &qss_pcie_anoc_cfg, &xs_qdss_stm, + &xs_sys_tcu_cfg, &qnm_gemnoc_cnoc, + &qnm_gemnoc_pcie, &qhs_aoss, + &qhs_ipa, &qhs_ipc_router, + &qhs_soccp, &qhs_tme_cfg, + &qns_apss, &qss_cfg, + &qss_ddrss_cfg, &qxs_boot_imem, + &qxs_imem, &qxs_modem_boot_imem, + &srvc_cnoc_main, &xs_pcie }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 5, + .nodes = { &qhs_display_cfg, &qhs_i2c, + &qhs_qup02, &qhs_qup1, + &qhs_qup2 }, +}; + +static struct qcom_icc_bcm bcm_co0 = { + .name = "CO0", + .enable_mask = BIT(0), + .num_nodes = 2, + .nodes = { &qnm_nsp, &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_bcm bcm_lp0 = { + .name = "LP0", + .num_nodes = 2, + .nodes = { &qnm_lpass_lpinoc, &qns_lpass_aggnoc }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .enable_mask = BIT(0), + .num_nodes = 9, + .nodes = { &qnm_camnoc_hf, &qnm_camnoc_nrt_icp_sf, + &qnm_camnoc_rt_cdm_sf, &qnm_camnoc_sf, + &qnm_vapss_hcp, &qnm_video_cv_cpu, + &qnm_video_mvp, &qnm_video_v_cpu, + &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_qup0 = { + .name = "QUP0", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup1 = { + .name = "QUP1", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup2 = { + .name = "QUP2", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh1 = { + .name = "SH1", + .enable_mask = BIT(0), + .num_nodes = 14, + .nodes = { &alm_gpu_tcu, &alm_sys_tcu, + &chm_apps, &qnm_gpu, + &qnm_mdsp, &qnm_mnoc_hf, + &qnm_mnoc_sf, &qnm_nsp_gemnoc, + &qnm_pcie, &qnm_snoc_sf, + &xm_gic, &chs_ubwc_p, + &qns_gem_noc_cnoc, &qns_pcie }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn2 = { + .name = "SN2", + .num_nodes = 1, + .nodes = { &qnm_aggre1_noc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 1, + .nodes = { &qnm_aggre2_noc }, +}; + +static struct qcom_icc_bcm bcm_sn4 = { + .name = "SN4", + .num_nodes = 1, + .nodes = { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_bcm bcm_ubw0 = { + .name = "UBW0", + .num_nodes = 1, + .nodes = { &qnm_ubwc_p }, +}; + +static struct qcom_icc_node * const aggre1_noc_nodes[] = { + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_QUP_1] = &qhm_qup1, + [MASTER_QUP_3] = &qxm_qup02, + [MASTER_SDCC_4] = &xm_sdc4, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB3_0] = &xm_usb3_0, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, +}; + +static const struct qcom_icc_desc sm8750_aggre1_noc = { + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), +}; + +static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { + &bcm_ce0, +}; + +static struct qcom_icc_node * const aggre2_noc_nodes[] = { + [MASTER_QDSS_BAM] = &qhm_qdss_bam, + [MASTER_QUP_2] = &qhm_qup2, + [MASTER_CRYPTO] = &qxm_crypto, + [MASTER_IPA] = &qxm_ipa, + [MASTER_SOCCP_AGGR_NOC] = &qxm_soccp, + [MASTER_SP] = &qxm_sp, + [MASTER_QDSS_ETR] = &xm_qdss_etr_0, + [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1, + [MASTER_SDCC_2] = &xm_sdc2, + [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, +}; + +static const struct qcom_icc_desc sm8750_aggre2_noc = { + .nodes = aggre2_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), + .bcms = aggre2_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), +}; + +static struct qcom_icc_bcm * const clk_virt_bcms[] = { + &bcm_qup0, + &bcm_qup1, + &bcm_qup2, +}; + +static struct qcom_icc_node * const clk_virt_nodes[] = { + [MASTER_QUP_CORE_0] = &qup0_core_master, + [MASTER_QUP_CORE_1] = &qup1_core_master, + [MASTER_QUP_CORE_2] = &qup2_core_master, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, + [SLAVE_QUP_CORE_2] = &qup2_core_slave, +}; + +static const struct qcom_icc_desc sm8750_clk_virt = { + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .bcms = clk_virt_bcms, + .num_bcms = ARRAY_SIZE(clk_virt_bcms), +}; + +static struct qcom_icc_bcm * const config_noc_bcms[] = { + &bcm_cn0, + &bcm_cn1, +}; + +static struct qcom_icc_node * const config_noc_nodes[] = { + [MASTER_CNOC_CFG] = &qsm_cfg, + [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, + [SLAVE_AHB2PHY_NORTH] = &qhs_ahb2phy1, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_EVA_CFG] = &qhs_eva_cfg, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_I2C] = &qhs_i2c, + [SLAVE_I3C_IBI0_CFG] = &qhs_i3c_ibi0_cfg, + [SLAVE_I3C_IBI1_CFG] = &qhs_i3c_ibi1_cfg, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_CNOC_MSS] = &qhs_mss_cfg, + [SLAVE_PCIE_CFG] = &qhs_pcie_cfg, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_3] = &qhs_qup02, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_QUP_2] = &qhs_qup2, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_SDCC_4] = &qhs_sdc4, + [SLAVE_SPSS_CFG] = &qhs_spss_cfg, + [SLAVE_TCSR] = &qhs_tcsr, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB3_0] = &qhs_usb3_0, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_CNOC_MNOC_CFG] = &qss_mnoc_cfg, + [SLAVE_PCIE_ANOC_CFG] = &qss_pcie_anoc_cfg, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct qcom_icc_desc sm8750_config_noc = { + .nodes = config_noc_nodes, + .num_nodes = ARRAY_SIZE(config_noc_nodes), + .bcms = config_noc_bcms, + .num_bcms = ARRAY_SIZE(config_noc_bcms), +}; + +static struct qcom_icc_bcm * const cnoc_main_bcms[] = { + &bcm_cn0, +}; + +static struct qcom_icc_node * const cnoc_main_nodes[] = { + [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc, + [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_IPA_CFG] = &qhs_ipa, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_SOCCP] = &qhs_soccp, + [SLAVE_TME_CFG] = &qhs_tme_cfg, + [SLAVE_APPSS] = &qns_apss, + [SLAVE_CNOC_CFG] = &qss_cfg, + [SLAVE_DDRSS_CFG] = &qss_ddrss_cfg, + [SLAVE_BOOT_IMEM] = &qxs_boot_imem, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_BOOT_IMEM_2] = &qxs_modem_boot_imem, + [SLAVE_SERVICE_CNOC] = &srvc_cnoc_main, + [SLAVE_PCIE_0] = &xs_pcie, +}; + +static const struct qcom_icc_desc sm8750_cnoc_main = { + .nodes = cnoc_main_nodes, + .num_nodes = ARRAY_SIZE(cnoc_main_nodes), + .bcms = cnoc_main_bcms, + .num_bcms = ARRAY_SIZE(cnoc_main_bcms), +}; + +static struct qcom_icc_bcm * const gem_noc_bcms[] = { + &bcm_sh0, + &bcm_sh1, + &bcm_ubw0, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_LPASS_GEM_NOC] = &qnm_lpass_gemnoc, + [MASTER_MSS_PROC] = &qnm_mdsp, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_COMPUTE_NOC] = &qnm_nsp_gemnoc, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_UBWC_P] = &qnm_ubwc_p, + [MASTER_GIC] = &xm_gic, + [SLAVE_UBWC_P] = &chs_ubwc_p, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, +}; + +static const struct qcom_icc_desc sm8750_gem_noc = { + .nodes = gem_noc_nodes, + .num_nodes = ARRAY_SIZE(gem_noc_nodes), + .bcms = gem_noc_bcms, + .num_bcms = ARRAY_SIZE(gem_noc_bcms), +}; + +static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { + [MASTER_LPIAON_NOC] = &qnm_lpiaon_noc, + [SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc, +}; + +static const struct qcom_icc_desc sm8750_lpass_ag_noc = { + .nodes = lpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), +}; + +static struct qcom_icc_bcm * const lpass_lpiaon_noc_bcms[] = { + &bcm_lp0, +}; + +static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = { + [MASTER_LPASS_LPINOC] = &qnm_lpass_lpinoc, + [SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc, +}; + +static const struct qcom_icc_desc sm8750_lpass_lpiaon_noc = { + .nodes = lpass_lpiaon_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes), + .bcms = lpass_lpiaon_noc_bcms, + .num_bcms = ARRAY_SIZE(lpass_lpiaon_noc_bcms), +}; + +static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = { + [MASTER_LPASS_PROC] = &qnm_lpinoc_dsp_qns4m, + [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc, +}; + +static const struct qcom_icc_desc sm8750_lpass_lpicx_noc = { + .nodes = lpass_lpicx_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes), +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_acv, + &bcm_mc0, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI1] = &ebi, +}; + +static const struct qcom_icc_desc sm8750_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), +}; + +static struct qcom_icc_bcm * const mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, +}; + +static struct qcom_icc_node * const mmss_noc_nodes[] = { + [MASTER_CAMNOC_HF] = &qnm_camnoc_hf, + [MASTER_CAMNOC_NRT_ICP_SF] = &qnm_camnoc_nrt_icp_sf, + [MASTER_CAMNOC_RT_CDM_SF] = &qnm_camnoc_rt_cdm_sf, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_MDP] = &qnm_mdp, + [MASTER_CDSP_HCP] = &qnm_vapss_hcp, + [MASTER_VIDEO_CV_PROC] = &qnm_video_cv_cpu, + [MASTER_VIDEO_EVA] = &qnm_video_eva, + [MASTER_VIDEO_MVP] = &qnm_video_mvp, + [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu, + [MASTER_CNOC_MNOC_CFG] = &qsm_mnoc_cfg, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, + [SLAVE_SERVICE_MNOC] = &srvc_mnoc, +}; + +static const struct qcom_icc_desc sm8750_mmss_noc = { + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm * const nsp_noc_bcms[] = { + &bcm_co0, +}; + +static struct qcom_icc_node * const nsp_noc_nodes[] = { + [MASTER_CDSP_PROC] = &qnm_nsp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, +}; + +static const struct qcom_icc_desc sm8750_nsp_noc = { + .nodes = nsp_noc_nodes, + .num_nodes = ARRAY_SIZE(nsp_noc_nodes), + .bcms = nsp_noc_bcms, + .num_bcms = ARRAY_SIZE(nsp_noc_bcms), +}; + +static struct qcom_icc_bcm * const pcie_anoc_bcms[] = { + &bcm_sn4, +}; + +static struct qcom_icc_node * const pcie_anoc_nodes[] = { + [MASTER_PCIE_ANOC_CFG] = &qsm_pcie_anoc_cfg, + [MASTER_PCIE_0] = &xm_pcie3, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, + [SLAVE_SERVICE_PCIE_ANOC] = &srvc_pcie_aggre_noc, +}; + +static const struct qcom_icc_desc sm8750_pcie_anoc = { + .nodes = pcie_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_anoc_nodes), + .bcms = pcie_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_anoc_bcms), +}; + +static struct qcom_icc_bcm * const system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn2, + &bcm_sn3, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc, + [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, +}; + +static const struct qcom_icc_desc sm8750_system_noc = { + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,sm8750-aggre1-noc", .data = &sm8750_aggre1_noc}, + { .compatible = "qcom,sm8750-aggre2-noc", .data = &sm8750_aggre2_noc}, + { .compatible = "qcom,sm8750-clk-virt", .data = &sm8750_clk_virt}, + { .compatible = "qcom,sm8750-config-noc", .data = &sm8750_config_noc}, + { .compatible = "qcom,sm8750-cnoc-main", .data = &sm8750_cnoc_main}, + { .compatible = "qcom,sm8750-gem-noc", .data = &sm8750_gem_noc}, + { .compatible = "qcom,sm8750-lpass-ag-noc", .data = &sm8750_lpass_ag_noc}, + { .compatible = "qcom,sm8750-lpass-lpiaon-noc", .data = &sm8750_lpass_lpiaon_noc}, + { .compatible = "qcom,sm8750-lpass-lpicx-noc", .data = &sm8750_lpass_lpicx_noc}, + { .compatible = "qcom,sm8750-mc-virt", .data = &sm8750_mc_virt}, + { .compatible = "qcom,sm8750-mmss-noc", .data = &sm8750_mmss_noc}, + { .compatible = "qcom,sm8750-nsp-noc", .data = &sm8750_nsp_noc}, + { .compatible = "qcom,sm8750-pcie-anoc", .data = &sm8750_pcie_anoc}, + { .compatible = "qcom,sm8750-system-noc", .data = &sm8750_system_noc}, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-sm8750", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} +module_exit(qnoc_driver_exit); + +MODULE_DESCRIPTION("SM8750 NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index ae4e8b8e6eb7..043b9ec756ff 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -200,7 +200,7 @@ static int memstick_dummy_check(struct memstick_dev *card) /** * memstick_detect_change - schedule media detection on memstick host - * @host - host to use + * @host: host to use */ void memstick_detect_change(struct memstick_host *host) { @@ -210,13 +210,15 @@ EXPORT_SYMBOL(memstick_detect_change); /** * memstick_next_req - called by host driver to obtain next request to process - * @host - host to use - * @mrq - pointer to stick the request to + * @host: host to use + * @mrq: pointer to stick the request to * * Host calls this function from idle state (*mrq == NULL) or after finishing * previous request (*mrq should point to it). If previous request was - * unsuccessful, it is retried for predetermined number of times. Return value - * of 0 means that new request was assigned to the host. + * unsuccessful, it is retried for predetermined number of times. + * + * Returns: value of 0 means that new request was assigned to the host. + * Otherwise a negative error code is returned. */ int memstick_next_req(struct memstick_host *host, struct memstick_request **mrq) { @@ -242,7 +244,7 @@ EXPORT_SYMBOL(memstick_next_req); /** * memstick_new_req - notify the host that some requests are pending - * @host - host to use + * @host: host to use */ void memstick_new_req(struct memstick_host *host) { @@ -256,9 +258,9 @@ EXPORT_SYMBOL(memstick_new_req); /** * memstick_init_req_sg - set request fields needed for bulk data transfer - * @mrq - request to use - * @tpc - memstick Transport Protocol Command - * @sg - TPC argument + * @mrq: request to use + * @tpc: memstick Transport Protocol Command + * @sg: TPC argument */ void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc, const struct scatterlist *sg) @@ -281,10 +283,10 @@ EXPORT_SYMBOL(memstick_init_req_sg); /** * memstick_init_req - set request fields needed for short data transfer - * @mrq - request to use - * @tpc - memstick Transport Protocol Command - * @buf - TPC argument buffer - * @length - TPC argument size + * @mrq: request to use + * @tpc: memstick Transport Protocol Command + * @buf: TPC argument buffer + * @length: TPC argument size * * The intended use of this function (transfer of data items several bytes * in size) allows us to just copy the value between request structure and @@ -360,7 +362,9 @@ static int h_memstick_set_rw_addr(struct memstick_dev *card, /** * memstick_set_rw_addr - issue SET_RW_REG_ADDR request and wait for it to * complete - * @card - media device to use + * @card: media device to use + * + * Returns: error setting for the current request */ int memstick_set_rw_addr(struct memstick_dev *card) { @@ -487,6 +491,8 @@ out_power_off: * memstick_alloc_host - allocate a memstick_host structure * @extra: size of the user private data to allocate * @dev: parent device of the host + * + * Returns: %NULL on failure or the allocated &memstick_host pointer on success */ struct memstick_host *memstick_alloc_host(unsigned int extra, struct device *dev) @@ -507,7 +513,9 @@ EXPORT_SYMBOL(memstick_alloc_host); /** * memstick_add_host - start request processing on memstick host - * @host - host to use + * @host: host to use + * + * Returns: %0 on success or a negative error code on failure */ int memstick_add_host(struct memstick_host *host) { @@ -543,7 +551,7 @@ EXPORT_SYMBOL(memstick_add_host); /** * memstick_remove_host - stop request processing on memstick host - * @host - host to use + * @host: host to use */ void memstick_remove_host(struct memstick_host *host) { @@ -565,7 +573,7 @@ EXPORT_SYMBOL(memstick_remove_host); /** * memstick_free_host - free memstick host - * @host - host to use + * @host: host to use */ void memstick_free_host(struct memstick_host *host) { @@ -576,7 +584,7 @@ EXPORT_SYMBOL(memstick_free_host); /** * memstick_suspend_host - notify bus driver of host suspension - * @host - host to use + * @host: host to use */ void memstick_suspend_host(struct memstick_host *host) { @@ -588,7 +596,7 @@ EXPORT_SYMBOL(memstick_suspend_host); /** * memstick_resume_host - notify bus driver of host resumption - * @host - host to use + * @host: host to use */ void memstick_resume_host(struct memstick_host *host) { diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 09cbe3f0ab1e..56bc72c7ce4a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -517,7 +517,6 @@ config OPEN_DICE config NTSYNC tristate "NT synchronization primitive emulation" - depends on BROKEN help This module provides kernel support for emulation of Windows NT synchronization primitives. It is not a hardware driver. @@ -613,8 +612,7 @@ config MARVELL_CN10K_DPI config MCHP_LAN966X_PCI tristate "Microchip LAN966x PCIe Support" depends on PCI - select OF - select OF_OVERLAY + depends on OF_OVERLAY select IRQ_DOMAIN help This enables the support for the LAN966x PCIe device. diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 40bf953185c7..545aad06d088 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o +obj-$(CONFIG_TEST_MISC_MINOR) += misc_minor_kunit.o obj-$(CONFIG_SGI_XP) += sgi-xp/ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_SMPRO_ERRMON) += smpro-errmon.o diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c index 2bb1dd2511f9..fc64474b8241 100644 --- a/drivers/misc/c2port/core.c +++ b/drivers/misc/c2port/core.c @@ -714,7 +714,7 @@ static ssize_t __c2port_read_flash_data(struct c2port_device *dev, } static ssize_t c2port_read_flash_data(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buffer, loff_t offset, size_t count) { struct c2port_device *c2dev = dev_get_drvdata(kobj_to_dev(kobj)); @@ -829,7 +829,7 @@ static ssize_t __c2port_write_flash_data(struct c2port_device *dev, } static ssize_t c2port_write_flash_data(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buffer, loff_t offset, size_t count) { struct c2port_device *c2dev = dev_get_drvdata(kobj_to_dev(kobj)); @@ -849,8 +849,8 @@ static ssize_t c2port_write_flash_data(struct file *filp, struct kobject *kobj, return ret; } /* size is computed at run-time */ -static BIN_ATTR(flash_data, 0644, c2port_read_flash_data, - c2port_write_flash_data, 0); +static const BIN_ATTR(flash_data, 0644, c2port_read_flash_data, + c2port_write_flash_data, 0); /* * Class attributes @@ -869,14 +869,27 @@ static struct attribute *c2port_attrs[] = { NULL, }; -static struct bin_attribute *c2port_bin_attrs[] = { +static const struct bin_attribute *const c2port_bin_attrs[] = { &bin_attr_flash_data, NULL, }; +static size_t c2port_bin_attr_size(struct kobject *kobj, + const struct bin_attribute *attr, + int i) +{ + struct c2port_device *c2dev = dev_get_drvdata(kobj_to_dev(kobj)); + + if (attr == &bin_attr_flash_data) + return c2dev->ops->blocks_num * c2dev->ops->block_size; + + return attr->size; +} + static const struct attribute_group c2port_group = { .attrs = c2port_attrs, - .bin_attrs = c2port_bin_attrs, + .bin_attrs_new = c2port_bin_attrs, + .bin_size = c2port_bin_attr_size, }; static const struct attribute_group *c2port_groups[] = { @@ -912,8 +925,7 @@ struct c2port_device *c2port_device_register(char *name, if (ret < 0) goto error_idr_alloc; c2dev->id = ret; - - bin_attr_flash_data.size = ops->blocks_num * ops->block_size; + c2dev->ops = ops; c2dev->dev = device_create(c2port_class, NULL, 0, c2dev, "c2port%d", c2dev->id); @@ -924,7 +936,6 @@ struct c2port_device *c2port_device_register(char *name, dev_set_drvdata(c2dev->dev, c2dev); strscpy(c2dev->name, name, sizeof(c2dev->name)); - c2dev->ops = ops; mutex_init(&c2dev->mutex); /* By default C2 port access is off */ diff --git a/drivers/misc/cardreader/rtsx_usb.c b/drivers/misc/cardreader/rtsx_usb.c index 77b0490a1b38..e0174da5e9fc 100644 --- a/drivers/misc/cardreader/rtsx_usb.c +++ b/drivers/misc/cardreader/rtsx_usb.c @@ -286,6 +286,7 @@ static int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status) int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status) { int ret; + u8 interrupt_val = 0; u16 *buf; if (!status) @@ -308,6 +309,20 @@ int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status) ret = rtsx_usb_get_status_with_bulk(ucr, status); } + rtsx_usb_read_register(ucr, CARD_INT_PEND, &interrupt_val); + /* Cross check presence with interrupts */ + if (*status & XD_CD) + if (!(interrupt_val & XD_INT)) + *status &= ~XD_CD; + + if (*status & SD_CD) + if (!(interrupt_val & SD_INT)) + *status &= ~SD_CD; + + if (*status & MS_CD) + if (!(interrupt_val & MS_INT)) + *status &= ~MS_CD; + /* usb_control_msg may return positive when success */ if (ret < 0) return ret; diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c index 409bd1c39663..b1fc6446bd4b 100644 --- a/drivers/misc/cxl/sysfs.c +++ b/drivers/misc/cxl/sysfs.c @@ -444,7 +444,7 @@ static ssize_t api_version_compatible_show(struct device *device, } static ssize_t afu_eb_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct cxl_afu *afu = to_cxl_afu(kobj_to_dev(kobj)); @@ -538,7 +538,7 @@ static ssize_t class_show(struct kobject *kobj, } static ssize_t afu_read_config(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct afu_config_record *cr = to_cr(kobj); @@ -620,7 +620,7 @@ static struct afu_config_record *cxl_sysfs_afu_new_cr(struct cxl_afu *afu, int c cr->config_attr.attr.name = "config"; cr->config_attr.attr.mode = S_IRUSR; cr->config_attr.size = afu->crs_len; - cr->config_attr.read = afu_read_config; + cr->config_attr.read_new = afu_read_config; rc = kobject_init_and_add(&cr->kobj, &afu_config_record_type, &afu->dev.kobj, "cr%i", cr->cr); @@ -693,7 +693,7 @@ int cxl_sysfs_afu_add(struct cxl_afu *afu) afu->attr_eb.attr.name = "afu_err_buff"; afu->attr_eb.attr.mode = S_IRUGO; afu->attr_eb.size = afu->eb_len; - afu->attr_eb.read = afu_eb_read; + afu->attr_eb.read_new = afu_eb_read; rc = device_create_bin_file(&afu->dev, &afu->attr_eb); if (rc) { diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index 4175df7ef011..5d5a70a62e98 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -154,7 +154,7 @@ static const struct attribute_group ds1682_group = { * User data attribute */ static ssize_t ds1682_eeprom_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct i2c_client *client = kobj_to_i2c_client(kobj); @@ -172,7 +172,7 @@ static ssize_t ds1682_eeprom_read(struct file *filp, struct kobject *kobj, } static ssize_t ds1682_eeprom_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct i2c_client *client = kobj_to_i2c_client(kobj); @@ -194,8 +194,8 @@ static const struct bin_attribute ds1682_eeprom_attr = { .mode = S_IRUGO | S_IWUSR, }, .size = DS1682_EEPROM_SIZE, - .read = ds1682_eeprom_read, - .write = ds1682_eeprom_write, + .read_new = ds1682_eeprom_read, + .write_new = ds1682_eeprom_write, }; static int ds1682_nvmem_read(void *priv, unsigned int offset, void *val, diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 43421fe37d33..1fc632ebf22f 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -847,7 +847,7 @@ err_mutex_unlock: * @count: Number of bytes to write */ static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct idt_89hpesx_dev *pdev; @@ -871,7 +871,7 @@ static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, * @count: Number of bytes to write */ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct idt_89hpesx_dev *pdev; @@ -1017,7 +1017,7 @@ static ssize_t idt_dbgfs_csr_read(struct file *filep, char __user *ubuf, * NOTE Size will be changed in compliance with OF node. EEPROM attribute will * be read-only as well if the corresponding flag is specified in OF node. */ -static BIN_ATTR_RW(eeprom, EEPROM_DEF_SIZE); +static const BIN_ATTR_RW(eeprom, EEPROM_DEF_SIZE); /* * csr_dbgfs_ops - CSR debugfs-node read/write operations diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index 6fab2ffa736b..1c36ad153e78 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -104,7 +104,7 @@ exit_up: } static ssize_t max6875_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct i2c_client *client = kobj_to_i2c_client(kobj); @@ -127,7 +127,7 @@ static const struct bin_attribute user_eeprom_attr = { .mode = S_IRUGO, }, .size = USER_EEPROM_SIZE, - .read = max6875_read, + .read_new = max6875_read, }; static int max6875_probe(struct i2c_client *client) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 48d08eeb2d20..7b7a22c91fe4 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -139,14 +139,14 @@ struct fastrpc_mmap_rsp_msg { }; struct fastrpc_mmap_req_msg { - s32 pgid; + s32 client_id; u32 flags; u64 vaddr; s32 num; }; struct fastrpc_mem_map_req_msg { - s32 pgid; + s32 client_id; s32 fd; s32 offset; u32 flags; @@ -156,20 +156,20 @@ struct fastrpc_mem_map_req_msg { }; struct fastrpc_munmap_req_msg { - s32 pgid; + s32 client_id; u64 vaddr; u64 size; }; struct fastrpc_mem_unmap_req_msg { - s32 pgid; + s32 client_id; s32 fd; u64 vaddrin; u64 len; }; struct fastrpc_msg { - int pid; /* process group id */ + int client_id; /* process client id */ int tid; /* thread id */ u64 ctx; /* invoke caller context */ u32 handle; /* handle to invoke */ @@ -234,7 +234,7 @@ struct fastrpc_invoke_ctx { int nbufs; int retval; int pid; - int tgid; + int client_id; u32 sc; u32 *crc; u64 ctxid; @@ -299,7 +299,7 @@ struct fastrpc_user { struct fastrpc_session_ctx *sctx; struct fastrpc_buf *init_mem; - int tgid; + int client_id; int pd; bool is_secure_dev; /* Lock for lists */ @@ -614,7 +614,7 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc( ctx->sc = sc; ctx->retval = -1; ctx->pid = current->pid; - ctx->tgid = user->tgid; + ctx->client_id = user->client_id; ctx->cctx = cctx; init_completion(&ctx->work); INIT_WORK(&ctx->put_work, fastrpc_context_put_wq); @@ -992,7 +992,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) mmap_read_lock(current->mm); vma = find_vma(current->mm, ctx->args[i].ptr); if (vma) - pages[i].addr += ctx->args[i].ptr - + pages[i].addr += (ctx->args[i].ptr & PAGE_MASK) - vma->vm_start; mmap_read_unlock(current->mm); @@ -1019,8 +1019,8 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) (pkt_size - rlen); pages[i].addr = pages[i].addr & PAGE_MASK; - pg_start = (args & PAGE_MASK) >> PAGE_SHIFT; - pg_end = ((args + len - 1) & PAGE_MASK) >> PAGE_SHIFT; + pg_start = (rpra[i].buf.pv & PAGE_MASK) >> PAGE_SHIFT; + pg_end = ((rpra[i].buf.pv + len - 1) & PAGE_MASK) >> PAGE_SHIFT; pages[i].size = (pg_end - pg_start + 1) * PAGE_SIZE; args = args + mlen; rlen -= mlen; @@ -1115,11 +1115,11 @@ static int fastrpc_invoke_send(struct fastrpc_session_ctx *sctx, int ret; cctx = fl->cctx; - msg->pid = fl->tgid; + msg->client_id = fl->client_id; msg->tid = current->pid; if (kernel) - msg->pid = 0; + msg->client_id = 0; msg->ctx = ctx->ctxid | fl->pd; msg->handle = handle; @@ -1244,7 +1244,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, int err; bool scm_done = false; struct { - int pgid; + int client_id; u32 namelen; u32 pageslen; } inbuf; @@ -1293,7 +1293,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, } } - inbuf.pgid = fl->tgid; + inbuf.client_id = fl->client_id; inbuf.namelen = init.namelen; inbuf.pageslen = 0; fl->pd = USER_PD; @@ -1363,7 +1363,7 @@ static int fastrpc_init_create_process(struct fastrpc_user *fl, int memlen; int err; struct { - int pgid; + int client_id; u32 namelen; u32 filelen; u32 pageslen; @@ -1395,7 +1395,7 @@ static int fastrpc_init_create_process(struct fastrpc_user *fl, goto err; } - inbuf.pgid = fl->tgid; + inbuf.client_id = fl->client_id; inbuf.namelen = strlen(current->comm) + 1; inbuf.filelen = init.filelen; inbuf.pageslen = 1; @@ -1469,8 +1469,9 @@ err: } static struct fastrpc_session_ctx *fastrpc_session_alloc( - struct fastrpc_channel_ctx *cctx) + struct fastrpc_user *fl) { + struct fastrpc_channel_ctx *cctx = fl->cctx; struct fastrpc_session_ctx *session = NULL; unsigned long flags; int i; @@ -1480,6 +1481,8 @@ static struct fastrpc_session_ctx *fastrpc_session_alloc( if (!cctx->session[i].used && cctx->session[i].valid) { cctx->session[i].used = true; session = &cctx->session[i]; + /* any non-zero ID will work, session_idx + 1 is the simplest one */ + fl->client_id = i + 1; break; } } @@ -1501,12 +1504,12 @@ static void fastrpc_session_free(struct fastrpc_channel_ctx *cctx, static int fastrpc_release_current_dsp_process(struct fastrpc_user *fl) { struct fastrpc_invoke_args args[1]; - int tgid = 0; + int client_id = 0; u32 sc; - tgid = fl->tgid; - args[0].ptr = (u64)(uintptr_t) &tgid; - args[0].length = sizeof(tgid); + client_id = fl->client_id; + args[0].ptr = (u64)(uintptr_t) &client_id; + args[0].length = sizeof(client_id); args[0].fd = -1; sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_RELEASE, 1, 0); @@ -1579,11 +1582,10 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) INIT_LIST_HEAD(&fl->maps); INIT_LIST_HEAD(&fl->mmaps); INIT_LIST_HEAD(&fl->user); - fl->tgid = current->tgid; fl->cctx = cctx; fl->is_secure_dev = fdevice->secure; - fl->sctx = fastrpc_session_alloc(cctx); + fl->sctx = fastrpc_session_alloc(fl); if (!fl->sctx) { dev_err(&cctx->rpdev->dev, "No session available\n"); mutex_destroy(&fl->mutex); @@ -1647,11 +1649,11 @@ static int fastrpc_dmabuf_alloc(struct fastrpc_user *fl, char __user *argp) static int fastrpc_init_attach(struct fastrpc_user *fl, int pd) { struct fastrpc_invoke_args args[1]; - int tgid = fl->tgid; + int client_id = fl->client_id; u32 sc; - args[0].ptr = (u64)(uintptr_t) &tgid; - args[0].length = sizeof(tgid); + args[0].ptr = (u64)(uintptr_t) &client_id; + args[0].length = sizeof(client_id); args[0].fd = -1; sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_ATTACH, 1, 0); fl->pd = pd; @@ -1803,7 +1805,7 @@ static int fastrpc_req_munmap_impl(struct fastrpc_user *fl, struct fastrpc_buf * int err; u32 sc; - req_msg.pgid = fl->tgid; + req_msg.client_id = fl->client_id; req_msg.size = buf->size; req_msg.vaddr = buf->raddr; @@ -1889,7 +1891,7 @@ static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp) return err; } - req_msg.pgid = fl->tgid; + req_msg.client_id = fl->client_id; req_msg.flags = req.flags; req_msg.vaddr = req.vaddrin; req_msg.num = sizeof(pages); @@ -1978,7 +1980,7 @@ static int fastrpc_req_mem_unmap_impl(struct fastrpc_user *fl, struct fastrpc_me return -EINVAL; } - req_msg.pgid = fl->tgid; + req_msg.client_id = fl->client_id; req_msg.len = map->len; req_msg.vaddrin = map->raddr; req_msg.fd = map->fd; @@ -2031,7 +2033,7 @@ static int fastrpc_req_mem_map(struct fastrpc_user *fl, char __user *argp) return err; } - req_msg.pgid = fl->tgid; + req_msg.client_id = fl->client_id; req_msg.fd = req.fd; req_msg.offset = req.offset; req_msg.vaddrin = req.vaddrin; @@ -2344,7 +2346,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) err = fastrpc_device_register(rdev, data, false, domains[domain_id]); if (err) - goto fdev_error; + goto populate_error; break; default: err = -EINVAL; diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c index 255d3022dae8..d0c6113dcff3 100644 --- a/drivers/misc/keba/cp500.c +++ b/drivers/misc/keba/cp500.c @@ -126,8 +126,9 @@ static struct cp500_devs cp520_devices = { }; struct cp500_nvmem { - struct nvmem_device *nvmem; + struct nvmem_device *base_nvmem; unsigned int offset; + struct nvmem_device *nvmem; }; struct cp500 { @@ -581,8 +582,8 @@ static int cp500_nvmem_read(void *priv, unsigned int offset, void *val, struct cp500_nvmem *nvmem = priv; int ret; - ret = nvmem_device_read(nvmem->nvmem, nvmem->offset + offset, bytes, - val); + ret = nvmem_device_read(nvmem->base_nvmem, nvmem->offset + offset, + bytes, val); if (ret != bytes) return ret; @@ -595,15 +596,16 @@ static int cp500_nvmem_write(void *priv, unsigned int offset, void *val, struct cp500_nvmem *nvmem = priv; int ret; - ret = nvmem_device_write(nvmem->nvmem, nvmem->offset + offset, bytes, - val); + ret = nvmem_device_write(nvmem->base_nvmem, nvmem->offset + offset, + bytes, val); if (ret != bytes) return ret; return 0; } -static int cp500_nvmem_register(struct cp500 *cp500, struct nvmem_device *nvmem) +static int cp500_nvmem_register(struct cp500 *cp500, + struct nvmem_device *base_nvmem) { struct device *dev = &cp500->pci_dev->dev; struct nvmem_config nvmem_config = {}; @@ -625,27 +627,52 @@ static int cp500_nvmem_register(struct cp500 *cp500, struct nvmem_device *nvmem) nvmem_config.reg_read = cp500_nvmem_read; nvmem_config.reg_write = cp500_nvmem_write; - cp500->nvmem_cpu.nvmem = nvmem; + cp500->nvmem_cpu.base_nvmem = base_nvmem; cp500->nvmem_cpu.offset = CP500_EEPROM_CPU_OFFSET; nvmem_config.name = CP500_EEPROM_CPU_NAME; nvmem_config.size = CP500_EEPROM_CPU_SIZE; nvmem_config.priv = &cp500->nvmem_cpu; - tmp = devm_nvmem_register(dev, &nvmem_config); + tmp = nvmem_register(&nvmem_config); if (IS_ERR(tmp)) return PTR_ERR(tmp); + cp500->nvmem_cpu.nvmem = tmp; - cp500->nvmem_user.nvmem = nvmem; + cp500->nvmem_user.base_nvmem = base_nvmem; cp500->nvmem_user.offset = CP500_EEPROM_USER_OFFSET; nvmem_config.name = CP500_EEPROM_USER_NAME; nvmem_config.size = CP500_EEPROM_USER_SIZE; nvmem_config.priv = &cp500->nvmem_user; - tmp = devm_nvmem_register(dev, &nvmem_config); - if (IS_ERR(tmp)) + tmp = nvmem_register(&nvmem_config); + if (IS_ERR(tmp)) { + nvmem_unregister(cp500->nvmem_cpu.nvmem); + cp500->nvmem_cpu.nvmem = NULL; + return PTR_ERR(tmp); + } + cp500->nvmem_user.nvmem = tmp; return 0; } +static void cp500_nvmem_unregister(struct cp500 *cp500) +{ + int notified; + + if (cp500->nvmem_user.nvmem) { + nvmem_unregister(cp500->nvmem_user.nvmem); + cp500->nvmem_user.nvmem = NULL; + } + if (cp500->nvmem_cpu.nvmem) { + nvmem_unregister(cp500->nvmem_cpu.nvmem); + cp500->nvmem_cpu.nvmem = NULL; + } + + /* CPU and user nvmem use the same base_nvmem, put only once */ + notified = atomic_read(&cp500->nvmem_notified); + if (notified) + nvmem_device_put(cp500->nvmem_cpu.base_nvmem); +} + static int cp500_nvmem_match(struct device *dev, const void *data) { const struct cp500 *cp500 = data; @@ -663,13 +690,6 @@ static int cp500_nvmem_match(struct device *dev, const void *data) return 0; } -static void cp500_devm_nvmem_put(void *data) -{ - struct nvmem_device *nvmem = data; - - nvmem_device_put(nvmem); -} - static int cp500_nvmem(struct notifier_block *nb, unsigned long action, void *data) { @@ -698,10 +718,6 @@ static int cp500_nvmem(struct notifier_block *nb, unsigned long action, return NOTIFY_DONE; } - ret = devm_add_action_or_reset(dev, cp500_devm_nvmem_put, nvmem); - if (ret) - return ret; - ret = cp500_nvmem_register(cp500, nvmem); if (ret) return ret; @@ -932,12 +948,17 @@ static void cp500_remove(struct pci_dev *pci_dev) { struct cp500 *cp500 = pci_get_drvdata(pci_dev); + /* + * unregister CPU and user nvmem and put base_nvmem before parent + * auxiliary device of base_nvmem is unregistered + */ + nvmem_unregister_notifier(&cp500->nvmem_notifier); + cp500_nvmem_unregister(cp500); + cp500_unregister_auxiliary_devs(cp500); cp500_disable(cp500); - nvmem_unregister_notifier(&cp500->nvmem_notifier); - pci_set_drvdata(pci_dev, 0); pci_free_irq_vectors(pci_dev); diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c index 3c1359d8d4e6..04756302b878 100644 --- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c +++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c @@ -147,6 +147,9 @@ static int pci1xxxx_gpio_set_config(struct gpio_chip *gpio, unsigned int offset, case PIN_CONFIG_DRIVE_OPEN_DRAIN: pci1xxx_assign_bit(priv->reg_base, OPENDRAIN_OFFSET(offset), (offset % 32), true); break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + pci1xxx_assign_bit(priv->reg_base, OPENDRAIN_OFFSET(offset), (offset % 32), false); + break; default: ret = -ENOTSUPP; break; diff --git a/drivers/misc/misc_minor_kunit.c b/drivers/misc/misc_minor_kunit.c new file mode 100644 index 000000000000..293e0fb7e43e --- /dev/null +++ b/drivers/misc/misc_minor_kunit.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <kunit/test.h> +#include <kunit/test-bug.h> +#include <linux/module.h> +#include <linux/miscdevice.h> + +/* dynamic minor (2) */ +static struct miscdevice dev_dynamic_minor = { + .minor = 2, + .name = "dev_dynamic_minor", +}; + +/* static minor (LCD_MINOR) */ +static struct miscdevice dev_static_minor = { + .minor = LCD_MINOR, + .name = "dev_static_minor", +}; + +/* misc dynamic minor */ +static struct miscdevice dev_misc_dynamic_minor = { + .minor = MISC_DYNAMIC_MINOR, + .name = "dev_misc_dynamic_minor", +}; + +static void kunit_dynamic_minor(struct kunit *test) +{ + int ret; + + ret = misc_register(&dev_dynamic_minor); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 2, dev_dynamic_minor.minor); + misc_deregister(&dev_dynamic_minor); +} + +static void kunit_static_minor(struct kunit *test) +{ + int ret; + + ret = misc_register(&dev_static_minor); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, LCD_MINOR, dev_static_minor.minor); + misc_deregister(&dev_static_minor); +} + +static void kunit_misc_dynamic_minor(struct kunit *test) +{ + int ret; + + ret = misc_register(&dev_misc_dynamic_minor); + KUNIT_EXPECT_EQ(test, 0, ret); + misc_deregister(&dev_misc_dynamic_minor); +} + +static struct kunit_case test_cases[] = { + KUNIT_CASE(kunit_dynamic_minor), + KUNIT_CASE(kunit_static_minor), + KUNIT_CASE(kunit_misc_dynamic_minor), + {} +}; + +static struct kunit_suite test_suite = { + .name = "misc_minor_test", + .test_cases = test_cases, +}; +kunit_test_suite(test_suite); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vimal Agrawal"); +MODULE_DESCRIPTION("misc minor testing"); diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c index 4954553b7baa..055395cde42b 100644 --- a/drivers/misc/ntsync.c +++ b/drivers/misc/ntsync.c @@ -6,11 +6,17 @@ */ #include <linux/anon_inodes.h> +#include <linux/atomic.h> #include <linux/file.h> #include <linux/fs.h> +#include <linux/hrtimer.h> +#include <linux/ktime.h> #include <linux/miscdevice.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/overflow.h> +#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <uapi/linux/ntsync.h> @@ -19,6 +25,8 @@ enum ntsync_type { NTSYNC_TYPE_SEM, + NTSYNC_TYPE_MUTEX, + NTSYNC_TYPE_EVENT, }; /* @@ -30,10 +38,13 @@ enum ntsync_type { * * Both rely on struct file for reference counting. Individual * ntsync_obj objects take a reference to the device when created. + * Wait operations take a reference to each object being waited on for + * the duration of the wait. */ struct ntsync_obj { spinlock_t lock; + int dev_locked; enum ntsync_type type; @@ -46,22 +57,344 @@ struct ntsync_obj { __u32 count; __u32 max; } sem; + struct { + __u32 count; + pid_t owner; + bool ownerdead; + } mutex; + struct { + bool manual; + bool signaled; + } event; } u; + + /* + * any_waiters is protected by the object lock, but all_waiters is + * protected by the device wait_all_lock. + */ + struct list_head any_waiters; + struct list_head all_waiters; + + /* + * Hint describing how many tasks are queued on this object in a + * wait-all operation. + * + * Any time we do a wake, we may need to wake "all" waiters as well as + * "any" waiters. In order to atomically wake "all" waiters, we must + * lock all of the objects, and that means grabbing the wait_all_lock + * below (and, due to lock ordering rules, before locking this object). + * However, wait-all is a rare operation, and grabbing the wait-all + * lock for every wake would create unnecessary contention. + * Therefore we first check whether all_hint is zero, and, if it is, + * we skip trying to wake "all" waiters. + * + * Since wait requests must originate from user-space threads, we're + * limited here by PID_MAX_LIMIT, so there's no risk of overflow. + */ + atomic_t all_hint; +}; + +struct ntsync_q_entry { + struct list_head node; + struct ntsync_q *q; + struct ntsync_obj *obj; + __u32 index; +}; + +struct ntsync_q { + struct task_struct *task; + __u32 owner; + + /* + * Protected via atomic_try_cmpxchg(). Only the thread that wins the + * compare-and-swap may actually change object states and wake this + * task. + */ + atomic_t signaled; + + bool all; + bool ownerdead; + __u32 count; + struct ntsync_q_entry entries[]; }; struct ntsync_device { + /* + * Wait-all operations must atomically grab all objects, and be totally + * ordered with respect to each other and wait-any operations. + * If one thread is trying to acquire several objects, another thread + * cannot touch the object at the same time. + * + * This device-wide lock is used to serialize wait-for-all + * operations, and operations on an object that is involved in a + * wait-for-all. + */ + struct mutex wait_all_lock; + struct file *file; }; /* + * Single objects are locked using obj->lock. + * + * Multiple objects are 'locked' while holding dev->wait_all_lock. + * In this case however, individual objects are not locked by holding + * obj->lock, but by setting obj->dev_locked. + * + * This means that in order to lock a single object, the sequence is slightly + * more complicated than usual. Specifically it needs to check obj->dev_locked + * after acquiring obj->lock, if set, it needs to drop the lock and acquire + * dev->wait_all_lock in order to serialize against the multi-object operation. + */ + +static void dev_lock_obj(struct ntsync_device *dev, struct ntsync_obj *obj) +{ + lockdep_assert_held(&dev->wait_all_lock); + lockdep_assert(obj->dev == dev); + spin_lock(&obj->lock); + /* + * By setting obj->dev_locked inside obj->lock, it is ensured that + * anyone holding obj->lock must see the value. + */ + obj->dev_locked = 1; + spin_unlock(&obj->lock); +} + +static void dev_unlock_obj(struct ntsync_device *dev, struct ntsync_obj *obj) +{ + lockdep_assert_held(&dev->wait_all_lock); + lockdep_assert(obj->dev == dev); + spin_lock(&obj->lock); + obj->dev_locked = 0; + spin_unlock(&obj->lock); +} + +static void obj_lock(struct ntsync_obj *obj) +{ + struct ntsync_device *dev = obj->dev; + + for (;;) { + spin_lock(&obj->lock); + if (likely(!obj->dev_locked)) + break; + + spin_unlock(&obj->lock); + mutex_lock(&dev->wait_all_lock); + spin_lock(&obj->lock); + /* + * obj->dev_locked should be set and released under the same + * wait_all_lock section, since we now own this lock, it should + * be clear. + */ + lockdep_assert(!obj->dev_locked); + spin_unlock(&obj->lock); + mutex_unlock(&dev->wait_all_lock); + } +} + +static void obj_unlock(struct ntsync_obj *obj) +{ + spin_unlock(&obj->lock); +} + +static bool ntsync_lock_obj(struct ntsync_device *dev, struct ntsync_obj *obj) +{ + bool all; + + obj_lock(obj); + all = atomic_read(&obj->all_hint); + if (unlikely(all)) { + obj_unlock(obj); + mutex_lock(&dev->wait_all_lock); + dev_lock_obj(dev, obj); + } + + return all; +} + +static void ntsync_unlock_obj(struct ntsync_device *dev, struct ntsync_obj *obj, bool all) +{ + if (all) { + dev_unlock_obj(dev, obj); + mutex_unlock(&dev->wait_all_lock); + } else { + obj_unlock(obj); + } +} + +#define ntsync_assert_held(obj) \ + lockdep_assert((lockdep_is_held(&(obj)->lock) != LOCK_STATE_NOT_HELD) || \ + ((lockdep_is_held(&(obj)->dev->wait_all_lock) != LOCK_STATE_NOT_HELD) && \ + (obj)->dev_locked)) + +static bool is_signaled(struct ntsync_obj *obj, __u32 owner) +{ + ntsync_assert_held(obj); + + switch (obj->type) { + case NTSYNC_TYPE_SEM: + return !!obj->u.sem.count; + case NTSYNC_TYPE_MUTEX: + if (obj->u.mutex.owner && obj->u.mutex.owner != owner) + return false; + return obj->u.mutex.count < UINT_MAX; + case NTSYNC_TYPE_EVENT: + return obj->u.event.signaled; + } + + WARN(1, "bad object type %#x\n", obj->type); + return false; +} + +/* + * "locked_obj" is an optional pointer to an object which is already locked and + * should not be locked again. This is necessary so that changing an object's + * state and waking it can be a single atomic operation. + */ +static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q, + struct ntsync_obj *locked_obj) +{ + __u32 count = q->count; + bool can_wake = true; + int signaled = -1; + __u32 i; + + lockdep_assert_held(&dev->wait_all_lock); + if (locked_obj) + lockdep_assert(locked_obj->dev_locked); + + for (i = 0; i < count; i++) { + if (q->entries[i].obj != locked_obj) + dev_lock_obj(dev, q->entries[i].obj); + } + + for (i = 0; i < count; i++) { + if (!is_signaled(q->entries[i].obj, q->owner)) { + can_wake = false; + break; + } + } + + if (can_wake && atomic_try_cmpxchg(&q->signaled, &signaled, 0)) { + for (i = 0; i < count; i++) { + struct ntsync_obj *obj = q->entries[i].obj; + + switch (obj->type) { + case NTSYNC_TYPE_SEM: + obj->u.sem.count--; + break; + case NTSYNC_TYPE_MUTEX: + if (obj->u.mutex.ownerdead) + q->ownerdead = true; + obj->u.mutex.ownerdead = false; + obj->u.mutex.count++; + obj->u.mutex.owner = q->owner; + break; + case NTSYNC_TYPE_EVENT: + if (!obj->u.event.manual) + obj->u.event.signaled = false; + break; + } + } + wake_up_process(q->task); + } + + for (i = 0; i < count; i++) { + if (q->entries[i].obj != locked_obj) + dev_unlock_obj(dev, q->entries[i].obj); + } +} + +static void try_wake_all_obj(struct ntsync_device *dev, struct ntsync_obj *obj) +{ + struct ntsync_q_entry *entry; + + lockdep_assert_held(&dev->wait_all_lock); + lockdep_assert(obj->dev_locked); + + list_for_each_entry(entry, &obj->all_waiters, node) + try_wake_all(dev, entry->q, obj); +} + +static void try_wake_any_sem(struct ntsync_obj *sem) +{ + struct ntsync_q_entry *entry; + + ntsync_assert_held(sem); + lockdep_assert(sem->type == NTSYNC_TYPE_SEM); + + list_for_each_entry(entry, &sem->any_waiters, node) { + struct ntsync_q *q = entry->q; + int signaled = -1; + + if (!sem->u.sem.count) + break; + + if (atomic_try_cmpxchg(&q->signaled, &signaled, entry->index)) { + sem->u.sem.count--; + wake_up_process(q->task); + } + } +} + +static void try_wake_any_mutex(struct ntsync_obj *mutex) +{ + struct ntsync_q_entry *entry; + + ntsync_assert_held(mutex); + lockdep_assert(mutex->type == NTSYNC_TYPE_MUTEX); + + list_for_each_entry(entry, &mutex->any_waiters, node) { + struct ntsync_q *q = entry->q; + int signaled = -1; + + if (mutex->u.mutex.count == UINT_MAX) + break; + if (mutex->u.mutex.owner && mutex->u.mutex.owner != q->owner) + continue; + + if (atomic_try_cmpxchg(&q->signaled, &signaled, entry->index)) { + if (mutex->u.mutex.ownerdead) + q->ownerdead = true; + mutex->u.mutex.ownerdead = false; + mutex->u.mutex.count++; + mutex->u.mutex.owner = q->owner; + wake_up_process(q->task); + } + } +} + +static void try_wake_any_event(struct ntsync_obj *event) +{ + struct ntsync_q_entry *entry; + + ntsync_assert_held(event); + lockdep_assert(event->type == NTSYNC_TYPE_EVENT); + + list_for_each_entry(entry, &event->any_waiters, node) { + struct ntsync_q *q = entry->q; + int signaled = -1; + + if (!event->u.event.signaled) + break; + + if (atomic_try_cmpxchg(&q->signaled, &signaled, entry->index)) { + if (!event->u.event.manual) + event->u.event.signaled = false; + wake_up_process(q->task); + } + } +} + +/* * Actually change the semaphore state, returning -EOVERFLOW if it is made * invalid. */ -static int post_sem_state(struct ntsync_obj *sem, __u32 count) +static int release_sem_state(struct ntsync_obj *sem, __u32 count) { __u32 sum; - lockdep_assert_held(&sem->lock); + ntsync_assert_held(sem); if (check_add_overflow(sem->u.sem.count, count, &sum) || sum > sem->u.sem.max) @@ -71,11 +404,13 @@ static int post_sem_state(struct ntsync_obj *sem, __u32 count) return 0; } -static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp) +static int ntsync_sem_release(struct ntsync_obj *sem, void __user *argp) { + struct ntsync_device *dev = sem->dev; __u32 __user *user_args = argp; __u32 prev_count; __u32 args; + bool all; int ret; if (copy_from_user(&args, argp, sizeof(args))) @@ -84,12 +419,17 @@ static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp) if (sem->type != NTSYNC_TYPE_SEM) return -EINVAL; - spin_lock(&sem->lock); + all = ntsync_lock_obj(dev, sem); prev_count = sem->u.sem.count; - ret = post_sem_state(sem, args); + ret = release_sem_state(sem, args); + if (!ret) { + if (all) + try_wake_all_obj(dev, sem); + try_wake_any_sem(sem); + } - spin_unlock(&sem->lock); + ntsync_unlock_obj(dev, sem, all); if (!ret && put_user(prev_count, user_args)) ret = -EFAULT; @@ -97,13 +437,229 @@ static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp) return ret; } -static int ntsync_obj_release(struct inode *inode, struct file *file) +/* + * Actually change the mutex state, returning -EPERM if not the owner. + */ +static int unlock_mutex_state(struct ntsync_obj *mutex, + const struct ntsync_mutex_args *args) { - struct ntsync_obj *obj = file->private_data; + ntsync_assert_held(mutex); + + if (mutex->u.mutex.owner != args->owner) + return -EPERM; + + if (!--mutex->u.mutex.count) + mutex->u.mutex.owner = 0; + return 0; +} + +static int ntsync_mutex_unlock(struct ntsync_obj *mutex, void __user *argp) +{ + struct ntsync_mutex_args __user *user_args = argp; + struct ntsync_device *dev = mutex->dev; + struct ntsync_mutex_args args; + __u32 prev_count; + bool all; + int ret; + + if (copy_from_user(&args, argp, sizeof(args))) + return -EFAULT; + if (!args.owner) + return -EINVAL; + + if (mutex->type != NTSYNC_TYPE_MUTEX) + return -EINVAL; + + all = ntsync_lock_obj(dev, mutex); + + prev_count = mutex->u.mutex.count; + ret = unlock_mutex_state(mutex, &args); + if (!ret) { + if (all) + try_wake_all_obj(dev, mutex); + try_wake_any_mutex(mutex); + } + + ntsync_unlock_obj(dev, mutex, all); + + if (!ret && put_user(prev_count, &user_args->count)) + ret = -EFAULT; + + return ret; +} + +/* + * Actually change the mutex state to mark its owner as dead, + * returning -EPERM if not the owner. + */ +static int kill_mutex_state(struct ntsync_obj *mutex, __u32 owner) +{ + ntsync_assert_held(mutex); + + if (mutex->u.mutex.owner != owner) + return -EPERM; + + mutex->u.mutex.ownerdead = true; + mutex->u.mutex.owner = 0; + mutex->u.mutex.count = 0; + return 0; +} + +static int ntsync_mutex_kill(struct ntsync_obj *mutex, void __user *argp) +{ + struct ntsync_device *dev = mutex->dev; + __u32 owner; + bool all; + int ret; + + if (get_user(owner, (__u32 __user *)argp)) + return -EFAULT; + if (!owner) + return -EINVAL; + + if (mutex->type != NTSYNC_TYPE_MUTEX) + return -EINVAL; + + all = ntsync_lock_obj(dev, mutex); + + ret = kill_mutex_state(mutex, owner); + if (!ret) { + if (all) + try_wake_all_obj(dev, mutex); + try_wake_any_mutex(mutex); + } + + ntsync_unlock_obj(dev, mutex, all); + + return ret; +} + +static int ntsync_event_set(struct ntsync_obj *event, void __user *argp, bool pulse) +{ + struct ntsync_device *dev = event->dev; + __u32 prev_state; + bool all; + + if (event->type != NTSYNC_TYPE_EVENT) + return -EINVAL; + + all = ntsync_lock_obj(dev, event); + + prev_state = event->u.event.signaled; + event->u.event.signaled = true; + if (all) + try_wake_all_obj(dev, event); + try_wake_any_event(event); + if (pulse) + event->u.event.signaled = false; + + ntsync_unlock_obj(dev, event, all); + + if (put_user(prev_state, (__u32 __user *)argp)) + return -EFAULT; + + return 0; +} + +static int ntsync_event_reset(struct ntsync_obj *event, void __user *argp) +{ + struct ntsync_device *dev = event->dev; + __u32 prev_state; + bool all; + + if (event->type != NTSYNC_TYPE_EVENT) + return -EINVAL; + + all = ntsync_lock_obj(dev, event); + + prev_state = event->u.event.signaled; + event->u.event.signaled = false; + + ntsync_unlock_obj(dev, event, all); + + if (put_user(prev_state, (__u32 __user *)argp)) + return -EFAULT; + + return 0; +} + +static int ntsync_sem_read(struct ntsync_obj *sem, void __user *argp) +{ + struct ntsync_sem_args __user *user_args = argp; + struct ntsync_device *dev = sem->dev; + struct ntsync_sem_args args; + bool all; + + if (sem->type != NTSYNC_TYPE_SEM) + return -EINVAL; + + all = ntsync_lock_obj(dev, sem); + + args.count = sem->u.sem.count; + args.max = sem->u.sem.max; + + ntsync_unlock_obj(dev, sem, all); + + if (copy_to_user(user_args, &args, sizeof(args))) + return -EFAULT; + return 0; +} +static int ntsync_mutex_read(struct ntsync_obj *mutex, void __user *argp) +{ + struct ntsync_mutex_args __user *user_args = argp; + struct ntsync_device *dev = mutex->dev; + struct ntsync_mutex_args args; + bool all; + int ret; + + if (mutex->type != NTSYNC_TYPE_MUTEX) + return -EINVAL; + + all = ntsync_lock_obj(dev, mutex); + + args.count = mutex->u.mutex.count; + args.owner = mutex->u.mutex.owner; + ret = mutex->u.mutex.ownerdead ? -EOWNERDEAD : 0; + + ntsync_unlock_obj(dev, mutex, all); + + if (copy_to_user(user_args, &args, sizeof(args))) + return -EFAULT; + return ret; +} + +static int ntsync_event_read(struct ntsync_obj *event, void __user *argp) +{ + struct ntsync_event_args __user *user_args = argp; + struct ntsync_device *dev = event->dev; + struct ntsync_event_args args; + bool all; + + if (event->type != NTSYNC_TYPE_EVENT) + return -EINVAL; + + all = ntsync_lock_obj(dev, event); + + args.manual = event->u.event.manual; + args.signaled = event->u.event.signaled; + + ntsync_unlock_obj(dev, event, all); + + if (copy_to_user(user_args, &args, sizeof(args))) + return -EFAULT; + return 0; +} + +static void ntsync_free_obj(struct ntsync_obj *obj) +{ fput(obj->dev->file); kfree(obj); +} +static int ntsync_obj_release(struct inode *inode, struct file *file) +{ + ntsync_free_obj(file->private_data); return 0; } @@ -114,8 +670,24 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd, void __user *argp = (void __user *)parm; switch (cmd) { - case NTSYNC_IOC_SEM_POST: - return ntsync_sem_post(obj, argp); + case NTSYNC_IOC_SEM_RELEASE: + return ntsync_sem_release(obj, argp); + case NTSYNC_IOC_SEM_READ: + return ntsync_sem_read(obj, argp); + case NTSYNC_IOC_MUTEX_UNLOCK: + return ntsync_mutex_unlock(obj, argp); + case NTSYNC_IOC_MUTEX_KILL: + return ntsync_mutex_kill(obj, argp); + case NTSYNC_IOC_MUTEX_READ: + return ntsync_mutex_read(obj, argp); + case NTSYNC_IOC_EVENT_SET: + return ntsync_event_set(obj, argp, false); + case NTSYNC_IOC_EVENT_RESET: + return ntsync_event_reset(obj, argp); + case NTSYNC_IOC_EVENT_PULSE: + return ntsync_event_set(obj, argp, true); + case NTSYNC_IOC_EVENT_READ: + return ntsync_event_read(obj, argp); default: return -ENOIOCTLCMD; } @@ -140,6 +712,9 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev, obj->dev = dev; get_file(dev->file); spin_lock_init(&obj->lock); + INIT_LIST_HEAD(&obj->any_waiters); + INIT_LIST_HEAD(&obj->all_waiters); + atomic_set(&obj->all_hint, 0); return obj; } @@ -165,7 +740,6 @@ static int ntsync_obj_get_fd(struct ntsync_obj *obj) static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp) { - struct ntsync_sem_args __user *user_args = argp; struct ntsync_sem_args args; struct ntsync_obj *sem; int fd; @@ -182,12 +756,398 @@ static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp) sem->u.sem.count = args.count; sem->u.sem.max = args.max; fd = ntsync_obj_get_fd(sem); - if (fd < 0) { - kfree(sem); - return fd; + if (fd < 0) + ntsync_free_obj(sem); + + return fd; +} + +static int ntsync_create_mutex(struct ntsync_device *dev, void __user *argp) +{ + struct ntsync_mutex_args args; + struct ntsync_obj *mutex; + int fd; + + if (copy_from_user(&args, argp, sizeof(args))) + return -EFAULT; + + if (!args.owner != !args.count) + return -EINVAL; + + mutex = ntsync_alloc_obj(dev, NTSYNC_TYPE_MUTEX); + if (!mutex) + return -ENOMEM; + mutex->u.mutex.count = args.count; + mutex->u.mutex.owner = args.owner; + fd = ntsync_obj_get_fd(mutex); + if (fd < 0) + ntsync_free_obj(mutex); + + return fd; +} + +static int ntsync_create_event(struct ntsync_device *dev, void __user *argp) +{ + struct ntsync_event_args args; + struct ntsync_obj *event; + int fd; + + if (copy_from_user(&args, argp, sizeof(args))) + return -EFAULT; + + event = ntsync_alloc_obj(dev, NTSYNC_TYPE_EVENT); + if (!event) + return -ENOMEM; + event->u.event.manual = args.manual; + event->u.event.signaled = args.signaled; + fd = ntsync_obj_get_fd(event); + if (fd < 0) + ntsync_free_obj(event); + + return fd; +} + +static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd) +{ + struct file *file = fget(fd); + struct ntsync_obj *obj; + + if (!file) + return NULL; + + if (file->f_op != &ntsync_obj_fops) { + fput(file); + return NULL; + } + + obj = file->private_data; + if (obj->dev != dev) { + fput(file); + return NULL; } - return put_user(fd, &user_args->sem); + return obj; +} + +static void put_obj(struct ntsync_obj *obj) +{ + fput(obj->file); +} + +static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_args *args) +{ + ktime_t timeout = ns_to_ktime(args->timeout); + clockid_t clock = CLOCK_MONOTONIC; + ktime_t *timeout_ptr; + int ret = 0; + + timeout_ptr = (args->timeout == U64_MAX ? NULL : &timeout); + + if (args->flags & NTSYNC_WAIT_REALTIME) + clock = CLOCK_REALTIME; + + do { + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + set_current_state(TASK_INTERRUPTIBLE); + if (atomic_read(&q->signaled) != -1) { + ret = 0; + break; + } + ret = schedule_hrtimeout_range_clock(timeout_ptr, 0, HRTIMER_MODE_ABS, clock); + } while (ret < 0); + __set_current_state(TASK_RUNNING); + + return ret; +} + +/* + * Allocate and initialize the ntsync_q structure, but do not queue us yet. + */ +static int setup_wait(struct ntsync_device *dev, + const struct ntsync_wait_args *args, bool all, + struct ntsync_q **ret_q) +{ + int fds[NTSYNC_MAX_WAIT_COUNT + 1]; + const __u32 count = args->count; + struct ntsync_q *q; + __u32 total_count; + __u32 i, j; + + if (args->pad || (args->flags & ~NTSYNC_WAIT_REALTIME)) + return -EINVAL; + + if (args->count > NTSYNC_MAX_WAIT_COUNT) + return -EINVAL; + + total_count = count; + if (args->alert) + total_count++; + + if (copy_from_user(fds, u64_to_user_ptr(args->objs), + array_size(count, sizeof(*fds)))) + return -EFAULT; + if (args->alert) + fds[count] = args->alert; + + q = kmalloc(struct_size(q, entries, total_count), GFP_KERNEL); + if (!q) + return -ENOMEM; + q->task = current; + q->owner = args->owner; + atomic_set(&q->signaled, -1); + q->all = all; + q->ownerdead = false; + q->count = count; + + for (i = 0; i < total_count; i++) { + struct ntsync_q_entry *entry = &q->entries[i]; + struct ntsync_obj *obj = get_obj(dev, fds[i]); + + if (!obj) + goto err; + + if (all) { + /* Check that the objects are all distinct. */ + for (j = 0; j < i; j++) { + if (obj == q->entries[j].obj) { + put_obj(obj); + goto err; + } + } + } + + entry->obj = obj; + entry->q = q; + entry->index = i; + } + + *ret_q = q; + return 0; + +err: + for (j = 0; j < i; j++) + put_obj(q->entries[j].obj); + kfree(q); + return -EINVAL; +} + +static void try_wake_any_obj(struct ntsync_obj *obj) +{ + switch (obj->type) { + case NTSYNC_TYPE_SEM: + try_wake_any_sem(obj); + break; + case NTSYNC_TYPE_MUTEX: + try_wake_any_mutex(obj); + break; + case NTSYNC_TYPE_EVENT: + try_wake_any_event(obj); + break; + } +} + +static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp) +{ + struct ntsync_wait_args args; + __u32 i, total_count; + struct ntsync_q *q; + int signaled; + bool all; + int ret; + + if (copy_from_user(&args, argp, sizeof(args))) + return -EFAULT; + + ret = setup_wait(dev, &args, false, &q); + if (ret < 0) + return ret; + + total_count = args.count; + if (args.alert) + total_count++; + + /* queue ourselves */ + + for (i = 0; i < total_count; i++) { + struct ntsync_q_entry *entry = &q->entries[i]; + struct ntsync_obj *obj = entry->obj; + + all = ntsync_lock_obj(dev, obj); + list_add_tail(&entry->node, &obj->any_waiters); + ntsync_unlock_obj(dev, obj, all); + } + + /* + * Check if we are already signaled. + * + * Note that the API requires that normal objects are checked before + * the alert event. Hence we queue the alert event last, and check + * objects in order. + */ + + for (i = 0; i < total_count; i++) { + struct ntsync_obj *obj = q->entries[i].obj; + + if (atomic_read(&q->signaled) != -1) + break; + + all = ntsync_lock_obj(dev, obj); + try_wake_any_obj(obj); + ntsync_unlock_obj(dev, obj, all); + } + + /* sleep */ + + ret = ntsync_schedule(q, &args); + + /* and finally, unqueue */ + + for (i = 0; i < total_count; i++) { + struct ntsync_q_entry *entry = &q->entries[i]; + struct ntsync_obj *obj = entry->obj; + + all = ntsync_lock_obj(dev, obj); + list_del(&entry->node); + ntsync_unlock_obj(dev, obj, all); + + put_obj(obj); + } + + signaled = atomic_read(&q->signaled); + if (signaled != -1) { + struct ntsync_wait_args __user *user_args = argp; + + /* even if we caught a signal, we need to communicate success */ + ret = q->ownerdead ? -EOWNERDEAD : 0; + + if (put_user(signaled, &user_args->index)) + ret = -EFAULT; + } else if (!ret) { + ret = -ETIMEDOUT; + } + + kfree(q); + return ret; +} + +static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp) +{ + struct ntsync_wait_args args; + struct ntsync_q *q; + int signaled; + __u32 i; + int ret; + + if (copy_from_user(&args, argp, sizeof(args))) + return -EFAULT; + + ret = setup_wait(dev, &args, true, &q); + if (ret < 0) + return ret; + + /* queue ourselves */ + + mutex_lock(&dev->wait_all_lock); + + for (i = 0; i < args.count; i++) { + struct ntsync_q_entry *entry = &q->entries[i]; + struct ntsync_obj *obj = entry->obj; + + atomic_inc(&obj->all_hint); + + /* + * obj->all_waiters is protected by dev->wait_all_lock rather + * than obj->lock, so there is no need to acquire obj->lock + * here. + */ + list_add_tail(&entry->node, &obj->all_waiters); + } + if (args.alert) { + struct ntsync_q_entry *entry = &q->entries[args.count]; + struct ntsync_obj *obj = entry->obj; + + dev_lock_obj(dev, obj); + list_add_tail(&entry->node, &obj->any_waiters); + dev_unlock_obj(dev, obj); + } + + /* check if we are already signaled */ + + try_wake_all(dev, q, NULL); + + mutex_unlock(&dev->wait_all_lock); + + /* + * Check if the alert event is signaled, making sure to do so only + * after checking if the other objects are signaled. + */ + + if (args.alert) { + struct ntsync_obj *obj = q->entries[args.count].obj; + + if (atomic_read(&q->signaled) == -1) { + bool all = ntsync_lock_obj(dev, obj); + try_wake_any_obj(obj); + ntsync_unlock_obj(dev, obj, all); + } + } + + /* sleep */ + + ret = ntsync_schedule(q, &args); + + /* and finally, unqueue */ + + mutex_lock(&dev->wait_all_lock); + + for (i = 0; i < args.count; i++) { + struct ntsync_q_entry *entry = &q->entries[i]; + struct ntsync_obj *obj = entry->obj; + + /* + * obj->all_waiters is protected by dev->wait_all_lock rather + * than obj->lock, so there is no need to acquire it here. + */ + list_del(&entry->node); + + atomic_dec(&obj->all_hint); + + put_obj(obj); + } + + mutex_unlock(&dev->wait_all_lock); + + if (args.alert) { + struct ntsync_q_entry *entry = &q->entries[args.count]; + struct ntsync_obj *obj = entry->obj; + bool all; + + all = ntsync_lock_obj(dev, obj); + list_del(&entry->node); + ntsync_unlock_obj(dev, obj, all); + + put_obj(obj); + } + + signaled = atomic_read(&q->signaled); + if (signaled != -1) { + struct ntsync_wait_args __user *user_args = argp; + + /* even if we caught a signal, we need to communicate success */ + ret = q->ownerdead ? -EOWNERDEAD : 0; + + if (put_user(signaled, &user_args->index)) + ret = -EFAULT; + } else if (!ret) { + ret = -ETIMEDOUT; + } + + kfree(q); + return ret; } static int ntsync_char_open(struct inode *inode, struct file *file) @@ -198,6 +1158,8 @@ static int ntsync_char_open(struct inode *inode, struct file *file) if (!dev) return -ENOMEM; + mutex_init(&dev->wait_all_lock); + file->private_data = dev; dev->file = file; return nonseekable_open(inode, file); @@ -219,8 +1181,16 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, void __user *argp = (void __user *)parm; switch (cmd) { + case NTSYNC_IOC_CREATE_EVENT: + return ntsync_create_event(dev, argp); + case NTSYNC_IOC_CREATE_MUTEX: + return ntsync_create_mutex(dev, argp); case NTSYNC_IOC_CREATE_SEM: return ntsync_create_sem(dev, argp); + case NTSYNC_IOC_WAIT_ALL: + return ntsync_wait_all(dev, argp); + case NTSYNC_IOC_WAIT_ANY: + return ntsync_wait_any(dev, argp); default: return -ENOIOCTLCMD; } diff --git a/drivers/misc/ocxl/sysfs.c b/drivers/misc/ocxl/sysfs.c index 07520d6e6dc5..e849641687a0 100644 --- a/drivers/misc/ocxl/sysfs.c +++ b/drivers/misc/ocxl/sysfs.c @@ -94,7 +94,7 @@ static struct device_attribute afu_attrs[] = { }; static ssize_t global_mmio_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct ocxl_afu *afu = to_afu(kobj_to_dev(kobj)); @@ -155,7 +155,7 @@ int ocxl_sysfs_register_afu(struct ocxl_file_info *info) info->attr_global_mmio.attr.name = "global_mmio_area"; info->attr_global_mmio.attr.mode = 0600; info->attr_global_mmio.size = info->afu->config.global_mmio_size; - info->attr_global_mmio.read = global_mmio_read; + info->attr_global_mmio.read_new = global_mmio_read; info->attr_global_mmio.mmap = global_mmio_mmap; rc = device_create_bin_file(&info->dev, &info->attr_global_mmio); if (rc) { diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 8d2b7135738e..6121c0940cd1 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -483,7 +483,7 @@ static int pch_phub_write_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data) } static ssize_t pch_phub_bin_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { unsigned int rom_signature; @@ -553,7 +553,7 @@ return_err_nomutex: } static ssize_t pch_phub_bin_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { int err; @@ -655,8 +655,8 @@ static const struct bin_attribute pch_bin_attr = { .mode = S_IRUGO | S_IWUSR, }, .size = PCH_PHUB_OROM_SIZE + 1, - .read = pch_phub_bin_read, - .write = pch_phub_bin_write, + .read_new = pch_phub_bin_read, + .write_new = pch_phub_bin_write, }; static int pch_phub_probe(struct pci_dev *pdev, diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index e40b027a88e2..e5069882457e 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -23,7 +23,7 @@ #define SRAM_GRANULARITY 32 static ssize_t sram_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buf, loff_t pos, size_t count) { struct sram_partition *part; @@ -38,7 +38,7 @@ static ssize_t sram_read(struct file *filp, struct kobject *kobj, } static ssize_t sram_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buf, loff_t pos, size_t count) { struct sram_partition *part; @@ -83,8 +83,8 @@ static int sram_add_export(struct sram_dev *sram, struct sram_reserve *block, return -ENOMEM; part->battr.attr.mode = S_IRUSR | S_IWUSR; - part->battr.read = sram_read; - part->battr.write = sram_write; + part->battr.read_new = sram_read; + part->battr.write_new = sram_write; part->battr.size = block->size; return device_create_bin_file(sram->dev, &part->battr); diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index d6494dfc20a7..fff85bbf0ecd 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -213,7 +213,7 @@ static struct attribute *nvmem_attrs[] = { }; static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, + const struct bin_attribute *attr, char *buf, loff_t pos, size_t count) { struct device *dev; @@ -246,7 +246,7 @@ static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, } static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, + const struct bin_attribute *attr, char *buf, loff_t pos, size_t count) { struct device *dev; @@ -340,7 +340,7 @@ static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry, const char *id, int index); static ssize_t nvmem_cell_attr_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, + const struct bin_attribute *attr, char *buf, loff_t pos, size_t count) { struct nvmem_cell_entry *entry; @@ -374,22 +374,22 @@ destroy_cell: } /* default read/write permissions */ -static struct bin_attribute bin_attr_rw_nvmem = { +static const struct bin_attribute bin_attr_rw_nvmem = { .attr = { .name = "nvmem", .mode = 0644, }, - .read = bin_attr_nvmem_read, - .write = bin_attr_nvmem_write, + .read_new = bin_attr_nvmem_read, + .write_new = bin_attr_nvmem_write, }; -static struct bin_attribute *nvmem_bin_attributes[] = { +static const struct bin_attribute *const nvmem_bin_attributes[] = { &bin_attr_rw_nvmem, NULL, }; static const struct attribute_group nvmem_bin_group = { - .bin_attrs = nvmem_bin_attributes, + .bin_attrs_new = nvmem_bin_attributes, .attrs = nvmem_attrs, .is_bin_visible = nvmem_bin_attr_is_visible, .bin_size = nvmem_bin_attr_size, @@ -401,12 +401,12 @@ static const struct attribute_group *nvmem_dev_groups[] = { NULL, }; -static struct bin_attribute bin_attr_nvmem_eeprom_compat = { +static const struct bin_attribute bin_attr_nvmem_eeprom_compat = { .attr = { .name = "eeprom", }, - .read = bin_attr_nvmem_read, - .write = bin_attr_nvmem_write, + .read_new = bin_attr_nvmem_read, + .write_new = bin_attr_nvmem_write, }; /* @@ -461,6 +461,7 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) .name = "cells", }; struct nvmem_cell_entry *entry; + const struct bin_attribute **pattrs; struct bin_attribute *attrs; unsigned int ncells = 0, i = 0; int ret = 0; @@ -472,9 +473,9 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) /* Allocate an array of attributes with a sentinel */ ncells = list_count_nodes(&nvmem->cells); - group.bin_attrs = devm_kcalloc(&nvmem->dev, ncells + 1, - sizeof(struct bin_attribute *), GFP_KERNEL); - if (!group.bin_attrs) { + pattrs = devm_kcalloc(&nvmem->dev, ncells + 1, + sizeof(struct bin_attribute *), GFP_KERNEL); + if (!pattrs) { ret = -ENOMEM; goto unlock_mutex; } @@ -494,17 +495,19 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) entry->bit_offset); attrs[i].attr.mode = 0444 & nvmem_bin_attr_get_umode(nvmem); attrs[i].size = entry->bytes; - attrs[i].read = &nvmem_cell_attr_read; + attrs[i].read_new = &nvmem_cell_attr_read; attrs[i].private = entry; if (!attrs[i].attr.name) { ret = -ENOMEM; goto unlock_mutex; } - group.bin_attrs[i] = &attrs[i]; + pattrs[i] = &attrs[i]; i++; } + group.bin_attrs_new = pattrs; + ret = device_add_group(&nvmem->dev, &group); if (ret) goto unlock_mutex; @@ -1790,6 +1793,8 @@ static int __nvmem_cell_entry_write(struct nvmem_cell_entry *cell, void *buf, si return -EINVAL; if (cell->bit_offset || cell->nbits) { + if (len != BITS_TO_BYTES(cell->nbits) && len != cell->bytes) + return -EINVAL; buf = nvmem_cell_prepare_write_buffer(cell, buf, len); if (IS_ERR(buf)) return PTR_ERR(buf); diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c index 1ba494497698..ca6dd71d8a2e 100644 --- a/drivers/nvmem/imx-ocotp-ele.c +++ b/drivers/nvmem/imx-ocotp-ele.c @@ -71,13 +71,15 @@ static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, siz u32 *buf; void *p; int i; + u8 skipbytes; - index = offset; - num_bytes = round_up(bytes, 4); - count = num_bytes >> 2; + if (offset + bytes > priv->data->size) + bytes = priv->data->size - offset; - if (count > ((priv->data->size >> 2) - index)) - count = (priv->data->size >> 2) - index; + index = offset >> 2; + skipbytes = offset - (index << 2); + num_bytes = round_up(bytes + skipbytes, 4); + count = num_bytes >> 2; p = kzalloc(num_bytes, GFP_KERNEL); if (!p) @@ -100,7 +102,7 @@ static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, siz *buf++ = readl_relaxed(reg + (i << 2)); } - memcpy(val, (u8 *)p, bytes); + memcpy(val, ((u8 *)p) + skipbytes, bytes); mutex_unlock(&priv->lock); @@ -109,6 +111,26 @@ static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, siz return 0; }; +static int imx_ocotp_cell_pp(void *context, const char *id, int index, + unsigned int offset, void *data, size_t bytes) +{ + u8 *buf = data; + int i; + + /* Deal with some post processing of nvmem cell data */ + if (id && !strcmp(id, "mac-address")) + for (i = 0; i < bytes / 2; i++) + swap(buf[i], buf[bytes - i - 1]); + + return 0; +} + +static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem, + struct nvmem_cell_info *cell) +{ + cell->read_post_process = imx_ocotp_cell_pp; +} + static int imx_ele_ocotp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -131,10 +153,12 @@ static int imx_ele_ocotp_probe(struct platform_device *pdev) priv->config.owner = THIS_MODULE; priv->config.size = priv->data->size; priv->config.reg_read = priv->data->reg_read; - priv->config.word_size = 4; + priv->config.word_size = 1; priv->config.stride = 1; priv->config.priv = priv; priv->config.read_only = true; + priv->config.add_legacy_fixed_of_cells = true; + priv->config.fixup_dt_cell_info = imx_ocotp_fixup_dt_cell_info; mutex_init(&priv->lock); nvmem = devm_nvmem_register(dev, &priv->config); diff --git a/drivers/nvmem/qcom-spmi-sdam.c b/drivers/nvmem/qcom-spmi-sdam.c index 9aa8f42faa4c..4f1cca6eab71 100644 --- a/drivers/nvmem/qcom-spmi-sdam.c +++ b/drivers/nvmem/qcom-spmi-sdam.c @@ -144,6 +144,7 @@ static int sdam_probe(struct platform_device *pdev) sdam->sdam_config.owner = THIS_MODULE; sdam->sdam_config.add_legacy_fixed_of_cells = true; sdam->sdam_config.stride = 1; + sdam->sdam_config.size = sdam->size; sdam->sdam_config.word_size = 1; sdam->sdam_config.reg_read = sdam_read; sdam->sdam_config.reg_write = sdam_write; diff --git a/drivers/nvmem/rmem.c b/drivers/nvmem/rmem.c index 7f907c5a445e..b39d628cb60a 100644 --- a/drivers/nvmem/rmem.c +++ b/drivers/nvmem/rmem.c @@ -3,28 +3,40 @@ * Copyright (C) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de> */ +#include <linux/crc32.h> #include <linux/io.h> #include <linux/module.h> #include <linux/nvmem-provider.h> #include <linux/of_reserved_mem.h> #include <linux/platform_device.h> +#include <linux/slab.h> struct rmem { struct device *dev; struct nvmem_device *nvmem; struct reserved_mem *mem; +}; + +struct rmem_match_data { + int (*checksum)(struct rmem *priv); +}; - phys_addr_t size; +struct __packed rmem_eyeq5_header { + u32 magic; + u32 version; + u32 size; }; +#define RMEM_EYEQ5_MAGIC ((u32)0xDABBAD00) + static int rmem_read(void *context, unsigned int offset, void *val, size_t bytes) { struct rmem *priv = context; - size_t available = priv->mem->size; - loff_t off = offset; void *addr; - int count; + + if ((phys_addr_t)offset + bytes > priv->mem->size) + return -EIO; /* * Only map the reserved memory at this point to avoid potential rogue @@ -36,26 +48,79 @@ static int rmem_read(void *context, unsigned int offset, * An alternative would be setting the memory as RO, set_memory_ro(), * but as of Dec 2020 this isn't possible on arm64. */ - addr = memremap(priv->mem->base, available, MEMREMAP_WB); + addr = memremap(priv->mem->base, priv->mem->size, MEMREMAP_WB); if (!addr) { dev_err(priv->dev, "Failed to remap memory region\n"); return -ENOMEM; } - count = memory_read_from_buffer(val, bytes, &off, addr, available); + memcpy(val, addr + offset, bytes); memunmap(addr); - if (count < 0) - return count; + return 0; +} + +static int rmem_eyeq5_checksum(struct rmem *priv) +{ + void *buf __free(kfree) = NULL; + struct rmem_eyeq5_header header; + u32 computed_crc, *target_crc; + size_t data_size; + int ret; + + ret = rmem_read(priv, 0, &header, sizeof(header)); + if (ret) + return ret; + + if (header.magic != RMEM_EYEQ5_MAGIC) + return -EINVAL; + + /* + * Avoid massive kmalloc() if header read is invalid; + * the check would be done by the next rmem_read() anyway. + */ + if (header.size > priv->mem->size) + return -EINVAL; + + /* + * 0 +-------------------+ + * | Header (12 bytes) | \ + * +-------------------+ | + * | | | data to be CRCed + * | ... | | + * | | / + * data_size +-------------------+ + * | CRC (4 bytes) | + * header.size +-------------------+ + */ + + buf = kmalloc(header.size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = rmem_read(priv, 0, buf, header.size); + if (ret) + return ret; - return count == bytes ? 0 : -EIO; + data_size = header.size - sizeof(*target_crc); + target_crc = buf + data_size; + computed_crc = crc32(U32_MAX, buf, data_size) ^ U32_MAX; + + if (computed_crc == *target_crc) + return 0; + + dev_err(priv->dev, + "checksum failed: computed %#x, expected %#x, header (%#x, %#x, %#x)\n", + computed_crc, *target_crc, header.magic, header.version, header.size); + return -EINVAL; } static int rmem_probe(struct platform_device *pdev) { struct nvmem_config config = { }; struct device *dev = &pdev->dev; + const struct rmem_match_data *match_data = device_get_match_data(dev); struct reserved_mem *mem; struct rmem *priv; @@ -78,10 +143,22 @@ static int rmem_probe(struct platform_device *pdev) config.size = mem->size; config.reg_read = rmem_read; + if (match_data && match_data->checksum) { + int ret = match_data->checksum(priv); + + if (ret) + return ret; + } + return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config)); } +static const struct rmem_match_data rmem_eyeq5_match_data = { + .checksum = rmem_eyeq5_checksum, +}; + static const struct of_device_id rmem_match[] = { + { .compatible = "mobileye,eyeq5-bootloader-config", .data = &rmem_eyeq5_match_data }, { .compatible = "nvmem-rmem", }, { /* sentinel */ }, }; diff --git a/drivers/pps/Makefile b/drivers/pps/Makefile index ceaf65cc1f1d..0aea394d4e4d 100644 --- a/drivers/pps/Makefile +++ b/drivers/pps/Makefile @@ -6,6 +6,7 @@ pps_core-y := pps.o kapi.o sysfs.o pps_core-$(CONFIG_NTP_PPS) += kc.o obj-$(CONFIG_PPS) := pps_core.o -obj-y += clients/ generators/ +obj-y += clients/ +obj-$(CONFIG_PPS_GENERATOR) += generators/ ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c index 634c3b2f8c26..75c1bae30a7c 100644 --- a/drivers/pps/clients/pps-gpio.c +++ b/drivers/pps/clients/pps-gpio.c @@ -52,7 +52,9 @@ static irqreturn_t pps_gpio_irq_handler(int irq, void *data) info = data; - rising_edge = gpiod_get_value(info->gpio_pin); + /* Small trick to bypass the check on edge's direction when capture_clear is unset */ + rising_edge = info->capture_clear ? + gpiod_get_value(info->gpio_pin) : !info->assert_falling_edge; if ((rising_edge && !info->assert_falling_edge) || (!rising_edge && info->assert_falling_edge)) pps_event(info->pps, &ts, PPS_CAPTUREASSERT, data); @@ -60,6 +62,8 @@ static irqreturn_t pps_gpio_irq_handler(int irq, void *data) ((rising_edge && info->assert_falling_edge) || (!rising_edge && !info->assert_falling_edge))) pps_event(info->pps, &ts, PPS_CAPTURECLEAR, data); + else + dev_warn_ratelimited(&info->pps->dev, "IRQ did not trigger any PPS event\n"); return IRQ_HANDLED; } @@ -214,8 +218,8 @@ static int pps_gpio_probe(struct platform_device *pdev) return -EINVAL; } - dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", - data->irq); + dev_dbg(&data->pps->dev, "Registered IRQ %d as PPS source\n", + data->irq); return 0; } diff --git a/drivers/pps/clients/pps-ktimer.c b/drivers/pps/clients/pps-ktimer.c index d33106bd7a29..2f465549b843 100644 --- a/drivers/pps/clients/pps-ktimer.c +++ b/drivers/pps/clients/pps-ktimer.c @@ -56,7 +56,7 @@ static struct pps_source_info pps_ktimer_info = { static void __exit pps_ktimer_exit(void) { - dev_info(pps->dev, "ktimer PPS source unregistered\n"); + dev_dbg(&pps->dev, "ktimer PPS source unregistered\n"); del_timer_sync(&ktimer); pps_unregister_source(pps); @@ -74,7 +74,7 @@ static int __init pps_ktimer_init(void) timer_setup(&ktimer, pps_ktimer_event, 0); mod_timer(&ktimer, jiffies + HZ); - dev_info(pps->dev, "ktimer PPS source registered\n"); + dev_dbg(&pps->dev, "ktimer PPS source registered\n"); return 0; } diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c index 443d6bae19d1..fa5660f3c4b7 100644 --- a/drivers/pps/clients/pps-ldisc.c +++ b/drivers/pps/clients/pps-ldisc.c @@ -32,7 +32,7 @@ static void pps_tty_dcd_change(struct tty_struct *tty, bool active) pps_event(pps, &ts, active ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR, NULL); - dev_dbg(pps->dev, "PPS %s at %lu\n", + dev_dbg(&pps->dev, "PPS %s at %lu\n", active ? "assert" : "clear", jiffies); } @@ -69,7 +69,7 @@ static int pps_tty_open(struct tty_struct *tty) goto err_unregister; } - dev_info(pps->dev, "source \"%s\" added\n", info.path); + dev_dbg(&pps->dev, "source \"%s\" added\n", info.path); return 0; @@ -89,7 +89,7 @@ static void pps_tty_close(struct tty_struct *tty) if (WARN_ON(!pps)) return; - dev_info(pps->dev, "removed\n"); + dev_info(&pps->dev, "removed\n"); pps_unregister_source(pps); } diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c index abaffb4e1c1c..24db06750297 100644 --- a/drivers/pps/clients/pps_parport.c +++ b/drivers/pps/clients/pps_parport.c @@ -81,7 +81,7 @@ static void parport_irq(void *handle) /* check the signal (no signal means the pulse is lost this time) */ if (!signal_is_set(port)) { local_irq_restore(flags); - dev_err(dev->pps->dev, "lost the signal\n"); + dev_err(&dev->pps->dev, "lost the signal\n"); goto out_assert; } @@ -98,7 +98,7 @@ static void parport_irq(void *handle) /* timeout */ dev->cw_err++; if (dev->cw_err >= CLEAR_WAIT_MAX_ERRORS) { - dev_err(dev->pps->dev, "disabled clear edge capture after %d" + dev_err(&dev->pps->dev, "disabled clear edge capture after %d" " timeouts\n", dev->cw_err); dev->cw = 0; dev->cw_err = 0; diff --git a/drivers/pps/generators/Kconfig b/drivers/pps/generators/Kconfig index d615e640fcad..cd94bf3bfaf2 100644 --- a/drivers/pps/generators/Kconfig +++ b/drivers/pps/generators/Kconfig @@ -3,7 +3,25 @@ # PPS generators configuration # -comment "PPS generators support" +menuconfig PPS_GENERATOR + tristate "PPS generators support" + help + PPS generators are special hardware which are able to produce PPS + (Pulse Per Second) signals. + + To compile this driver as a module, choose M here: the module + will be called pps_gen_core. + +if PPS_GENERATOR + +config PPS_GENERATOR_DUMMY + tristate "Dummy PPS generator (Testing generator, use for debug)" + help + If you say yes here you get support for a PPS debugging generator + (which generates no PPS signal at all). + + This driver can also be built as a module. If so, the module + will be called pps_gen-dummy. config PPS_GENERATOR_PARPORT tristate "Parallel port PPS signal generator" @@ -12,3 +30,5 @@ config PPS_GENERATOR_PARPORT If you say yes here you get support for a PPS signal generator which utilizes STROBE pin of a parallel port to send PPS signals. It uses parport abstraction layer and hrtimers to precisely control the signal. + +endif # PPS_GENERATOR diff --git a/drivers/pps/generators/Makefile b/drivers/pps/generators/Makefile index 2589fd0f2481..dc1aa5a4688b 100644 --- a/drivers/pps/generators/Makefile +++ b/drivers/pps/generators/Makefile @@ -3,6 +3,10 @@ # Makefile for PPS generators. # +pps_gen_core-y := pps_gen.o sysfs.o +obj-$(CONFIG_PPS_GENERATOR) := pps_gen_core.o + +obj-$(CONFIG_PPS_GENERATOR_DUMMY) += pps_gen-dummy.o obj-$(CONFIG_PPS_GENERATOR_PARPORT) += pps_gen_parport.o ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG diff --git a/drivers/pps/generators/pps_gen-dummy.c b/drivers/pps/generators/pps_gen-dummy.c new file mode 100644 index 000000000000..b284c200cbe5 --- /dev/null +++ b/drivers/pps/generators/pps_gen-dummy.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PPS dummy generator + * + * Copyright (C) 2024 Rodolfo Giometti <giometti@enneenne.com> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/time.h> +#include <linux/timer.h> +#include <linux/random.h> +#include <linux/pps_gen_kernel.h> + +static struct pps_gen_device *pps_gen; +static struct timer_list ktimer; + +static unsigned int get_random_delay(void) +{ + unsigned int delay = get_random_u8() & 0x0f; + + return (delay + 1) * HZ; +} + +/* + * The kernel timer + */ + +static void pps_gen_ktimer_event(struct timer_list *unused) +{ + pps_gen_event(pps_gen, PPS_GEN_EVENT_MISSEDPULSE, NULL); +} + +/* + * PPS Generator methods + */ + +static int pps_gen_dummy_get_time(struct pps_gen_device *pps_gen, + struct timespec64 *time) +{ + struct system_time_snapshot snap; + + ktime_get_snapshot(&snap); + *time = ktime_to_timespec64(snap.real); + + return 0; +} + +static int pps_gen_dummy_enable(struct pps_gen_device *pps_gen, bool enable) +{ + if (enable) + mod_timer(&ktimer, jiffies + get_random_delay()); + else + del_timer_sync(&ktimer); + + return 0; +} + +/* + * The PPS info struct + */ + +static struct pps_gen_source_info pps_gen_dummy_info = { + .use_system_clock = true, + .get_time = pps_gen_dummy_get_time, + .enable = pps_gen_dummy_enable, +}; + +/* + * Module staff + */ + +static void __exit pps_gen_dummy_exit(void) +{ + del_timer_sync(&ktimer); + pps_gen_unregister_source(pps_gen); +} + +static int __init pps_gen_dummy_init(void) +{ + pps_gen = pps_gen_register_source(&pps_gen_dummy_info); + if (IS_ERR(pps_gen)) + return PTR_ERR(pps_gen); + + timer_setup(&ktimer, pps_gen_ktimer_event, 0); + + return 0; +} + +module_init(pps_gen_dummy_init); +module_exit(pps_gen_dummy_exit); + +MODULE_AUTHOR("Rodolfo Giometti <giometti@enneenne.com>"); +MODULE_DESCRIPTION("LinuxPPS dummy generator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pps/generators/pps_gen.c b/drivers/pps/generators/pps_gen.c new file mode 100644 index 000000000000..ca592f1736f4 --- /dev/null +++ b/drivers/pps/generators/pps_gen.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PPS generators core file + * + * Copyright (C) 2024 Rodolfo Giometti <giometti@enneenne.com> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/time.h> +#include <linux/timex.h> +#include <linux/uaccess.h> +#include <linux/idr.h> +#include <linux/cdev.h> +#include <linux/poll.h> +#include <linux/fs.h> +#include <linux/pps_gen_kernel.h> +#include <linux/slab.h> + +/* + * Local variables + */ + +static dev_t pps_gen_devt; +static struct class *pps_gen_class; + +static DEFINE_IDA(pps_gen_ida); + +/* + * Char device methods + */ + +static __poll_t pps_gen_cdev_poll(struct file *file, poll_table *wait) +{ + struct pps_gen_device *pps_gen = file->private_data; + + poll_wait(file, &pps_gen->queue, wait); + return EPOLLIN | EPOLLRDNORM; +} + +static int pps_gen_cdev_fasync(int fd, struct file *file, int on) +{ + struct pps_gen_device *pps_gen = file->private_data; + + return fasync_helper(fd, file, on, &pps_gen->async_queue); +} + +static long pps_gen_cdev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct pps_gen_device *pps_gen = file->private_data; + void __user *uarg = (void __user *) arg; + unsigned int __user *uiuarg = (unsigned int __user *) arg; + unsigned int status; + int ret; + + switch (cmd) { + case PPS_GEN_SETENABLE: + dev_dbg(pps_gen->dev, "PPS_GEN_SETENABLE\n"); + + ret = get_user(status, uiuarg); + if (ret) + return -EFAULT; + + ret = pps_gen->info.enable(pps_gen, status); + if (ret) + return ret; + pps_gen->enabled = status; + + break; + + case PPS_GEN_USESYSTEMCLOCK: + dev_dbg(pps_gen->dev, "PPS_GEN_USESYSTEMCLOCK\n"); + + ret = put_user(pps_gen->info.use_system_clock, uiuarg); + if (ret) + return -EFAULT; + + break; + + case PPS_GEN_FETCHEVENT: { + struct pps_gen_event info; + unsigned int ev = pps_gen->last_ev; + + dev_dbg(pps_gen->dev, "PPS_GEN_FETCHEVENT\n"); + + ret = wait_event_interruptible(pps_gen->queue, + ev != pps_gen->last_ev); + if (ret == -ERESTARTSYS) { + dev_dbg(pps_gen->dev, "pending signal caught\n"); + return -EINTR; + } + + spin_lock_irq(&pps_gen->lock); + info.sequence = pps_gen->sequence; + info.event = pps_gen->event; + spin_unlock_irq(&pps_gen->lock); + + ret = copy_to_user(uarg, &info, sizeof(struct pps_gen_event)); + if (ret) + return -EFAULT; + + break; + } + default: + return -ENOTTY; + } + + return 0; +} + +static int pps_gen_cdev_open(struct inode *inode, struct file *file) +{ + struct pps_gen_device *pps_gen = container_of(inode->i_cdev, + struct pps_gen_device, cdev); + + get_device(pps_gen->dev); + file->private_data = pps_gen; + return 0; +} + +static int pps_gen_cdev_release(struct inode *inode, struct file *file) +{ + struct pps_gen_device *pps_gen = file->private_data; + + put_device(pps_gen->dev); + return 0; +} + +/* + * Char device stuff + */ + +static const struct file_operations pps_gen_cdev_fops = { + .owner = THIS_MODULE, + .poll = pps_gen_cdev_poll, + .fasync = pps_gen_cdev_fasync, + .unlocked_ioctl = pps_gen_cdev_ioctl, + .open = pps_gen_cdev_open, + .release = pps_gen_cdev_release, +}; + +static void pps_gen_device_destruct(struct device *dev) +{ + struct pps_gen_device *pps_gen = dev_get_drvdata(dev); + + cdev_del(&pps_gen->cdev); + + pr_debug("deallocating pps-gen%d\n", pps_gen->id); + ida_free(&pps_gen_ida, pps_gen->id); + + kfree(dev); + kfree(pps_gen); +} + +static int pps_gen_register_cdev(struct pps_gen_device *pps_gen) +{ + int err; + dev_t devt; + + err = ida_alloc_max(&pps_gen_ida, PPS_GEN_MAX_SOURCES - 1, GFP_KERNEL); + if (err < 0) { + if (err == -ENOSPC) { + pr_err("too many PPS sources in the system\n"); + err = -EBUSY; + } + return err; + } + pps_gen->id = err; + + devt = MKDEV(MAJOR(pps_gen_devt), pps_gen->id); + + cdev_init(&pps_gen->cdev, &pps_gen_cdev_fops); + pps_gen->cdev.owner = pps_gen->info.owner; + + err = cdev_add(&pps_gen->cdev, devt, 1); + if (err) { + pr_err("failed to add char device %d:%d\n", + MAJOR(pps_gen_devt), pps_gen->id); + goto free_ida; + } + pps_gen->dev = device_create(pps_gen_class, pps_gen->info.parent, devt, + pps_gen, "pps-gen%d", pps_gen->id); + if (IS_ERR(pps_gen->dev)) { + err = PTR_ERR(pps_gen->dev); + goto del_cdev; + } + pps_gen->dev->release = pps_gen_device_destruct; + dev_set_drvdata(pps_gen->dev, pps_gen); + + pr_debug("generator got cdev (%d:%d)\n", + MAJOR(pps_gen_devt), pps_gen->id); + + return 0; + +del_cdev: + cdev_del(&pps_gen->cdev); +free_ida: + ida_free(&pps_gen_ida, pps_gen->id); + return err; +} + +static void pps_gen_unregister_cdev(struct pps_gen_device *pps_gen) +{ + pr_debug("unregistering pps-gen%d\n", pps_gen->id); + device_destroy(pps_gen_class, pps_gen->dev->devt); +} + +/* + * Exported functions + */ + +/** + * pps_gen_register_source() - add a PPS generator in the system + * @info: the PPS generator info struct + * + * This function is used to register a new PPS generator in the system. + * When it returns successfully the new generator is up and running, and + * it can be managed by the userspace. + * + * Return: the PPS generator device in case of success, and ERR_PTR(errno) + * otherwise. + */ +struct pps_gen_device *pps_gen_register_source(struct pps_gen_source_info *info) +{ + struct pps_gen_device *pps_gen; + int err; + + pps_gen = kzalloc(sizeof(struct pps_gen_device), GFP_KERNEL); + if (pps_gen == NULL) { + err = -ENOMEM; + goto pps_gen_register_source_exit; + } + pps_gen->info = *info; + pps_gen->enabled = false; + + init_waitqueue_head(&pps_gen->queue); + spin_lock_init(&pps_gen->lock); + + /* Create the char device */ + err = pps_gen_register_cdev(pps_gen); + if (err < 0) { + pr_err(" unable to create char device\n"); + goto kfree_pps_gen; + } + + return pps_gen; + +kfree_pps_gen: + kfree(pps_gen); + +pps_gen_register_source_exit: + pr_err("unable to register generator\n"); + + return ERR_PTR(err); +} +EXPORT_SYMBOL(pps_gen_register_source); + +/** + * pps_gen_unregister_source() - remove a PPS generator from the system + * @pps_gen: the PPS generator device to be removed + * + * This function is used to deregister a PPS generator from the system. When + * called, it disables the generator so no pulses are generated anymore. + */ +void pps_gen_unregister_source(struct pps_gen_device *pps_gen) +{ + pps_gen_unregister_cdev(pps_gen); +} +EXPORT_SYMBOL(pps_gen_unregister_source); + +/* pps_gen_event - register a PPS generator event into the system + * @pps: the PPS generator device + * @event: the event type + * @data: userdef pointer + * + * This function is used by each PPS generator in order to register a new + * PPS event into the system (it's usually called inside an IRQ handler). + */ +void pps_gen_event(struct pps_gen_device *pps_gen, + unsigned int event, void *data) +{ + unsigned long flags; + + dev_dbg(pps_gen->dev, "PPS generator event %u\n", event); + + spin_lock_irqsave(&pps_gen->lock, flags); + + pps_gen->event = event; + pps_gen->sequence++; + + pps_gen->last_ev++; + wake_up_interruptible_all(&pps_gen->queue); + kill_fasync(&pps_gen->async_queue, SIGIO, POLL_IN); + + spin_unlock_irqrestore(&pps_gen->lock, flags); +} +EXPORT_SYMBOL(pps_gen_event); + +/* + * Module stuff + */ + +static void __exit pps_gen_exit(void) +{ + class_destroy(pps_gen_class); + unregister_chrdev_region(pps_gen_devt, PPS_GEN_MAX_SOURCES); +} + +static int __init pps_gen_init(void) +{ + int err; + + pps_gen_class = class_create("pps-gen"); + if (IS_ERR(pps_gen_class)) { + pr_err("failed to allocate class\n"); + return PTR_ERR(pps_gen_class); + } + pps_gen_class->dev_groups = pps_gen_groups; + + err = alloc_chrdev_region(&pps_gen_devt, 0, + PPS_GEN_MAX_SOURCES, "pps-gen"); + if (err < 0) { + pr_err("failed to allocate char device region\n"); + goto remove_class; + } + + return 0; + +remove_class: + class_destroy(pps_gen_class); + return err; +} + +subsys_initcall(pps_gen_init); +module_exit(pps_gen_exit); + +MODULE_AUTHOR("Rodolfo Giometti <giometti@enneenne.com>"); +MODULE_DESCRIPTION("LinuxPPS generators support"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pps/generators/sysfs.c b/drivers/pps/generators/sysfs.c new file mode 100644 index 000000000000..faf8b1c6d202 --- /dev/null +++ b/drivers/pps/generators/sysfs.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PPS generators sysfs support + * + * Copyright (C) 2024 Rodolfo Giometti <giometti@enneenne.com> + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/pps_gen_kernel.h> + +/* + * Attribute functions + */ + +static ssize_t system_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pps_gen_device *pps_gen = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", pps_gen->info.use_system_clock); +} +static DEVICE_ATTR_RO(system); + +static ssize_t time_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pps_gen_device *pps_gen = dev_get_drvdata(dev); + struct timespec64 time; + int ret; + + ret = pps_gen->info.get_time(pps_gen, &time); + if (ret) + return ret; + + return sysfs_emit(buf, "%llu %09lu\n", time.tv_sec, time.tv_nsec); +} +static DEVICE_ATTR_RO(time); + +static ssize_t enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pps_gen_device *pps_gen = dev_get_drvdata(dev); + bool status; + int ret; + + ret = kstrtobool(buf, &status); + if (ret) + return ret; + + ret = pps_gen->info.enable(pps_gen, status); + if (ret) + return ret; + pps_gen->enabled = status; + + return count; +} +static DEVICE_ATTR_WO(enable); + +static struct attribute *pps_gen_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_time.attr, + &dev_attr_system.attr, + NULL, +}; + +static const struct attribute_group pps_gen_group = { + .attrs = pps_gen_attrs, +}; + +const struct attribute_group *pps_gen_groups[] = { + &pps_gen_group, + NULL, +}; diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index d9d566f70ed1..92d1b62ea239 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c @@ -41,7 +41,7 @@ static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset) static void pps_echo_client_default(struct pps_device *pps, int event, void *data) { - dev_info(pps->dev, "echo %s %s\n", + dev_info(&pps->dev, "echo %s %s\n", event & PPS_CAPTUREASSERT ? "assert" : "", event & PPS_CAPTURECLEAR ? "clear" : ""); } @@ -112,7 +112,7 @@ struct pps_device *pps_register_source(struct pps_source_info *info, goto kfree_pps; } - dev_info(pps->dev, "new PPS source %s\n", info->name); + dev_dbg(&pps->dev, "new PPS source %s\n", info->name); return pps; @@ -166,7 +166,7 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, /* check event type */ BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0); - dev_dbg(pps->dev, "PPS event at %lld.%09ld\n", + dev_dbg(&pps->dev, "PPS event at %lld.%09ld\n", (s64)ts->ts_real.tv_sec, ts->ts_real.tv_nsec); timespec_to_pps_ktime(&ts_real, ts->ts_real); @@ -188,7 +188,7 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, /* Save the time stamp */ pps->assert_tu = ts_real; pps->assert_sequence++; - dev_dbg(pps->dev, "capture assert seq #%u\n", + dev_dbg(&pps->dev, "capture assert seq #%u\n", pps->assert_sequence); captured = ~0; @@ -202,7 +202,7 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, /* Save the time stamp */ pps->clear_tu = ts_real; pps->clear_sequence++; - dev_dbg(pps->dev, "capture clear seq #%u\n", + dev_dbg(&pps->dev, "capture clear seq #%u\n", pps->clear_sequence); captured = ~0; diff --git a/drivers/pps/kc.c b/drivers/pps/kc.c index 50dc59af45be..fbd23295afd7 100644 --- a/drivers/pps/kc.c +++ b/drivers/pps/kc.c @@ -43,11 +43,11 @@ int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args) pps_kc_hardpps_mode = 0; pps_kc_hardpps_dev = NULL; spin_unlock_irq(&pps_kc_hardpps_lock); - dev_info(pps->dev, "unbound kernel" + dev_info(&pps->dev, "unbound kernel" " consumer\n"); } else { spin_unlock_irq(&pps_kc_hardpps_lock); - dev_err(pps->dev, "selected kernel consumer" + dev_err(&pps->dev, "selected kernel consumer" " is not bound\n"); return -EINVAL; } @@ -57,11 +57,11 @@ int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args) pps_kc_hardpps_mode = bind_args->edge; pps_kc_hardpps_dev = pps; spin_unlock_irq(&pps_kc_hardpps_lock); - dev_info(pps->dev, "bound kernel consumer: " + dev_info(&pps->dev, "bound kernel consumer: " "edge=0x%x\n", bind_args->edge); } else { spin_unlock_irq(&pps_kc_hardpps_lock); - dev_err(pps->dev, "another kernel consumer" + dev_err(&pps->dev, "another kernel consumer" " is already bound\n"); return -EINVAL; } @@ -83,7 +83,7 @@ void pps_kc_remove(struct pps_device *pps) pps_kc_hardpps_mode = 0; pps_kc_hardpps_dev = NULL; spin_unlock_irq(&pps_kc_hardpps_lock); - dev_info(pps->dev, "unbound kernel consumer" + dev_info(&pps->dev, "unbound kernel consumer" " on device removal\n"); } else spin_unlock_irq(&pps_kc_hardpps_lock); diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index 25d47907db17..6a02245ea35f 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -25,7 +25,7 @@ * Local variables */ -static dev_t pps_devt; +static int pps_major; static struct class *pps_class; static DEFINE_MUTEX(pps_idr_lock); @@ -62,7 +62,7 @@ static int pps_cdev_pps_fetch(struct pps_device *pps, struct pps_fdata *fdata) else { unsigned long ticks; - dev_dbg(pps->dev, "timeout %lld.%09d\n", + dev_dbg(&pps->dev, "timeout %lld.%09d\n", (long long) fdata->timeout.sec, fdata->timeout.nsec); ticks = fdata->timeout.sec * HZ; @@ -80,7 +80,7 @@ static int pps_cdev_pps_fetch(struct pps_device *pps, struct pps_fdata *fdata) /* Check for pending signals */ if (err == -ERESTARTSYS) { - dev_dbg(pps->dev, "pending signal caught\n"); + dev_dbg(&pps->dev, "pending signal caught\n"); return -EINTR; } @@ -98,7 +98,7 @@ static long pps_cdev_ioctl(struct file *file, switch (cmd) { case PPS_GETPARAMS: - dev_dbg(pps->dev, "PPS_GETPARAMS\n"); + dev_dbg(&pps->dev, "PPS_GETPARAMS\n"); spin_lock_irq(&pps->lock); @@ -114,7 +114,7 @@ static long pps_cdev_ioctl(struct file *file, break; case PPS_SETPARAMS: - dev_dbg(pps->dev, "PPS_SETPARAMS\n"); + dev_dbg(&pps->dev, "PPS_SETPARAMS\n"); /* Check the capabilities */ if (!capable(CAP_SYS_TIME)) @@ -124,14 +124,14 @@ static long pps_cdev_ioctl(struct file *file, if (err) return -EFAULT; if (!(params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) { - dev_dbg(pps->dev, "capture mode unspecified (%x)\n", + dev_dbg(&pps->dev, "capture mode unspecified (%x)\n", params.mode); return -EINVAL; } /* Check for supported capabilities */ if ((params.mode & ~pps->info.mode) != 0) { - dev_dbg(pps->dev, "unsupported capabilities (%x)\n", + dev_dbg(&pps->dev, "unsupported capabilities (%x)\n", params.mode); return -EINVAL; } @@ -144,7 +144,7 @@ static long pps_cdev_ioctl(struct file *file, /* Restore the read only parameters */ if ((params.mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) { /* section 3.3 of RFC 2783 interpreted */ - dev_dbg(pps->dev, "time format unspecified (%x)\n", + dev_dbg(&pps->dev, "time format unspecified (%x)\n", params.mode); pps->params.mode |= PPS_TSFMT_TSPEC; } @@ -165,7 +165,7 @@ static long pps_cdev_ioctl(struct file *file, break; case PPS_GETCAP: - dev_dbg(pps->dev, "PPS_GETCAP\n"); + dev_dbg(&pps->dev, "PPS_GETCAP\n"); err = put_user(pps->info.mode, iuarg); if (err) @@ -176,7 +176,7 @@ static long pps_cdev_ioctl(struct file *file, case PPS_FETCH: { struct pps_fdata fdata; - dev_dbg(pps->dev, "PPS_FETCH\n"); + dev_dbg(&pps->dev, "PPS_FETCH\n"); err = copy_from_user(&fdata, uarg, sizeof(struct pps_fdata)); if (err) @@ -206,7 +206,7 @@ static long pps_cdev_ioctl(struct file *file, case PPS_KC_BIND: { struct pps_bind_args bind_args; - dev_dbg(pps->dev, "PPS_KC_BIND\n"); + dev_dbg(&pps->dev, "PPS_KC_BIND\n"); /* Check the capabilities */ if (!capable(CAP_SYS_TIME)) @@ -218,7 +218,7 @@ static long pps_cdev_ioctl(struct file *file, /* Check for supported capabilities */ if ((bind_args.edge & ~pps->info.mode) != 0) { - dev_err(pps->dev, "unsupported capabilities (%x)\n", + dev_err(&pps->dev, "unsupported capabilities (%x)\n", bind_args.edge); return -EINVAL; } @@ -227,7 +227,7 @@ static long pps_cdev_ioctl(struct file *file, if (bind_args.tsformat != PPS_TSFMT_TSPEC || (bind_args.edge & ~PPS_CAPTUREBOTH) != 0 || bind_args.consumer != PPS_KC_HARDPPS) { - dev_err(pps->dev, "invalid kernel consumer bind" + dev_err(&pps->dev, "invalid kernel consumer bind" " parameters (%x)\n", bind_args.edge); return -EINVAL; } @@ -259,7 +259,7 @@ static long pps_cdev_compat_ioctl(struct file *file, struct pps_fdata fdata; int err; - dev_dbg(pps->dev, "PPS_FETCH\n"); + dev_dbg(&pps->dev, "PPS_FETCH\n"); err = copy_from_user(&compat, uarg, sizeof(struct pps_fdata_compat)); if (err) @@ -296,20 +296,36 @@ static long pps_cdev_compat_ioctl(struct file *file, #define pps_cdev_compat_ioctl NULL #endif +static struct pps_device *pps_idr_get(unsigned long id) +{ + struct pps_device *pps; + + mutex_lock(&pps_idr_lock); + pps = idr_find(&pps_idr, id); + if (pps) + get_device(&pps->dev); + + mutex_unlock(&pps_idr_lock); + return pps; +} + static int pps_cdev_open(struct inode *inode, struct file *file) { - struct pps_device *pps = container_of(inode->i_cdev, - struct pps_device, cdev); + struct pps_device *pps = pps_idr_get(iminor(inode)); + + if (!pps) + return -ENODEV; + file->private_data = pps; - kobject_get(&pps->dev->kobj); return 0; } static int pps_cdev_release(struct inode *inode, struct file *file) { - struct pps_device *pps = container_of(inode->i_cdev, - struct pps_device, cdev); - kobject_put(&pps->dev->kobj); + struct pps_device *pps = file->private_data; + + WARN_ON(pps->id != iminor(inode)); + put_device(&pps->dev); return 0; } @@ -331,22 +347,13 @@ static void pps_device_destruct(struct device *dev) { struct pps_device *pps = dev_get_drvdata(dev); - cdev_del(&pps->cdev); - - /* Now we can release the ID for re-use */ pr_debug("deallocating pps%d\n", pps->id); - mutex_lock(&pps_idr_lock); - idr_remove(&pps_idr, pps->id); - mutex_unlock(&pps_idr_lock); - - kfree(dev); kfree(pps); } int pps_register_cdev(struct pps_device *pps) { int err; - dev_t devt; mutex_lock(&pps_idr_lock); /* @@ -363,40 +370,29 @@ int pps_register_cdev(struct pps_device *pps) goto out_unlock; } pps->id = err; - mutex_unlock(&pps_idr_lock); - - devt = MKDEV(MAJOR(pps_devt), pps->id); - - cdev_init(&pps->cdev, &pps_cdev_fops); - pps->cdev.owner = pps->info.owner; - err = cdev_add(&pps->cdev, devt, 1); - if (err) { - pr_err("%s: failed to add char device %d:%d\n", - pps->info.name, MAJOR(pps_devt), pps->id); + pps->dev.class = pps_class; + pps->dev.parent = pps->info.dev; + pps->dev.devt = MKDEV(pps_major, pps->id); + dev_set_drvdata(&pps->dev, pps); + dev_set_name(&pps->dev, "pps%d", pps->id); + err = device_register(&pps->dev); + if (err) goto free_idr; - } - pps->dev = device_create(pps_class, pps->info.dev, devt, pps, - "pps%d", pps->id); - if (IS_ERR(pps->dev)) { - err = PTR_ERR(pps->dev); - goto del_cdev; - } /* Override the release function with our own */ - pps->dev->release = pps_device_destruct; + pps->dev.release = pps_device_destruct; - pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, - MAJOR(pps_devt), pps->id); + pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, pps_major, + pps->id); + get_device(&pps->dev); + mutex_unlock(&pps_idr_lock); return 0; -del_cdev: - cdev_del(&pps->cdev); - free_idr: - mutex_lock(&pps_idr_lock); idr_remove(&pps_idr, pps->id); + put_device(&pps->dev); out_unlock: mutex_unlock(&pps_idr_lock); return err; @@ -406,7 +402,13 @@ void pps_unregister_cdev(struct pps_device *pps) { pr_debug("unregistering pps%d\n", pps->id); pps->lookup_cookie = NULL; - device_destroy(pps_class, pps->dev->devt); + device_destroy(pps_class, pps->dev.devt); + + /* Now we can release the ID for re-use */ + mutex_lock(&pps_idr_lock); + idr_remove(&pps_idr, pps->id); + put_device(&pps->dev); + mutex_unlock(&pps_idr_lock); } /* @@ -426,6 +428,11 @@ void pps_unregister_cdev(struct pps_device *pps) * so that it will not be used again, even if the pps device cannot * be removed from the idr due to pending references holding the minor * number in use. + * + * Since pps_idr holds a reference to the device, the returned + * pps_device is guaranteed to be valid until pps_unregister_cdev() is + * called on it. But after calling pps_unregister_cdev(), it may be + * freed at any time. */ struct pps_device *pps_lookup_dev(void const *cookie) { @@ -448,13 +455,11 @@ EXPORT_SYMBOL(pps_lookup_dev); static void __exit pps_exit(void) { class_destroy(pps_class); - unregister_chrdev_region(pps_devt, PPS_MAX_SOURCES); + __unregister_chrdev(pps_major, 0, PPS_MAX_SOURCES, "pps"); } static int __init pps_init(void) { - int err; - pps_class = class_create("pps"); if (IS_ERR(pps_class)) { pr_err("failed to allocate class\n"); @@ -462,8 +467,9 @@ static int __init pps_init(void) } pps_class->dev_groups = pps_groups; - err = alloc_chrdev_region(&pps_devt, 0, PPS_MAX_SOURCES, "pps"); - if (err < 0) { + pps_major = __register_chrdev(0, 0, PPS_MAX_SOURCES, "pps", + &pps_cdev_fops); + if (pps_major < 0) { pr_err("failed to allocate char device region\n"); goto remove_class; } @@ -476,8 +482,7 @@ static int __init pps_init(void) remove_class: class_destroy(pps_class); - - return err; + return pps_major; } subsys_initcall(pps_init); diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 7f08c70d8123..b651087f426f 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -4420,7 +4420,7 @@ ptp_ocp_complete(struct ptp_ocp *bp) pps = pps_lookup_dev(bp->ptp); if (pps) - ptp_ocp_symlink(bp, pps->dev, "pps"); + ptp_ocp_symlink(bp, &pps->dev, "pps"); ptp_ocp_debugfs_add_device(bp); diff --git a/drivers/slimbus/messaging.c b/drivers/slimbus/messaging.c index 242570a5e565..e7aa9bd4b44b 100644 --- a/drivers/slimbus/messaging.c +++ b/drivers/slimbus/messaging.c @@ -13,8 +13,8 @@ * * @ctrl: Controller handle * @reply: Reply received from the device - * @len: Length of the reply * @tid: Transaction ID received with which framework can associate reply. + * @len: Length of the reply * * Called by controller to inform framework about the response received. * This helps in making the API asynchronous, and controller-driver doesn't need diff --git a/drivers/spmi/hisi-spmi-controller.c b/drivers/spmi/hisi-spmi-controller.c index 3cafdf22c909..122140b97579 100644 --- a/drivers/spmi/hisi-spmi-controller.c +++ b/drivers/spmi/hisi-spmi-controller.c @@ -300,9 +300,6 @@ static int spmi_controller_probe(struct platform_device *pdev) spin_lock_init(&spmi_controller->lock); - ctrl->dev.parent = pdev->dev.parent; - ctrl->dev.of_node = of_node_get(pdev->dev.of_node); - /* Callbacks */ ctrl->read_cmd = spmi_read_cmd; ctrl->write_cmd = spmi_write_cmd; diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index fb0101da1485..3cf8d9bd4566 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -517,7 +517,7 @@ static void of_spmi_register_devices(struct spmi_controller *ctrl) if (!sdev) continue; - sdev->dev.of_node = node; + device_set_node(&sdev->dev, of_fwnode_handle(node)); sdev->usid = (u8)reg[0]; err = spmi_device_add(sdev); diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 004a549c6c7d..d93ed4e86a17 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -565,7 +565,7 @@ static __poll_t uio_poll(struct file *filep, poll_table *wait) mutex_lock(&idev->info_lock); if (!idev->info || !idev->info->irq) - ret = -EIO; + ret = EPOLLERR; mutex_unlock(&idev->info_lock); if (ret) diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index c70dd81bfc61..31aa75110ba5 100644 --- a/drivers/uio/uio_dmem_genirq.c +++ b/drivers/uio/uio_dmem_genirq.c @@ -167,6 +167,8 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) } uioinfo->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOFn", pdev->dev.of_node); + if (!uioinfo->name) + return -ENOMEM; uioinfo->version = "devicetree"; } diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index a2ecbb863c57..e2a568c9a43a 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -7,7 +7,7 @@ * It is a I2C to 1-wire bridge. * There are two variations: -100 and -800, which have 1 or 8 1-wire ports. * The complete datasheet can be obtained from MAXIM's website at: - * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/4382 + * https://www.analog.com/en/products/ds2482-100.html */ #include <linux/module.h> @@ -15,6 +15,7 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/delay.h> +#include <linux/regulator/consumer.h> #include <linux/w1.h> @@ -445,17 +446,20 @@ static int ds2482_probe(struct i2c_client *client) int err = -ENODEV; int temp1; int idx; + int ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA | I2C_FUNC_SMBUS_BYTE)) return -ENODEV; - data = kzalloc(sizeof(struct ds2482_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct ds2482_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = devm_regulator_get_enable(&client->dev, "vcc"); + if (ret) + return dev_err_probe(&client->dev, ret, "Failed to enable regulator\n"); data->client = client; i2c_set_clientdata(client, data); @@ -463,7 +467,7 @@ static int ds2482_probe(struct i2c_client *client) /* Reset the device (sets the read_ptr to status) */ if (ds2482_send_cmd(data, DS2482_CMD_RESET) < 0) { dev_warn(&client->dev, "DS2482 reset failed.\n"); - goto exit_free; + return err; } /* Sleep at least 525ns to allow the reset to complete */ @@ -474,7 +478,7 @@ static int ds2482_probe(struct i2c_client *client) if (temp1 != (DS2482_REG_STS_LL | DS2482_REG_STS_RST)) { dev_warn(&client->dev, "DS2482 reset status " "0x%02X - not a DS2482\n", temp1); - goto exit_free; + return err; } /* Detect the 8-port version */ @@ -516,9 +520,6 @@ exit_w1_remove: if (data->w1_ch[idx].pdev != NULL) w1_remove_master_device(&data->w1_ch[idx].w1_bm); } -exit_free: - kfree(data); -exit: return err; } @@ -532,9 +533,6 @@ static void ds2482_remove(struct i2c_client *client) if (data->w1_ch[idx].pdev != NULL) w1_remove_master_device(&data->w1_ch[idx].w1_bm); } - - /* Free the memory */ - kfree(data); } /* diff --git a/drivers/w1/slaves/w1_ds2406.c b/drivers/w1/slaves/w1_ds2406.c index 2f5926859b8b..1cae9b243ff8 100644 --- a/drivers/w1/slaves/w1_ds2406.c +++ b/drivers/w1/slaves/w1_ds2406.c @@ -24,7 +24,7 @@ static ssize_t w1_f12_read_state( struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { u8 w1_buf[6] = {W1_F12_FUNC_READ_STATUS, 7, 0, 0, 0, 0}; @@ -61,7 +61,7 @@ static ssize_t w1_f12_read_state( static ssize_t w1_f12_write_output( struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -95,14 +95,14 @@ static ssize_t w1_f12_write_output( } #define NB_SYSFS_BIN_FILES 2 -static struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { +static const struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { { .attr = { .name = "state", .mode = 0444, }, .size = 1, - .read = w1_f12_read_state, + .read_new = w1_f12_read_state, }, { .attr = { @@ -110,7 +110,7 @@ static struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { .mode = 0664, }, .size = 1, - .write = w1_f12_write_output, + .write_new = w1_f12_write_output, } }; diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c index 56f822a1dfdb..beccd2912d2a 100644 --- a/drivers/w1/slaves/w1_ds2408.c +++ b/drivers/w1/slaves/w1_ds2408.c @@ -65,8 +65,8 @@ static int _read_reg(struct w1_slave *sl, u8 address, unsigned char *buf) } static ssize_t state_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { dev_dbg(&kobj_to_w1_slave(kobj)->dev, "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", @@ -77,7 +77,7 @@ static ssize_t state_read(struct file *filp, struct kobject *kobj, } static ssize_t output_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { dev_dbg(&kobj_to_w1_slave(kobj)->dev, @@ -90,7 +90,7 @@ static ssize_t output_read(struct file *filp, struct kobject *kobj, } static ssize_t activity_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { dev_dbg(&kobj_to_w1_slave(kobj)->dev, @@ -103,8 +103,8 @@ static ssize_t activity_read(struct file *filp, struct kobject *kobj, } static ssize_t cond_search_mask_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, - loff_t off, size_t count) + const struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { dev_dbg(&kobj_to_w1_slave(kobj)->dev, "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", @@ -117,7 +117,7 @@ static ssize_t cond_search_mask_read(struct file *filp, struct kobject *kobj, static ssize_t cond_search_polarity_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { if (count != 1 || off != 0) @@ -127,8 +127,8 @@ static ssize_t cond_search_polarity_read(struct file *filp, } static ssize_t status_control_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, - loff_t off, size_t count) + const struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { if (count != 1 || off != 0) return -EFAULT; @@ -160,7 +160,7 @@ static bool optional_read_back_valid(struct w1_slave *sl, u8 expected) #endif static ssize_t output_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -210,7 +210,7 @@ out: * Writing to the activity file resets the activity latches. */ static ssize_t activity_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -240,8 +240,8 @@ error: } static ssize_t status_control_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, - loff_t off, size_t count) + const struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); u8 w1_buf[4]; @@ -310,14 +310,14 @@ out: return res; } -static BIN_ATTR_RO(state, 1); -static BIN_ATTR_RW(output, 1); -static BIN_ATTR_RW(activity, 1); -static BIN_ATTR_RO(cond_search_mask, 1); -static BIN_ATTR_RO(cond_search_polarity, 1); -static BIN_ATTR_RW(status_control, 1); +static const BIN_ATTR_RO(state, 1); +static const BIN_ATTR_RW(output, 1); +static const BIN_ATTR_RW(activity, 1); +static const BIN_ATTR_RO(cond_search_mask, 1); +static const BIN_ATTR_RO(cond_search_polarity, 1); +static const BIN_ATTR_RW(status_control, 1); -static struct bin_attribute *w1_f29_bin_attrs[] = { +static const struct bin_attribute *const w1_f29_bin_attrs[] = { &bin_attr_state, &bin_attr_output, &bin_attr_activity, @@ -328,7 +328,7 @@ static struct bin_attribute *w1_f29_bin_attrs[] = { }; static const struct attribute_group w1_f29_group = { - .bin_attrs = w1_f29_bin_attrs, + .bin_attrs_new = w1_f29_bin_attrs, }; static const struct attribute_group *w1_f29_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c index 739009806467..5fa46017ca7c 100644 --- a/drivers/w1/slaves/w1_ds2413.c +++ b/drivers/w1/slaves/w1_ds2413.c @@ -25,8 +25,8 @@ #define W1_F3A_INVALID_PIO_STATE 0xFF static ssize_t state_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); unsigned int retries = W1_F3A_RETRIES; @@ -78,10 +78,10 @@ out: return bytes_read; } -static BIN_ATTR_RO(state, 1); +static const BIN_ATTR_RO(state, 1); static ssize_t output_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -128,16 +128,16 @@ out: return bytes_written; } -static BIN_ATTR(output, 0664, NULL, output_write, 1); +static const BIN_ATTR(output, 0664, NULL, output_write, 1); -static struct bin_attribute *w1_f3a_bin_attrs[] = { +static const struct bin_attribute *const w1_f3a_bin_attrs[] = { &bin_attr_state, &bin_attr_output, NULL, }; static const struct attribute_group w1_f3a_group = { - .bin_attrs = w1_f3a_bin_attrs, + .bin_attrs_new = w1_f3a_bin_attrs, }; static const struct attribute_group *w1_f3a_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2430.c b/drivers/w1/slaves/w1_ds2430.c index 0ea7d779d17a..ff56e2e68e58 100644 --- a/drivers/w1/slaves/w1_ds2430.c +++ b/drivers/w1/slaves/w1_ds2430.c @@ -95,7 +95,7 @@ static int w1_f14_readblock(struct w1_slave *sl, int off, int count, char *buf) } static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -202,7 +202,7 @@ retry: } static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -263,15 +263,15 @@ out_up: return count; } -static BIN_ATTR_RW(eeprom, W1_F14_EEPROM_SIZE); +static const BIN_ATTR_RW(eeprom, W1_F14_EEPROM_SIZE); -static struct bin_attribute *w1_f14_bin_attrs[] = { +static const struct bin_attribute *const w1_f14_bin_attrs[] = { &bin_attr_eeprom, NULL, }; static const struct attribute_group w1_f14_group = { - .bin_attrs = w1_f14_bin_attrs, + .bin_attrs_new = w1_f14_bin_attrs, }; static const struct attribute_group *w1_f14_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c index 6856b1c29e17..27b390fb59da 100644 --- a/drivers/w1/slaves/w1_ds2431.c +++ b/drivers/w1/slaves/w1_ds2431.c @@ -95,7 +95,7 @@ static int w1_f2d_readblock(struct w1_slave *sl, int off, int count, char *buf) } static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -201,7 +201,7 @@ retry: } static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -262,15 +262,15 @@ out_up: return count; } -static BIN_ATTR_RW(eeprom, W1_F2D_EEPROM_SIZE); +static const BIN_ATTR_RW(eeprom, W1_F2D_EEPROM_SIZE); -static struct bin_attribute *w1_f2d_bin_attrs[] = { +static const struct bin_attribute *const w1_f2d_bin_attrs[] = { &bin_attr_eeprom, NULL, }; static const struct attribute_group w1_f2d_group = { - .bin_attrs = w1_f2d_bin_attrs, + .bin_attrs_new = w1_f2d_bin_attrs, }; static const struct attribute_group *w1_f2d_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 250b7f7ec429..22331d840ec1 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -110,7 +110,7 @@ static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data, #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -224,7 +224,7 @@ static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data) } static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -274,27 +274,27 @@ out_up: return count; } -static struct bin_attribute bin_attr_f23_eeprom = { +static const struct bin_attribute bin_attr_f23_eeprom = { .attr = { .name = "eeprom", .mode = 0644 }, - .read = eeprom_read, - .write = eeprom_write, + .read_new = eeprom_read, + .write_new = eeprom_write, .size = W1_EEPROM_DS2433_SIZE, }; -static struct bin_attribute bin_attr_f43_eeprom = { +static const struct bin_attribute bin_attr_f43_eeprom = { .attr = { .name = "eeprom", .mode = 0644 }, - .read = eeprom_read, - .write = eeprom_write, + .read_new = eeprom_read, + .write_new = eeprom_write, .size = W1_EEPROM_DS28EC20_SIZE, }; -static struct bin_attribute *w1_f23_bin_attributes[] = { +static const struct bin_attribute *const w1_f23_bin_attributes[] = { &bin_attr_f23_eeprom, NULL, }; static const struct attribute_group w1_f23_group = { - .bin_attrs = w1_f23_bin_attributes, + .bin_attrs_new = w1_f23_bin_attributes, }; static const struct attribute_group *w1_f23_groups[] = { @@ -302,13 +302,13 @@ static const struct attribute_group *w1_f23_groups[] = { NULL, }; -static struct bin_attribute *w1_f43_bin_attributes[] = { +static const struct bin_attribute *const w1_f43_bin_attributes[] = { &bin_attr_f43_eeprom, NULL, }; static const struct attribute_group w1_f43_group = { - .bin_attrs = w1_f43_bin_attributes, + .bin_attrs_new = w1_f43_bin_attributes, }; static const struct attribute_group *w1_f43_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2438.c b/drivers/w1/slaves/w1_ds2438.c index e008c27b3db9..630a6db5045e 100644 --- a/drivers/w1/slaves/w1_ds2438.c +++ b/drivers/w1/slaves/w1_ds2438.c @@ -288,7 +288,7 @@ static int w1_ds2438_get_current(struct w1_slave *sl, int16_t *voltage) } static ssize_t iad_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -310,7 +310,7 @@ static ssize_t iad_write(struct file *filp, struct kobject *kobj, } static ssize_t iad_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -331,7 +331,7 @@ static ssize_t iad_read(struct file *filp, struct kobject *kobj, } static ssize_t page0_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -361,7 +361,7 @@ static ssize_t page0_read(struct file *filp, struct kobject *kobj, } static ssize_t page1_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -391,7 +391,7 @@ static ssize_t page1_read(struct file *filp, struct kobject *kobj, } static ssize_t offset_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -410,7 +410,7 @@ static ssize_t offset_write(struct file *filp, struct kobject *kobj, } static ssize_t temperature_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -431,7 +431,7 @@ static ssize_t temperature_read(struct file *filp, struct kobject *kobj, } static ssize_t vad_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -452,7 +452,7 @@ static ssize_t vad_read(struct file *filp, struct kobject *kobj, } static ssize_t vdd_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -472,15 +472,15 @@ static ssize_t vdd_read(struct file *filp, struct kobject *kobj, return ret; } -static BIN_ATTR_RW(iad, 0); -static BIN_ATTR_RO(page0, DS2438_PAGE_SIZE); -static BIN_ATTR_RO(page1, DS2438_PAGE_SIZE); -static BIN_ATTR_WO(offset, 2); -static BIN_ATTR_RO(temperature, 0/* real length varies */); -static BIN_ATTR_RO(vad, 0/* real length varies */); -static BIN_ATTR_RO(vdd, 0/* real length varies */); +static const BIN_ATTR_RW(iad, 0); +static const BIN_ATTR_RO(page0, DS2438_PAGE_SIZE); +static const BIN_ATTR_RO(page1, DS2438_PAGE_SIZE); +static const BIN_ATTR_WO(offset, 2); +static const BIN_ATTR_RO(temperature, 0/* real length varies */); +static const BIN_ATTR_RO(vad, 0/* real length varies */); +static const BIN_ATTR_RO(vdd, 0/* real length varies */); -static struct bin_attribute *w1_ds2438_bin_attrs[] = { +static const struct bin_attribute *const w1_ds2438_bin_attrs[] = { &bin_attr_iad, &bin_attr_page0, &bin_attr_page1, @@ -492,7 +492,7 @@ static struct bin_attribute *w1_ds2438_bin_attrs[] = { }; static const struct attribute_group w1_ds2438_group = { - .bin_attrs = w1_ds2438_bin_attrs, + .bin_attrs_new = w1_ds2438_bin_attrs, }; static const struct attribute_group *w1_ds2438_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c index 3cde1bb1886b..ba7beb7b01f9 100644 --- a/drivers/w1/slaves/w1_ds2780.c +++ b/drivers/w1/slaves/w1_ds2780.c @@ -87,7 +87,7 @@ int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd) EXPORT_SYMBOL(w1_ds2780_eeprom_cmd); static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -95,15 +95,15 @@ static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, return w1_ds2780_io(dev, buf, off, count, 0); } -static BIN_ATTR_RO(w1_slave, DS2780_DATA_SIZE); +static const BIN_ATTR_RO(w1_slave, DS2780_DATA_SIZE); -static struct bin_attribute *w1_ds2780_bin_attrs[] = { +static const struct bin_attribute *const w1_ds2780_bin_attrs[] = { &bin_attr_w1_slave, NULL, }; static const struct attribute_group w1_ds2780_group = { - .bin_attrs = w1_ds2780_bin_attrs, + .bin_attrs_new = w1_ds2780_bin_attrs, }; static const struct attribute_group *w1_ds2780_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2781.c b/drivers/w1/slaves/w1_ds2781.c index e418484b4a49..acd04ee96e81 100644 --- a/drivers/w1/slaves/w1_ds2781.c +++ b/drivers/w1/slaves/w1_ds2781.c @@ -84,7 +84,7 @@ int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd) EXPORT_SYMBOL(w1_ds2781_eeprom_cmd); static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -92,15 +92,15 @@ static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj, return w1_ds2781_io(dev, buf, off, count, 0); } -static BIN_ATTR_RO(w1_slave, DS2781_DATA_SIZE); +static const BIN_ATTR_RO(w1_slave, DS2781_DATA_SIZE); -static struct bin_attribute *w1_ds2781_bin_attrs[] = { +static const struct bin_attribute *const w1_ds2781_bin_attrs[] = { &bin_attr_w1_slave, NULL, }; static const struct attribute_group w1_ds2781_group = { - .bin_attrs = w1_ds2781_bin_attrs, + .bin_attrs_new = w1_ds2781_bin_attrs, }; static const struct attribute_group *w1_ds2781_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2805.c b/drivers/w1/slaves/w1_ds2805.c index 4c1a2c515317..6ee895640d4a 100644 --- a/drivers/w1/slaves/w1_ds2805.c +++ b/drivers/w1/slaves/w1_ds2805.c @@ -92,7 +92,7 @@ static int w1_f0d_readblock(struct w1_slave *sl, int off, int count, char *buf) } static ssize_t w1_f0d_read_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -200,7 +200,7 @@ retry: } static ssize_t w1_f0d_write_bin(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -261,14 +261,14 @@ out_up: return count; } -static struct bin_attribute w1_f0d_bin_attr = { +static const struct bin_attribute w1_f0d_bin_attr = { .attr = { .name = "eeprom", .mode = 0644, }, .size = W1_F0D_EEPROM_SIZE, - .read = w1_f0d_read_bin, - .write = w1_f0d_write_bin, + .read_new = w1_f0d_read_bin, + .write_new = w1_f0d_write_bin, }; static int w1_f0d_add_slave(struct w1_slave *sl) diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c index 2854b8b9e93f..d99ffadbe29b 100644 --- a/drivers/w1/slaves/w1_ds28e04.c +++ b/drivers/w1/slaves/w1_ds28e04.c @@ -112,7 +112,7 @@ static int w1_f1C_read(struct w1_slave *sl, int addr, int len, char *data) } static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -223,7 +223,7 @@ static int w1_f1C_write(struct w1_slave *sl, int addr, int len, const u8 *data) } static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -276,10 +276,10 @@ out_up: return count; } -static BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE); +static const BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE); static ssize_t pio_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, loff_t off, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -298,8 +298,8 @@ static ssize_t pio_read(struct file *filp, struct kobject *kobj, } static ssize_t pio_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -337,7 +337,7 @@ static ssize_t pio_write(struct file *filp, struct kobject *kobj, return count; } -static BIN_ATTR_RW(pio, 1); +static const BIN_ATTR_RW(pio, 1); static ssize_t crccheck_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -363,7 +363,7 @@ static struct attribute *w1_f1C_attrs[] = { NULL, }; -static struct bin_attribute *w1_f1C_bin_attrs[] = { +static const struct bin_attribute *const w1_f1C_bin_attrs[] = { &bin_attr_eeprom, &bin_attr_pio, NULL, @@ -371,7 +371,7 @@ static struct bin_attribute *w1_f1C_bin_attrs[] = { static const struct attribute_group w1_f1C_group = { .attrs = w1_f1C_attrs, - .bin_attrs = w1_f1C_bin_attrs, + .bin_attrs_new = w1_f1C_bin_attrs, }; static const struct attribute_group *w1_f1C_groups[] = { diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c index 52261b54d842..5738cbce1a37 100644 --- a/drivers/w1/slaves/w1_ds28e17.c +++ b/drivers/w1/slaves/w1_ds28e17.c @@ -583,7 +583,7 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr, return result; /* Return current speed value. */ - return sprintf(buf, "%d\n", result); + return sysfs_emit(buf, "%d\n", result); } static ssize_t speed_store(struct device *dev, struct device_attribute *attr, @@ -633,7 +633,7 @@ static ssize_t stretch_show(struct device *dev, struct device_attribute *attr, struct w1_f19_data *data = sl->family_data; /* Return current stretch value. */ - return sprintf(buf, "%d\n", data->stretch); + return sysfs_emit(buf, "%d\n", data->stretch); } static ssize_t stretch_store(struct device *dev, struct device_attribute *attr, diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index d82e86d3ddf6..29f200bbab41 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -111,7 +111,7 @@ ATTRIBUTE_GROUPS(w1_slave); /* Default family */ static ssize_t rw_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, loff_t off, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -130,8 +130,8 @@ out_up: } static ssize_t rw_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -141,15 +141,15 @@ static ssize_t rw_read(struct file *filp, struct kobject *kobj, return count; } -static BIN_ATTR_RW(rw, PAGE_SIZE); +static const BIN_ATTR_RW(rw, PAGE_SIZE); -static struct bin_attribute *w1_slave_bin_attrs[] = { +static const struct bin_attribute *const w1_slave_bin_attrs[] = { &bin_attr_rw, NULL, }; static const struct attribute_group w1_slave_default_group = { - .bin_attrs = w1_slave_bin_attrs, + .bin_attrs_new = w1_slave_bin_attrs, }; static const struct attribute_group *w1_slave_default_groups[] = { |
