diff options
Diffstat (limited to 'drivers')
1782 files changed, 59522 insertions, 17084 deletions
diff --git a/drivers/accel/ivpu/ivpu_debugfs.c b/drivers/accel/ivpu/ivpu_debugfs.c index 19035230563d..7cb962e21453 100644 --- a/drivers/accel/ivpu/ivpu_debugfs.c +++ b/drivers/accel/ivpu/ivpu_debugfs.c @@ -102,7 +102,7 @@ static int reset_pending_show(struct seq_file *s, void *v) { struct ivpu_device *vdev = seq_to_ivpu(s); - seq_printf(s, "%d\n", atomic_read(&vdev->pm->in_reset)); + seq_printf(s, "%d\n", atomic_read(&vdev->pm->reset_pending)); return 0; } @@ -130,7 +130,9 @@ dvfs_mode_fops_write(struct file *file, const char __user *user_buf, size_t size fw->dvfs_mode = dvfs_mode; - ivpu_pm_schedule_recovery(vdev); + ret = pci_try_reset_function(to_pci_dev(vdev->drm.dev)); + if (ret) + return ret; return size; } @@ -190,7 +192,10 @@ fw_profiling_freq_fops_write(struct file *file, const char __user *user_buf, return ret; ivpu_hw_profiling_freq_drive(vdev, enable); - ivpu_pm_schedule_recovery(vdev); + + ret = pci_try_reset_function(to_pci_dev(vdev->drm.dev)); + if (ret) + return ret; return size; } @@ -301,11 +306,18 @@ static ssize_t ivpu_force_recovery_fn(struct file *file, const char __user *user_buf, size_t size, loff_t *pos) { struct ivpu_device *vdev = file->private_data; + int ret; if (!size) return -EINVAL; - ivpu_pm_schedule_recovery(vdev); + ret = ivpu_rpm_get(vdev); + if (ret) + return ret; + + ivpu_pm_trigger_recovery(vdev, "debugfs"); + flush_work(&vdev->pm->recovery_work); + ivpu_rpm_put(vdev); return size; } diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index 64927682161b..4b0640226986 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -6,6 +6,7 @@ #include <linux/firmware.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/pm_runtime.h> #include <drm/drm_accel.h> #include <drm/drm_file.h> @@ -17,6 +18,7 @@ #include "ivpu_debugfs.h" #include "ivpu_drv.h" #include "ivpu_fw.h" +#include "ivpu_fw_log.h" #include "ivpu_gem.h" #include "ivpu_hw.h" #include "ivpu_ipc.h" @@ -65,22 +67,20 @@ struct ivpu_file_priv *ivpu_file_priv_get(struct ivpu_file_priv *file_priv) return file_priv; } -struct ivpu_file_priv *ivpu_file_priv_get_by_ctx_id(struct ivpu_device *vdev, unsigned long id) +static void file_priv_unbind(struct ivpu_device *vdev, struct ivpu_file_priv *file_priv) { - struct ivpu_file_priv *file_priv; - - xa_lock_irq(&vdev->context_xa); - file_priv = xa_load(&vdev->context_xa, id); - /* file_priv may still be in context_xa during file_priv_release() */ - if (file_priv && !kref_get_unless_zero(&file_priv->ref)) - file_priv = NULL; - xa_unlock_irq(&vdev->context_xa); - - if (file_priv) - ivpu_dbg(vdev, KREF, "file_priv get by id: ctx %u refcount %u\n", - file_priv->ctx.id, kref_read(&file_priv->ref)); - - return file_priv; + mutex_lock(&file_priv->lock); + if (file_priv->bound) { + ivpu_dbg(vdev, FILE, "file_priv unbind: ctx %u\n", file_priv->ctx.id); + + ivpu_cmdq_release_all_locked(file_priv); + ivpu_jsm_context_release(vdev, file_priv->ctx.id); + ivpu_bo_unbind_all_bos_from_context(vdev, &file_priv->ctx); + ivpu_mmu_user_context_fini(vdev, &file_priv->ctx); + file_priv->bound = false; + drm_WARN_ON(&vdev->drm, !xa_erase_irq(&vdev->context_xa, file_priv->ctx.id)); + } + mutex_unlock(&file_priv->lock); } static void file_priv_release(struct kref *ref) @@ -88,13 +88,15 @@ static void file_priv_release(struct kref *ref) struct ivpu_file_priv *file_priv = container_of(ref, struct ivpu_file_priv, ref); struct ivpu_device *vdev = file_priv->vdev; - ivpu_dbg(vdev, FILE, "file_priv release: ctx %u\n", file_priv->ctx.id); + ivpu_dbg(vdev, FILE, "file_priv release: ctx %u bound %d\n", + file_priv->ctx.id, (bool)file_priv->bound); + + pm_runtime_get_sync(vdev->drm.dev); + mutex_lock(&vdev->context_list_lock); + file_priv_unbind(vdev, file_priv); + mutex_unlock(&vdev->context_list_lock); + pm_runtime_put_autosuspend(vdev->drm.dev); - ivpu_cmdq_release_all(file_priv); - ivpu_jsm_context_release(vdev, file_priv->ctx.id); - ivpu_bo_remove_all_bos_from_context(vdev, &file_priv->ctx); - ivpu_mmu_user_context_fini(vdev, &file_priv->ctx); - drm_WARN_ON(&vdev->drm, xa_erase_irq(&vdev->context_xa, file_priv->ctx.id) != file_priv); mutex_destroy(&file_priv->lock); kfree(file_priv); } @@ -176,9 +178,6 @@ static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_f case DRM_IVPU_PARAM_CONTEXT_BASE_ADDRESS: args->value = vdev->hw->ranges.user.start; break; - case DRM_IVPU_PARAM_CONTEXT_PRIORITY: - args->value = file_priv->priority; - break; case DRM_IVPU_PARAM_CONTEXT_ID: args->value = file_priv->ctx.id; break; @@ -218,17 +217,10 @@ static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_f static int ivpu_set_param_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { - struct ivpu_file_priv *file_priv = file->driver_priv; struct drm_ivpu_param *args = data; int ret = 0; switch (args->param) { - case DRM_IVPU_PARAM_CONTEXT_PRIORITY: - if (args->value <= DRM_IVPU_CONTEXT_PRIORITY_REALTIME) - file_priv->priority = args->value; - else - ret = -EINVAL; - break; default: ret = -EINVAL; } @@ -241,50 +233,53 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file) struct ivpu_device *vdev = to_ivpu_device(dev); struct ivpu_file_priv *file_priv; u32 ctx_id; - void *old; - int ret; + int idx, ret; - ret = xa_alloc_irq(&vdev->context_xa, &ctx_id, NULL, vdev->context_xa_limit, GFP_KERNEL); - if (ret) { - ivpu_err(vdev, "Failed to allocate context id: %d\n", ret); - return ret; - } + if (!drm_dev_enter(dev, &idx)) + return -ENODEV; file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); if (!file_priv) { ret = -ENOMEM; - goto err_xa_erase; + goto err_dev_exit; } file_priv->vdev = vdev; - file_priv->priority = DRM_IVPU_CONTEXT_PRIORITY_NORMAL; + file_priv->bound = true; kref_init(&file_priv->ref); mutex_init(&file_priv->lock); + mutex_lock(&vdev->context_list_lock); + + ret = xa_alloc_irq(&vdev->context_xa, &ctx_id, file_priv, + vdev->context_xa_limit, GFP_KERNEL); + if (ret) { + ivpu_err(vdev, "Failed to allocate context id: %d\n", ret); + goto err_unlock; + } + ret = ivpu_mmu_user_context_init(vdev, &file_priv->ctx, ctx_id); if (ret) - goto err_mutex_destroy; + goto err_xa_erase; - old = xa_store_irq(&vdev->context_xa, ctx_id, file_priv, GFP_KERNEL); - if (xa_is_err(old)) { - ret = xa_err(old); - ivpu_err(vdev, "Failed to store context %u: %d\n", ctx_id, ret); - goto err_ctx_fini; - } + mutex_unlock(&vdev->context_list_lock); + drm_dev_exit(idx); + + file->driver_priv = file_priv; ivpu_dbg(vdev, FILE, "file_priv create: ctx %u process %s pid %d\n", ctx_id, current->comm, task_pid_nr(current)); - file->driver_priv = file_priv; return 0; -err_ctx_fini: - ivpu_mmu_user_context_fini(vdev, &file_priv->ctx); -err_mutex_destroy: - mutex_destroy(&file_priv->lock); - kfree(file_priv); err_xa_erase: xa_erase_irq(&vdev->context_xa, ctx_id); +err_unlock: + mutex_unlock(&vdev->context_list_lock); + mutex_destroy(&file_priv->lock); + kfree(file_priv); +err_dev_exit: + drm_dev_exit(idx); return ret; } @@ -340,8 +335,6 @@ static int ivpu_wait_for_ready(struct ivpu_device *vdev) if (!ret) ivpu_dbg(vdev, PM, "VPU ready message received successfully\n"); - else - ivpu_hw_diagnose_failure(vdev); return ret; } @@ -369,6 +362,9 @@ int ivpu_boot(struct ivpu_device *vdev) ret = ivpu_wait_for_ready(vdev); if (ret) { ivpu_err(vdev, "Failed to boot the firmware: %d\n", ret); + ivpu_hw_diagnose_failure(vdev); + ivpu_mmu_evtq_dump(vdev); + ivpu_fw_log_dump(vdev); return ret; } @@ -484,9 +480,8 @@ static int ivpu_pci_init(struct ivpu_device *vdev) /* Clear any pending errors */ pcie_capability_clear_word(pdev, PCI_EXP_DEVSTA, 0x3f); - /* VPU 37XX does not require 10m D3hot delay */ - if (ivpu_hw_gen(vdev) == IVPU_HW_37XX) - pdev->d3hot_delay = 0; + /* NPU does not require 10m D3hot delay */ + pdev->d3hot_delay = 0; ret = pcim_enable_device(pdev); if (ret) { @@ -540,6 +535,10 @@ static int ivpu_dev_init(struct ivpu_device *vdev) lockdep_set_class(&vdev->submitted_jobs_xa.xa_lock, &submitted_jobs_xa_lock_class_key); INIT_LIST_HEAD(&vdev->bo_list); + ret = drmm_mutex_init(&vdev->drm, &vdev->context_list_lock); + if (ret) + goto err_xa_destroy; + ret = drmm_mutex_init(&vdev->drm, &vdev->bo_list_lock); if (ret) goto err_xa_destroy; @@ -611,14 +610,30 @@ err_xa_destroy: return ret; } +static void ivpu_bo_unbind_all_user_contexts(struct ivpu_device *vdev) +{ + struct ivpu_file_priv *file_priv; + unsigned long ctx_id; + + mutex_lock(&vdev->context_list_lock); + + xa_for_each(&vdev->context_xa, ctx_id, file_priv) + file_priv_unbind(vdev, file_priv); + + mutex_unlock(&vdev->context_list_lock); +} + static void ivpu_dev_fini(struct ivpu_device *vdev) { ivpu_pm_disable(vdev); ivpu_shutdown(vdev); if (IVPU_WA(d3hot_after_power_off)) pci_set_power_state(to_pci_dev(vdev->drm.dev), PCI_D3hot); + + ivpu_jobs_abort_all(vdev); ivpu_job_done_consumer_fini(vdev); ivpu_pm_cancel_recovery(vdev); + ivpu_bo_unbind_all_user_contexts(vdev); ivpu_ipc_fini(vdev); ivpu_fw_fini(vdev); diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index ebc4b84f27b2..069ace4adb2d 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -56,6 +56,7 @@ #define IVPU_DBG_JSM BIT(10) #define IVPU_DBG_KREF BIT(11) #define IVPU_DBG_RPM BIT(12) +#define IVPU_DBG_MMU_MAP BIT(13) #define ivpu_err(vdev, fmt, ...) \ drm_err(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__) @@ -114,6 +115,7 @@ struct ivpu_device { struct ivpu_mmu_context gctx; struct ivpu_mmu_context rctx; + struct mutex context_list_lock; /* Protects user context addition/removal */ struct xarray context_xa; struct xa_limit context_xa_limit; @@ -145,8 +147,8 @@ struct ivpu_file_priv { struct mutex lock; /* Protects cmdq */ struct ivpu_cmdq *cmdq[IVPU_NUM_ENGINES]; struct ivpu_mmu_context ctx; - u32 priority; bool has_mmu_faults; + bool bound; }; extern int ivpu_dbg_mask; @@ -162,7 +164,6 @@ extern bool ivpu_disable_mmu_cont_pages; extern int ivpu_test_mode; struct ivpu_file_priv *ivpu_file_priv_get(struct ivpu_file_priv *file_priv); -struct ivpu_file_priv *ivpu_file_priv_get_by_ctx_id(struct ivpu_device *vdev, unsigned long id); void ivpu_file_priv_put(struct ivpu_file_priv **link); int ivpu_boot(struct ivpu_device *vdev); diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c index 6576232f3e67..5fa8bd4603d5 100644 --- a/drivers/accel/ivpu/ivpu_fw.c +++ b/drivers/accel/ivpu/ivpu_fw.c @@ -222,7 +222,6 @@ ivpu_fw_init_wa(struct ivpu_device *vdev) const struct vpu_firmware_header *fw_hdr = (const void *)vdev->fw->file->data; if (IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, BOOT, 3, 17) || - (ivpu_hw_gen(vdev) > IVPU_HW_37XX) || (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_DISABLE)) vdev->wa.disable_d0i3_msg = true; diff --git a/drivers/accel/ivpu/ivpu_gem.c b/drivers/accel/ivpu/ivpu_gem.c index 1dda4f38ea25..e9ddbe9f50eb 100644 --- a/drivers/accel/ivpu/ivpu_gem.c +++ b/drivers/accel/ivpu/ivpu_gem.c @@ -24,14 +24,11 @@ static const struct drm_gem_object_funcs ivpu_gem_funcs; static inline void ivpu_dbg_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, const char *action) { - if (bo->ctx) - ivpu_dbg(vdev, BO, "%6s: size %zu has_pages %d dma_mapped %d handle %u ctx %d vpu_addr 0x%llx mmu_mapped %d\n", - action, ivpu_bo_size(bo), (bool)bo->base.pages, (bool)bo->base.sgt, - bo->handle, bo->ctx->id, bo->vpu_addr, bo->mmu_mapped); - else - ivpu_dbg(vdev, BO, "%6s: size %zu has_pages %d dma_mapped %d handle %u (not added to context)\n", - action, ivpu_bo_size(bo), (bool)bo->base.pages, (bool)bo->base.sgt, - bo->handle); + ivpu_dbg(vdev, BO, + "%6s: bo %8p vpu_addr %9llx size %8zu ctx %d has_pages %d dma_mapped %d mmu_mapped %d wc %d imported %d\n", + action, bo, bo->vpu_addr, ivpu_bo_size(bo), bo->ctx ? bo->ctx->id : 0, + (bool)bo->base.pages, (bool)bo->base.sgt, bo->mmu_mapped, bo->base.map_wc, + (bool)bo->base.base.import_attach); } /* @@ -49,12 +46,7 @@ int __must_check ivpu_bo_pin(struct ivpu_bo *bo) mutex_lock(&bo->lock); ivpu_dbg_bo(vdev, bo, "pin"); - - if (!bo->ctx) { - ivpu_err(vdev, "vpu_addr not allocated for BO %d\n", bo->handle); - ret = -EINVAL; - goto unlock; - } + drm_WARN_ON(&vdev->drm, !bo->ctx); if (!bo->mmu_mapped) { struct sg_table *sgt = drm_gem_shmem_get_pages_sgt(&bo->base); @@ -85,7 +77,10 @@ ivpu_bo_alloc_vpu_addr(struct ivpu_bo *bo, struct ivpu_mmu_context *ctx, const struct ivpu_addr_range *range) { struct ivpu_device *vdev = ivpu_bo_to_vdev(bo); - int ret; + int idx, ret; + + if (!drm_dev_enter(&vdev->drm, &idx)) + return -ENODEV; mutex_lock(&bo->lock); @@ -101,6 +96,8 @@ ivpu_bo_alloc_vpu_addr(struct ivpu_bo *bo, struct ivpu_mmu_context *ctx, mutex_unlock(&bo->lock); + drm_dev_exit(idx); + return ret; } @@ -108,11 +105,7 @@ static void ivpu_bo_unbind_locked(struct ivpu_bo *bo) { struct ivpu_device *vdev = ivpu_bo_to_vdev(bo); - lockdep_assert_held(&bo->lock); - - ivpu_dbg_bo(vdev, bo, "unbind"); - - /* TODO: dma_unmap */ + lockdep_assert(lockdep_is_held(&bo->lock) || !kref_read(&bo->base.base.refcount)); if (bo->mmu_mapped) { drm_WARN_ON(&vdev->drm, !bo->ctx); @@ -124,19 +117,23 @@ static void ivpu_bo_unbind_locked(struct ivpu_bo *bo) if (bo->ctx) { ivpu_mmu_context_remove_node(bo->ctx, &bo->mm_node); - bo->vpu_addr = 0; bo->ctx = NULL; } -} -static void ivpu_bo_unbind(struct ivpu_bo *bo) -{ - mutex_lock(&bo->lock); - ivpu_bo_unbind_locked(bo); - mutex_unlock(&bo->lock); + if (bo->base.base.import_attach) + return; + + dma_resv_lock(bo->base.base.resv, NULL); + if (bo->base.sgt) { + dma_unmap_sgtable(vdev->drm.dev, bo->base.sgt, DMA_BIDIRECTIONAL, 0); + sg_free_table(bo->base.sgt); + kfree(bo->base.sgt); + bo->base.sgt = NULL; + } + dma_resv_unlock(bo->base.base.resv); } -void ivpu_bo_remove_all_bos_from_context(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) +void ivpu_bo_unbind_all_bos_from_context(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) { struct ivpu_bo *bo; @@ -146,8 +143,10 @@ void ivpu_bo_remove_all_bos_from_context(struct ivpu_device *vdev, struct ivpu_m mutex_lock(&vdev->bo_list_lock); list_for_each_entry(bo, &vdev->bo_list, bo_list_node) { mutex_lock(&bo->lock); - if (bo->ctx == ctx) + if (bo->ctx == ctx) { + ivpu_dbg_bo(vdev, bo, "unbind"); ivpu_bo_unbind_locked(bo); + } mutex_unlock(&bo->lock); } mutex_unlock(&vdev->bo_list_lock); @@ -199,9 +198,6 @@ ivpu_bo_create(struct ivpu_device *vdev, u64 size, u32 flags) list_add_tail(&bo->bo_list_node, &vdev->bo_list); mutex_unlock(&vdev->bo_list_lock); - ivpu_dbg(vdev, BO, "create: vpu_addr 0x%llx size %zu flags 0x%x\n", - bo->vpu_addr, bo->base.base.size, flags); - return bo; } @@ -212,6 +208,12 @@ static int ivpu_bo_open(struct drm_gem_object *obj, struct drm_file *file) struct ivpu_bo *bo = to_ivpu_bo(obj); struct ivpu_addr_range *range; + if (bo->ctx) { + ivpu_warn(vdev, "Can't add BO to ctx %u: already in ctx %u\n", + file_priv->ctx.id, bo->ctx->id); + return -EALREADY; + } + if (bo->flags & DRM_IVPU_BO_SHAVE_MEM) range = &vdev->hw->ranges.shave; else if (bo->flags & DRM_IVPU_BO_DMA_MEM) @@ -227,62 +229,24 @@ static void ivpu_bo_free(struct drm_gem_object *obj) struct ivpu_device *vdev = to_ivpu_device(obj->dev); struct ivpu_bo *bo = to_ivpu_bo(obj); + ivpu_dbg_bo(vdev, bo, "free"); + mutex_lock(&vdev->bo_list_lock); list_del(&bo->bo_list_node); mutex_unlock(&vdev->bo_list_lock); drm_WARN_ON(&vdev->drm, !dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ)); - ivpu_dbg_bo(vdev, bo, "free"); - - ivpu_bo_unbind(bo); + ivpu_bo_unbind_locked(bo); mutex_destroy(&bo->lock); drm_WARN_ON(obj->dev, bo->base.pages_use_count > 1); drm_gem_shmem_free(&bo->base); } -static const struct dma_buf_ops ivpu_bo_dmabuf_ops = { - .cache_sgt_mapping = true, - .attach = drm_gem_map_attach, - .detach = drm_gem_map_detach, - .map_dma_buf = drm_gem_map_dma_buf, - .unmap_dma_buf = drm_gem_unmap_dma_buf, - .release = drm_gem_dmabuf_release, - .mmap = drm_gem_dmabuf_mmap, - .vmap = drm_gem_dmabuf_vmap, - .vunmap = drm_gem_dmabuf_vunmap, -}; - -static struct dma_buf *ivpu_bo_export(struct drm_gem_object *obj, int flags) -{ - struct drm_device *dev = obj->dev; - struct dma_buf_export_info exp_info = { - .exp_name = KBUILD_MODNAME, - .owner = dev->driver->fops->owner, - .ops = &ivpu_bo_dmabuf_ops, - .size = obj->size, - .flags = flags, - .priv = obj, - .resv = obj->resv, - }; - void *sgt; - - /* - * Make sure that pages are allocated and dma-mapped before exporting the bo. - * DMA-mapping is required if the bo will be imported to the same device. - */ - sgt = drm_gem_shmem_get_pages_sgt(to_drm_gem_shmem_obj(obj)); - if (IS_ERR(sgt)) - return sgt; - - return drm_gem_dmabuf_export(dev, &exp_info); -} - static const struct drm_gem_object_funcs ivpu_gem_funcs = { .free = ivpu_bo_free, .open = ivpu_bo_open, - .export = ivpu_bo_export, .print_info = drm_gem_shmem_object_print_info, .pin = drm_gem_shmem_object_pin, .unpin = drm_gem_shmem_object_unpin, @@ -315,11 +279,9 @@ int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *fi return PTR_ERR(bo); } - ret = drm_gem_handle_create(file, &bo->base.base, &bo->handle); - if (!ret) { + ret = drm_gem_handle_create(file, &bo->base.base, &args->handle); + if (!ret) args->vpu_addr = bo->vpu_addr; - args->handle = bo->handle; - } drm_gem_object_put(&bo->base.base); @@ -361,7 +323,9 @@ ivpu_bo_alloc_internal(struct ivpu_device *vdev, u64 vpu_addr, u64 size, u32 fla if (ret) goto err_put; + dma_resv_lock(bo->base.base.resv, NULL); ret = drm_gem_shmem_vmap(&bo->base, &map); + dma_resv_unlock(bo->base.base.resv); if (ret) goto err_put; @@ -376,7 +340,10 @@ void ivpu_bo_free_internal(struct ivpu_bo *bo) { struct iosys_map map = IOSYS_MAP_INIT_VADDR(bo->base.vaddr); + dma_resv_lock(bo->base.base.resv, NULL); drm_gem_shmem_vunmap(&bo->base, &map); + dma_resv_unlock(bo->base.base.resv); + drm_gem_object_put(&bo->base.base); } @@ -432,19 +399,11 @@ int ivpu_bo_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file static void ivpu_bo_print_info(struct ivpu_bo *bo, struct drm_printer *p) { - unsigned long dma_refcount = 0; - mutex_lock(&bo->lock); - if (bo->base.base.dma_buf && bo->base.base.dma_buf->file) - dma_refcount = atomic_long_read(&bo->base.base.dma_buf->file->f_count); - - drm_printf(p, "%-3u %-6d 0x%-12llx %-10lu 0x%-8x %-4u %-8lu", - bo->ctx->id, bo->handle, bo->vpu_addr, bo->base.base.size, - bo->flags, kref_read(&bo->base.base.refcount), dma_refcount); - - if (bo->base.base.import_attach) - drm_printf(p, " imported"); + drm_printf(p, "%-9p %-3u 0x%-12llx %-10lu 0x%-8x %-4u", + bo, bo->ctx->id, bo->vpu_addr, bo->base.base.size, + bo->flags, kref_read(&bo->base.base.refcount)); if (bo->base.pages) drm_printf(p, " has_pages"); @@ -452,6 +411,9 @@ static void ivpu_bo_print_info(struct ivpu_bo *bo, struct drm_printer *p) if (bo->mmu_mapped) drm_printf(p, " mmu_mapped"); + if (bo->base.base.import_attach) + drm_printf(p, " imported"); + drm_printf(p, "\n"); mutex_unlock(&bo->lock); @@ -462,8 +424,8 @@ void ivpu_bo_list(struct drm_device *dev, struct drm_printer *p) struct ivpu_device *vdev = to_ivpu_device(dev); struct ivpu_bo *bo; - drm_printf(p, "%-3s %-6s %-14s %-10s %-10s %-4s %-8s %s\n", - "ctx", "handle", "vpu_addr", "size", "flags", "refs", "dma_refs", "attribs"); + drm_printf(p, "%-9s %-3s %-14s %-10s %-10s %-4s %s\n", + "bo", "ctx", "vpu_addr", "size", "flags", "refs", "attribs"); mutex_lock(&vdev->bo_list_lock); list_for_each_entry(bo, &vdev->bo_list, bo_list_node) diff --git a/drivers/accel/ivpu/ivpu_gem.h b/drivers/accel/ivpu/ivpu_gem.h index d75cad0d3c74..a8559211c70d 100644 --- a/drivers/accel/ivpu/ivpu_gem.h +++ b/drivers/accel/ivpu/ivpu_gem.h @@ -19,14 +19,13 @@ struct ivpu_bo { struct mutex lock; /* Protects: ctx, mmu_mapped, vpu_addr */ u64 vpu_addr; - u32 handle; u32 flags; u32 job_status; /* Valid only for command buffer */ bool mmu_mapped; }; int ivpu_bo_pin(struct ivpu_bo *bo); -void ivpu_bo_remove_all_bos_from_context(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx); +void ivpu_bo_unbind_all_bos_from_context(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx); struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t size); struct ivpu_bo *ivpu_bo_alloc_internal(struct ivpu_device *vdev, u64 vpu_addr, u64 size, u32 flags); diff --git a/drivers/accel/ivpu/ivpu_hw_37xx.c b/drivers/accel/ivpu/ivpu_hw_37xx.c index 574cdeefb66b..77accd029c4a 100644 --- a/drivers/accel/ivpu/ivpu_hw_37xx.c +++ b/drivers/accel/ivpu/ivpu_hw_37xx.c @@ -525,7 +525,7 @@ static void ivpu_boot_no_snoop_enable(struct ivpu_device *vdev) u32 val = REGV_RD32(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES); val = REG_SET_FLD(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES, NOSNOOP_OVERRIDE_EN, val); - val = REG_SET_FLD(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES, AW_NOSNOOP_OVERRIDE, val); + val = REG_CLR_FLD(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES, AW_NOSNOOP_OVERRIDE, val); val = REG_SET_FLD(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES, AR_NOSNOOP_OVERRIDE, val); REGV_WR32(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES, val); @@ -875,24 +875,18 @@ static void ivpu_hw_37xx_irq_disable(struct ivpu_device *vdev) static void ivpu_hw_37xx_irq_wdt_nce_handler(struct ivpu_device *vdev) { - ivpu_err_ratelimited(vdev, "WDT NCE irq\n"); - - ivpu_pm_schedule_recovery(vdev); + ivpu_pm_trigger_recovery(vdev, "WDT NCE IRQ"); } static void ivpu_hw_37xx_irq_wdt_mss_handler(struct ivpu_device *vdev) { - ivpu_err_ratelimited(vdev, "WDT MSS irq\n"); - ivpu_hw_wdt_disable(vdev); - ivpu_pm_schedule_recovery(vdev); + ivpu_pm_trigger_recovery(vdev, "WDT MSS IRQ"); } static void ivpu_hw_37xx_irq_noc_firewall_handler(struct ivpu_device *vdev) { - ivpu_err_ratelimited(vdev, "NOC Firewall irq\n"); - - ivpu_pm_schedule_recovery(vdev); + ivpu_pm_trigger_recovery(vdev, "NOC Firewall IRQ"); } /* Handler for IRQs from VPU core (irqV) */ @@ -970,7 +964,7 @@ static bool ivpu_hw_37xx_irqb_handler(struct ivpu_device *vdev, int irq) REGB_WR32(VPU_37XX_BUTTRESS_INTERRUPT_STAT, status); if (schedule_recovery) - ivpu_pm_schedule_recovery(vdev); + ivpu_pm_trigger_recovery(vdev, "Buttress IRQ"); return true; } diff --git a/drivers/accel/ivpu/ivpu_hw_40xx.c b/drivers/accel/ivpu/ivpu_hw_40xx.c index eba2fdef2ace..1c995307c113 100644 --- a/drivers/accel/ivpu/ivpu_hw_40xx.c +++ b/drivers/accel/ivpu/ivpu_hw_40xx.c @@ -530,7 +530,7 @@ static void ivpu_boot_no_snoop_enable(struct ivpu_device *vdev) u32 val = REGV_RD32(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES); val = REG_SET_FLD(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, SNOOP_OVERRIDE_EN, val); - val = REG_CLR_FLD(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, AW_SNOOP_OVERRIDE, val); + val = REG_SET_FLD(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, AW_SNOOP_OVERRIDE, val); val = REG_CLR_FLD(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, AR_SNOOP_OVERRIDE, val); REGV_WR32(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, val); @@ -704,7 +704,6 @@ static int ivpu_hw_40xx_info_init(struct ivpu_device *vdev) { struct ivpu_hw_info *hw = vdev->hw; u32 tile_disable; - u32 tile_enable; u32 fuse; fuse = REGB_RD32(VPU_40XX_BUTTRESS_TILE_FUSE); @@ -725,10 +724,6 @@ static int ivpu_hw_40xx_info_init(struct ivpu_device *vdev) else ivpu_dbg(vdev, MISC, "Fuse: All %d tiles enabled\n", TILE_MAX_NUM); - tile_enable = (~tile_disable) & TILE_MAX_MASK; - - hw->sku = REG_SET_FLD_NUM(SKU, HW_ID, LNL_HW_ID, hw->sku); - hw->sku = REG_SET_FLD_NUM(SKU, TILE, tile_enable, hw->sku); hw->tile_fuse = tile_disable; hw->pll.profiling_freq = PLL_PROFILING_FREQ_DEFAULT; @@ -746,7 +741,7 @@ static int ivpu_hw_40xx_info_init(struct ivpu_device *vdev) return 0; } -static int ivpu_hw_40xx_reset(struct ivpu_device *vdev) +static int ivpu_hw_40xx_ip_reset(struct ivpu_device *vdev) { int ret; u32 val; @@ -768,6 +763,23 @@ static int ivpu_hw_40xx_reset(struct ivpu_device *vdev) return ret; } +static int ivpu_hw_40xx_reset(struct ivpu_device *vdev) +{ + int ret = 0; + + if (ivpu_hw_40xx_ip_reset(vdev)) { + ivpu_err(vdev, "Failed to reset VPU IP\n"); + ret = -EIO; + } + + if (ivpu_pll_disable(vdev)) { + ivpu_err(vdev, "Failed to disable PLL\n"); + ret = -EIO; + } + + return ret; +} + static int ivpu_hw_40xx_d0i3_enable(struct ivpu_device *vdev) { int ret; @@ -913,7 +925,7 @@ static int ivpu_hw_40xx_power_down(struct ivpu_device *vdev) ivpu_hw_40xx_save_d0i3_entry_timestamp(vdev); - if (!ivpu_hw_40xx_is_idle(vdev) && ivpu_hw_40xx_reset(vdev)) + if (!ivpu_hw_40xx_is_idle(vdev) && ivpu_hw_40xx_ip_reset(vdev)) ivpu_warn(vdev, "Failed to reset the VPU\n"); if (ivpu_pll_disable(vdev)) { @@ -1032,18 +1044,18 @@ static void ivpu_hw_40xx_irq_disable(struct ivpu_device *vdev) static void ivpu_hw_40xx_irq_wdt_nce_handler(struct ivpu_device *vdev) { /* TODO: For LNN hang consider engine reset instead of full recovery */ - ivpu_pm_schedule_recovery(vdev); + ivpu_pm_trigger_recovery(vdev, "WDT NCE IRQ"); } static void ivpu_hw_40xx_irq_wdt_mss_handler(struct ivpu_device *vdev) { ivpu_hw_wdt_disable(vdev); - ivpu_pm_schedule_recovery(vdev); + ivpu_pm_trigger_recovery(vdev, "WDT MSS IRQ"); } static void ivpu_hw_40xx_irq_noc_firewall_handler(struct ivpu_device *vdev) { - ivpu_pm_schedule_recovery(vdev); + ivpu_pm_trigger_recovery(vdev, "NOC Firewall IRQ"); } /* Handler for IRQs from VPU core (irqV) */ @@ -1137,7 +1149,7 @@ static bool ivpu_hw_40xx_irqb_handler(struct ivpu_device *vdev, int irq) REGB_WR32(VPU_40XX_BUTTRESS_INTERRUPT_STAT, status); if (schedule_recovery) - ivpu_pm_schedule_recovery(vdev); + ivpu_pm_trigger_recovery(vdev, "Buttress IRQ"); return true; } diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c index e86621f16f85..fa66c39b57ec 100644 --- a/drivers/accel/ivpu/ivpu_ipc.c +++ b/drivers/accel/ivpu/ivpu_ipc.c @@ -343,10 +343,8 @@ int ivpu_ipc_send_receive_active(struct ivpu_device *vdev, struct vpu_jsm_msg *r hb_ret = ivpu_ipc_send_receive_internal(vdev, &hb_req, VPU_JSM_MSG_QUERY_ENGINE_HB_DONE, &hb_resp, VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm); - if (hb_ret == -ETIMEDOUT) { - ivpu_hw_diagnose_failure(vdev); - ivpu_pm_schedule_recovery(vdev); - } + if (hb_ret == -ETIMEDOUT) + ivpu_pm_trigger_recovery(vdev, "IPC timeout"); return ret; } diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index 7206cf9cdb4a..e70cfb859339 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -112,22 +112,20 @@ static void ivpu_cmdq_release_locked(struct ivpu_file_priv *file_priv, u16 engin } } -void ivpu_cmdq_release_all(struct ivpu_file_priv *file_priv) +void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv) { int i; - mutex_lock(&file_priv->lock); + lockdep_assert_held(&file_priv->lock); for (i = 0; i < IVPU_NUM_ENGINES; i++) ivpu_cmdq_release_locked(file_priv, i); - - mutex_unlock(&file_priv->lock); } /* * Mark the doorbell as unregistered and reset job queue pointers. * This function needs to be called when the VPU hardware is restarted - * and FW looses job queue state. The next time job queue is used it + * and FW loses job queue state. The next time job queue is used it * will be registered again. */ static void ivpu_cmdq_reset_locked(struct ivpu_file_priv *file_priv, u16 engine) @@ -161,15 +159,13 @@ void ivpu_cmdq_reset_all_contexts(struct ivpu_device *vdev) struct ivpu_file_priv *file_priv; unsigned long ctx_id; - xa_for_each(&vdev->context_xa, ctx_id, file_priv) { - file_priv = ivpu_file_priv_get_by_ctx_id(vdev, ctx_id); - if (!file_priv) - continue; + mutex_lock(&vdev->context_list_lock); + xa_for_each(&vdev->context_xa, ctx_id, file_priv) ivpu_cmdq_reset_all(file_priv); - ivpu_file_priv_put(&file_priv); - } + mutex_unlock(&vdev->context_list_lock); + } static int ivpu_cmdq_push_job(struct ivpu_cmdq *cmdq, struct ivpu_job *job) @@ -243,60 +239,32 @@ static struct dma_fence *ivpu_fence_create(struct ivpu_device *vdev) return &fence->base; } -static void job_get(struct ivpu_job *job, struct ivpu_job **link) +static void ivpu_job_destroy(struct ivpu_job *job) { struct ivpu_device *vdev = job->vdev; - - kref_get(&job->ref); - *link = job; - - ivpu_dbg(vdev, KREF, "Job get: id %u refcount %u\n", job->job_id, kref_read(&job->ref)); -} - -static void job_release(struct kref *ref) -{ - struct ivpu_job *job = container_of(ref, struct ivpu_job, ref); - struct ivpu_device *vdev = job->vdev; u32 i; + ivpu_dbg(vdev, JOB, "Job destroyed: id %3u ctx %2d engine %d", + job->job_id, job->file_priv->ctx.id, job->engine_idx); + for (i = 0; i < job->bo_count; i++) if (job->bos[i]) drm_gem_object_put(&job->bos[i]->base.base); dma_fence_put(job->done_fence); ivpu_file_priv_put(&job->file_priv); - - ivpu_dbg(vdev, KREF, "Job released: id %u\n", job->job_id); kfree(job); - - /* Allow the VPU to get suspended, must be called after ivpu_file_priv_put() */ - ivpu_rpm_put(vdev); -} - -static void job_put(struct ivpu_job *job) -{ - struct ivpu_device *vdev = job->vdev; - - ivpu_dbg(vdev, KREF, "Job put: id %u refcount %u\n", job->job_id, kref_read(&job->ref)); - kref_put(&job->ref, job_release); } static struct ivpu_job * -ivpu_create_job(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count) +ivpu_job_create(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count) { struct ivpu_device *vdev = file_priv->vdev; struct ivpu_job *job; - int ret; - - ret = ivpu_rpm_get(vdev); - if (ret < 0) - return NULL; job = kzalloc(struct_size(job, bos, bo_count), GFP_KERNEL); if (!job) - goto err_rpm_put; - - kref_init(&job->ref); + return NULL; job->vdev = vdev; job->engine_idx = engine_idx; @@ -310,17 +278,14 @@ ivpu_create_job(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count) job->file_priv = ivpu_file_priv_get(file_priv); ivpu_dbg(vdev, JOB, "Job created: ctx %2d engine %d", file_priv->ctx.id, job->engine_idx); - return job; err_free_job: kfree(job); -err_rpm_put: - ivpu_rpm_put(vdev); return NULL; } -static int ivpu_job_done(struct ivpu_device *vdev, u32 job_id, u32 job_status) +static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status) { struct ivpu_job *job; @@ -329,7 +294,7 @@ static int ivpu_job_done(struct ivpu_device *vdev, u32 job_id, u32 job_status) return -ENOENT; if (job->file_priv->has_mmu_faults) - job_status = VPU_JSM_STATUS_ABORTED; + job_status = DRM_IVPU_JOB_STATUS_ABORTED; job->bos[CMD_BUF_IDX]->job_status = job_status; dma_fence_signal(job->done_fence); @@ -337,9 +302,10 @@ static int ivpu_job_done(struct ivpu_device *vdev, u32 job_id, u32 job_status) ivpu_dbg(vdev, JOB, "Job complete: id %3u ctx %2d engine %d status 0x%x\n", job->job_id, job->file_priv->ctx.id, job->engine_idx, job_status); + ivpu_job_destroy(job); ivpu_stop_job_timeout_detection(vdev); - job_put(job); + ivpu_rpm_put(vdev); return 0; } @@ -349,10 +315,10 @@ void ivpu_jobs_abort_all(struct ivpu_device *vdev) unsigned long id; xa_for_each(&vdev->submitted_jobs_xa, id, job) - ivpu_job_done(vdev, id, VPU_JSM_STATUS_ABORTED); + ivpu_job_signal_and_destroy(vdev, id, DRM_IVPU_JOB_STATUS_ABORTED); } -static int ivpu_direct_job_submission(struct ivpu_job *job) +static int ivpu_job_submit(struct ivpu_job *job) { struct ivpu_file_priv *file_priv = job->file_priv; struct ivpu_device *vdev = job->vdev; @@ -360,53 +326,65 @@ static int ivpu_direct_job_submission(struct ivpu_job *job) struct ivpu_cmdq *cmdq; int ret; + ret = ivpu_rpm_get(vdev); + if (ret < 0) + return ret; + mutex_lock(&file_priv->lock); cmdq = ivpu_cmdq_acquire(job->file_priv, job->engine_idx); if (!cmdq) { - ivpu_warn(vdev, "Failed get job queue, ctx %d engine %d\n", - file_priv->ctx.id, job->engine_idx); + ivpu_warn_ratelimited(vdev, "Failed get job queue, ctx %d engine %d\n", + file_priv->ctx.id, job->engine_idx); ret = -EINVAL; - goto err_unlock; + goto err_unlock_file_priv; } job_id_range.min = FIELD_PREP(JOB_ID_CONTEXT_MASK, (file_priv->ctx.id - 1)); job_id_range.max = job_id_range.min | JOB_ID_JOB_MASK; - job_get(job, &job); - ret = xa_alloc(&vdev->submitted_jobs_xa, &job->job_id, job, job_id_range, GFP_KERNEL); + xa_lock(&vdev->submitted_jobs_xa); + ret = __xa_alloc(&vdev->submitted_jobs_xa, &job->job_id, job, job_id_range, GFP_KERNEL); if (ret) { - ivpu_warn_ratelimited(vdev, "Failed to allocate job id: %d\n", ret); - goto err_job_put; + ivpu_dbg(vdev, JOB, "Too many active jobs in ctx %d\n", + file_priv->ctx.id); + ret = -EBUSY; + goto err_unlock_submitted_jobs_xa; } ret = ivpu_cmdq_push_job(cmdq, job); if (ret) - goto err_xa_erase; + goto err_erase_xa; ivpu_start_job_timeout_detection(vdev); - ivpu_dbg(vdev, JOB, "Job submitted: id %3u addr 0x%llx ctx %2d engine %d next %d\n", - job->job_id, job->cmd_buf_vpu_addr, file_priv->ctx.id, - job->engine_idx, cmdq->jobq->header.tail); - - if (ivpu_test_mode & IVPU_TEST_MODE_NULL_HW) { - ivpu_job_done(vdev, job->job_id, VPU_JSM_STATUS_SUCCESS); + if (unlikely(ivpu_test_mode & IVPU_TEST_MODE_NULL_HW)) { cmdq->jobq->header.head = cmdq->jobq->header.tail; wmb(); /* Flush WC buffer for jobq header */ } else { ivpu_cmdq_ring_db(vdev, cmdq); } + ivpu_dbg(vdev, JOB, "Job submitted: id %3u ctx %2d engine %d addr 0x%llx next %d\n", + job->job_id, file_priv->ctx.id, job->engine_idx, + job->cmd_buf_vpu_addr, cmdq->jobq->header.tail); + + xa_unlock(&vdev->submitted_jobs_xa); + mutex_unlock(&file_priv->lock); + + if (unlikely(ivpu_test_mode & IVPU_TEST_MODE_NULL_HW)) + ivpu_job_signal_and_destroy(vdev, job->job_id, VPU_JSM_STATUS_SUCCESS); + return 0; -err_xa_erase: - xa_erase(&vdev->submitted_jobs_xa, job->job_id); -err_job_put: - job_put(job); -err_unlock: +err_erase_xa: + __xa_erase(&vdev->submitted_jobs_xa, job->job_id); +err_unlock_submitted_jobs_xa: + xa_unlock(&vdev->submitted_jobs_xa); +err_unlock_file_priv: mutex_unlock(&file_priv->lock); + ivpu_rpm_put(vdev); return ret; } @@ -488,6 +466,9 @@ int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file) if (params->engine > DRM_IVPU_ENGINE_COPY) return -EINVAL; + if (params->priority > DRM_IVPU_JOB_PRIORITY_REALTIME) + return -EINVAL; + if (params->buffer_count == 0 || params->buffer_count > JOB_MAX_BUFFER_COUNT) return -EINVAL; @@ -509,44 +490,49 @@ int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file) params->buffer_count * sizeof(u32)); if (ret) { ret = -EFAULT; - goto free_handles; + goto err_free_handles; } if (!drm_dev_enter(&vdev->drm, &idx)) { ret = -ENODEV; - goto free_handles; + goto err_free_handles; } ivpu_dbg(vdev, JOB, "Submit ioctl: ctx %u buf_count %u\n", file_priv->ctx.id, params->buffer_count); - job = ivpu_create_job(file_priv, params->engine, params->buffer_count); + job = ivpu_job_create(file_priv, params->engine, params->buffer_count); if (!job) { ivpu_err(vdev, "Failed to create job\n"); ret = -ENOMEM; - goto dev_exit; + goto err_exit_dev; } ret = ivpu_job_prepare_bos_for_submit(file, job, buf_handles, params->buffer_count, params->commands_offset); if (ret) { - ivpu_err(vdev, "Failed to prepare job, ret %d\n", ret); - goto job_put; + ivpu_err(vdev, "Failed to prepare job: %d\n", ret); + goto err_destroy_job; } - ret = ivpu_direct_job_submission(job); - if (ret) { - dma_fence_signal(job->done_fence); - ivpu_err(vdev, "Failed to submit job to the HW, ret %d\n", ret); - } + down_read(&vdev->pm->reset_lock); + ret = ivpu_job_submit(job); + up_read(&vdev->pm->reset_lock); + if (ret) + goto err_signal_fence; -job_put: - job_put(job); -dev_exit: drm_dev_exit(idx); -free_handles: kfree(buf_handles); + return ret; +err_signal_fence: + dma_fence_signal(job->done_fence); +err_destroy_job: + ivpu_job_destroy(job); +err_exit_dev: + drm_dev_exit(idx); +err_free_handles: + kfree(buf_handles); return ret; } @@ -568,7 +554,7 @@ ivpu_job_done_callback(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr, } payload = (struct vpu_ipc_msg_payload_job_done *)&jsm_msg->payload; - ret = ivpu_job_done(vdev, payload->job_id, payload->job_status); + ret = ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status); if (!ret && !xa_empty(&vdev->submitted_jobs_xa)) ivpu_start_job_timeout_detection(vdev); } diff --git a/drivers/accel/ivpu/ivpu_job.h b/drivers/accel/ivpu/ivpu_job.h index 45a2f2ec82e5..ca4984071cc7 100644 --- a/drivers/accel/ivpu/ivpu_job.h +++ b/drivers/accel/ivpu/ivpu_job.h @@ -43,7 +43,6 @@ struct ivpu_cmdq { will update the job status */ struct ivpu_job { - struct kref ref; struct ivpu_device *vdev; struct ivpu_file_priv *file_priv; struct dma_fence *done_fence; @@ -56,7 +55,7 @@ struct ivpu_job { int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file); -void ivpu_cmdq_release_all(struct ivpu_file_priv *file_priv); +void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv); void ivpu_cmdq_reset_all_contexts(struct ivpu_device *vdev); void ivpu_job_done_consumer_init(struct ivpu_device *vdev); diff --git a/drivers/accel/ivpu/ivpu_mmu.c b/drivers/accel/ivpu/ivpu_mmu.c index 2228c44b115f..91bd640655ab 100644 --- a/drivers/accel/ivpu/ivpu_mmu.c +++ b/drivers/accel/ivpu/ivpu_mmu.c @@ -7,6 +7,7 @@ #include <linux/highmem.h> #include "ivpu_drv.h" +#include "ivpu_hw.h" #include "ivpu_hw_reg_io.h" #include "ivpu_mmu.h" #include "ivpu_mmu_context.h" @@ -71,10 +72,10 @@ #define IVPU_MMU_Q_COUNT_LOG2 4 /* 16 entries */ #define IVPU_MMU_Q_COUNT ((u32)1 << IVPU_MMU_Q_COUNT_LOG2) -#define IVPU_MMU_Q_WRAP_BIT (IVPU_MMU_Q_COUNT << 1) -#define IVPU_MMU_Q_WRAP_MASK (IVPU_MMU_Q_WRAP_BIT - 1) -#define IVPU_MMU_Q_IDX_MASK (IVPU_MMU_Q_COUNT - 1) +#define IVPU_MMU_Q_WRAP_MASK GENMASK(IVPU_MMU_Q_COUNT_LOG2, 0) +#define IVPU_MMU_Q_IDX_MASK (IVPU_MMU_Q_COUNT - 1) #define IVPU_MMU_Q_IDX(val) ((val) & IVPU_MMU_Q_IDX_MASK) +#define IVPU_MMU_Q_WRP(val) ((val) & IVPU_MMU_Q_COUNT) #define IVPU_MMU_CMDQ_CMD_SIZE 16 #define IVPU_MMU_CMDQ_SIZE (IVPU_MMU_Q_COUNT * IVPU_MMU_CMDQ_CMD_SIZE) @@ -474,20 +475,32 @@ static int ivpu_mmu_cmdq_wait_for_cons(struct ivpu_device *vdev) return 0; } +static bool ivpu_mmu_queue_is_full(struct ivpu_mmu_queue *q) +{ + return ((IVPU_MMU_Q_IDX(q->prod) == IVPU_MMU_Q_IDX(q->cons)) && + (IVPU_MMU_Q_WRP(q->prod) != IVPU_MMU_Q_WRP(q->cons))); +} + +static bool ivpu_mmu_queue_is_empty(struct ivpu_mmu_queue *q) +{ + return ((IVPU_MMU_Q_IDX(q->prod) == IVPU_MMU_Q_IDX(q->cons)) && + (IVPU_MMU_Q_WRP(q->prod) == IVPU_MMU_Q_WRP(q->cons))); +} + static int ivpu_mmu_cmdq_cmd_write(struct ivpu_device *vdev, const char *name, u64 data0, u64 data1) { - struct ivpu_mmu_queue *q = &vdev->mmu->cmdq; - u64 *queue_buffer = q->base; - int idx = IVPU_MMU_Q_IDX(q->prod) * (IVPU_MMU_CMDQ_CMD_SIZE / sizeof(*queue_buffer)); + struct ivpu_mmu_queue *cmdq = &vdev->mmu->cmdq; + u64 *queue_buffer = cmdq->base; + int idx = IVPU_MMU_Q_IDX(cmdq->prod) * (IVPU_MMU_CMDQ_CMD_SIZE / sizeof(*queue_buffer)); - if (!CIRC_SPACE(IVPU_MMU_Q_IDX(q->prod), IVPU_MMU_Q_IDX(q->cons), IVPU_MMU_Q_COUNT)) { + if (ivpu_mmu_queue_is_full(cmdq)) { ivpu_err(vdev, "Failed to write MMU CMD %s\n", name); return -EBUSY; } queue_buffer[idx] = data0; queue_buffer[idx + 1] = data1; - q->prod = (q->prod + 1) & IVPU_MMU_Q_WRAP_MASK; + cmdq->prod = (cmdq->prod + 1) & IVPU_MMU_Q_WRAP_MASK; ivpu_dbg(vdev, MMU, "CMD write: %s data: 0x%llx 0x%llx\n", name, data0, data1); @@ -518,6 +531,7 @@ static int ivpu_mmu_cmdq_sync(struct ivpu_device *vdev) ivpu_err(vdev, "Timed out waiting for MMU consumer: %d, error: %s\n", ret, ivpu_mmu_cmdq_err_to_str(err)); + ivpu_hw_diagnose_failure(vdev); } return ret; @@ -558,7 +572,6 @@ static int ivpu_mmu_reset(struct ivpu_device *vdev) mmu->cmdq.cons = 0; memset(mmu->evtq.base, 0, IVPU_MMU_EVTQ_SIZE); - clflush_cache_range(mmu->evtq.base, IVPU_MMU_EVTQ_SIZE); mmu->evtq.prod = 0; mmu->evtq.cons = 0; @@ -872,20 +885,15 @@ static u32 *ivpu_mmu_get_event(struct ivpu_device *vdev) u32 *evt = evtq->base + (idx * IVPU_MMU_EVTQ_CMD_SIZE); evtq->prod = REGV_RD32(IVPU_MMU_REG_EVTQ_PROD_SEC); - if (!CIRC_CNT(IVPU_MMU_Q_IDX(evtq->prod), IVPU_MMU_Q_IDX(evtq->cons), IVPU_MMU_Q_COUNT)) + if (ivpu_mmu_queue_is_empty(evtq)) return NULL; - clflush_cache_range(evt, IVPU_MMU_EVTQ_CMD_SIZE); - evtq->cons = (evtq->cons + 1) & IVPU_MMU_Q_WRAP_MASK; - REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, evtq->cons); - return evt; } void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev) { - bool schedule_recovery = false; u32 *event; u32 ssid; @@ -895,14 +903,22 @@ void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev) ivpu_mmu_dump_event(vdev, event); ssid = FIELD_GET(IVPU_MMU_EVT_SSID_MASK, event[0]); - if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID) - schedule_recovery = true; - else - ivpu_mmu_user_context_mark_invalid(vdev, ssid); + if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID) { + ivpu_pm_trigger_recovery(vdev, "MMU event"); + return; + } + + ivpu_mmu_user_context_mark_invalid(vdev, ssid); + REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, vdev->mmu->evtq.cons); } +} - if (schedule_recovery) - ivpu_pm_schedule_recovery(vdev); +void ivpu_mmu_evtq_dump(struct ivpu_device *vdev) +{ + u32 *event; + + while ((event = ivpu_mmu_get_event(vdev)) != NULL) + ivpu_mmu_dump_event(vdev, event); } void ivpu_mmu_irq_gerr_handler(struct ivpu_device *vdev) diff --git a/drivers/accel/ivpu/ivpu_mmu.h b/drivers/accel/ivpu/ivpu_mmu.h index cb551126806b..6fa35c240710 100644 --- a/drivers/accel/ivpu/ivpu_mmu.h +++ b/drivers/accel/ivpu/ivpu_mmu.h @@ -46,5 +46,6 @@ int ivpu_mmu_invalidate_tlb(struct ivpu_device *vdev, u16 ssid); void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev); void ivpu_mmu_irq_gerr_handler(struct ivpu_device *vdev); +void ivpu_mmu_evtq_dump(struct ivpu_device *vdev); #endif /* __IVPU_MMU_H__ */ diff --git a/drivers/accel/ivpu/ivpu_mmu_context.c b/drivers/accel/ivpu/ivpu_mmu_context.c index 12a8c09d4547..fe6161299236 100644 --- a/drivers/accel/ivpu/ivpu_mmu_context.c +++ b/drivers/accel/ivpu/ivpu_mmu_context.c @@ -355,6 +355,9 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, dma_addr_t dma_addr = sg_dma_address(sg) - sg->offset; size_t size = sg_dma_len(sg) + sg->offset; + ivpu_dbg(vdev, MMU_MAP, "Map ctx: %u dma_addr: 0x%llx vpu_addr: 0x%llx size: %lu\n", + ctx->id, dma_addr, vpu_addr, size); + ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot); if (ret) { ivpu_err(vdev, "Failed to map context pages\n"); @@ -366,6 +369,7 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, /* Ensure page table modifications are flushed from wc buffers to memory */ wmb(); + mutex_unlock(&ctx->lock); ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); @@ -388,14 +392,19 @@ ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ct mutex_lock(&ctx->lock); for_each_sgtable_dma_sg(sgt, sg, i) { + dma_addr_t dma_addr = sg_dma_address(sg) - sg->offset; size_t size = sg_dma_len(sg) + sg->offset; + ivpu_dbg(vdev, MMU_MAP, "Unmap ctx: %u dma_addr: 0x%llx vpu_addr: 0x%llx size: %lu\n", + ctx->id, dma_addr, vpu_addr, size); + ivpu_mmu_context_unmap_pages(ctx, vpu_addr, size); vpu_addr += size; } /* Ensure page table modifications are flushed from wc buffers to memory */ wmb(); + mutex_unlock(&ctx->lock); ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c index 0af8864cb3b5..f501f27ebafd 100644 --- a/drivers/accel/ivpu/ivpu_pm.c +++ b/drivers/accel/ivpu/ivpu_pm.c @@ -13,6 +13,7 @@ #include "ivpu_drv.h" #include "ivpu_hw.h" #include "ivpu_fw.h" +#include "ivpu_fw_log.h" #include "ivpu_ipc.h" #include "ivpu_job.h" #include "ivpu_jsm_msg.h" @@ -111,6 +112,14 @@ static void ivpu_pm_recovery_work(struct work_struct *work) char *evt[2] = {"IVPU_PM_EVENT=IVPU_RECOVER", NULL}; int ret; + ivpu_err(vdev, "Recovering the VPU (reset #%d)\n", atomic_read(&vdev->pm->reset_counter)); + + ret = pm_runtime_resume_and_get(vdev->drm.dev); + if (ret) + ivpu_err(vdev, "Failed to resume VPU: %d\n", ret); + + ivpu_fw_log_dump(vdev); + retry: ret = pci_try_reset_function(to_pci_dev(vdev->drm.dev)); if (ret == -EAGAIN && !drm_dev_is_unplugged(&vdev->drm)) { @@ -122,11 +131,13 @@ retry: ivpu_err(vdev, "Failed to reset VPU: %d\n", ret); kobject_uevent_env(&vdev->drm.dev->kobj, KOBJ_CHANGE, evt); + pm_runtime_mark_last_busy(vdev->drm.dev); + pm_runtime_put_autosuspend(vdev->drm.dev); } -void ivpu_pm_schedule_recovery(struct ivpu_device *vdev) +void ivpu_pm_trigger_recovery(struct ivpu_device *vdev, const char *reason) { - struct ivpu_pm_info *pm = vdev->pm; + ivpu_err(vdev, "Recovery triggered by %s\n", reason); if (ivpu_disable_recovery) { ivpu_err(vdev, "Recovery not available when disable_recovery param is set\n"); @@ -138,10 +149,11 @@ void ivpu_pm_schedule_recovery(struct ivpu_device *vdev) return; } - /* Schedule recovery if it's not in progress */ - if (atomic_cmpxchg(&pm->in_reset, 0, 1) == 0) { - ivpu_hw_irq_disable(vdev); - queue_work(system_long_wq, &pm->recovery_work); + /* Trigger recovery if it's not in progress */ + if (atomic_cmpxchg(&vdev->pm->reset_pending, 0, 1) == 0) { + ivpu_hw_diagnose_failure(vdev); + ivpu_hw_irq_disable(vdev); /* Disable IRQ early to protect from IRQ storm */ + queue_work(system_long_wq, &vdev->pm->recovery_work); } } @@ -149,12 +161,8 @@ static void ivpu_job_timeout_work(struct work_struct *work) { struct ivpu_pm_info *pm = container_of(work, struct ivpu_pm_info, job_timeout_work.work); struct ivpu_device *vdev = pm->vdev; - unsigned long timeout_ms = ivpu_tdr_timeout_ms ? ivpu_tdr_timeout_ms : vdev->timeout.tdr; - ivpu_err(vdev, "TDR detected, timeout %lu ms", timeout_ms); - ivpu_hw_diagnose_failure(vdev); - - ivpu_pm_schedule_recovery(vdev); + ivpu_pm_trigger_recovery(vdev, "TDR"); } void ivpu_start_job_timeout_detection(struct ivpu_device *vdev) @@ -227,6 +235,9 @@ int ivpu_pm_runtime_suspend_cb(struct device *dev) bool hw_is_idle = true; int ret; + drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->submitted_jobs_xa)); + drm_WARN_ON(&vdev->drm, work_pending(&vdev->pm->recovery_work)); + ivpu_dbg(vdev, PM, "Runtime suspend..\n"); if (!ivpu_hw_is_idle(vdev) && vdev->pm->suspend_reschedule_counter) { @@ -247,7 +258,8 @@ int ivpu_pm_runtime_suspend_cb(struct device *dev) ivpu_err(vdev, "Failed to set suspend VPU: %d\n", ret); if (!hw_is_idle) { - ivpu_warn(vdev, "VPU failed to enter idle, force suspended.\n"); + ivpu_err(vdev, "VPU failed to enter idle, force suspended.\n"); + ivpu_fw_log_dump(vdev); ivpu_pm_prepare_cold_boot(vdev); } else { ivpu_pm_prepare_warm_boot(vdev); @@ -308,11 +320,12 @@ void ivpu_pm_reset_prepare_cb(struct pci_dev *pdev) { struct ivpu_device *vdev = pci_get_drvdata(pdev); - pm_runtime_get_sync(vdev->drm.dev); - ivpu_dbg(vdev, PM, "Pre-reset..\n"); atomic_inc(&vdev->pm->reset_counter); - atomic_set(&vdev->pm->in_reset, 1); + atomic_set(&vdev->pm->reset_pending, 1); + + pm_runtime_get_sync(vdev->drm.dev); + down_write(&vdev->pm->reset_lock); ivpu_prepare_for_reset(vdev); ivpu_hw_reset(vdev); ivpu_pm_prepare_cold_boot(vdev); @@ -329,9 +342,11 @@ void ivpu_pm_reset_done_cb(struct pci_dev *pdev) ret = ivpu_resume(vdev); if (ret) ivpu_err(vdev, "Failed to set RESUME state: %d\n", ret); - atomic_set(&vdev->pm->in_reset, 0); + up_write(&vdev->pm->reset_lock); + atomic_set(&vdev->pm->reset_pending, 0); ivpu_dbg(vdev, PM, "Post-reset done.\n"); + pm_runtime_mark_last_busy(vdev->drm.dev); pm_runtime_put_autosuspend(vdev->drm.dev); } @@ -344,7 +359,10 @@ void ivpu_pm_init(struct ivpu_device *vdev) pm->vdev = vdev; pm->suspend_reschedule_counter = PM_RESCHEDULE_LIMIT; - atomic_set(&pm->in_reset, 0); + init_rwsem(&pm->reset_lock); + atomic_set(&pm->reset_pending, 0); + atomic_set(&pm->reset_counter, 0); + INIT_WORK(&pm->recovery_work, ivpu_pm_recovery_work); INIT_DELAYED_WORK(&pm->job_timeout_work, ivpu_job_timeout_work); diff --git a/drivers/accel/ivpu/ivpu_pm.h b/drivers/accel/ivpu/ivpu_pm.h index 97c6e0b0aa42..ec60fbeefefc 100644 --- a/drivers/accel/ivpu/ivpu_pm.h +++ b/drivers/accel/ivpu/ivpu_pm.h @@ -6,6 +6,7 @@ #ifndef __IVPU_PM_H__ #define __IVPU_PM_H__ +#include <linux/rwsem.h> #include <linux/types.h> struct ivpu_device; @@ -14,8 +15,9 @@ struct ivpu_pm_info { struct ivpu_device *vdev; struct delayed_work job_timeout_work; struct work_struct recovery_work; - atomic_t in_reset; + struct rw_semaphore reset_lock; atomic_t reset_counter; + atomic_t reset_pending; bool is_warmboot; u32 suspend_reschedule_counter; }; @@ -37,7 +39,7 @@ int __must_check ivpu_rpm_get(struct ivpu_device *vdev); int __must_check ivpu_rpm_get_if_active(struct ivpu_device *vdev); void ivpu_rpm_put(struct ivpu_device *vdev); -void ivpu_pm_schedule_recovery(struct ivpu_device *vdev); +void ivpu_pm_trigger_recovery(struct ivpu_device *vdev, const char *reason); void ivpu_start_job_timeout_detection(struct ivpu_device *vdev); void ivpu_stop_job_timeout_detection(struct ivpu_device *vdev); diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 6f2bfcf7645c..3c3f8037ebed 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -314,7 +314,6 @@ config ACPI_HOTPLUG_CPU bool depends on ACPI_PROCESSOR && HOTPLUG_CPU select ACPI_CONTAINER - default y config ACPI_PROCESSOR_AGGREGATOR tristate "Processor Aggregator" diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 0f5218e361df..4fe2ef54088c 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -184,24 +184,6 @@ static void __init acpi_pcc_cpufreq_init(void) {} /* Initialization */ #ifdef CONFIG_ACPI_HOTPLUG_CPU -int __weak acpi_map_cpu(acpi_handle handle, - phys_cpuid_t physid, u32 acpi_id, int *pcpu) -{ - return -ENODEV; -} - -int __weak acpi_unmap_cpu(int cpu) -{ - return -ENODEV; -} - -int __weak arch_register_cpu(int cpu) -{ - return -ENODEV; -} - -void __weak arch_unregister_cpu(int cpu) {} - static int acpi_processor_hotadd_init(struct acpi_processor *pr) { unsigned long long sta; diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index ab2a82cb1b0b..fe825a432c5b 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -26,6 +26,7 @@ #include <linux/interrupt.h> #include <linux/timer.h> #include <linux/cper.h> +#include <linux/cxl-event.h> #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/ratelimit.h> @@ -673,6 +674,52 @@ static void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, schedule_work(&entry->work); } +/* + * Only a single callback can be registered for CXL CPER events. + */ +static DECLARE_RWSEM(cxl_cper_rw_sem); +static cxl_cper_callback cper_callback; + +static void cxl_cper_post_event(enum cxl_event_type event_type, + struct cxl_cper_event_rec *rec) +{ + if (rec->hdr.length <= sizeof(rec->hdr) || + rec->hdr.length > sizeof(*rec)) { + pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n", + rec->hdr.length); + return; + } + + if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) { + pr_err(FW_WARN "CXL CPER invalid event\n"); + return; + } + + guard(rwsem_read)(&cxl_cper_rw_sem); + if (cper_callback) + cper_callback(event_type, rec); +} + +int cxl_cper_register_callback(cxl_cper_callback callback) +{ + guard(rwsem_write)(&cxl_cper_rw_sem); + if (cper_callback) + return -EINVAL; + cper_callback = callback; + return 0; +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_register_callback, CXL); + +int cxl_cper_unregister_callback(cxl_cper_callback callback) +{ + guard(rwsem_write)(&cxl_cper_rw_sem); + if (callback != cper_callback) + return -EINVAL; + cper_callback = NULL; + return 0; +} +EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_callback, CXL); + static bool ghes_do_proc(struct ghes *ghes, const struct acpi_hest_generic_status *estatus) { @@ -707,6 +754,22 @@ static bool ghes_do_proc(struct ghes *ghes, } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { queued = ghes_handle_arm_hw_error(gdata, sev, sync); + } else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) { + struct cxl_cper_event_rec *rec = + acpi_hest_get_payload(gdata); + + cxl_cper_post_event(CXL_CPER_EVENT_GEN_MEDIA, rec); + } else if (guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID)) { + struct cxl_cper_event_rec *rec = + acpi_hest_get_payload(gdata); + + cxl_cper_post_event(CXL_CPER_EVENT_DRAM, rec); + } else if (guid_equal(sec_type, + &CPER_SEC_CXL_MEM_MODULE_GUID)) { + struct cxl_cper_event_rec *rec = + acpi_hest_get_payload(gdata); + + cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec); } else { void *err = acpi_hest_get_payload(gdata); diff --git a/drivers/acpi/numa/hmat.c b/drivers/acpi/numa/hmat.c index 9ef5f1bdcfdb..d6b85f0f6082 100644 --- a/drivers/acpi/numa/hmat.c +++ b/drivers/acpi/numa/hmat.c @@ -58,14 +58,22 @@ struct target_cache { struct node_cache_attrs cache_attrs; }; +enum { + NODE_ACCESS_CLASS_0 = 0, + NODE_ACCESS_CLASS_1, + NODE_ACCESS_CLASS_GENPORT_SINK, + NODE_ACCESS_CLASS_MAX, +}; + struct memory_target { struct list_head node; unsigned int memory_pxm; unsigned int processor_pxm; struct resource memregions; - struct node_hmem_attrs hmem_attrs[2]; + struct access_coordinate coord[NODE_ACCESS_CLASS_MAX]; struct list_head caches; struct node_cache_attrs cache_attrs; + u8 gen_port_device_handle[ACPI_SRAT_DEVICE_HANDLE_SIZE]; bool registered; }; @@ -100,6 +108,47 @@ static struct memory_target *find_mem_target(unsigned int mem_pxm) return NULL; } +static struct memory_target *acpi_find_genport_target(u32 uid) +{ + struct memory_target *target; + u32 target_uid; + u8 *uid_ptr; + + list_for_each_entry(target, &targets, node) { + uid_ptr = target->gen_port_device_handle + 8; + target_uid = *(u32 *)uid_ptr; + if (uid == target_uid) + return target; + } + + return NULL; +} + +/** + * acpi_get_genport_coordinates - Retrieve the access coordinates for a generic port + * @uid: ACPI unique id + * @coord: The access coordinates written back out for the generic port + * + * Return: 0 on success. Errno on failure. + * + * Only supports device handles that are ACPI. Assume ACPI0016 HID for CXL. + */ +int acpi_get_genport_coordinates(u32 uid, + struct access_coordinate *coord) +{ + struct memory_target *target; + + guard(mutex)(&target_lock); + target = acpi_find_genport_target(uid); + if (!target) + return -ENOENT; + + *coord = target->coord[NODE_ACCESS_CLASS_GENPORT_SINK]; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(acpi_get_genport_coordinates, CXL); + static __init void alloc_memory_initiator(unsigned int cpu_pxm) { struct memory_initiator *initiator; @@ -120,8 +169,7 @@ static __init void alloc_memory_initiator(unsigned int cpu_pxm) list_add_tail(&initiator->node, &initiators); } -static __init void alloc_memory_target(unsigned int mem_pxm, - resource_size_t start, resource_size_t len) +static __init struct memory_target *alloc_target(unsigned int mem_pxm) { struct memory_target *target; @@ -129,7 +177,7 @@ static __init void alloc_memory_target(unsigned int mem_pxm, if (!target) { target = kzalloc(sizeof(*target), GFP_KERNEL); if (!target) - return; + return NULL; target->memory_pxm = mem_pxm; target->processor_pxm = PXM_INVAL; target->memregions = (struct resource) { @@ -142,6 +190,19 @@ static __init void alloc_memory_target(unsigned int mem_pxm, INIT_LIST_HEAD(&target->caches); } + return target; +} + +static __init void alloc_memory_target(unsigned int mem_pxm, + resource_size_t start, + resource_size_t len) +{ + struct memory_target *target; + + target = alloc_target(mem_pxm); + if (!target) + return; + /* * There are potentially multiple ranges per PXM, so record each * in the per-target memregions resource tree. @@ -152,6 +213,18 @@ static __init void alloc_memory_target(unsigned int mem_pxm, start, start + len, mem_pxm); } +static __init void alloc_genport_target(unsigned int mem_pxm, u8 *handle) +{ + struct memory_target *target; + + target = alloc_target(mem_pxm); + if (!target) + return; + + memcpy(target->gen_port_device_handle, handle, + ACPI_SRAT_DEVICE_HANDLE_SIZE); +} + static __init const char *hmat_data_type(u8 type) { switch (type) { @@ -228,24 +301,24 @@ static void hmat_update_target_access(struct memory_target *target, { switch (type) { case ACPI_HMAT_ACCESS_LATENCY: - target->hmem_attrs[access].read_latency = value; - target->hmem_attrs[access].write_latency = value; + target->coord[access].read_latency = value; + target->coord[access].write_latency = value; break; case ACPI_HMAT_READ_LATENCY: - target->hmem_attrs[access].read_latency = value; + target->coord[access].read_latency = value; break; case ACPI_HMAT_WRITE_LATENCY: - target->hmem_attrs[access].write_latency = value; + target->coord[access].write_latency = value; break; case ACPI_HMAT_ACCESS_BANDWIDTH: - target->hmem_attrs[access].read_bandwidth = value; - target->hmem_attrs[access].write_bandwidth = value; + target->coord[access].read_bandwidth = value; + target->coord[access].write_bandwidth = value; break; case ACPI_HMAT_READ_BANDWIDTH: - target->hmem_attrs[access].read_bandwidth = value; + target->coord[access].read_bandwidth = value; break; case ACPI_HMAT_WRITE_BANDWIDTH: - target->hmem_attrs[access].write_bandwidth = value; + target->coord[access].write_bandwidth = value; break; default: break; @@ -291,11 +364,28 @@ static __init void hmat_add_locality(struct acpi_hmat_locality *hmat_loc) } } +static __init void hmat_update_target(unsigned int tgt_pxm, unsigned int init_pxm, + u8 mem_hier, u8 type, u32 value) +{ + struct memory_target *target = find_mem_target(tgt_pxm); + + if (mem_hier != ACPI_HMAT_MEMORY) + return; + + if (target && target->processor_pxm == init_pxm) { + hmat_update_target_access(target, type, value, + NODE_ACCESS_CLASS_0); + /* If the node has a CPU, update access 1 */ + if (node_state(pxm_to_node(init_pxm), N_CPU)) + hmat_update_target_access(target, type, value, + NODE_ACCESS_CLASS_1); + } +} + static __init int hmat_parse_locality(union acpi_subtable_headers *header, const unsigned long end) { struct acpi_hmat_locality *hmat_loc = (void *)header; - struct memory_target *target; unsigned int init, targ, total_size, ipds, tpds; u32 *inits, *targs, value; u16 *entries; @@ -336,15 +426,8 @@ static __init int hmat_parse_locality(union acpi_subtable_headers *header, inits[init], targs[targ], value, hmat_data_type_suffix(type)); - if (mem_hier == ACPI_HMAT_MEMORY) { - target = find_mem_target(targs[targ]); - if (target && target->processor_pxm == inits[init]) { - hmat_update_target_access(target, type, value, 0); - /* If the node has a CPU, update access 1 */ - if (node_state(pxm_to_node(inits[init]), N_CPU)) - hmat_update_target_access(target, type, value, 1); - } - } + hmat_update_target(targs[targ], inits[init], + mem_hier, type, value); } } @@ -491,6 +574,27 @@ static __init int srat_parse_mem_affinity(union acpi_subtable_headers *header, return 0; } +static __init int srat_parse_genport_affinity(union acpi_subtable_headers *header, + const unsigned long end) +{ + struct acpi_srat_generic_affinity *ga = (void *)header; + + if (!ga) + return -EINVAL; + + if (!(ga->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED)) + return 0; + + /* Skip PCI device_handle for now */ + if (ga->device_handle_type != 0) + return 0; + + alloc_genport_target(ga->proximity_domain, + (u8 *)ga->device_handle); + + return 0; +} + static u32 hmat_initiator_perf(struct memory_target *target, struct memory_initiator *initiator, struct acpi_hmat_locality *hmat_loc) @@ -592,6 +696,11 @@ static void hmat_update_target_attrs(struct memory_target *target, u32 best = 0; int i; + /* Don't update for generic port if there's no device handle */ + if (access == NODE_ACCESS_CLASS_GENPORT_SINK && + !(*(u16 *)target->gen_port_device_handle)) + return; + bitmap_zero(p_nodes, MAX_NUMNODES); /* * If the Address Range Structure provides a local processor pxm, set @@ -661,6 +770,14 @@ static void __hmat_register_target_initiators(struct memory_target *target, } } +static void hmat_register_generic_target_initiators(struct memory_target *target) +{ + static DECLARE_BITMAP(p_nodes, MAX_NUMNODES); + + __hmat_register_target_initiators(target, p_nodes, + NODE_ACCESS_CLASS_GENPORT_SINK); +} + static void hmat_register_target_initiators(struct memory_target *target) { static DECLARE_BITMAP(p_nodes, MAX_NUMNODES); @@ -681,7 +798,7 @@ static void hmat_register_target_cache(struct memory_target *target) static void hmat_register_target_perf(struct memory_target *target, int access) { unsigned mem_nid = pxm_to_node(target->memory_pxm); - node_set_perf_attrs(mem_nid, &target->hmem_attrs[access], access); + node_set_perf_attrs(mem_nid, &target->coord[access], access); } static void hmat_register_target_devices(struct memory_target *target) @@ -713,6 +830,17 @@ static void hmat_register_target(struct memory_target *target) hmat_register_target_devices(target); /* + * Register generic port perf numbers. The nid may not be + * initialized and is still NUMA_NO_NODE. + */ + mutex_lock(&target_lock); + if (*(u16 *)target->gen_port_device_handle) { + hmat_register_generic_target_initiators(target); + target->registered = true; + } + mutex_unlock(&target_lock); + + /* * Skip offline nodes. This can happen when memory * marked EFI_MEMORY_SP, "specific purpose", is applied * to all the memory in a proximity domain leading to @@ -726,8 +854,8 @@ static void hmat_register_target(struct memory_target *target) if (!target->registered) { hmat_register_target_initiators(target); hmat_register_target_cache(target); - hmat_register_target_perf(target, 0); - hmat_register_target_perf(target, 1); + hmat_register_target_perf(target, NODE_ACCESS_CLASS_0); + hmat_register_target_perf(target, NODE_ACCESS_CLASS_1); target->registered = true; } mutex_unlock(&target_lock); @@ -765,7 +893,7 @@ static int hmat_set_default_dram_perf(void) int rc; int nid, pxm; struct memory_target *target; - struct node_hmem_attrs *attrs; + struct access_coordinate *attrs; if (!default_dram_type) return -EIO; @@ -775,7 +903,7 @@ static int hmat_set_default_dram_perf(void) target = find_mem_target(pxm); if (!target) continue; - attrs = &target->hmem_attrs[1]; + attrs = &target->coord[1]; rc = mt_set_default_dram_perf(nid, attrs, "ACPI HMAT"); if (rc) return rc; @@ -789,7 +917,7 @@ static int hmat_calculate_adistance(struct notifier_block *self, { static DECLARE_BITMAP(p_nodes, MAX_NUMNODES); struct memory_target *target; - struct node_hmem_attrs *perf; + struct access_coordinate *perf; int *adist = data; int pxm; @@ -802,7 +930,7 @@ static int hmat_calculate_adistance(struct notifier_block *self, hmat_update_target_attrs(target, p_nodes, 1); mutex_unlock(&target_lock); - perf = &target->hmem_attrs[1]; + perf = &target->coord[1]; if (mt_perf_to_adistance(perf, adist)) return NOTIFY_OK; @@ -870,6 +998,13 @@ static __init int hmat_init(void) ACPI_SRAT_TYPE_MEMORY_AFFINITY, srat_parse_mem_affinity, 0) < 0) goto out_put; + + if (acpi_table_parse_entries(ACPI_SIG_SRAT, + sizeof(struct acpi_table_srat), + ACPI_SRAT_TYPE_GENERIC_PORT_AFFINITY, + srat_parse_genport_affinity, 0) < 0) + goto out_put; + acpi_put_table(tbl); status = acpi_get_table(ACPI_SIG_HMAT, 0, &tbl); diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 07d76fb740b6..a6ead5204046 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -881,6 +881,7 @@ static struct fwnode_handle *acpi_parse_string_ref(const struct fwnode_handle *f * @index: Index of the reference to return * @num_args: Maximum number of arguments after each reference * @args: Location to store the returned reference with optional arguments + * (may be NULL) * * Find property with @name, verifify that it is a package containing at least * one object reference and if so, store the ACPI device object pointer to the @@ -938,6 +939,9 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (!device) return -EINVAL; + if (!args) + return 0; + args->fwnode = acpi_fwnode_handle(device); args->nargs = 0; diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 0e2c397b1399..dacad1d846c0 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -462,6 +462,13 @@ static const struct dmi_system_id irq1_level_low_skip_override[] = { }, }, { + /* Asus ExpertBook B1502CGA */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_BOARD_NAME, "B1502CGA"), + }, + }, + { /* Asus ExpertBook B2402CBA */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), @@ -483,6 +490,20 @@ static const struct dmi_system_id irq1_level_low_skip_override[] = { }, }, { + /* Asus Vivobook E1504GA */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_BOARD_NAME, "E1504GA"), + }, + }, + { + /* Asus Vivobook E1504GAB */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_BOARD_NAME, "E1504GAB"), + }, + }, + { /* LG Electronics 17U70P */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 0ba008773b00..e6ed1ba91e5c 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1561,8 +1561,7 @@ static inline const struct iommu_ops *acpi_iommu_fwspec_ops(struct device *dev) return fwspec ? fwspec->ops : NULL; } -static const struct iommu_ops *acpi_iommu_configure_id(struct device *dev, - const u32 *id_in) +static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in) { int err; const struct iommu_ops *ops; @@ -1576,7 +1575,7 @@ static const struct iommu_ops *acpi_iommu_configure_id(struct device *dev, ops = acpi_iommu_fwspec_ops(dev); if (ops) { mutex_unlock(&iommu_probe_device_lock); - return ops; + return 0; } err = iort_iommu_configure_id(dev, id_in); @@ -1593,12 +1592,14 @@ static const struct iommu_ops *acpi_iommu_configure_id(struct device *dev, /* Ignore all other errors apart from EPROBE_DEFER */ if (err == -EPROBE_DEFER) { - return ERR_PTR(err); + return err; } else if (err) { dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); - return NULL; + return -ENODEV; } - return acpi_iommu_fwspec_ops(dev); + if (!acpi_iommu_fwspec_ops(dev)) + return -ENODEV; + return 0; } #else /* !CONFIG_IOMMU_API */ @@ -1610,10 +1611,9 @@ int acpi_iommu_fwspec_init(struct device *dev, u32 id, return -ENODEV; } -static const struct iommu_ops *acpi_iommu_configure_id(struct device *dev, - const u32 *id_in) +static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in) { - return NULL; + return -ENODEV; } #endif /* !CONFIG_IOMMU_API */ @@ -1627,7 +1627,7 @@ static const struct iommu_ops *acpi_iommu_configure_id(struct device *dev, int acpi_dma_configure_id(struct device *dev, enum dev_dma_attr attr, const u32 *input_id) { - const struct iommu_ops *iommu; + int ret; if (attr == DEV_DMA_NOT_SUPPORTED) { set_dma_ops(dev, &dma_dummy_ops); @@ -1636,12 +1636,16 @@ int acpi_dma_configure_id(struct device *dev, enum dev_dma_attr attr, acpi_arch_dma_setup(dev); - iommu = acpi_iommu_configure_id(dev, input_id); - if (PTR_ERR(iommu) == -EPROBE_DEFER) + ret = acpi_iommu_configure_id(dev, input_id); + if (ret == -EPROBE_DEFER) return -EPROBE_DEFER; - arch_setup_dma_ops(dev, 0, U64_MAX, - iommu, attr == DEV_DMA_COHERENT); + /* + * Historically this routine doesn't fail driver probing due to errors + * in acpi_iommu_configure_id() + */ + + arch_setup_dma_ops(dev, 0, U64_MAX, attr == DEV_DMA_COHERENT); return 0; } diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index c1516337f668..b07f7d091d13 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -251,8 +251,9 @@ int __init_or_acpilib acpi_table_parse_entries_array( return -ENODEV; } - count = acpi_parse_entries_array(id, table_size, table_header, - proc, proc_num, max_entries); + count = acpi_parse_entries_array(id, table_size, + (union fw_table_header *)table_header, + proc, proc_num, max_entries); acpi_put_table(table_header); return count; diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 7658103ba760..eca24f41556d 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -478,6 +478,16 @@ binder_enqueue_thread_work_ilocked(struct binder_thread *thread, { WARN_ON(!list_empty(&thread->waiting_thread_node)); binder_enqueue_work_ilocked(work, &thread->todo); + + /* (e)poll-based threads require an explicit wakeup signal when + * queuing their own work; they rely on these events to consume + * messages without I/O block. Without it, threads risk waiting + * indefinitely without handling the work. + */ + if (thread->looper & BINDER_LOOPER_STATE_POLL && + thread->pid == current->pid && !thread->process_todo) + wake_up_interruptible_sync(&thread->wait); + thread->process_todo = true; } @@ -2077,9 +2087,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, * Convert the address to an offset relative to * the base of the transaction buffer. */ - fda_offset = - (parent->buffer - (uintptr_t)buffer->user_data) + - fda->parent_offset; + fda_offset = parent->buffer - buffer->user_data + + fda->parent_offset; for (fd_index = 0; fd_index < fda->num_fds; fd_index++) { u32 fd; @@ -2597,7 +2606,7 @@ static int binder_translate_fd_array(struct list_head *pf_head, * Convert the address to an offset relative to * the base of the transaction buffer. */ - fda_offset = (parent->buffer - (uintptr_t)t->buffer->user_data) + + fda_offset = parent->buffer - t->buffer->user_data + fda->parent_offset; sender_ufda_base = (void __user *)(uintptr_t)sender_uparent->buffer + fda->parent_offset; @@ -2672,8 +2681,9 @@ static int binder_fixup_parent(struct list_head *pf_head, proc->pid, thread->pid); return -EINVAL; } - buffer_offset = bp->parent_offset + - (uintptr_t)parent->buffer - (uintptr_t)b->user_data; + + buffer_offset = bp->parent_offset + parent->buffer - b->user_data; + return binder_add_fixup(pf_head, buffer_offset, bp->buffer, 0); } @@ -3225,7 +3235,7 @@ static void binder_transaction(struct binder_proc *proc, t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size, tr->offsets_size, extra_buffers_size, - !reply && (t->flags & TF_ONE_WAY), current->tgid); + !reply && (t->flags & TF_ONE_WAY)); if (IS_ERR(t->buffer)) { char *s; @@ -3250,7 +3260,7 @@ static void binder_transaction(struct binder_proc *proc, ALIGN(extra_buffers_size, sizeof(void *)) - ALIGN(secctx_sz, sizeof(u64)); - t->security_ctx = (uintptr_t)t->buffer->user_data + buf_offset; + t->security_ctx = t->buffer->user_data + buf_offset; err = binder_alloc_copy_to_buffer(&target_proc->alloc, t->buffer, buf_offset, secctx, secctx_sz); @@ -3527,8 +3537,7 @@ static void binder_transaction(struct binder_proc *proc, goto err_translate_failed; } /* Fixup buffer pointer to target proc address space */ - bp->buffer = (uintptr_t) - t->buffer->user_data + sg_buf_offset; + bp->buffer = t->buffer->user_data + sg_buf_offset; sg_buf_offset += ALIGN(bp->length, sizeof(u64)); num_valid = (buffer_offset - off_start_offset) / @@ -4698,7 +4707,7 @@ retry: } trd->data_size = t->buffer->data_size; trd->offsets_size = t->buffer->offsets_size; - trd->data.ptr.buffer = (uintptr_t)t->buffer->user_data; + trd->data.ptr.buffer = t->buffer->user_data; trd->data.ptr.offsets = trd->data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *)); @@ -5030,7 +5039,7 @@ static __poll_t binder_poll(struct file *filp, thread = binder_get_thread(proc); if (!thread) - return POLLERR; + return EPOLLERR; binder_inner_proc_lock(thread->proc); thread->looper |= BINDER_LOOPER_STATE_POLL; @@ -5981,9 +5990,9 @@ static void print_binder_transaction_ilocked(struct seq_file *m, } if (buffer->target_node) seq_printf(m, " node %d", buffer->target_node->debug_id); - seq_printf(m, " size %zd:%zd data %pK\n", + seq_printf(m, " size %zd:%zd offset %lx\n", buffer->data_size, buffer->offsets_size, - buffer->user_data); + proc->alloc.buffer - buffer->user_data); } static void print_binder_work_ilocked(struct seq_file *m, diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index f69d30c9f50f..e0e4dc38b692 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -26,7 +26,7 @@ #include "binder_alloc.h" #include "binder_trace.h" -struct list_lru binder_alloc_lru; +struct list_lru binder_freelist; static DEFINE_MUTEX(binder_alloc_mmap_lock); @@ -125,23 +125,20 @@ static void binder_insert_allocated_buffer_locked( static struct binder_buffer *binder_alloc_prepare_to_free_locked( struct binder_alloc *alloc, - uintptr_t user_ptr) + unsigned long user_ptr) { struct rb_node *n = alloc->allocated_buffers.rb_node; struct binder_buffer *buffer; - void __user *uptr; - - uptr = (void __user *)user_ptr; while (n) { buffer = rb_entry(n, struct binder_buffer, rb_node); BUG_ON(buffer->free); - if (uptr < buffer->user_data) + if (user_ptr < buffer->user_data) { n = n->rb_left; - else if (uptr > buffer->user_data) + } else if (user_ptr > buffer->user_data) { n = n->rb_right; - else { + } else { /* * Guard against user threads attempting to * free the buffer when in use by kernel or @@ -168,145 +165,168 @@ static struct binder_buffer *binder_alloc_prepare_to_free_locked( * Return: Pointer to buffer or NULL */ struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc, - uintptr_t user_ptr) + unsigned long user_ptr) { struct binder_buffer *buffer; - mutex_lock(&alloc->mutex); + spin_lock(&alloc->lock); buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr); - mutex_unlock(&alloc->mutex); + spin_unlock(&alloc->lock); return buffer; } -static int binder_update_page_range(struct binder_alloc *alloc, int allocate, - void __user *start, void __user *end) +static inline void +binder_set_installed_page(struct binder_lru_page *lru_page, + struct page *page) +{ + /* Pairs with acquire in binder_get_installed_page() */ + smp_store_release(&lru_page->page_ptr, page); +} + +static inline struct page * +binder_get_installed_page(struct binder_lru_page *lru_page) +{ + /* Pairs with release in binder_set_installed_page() */ + return smp_load_acquire(&lru_page->page_ptr); +} + +static void binder_lru_freelist_add(struct binder_alloc *alloc, + unsigned long start, unsigned long end) { - void __user *page_addr; - unsigned long user_page_addr; struct binder_lru_page *page; - struct vm_area_struct *vma = NULL; - struct mm_struct *mm = NULL; - bool need_mm = false; + unsigned long page_addr; - binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: %s pages %pK-%pK\n", alloc->pid, - allocate ? "allocate" : "free", start, end); + trace_binder_update_page_range(alloc, false, start, end); - if (end <= start) - return 0; + for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { + size_t index; + int ret; - trace_binder_update_page_range(alloc, allocate, start, end); + index = (page_addr - alloc->buffer) / PAGE_SIZE; + page = &alloc->pages[index]; - if (allocate == 0) - goto free_range; + if (!binder_get_installed_page(page)) + continue; - for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { - page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE]; - if (!page->page_ptr) { - need_mm = true; - break; - } + trace_binder_free_lru_start(alloc, index); + + ret = list_lru_add_obj(&binder_freelist, &page->lru); + 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) +{ + struct page *page; + int ret = 0; - if (need_mm && mmget_not_zero(alloc->mm)) - mm = alloc->mm; + if (!mmget_not_zero(alloc->mm)) + return -ESRCH; - if (mm) { - mmap_write_lock(mm); - vma = alloc->vma; + /* + * Protected with mmap_sem in write mode as multiple tasks + * might race to install the same page. + */ + mmap_write_lock(alloc->mm); + if (binder_get_installed_page(lru_page)) + goto out; + + if (!alloc->vma) { + pr_err("%d: %s failed, no vma\n", alloc->pid, __func__); + ret = -ESRCH; + goto out; } - if (!vma && need_mm) { - binder_alloc_debug(BINDER_DEBUG_USER_ERROR, - "%d: binder_alloc_buf failed to map pages in userspace, no vma\n", - alloc->pid); - goto err_no_vma; + page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); + if (!page) { + pr_err("%d: failed to allocate page\n", alloc->pid); + ret = -ENOMEM; + goto out; } - for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { + ret = vm_insert_page(alloc->vma, addr, page); + if (ret) { + 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; + } + + /* 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; +} + +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; + + start = buffer->user_data & PAGE_MASK; + final = PAGE_ALIGN(buffer->user_data + size); + + for (page_addr = start; page_addr < final; page_addr += PAGE_SIZE) { + unsigned long index; int ret; - bool on_lru; - size_t index; index = (page_addr - alloc->buffer) / PAGE_SIZE; page = &alloc->pages[index]; - if (page->page_ptr) { - trace_binder_alloc_lru_start(alloc, index); - - on_lru = list_lru_del_obj(&binder_alloc_lru, &page->lru); - WARN_ON(!on_lru); - - trace_binder_alloc_lru_end(alloc, index); + if (binder_get_installed_page(page)) continue; - } - - if (WARN_ON(!vma)) - goto err_page_ptr_cleared; trace_binder_alloc_page_start(alloc, index); - page->page_ptr = alloc_page(GFP_KERNEL | - __GFP_HIGHMEM | - __GFP_ZERO); - if (!page->page_ptr) { - pr_err("%d: binder_alloc_buf failed for page at %pK\n", - alloc->pid, page_addr); - goto err_alloc_page_failed; - } - page->alloc = alloc; - INIT_LIST_HEAD(&page->lru); - - user_page_addr = (uintptr_t)page_addr; - ret = vm_insert_page(vma, user_page_addr, page[0].page_ptr); - if (ret) { - pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n", - alloc->pid, user_page_addr); - goto err_vm_insert_page_failed; - } - if (index + 1 > alloc->pages_high) - alloc->pages_high = index + 1; + ret = binder_install_single_page(alloc, page, page_addr); + if (ret) + return ret; trace_binder_alloc_page_end(alloc, index); } - if (mm) { - mmap_write_unlock(mm); - mmput(mm); - } + return 0; +} -free_range: - for (page_addr = end - PAGE_SIZE; 1; page_addr -= PAGE_SIZE) { - bool ret; - size_t index; +/* The range of pages should exclude those shared with other buffers */ +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; + + trace_binder_update_page_range(alloc, true, start, end); + + for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { + unsigned long index; + bool on_lru; index = (page_addr - alloc->buffer) / PAGE_SIZE; page = &alloc->pages[index]; - trace_binder_free_lru_start(alloc, index); + if (page->page_ptr) { + trace_binder_alloc_lru_start(alloc, index); - ret = list_lru_add_obj(&binder_alloc_lru, &page->lru); - WARN_ON(!ret); + on_lru = list_lru_del_obj(&binder_freelist, &page->lru); + WARN_ON(!on_lru); - trace_binder_free_lru_end(alloc, index); - if (page_addr == start) - break; - continue; - -err_vm_insert_page_failed: - __free_page(page->page_ptr); - page->page_ptr = NULL; -err_alloc_page_failed: -err_page_ptr_cleared: - if (page_addr == start) - break; - } -err_no_vma: - if (mm) { - mmap_write_unlock(mm); - mmput(mm); + trace_binder_alloc_lru_end(alloc, index); + continue; + } + + if (index + 1 > alloc->pages_high) + alloc->pages_high = index + 1; } - return vma ? -ENOMEM : -ESRCH; } static inline void binder_alloc_set_vma(struct binder_alloc *alloc, @@ -323,7 +343,44 @@ static inline struct vm_area_struct *binder_alloc_get_vma( return smp_load_acquire(&alloc->vma); } -static bool debug_low_async_space_locked(struct binder_alloc *alloc, int pid) +static void debug_no_space_locked(struct binder_alloc *alloc) +{ + size_t largest_alloc_size = 0; + struct binder_buffer *buffer; + size_t allocated_buffers = 0; + size_t largest_free_size = 0; + size_t total_alloc_size = 0; + size_t total_free_size = 0; + size_t free_buffers = 0; + size_t buffer_size; + struct rb_node *n; + + for (n = rb_first(&alloc->allocated_buffers); n; n = rb_next(n)) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + buffer_size = binder_alloc_buffer_size(alloc, buffer); + allocated_buffers++; + total_alloc_size += buffer_size; + if (buffer_size > largest_alloc_size) + largest_alloc_size = buffer_size; + } + + for (n = rb_first(&alloc->free_buffers); n; n = rb_next(n)) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + buffer_size = binder_alloc_buffer_size(alloc, buffer); + free_buffers++; + total_free_size += buffer_size; + if (buffer_size > largest_free_size) + largest_free_size = buffer_size; + } + + binder_alloc_debug(BINDER_DEBUG_USER_ERROR, + "allocated: %zd (num: %zd largest: %zd), free: %zd (num: %zd largest: %zd)\n", + total_alloc_size, allocated_buffers, + largest_alloc_size, total_free_size, + free_buffers, largest_free_size); +} + +static bool debug_low_async_space_locked(struct binder_alloc *alloc) { /* * Find the amount and size of buffers allocated by the current caller; @@ -332,10 +389,20 @@ static bool debug_low_async_space_locked(struct binder_alloc *alloc, int pid) * and at some point we'll catch them in the act. This is more efficient * than keeping a map per pid. */ - struct rb_node *n; struct binder_buffer *buffer; size_t total_alloc_size = 0; + int pid = current->tgid; size_t num_buffers = 0; + struct rb_node *n; + + /* + * Only start detecting spammers once we have less than 20% of async + * space left (which is less than 10% of total buffer size). + */ + if (alloc->free_async_space >= alloc->buffer_size / 10) { + alloc->oneway_spam_detected = false; + return false; + } for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n)) { @@ -344,8 +411,7 @@ static bool debug_low_async_space_locked(struct binder_alloc *alloc, int pid) continue; if (!buffer->async_transaction) continue; - total_alloc_size += binder_alloc_buffer_size(alloc, buffer) - + sizeof(struct binder_buffer); + total_alloc_size += binder_alloc_buffer_size(alloc, buffer); num_buffers++; } @@ -366,58 +432,28 @@ static bool debug_low_async_space_locked(struct binder_alloc *alloc, int pid) return false; } +/* Callers preallocate @new_buffer, it is freed by this function if unused */ static struct binder_buffer *binder_alloc_new_buf_locked( struct binder_alloc *alloc, - size_t data_size, - size_t offsets_size, - size_t extra_buffers_size, - int is_async, - int pid) + struct binder_buffer *new_buffer, + size_t size, + int is_async) { struct rb_node *n = alloc->free_buffers.rb_node; + struct rb_node *best_fit = NULL; struct binder_buffer *buffer; + unsigned long next_used_page; + unsigned long curr_last_page; size_t buffer_size; - struct rb_node *best_fit = NULL; - void __user *has_page_addr; - void __user *end_page_addr; - size_t size, data_offsets_size; - int ret; - /* Check binder_alloc is fully initialized */ - if (!binder_alloc_get_vma(alloc)) { - binder_alloc_debug(BINDER_DEBUG_USER_ERROR, - "%d: binder_alloc_buf, no vma\n", - alloc->pid); - return ERR_PTR(-ESRCH); - } - - data_offsets_size = ALIGN(data_size, sizeof(void *)) + - ALIGN(offsets_size, sizeof(void *)); - - if (data_offsets_size < data_size || data_offsets_size < offsets_size) { - binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: got transaction with invalid size %zd-%zd\n", - alloc->pid, data_size, offsets_size); - return ERR_PTR(-EINVAL); - } - size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *)); - if (size < data_offsets_size || size < extra_buffers_size) { - binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: got transaction with invalid extra_buffers_size %zd\n", - alloc->pid, extra_buffers_size); - return ERR_PTR(-EINVAL); - } - if (is_async && - alloc->free_async_space < size + sizeof(struct binder_buffer)) { + if (is_async && alloc->free_async_space < size) { binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, "%d: binder_alloc_buf size %zd failed, no async space left\n", alloc->pid, size); - return ERR_PTR(-ENOSPC); + buffer = ERR_PTR(-ENOSPC); + goto out; } - /* Pad 0-size buffers so they get assigned unique addresses */ - size = max(size, sizeof(void *)); - while (n) { buffer = rb_entry(n, struct binder_buffer, rb_node); BUG_ON(!buffer->free); @@ -426,121 +462,92 @@ static struct binder_buffer *binder_alloc_new_buf_locked( if (size < buffer_size) { best_fit = n; n = n->rb_left; - } else if (size > buffer_size) + } else if (size > buffer_size) { n = n->rb_right; - else { + } else { best_fit = n; break; } } - if (best_fit == NULL) { - size_t allocated_buffers = 0; - size_t largest_alloc_size = 0; - size_t total_alloc_size = 0; - size_t free_buffers = 0; - size_t largest_free_size = 0; - size_t total_free_size = 0; - - for (n = rb_first(&alloc->allocated_buffers); n != NULL; - n = rb_next(n)) { - buffer = rb_entry(n, struct binder_buffer, rb_node); - buffer_size = binder_alloc_buffer_size(alloc, buffer); - allocated_buffers++; - total_alloc_size += buffer_size; - if (buffer_size > largest_alloc_size) - largest_alloc_size = buffer_size; - } - for (n = rb_first(&alloc->free_buffers); n != NULL; - n = rb_next(n)) { - buffer = rb_entry(n, struct binder_buffer, rb_node); - buffer_size = binder_alloc_buffer_size(alloc, buffer); - free_buffers++; - total_free_size += buffer_size; - if (buffer_size > largest_free_size) - largest_free_size = buffer_size; - } + + if (unlikely(!best_fit)) { binder_alloc_debug(BINDER_DEBUG_USER_ERROR, "%d: binder_alloc_buf size %zd failed, no address space\n", alloc->pid, size); - binder_alloc_debug(BINDER_DEBUG_USER_ERROR, - "allocated: %zd (num: %zd largest: %zd), free: %zd (num: %zd largest: %zd)\n", - total_alloc_size, allocated_buffers, - largest_alloc_size, total_free_size, - free_buffers, largest_free_size); - return ERR_PTR(-ENOSPC); + debug_no_space_locked(alloc); + buffer = ERR_PTR(-ENOSPC); + goto out; } - if (n == NULL) { + + if (buffer_size != size) { + /* Found an oversized buffer and needs to be split */ buffer = rb_entry(best_fit, struct binder_buffer, rb_node); buffer_size = binder_alloc_buffer_size(alloc, buffer); + + WARN_ON(n || buffer_size == size); + new_buffer->user_data = buffer->user_data + size; + list_add(&new_buffer->entry, &buffer->entry); + new_buffer->free = 1; + binder_insert_free_buffer(alloc, new_buffer); + new_buffer = NULL; } binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, "%d: binder_alloc_buf size %zd got buffer %pK size %zd\n", alloc->pid, size, buffer, buffer_size); - has_page_addr = (void __user *) - (((uintptr_t)buffer->user_data + buffer_size) & PAGE_MASK); - WARN_ON(n && buffer_size != size); - end_page_addr = - (void __user *)PAGE_ALIGN((uintptr_t)buffer->user_data + size); - if (end_page_addr > has_page_addr) - end_page_addr = has_page_addr; - ret = binder_update_page_range(alloc, 1, (void __user *) - PAGE_ALIGN((uintptr_t)buffer->user_data), end_page_addr); - if (ret) - return ERR_PTR(ret); - - if (buffer_size != size) { - struct binder_buffer *new_buffer; - - new_buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); - if (!new_buffer) { - pr_err("%s: %d failed to alloc new buffer struct\n", - __func__, alloc->pid); - goto err_alloc_buf_struct_failed; - } - new_buffer->user_data = (u8 __user *)buffer->user_data + size; - list_add(&new_buffer->entry, &buffer->entry); - new_buffer->free = 1; - binder_insert_free_buffer(alloc, new_buffer); - } + /* + * Now we remove the pages from the freelist. A clever calculation + * with buffer_size determines if the last page is shared with an + * adjacent in-use buffer. In such case, the page has been already + * removed from the freelist so we trim our range short. + */ + next_used_page = (buffer->user_data + buffer_size) & PAGE_MASK; + curr_last_page = PAGE_ALIGN(buffer->user_data + size); + binder_lru_freelist_del(alloc, PAGE_ALIGN(buffer->user_data), + min(next_used_page, curr_last_page)); - rb_erase(best_fit, &alloc->free_buffers); + rb_erase(&buffer->rb_node, &alloc->free_buffers); buffer->free = 0; buffer->allow_user_free = 0; binder_insert_allocated_buffer_locked(alloc, buffer); - binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: binder_alloc_buf size %zd got %pK\n", - alloc->pid, size, buffer); - buffer->data_size = data_size; - buffer->offsets_size = offsets_size; buffer->async_transaction = is_async; - buffer->extra_buffers_size = extra_buffers_size; - buffer->pid = pid; buffer->oneway_spam_suspect = false; if (is_async) { - alloc->free_async_space -= size + sizeof(struct binder_buffer); + alloc->free_async_space -= size; binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, "%d: binder_alloc_buf size %zd async free %zd\n", alloc->pid, size, alloc->free_async_space); - if (alloc->free_async_space < alloc->buffer_size / 10) { - /* - * Start detecting spammers once we have less than 20% - * of async space left (which is less than 10% of total - * buffer size). - */ - buffer->oneway_spam_suspect = debug_low_async_space_locked(alloc, pid); - } else { - alloc->oneway_spam_detected = false; - } + if (debug_low_async_space_locked(alloc)) + buffer->oneway_spam_suspect = true; } + +out: + /* Discard possibly unused new_buffer */ + kfree(new_buffer); return buffer; +} -err_alloc_buf_struct_failed: - binder_update_page_range(alloc, 0, (void __user *) - PAGE_ALIGN((uintptr_t)buffer->user_data), - end_page_addr); - return ERR_PTR(-ENOMEM); +/* Calculate the sanitized total size, returns 0 for invalid request */ +static inline size_t sanitized_size(size_t data_size, + size_t offsets_size, + size_t extra_buffers_size) +{ + size_t total, tmp; + + /* Align to pointer size and check for overflows */ + tmp = ALIGN(data_size, sizeof(void *)) + + ALIGN(offsets_size, sizeof(void *)); + if (tmp < data_size || tmp < offsets_size) + return 0; + total = tmp + ALIGN(extra_buffers_size, sizeof(void *)); + if (total < tmp || total < extra_buffers_size) + return 0; + + /* Pad 0-sized buffers so they get a unique address */ + total = max(total, sizeof(void *)); + + return total; } /** @@ -550,87 +557,101 @@ err_alloc_buf_struct_failed: * @offsets_size: user specified buffer offset * @extra_buffers_size: size of extra space for meta-data (eg, security context) * @is_async: buffer for async transaction - * @pid: pid to attribute allocation to (used for debugging) * * Allocate a new buffer given the requested sizes. Returns * the kernel version of the buffer pointer. The size allocated * is the sum of the three given sizes (each rounded up to * pointer-sized boundary) * - * Return: The allocated buffer or %NULL if error + * Return: The allocated buffer or %ERR_PTR(-errno) if error */ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, size_t data_size, size_t offsets_size, size_t extra_buffers_size, - int is_async, - int pid) + int is_async) { - struct binder_buffer *buffer; + struct binder_buffer *buffer, *next; + size_t size; + int ret; + + /* Check binder_alloc is fully initialized */ + if (!binder_alloc_get_vma(alloc)) { + binder_alloc_debug(BINDER_DEBUG_USER_ERROR, + "%d: binder_alloc_buf, no vma\n", + alloc->pid); + return ERR_PTR(-ESRCH); + } + + size = sanitized_size(data_size, offsets_size, extra_buffers_size); + if (unlikely(!size)) { + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: got transaction with invalid size %zd-%zd-%zd\n", + alloc->pid, data_size, offsets_size, + extra_buffers_size); + return ERR_PTR(-EINVAL); + } - mutex_lock(&alloc->mutex); - buffer = binder_alloc_new_buf_locked(alloc, data_size, offsets_size, - extra_buffers_size, is_async, pid); - mutex_unlock(&alloc->mutex); + /* Preallocate the next buffer */ + next = kzalloc(sizeof(*next), GFP_KERNEL); + if (!next) + return ERR_PTR(-ENOMEM); + + spin_lock(&alloc->lock); + buffer = binder_alloc_new_buf_locked(alloc, next, size, is_async); + if (IS_ERR(buffer)) { + spin_unlock(&alloc->lock); + goto out; + } + + buffer->data_size = data_size; + buffer->offsets_size = offsets_size; + buffer->extra_buffers_size = extra_buffers_size; + buffer->pid = current->tgid; + spin_unlock(&alloc->lock); + + ret = binder_install_buffer_pages(alloc, buffer, size); + if (ret) { + binder_alloc_free_buf(alloc, buffer); + buffer = ERR_PTR(ret); + } +out: return buffer; } -static void __user *buffer_start_page(struct binder_buffer *buffer) +static unsigned long buffer_start_page(struct binder_buffer *buffer) { - return (void __user *)((uintptr_t)buffer->user_data & PAGE_MASK); + return buffer->user_data & PAGE_MASK; } -static void __user *prev_buffer_end_page(struct binder_buffer *buffer) +static unsigned long prev_buffer_end_page(struct binder_buffer *buffer) { - return (void __user *) - (((uintptr_t)(buffer->user_data) - 1) & PAGE_MASK); + return (buffer->user_data - 1) & PAGE_MASK; } static void binder_delete_free_buffer(struct binder_alloc *alloc, struct binder_buffer *buffer) { - struct binder_buffer *prev, *next = NULL; - bool to_free = true; + struct binder_buffer *prev, *next; + + if (PAGE_ALIGNED(buffer->user_data)) + goto skip_freelist; BUG_ON(alloc->buffers.next == &buffer->entry); prev = binder_buffer_prev(buffer); BUG_ON(!prev->free); - if (prev_buffer_end_page(prev) == buffer_start_page(buffer)) { - to_free = false; - binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: merge free, buffer %pK share page with %pK\n", - alloc->pid, buffer->user_data, - prev->user_data); - } + if (prev_buffer_end_page(prev) == buffer_start_page(buffer)) + goto skip_freelist; if (!list_is_last(&buffer->entry, &alloc->buffers)) { next = binder_buffer_next(buffer); - if (buffer_start_page(next) == buffer_start_page(buffer)) { - to_free = false; - binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: merge free, buffer %pK share page with %pK\n", - alloc->pid, - buffer->user_data, - next->user_data); - } - } - - if (PAGE_ALIGNED(buffer->user_data)) { - binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: merge free, buffer start %pK is page aligned\n", - alloc->pid, buffer->user_data); - to_free = false; + if (buffer_start_page(next) == buffer_start_page(buffer)) + goto skip_freelist; } - if (to_free) { - binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: merge free, buffer %pK do not share page with %pK or %pK\n", - alloc->pid, buffer->user_data, - prev->user_data, - next ? next->user_data : NULL); - binder_update_page_range(alloc, 0, buffer_start_page(buffer), - buffer_start_page(buffer) + PAGE_SIZE); - } + binder_lru_freelist_add(alloc, buffer_start_page(buffer), + buffer_start_page(buffer) + PAGE_SIZE); +skip_freelist: list_del(&buffer->entry); kfree(buffer); } @@ -657,17 +678,14 @@ static void binder_free_buf_locked(struct binder_alloc *alloc, BUG_ON(buffer->user_data > alloc->buffer + alloc->buffer_size); if (buffer->async_transaction) { - alloc->free_async_space += buffer_size + sizeof(struct binder_buffer); - + alloc->free_async_space += buffer_size; binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, "%d: binder_free_buf size %zd async free %zd\n", alloc->pid, size, alloc->free_async_space); } - binder_update_page_range(alloc, 0, - (void __user *)PAGE_ALIGN((uintptr_t)buffer->user_data), - (void __user *)(((uintptr_t) - buffer->user_data + buffer_size) & PAGE_MASK)); + binder_lru_freelist_add(alloc, PAGE_ALIGN(buffer->user_data), + (buffer->user_data + buffer_size) & PAGE_MASK); rb_erase(&buffer->rb_node, &alloc->allocated_buffers); buffer->free = 1; @@ -691,8 +709,68 @@ static void binder_free_buf_locked(struct binder_alloc *alloc, binder_insert_free_buffer(alloc, buffer); } +/** + * binder_alloc_get_page() - get kernel pointer for given buffer offset + * @alloc: binder_alloc for this proc + * @buffer: binder buffer to be accessed + * @buffer_offset: offset into @buffer data + * @pgoffp: address to copy final page offset to + * + * Lookup the struct page corresponding to the address + * at @buffer_offset into @buffer->user_data. If @pgoffp is not + * NULL, the byte-offset into the page is written there. + * + * The caller is responsible to ensure that the offset points + * to a valid address within the @buffer and that @buffer is + * not freeable by the user. Since it can't be freed, we are + * guaranteed that the corresponding elements of @alloc->pages[] + * cannot change. + * + * Return: struct page + */ +static struct page *binder_alloc_get_page(struct binder_alloc *alloc, + struct binder_buffer *buffer, + binder_size_t buffer_offset, + pgoff_t *pgoffp) +{ + binder_size_t buffer_space_offset = buffer_offset + + (buffer->user_data - alloc->buffer); + 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; +} + +/** + * binder_alloc_clear_buf() - zero out buffer + * @alloc: binder_alloc for this proc + * @buffer: binder buffer to be cleared + * + * memset the given buffer to 0 + */ static void binder_alloc_clear_buf(struct binder_alloc *alloc, - struct binder_buffer *buffer); + struct binder_buffer *buffer) +{ + size_t bytes = binder_alloc_buffer_size(alloc, buffer); + binder_size_t buffer_offset = 0; + + while (bytes) { + unsigned long size; + struct page *page; + pgoff_t pgoff; + + page = binder_alloc_get_page(alloc, buffer, + buffer_offset, &pgoff); + size = min_t(size_t, bytes, PAGE_SIZE - pgoff); + memset_page(page, pgoff, 0, size); + bytes -= size; + buffer_offset += size; + } +} + /** * binder_alloc_free_buf() - free a binder buffer * @alloc: binder_alloc for this proc @@ -706,18 +784,18 @@ 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_alloc_free_buf_locked(). However, that could - * increase contention for the alloc mutex if clear_on_free - * is used frequently for large buffers. The mutex is not + * 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 * needed for correctness here. */ if (buffer->clear_on_free) { binder_alloc_clear_buf(alloc, buffer); buffer->clear_on_free = false; } - mutex_lock(&alloc->mutex); + spin_lock(&alloc->lock); binder_free_buf_locked(alloc, buffer); - mutex_unlock(&alloc->mutex); + spin_unlock(&alloc->lock); } /** @@ -736,9 +814,9 @@ void binder_alloc_free_buf(struct binder_alloc *alloc, int binder_alloc_mmap_handler(struct binder_alloc *alloc, struct vm_area_struct *vma) { - int ret; - const char *failure_string; struct binder_buffer *buffer; + const char *failure_string; + int ret, i; if (unlikely(vma->vm_mm != alloc->mm)) { ret = -EINVAL; @@ -756,7 +834,7 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, SZ_4M); mutex_unlock(&binder_alloc_mmap_lock); - alloc->buffer = (void __user *)vma->vm_start; + alloc->buffer = vma->vm_start; alloc->pages = kcalloc(alloc->buffer_size / PAGE_SIZE, sizeof(alloc->pages[0]), @@ -767,6 +845,11 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, 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; @@ -789,7 +872,7 @@ err_alloc_buf_struct_failed: kfree(alloc->pages); alloc->pages = NULL; err_alloc_pages_failed: - alloc->buffer = NULL; + alloc->buffer = 0; mutex_lock(&binder_alloc_mmap_lock); alloc->buffer_size = 0; err_already_mapped: @@ -810,7 +893,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) struct binder_buffer *buffer; buffers = 0; - mutex_lock(&alloc->mutex); + spin_lock(&alloc->lock); BUG_ON(alloc->vma); while ((n = rb_first(&alloc->allocated_buffers))) { @@ -842,25 +925,25 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) int i; for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { - void __user *page_addr; + unsigned long page_addr; bool on_lru; if (!alloc->pages[i].page_ptr) continue; - on_lru = list_lru_del_obj(&binder_alloc_lru, - &alloc->pages[i].lru); + on_lru = list_lru_del_obj(&binder_freelist, + &alloc->pages[i].lru); page_addr = alloc->buffer + i * PAGE_SIZE; binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%s: %d: page %d at %pK %s\n", - __func__, alloc->pid, i, page_addr, + "%s: %d: page %d %s\n", + __func__, alloc->pid, i, on_lru ? "on lru" : "active"); __free_page(alloc->pages[i].page_ptr); page_count++; } kfree(alloc->pages); } - mutex_unlock(&alloc->mutex); + spin_unlock(&alloc->lock); if (alloc->mm) mmdrop(alloc->mm); @@ -869,16 +952,6 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) __func__, alloc->pid, buffers, page_count); } -static void print_binder_buffer(struct seq_file *m, const char *prefix, - struct binder_buffer *buffer) -{ - seq_printf(m, "%s %d: %pK size %zd:%zd:%zd %s\n", - prefix, buffer->debug_id, buffer->user_data, - buffer->data_size, buffer->offsets_size, - buffer->extra_buffers_size, - buffer->transaction ? "active" : "delivered"); -} - /** * binder_alloc_print_allocated() - print buffer info * @m: seq_file for output via seq_printf() @@ -890,13 +963,20 @@ static void print_binder_buffer(struct seq_file *m, const char *prefix, void binder_alloc_print_allocated(struct seq_file *m, struct binder_alloc *alloc) { + struct binder_buffer *buffer; struct rb_node *n; - mutex_lock(&alloc->mutex); - for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n)) - print_binder_buffer(m, " buffer", - rb_entry(n, struct binder_buffer, rb_node)); - mutex_unlock(&alloc->mutex); + spin_lock(&alloc->lock); + 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->data_size, buffer->offsets_size, + buffer->extra_buffers_size, + buffer->transaction ? "active" : "delivered"); + } + spin_unlock(&alloc->lock); } /** @@ -913,7 +993,7 @@ void binder_alloc_print_pages(struct seq_file *m, int lru = 0; int free = 0; - mutex_lock(&alloc->mutex); + spin_lock(&alloc->lock); /* * Make sure the binder_alloc is fully initialized, otherwise we might * read inconsistent state. @@ -929,7 +1009,7 @@ void binder_alloc_print_pages(struct seq_file *m, lru++; } } - mutex_unlock(&alloc->mutex); + spin_unlock(&alloc->lock); seq_printf(m, " pages: %d:%d:%d\n", active, lru, free); seq_printf(m, " pages high watermark: %zu\n", alloc->pages_high); } @@ -945,10 +1025,10 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc) struct rb_node *n; int count = 0; - mutex_lock(&alloc->mutex); + spin_lock(&alloc->lock); for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n)) count++; - mutex_unlock(&alloc->mutex); + spin_unlock(&alloc->lock); return count; } @@ -981,33 +1061,39 @@ enum lru_status binder_alloc_free_page(struct list_head *item, void *cb_arg) __must_hold(lock) { - struct mm_struct *mm = NULL; - struct binder_lru_page *page = container_of(item, - struct binder_lru_page, - lru); - struct binder_alloc *alloc; - uintptr_t page_addr; - size_t index; + struct binder_lru_page *page = container_of(item, typeof(*page), lru); + struct binder_alloc *alloc = page->alloc; + struct mm_struct *mm = alloc->mm; struct vm_area_struct *vma; + struct page *page_to_free; + unsigned long page_addr; + size_t index; - alloc = page->alloc; - if (!mutex_trylock(&alloc->mutex)) - goto err_get_alloc_mutex_failed; - + 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 = (uintptr_t)alloc->buffer + index * PAGE_SIZE; + page_addr = alloc->buffer + index * PAGE_SIZE; - mm = alloc->mm; - if (!mmget_not_zero(mm)) - goto err_mmget; - if (!mmap_read_trylock(mm)) - goto err_mmap_read_lock_failed; - vma = binder_alloc_get_vma(alloc); + vma = vma_lookup(mm, page_addr); + if (vma && vma != binder_alloc_get_vma(alloc)) + goto err_invalid_vma; + + trace_binder_unmap_kernel_start(alloc, index); + + page_to_free = page->page_ptr; + page->page_ptr = NULL; + + trace_binder_unmap_kernel_end(alloc, index); list_lru_isolate(lru, item); + spin_unlock(&alloc->lock); spin_unlock(lock); if (vma) { @@ -1017,39 +1103,35 @@ enum lru_status binder_alloc_free_page(struct list_head *item, trace_binder_unmap_user_end(alloc, index); } + mmap_read_unlock(mm); mmput_async(mm); - - trace_binder_unmap_kernel_start(alloc, index); - - __free_page(page->page_ptr); - page->page_ptr = NULL; - - trace_binder_unmap_kernel_end(alloc, index); + __free_page(page_to_free); spin_lock(lock); - mutex_unlock(&alloc->mutex); return LRU_REMOVED_RETRY; +err_invalid_vma: +err_page_already_freed: + spin_unlock(&alloc->lock); +err_get_alloc_lock_failed: + mmap_read_unlock(mm); err_mmap_read_lock_failed: mmput_async(mm); err_mmget: -err_page_already_freed: - mutex_unlock(&alloc->mutex); -err_get_alloc_mutex_failed: return LRU_SKIP; } static unsigned long binder_shrink_count(struct shrinker *shrink, struct shrink_control *sc) { - return list_lru_count(&binder_alloc_lru); + return list_lru_count(&binder_freelist); } static unsigned long binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) { - return list_lru_walk(&binder_alloc_lru, binder_alloc_free_page, + return list_lru_walk(&binder_freelist, binder_alloc_free_page, NULL, sc->nr_to_scan); } @@ -1067,7 +1149,7 @@ void binder_alloc_init(struct binder_alloc *alloc) alloc->pid = current->group_leader->pid; alloc->mm = current->mm; mmgrab(alloc->mm); - mutex_init(&alloc->mutex); + spin_lock_init(&alloc->lock); INIT_LIST_HEAD(&alloc->buffers); } @@ -1075,13 +1157,13 @@ int binder_alloc_shrinker_init(void) { int ret; - ret = list_lru_init(&binder_alloc_lru); + ret = list_lru_init(&binder_freelist); if (ret) return ret; binder_shrinker = shrinker_alloc(0, "android-binder"); if (!binder_shrinker) { - list_lru_destroy(&binder_alloc_lru); + list_lru_destroy(&binder_freelist); return -ENOMEM; } @@ -1096,7 +1178,7 @@ int binder_alloc_shrinker_init(void) void binder_alloc_shrinker_exit(void) { shrinker_free(binder_shrinker); - list_lru_destroy(&binder_alloc_lru); + list_lru_destroy(&binder_freelist); } /** @@ -1132,68 +1214,6 @@ static inline bool check_buffer(struct binder_alloc *alloc, } /** - * binder_alloc_get_page() - get kernel pointer for given buffer offset - * @alloc: binder_alloc for this proc - * @buffer: binder buffer to be accessed - * @buffer_offset: offset into @buffer data - * @pgoffp: address to copy final page offset to - * - * Lookup the struct page corresponding to the address - * at @buffer_offset into @buffer->user_data. If @pgoffp is not - * NULL, the byte-offset into the page is written there. - * - * The caller is responsible to ensure that the offset points - * to a valid address within the @buffer and that @buffer is - * not freeable by the user. Since it can't be freed, we are - * guaranteed that the corresponding elements of @alloc->pages[] - * cannot change. - * - * Return: struct page - */ -static struct page *binder_alloc_get_page(struct binder_alloc *alloc, - struct binder_buffer *buffer, - binder_size_t buffer_offset, - pgoff_t *pgoffp) -{ - binder_size_t buffer_space_offset = buffer_offset + - (buffer->user_data - alloc->buffer); - 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; -} - -/** - * binder_alloc_clear_buf() - zero out buffer - * @alloc: binder_alloc for this proc - * @buffer: binder buffer to be cleared - * - * memset the given buffer to 0 - */ -static void binder_alloc_clear_buf(struct binder_alloc *alloc, - struct binder_buffer *buffer) -{ - size_t bytes = binder_alloc_buffer_size(alloc, buffer); - binder_size_t buffer_offset = 0; - - while (bytes) { - unsigned long size; - struct page *page; - pgoff_t pgoff; - - page = binder_alloc_get_page(alloc, buffer, - buffer_offset, &pgoff); - size = min_t(size_t, bytes, PAGE_SIZE - pgoff); - memset_page(page, pgoff, 0, size); - bytes -= size; - buffer_offset += size; - } -} - -/** * binder_alloc_copy_user_to_buffer() - copy src user to tgt user * @alloc: binder_alloc for this proc * @buffer: binder buffer to be accessed diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index dc1e2b01dd64..70387234477e 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -9,13 +9,13 @@ #include <linux/rbtree.h> #include <linux/list.h> #include <linux/mm.h> -#include <linux/rtmutex.h> +#include <linux/spinlock.h> #include <linux/vmalloc.h> #include <linux/slab.h> #include <linux/list_lru.h> #include <uapi/linux/android/binder.h> -extern struct list_lru binder_alloc_lru; +extern struct list_lru binder_freelist; struct binder_transaction; /** @@ -49,21 +49,19 @@ struct binder_buffer { unsigned async_transaction:1; unsigned oneway_spam_suspect:1; unsigned debug_id:27; - struct binder_transaction *transaction; - struct binder_node *target_node; size_t data_size; size_t offsets_size; size_t extra_buffers_size; - void __user *user_data; - int pid; + unsigned long user_data; + int pid; }; /** * struct binder_lru_page - page object used for binder shrinker * @page_ptr: pointer to physical page in mmap'd space - * @lru: entry in binder_alloc_lru + * @lru: entry in binder_freelist * @alloc: binder_alloc for a proc */ struct binder_lru_page { @@ -74,7 +72,7 @@ struct binder_lru_page { /** * struct binder_alloc - per-binder proc state for binder allocator - * @mutex: protects binder_alloc fields + * @lock: protects binder_alloc fields * @vma: vm_area_struct passed to mmap_handler * (invariant after mmap) * @mm: copy of task->mm (invariant after open) @@ -98,10 +96,10 @@ struct binder_lru_page { * struct binder_buffer objects used to track the user buffers */ struct binder_alloc { - struct mutex mutex; + spinlock_t lock; struct vm_area_struct *vma; struct mm_struct *mm; - void __user *buffer; + unsigned long buffer; struct list_head buffers; struct rb_root free_buffers; struct rb_root allocated_buffers; @@ -121,27 +119,26 @@ static inline void binder_selftest_alloc(struct binder_alloc *alloc) {} enum lru_status binder_alloc_free_page(struct list_head *item, struct list_lru_one *lru, spinlock_t *lock, void *cb_arg); -extern struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, - size_t data_size, - size_t offsets_size, - size_t extra_buffers_size, - int is_async, - int pid); -extern void binder_alloc_init(struct binder_alloc *alloc); -extern int binder_alloc_shrinker_init(void); -extern void binder_alloc_shrinker_exit(void); -extern void binder_alloc_vma_close(struct binder_alloc *alloc); -extern struct binder_buffer * +struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, + size_t data_size, + size_t offsets_size, + size_t extra_buffers_size, + int is_async); +void binder_alloc_init(struct binder_alloc *alloc); +int binder_alloc_shrinker_init(void); +void binder_alloc_shrinker_exit(void); +void binder_alloc_vma_close(struct binder_alloc *alloc); +struct binder_buffer * binder_alloc_prepare_to_free(struct binder_alloc *alloc, - uintptr_t user_ptr); -extern void binder_alloc_free_buf(struct binder_alloc *alloc, - struct binder_buffer *buffer); -extern int binder_alloc_mmap_handler(struct binder_alloc *alloc, - struct vm_area_struct *vma); -extern void binder_alloc_deferred_release(struct binder_alloc *alloc); -extern int binder_alloc_get_allocated_count(struct binder_alloc *alloc); -extern void binder_alloc_print_allocated(struct seq_file *m, - struct binder_alloc *alloc); + unsigned long user_ptr); +void binder_alloc_free_buf(struct binder_alloc *alloc, + struct binder_buffer *buffer); +int binder_alloc_mmap_handler(struct binder_alloc *alloc, + struct vm_area_struct *vma); +void binder_alloc_deferred_release(struct binder_alloc *alloc); +int binder_alloc_get_allocated_count(struct binder_alloc *alloc); +void binder_alloc_print_allocated(struct seq_file *m, + struct binder_alloc *alloc); void binder_alloc_print_pages(struct seq_file *m, struct binder_alloc *alloc); @@ -156,9 +153,9 @@ binder_alloc_get_free_async_space(struct binder_alloc *alloc) { size_t free_async_space; - mutex_lock(&alloc->mutex); + spin_lock(&alloc->lock); free_async_space = alloc->free_async_space; - mutex_unlock(&alloc->mutex); + spin_unlock(&alloc->lock); return free_async_space; } diff --git a/drivers/android/binder_alloc_selftest.c b/drivers/android/binder_alloc_selftest.c index c2b323bc3b3a..81442fe20a69 100644 --- a/drivers/android/binder_alloc_selftest.c +++ b/drivers/android/binder_alloc_selftest.c @@ -72,6 +72,10 @@ enum buf_end_align_type { * buf1 ]|[ buf2 | buf2 | buf2 ][ ... */ NEXT_NEXT_UNALIGNED, + /** + * @LOOP_END: The number of enum values in &buf_end_align_type. + * It is used for controlling loop termination. + */ LOOP_END, }; @@ -93,11 +97,11 @@ static bool check_buffer_pages_allocated(struct binder_alloc *alloc, struct binder_buffer *buffer, size_t size) { - void __user *page_addr; - void __user *end; + unsigned long page_addr; + unsigned long end; int page_index; - end = (void __user *)PAGE_ALIGN((uintptr_t)buffer->user_data + size); + 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; @@ -119,7 +123,7 @@ static void binder_selftest_alloc_buf(struct binder_alloc *alloc, int i; for (i = 0; i < BUFFER_NUM; i++) { - buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0, 0); + buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0); if (IS_ERR(buffers[i]) || !check_buffer_pages_allocated(alloc, buffers[i], sizes[i])) { @@ -158,8 +162,8 @@ static void binder_selftest_free_page(struct binder_alloc *alloc) int i; unsigned long count; - while ((count = list_lru_count(&binder_alloc_lru))) { - list_lru_walk(&binder_alloc_lru, binder_alloc_free_page, + while ((count = list_lru_count(&binder_freelist))) { + list_lru_walk(&binder_freelist, binder_alloc_free_page, NULL, count); } @@ -183,7 +187,7 @@ static void binder_selftest_alloc_free(struct binder_alloc *alloc, /* Allocate from lru. */ binder_selftest_alloc_buf(alloc, buffers, sizes, seq); - if (list_lru_count(&binder_alloc_lru)) + if (list_lru_count(&binder_freelist)) pr_err("lru list should be empty but is not\n"); binder_selftest_free_buf(alloc, buffers, sizes, seq, end); diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h index 8cc07e6a4273..fe38c6fc65d0 100644 --- a/drivers/android/binder_trace.h +++ b/drivers/android/binder_trace.h @@ -317,7 +317,7 @@ DEFINE_EVENT(binder_buffer_class, binder_transaction_update_buffer_release, TRACE_EVENT(binder_update_page_range, TP_PROTO(struct binder_alloc *alloc, bool allocate, - void __user *start, void __user *end), + unsigned long start, unsigned long end), TP_ARGS(alloc, allocate, start, end), TP_STRUCT__entry( __field(int, proc) diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 1224ab7aa070..3001d754ac36 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -29,7 +29,6 @@ #include <linux/uaccess.h> #include <linux/user_namespace.h> #include <linux/xarray.h> -#include <uapi/asm-generic/errno-base.h> #include <uapi/linux/android/binder.h> #include <uapi/linux/android/binderfs.h> diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 3a5f3255f51b..da2e74fce2d9 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -48,6 +48,7 @@ enum { enum board_ids { /* board IDs by feature in alphabetical order */ board_ahci, + board_ahci_43bit_dma, board_ahci_ign_iferr, board_ahci_low_power, board_ahci_no_debounce_delay, @@ -128,6 +129,13 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, + [board_ahci_43bit_dma] = { + AHCI_HFLAGS (AHCI_HFLAG_43BIT_ONLY), + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ops, + }, [board_ahci_ign_iferr] = { AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR), .flags = AHCI_FLAG_COMMON, @@ -597,14 +605,14 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(PROMISE, 0x3f20), board_ahci }, /* PDC42819 */ { PCI_VDEVICE(PROMISE, 0x3781), board_ahci }, /* FastTrak TX8660 ahci-mode */ - /* Asmedia */ - { PCI_VDEVICE(ASMEDIA, 0x0601), board_ahci }, /* ASM1060 */ - { PCI_VDEVICE(ASMEDIA, 0x0602), board_ahci }, /* ASM1060 */ - { PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci }, /* ASM1061 */ - { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1062 */ - { PCI_VDEVICE(ASMEDIA, 0x0621), board_ahci }, /* ASM1061R */ - { PCI_VDEVICE(ASMEDIA, 0x0622), board_ahci }, /* ASM1062R */ - { PCI_VDEVICE(ASMEDIA, 0x0624), board_ahci }, /* ASM1062+JMB575 */ + /* ASMedia */ + { PCI_VDEVICE(ASMEDIA, 0x0601), board_ahci_43bit_dma }, /* ASM1060 */ + { PCI_VDEVICE(ASMEDIA, 0x0602), board_ahci_43bit_dma }, /* ASM1060 */ + { PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci_43bit_dma }, /* ASM1061 */ + { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci_43bit_dma }, /* ASM1061/1062 */ + { PCI_VDEVICE(ASMEDIA, 0x0621), board_ahci_43bit_dma }, /* ASM1061R */ + { PCI_VDEVICE(ASMEDIA, 0x0622), board_ahci_43bit_dma }, /* ASM1062R */ + { PCI_VDEVICE(ASMEDIA, 0x0624), board_ahci_43bit_dma }, /* ASM1062+JMB575 */ { PCI_VDEVICE(ASMEDIA, 0x1062), board_ahci }, /* ASM1062A */ { PCI_VDEVICE(ASMEDIA, 0x1064), board_ahci }, /* ASM1064 */ { PCI_VDEVICE(ASMEDIA, 0x1164), board_ahci }, /* ASM1164 */ @@ -663,6 +671,11 @@ MODULE_PARM_DESC(mobile_lpm_policy, "Default LPM policy for mobile chipsets"); static void ahci_pci_save_initial_config(struct pci_dev *pdev, struct ahci_host_priv *hpriv) { + if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && pdev->device == 0x1166) { + dev_info(&pdev->dev, "ASM1166 has only six ports\n"); + hpriv->saved_port_map = 0x3f; + } + if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361) { dev_info(&pdev->dev, "JMB361 has only one port\n"); hpriv->saved_port_map = 1; @@ -949,11 +962,20 @@ static int ahci_pci_device_resume(struct device *dev) #endif /* CONFIG_PM */ -static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac) +static int ahci_configure_dma_masks(struct pci_dev *pdev, + struct ahci_host_priv *hpriv) { - const int dma_bits = using_dac ? 64 : 32; + int dma_bits; int rc; + if (hpriv->cap & HOST_CAP_64) { + dma_bits = 64; + if (hpriv->flags & AHCI_HFLAG_43BIT_ONLY) + dma_bits = 43; + } else { + dma_bits = 32; + } + /* * If the device fixup already set the dma_mask to some non-standard * value, don't extend it here. This happens on STA2X11, for example. @@ -1926,7 +1948,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ahci_gtf_filter_workaround(host); /* initialize adapter */ - rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64); + rc = ahci_configure_dma_masks(pdev, hpriv); if (rc) return rc; diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 4bae95b06ae3..df8f8a1a3a34 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -247,6 +247,7 @@ enum { AHCI_HFLAG_SUSPEND_PHYS = BIT(26), /* handle PHYs during suspend/resume */ AHCI_HFLAG_NO_SXS = BIT(28), /* SXS not supported */ + AHCI_HFLAG_43BIT_ONLY = BIT(29), /* 43bit DMA addr limit */ /* ap->flags bits */ diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index b6656c287175..0fb1934875f2 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -784,7 +784,7 @@ bool sata_lpm_ignore_phy_events(struct ata_link *link) EXPORT_SYMBOL_GPL(sata_lpm_ignore_phy_events); static const char *ata_lpm_policy_names[] = { - [ATA_LPM_UNKNOWN] = "max_performance", + [ATA_LPM_UNKNOWN] = "keep_firmware_settings", [ATA_LPM_MAX_POWER] = "max_performance", [ATA_LPM_MED_POWER] = "medium_power", [ATA_LPM_MED_POWER_WITH_DIPM] = "med_power_with_dipm", diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c index e327a0229dc1..e7f713cd70d3 100644 --- a/drivers/atm/idt77252.c +++ b/drivers/atm/idt77252.c @@ -2930,6 +2930,8 @@ open_card_ubr0(struct idt77252_dev *card) vc->scq = alloc_scq(card, vc->class); if (!vc->scq) { printk("%s: can't get SCQ.\n", card->name); + kfree(card->vcs[0]); + card->vcs[0] = NULL; return -ENOMEM; } diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 5aaa0865625d..018ac202de34 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -219,20 +219,34 @@ static DECLARE_WORK(update_topology_flags_work, update_topology_flags_workfn); static DEVICE_ATTR_RO(cpu_capacity); -static int register_cpu_capacity_sysctl(void) +static int cpu_capacity_sysctl_add(unsigned int cpu) { - int i; - struct device *cpu; + struct device *cpu_dev = get_cpu_device(cpu); - for_each_possible_cpu(i) { - cpu = get_cpu_device(i); - if (!cpu) { - pr_err("%s: too early to get CPU%d device!\n", - __func__, i); - continue; - } - device_create_file(cpu, &dev_attr_cpu_capacity); - } + if (!cpu_dev) + return -ENOENT; + + device_create_file(cpu_dev, &dev_attr_cpu_capacity); + + return 0; +} + +static int cpu_capacity_sysctl_remove(unsigned int cpu) +{ + struct device *cpu_dev = get_cpu_device(cpu); + + if (!cpu_dev) + return -ENOENT; + + device_remove_file(cpu_dev, &dev_attr_cpu_capacity); + + return 0; +} + +static int register_cpu_capacity_sysctl(void) +{ + cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "topology/cpu-capacity", + cpu_capacity_sysctl_add, cpu_capacity_sysctl_remove); return 0; } diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c index 4d4c2c8d26c4..d3a2c40c2f12 100644 --- a/drivers/base/auxiliary.c +++ b/drivers/base/auxiliary.c @@ -244,7 +244,7 @@ static void auxiliary_bus_shutdown(struct device *dev) auxdrv->shutdown(auxdev); } -static struct bus_type auxiliary_bus_type = { +static const struct bus_type auxiliary_bus_type = { .name = "auxiliary", .probe = auxiliary_bus_probe, .remove = auxiliary_bus_remove, diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 84a21084d67d..daee55c9b2d9 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -1030,7 +1030,7 @@ static void device_insertion_sort_klist(struct device *a, struct list_head *list list_move_tail(&a->p->knode_bus.n_node, list); } -void bus_sort_breadthfirst(struct bus_type *bus, +void bus_sort_breadthfirst(const struct bus_type *bus, int (*compare)(const struct device *a, const struct device *b)) { @@ -1194,7 +1194,7 @@ static void system_root_device_release(struct device *dev) kfree(dev); } -static int subsys_register(struct bus_type *subsys, +static int subsys_register(const struct bus_type *subsys, const struct attribute_group **groups, struct kobject *parent_of_root) { @@ -1264,7 +1264,7 @@ err_sp: * directory itself and not some create fake root-device placed in * /sys/devices/system/<name>. */ -int subsys_system_register(struct bus_type *subsys, +int subsys_system_register(const struct bus_type *subsys, const struct attribute_group **groups) { return subsys_register(subsys, groups, &system_kset->kobj); @@ -1282,7 +1282,7 @@ EXPORT_SYMBOL_GPL(subsys_system_register); * There's no restriction on device naming. This is for kernel software * constructs which need sysfs interface. */ -int subsys_virtual_register(struct bus_type *subsys, +int subsys_virtual_register(const struct bus_type *subsys, const struct attribute_group **groups) { struct kobject *virtual_dir; diff --git a/drivers/base/class.c b/drivers/base/class.c index 7e78aee0fd6c..7b38fdf8e1d7 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -213,6 +213,7 @@ int class_register(const struct class *cls) return 0; err_out: + lockdep_unregister_key(key); kfree(cp); return error; } diff --git a/drivers/base/container.c b/drivers/base/container.c index 1ba42d2d3532..f40588ebc3f5 100644 --- a/drivers/base/container.c +++ b/drivers/base/container.c @@ -24,7 +24,7 @@ static int container_offline(struct device *dev) return cdev->offline ? cdev->offline(cdev) : 0; } -struct bus_type container_subsys = { +const struct bus_type container_subsys = { .name = CONTAINER_BUS_NAME, .dev_name = CONTAINER_BUS_NAME, .online = trivial_online, diff --git a/drivers/base/core.c b/drivers/base/core.c index 67ba592afc77..14d46af40f9a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -298,7 +298,7 @@ static inline bool device_link_flag_is_sync_state_only(u32 flags) * Check if @target depends on @dev or any device dependent on it (its child or * its consumer etc). Return 1 if that is the case or 0 otherwise. */ -int device_is_dependent(struct device *dev, void *target) +static int device_is_dependent(struct device *dev, void *target) { struct device_link *link; int ret; @@ -1641,7 +1641,7 @@ static void device_links_purge(struct device *dev) #define FW_DEVLINK_FLAGS_RPM (FW_DEVLINK_FLAGS_ON | \ DL_FLAG_PM_RUNTIME) -static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_ON; +static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_RPM; static int __init fw_devlink_setup(char *arg) { if (!arg) @@ -4944,13 +4944,14 @@ define_dev_printk_level(_dev_info, KERN_INFO); * * return dev_err_probe(dev, err, ...); * - * Note that it is deemed acceptable to use this function for error - * prints during probe even if the @err is known to never be -EPROBE_DEFER. + * Using this helper in your probe function is totally fine even if @err is + * known to never be -EPROBE_DEFER. * The benefit compared to a normal dev_err() is the standardized format - * of the error code and the fact that the error code is returned. + * of the error code, it being emitted symbolically (i.e. you get "EAGAIN" + * instead of "-35") and the fact that the error code is returned which allows + * more compact error paths. * * Returns @err. - * */ int dev_err_probe(const struct device *dev, int err, const char *fmt, ...) { diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 548491de818e..47de0f140ba6 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -525,19 +525,42 @@ bool cpu_is_hotpluggable(unsigned int cpu) EXPORT_SYMBOL_GPL(cpu_is_hotpluggable); #ifdef CONFIG_GENERIC_CPU_DEVICES -static DEFINE_PER_CPU(struct cpu, cpu_devices); -#endif +DEFINE_PER_CPU(struct cpu, cpu_devices); + +bool __weak arch_cpu_is_hotpluggable(int cpu) +{ + return false; +} + +int __weak arch_register_cpu(int cpu) +{ + struct cpu *c = &per_cpu(cpu_devices, cpu); + + c->hotpluggable = arch_cpu_is_hotpluggable(cpu); + + return register_cpu(c, cpu); +} + +#ifdef CONFIG_HOTPLUG_CPU +void __weak arch_unregister_cpu(int num) +{ + unregister_cpu(&per_cpu(cpu_devices, num)); +} +#endif /* CONFIG_HOTPLUG_CPU */ +#endif /* CONFIG_GENERIC_CPU_DEVICES */ static void __init cpu_dev_register_generic(void) { -#ifdef CONFIG_GENERIC_CPU_DEVICES - int i; + int i, ret; - for_each_possible_cpu(i) { - if (register_cpu(&per_cpu(cpu_devices, i), i)) - panic("Failed to register CPU device"); + if (!IS_ENABLED(CONFIG_GENERIC_CPU_DEVICES)) + return; + + for_each_present_cpu(i) { + ret = arch_register_cpu(i); + if (ret) + pr_warn("register_cpu %d failed (%d)\n", i, ret); } -#endif } #ifdef CONFIG_GENERIC_CPU_VULNERABILITIES diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 0c3725c3eefa..85152537dbf1 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -313,7 +313,7 @@ static void deferred_probe_timeout_work_func(struct work_struct *work) mutex_lock(&deferred_probe_mutex); list_for_each_entry(p, &deferred_probe_pending_list, deferred_probe) - dev_info(p->device, "deferred probe pending\n"); + dev_info(p->device, "deferred probe pending: %s", p->deferred_probe_reason ?: "(reason unknown)\n"); mutex_unlock(&deferred_probe_mutex); fw_devlink_probing_done(); diff --git a/drivers/base/init.c b/drivers/base/init.c index 397eb9880cec..c4954835128c 100644 --- a/drivers/base/init.c +++ b/drivers/base/init.c @@ -35,8 +35,8 @@ void __init driver_init(void) of_core_init(); platform_bus_init(); auxiliary_bus_init(); - cpu_dev_init(); memory_dev_init(); node_dev_init(); + cpu_dev_init(); container_dev_init(); } diff --git a/drivers/base/isa.c b/drivers/base/isa.c index 675ad3139224..e23d0b49a793 100644 --- a/drivers/base/isa.c +++ b/drivers/base/isa.c @@ -82,7 +82,7 @@ static int isa_bus_resume(struct device *dev) return 0; } -static struct bus_type isa_bus_type = { +static const struct bus_type isa_bus_type = { .name = "isa", .match = isa_bus_match, .probe = isa_bus_probe, diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 8a13babd826c..14f964a7719b 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -68,7 +68,7 @@ static inline unsigned long phys_to_block_id(unsigned long phys) static int memory_subsys_online(struct device *dev); static int memory_subsys_offline(struct device *dev); -static struct bus_type memory_subsys = { +static const struct bus_type memory_subsys = { .name = MEMORY_CLASS_NAME, .dev_name = MEMORY_CLASS_NAME, .online = memory_subsys_online, diff --git a/drivers/base/node.c b/drivers/base/node.c index 493d533f8375..1c05640461dd 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -21,7 +21,7 @@ #include <linux/swap.h> #include <linux/slab.h> -static struct bus_type node_subsys = { +static const struct bus_type node_subsys = { .name = "node", .dev_name = "node", }; @@ -74,14 +74,14 @@ static BIN_ATTR_RO(cpulist, CPULIST_FILE_MAX_BYTES); * @dev: Device for this memory access class * @list_node: List element in the node's access list * @access: The access class rank - * @hmem_attrs: Heterogeneous memory performance attributes + * @coord: Heterogeneous memory performance coordinates */ struct node_access_nodes { struct device dev; struct list_head list_node; unsigned int access; #ifdef CONFIG_HMEM_REPORTING - struct node_hmem_attrs hmem_attrs; + struct access_coordinate coord; #endif }; #define to_access_nodes(dev) container_of(dev, struct node_access_nodes, dev) @@ -167,7 +167,7 @@ static ssize_t property##_show(struct device *dev, \ char *buf) \ { \ return sysfs_emit(buf, "%u\n", \ - to_access_nodes(dev)->hmem_attrs.property); \ + to_access_nodes(dev)->coord.property); \ } \ static DEVICE_ATTR_RO(property) @@ -187,10 +187,10 @@ static struct attribute *access_attrs[] = { /** * node_set_perf_attrs - Set the performance values for given access class * @nid: Node identifier to be set - * @hmem_attrs: Heterogeneous memory performance attributes + * @coord: Heterogeneous memory performance coordinates * @access: The access class the for the given attributes */ -void node_set_perf_attrs(unsigned int nid, struct node_hmem_attrs *hmem_attrs, +void node_set_perf_attrs(unsigned int nid, struct access_coordinate *coord, unsigned int access) { struct node_access_nodes *c; @@ -205,7 +205,7 @@ void node_set_perf_attrs(unsigned int nid, struct node_hmem_attrs *hmem_attrs, if (!c) return; - c->hmem_attrs = *hmem_attrs; + c->coord = *coord; for (i = 0; access_attrs[i] != NULL; i++) { if (sysfs_add_file_to_group(&c->dev.kobj, access_attrs[i], "initiators")) { @@ -868,11 +868,15 @@ int __register_one_node(int nid) { int error; int cpu; + struct node *node; - node_devices[nid] = kzalloc(sizeof(struct node), GFP_KERNEL); - if (!node_devices[nid]) + node = kzalloc(sizeof(struct node), GFP_KERNEL); + if (!node) return -ENOMEM; + INIT_LIST_HEAD(&node->access_list); + node_devices[nid] = node; + error = register_node(node_devices[nid], nid); /* link cpu under this node */ @@ -881,7 +885,6 @@ int __register_one_node(int nid) register_cpu_under_node(cpu, nid); } - INIT_LIST_HEAD(&node_devices[nid]->access_list); node_init_caches(nid); return error; diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index 4110c19c08dc..e18ba676cdf6 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -793,7 +793,7 @@ static int pm_clk_notify(struct notifier_block *nb, * the remaining members of @clknb should be populated prior to calling this * routine. */ -void pm_clk_add_notifier(struct bus_type *bus, +void pm_clk_add_notifier(const struct bus_type *bus, struct pm_clk_notifier_block *clknb) { if (!bus || !clknb) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9c5a5f4dba5a..fadcd0379dc2 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -579,7 +579,7 @@ bool dev_pm_skip_resume(struct device *dev) } /** - * __device_resume_noirq - Execute a "noirq resume" callback for given device. + * device_resume_noirq - Execute a "noirq resume" callback for given device. * @dev: Device to handle. * @state: PM transition of the system being carried out. * @async: If true, the device is being resumed asynchronously. @@ -587,7 +587,7 @@ bool dev_pm_skip_resume(struct device *dev) * The driver of @dev will not receive interrupts while this function is being * executed. */ -static void __device_resume_noirq(struct device *dev, pm_message_t state, bool async) +static void device_resume_noirq(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; const char *info = NULL; @@ -674,16 +674,22 @@ static bool dpm_async_fn(struct device *dev, async_func_t func) { reinit_completion(&dev->power.completion); - if (!is_async(dev)) - return false; - - get_device(dev); + if (is_async(dev)) { + dev->power.async_in_progress = true; - if (async_schedule_dev_nocall(func, dev)) - return true; + get_device(dev); - put_device(dev); + if (async_schedule_dev_nocall(func, dev)) + return true; + put_device(dev); + } + /* + * Because async_schedule_dev_nocall() above has returned false or it + * has not been called at all, func() is not running and it is safe to + * update the async_in_progress flag without extra synchronization. + */ + dev->power.async_in_progress = false; return false; } @@ -691,18 +697,10 @@ static void async_resume_noirq(void *data, async_cookie_t cookie) { struct device *dev = data; - __device_resume_noirq(dev, pm_transition, true); + device_resume_noirq(dev, pm_transition, true); put_device(dev); } -static void device_resume_noirq(struct device *dev) -{ - if (dpm_async_fn(dev, async_resume_noirq)) - return; - - __device_resume_noirq(dev, pm_transition, false); -} - static void dpm_noirq_resume_devices(pm_message_t state) { struct device *dev; @@ -712,18 +710,28 @@ static void dpm_noirq_resume_devices(pm_message_t state) mutex_lock(&dpm_list_mtx); pm_transition = state; + /* + * Trigger the resume of "async" devices upfront so they don't have to + * wait for the "non-async" ones they don't depend on. + */ + list_for_each_entry(dev, &dpm_noirq_list, power.entry) + dpm_async_fn(dev, async_resume_noirq); + while (!list_empty(&dpm_noirq_list)) { dev = to_device(dpm_noirq_list.next); - get_device(dev); list_move_tail(&dev->power.entry, &dpm_late_early_list); - mutex_unlock(&dpm_list_mtx); + if (!dev->power.async_in_progress) { + get_device(dev); - device_resume_noirq(dev); + mutex_unlock(&dpm_list_mtx); - put_device(dev); + device_resume_noirq(dev, state, false); - mutex_lock(&dpm_list_mtx); + put_device(dev); + + mutex_lock(&dpm_list_mtx); + } } mutex_unlock(&dpm_list_mtx); async_synchronize_full(); @@ -747,14 +755,14 @@ void dpm_resume_noirq(pm_message_t state) } /** - * __device_resume_early - Execute an "early resume" callback for given device. + * device_resume_early - Execute an "early resume" callback for given device. * @dev: Device to handle. * @state: PM transition of the system being carried out. * @async: If true, the device is being resumed asynchronously. * * Runtime PM is disabled for @dev while this function is being executed. */ -static void __device_resume_early(struct device *dev, pm_message_t state, bool async) +static void device_resume_early(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; const char *info = NULL; @@ -820,18 +828,10 @@ static void async_resume_early(void *data, async_cookie_t cookie) { struct device *dev = data; - __device_resume_early(dev, pm_transition, true); + device_resume_early(dev, pm_transition, true); put_device(dev); } -static void device_resume_early(struct device *dev) -{ - if (dpm_async_fn(dev, async_resume_early)) - return; - - __device_resume_early(dev, pm_transition, false); -} - /** * dpm_resume_early - Execute "early resume" callbacks for all devices. * @state: PM transition of the system being carried out. @@ -845,18 +845,28 @@ void dpm_resume_early(pm_message_t state) mutex_lock(&dpm_list_mtx); pm_transition = state; + /* + * Trigger the resume of "async" devices upfront so they don't have to + * wait for the "non-async" ones they don't depend on. + */ + list_for_each_entry(dev, &dpm_late_early_list, power.entry) + dpm_async_fn(dev, async_resume_early); + while (!list_empty(&dpm_late_early_list)) { dev = to_device(dpm_late_early_list.next); - get_device(dev); list_move_tail(&dev->power.entry, &dpm_suspended_list); - mutex_unlock(&dpm_list_mtx); + if (!dev->power.async_in_progress) { + get_device(dev); - device_resume_early(dev); + mutex_unlock(&dpm_list_mtx); - put_device(dev); + device_resume_early(dev, state, false); - mutex_lock(&dpm_list_mtx); + put_device(dev); + + mutex_lock(&dpm_list_mtx); + } } mutex_unlock(&dpm_list_mtx); async_synchronize_full(); @@ -876,12 +886,12 @@ void dpm_resume_start(pm_message_t state) EXPORT_SYMBOL_GPL(dpm_resume_start); /** - * __device_resume - Execute "resume" callbacks for given device. + * device_resume - Execute "resume" callbacks for given device. * @dev: Device to handle. * @state: PM transition of the system being carried out. * @async: If true, the device is being resumed asynchronously. */ -static void __device_resume(struct device *dev, pm_message_t state, bool async) +static void device_resume(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; const char *info = NULL; @@ -975,18 +985,10 @@ static void async_resume(void *data, async_cookie_t cookie) { struct device *dev = data; - __device_resume(dev, pm_transition, true); + device_resume(dev, pm_transition, true); put_device(dev); } -static void device_resume(struct device *dev) -{ - if (dpm_async_fn(dev, async_resume)) - return; - - __device_resume(dev, pm_transition, false); -} - /** * dpm_resume - Execute "resume" callbacks for non-sysdev devices. * @state: PM transition of the system being carried out. @@ -1006,16 +1008,25 @@ void dpm_resume(pm_message_t state) pm_transition = state; async_error = 0; + /* + * Trigger the resume of "async" devices upfront so they don't have to + * wait for the "non-async" ones they don't depend on. + */ + list_for_each_entry(dev, &dpm_suspended_list, power.entry) + dpm_async_fn(dev, async_resume); + while (!list_empty(&dpm_suspended_list)) { dev = to_device(dpm_suspended_list.next); get_device(dev); - mutex_unlock(&dpm_list_mtx); + if (!dev->power.async_in_progress) { + mutex_unlock(&dpm_list_mtx); - device_resume(dev); + device_resume(dev, state, false); - mutex_lock(&dpm_list_mtx); + mutex_lock(&dpm_list_mtx); + } if (!list_empty(&dev->power.entry)) list_move_tail(&dev->power.entry, &dpm_prepared_list); diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 8e93167f1783..bd77f6734f14 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -201,7 +201,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) if (!qos) return -ENOMEM; - n = kzalloc(3 * sizeof(*n), GFP_KERNEL); + n = kcalloc(3, sizeof(*n), GFP_KERNEL); if (!n) { kfree(qos); return -ENOMEM; diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index 72b7a92337b1..cd6e559648b2 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -120,7 +120,7 @@ static unsigned int read_magic_time(void) struct rtc_time time; unsigned int val; - if (mc146818_get_time(&time) < 0) { + if (mc146818_get_time(&time, 1000) < 0) { pr_err("Unable to read current time from RTC\n"); return 0; } diff --git a/drivers/base/property.c b/drivers/base/property.c index b79608ee0b46..a1b01ab42052 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -473,7 +473,7 @@ int fwnode_property_match_string(const struct fwnode_handle *fwnode, const char **values; int nval, ret; - nval = fwnode_property_read_string_array(fwnode, propname, NULL, 0); + nval = fwnode_property_string_array_count(fwnode, propname); if (nval < 0) return nval; @@ -499,6 +499,41 @@ out_free: EXPORT_SYMBOL_GPL(fwnode_property_match_string); /** + * fwnode_property_match_property_string - find a property string value in an array and return index + * @fwnode: Firmware node to get the property of + * @propname: Name of the property holding the string value + * @array: String array to search in + * @n: Size of the @array + * + * Find a property string value in a given @array and if it is found return + * the index back. + * + * Return: index, starting from %0, if the string value was found in the @array (success), + * %-ENOENT when the string value was not found in the @array, + * %-EINVAL if given arguments are not valid, + * %-ENODATA if the property does not have a value, + * %-EPROTO or %-EILSEQ if the property is not a string, + * %-ENXIO if no suitable firmware interface is present. + */ +int fwnode_property_match_property_string(const struct fwnode_handle *fwnode, + const char *propname, const char * const *array, size_t n) +{ + const char *string; + int ret; + + ret = fwnode_property_read_string(fwnode, propname, &string); + if (ret) + return ret; + + ret = match_string(array, n, string); + if (ret < 0) + ret = -ENOENT; + + return ret; +} +EXPORT_SYMBOL_GPL(fwnode_property_match_property_string); + +/** * fwnode_property_get_reference_args() - Find a reference with arguments * @fwnode: Firmware node where to look for the reference * @prop: The name of the property @@ -508,6 +543,7 @@ EXPORT_SYMBOL_GPL(fwnode_property_match_string); * @nargs: Number of arguments. Ignored if @nargs_prop is non-NULL. * @index: Index of the reference, from zero onwards. * @args: Result structure with reference and integer arguments. + * May be NULL. * * Obtain a reference based on a named property in an fwnode, with * integer arguments. diff --git a/drivers/base/soc.c b/drivers/base/soc.c index 8dec5228fde3..282c38aece0d 100644 --- a/drivers/base/soc.c +++ b/drivers/base/soc.c @@ -28,7 +28,7 @@ struct soc_device { int soc_dev_num; }; -static struct bus_type soc_bus_type = { +static const struct bus_type soc_bus_type = { .name = "soc", }; static bool soc_bus_registered; @@ -106,7 +106,7 @@ static void soc_release(struct device *dev) { struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); - ida_simple_remove(&soc_ida, soc_dev->soc_dev_num); + ida_free(&soc_ida, soc_dev->soc_dev_num); kfree(soc_dev->dev.groups); kfree(soc_dev); } @@ -155,7 +155,7 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr soc_attr_groups[1] = soc_dev_attr->custom_attr_group; /* Fetch a unique (reclaimable) SOC ID. */ - ret = ida_simple_get(&soc_ida, 0, 0, GFP_KERNEL); + ret = ida_alloc(&soc_ida, GFP_KERNEL); if (ret < 0) goto out3; soc_dev->soc_dev_num = ret; diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 1886995a0b3a..36512fb75a20 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -541,6 +541,9 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, if (nargs > NR_FWNODE_REFERENCE_ARGS) return -EINVAL; + if (!args) + return 0; + args->fwnode = software_node_get(refnode); args->nargs = nargs; @@ -747,10 +750,10 @@ static void software_node_release(struct kobject *kobj) struct swnode *swnode = kobj_to_swnode(kobj); if (swnode->parent) { - ida_simple_remove(&swnode->parent->child_ids, swnode->id); + ida_free(&swnode->parent->child_ids, swnode->id); list_del(&swnode->entry); } else { - ida_simple_remove(&swnode_root_ids, swnode->id); + ida_free(&swnode_root_ids, swnode->id); } if (swnode->allocated) @@ -776,8 +779,8 @@ swnode_register(const struct software_node *node, struct swnode *parent, if (!swnode) return ERR_PTR(-ENOMEM); - ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids, - 0, 0, GFP_KERNEL); + ret = ida_alloc(parent ? &parent->child_ids : &swnode_root_ids, + GFP_KERNEL); if (ret < 0) { kfree(swnode); return ERR_PTR(ret); diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c index d2dbf8aaccb5..b1b47d88f5db 100644 --- a/drivers/block/aoe/aoeblk.c +++ b/drivers/block/aoe/aoeblk.c @@ -333,6 +333,7 @@ aoeblk_gdalloc(void *vp) struct gendisk *gd; mempool_t *mp; struct blk_mq_tag_set *set; + sector_t ssize; ulong flags; int late = 0; int err; @@ -396,7 +397,7 @@ aoeblk_gdalloc(void *vp) gd->minors = AOE_PARTITIONS; gd->fops = &aoe_bdops; gd->private_data = d; - set_capacity(gd, d->ssize); + ssize = d->ssize; snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%d", d->aoemajor, d->aoeminor); @@ -405,6 +406,8 @@ aoeblk_gdalloc(void *vp) spin_unlock_irqrestore(&d->lock, flags); + set_capacity(gd, ssize); + err = device_add_disk(NULL, gd, aoe_attr_groups); if (err) goto out_disk_cleanup; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 146b32fa7b47..f8145499da38 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -165,39 +165,37 @@ static loff_t get_loop_size(struct loop_device *lo, struct file *file) return get_size(lo->lo_offset, lo->lo_sizelimit, file); } +/* + * We support direct I/O only if lo_offset is aligned with the logical I/O size + * of backing device, and the logical block size of loop is bigger than that of + * the backing device. + */ +static bool lo_bdev_can_use_dio(struct loop_device *lo, + struct block_device *backing_bdev) +{ + unsigned short sb_bsize = bdev_logical_block_size(backing_bdev); + + if (queue_logical_block_size(lo->lo_queue) < sb_bsize) + return false; + if (lo->lo_offset & (sb_bsize - 1)) + return false; + return true; +} + static void __loop_update_dio(struct loop_device *lo, bool dio) { struct file *file = lo->lo_backing_file; - struct address_space *mapping = file->f_mapping; - struct inode *inode = mapping->host; - unsigned short sb_bsize = 0; - unsigned dio_align = 0; + struct inode *inode = file->f_mapping->host; + struct block_device *backing_bdev = NULL; bool use_dio; - if (inode->i_sb->s_bdev) { - sb_bsize = bdev_logical_block_size(inode->i_sb->s_bdev); - dio_align = sb_bsize - 1; - } + if (S_ISBLK(inode->i_mode)) + backing_bdev = I_BDEV(inode); + else if (inode->i_sb->s_bdev) + backing_bdev = inode->i_sb->s_bdev; - /* - * We support direct I/O only if lo_offset is aligned with the - * logical I/O size of backing device, and the logical block - * size of loop is bigger than the backing device's. - * - * TODO: the above condition may be loosed in the future, and - * direct I/O may be switched runtime at that time because most - * of requests in sane applications should be PAGE_SIZE aligned - */ - if (dio) { - if (queue_logical_block_size(lo->lo_queue) >= sb_bsize && - !(lo->lo_offset & dio_align) && - (file->f_mode & FMODE_CAN_ODIRECT)) - use_dio = true; - else - use_dio = false; - } else { - use_dio = false; - } + use_dio = dio && (file->f_mode & FMODE_CAN_ODIRECT) && + (!backing_bdev || lo_bdev_can_use_dio(lo, backing_bdev)); if (lo->use_dio == use_dio) return; diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 4e72ec4e25ac..33a8f37bb6a1 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -508,7 +508,7 @@ static int __sock_xmit(struct nbd_device *nbd, struct socket *sock, int send, struct iov_iter *iter, int msg_flags, int *sent) { int result; - struct msghdr msg; + struct msghdr msg = {} ; unsigned int noreclaim_flag; if (unlikely(!sock)) { @@ -524,10 +524,6 @@ static int __sock_xmit(struct nbd_device *nbd, struct socket *sock, int send, do { sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC; sock->sk->sk_use_task_frag = false; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = NULL; - msg.msg_controllen = 0; msg.msg_flags = msg_flags | MSG_NOSIGNAL; if (send) diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index 9f7695f00c2d..36755f263e8e 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -1840,7 +1840,7 @@ static void null_del_dev(struct nullb *nullb) dev = nullb->dev; - ida_simple_remove(&nullb_indexes, nullb->index); + ida_free(&nullb_indexes, nullb->index); list_del_init(&nullb->list); @@ -2174,7 +2174,7 @@ static int null_add_dev(struct nullb_device *dev) blk_queue_flag_set(QUEUE_FLAG_NONROT, nullb->q); mutex_lock(&lock); - rv = ida_simple_get(&nullb_indexes, 0, 0, GFP_KERNEL); + rv = ida_alloc(&nullb_indexes, GFP_KERNEL); if (rv < 0) { mutex_unlock(&lock); goto out_cleanup_zone; diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index a999b698b131..12b5d53ec856 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -3452,14 +3452,15 @@ static bool rbd_lock_add_request(struct rbd_img_request *img_req) static void rbd_lock_del_request(struct rbd_img_request *img_req) { struct rbd_device *rbd_dev = img_req->rbd_dev; - bool need_wakeup; + bool need_wakeup = false; lockdep_assert_held(&rbd_dev->lock_rwsem); spin_lock(&rbd_dev->lock_lists_lock); - rbd_assert(!list_empty(&img_req->lock_item)); - list_del_init(&img_req->lock_item); - need_wakeup = (rbd_dev->lock_state == RBD_LOCK_STATE_RELEASING && - list_empty(&rbd_dev->running_list)); + if (!list_empty(&img_req->lock_item)) { + list_del_init(&img_req->lock_item); + need_wakeup = (rbd_dev->lock_state == RBD_LOCK_STATE_RELEASING && + list_empty(&rbd_dev->running_list)); + } spin_unlock(&rbd_dev->lock_lists_lock); if (need_wakeup) complete(&rbd_dev->releasing_wait); @@ -3842,14 +3843,19 @@ static void wake_lock_waiters(struct rbd_device *rbd_dev, int result) return; } - list_for_each_entry(img_req, &rbd_dev->acquiring_list, lock_item) { + while (!list_empty(&rbd_dev->acquiring_list)) { + img_req = list_first_entry(&rbd_dev->acquiring_list, + struct rbd_img_request, lock_item); mutex_lock(&img_req->state_mutex); rbd_assert(img_req->state == RBD_IMG_EXCLUSIVE_LOCK); + if (!result) + list_move_tail(&img_req->lock_item, + &rbd_dev->running_list); + else + list_del_init(&img_req->lock_item); rbd_img_schedule(img_req, result); mutex_unlock(&img_req->state_mutex); } - - list_splice_tail_init(&rbd_dev->acquiring_list, &rbd_dev->running_list); } static bool locker_equal(const struct ceph_locker *lhs, @@ -5326,7 +5332,7 @@ static void rbd_dev_release(struct device *dev) if (need_put) { destroy_workqueue(rbd_dev->task_wq); - ida_simple_remove(&rbd_dev_id_ida, rbd_dev->dev_id); + ida_free(&rbd_dev_id_ida, rbd_dev->dev_id); } rbd_dev_free(rbd_dev); @@ -5402,9 +5408,9 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc, return NULL; /* get an id and fill in device name */ - rbd_dev->dev_id = ida_simple_get(&rbd_dev_id_ida, 0, - minor_to_rbd_dev_id(1 << MINORBITS), - GFP_KERNEL); + rbd_dev->dev_id = ida_alloc_max(&rbd_dev_id_ida, + minor_to_rbd_dev_id(1 << MINORBITS) - 1, + GFP_KERNEL); if (rbd_dev->dev_id < 0) goto fail_rbd_dev; @@ -5425,7 +5431,7 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc, return rbd_dev; fail_dev_id: - ida_simple_remove(&rbd_dev_id_ida, rbd_dev->dev_id); + ida_free(&rbd_dev_id_ida, rbd_dev->dev_id); fail_rbd_dev: rbd_dev_free(rbd_dev); return NULL; diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 3b6b9abb8ce1..2bf14a0e2815 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -367,8 +367,6 @@ static void virtblk_done(struct virtqueue *vq) blk_mq_complete_request(req); req_done = true; } - if (unlikely(virtqueue_is_broken(vq))) - break; } while (!virtqueue_enable_cb(vq)); /* In case queue is stopped waiting for more buffers. */ @@ -1595,14 +1593,15 @@ static int virtblk_freeze(struct virtio_device *vdev) { struct virtio_blk *vblk = vdev->priv; + /* Ensure no requests in virtqueues before deleting vqs. */ + blk_mq_freeze_queue(vblk->disk->queue); + /* Ensure we don't receive any more interrupts */ virtio_reset_device(vdev); /* Make sure no work handler is accessing the device. */ flush_work(&vblk->config_work); - blk_mq_quiesce_queue(vblk->disk->queue); - vdev->config->del_vqs(vdev); kfree(vblk->vqs); @@ -1620,7 +1619,7 @@ static int virtblk_restore(struct virtio_device *vdev) virtio_device_ready(vdev); - blk_mq_unquiesce_queue(vblk->disk->queue); + blk_mq_unfreeze_queue(vblk->disk->queue); return 0; } #endif diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c index 203a000a84e3..3c84fcbda01a 100644 --- a/drivers/bluetooth/btmtkuart.c +++ b/drivers/bluetooth/btmtkuart.c @@ -383,8 +383,8 @@ static void btmtkuart_recv(struct hci_dev *hdev, const u8 *data, size_t count) } } -static int btmtkuart_receive_buf(struct serdev_device *serdev, const u8 *data, - size_t count) +static ssize_t btmtkuart_receive_buf(struct serdev_device *serdev, + const u8 *data, size_t count) { struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev); diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index b7c56be078f8..1d592ac413d1 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -1264,8 +1264,8 @@ static const struct h4_recv_pkt nxp_recv_pkts[] = { { NXP_RECV_FW_REQ_V3, .recv = nxp_recv_fw_req_v3 }, }; -static int btnxpuart_receive_buf(struct serdev_device *serdev, const u8 *data, - size_t count) +static ssize_t btnxpuart_receive_buf(struct serdev_device *serdev, + const u8 *data, size_t count) { struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev); diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 7835170b1d66..d31edad7a056 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4796,10 +4796,8 @@ static struct usb_driver btusb_driver = { .disable_hub_initiated_lpm = 1, #ifdef CONFIG_DEV_COREDUMP - .drvwrap = { - .driver = { - .coredump = btusb_coredump, - }, + .driver = { + .coredump = btusb_coredump, }, #endif }; diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c index f16fd79bc02b..39c8b567da3c 100644 --- a/drivers/bluetooth/hci_serdev.c +++ b/drivers/bluetooth/hci_serdev.c @@ -271,8 +271,8 @@ static void hci_uart_write_wakeup(struct serdev_device *serdev) * * Return: number of processed bytes */ -static int hci_uart_receive_buf(struct serdev_device *serdev, const u8 *data, - size_t count) +static ssize_t hci_uart_receive_buf(struct serdev_device *serdev, + const u8 *data, size_t count) { struct hci_uart *hu = serdev_device_get_drvdata(serdev); diff --git a/drivers/bus/mhi/ep/internal.h b/drivers/bus/mhi/ep/internal.h index a2125fa5fe2f..577965f95fda 100644 --- a/drivers/bus/mhi/ep/internal.h +++ b/drivers/bus/mhi/ep/internal.h @@ -126,6 +126,7 @@ struct mhi_ep_ring { union mhi_ep_ring_ctx *ring_ctx; struct mhi_ring_element *ring_cache; enum mhi_ep_ring_type type; + struct delayed_work intmodt_work; u64 rbase; size_t rd_offset; size_t wr_offset; @@ -135,7 +136,9 @@ struct mhi_ep_ring { u32 ch_id; u32 er_index; u32 irq_vector; + u32 intmodt; bool started; + bool irq_pending; }; struct mhi_ep_cmd { @@ -159,6 +162,7 @@ struct mhi_ep_chan { void (*xfer_cb)(struct mhi_ep_device *mhi_dev, struct mhi_result *result); enum mhi_ch_state state; enum dma_data_direction dir; + size_t rd_offset; u64 tre_loc; u32 tre_size; u32 tre_bytes_left; diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 600881808982..65fc1d738bec 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -54,11 +54,27 @@ static int mhi_ep_send_event(struct mhi_ep_cntrl *mhi_cntrl, u32 ring_idx, mutex_unlock(&mhi_cntrl->event_lock); /* - * Raise IRQ to host only if the BEI flag is not set in TRE. Host might - * set this flag for interrupt moderation as per MHI protocol. + * As per the MHI specification, section 4.3, Interrupt moderation: + * + * 1. If BEI flag is not set, cancel any pending intmodt work if started + * for the event ring and raise IRQ immediately. + * + * 2. If both BEI and intmodt are set, and if no IRQ is pending for the + * same event ring, start the IRQ delayed work as per the value of + * intmodt. If previous IRQ is pending, then do nothing as the pending + * IRQ is enough for the host to process the current event ring element. + * + * 3. If BEI is set and intmodt is not set, no need to raise IRQ. */ - if (!bei) + if (!bei) { + if (READ_ONCE(ring->irq_pending)) + cancel_delayed_work(&ring->intmodt_work); + mhi_cntrl->raise_irq(mhi_cntrl, ring->irq_vector); + } else if (ring->intmodt && !READ_ONCE(ring->irq_pending)) { + WRITE_ONCE(ring->irq_pending, true); + schedule_delayed_work(&ring->intmodt_work, msecs_to_jiffies(ring->intmodt)); + } return 0; @@ -71,45 +87,77 @@ err_unlock: static int mhi_ep_send_completion_event(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring, struct mhi_ring_element *tre, u32 len, enum mhi_ev_ccs code) { - struct mhi_ring_element event = {}; + struct mhi_ring_element *event; + int ret; + + event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL | GFP_DMA); + if (!event) + return -ENOMEM; - event.ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(*tre)); - event.dword[0] = MHI_TRE_EV_DWORD0(code, len); - event.dword[1] = MHI_TRE_EV_DWORD1(ring->ch_id, MHI_PKT_TYPE_TX_EVENT); + event->ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(*tre)); + event->dword[0] = MHI_TRE_EV_DWORD0(code, len); + event->dword[1] = MHI_TRE_EV_DWORD1(ring->ch_id, MHI_PKT_TYPE_TX_EVENT); - return mhi_ep_send_event(mhi_cntrl, ring->er_index, &event, MHI_TRE_DATA_GET_BEI(tre)); + ret = mhi_ep_send_event(mhi_cntrl, ring->er_index, event, MHI_TRE_DATA_GET_BEI(tre)); + kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event); + + return ret; } int mhi_ep_send_state_change_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state state) { - struct mhi_ring_element event = {}; + struct mhi_ring_element *event; + int ret; - event.dword[0] = MHI_SC_EV_DWORD0(state); - event.dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_STATE_CHANGE_EVENT); + event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL | GFP_DMA); + if (!event) + return -ENOMEM; - return mhi_ep_send_event(mhi_cntrl, 0, &event, 0); + event->dword[0] = MHI_SC_EV_DWORD0(state); + event->dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_STATE_CHANGE_EVENT); + + ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0); + kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event); + + return ret; } int mhi_ep_send_ee_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_ee_type exec_env) { - struct mhi_ring_element event = {}; + struct mhi_ring_element *event; + int ret; - event.dword[0] = MHI_EE_EV_DWORD0(exec_env); - event.dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_EE_EVENT); + event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL | GFP_DMA); + if (!event) + return -ENOMEM; + + event->dword[0] = MHI_EE_EV_DWORD0(exec_env); + event->dword[1] = MHI_SC_EV_DWORD1(MHI_PKT_TYPE_EE_EVENT); - return mhi_ep_send_event(mhi_cntrl, 0, &event, 0); + ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0); + kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event); + + return ret; } static int mhi_ep_send_cmd_comp_event(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_ev_ccs code) { struct mhi_ep_ring *ring = &mhi_cntrl->mhi_cmd->ring; - struct mhi_ring_element event = {}; + struct mhi_ring_element *event; + int ret; + + event = kmem_cache_zalloc(mhi_cntrl->ev_ring_el_cache, GFP_KERNEL | GFP_DMA); + if (!event) + return -ENOMEM; + + event->ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(struct mhi_ring_element)); + event->dword[0] = MHI_CC_EV_DWORD0(code); + event->dword[1] = MHI_CC_EV_DWORD1(MHI_PKT_TYPE_CMD_COMPLETION_EVENT); - event.ptr = cpu_to_le64(ring->rbase + ring->rd_offset * sizeof(struct mhi_ring_element)); - event.dword[0] = MHI_CC_EV_DWORD0(code); - event.dword[1] = MHI_CC_EV_DWORD1(MHI_PKT_TYPE_CMD_COMPLETION_EVENT); + ret = mhi_ep_send_event(mhi_cntrl, 0, event, 0); + kmem_cache_free(mhi_cntrl->ev_ring_el_cache, event); - return mhi_ep_send_event(mhi_cntrl, 0, &event, 0); + return ret; } static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_element *el) @@ -151,6 +199,8 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele goto err_unlock; } + + mhi_chan->rd_offset = ch_ring->rd_offset; } /* Set channel state to RUNNING */ @@ -280,22 +330,85 @@ bool mhi_ep_queue_is_empty(struct mhi_ep_device *mhi_dev, enum dma_data_directio struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl; struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring; - return !!(ring->rd_offset == ring->wr_offset); + return !!(mhi_chan->rd_offset == ring->wr_offset); } EXPORT_SYMBOL_GPL(mhi_ep_queue_is_empty); +static void mhi_ep_read_completion(struct mhi_ep_buf_info *buf_info) +{ + struct mhi_ep_device *mhi_dev = buf_info->mhi_dev; + struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl; + struct mhi_ep_chan *mhi_chan = mhi_dev->ul_chan; + struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring; + struct mhi_ring_element *el = &ring->ring_cache[ring->rd_offset]; + struct mhi_result result = {}; + int ret; + + if (mhi_chan->xfer_cb) { + result.buf_addr = buf_info->cb_buf; + result.dir = mhi_chan->dir; + result.bytes_xferd = buf_info->size; + + mhi_chan->xfer_cb(mhi_dev, &result); + } + + /* + * The host will split the data packet into multiple TREs if it can't fit + * the packet in a single TRE. In that case, CHAIN flag will be set by the + * host for all TREs except the last one. + */ + if (buf_info->code != MHI_EV_CC_OVERFLOW) { + if (MHI_TRE_DATA_GET_CHAIN(el)) { + /* + * IEOB (Interrupt on End of Block) flag will be set by the host if + * it expects the completion event for all TREs of a TD. + */ + if (MHI_TRE_DATA_GET_IEOB(el)) { + ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, + MHI_TRE_DATA_GET_LEN(el), + MHI_EV_CC_EOB); + if (ret < 0) { + dev_err(&mhi_chan->mhi_dev->dev, + "Error sending transfer compl. event\n"); + goto err_free_tre_buf; + } + } + } else { + /* + * IEOT (Interrupt on End of Transfer) flag will be set by the host + * for the last TRE of the TD and expects the completion event for + * the same. + */ + if (MHI_TRE_DATA_GET_IEOT(el)) { + ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, + MHI_TRE_DATA_GET_LEN(el), + MHI_EV_CC_EOT); + if (ret < 0) { + dev_err(&mhi_chan->mhi_dev->dev, + "Error sending transfer compl. event\n"); + goto err_free_tre_buf; + } + } + } + } + + mhi_ep_ring_inc_index(ring); + +err_free_tre_buf: + kmem_cache_free(mhi_cntrl->tre_buf_cache, buf_info->cb_buf); +} + static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl, - struct mhi_ep_ring *ring, - struct mhi_result *result, - u32 len) + struct mhi_ep_ring *ring) { struct mhi_ep_chan *mhi_chan = &mhi_cntrl->mhi_chan[ring->ch_id]; struct device *dev = &mhi_cntrl->mhi_dev->dev; size_t tr_len, read_offset, write_offset; + struct mhi_ep_buf_info buf_info = {}; + u32 len = MHI_EP_DEFAULT_MTU; struct mhi_ring_element *el; bool tr_done = false; - void *write_addr; - u64 read_addr; + void *buf_addr; u32 buf_left; int ret; @@ -308,7 +421,7 @@ static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl, return -ENODEV; } - el = &ring->ring_cache[ring->rd_offset]; + el = &ring->ring_cache[mhi_chan->rd_offset]; /* Check if there is data pending to be read from previous read operation */ if (mhi_chan->tre_bytes_left) { @@ -324,81 +437,51 @@ static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl, read_offset = mhi_chan->tre_size - mhi_chan->tre_bytes_left; write_offset = len - buf_left; - read_addr = mhi_chan->tre_loc + read_offset; - write_addr = result->buf_addr + write_offset; + + buf_addr = kmem_cache_zalloc(mhi_cntrl->tre_buf_cache, GFP_KERNEL | GFP_DMA); + if (!buf_addr) + return -ENOMEM; + + buf_info.host_addr = mhi_chan->tre_loc + read_offset; + buf_info.dev_addr = buf_addr + write_offset; + buf_info.size = tr_len; + buf_info.cb = mhi_ep_read_completion; + buf_info.cb_buf = buf_addr; + buf_info.mhi_dev = mhi_chan->mhi_dev; + + if (mhi_chan->tre_bytes_left - tr_len) + buf_info.code = MHI_EV_CC_OVERFLOW; dev_dbg(dev, "Reading %zd bytes from channel (%u)\n", tr_len, ring->ch_id); - ret = mhi_cntrl->read_from_host(mhi_cntrl, read_addr, write_addr, tr_len); + ret = mhi_cntrl->read_async(mhi_cntrl, &buf_info); if (ret < 0) { dev_err(&mhi_chan->mhi_dev->dev, "Error reading from channel\n"); - return ret; + goto err_free_buf_addr; } buf_left -= tr_len; mhi_chan->tre_bytes_left -= tr_len; - /* - * Once the TRE (Transfer Ring Element) of a TD (Transfer Descriptor) has been - * read completely: - * - * 1. Send completion event to the host based on the flags set in TRE. - * 2. Increment the local read offset of the transfer ring. - */ if (!mhi_chan->tre_bytes_left) { - /* - * The host will split the data packet into multiple TREs if it can't fit - * the packet in a single TRE. In that case, CHAIN flag will be set by the - * host for all TREs except the last one. - */ - if (MHI_TRE_DATA_GET_CHAIN(el)) { - /* - * IEOB (Interrupt on End of Block) flag will be set by the host if - * it expects the completion event for all TREs of a TD. - */ - if (MHI_TRE_DATA_GET_IEOB(el)) { - ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, - MHI_TRE_DATA_GET_LEN(el), - MHI_EV_CC_EOB); - if (ret < 0) { - dev_err(&mhi_chan->mhi_dev->dev, - "Error sending transfer compl. event\n"); - return ret; - } - } - } else { - /* - * IEOT (Interrupt on End of Transfer) flag will be set by the host - * for the last TRE of the TD and expects the completion event for - * the same. - */ - if (MHI_TRE_DATA_GET_IEOT(el)) { - ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, - MHI_TRE_DATA_GET_LEN(el), - MHI_EV_CC_EOT); - if (ret < 0) { - dev_err(&mhi_chan->mhi_dev->dev, - "Error sending transfer compl. event\n"); - return ret; - } - } - + if (MHI_TRE_DATA_GET_IEOT(el)) tr_done = true; - } - mhi_ep_ring_inc_index(ring); + mhi_chan->rd_offset = (mhi_chan->rd_offset + 1) % ring->ring_size; } - - result->bytes_xferd += tr_len; } while (buf_left && !tr_done); return 0; + +err_free_buf_addr: + kmem_cache_free(mhi_cntrl->tre_buf_cache, buf_addr); + + return ret; } -static int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring, struct mhi_ring_element *el) +static int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring) { struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl; struct mhi_result result = {}; - u32 len = MHI_EP_DEFAULT_MTU; struct mhi_ep_chan *mhi_chan; int ret; @@ -419,44 +502,59 @@ static int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring, struct mhi_ring_elem mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); } else { /* UL channel */ - result.buf_addr = kzalloc(len, GFP_KERNEL); - if (!result.buf_addr) - return -ENOMEM; - do { - ret = mhi_ep_read_channel(mhi_cntrl, ring, &result, len); + ret = mhi_ep_read_channel(mhi_cntrl, ring); if (ret < 0) { dev_err(&mhi_chan->mhi_dev->dev, "Failed to read channel\n"); - kfree(result.buf_addr); return ret; } - result.dir = mhi_chan->dir; - mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); - result.bytes_xferd = 0; - memset(result.buf_addr, 0, len); - /* Read until the ring becomes empty */ } while (!mhi_ep_queue_is_empty(mhi_chan->mhi_dev, DMA_TO_DEVICE)); - - kfree(result.buf_addr); } return 0; } +static void mhi_ep_skb_completion(struct mhi_ep_buf_info *buf_info) +{ + struct mhi_ep_device *mhi_dev = buf_info->mhi_dev; + struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl; + struct mhi_ep_chan *mhi_chan = mhi_dev->dl_chan; + struct mhi_ep_ring *ring = &mhi_cntrl->mhi_chan[mhi_chan->chan].ring; + struct mhi_ring_element *el = &ring->ring_cache[ring->rd_offset]; + struct device *dev = &mhi_dev->dev; + struct mhi_result result = {}; + int ret; + + if (mhi_chan->xfer_cb) { + result.buf_addr = buf_info->cb_buf; + result.dir = mhi_chan->dir; + result.bytes_xferd = buf_info->size; + + mhi_chan->xfer_cb(mhi_dev, &result); + } + + ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, buf_info->size, + buf_info->code); + if (ret) { + dev_err(dev, "Error sending transfer completion event\n"); + return; + } + + mhi_ep_ring_inc_index(ring); +} + /* TODO: Handle partially formed TDs */ int mhi_ep_queue_skb(struct mhi_ep_device *mhi_dev, struct sk_buff *skb) { struct mhi_ep_cntrl *mhi_cntrl = mhi_dev->mhi_cntrl; struct mhi_ep_chan *mhi_chan = mhi_dev->dl_chan; struct device *dev = &mhi_chan->mhi_dev->dev; + struct mhi_ep_buf_info buf_info = {}; struct mhi_ring_element *el; u32 buf_left, read_offset; struct mhi_ep_ring *ring; - enum mhi_ev_ccs code; - void *read_addr; - u64 write_addr; size_t tr_len; u32 tre_len; int ret; @@ -480,40 +578,44 @@ int mhi_ep_queue_skb(struct mhi_ep_device *mhi_dev, struct sk_buff *skb) goto err_exit; } - el = &ring->ring_cache[ring->rd_offset]; + el = &ring->ring_cache[mhi_chan->rd_offset]; tre_len = MHI_TRE_DATA_GET_LEN(el); tr_len = min(buf_left, tre_len); read_offset = skb->len - buf_left; - read_addr = skb->data + read_offset; - write_addr = MHI_TRE_DATA_GET_PTR(el); - dev_dbg(dev, "Writing %zd bytes to channel (%u)\n", tr_len, ring->ch_id); - ret = mhi_cntrl->write_to_host(mhi_cntrl, read_addr, write_addr, tr_len); - if (ret < 0) { - dev_err(dev, "Error writing to the channel\n"); - goto err_exit; - } + buf_info.dev_addr = skb->data + read_offset; + buf_info.host_addr = MHI_TRE_DATA_GET_PTR(el); + buf_info.size = tr_len; + buf_info.cb = mhi_ep_skb_completion; + buf_info.cb_buf = skb; + buf_info.mhi_dev = mhi_dev; - buf_left -= tr_len; /* * For all TREs queued by the host for DL channel, only the EOT flag will be set. * If the packet doesn't fit into a single TRE, send the OVERFLOW event to * the host so that the host can adjust the packet boundary to next TREs. Else send * the EOT event to the host indicating the packet boundary. */ - if (buf_left) - code = MHI_EV_CC_OVERFLOW; + if (buf_left - tr_len) + buf_info.code = MHI_EV_CC_OVERFLOW; else - code = MHI_EV_CC_EOT; + buf_info.code = MHI_EV_CC_EOT; - ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, tr_len, code); - if (ret) { - dev_err(dev, "Error sending transfer completion event\n"); + dev_dbg(dev, "Writing %zd bytes to channel (%u)\n", tr_len, ring->ch_id); + ret = mhi_cntrl->write_async(mhi_cntrl, &buf_info); + if (ret < 0) { + dev_err(dev, "Error writing to the channel\n"); goto err_exit; } - mhi_ep_ring_inc_index(ring); + buf_left -= tr_len; + + /* + * Update the read offset cached in mhi_chan. Actual read offset + * will be updated by the completion handler. + */ + mhi_chan->rd_offset = (mhi_chan->rd_offset + 1) % ring->ring_size; } while (buf_left); mutex_unlock(&mhi_chan->lock); @@ -714,7 +816,6 @@ static void mhi_ep_ch_ring_worker(struct work_struct *work) struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, ch_ring_work); struct device *dev = &mhi_cntrl->mhi_dev->dev; struct mhi_ep_ring_item *itr, *tmp; - struct mhi_ring_element *el; struct mhi_ep_ring *ring; struct mhi_ep_chan *chan; unsigned long flags; @@ -748,31 +849,29 @@ static void mhi_ep_ch_ring_worker(struct work_struct *work) if (ret) { dev_err(dev, "Error updating write offset for ring\n"); mutex_unlock(&chan->lock); - kfree(itr); + kmem_cache_free(mhi_cntrl->ring_item_cache, itr); continue; } /* Sanity check to make sure there are elements in the ring */ - if (ring->rd_offset == ring->wr_offset) { + if (chan->rd_offset == ring->wr_offset) { mutex_unlock(&chan->lock); - kfree(itr); + kmem_cache_free(mhi_cntrl->ring_item_cache, itr); continue; } - el = &ring->ring_cache[ring->rd_offset]; - dev_dbg(dev, "Processing the ring for channel (%u)\n", ring->ch_id); - ret = mhi_ep_process_ch_ring(ring, el); + ret = mhi_ep_process_ch_ring(ring); if (ret) { dev_err(dev, "Error processing ring for channel (%u): %d\n", ring->ch_id, ret); mutex_unlock(&chan->lock); - kfree(itr); + kmem_cache_free(mhi_cntrl->ring_item_cache, itr); continue; } mutex_unlock(&chan->lock); - kfree(itr); + kmem_cache_free(mhi_cntrl->ring_item_cache, itr); } } @@ -828,7 +927,7 @@ static void mhi_ep_queue_channel_db(struct mhi_ep_cntrl *mhi_cntrl, unsigned lon u32 ch_id = ch_idx + i; ring = &mhi_cntrl->mhi_chan[ch_id].ring; - item = kzalloc(sizeof(*item), GFP_ATOMIC); + item = kmem_cache_zalloc(mhi_cntrl->ring_item_cache, GFP_ATOMIC); if (!item) return; @@ -1365,6 +1464,10 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl, if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->mmio || !mhi_cntrl->irq) return -EINVAL; + if (!mhi_cntrl->read_sync || !mhi_cntrl->write_sync || + !mhi_cntrl->read_async || !mhi_cntrl->write_async) + return -EINVAL; + ret = mhi_ep_chan_init(mhi_cntrl, config); if (ret) return ret; @@ -1375,6 +1478,29 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl, goto err_free_ch; } + mhi_cntrl->ev_ring_el_cache = kmem_cache_create("mhi_ep_event_ring_el", + sizeof(struct mhi_ring_element), 0, + SLAB_CACHE_DMA, NULL); + if (!mhi_cntrl->ev_ring_el_cache) { + ret = -ENOMEM; + goto err_free_cmd; + } + + mhi_cntrl->tre_buf_cache = kmem_cache_create("mhi_ep_tre_buf", MHI_EP_DEFAULT_MTU, 0, + SLAB_CACHE_DMA, NULL); + if (!mhi_cntrl->tre_buf_cache) { + ret = -ENOMEM; + goto err_destroy_ev_ring_el_cache; + } + + mhi_cntrl->ring_item_cache = kmem_cache_create("mhi_ep_ring_item", + sizeof(struct mhi_ep_ring_item), 0, + 0, NULL); + if (!mhi_cntrl->ev_ring_el_cache) { + ret = -ENOMEM; + goto err_destroy_tre_buf_cache; + } + INIT_WORK(&mhi_cntrl->state_work, mhi_ep_state_worker); INIT_WORK(&mhi_cntrl->reset_work, mhi_ep_reset_worker); INIT_WORK(&mhi_cntrl->cmd_ring_work, mhi_ep_cmd_ring_worker); @@ -1383,7 +1509,7 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl, mhi_cntrl->wq = alloc_workqueue("mhi_ep_wq", 0, 0); if (!mhi_cntrl->wq) { ret = -ENOMEM; - goto err_free_cmd; + goto err_destroy_ring_item_cache; } INIT_LIST_HEAD(&mhi_cntrl->st_transition_list); @@ -1442,6 +1568,12 @@ err_ida_free: ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index); err_destroy_wq: destroy_workqueue(mhi_cntrl->wq); +err_destroy_ring_item_cache: + kmem_cache_destroy(mhi_cntrl->ring_item_cache); +err_destroy_ev_ring_el_cache: + kmem_cache_destroy(mhi_cntrl->ev_ring_el_cache); +err_destroy_tre_buf_cache: + kmem_cache_destroy(mhi_cntrl->tre_buf_cache); err_free_cmd: kfree(mhi_cntrl->mhi_cmd); err_free_ch: @@ -1463,6 +1595,9 @@ void mhi_ep_unregister_controller(struct mhi_ep_cntrl *mhi_cntrl) free_irq(mhi_cntrl->irq, mhi_cntrl); + kmem_cache_destroy(mhi_cntrl->tre_buf_cache); + kmem_cache_destroy(mhi_cntrl->ev_ring_el_cache); + kmem_cache_destroy(mhi_cntrl->ring_item_cache); kfree(mhi_cntrl->mhi_cmd); kfree(mhi_cntrl->mhi_chan); diff --git a/drivers/bus/mhi/ep/ring.c b/drivers/bus/mhi/ep/ring.c index 115518ec76a4..aeb53b2c34a8 100644 --- a/drivers/bus/mhi/ep/ring.c +++ b/drivers/bus/mhi/ep/ring.c @@ -30,7 +30,8 @@ static int __mhi_ep_cache_ring(struct mhi_ep_ring *ring, size_t end) { struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl; struct device *dev = &mhi_cntrl->mhi_dev->dev; - size_t start, copy_size; + struct mhi_ep_buf_info buf_info = {}; + size_t start; int ret; /* Don't proceed in the case of event ring. This happens during mhi_ep_ring_start(). */ @@ -43,30 +44,34 @@ static int __mhi_ep_cache_ring(struct mhi_ep_ring *ring, size_t end) start = ring->wr_offset; if (start < end) { - copy_size = (end - start) * sizeof(struct mhi_ring_element); - ret = mhi_cntrl->read_from_host(mhi_cntrl, ring->rbase + - (start * sizeof(struct mhi_ring_element)), - &ring->ring_cache[start], copy_size); + buf_info.size = (end - start) * sizeof(struct mhi_ring_element); + buf_info.host_addr = ring->rbase + (start * sizeof(struct mhi_ring_element)); + buf_info.dev_addr = &ring->ring_cache[start]; + + ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info); if (ret < 0) return ret; } else { - copy_size = (ring->ring_size - start) * sizeof(struct mhi_ring_element); - ret = mhi_cntrl->read_from_host(mhi_cntrl, ring->rbase + - (start * sizeof(struct mhi_ring_element)), - &ring->ring_cache[start], copy_size); + buf_info.size = (ring->ring_size - start) * sizeof(struct mhi_ring_element); + buf_info.host_addr = ring->rbase + (start * sizeof(struct mhi_ring_element)); + buf_info.dev_addr = &ring->ring_cache[start]; + + ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info); if (ret < 0) return ret; if (end) { - ret = mhi_cntrl->read_from_host(mhi_cntrl, ring->rbase, - &ring->ring_cache[0], - end * sizeof(struct mhi_ring_element)); + buf_info.host_addr = ring->rbase; + buf_info.dev_addr = &ring->ring_cache[0]; + buf_info.size = end * sizeof(struct mhi_ring_element); + + ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info); if (ret < 0) return ret; } } - dev_dbg(dev, "Cached ring: start %zu end %zu size %zu\n", start, end, copy_size); + dev_dbg(dev, "Cached ring: start %zu end %zu size %zu\n", start, end, buf_info.size); return 0; } @@ -102,6 +107,7 @@ int mhi_ep_ring_add_element(struct mhi_ep_ring *ring, struct mhi_ring_element *e { struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl; struct device *dev = &mhi_cntrl->mhi_dev->dev; + struct mhi_ep_buf_info buf_info = {}; size_t old_offset = 0; u32 num_free_elem; __le64 rp; @@ -133,12 +139,11 @@ int mhi_ep_ring_add_element(struct mhi_ep_ring *ring, struct mhi_ring_element *e rp = cpu_to_le64(ring->rd_offset * sizeof(*el) + ring->rbase); memcpy_toio((void __iomem *) &ring->ring_ctx->generic.rp, &rp, sizeof(u64)); - ret = mhi_cntrl->write_to_host(mhi_cntrl, el, ring->rbase + (old_offset * sizeof(*el)), - sizeof(*el)); - if (ret < 0) - return ret; + buf_info.host_addr = ring->rbase + (old_offset * sizeof(*el)); + buf_info.dev_addr = el; + buf_info.size = sizeof(*el); - return 0; + return mhi_cntrl->write_sync(mhi_cntrl, &buf_info); } void mhi_ep_ring_init(struct mhi_ep_ring *ring, enum mhi_ep_ring_type type, u32 id) @@ -157,6 +162,15 @@ void mhi_ep_ring_init(struct mhi_ep_ring *ring, enum mhi_ep_ring_type type, u32 } } +static void mhi_ep_raise_irq(struct work_struct *work) +{ + struct mhi_ep_ring *ring = container_of(work, struct mhi_ep_ring, intmodt_work.work); + struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl; + + mhi_cntrl->raise_irq(mhi_cntrl, ring->irq_vector); + WRITE_ONCE(ring->irq_pending, false); +} + int mhi_ep_ring_start(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring, union mhi_ep_ring_ctx *ctx) { @@ -173,8 +187,13 @@ int mhi_ep_ring_start(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring, if (ring->type == RING_TYPE_CH) ring->er_index = le32_to_cpu(ring->ring_ctx->ch.erindex); - if (ring->type == RING_TYPE_ER) + if (ring->type == RING_TYPE_ER) { ring->irq_vector = le32_to_cpu(ring->ring_ctx->ev.msivec); + ring->intmodt = FIELD_GET(EV_CTX_INTMODT_MASK, + le32_to_cpu(ring->ring_ctx->ev.intmod)); + + INIT_DELAYED_WORK(&ring->intmodt_work, mhi_ep_raise_irq); + } /* During ring init, both rp and wp are equal */ memcpy_fromio(&val, (void __iomem *) &ring->ring_ctx->generic.rp, sizeof(u64)); @@ -201,6 +220,9 @@ int mhi_ep_ring_start(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring, void mhi_ep_ring_reset(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring) { + if (ring->type == RING_TYPE_ER) + cancel_delayed_work_sync(&ring->intmodt_work); + ring->started = false; kfree(ring->ring_cache); ring->ring_cache = NULL; diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index f78aefd2d7a3..65ceac1837f9 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -881,6 +881,7 @@ static int parse_config(struct mhi_controller *mhi_cntrl, if (!mhi_cntrl->timeout_ms) mhi_cntrl->timeout_ms = MHI_TIMEOUT_MS; + mhi_cntrl->ready_timeout_ms = config->ready_timeout_ms; mhi_cntrl->bounce_buf = config->use_bounce_buf; mhi_cntrl->buffer_len = config->buf_len; if (!mhi_cntrl->buffer_len) diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h index 2e139e76de4c..30ac415a3000 100644 --- a/drivers/bus/mhi/host/internal.h +++ b/drivers/bus/mhi/host/internal.h @@ -321,7 +321,7 @@ int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl, u32 *out); int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base, u32 offset, u32 mask, - u32 val, u32 delayus); + u32 val, u32 delayus, u32 timeout_ms); void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base, u32 offset, u32 val); int __must_check mhi_write_reg_field(struct mhi_controller *mhi_cntrl, diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index dcf627b36e82..abb561db9ae1 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -40,10 +40,11 @@ int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl, int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base, u32 offset, - u32 mask, u32 val, u32 delayus) + u32 mask, u32 val, u32 delayus, + u32 timeout_ms) { int ret; - u32 out, retry = (mhi_cntrl->timeout_ms * 1000) / delayus; + u32 out, retry = (timeout_ms * 1000) / delayus; while (retry--) { ret = mhi_read_reg_field(mhi_cntrl, base, offset, mask, &out); @@ -268,7 +269,8 @@ static void mhi_del_ring_element(struct mhi_controller *mhi_cntrl, static bool is_valid_ring_ptr(struct mhi_ring *ring, dma_addr_t addr) { - return addr >= ring->iommu_base && addr < ring->iommu_base + ring->len; + return addr >= ring->iommu_base && addr < ring->iommu_base + ring->len && + !(addr & (sizeof(struct mhi_ring_element) - 1)); } int mhi_destroy_device(struct device *dev, void *data) @@ -642,6 +644,8 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, mhi_del_ring_element(mhi_cntrl, tre_ring); local_rp = tre_ring->rp; + read_unlock_bh(&mhi_chan->lock); + /* notify client */ mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); @@ -667,6 +671,8 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, kfree(buf_info->cb_buf); } } + + read_lock_bh(&mhi_chan->lock); } break; } /* CC_EOT */ @@ -1122,17 +1128,15 @@ static int mhi_queue(struct mhi_device *mhi_dev, struct mhi_buf_info *buf_info, if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) return -EIO; - read_lock_irqsave(&mhi_cntrl->pm_lock, flags); - ret = mhi_is_ring_full(mhi_cntrl, tre_ring); - if (unlikely(ret)) { - ret = -EAGAIN; - goto exit_unlock; - } + if (unlikely(ret)) + return -EAGAIN; ret = mhi_gen_tre(mhi_cntrl, mhi_chan, buf_info, mflags); if (unlikely(ret)) - goto exit_unlock; + return ret; + + read_lock_irqsave(&mhi_cntrl->pm_lock, flags); /* Packet is queued, take a usage ref to exit M3 if necessary * for host->device buffer, balanced put is done on buffer completion @@ -1152,7 +1156,6 @@ static int mhi_queue(struct mhi_device *mhi_dev, struct mhi_buf_info *buf_info, if (dir == DMA_FROM_DEVICE) mhi_cntrl->runtime_put(mhi_cntrl); -exit_unlock: read_unlock_irqrestore(&mhi_cntrl->pm_lock, flags); return ret; @@ -1204,6 +1207,9 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int eot, eob, chain, bei; int ret; + /* Protect accesses for reading and incrementing WP */ + write_lock_bh(&mhi_chan->lock); + buf_ring = &mhi_chan->buf_ring; tre_ring = &mhi_chan->tre_ring; @@ -1221,8 +1227,10 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, if (!info->pre_mapped) { ret = mhi_cntrl->map_single(mhi_cntrl, buf_info); - if (ret) + if (ret) { + write_unlock_bh(&mhi_chan->lock); return ret; + } } eob = !!(flags & MHI_EOB); @@ -1239,6 +1247,8 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, mhi_add_ring_element(mhi_cntrl, tre_ring); mhi_add_ring_element(mhi_cntrl, buf_ring); + write_unlock_bh(&mhi_chan->lock); + return 0; } diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 08f3f039dbdd..cd6cd14b3d29 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -269,6 +269,16 @@ static struct mhi_event_config modem_qcom_v1_mhi_events[] = { MHI_EVENT_CONFIG_HW_DATA(5, 2048, 101) }; +static const struct mhi_controller_config modem_qcom_v2_mhiv_config = { + .max_channels = 128, + .timeout_ms = 8000, + .ready_timeout_ms = 50000, + .num_channels = ARRAY_SIZE(modem_qcom_v1_mhi_channels), + .ch_cfg = modem_qcom_v1_mhi_channels, + .num_events = ARRAY_SIZE(modem_qcom_v1_mhi_events), + .event_cfg = modem_qcom_v1_mhi_events, +}; + static const struct mhi_controller_config modem_qcom_v1_mhiv_config = { .max_channels = 128, .timeout_ms = 8000, @@ -278,6 +288,16 @@ static const struct mhi_controller_config modem_qcom_v1_mhiv_config = { .event_cfg = modem_qcom_v1_mhi_events, }; +static const struct mhi_pci_dev_info mhi_qcom_sdx75_info = { + .name = "qcom-sdx75m", + .fw = "qcom/sdx75m/xbl.elf", + .edl = "qcom/sdx75m/edl.mbn", + .config = &modem_qcom_v2_mhiv_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .sideband_wake = false, +}; + static const struct mhi_pci_dev_info mhi_qcom_sdx65_info = { .name = "qcom-sdx65m", .fw = "qcom/sdx65m/xbl.elf", @@ -600,6 +620,8 @@ static const struct pci_device_id mhi_pci_id_table[] = { .driver_data = (kernel_ulong_t) &mhi_telit_fn990_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0308), .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 }, { 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) */ diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c index 8a4362d75fc4..a2f2feef1476 100644 --- a/drivers/bus/mhi/host/pm.c +++ b/drivers/bus/mhi/host/pm.c @@ -163,6 +163,7 @@ int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl) enum mhi_pm_state cur_state; struct device *dev = &mhi_cntrl->mhi_dev->dev; u32 interval_us = 25000; /* poll register field every 25 milliseconds */ + u32 timeout_ms; int ret, i; /* Check if device entered error state */ @@ -173,14 +174,18 @@ int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl) /* Wait for RESET to be cleared and READY bit to be set by the device */ ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, - MHICTRL_RESET_MASK, 0, interval_us); + MHICTRL_RESET_MASK, 0, interval_us, + mhi_cntrl->timeout_ms); if (ret) { dev_err(dev, "Device failed to clear MHI Reset\n"); return ret; } + timeout_ms = mhi_cntrl->ready_timeout_ms ? + mhi_cntrl->ready_timeout_ms : mhi_cntrl->timeout_ms; ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS, - MHISTATUS_READY_MASK, 1, interval_us); + MHISTATUS_READY_MASK, 1, interval_us, + timeout_ms); if (ret) { dev_err(dev, "Device failed to enter MHI Ready\n"); return ret; @@ -479,7 +484,7 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl) /* Wait for the reset bit to be cleared by the device */ ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, - MHICTRL_RESET_MASK, 0, 25000); + MHICTRL_RESET_MASK, 0, 25000, mhi_cntrl->timeout_ms); if (ret) dev_err(dev, "Device failed to clear MHI Reset\n"); @@ -492,8 +497,8 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl) if (!MHI_IN_PBL(mhi_get_exec_env(mhi_cntrl))) { /* wait for ready to be set */ ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, - MHISTATUS, - MHISTATUS_READY_MASK, 1, 25000); + MHISTATUS, MHISTATUS_READY_MASK, + 1, 25000, mhi_cntrl->timeout_ms); if (ret) dev_err(dev, "Device failed to enter READY state\n"); } @@ -1111,7 +1116,8 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) if (state == MHI_STATE_SYS_ERR) { mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, - MHICTRL_RESET_MASK, 0, interval_us); + MHICTRL_RESET_MASK, 0, interval_us, + mhi_cntrl->timeout_ms); if (ret) { dev_info(dev, "Failed to reset MHI due to syserr state\n"); goto error_exit; @@ -1202,14 +1208,18 @@ EXPORT_SYMBOL_GPL(mhi_power_down); int mhi_sync_power_up(struct mhi_controller *mhi_cntrl) { int ret = mhi_async_power_up(mhi_cntrl); + u32 timeout_ms; if (ret) return ret; + /* Some devices need more time to set ready during power up */ + timeout_ms = mhi_cntrl->ready_timeout_ms ? + mhi_cntrl->ready_timeout_ms : mhi_cntrl->timeout_ms; wait_event_timeout(mhi_cntrl->state_event, MHI_IN_MISSION_MODE(mhi_cntrl->ee) || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); + msecs_to_jiffies(timeout_ms)); ret = (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) ? 0 : -ETIMEDOUT; if (ret) diff --git a/drivers/bus/moxtet.c b/drivers/bus/moxtet.c index e384fbc6c1d9..641c1a6adc8a 100644 --- a/drivers/bus/moxtet.c +++ b/drivers/bus/moxtet.c @@ -102,7 +102,7 @@ static int moxtet_match(struct device *dev, struct device_driver *drv) return 0; } -static struct bus_type moxtet_bus_type = { +static const struct bus_type moxtet_bus_type = { .name = "moxtet", .dev_groups = moxtet_dev_groups, .match = moxtet_match, diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index 4461c6c9313f..b74d76afccb6 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -57,13 +57,17 @@ #include <linux/init.h> #include <linux/kernel.h> +#include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/idr.h> #include <linux/cdx/cdx_bus.h> #include <linux/iommu.h> #include <linux/dma-map-ops.h> +#include <linux/debugfs.h> #include "cdx.h" /* Default DMA mask for devices on a CDX bus */ @@ -74,9 +78,13 @@ static DEFINE_IDA(cdx_controller_ida); /* Lock to protect controller ops */ static DEFINE_MUTEX(cdx_controller_lock); +/* Debugfs dir for cdx bus */ +static struct dentry *cdx_debugfs_dir; static char *compat_node_name = "xlnx,versal-net-cdx"; +static void cdx_destroy_res_attr(struct cdx_device *cdx_dev, int num); + /** * cdx_dev_reset - Reset a CDX device * @dev: CDX device @@ -145,6 +153,8 @@ static int cdx_unregister_device(struct device *dev, if (cdx_dev->enabled && cdx->ops->bus_disable) cdx->ops->bus_disable(cdx, cdx_dev->bus_num); } else { + cdx_destroy_res_attr(cdx_dev, MAX_CDX_DEV_RESOURCES); + debugfs_remove_recursive(cdx_dev->debugfs_dir); kfree(cdx_dev->driver_override); cdx_dev->driver_override = NULL; } @@ -548,6 +558,31 @@ static const struct attribute_group *cdx_dev_groups[] = { NULL, }; +static int cdx_debug_resource_show(struct seq_file *s, void *data) +{ + struct cdx_device *cdx_dev = s->private; + int i; + + for (i = 0; i < MAX_CDX_DEV_RESOURCES; i++) { + struct resource *res = &cdx_dev->res[i]; + + seq_printf(s, "%pr\n", res); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(cdx_debug_resource); + +static void cdx_device_debugfs_init(struct cdx_device *cdx_dev) +{ + cdx_dev->debugfs_dir = debugfs_create_dir(dev_name(&cdx_dev->dev), cdx_debugfs_dir); + if (IS_ERR(cdx_dev->debugfs_dir)) + return; + + debugfs_create_file("resource", 0444, cdx_dev->debugfs_dir, cdx_dev, + &cdx_debug_resource_fops); +} + static ssize_t rescan_store(const struct bus_type *bus, const char *buf, size_t count) { @@ -569,12 +604,12 @@ static ssize_t rescan_store(const struct bus_type *bus, /* Rescan all the devices */ for_each_compatible_node(np, NULL, compat_node_name) { - if (!np) - return -EINVAL; - pd = of_find_device_by_node(np); - if (!pd) - return -EINVAL; + if (!pd) { + of_node_put(np); + count = -EINVAL; + goto unlock; + } cdx = platform_get_drvdata(pd); if (cdx && cdx->controller_registered && cdx->ops->scan) @@ -583,6 +618,7 @@ static ssize_t rescan_store(const struct bus_type *bus, put_device(&pd->dev); } +unlock: mutex_unlock(&cdx_controller_lock); return count; @@ -640,11 +676,105 @@ static void cdx_device_release(struct device *dev) kfree(cdx_dev); } +static const struct vm_operations_struct cdx_phys_vm_ops = { +#ifdef CONFIG_HAVE_IOREMAP_PROT + .access = generic_access_phys, +#endif +}; + +/** + * cdx_mmap_resource - map a CDX resource into user memory space + * @fp: File pointer. Not used in this function, but required where + * this API is registered as a callback. + * @kobj: kobject for mapping + * @attr: struct bin_attribute for the file being mapped + * @vma: struct vm_area_struct passed into the mmap + * + * Use the regular CDX mapping routines to map a CDX resource into userspace. + * + * Return: true on success, false otherwise. + */ +static int cdx_mmap_resource(struct file *fp, struct kobject *kobj, + struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + struct cdx_device *cdx_dev = to_cdx_device(kobj_to_dev(kobj)); + int num = (unsigned long)attr->private; + struct resource *res; + unsigned long size; + + res = &cdx_dev->res[num]; + if (iomem_is_exclusive(res->start)) + return -EINVAL; + + /* Make sure the caller is mapping a valid resource for this device */ + size = ((cdx_resource_len(cdx_dev, num) - 1) >> PAGE_SHIFT) + 1; + if (vma->vm_pgoff + vma_pages(vma) > size) + return -EINVAL; + + /* + * Map memory region and vm->vm_pgoff is expected to be an + * offset within that region. + */ + vma->vm_page_prot = pgprot_device(vma->vm_page_prot); + vma->vm_pgoff += (cdx_resource_start(cdx_dev, num) >> PAGE_SHIFT); + vma->vm_ops = &cdx_phys_vm_ops; + return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + +static void cdx_destroy_res_attr(struct cdx_device *cdx_dev, int num) +{ + int i; + + /* removing the bin attributes */ + for (i = 0; i < num; i++) { + struct bin_attribute *res_attr; + + res_attr = cdx_dev->res_attr[i]; + if (res_attr) { + sysfs_remove_bin_file(&cdx_dev->dev.kobj, res_attr); + kfree(res_attr); + } + } +} + +#define CDX_RES_ATTR_NAME_LEN 10 +static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num) +{ + struct bin_attribute *res_attr; + char *res_attr_name; + int ret; + + res_attr = kzalloc(sizeof(*res_attr) + CDX_RES_ATTR_NAME_LEN, GFP_ATOMIC); + if (!res_attr) + return -ENOMEM; + + res_attr_name = (char *)(res_attr + 1); + + sysfs_bin_attr_init(res_attr); + + cdx_dev->res_attr[num] = res_attr; + sprintf(res_attr_name, "resource%d", num); + + res_attr->mmap = cdx_mmap_resource; + res_attr->attr.name = res_attr_name; + res_attr->attr.mode = 0600; + res_attr->size = cdx_resource_len(cdx_dev, num); + res_attr->private = (void *)(unsigned long)num; + ret = sysfs_create_bin_file(&cdx_dev->dev.kobj, res_attr); + if (ret) + kfree(res_attr); + + return ret; +} + int cdx_device_add(struct cdx_dev_params *dev_params) { struct cdx_controller *cdx = dev_params->cdx; struct cdx_device *cdx_dev; - int ret; + int ret, i; cdx_dev = kzalloc(sizeof(*cdx_dev), GFP_KERNEL); if (!cdx_dev) @@ -687,7 +817,28 @@ int cdx_device_add(struct cdx_dev_params *dev_params) goto fail; } + /* Create resource<N> attributes */ + for (i = 0; i < MAX_CDX_DEV_RESOURCES; i++) { + if (cdx_resource_flags(cdx_dev, i) & IORESOURCE_MEM) { + /* skip empty resources */ + if (!cdx_resource_len(cdx_dev, i)) + continue; + + ret = cdx_create_res_attr(cdx_dev, i); + if (ret != 0) { + dev_err(&cdx_dev->dev, + "cdx device resource<%d> file creation failed: %d", i, ret); + goto resource_create_fail; + } + } + } + + cdx_device_debugfs_init(cdx_dev); + return 0; +resource_create_fail: + cdx_destroy_res_attr(cdx_dev, i); + device_del(&cdx_dev->dev); fail: /* * Do not free cdx_dev here as it would be freed in @@ -788,6 +939,12 @@ EXPORT_SYMBOL_NS_GPL(cdx_unregister_controller, CDX_BUS_CONTROLLER); static int __init cdx_bus_init(void) { - return bus_register(&cdx_bus_type); + int ret; + + ret = bus_register(&cdx_bus_type); + if (!ret) + cdx_debugfs_dir = debugfs_create_dir(cdx_bus_type.name, NULL); + + return ret; } postcore_initcall(cdx_bus_init); diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 4c188e9e477c..ee951b265213 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -299,7 +299,7 @@ static int register_device(int minor, struct pp_struct *pp) goto err; } - index = ida_simple_get(&ida_index, 0, 0, GFP_KERNEL); + index = ida_alloc(&ida_index, GFP_KERNEL); memset(&ppdev_cb, 0, sizeof(ppdev_cb)); ppdev_cb.irq_func = pp_irq; ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; @@ -310,7 +310,7 @@ static int register_device(int minor, struct pp_struct *pp) if (!pdev) { pr_warn("%s: failed to register device!\n", name); rc = -ENXIO; - ida_simple_remove(&ida_index, index); + ida_free(&ida_index, index); goto err; } @@ -750,7 +750,7 @@ static int pp_release(struct inode *inode, struct file *file) if (pp->pdev) { parport_unregister_device(pp->pdev); - ida_simple_remove(&ida_index, pp->index); + ida_free(&ida_index, pp->index); pp->pdev = NULL; pr_debug(CHRDEV "%x: unregistered pardevice\n", minor); } diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index 5af804c17a75..4c806a189ee5 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -40,7 +40,7 @@ static struct ttyprintk_port tpk_port; static int tpk_curr; -static char tpk_buffer[TPK_STR_SIZE + 4]; +static u8 tpk_buffer[TPK_STR_SIZE + 4]; static void tpk_flush(void) { @@ -51,9 +51,9 @@ static void tpk_flush(void) } } -static int tpk_printk(const u8 *buf, int count) +static int tpk_printk(const u8 *buf, size_t count) { - int i; + size_t i; for (i = 0; i < count; i++) { if (tpk_curr >= TPK_STR_SIZE) { diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 431e9e5bf9c1..035f89f1a251 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -230,9 +230,6 @@ struct port { bool guest_connected; }; -/* This is the very early arch-specified put chars function. */ -static int (*early_put_chars)(u32, const char *, int); - static struct port *find_port_by_vtermno(u32 vtermno) { struct port *port; @@ -653,7 +650,7 @@ done: * Give out the data that's requested from the buffer that we have * queued up. */ -static ssize_t fill_readbuf(struct port *port, char __user *out_buf, +static ssize_t fill_readbuf(struct port *port, u8 __user *out_buf, size_t out_count, bool to_user) { struct port_buffer *buf; @@ -672,7 +669,7 @@ static ssize_t fill_readbuf(struct port *port, char __user *out_buf, if (ret) return -EFAULT; } else { - memcpy((__force char *)out_buf, buf->buf + buf->offset, + memcpy((__force u8 *)out_buf, buf->buf + buf->offset, out_count); } @@ -1107,16 +1104,13 @@ static const struct file_operations port_fops = { * it to finish: inefficient in theory, but in practice * implementations will do it immediately. */ -static int put_chars(u32 vtermno, const char *buf, int count) +static ssize_t put_chars(u32 vtermno, const u8 *buf, size_t count) { struct port *port; struct scatterlist sg[1]; void *data; int ret; - if (unlikely(early_put_chars)) - return early_put_chars(vtermno, buf, count); - port = find_port_by_vtermno(vtermno); if (!port) return -EPIPE; @@ -1138,14 +1132,10 @@ static int put_chars(u32 vtermno, const char *buf, int count) * We call out to fill_readbuf that gets us the required data from the * buffers that are queued up. */ -static int get_chars(u32 vtermno, char *buf, int count) +static ssize_t get_chars(u32 vtermno, u8 *buf, size_t count) { struct port *port; - /* If we've not set up the port yet, we have no input to give. */ - if (unlikely(early_put_chars)) - return 0; - port = find_port_by_vtermno(vtermno); if (!port) return -EPIPE; @@ -1153,7 +1143,7 @@ static int get_chars(u32 vtermno, char *buf, int count) /* If we don't have an input queue yet, we can't get input. */ BUG_ON(!port->in_vq); - return fill_readbuf(port, (__force char __user *)buf, count, false); + return fill_readbuf(port, (__force u8 __user *)buf, count, false); } static void resize_console(struct port *port) @@ -1201,21 +1191,6 @@ static const struct hv_ops hv_ops = { .notifier_hangup = notifier_del_vio, }; -/* - * Console drivers are initialized very early so boot messages can go - * out, so we do things slightly differently from the generic virtio - * initialization of the net and block drivers. - * - * At this stage, the console is output-only. It's too early to set - * up a virtqueue, so we let the drivers do some boutique early-output - * thing. - */ -int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) -{ - early_put_chars = put_chars; - return hvc_instantiate(0, 0, &hv_ops); -} - static int init_port_console(struct port *port) { int ret; @@ -1256,13 +1231,6 @@ static int init_port_console(struct port *port) spin_unlock_irq(&pdrvdata_lock); port->guest_connected = true; - /* - * Start using the new console output if this is the first - * console to come up. - */ - if (early_put_chars) - early_put_chars = NULL; - /* Notify host of port being opened */ send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); @@ -1999,7 +1967,6 @@ static int virtcons_probe(struct virtio_device *vdev) struct ports_device *portdev; int err; bool multiport; - bool early = early_put_chars != NULL; /* We only need a config space if features are offered */ if (!vdev->config->get && @@ -2010,9 +1977,6 @@ static int virtcons_probe(struct virtio_device *vdev) return -EINVAL; } - /* Ensure to read early_put_chars now */ - barrier(); - portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); if (!portdev) { err = -ENOMEM; @@ -2100,18 +2064,6 @@ static int virtcons_probe(struct virtio_device *vdev) __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, VIRTIO_CONSOLE_DEVICE_READY, 1); - /* - * If there was an early virtio console, assume that there are no - * other consoles. We need to wait until the hvc_alloc matches the - * hvc_instantiate, otherwise tty_open will complain, resulting in - * a "Warning: unable to open an initial console" boot failure. - * Without multiport this is done in add_port above. With multiport - * this might take some host<->guest communication - thus we have to - * wait. - */ - if (multiport && early) - wait_for_completion(&early_console_added); - return 0; free_chrdev: diff --git a/drivers/clk/qcom/gcc-x1e80100.c b/drivers/clk/qcom/gcc-x1e80100.c index 74db7fef237b..d7182d6e9783 100644 --- a/drivers/clk/qcom/gcc-x1e80100.c +++ b/drivers/clk/qcom/gcc-x1e80100.c @@ -4,8 +4,9 @@ */ #include <linux/clk-provider.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/platform_device.h> #include <linux/regmap.h> #include <dt-bindings/clock/qcom,x1e80100-gcc.h> diff --git a/drivers/clocksource/timer-cadence-ttc.c b/drivers/clocksource/timer-cadence-ttc.c index 32daaac9b132..ca7a06489c40 100644 --- a/drivers/clocksource/timer-cadence-ttc.c +++ b/drivers/clocksource/timer-cadence-ttc.c @@ -69,7 +69,7 @@ * @base_addr: Base address of timer * @freq: Timer input clock frequency * @clk: Associated clock source - * @clk_rate_change_nb Notifier block for clock rate changes + * @clk_rate_change_nb: Notifier block for clock rate changes */ struct ttc_timer { void __iomem *base_addr; @@ -134,7 +134,7 @@ static void ttc_set_interval(struct ttc_timer *timer, * @irq: IRQ number of the Timer * @dev_id: void pointer to the ttc_timer instance * - * returns: Always IRQ_HANDLED - success + * Returns: Always IRQ_HANDLED - success **/ static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id) { @@ -151,8 +151,9 @@ static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id) /** * __ttc_clocksource_read - Reads the timer counter register + * @cs: &clocksource to read from * - * returns: Current timer counter register value + * Returns: Current timer counter register value **/ static u64 __ttc_clocksource_read(struct clocksource *cs) { @@ -173,7 +174,7 @@ static u64 notrace ttc_sched_clock_read(void) * @cycles: Timer interval ticks * @evt: Address of clock event instance * - * returns: Always 0 - success + * Returns: Always %0 - success **/ static int ttc_set_next_event(unsigned long cycles, struct clock_event_device *evt) @@ -186,9 +187,12 @@ static int ttc_set_next_event(unsigned long cycles, } /** - * ttc_set_{shutdown|oneshot|periodic} - Sets the state of timer - * + * ttc_shutdown - Sets the state of timer * @evt: Address of clock event instance + * + * Used for shutdown or oneshot. + * + * Returns: Always %0 - success **/ static int ttc_shutdown(struct clock_event_device *evt) { @@ -202,6 +206,12 @@ static int ttc_shutdown(struct clock_event_device *evt) return 0; } +/** + * ttc_set_periodic - Sets the state of timer + * @evt: Address of clock event instance + * + * Returns: Always %0 - success + */ static int ttc_set_periodic(struct clock_event_device *evt) { struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt); diff --git a/drivers/clocksource/timer-ep93xx.c b/drivers/clocksource/timer-ep93xx.c index bc0ca6e12334..6981ff3ac8a9 100644 --- a/drivers/clocksource/timer-ep93xx.c +++ b/drivers/clocksource/timer-ep93xx.c @@ -155,9 +155,8 @@ static int __init ep93xx_timer_of_init(struct device_node *np) ep93xx_tcu = tcu; irq = irq_of_parse_and_map(np, 0); - if (irq == 0) - irq = -EINVAL; - if (irq < 0) { + if (!irq) { + ret = -EINVAL; pr_err("EP93XX Timer Can't parse IRQ %d", irq); goto out_free; } diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c index 57857c0dfba9..e66dcbd66566 100644 --- a/drivers/clocksource/timer-riscv.c +++ b/drivers/clocksource/timer-riscv.c @@ -61,12 +61,19 @@ static int riscv_clock_next_event(unsigned long delta, return 0; } +static int riscv_clock_shutdown(struct clock_event_device *evt) +{ + riscv_clock_event_stop(); + return 0; +} + static unsigned int riscv_clock_event_irq; static DEFINE_PER_CPU(struct clock_event_device, riscv_clock_event) = { .name = "riscv_timer_clockevent", .features = CLOCK_EVT_FEAT_ONESHOT, .rating = 100, .set_next_event = riscv_clock_next_event, + .set_state_shutdown = riscv_clock_shutdown, }; /* diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index 5f60f6bd3386..56acf2617262 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -183,7 +183,7 @@ static inline u32 dmtimer_read(struct dmtimer *timer, u32 reg) * dmtimer_write - write timer registers in posted and non-posted mode * @timer: timer pointer over which write operation is to perform * @reg: lowest byte holds the register offset - * @value: data to write into the register + * @val: data to write into the register * * The posted mode bit is encoded in reg. Note that in posted mode, the write * pending bit must be checked. Otherwise a write on a register which has a @@ -949,7 +949,7 @@ static int omap_dm_timer_set_int_enable(struct omap_dm_timer *cookie, /** * omap_dm_timer_set_int_disable - disable timer interrupts - * @timer: pointer to timer handle + * @cookie: pointer to timer cookie * @mask: bit mask of interrupts to be disabled * * Disables the specified timer interrupts for a timer. diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c index 1548dea15df1..1b481731df96 100644 --- a/drivers/comedi/comedi_fops.c +++ b/drivers/comedi/comedi_fops.c @@ -1714,8 +1714,8 @@ static int __comedi_get_user_chanlist(struct comedi_device *dev, lockdep_assert_held(&dev->mutex); cmd->chanlist = NULL; - chanlist = memdup_user(user_chanlist, - cmd->chanlist_len * sizeof(unsigned int)); + chanlist = memdup_array_user(user_chanlist, + cmd->chanlist_len, sizeof(unsigned int)); if (IS_ERR(chanlist)) return PTR_ERR(chanlist); diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 1f6186475715..1791d37fbc53 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -1232,14 +1232,13 @@ static void amd_pstate_epp_update_limit(struct cpufreq_policy *policy) max_limit_perf = div_u64(policy->max * cpudata->highest_perf, cpudata->max_freq); min_limit_perf = div_u64(policy->min * cpudata->highest_perf, cpudata->max_freq); + WRITE_ONCE(cpudata->max_limit_perf, max_limit_perf); + WRITE_ONCE(cpudata->min_limit_perf, min_limit_perf); + max_perf = clamp_t(unsigned long, max_perf, cpudata->min_limit_perf, cpudata->max_limit_perf); min_perf = clamp_t(unsigned long, min_perf, cpudata->min_limit_perf, cpudata->max_limit_perf); - - WRITE_ONCE(cpudata->max_limit_perf, max_limit_perf); - WRITE_ONCE(cpudata->min_limit_perf, min_limit_perf); - value = READ_ONCE(cpudata->cppc_req_cached); if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 3c69040920b8..ca94e60e705a 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -302,7 +302,10 @@ static bool hwp_forced __read_mostly; static struct cpufreq_driver *intel_pstate_driver __read_mostly; -#define HYBRID_SCALING_FACTOR 78741 +#define HYBRID_SCALING_FACTOR 78741 +#define HYBRID_SCALING_FACTOR_MTL 80000 + +static int hybrid_scaling_factor = HYBRID_SCALING_FACTOR; static inline int core_get_scaling(void) { @@ -422,7 +425,7 @@ static int intel_pstate_cppc_get_scaling(int cpu) */ if (!ret && cppc_perf.nominal_perf && cppc_perf.nominal_freq && cppc_perf.nominal_perf * 100 != cppc_perf.nominal_freq) - return HYBRID_SCALING_FACTOR; + return hybrid_scaling_factor; return core_get_scaling(); } @@ -526,6 +529,30 @@ static int intel_pstate_cppc_get_scaling(int cpu) } #endif /* CONFIG_ACPI_CPPC_LIB */ +static int intel_pstate_freq_to_hwp_rel(struct cpudata *cpu, int freq, + unsigned int relation) +{ + if (freq == cpu->pstate.turbo_freq) + return cpu->pstate.turbo_pstate; + + if (freq == cpu->pstate.max_freq) + return cpu->pstate.max_pstate; + + switch (relation) { + case CPUFREQ_RELATION_H: + return freq / cpu->pstate.scaling; + case CPUFREQ_RELATION_C: + return DIV_ROUND_CLOSEST(freq, cpu->pstate.scaling); + } + + return DIV_ROUND_UP(freq, cpu->pstate.scaling); +} + +static int intel_pstate_freq_to_hwp(struct cpudata *cpu, int freq) +{ + return intel_pstate_freq_to_hwp_rel(cpu, freq, CPUFREQ_RELATION_L); +} + /** * intel_pstate_hybrid_hwp_adjust - Calibrate HWP performance levels. * @cpu: Target CPU. @@ -543,6 +570,7 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; int perf_ctl_turbo = pstate_funcs.get_turbo(cpu->cpu); int scaling = cpu->pstate.scaling; + int freq; pr_debug("CPU%d: perf_ctl_max_phys = %d\n", cpu->cpu, perf_ctl_max_phys); pr_debug("CPU%d: perf_ctl_turbo = %d\n", cpu->cpu, perf_ctl_turbo); @@ -556,16 +584,16 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) cpu->pstate.max_freq = rounddown(cpu->pstate.max_pstate * scaling, perf_ctl_scaling); - cpu->pstate.max_pstate_physical = - DIV_ROUND_UP(perf_ctl_max_phys * perf_ctl_scaling, - scaling); + freq = perf_ctl_max_phys * perf_ctl_scaling; + cpu->pstate.max_pstate_physical = intel_pstate_freq_to_hwp(cpu, freq); - cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; + freq = cpu->pstate.min_pstate * perf_ctl_scaling; + cpu->pstate.min_freq = freq; /* * Cast the min P-state value retrieved via pstate_funcs.get_min() to * the effective range of HWP performance levels. */ - cpu->pstate.min_pstate = DIV_ROUND_UP(cpu->pstate.min_freq, scaling); + cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, freq); } static inline void update_turbo_state(void) @@ -1968,7 +1996,7 @@ static int hwp_get_cpu_scaling(int cpu) smp_call_function_single(cpu, hybrid_get_type, &cpu_type, 1); /* P-cores have a smaller perf level-to-freqency scaling factor. */ if (cpu_type == 0x40) - return HYBRID_SCALING_FACTOR; + return hybrid_scaling_factor; /* Use default core scaling for E-cores */ if (cpu_type == 0x20) @@ -2525,13 +2553,12 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, * abstract values to represent performance rather than pure ratios. */ if (hwp_active && cpu->pstate.scaling != perf_ctl_scaling) { - int scaling = cpu->pstate.scaling; int freq; freq = max_policy_perf * perf_ctl_scaling; - max_policy_perf = DIV_ROUND_UP(freq, scaling); + max_policy_perf = intel_pstate_freq_to_hwp(cpu, freq); freq = min_policy_perf * perf_ctl_scaling; - min_policy_perf = DIV_ROUND_UP(freq, scaling); + min_policy_perf = intel_pstate_freq_to_hwp(cpu, freq); } pr_debug("cpu:%d min_policy_perf:%d max_policy_perf:%d\n", @@ -2905,18 +2932,7 @@ static int intel_cpufreq_target(struct cpufreq_policy *policy, cpufreq_freq_transition_begin(policy, &freqs); - switch (relation) { - case CPUFREQ_RELATION_L: - target_pstate = DIV_ROUND_UP(freqs.new, cpu->pstate.scaling); - break; - case CPUFREQ_RELATION_H: - target_pstate = freqs.new / cpu->pstate.scaling; - break; - default: - target_pstate = DIV_ROUND_CLOSEST(freqs.new, cpu->pstate.scaling); - break; - } - + target_pstate = intel_pstate_freq_to_hwp_rel(cpu, freqs.new, relation); target_pstate = intel_cpufreq_update_pstate(policy, target_pstate, false); freqs.new = target_pstate * cpu->pstate.scaling; @@ -2934,7 +2950,7 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy, update_turbo_state(); - target_pstate = DIV_ROUND_UP(target_freq, cpu->pstate.scaling); + target_pstate = intel_pstate_freq_to_hwp(cpu, target_freq); target_pstate = intel_cpufreq_update_pstate(policy, target_pstate, true); @@ -3399,6 +3415,11 @@ static const struct x86_cpu_id intel_epp_balance_perf[] = { {} }; +static const struct x86_cpu_id intel_hybrid_scaling_factor[] = { + X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, HYBRID_SCALING_FACTOR_MTL), + {} +}; + static int __init intel_pstate_init(void) { static struct cpudata **_all_cpu_data; @@ -3489,9 +3510,16 @@ hwp_cpu_matched: if (hwp_active) { const struct x86_cpu_id *id = x86_match_cpu(intel_epp_balance_perf); + const struct x86_cpu_id *hybrid_id = x86_match_cpu(intel_hybrid_scaling_factor); if (id) epp_values[EPP_INDEX_BALANCE_PERFORMANCE] = id->driver_data; + + if (hybrid_id) { + hybrid_scaling_factor = hybrid_id->driver_data; + pr_debug("hybrid scaling factor: %d\n", hybrid_scaling_factor); + } + } mutex_lock(&intel_pstate_driver_lock); diff --git a/drivers/crypto/caam/caamalg_qi2.c b/drivers/crypto/caam/caamalg_qi2.c index a148ff1f0872..a4f6884416a0 100644 --- a/drivers/crypto/caam/caamalg_qi2.c +++ b/drivers/crypto/caam/caamalg_qi2.c @@ -4545,6 +4545,7 @@ struct caam_hash_alg { struct list_head entry; struct device *dev; int alg_type; + bool is_hmac; struct ahash_alg ahash_alg; }; @@ -4571,7 +4572,7 @@ static int caam_hash_cra_init(struct crypto_tfm *tfm) ctx->dev = caam_hash->dev; - if (alg->setkey) { + if (caam_hash->is_hmac) { ctx->adata.key_dma = dma_map_single_attrs(ctx->dev, ctx->key, ARRAY_SIZE(ctx->key), DMA_TO_DEVICE, @@ -4611,7 +4612,7 @@ static int caam_hash_cra_init(struct crypto_tfm *tfm) * For keyed hash algorithms shared descriptors * will be created later in setkey() callback */ - return alg->setkey ? 0 : ahash_set_sh_desc(ahash); + return caam_hash->is_hmac ? 0 : ahash_set_sh_desc(ahash); } static void caam_hash_cra_exit(struct crypto_tfm *tfm) @@ -4646,12 +4647,14 @@ static struct caam_hash_alg *caam_hash_alloc(struct device *dev, template->hmac_name); snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s", template->hmac_driver_name); + t_alg->is_hmac = true; } else { snprintf(alg->cra_name, CRYPTO_MAX_ALG_NAME, "%s", template->name); snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s", template->driver_name); t_alg->ahash_alg.setkey = NULL; + t_alg->is_hmac = false; } alg->cra_module = THIS_MODULE; alg->cra_init = caam_hash_cra_init; diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 290c8500c247..fdd724228c2f 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -1753,6 +1753,7 @@ static struct caam_hash_template driver_hash[] = { struct caam_hash_alg { struct list_head entry; int alg_type; + bool is_hmac; struct ahash_engine_alg ahash_alg; }; @@ -1804,7 +1805,7 @@ static int caam_hash_cra_init(struct crypto_tfm *tfm) } else { if (priv->era >= 6) { ctx->dir = DMA_BIDIRECTIONAL; - ctx->key_dir = alg->setkey ? DMA_TO_DEVICE : DMA_NONE; + ctx->key_dir = caam_hash->is_hmac ? DMA_TO_DEVICE : DMA_NONE; } else { ctx->dir = DMA_TO_DEVICE; ctx->key_dir = DMA_NONE; @@ -1862,7 +1863,7 @@ static int caam_hash_cra_init(struct crypto_tfm *tfm) * For keyed hash algorithms shared descriptors * will be created later in setkey() callback */ - return alg->setkey ? 0 : ahash_set_sh_desc(ahash); + return caam_hash->is_hmac ? 0 : ahash_set_sh_desc(ahash); } static void caam_hash_cra_exit(struct crypto_tfm *tfm) @@ -1915,12 +1916,14 @@ caam_hash_alloc(struct caam_hash_template *template, template->hmac_name); snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s", template->hmac_driver_name); + t_alg->is_hmac = true; } else { snprintf(alg->cra_name, CRYPTO_MAX_ALG_NAME, "%s", template->name); snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s", template->driver_name); halg->setkey = NULL; + t_alg->is_hmac = false; } alg->cra_module = THIS_MODULE; alg->cra_init = caam_hash_cra_init; diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index e4d3f45242f6..b04bc1d3d627 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -534,10 +534,16 @@ EXPORT_SYMBOL_GPL(sev_platform_init); static int __sev_platform_shutdown_locked(int *error) { - struct sev_device *sev = psp_master->sev_data; + struct psp_device *psp = psp_master; + struct sev_device *sev; int ret; - if (!sev || sev->state == SEV_STATE_UNINIT) + if (!psp || !psp->sev_data) + return 0; + + sev = psp->sev_data; + + if (sev->state == SEV_STATE_UNINIT) return 0; ret = __sev_do_cmd_locked(SEV_CMD_SHUTDOWN, NULL, error); diff --git a/drivers/crypto/intel/qat/qat_4xxx/adf_4xxx_hw_data.c b/drivers/crypto/intel/qat/qat_4xxx/adf_4xxx_hw_data.c index 479062aa5e6b..94a0ebb03d8c 100644 --- a/drivers/crypto/intel/qat/qat_4xxx/adf_4xxx_hw_data.c +++ b/drivers/crypto/intel/qat/qat_4xxx/adf_4xxx_hw_data.c @@ -463,6 +463,7 @@ void adf_init_hw_data_4xxx(struct adf_hw_device_data *hw_data, u32 dev_id) hw_data->fw_name = ADF_402XX_FW; hw_data->fw_mmp_name = ADF_402XX_MMP; hw_data->uof_get_name = uof_get_name_402xx; + hw_data->get_ena_thd_mask = get_ena_thd_mask; break; case ADF_401XX_PCI_DEVICE_ID: hw_data->fw_name = ADF_4XXX_FW; diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig index 8ea1d340e438..67998dbd1d46 100644 --- a/drivers/cxl/Kconfig +++ b/drivers/cxl/Kconfig @@ -5,6 +5,7 @@ menuconfig CXL_BUS select FW_LOADER select FW_UPLOAD select PCI_DOE + select FIRMWARE_TABLE help CXL is a bus that is electrically compatible with PCI Express, but layers three protocols on that signalling (CXL.io, CXL.cache, and @@ -54,8 +55,10 @@ config CXL_MEM_RAW_COMMANDS config CXL_ACPI tristate "CXL ACPI: Platform Support" depends on ACPI + depends on ACPI_NUMA default CXL_BUS select ACPI_TABLE_LIB + select ACPI_HMAT help Enable support for host managed device memory (HDM) resources published by a platform's ACPI CXL memory layout description. See diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c index 2034eb4ce83f..dcf2b39e1048 100644 --- a/drivers/cxl/acpi.c +++ b/drivers/cxl/acpi.c @@ -6,6 +6,7 @@ #include <linux/kernel.h> #include <linux/acpi.h> #include <linux/pci.h> +#include <linux/node.h> #include <asm/div64.h> #include "cxlpci.h" #include "cxl.h" @@ -17,6 +18,10 @@ struct cxl_cxims_data { u64 xormaps[] __counted_by(nr_maps); }; +static const guid_t acpi_cxl_qtg_id_guid = + GUID_INIT(0xF365F9A6, 0xA7DE, 0x4071, + 0xA6, 0x6A, 0xB4, 0x0C, 0x0B, 0x4F, 0x8E, 0x52); + /* * Find a targets entry (n) in the host bridge interleave list. * CXL Specification 3.0 Table 9-22 @@ -194,6 +199,123 @@ struct cxl_cfmws_context { int id; }; +/** + * cxl_acpi_evaluate_qtg_dsm - Retrieve QTG ids via ACPI _DSM + * @handle: ACPI handle + * @coord: performance access coordinates + * @entries: number of QTG IDs to return + * @qos_class: int array provided by caller to return QTG IDs + * + * Return: number of QTG IDs returned, or -errno for errors + * + * Issue QTG _DSM with accompanied bandwidth and latency data in order to get + * the QTG IDs that are suitable for the performance point in order of most + * suitable to least suitable. Write back array of QTG IDs and return the + * actual number of QTG IDs written back. + */ +static int +cxl_acpi_evaluate_qtg_dsm(acpi_handle handle, struct access_coordinate *coord, + int entries, int *qos_class) +{ + union acpi_object *out_obj, *out_buf, *obj; + union acpi_object in_array[4] = { + [0].integer = { ACPI_TYPE_INTEGER, coord->read_latency }, + [1].integer = { ACPI_TYPE_INTEGER, coord->write_latency }, + [2].integer = { ACPI_TYPE_INTEGER, coord->read_bandwidth }, + [3].integer = { ACPI_TYPE_INTEGER, coord->write_bandwidth }, + }; + union acpi_object in_obj = { + .package = { + .type = ACPI_TYPE_PACKAGE, + .count = 4, + .elements = in_array, + }, + }; + int count, pkg_entries, i; + u16 max_qtg; + int rc; + + if (!entries) + return -EINVAL; + + out_obj = acpi_evaluate_dsm(handle, &acpi_cxl_qtg_id_guid, 1, 1, &in_obj); + if (!out_obj) + return -ENXIO; + + if (out_obj->type != ACPI_TYPE_PACKAGE) { + rc = -ENXIO; + goto out; + } + + /* Check Max QTG ID */ + obj = &out_obj->package.elements[0]; + if (obj->type != ACPI_TYPE_INTEGER) { + rc = -ENXIO; + goto out; + } + + max_qtg = obj->integer.value; + + /* It's legal to have 0 QTG entries */ + pkg_entries = out_obj->package.count; + if (pkg_entries <= 1) { + rc = 0; + goto out; + } + + /* Retrieve QTG IDs package */ + obj = &out_obj->package.elements[1]; + if (obj->type != ACPI_TYPE_PACKAGE) { + rc = -ENXIO; + goto out; + } + + pkg_entries = obj->package.count; + count = min(entries, pkg_entries); + for (i = 0; i < count; i++) { + u16 qtg_id; + + out_buf = &obj->package.elements[i]; + if (out_buf->type != ACPI_TYPE_INTEGER) { + rc = -ENXIO; + goto out; + } + + qtg_id = out_buf->integer.value; + if (qtg_id > max_qtg) + pr_warn("QTG ID %u greater than MAX %u\n", + qtg_id, max_qtg); + + qos_class[i] = qtg_id; + } + rc = count; + +out: + ACPI_FREE(out_obj); + return rc; +} + +static int cxl_acpi_qos_class(struct cxl_root *cxl_root, + struct access_coordinate *coord, int entries, + int *qos_class) +{ + struct device *dev = cxl_root->port.uport_dev; + acpi_handle handle; + + if (!dev_is_platform(dev)) + return -ENODEV; + + handle = ACPI_HANDLE(dev); + if (!handle) + return -ENODEV; + + return cxl_acpi_evaluate_qtg_dsm(handle, coord, entries, qos_class); +} + +static const struct cxl_root_ops acpi_root_ops = { + .qos_class = cxl_acpi_qos_class, +}; + static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg, const unsigned long end) { @@ -389,8 +511,29 @@ static int cxl_get_chbs(struct device *dev, struct acpi_device *hb, return 0; } +static int get_genport_coordinates(struct device *dev, struct cxl_dport *dport) +{ + struct acpi_device *hb = to_cxl_host_bridge(NULL, dev); + u32 uid; + int rc; + + if (kstrtou32(acpi_device_uid(hb), 0, &uid)) + return -EINVAL; + + rc = acpi_get_genport_coordinates(uid, &dport->hb_coord); + if (rc < 0) + return rc; + + /* Adjust back to picoseconds from nanoseconds */ + dport->hb_coord.read_latency *= 1000; + dport->hb_coord.write_latency *= 1000; + + return 0; +} + static int add_host_bridge_dport(struct device *match, void *arg) { + int ret; acpi_status rc; struct device *bridge; struct cxl_dport *dport; @@ -440,6 +583,10 @@ static int add_host_bridge_dport(struct device *match, void *arg) if (IS_ERR(dport)) return PTR_ERR(dport); + ret = get_genport_coordinates(match, dport); + if (ret) + dev_dbg(match, "Failed to get generic port perf coordinates.\n"); + return 0; } @@ -656,6 +803,7 @@ static int cxl_acpi_probe(struct platform_device *pdev) { int rc; struct resource *cxl_res; + struct cxl_root *cxl_root; struct cxl_port *root_port; struct device *host = &pdev->dev; struct acpi_device *adev = ACPI_COMPANION(host); @@ -675,9 +823,10 @@ static int cxl_acpi_probe(struct platform_device *pdev) cxl_res->end = -1; cxl_res->flags = IORESOURCE_MEM; - root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL); - if (IS_ERR(root_port)) - return PTR_ERR(root_port); + cxl_root = devm_cxl_add_root(host, &acpi_root_ops); + if (IS_ERR(cxl_root)) + return PTR_ERR(cxl_root); + root_port = &cxl_root->port; rc = bus_for_each_dev(adev->dev.bus, NULL, root_port, add_host_bridge_dport); diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile index 1f66b5d4d935..9259bcc6773c 100644 --- a/drivers/cxl/core/Makefile +++ b/drivers/cxl/core/Makefile @@ -13,5 +13,6 @@ cxl_core-y += mbox.o cxl_core-y += pci.o cxl_core-y += hdm.o cxl_core-y += pmu.o +cxl_core-y += cdat.o cxl_core-$(CONFIG_TRACING) += trace.o cxl_core-$(CONFIG_CXL_REGION) += region.o diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c new file mode 100644 index 000000000000..6fe11546889f --- /dev/null +++ b/drivers/cxl/core/cdat.c @@ -0,0 +1,521 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2023 Intel Corporation. All rights reserved. */ +#include <linux/acpi.h> +#include <linux/xarray.h> +#include <linux/fw_table.h> +#include <linux/node.h> +#include <linux/overflow.h> +#include "cxlpci.h" +#include "cxlmem.h" +#include "core.h" +#include "cxl.h" + +struct dsmas_entry { + struct range dpa_range; + u8 handle; + struct access_coordinate coord; + + int entries; + int qos_class; +}; + +static int cdat_dsmas_handler(union acpi_subtable_headers *header, void *arg, + const unsigned long end) +{ + struct acpi_cdat_header *hdr = &header->cdat; + struct acpi_cdat_dsmas *dsmas; + int size = sizeof(*hdr) + sizeof(*dsmas); + struct xarray *dsmas_xa = arg; + struct dsmas_entry *dent; + u16 len; + int rc; + + len = le16_to_cpu((__force __le16)hdr->length); + if (len != size || (unsigned long)hdr + len > end) { + pr_warn("Malformed DSMAS table length: (%u:%u)\n", size, len); + return -EINVAL; + } + + /* Skip common header */ + dsmas = (struct acpi_cdat_dsmas *)(hdr + 1); + + dent = kzalloc(sizeof(*dent), GFP_KERNEL); + if (!dent) + return -ENOMEM; + + dent->handle = dsmas->dsmad_handle; + dent->dpa_range.start = le64_to_cpu((__force __le64)dsmas->dpa_base_address); + dent->dpa_range.end = le64_to_cpu((__force __le64)dsmas->dpa_base_address) + + le64_to_cpu((__force __le64)dsmas->dpa_length) - 1; + + rc = xa_insert(dsmas_xa, dent->handle, dent, GFP_KERNEL); + if (rc) { + kfree(dent); + return rc; + } + + return 0; +} + +static void cxl_access_coordinate_set(struct access_coordinate *coord, + int access, unsigned int val) +{ + switch (access) { + case ACPI_HMAT_ACCESS_LATENCY: + coord->read_latency = val; + coord->write_latency = val; + break; + case ACPI_HMAT_READ_LATENCY: + coord->read_latency = val; + break; + case ACPI_HMAT_WRITE_LATENCY: + coord->write_latency = val; + break; + case ACPI_HMAT_ACCESS_BANDWIDTH: + coord->read_bandwidth = val; + coord->write_bandwidth = val; + break; + case ACPI_HMAT_READ_BANDWIDTH: + coord->read_bandwidth = val; + break; + case ACPI_HMAT_WRITE_BANDWIDTH: + coord->write_bandwidth = val; + break; + } +} + +static int cdat_dslbis_handler(union acpi_subtable_headers *header, void *arg, + const unsigned long end) +{ + struct acpi_cdat_header *hdr = &header->cdat; + struct acpi_cdat_dslbis *dslbis; + int size = sizeof(*hdr) + sizeof(*dslbis); + struct xarray *dsmas_xa = arg; + struct dsmas_entry *dent; + __le64 le_base; + __le16 le_val; + u64 val; + u16 len; + int rc; + + len = le16_to_cpu((__force __le16)hdr->length); + if (len != size || (unsigned long)hdr + len > end) { + pr_warn("Malformed DSLBIS table length: (%u:%u)\n", size, len); + return -EINVAL; + } + + /* Skip common header */ + dslbis = (struct acpi_cdat_dslbis *)(hdr + 1); + + /* Skip unrecognized data type */ + if (dslbis->data_type > ACPI_HMAT_WRITE_BANDWIDTH) + return 0; + + /* Not a memory type, skip */ + if ((dslbis->flags & ACPI_HMAT_MEMORY_HIERARCHY) != ACPI_HMAT_MEMORY) + return 0; + + dent = xa_load(dsmas_xa, dslbis->handle); + if (!dent) { + pr_warn("No matching DSMAS entry for DSLBIS entry.\n"); + return 0; + } + + le_base = (__force __le64)dslbis->entry_base_unit; + le_val = (__force __le16)dslbis->entry[0]; + rc = check_mul_overflow(le64_to_cpu(le_base), + le16_to_cpu(le_val), &val); + if (rc) + pr_warn("DSLBIS value overflowed.\n"); + + cxl_access_coordinate_set(&dent->coord, dslbis->data_type, val); + + return 0; +} + +static int cdat_table_parse_output(int rc) +{ + if (rc < 0) + return rc; + if (rc == 0) + return -ENOENT; + + return 0; +} + +static int cxl_cdat_endpoint_process(struct cxl_port *port, + struct xarray *dsmas_xa) +{ + int rc; + + rc = cdat_table_parse(ACPI_CDAT_TYPE_DSMAS, cdat_dsmas_handler, + dsmas_xa, port->cdat.table); + rc = cdat_table_parse_output(rc); + if (rc) + return rc; + + rc = cdat_table_parse(ACPI_CDAT_TYPE_DSLBIS, cdat_dslbis_handler, + dsmas_xa, port->cdat.table); + return cdat_table_parse_output(rc); +} + +static int cxl_port_perf_data_calculate(struct cxl_port *port, + struct xarray *dsmas_xa) +{ + struct access_coordinate c; + struct dsmas_entry *dent; + int valid_entries = 0; + unsigned long index; + int rc; + + rc = cxl_endpoint_get_perf_coordinates(port, &c); + if (rc) { + dev_dbg(&port->dev, "Failed to retrieve perf coordinates.\n"); + return rc; + } + + struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port); + + if (!cxl_root) + return -ENODEV; + + if (!cxl_root->ops || !cxl_root->ops->qos_class) + return -EOPNOTSUPP; + + xa_for_each(dsmas_xa, index, dent) { + int qos_class; + + dent->coord.read_latency = dent->coord.read_latency + + c.read_latency; + dent->coord.write_latency = dent->coord.write_latency + + c.write_latency; + dent->coord.read_bandwidth = min_t(int, c.read_bandwidth, + dent->coord.read_bandwidth); + dent->coord.write_bandwidth = min_t(int, c.write_bandwidth, + dent->coord.write_bandwidth); + + dent->entries = 1; + rc = cxl_root->ops->qos_class(cxl_root, &dent->coord, 1, + &qos_class); + if (rc != 1) + continue; + + valid_entries++; + dent->qos_class = qos_class; + } + + if (!valid_entries) + return -ENOENT; + + return 0; +} + +static void add_perf_entry(struct device *dev, struct dsmas_entry *dent, + struct list_head *list) +{ + struct cxl_dpa_perf *dpa_perf; + + dpa_perf = kzalloc(sizeof(*dpa_perf), GFP_KERNEL); + if (!dpa_perf) + return; + + dpa_perf->dpa_range = dent->dpa_range; + dpa_perf->coord = dent->coord; + dpa_perf->qos_class = dent->qos_class; + list_add_tail(&dpa_perf->list, list); + dev_dbg(dev, + "DSMAS: dpa: %#llx qos: %d read_bw: %d write_bw %d read_lat: %d write_lat: %d\n", + dent->dpa_range.start, dpa_perf->qos_class, + dent->coord.read_bandwidth, dent->coord.write_bandwidth, + dent->coord.read_latency, dent->coord.write_latency); +} + +static void free_perf_ents(void *data) +{ + struct cxl_memdev_state *mds = data; + struct cxl_dpa_perf *dpa_perf, *n; + LIST_HEAD(discard); + + list_splice_tail_init(&mds->ram_perf_list, &discard); + list_splice_tail_init(&mds->pmem_perf_list, &discard); + list_for_each_entry_safe(dpa_perf, n, &discard, list) { + list_del(&dpa_perf->list); + kfree(dpa_perf); + } +} + +static void cxl_memdev_set_qos_class(struct cxl_dev_state *cxlds, + struct xarray *dsmas_xa) +{ + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds); + struct device *dev = cxlds->dev; + struct range pmem_range = { + .start = cxlds->pmem_res.start, + .end = cxlds->pmem_res.end, + }; + struct range ram_range = { + .start = cxlds->ram_res.start, + .end = cxlds->ram_res.end, + }; + struct dsmas_entry *dent; + unsigned long index; + + xa_for_each(dsmas_xa, index, dent) { + if (resource_size(&cxlds->ram_res) && + range_contains(&ram_range, &dent->dpa_range)) + add_perf_entry(dev, dent, &mds->ram_perf_list); + else if (resource_size(&cxlds->pmem_res) && + range_contains(&pmem_range, &dent->dpa_range)) + add_perf_entry(dev, dent, &mds->pmem_perf_list); + else + dev_dbg(dev, "no partition for dsmas dpa: %#llx\n", + dent->dpa_range.start); + } + + devm_add_action_or_reset(&cxlds->cxlmd->dev, free_perf_ents, mds); +} + +static int match_cxlrd_qos_class(struct device *dev, void *data) +{ + int dev_qos_class = *(int *)data; + struct cxl_root_decoder *cxlrd; + + if (!is_root_decoder(dev)) + return 0; + + cxlrd = to_cxl_root_decoder(dev); + if (cxlrd->qos_class == CXL_QOS_CLASS_INVALID) + return 0; + + if (cxlrd->qos_class == dev_qos_class) + return 1; + + return 0; +} + +static void cxl_qos_match(struct cxl_port *root_port, + struct list_head *work_list, + struct list_head *discard_list) +{ + struct cxl_dpa_perf *dpa_perf, *n; + + list_for_each_entry_safe(dpa_perf, n, work_list, list) { + int rc; + + if (dpa_perf->qos_class == CXL_QOS_CLASS_INVALID) + return; + + rc = device_for_each_child(&root_port->dev, + (void *)&dpa_perf->qos_class, + match_cxlrd_qos_class); + if (!rc) + list_move_tail(&dpa_perf->list, discard_list); + } +} + +static int match_cxlrd_hb(struct device *dev, void *data) +{ + struct device *host_bridge = data; + struct cxl_switch_decoder *cxlsd; + struct cxl_root_decoder *cxlrd; + + if (!is_root_decoder(dev)) + return 0; + + cxlrd = to_cxl_root_decoder(dev); + cxlsd = &cxlrd->cxlsd; + + guard(rwsem_read)(&cxl_region_rwsem); + for (int i = 0; i < cxlsd->nr_targets; i++) { + if (host_bridge == cxlsd->target[i]->dport_dev) + return 1; + } + + return 0; +} + +static void discard_dpa_perf(struct list_head *list) +{ + struct cxl_dpa_perf *dpa_perf, *n; + + list_for_each_entry_safe(dpa_perf, n, list, list) { + list_del(&dpa_perf->list); + kfree(dpa_perf); + } +} +DEFINE_FREE(dpa_perf, struct list_head *, if (!list_empty(_T)) discard_dpa_perf(_T)) + +static int cxl_qos_class_verify(struct cxl_memdev *cxlmd) +{ + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds); + LIST_HEAD(__discard); + struct list_head *discard __free(dpa_perf) = &__discard; + struct cxl_port *root_port; + int rc; + + struct cxl_root *cxl_root __free(put_cxl_root) = + find_cxl_root(cxlmd->endpoint); + + if (!cxl_root) + return -ENODEV; + + root_port = &cxl_root->port; + + /* Check that the QTG IDs are all sane between end device and root decoders */ + cxl_qos_match(root_port, &mds->ram_perf_list, discard); + cxl_qos_match(root_port, &mds->pmem_perf_list, discard); + + /* Check to make sure that the device's host bridge is under a root decoder */ + rc = device_for_each_child(&root_port->dev, + (void *)cxlmd->endpoint->host_bridge, + match_cxlrd_hb); + if (!rc) { + list_splice_tail_init(&mds->ram_perf_list, discard); + list_splice_tail_init(&mds->pmem_perf_list, discard); + } + + return rc; +} + +static void discard_dsmas(struct xarray *xa) +{ + unsigned long index; + void *ent; + + xa_for_each(xa, index, ent) { + xa_erase(xa, index); + kfree(ent); + } + xa_destroy(xa); +} +DEFINE_FREE(dsmas, struct xarray *, if (_T) discard_dsmas(_T)) + +void cxl_endpoint_parse_cdat(struct cxl_port *port) +{ + struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev); + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct xarray __dsmas_xa; + struct xarray *dsmas_xa __free(dsmas) = &__dsmas_xa; + int rc; + + xa_init(&__dsmas_xa); + if (!port->cdat.table) + return; + + rc = cxl_cdat_endpoint_process(port, dsmas_xa); + if (rc < 0) { + dev_dbg(&port->dev, "Failed to parse CDAT: %d\n", rc); + return; + } + + rc = cxl_port_perf_data_calculate(port, dsmas_xa); + if (rc) { + dev_dbg(&port->dev, "Failed to do perf coord calculations.\n"); + return; + } + + cxl_memdev_set_qos_class(cxlds, dsmas_xa); + cxl_qos_class_verify(cxlmd); +} +EXPORT_SYMBOL_NS_GPL(cxl_endpoint_parse_cdat, CXL); + +static int cdat_sslbis_handler(union acpi_subtable_headers *header, void *arg, + const unsigned long end) +{ + struct acpi_cdat_sslbis *sslbis; + int size = sizeof(header->cdat) + sizeof(*sslbis); + struct cxl_port *port = arg; + struct device *dev = &port->dev; + struct acpi_cdat_sslbe *entry; + int remain, entries, i; + u16 len; + + len = le16_to_cpu((__force __le16)header->cdat.length); + remain = len - size; + if (!remain || remain % sizeof(*entry) || + (unsigned long)header + len > end) { + dev_warn(dev, "Malformed SSLBIS table length: (%u)\n", len); + return -EINVAL; + } + + /* Skip common header */ + sslbis = (struct acpi_cdat_sslbis *)((unsigned long)header + + sizeof(header->cdat)); + + /* Unrecognized data type, we can skip */ + if (sslbis->data_type > ACPI_HMAT_WRITE_BANDWIDTH) + return 0; + + entries = remain / sizeof(*entry); + entry = (struct acpi_cdat_sslbe *)((unsigned long)header + sizeof(*sslbis)); + + for (i = 0; i < entries; i++) { + u16 x = le16_to_cpu((__force __le16)entry->portx_id); + u16 y = le16_to_cpu((__force __le16)entry->porty_id); + __le64 le_base; + __le16 le_val; + struct cxl_dport *dport; + unsigned long index; + u16 dsp_id; + u64 val; + + switch (x) { + case ACPI_CDAT_SSLBIS_US_PORT: + dsp_id = y; + break; + case ACPI_CDAT_SSLBIS_ANY_PORT: + switch (y) { + case ACPI_CDAT_SSLBIS_US_PORT: + dsp_id = x; + break; + case ACPI_CDAT_SSLBIS_ANY_PORT: + dsp_id = ACPI_CDAT_SSLBIS_ANY_PORT; + break; + default: + dsp_id = y; + break; + } + break; + default: + dsp_id = x; + break; + } + + le_base = (__force __le64)sslbis->entry_base_unit; + le_val = (__force __le16)entry->latency_or_bandwidth; + + if (check_mul_overflow(le64_to_cpu(le_base), + le16_to_cpu(le_val), &val)) + dev_warn(dev, "SSLBIS value overflowed!\n"); + + xa_for_each(&port->dports, index, dport) { + if (dsp_id == ACPI_CDAT_SSLBIS_ANY_PORT || + dsp_id == dport->port_id) + cxl_access_coordinate_set(&dport->sw_coord, + sslbis->data_type, + val); + } + + entry++; + } + + return 0; +} + +void cxl_switch_parse_cdat(struct cxl_port *port) +{ + int rc; + + if (!port->cdat.table) + return; + + rc = cdat_table_parse(ACPI_CDAT_TYPE_SSLBIS, cdat_sslbis_handler, + port, port->cdat.table); + rc = cdat_table_parse_output(rc); + if (rc) + dev_dbg(&port->dev, "Failed to parse SSLBIS: %d\n", rc); +} +EXPORT_SYMBOL_NS_GPL(cxl_switch_parse_cdat, CXL); + +MODULE_IMPORT_NS(CXL); diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 86d7ba23235e..3b64fb1b9ed0 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -88,4 +88,6 @@ enum cxl_poison_trace_type { CXL_POISON_TRACE_CLEAR, }; +long cxl_pci_get_latency(struct pci_dev *pdev); + #endif /* __CXL_CORE_H__ */ diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index 36270dcfb42e..27166a411705 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -63,6 +63,7 @@ static struct cxl_mem_command cxl_mem_commands[CXL_MEM_COMMAND_ID_MAX] = { CXL_CMD(GET_SHUTDOWN_STATE, 0, 0x1, 0), CXL_CMD(SET_SHUTDOWN_STATE, 0x1, 0, 0), CXL_CMD(GET_SCAN_MEDIA_CAPS, 0x10, 0x4, 0), + CXL_CMD(GET_TIMESTAMP, 0, 0x8, 0), }; /* @@ -836,54 +837,37 @@ out: } EXPORT_SYMBOL_NS_GPL(cxl_enumerate_cmds, CXL); -/* - * General Media Event Record - * CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43 - */ -static const uuid_t gen_media_event_uuid = - UUID_INIT(0xfbcd0a77, 0xc260, 0x417f, - 0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6); - -/* - * DRAM Event Record - * CXL rev 3.0 section 8.2.9.2.1.2; Table 8-44 - */ -static const uuid_t dram_event_uuid = - UUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, - 0xb8, 0xaf, 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24); - -/* - * Memory Module Event Record - * CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45 - */ -static const uuid_t mem_mod_event_uuid = - UUID_INIT(0xfe927475, 0xdd59, 0x4339, - 0xa5, 0x86, 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74); - -static void cxl_event_trace_record(const struct cxl_memdev *cxlmd, - enum cxl_event_log_type type, - struct cxl_event_record_raw *record) +void cxl_event_trace_record(const struct cxl_memdev *cxlmd, + enum cxl_event_log_type type, + enum cxl_event_type event_type, + const uuid_t *uuid, union cxl_event *evt) { - uuid_t *id = &record->hdr.id; - - if (uuid_equal(id, &gen_media_event_uuid)) { - struct cxl_event_gen_media *rec = - (struct cxl_event_gen_media *)record; + if (event_type == CXL_CPER_EVENT_GEN_MEDIA) + trace_cxl_general_media(cxlmd, type, &evt->gen_media); + else if (event_type == CXL_CPER_EVENT_DRAM) + trace_cxl_dram(cxlmd, type, &evt->dram); + else if (event_type == CXL_CPER_EVENT_MEM_MODULE) + trace_cxl_memory_module(cxlmd, type, &evt->mem_module); + else + trace_cxl_generic_event(cxlmd, type, uuid, &evt->generic); +} +EXPORT_SYMBOL_NS_GPL(cxl_event_trace_record, CXL); - trace_cxl_general_media(cxlmd, type, rec); - } else if (uuid_equal(id, &dram_event_uuid)) { - struct cxl_event_dram *rec = (struct cxl_event_dram *)record; +static void __cxl_event_trace_record(const struct cxl_memdev *cxlmd, + enum cxl_event_log_type type, + struct cxl_event_record_raw *record) +{ + enum cxl_event_type ev_type = CXL_CPER_EVENT_GENERIC; + const uuid_t *uuid = &record->id; - trace_cxl_dram(cxlmd, type, rec); - } else if (uuid_equal(id, &mem_mod_event_uuid)) { - struct cxl_event_mem_module *rec = - (struct cxl_event_mem_module *)record; + if (uuid_equal(uuid, &CXL_EVENT_GEN_MEDIA_UUID)) + ev_type = CXL_CPER_EVENT_GEN_MEDIA; + else if (uuid_equal(uuid, &CXL_EVENT_DRAM_UUID)) + ev_type = CXL_CPER_EVENT_DRAM; + else if (uuid_equal(uuid, &CXL_EVENT_MEM_MODULE_UUID)) + ev_type = CXL_CPER_EVENT_MEM_MODULE; - trace_cxl_memory_module(cxlmd, type, rec); - } else { - /* For unknown record types print just the header */ - trace_cxl_generic_event(cxlmd, type, record); - } + cxl_event_trace_record(cxlmd, type, ev_type, uuid, &record->event); } static int cxl_clear_event_record(struct cxl_memdev_state *mds, @@ -926,7 +910,10 @@ static int cxl_clear_event_record(struct cxl_memdev_state *mds, */ i = 0; for (cnt = 0; cnt < total; cnt++) { - payload->handles[i++] = get_pl->records[cnt].hdr.handle; + struct cxl_event_record_raw *raw = &get_pl->records[cnt]; + struct cxl_event_generic *gen = &raw->event.generic; + + payload->handles[i++] = gen->hdr.handle; dev_dbg(mds->cxlds.dev, "Event log '%d': Clearing %u\n", log, le16_to_cpu(payload->handles[i])); @@ -991,8 +978,8 @@ static void cxl_mem_get_records_log(struct cxl_memdev_state *mds, break; for (i = 0; i < nr_rec; i++) - cxl_event_trace_record(cxlmd, type, - &payload->records[i]); + __cxl_event_trace_record(cxlmd, type, + &payload->records[i]); if (payload->flags & CXL_GET_EVENT_FLAG_OVERFLOW) trace_cxl_overflow(cxlmd, type, payload); @@ -1404,6 +1391,8 @@ struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev) mds->cxlds.reg_map.host = dev; mds->cxlds.reg_map.resource = CXL_RESOURCE_NONE; mds->cxlds.type = CXL_DEVTYPE_CLASSMEM; + INIT_LIST_HEAD(&mds->ram_perf_list); + INIT_LIST_HEAD(&mds->pmem_perf_list); return mds; } diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index 2f43d368ba07..dae8802ecdb0 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -114,7 +114,7 @@ static DEVICE_ATTR_RO(serial); static ssize_t numa_node_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", dev_to_node(dev)); + return sysfs_emit(buf, "%d\n", dev_to_node(dev)); } static DEVICE_ATTR_RO(numa_node); diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index 37e1652afbc7..6c9c8d92f8f7 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2021 Intel Corporation. All rights reserved. */ +#include <linux/units.h> #include <linux/io-64-nonatomic-lo-hi.h> #include <linux/device.h> #include <linux/delay.h> @@ -979,3 +980,38 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev, return PCI_ERS_RESULT_NEED_RESET; } EXPORT_SYMBOL_NS_GPL(cxl_error_detected, CXL); + +static int cxl_flit_size(struct pci_dev *pdev) +{ + if (cxl_pci_flit_256(pdev)) + return 256; + + return 68; +} + +/** + * cxl_pci_get_latency - calculate the link latency for the PCIe link + * @pdev: PCI device + * + * return: calculated latency or 0 for no latency + * + * CXL Memory Device SW Guide v1.0 2.11.4 Link latency calculation + * Link latency = LinkPropagationLatency + FlitLatency + RetimerLatency + * LinkProgationLatency is negligible, so 0 will be used + * RetimerLatency is assumed to be negligible and 0 will be used + * FlitLatency = FlitSize / LinkBandwidth + * FlitSize is defined by spec. CXL rev3.0 4.2.1. + * 68B flit is used up to 32GT/s. >32GT/s, 256B flit size is used. + * The FlitLatency is converted to picoseconds. + */ +long cxl_pci_get_latency(struct pci_dev *pdev) +{ + long bw; + + bw = pcie_link_speed_mbps(pdev); + if (bw < 0) + return 0; + bw /= BITS_PER_BYTE; + + return cxl_flit_size(pdev) * MEGA / bw; +} diff --git a/drivers/cxl/core/pmem.c b/drivers/cxl/core/pmem.c index fc94f5240327..e69625a8d6a1 100644 --- a/drivers/cxl/core/pmem.c +++ b/drivers/cxl/core/pmem.c @@ -64,14 +64,14 @@ static int match_nvdimm_bridge(struct device *dev, void *data) struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_memdev *cxlmd) { - struct cxl_port *port = find_cxl_root(cxlmd->endpoint); + struct cxl_root *cxl_root __free(put_cxl_root) = + find_cxl_root(cxlmd->endpoint); struct device *dev; - if (!port) + if (!cxl_root) return NULL; - dev = device_find_child(&port->dev, NULL, match_nvdimm_bridge); - put_device(&port->dev); + dev = device_find_child(&cxl_root->port.dev, NULL, match_nvdimm_bridge); if (!dev) return NULL; diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index b7c93bb18f6e..e59d9d37aa65 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -9,6 +9,7 @@ #include <linux/pci.h> #include <linux/slab.h> #include <linux/idr.h> +#include <linux/node.h> #include <cxlmem.h> #include <cxlpci.h> #include <cxl.h> @@ -172,14 +173,10 @@ static ssize_t target_list_show(struct device *dev, { struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(dev); ssize_t offset; - unsigned int seq; int rc; - do { - seq = read_seqbegin(&cxlsd->target_lock); - rc = emit_target_list(cxlsd, buf); - } while (read_seqretry(&cxlsd->target_lock, seq)); - + guard(rwsem_read)(&cxl_region_rwsem); + rc = emit_target_list(cxlsd, buf); if (rc < 0) return rc; offset = rc; @@ -541,7 +538,10 @@ static void cxl_port_release(struct device *dev) xa_destroy(&port->dports); xa_destroy(&port->regions); ida_free(&cxl_port_ida, port->id); - kfree(port); + if (is_cxl_root(port)) + kfree(to_cxl_root(port)); + else + kfree(port); } static ssize_t decoders_committed_show(struct device *dev, @@ -669,17 +669,31 @@ static struct lock_class_key cxl_port_key; static struct cxl_port *cxl_port_alloc(struct device *uport_dev, struct cxl_dport *parent_dport) { - struct cxl_port *port; + struct cxl_root *cxl_root __free(kfree) = NULL; + struct cxl_port *port, *_port __free(kfree) = NULL; struct device *dev; int rc; - port = kzalloc(sizeof(*port), GFP_KERNEL); - if (!port) - return ERR_PTR(-ENOMEM); + /* No parent_dport, root cxl_port */ + if (!parent_dport) { + cxl_root = kzalloc(sizeof(*cxl_root), GFP_KERNEL); + if (!cxl_root) + return ERR_PTR(-ENOMEM); + } else { + _port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!_port) + return ERR_PTR(-ENOMEM); + } rc = ida_alloc(&cxl_port_ida, GFP_KERNEL); if (rc < 0) - goto err; + return ERR_PTR(rc); + + if (cxl_root) + port = &no_free_ptr(cxl_root)->port; + else + port = no_free_ptr(_port); + port->id = rc; port->uport_dev = uport_dev; @@ -731,10 +745,6 @@ static struct cxl_port *cxl_port_alloc(struct device *uport_dev, dev->type = &cxl_port_type; return port; - -err: - kfree(port); - return ERR_PTR(rc); } static int cxl_setup_comp_regs(struct device *host, struct cxl_register_map *map, @@ -841,6 +851,9 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host, if (rc) return ERR_PTR(rc); + if (parent_dport && dev_is_pci(uport_dev)) + port->pci_latency = cxl_pci_get_latency(to_pci_dev(uport_dev)); + return port; err: @@ -884,6 +897,22 @@ struct cxl_port *devm_cxl_add_port(struct device *host, } EXPORT_SYMBOL_NS_GPL(devm_cxl_add_port, CXL); +struct cxl_root *devm_cxl_add_root(struct device *host, + const struct cxl_root_ops *ops) +{ + struct cxl_root *cxl_root; + struct cxl_port *port; + + port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL); + if (IS_ERR(port)) + return (struct cxl_root *)port; + + cxl_root = to_cxl_root(port); + cxl_root->ops = ops; + return cxl_root; +} +EXPORT_SYMBOL_NS_GPL(devm_cxl_add_root, CXL); + struct pci_bus *cxl_port_to_pci_bus(struct cxl_port *port) { /* There is no pci_bus associated with a CXL platform-root port */ @@ -939,7 +968,7 @@ static bool dev_is_cxl_root_child(struct device *dev) return false; } -struct cxl_port *find_cxl_root(struct cxl_port *port) +struct cxl_root *find_cxl_root(struct cxl_port *port) { struct cxl_port *iter = port; @@ -949,10 +978,19 @@ struct cxl_port *find_cxl_root(struct cxl_port *port) if (!iter) return NULL; get_device(&iter->dev); - return iter; + return to_cxl_root(iter); } EXPORT_SYMBOL_NS_GPL(find_cxl_root, CXL); +void put_cxl_root(struct cxl_root *cxl_root) +{ + if (!cxl_root) + return; + + put_device(&cxl_root->port.dev); +} +EXPORT_SYMBOL_NS_GPL(put_cxl_root, CXL); + static struct cxl_dport *find_dport(struct cxl_port *port, int id) { struct cxl_dport *dport; @@ -1108,6 +1146,9 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev, if (rc) return ERR_PTR(rc); + if (dev_is_pci(dport_dev)) + dport->link_latency = cxl_pci_get_latency(to_pci_dev(dport_dev)); + return dport; } @@ -1633,7 +1674,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_mem_find_port, CXL); static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd, struct cxl_port *port, int *target_map) { - int i, rc = 0; + int i; if (!target_map) return 0; @@ -1643,19 +1684,16 @@ static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd, if (xa_empty(&port->dports)) return -EINVAL; - write_seqlock(&cxlsd->target_lock); - for (i = 0; i < cxlsd->nr_targets; i++) { + guard(rwsem_write)(&cxl_region_rwsem); + for (i = 0; i < cxlsd->cxld.interleave_ways; i++) { struct cxl_dport *dport = find_dport(port, target_map[i]); - if (!dport) { - rc = -ENXIO; - break; - } + if (!dport) + return -ENXIO; cxlsd->target[i] = dport; } - write_sequnlock(&cxlsd->target_lock); - return rc; + return 0; } struct cxl_dport *cxl_hb_modulo(struct cxl_root_decoder *cxlrd, int pos) @@ -1725,7 +1763,6 @@ static int cxl_switch_decoder_init(struct cxl_port *port, return -EINVAL; cxlsd->nr_targets = nr_targets; - seqlock_init(&cxlsd->target_lock); return cxl_decoder_init(port, &cxlsd->cxld); } @@ -2059,6 +2096,80 @@ bool schedule_cxl_memdev_detach(struct cxl_memdev *cxlmd) } EXPORT_SYMBOL_NS_GPL(schedule_cxl_memdev_detach, CXL); +static void combine_coordinates(struct access_coordinate *c1, + struct access_coordinate *c2) +{ + if (c2->write_bandwidth) + c1->write_bandwidth = min(c1->write_bandwidth, + c2->write_bandwidth); + c1->write_latency += c2->write_latency; + + if (c2->read_bandwidth) + c1->read_bandwidth = min(c1->read_bandwidth, + c2->read_bandwidth); + c1->read_latency += c2->read_latency; +} + +/** + * cxl_endpoint_get_perf_coordinates - Retrieve performance numbers stored in dports + * of CXL path + * @port: endpoint cxl_port + * @coord: output performance data + * + * Return: errno on failure, 0 on success. + */ +int cxl_endpoint_get_perf_coordinates(struct cxl_port *port, + struct access_coordinate *coord) +{ + struct access_coordinate c = { + .read_bandwidth = UINT_MAX, + .write_bandwidth = UINT_MAX, + }; + struct cxl_port *iter = port; + struct cxl_dport *dport; + struct pci_dev *pdev; + unsigned int bw; + + if (!is_cxl_endpoint(port)) + return -EINVAL; + + dport = iter->parent_dport; + + /* + * Exit the loop when the parent port of the current port is cxl root. + * The iterative loop starts at the endpoint and gathers the + * latency of the CXL link from the current iter to the next downstream + * port each iteration. If the parent is cxl root then there is + * nothing to gather. + */ + while (iter && !is_cxl_root(to_cxl_port(iter->dev.parent))) { + combine_coordinates(&c, &dport->sw_coord); + c.write_latency += dport->link_latency; + c.read_latency += dport->link_latency; + + iter = to_cxl_port(iter->dev.parent); + dport = iter->parent_dport; + } + + /* Augment with the generic port (host bridge) perf data */ + combine_coordinates(&c, &dport->hb_coord); + + /* Get the calculated PCI paths bandwidth */ + pdev = to_pci_dev(port->uport_dev->parent); + bw = pcie_bandwidth_available(pdev, NULL, NULL, NULL); + if (bw == 0) + return -ENXIO; + bw /= BITS_PER_BYTE; + + c.write_bandwidth = min(c.write_bandwidth, bw); + c.read_bandwidth = min(c.read_bandwidth, bw); + + *coord = c; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cxl_endpoint_get_perf_coordinates, CXL); + /* for user tooling to ensure port disable work has completed */ static ssize_t flush_store(const struct bus_type *bus, const char *buf, size_t count) { diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 3e817a6f94c6..ce0e2d82bb2b 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -397,7 +397,7 @@ static ssize_t interleave_ways_store(struct device *dev, return rc; /* - * Even for x3, x9, and x12 interleaves the region interleave must be a + * Even for x3, x6, and x12 interleaves the region interleave must be a * power of 2 multiple of the host bridge interleave. */ if (!is_power_of_2(val / cxld->interleave_ways) || @@ -525,7 +525,7 @@ static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size) struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); struct cxl_region_params *p = &cxlr->params; struct resource *res; - u32 remainder = 0; + u64 remainder = 0; lockdep_assert_held_write(&cxl_region_rwsem); @@ -545,15 +545,16 @@ static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size) (cxlr->mode == CXL_DECODER_PMEM && uuid_is_null(&p->uuid))) return -ENXIO; - div_u64_rem(size, SZ_256M * p->interleave_ways, &remainder); + div64_u64_rem(size, (u64)SZ_256M * p->interleave_ways, &remainder); if (remainder) return -EINVAL; res = alloc_free_mem_region(cxlrd->res, size, SZ_256M, dev_name(&cxlr->dev)); if (IS_ERR(res)) { - dev_dbg(&cxlr->dev, "failed to allocate HPA: %ld\n", - PTR_ERR(res)); + dev_dbg(&cxlr->dev, + "HPA allocation error (%ld) for size:%pap in %s %pr\n", + PTR_ERR(res), &size, cxlrd->res->name, cxlrd->res); return PTR_ERR(res); } @@ -2083,13 +2084,13 @@ static struct cxl_region *to_cxl_region(struct device *dev) return container_of(dev, struct cxl_region, dev); } -static void unregister_region(void *dev) +static void unregister_region(void *_cxlr) { - struct cxl_region *cxlr = to_cxl_region(dev); + struct cxl_region *cxlr = _cxlr; struct cxl_region_params *p = &cxlr->params; int i; - device_del(dev); + device_del(&cxlr->dev); /* * Now that region sysfs is shutdown, the parameter block is now @@ -2100,7 +2101,7 @@ static void unregister_region(void *dev) detach_target(cxlr, i); cxl_region_iomem_release(cxlr); - put_device(dev); + put_device(&cxlr->dev); } static struct lock_class_key cxl_region_key; diff --git a/drivers/cxl/core/trace.h b/drivers/cxl/core/trace.h index a0b5819bc70b..bdf117a33744 100644 --- a/drivers/cxl/core/trace.h +++ b/drivers/cxl/core/trace.h @@ -181,6 +181,7 @@ TRACE_EVENT(cxl_overflow, * 1) Add CXL_EVT_TP_entry to TP_STRUCT__entry * 2) Use CXL_EVT_TP_fast_assign within TP_fast_assign; * pass the dev, log, and CXL event header + * NOTE: The uuid must be assigned by the specific trace event * 3) Use CXL_EVT_TP_printk() instead of TP_printk() * * See the generic_event tracepoint as an example. @@ -203,7 +204,6 @@ TRACE_EVENT(cxl_overflow, __assign_str(host, dev_name((cxlmd)->dev.parent)); \ __entry->log = (l); \ __entry->serial = (cxlmd)->cxlds->serial; \ - memcpy(&__entry->hdr_uuid, &(hdr).id, sizeof(uuid_t)); \ __entry->hdr_length = (hdr).length; \ __entry->hdr_flags = get_unaligned_le24((hdr).flags); \ __entry->hdr_handle = le16_to_cpu((hdr).handle); \ @@ -225,9 +225,9 @@ TRACE_EVENT(cxl_overflow, TRACE_EVENT(cxl_generic_event, TP_PROTO(const struct cxl_memdev *cxlmd, enum cxl_event_log_type log, - struct cxl_event_record_raw *rec), + const uuid_t *uuid, struct cxl_event_generic *gen_rec), - TP_ARGS(cxlmd, log, rec), + TP_ARGS(cxlmd, log, uuid, gen_rec), TP_STRUCT__entry( CXL_EVT_TP_entry @@ -235,8 +235,9 @@ TRACE_EVENT(cxl_generic_event, ), TP_fast_assign( - CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr); - memcpy(__entry->data, &rec->data, CXL_EVENT_RECORD_DATA_LENGTH); + CXL_EVT_TP_fast_assign(cxlmd, log, gen_rec->hdr); + memcpy(&__entry->hdr_uuid, uuid, sizeof(uuid_t)); + memcpy(__entry->data, gen_rec->data, CXL_EVENT_RECORD_DATA_LENGTH); ), CXL_EVT_TP_printk("%s", @@ -337,6 +338,7 @@ TRACE_EVENT(cxl_general_media, TP_fast_assign( CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr); + __entry->hdr_uuid = CXL_EVENT_GEN_MEDIA_UUID; /* General Media */ __entry->dpa = le64_to_cpu(rec->phys_addr); @@ -423,6 +425,7 @@ TRACE_EVENT(cxl_dram, TP_fast_assign( CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr); + __entry->hdr_uuid = CXL_EVENT_DRAM_UUID; /* DRAM */ __entry->dpa = le64_to_cpu(rec->phys_addr); @@ -570,6 +573,7 @@ TRACE_EVENT(cxl_memory_module, TP_fast_assign( CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr); + __entry->hdr_uuid = CXL_EVENT_MEM_MODULE_UUID; /* Memory Module Event */ __entry->event_type = rec->event_type; diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 687043ece101..b6017c0c57b4 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -8,6 +8,7 @@ #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/log2.h> +#include <linux/node.h> #include <linux/io.h> /** @@ -412,7 +413,6 @@ struct cxl_endpoint_decoder { /** * struct cxl_switch_decoder - Switch specific CXL HDM Decoder * @cxld: base cxl_decoder object - * @target_lock: coordinate coherent reads of the target list * @nr_targets: number of elements in @target * @target: active ordered target list in current decoder configuration * @@ -424,7 +424,6 @@ struct cxl_endpoint_decoder { */ struct cxl_switch_decoder { struct cxl_decoder cxld; - seqlock_t target_lock; int nr_targets; struct cxl_dport *target[]; }; @@ -590,6 +589,7 @@ struct cxl_dax_region { * @depth: How deep this port is relative to the root. depth 0 is the root. * @cdat: Cached CDAT data * @cdat_available: Should a CDAT attribute be available in sysfs + * @pci_latency: Upstream latency in picoseconds */ struct cxl_port { struct device dev; @@ -612,6 +612,30 @@ struct cxl_port { size_t length; } cdat; bool cdat_available; + long pci_latency; +}; + +/** + * struct cxl_root - logical collection of root cxl_port items + * + * @port: cxl_port member + * @ops: cxl root operations + */ +struct cxl_root { + struct cxl_port port; + const struct cxl_root_ops *ops; +}; + +static inline struct cxl_root * +to_cxl_root(const struct cxl_port *port) +{ + return container_of(port, struct cxl_root, port); +} + +struct cxl_root_ops { + int (*qos_class)(struct cxl_root *cxl_root, + struct access_coordinate *coord, int entries, + int *qos_class); }; static inline struct cxl_dport * @@ -634,6 +658,9 @@ struct cxl_rcrb_info { * @rch: Indicate whether this dport was enumerated in RCH or VH mode * @port: reference to cxl_port that contains this downstream port * @regs: Dport parsed register blocks + * @sw_coord: access coordinates (performance) for switch from CDAT + * @hb_coord: access coordinates (performance) from ACPI generic port (host bridge) + * @link_latency: calculated PCIe downstream latency */ struct cxl_dport { struct device *dport_dev; @@ -643,6 +670,9 @@ struct cxl_dport { bool rch; struct cxl_port *port; struct cxl_regs regs; + struct access_coordinate sw_coord; + struct access_coordinate hb_coord; + long link_latency; }; /** @@ -700,7 +730,12 @@ struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport_dev, resource_size_t component_reg_phys, struct cxl_dport *parent_dport); -struct cxl_port *find_cxl_root(struct cxl_port *port); +struct cxl_root *devm_cxl_add_root(struct device *host, + const struct cxl_root_ops *ops); +struct cxl_root *find_cxl_root(struct cxl_port *port); +void put_cxl_root(struct cxl_root *cxl_root); +DEFINE_FREE(put_cxl_root, struct cxl_root *, if (_T) put_cxl_root(_T)) + int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd); void cxl_bus_rescan(void); void cxl_bus_drain(void); @@ -839,6 +874,12 @@ static inline struct cxl_dax_region *to_cxl_dax_region(struct device *dev) } #endif +void cxl_endpoint_parse_cdat(struct cxl_port *port); +void cxl_switch_parse_cdat(struct cxl_port *port); + +int cxl_endpoint_get_perf_coordinates(struct cxl_port *port, + struct access_coordinate *coord); + /* * Unit test builds overrides this to __weak, find the 'strong' version * of these symbols in tools/testing/cxl/. diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index a2fcbca253f3..5303d6942b88 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -6,6 +6,8 @@ #include <linux/cdev.h> #include <linux/uuid.h> #include <linux/rcuwait.h> +#include <linux/cxl-event.h> +#include <linux/node.h> #include "cxl.h" /* CXL 2.0 8.2.8.5.1.1 Memory Device Status Register */ @@ -392,6 +394,20 @@ enum cxl_devtype { }; /** + * struct cxl_dpa_perf - DPA performance property entry + * @list - list entry + * @dpa_range - range for DPA address + * @coord - QoS performance data (i.e. latency, bandwidth) + * @qos_class - QoS Class cookies + */ +struct cxl_dpa_perf { + struct list_head list; + struct range dpa_range; + struct access_coordinate coord; + int qos_class; +}; + +/** * struct cxl_dev_state - The driver device state * * cxl_dev_state represents the CXL driver/device state. It provides an @@ -455,6 +471,8 @@ struct cxl_dev_state { * @security: security driver state info * @fw: firmware upload / activation state * @mbox_send: @dev specific transport for transmitting mailbox commands + * @ram_perf_list: performance data entries matched to RAM + * @pmem_perf_list: performance data entries matched to PMEM * * See CXL 3.0 8.2.9.8.2 Capacity Configuration and Label Storage for * details on capacity parameters. @@ -475,6 +493,10 @@ struct cxl_memdev_state { u64 active_persistent_bytes; u64 next_volatile_bytes; u64 next_persistent_bytes; + + struct list_head ram_perf_list; + struct list_head pmem_perf_list; + struct cxl_event_state event; struct cxl_poison_state poison; struct cxl_security_state security; @@ -503,6 +525,7 @@ enum cxl_opcode { CXL_MBOX_OP_GET_FW_INFO = 0x0200, CXL_MBOX_OP_TRANSFER_FW = 0x0201, CXL_MBOX_OP_ACTIVATE_FW = 0x0202, + CXL_MBOX_OP_GET_TIMESTAMP = 0x0300, CXL_MBOX_OP_SET_TIMESTAMP = 0x0301, CXL_MBOX_OP_GET_SUPPORTED_LOGS = 0x0400, CXL_MBOX_OP_GET_LOG = 0x0401, @@ -580,25 +603,28 @@ struct cxl_mbox_identify { } __packed; /* - * Common Event Record Format - * CXL rev 3.0 section 8.2.9.2.1; Table 8-42 + * General Media Event Record UUID + * CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43 */ -struct cxl_event_record_hdr { - uuid_t id; - u8 length; - u8 flags[3]; - __le16 handle; - __le16 related_handle; - __le64 timestamp; - u8 maint_op_class; - u8 reserved[15]; -} __packed; +#define CXL_EVENT_GEN_MEDIA_UUID \ + UUID_INIT(0xfbcd0a77, 0xc260, 0x417f, 0x85, 0xa9, 0x08, 0x8b, 0x16, \ + 0x21, 0xeb, 0xa6) -#define CXL_EVENT_RECORD_DATA_LENGTH 0x50 -struct cxl_event_record_raw { - struct cxl_event_record_hdr hdr; - u8 data[CXL_EVENT_RECORD_DATA_LENGTH]; -} __packed; +/* + * DRAM Event Record UUID + * CXL rev 3.0 section 8.2.9.2.1.2; Table 8-44 + */ +#define CXL_EVENT_DRAM_UUID \ + UUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, 0xb8, 0xaf, 0x4e, 0x9b, 0xfb, \ + 0x5c, 0x96, 0x24) + +/* + * Memory Module Event Record UUID + * CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45 + */ +#define CXL_EVENT_MEM_MODULE_UUID \ + UUID_INIT(0xfe927475, 0xdd59, 0x4339, 0xa5, 0x86, 0x79, 0xba, 0xb1, \ + 0x13, 0xb7, 0x74) /* * Get Event Records output payload @@ -641,74 +667,6 @@ struct cxl_mbox_clear_event_payload { } __packed; #define CXL_CLEAR_EVENT_MAX_HANDLES U8_MAX -/* - * General Media Event Record - * CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43 - */ -#define CXL_EVENT_GEN_MED_COMP_ID_SIZE 0x10 -struct cxl_event_gen_media { - struct cxl_event_record_hdr hdr; - __le64 phys_addr; - u8 descriptor; - u8 type; - u8 transaction_type; - u8 validity_flags[2]; - u8 channel; - u8 rank; - u8 device[3]; - u8 component_id[CXL_EVENT_GEN_MED_COMP_ID_SIZE]; - u8 reserved[46]; -} __packed; - -/* - * DRAM Event Record - DER - * CXL rev 3.0 section 8.2.9.2.1.2; Table 3-44 - */ -#define CXL_EVENT_DER_CORRECTION_MASK_SIZE 0x20 -struct cxl_event_dram { - struct cxl_event_record_hdr hdr; - __le64 phys_addr; - u8 descriptor; - u8 type; - u8 transaction_type; - u8 validity_flags[2]; - u8 channel; - u8 rank; - u8 nibble_mask[3]; - u8 bank_group; - u8 bank; - u8 row[3]; - u8 column[2]; - u8 correction_mask[CXL_EVENT_DER_CORRECTION_MASK_SIZE]; - u8 reserved[0x17]; -} __packed; - -/* - * Get Health Info Record - * CXL rev 3.0 section 8.2.9.8.3.1; Table 8-100 - */ -struct cxl_get_health_info { - u8 health_status; - u8 media_status; - u8 add_status; - u8 life_used; - u8 device_temp[2]; - u8 dirty_shutdown_cnt[4]; - u8 cor_vol_err_cnt[4]; - u8 cor_per_err_cnt[4]; -} __packed; - -/* - * Memory Module Event Record - * CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45 - */ -struct cxl_event_mem_module { - struct cxl_event_record_hdr hdr; - u8 event_type; - struct cxl_get_health_info info; - u8 reserved[0x3d]; -} __packed; - struct cxl_mbox_get_partition_info { __le64 active_volatile_cap; __le64 active_persistent_cap; @@ -866,6 +824,10 @@ void set_exclusive_cxl_commands(struct cxl_memdev_state *mds, void clear_exclusive_cxl_commands(struct cxl_memdev_state *mds, unsigned long *cmds); void cxl_mem_get_event_records(struct cxl_memdev_state *mds, u32 status); +void cxl_event_trace_record(const struct cxl_memdev *cxlmd, + enum cxl_event_log_type type, + enum cxl_event_type event_type, + const uuid_t *uuid, union cxl_event *evt); int cxl_set_timestamp(struct cxl_memdev_state *mds); int cxl_poison_state_init(struct cxl_memdev_state *mds); int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len, diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h index 0fa4799ea316..711b05d9a370 100644 --- a/drivers/cxl/cxlpci.h +++ b/drivers/cxl/cxlpci.h @@ -85,6 +85,19 @@ struct cdat_entry_header { __le16 length; } __packed; +/* + * CXL v3.0 6.2.3 Table 6-4 + * The table indicates that if PCIe Flit Mode is set, then CXL is in 256B flits + * mode, otherwise it's 68B flits mode. + */ +static inline bool cxl_pci_flit_256(struct pci_dev *pdev) +{ + u16 lnksta2; + + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA2, &lnksta2); + return lnksta2 & PCI_EXP_LNKSTA2_FLIT; +} + int devm_cxl_port_enumerate_dports(struct cxl_port *port); struct cxl_dev_state; int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm, diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c index e087febf9af0..c5c9d8e0d88d 100644 --- a/drivers/cxl/mem.c +++ b/drivers/cxl/mem.c @@ -215,23 +215,78 @@ static ssize_t trigger_poison_list_store(struct device *dev, } static DEVICE_ATTR_WO(trigger_poison_list); +static ssize_t ram_qos_class_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cxl_memdev *cxlmd = to_cxl_memdev(dev); + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds); + struct cxl_dpa_perf *dpa_perf; + + if (!dev->driver) + return -ENOENT; + + if (list_empty(&mds->ram_perf_list)) + return -ENOENT; + + dpa_perf = list_first_entry(&mds->ram_perf_list, struct cxl_dpa_perf, + list); + + return sysfs_emit(buf, "%d\n", dpa_perf->qos_class); +} + +static struct device_attribute dev_attr_ram_qos_class = + __ATTR(qos_class, 0444, ram_qos_class_show, NULL); + +static ssize_t pmem_qos_class_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cxl_memdev *cxlmd = to_cxl_memdev(dev); + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds); + struct cxl_dpa_perf *dpa_perf; + + if (!dev->driver) + return -ENOENT; + + if (list_empty(&mds->pmem_perf_list)) + return -ENOENT; + + dpa_perf = list_first_entry(&mds->pmem_perf_list, struct cxl_dpa_perf, + list); + + return sysfs_emit(buf, "%d\n", dpa_perf->qos_class); +} + +static struct device_attribute dev_attr_pmem_qos_class = + __ATTR(qos_class, 0444, pmem_qos_class_show, NULL); + static umode_t cxl_mem_visible(struct kobject *kobj, struct attribute *a, int n) { - if (a == &dev_attr_trigger_poison_list.attr) { - struct device *dev = kobj_to_dev(kobj); - struct cxl_memdev *cxlmd = to_cxl_memdev(dev); - struct cxl_memdev_state *mds = - to_cxl_memdev_state(cxlmd->cxlds); + struct device *dev = kobj_to_dev(kobj); + struct cxl_memdev *cxlmd = to_cxl_memdev(dev); + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); + if (a == &dev_attr_trigger_poison_list.attr) if (!test_bit(CXL_POISON_ENABLED_LIST, mds->poison.enabled_cmds)) return 0; - } + + if (a == &dev_attr_pmem_qos_class.attr) + if (list_empty(&mds->pmem_perf_list)) + return 0; + + if (a == &dev_attr_ram_qos_class.attr) + if (list_empty(&mds->ram_perf_list)) + return 0; + return a->mode; } static struct attribute *cxl_mem_attrs[] = { &dev_attr_trigger_poison_list.attr, + &dev_attr_ram_qos_class.attr, + &dev_attr_pmem_qos_class.attr, NULL }; diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index 0155fb66b580..233e7c42c161 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2020 Intel Corporation. All rights reserved. */ +#include <asm-generic/unaligned.h> #include <linux/io-64-nonatomic-lo-hi.h> #include <linux/moduleparam.h> #include <linux/module.h> @@ -381,7 +382,7 @@ static int cxl_pci_mbox_send(struct cxl_memdev_state *mds, return rc; } -static int cxl_pci_setup_mailbox(struct cxl_memdev_state *mds) +static int cxl_pci_setup_mailbox(struct cxl_memdev_state *mds, bool irq_avail) { struct cxl_dev_state *cxlds = &mds->cxlds; const int cap = readl(cxlds->regs.mbox + CXLDEV_MBOX_CAPS_OFFSET); @@ -440,7 +441,7 @@ static int cxl_pci_setup_mailbox(struct cxl_memdev_state *mds) INIT_DELAYED_WORK(&mds->security.poll_dwork, cxl_mbox_sanitize_work); /* background command interrupts are optional */ - if (!(cap & CXLDEV_MBOX_CAP_BG_CMD_IRQ)) + if (!(cap & CXLDEV_MBOX_CAP_BG_CMD_IRQ) || !irq_avail) return 0; msgnum = FIELD_GET(CXLDEV_MBOX_CAP_IRQ_MSGNUM_MASK, cap); @@ -587,7 +588,7 @@ static int cxl_mem_alloc_event_buf(struct cxl_memdev_state *mds) return devm_add_action_or_reset(mds->cxlds.dev, free_event_buf, buf); } -static int cxl_alloc_irq_vectors(struct pci_dev *pdev) +static bool cxl_alloc_irq_vectors(struct pci_dev *pdev) { int nvecs; @@ -604,9 +605,9 @@ static int cxl_alloc_irq_vectors(struct pci_dev *pdev) PCI_IRQ_MSIX | PCI_IRQ_MSI); if (nvecs < 1) { dev_dbg(&pdev->dev, "Failed to alloc irq vectors: %d\n", nvecs); - return -ENXIO; + return false; } - return 0; + return true; } static irqreturn_t cxl_event_thread(int irq, void *id) @@ -742,7 +743,7 @@ static bool cxl_event_int_is_fw(u8 setting) } static int cxl_event_config(struct pci_host_bridge *host_bridge, - struct cxl_memdev_state *mds) + struct cxl_memdev_state *mds, bool irq_avail) { struct cxl_event_interrupt_policy policy; int rc; @@ -754,6 +755,11 @@ static int cxl_event_config(struct pci_host_bridge *host_bridge, if (!host_bridge->native_cxl_error) return 0; + if (!irq_avail) { + dev_info(mds->cxlds.dev, "No interrupt support, disable event processing.\n"); + return 0; + } + rc = cxl_mem_alloc_event_buf(mds); if (rc) return rc; @@ -788,6 +794,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct cxl_register_map map; struct cxl_memdev *cxlmd; int i, rc, pmu_count; + bool irq_avail; /* * Double check the anonymous union trickery in struct cxl_regs @@ -845,11 +852,9 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) else dev_warn(&pdev->dev, "Media not active (%d)\n", rc); - rc = cxl_alloc_irq_vectors(pdev); - if (rc) - return rc; + irq_avail = cxl_alloc_irq_vectors(pdev); - rc = cxl_pci_setup_mailbox(mds); + rc = cxl_pci_setup_mailbox(mds, irq_avail); if (rc) return rc; @@ -908,7 +913,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) } } - rc = cxl_event_config(host_bridge, mds); + rc = cxl_event_config(host_bridge, mds, irq_avail); if (rc) return rc; @@ -969,6 +974,61 @@ static struct pci_driver cxl_pci_driver = { }, }; +#define CXL_EVENT_HDR_FLAGS_REC_SEVERITY GENMASK(1, 0) +static void cxl_cper_event_call(enum cxl_event_type ev_type, + struct cxl_cper_event_rec *rec) +{ + struct cper_cxl_event_devid *device_id = &rec->hdr.device_id; + struct pci_dev *pdev __free(pci_dev_put) = NULL; + enum cxl_event_log_type log_type; + struct cxl_dev_state *cxlds; + unsigned int devfn; + u32 hdr_flags; + + devfn = PCI_DEVFN(device_id->device_num, device_id->func_num); + pdev = pci_get_domain_bus_and_slot(device_id->segment_num, + device_id->bus_num, devfn); + if (!pdev) + return; + + guard(pci_dev)(pdev); + if (pdev->driver != &cxl_pci_driver) + return; + + cxlds = pci_get_drvdata(pdev); + if (!cxlds) + return; + + /* Fabricate a log type */ + hdr_flags = get_unaligned_le24(rec->event.generic.hdr.flags); + log_type = FIELD_GET(CXL_EVENT_HDR_FLAGS_REC_SEVERITY, hdr_flags); + + cxl_event_trace_record(cxlds->cxlmd, log_type, ev_type, + &uuid_null, &rec->event); +} + +static int __init cxl_pci_driver_init(void) +{ + int rc; + + rc = cxl_cper_register_callback(cxl_cper_event_call); + if (rc) + return rc; + + rc = pci_register_driver(&cxl_pci_driver); + if (rc) + cxl_cper_unregister_callback(cxl_cper_event_call); + + return rc; +} + +static void __exit cxl_pci_driver_exit(void) +{ + pci_unregister_driver(&cxl_pci_driver); + cxl_cper_unregister_callback(cxl_cper_event_call); +} + +module_init(cxl_pci_driver_init); +module_exit(cxl_pci_driver_exit); MODULE_LICENSE("GPL v2"); -module_pci_driver(cxl_pci_driver); MODULE_IMPORT_NS(CXL); diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c index 47bc8e0b8590..97c21566677a 100644 --- a/drivers/cxl/port.c +++ b/drivers/cxl/port.c @@ -69,6 +69,8 @@ static int cxl_switch_port_probe(struct cxl_port *port) if (rc < 0) return rc; + cxl_switch_parse_cdat(port); + cxlhdm = devm_cxl_setup_hdm(port, NULL); if (!IS_ERR(cxlhdm)) return devm_cxl_enumerate_decoders(cxlhdm, NULL); @@ -109,6 +111,7 @@ static int cxl_endpoint_port_probe(struct cxl_port *port) /* Cache the data early to ensure is_visible() works */ read_cdat_data(port); + cxl_endpoint_parse_cdat(port); get_device(&cxlmd->dev); rc = devm_add_action_or_reset(&port->dev, schedule_detach, cxlmd); @@ -127,14 +130,15 @@ static int cxl_endpoint_port_probe(struct cxl_port *port) * This can't fail in practice as CXL root exit unregisters all * descendant ports and that in turn synchronizes with cxl_port_probe() */ - root = find_cxl_root(port); + struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port); + + root = &cxl_root->port; /* * Now that all endpoint decoders are successfully enumerated, try to * assemble regions from committed decoders */ device_for_each_child(&port->dev, root, discover_region); - put_device(&root->dev); return 0; } diff --git a/drivers/dma-buf/heaps/cma_heap.c b/drivers/dma-buf/heaps/cma_heap.c index ee899f8e6721..4a63567e93ba 100644 --- a/drivers/dma-buf/heaps/cma_heap.c +++ b/drivers/dma-buf/heaps/cma_heap.c @@ -168,10 +168,7 @@ static vm_fault_t cma_heap_vm_fault(struct vm_fault *vmf) if (vmf->pgoff > buffer->pagecount) return VM_FAULT_SIGBUS; - vmf->page = buffer->pages[vmf->pgoff]; - get_page(vmf->page); - - return 0; + return vmf_insert_pfn(vma, vmf->address, page_to_pfn(buffer->pages[vmf->pgoff])); } static const struct vm_operations_struct dma_heap_vm_ops = { @@ -185,6 +182,8 @@ static int cma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0) return -EINVAL; + vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP); + vma->vm_ops = &dma_heap_vm_ops; vma->vm_private_data = buffer; diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 70ba506dabab..e928f2ca0f1e 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -378,6 +378,20 @@ config LPC18XX_DMAMUX Enable support for DMA on NXP LPC18xx/43xx platforms with PL080 and multiplexed DMA request lines. +config LS2X_APB_DMA + tristate "Loongson LS2X APB DMA support" + depends on LOONGARCH || COMPILE_TEST + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Support for the Loongson LS2X APB DMA controller driver. The + DMA controller is having single DMA channel which can be + configured for different peripherals like audio, nand, sdio + etc which is in APB bus. + + This DMA controller transfers data from memory to peripheral fifo. + It does not support memory to memory data transfer. + config MCF_EDMA tristate "Freescale eDMA engine support, ColdFire mcf5441x SoCs" depends on M5441x || COMPILE_TEST diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 83553a97a010..dfd40d14e408 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-y += idxd/ obj-$(CONFIG_K3_DMA) += k3dma.o obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o +obj-$(CONFIG_LS2X_APB_DMA) += ls2x-apb-dma.o obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o obj-$(CONFIG_MILBEAUT_XDMAC) += milbeaut-xdmac.o obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o diff --git a/drivers/dma/apple-admac.c b/drivers/dma/apple-admac.c index 5b63996640d9..9588773dd2eb 100644 --- a/drivers/dma/apple-admac.c +++ b/drivers/dma/apple-admac.c @@ -57,6 +57,8 @@ #define REG_BUS_WIDTH(ch) (0x8040 + (ch) * 0x200) +#define BUS_WIDTH_WORD_SIZE GENMASK(3, 0) +#define BUS_WIDTH_FRAME_SIZE GENMASK(7, 4) #define BUS_WIDTH_8BIT 0x00 #define BUS_WIDTH_16BIT 0x01 #define BUS_WIDTH_32BIT 0x02 @@ -740,7 +742,8 @@ static int admac_device_config(struct dma_chan *chan, struct admac_data *ad = adchan->host; bool is_tx = admac_chan_direction(adchan->no) == DMA_MEM_TO_DEV; int wordsize = 0; - u32 bus_width = 0; + u32 bus_width = readl_relaxed(ad->base + REG_BUS_WIDTH(adchan->no)) & + ~(BUS_WIDTH_WORD_SIZE | BUS_WIDTH_FRAME_SIZE); switch (is_tx ? config->dst_addr_width : config->src_addr_width) { case DMA_SLAVE_BUSWIDTH_1_BYTE: diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index fb89ecbf0cc5..40052d1bd0b5 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -222,8 +222,14 @@ struct atdma_sg { * @vd: pointer to the virtual dma descriptor. * @atchan: pointer to the atmel dma channel. * @total_len: total transaction byte count - * @sg_len: number of sg entries. + * @sglen: number of sg entries. * @sg: array of sgs. + * @boundary: number of transfers to perform before the automatic address increment operation + * @dst_hole: value to add to the destination address when the boundary has been reached + * @src_hole: value to add to the source address when the boundary has been reached + * @memset_buffer: buffer used for the memset operation + * @memset_paddr: physical address of the buffer used for the memset operation + * @memset_vaddr: virtual address of the buffer used for the memset operation */ struct at_desc { struct virt_dma_desc vd; @@ -245,7 +251,10 @@ struct at_desc { /*-- Channels --------------------------------------------------------*/ /** - * atc_status - information bits stored in channel status flag + * enum atc_status - information bits stored in channel status flag + * + * @ATC_IS_PAUSED: If channel is pauses + * @ATC_IS_CYCLIC: If channel is cyclic * * Manipulated with atomic operations. */ @@ -282,7 +291,6 @@ struct at_dma_chan { u32 save_cfg; u32 save_dscr; struct dma_slave_config dma_sconfig; - bool cyclic; struct at_desc *desc; }; @@ -328,12 +336,12 @@ static inline u8 convert_buswidth(enum dma_slave_buswidth addr_width) /** * struct at_dma - internal representation of an Atmel HDMA Controller * @dma_device: dmaengine dma_device object members - * @atdma_devtype: identifier of DMA controller compatibility - * @ch_regs: memory mapped register base + * @regs: memory mapped register base * @clk: dma controller clock * @save_imr: interrupt mask register that is saved on suspend/resume cycle * @all_chan_mask: all channels availlable in a mask * @lli_pool: hw lli table + * @memset_pool: hw memset pool * @chan: channels table to store at_dma_chan structures */ struct at_dma { @@ -626,6 +634,9 @@ static inline u32 atc_calc_bytes_left(u32 current_len, u32 ctrla) /** * atc_get_llis_residue - Get residue for a hardware linked list transfer + * @atchan: pointer to an atmel hdmac channel. + * @desc: pointer to the descriptor for which the residue is calculated. + * @residue: residue to be set to dma_tx_state. * * Calculate the residue by removing the length of the Linked List Item (LLI) * already transferred from the total length. To get the current LLI we can use @@ -661,10 +672,8 @@ static inline u32 atc_calc_bytes_left(u32 current_len, u32 ctrla) * two DSCR values are different, we read again the CTRLA then the DSCR till two * consecutive read values from DSCR are equal or till the maximum trials is * reach. This algorithm is very unlikely not to find a stable value for DSCR. - * @atchan: pointer to an atmel hdmac channel. - * @desc: pointer to the descriptor for which the residue is calculated. - * @residue: residue to be set to dma_tx_state. - * Returns 0 on success, -errno otherwise. + * + * Returns: %0 on success, -errno otherwise. */ static int atc_get_llis_residue(struct at_dma_chan *atchan, struct at_desc *desc, u32 *residue) @@ -731,7 +740,8 @@ static int atc_get_llis_residue(struct at_dma_chan *atchan, * @chan: DMA channel * @cookie: transaction identifier to check status of * @residue: residue to be updated. - * Return 0 on success, -errono otherwise. + * + * Return: %0 on success, -errno otherwise. */ static int atc_get_residue(struct dma_chan *chan, dma_cookie_t cookie, u32 *residue) @@ -1710,7 +1720,7 @@ static void atc_issue_pending(struct dma_chan *chan) * atc_alloc_chan_resources - allocate resources for DMA channel * @chan: allocate descriptor resources for this channel * - * return - the number of allocated descriptors + * Return: the number of allocated descriptors */ static int atc_alloc_chan_resources(struct dma_chan *chan) { diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 2457a420c13d..4e339c04fc1e 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -81,9 +81,13 @@ #define AXI_DMAC_REG_CURRENT_DEST_ADDR 0x438 #define AXI_DMAC_REG_PARTIAL_XFER_LEN 0x44c #define AXI_DMAC_REG_PARTIAL_XFER_ID 0x450 +#define AXI_DMAC_REG_CURRENT_SG_ID 0x454 +#define AXI_DMAC_REG_SG_ADDRESS 0x47c +#define AXI_DMAC_REG_SG_ADDRESS_HIGH 0x4bc #define AXI_DMAC_CTRL_ENABLE BIT(0) #define AXI_DMAC_CTRL_PAUSE BIT(1) +#define AXI_DMAC_CTRL_ENABLE_SG BIT(2) #define AXI_DMAC_IRQ_SOT BIT(0) #define AXI_DMAC_IRQ_EOT BIT(1) @@ -97,20 +101,35 @@ /* The maximum ID allocated by the hardware is 31 */ #define AXI_DMAC_SG_UNUSED 32U +/* Flags for axi_dmac_hw_desc.flags */ +#define AXI_DMAC_HW_FLAG_LAST BIT(0) +#define AXI_DMAC_HW_FLAG_IRQ BIT(1) + +struct axi_dmac_hw_desc { + u32 flags; + u32 id; + u64 dest_addr; + u64 src_addr; + u64 next_sg_addr; + u32 y_len; + u32 x_len; + u32 src_stride; + u32 dst_stride; + u64 __pad[2]; +}; + struct axi_dmac_sg { - dma_addr_t src_addr; - dma_addr_t dest_addr; - unsigned int x_len; - unsigned int y_len; - unsigned int dest_stride; - unsigned int src_stride; - unsigned int id; unsigned int partial_len; bool schedule_when_free; + + struct axi_dmac_hw_desc *hw; + dma_addr_t hw_phys; }; struct axi_dmac_desc { struct virt_dma_desc vdesc; + struct axi_dmac_chan *chan; + bool cyclic; bool have_partial_xfer; @@ -139,6 +158,7 @@ struct axi_dmac_chan { bool hw_partial_xfer; bool hw_cyclic; bool hw_2d; + bool hw_sg; }; struct axi_dmac { @@ -213,9 +233,11 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) unsigned int flags = 0; unsigned int val; - val = axi_dmac_read(dmac, AXI_DMAC_REG_START_TRANSFER); - if (val) /* Queue is full, wait for the next SOT IRQ */ - return; + if (!chan->hw_sg) { + val = axi_dmac_read(dmac, AXI_DMAC_REG_START_TRANSFER); + if (val) /* Queue is full, wait for the next SOT IRQ */ + return; + } desc = chan->next_desc; @@ -229,14 +251,15 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) sg = &desc->sg[desc->num_submitted]; /* Already queued in cyclic mode. Wait for it to finish */ - if (sg->id != AXI_DMAC_SG_UNUSED) { + if (sg->hw->id != AXI_DMAC_SG_UNUSED) { sg->schedule_when_free = true; return; } - desc->num_submitted++; - if (desc->num_submitted == desc->num_sgs || - desc->have_partial_xfer) { + if (chan->hw_sg) { + chan->next_desc = NULL; + } else if (++desc->num_submitted == desc->num_sgs || + desc->have_partial_xfer) { if (desc->cyclic) desc->num_submitted = 0; /* Start again */ else @@ -246,32 +269,42 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) chan->next_desc = desc; } - sg->id = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_ID); + sg->hw->id = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_ID); - if (axi_dmac_dest_is_mem(chan)) { - axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, sg->dest_addr); - axi_dmac_write(dmac, AXI_DMAC_REG_DEST_STRIDE, sg->dest_stride); - } + if (!chan->hw_sg) { + if (axi_dmac_dest_is_mem(chan)) { + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, sg->hw->dest_addr); + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_STRIDE, sg->hw->dst_stride); + } - if (axi_dmac_src_is_mem(chan)) { - axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, sg->src_addr); - axi_dmac_write(dmac, AXI_DMAC_REG_SRC_STRIDE, sg->src_stride); + if (axi_dmac_src_is_mem(chan)) { + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, sg->hw->src_addr); + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_STRIDE, sg->hw->src_stride); + } } /* * If the hardware supports cyclic transfers and there is no callback to - * call and only a single segment, enable hw cyclic mode to avoid - * unnecessary interrupts. + * call, enable hw cyclic mode to avoid unnecessary interrupts. */ - if (chan->hw_cyclic && desc->cyclic && !desc->vdesc.tx.callback && - desc->num_sgs == 1) - flags |= AXI_DMAC_FLAG_CYCLIC; + if (chan->hw_cyclic && desc->cyclic && !desc->vdesc.tx.callback) { + if (chan->hw_sg) + desc->sg[desc->num_sgs - 1].hw->flags &= ~AXI_DMAC_HW_FLAG_IRQ; + else if (desc->num_sgs == 1) + flags |= AXI_DMAC_FLAG_CYCLIC; + } if (chan->hw_partial_xfer) flags |= AXI_DMAC_FLAG_PARTIAL_REPORT; - axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, sg->x_len - 1); - axi_dmac_write(dmac, AXI_DMAC_REG_Y_LENGTH, sg->y_len - 1); + if (chan->hw_sg) { + axi_dmac_write(dmac, AXI_DMAC_REG_SG_ADDRESS, (u32)sg->hw_phys); + axi_dmac_write(dmac, AXI_DMAC_REG_SG_ADDRESS_HIGH, + (u64)sg->hw_phys >> 32); + } else { + axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, sg->hw->x_len); + axi_dmac_write(dmac, AXI_DMAC_REG_Y_LENGTH, sg->hw->y_len); + } axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, flags); axi_dmac_write(dmac, AXI_DMAC_REG_START_TRANSFER, 1); } @@ -286,9 +319,9 @@ static inline unsigned int axi_dmac_total_sg_bytes(struct axi_dmac_chan *chan, struct axi_dmac_sg *sg) { if (chan->hw_2d) - return sg->x_len * sg->y_len; + return (sg->hw->x_len + 1) * (sg->hw->y_len + 1); else - return sg->x_len; + return (sg->hw->x_len + 1); } static void axi_dmac_dequeue_partial_xfers(struct axi_dmac_chan *chan) @@ -307,9 +340,9 @@ static void axi_dmac_dequeue_partial_xfers(struct axi_dmac_chan *chan) list_for_each_entry(desc, &chan->active_descs, vdesc.node) { for (i = 0; i < desc->num_sgs; i++) { sg = &desc->sg[i]; - if (sg->id == AXI_DMAC_SG_UNUSED) + if (sg->hw->id == AXI_DMAC_SG_UNUSED) continue; - if (sg->id == id) { + if (sg->hw->id == id) { desc->have_partial_xfer = true; sg->partial_len = len; found_sg = true; @@ -348,6 +381,9 @@ static void axi_dmac_compute_residue(struct axi_dmac_chan *chan, rslt->result = DMA_TRANS_NOERROR; rslt->residue = 0; + if (chan->hw_sg) + return; + /* * We get here if the last completed segment is partial, which * means we can compute the residue from that segment onwards @@ -374,36 +410,47 @@ static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan, (completed_transfers & AXI_DMAC_FLAG_PARTIAL_XFER_DONE)) axi_dmac_dequeue_partial_xfers(chan); - do { - sg = &active->sg[active->num_completed]; - if (sg->id == AXI_DMAC_SG_UNUSED) /* Not yet submitted */ - break; - if (!(BIT(sg->id) & completed_transfers)) - break; - active->num_completed++; - sg->id = AXI_DMAC_SG_UNUSED; - if (sg->schedule_when_free) { - sg->schedule_when_free = false; - start_next = true; + if (chan->hw_sg) { + if (active->cyclic) { + vchan_cyclic_callback(&active->vdesc); + } else { + list_del(&active->vdesc.node); + vchan_cookie_complete(&active->vdesc); + active = axi_dmac_active_desc(chan); + start_next = !!active; } + } else { + do { + sg = &active->sg[active->num_completed]; + if (sg->hw->id == AXI_DMAC_SG_UNUSED) /* Not yet submitted */ + break; + if (!(BIT(sg->hw->id) & completed_transfers)) + break; + active->num_completed++; + sg->hw->id = AXI_DMAC_SG_UNUSED; + if (sg->schedule_when_free) { + sg->schedule_when_free = false; + start_next = true; + } - if (sg->partial_len) - axi_dmac_compute_residue(chan, active); + if (sg->partial_len) + axi_dmac_compute_residue(chan, active); - if (active->cyclic) - vchan_cyclic_callback(&active->vdesc); + if (active->cyclic) + vchan_cyclic_callback(&active->vdesc); - if (active->num_completed == active->num_sgs || - sg->partial_len) { - if (active->cyclic) { - active->num_completed = 0; /* wrap around */ - } else { - list_del(&active->vdesc.node); - vchan_cookie_complete(&active->vdesc); - active = axi_dmac_active_desc(chan); + if (active->num_completed == active->num_sgs || + sg->partial_len) { + if (active->cyclic) { + active->num_completed = 0; /* wrap around */ + } else { + list_del(&active->vdesc.node); + vchan_cookie_complete(&active->vdesc); + active = axi_dmac_active_desc(chan); + } } - } - } while (active); + } while (active); + } return start_next; } @@ -467,8 +514,12 @@ static void axi_dmac_issue_pending(struct dma_chan *c) struct axi_dmac_chan *chan = to_axi_dmac_chan(c); struct axi_dmac *dmac = chan_to_axi_dmac(chan); unsigned long flags; + u32 ctrl = AXI_DMAC_CTRL_ENABLE; - axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, AXI_DMAC_CTRL_ENABLE); + if (chan->hw_sg) + ctrl |= AXI_DMAC_CTRL_ENABLE_SG; + + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, ctrl); spin_lock_irqsave(&chan->vchan.lock, flags); if (vchan_issue_pending(&chan->vchan)) @@ -476,22 +527,58 @@ static void axi_dmac_issue_pending(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); } -static struct axi_dmac_desc *axi_dmac_alloc_desc(unsigned int num_sgs) +static struct axi_dmac_desc * +axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs) { + struct axi_dmac *dmac = chan_to_axi_dmac(chan); + struct device *dev = dmac->dma_dev.dev; + struct axi_dmac_hw_desc *hws; struct axi_dmac_desc *desc; + dma_addr_t hw_phys; unsigned int i; desc = kzalloc(struct_size(desc, sg, num_sgs), GFP_NOWAIT); if (!desc) return NULL; desc->num_sgs = num_sgs; + desc->chan = chan; + + hws = dma_alloc_coherent(dev, PAGE_ALIGN(num_sgs * sizeof(*hws)), + &hw_phys, GFP_ATOMIC); + if (!hws) { + kfree(desc); + return NULL; + } - for (i = 0; i < num_sgs; i++) - desc->sg[i].id = AXI_DMAC_SG_UNUSED; + for (i = 0; i < num_sgs; i++) { + desc->sg[i].hw = &hws[i]; + desc->sg[i].hw_phys = hw_phys + i * sizeof(*hws); + + hws[i].id = AXI_DMAC_SG_UNUSED; + hws[i].flags = 0; + + /* Link hardware descriptors */ + hws[i].next_sg_addr = hw_phys + (i + 1) * sizeof(*hws); + } + + /* The last hardware descriptor will trigger an interrupt */ + desc->sg[num_sgs - 1].hw->flags = AXI_DMAC_HW_FLAG_LAST | AXI_DMAC_HW_FLAG_IRQ; return desc; } +static void axi_dmac_free_desc(struct axi_dmac_desc *desc) +{ + struct axi_dmac *dmac = chan_to_axi_dmac(desc->chan); + struct device *dev = dmac->dma_dev.dev; + struct axi_dmac_hw_desc *hw = desc->sg[0].hw; + dma_addr_t hw_phys = desc->sg[0].hw_phys; + + dma_free_coherent(dev, PAGE_ALIGN(desc->num_sgs * sizeof(*hw)), + hw, hw_phys); + kfree(desc); +} + static struct axi_dmac_sg *axi_dmac_fill_linear_sg(struct axi_dmac_chan *chan, enum dma_transfer_direction direction, dma_addr_t addr, unsigned int num_periods, unsigned int period_len, @@ -508,26 +595,24 @@ static struct axi_dmac_sg *axi_dmac_fill_linear_sg(struct axi_dmac_chan *chan, segment_size = ((segment_size - 1) | chan->length_align_mask) + 1; for (i = 0; i < num_periods; i++) { - len = period_len; - - while (len > segment_size) { + for (len = period_len; len > segment_size; sg++) { if (direction == DMA_DEV_TO_MEM) - sg->dest_addr = addr; + sg->hw->dest_addr = addr; else - sg->src_addr = addr; - sg->x_len = segment_size; - sg->y_len = 1; - sg++; + sg->hw->src_addr = addr; + sg->hw->x_len = segment_size - 1; + sg->hw->y_len = 0; + sg->hw->flags = 0; addr += segment_size; len -= segment_size; } if (direction == DMA_DEV_TO_MEM) - sg->dest_addr = addr; + sg->hw->dest_addr = addr; else - sg->src_addr = addr; - sg->x_len = len; - sg->y_len = 1; + sg->hw->src_addr = addr; + sg->hw->x_len = len - 1; + sg->hw->y_len = 0; sg++; addr += len; } @@ -554,7 +639,7 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_slave_sg( for_each_sg(sgl, sg, sg_len, i) num_sgs += DIV_ROUND_UP(sg_dma_len(sg), chan->max_length); - desc = axi_dmac_alloc_desc(num_sgs); + desc = axi_dmac_alloc_desc(chan, num_sgs); if (!desc) return NULL; @@ -563,7 +648,7 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_slave_sg( for_each_sg(sgl, sg, sg_len, i) { if (!axi_dmac_check_addr(chan, sg_dma_address(sg)) || !axi_dmac_check_len(chan, sg_dma_len(sg))) { - kfree(desc); + axi_dmac_free_desc(desc); return NULL; } @@ -583,7 +668,7 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_dma_cyclic( { struct axi_dmac_chan *chan = to_axi_dmac_chan(c); struct axi_dmac_desc *desc; - unsigned int num_periods, num_segments; + unsigned int num_periods, num_segments, num_sgs; if (direction != chan->direction) return NULL; @@ -597,11 +682,16 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_dma_cyclic( num_periods = buf_len / period_len; num_segments = DIV_ROUND_UP(period_len, chan->max_length); + num_sgs = num_periods * num_segments; - desc = axi_dmac_alloc_desc(num_periods * num_segments); + desc = axi_dmac_alloc_desc(chan, num_sgs); if (!desc) return NULL; + /* Chain the last descriptor to the first, and remove its "last" flag */ + desc->sg[num_sgs - 1].hw->next_sg_addr = desc->sg[0].hw_phys; + desc->sg[num_sgs - 1].hw->flags &= ~AXI_DMAC_HW_FLAG_LAST; + axi_dmac_fill_linear_sg(chan, direction, buf_addr, num_periods, period_len, desc->sg); @@ -653,26 +743,26 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_interleaved( return NULL; } - desc = axi_dmac_alloc_desc(1); + desc = axi_dmac_alloc_desc(chan, 1); if (!desc) return NULL; if (axi_dmac_src_is_mem(chan)) { - desc->sg[0].src_addr = xt->src_start; - desc->sg[0].src_stride = xt->sgl[0].size + src_icg; + desc->sg[0].hw->src_addr = xt->src_start; + desc->sg[0].hw->src_stride = xt->sgl[0].size + src_icg; } if (axi_dmac_dest_is_mem(chan)) { - desc->sg[0].dest_addr = xt->dst_start; - desc->sg[0].dest_stride = xt->sgl[0].size + dst_icg; + desc->sg[0].hw->dest_addr = xt->dst_start; + desc->sg[0].hw->dst_stride = xt->sgl[0].size + dst_icg; } if (chan->hw_2d) { - desc->sg[0].x_len = xt->sgl[0].size; - desc->sg[0].y_len = xt->numf; + desc->sg[0].hw->x_len = xt->sgl[0].size - 1; + desc->sg[0].hw->y_len = xt->numf - 1; } else { - desc->sg[0].x_len = xt->sgl[0].size * xt->numf; - desc->sg[0].y_len = 1; + desc->sg[0].hw->x_len = xt->sgl[0].size * xt->numf - 1; + desc->sg[0].hw->y_len = 0; } if (flags & DMA_CYCLIC) @@ -688,7 +778,7 @@ static void axi_dmac_free_chan_resources(struct dma_chan *c) static void axi_dmac_desc_free(struct virt_dma_desc *vdesc) { - kfree(container_of(vdesc, struct axi_dmac_desc, vdesc)); + axi_dmac_free_desc(to_axi_dmac_desc(vdesc)); } static bool axi_dmac_regmap_rdwr(struct device *dev, unsigned int reg) @@ -714,6 +804,9 @@ static bool axi_dmac_regmap_rdwr(struct device *dev, unsigned int reg) case AXI_DMAC_REG_CURRENT_DEST_ADDR: case AXI_DMAC_REG_PARTIAL_XFER_LEN: case AXI_DMAC_REG_PARTIAL_XFER_ID: + case AXI_DMAC_REG_CURRENT_SG_ID: + case AXI_DMAC_REG_SG_ADDRESS: + case AXI_DMAC_REG_SG_ADDRESS_HIGH: return true; default: return false; @@ -866,6 +959,10 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version) if (axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS) == AXI_DMAC_FLAG_CYCLIC) chan->hw_cyclic = true; + axi_dmac_write(dmac, AXI_DMAC_REG_SG_ADDRESS, 0xffffffff); + if (axi_dmac_read(dmac, AXI_DMAC_REG_SG_ADDRESS)) + chan->hw_sg = true; + axi_dmac_write(dmac, AXI_DMAC_REG_Y_LENGTH, 1); if (axi_dmac_read(dmac, AXI_DMAC_REG_Y_LENGTH) == 1) chan->hw_2d = true; @@ -911,6 +1008,7 @@ static int axi_dmac_probe(struct platform_device *pdev) struct axi_dmac *dmac; struct regmap *regmap; unsigned int version; + u32 irq_mask = 0; int ret; dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL); @@ -966,6 +1064,7 @@ static int axi_dmac_probe(struct platform_device *pdev) dma_dev->dst_addr_widths = BIT(dmac->chan.dest_width); dma_dev->directions = BIT(dmac->chan.direction); dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; + dma_dev->max_sg_burst = 31; /* 31 SGs maximum in one burst */ INIT_LIST_HEAD(&dma_dev->channels); dmac->chan.vchan.desc_free = axi_dmac_desc_free; @@ -977,7 +1076,10 @@ static int axi_dmac_probe(struct platform_device *pdev) dma_dev->copy_align = (dmac->chan.address_align_mask + 1); - axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_MASK, 0x00); + if (dmac->chan.hw_sg) + irq_mask |= AXI_DMAC_IRQ_SOT; + + axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_MASK, irq_mask); if (of_dma_is_coherent(pdev->dev.of_node)) { ret = axi_dmac_read(dmac, AXI_DMAC_REG_COHERENCY_DESC); diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index b7388ae62d7f..491b22240221 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -1103,6 +1103,9 @@ EXPORT_SYMBOL_GPL(dma_async_device_channel_register); static void __dma_async_device_channel_unregister(struct dma_device *device, struct dma_chan *chan) { + if (chan->local == NULL) + return; + WARN_ONCE(!device->device_release && chan->client_count, "%s called while %d clients hold a reference\n", __func__, chan->client_count); diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index ffe621695e47..a4f608837849 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -21,6 +21,10 @@ #include <linux/slab.h> #include <linux/wait.h> +static bool nobounce; +module_param(nobounce, bool, 0644); +MODULE_PARM_DESC(nobounce, "Prevent using swiotlb buffer (default: use swiotlb buffer)"); + static unsigned int test_buf_size = 16384; module_param(test_buf_size, uint, 0644); MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer"); @@ -90,6 +94,7 @@ MODULE_PARM_DESC(polled, "Use polling for completion instead of interrupts"); /** * struct dmatest_params - test parameters. + * @nobounce: prevent using swiotlb buffer * @buf_size: size of the memcpy test buffer * @channel: bus ID of the channel to test * @device: bus ID of the DMA Engine to test @@ -106,6 +111,7 @@ MODULE_PARM_DESC(polled, "Use polling for completion instead of interrupts"); * @polled: use polling for completion instead of interrupts */ struct dmatest_params { + bool nobounce; unsigned int buf_size; char channel[20]; char device[32]; @@ -215,6 +221,7 @@ struct dmatest_done { struct dmatest_data { u8 **raw; u8 **aligned; + gfp_t gfp_flags; unsigned int cnt; unsigned int off; }; @@ -533,7 +540,7 @@ static int dmatest_alloc_test_data(struct dmatest_data *d, goto err; for (i = 0; i < d->cnt; i++) { - d->raw[i] = kmalloc(buf_size + align, GFP_KERNEL); + d->raw[i] = kmalloc(buf_size + align, d->gfp_flags); if (!d->raw[i]) goto err; @@ -655,6 +662,13 @@ static int dmatest_func(void *data) goto err_free_coefs; } + src->gfp_flags = GFP_KERNEL; + dst->gfp_flags = GFP_KERNEL; + if (params->nobounce) { + src->gfp_flags = GFP_DMA; + dst->gfp_flags = GFP_DMA; + } + if (dmatest_alloc_test_data(src, buf_size, align) < 0) goto err_free_coefs; @@ -1093,6 +1107,7 @@ static void add_threaded_test(struct dmatest_info *info) struct dmatest_params *params = &info->params; /* Copy test parameters */ + params->nobounce = nobounce; params->buf_size = test_buf_size; strscpy(params->channel, strim(test_channel), sizeof(params->channel)); strscpy(params->device, strim(test_device), sizeof(params->device)); diff --git a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c index 0745d9e7d259..406f169b09a7 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c +++ b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c @@ -176,7 +176,7 @@ dw_edma_debugfs_regs_wr(struct dw_edma *dw, struct dentry *dent) }; struct dentry *regs_dent, *ch_dent; int nr_entries, i; - char name[16]; + char name[32]; regs_dent = debugfs_create_dir(WRITE_STR, dent); @@ -239,7 +239,7 @@ static noinline_for_stack void dw_edma_debugfs_regs_rd(struct dw_edma *dw, }; struct dentry *regs_dent, *ch_dent; int nr_entries, i; - char name[16]; + char name[32]; regs_dent = debugfs_create_dir(READ_STR, dent); diff --git a/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c index 520c81978b08..dcdc57fe976c 100644 --- a/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c +++ b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c @@ -116,7 +116,7 @@ static void dw_hdma_debugfs_regs_ch(struct dw_edma *dw, enum dw_edma_dir dir, static void dw_hdma_debugfs_regs_wr(struct dw_edma *dw, struct dentry *dent) { struct dentry *regs_dent, *ch_dent; - char name[16]; + char name[32]; int i; regs_dent = debugfs_create_dir(WRITE_STR, dent); @@ -133,7 +133,7 @@ static void dw_hdma_debugfs_regs_wr(struct dw_edma *dw, struct dentry *dent) static void dw_hdma_debugfs_regs_rd(struct dw_edma *dw, struct dentry *dent) { struct dentry *regs_dent, *ch_dent; - char name[16]; + char name[32]; int i; regs_dent = debugfs_create_dir(READ_STR, dent); diff --git a/drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c b/drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c index 7958ac33e36c..5a8061a307cd 100644 --- a/drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c +++ b/drivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c @@ -38,15 +38,17 @@ static int dpaa2_qdma_alloc_chan_resources(struct dma_chan *chan) if (!dpaa2_chan->fd_pool) goto err; - dpaa2_chan->fl_pool = dma_pool_create("fl_pool", dev, - sizeof(struct dpaa2_fl_entry), - sizeof(struct dpaa2_fl_entry), 0); + dpaa2_chan->fl_pool = + dma_pool_create("fl_pool", dev, + sizeof(struct dpaa2_fl_entry) * 3, + sizeof(struct dpaa2_fl_entry), 0); + if (!dpaa2_chan->fl_pool) goto err_fd; dpaa2_chan->sdd_pool = dma_pool_create("sdd_pool", dev, - sizeof(struct dpaa2_qdma_sd_d), + sizeof(struct dpaa2_qdma_sd_d) * 2, sizeof(struct dpaa2_qdma_sd_d), 0); if (!dpaa2_chan->sdd_pool) goto err_fl; diff --git a/drivers/dma/fsl-edma-main.c b/drivers/dma/fsl-edma-main.c index 238a69bd0d6f..45cc419b1b4a 100644 --- a/drivers/dma/fsl-edma-main.c +++ b/drivers/dma/fsl-edma-main.c @@ -9,6 +9,7 @@ * Vybrid and Layerscape SoCs. */ +#include <dt-bindings/dma/fsl-edma.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/clk.h> @@ -21,10 +22,6 @@ #include "fsl-edma-common.h" -#define ARGS_RX BIT(0) -#define ARGS_REMOTE BIT(1) -#define ARGS_MULTI_FIFO BIT(2) - static void fsl_edma_synchronize(struct dma_chan *chan) { struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); @@ -153,9 +150,15 @@ static struct dma_chan *fsl_edma3_xlate(struct of_phandle_args *dma_spec, i = fsl_chan - fsl_edma->chans; fsl_chan->priority = dma_spec->args[1]; - fsl_chan->is_rxchan = dma_spec->args[2] & ARGS_RX; - fsl_chan->is_remote = dma_spec->args[2] & ARGS_REMOTE; - fsl_chan->is_multi_fifo = dma_spec->args[2] & ARGS_MULTI_FIFO; + fsl_chan->is_rxchan = dma_spec->args[2] & FSL_EDMA_RX; + fsl_chan->is_remote = dma_spec->args[2] & FSL_EDMA_REMOTE; + fsl_chan->is_multi_fifo = dma_spec->args[2] & FSL_EDMA_MULTI_FIFO; + + if ((dma_spec->args[2] & FSL_EDMA_EVEN_CH) && (i & 0x1)) + continue; + + if ((dma_spec->args[2] & FSL_EDMA_ODD_CH) && !(i & 0x1)) + continue; if (!b_chmux && i == dma_spec->args[0]) { chan = dma_get_slave_channel(chan); diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c index 47cb28468049..f405c77060ad 100644 --- a/drivers/dma/fsl-qdma.c +++ b/drivers/dma/fsl-qdma.c @@ -514,11 +514,11 @@ static struct fsl_qdma_queue queue_temp = queue_head + i + (j * queue_num); queue_temp->cq = - dma_alloc_coherent(&pdev->dev, - sizeof(struct fsl_qdma_format) * - queue_size[i], - &queue_temp->bus_addr, - GFP_KERNEL); + dmam_alloc_coherent(&pdev->dev, + sizeof(struct fsl_qdma_format) * + queue_size[i], + &queue_temp->bus_addr, + GFP_KERNEL); if (!queue_temp->cq) return NULL; queue_temp->block_base = fsl_qdma->block_base + @@ -563,15 +563,14 @@ static struct fsl_qdma_queue /* * Buffer for queue command */ - status_head->cq = dma_alloc_coherent(&pdev->dev, - sizeof(struct fsl_qdma_format) * - status_size, - &status_head->bus_addr, - GFP_KERNEL); - if (!status_head->cq) { - devm_kfree(&pdev->dev, status_head); + status_head->cq = dmam_alloc_coherent(&pdev->dev, + sizeof(struct fsl_qdma_format) * + status_size, + &status_head->bus_addr, + GFP_KERNEL); + if (!status_head->cq) return NULL; - } + status_head->n_cq = status_size; status_head->virt_head = status_head->cq; status_head->virt_tail = status_head->cq; @@ -805,7 +804,7 @@ fsl_qdma_irq_init(struct platform_device *pdev, int i; int cpu; int ret; - char irq_name[20]; + char irq_name[32]; fsl_qdma->error_irq = platform_get_irq_byname(pdev, "qdma-error"); @@ -1268,8 +1267,6 @@ static void fsl_qdma_cleanup_vchan(struct dma_device *dmadev) static void fsl_qdma_remove(struct platform_device *pdev) { - int i; - struct fsl_qdma_queue *status; struct device_node *np = pdev->dev.of_node; struct fsl_qdma_engine *fsl_qdma = platform_get_drvdata(pdev); @@ -1277,12 +1274,6 @@ static void fsl_qdma_remove(struct platform_device *pdev) fsl_qdma_cleanup_vchan(&fsl_qdma->dma_dev); of_dma_controller_free(np); dma_async_device_unregister(&fsl_qdma->dma_dev); - - for (i = 0; i < fsl_qdma->block_number; i++) { - status = fsl_qdma->status[i]; - dma_free_coherent(&pdev->dev, sizeof(struct fsl_qdma_format) * - status->n_cq, status->cq, status->bus_addr); - } } static const struct of_device_id fsl_qdma_dt_ids[] = { diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c index 1d918d45d9f6..77f8885cf407 100644 --- a/drivers/dma/idxd/cdev.c +++ b/drivers/dma/idxd/cdev.c @@ -165,7 +165,7 @@ static void idxd_cdev_dev_release(struct device *dev) struct idxd_wq *wq = idxd_cdev->wq; cdev_ctx = &ictx[wq->idxd->data->type]; - ida_simple_remove(&cdev_ctx->minor_ida, idxd_cdev->minor); + ida_free(&cdev_ctx->minor_ida, idxd_cdev->minor); kfree(idxd_cdev); } @@ -463,7 +463,7 @@ int idxd_wq_add_cdev(struct idxd_wq *wq) cdev = &idxd_cdev->cdev; dev = cdev_dev(idxd_cdev); cdev_ctx = &ictx[wq->idxd->data->type]; - minor = ida_simple_get(&cdev_ctx->minor_ida, 0, MINORMASK, GFP_KERNEL); + minor = ida_alloc_max(&cdev_ctx->minor_ida, MINORMASK, GFP_KERNEL); if (minor < 0) { kfree(idxd_cdev); return minor; diff --git a/drivers/dma/idxd/device.c b/drivers/dma/idxd/device.c index f43d81128b96..ecfdf4a8f1f8 100644 --- a/drivers/dma/idxd/device.c +++ b/drivers/dma/idxd/device.c @@ -807,6 +807,9 @@ err_bmap: static void idxd_device_evl_free(struct idxd_device *idxd) { + void *evl_log; + unsigned int evl_log_size; + dma_addr_t evl_dma; union gencfg_reg gencfg; union genctrl_reg genctrl; struct device *dev = &idxd->pdev->dev; @@ -827,11 +830,15 @@ static void idxd_device_evl_free(struct idxd_device *idxd) iowrite64(0, idxd->reg_base + IDXD_EVLCFG_OFFSET); iowrite64(0, idxd->reg_base + IDXD_EVLCFG_OFFSET + 8); - dma_free_coherent(dev, evl->log_size, evl->log, evl->dma); bitmap_free(evl->bmap); + evl_log = evl->log; + evl_log_size = evl->log_size; + evl_dma = evl->dma; evl->log = NULL; evl->size = IDXD_EVL_SIZE_MIN; spin_unlock(&evl->lock); + + dma_free_coherent(dev, evl_log_size, evl_log, evl_dma); } static void idxd_group_config_write(struct idxd_group *group) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index f81ecf5863e8..9b42f5e96b1e 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -421,9 +421,7 @@ struct sdma_desc { * @shp_addr: value for gReg[6] * @per_addr: value for gReg[2] * @status: status of dma channel - * @context_loaded: ensure context is only loaded once * @data: specific sdma interface structure - * @bd_pool: dma_pool for bd * @terminate_worker: used to call back into terminate work function * @terminated: terminated list * @is_ram_script: flag for script in ram @@ -486,8 +484,6 @@ struct sdma_channel { * @num_script_addrs: Number of script addresses in this image * @ram_code_start: offset of SDMA ram image in this firmware image * @ram_code_size: size of SDMA ram image - * @script_addrs: Stores the start address of the SDMA scripts - * (in SDMA memory space) */ struct sdma_firmware_header { u32 magic; diff --git a/drivers/dma/ls2x-apb-dma.c b/drivers/dma/ls2x-apb-dma.c new file mode 100644 index 000000000000..a49913f3ed3f --- /dev/null +++ b/drivers/dma/ls2x-apb-dma.c @@ -0,0 +1,705 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for the Loongson LS2X APB DMA Controller + * + * Copyright (C) 2017-2023 Loongson Corporation + */ + +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_dma.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "dmaengine.h" +#include "virt-dma.h" + +/* Global Configuration Register */ +#define LDMA_ORDER_ERG 0x0 + +/* Bitfield definitions */ + +/* Bitfields in Global Configuration Register */ +#define LDMA_64BIT_EN BIT(0) /* 1: 64 bit support */ +#define LDMA_UNCOHERENT_EN BIT(1) /* 0: cache, 1: uncache */ +#define LDMA_ASK_VALID BIT(2) +#define LDMA_START BIT(3) /* DMA start operation */ +#define LDMA_STOP BIT(4) /* DMA stop operation */ +#define LDMA_CONFIG_MASK GENMASK(4, 0) /* DMA controller config bits mask */ + +/* Bitfields in ndesc_addr field of HW decriptor */ +#define LDMA_DESC_EN BIT(0) /*1: The next descriptor is valid */ +#define LDMA_DESC_ADDR_LOW GENMASK(31, 1) + +/* Bitfields in cmd field of HW decriptor */ +#define LDMA_INT BIT(1) /* Enable DMA interrupts */ +#define LDMA_DATA_DIRECTION BIT(12) /* 1: write to device, 0: read from device */ + +#define LDMA_SLAVE_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)) + +#define LDMA_MAX_TRANS_LEN U32_MAX + +/*-- descriptors -----------------------------------------------------*/ + +/* + * struct ls2x_dma_hw_desc - DMA HW descriptor + * @ndesc_addr: the next descriptor low address. + * @mem_addr: memory low address. + * @apb_addr: device buffer address. + * @len: length of a piece of carried content, in words. + * @step_len: length between two moved memory data blocks. + * @step_times: number of blocks to be carried in a single DMA operation. + * @cmd: descriptor command or state. + * @stats: DMA status. + * @high_ndesc_addr: the next descriptor high address. + * @high_mem_addr: memory high address. + * @reserved: reserved + */ +struct ls2x_dma_hw_desc { + u32 ndesc_addr; + u32 mem_addr; + u32 apb_addr; + u32 len; + u32 step_len; + u32 step_times; + u32 cmd; + u32 stats; + u32 high_ndesc_addr; + u32 high_mem_addr; + u32 reserved[2]; +} __packed; + +/* + * struct ls2x_dma_sg - ls2x dma scatter gather entry + * @hw: the pointer to DMA HW descriptor. + * @llp: physical address of the DMA HW descriptor. + * @phys: destination or source address(mem). + * @len: number of Bytes to read. + */ +struct ls2x_dma_sg { + struct ls2x_dma_hw_desc *hw; + dma_addr_t llp; + dma_addr_t phys; + u32 len; +}; + +/* + * struct ls2x_dma_desc - software descriptor + * @vdesc: pointer to the virtual dma descriptor. + * @cyclic: flag to dma cyclic + * @burst_size: burst size of transaction, in words. + * @desc_num: number of sg entries. + * @direction: transfer direction, to or from device. + * @status: dma controller status. + * @sg: array of sgs. + */ +struct ls2x_dma_desc { + struct virt_dma_desc vdesc; + bool cyclic; + size_t burst_size; + u32 desc_num; + enum dma_transfer_direction direction; + enum dma_status status; + struct ls2x_dma_sg sg[] __counted_by(desc_num); +}; + +/*-- Channels --------------------------------------------------------*/ + +/* + * struct ls2x_dma_chan - internal representation of an LS2X APB DMA channel + * @vchan: virtual dma channel entry. + * @desc: pointer to the ls2x sw dma descriptor. + * @pool: hw desc table + * @irq: irq line + * @sconfig: configuration for slave transfers, passed via .device_config + */ +struct ls2x_dma_chan { + struct virt_dma_chan vchan; + struct ls2x_dma_desc *desc; + void *pool; + int irq; + struct dma_slave_config sconfig; +}; + +/*-- Controller ------------------------------------------------------*/ + +/* + * struct ls2x_dma_priv - LS2X APB DMAC specific information + * @ddev: dmaengine dma_device object members + * @dma_clk: DMAC clock source + * @regs: memory mapped register base + * @lchan: channel to store ls2x_dma_chan structures + */ +struct ls2x_dma_priv { + struct dma_device ddev; + struct clk *dma_clk; + void __iomem *regs; + struct ls2x_dma_chan lchan; +}; + +/*-- Helper functions ------------------------------------------------*/ + +static inline struct ls2x_dma_desc *to_ldma_desc(struct virt_dma_desc *vdesc) +{ + return container_of(vdesc, struct ls2x_dma_desc, vdesc); +} + +static inline struct ls2x_dma_chan *to_ldma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct ls2x_dma_chan, vchan.chan); +} + +static inline struct ls2x_dma_priv *to_ldma_priv(struct dma_device *ddev) +{ + return container_of(ddev, struct ls2x_dma_priv, ddev); +} + +static struct device *chan2dev(struct dma_chan *chan) +{ + return &chan->dev->device; +} + +static void ls2x_dma_desc_free(struct virt_dma_desc *vdesc) +{ + struct ls2x_dma_chan *lchan = to_ldma_chan(vdesc->tx.chan); + struct ls2x_dma_desc *desc = to_ldma_desc(vdesc); + int i; + + for (i = 0; i < desc->desc_num; i++) { + if (desc->sg[i].hw) + dma_pool_free(lchan->pool, desc->sg[i].hw, + desc->sg[i].llp); + } + + kfree(desc); +} + +static void ls2x_dma_write_cmd(struct ls2x_dma_chan *lchan, bool cmd) +{ + struct ls2x_dma_priv *priv = to_ldma_priv(lchan->vchan.chan.device); + u64 val; + + val = lo_hi_readq(priv->regs + LDMA_ORDER_ERG) & ~LDMA_CONFIG_MASK; + val |= LDMA_64BIT_EN | cmd; + lo_hi_writeq(val, priv->regs + LDMA_ORDER_ERG); +} + +static void ls2x_dma_start_transfer(struct ls2x_dma_chan *lchan) +{ + struct ls2x_dma_priv *priv = to_ldma_priv(lchan->vchan.chan.device); + struct ls2x_dma_sg *ldma_sg; + struct virt_dma_desc *vdesc; + u64 val; + + /* Get the next descriptor */ + vdesc = vchan_next_desc(&lchan->vchan); + if (!vdesc) { + lchan->desc = NULL; + return; + } + + list_del(&vdesc->node); + lchan->desc = to_ldma_desc(vdesc); + ldma_sg = &lchan->desc->sg[0]; + + /* Start DMA */ + lo_hi_writeq(0, priv->regs + LDMA_ORDER_ERG); + val = (ldma_sg->llp & ~LDMA_CONFIG_MASK) | LDMA_64BIT_EN | LDMA_START; + lo_hi_writeq(val, priv->regs + LDMA_ORDER_ERG); +} + +static size_t ls2x_dmac_detect_burst(struct ls2x_dma_chan *lchan) +{ + u32 maxburst, buswidth; + + /* Reject definitely invalid configurations */ + if ((lchan->sconfig.src_addr_width & LDMA_SLAVE_BUSWIDTHS) && + (lchan->sconfig.dst_addr_width & LDMA_SLAVE_BUSWIDTHS)) + return 0; + + if (lchan->sconfig.direction == DMA_MEM_TO_DEV) { + maxburst = lchan->sconfig.dst_maxburst; + buswidth = lchan->sconfig.dst_addr_width; + } else { + maxburst = lchan->sconfig.src_maxburst; + buswidth = lchan->sconfig.src_addr_width; + } + + /* If maxburst is zero, fallback to LDMA_MAX_TRANS_LEN */ + return maxburst ? (maxburst * buswidth) >> 2 : LDMA_MAX_TRANS_LEN; +} + +static void ls2x_dma_fill_desc(struct ls2x_dma_chan *lchan, u32 sg_index, + struct ls2x_dma_desc *desc) +{ + struct ls2x_dma_sg *ldma_sg = &desc->sg[sg_index]; + u32 num_segments, segment_size; + + if (desc->direction == DMA_MEM_TO_DEV) { + ldma_sg->hw->cmd = LDMA_INT | LDMA_DATA_DIRECTION; + ldma_sg->hw->apb_addr = lchan->sconfig.dst_addr; + } else { + ldma_sg->hw->cmd = LDMA_INT; + ldma_sg->hw->apb_addr = lchan->sconfig.src_addr; + } + + ldma_sg->hw->mem_addr = lower_32_bits(ldma_sg->phys); + ldma_sg->hw->high_mem_addr = upper_32_bits(ldma_sg->phys); + + /* Split into multiple equally sized segments if necessary */ + num_segments = DIV_ROUND_UP((ldma_sg->len + 3) >> 2, desc->burst_size); + segment_size = DIV_ROUND_UP((ldma_sg->len + 3) >> 2, num_segments); + + /* Word count register takes input in words */ + ldma_sg->hw->len = segment_size; + ldma_sg->hw->step_times = num_segments; + ldma_sg->hw->step_len = 0; + + /* lets make a link list */ + if (sg_index) { + desc->sg[sg_index - 1].hw->ndesc_addr = ldma_sg->llp | LDMA_DESC_EN; + desc->sg[sg_index - 1].hw->high_ndesc_addr = upper_32_bits(ldma_sg->llp); + } +} + +/*-- DMA Engine API --------------------------------------------------*/ + +/* + * ls2x_dma_alloc_chan_resources - allocate resources for DMA channel + * @chan: allocate descriptor resources for this channel + * + * return - the number of allocated descriptors + */ +static int ls2x_dma_alloc_chan_resources(struct dma_chan *chan) +{ + struct ls2x_dma_chan *lchan = to_ldma_chan(chan); + + /* Create a pool of consistent memory blocks for hardware descriptors */ + lchan->pool = dma_pool_create(dev_name(chan2dev(chan)), + chan->device->dev, PAGE_SIZE, + __alignof__(struct ls2x_dma_hw_desc), 0); + if (!lchan->pool) { + dev_err(chan2dev(chan), "No memory for descriptors\n"); + return -ENOMEM; + } + + return 1; +} + +/* + * ls2x_dma_free_chan_resources - free all channel resources + * @chan: DMA channel + */ +static void ls2x_dma_free_chan_resources(struct dma_chan *chan) +{ + struct ls2x_dma_chan *lchan = to_ldma_chan(chan); + + vchan_free_chan_resources(to_virt_chan(chan)); + dma_pool_destroy(lchan->pool); + lchan->pool = NULL; +} + +/* + * ls2x_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction + * @chan: DMA channel + * @sgl: scatterlist to transfer to/from + * @sg_len: number of entries in @scatterlist + * @direction: DMA direction + * @flags: tx descriptor status flags + * @context: transaction context (ignored) + * + * Return: Async transaction descriptor on success and NULL on failure + */ +static struct dma_async_tx_descriptor * +ls2x_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + u32 sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct ls2x_dma_chan *lchan = to_ldma_chan(chan); + struct ls2x_dma_desc *desc; + struct scatterlist *sg; + size_t burst_size; + int i; + + if (unlikely(!sg_len || !is_slave_direction(direction))) + return NULL; + + burst_size = ls2x_dmac_detect_burst(lchan); + if (!burst_size) + return NULL; + + desc = kzalloc(struct_size(desc, sg, sg_len), GFP_NOWAIT); + if (!desc) + return NULL; + + desc->desc_num = sg_len; + desc->direction = direction; + desc->burst_size = burst_size; + + for_each_sg(sgl, sg, sg_len, i) { + struct ls2x_dma_sg *ldma_sg = &desc->sg[i]; + + /* Allocate DMA capable memory for hardware descriptor */ + ldma_sg->hw = dma_pool_alloc(lchan->pool, GFP_NOWAIT, &ldma_sg->llp); + if (!ldma_sg->hw) { + desc->desc_num = i; + ls2x_dma_desc_free(&desc->vdesc); + return NULL; + } + + ldma_sg->phys = sg_dma_address(sg); + ldma_sg->len = sg_dma_len(sg); + + ls2x_dma_fill_desc(lchan, i, desc); + } + + /* Setting the last descriptor enable bit */ + desc->sg[sg_len - 1].hw->ndesc_addr &= ~LDMA_DESC_EN; + desc->status = DMA_IN_PROGRESS; + + return vchan_tx_prep(&lchan->vchan, &desc->vdesc, flags); +} + +/* + * ls2x_dma_prep_dma_cyclic - prepare the cyclic DMA transfer + * @chan: the DMA channel to prepare + * @buf_addr: physical DMA address where the buffer starts + * @buf_len: total number of bytes for the entire buffer + * @period_len: number of bytes for each period + * @direction: transfer direction, to or from device + * @flags: tx descriptor status flags + * + * Return: Async transaction descriptor on success and NULL on failure + */ +static struct dma_async_tx_descriptor * +ls2x_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags) +{ + struct ls2x_dma_chan *lchan = to_ldma_chan(chan); + struct ls2x_dma_desc *desc; + size_t burst_size; + u32 num_periods; + int i; + + if (unlikely(!buf_len || !period_len)) + return NULL; + + if (unlikely(!is_slave_direction(direction))) + return NULL; + + burst_size = ls2x_dmac_detect_burst(lchan); + if (!burst_size) + return NULL; + + num_periods = buf_len / period_len; + desc = kzalloc(struct_size(desc, sg, num_periods), GFP_NOWAIT); + if (!desc) + return NULL; + + desc->desc_num = num_periods; + desc->direction = direction; + desc->burst_size = burst_size; + + /* Build cyclic linked list */ + for (i = 0; i < num_periods; i++) { + struct ls2x_dma_sg *ldma_sg = &desc->sg[i]; + + /* Allocate DMA capable memory for hardware descriptor */ + ldma_sg->hw = dma_pool_alloc(lchan->pool, GFP_NOWAIT, &ldma_sg->llp); + if (!ldma_sg->hw) { + desc->desc_num = i; + ls2x_dma_desc_free(&desc->vdesc); + return NULL; + } + + ldma_sg->phys = buf_addr + period_len * i; + ldma_sg->len = period_len; + + ls2x_dma_fill_desc(lchan, i, desc); + } + + /* Lets make a cyclic list */ + desc->sg[num_periods - 1].hw->ndesc_addr = desc->sg[0].llp | LDMA_DESC_EN; + desc->sg[num_periods - 1].hw->high_ndesc_addr = upper_32_bits(desc->sg[0].llp); + desc->cyclic = true; + desc->status = DMA_IN_PROGRESS; + + return vchan_tx_prep(&lchan->vchan, &desc->vdesc, flags); +} + +/* + * ls2x_slave_config - set slave configuration for channel + * @chan: dma channel + * @cfg: slave configuration + * + * Sets slave configuration for channel + */ +static int ls2x_dma_slave_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct ls2x_dma_chan *lchan = to_ldma_chan(chan); + + memcpy(&lchan->sconfig, config, sizeof(*config)); + return 0; +} + +/* + * ls2x_dma_issue_pending - push pending transactions to the hardware + * @chan: channel + * + * When this function is called, all pending transactions are pushed to the + * hardware and executed. + */ +static void ls2x_dma_issue_pending(struct dma_chan *chan) +{ + struct ls2x_dma_chan *lchan = to_ldma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&lchan->vchan.lock, flags); + if (vchan_issue_pending(&lchan->vchan) && !lchan->desc) + ls2x_dma_start_transfer(lchan); + spin_unlock_irqrestore(&lchan->vchan.lock, flags); +} + +/* + * ls2x_dma_terminate_all - terminate all transactions + * @chan: channel + * + * Stops all DMA transactions. + */ +static int ls2x_dma_terminate_all(struct dma_chan *chan) +{ + struct ls2x_dma_chan *lchan = to_ldma_chan(chan); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&lchan->vchan.lock, flags); + /* Setting stop cmd */ + ls2x_dma_write_cmd(lchan, LDMA_STOP); + if (lchan->desc) { + vchan_terminate_vdesc(&lchan->desc->vdesc); + lchan->desc = NULL; + } + + vchan_get_all_descriptors(&lchan->vchan, &head); + spin_unlock_irqrestore(&lchan->vchan.lock, flags); + + vchan_dma_desc_free_list(&lchan->vchan, &head); + return 0; +} + +/* + * ls2x_dma_synchronize - Synchronizes the termination of transfers to the + * current context. + * @chan: channel + */ +static void ls2x_dma_synchronize(struct dma_chan *chan) +{ + struct ls2x_dma_chan *lchan = to_ldma_chan(chan); + + vchan_synchronize(&lchan->vchan); +} + +static int ls2x_dma_pause(struct dma_chan *chan) +{ + struct ls2x_dma_chan *lchan = to_ldma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&lchan->vchan.lock, flags); + if (lchan->desc && lchan->desc->status == DMA_IN_PROGRESS) { + ls2x_dma_write_cmd(lchan, LDMA_STOP); + lchan->desc->status = DMA_PAUSED; + } + spin_unlock_irqrestore(&lchan->vchan.lock, flags); + + return 0; +} + +static int ls2x_dma_resume(struct dma_chan *chan) +{ + struct ls2x_dma_chan *lchan = to_ldma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&lchan->vchan.lock, flags); + if (lchan->desc && lchan->desc->status == DMA_PAUSED) { + lchan->desc->status = DMA_IN_PROGRESS; + ls2x_dma_write_cmd(lchan, LDMA_START); + } + spin_unlock_irqrestore(&lchan->vchan.lock, flags); + + return 0; +} + +/* + * ls2x_dma_isr - LS2X DMA Interrupt handler + * @irq: IRQ number + * @dev_id: Pointer to ls2x_dma_chan + * + * Return: IRQ_HANDLED/IRQ_NONE + */ +static irqreturn_t ls2x_dma_isr(int irq, void *dev_id) +{ + struct ls2x_dma_chan *lchan = dev_id; + struct ls2x_dma_desc *desc; + + spin_lock(&lchan->vchan.lock); + desc = lchan->desc; + if (desc) { + if (desc->cyclic) { + vchan_cyclic_callback(&desc->vdesc); + } else { + desc->status = DMA_COMPLETE; + vchan_cookie_complete(&desc->vdesc); + ls2x_dma_start_transfer(lchan); + } + + /* ls2x_dma_start_transfer() updates lchan->desc */ + if (!lchan->desc) + ls2x_dma_write_cmd(lchan, LDMA_STOP); + } + spin_unlock(&lchan->vchan.lock); + + return IRQ_HANDLED; +} + +static int ls2x_dma_chan_init(struct platform_device *pdev, + struct ls2x_dma_priv *priv) +{ + struct ls2x_dma_chan *lchan = &priv->lchan; + struct device *dev = &pdev->dev; + int ret; + + lchan->irq = platform_get_irq(pdev, 0); + if (lchan->irq < 0) + return lchan->irq; + + ret = devm_request_irq(dev, lchan->irq, ls2x_dma_isr, IRQF_TRIGGER_RISING, + dev_name(&pdev->dev), lchan); + if (ret) + return ret; + + /* Initialize channels related values */ + INIT_LIST_HEAD(&priv->ddev.channels); + lchan->vchan.desc_free = ls2x_dma_desc_free; + vchan_init(&lchan->vchan, &priv->ddev); + + return 0; +} + +/* + * ls2x_dma_probe - Driver probe function + * @pdev: Pointer to the platform_device structure + * + * Return: '0' on success and failure value on error + */ +static int ls2x_dma_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ls2x_dma_priv *priv; + struct dma_device *ddev; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->regs)) + return dev_err_probe(dev, PTR_ERR(priv->regs), + "devm_platform_ioremap_resource failed.\n"); + + priv->dma_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->dma_clk)) + return dev_err_probe(dev, PTR_ERR(priv->dma_clk), "devm_clk_get failed.\n"); + + ret = clk_prepare_enable(priv->dma_clk); + if (ret) + return dev_err_probe(dev, ret, "clk_prepare_enable failed.\n"); + + ret = ls2x_dma_chan_init(pdev, priv); + if (ret) + goto disable_clk; + + ddev = &priv->ddev; + ddev->dev = dev; + dma_cap_zero(ddev->cap_mask); + dma_cap_set(DMA_SLAVE, ddev->cap_mask); + dma_cap_set(DMA_CYCLIC, ddev->cap_mask); + + ddev->device_alloc_chan_resources = ls2x_dma_alloc_chan_resources; + ddev->device_free_chan_resources = ls2x_dma_free_chan_resources; + ddev->device_tx_status = dma_cookie_status; + ddev->device_issue_pending = ls2x_dma_issue_pending; + ddev->device_prep_slave_sg = ls2x_dma_prep_slave_sg; + ddev->device_prep_dma_cyclic = ls2x_dma_prep_dma_cyclic; + ddev->device_config = ls2x_dma_slave_config; + ddev->device_terminate_all = ls2x_dma_terminate_all; + ddev->device_synchronize = ls2x_dma_synchronize; + ddev->device_pause = ls2x_dma_pause; + ddev->device_resume = ls2x_dma_resume; + + ddev->src_addr_widths = LDMA_SLAVE_BUSWIDTHS; + ddev->dst_addr_widths = LDMA_SLAVE_BUSWIDTHS; + ddev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + + ret = dma_async_device_register(&priv->ddev); + if (ret < 0) + goto disable_clk; + + ret = of_dma_controller_register(dev->of_node, of_dma_xlate_by_chan_id, priv); + if (ret < 0) + goto unregister_dmac; + + platform_set_drvdata(pdev, priv); + + dev_info(dev, "Loongson LS2X APB DMA driver registered successfully.\n"); + return 0; + +unregister_dmac: + dma_async_device_unregister(&priv->ddev); +disable_clk: + clk_disable_unprepare(priv->dma_clk); + + return ret; +} + +/* + * ls2x_dma_remove - Driver remove function + * @pdev: Pointer to the platform_device structure + */ +static void ls2x_dma_remove(struct platform_device *pdev) +{ + struct ls2x_dma_priv *priv = platform_get_drvdata(pdev); + + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&priv->ddev); + clk_disable_unprepare(priv->dma_clk); +} + +static const struct of_device_id ls2x_dma_of_match_table[] = { + { .compatible = "loongson,ls2k1000-apbdma" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ls2x_dma_of_match_table); + +static struct platform_driver ls2x_dmac_driver = { + .probe = ls2x_dma_probe, + .remove_new = ls2x_dma_remove, + .driver = { + .name = "ls2x-apbdma", + .of_match_table = ls2x_dma_of_match_table, + }, +}; +module_platform_driver(ls2x_dmac_driver); + +MODULE_DESCRIPTION("Loongson LS2X APB DMA Controller driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma/milbeaut-hdmac.c b/drivers/dma/milbeaut-hdmac.c index 1b0a95892627..7b41c670970a 100644 --- a/drivers/dma/milbeaut-hdmac.c +++ b/drivers/dma/milbeaut-hdmac.c @@ -531,7 +531,7 @@ disable_clk: return ret; } -static int milbeaut_hdmac_remove(struct platform_device *pdev) +static void milbeaut_hdmac_remove(struct platform_device *pdev) { struct milbeaut_hdmac_device *mdev = platform_get_drvdata(pdev); struct dma_chan *chan; @@ -546,16 +546,21 @@ static int milbeaut_hdmac_remove(struct platform_device *pdev) */ list_for_each_entry(chan, &mdev->ddev.channels, device_node) { ret = dmaengine_terminate_sync(chan); - if (ret) - return ret; + if (ret) { + /* + * This results in resource leakage and maybe also + * use-after-free errors as e.g. *mdev is kfreed. + */ + dev_alert(&pdev->dev, "Failed to terminate channel %d (%pe)\n", + chan->chan_id, ERR_PTR(ret)); + return; + } milbeaut_hdmac_free_chan_resources(chan); } of_dma_controller_free(pdev->dev.of_node); dma_async_device_unregister(&mdev->ddev); clk_disable_unprepare(mdev->clk); - - return 0; } static const struct of_device_id milbeaut_hdmac_match[] = { @@ -566,7 +571,7 @@ MODULE_DEVICE_TABLE(of, milbeaut_hdmac_match); static struct platform_driver milbeaut_hdmac_driver = { .probe = milbeaut_hdmac_probe, - .remove = milbeaut_hdmac_remove, + .remove_new = milbeaut_hdmac_remove, .driver = { .name = "milbeaut-m10v-hdmac", .of_match_table = milbeaut_hdmac_match, diff --git a/drivers/dma/milbeaut-xdmac.c b/drivers/dma/milbeaut-xdmac.c index d29d01e730aa..2cce529b448e 100644 --- a/drivers/dma/milbeaut-xdmac.c +++ b/drivers/dma/milbeaut-xdmac.c @@ -368,7 +368,7 @@ disable_xdmac: return ret; } -static int milbeaut_xdmac_remove(struct platform_device *pdev) +static void milbeaut_xdmac_remove(struct platform_device *pdev) { struct milbeaut_xdmac_device *mdev = platform_get_drvdata(pdev); struct dma_chan *chan; @@ -383,8 +383,15 @@ static int milbeaut_xdmac_remove(struct platform_device *pdev) */ list_for_each_entry(chan, &mdev->ddev.channels, device_node) { ret = dmaengine_terminate_sync(chan); - if (ret) - return ret; + if (ret) { + /* + * This results in resource leakage and maybe also + * use-after-free errors as e.g. *mdev is kfreed. + */ + dev_alert(&pdev->dev, "Failed to terminate channel %d (%pe)\n", + chan->chan_id, ERR_PTR(ret)); + return; + } milbeaut_xdmac_free_chan_resources(chan); } @@ -392,8 +399,6 @@ static int milbeaut_xdmac_remove(struct platform_device *pdev) dma_async_device_unregister(&mdev->ddev); disable_xdmac(mdev); - - return 0; } static const struct of_device_id milbeaut_xdmac_match[] = { @@ -404,7 +409,7 @@ MODULE_DEVICE_TABLE(of, milbeaut_xdmac_match); static struct platform_driver milbeaut_xdmac_driver = { .probe = milbeaut_xdmac_probe, - .remove = milbeaut_xdmac_remove, + .remove_new = milbeaut_xdmac_remove, .driver = { .name = "milbeaut-m10v-xdmac", .of_match_table = milbeaut_xdmac_match, diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 3cf0b38387ae..c29744bfdf2c 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1053,6 +1053,9 @@ static bool _trigger(struct pl330_thread *thrd) thrd->req_running = idx; + if (desc->rqtype == DMA_MEM_TO_DEV || desc->rqtype == DMA_DEV_TO_MEM) + UNTIL(thrd, PL330_STATE_WFP); + return true; } diff --git a/drivers/dma/sf-pdma/sf-pdma.c b/drivers/dma/sf-pdma/sf-pdma.c index 3125a2f162b4..428473611115 100644 --- a/drivers/dma/sf-pdma/sf-pdma.c +++ b/drivers/dma/sf-pdma/sf-pdma.c @@ -20,10 +20,13 @@ #include <linux/mod_devicetable.h> #include <linux/dma-mapping.h> #include <linux/of.h> +#include <linux/of_dma.h> #include <linux/slab.h> #include "sf-pdma.h" +#define PDMA_QUIRK_NO_STRICT_ORDERING BIT(0) + #ifndef readq static inline unsigned long long readq(void __iomem *addr) { @@ -65,7 +68,7 @@ static struct sf_pdma_desc *sf_pdma_alloc_desc(struct sf_pdma_chan *chan) static void sf_pdma_fill_desc(struct sf_pdma_desc *desc, u64 dst, u64 src, u64 size) { - desc->xfer_type = PDMA_FULL_SPEED; + desc->xfer_type = desc->chan->pdma->transfer_type; desc->xfer_size = size; desc->dst_addr = dst; desc->src_addr = src; @@ -492,6 +495,7 @@ static void sf_pdma_setup_chans(struct sf_pdma *pdma) static int sf_pdma_probe(struct platform_device *pdev) { + const struct sf_pdma_driver_platdata *ddata; struct sf_pdma *pdma; int ret, n_chans; const enum dma_slave_buswidth widths = @@ -517,6 +521,14 @@ static int sf_pdma_probe(struct platform_device *pdev) pdma->n_chans = n_chans; + pdma->transfer_type = PDMA_FULL_SPEED | PDMA_STRICT_ORDERING; + + ddata = device_get_match_data(&pdev->dev); + if (ddata) { + if (ddata->quirks & PDMA_QUIRK_NO_STRICT_ORDERING) + pdma->transfer_type &= ~PDMA_STRICT_ORDERING; + } + pdma->membase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pdma->membase)) return PTR_ERR(pdma->membase); @@ -563,7 +575,20 @@ static int sf_pdma_probe(struct platform_device *pdev) return ret; } + ret = of_dma_controller_register(pdev->dev.of_node, + of_dma_xlate_by_chan_id, pdma); + if (ret < 0) { + dev_err(&pdev->dev, + "Can't register SiFive Platform OF_DMA. (%d)\n", ret); + goto err_unregister; + } + return 0; + +err_unregister: + dma_async_device_unregister(&pdma->dma_dev); + + return ret; } static void sf_pdma_remove(struct platform_device *pdev) @@ -583,12 +608,25 @@ static void sf_pdma_remove(struct platform_device *pdev) tasklet_kill(&ch->err_tasklet); } + if (pdev->dev.of_node) + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&pdma->dma_dev); } +static const struct sf_pdma_driver_platdata mpfs_pdma = { + .quirks = PDMA_QUIRK_NO_STRICT_ORDERING, +}; + static const struct of_device_id sf_pdma_dt_ids[] = { - { .compatible = "sifive,fu540-c000-pdma" }, - { .compatible = "sifive,pdma0" }, + { + .compatible = "sifive,fu540-c000-pdma", + }, { + .compatible = "sifive,pdma0", + }, { + .compatible = "microchip,mpfs-pdma", + .data = &mpfs_pdma, + }, {}, }; MODULE_DEVICE_TABLE(of, sf_pdma_dt_ids); diff --git a/drivers/dma/sf-pdma/sf-pdma.h b/drivers/dma/sf-pdma/sf-pdma.h index d05772b5d8d3..215e07183d7e 100644 --- a/drivers/dma/sf-pdma/sf-pdma.h +++ b/drivers/dma/sf-pdma/sf-pdma.h @@ -48,7 +48,8 @@ #define PDMA_ERR_STATUS_MASK GENMASK(31, 31) /* Transfer Type */ -#define PDMA_FULL_SPEED 0xFF000008 +#define PDMA_FULL_SPEED 0xFF000000 +#define PDMA_STRICT_ORDERING BIT(3) /* Error Recovery */ #define MAX_RETRY 1 @@ -112,8 +113,13 @@ struct sf_pdma { struct dma_device dma_dev; void __iomem *membase; void __iomem *mappedbase; + u32 transfer_type; u32 n_chans; struct sf_pdma_chan chans[] __counted_by(n_chans); }; +struct sf_pdma_driver_platdata { + u32 quirks; +}; + #endif /* _SF_PDMA_H */ diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index fea5bda34bc2..1f1e86ba5c66 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -755,11 +755,11 @@ static struct dma_chan *rz_dmac_of_xlate(struct of_phandle_args *dma_spec, static int rz_dmac_chan_probe(struct rz_dmac *dmac, struct rz_dmac_chan *channel, - unsigned int index) + u8 index) { struct platform_device *pdev = to_platform_device(dmac->dev); struct rz_lmdesc *lmdesc; - char pdev_irqname[5]; + char pdev_irqname[6]; char *irqname; int ret; @@ -767,7 +767,7 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac, channel->mid_rid = -EINVAL; /* Request the channel interrupt. */ - sprintf(pdev_irqname, "ch%u", index); + scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index); channel->irq = platform_get_irq_byname(pdev, pdev_irqname); if (channel->irq < 0) return channel->irq; @@ -845,9 +845,9 @@ static int rz_dmac_probe(struct platform_device *pdev) struct dma_device *engine; struct rz_dmac *dmac; int channel_num; - unsigned int i; int ret; int irq; + u8 i; dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL); if (!dmac) diff --git a/drivers/dma/sh/shdma.h b/drivers/dma/sh/shdma.h index 9c121a4b33ad..f97d80343aea 100644 --- a/drivers/dma/sh/shdma.h +++ b/drivers/dma/sh/shdma.h @@ -25,7 +25,7 @@ struct sh_dmae_chan { const struct sh_dmae_slave_config *config; /* Slave DMA configuration */ int xmit_shift; /* log_2(bytes_per_xfer) */ void __iomem *base; - char dev_id[16]; /* unique name per DMAC of channel */ + char dev_id[32]; /* unique name per DMAC of channel */ int pm_error; dma_addr_t slave_addr; }; diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c index a9b4302f6050..f7cd0cad056c 100644 --- a/drivers/dma/sh/usb-dmac.c +++ b/drivers/dma/sh/usb-dmac.c @@ -706,10 +706,10 @@ static const struct dev_pm_ops usb_dmac_pm = { static int usb_dmac_chan_probe(struct usb_dmac *dmac, struct usb_dmac_chan *uchan, - unsigned int index) + u8 index) { struct platform_device *pdev = to_platform_device(dmac->dev); - char pdev_irqname[5]; + char pdev_irqname[6]; char *irqname; int ret; @@ -717,7 +717,7 @@ static int usb_dmac_chan_probe(struct usb_dmac *dmac, uchan->iomem = dmac->iomem + USB_DMAC_CHAN_OFFSET(index); /* Request the channel interrupt. */ - sprintf(pdev_irqname, "ch%u", index); + scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index); uchan->irq = platform_get_irq_byname(pdev, pdev_irqname); if (uchan->irq < 0) return -ENODEV; @@ -768,8 +768,8 @@ static int usb_dmac_probe(struct platform_device *pdev) const enum dma_slave_buswidth widths = USB_DMAC_SLAVE_BUSWIDTH; struct dma_device *engine; struct usb_dmac *dmac; - unsigned int i; int ret; + u8 i; dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL); if (!dmac) @@ -869,7 +869,7 @@ static void usb_dmac_chan_remove(struct usb_dmac *dmac, static void usb_dmac_remove(struct platform_device *pdev) { struct usb_dmac *dmac = platform_get_drvdata(pdev); - int i; + u8 i; for (i = 0; i < dmac->n_channels; ++i) usb_dmac_chan_remove(dmac, &dmac->channels[i]); diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 002833fb1fa0..2c489299148e 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -31,13 +31,11 @@ /** * struct stedma40_platform_data - Configuration struct for the dma device. * - * @dev_tx: mapping between destination event line and io address - * @dev_rx: mapping between source event line and io address * @disabled_channels: A vector, ending with -1, that marks physical channels * that are for different reasons not available for the driver. * @soft_lli_chans: A vector, that marks physical channels will use LLI by SW * which avoids HW bug that exists in some versions of the controller. - * SoftLLI introduces relink overhead that could impact performace for + * SoftLLI introduces relink overhead that could impact performance for * certain use cases. * @num_of_soft_lli_chans: The number of channels that needs to be configured * to use SoftLLI. @@ -184,7 +182,7 @@ static __maybe_unused u32 d40_backup_regs[] = { /* * since 9540 and 8540 has the same HW revision - * use v4a for 9540 or ealier + * use v4a for 9540 or earlier * use v4b for 8540 or later * HW revision: * DB8500ed has revision 0 @@ -411,7 +409,7 @@ struct d40_desc { * * @base: The virtual address of LCLA. 18 bit aligned. * @dma_addr: DMA address, if mapped - * @base_unaligned: The orignal kmalloc pointer, if kmalloc is used. + * @base_unaligned: The original kmalloc pointer, if kmalloc is used. * This pointer is only there for clean-up on error. * @pages: The number of pages needed for all physical channels. * Only used later for clean-up on error @@ -1655,7 +1653,7 @@ static void dma_tasklet(struct tasklet_struct *t) return; check_pending_tx: - /* Rescue manouver if receiving double interrupts */ + /* Rescue maneuver if receiving double interrupts */ if (d40c->pending_tx > 0) d40c->pending_tx--; spin_unlock_irqrestore(&d40c->lock, flags); @@ -3412,7 +3410,7 @@ static int __init d40_lcla_allocate(struct d40_base *base) base->lcla_pool.base = (void *)page_list[i]; } else { /* - * After many attempts and no succees with finding the correct + * After many attempts and no success with finding the correct * alignment, try with allocating a big buffer. */ dev_warn(base->dev, diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index fa4d4142a68a..88547a23825b 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -1348,8 +1348,8 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id) static int tegra_dma_probe(struct platform_device *pdev) { const struct tegra_dma_chip_data *cdata = NULL; - struct iommu_fwspec *iommu_spec; - unsigned int stream_id, i; + unsigned int i; + u32 stream_id; struct tegra_dma *tdma; int ret; @@ -1378,12 +1378,10 @@ static int tegra_dma_probe(struct platform_device *pdev) tdma->dma_dev.dev = &pdev->dev; - iommu_spec = dev_iommu_fwspec_get(&pdev->dev); - if (!iommu_spec) { + if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) { dev_err(&pdev->dev, "Missing iommu stream-id\n"); return -EINVAL; } - stream_id = iommu_spec->ids[0] & 0xffff; ret = device_property_read_u32(&pdev->dev, "dma-channel-mask", &tdma->chan_mask); diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c index 7a0586633bf3..24ad7077c53b 100644 --- a/drivers/dma/tegra210-adma.c +++ b/drivers/dma/tegra210-adma.c @@ -153,6 +153,7 @@ struct tegra_adma { void __iomem *base_addr; struct clk *ahub_clk; unsigned int nr_channels; + unsigned long *dma_chan_mask; unsigned long rx_requests_reserved; unsigned long tx_requests_reserved; @@ -741,6 +742,10 @@ static int __maybe_unused tegra_adma_runtime_suspend(struct device *dev) for (i = 0; i < tdma->nr_channels; i++) { tdc = &tdma->channels[i]; + /* skip for reserved channels */ + if (!tdc->tdma) + continue; + ch_reg = &tdc->ch_regs; ch_reg->cmd = tdma_ch_read(tdc, ADMA_CH_CMD); /* skip if channel is not active */ @@ -779,6 +784,9 @@ static int __maybe_unused tegra_adma_runtime_resume(struct device *dev) for (i = 0; i < tdma->nr_channels; i++) { tdc = &tdma->channels[i]; + /* skip for reserved channels */ + if (!tdc->tdma) + continue; ch_reg = &tdc->ch_regs; /* skip if channel was not active earlier */ if (!ch_reg->cmd) @@ -867,10 +875,31 @@ static int tegra_adma_probe(struct platform_device *pdev) return PTR_ERR(tdma->ahub_clk); } + tdma->dma_chan_mask = devm_kzalloc(&pdev->dev, + BITS_TO_LONGS(tdma->nr_channels) * sizeof(unsigned long), + GFP_KERNEL); + if (!tdma->dma_chan_mask) + return -ENOMEM; + + /* Enable all channels by default */ + bitmap_fill(tdma->dma_chan_mask, tdma->nr_channels); + + ret = of_property_read_u32_array(pdev->dev.of_node, "dma-channel-mask", + (u32 *)tdma->dma_chan_mask, + BITS_TO_U32(tdma->nr_channels)); + if (ret < 0 && (ret != -EINVAL)) { + dev_err(&pdev->dev, "dma-channel-mask is not complete.\n"); + return ret; + } + INIT_LIST_HEAD(&tdma->dma_dev.channels); for (i = 0; i < tdma->nr_channels; i++) { struct tegra_adma_chan *tdc = &tdma->channels[i]; + /* skip for reserved channels */ + if (!test_bit(i, tdma->dma_chan_mask)) + continue; + tdc->chan_addr = tdma->base_addr + cdata->ch_base_offset + (cdata->ch_reg_size * i); @@ -957,8 +986,10 @@ static void tegra_adma_remove(struct platform_device *pdev) of_dma_controller_free(pdev->dev.of_node); dma_async_device_unregister(&tdma->dma_dev); - for (i = 0; i < tdma->nr_channels; ++i) - irq_dispose_mapping(tdma->channels[i].irq); + for (i = 0; i < tdma->nr_channels; ++i) { + if (tdma->channels[i].irq) + irq_dispose_mapping(tdma->channels[i].irq); + } pm_runtime_disable(&pdev->dev); } diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile index acc950bf609c..d376c117cecf 100644 --- a/drivers/dma/ti/Makefile +++ b/drivers/dma/ti/Makefile @@ -12,6 +12,7 @@ k3-psil-lib-objs := k3-psil.o \ k3-psil-j721s2.o \ k3-psil-am62.o \ k3-psil-am62a.o \ - k3-psil-j784s4.o + k3-psil-j784s4.o \ + k3-psil-am62p.o obj-$(CONFIG_TI_K3_PSIL) += k3-psil-lib.o obj-$(CONFIG_TI_DMA_CROSSBAR) += dma-crossbar.o diff --git a/drivers/dma/ti/edma.c b/drivers/dma/ti/edma.c index f1f920861fa9..5f8d2e93ff3f 100644 --- a/drivers/dma/ti/edma.c +++ b/drivers/dma/ti/edma.c @@ -2404,6 +2404,11 @@ static int edma_probe(struct platform_device *pdev) if (irq > 0) { irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s_ccint", dev_name(dev)); + if (!irq_name) { + ret = -ENOMEM; + goto err_disable_pm; + } + ret = devm_request_irq(dev, irq, dma_irq_handler, 0, irq_name, ecc); if (ret) { @@ -2420,6 +2425,11 @@ static int edma_probe(struct platform_device *pdev) if (irq > 0) { irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s_ccerrint", dev_name(dev)); + if (!irq_name) { + ret = -ENOMEM; + goto err_disable_pm; + } + ret = devm_request_irq(dev, irq, dma_ccerr_handler, 0, irq_name, ecc); if (ret) { diff --git a/drivers/dma/ti/k3-psil-am62p.c b/drivers/dma/ti/k3-psil-am62p.c new file mode 100644 index 000000000000..0f338e16d971 --- /dev/null +++ b/drivers/dma/ti/k3-psil-am62p.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com + */ + +#include <linux/kernel.h> + +#include "k3-psil-priv.h" + +#define PSIL_PDMA_XY_TR(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_PDMA_XY, \ + .mapped_channel_id = -1, \ + .default_flow_id = -1, \ + }, \ + } + +#define PSIL_PDMA_XY_PKT(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_PDMA_XY, \ + .mapped_channel_id = -1, \ + .default_flow_id = -1, \ + .pkt_mode = 1, \ + }, \ + } + +#define PSIL_ETHERNET(x, ch, flow_base, flow_cnt) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_NATIVE, \ + .pkt_mode = 1, \ + .needs_epib = 1, \ + .psd_size = 16, \ + .mapped_channel_id = ch, \ + .flow_start = flow_base, \ + .flow_num = flow_cnt, \ + .default_flow_id = flow_base, \ + }, \ + } + +#define PSIL_SAUL(x, ch, flow_base, flow_cnt, default_flow, tx) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_NATIVE, \ + .pkt_mode = 1, \ + .needs_epib = 1, \ + .psd_size = 64, \ + .mapped_channel_id = ch, \ + .flow_start = flow_base, \ + .flow_num = flow_cnt, \ + .default_flow_id = default_flow, \ + .notdpkt = tx, \ + }, \ + } + +#define PSIL_PDMA_MCASP(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_PDMA_XY, \ + .pdma_acc32 = 1, \ + .pdma_burst = 1, \ + }, \ + } + +#define PSIL_CSI2RX(x) \ + { \ + .thread_id = x, \ + .ep_config = { \ + .ep_type = PSIL_EP_NATIVE, \ + }, \ + } + +/* PSI-L source thread IDs, used for RX (DMA_DEV_TO_MEM) */ +static struct psil_ep am62p_src_ep_map[] = { + /* SAUL */ + PSIL_SAUL(0x7504, 20, 35, 8, 35, 0), + PSIL_SAUL(0x7505, 21, 35, 8, 36, 0), + PSIL_SAUL(0x7506, 22, 43, 8, 43, 0), + PSIL_SAUL(0x7507, 23, 43, 8, 44, 0), + /* PDMA_MAIN0 - SPI0-2 */ + PSIL_PDMA_XY_PKT(0x4300), + PSIL_PDMA_XY_PKT(0x4301), + PSIL_PDMA_XY_PKT(0x4302), + PSIL_PDMA_XY_PKT(0x4303), + PSIL_PDMA_XY_PKT(0x4304), + PSIL_PDMA_XY_PKT(0x4305), + PSIL_PDMA_XY_PKT(0x4306), + PSIL_PDMA_XY_PKT(0x4307), + PSIL_PDMA_XY_PKT(0x4308), + PSIL_PDMA_XY_PKT(0x4309), + PSIL_PDMA_XY_PKT(0x430a), + PSIL_PDMA_XY_PKT(0x430b), + /* PDMA_MAIN1 - UART0-6 */ + PSIL_PDMA_XY_PKT(0x4400), + PSIL_PDMA_XY_PKT(0x4401), + PSIL_PDMA_XY_PKT(0x4402), + PSIL_PDMA_XY_PKT(0x4403), + PSIL_PDMA_XY_PKT(0x4404), + PSIL_PDMA_XY_PKT(0x4405), + PSIL_PDMA_XY_PKT(0x4406), + /* PDMA_MAIN2 - MCASP0-2 */ + PSIL_PDMA_MCASP(0x4500), + PSIL_PDMA_MCASP(0x4501), + PSIL_PDMA_MCASP(0x4502), + /* CPSW3G */ + PSIL_ETHERNET(0x4600, 19, 19, 16), + /* CSI2RX */ + PSIL_CSI2RX(0x5000), + PSIL_CSI2RX(0x5001), + PSIL_CSI2RX(0x5002), + PSIL_CSI2RX(0x5003), + PSIL_CSI2RX(0x5004), + PSIL_CSI2RX(0x5005), + PSIL_CSI2RX(0x5006), + PSIL_CSI2RX(0x5007), + PSIL_CSI2RX(0x5008), + PSIL_CSI2RX(0x5009), + PSIL_CSI2RX(0x500a), + PSIL_CSI2RX(0x500b), + PSIL_CSI2RX(0x500c), + PSIL_CSI2RX(0x500d), + PSIL_CSI2RX(0x500e), + PSIL_CSI2RX(0x500f), + PSIL_CSI2RX(0x5010), + PSIL_CSI2RX(0x5011), + PSIL_CSI2RX(0x5012), + PSIL_CSI2RX(0x5013), + PSIL_CSI2RX(0x5014), + PSIL_CSI2RX(0x5015), + PSIL_CSI2RX(0x5016), + PSIL_CSI2RX(0x5017), + PSIL_CSI2RX(0x5018), + PSIL_CSI2RX(0x5019), + PSIL_CSI2RX(0x501a), + PSIL_CSI2RX(0x501b), + PSIL_CSI2RX(0x501c), + PSIL_CSI2RX(0x501d), + PSIL_CSI2RX(0x501e), + PSIL_CSI2RX(0x501f), + PSIL_CSI2RX(0x5000), + PSIL_CSI2RX(0x5001), + PSIL_CSI2RX(0x5002), + PSIL_CSI2RX(0x5003), + PSIL_CSI2RX(0x5004), + PSIL_CSI2RX(0x5005), + PSIL_CSI2RX(0x5006), + PSIL_CSI2RX(0x5007), + PSIL_CSI2RX(0x5008), + PSIL_CSI2RX(0x5009), + PSIL_CSI2RX(0x500a), + PSIL_CSI2RX(0x500b), + PSIL_CSI2RX(0x500c), + PSIL_CSI2RX(0x500d), + PSIL_CSI2RX(0x500e), + PSIL_CSI2RX(0x500f), + PSIL_CSI2RX(0x5010), + PSIL_CSI2RX(0x5011), + PSIL_CSI2RX(0x5012), + PSIL_CSI2RX(0x5013), + PSIL_CSI2RX(0x5014), + PSIL_CSI2RX(0x5015), + PSIL_CSI2RX(0x5016), + PSIL_CSI2RX(0x5017), + PSIL_CSI2RX(0x5018), + PSIL_CSI2RX(0x5019), + PSIL_CSI2RX(0x501a), + PSIL_CSI2RX(0x501b), + PSIL_CSI2RX(0x501c), + PSIL_CSI2RX(0x501d), + PSIL_CSI2RX(0x501e), + PSIL_CSI2RX(0x501f), + /* CSIRX 1-3 (only for J722S) */ + PSIL_CSI2RX(0x5100), + PSIL_CSI2RX(0x5101), + PSIL_CSI2RX(0x5102), + PSIL_CSI2RX(0x5103), + PSIL_CSI2RX(0x5104), + PSIL_CSI2RX(0x5105), + PSIL_CSI2RX(0x5106), + PSIL_CSI2RX(0x5107), + PSIL_CSI2RX(0x5108), + PSIL_CSI2RX(0x5109), + PSIL_CSI2RX(0x510a), + PSIL_CSI2RX(0x510b), + PSIL_CSI2RX(0x510c), + PSIL_CSI2RX(0x510d), + PSIL_CSI2RX(0x510e), + PSIL_CSI2RX(0x510f), + PSIL_CSI2RX(0x5110), + PSIL_CSI2RX(0x5111), + PSIL_CSI2RX(0x5112), + PSIL_CSI2RX(0x5113), + PSIL_CSI2RX(0x5114), + PSIL_CSI2RX(0x5115), + PSIL_CSI2RX(0x5116), + PSIL_CSI2RX(0x5117), + PSIL_CSI2RX(0x5118), + PSIL_CSI2RX(0x5119), + PSIL_CSI2RX(0x511a), + PSIL_CSI2RX(0x511b), + PSIL_CSI2RX(0x511c), + PSIL_CSI2RX(0x511d), + PSIL_CSI2RX(0x511e), + PSIL_CSI2RX(0x511f), + PSIL_CSI2RX(0x5200), + PSIL_CSI2RX(0x5201), + PSIL_CSI2RX(0x5202), + PSIL_CSI2RX(0x5203), + PSIL_CSI2RX(0x5204), + PSIL_CSI2RX(0x5205), + PSIL_CSI2RX(0x5206), + PSIL_CSI2RX(0x5207), + PSIL_CSI2RX(0x5208), + PSIL_CSI2RX(0x5209), + PSIL_CSI2RX(0x520a), + PSIL_CSI2RX(0x520b), + PSIL_CSI2RX(0x520c), + PSIL_CSI2RX(0x520d), + PSIL_CSI2RX(0x520e), + PSIL_CSI2RX(0x520f), + PSIL_CSI2RX(0x5210), + PSIL_CSI2RX(0x5211), + PSIL_CSI2RX(0x5212), + PSIL_CSI2RX(0x5213), + PSIL_CSI2RX(0x5214), + PSIL_CSI2RX(0x5215), + PSIL_CSI2RX(0x5216), + PSIL_CSI2RX(0x5217), + PSIL_CSI2RX(0x5218), + PSIL_CSI2RX(0x5219), + PSIL_CSI2RX(0x521a), + PSIL_CSI2RX(0x521b), + PSIL_CSI2RX(0x521c), + PSIL_CSI2RX(0x521d), + PSIL_CSI2RX(0x521e), + PSIL_CSI2RX(0x521f), + PSIL_CSI2RX(0x5300), + PSIL_CSI2RX(0x5301), + PSIL_CSI2RX(0x5302), + PSIL_CSI2RX(0x5303), + PSIL_CSI2RX(0x5304), + PSIL_CSI2RX(0x5305), + PSIL_CSI2RX(0x5306), + PSIL_CSI2RX(0x5307), + PSIL_CSI2RX(0x5308), + PSIL_CSI2RX(0x5309), + PSIL_CSI2RX(0x530a), + PSIL_CSI2RX(0x530b), + PSIL_CSI2RX(0x530c), + PSIL_CSI2RX(0x530d), + PSIL_CSI2RX(0x530e), + PSIL_CSI2RX(0x530f), + PSIL_CSI2RX(0x5310), + PSIL_CSI2RX(0x5311), + PSIL_CSI2RX(0x5312), + PSIL_CSI2RX(0x5313), + PSIL_CSI2RX(0x5314), + PSIL_CSI2RX(0x5315), + PSIL_CSI2RX(0x5316), + PSIL_CSI2RX(0x5317), + PSIL_CSI2RX(0x5318), + PSIL_CSI2RX(0x5319), + PSIL_CSI2RX(0x531a), + PSIL_CSI2RX(0x531b), + PSIL_CSI2RX(0x531c), + PSIL_CSI2RX(0x531d), + PSIL_CSI2RX(0x531e), + PSIL_CSI2RX(0x531f), +}; + +/* PSI-L destination thread IDs, used for TX (DMA_MEM_TO_DEV) */ +static struct psil_ep am62p_dst_ep_map[] = { + /* SAUL */ + PSIL_SAUL(0xf500, 27, 83, 8, 83, 1), + PSIL_SAUL(0xf501, 28, 91, 8, 91, 1), + /* PDMA_MAIN0 - SPI0-2 */ + PSIL_PDMA_XY_PKT(0xc300), + PSIL_PDMA_XY_PKT(0xc301), + PSIL_PDMA_XY_PKT(0xc302), + PSIL_PDMA_XY_PKT(0xc303), + PSIL_PDMA_XY_PKT(0xc304), + PSIL_PDMA_XY_PKT(0xc305), + PSIL_PDMA_XY_PKT(0xc306), + PSIL_PDMA_XY_PKT(0xc307), + PSIL_PDMA_XY_PKT(0xc308), + PSIL_PDMA_XY_PKT(0xc309), + PSIL_PDMA_XY_PKT(0xc30a), + PSIL_PDMA_XY_PKT(0xc30b), + /* PDMA_MAIN1 - UART0-6 */ + PSIL_PDMA_XY_PKT(0xc400), + PSIL_PDMA_XY_PKT(0xc401), + PSIL_PDMA_XY_PKT(0xc402), + PSIL_PDMA_XY_PKT(0xc403), + PSIL_PDMA_XY_PKT(0xc404), + PSIL_PDMA_XY_PKT(0xc405), + PSIL_PDMA_XY_PKT(0xc406), + /* PDMA_MAIN2 - MCASP0-2 */ + PSIL_PDMA_MCASP(0xc500), + PSIL_PDMA_MCASP(0xc501), + PSIL_PDMA_MCASP(0xc502), + /* CPSW3G */ + PSIL_ETHERNET(0xc600, 19, 19, 8), + PSIL_ETHERNET(0xc601, 20, 27, 8), + PSIL_ETHERNET(0xc602, 21, 35, 8), + PSIL_ETHERNET(0xc603, 22, 43, 8), + PSIL_ETHERNET(0xc604, 23, 51, 8), + PSIL_ETHERNET(0xc605, 24, 59, 8), + PSIL_ETHERNET(0xc606, 25, 67, 8), + PSIL_ETHERNET(0xc607, 26, 75, 8), +}; + +struct psil_ep_map am62p_ep_map = { + .name = "am62p", + .src = am62p_src_ep_map, + .src_count = ARRAY_SIZE(am62p_src_ep_map), + .dst = am62p_dst_ep_map, + .dst_count = ARRAY_SIZE(am62p_dst_ep_map), +}; diff --git a/drivers/dma/ti/k3-psil-priv.h b/drivers/dma/ti/k3-psil-priv.h index c383723d1c8f..a577be97e344 100644 --- a/drivers/dma/ti/k3-psil-priv.h +++ b/drivers/dma/ti/k3-psil-priv.h @@ -45,5 +45,6 @@ extern struct psil_ep_map j721s2_ep_map; extern struct psil_ep_map am62_ep_map; extern struct psil_ep_map am62a_ep_map; extern struct psil_ep_map j784s4_ep_map; +extern struct psil_ep_map am62p_ep_map; #endif /* K3_PSIL_PRIV_H_ */ diff --git a/drivers/dma/ti/k3-psil.c b/drivers/dma/ti/k3-psil.c index c11389d67a3f..25148d952472 100644 --- a/drivers/dma/ti/k3-psil.c +++ b/drivers/dma/ti/k3-psil.c @@ -26,6 +26,8 @@ static const struct soc_device_attribute k3_soc_devices[] = { { .family = "AM62X", .data = &am62_ep_map }, { .family = "AM62AX", .data = &am62a_ep_map }, { .family = "J784S4", .data = &j784s4_ep_map }, + { .family = "AM62PX", .data = &am62p_ep_map }, + { .family = "J722S", .data = &am62p_ep_map }, { /* sentinel */ } }; diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 30fd2f386f36..6400d06588a2 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -3968,6 +3968,7 @@ static void udma_desc_pre_callback(struct virt_dma_chan *vc, { struct udma_chan *uc = to_udma_chan(&vc->chan); struct udma_desc *d; + u8 status; if (!vd) return; @@ -3977,12 +3978,12 @@ static void udma_desc_pre_callback(struct virt_dma_chan *vc, if (d->metadata_size) udma_fetch_epib(uc, d); - /* Provide residue information for the client */ if (result) { void *desc_vaddr = udma_curr_cppi5_desc_vaddr(d, d->desc_idx); if (cppi5_desc_get_type(desc_vaddr) == CPPI5_INFO0_DESC_TYPE_VAL_HOST) { + /* Provide residue information for the client */ result->residue = d->residue - cppi5_hdesc_get_pktlen(desc_vaddr); if (result->residue) @@ -3991,7 +3992,12 @@ static void udma_desc_pre_callback(struct virt_dma_chan *vc, result->result = DMA_TRANS_NOERROR; } else { result->residue = 0; - result->result = DMA_TRANS_NOERROR; + /* Propagate TR Response errors to the client */ + status = d->hwdesc[0].tr_resp_base->status; + if (status) + result->result = DMA_TRANS_ABORTED; + else + result->result = DMA_TRANS_NOERROR; } } } @@ -4441,6 +4447,8 @@ static const struct soc_device_attribute k3_soc_devices[] = { { .family = "AM62X", .data = &am64_soc_data }, { .family = "AM62AX", .data = &am64_soc_data }, { .family = "J784S4", .data = &j721e_soc_data }, + { .family = "AM62PX", .data = &am64_soc_data }, + { .family = "J722S", .data = &am64_soc_data }, { /* sentinel */ } }; diff --git a/drivers/dma/uniphier-mdmac.c b/drivers/dma/uniphier-mdmac.c index 618839df0748..ad7125f6e2ca 100644 --- a/drivers/dma/uniphier-mdmac.c +++ b/drivers/dma/uniphier-mdmac.c @@ -453,7 +453,7 @@ disable_clk: return ret; } -static int uniphier_mdmac_remove(struct platform_device *pdev) +static void uniphier_mdmac_remove(struct platform_device *pdev) { struct uniphier_mdmac_device *mdev = platform_get_drvdata(pdev); struct dma_chan *chan; @@ -468,16 +468,21 @@ static int uniphier_mdmac_remove(struct platform_device *pdev) */ list_for_each_entry(chan, &mdev->ddev.channels, device_node) { ret = dmaengine_terminate_sync(chan); - if (ret) - return ret; + if (ret) { + /* + * This results in resource leakage and maybe also + * use-after-free errors as e.g. *mdev is kfreed. + */ + dev_alert(&pdev->dev, "Failed to terminate channel %d (%pe)\n", + chan->chan_id, ERR_PTR(ret)); + return; + } uniphier_mdmac_free_chan_resources(chan); } of_dma_controller_free(pdev->dev.of_node); dma_async_device_unregister(&mdev->ddev); clk_disable_unprepare(mdev->clk); - - return 0; } static const struct of_device_id uniphier_mdmac_match[] = { @@ -488,7 +493,7 @@ MODULE_DEVICE_TABLE(of, uniphier_mdmac_match); static struct platform_driver uniphier_mdmac_driver = { .probe = uniphier_mdmac_probe, - .remove = uniphier_mdmac_remove, + .remove_new = uniphier_mdmac_remove, .driver = { .name = "uniphier-mio-dmac", .of_match_table = uniphier_mdmac_match, diff --git a/drivers/dma/uniphier-xdmac.c b/drivers/dma/uniphier-xdmac.c index 3a8ee2b173b5..3ce2dc2ad9de 100644 --- a/drivers/dma/uniphier-xdmac.c +++ b/drivers/dma/uniphier-xdmac.c @@ -563,7 +563,7 @@ out_unregister_dmac: return ret; } -static int uniphier_xdmac_remove(struct platform_device *pdev) +static void uniphier_xdmac_remove(struct platform_device *pdev) { struct uniphier_xdmac_device *xdev = platform_get_drvdata(pdev); struct dma_device *ddev = &xdev->ddev; @@ -579,15 +579,20 @@ static int uniphier_xdmac_remove(struct platform_device *pdev) */ list_for_each_entry(chan, &ddev->channels, device_node) { ret = dmaengine_terminate_sync(chan); - if (ret) - return ret; + if (ret) { + /* + * This results in resource leakage and maybe also + * use-after-free errors as e.g. *xdev is kfreed. + */ + dev_alert(&pdev->dev, "Failed to terminate channel %d (%pe)\n", + chan->chan_id, ERR_PTR(ret)); + return; + } uniphier_xdmac_free_chan_resources(chan); } of_dma_controller_free(pdev->dev.of_node); dma_async_device_unregister(ddev); - - return 0; } static const struct of_device_id uniphier_xdmac_match[] = { @@ -598,7 +603,7 @@ MODULE_DEVICE_TABLE(of, uniphier_xdmac_match); static struct platform_driver uniphier_xdmac_driver = { .probe = uniphier_xdmac_probe, - .remove = uniphier_xdmac_remove, + .remove_new = uniphier_xdmac_remove, .driver = { .name = "uniphier-xdmac", .of_match_table = uniphier_xdmac_match, diff --git a/drivers/dma/xilinx/xdma-regs.h b/drivers/dma/xilinx/xdma-regs.h index e641a5083e14..98f5f6fb9ff9 100644 --- a/drivers/dma/xilinx/xdma-regs.h +++ b/drivers/dma/xilinx/xdma-regs.h @@ -64,9 +64,10 @@ struct xdma_hw_desc { __le64 next_desc; }; -#define XDMA_DESC_SIZE sizeof(struct xdma_hw_desc) -#define XDMA_DESC_BLOCK_SIZE (XDMA_DESC_SIZE * XDMA_DESC_ADJACENT) -#define XDMA_DESC_BLOCK_ALIGN 4096 +#define XDMA_DESC_SIZE sizeof(struct xdma_hw_desc) +#define XDMA_DESC_BLOCK_SIZE (XDMA_DESC_SIZE * XDMA_DESC_ADJACENT) +#define XDMA_DESC_BLOCK_ALIGN 32 +#define XDMA_DESC_BLOCK_BOUNDARY 4096 /* * Channel registers @@ -76,6 +77,7 @@ struct xdma_hw_desc { #define XDMA_CHAN_CONTROL_W1S 0x8 #define XDMA_CHAN_CONTROL_W1C 0xc #define XDMA_CHAN_STATUS 0x40 +#define XDMA_CHAN_STATUS_RC 0x44 #define XDMA_CHAN_COMPLETED_DESC 0x48 #define XDMA_CHAN_ALIGNMENTS 0x4c #define XDMA_CHAN_INTR_ENABLE 0x90 @@ -101,6 +103,7 @@ struct xdma_hw_desc { #define CHAN_CTRL_IE_MAGIC_STOPPED BIT(4) #define CHAN_CTRL_IE_IDLE_STOPPED BIT(6) #define CHAN_CTRL_IE_READ_ERROR GENMASK(13, 9) +#define CHAN_CTRL_IE_WRITE_ERROR GENMASK(18, 14) #define CHAN_CTRL_IE_DESC_ERROR GENMASK(23, 19) #define CHAN_CTRL_NON_INCR_ADDR BIT(25) #define CHAN_CTRL_POLL_MODE_WB BIT(26) @@ -111,8 +114,17 @@ struct xdma_hw_desc { CHAN_CTRL_IE_DESC_ALIGN_MISMATCH | \ CHAN_CTRL_IE_MAGIC_STOPPED | \ CHAN_CTRL_IE_READ_ERROR | \ + CHAN_CTRL_IE_WRITE_ERROR | \ CHAN_CTRL_IE_DESC_ERROR) +#define XDMA_CHAN_STATUS_MASK CHAN_CTRL_START + +#define XDMA_CHAN_ERROR_MASK (CHAN_CTRL_IE_DESC_ALIGN_MISMATCH | \ + CHAN_CTRL_IE_MAGIC_STOPPED | \ + CHAN_CTRL_IE_READ_ERROR | \ + CHAN_CTRL_IE_WRITE_ERROR | \ + CHAN_CTRL_IE_DESC_ERROR) + /* bits of the channel interrupt enable mask */ #define CHAN_IM_DESC_ERROR BIT(19) #define CHAN_IM_READ_ERROR BIT(9) @@ -134,18 +146,6 @@ struct xdma_hw_desc { #define XDMA_SGDMA_DESC_ADJ 0x4088 #define XDMA_SGDMA_DESC_CREDIT 0x408c -/* bits of the SG DMA control register */ -#define XDMA_CTRL_RUN_STOP BIT(0) -#define XDMA_CTRL_IE_DESC_STOPPED BIT(1) -#define XDMA_CTRL_IE_DESC_COMPLETED BIT(2) -#define XDMA_CTRL_IE_DESC_ALIGN_MISMATCH BIT(3) -#define XDMA_CTRL_IE_MAGIC_STOPPED BIT(4) -#define XDMA_CTRL_IE_IDLE_STOPPED BIT(6) -#define XDMA_CTRL_IE_READ_ERROR GENMASK(13, 9) -#define XDMA_CTRL_IE_DESC_ERROR GENMASK(23, 19) -#define XDMA_CTRL_NON_INCR_ADDR BIT(25) -#define XDMA_CTRL_POLL_MODE_WB BIT(26) - /* * interrupt registers */ diff --git a/drivers/dma/xilinx/xdma.c b/drivers/dma/xilinx/xdma.c index 84a88029226f..170017ff2aad 100644 --- a/drivers/dma/xilinx/xdma.c +++ b/drivers/dma/xilinx/xdma.c @@ -78,27 +78,31 @@ struct xdma_chan { * @vdesc: Virtual DMA descriptor * @chan: DMA channel pointer * @dir: Transferring direction of the request - * @dev_addr: Physical address on DMA device side * @desc_blocks: Hardware descriptor blocks * @dblk_num: Number of hardware descriptor blocks * @desc_num: Number of hardware descriptors * @completed_desc_num: Completed hardware descriptors * @cyclic: Cyclic transfer vs. scatter-gather + * @interleaved_dma: Interleaved DMA transfer * @periods: Number of periods in the cyclic transfer * @period_size: Size of a period in bytes in cyclic transfers + * @frames_left: Number of frames left in interleaved DMA transfer + * @error: tx error flag */ struct xdma_desc { struct virt_dma_desc vdesc; struct xdma_chan *chan; enum dma_transfer_direction dir; - u64 dev_addr; struct xdma_desc_block *desc_blocks; u32 dblk_num; u32 desc_num; u32 completed_desc_num; bool cyclic; + bool interleaved_dma; u32 periods; u32 period_size; + u32 frames_left; + bool error; }; #define XDMA_DEV_STATUS_REG_DMA BIT(0) @@ -276,6 +280,7 @@ xdma_alloc_desc(struct xdma_chan *chan, u32 desc_num, bool cyclic) sw_desc->chan = chan; sw_desc->desc_num = desc_num; sw_desc->cyclic = cyclic; + sw_desc->error = false; dblk_num = DIV_ROUND_UP(desc_num, XDMA_DESC_ADJACENT); sw_desc->desc_blocks = kcalloc(dblk_num, sizeof(*sw_desc->desc_blocks), GFP_NOWAIT); @@ -371,6 +376,31 @@ static int xdma_xfer_start(struct xdma_chan *xchan) return ret; xchan->busy = true; + + return 0; +} + +/** + * xdma_xfer_stop - Stop DMA transfer + * @xchan: DMA channel pointer + */ +static int xdma_xfer_stop(struct xdma_chan *xchan) +{ + int ret; + u32 val; + struct xdma_device *xdev = xchan->xdev_hdl; + + /* clear run stop bit to prevent any further auto-triggering */ + ret = regmap_write(xdev->rmap, xchan->base + XDMA_CHAN_CONTROL_W1C, + CHAN_CTRL_RUN_STOP); + if (ret) + return ret; + + /* Clear the channel status register */ + ret = regmap_read(xdev->rmap, xchan->base + XDMA_CHAN_STATUS_RC, &val); + if (ret) + return ret; + return 0; } @@ -476,6 +506,84 @@ static void xdma_issue_pending(struct dma_chan *chan) } /** + * xdma_terminate_all - Terminate all transactions + * @chan: DMA channel pointer + */ +static int xdma_terminate_all(struct dma_chan *chan) +{ + struct xdma_chan *xdma_chan = to_xdma_chan(chan); + struct virt_dma_desc *vd; + unsigned long flags; + LIST_HEAD(head); + + xdma_xfer_stop(xdma_chan); + + spin_lock_irqsave(&xdma_chan->vchan.lock, flags); + + xdma_chan->busy = false; + vd = vchan_next_desc(&xdma_chan->vchan); + if (vd) { + list_del(&vd->node); + dma_cookie_complete(&vd->tx); + vchan_terminate_vdesc(vd); + } + vchan_get_all_descriptors(&xdma_chan->vchan, &head); + list_splice_tail(&head, &xdma_chan->vchan.desc_terminated); + + spin_unlock_irqrestore(&xdma_chan->vchan.lock, flags); + + return 0; +} + +/** + * xdma_synchronize - Synchronize terminated transactions + * @chan: DMA channel pointer + */ +static void xdma_synchronize(struct dma_chan *chan) +{ + struct xdma_chan *xdma_chan = to_xdma_chan(chan); + + vchan_synchronize(&xdma_chan->vchan); +} + +/** + * xdma_fill_descs - Fill hardware descriptors with contiguous memory block addresses + * @sw_desc: tx descriptor state container + * @src_addr: Value for a ->src_addr field of a first descriptor + * @dst_addr: Value for a ->dst_addr field of a first descriptor + * @size: Total size of a contiguous memory block + * @filled_descs_num: Number of filled hardware descriptors for corresponding sw_desc + */ +static inline u32 xdma_fill_descs(struct xdma_desc *sw_desc, u64 src_addr, + u64 dst_addr, u32 size, u32 filled_descs_num) +{ + u32 left = size, len, desc_num = filled_descs_num; + struct xdma_desc_block *dblk; + struct xdma_hw_desc *desc; + + dblk = sw_desc->desc_blocks + (desc_num / XDMA_DESC_ADJACENT); + desc = dblk->virt_addr; + desc += desc_num & XDMA_DESC_ADJACENT_MASK; + do { + len = min_t(u32, left, XDMA_DESC_BLEN_MAX); + /* set hardware descriptor */ + desc->bytes = cpu_to_le32(len); + desc->src_addr = cpu_to_le64(src_addr); + desc->dst_addr = cpu_to_le64(dst_addr); + if (!(++desc_num & XDMA_DESC_ADJACENT_MASK)) + desc = (++dblk)->virt_addr; + else + desc++; + + src_addr += len; + dst_addr += len; + left -= len; + } while (left); + + return desc_num - filled_descs_num; +} + +/** * xdma_prep_device_sg - prepare a descriptor for a DMA transaction * @chan: DMA channel pointer * @sgl: Transfer scatter gather list @@ -491,13 +599,10 @@ xdma_prep_device_sg(struct dma_chan *chan, struct scatterlist *sgl, { struct xdma_chan *xdma_chan = to_xdma_chan(chan); struct dma_async_tx_descriptor *tx_desc; - u32 desc_num = 0, i, len, rest; - struct xdma_desc_block *dblk; - struct xdma_hw_desc *desc; struct xdma_desc *sw_desc; - u64 dev_addr, *src, *dst; + u32 desc_num = 0, i; + u64 addr, dev_addr, *src, *dst; struct scatterlist *sg; - u64 addr; for_each_sg(sgl, sg, sg_len, i) desc_num += DIV_ROUND_UP(sg_dma_len(sg), XDMA_DESC_BLEN_MAX); @@ -506,6 +611,8 @@ xdma_prep_device_sg(struct dma_chan *chan, struct scatterlist *sgl, if (!sw_desc) return NULL; sw_desc->dir = dir; + sw_desc->cyclic = false; + sw_desc->interleaved_dma = false; if (dir == DMA_MEM_TO_DEV) { dev_addr = xdma_chan->cfg.dst_addr; @@ -517,32 +624,11 @@ xdma_prep_device_sg(struct dma_chan *chan, struct scatterlist *sgl, dst = &addr; } - dblk = sw_desc->desc_blocks; - desc = dblk->virt_addr; - desc_num = 1; + desc_num = 0; for_each_sg(sgl, sg, sg_len, i) { addr = sg_dma_address(sg); - rest = sg_dma_len(sg); - - do { - len = min_t(u32, rest, XDMA_DESC_BLEN_MAX); - /* set hardware descriptor */ - desc->bytes = cpu_to_le32(len); - desc->src_addr = cpu_to_le64(*src); - desc->dst_addr = cpu_to_le64(*dst); - - if (!(desc_num & XDMA_DESC_ADJACENT_MASK)) { - dblk++; - desc = dblk->virt_addr; - } else { - desc++; - } - - desc_num++; - dev_addr += len; - addr += len; - rest -= len; - } while (rest); + desc_num += xdma_fill_descs(sw_desc, *src, *dst, sg_dma_len(sg), desc_num); + dev_addr += sg_dma_len(sg); } tx_desc = vchan_tx_prep(&xdma_chan->vchan, &sw_desc->vdesc, flags); @@ -576,9 +662,9 @@ xdma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t address, struct xdma_device *xdev = xdma_chan->xdev_hdl; unsigned int periods = size / period_size; struct dma_async_tx_descriptor *tx_desc; - struct xdma_desc_block *dblk; - struct xdma_hw_desc *desc; struct xdma_desc *sw_desc; + u64 addr, dev_addr, *src, *dst; + u32 desc_num; unsigned int i; /* @@ -602,22 +688,23 @@ xdma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t address, sw_desc->periods = periods; sw_desc->period_size = period_size; sw_desc->dir = dir; + sw_desc->interleaved_dma = false; - dblk = sw_desc->desc_blocks; - desc = dblk->virt_addr; + addr = address; + if (dir == DMA_MEM_TO_DEV) { + dev_addr = xdma_chan->cfg.dst_addr; + src = &addr; + dst = &dev_addr; + } else { + dev_addr = xdma_chan->cfg.src_addr; + src = &dev_addr; + dst = &addr; + } - /* fill hardware descriptor */ + desc_num = 0; for (i = 0; i < periods; i++) { - desc->bytes = cpu_to_le32(period_size); - if (dir == DMA_MEM_TO_DEV) { - desc->src_addr = cpu_to_le64(address + i * period_size); - desc->dst_addr = cpu_to_le64(xdma_chan->cfg.dst_addr); - } else { - desc->src_addr = cpu_to_le64(xdma_chan->cfg.src_addr); - desc->dst_addr = cpu_to_le64(address + i * period_size); - } - - desc++; + desc_num += xdma_fill_descs(sw_desc, *src, *dst, period_size, desc_num); + addr += i * period_size; } tx_desc = vchan_tx_prep(&xdma_chan->vchan, &sw_desc->vdesc, flags); @@ -633,6 +720,57 @@ failed: } /** + * xdma_prep_interleaved_dma - Prepare virtual descriptor for interleaved DMA transfers + * @chan: DMA channel + * @xt: DMA transfer template + * @flags: tx flags + */ +static struct dma_async_tx_descriptor * +xdma_prep_interleaved_dma(struct dma_chan *chan, + struct dma_interleaved_template *xt, + unsigned long flags) +{ + int i; + u32 desc_num = 0, period_size = 0; + struct dma_async_tx_descriptor *tx_desc; + struct xdma_chan *xchan = to_xdma_chan(chan); + struct xdma_desc *sw_desc; + u64 src_addr, dst_addr; + + for (i = 0; i < xt->frame_size; ++i) + desc_num += DIV_ROUND_UP(xt->sgl[i].size, XDMA_DESC_BLEN_MAX); + + sw_desc = xdma_alloc_desc(xchan, desc_num, false); + if (!sw_desc) + return NULL; + sw_desc->dir = xt->dir; + sw_desc->interleaved_dma = true; + sw_desc->cyclic = flags & DMA_PREP_REPEAT; + sw_desc->frames_left = xt->numf; + sw_desc->periods = xt->numf; + + desc_num = 0; + src_addr = xt->src_start; + dst_addr = xt->dst_start; + for (i = 0; i < xt->frame_size; ++i) { + desc_num += xdma_fill_descs(sw_desc, src_addr, dst_addr, xt->sgl[i].size, desc_num); + src_addr += dmaengine_get_src_icg(xt, &xt->sgl[i]) + (xt->src_inc ? + xt->sgl[i].size : 0); + dst_addr += dmaengine_get_dst_icg(xt, &xt->sgl[i]) + (xt->dst_inc ? + xt->sgl[i].size : 0); + period_size += xt->sgl[i].size; + } + sw_desc->period_size = period_size; + + tx_desc = vchan_tx_prep(&xchan->vchan, &sw_desc->vdesc, flags); + if (tx_desc) + return tx_desc; + + xdma_free_desc(&sw_desc->vdesc); + return NULL; +} + +/** * xdma_device_config - Configure the DMA channel * @chan: DMA channel * @cfg: channel configuration @@ -677,9 +815,8 @@ static int xdma_alloc_chan_resources(struct dma_chan *chan) return -EINVAL; } - xdma_chan->desc_pool = dma_pool_create(dma_chan_name(chan), - dev, XDMA_DESC_BLOCK_SIZE, - XDMA_DESC_BLOCK_ALIGN, 0); + xdma_chan->desc_pool = dma_pool_create(dma_chan_name(chan), dev, XDMA_DESC_BLOCK_SIZE, + XDMA_DESC_BLOCK_ALIGN, XDMA_DESC_BLOCK_BOUNDARY); if (!xdma_chan->desc_pool) { xdma_err(xdev, "unable to allocate descriptor pool"); return -ENOMEM; @@ -706,20 +843,20 @@ static enum dma_status xdma_tx_status(struct dma_chan *chan, dma_cookie_t cookie spin_lock_irqsave(&xdma_chan->vchan.lock, flags); vd = vchan_find_desc(&xdma_chan->vchan, cookie); - if (vd) - desc = to_xdma_desc(vd); - if (!desc || !desc->cyclic) { - spin_unlock_irqrestore(&xdma_chan->vchan.lock, flags); - return ret; - } - - period_idx = desc->completed_desc_num % desc->periods; - residue = (desc->periods - period_idx) * desc->period_size; + if (!vd) + goto out; + desc = to_xdma_desc(vd); + if (desc->error) { + ret = DMA_ERROR; + } else if (desc->cyclic) { + period_idx = desc->completed_desc_num % desc->periods; + residue = (desc->periods - period_idx) * desc->period_size; + dma_set_residue(state, residue); + } +out: spin_unlock_irqrestore(&xdma_chan->vchan.lock, flags); - dma_set_residue(state, residue); - return ret; } @@ -732,11 +869,12 @@ static irqreturn_t xdma_channel_isr(int irq, void *dev_id) { struct xdma_chan *xchan = dev_id; u32 complete_desc_num = 0; - struct xdma_device *xdev; - struct virt_dma_desc *vd; + struct xdma_device *xdev = xchan->xdev_hdl; + struct virt_dma_desc *vd, *next_vd; struct xdma_desc *desc; int ret; u32 st; + bool repeat_tx; spin_lock(&xchan->vchan.lock); @@ -745,45 +883,76 @@ static irqreturn_t xdma_channel_isr(int irq, void *dev_id) if (!vd) goto out; - xchan->busy = false; + /* Clear-on-read the status register */ + ret = regmap_read(xdev->rmap, xchan->base + XDMA_CHAN_STATUS_RC, &st); + if (ret) + goto out; + desc = to_xdma_desc(vd); - xdev = xchan->xdev_hdl; + + st &= XDMA_CHAN_STATUS_MASK; + if ((st & XDMA_CHAN_ERROR_MASK) || + !(st & (CHAN_CTRL_IE_DESC_COMPLETED | CHAN_CTRL_IE_DESC_STOPPED))) { + desc->error = true; + xdma_err(xdev, "channel error, status register value: 0x%x", st); + goto out; + } ret = regmap_read(xdev->rmap, xchan->base + XDMA_CHAN_COMPLETED_DESC, &complete_desc_num); if (ret) goto out; - desc->completed_desc_num += complete_desc_num; + if (desc->interleaved_dma) { + xchan->busy = false; + desc->completed_desc_num += complete_desc_num; + if (complete_desc_num == XDMA_DESC_BLOCK_NUM * XDMA_DESC_ADJACENT) { + xdma_xfer_start(xchan); + goto out; + } - if (desc->cyclic) { - ret = regmap_read(xdev->rmap, xchan->base + XDMA_CHAN_STATUS, - &st); - if (ret) + /* last desc of any frame */ + desc->frames_left--; + if (desc->frames_left) + goto out; + + /* last desc of the last frame */ + repeat_tx = vd->tx.flags & DMA_PREP_REPEAT; + next_vd = list_first_entry_or_null(&vd->node, struct virt_dma_desc, node); + if (next_vd) + repeat_tx = repeat_tx && !(next_vd->tx.flags & DMA_PREP_LOAD_EOT); + if (repeat_tx) { + desc->frames_left = desc->periods; + desc->completed_desc_num = 0; + vchan_cyclic_callback(vd); + } else { + list_del(&vd->node); + vchan_cookie_complete(vd); + } + /* start (or continue) the tx of a first desc on the vc.desc_issued list, if any */ + xdma_xfer_start(xchan); + } else if (!desc->cyclic) { + xchan->busy = false; + desc->completed_desc_num += complete_desc_num; + + /* if all data blocks are transferred, remove and complete the request */ + if (desc->completed_desc_num == desc->desc_num) { + list_del(&vd->node); + vchan_cookie_complete(vd); goto out; + } - regmap_write(xdev->rmap, xchan->base + XDMA_CHAN_STATUS, st); + if (desc->completed_desc_num > desc->desc_num || + complete_desc_num != XDMA_DESC_BLOCK_NUM * XDMA_DESC_ADJACENT) + goto out; + /* transfer the rest of data */ + xdma_xfer_start(xchan); + } else { + desc->completed_desc_num = complete_desc_num; vchan_cyclic_callback(vd); - goto out; - } - - /* - * if all data blocks are transferred, remove and complete the request - */ - if (desc->completed_desc_num == desc->desc_num) { - list_del(&vd->node); - vchan_cookie_complete(vd); - goto out; } - if (desc->completed_desc_num > desc->desc_num || - complete_desc_num != XDMA_DESC_BLOCK_NUM * XDMA_DESC_ADJACENT) - goto out; - - /* transfer the rest of data (SG only) */ - xdma_xfer_start(xchan); - out: spin_unlock(&xchan->vchan.lock); return IRQ_HANDLED; @@ -1080,6 +1249,9 @@ static int xdma_probe(struct platform_device *pdev) dma_cap_set(DMA_SLAVE, xdev->dma_dev.cap_mask); dma_cap_set(DMA_PRIVATE, xdev->dma_dev.cap_mask); dma_cap_set(DMA_CYCLIC, xdev->dma_dev.cap_mask); + dma_cap_set(DMA_INTERLEAVE, xdev->dma_dev.cap_mask); + dma_cap_set(DMA_REPEAT, xdev->dma_dev.cap_mask); + dma_cap_set(DMA_LOAD_EOT, xdev->dma_dev.cap_mask); xdev->dma_dev.dev = &pdev->dev; xdev->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; @@ -1089,10 +1261,13 @@ static int xdma_probe(struct platform_device *pdev) xdev->dma_dev.device_prep_slave_sg = xdma_prep_device_sg; xdev->dma_dev.device_config = xdma_device_config; xdev->dma_dev.device_issue_pending = xdma_issue_pending; + xdev->dma_dev.device_terminate_all = xdma_terminate_all; + xdev->dma_dev.device_synchronize = xdma_synchronize; xdev->dma_dev.filter.map = pdata->device_map; xdev->dma_dev.filter.mapcnt = pdata->device_map_cnt; xdev->dma_dev.filter.fn = xdma_filter_fn; xdev->dma_dev.device_prep_dma_cyclic = xdma_prep_dma_cyclic; + xdev->dma_dev.device_prep_interleaved_dma = xdma_prep_interleaved_dma; ret = dma_async_device_register(&xdev->dma_dev); if (ret) { diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c index 69587d85a7cd..b82815e64d24 100644 --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -309,7 +309,7 @@ static ssize_t xilinx_dpdma_debugfs_desc_done_irq_read(char *buf) out_str_len = strlen(XILINX_DPDMA_DEBUGFS_UINT16_MAX_STR); out_str_len = min_t(size_t, XILINX_DPDMA_DEBUGFS_READ_MAX_SIZE, - out_str_len); + out_str_len + 1); snprintf(buf, out_str_len, "%d", dpdma_debugfs.xilinx_dpdma_irq_done_count); diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index 1eca8cc271f8..5152bd1b0daf 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -29,8 +29,6 @@ static u32 dpll_pin_xa_id; WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED)) #define ASSERT_DPLL_NOT_REGISTERED(d) \ WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED)) -#define ASSERT_PIN_REGISTERED(p) \ - WARN_ON_ONCE(!xa_get_mark(&dpll_pin_xa, (p)->id, DPLL_REGISTERED)) struct dpll_device_registration { struct list_head list; @@ -425,6 +423,53 @@ void dpll_device_unregister(struct dpll_device *dpll, } EXPORT_SYMBOL_GPL(dpll_device_unregister); +static void dpll_pin_prop_free(struct dpll_pin_properties *prop) +{ + kfree(prop->package_label); + kfree(prop->panel_label); + kfree(prop->board_label); + kfree(prop->freq_supported); +} + +static int dpll_pin_prop_dup(const struct dpll_pin_properties *src, + struct dpll_pin_properties *dst) +{ + memcpy(dst, src, sizeof(*dst)); + if (src->freq_supported && src->freq_supported_num) { + size_t freq_size = src->freq_supported_num * + sizeof(*src->freq_supported); + dst->freq_supported = kmemdup(src->freq_supported, + freq_size, GFP_KERNEL); + if (!src->freq_supported) + return -ENOMEM; + } + if (src->board_label) { + dst->board_label = kstrdup(src->board_label, GFP_KERNEL); + if (!dst->board_label) + goto err_board_label; + } + if (src->panel_label) { + dst->panel_label = kstrdup(src->panel_label, GFP_KERNEL); + if (!dst->panel_label) + goto err_panel_label; + } + if (src->package_label) { + dst->package_label = kstrdup(src->package_label, GFP_KERNEL); + if (!dst->package_label) + goto err_package_label; + } + + return 0; + +err_package_label: + kfree(dst->panel_label); +err_panel_label: + kfree(dst->board_label); +err_board_label: + kfree(dst->freq_supported); + return -ENOMEM; +} + static struct dpll_pin * dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module, const struct dpll_pin_properties *prop) @@ -441,20 +486,24 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module, if (WARN_ON(prop->type < DPLL_PIN_TYPE_MUX || prop->type > DPLL_PIN_TYPE_MAX)) { ret = -EINVAL; - goto err; + goto err_pin_prop; } - pin->prop = prop; + ret = dpll_pin_prop_dup(prop, &pin->prop); + if (ret) + goto err_pin_prop; refcount_set(&pin->refcount, 1); xa_init_flags(&pin->dpll_refs, XA_FLAGS_ALLOC); xa_init_flags(&pin->parent_refs, XA_FLAGS_ALLOC); ret = xa_alloc_cyclic(&dpll_pin_xa, &pin->id, pin, xa_limit_32b, &dpll_pin_xa_id, GFP_KERNEL); if (ret) - goto err; + goto err_xa_alloc; return pin; -err: +err_xa_alloc: xa_destroy(&pin->dpll_refs); xa_destroy(&pin->parent_refs); + dpll_pin_prop_free(&pin->prop); +err_pin_prop: kfree(pin); return ERR_PTR(ret); } @@ -514,6 +563,7 @@ void dpll_pin_put(struct dpll_pin *pin) xa_destroy(&pin->dpll_refs); xa_destroy(&pin->parent_refs); xa_erase(&dpll_pin_xa, pin->id); + dpll_pin_prop_free(&pin->prop); kfree(pin); } mutex_unlock(&dpll_lock); @@ -564,8 +614,6 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin, WARN_ON(!ops->state_on_dpll_get) || WARN_ON(!ops->direction_get)) return -EINVAL; - if (ASSERT_DPLL_REGISTERED(dpll)) - return -EINVAL; mutex_lock(&dpll_lock); if (WARN_ON(!(dpll->module == pin->module && @@ -636,15 +684,13 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin, unsigned long i, stop; int ret; - if (WARN_ON(parent->prop->type != DPLL_PIN_TYPE_MUX)) + if (WARN_ON(parent->prop.type != DPLL_PIN_TYPE_MUX)) return -EINVAL; if (WARN_ON(!ops) || WARN_ON(!ops->state_on_pin_get) || WARN_ON(!ops->direction_get)) return -EINVAL; - if (ASSERT_PIN_REGISTERED(parent)) - return -EINVAL; mutex_lock(&dpll_lock); ret = dpll_xa_ref_pin_add(&pin->parent_refs, parent, ops, priv); diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h index 5585873c5c1b..717f715015c7 100644 --- a/drivers/dpll/dpll_core.h +++ b/drivers/dpll/dpll_core.h @@ -44,7 +44,7 @@ struct dpll_device { * @module: module of creator * @dpll_refs: hold referencees to dplls pin was registered with * @parent_refs: hold references to parent pins pin was registered with - * @prop: pointer to pin properties given by registerer + * @prop: pin properties copied from the registerer * @rclk_dev_name: holds name of device when pin can recover clock from it * @refcount: refcount **/ @@ -55,7 +55,7 @@ struct dpll_pin { struct module *module; struct xarray dpll_refs; struct xarray parent_refs; - const struct dpll_pin_properties *prop; + struct dpll_pin_properties prop; refcount_t refcount; }; diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index 3370dbddb86b..314bb3775465 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -303,17 +303,17 @@ dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin, if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY, sizeof(freq), &freq, DPLL_A_PIN_PAD)) return -EMSGSIZE; - for (fs = 0; fs < pin->prop->freq_supported_num; fs++) { + for (fs = 0; fs < pin->prop.freq_supported_num; fs++) { nest = nla_nest_start(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED); if (!nest) return -EMSGSIZE; - freq = pin->prop->freq_supported[fs].min; + freq = pin->prop.freq_supported[fs].min; if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN, sizeof(freq), &freq, DPLL_A_PIN_PAD)) { nla_nest_cancel(msg, nest); return -EMSGSIZE; } - freq = pin->prop->freq_supported[fs].max; + freq = pin->prop.freq_supported[fs].max; if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX, sizeof(freq), &freq, DPLL_A_PIN_PAD)) { nla_nest_cancel(msg, nest); @@ -329,9 +329,9 @@ static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq) { int fs; - for (fs = 0; fs < pin->prop->freq_supported_num; fs++) - if (freq >= pin->prop->freq_supported[fs].min && - freq <= pin->prop->freq_supported[fs].max) + for (fs = 0; fs < pin->prop.freq_supported_num; fs++) + if (freq >= pin->prop.freq_supported[fs].min && + freq <= pin->prop.freq_supported[fs].max) return true; return false; } @@ -421,7 +421,7 @@ static int dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin, struct netlink_ext_ack *extack) { - const struct dpll_pin_properties *prop = pin->prop; + const struct dpll_pin_properties *prop = &pin->prop; struct dpll_pin_ref *ref; int ret; @@ -553,6 +553,24 @@ __dpll_device_change_ntf(struct dpll_device *dpll) return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll); } +static bool dpll_pin_available(struct dpll_pin *pin) +{ + struct dpll_pin_ref *par_ref; + unsigned long i; + + if (!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED)) + return false; + xa_for_each(&pin->parent_refs, i, par_ref) + if (xa_get_mark(&dpll_pin_xa, par_ref->pin->id, + DPLL_REGISTERED)) + return true; + xa_for_each(&pin->dpll_refs, i, par_ref) + if (xa_get_mark(&dpll_device_xa, par_ref->dpll->id, + DPLL_REGISTERED)) + return true; + return false; +} + /** * dpll_device_change_ntf - notify that the dpll device has been changed * @dpll: registered dpll pointer @@ -579,7 +597,7 @@ dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin) int ret = -ENOMEM; void *hdr; - if (WARN_ON(!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED))) + if (!dpll_pin_available(pin)) return -ENODEV; msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); @@ -717,7 +735,7 @@ dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx, int ret; if (!(DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE & - pin->prop->capabilities)) { + pin->prop.capabilities)) { NL_SET_ERR_MSG(extack, "state changing is not allowed"); return -EOPNOTSUPP; } @@ -753,7 +771,7 @@ dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin, int ret; if (!(DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE & - pin->prop->capabilities)) { + pin->prop.capabilities)) { NL_SET_ERR_MSG(extack, "state changing is not allowed"); return -EOPNOTSUPP; } @@ -780,7 +798,7 @@ dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin, int ret; if (!(DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE & - pin->prop->capabilities)) { + pin->prop.capabilities)) { NL_SET_ERR_MSG(extack, "prio changing is not allowed"); return -EOPNOTSUPP; } @@ -808,7 +826,7 @@ dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device *dpll, int ret; if (!(DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE & - pin->prop->capabilities)) { + pin->prop.capabilities)) { NL_SET_ERR_MSG(extack, "direction changing is not allowed"); return -EOPNOTSUPP; } @@ -838,8 +856,8 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr, int ret; phase_adj = nla_get_s32(phase_adj_attr); - if (phase_adj > pin->prop->phase_range.max || - phase_adj < pin->prop->phase_range.min) { + if (phase_adj > pin->prop.phase_range.max || + phase_adj < pin->prop.phase_range.min) { NL_SET_ERR_MSG_ATTR(extack, phase_adj_attr, "phase adjust value not supported"); return -EINVAL; @@ -1023,7 +1041,7 @@ dpll_pin_find(u64 clock_id, struct nlattr *mod_name_attr, unsigned long i; xa_for_each_marked(&dpll_pin_xa, i, pin, DPLL_REGISTERED) { - prop = pin->prop; + prop = &pin->prop; cid_match = clock_id ? pin->clock_id == clock_id : true; mod_match = mod_name_attr && module_name(pin->module) ? !nla_strcmp(mod_name_attr, @@ -1130,6 +1148,10 @@ int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info) } pin = dpll_pin_find_from_nlattr(info); if (!IS_ERR(pin)) { + if (!dpll_pin_available(pin)) { + nlmsg_free(msg); + return -ENODEV; + } ret = dpll_msg_add_pin_handle(msg, pin); if (ret) { nlmsg_free(msg); @@ -1179,6 +1201,8 @@ int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) xa_for_each_marked_start(&dpll_pin_xa, i, pin, DPLL_REGISTERED, ctx->idx) { + if (!dpll_pin_available(pin)) + continue; hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, &dpll_nl_family, NLM_F_MULTI, @@ -1441,7 +1465,8 @@ int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb, } info->user_ptr[0] = xa_load(&dpll_pin_xa, nla_get_u32(info->attrs[DPLL_A_PIN_ID])); - if (!info->user_ptr[0]) { + if (!info->user_ptr[0] || + !dpll_pin_available(info->user_ptr[0])) { NL_SET_ERR_MSG(info->extack, "pin not found"); ret = -ENODEV; goto unlock_dev; diff --git a/drivers/edac/edac_device.h b/drivers/edac/edac_device.h index 3f44e6b9d387..7db22a4c83ef 100644 --- a/drivers/edac/edac_device.h +++ b/drivers/edac/edac_device.h @@ -176,7 +176,7 @@ struct edac_device_ctl_info { struct edac_dev_sysfs_attribute *sysfs_attributes; /* pointer to main 'edac' subsys in sysfs */ - struct bus_type *edac_subsys; + const struct bus_type *edac_subsys; /* the internal state of this controller instance */ int op_state; diff --git a/drivers/edac/edac_device_sysfs.c b/drivers/edac/edac_device_sysfs.c index 010c26be5846..237a542e045a 100644 --- a/drivers/edac/edac_device_sysfs.c +++ b/drivers/edac/edac_device_sysfs.c @@ -229,7 +229,7 @@ static struct kobj_type ktype_device_ctrl = { int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev) { struct device *dev_root; - struct bus_type *edac_subsys; + const struct bus_type *edac_subsys; int err = -ENODEV; edac_dbg(1, "\n"); diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c index 32a931d0cb71..1c9f62382666 100644 --- a/drivers/edac/edac_module.c +++ b/drivers/edac/edac_module.c @@ -67,7 +67,7 @@ char *edac_op_state_to_string(int opstate) * sysfs object: /sys/devices/system/edac * need to export to other files */ -static struct bus_type edac_subsys = { +static const struct bus_type edac_subsys = { .name = "edac", .dev_name = "edac", }; @@ -90,7 +90,7 @@ static void edac_subsys_exit(void) } /* return pointer to the 'edac' node in sysfs */ -struct bus_type *edac_get_sysfs_subsys(void) +const struct bus_type *edac_get_sysfs_subsys(void) { return &edac_subsys; } diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c index 901d4cd3ca38..7b44afcf48db 100644 --- a/drivers/edac/edac_pci_sysfs.c +++ b/drivers/edac/edac_pci_sysfs.c @@ -338,7 +338,7 @@ static struct kobj_type ktype_edac_pci_main_kobj = { static int edac_pci_main_kobj_setup(void) { int err = -ENODEV; - struct bus_type *edac_subsys; + const struct bus_type *edac_subsys; struct device *dev_root; edac_dbg(0, "\n"); diff --git a/drivers/edac/versal_edac.c b/drivers/edac/versal_edac.c index 8625de20fc71..62caf454b567 100644 --- a/drivers/edac/versal_edac.c +++ b/drivers/edac/versal_edac.c @@ -1005,7 +1005,7 @@ static int mc_probe(struct platform_device *pdev) goto free_edac_mc; } - rc = xlnx_register_event(PM_NOTIFY_CB, EVENT_ERROR_PMC_ERR1, + rc = xlnx_register_event(PM_NOTIFY_CB, VERSAL_EVENT_ERROR_PMC_ERR1, XPM_EVENT_ERROR_MASK_DDRMC_CR | XPM_EVENT_ERROR_MASK_DDRMC_NCR | XPM_EVENT_ERROR_MASK_NOC_CR | XPM_EVENT_ERROR_MASK_NOC_NCR, false, err_callback, mci); @@ -1042,7 +1042,7 @@ static int mc_remove(struct platform_device *pdev) debugfs_remove_recursive(priv->debugfs); #endif - xlnx_unregister_event(PM_NOTIFY_CB, EVENT_ERROR_PMC_ERR1, + xlnx_unregister_event(PM_NOTIFY_CB, VERSAL_EVENT_ERROR_PMC_ERR1, XPM_EVENT_ERROR_MASK_DDRMC_CR | XPM_EVENT_ERROR_MASK_NOC_CR | XPM_EVENT_ERROR_MASK_NOC_NCR | diff --git a/drivers/extcon/extcon-qcom-spmi-misc.c b/drivers/extcon/extcon-qcom-spmi-misc.c index f72e90ceca53..53de581a393a 100644 --- a/drivers/extcon/extcon-qcom-spmi-misc.c +++ b/drivers/extcon/extcon-qcom-spmi-misc.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * extcon-qcom-spmi-misc.c - Qualcomm USB extcon driver to support USB ID * and VBUS detection based on extcon-usb-gpio.c. * diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c index 4d08c2123e59..2eab341de6b7 100644 --- a/drivers/extcon/extcon-usbc-tusb320.c +++ b/drivers/extcon/extcon-usbc-tusb320.c @@ -17,6 +17,7 @@ #include <linux/usb/typec.h> #include <linux/usb/typec_altmode.h> #include <linux/usb/role.h> +#include <linux/irq.h> #define TUSB320_REG8 0x8 #define TUSB320_REG8_CURRENT_MODE_ADVERTISE GENMASK(7, 6) @@ -515,6 +516,8 @@ static int tusb320_probe(struct i2c_client *client) const void *match_data; unsigned int revision; int ret; + u32 irq_trigger_type = IRQF_TRIGGER_FALLING; + struct irq_data *irq_d; priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -568,9 +571,13 @@ static int tusb320_probe(struct i2c_client *client) */ tusb320_state_update_handler(priv, true); + irq_d = irq_get_irq_data(client->irq); + if (irq_d) + irq_trigger_type = irqd_get_trigger_type(irq_d); + ret = devm_request_threaded_irq(priv->dev, client->irq, NULL, tusb320_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + IRQF_ONESHOT | irq_trigger_type, client->name, priv); if (ret) tusb320_typec_remove(priv); diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 6f7a60d2ed91..e7f55c021e56 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -1280,8 +1280,6 @@ int extcon_dev_register(struct extcon_dev *edev) edev->id = ret; - dev_set_name(&edev->dev, "extcon%d", edev->id); - ret = extcon_alloc_cables(edev); if (ret < 0) goto err_alloc_cables; @@ -1310,6 +1308,7 @@ int extcon_dev_register(struct extcon_dev *edev) RAW_INIT_NOTIFIER_HEAD(&edev->nh_all); dev_set_drvdata(&edev->dev, edev); + dev_set_name(&edev->dev, "extcon%d", edev->id); edev->state = 0; ret = device_register(&edev->dev); diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 6ac5ff20a2fe..8aaa7fcb2630 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -429,7 +429,23 @@ static void bm_work(struct work_struct *work) */ card->bm_generation = generation; - if (root_device == NULL) { + if (card->gap_count == 0) { + /* + * If self IDs have inconsistent gap counts, do a + * bus reset ASAP. The config rom read might never + * complete, so don't wait for it. However, still + * send a PHY configuration packet prior to the + * bus reset. The PHY configuration packet might + * fail, but 1394-2008 8.4.5.2 explicitly permits + * it in this case, so it should be safe to try. + */ + new_root_id = local_id; + /* + * We must always send a bus reset if the gap count + * is inconsistent, so bypass the 5-reset limit. + */ + card->bm_retries = 0; + } else if (root_device == NULL) { /* * Either link_on is false, or we failed to read the * config rom. In either case, pick another root. diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 0547253d16fe..7d3346b3a2bf 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -118,10 +118,9 @@ static int textual_leaf_to_string(const u32 *block, char *buf, size_t size) * @buf: where to put the string * @size: size of @buf, in bytes * - * The string is taken from a minimal ASCII text descriptor leaf after - * the immediate entry with @key. The string is zero-terminated. - * An overlong string is silently truncated such that it and the - * zero byte fit into @size. + * The string is taken from a minimal ASCII text descriptor leaf just after the entry with the + * @key. The string is zero-terminated. An overlong string is silently truncated such that it + * and the zero byte fit into @size. * * Returns strlen(buf) or a negative error code. */ @@ -368,8 +367,17 @@ static ssize_t show_text_leaf(struct device *dev, for (i = 0; i < ARRAY_SIZE(directories) && !!directories[i]; ++i) { int result = fw_csr_string(directories[i], attr->key, buf, bufsize); // Detected. - if (result >= 0) + if (result >= 0) { ret = result; + } else if (i == 0 && attr->key == CSR_VENDOR) { + // Sony DVMC-DA1 has configuration ROM such that the descriptor leaf entry + // in the root directory follows to the directory entry for vendor ID + // instead of the immediate value for vendor ID. + result = fw_csr_string(directories[i], CSR_DIRECTORY | attr->key, buf, + bufsize); + if (result >= 0) + ret = result; + } } if (ret >= 0) { diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 6146b2927d5c..f2556a8e9401 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -107,12 +107,12 @@ struct ffa_drv_info { struct work_struct notif_pcpu_work; struct work_struct irq_work; struct xarray partition_info; - unsigned int partition_count; DECLARE_HASHTABLE(notifier_hash, ilog2(FFA_MAX_NOTIFICATIONS)); struct mutex notify_lock; /* lock to protect notifier hashtable */ }; static struct ffa_drv_info *drv_info; +static void ffa_partitions_cleanup(void); /* * The driver must be able to support all the versions from the earliest @@ -733,6 +733,11 @@ static void __do_sched_recv_cb(u16 part_id, u16 vcpu, bool is_per_vcpu) void *cb_data; partition = xa_load(&drv_info->partition_info, part_id); + if (!partition) { + pr_err("%s: Invalid partition ID 0x%x\n", __func__, part_id); + return; + } + read_lock(&partition->rw_lock); callback = partition->callback; cb_data = partition->cb_data; @@ -915,6 +920,11 @@ static int ffa_sched_recv_cb_update(u16 part_id, ffa_sched_recv_cb callback, return -EOPNOTSUPP; partition = xa_load(&drv_info->partition_info, part_id); + if (!partition) { + pr_err("%s: Invalid partition ID 0x%x\n", __func__, part_id); + return -EINVAL; + } + write_lock(&partition->rw_lock); cb_valid = !!partition->callback; @@ -1186,9 +1196,9 @@ void ffa_device_match_uuid(struct ffa_device *ffa_dev, const uuid_t *uuid) kfree(pbuf); } -static void ffa_setup_partitions(void) +static int ffa_setup_partitions(void) { - int count, idx; + int count, idx, ret; uuid_t uuid; struct ffa_device *ffa_dev; struct ffa_dev_part_info *info; @@ -1197,7 +1207,7 @@ static void ffa_setup_partitions(void) count = ffa_partition_probe(&uuid_null, &pbuf); if (count <= 0) { pr_info("%s: No partitions found, error %d\n", __func__, count); - return; + return -EINVAL; } xa_init(&drv_info->partition_info); @@ -1226,40 +1236,53 @@ static void ffa_setup_partitions(void) ffa_device_unregister(ffa_dev); continue; } - xa_store(&drv_info->partition_info, tpbuf->id, info, GFP_KERNEL); + rwlock_init(&info->rw_lock); + ret = xa_insert(&drv_info->partition_info, tpbuf->id, + info, GFP_KERNEL); + if (ret) { + pr_err("%s: failed to save partition ID 0x%x - ret:%d\n", + __func__, tpbuf->id, ret); + ffa_device_unregister(ffa_dev); + kfree(info); + } } - drv_info->partition_count = count; kfree(pbuf); /* Allocate for the host */ info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return; - xa_store(&drv_info->partition_info, drv_info->vm_id, info, GFP_KERNEL); - drv_info->partition_count++; + if (!info) { + pr_err("%s: failed to alloc Host partition ID 0x%x. Abort.\n", + __func__, drv_info->vm_id); + /* Already registered devices are freed on bus_exit */ + ffa_partitions_cleanup(); + return -ENOMEM; + } + + rwlock_init(&info->rw_lock); + ret = xa_insert(&drv_info->partition_info, drv_info->vm_id, + info, GFP_KERNEL); + if (ret) { + pr_err("%s: failed to save Host partition ID 0x%x - ret:%d. Abort.\n", + __func__, drv_info->vm_id, ret); + kfree(info); + /* Already registered devices are freed on bus_exit */ + ffa_partitions_cleanup(); + } + + return ret; } static void ffa_partitions_cleanup(void) { - struct ffa_dev_part_info **info; - int idx, count = drv_info->partition_count; - - if (!count) - return; - - info = kcalloc(count, sizeof(*info), GFP_KERNEL); - if (!info) - return; - - xa_extract(&drv_info->partition_info, (void **)info, 0, VM_ID_MASK, - count, XA_PRESENT); + struct ffa_dev_part_info *info; + unsigned long idx; - for (idx = 0; idx < count; idx++) - kfree(info[idx]); - kfree(info); + xa_for_each(&drv_info->partition_info, idx, info) { + xa_erase(&drv_info->partition_info, idx); + kfree(info); + } - drv_info->partition_count = 0; xa_destroy(&drv_info->partition_info); } @@ -1508,7 +1531,11 @@ static int __init ffa_init(void) ffa_notifications_setup(); - ffa_setup_partitions(); + ret = ffa_setup_partitions(); + if (ret) { + pr_err("failed to setup partitions\n"); + goto cleanup_notifs; + } ret = ffa_sched_recv_cb_update(drv_info->vm_id, ffa_self_notif_handle, drv_info, true); @@ -1516,6 +1543,9 @@ static int __init ffa_init(void) pr_info("Failed to register driver sched callback %d\n", ret); return 0; + +cleanup_notifs: + ffa_notifications_cleanup(); free_pages: if (drv_info->tx_buffer) free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE); @@ -1535,7 +1565,6 @@ static void __exit ffa_exit(void) ffa_rxtx_unmap(drv_info->vm_id); free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE); free_pages_exact(drv_info->rx_buffer, RXTX_BUFFER_SIZE); - xa_destroy(&drv_info->partition_info); kfree(drv_info); arm_ffa_bus_exit(); } diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index c0644558042a..e2050adbf85c 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -13,7 +13,7 @@ #include "notify.h" /* Updated only after ALL the mandatory features for that version are merged */ -#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20001 +#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000 enum scmi_clock_protocol_cmd { CLOCK_ATTRIBUTES = 0x3, @@ -954,8 +954,7 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) scmi_clock_describe_rates_get(ph, clkid, clk); } - if (PROTOCOL_REV_MAJOR(version) >= 0x2 && - PROTOCOL_REV_MINOR(version) >= 0x1) { + if (PROTOCOL_REV_MAJOR(version) >= 0x3) { cinfo->clock_config_set = scmi_clock_config_set_v2; cinfo->clock_config_get = scmi_clock_config_get_v2; } else { diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index c46dc5215af7..00b165d1f502 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -314,6 +314,7 @@ void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem, void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem); bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, struct scmi_xfer *xfer); +bool shmem_channel_free(struct scmi_shared_mem __iomem *shmem); /* declarations for message passing transports */ struct scmi_msg_payld; diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index a9f70e6e58ac..3ea64b22cf0d 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2834,7 +2834,7 @@ clear_ida: return ret; } -static int scmi_remove(struct platform_device *pdev) +static void scmi_remove(struct platform_device *pdev) { int id; struct scmi_info *info = platform_get_drvdata(pdev); @@ -2868,8 +2868,6 @@ static int scmi_remove(struct platform_device *pdev) scmi_cleanup_txrx_channels(info); ida_free(&scmi_id, info->id); - - return 0; } static ssize_t protocol_version_show(struct device *dev, @@ -2947,7 +2945,7 @@ static struct platform_driver scmi_driver = { .dev_groups = versions_groups, }, .probe = scmi_probe, - .remove = scmi_remove, + .remove_new = scmi_remove, }; /** diff --git a/drivers/firmware/arm_scmi/mailbox.c b/drivers/firmware/arm_scmi/mailbox.c index 19246ed1f01f..b8d470417e8f 100644 --- a/drivers/firmware/arm_scmi/mailbox.c +++ b/drivers/firmware/arm_scmi/mailbox.c @@ -45,6 +45,20 @@ static void rx_callback(struct mbox_client *cl, void *m) { struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl); + /* + * An A2P IRQ is NOT valid when received while the platform still has + * the ownership of the channel, because the platform at first releases + * the SMT channel and then sends the completion interrupt. + * + * This addresses a possible race condition in which a spurious IRQ from + * a previous timed-out reply which arrived late could be wrongly + * associated with the next pending transaction. + */ + if (cl->knows_txdone && !shmem_channel_free(smbox->shmem)) { + dev_warn(smbox->cinfo->dev, "Ignoring spurious A2P IRQ !\n"); + return; + } + scmi_rx_callback(smbox->cinfo, shmem_read_header(smbox->shmem), NULL); } diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 8ea2a7b3d35d..211e8e0aef2c 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -350,8 +350,8 @@ process_response_opp(struct scmi_opp *opp, unsigned int loop_idx, } static inline void -process_response_opp_v4(struct perf_dom_info *dom, struct scmi_opp *opp, - unsigned int loop_idx, +process_response_opp_v4(struct device *dev, struct perf_dom_info *dom, + struct scmi_opp *opp, unsigned int loop_idx, const struct scmi_msg_resp_perf_describe_levels_v4 *r) { opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val); @@ -362,10 +362,23 @@ process_response_opp_v4(struct perf_dom_info *dom, struct scmi_opp *opp, /* Note that PERF v4 reports always five 32-bit words */ opp->indicative_freq = le32_to_cpu(r->opp[loop_idx].indicative_freq); if (dom->level_indexing_mode) { + int ret; + opp->level_index = le32_to_cpu(r->opp[loop_idx].level_index); - xa_store(&dom->opps_by_idx, opp->level_index, opp, GFP_KERNEL); - xa_store(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL); + ret = xa_insert(&dom->opps_by_idx, opp->level_index, opp, + GFP_KERNEL); + if (ret) + dev_warn(dev, + "Failed to add opps_by_idx at %d - ret:%d\n", + opp->level_index, ret); + + ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL); + if (ret) + dev_warn(dev, + "Failed to add opps_by_lvl at %d - ret:%d\n", + opp->perf, ret); + hash_add(dom->opps_by_freq, &opp->hash, opp->indicative_freq); } } @@ -382,7 +395,7 @@ iter_perf_levels_process_response(const struct scmi_protocol_handle *ph, if (PROTOCOL_REV_MAJOR(p->version) <= 0x3) process_response_opp(opp, st->loop_idx, response); else - process_response_opp_v4(p->perf_dom, opp, st->loop_idx, + process_response_opp_v4(ph->dev, p->perf_dom, opp, st->loop_idx, response); p->perf_dom->opp_count++; diff --git a/drivers/firmware/arm_scmi/raw_mode.c b/drivers/firmware/arm_scmi/raw_mode.c index 0493aa3c12bf..350573518503 100644 --- a/drivers/firmware/arm_scmi/raw_mode.c +++ b/drivers/firmware/arm_scmi/raw_mode.c @@ -1111,7 +1111,6 @@ static int scmi_raw_mode_setup(struct scmi_raw_mode_info *raw, int i; for (i = 0; i < num_chans; i++) { - void *xret; struct scmi_raw_queue *q; q = scmi_raw_queue_init(raw); @@ -1120,13 +1119,12 @@ static int scmi_raw_mode_setup(struct scmi_raw_mode_info *raw, goto err_xa; } - xret = xa_store(&raw->chans_q, channels[i], q, + ret = xa_insert(&raw->chans_q, channels[i], q, GFP_KERNEL); - if (xa_err(xret)) { + if (ret) { dev_err(dev, "Fail to allocate Raw queue 0x%02X\n", channels[i]); - ret = xa_err(xret); goto err_xa; } } @@ -1322,6 +1320,12 @@ void scmi_raw_message_report(void *r, struct scmi_xfer *xfer, dev = raw->handle->dev; q = scmi_raw_queue_select(raw, idx, SCMI_XFER_IS_CHAN_SET(xfer) ? chan_id : 0); + if (!q) { + dev_warn(dev, + "RAW[%d] - NO queue for chan 0x%X. Dropping report.\n", + idx, chan_id); + return; + } /* * Grab the msg_q_lock upfront to avoid a possible race between diff --git a/drivers/firmware/arm_scmi/shmem.c b/drivers/firmware/arm_scmi/shmem.c index 87b4f4d35f06..8bf495bcad09 100644 --- a/drivers/firmware/arm_scmi/shmem.c +++ b/drivers/firmware/arm_scmi/shmem.c @@ -10,7 +10,7 @@ #include <linux/processor.h> #include <linux/types.h> -#include <asm-generic/bug.h> +#include <linux/bug.h> #include "common.h" @@ -122,3 +122,9 @@ bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR | SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); } + +bool shmem_channel_free(struct scmi_shared_mem __iomem *shmem) +{ + return (ioread32(&shmem->channel_status) & + SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); +} diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index 3f123f592cb4..94a6b4e667de 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -863,7 +863,7 @@ static void scpi_free_channels(void *data) mbox_free_channel(info->channels[i].chan); } -static int scpi_remove(struct platform_device *pdev) +static void scpi_remove(struct platform_device *pdev) { int i; struct scpi_drvinfo *info = platform_get_drvdata(pdev); @@ -874,8 +874,6 @@ static int scpi_remove(struct platform_device *pdev) kfree(info->dvfs[i]->opps); kfree(info->dvfs[i]); } - - return 0; } #define MAX_SCPI_XFERS 10 @@ -1048,7 +1046,7 @@ static struct platform_driver scpi_driver = { .dev_groups = versions_groups, }, .probe = scpi_probe, - .remove = scpi_remove, + .remove_new = scpi_remove, }; module_platform_driver(scpi_driver); diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c index 83f5bb57fa4c..83092d93f36a 100644 --- a/drivers/firmware/efi/arm-runtime.c +++ b/drivers/firmware/efi/arm-runtime.c @@ -107,7 +107,7 @@ static int __init arm_enable_runtime_services(void) efi_memory_desc_t *md; for_each_efi_memory_desc(md) { - int md_size = md->num_pages << EFI_PAGE_SHIFT; + u64 md_size = md->num_pages << EFI_PAGE_SHIFT; struct resource *res; if (!(md->attribute & EFI_MEMORY_SP)) diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index 35c37f667781..9b3884ff81e6 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -523,6 +523,17 @@ static void cper_print_tstamp(const char *pfx, } } +struct ignore_section { + guid_t guid; + const char *name; +}; + +static const struct ignore_section ignore_sections[] = { + { .guid = CPER_SEC_CXL_GEN_MEDIA_GUID, .name = "CXL General Media Event" }, + { .guid = CPER_SEC_CXL_DRAM_GUID, .name = "CXL DRAM Event" }, + { .guid = CPER_SEC_CXL_MEM_MODULE_GUID, .name = "CXL Memory Module Event" }, +}; + static void cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata, int sec_no) @@ -543,6 +554,14 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); snprintf(newpfx, sizeof(newpfx), "%s ", pfx); + + for (int i = 0; i < ARRAY_SIZE(ignore_sections); i++) { + if (guid_equal(sec_type, &ignore_sections[i].guid)) { + printk("%ssection_type: %s\n", newpfx, ignore_sections[i].name); + return; + } + } + if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) { struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata); diff --git a/drivers/firmware/efi/efi-init.c b/drivers/firmware/efi/efi-init.c index d4987d013080..a00e07b853f2 100644 --- a/drivers/firmware/efi/efi-init.c +++ b/drivers/firmware/efi/efi-init.c @@ -144,15 +144,6 @@ static __init int is_usable_memory(efi_memory_desc_t *md) case EFI_CONVENTIONAL_MEMORY: case EFI_PERSISTENT_MEMORY: /* - * Special purpose memory is 'soft reserved', which means it - * is set aside initially, but can be hotplugged back in or - * be assigned to the dax driver after boot. - */ - if (efi_soft_reserve_enabled() && - (md->attribute & EFI_MEMORY_SP)) - return false; - - /* * According to the spec, these regions are no longer reserved * after calling ExitBootServices(). However, we can only use * them as System RAM if they can be mapped writeback cacheable. @@ -196,6 +187,16 @@ static __init void reserve_regions(void) size = npages << PAGE_SHIFT; if (is_memory(md)) { + /* + * Special purpose memory is 'soft reserved', which + * means it is set aside initially. Don't add a memblock + * for it now so that it can be hotplugged back in or + * be assigned to the dax driver after boot. + */ + if (efi_soft_reserve_enabled() && + (md->attribute & EFI_MEMORY_SP)) + continue; + early_init_dt_add_memory_arch(paddr, size); if (!is_usable_memory(md)) diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 06964a3c130f..73f4810f6db3 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -28,7 +28,7 @@ cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \ -DEFI_HAVE_MEMCHR -DEFI_HAVE_STRRCHR \ -DEFI_HAVE_STRCMP -fno-builtin -fpic \ $(call cc-option,-mno-single-pic-base) -cflags-$(CONFIG_RISCV) += -fpic -DNO_ALTERNATIVE +cflags-$(CONFIG_RISCV) += -fpic -DNO_ALTERNATIVE -mno-relax cflags-$(CONFIG_LOONGARCH) += -fpie cflags-$(CONFIG_EFI_PARAMS_FROM_FDT) += -I$(srctree)/scripts/dtc/libfdt @@ -143,7 +143,7 @@ STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS # exist. STUBCOPY_FLAGS-$(CONFIG_RISCV) += --prefix-alloc-sections=.init \ --prefix-symbols=__efistub_ -STUBCOPY_RELOC-$(CONFIG_RISCV) := R_RISCV_HI20 +STUBCOPY_RELOC-$(CONFIG_RISCV) := -E R_RISCV_HI20\|R_RISCV_$(BITS)\|R_RISCV_RELAX # For LoongArch, keep all the symbols in .init section and make sure that no # absolute symbols references exist. diff --git a/drivers/firmware/efi/libstub/alignedmem.c b/drivers/firmware/efi/libstub/alignedmem.c index 6b83c492c3b8..31928bd87e0f 100644 --- a/drivers/firmware/efi/libstub/alignedmem.c +++ b/drivers/firmware/efi/libstub/alignedmem.c @@ -14,6 +14,7 @@ * @max: the address that the last allocated memory page shall not * exceed * @align: minimum alignment of the base of the allocation + * @memory_type: the type of memory to allocate * * Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according * to @align, which should be >= EFI_ALLOC_ALIGN. The last allocated page will diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 212687c30d79..c04b82ea40f2 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -956,7 +956,8 @@ efi_status_t efi_get_random_bytes(unsigned long size, u8 *out); efi_status_t efi_random_alloc(unsigned long size, unsigned long align, unsigned long *addr, unsigned long random_seed, - int memory_type, unsigned long alloc_limit); + int memory_type, unsigned long alloc_min, + unsigned long alloc_max); efi_status_t efi_random_get_seed(void); diff --git a/drivers/firmware/efi/libstub/kaslr.c b/drivers/firmware/efi/libstub/kaslr.c index 62d63f7a2645..1a9808012abd 100644 --- a/drivers/firmware/efi/libstub/kaslr.c +++ b/drivers/firmware/efi/libstub/kaslr.c @@ -119,7 +119,7 @@ efi_status_t efi_kaslr_relocate_kernel(unsigned long *image_addr, */ status = efi_random_alloc(*reserve_size, min_kimg_align, reserve_addr, phys_seed, - EFI_LOADER_CODE, EFI_ALLOC_LIMIT); + EFI_LOADER_CODE, 0, EFI_ALLOC_LIMIT); if (status != EFI_SUCCESS) efi_warn("efi_random_alloc() failed: 0x%lx\n", status); } else { diff --git a/drivers/firmware/efi/libstub/randomalloc.c b/drivers/firmware/efi/libstub/randomalloc.c index 674a064b8f7a..4e96a855fdf4 100644 --- a/drivers/firmware/efi/libstub/randomalloc.c +++ b/drivers/firmware/efi/libstub/randomalloc.c @@ -17,7 +17,7 @@ static unsigned long get_entry_num_slots(efi_memory_desc_t *md, unsigned long size, unsigned long align_shift, - u64 alloc_limit) + u64 alloc_min, u64 alloc_max) { unsigned long align = 1UL << align_shift; u64 first_slot, last_slot, region_end; @@ -30,11 +30,11 @@ static unsigned long get_entry_num_slots(efi_memory_desc_t *md, return 0; region_end = min(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - 1, - alloc_limit); + alloc_max); if (region_end < size) return 0; - first_slot = round_up(md->phys_addr, align); + first_slot = round_up(max(md->phys_addr, alloc_min), align); last_slot = round_down(region_end - size + 1, align); if (first_slot > last_slot) @@ -56,7 +56,8 @@ efi_status_t efi_random_alloc(unsigned long size, unsigned long *addr, unsigned long random_seed, int memory_type, - unsigned long alloc_limit) + unsigned long alloc_min, + unsigned long alloc_max) { unsigned long total_slots = 0, target_slot; unsigned long total_mirrored_slots = 0; @@ -78,7 +79,8 @@ efi_status_t efi_random_alloc(unsigned long size, efi_memory_desc_t *md = (void *)map->map + map_offset; unsigned long slots; - slots = get_entry_num_slots(md, size, ilog2(align), alloc_limit); + slots = get_entry_num_slots(md, size, ilog2(align), alloc_min, + alloc_max); MD_NUM_SLOTS(md) = slots; total_slots += slots; if (md->attribute & EFI_MEMORY_MORE_RELIABLE) diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 0d510c9a06a4..99429bc4b0c7 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -223,8 +223,8 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params) } } -void efi_adjust_memory_range_protection(unsigned long start, - unsigned long size) +efi_status_t efi_adjust_memory_range_protection(unsigned long start, + unsigned long size) { efi_status_t status; efi_gcd_memory_space_desc_t desc; @@ -236,13 +236,17 @@ void efi_adjust_memory_range_protection(unsigned long start, rounded_end = roundup(start + size, EFI_PAGE_SIZE); if (memattr != NULL) { - efi_call_proto(memattr, clear_memory_attributes, rounded_start, - rounded_end - rounded_start, EFI_MEMORY_XP); - return; + status = efi_call_proto(memattr, clear_memory_attributes, + rounded_start, + rounded_end - rounded_start, + EFI_MEMORY_XP); + if (status != EFI_SUCCESS) + efi_warn("Failed to clear EFI_MEMORY_XP attribute\n"); + return status; } if (efi_dxe_table == NULL) - return; + return EFI_SUCCESS; /* * Don't modify memory region attributes, they are @@ -255,7 +259,7 @@ void efi_adjust_memory_range_protection(unsigned long start, status = efi_dxe_call(get_memory_space_descriptor, start, &desc); if (status != EFI_SUCCESS) - return; + break; next = desc.base_address + desc.length; @@ -280,8 +284,10 @@ void efi_adjust_memory_range_protection(unsigned long start, unprotect_start, unprotect_start + unprotect_size, status); + break; } } + return EFI_SUCCESS; } static void setup_unaccepted_memory(void) @@ -793,6 +799,7 @@ static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry) status = efi_random_alloc(alloc_size, CONFIG_PHYSICAL_ALIGN, &addr, seed[0], EFI_LOADER_CODE, + LOAD_PHYSICAL_ADDR, EFI_X86_KERNEL_ALLOC_LIMIT); if (status != EFI_SUCCESS) return status; @@ -805,9 +812,7 @@ static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry) *kernel_entry = addr + entry; - efi_adjust_memory_range_protection(addr, kernel_total_size); - - return EFI_SUCCESS; + return efi_adjust_memory_range_protection(addr, kernel_total_size); } static void __noreturn enter_kernel(unsigned long kernel_addr, diff --git a/drivers/firmware/efi/libstub/x86-stub.h b/drivers/firmware/efi/libstub/x86-stub.h index 37c5a36b9d8c..1c20e99a6494 100644 --- a/drivers/firmware/efi/libstub/x86-stub.h +++ b/drivers/firmware/efi/libstub/x86-stub.h @@ -5,8 +5,8 @@ extern void trampoline_32bit_src(void *, bool); extern const u16 trampoline_ljmp_imm_offset; -void efi_adjust_memory_range_protection(unsigned long start, - unsigned long size); +efi_status_t efi_adjust_memory_range_protection(unsigned long start, + unsigned long size); #ifdef CONFIG_X86_64 efi_status_t efi_setup_5level_paging(void); diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c index bdb17eac0cb4..1ceace956758 100644 --- a/drivers/firmware/efi/libstub/zboot.c +++ b/drivers/firmware/efi/libstub/zboot.c @@ -119,7 +119,7 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) } status = efi_random_alloc(alloc_size, min_kimg_align, &image_base, - seed, EFI_LOADER_CODE, EFI_ALLOC_LIMIT); + seed, EFI_LOADER_CODE, 0, EFI_ALLOC_LIMIT); if (status != EFI_SUCCESS) { efi_err("Failed to allocate memory\n"); goto free_cmdline; diff --git a/drivers/firmware/efi/riscv-runtime.c b/drivers/firmware/efi/riscv-runtime.c index 09525fb5c240..01f0f90ea418 100644 --- a/drivers/firmware/efi/riscv-runtime.c +++ b/drivers/firmware/efi/riscv-runtime.c @@ -85,7 +85,7 @@ static int __init riscv_enable_runtime_services(void) efi_memory_desc_t *md; for_each_efi_memory_desc(md) { - int md_size = md->num_pages << EFI_PAGE_SHIFT; + u64 md_size = md->num_pages << EFI_PAGE_SHIFT; struct resource *res; if (!(md->attribute & EFI_MEMORY_SP)) diff --git a/drivers/firmware/imx/imx-dsp.c b/drivers/firmware/imx/imx-dsp.c index a48a58e0c61f..01c8ef14eaec 100644 --- a/drivers/firmware/imx/imx-dsp.c +++ b/drivers/firmware/imx/imx-dsp.c @@ -160,7 +160,7 @@ static int imx_dsp_probe(struct platform_device *pdev) return 0; } -static int imx_dsp_remove(struct platform_device *pdev) +static void imx_dsp_remove(struct platform_device *pdev) { struct imx_dsp_chan *dsp_chan; struct imx_dsp_ipc *dsp_ipc; @@ -173,8 +173,6 @@ static int imx_dsp_remove(struct platform_device *pdev) mbox_free_channel(dsp_chan->ch); kfree(dsp_chan->name); } - - return 0; } static struct platform_driver imx_dsp_driver = { @@ -182,7 +180,7 @@ static struct platform_driver imx_dsp_driver = { .name = "imx-dsp", }, .probe = imx_dsp_probe, - .remove = imx_dsp_remove, + .remove_new = imx_dsp_remove, }; builtin_platform_driver(imx_dsp_driver); diff --git a/drivers/firmware/mtk-adsp-ipc.c b/drivers/firmware/mtk-adsp-ipc.c index 85e94ddc7204..a762302978de 100644 --- a/drivers/firmware/mtk-adsp-ipc.c +++ b/drivers/firmware/mtk-adsp-ipc.c @@ -116,7 +116,7 @@ static int mtk_adsp_ipc_probe(struct platform_device *pdev) return 0; } -static int mtk_adsp_ipc_remove(struct platform_device *pdev) +static void mtk_adsp_ipc_remove(struct platform_device *pdev) { struct mtk_adsp_ipc *adsp_ipc = dev_get_drvdata(&pdev->dev); struct mtk_adsp_chan *adsp_chan; @@ -126,8 +126,6 @@ static int mtk_adsp_ipc_remove(struct platform_device *pdev) adsp_chan = &adsp_ipc->chans[i]; mbox_free_channel(adsp_chan->ch); } - - return 0; } static struct platform_driver mtk_adsp_ipc_driver = { @@ -135,7 +133,7 @@ static struct platform_driver mtk_adsp_ipc_driver = { .name = "mtk-adsp-ipc", }, .probe = mtk_adsp_ipc_probe, - .remove = mtk_adsp_ipc_remove, + .remove_new = mtk_adsp_ipc_remove, }; builtin_platform_driver(mtk_adsp_ipc_driver); diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 1448f61173b3..03da9a4354f8 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -731,7 +731,7 @@ err_sel: return err; } -static int fw_cfg_sysfs_remove(struct platform_device *pdev) +static void fw_cfg_sysfs_remove(struct platform_device *pdev) { pr_debug("fw_cfg: unloading.\n"); fw_cfg_sysfs_cache_cleanup(); @@ -739,7 +739,6 @@ static int fw_cfg_sysfs_remove(struct platform_device *pdev) fw_cfg_io_cleanup(); fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset); fw_cfg_kobj_cleanup(fw_cfg_sel_ko); - return 0; } static const struct of_device_id fw_cfg_sysfs_mmio_match[] = { @@ -758,7 +757,7 @@ MODULE_DEVICE_TABLE(acpi, fw_cfg_sysfs_acpi_match); static struct platform_driver fw_cfg_sysfs_driver = { .probe = fw_cfg_sysfs_probe, - .remove = fw_cfg_sysfs_remove, + .remove_new = fw_cfg_sysfs_remove, .driver = { .name = "fw_cfg", .of_match_table = fw_cfg_sysfs_mmio_match, diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c index 4cd290a60fba..322aada20f74 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c @@ -317,7 +317,7 @@ static void rpi_firmware_shutdown(struct platform_device *pdev) rpi_firmware_property(fw, RPI_FIRMWARE_NOTIFY_REBOOT, NULL, 0); } -static int rpi_firmware_remove(struct platform_device *pdev) +static void rpi_firmware_remove(struct platform_device *pdev) { struct rpi_firmware *fw = platform_get_drvdata(pdev); @@ -327,8 +327,6 @@ static int rpi_firmware_remove(struct platform_device *pdev) rpi_clk = NULL; rpi_firmware_put(fw); - - return 0; } static const struct of_device_id rpi_firmware_of_match[] = { @@ -406,7 +404,7 @@ static struct platform_driver rpi_firmware_driver = { }, .probe = rpi_firmware_probe, .shutdown = rpi_firmware_shutdown, - .remove = rpi_firmware_remove, + .remove_new = rpi_firmware_remove, }; module_platform_driver(rpi_firmware_driver); diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index 4f7a7abada48..e20cee9c2d32 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -793,17 +793,16 @@ static int stratix10_rsu_probe(struct platform_device *pdev) return ret; } -static int stratix10_rsu_remove(struct platform_device *pdev) +static void stratix10_rsu_remove(struct platform_device *pdev) { struct stratix10_rsu_priv *priv = platform_get_drvdata(pdev); stratix10_svc_free_channel(priv->chan); - return 0; } static struct platform_driver stratix10_rsu_driver = { .probe = stratix10_rsu_probe, - .remove = stratix10_rsu_remove, + .remove_new = stratix10_rsu_remove, .driver = { .name = "stratix10-rsu", .dev_groups = rsu_groups, diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index c693da60e9a9..528f37417aea 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -1251,7 +1251,7 @@ err_destroy_pool: return ret; } -static int stratix10_svc_drv_remove(struct platform_device *pdev) +static void stratix10_svc_drv_remove(struct platform_device *pdev) { struct stratix10_svc *svc = dev_get_drvdata(&pdev->dev); struct stratix10_svc_controller *ctrl = platform_get_drvdata(pdev); @@ -1267,13 +1267,11 @@ static int stratix10_svc_drv_remove(struct platform_device *pdev) if (ctrl->genpool) gen_pool_destroy(ctrl->genpool); list_del(&ctrl->node); - - return 0; } static struct platform_driver stratix10_svc_driver = { .probe = stratix10_svc_drv_probe, - .remove = stratix10_svc_drv_remove, + .remove_new = stratix10_svc_drv_remove, .driver = { .name = "stratix10-svc", .of_match_table = stratix10_svc_drv_match, diff --git a/drivers/firmware/sysfb.c b/drivers/firmware/sysfb.c index 19706bd2642a..3c197db42c9d 100644 --- a/drivers/firmware/sysfb.c +++ b/drivers/firmware/sysfb.c @@ -71,7 +71,7 @@ EXPORT_SYMBOL_GPL(sysfb_disable); static __init int sysfb_init(void) { - const struct screen_info *si = &screen_info; + struct screen_info *si = &screen_info; struct simplefb_platform_data mode; const char *name; bool compatible; @@ -119,18 +119,6 @@ static __init int sysfb_init(void) if (ret) goto err; - /* - * The firmware framebuffer is now maintained by the created - * device. Disable screen_info after we've consumed it. Prevents - * invalid access during kexec reboots. - * - * TODO: Vgacon still relies on the global screen_info. Make - * vgacon work with the platform device, so we can clear - * the screen_info unconditionally. - */ - if (strcmp(name, "platform-framebuffer")) - screen_info.orig_video_isVGA = 0; - goto unlock_mutex; err: platform_device_put(pd); @@ -140,4 +128,4 @@ unlock_mutex: } /* must execute after PCI subsystem for EFI quirks */ -subsys_initcall_sync(sysfb_init); +device_initcall(sysfb_init); diff --git a/drivers/firmware/turris-mox-rwtm.c b/drivers/firmware/turris-mox-rwtm.c index 2de0fb139ce1..31d962cdd6eb 100644 --- a/drivers/firmware/turris-mox-rwtm.c +++ b/drivers/firmware/turris-mox-rwtm.c @@ -554,7 +554,7 @@ put_kobj: return ret; } -static int turris_mox_rwtm_remove(struct platform_device *pdev) +static void turris_mox_rwtm_remove(struct platform_device *pdev) { struct mox_rwtm *rwtm = platform_get_drvdata(pdev); @@ -562,8 +562,6 @@ static int turris_mox_rwtm_remove(struct platform_device *pdev) sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs); kobject_put(rwtm_to_kobj(rwtm)); mbox_free_channel(rwtm->mbox); - - return 0; } static const struct of_device_id turris_mox_rwtm_match[] = { @@ -576,7 +574,7 @@ MODULE_DEVICE_TABLE(of, turris_mox_rwtm_match); static struct platform_driver turris_mox_rwtm_driver = { .probe = turris_mox_rwtm_probe, - .remove = turris_mox_rwtm_remove, + .remove_new = turris_mox_rwtm_remove, .driver = { .name = DRIVER_NAME, .of_match_table = turris_mox_rwtm_match, diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index b0d22d4455d9..79789f0563f6 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -92,6 +92,8 @@ static int zynqmp_pm_ret_code(u32 ret_status) return 0; case XST_PM_NO_FEATURE: return -ENOTSUPP; + case XST_PM_INVALID_VERSION: + return -EOPNOTSUPP; case XST_PM_NO_ACCESS: return -EACCES; case XST_PM_ABORT_SUSPEND: @@ -101,13 +103,13 @@ static int zynqmp_pm_ret_code(u32 ret_status) case XST_PM_INTERNAL: case XST_PM_CONFLICT: case XST_PM_INVALID_NODE: + case XST_PM_INVALID_CRC: default: return -EINVAL; } } -static noinline int do_fw_call_fail(u64 arg0, u64 arg1, u64 arg2, - u32 *ret_payload) +static noinline int do_fw_call_fail(u32 *ret_payload, u32 num_args, ...) { return -ENODEV; } @@ -116,25 +118,35 @@ static noinline int do_fw_call_fail(u64 arg0, u64 arg1, u64 arg2, * PM function call wrapper * Invoke do_fw_call_smc or do_fw_call_hvc, depending on the configuration */ -static int (*do_fw_call)(u64, u64, u64, u32 *ret_payload) = do_fw_call_fail; +static int (*do_fw_call)(u32 *ret_payload, u32, ...) = do_fw_call_fail; /** * do_fw_call_smc() - Call system-level platform management layer (SMC) - * @arg0: Argument 0 to SMC call - * @arg1: Argument 1 to SMC call - * @arg2: Argument 2 to SMC call + * @num_args: Number of variable arguments should be <= 8 * @ret_payload: Returned value array * * Invoke platform management function via SMC call (no hypervisor present). * * Return: Returns status, either success or error+reason */ -static noinline int do_fw_call_smc(u64 arg0, u64 arg1, u64 arg2, - u32 *ret_payload) +static noinline int do_fw_call_smc(u32 *ret_payload, u32 num_args, ...) { struct arm_smccc_res res; + u64 args[8] = {0}; + va_list arg_list; + u8 i; - arm_smccc_smc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res); + if (num_args > 8) + return -EINVAL; + + va_start(arg_list, num_args); + + for (i = 0; i < num_args; i++) + args[i] = va_arg(arg_list, u64); + + va_end(arg_list); + + arm_smccc_smc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res); if (ret_payload) { ret_payload[0] = lower_32_bits(res.a0); @@ -148,9 +160,7 @@ static noinline int do_fw_call_smc(u64 arg0, u64 arg1, u64 arg2, /** * do_fw_call_hvc() - Call system-level platform management layer (HVC) - * @arg0: Argument 0 to HVC call - * @arg1: Argument 1 to HVC call - * @arg2: Argument 2 to HVC call + * @num_args: Number of variable arguments should be <= 8 * @ret_payload: Returned value array * * Invoke platform management function via HVC @@ -159,12 +169,24 @@ static noinline int do_fw_call_smc(u64 arg0, u64 arg1, u64 arg2, * * Return: Returns status, either success or error+reason */ -static noinline int do_fw_call_hvc(u64 arg0, u64 arg1, u64 arg2, - u32 *ret_payload) +static noinline int do_fw_call_hvc(u32 *ret_payload, u32 num_args, ...) { struct arm_smccc_res res; + u64 args[8] = {0}; + va_list arg_list; + u8 i; + + if (num_args > 8) + return -EINVAL; + + va_start(arg_list, num_args); + + for (i = 0; i < num_args; i++) + args[i] = va_arg(arg_list, u64); + + va_end(arg_list); - arm_smccc_hvc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res); + arm_smccc_hvc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res); if (ret_payload) { ret_payload[0] = lower_32_bits(res.a0); @@ -180,11 +202,31 @@ static int __do_feature_check_call(const u32 api_id, u32 *ret_payload) { int ret; u64 smc_arg[2]; + u32 module_id; + u32 feature_check_api_id; - smc_arg[0] = PM_SIP_SVC | PM_FEATURE_CHECK; - smc_arg[1] = api_id; + module_id = FIELD_GET(MODULE_ID_MASK, api_id); - ret = do_fw_call(smc_arg[0], smc_arg[1], 0, ret_payload); + /* + * Feature check of APIs belonging to PM, XSEM, and TF-A are handled by calling + * PM_FEATURE_CHECK API. For other modules, call PM_API_FEATURES API. + */ + if (module_id == PM_MODULE_ID || module_id == XSEM_MODULE_ID || module_id == TF_A_MODULE_ID) + feature_check_api_id = PM_FEATURE_CHECK; + else + feature_check_api_id = PM_API_FEATURES; + + /* + * Feature check of TF-A APIs is done in the TF-A layer and it expects for + * MODULE_ID_MASK bits of SMC's arg[0] to be the same as PM_MODULE_ID. + */ + if (module_id == TF_A_MODULE_ID) + module_id = PM_MODULE_ID; + + smc_arg[0] = PM_SIP_SVC | FIELD_PREP(MODULE_ID_MASK, module_id) | feature_check_api_id; + smc_arg[1] = (api_id & API_ID_MASK); + + ret = do_fw_call(ret_payload, 2, smc_arg[0], smc_arg[1]); if (ret) ret = -EOPNOTSUPP; else @@ -295,11 +337,8 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_is_function_supported); * zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer * caller function depending on the configuration * @pm_api_id: Requested PM-API call - * @arg0: Argument 0 to requested PM-API call - * @arg1: Argument 1 to requested PM-API call - * @arg2: Argument 2 to requested PM-API call - * @arg3: Argument 3 to requested PM-API call * @ret_payload: Returned value array + * @num_args: Number of arguments to requested PM-API call * * Invoke platform management function for SMC or HVC call, depending on * configuration. @@ -316,26 +355,38 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_is_function_supported); * * Return: Returns status, either success or error+reason */ -int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1, - u32 arg2, u32 arg3, u32 *ret_payload) +int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...) { /* * Added SIP service call Function Identifier * Make sure to stay in x0 register */ - u64 smc_arg[4]; - int ret; + u64 smc_arg[8]; + int ret, i; + va_list arg_list; + u32 args[14] = {0}; + + if (num_args > 14) + return -EINVAL; + + va_start(arg_list, num_args); /* Check if feature is supported or not */ ret = zynqmp_pm_feature(pm_api_id); if (ret < 0) return ret; + for (i = 0; i < num_args; i++) + args[i] = va_arg(arg_list, u32); + + va_end(arg_list); + smc_arg[0] = PM_SIP_SVC | pm_api_id; - smc_arg[1] = ((u64)arg1 << 32) | arg0; - smc_arg[2] = ((u64)arg3 << 32) | arg2; + for (i = 0; i < 7; i++) + smc_arg[i + 1] = ((u64)args[(i * 2) + 1] << 32) | args[i * 2]; - return do_fw_call(smc_arg[0], smc_arg[1], smc_arg[2], ret_payload); + return do_fw_call(ret_payload, 8, smc_arg[0], smc_arg[1], smc_arg[2], smc_arg[3], + smc_arg[4], smc_arg[5], smc_arg[6], smc_arg[7]); } static u32 pm_api_version; @@ -347,14 +398,12 @@ int zynqmp_pm_register_sgi(u32 sgi_num, u32 reset) { int ret; - ret = zynqmp_pm_invoke_fn(TF_A_PM_REGISTER_SGI, sgi_num, reset, 0, 0, - NULL); - if (!ret) + ret = zynqmp_pm_invoke_fn(TF_A_PM_REGISTER_SGI, NULL, 2, sgi_num, reset); + if (ret != -EOPNOTSUPP && !ret) return ret; /* try old implementation as fallback strategy if above fails */ - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_REGISTER_SGI, sgi_num, - reset, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, IOCTL_REGISTER_SGI, sgi_num, reset); } /** @@ -376,7 +425,7 @@ int zynqmp_pm_get_api_version(u32 *version) *version = pm_api_version; return 0; } - ret = zynqmp_pm_invoke_fn(PM_GET_API_VERSION, 0, 0, 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_GET_API_VERSION, ret_payload, 0); *version = ret_payload[1]; return ret; @@ -399,7 +448,7 @@ int zynqmp_pm_get_chipid(u32 *idcode, u32 *version) if (!idcode || !version) return -EINVAL; - ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, 0, 0, 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, ret_payload, 0); *idcode = ret_payload[1]; *version = ret_payload[2]; @@ -414,7 +463,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_get_chipid); * * Return: Returns status, either success or error+reason */ -static int zynqmp_pm_get_family_info(u32 *family, u32 *subfamily) +int zynqmp_pm_get_family_info(u32 *family, u32 *subfamily) { u32 ret_payload[PAYLOAD_ARG_CNT]; u32 idcode; @@ -427,7 +476,7 @@ static int zynqmp_pm_get_family_info(u32 *family, u32 *subfamily) return 0; } - ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, 0, 0, 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, ret_payload, 0); if (ret < 0) return ret; @@ -439,6 +488,7 @@ static int zynqmp_pm_get_family_info(u32 *family, u32 *subfamily) return 0; } +EXPORT_SYMBOL_GPL(zynqmp_pm_get_family_info); /** * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version @@ -459,8 +509,7 @@ static int zynqmp_pm_get_trustzone_version(u32 *version) *version = pm_tz_version; return 0; } - ret = zynqmp_pm_invoke_fn(PM_GET_TRUSTZONE_VERSION, 0, 0, - 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_GET_TRUSTZONE_VERSION, ret_payload, 0); *version = ret_payload[1]; return ret; @@ -507,8 +556,8 @@ int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out) { int ret; - ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, qdata.qid, qdata.arg1, - qdata.arg2, qdata.arg3, out); + ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, out, 4, qdata.qid, qdata.arg1, qdata.arg2, + qdata.arg3); /* * For clock name query, all bytes in SMC response are clock name @@ -530,7 +579,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_query_data); */ int zynqmp_pm_clock_enable(u32 clock_id) { - return zynqmp_pm_invoke_fn(PM_CLOCK_ENABLE, clock_id, 0, 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_CLOCK_ENABLE, NULL, 1, clock_id); } EXPORT_SYMBOL_GPL(zynqmp_pm_clock_enable); @@ -545,7 +594,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_clock_enable); */ int zynqmp_pm_clock_disable(u32 clock_id) { - return zynqmp_pm_invoke_fn(PM_CLOCK_DISABLE, clock_id, 0, 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_CLOCK_DISABLE, NULL, 1, clock_id); } EXPORT_SYMBOL_GPL(zynqmp_pm_clock_disable); @@ -564,8 +613,7 @@ int zynqmp_pm_clock_getstate(u32 clock_id, u32 *state) u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; - ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETSTATE, clock_id, 0, - 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETSTATE, ret_payload, 1, clock_id); *state = ret_payload[1]; return ret; @@ -584,8 +632,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getstate); */ int zynqmp_pm_clock_setdivider(u32 clock_id, u32 divider) { - return zynqmp_pm_invoke_fn(PM_CLOCK_SETDIVIDER, clock_id, divider, - 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_CLOCK_SETDIVIDER, NULL, 2, clock_id, divider); } EXPORT_SYMBOL_GPL(zynqmp_pm_clock_setdivider); @@ -604,8 +651,7 @@ int zynqmp_pm_clock_getdivider(u32 clock_id, u32 *divider) u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; - ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETDIVIDER, clock_id, 0, - 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETDIVIDER, ret_payload, 1, clock_id); *divider = ret_payload[1]; return ret; @@ -613,47 +659,6 @@ int zynqmp_pm_clock_getdivider(u32 clock_id, u32 *divider) EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getdivider); /** - * zynqmp_pm_clock_setrate() - Set the clock rate for given id - * @clock_id: ID of the clock - * @rate: rate value in hz - * - * This function is used by master to set rate for any clock. - * - * Return: Returns status, either success or error+reason - */ -int zynqmp_pm_clock_setrate(u32 clock_id, u64 rate) -{ - return zynqmp_pm_invoke_fn(PM_CLOCK_SETRATE, clock_id, - lower_32_bits(rate), - upper_32_bits(rate), - 0, NULL); -} -EXPORT_SYMBOL_GPL(zynqmp_pm_clock_setrate); - -/** - * zynqmp_pm_clock_getrate() - Get the clock rate for given id - * @clock_id: ID of the clock - * @rate: rate value in hz - * - * This function is used by master to get rate - * for any clock. - * - * Return: Returns status, either success or error+reason - */ -int zynqmp_pm_clock_getrate(u32 clock_id, u64 *rate) -{ - u32 ret_payload[PAYLOAD_ARG_CNT]; - int ret; - - ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETRATE, clock_id, 0, - 0, 0, ret_payload); - *rate = ((u64)ret_payload[2] << 32) | ret_payload[1]; - - return ret; -} -EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getrate); - -/** * zynqmp_pm_clock_setparent() - Set the clock parent for given id * @clock_id: ID of the clock * @parent_id: parent id @@ -664,8 +669,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getrate); */ int zynqmp_pm_clock_setparent(u32 clock_id, u32 parent_id) { - return zynqmp_pm_invoke_fn(PM_CLOCK_SETPARENT, clock_id, - parent_id, 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_CLOCK_SETPARENT, NULL, 2, clock_id, parent_id); } EXPORT_SYMBOL_GPL(zynqmp_pm_clock_setparent); @@ -684,8 +688,7 @@ int zynqmp_pm_clock_getparent(u32 clock_id, u32 *parent_id) u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; - ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETPARENT, clock_id, 0, - 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETPARENT, ret_payload, 1, clock_id); *parent_id = ret_payload[1]; return ret; @@ -704,8 +707,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getparent); */ int zynqmp_pm_set_pll_frac_mode(u32 clk_id, u32 mode) { - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_SET_PLL_FRAC_MODE, - clk_id, mode, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_SET_PLL_FRAC_MODE, clk_id, mode); } EXPORT_SYMBOL_GPL(zynqmp_pm_set_pll_frac_mode); @@ -721,8 +723,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_set_pll_frac_mode); */ int zynqmp_pm_get_pll_frac_mode(u32 clk_id, u32 *mode) { - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_GET_PLL_FRAC_MODE, - clk_id, 0, mode); + return zynqmp_pm_invoke_fn(PM_IOCTL, mode, 3, 0, IOCTL_GET_PLL_FRAC_MODE, clk_id); } EXPORT_SYMBOL_GPL(zynqmp_pm_get_pll_frac_mode); @@ -739,8 +740,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_get_pll_frac_mode); */ int zynqmp_pm_set_pll_frac_data(u32 clk_id, u32 data) { - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_SET_PLL_FRAC_DATA, - clk_id, data, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_SET_PLL_FRAC_DATA, clk_id, data); } EXPORT_SYMBOL_GPL(zynqmp_pm_set_pll_frac_data); @@ -756,8 +756,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_set_pll_frac_data); */ int zynqmp_pm_get_pll_frac_data(u32 clk_id, u32 *data) { - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_GET_PLL_FRAC_DATA, - clk_id, 0, data); + return zynqmp_pm_invoke_fn(PM_IOCTL, data, 3, 0, IOCTL_GET_PLL_FRAC_DATA, clk_id); } EXPORT_SYMBOL_GPL(zynqmp_pm_get_pll_frac_data); @@ -778,9 +777,8 @@ int zynqmp_pm_set_sd_tapdelay(u32 node_id, u32 type, u32 value) u32 mask = (node_id == NODE_SD_0) ? GENMASK(15, 0) : GENMASK(31, 16); if (value) { - return zynqmp_pm_invoke_fn(PM_IOCTL, node_id, - IOCTL_SET_SD_TAPDELAY, - type, value, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, node_id, IOCTL_SET_SD_TAPDELAY, type, + value); } /* @@ -798,7 +796,7 @@ int zynqmp_pm_set_sd_tapdelay(u32 node_id, u32 type, u32 value) * Use PM_MMIO_READ/PM_MMIO_WRITE to re-implement the missing counter * part of IOCTL_SET_SD_TAPDELAY which clears SDx_ITAPDLYENA bits. */ - return zynqmp_pm_invoke_fn(PM_MMIO_WRITE, reg, mask, 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_MMIO_WRITE, NULL, 2, reg, mask); } EXPORT_SYMBOL_GPL(zynqmp_pm_set_sd_tapdelay); @@ -814,8 +812,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_set_sd_tapdelay); */ int zynqmp_pm_sd_dll_reset(u32 node_id, u32 type) { - return zynqmp_pm_invoke_fn(PM_IOCTL, node_id, IOCTL_SD_DLL_RESET, - type, 0, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, node_id, IOCTL_SD_DLL_RESET, type); } EXPORT_SYMBOL_GPL(zynqmp_pm_sd_dll_reset); @@ -831,8 +828,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_sd_dll_reset); */ int zynqmp_pm_ospi_mux_select(u32 dev_id, u32 select) { - return zynqmp_pm_invoke_fn(PM_IOCTL, dev_id, IOCTL_OSPI_MUX_SELECT, - select, 0, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, dev_id, IOCTL_OSPI_MUX_SELECT, select); } EXPORT_SYMBOL_GPL(zynqmp_pm_ospi_mux_select); @@ -847,8 +843,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_ospi_mux_select); */ int zynqmp_pm_write_ggs(u32 index, u32 value) { - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_WRITE_GGS, - index, value, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_WRITE_GGS, index, value); } EXPORT_SYMBOL_GPL(zynqmp_pm_write_ggs); @@ -863,8 +858,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_write_ggs); */ int zynqmp_pm_read_ggs(u32 index, u32 *value) { - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_READ_GGS, - index, 0, value); + return zynqmp_pm_invoke_fn(PM_IOCTL, value, 3, 0, IOCTL_READ_GGS, index); } EXPORT_SYMBOL_GPL(zynqmp_pm_read_ggs); @@ -880,8 +874,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_read_ggs); */ int zynqmp_pm_write_pggs(u32 index, u32 value) { - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_WRITE_PGGS, index, value, - NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_WRITE_PGGS, index, value); } EXPORT_SYMBOL_GPL(zynqmp_pm_write_pggs); @@ -897,15 +890,13 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_write_pggs); */ int zynqmp_pm_read_pggs(u32 index, u32 *value) { - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_READ_PGGS, index, 0, - value); + return zynqmp_pm_invoke_fn(PM_IOCTL, value, 3, 0, IOCTL_READ_PGGS, index); } EXPORT_SYMBOL_GPL(zynqmp_pm_read_pggs); int zynqmp_pm_set_tapdelay_bypass(u32 index, u32 value) { - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_SET_TAPDELAY_BYPASS, - index, value, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_SET_TAPDELAY_BYPASS, index, value); } EXPORT_SYMBOL_GPL(zynqmp_pm_set_tapdelay_bypass); @@ -920,8 +911,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_set_tapdelay_bypass); */ int zynqmp_pm_set_boot_health_status(u32 value) { - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_SET_BOOT_HEALTH_STATUS, - value, 0, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, 0, IOCTL_SET_BOOT_HEALTH_STATUS, value); } /** @@ -935,8 +925,7 @@ int zynqmp_pm_set_boot_health_status(u32 value) int zynqmp_pm_reset_assert(const enum zynqmp_pm_reset reset, const enum zynqmp_pm_reset_action assert_flag) { - return zynqmp_pm_invoke_fn(PM_RESET_ASSERT, reset, assert_flag, - 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_RESET_ASSERT, NULL, 2, reset, assert_flag); } EXPORT_SYMBOL_GPL(zynqmp_pm_reset_assert); @@ -955,8 +944,7 @@ int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset, u32 *status) if (!status) return -EINVAL; - ret = zynqmp_pm_invoke_fn(PM_RESET_GET_STATUS, reset, 0, - 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_RESET_GET_STATUS, ret_payload, 1, reset); *status = ret_payload[1]; return ret; @@ -981,9 +969,8 @@ int zynqmp_pm_fpga_load(const u64 address, const u32 size, const u32 flags) u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; - ret = zynqmp_pm_invoke_fn(PM_FPGA_LOAD, lower_32_bits(address), - upper_32_bits(address), size, flags, - ret_payload); + ret = zynqmp_pm_invoke_fn(PM_FPGA_LOAD, ret_payload, 4, lower_32_bits(address), + upper_32_bits(address), size, flags); if (ret_payload[0]) return -ret_payload[0]; @@ -1008,7 +995,7 @@ int zynqmp_pm_fpga_get_status(u32 *value) if (!value) return -EINVAL; - ret = zynqmp_pm_invoke_fn(PM_FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_FPGA_GET_STATUS, ret_payload, 0); *value = ret_payload[1]; return ret; @@ -1036,11 +1023,9 @@ int zynqmp_pm_fpga_get_config_status(u32 *value) lower_addr = lower_32_bits((u64)&buf); upper_addr = upper_32_bits((u64)&buf); - ret = zynqmp_pm_invoke_fn(PM_FPGA_READ, - XILINX_ZYNQMP_PM_FPGA_CONFIG_STAT_OFFSET, - lower_addr, upper_addr, - XILINX_ZYNQMP_PM_FPGA_READ_CONFIG_REG, - ret_payload); + ret = zynqmp_pm_invoke_fn(PM_FPGA_READ, ret_payload, 4, + XILINX_ZYNQMP_PM_FPGA_CONFIG_STAT_OFFSET, lower_addr, upper_addr, + XILINX_ZYNQMP_PM_FPGA_READ_CONFIG_REG); *value = ret_payload[1]; @@ -1058,7 +1043,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_get_config_status); */ int zynqmp_pm_pinctrl_request(const u32 pin) { - return zynqmp_pm_invoke_fn(PM_PINCTRL_REQUEST, pin, 0, 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_PINCTRL_REQUEST, NULL, 1, pin); } EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_request); @@ -1072,36 +1057,11 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_request); */ int zynqmp_pm_pinctrl_release(const u32 pin) { - return zynqmp_pm_invoke_fn(PM_PINCTRL_RELEASE, pin, 0, 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_PINCTRL_RELEASE, NULL, 1, pin); } EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_release); /** - * zynqmp_pm_pinctrl_get_function - Read function id set for the given pin - * @pin: Pin number - * @id: Buffer to store function ID - * - * This function provides the function currently set for the given pin. - * - * Return: Returns status, either success or error+reason - */ -int zynqmp_pm_pinctrl_get_function(const u32 pin, u32 *id) -{ - u32 ret_payload[PAYLOAD_ARG_CNT]; - int ret; - - if (!id) - return -EINVAL; - - ret = zynqmp_pm_invoke_fn(PM_PINCTRL_GET_FUNCTION, pin, 0, - 0, 0, ret_payload); - *id = ret_payload[1]; - - return ret; -} -EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_get_function); - -/** * zynqmp_pm_pinctrl_set_function - Set requested function for the pin * @pin: Pin number * @id: Function ID to set @@ -1112,8 +1072,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_get_function); */ int zynqmp_pm_pinctrl_set_function(const u32 pin, const u32 id) { - return zynqmp_pm_invoke_fn(PM_PINCTRL_SET_FUNCTION, pin, id, - 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_PINCTRL_SET_FUNCTION, NULL, 2, pin, id); } EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_function); @@ -1136,8 +1095,7 @@ int zynqmp_pm_pinctrl_get_config(const u32 pin, const u32 param, if (!value) return -EINVAL; - ret = zynqmp_pm_invoke_fn(PM_PINCTRL_CONFIG_PARAM_GET, pin, param, - 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_PINCTRL_CONFIG_PARAM_GET, ret_payload, 2, pin, param); *value = ret_payload[1]; return ret; @@ -1166,8 +1124,7 @@ int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param, return -EOPNOTSUPP; } - return zynqmp_pm_invoke_fn(PM_PINCTRL_CONFIG_PARAM_SET, pin, - param, value, 0, NULL); + return zynqmp_pm_invoke_fn(PM_PINCTRL_CONFIG_PARAM_SET, NULL, 3, pin, param, value); } EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_config); @@ -1185,8 +1142,7 @@ unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode) unsigned int ret; u32 ret_payload[PAYLOAD_ARG_CNT]; - ret = zynqmp_pm_invoke_fn(PM_MMIO_READ, CRL_APB_BOOT_PIN_CTRL, 0, - 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_MMIO_READ, ret_payload, 1, CRL_APB_BOOT_PIN_CTRL); *ps_mode = ret_payload[1]; @@ -1205,8 +1161,8 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_read); */ int zynqmp_pm_bootmode_write(u32 ps_mode) { - return zynqmp_pm_invoke_fn(PM_MMIO_WRITE, CRL_APB_BOOT_PIN_CTRL, - CRL_APB_BOOTPIN_CTRL_MASK, ps_mode, 0, NULL); + return zynqmp_pm_invoke_fn(PM_MMIO_WRITE, NULL, 3, CRL_APB_BOOT_PIN_CTRL, + CRL_APB_BOOTPIN_CTRL_MASK, ps_mode); } EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_write); @@ -1221,7 +1177,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_write); */ int zynqmp_pm_init_finalize(void) { - return zynqmp_pm_invoke_fn(PM_PM_INIT_FINALIZE, 0, 0, 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_PM_INIT_FINALIZE, NULL, 0); } EXPORT_SYMBOL_GPL(zynqmp_pm_init_finalize); @@ -1235,7 +1191,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_init_finalize); */ int zynqmp_pm_set_suspend_mode(u32 mode) { - return zynqmp_pm_invoke_fn(PM_SET_SUSPEND_MODE, mode, 0, 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_SET_SUSPEND_MODE, NULL, 1, mode); } EXPORT_SYMBOL_GPL(zynqmp_pm_set_suspend_mode); @@ -1254,8 +1210,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_set_suspend_mode); int zynqmp_pm_request_node(const u32 node, const u32 capabilities, const u32 qos, const enum zynqmp_pm_request_ack ack) { - return zynqmp_pm_invoke_fn(PM_REQUEST_NODE, node, capabilities, - qos, ack, NULL); + return zynqmp_pm_invoke_fn(PM_REQUEST_NODE, NULL, 4, node, capabilities, qos, ack); } EXPORT_SYMBOL_GPL(zynqmp_pm_request_node); @@ -1271,7 +1226,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_request_node); */ int zynqmp_pm_release_node(const u32 node) { - return zynqmp_pm_invoke_fn(PM_RELEASE_NODE, node, 0, 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_RELEASE_NODE, NULL, 1, node); } EXPORT_SYMBOL_GPL(zynqmp_pm_release_node); @@ -1290,8 +1245,7 @@ int zynqmp_pm_get_rpu_mode(u32 node_id, enum rpu_oper_mode *rpu_mode) u32 ret_payload[PAYLOAD_ARG_CNT]; int ret; - ret = zynqmp_pm_invoke_fn(PM_IOCTL, node_id, - IOCTL_GET_RPU_OPER_MODE, 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_IOCTL, ret_payload, 2, node_id, IOCTL_GET_RPU_OPER_MODE); /* only set rpu_mode if no error */ if (ret == XST_PM_SUCCESS) @@ -1313,9 +1267,8 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_get_rpu_mode); */ int zynqmp_pm_set_rpu_mode(u32 node_id, enum rpu_oper_mode rpu_mode) { - return zynqmp_pm_invoke_fn(PM_IOCTL, node_id, - IOCTL_SET_RPU_OPER_MODE, (u32)rpu_mode, - 0, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, node_id, IOCTL_SET_RPU_OPER_MODE, + (u32)rpu_mode); } EXPORT_SYMBOL_GPL(zynqmp_pm_set_rpu_mode); @@ -1331,9 +1284,8 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_set_rpu_mode); */ int zynqmp_pm_set_tcm_config(u32 node_id, enum rpu_tcm_comb tcm_mode) { - return zynqmp_pm_invoke_fn(PM_IOCTL, node_id, - IOCTL_TCM_COMB_CONFIG, (u32)tcm_mode, 0, - NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 3, node_id, IOCTL_TCM_COMB_CONFIG, + (u32)tcm_mode); } EXPORT_SYMBOL_GPL(zynqmp_pm_set_tcm_config); @@ -1348,7 +1300,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_set_tcm_config); int zynqmp_pm_force_pwrdwn(const u32 node, const enum zynqmp_pm_request_ack ack) { - return zynqmp_pm_invoke_fn(PM_FORCE_POWERDOWN, node, ack, 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_FORCE_POWERDOWN, NULL, 2, node, ack); } EXPORT_SYMBOL_GPL(zynqmp_pm_force_pwrdwn); @@ -1367,8 +1319,8 @@ int zynqmp_pm_request_wake(const u32 node, const enum zynqmp_pm_request_ack ack) { /* set_addr flag is encoded into 1st bit of address */ - return zynqmp_pm_invoke_fn(PM_REQUEST_WAKEUP, node, address | set_addr, - address >> 32, ack, NULL); + return zynqmp_pm_invoke_fn(PM_REQUEST_WAKEUP, NULL, 4, node, address | set_addr, + address >> 32, ack); } EXPORT_SYMBOL_GPL(zynqmp_pm_request_wake); @@ -1388,15 +1340,14 @@ int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities, const u32 qos, const enum zynqmp_pm_request_ack ack) { - return zynqmp_pm_invoke_fn(PM_SET_REQUIREMENT, node, capabilities, - qos, ack, NULL); + return zynqmp_pm_invoke_fn(PM_SET_REQUIREMENT, NULL, 4, node, capabilities, qos, ack); } EXPORT_SYMBOL_GPL(zynqmp_pm_set_requirement); /** * zynqmp_pm_load_pdi - Load and process PDI - * @src: Source device where PDI is located - * @address: PDI src address + * @src: Source device where PDI is located + * @address: PDI src address * * This function provides support to load PDI from linux * @@ -1404,9 +1355,8 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_set_requirement); */ int zynqmp_pm_load_pdi(const u32 src, const u64 address) { - return zynqmp_pm_invoke_fn(PM_LOAD_PDI, src, - lower_32_bits(address), - upper_32_bits(address), 0, NULL); + return zynqmp_pm_invoke_fn(PM_LOAD_PDI, NULL, 3, src, lower_32_bits(address), + upper_32_bits(address)); } EXPORT_SYMBOL_GPL(zynqmp_pm_load_pdi); @@ -1426,9 +1376,8 @@ int zynqmp_pm_aes_engine(const u64 address, u32 *out) if (!out) return -EINVAL; - ret = zynqmp_pm_invoke_fn(PM_SECURE_AES, upper_32_bits(address), - lower_32_bits(address), - 0, 0, ret_payload); + ret = zynqmp_pm_invoke_fn(PM_SECURE_AES, ret_payload, 2, upper_32_bits(address), + lower_32_bits(address)); *out = ret_payload[1]; return ret; @@ -1456,8 +1405,7 @@ int zynqmp_pm_sha_hash(const u64 address, const u32 size, const u32 flags) u32 lower_addr = lower_32_bits(address); u32 upper_addr = upper_32_bits(address); - return zynqmp_pm_invoke_fn(PM_SECURE_SHA, upper_addr, lower_addr, - size, flags, NULL); + return zynqmp_pm_invoke_fn(PM_SECURE_SHA, NULL, 4, upper_addr, lower_addr, size, flags); } EXPORT_SYMBOL_GPL(zynqmp_pm_sha_hash); @@ -1479,8 +1427,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_sha_hash); int zynqmp_pm_register_notifier(const u32 node, const u32 event, const u32 wake, const u32 enable) { - return zynqmp_pm_invoke_fn(PM_REGISTER_NOTIFIER, node, event, - wake, enable, NULL); + return zynqmp_pm_invoke_fn(PM_REGISTER_NOTIFIER, NULL, 4, node, event, wake, enable); } EXPORT_SYMBOL_GPL(zynqmp_pm_register_notifier); @@ -1493,8 +1440,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_register_notifier); */ int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype) { - return zynqmp_pm_invoke_fn(PM_SYSTEM_SHUTDOWN, type, subtype, - 0, 0, NULL); + return zynqmp_pm_invoke_fn(PM_SYSTEM_SHUTDOWN, NULL, 2, type, subtype); } /** @@ -1506,8 +1452,7 @@ int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype) */ int zynqmp_pm_set_feature_config(enum pm_feature_config_id id, u32 value) { - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_SET_FEATURE_CONFIG, - id, value, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, 0, IOCTL_SET_FEATURE_CONFIG, id, value); } /** @@ -1520,8 +1465,7 @@ int zynqmp_pm_set_feature_config(enum pm_feature_config_id id, u32 value) int zynqmp_pm_get_feature_config(enum pm_feature_config_id id, u32 *payload) { - return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_GET_FEATURE_CONFIG, - id, 0, payload); + return zynqmp_pm_invoke_fn(PM_IOCTL, payload, 3, 0, IOCTL_GET_FEATURE_CONFIG, id); } /** @@ -1534,8 +1478,7 @@ int zynqmp_pm_get_feature_config(enum pm_feature_config_id id, */ int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value) { - return zynqmp_pm_invoke_fn(PM_IOCTL, node, IOCTL_SET_SD_CONFIG, - config, value, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, node, IOCTL_SET_SD_CONFIG, config, value); } EXPORT_SYMBOL_GPL(zynqmp_pm_set_sd_config); @@ -1550,8 +1493,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_set_sd_config); int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, u32 value) { - return zynqmp_pm_invoke_fn(PM_IOCTL, node, IOCTL_SET_GEM_CONFIG, - config, value, NULL); + return zynqmp_pm_invoke_fn(PM_IOCTL, NULL, 4, node, IOCTL_SET_GEM_CONFIG, config, value); } EXPORT_SYMBOL_GPL(zynqmp_pm_set_gem_config); @@ -1916,7 +1858,6 @@ ATTRIBUTE_GROUPS(zynqmp_firmware); static int zynqmp_firmware_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np; struct zynqmp_devinfo *devinfo; int ret; @@ -1924,22 +1865,9 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) if (ret) return ret; - np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp"); - if (!np) { - np = of_find_compatible_node(NULL, NULL, "xlnx,versal"); - if (!np) - return 0; - + ret = do_feature_check_call(PM_FEATURE_CHECK); + if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_1)) feature_check_enabled = true; - } - - if (!feature_check_enabled) { - ret = do_feature_check_call(PM_FEATURE_CHECK); - if (ret >= 0) - feature_check_enabled = true; - } - - of_node_put(np); devinfo = devm_kzalloc(dev, sizeof(*devinfo), GFP_KERNEL); if (!devinfo) @@ -1992,19 +1920,17 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) zynqmp_pm_api_debugfs_init(); - np = of_find_compatible_node(NULL, NULL, "xlnx,versal"); - if (np) { + if (pm_family_code == VERSAL_FAMILY_CODE) { em_dev = platform_device_register_data(&pdev->dev, "xlnx_event_manager", -1, NULL, 0); if (IS_ERR(em_dev)) dev_err_probe(&pdev->dev, PTR_ERR(em_dev), "EM register fail with error\n"); } - of_node_put(np); return of_platform_populate(dev->of_node, NULL, NULL, dev); } -static int zynqmp_firmware_remove(struct platform_device *pdev) +static void zynqmp_firmware_remove(struct platform_device *pdev) { struct pm_api_feature_data *feature_data; struct hlist_node *tmp; @@ -2019,8 +1945,6 @@ static int zynqmp_firmware_remove(struct platform_device *pdev) } platform_device_unregister(em_dev); - - return 0; } static const struct of_device_id zynqmp_firmware_of_match[] = { @@ -2037,6 +1961,6 @@ static struct platform_driver zynqmp_firmware_driver = { .dev_groups = zynqmp_firmware_groups, }, .probe = zynqmp_firmware_probe, - .remove = zynqmp_firmware_remove, + .remove_new = zynqmp_firmware_remove, }; module_platform_driver(zynqmp_firmware_driver); diff --git a/drivers/fpga/altera-fpga2sdram.c b/drivers/fpga/altera-fpga2sdram.c index 1fa2ccc321ab..6b60ca004345 100644 --- a/drivers/fpga/altera-fpga2sdram.c +++ b/drivers/fpga/altera-fpga2sdram.c @@ -147,20 +147,18 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) return ret; } -static int alt_fpga_bridge_remove(struct platform_device *pdev) +static void alt_fpga_bridge_remove(struct platform_device *pdev) { struct fpga_bridge *br = platform_get_drvdata(pdev); fpga_bridge_unregister(br); - - return 0; } MODULE_DEVICE_TABLE(of, altera_fpga_of_match); static struct platform_driver altera_fpga_driver = { .probe = alt_fpga_bridge_probe, - .remove = alt_fpga_bridge_remove, + .remove_new = alt_fpga_bridge_remove, .driver = { .name = "altera_fpga2sdram_bridge", .of_match_table = of_match_ptr(altera_fpga_of_match), diff --git a/drivers/fpga/altera-freeze-bridge.c b/drivers/fpga/altera-freeze-bridge.c index 0c3fb8226908..44061cb16f87 100644 --- a/drivers/fpga/altera-freeze-bridge.c +++ b/drivers/fpga/altera-freeze-bridge.c @@ -253,18 +253,16 @@ static int altera_freeze_br_probe(struct platform_device *pdev) return 0; } -static int altera_freeze_br_remove(struct platform_device *pdev) +static void altera_freeze_br_remove(struct platform_device *pdev) { struct fpga_bridge *br = platform_get_drvdata(pdev); fpga_bridge_unregister(br); - - return 0; } static struct platform_driver altera_freeze_br_driver = { .probe = altera_freeze_br_probe, - .remove = altera_freeze_br_remove, + .remove_new = altera_freeze_br_remove, .driver = { .name = "altera_freeze_br", .of_match_table = altera_freeze_br_of_match, diff --git a/drivers/fpga/altera-hps2fpga.c b/drivers/fpga/altera-hps2fpga.c index 578663503297..6f8e24be19c6 100644 --- a/drivers/fpga/altera-hps2fpga.c +++ b/drivers/fpga/altera-hps2fpga.c @@ -191,7 +191,7 @@ err: return ret; } -static int alt_fpga_bridge_remove(struct platform_device *pdev) +static void alt_fpga_bridge_remove(struct platform_device *pdev) { struct fpga_bridge *bridge = platform_get_drvdata(pdev); struct altera_hps2fpga_data *priv = bridge->priv; @@ -199,15 +199,13 @@ static int alt_fpga_bridge_remove(struct platform_device *pdev) fpga_bridge_unregister(bridge); clk_disable_unprepare(priv->clk); - - return 0; } MODULE_DEVICE_TABLE(of, altera_fpga_of_match); static struct platform_driver alt_fpga_bridge_driver = { .probe = alt_fpga_bridge_probe, - .remove = alt_fpga_bridge_remove, + .remove_new = alt_fpga_bridge_remove, .driver = { .name = "altera_hps2fpga_bridge", .of_match_table = of_match_ptr(altera_fpga_of_match), diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c index 7f621e96d3b8..c0a75ca360d6 100644 --- a/drivers/fpga/dfl-afu-main.c +++ b/drivers/fpga/dfl-afu-main.c @@ -932,15 +932,13 @@ exit: return ret; } -static int afu_remove(struct platform_device *pdev) +static void afu_remove(struct platform_device *pdev) { dev_dbg(&pdev->dev, "%s\n", __func__); dfl_fpga_dev_ops_unregister(pdev); dfl_fpga_dev_feature_uinit(pdev); afu_dev_destroy(pdev); - - return 0; } static const struct attribute_group *afu_dev_groups[] = { @@ -956,7 +954,7 @@ static struct platform_driver afu_driver = { .dev_groups = afu_dev_groups, }, .probe = afu_probe, - .remove = afu_remove, + .remove_new = afu_remove, }; static int __init afu_init(void) diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c index 808d1f4d76df..0b01b3895277 100644 --- a/drivers/fpga/dfl-fme-br.c +++ b/drivers/fpga/dfl-fme-br.c @@ -78,7 +78,7 @@ static int fme_br_probe(struct platform_device *pdev) return 0; } -static int fme_br_remove(struct platform_device *pdev) +static void fme_br_remove(struct platform_device *pdev) { struct fpga_bridge *br = platform_get_drvdata(pdev); struct fme_br_priv *priv = br->priv; @@ -89,8 +89,6 @@ static int fme_br_remove(struct platform_device *pdev) put_device(&priv->port_pdev->dev); if (priv->port_ops) dfl_fpga_port_ops_put(priv->port_ops); - - return 0; } static struct platform_driver fme_br_driver = { @@ -98,7 +96,7 @@ static struct platform_driver fme_br_driver = { .name = DFL_FPGA_FME_BRIDGE, }, .probe = fme_br_probe, - .remove = fme_br_remove, + .remove_new = fme_br_remove, }; module_platform_driver(fme_br_driver); diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c index 3dcf990bd261..a2b5da0093da 100644 --- a/drivers/fpga/dfl-fme-main.c +++ b/drivers/fpga/dfl-fme-main.c @@ -730,13 +730,11 @@ exit: return ret; } -static int fme_remove(struct platform_device *pdev) +static void fme_remove(struct platform_device *pdev) { dfl_fpga_dev_ops_unregister(pdev); dfl_fpga_dev_feature_uinit(pdev); fme_dev_destroy(pdev); - - return 0; } static const struct attribute_group *fme_dev_groups[] = { @@ -751,7 +749,7 @@ static struct platform_driver fme_driver = { .dev_groups = fme_dev_groups, }, .probe = fme_probe, - .remove = fme_remove, + .remove_new = fme_remove, }; module_platform_driver(fme_driver); diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c index 4aebde0a7f1c..71616f8b4982 100644 --- a/drivers/fpga/dfl-fme-region.c +++ b/drivers/fpga/dfl-fme-region.c @@ -61,15 +61,13 @@ eprobe_mgr_put: return ret; } -static int fme_region_remove(struct platform_device *pdev) +static void fme_region_remove(struct platform_device *pdev) { struct fpga_region *region = platform_get_drvdata(pdev); struct fpga_manager *mgr = region->mgr; fpga_region_unregister(region); fpga_mgr_put(mgr); - - return 0; } static struct platform_driver fme_region_driver = { @@ -77,7 +75,7 @@ static struct platform_driver fme_region_driver = { .name = DFL_FPGA_FME_REGION, }, .probe = fme_region_probe, - .remove = fme_region_remove, + .remove_new = fme_region_remove, }; module_platform_driver(fme_region_driver); diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index e73f88050f08..e6d12fbab653 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -2008,8 +2008,8 @@ long dfl_feature_ioctl_set_irq(struct platform_device *pdev, (hdr.start + hdr.count < hdr.start)) return -EINVAL; - fds = memdup_user((void __user *)(arg + sizeof(hdr)), - array_size(hdr.count, sizeof(s32))); + fds = memdup_array_user((void __user *)(arg + sizeof(hdr)), + hdr.count, sizeof(s32)); if (IS_ERR(fds)) return PTR_ERR(fds); diff --git a/drivers/fpga/intel-m10-bmc-sec-update.c b/drivers/fpga/intel-m10-bmc-sec-update.c index 31af2e08c825..89851b133709 100644 --- a/drivers/fpga/intel-m10-bmc-sec-update.c +++ b/drivers/fpga/intel-m10-bmc-sec-update.c @@ -730,15 +730,13 @@ fw_name_fail: return ret; } -static int m10bmc_sec_remove(struct platform_device *pdev) +static void m10bmc_sec_remove(struct platform_device *pdev) { struct m10bmc_sec *sec = dev_get_drvdata(&pdev->dev); firmware_upload_unregister(sec->fwl); kfree(sec->fw_name); xa_erase(&fw_upload_xa, sec->fw_name_id); - - return 0; } static const struct platform_device_id intel_m10bmc_sec_ids[] = { @@ -760,7 +758,7 @@ MODULE_DEVICE_TABLE(platform, intel_m10bmc_sec_ids); static struct platform_driver intel_m10bmc_sec_driver = { .probe = m10bmc_sec_probe, - .remove = m10bmc_sec_remove, + .remove_new = m10bmc_sec_remove, .driver = { .name = "intel-m10bmc-sec-update", .dev_groups = m10bmc_sec_attr_groups, diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c index a6affd83f275..8526a5a86f0c 100644 --- a/drivers/fpga/of-fpga-region.c +++ b/drivers/fpga/of-fpga-region.c @@ -425,20 +425,18 @@ eprobe_mgr_put: return ret; } -static int of_fpga_region_remove(struct platform_device *pdev) +static void of_fpga_region_remove(struct platform_device *pdev) { struct fpga_region *region = platform_get_drvdata(pdev); struct fpga_manager *mgr = region->mgr; fpga_region_unregister(region); fpga_mgr_put(mgr); - - return 0; } static struct platform_driver of_fpga_region_driver = { .probe = of_fpga_region_probe, - .remove = of_fpga_region_remove, + .remove_new = of_fpga_region_remove, .driver = { .name = "of-fpga-region", .of_match_table = of_match_ptr(fpga_region_of_match), diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c index cc4861e345c9..4c03513b8f03 100644 --- a/drivers/fpga/socfpga-a10.c +++ b/drivers/fpga/socfpga-a10.c @@ -517,15 +517,13 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev) return 0; } -static int socfpga_a10_fpga_remove(struct platform_device *pdev) +static void socfpga_a10_fpga_remove(struct platform_device *pdev) { struct fpga_manager *mgr = platform_get_drvdata(pdev); struct a10_fpga_priv *priv = mgr->priv; fpga_mgr_unregister(mgr); clk_disable_unprepare(priv->clk); - - return 0; } static const struct of_device_id socfpga_a10_fpga_of_match[] = { @@ -537,7 +535,7 @@ MODULE_DEVICE_TABLE(of, socfpga_a10_fpga_of_match); static struct platform_driver socfpga_a10_fpga_driver = { .probe = socfpga_a10_fpga_probe, - .remove = socfpga_a10_fpga_remove, + .remove_new = socfpga_a10_fpga_remove, .driver = { .name = "socfpga_a10_fpga_manager", .of_match_table = socfpga_a10_fpga_of_match, diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c index cacb9cc5757e..2c0def7d7cbb 100644 --- a/drivers/fpga/stratix10-soc.c +++ b/drivers/fpga/stratix10-soc.c @@ -436,15 +436,13 @@ probe_err: return ret; } -static int s10_remove(struct platform_device *pdev) +static void s10_remove(struct platform_device *pdev) { struct fpga_manager *mgr = platform_get_drvdata(pdev); struct s10_priv *priv = mgr->priv; fpga_mgr_unregister(mgr); stratix10_svc_free_channel(priv->chan); - - return 0; } static const struct of_device_id s10_of_match[] = { @@ -457,7 +455,7 @@ MODULE_DEVICE_TABLE(of, s10_of_match); static struct platform_driver s10_driver = { .probe = s10_probe, - .remove = s10_remove, + .remove_new = s10_remove, .driver = { .name = "Stratix10 SoC FPGA manager", .of_match_table = of_match_ptr(s10_of_match), diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c index 68835896f180..788dd2f63a65 100644 --- a/drivers/fpga/xilinx-pr-decoupler.c +++ b/drivers/fpga/xilinx-pr-decoupler.c @@ -150,7 +150,7 @@ err_clk: return err; } -static int xlnx_pr_decoupler_remove(struct platform_device *pdev) +static void xlnx_pr_decoupler_remove(struct platform_device *pdev) { struct fpga_bridge *bridge = platform_get_drvdata(pdev); struct xlnx_pr_decoupler_data *p = bridge->priv; @@ -158,13 +158,11 @@ static int xlnx_pr_decoupler_remove(struct platform_device *pdev) fpga_bridge_unregister(bridge); clk_unprepare(p->clk); - - return 0; } static struct platform_driver xlnx_pr_decoupler_driver = { .probe = xlnx_pr_decoupler_probe, - .remove = xlnx_pr_decoupler_remove, + .remove_new = xlnx_pr_decoupler_remove, .driver = { .name = "xlnx_pr_decoupler", .of_match_table = xlnx_pr_decoupler_of_match, diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index 96611d424a10..0ac93183d201 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c @@ -618,7 +618,7 @@ static int zynq_fpga_probe(struct platform_device *pdev) return 0; } -static int zynq_fpga_remove(struct platform_device *pdev) +static void zynq_fpga_remove(struct platform_device *pdev) { struct zynq_fpga_priv *priv; struct fpga_manager *mgr; @@ -629,8 +629,6 @@ static int zynq_fpga_remove(struct platform_device *pdev) fpga_mgr_unregister(mgr); clk_unprepare(priv->clk); - - return 0; } #ifdef CONFIG_OF @@ -644,7 +642,7 @@ MODULE_DEVICE_TABLE(of, zynq_fpga_of_match); static struct platform_driver zynq_fpga_driver = { .probe = zynq_fpga_probe, - .remove = zynq_fpga_remove, + .remove_new = zynq_fpga_remove, .driver = { .name = "zynq_fpga_manager", .of_match_table = of_match_ptr(zynq_fpga_of_match), diff --git a/drivers/gnss/serial.c b/drivers/gnss/serial.c index 5d8e9bfb24d0..baa956494e79 100644 --- a/drivers/gnss/serial.c +++ b/drivers/gnss/serial.c @@ -80,8 +80,8 @@ static const struct gnss_operations gnss_serial_gnss_ops = { .write_raw = gnss_serial_write_raw, }; -static int gnss_serial_receive_buf(struct serdev_device *serdev, - const unsigned char *buf, size_t count) +static ssize_t gnss_serial_receive_buf(struct serdev_device *serdev, + const u8 *buf, size_t count) { struct gnss_serial *gserial = serdev_device_get_drvdata(serdev); struct gnss_device *gdev = gserial->gdev; diff --git a/drivers/gnss/sirf.c b/drivers/gnss/sirf.c index bcb53ccfee4d..6801a8fb2040 100644 --- a/drivers/gnss/sirf.c +++ b/drivers/gnss/sirf.c @@ -160,8 +160,8 @@ static const struct gnss_operations sirf_gnss_ops = { .write_raw = sirf_write_raw, }; -static int sirf_receive_buf(struct serdev_device *serdev, - const unsigned char *buf, size_t count) +static ssize_t sirf_receive_buf(struct serdev_device *serdev, + const u8 *buf, size_t count) { struct sirf_data *data = serdev_device_get_drvdata(serdev); struct gnss_device *gdev = data->gdev; diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index be7f2fa5aa7b..806b88d8dfb7 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -330,20 +330,27 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) switch (flow_type) { case IRQ_TYPE_LEVEL_HIGH: sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 1); + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IC, 1); break; case IRQ_TYPE_LEVEL_LOW: sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 0); + sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IC, 1); break; case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_EDGE_BOTH: state = sprd_eic_get(chip, offset); - if (state) + if (state) { sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 0); - else + sprd_eic_update(chip, offset, + SPRD_EIC_DBNC_IC, 1); + } else { sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 1); + sprd_eic_update(chip, offset, + SPRD_EIC_DBNC_IC, 1); + } break; default: return -ENOTSUPP; @@ -355,20 +362,27 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) switch (flow_type) { case IRQ_TYPE_LEVEL_HIGH: sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 0); + sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTCLR, 1); break; case IRQ_TYPE_LEVEL_LOW: sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 1); + sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTCLR, 1); break; case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_EDGE_BOTH: state = sprd_eic_get(chip, offset); - if (state) + if (state) { sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 0); - else + sprd_eic_update(chip, offset, + SPRD_EIC_LATCH_INTCLR, 1); + } else { sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 1); + sprd_eic_update(chip, offset, + SPRD_EIC_LATCH_INTCLR, 1); + } break; default: return -ENOTSUPP; @@ -382,29 +396,34 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0); sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 0); sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 1); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTCLR, 1); irq_set_handler_locked(data, handle_edge_irq); break; case IRQ_TYPE_EDGE_FALLING: sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0); sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 0); sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 0); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTCLR, 1); irq_set_handler_locked(data, handle_edge_irq); break; case IRQ_TYPE_EDGE_BOTH: sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 0); sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 1); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTCLR, 1); irq_set_handler_locked(data, handle_edge_irq); break; case IRQ_TYPE_LEVEL_HIGH: sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0); sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 1); sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 1); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTCLR, 1); irq_set_handler_locked(data, handle_level_irq); break; case IRQ_TYPE_LEVEL_LOW: sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0); sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 1); sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 0); + sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTCLR, 1); irq_set_handler_locked(data, handle_level_irq); break; default: @@ -417,29 +436,34 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0); sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 0); sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 1); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTCLR, 1); irq_set_handler_locked(data, handle_edge_irq); break; case IRQ_TYPE_EDGE_FALLING: sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0); sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 0); sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 0); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTCLR, 1); irq_set_handler_locked(data, handle_edge_irq); break; case IRQ_TYPE_EDGE_BOTH: sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 0); sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 1); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTCLR, 1); irq_set_handler_locked(data, handle_edge_irq); break; case IRQ_TYPE_LEVEL_HIGH: sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0); sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 1); sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 1); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTCLR, 1); irq_set_handler_locked(data, handle_level_irq); break; case IRQ_TYPE_LEVEL_LOW: sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0); sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 1); sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 0); + sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTCLR, 1); irq_set_handler_locked(data, handle_level_irq); break; default: diff --git a/drivers/gpio/gpio-en7523.c b/drivers/gpio/gpio-en7523.c index f836a8db4c1d..69834db2c1cf 100644 --- a/drivers/gpio/gpio-en7523.c +++ b/drivers/gpio/gpio-en7523.c @@ -12,11 +12,11 @@ #define AIROHA_GPIO_MAX 32 /** - * airoha_gpio_ctrl - Airoha GPIO driver data + * struct airoha_gpio_ctrl - Airoha GPIO driver data * @gc: Associated gpio_chip instance. * @data: The data register. - * @dir0: The direction register for the lower 16 pins. - * @dir1: The direction register for the higher 16 pins. + * @dir: [0] The direction register for the lower 16 pins. + * [1]: The direction register for the higher 16 pins. * @output: The output enable register. */ struct airoha_gpio_ctrl { diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index 7a3e1760fc5b..d5906d419b0a 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -215,6 +215,8 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev) gs->gpio_clr_io + MLXBF_GPIO_FW_DATA_OUT_CLEAR, gs->gpio_set_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_SET, gs->gpio_clr_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR, 0); + if (ret) + return dev_err_probe(dev, ret, "%s: bgpio_init() failed", __func__); gc->request = gpiochip_generic_request; gc->free = gpiochip_generic_free; diff --git a/drivers/gpio/gpio-rtd.c b/drivers/gpio/gpio-rtd.c index a7939bd0aa56..bf7f008f58d7 100644 --- a/drivers/gpio/gpio-rtd.c +++ b/drivers/gpio/gpio-rtd.c @@ -525,18 +525,21 @@ static int rtd_gpio_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct gpio_irq_chip *irq_chip; struct rtd_gpio *data; + int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->irqs[0] = platform_get_irq(pdev, 0); - if (data->irqs[0] < 0) - return data->irqs[0]; + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + data->irqs[0] = ret; - data->irqs[1] = platform_get_irq(pdev, 1); - if (data->irqs[1] < 0) - return data->irqs[1]; + ret = platform_get_irq(pdev, 1); + if (ret < 0) + return ret; + data->irqs[1] = ret; data->info = device_get_match_data(dev); if (!data->info) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 88066826d8e5..cd3e9657cc36 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -1651,6 +1651,20 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { .ignore_interrupt = "INT33FC:00@3", }, }, + { + /* + * Spurious wakeups from TP_ATTN# pin + * Found in BIOS 0.35 + * https://gitlab.freedesktop.org/drm/amd/-/issues/3073 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "PNP0C50:00@8", + }, + }, {} /* Terminating entry */ }; diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 402f7d99b0c1..e7770eedd146 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -184,7 +184,7 @@ static void of_gpio_try_fixup_polarity(const struct device_node *np, const char *propname; bool active_high; } gpios[] = { -#if !IS_ENABLED(CONFIG_LCD_HX8357) +#if IS_ENABLED(CONFIG_LCD_HX8357) /* * Himax LCD controllers used incorrectly named * "gpios-reset" property and also specified wrong @@ -478,7 +478,7 @@ static struct gpio_desc *of_find_gpio_rename(struct device_node *np, */ const char *compatible; } gpios[] = { -#if !IS_ENABLED(CONFIG_LCD_HX8357) +#if IS_ENABLED(CONFIG_LCD_HX8357) /* Himax LCD controllers used "gpios-reset" */ { "reset", "gpios-reset", "himax,hx8357" }, { "reset", "gpios-reset", "himax,hx8369" }, diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 4dbf298bb5dd..6bf5332136e5 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -768,25 +768,6 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) return 0; } -int gpiochip_sysfs_register_all(void) -{ - struct gpio_device *gdev; - int ret; - - guard(rwsem_read)(&gpio_devices_sem); - - list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->mockdev) - continue; - - ret = gpiochip_sysfs_register(gdev); - if (ret) - return ret; - } - - return 0; -} - void gpiochip_sysfs_unregister(struct gpio_device *gdev) { struct gpio_desc *desc; @@ -811,7 +792,9 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) static int __init gpiolib_sysfs_init(void) { - int status; + int status; + unsigned long flags; + struct gpio_device *gdev; status = class_register(&gpio_class); if (status < 0) @@ -823,6 +806,26 @@ static int __init gpiolib_sysfs_init(void) * We run before arch_initcall() so chip->dev nodes can have * registered, and so arch_initcall() can always gpiod_export(). */ - return gpiochip_sysfs_register_all(); + spin_lock_irqsave(&gpio_lock, flags); + list_for_each_entry(gdev, &gpio_devices, list) { + if (gdev->mockdev) + continue; + + /* + * TODO we yield gpio_lock here because + * gpiochip_sysfs_register() acquires a mutex. This is unsafe + * and needs to be fixed. + * + * Also it would be nice to use gpio_device_find() here so we + * can keep gpio_chips local to gpiolib.c, but the yield of + * gpio_lock prevents us from doing this. + */ + spin_unlock_irqrestore(&gpio_lock, flags); + status = gpiochip_sysfs_register(gdev); + spin_lock_irqsave(&gpio_lock, flags); + } + spin_unlock_irqrestore(&gpio_lock, flags); + + return status; } postcore_initcall(gpiolib_sysfs_init); diff --git a/drivers/gpio/gpiolib-sysfs.h b/drivers/gpio/gpiolib-sysfs.h index ab157cec0b4b..b794b396d6a5 100644 --- a/drivers/gpio/gpiolib-sysfs.h +++ b/drivers/gpio/gpiolib-sysfs.h @@ -8,7 +8,6 @@ struct gpio_device; #ifdef CONFIG_GPIO_SYSFS int gpiochip_sysfs_register(struct gpio_device *gdev); -int gpiochip_sysfs_register_all(void); void gpiochip_sysfs_unregister(struct gpio_device *gdev); #else @@ -18,11 +17,6 @@ static inline int gpiochip_sysfs_register(struct gpio_device *gdev) return 0; } -static inline int gpiochip_sysfs_register_all(void) -{ - return 0; -} - static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev) { } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4c93cf73a826..8b3a0f45b574 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2,7 +2,6 @@ #include <linux/acpi.h> #include <linux/bitmap.h> -#include <linux/cleanup.h> #include <linux/compat.h> #include <linux/debugfs.h> #include <linux/device.h> @@ -16,7 +15,6 @@ #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/of.h> #include <linux/pinctrl/consumer.h> #include <linux/seq_file.h> @@ -83,9 +81,7 @@ DEFINE_SPINLOCK(gpio_lock); static DEFINE_MUTEX(gpio_lookup_lock); static LIST_HEAD(gpio_lookup_list); - LIST_HEAD(gpio_devices); -DECLARE_RWSEM(gpio_devices_sem); static DEFINE_MUTEX(gpio_machine_hogs_mutex); static LIST_HEAD(gpio_machine_hogs); @@ -117,15 +113,20 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) struct gpio_desc *gpio_to_desc(unsigned gpio) { struct gpio_device *gdev; + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); - scoped_guard(rwsem_read, &gpio_devices_sem) { - list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->base <= gpio && - gdev->base + gdev->ngpio > gpio) - return &gdev->descs[gpio - gdev->base]; + list_for_each_entry(gdev, &gpio_devices, list) { + if (gdev->base <= gpio && + gdev->base + gdev->ngpio > gpio) { + spin_unlock_irqrestore(&gpio_lock, flags); + return &gdev->descs[gpio - gdev->base]; } } + spin_unlock_irqrestore(&gpio_lock, flags); + if (!gpio_is_valid(gpio)) pr_warn("invalid GPIO %d\n", gpio); @@ -398,21 +399,26 @@ static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev) static struct gpio_desc *gpio_name_to_desc(const char * const name) { struct gpio_device *gdev; + unsigned long flags; if (!name) return NULL; - guard(rwsem_read)(&gpio_devices_sem); + spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(gdev, &gpio_devices, list) { struct gpio_desc *desc; for_each_gpio_desc(gdev->chip, desc) { - if (desc->name && !strcmp(desc->name, name)) + if (desc->name && !strcmp(desc->name, name)) { + spin_unlock_irqrestore(&gpio_lock, flags); return desc; + } } } + spin_unlock_irqrestore(&gpio_lock, flags); + return NULL; } @@ -807,6 +813,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *request_key) { struct gpio_device *gdev; + unsigned long flags; unsigned int i; int base = 0; int ret = 0; @@ -871,46 +878,49 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gdev->ngpio = gc->ngpio; - scoped_guard(rwsem_write, &gpio_devices_sem) { - /* - * TODO: this allocates a Linux GPIO number base in the global - * GPIO numberspace for this chip. In the long run we want to - * get *rid* of this numberspace and use only descriptors, but - * it may be a pipe dream. It will not happen before we get rid - * of the sysfs interface anyways. - */ - base = gc->base; + spin_lock_irqsave(&gpio_lock, flags); + /* + * TODO: this allocates a Linux GPIO number base in the global + * GPIO numberspace for this chip. In the long run we want to + * get *rid* of this numberspace and use only descriptors, but + * it may be a pipe dream. It will not happen before we get rid + * of the sysfs interface anyways. + */ + base = gc->base; + if (base < 0) { + base = gpiochip_find_base_unlocked(gc->ngpio); if (base < 0) { - base = gpiochip_find_base_unlocked(gc->ngpio); - if (base < 0) { - ret = base; - base = 0; - goto err_free_label; - } - /* - * TODO: it should not be necessary to reflect the assigned - * base outside of the GPIO subsystem. Go over drivers and - * see if anyone makes use of this, else drop this and assign - * a poison instead. - */ - gc->base = base; - } else { - dev_warn(&gdev->dev, - "Static allocation of GPIO base is deprecated, use dynamic allocation.\n"); - } - gdev->base = base; - - ret = gpiodev_add_to_list_unlocked(gdev); - if (ret) { - chip_err(gc, "GPIO integer space overlap, cannot add chip\n"); + spin_unlock_irqrestore(&gpio_lock, flags); + ret = base; + base = 0; goto err_free_label; } + /* + * TODO: it should not be necessary to reflect the assigned + * base outside of the GPIO subsystem. Go over drivers and + * see if anyone makes use of this, else drop this and assign + * a poison instead. + */ + gc->base = base; + } else { + dev_warn(&gdev->dev, + "Static allocation of GPIO base is deprecated, use dynamic allocation.\n"); + } + gdev->base = base; - for (i = 0; i < gc->ngpio; i++) - gdev->descs[i].gdev = gdev; + ret = gpiodev_add_to_list_unlocked(gdev); + if (ret) { + spin_unlock_irqrestore(&gpio_lock, flags); + chip_err(gc, "GPIO integer space overlap, cannot add chip\n"); + goto err_free_label; } + for (i = 0; i < gc->ngpio; i++) + gdev->descs[i].gdev = gdev; + + spin_unlock_irqrestore(&gpio_lock, flags); + BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); init_rwsem(&gdev->sem); @@ -995,14 +1005,15 @@ err_remove_of_chip: err_free_gpiochip_mask: gpiochip_remove_pin_ranges(gc); gpiochip_free_valid_mask(gc); +err_remove_from_list: + spin_lock_irqsave(&gpio_lock, flags); + list_del(&gdev->list); + spin_unlock_irqrestore(&gpio_lock, flags); if (gdev->dev.release) { /* release() has been registered by gpiochip_setup_dev() */ gpio_device_put(gdev); goto err_print_message; } -err_remove_from_list: - scoped_guard(rwsem_write, &gpio_devices_sem) - list_del(&gdev->list); err_free_label: kfree_const(gdev->label); err_free_descs: @@ -1065,7 +1076,7 @@ void gpiochip_remove(struct gpio_chip *gc) dev_crit(&gdev->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); - scoped_guard(rwsem_write, &gpio_devices_sem) + scoped_guard(spinlock_irqsave, &gpio_lock) list_del(&gdev->list); /* @@ -1114,7 +1125,7 @@ struct gpio_device *gpio_device_find(void *data, */ might_sleep(); - guard(rwsem_read)(&gpio_devices_sem); + guard(spinlock_irqsave)(&gpio_lock); list_for_each_entry(gdev, &gpio_devices, list) { if (gdev->chip && match(gdev->chip, data)) @@ -4725,33 +4736,35 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) { + unsigned long flags; struct gpio_device *gdev = NULL; loff_t index = *pos; s->private = ""; - guard(rwsem_read)(&gpio_devices_sem); - - list_for_each_entry(gdev, &gpio_devices, list) { - if (index-- == 0) + spin_lock_irqsave(&gpio_lock, flags); + list_for_each_entry(gdev, &gpio_devices, list) + if (index-- == 0) { + spin_unlock_irqrestore(&gpio_lock, flags); return gdev; - } + } + spin_unlock_irqrestore(&gpio_lock, flags); return NULL; } static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos) { + unsigned long flags; struct gpio_device *gdev = v; void *ret = NULL; - scoped_guard(rwsem_read, &gpio_devices_sem) { - if (list_is_last(&gdev->list, &gpio_devices)) - ret = NULL; - else - ret = list_first_entry(&gdev->list, struct gpio_device, - list); - } + spin_lock_irqsave(&gpio_lock, flags); + if (list_is_last(&gdev->list, &gpio_devices)) + ret = NULL; + else + ret = list_first_entry(&gdev->list, struct gpio_device, list); + spin_unlock_irqrestore(&gpio_lock, flags); s->private = "\n"; ++*pos; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 97df54abf57a..a4a2520b5f31 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -15,7 +15,6 @@ #include <linux/gpio/consumer.h> /* for enum gpiod_flags */ #include <linux/gpio/driver.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/notifier.h> #include <linux/rwsem.h> @@ -137,7 +136,6 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); extern spinlock_t gpio_lock; extern struct list_head gpio_devices; -extern struct rw_semaphore gpio_devices_sem; void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 9da14436a373..6dce81a061ab 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -254,8 +254,6 @@ extern int amdgpu_agp; extern int amdgpu_wbrf; -extern int fw_bo_location; - #define AMDGPU_VM_MAX_NUM_CTX 4096 #define AMDGPU_SG_THRESHOLD (256*1024*1024) #define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS 3000 @@ -1080,6 +1078,8 @@ struct amdgpu_device { bool in_s3; bool in_s4; bool in_s0ix; + /* indicate amdgpu suspension status */ + bool suspend_complete; enum pp_mp1_state mp1_state; struct amdgpu_doorbell_index doorbell_index; @@ -1146,6 +1146,7 @@ struct amdgpu_device { bool debug_vm; bool debug_largebar; bool debug_disable_soft_recovery; + bool debug_use_vram_fw_buf; }; static inline uint32_t amdgpu_ip_version(const struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 067690ba7bff..41db030ddc4e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -138,11 +138,34 @@ static void amdgpu_amdkfd_reset_work(struct work_struct *work) amdgpu_device_gpu_recover(adev, NULL, &reset_context); } +static const struct drm_client_funcs kfd_client_funcs = { + .unregister = drm_client_release, +}; + +int amdgpu_amdkfd_drm_client_create(struct amdgpu_device *adev) +{ + int ret; + + if (!adev->kfd.init_complete) + return 0; + + ret = drm_client_init(&adev->ddev, &adev->kfd.client, "kfd", + &kfd_client_funcs); + if (ret) { + dev_err(adev->dev, "Failed to init DRM client: %d\n", + ret); + return ret; + } + + drm_client_register(&adev->kfd.client); + + return 0; +} + void amdgpu_amdkfd_device_init(struct amdgpu_device *adev) { int i; int last_valid_bit; - int ret; amdgpu_amdkfd_gpuvm_init_mem_limits(); @@ -161,12 +184,6 @@ void amdgpu_amdkfd_device_init(struct amdgpu_device *adev) .enable_mes = adev->enable_mes, }; - ret = drm_client_init(&adev->ddev, &adev->kfd.client, "kfd", NULL); - if (ret) { - dev_err(adev->dev, "Failed to init DRM client: %d\n", ret); - return; - } - /* this is going to have a few of the MSBs set that we need to * clear */ @@ -205,10 +222,6 @@ void amdgpu_amdkfd_device_init(struct amdgpu_device *adev) adev->kfd.init_complete = kgd2kfd_device_init(adev->kfd.dev, &gpu_resources); - if (adev->kfd.init_complete) - drm_client_register(&adev->kfd.client); - else - drm_client_release(&adev->kfd.client); amdgpu_amdkfd_total_mem_size += adev->gmc.real_vram_size; @@ -695,10 +708,8 @@ err: void amdgpu_amdkfd_set_compute_idle(struct amdgpu_device *adev, bool idle) { enum amd_powergating_state state = idle ? AMD_PG_STATE_GATE : AMD_PG_STATE_UNGATE; - /* Temporary workaround to fix issues observed in some - * compute applications when GFXOFF is enabled on GFX11. - */ - if (IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0)) == 11) { + if (IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0)) == 11 && + ((adev->mes.kiq_version & AMDGPU_MES_VERSION_MASK) <= 64)) { pr_debug("GFXOFF is %s\n", idle ? "enabled" : "disabled"); amdgpu_gfx_off_ctrl(adev, idle); } else if ((IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0)) == 9) && diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index cf6ed5fce291..27c61c535e29 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -182,6 +182,8 @@ int amdgpu_queue_mask_bit_to_set_resource_bit(struct amdgpu_device *adev, struct amdgpu_amdkfd_fence *amdgpu_amdkfd_fence_create(u64 context, struct mm_struct *mm, struct svm_range_bo *svm_bo); + +int amdgpu_amdkfd_drm_client_create(struct amdgpu_device *adev); #if defined(CONFIG_DEBUG_FS) int kfd_debugfs_kfd_mem_limits(struct seq_file *m, void *data); #endif @@ -301,7 +303,7 @@ int amdgpu_amdkfd_gpuvm_map_memory_to_gpu(struct amdgpu_device *adev, struct kgd_mem *mem, void *drm_priv); int amdgpu_amdkfd_gpuvm_unmap_memory_from_gpu( struct amdgpu_device *adev, struct kgd_mem *mem, void *drm_priv); -void amdgpu_amdkfd_gpuvm_dmaunmap_mem(struct kgd_mem *mem, void *drm_priv); +int amdgpu_amdkfd_gpuvm_dmaunmap_mem(struct kgd_mem *mem, void *drm_priv); int amdgpu_amdkfd_gpuvm_sync_memory( struct amdgpu_device *adev, struct kgd_mem *mem, bool intr); int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem, @@ -311,7 +313,7 @@ void amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(struct kgd_mem *mem); int amdgpu_amdkfd_map_gtt_bo_to_gart(struct amdgpu_device *adev, struct amdgpu_bo *bo); int amdgpu_amdkfd_gpuvm_restore_process_bos(void *process_info, - struct dma_fence **ef); + struct dma_fence __rcu **ef); int amdgpu_amdkfd_gpuvm_get_vm_fault_info(struct amdgpu_device *adev, struct kfd_vm_fault_info *info); int amdgpu_amdkfd_gpuvm_import_dmabuf_fd(struct amdgpu_device *adev, int fd, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_arcturus.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_arcturus.c index 899e31e3a5e8..3a3f3ce09f00 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_arcturus.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_arcturus.c @@ -290,7 +290,7 @@ static int suspend_resume_compute_scheduler(struct amdgpu_device *adev, bool sus for (i = 0; i < adev->gfx.num_compute_rings; i++) { struct amdgpu_ring *ring = &adev->gfx.compute_ring[i]; - if (!(ring && drm_sched_wqueue_ready(&ring->sched))) + if (!amdgpu_ring_sched_ready(ring)) continue; /* stop secheduler and drain ring. */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index d17b2452cb1f..231fd927dcfb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -2085,21 +2085,35 @@ out: return ret; } -void amdgpu_amdkfd_gpuvm_dmaunmap_mem(struct kgd_mem *mem, void *drm_priv) +int amdgpu_amdkfd_gpuvm_dmaunmap_mem(struct kgd_mem *mem, void *drm_priv) { struct kfd_mem_attachment *entry; struct amdgpu_vm *vm; + int ret; vm = drm_priv_to_vm(drm_priv); mutex_lock(&mem->lock); + ret = amdgpu_bo_reserve(mem->bo, true); + if (ret) + goto out; + list_for_each_entry(entry, &mem->attachments, list) { - if (entry->bo_va->base.vm == vm) - kfd_mem_dmaunmap_attachment(mem, entry); + if (entry->bo_va->base.vm != vm) + continue; + if (entry->bo_va->base.bo->tbo.ttm && + !entry->bo_va->base.bo->tbo.ttm->sg) + continue; + + kfd_mem_dmaunmap_attachment(mem, entry); } + amdgpu_bo_unreserve(mem->bo); +out: mutex_unlock(&mem->lock); + + return ret; } int amdgpu_amdkfd_gpuvm_unmap_memory_from_gpu( @@ -2802,7 +2816,7 @@ unlock_out: put_task_struct(usertask); } -static void replace_eviction_fence(struct dma_fence **ef, +static void replace_eviction_fence(struct dma_fence __rcu **ef, struct dma_fence *new_ef) { struct dma_fence *old_ef = rcu_replace_pointer(*ef, new_ef, true @@ -2837,7 +2851,7 @@ static void replace_eviction_fence(struct dma_fence **ef, * 7. Add fence to all PD and PT BOs. * 8. Unreserve all BOs */ -int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef) +int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu **ef) { struct amdkfd_process_info *process_info = info; struct amdgpu_vm *peer_vm; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index e485dd3357c6..1afbb2e932c6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -1678,7 +1678,7 @@ static int amdgpu_debugfs_test_ib_show(struct seq_file *m, void *unused) for (i = 0; i < AMDGPU_MAX_RINGS; i++) { struct amdgpu_ring *ring = adev->rings[i]; - if (!ring || !drm_sched_wqueue_ready(&ring->sched)) + if (!amdgpu_ring_sched_ready(ring)) continue; drm_sched_wqueue_stop(&ring->sched); } @@ -1694,7 +1694,7 @@ static int amdgpu_debugfs_test_ib_show(struct seq_file *m, void *unused) for (i = 0; i < AMDGPU_MAX_RINGS; i++) { struct amdgpu_ring *ring = adev->rings[i]; - if (!ring || !drm_sched_wqueue_ready(&ring->sched)) + if (!amdgpu_ring_sched_ready(ring)) continue; drm_sched_wqueue_start(&ring->sched); } @@ -1916,8 +1916,8 @@ static int amdgpu_debugfs_ib_preempt(void *data, u64 val) ring = adev->rings[val]; - if (!ring || !ring->funcs->preempt_ib || - !drm_sched_wqueue_ready(&ring->sched)) + if (!amdgpu_ring_sched_ready(ring) || + !ring->funcs->preempt_ib) return -EINVAL; /* the last preemption failed */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 5bb444bb36ce..fdde7488d0ed 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1544,6 +1544,7 @@ bool amdgpu_device_need_post(struct amdgpu_device *adev) return true; fw_ver = *((uint32_t *)adev->pm.fw->data + 69); + release_firmware(adev->pm.fw); if (fw_ver < 0x00160e00) return true; } @@ -4120,23 +4121,13 @@ int amdgpu_device_init(struct amdgpu_device *adev, } } } else { - switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { - case IP_VERSION(13, 0, 0): - case IP_VERSION(13, 0, 7): - case IP_VERSION(13, 0, 10): - r = psp_gpu_reset(adev); - break; - default: - tmp = amdgpu_reset_method; - /* It should do a default reset when loading or reloading the driver, - * regardless of the module parameter reset_method. - */ - amdgpu_reset_method = AMD_RESET_METHOD_NONE; - r = amdgpu_asic_reset(adev); - amdgpu_reset_method = tmp; - break; - } - + tmp = amdgpu_reset_method; + /* It should do a default reset when loading or reloading the driver, + * regardless of the module parameter reset_method. + */ + amdgpu_reset_method = AMD_RESET_METHOD_NONE; + r = amdgpu_asic_reset(adev); + amdgpu_reset_method = tmp; if (r) { dev_err(adev->dev, "asic reset on init failed\n"); goto failed; @@ -5030,7 +5021,7 @@ bool amdgpu_device_has_job_running(struct amdgpu_device *adev) for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = adev->rings[i]; - if (!ring || !drm_sched_wqueue_ready(&ring->sched)) + if (!amdgpu_ring_sched_ready(ring)) continue; spin_lock(&ring->sched.job_list_lock); @@ -5169,7 +5160,7 @@ int amdgpu_device_pre_asic_reset(struct amdgpu_device *adev, for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = adev->rings[i]; - if (!ring || !drm_sched_wqueue_ready(&ring->sched)) + if (!amdgpu_ring_sched_ready(ring)) continue; /* Clear job fence from fence drv to avoid force_completion @@ -5245,7 +5236,6 @@ int amdgpu_do_asic_reset(struct list_head *device_list_handle, struct amdgpu_device *tmp_adev = NULL; bool need_full_reset, skip_hw_reset, vram_lost = false; int r = 0; - bool gpu_reset_for_dev_remove = 0; /* Try reset handler method first */ tmp_adev = list_first_entry(device_list_handle, struct amdgpu_device, @@ -5265,10 +5255,6 @@ int amdgpu_do_asic_reset(struct list_head *device_list_handle, test_bit(AMDGPU_NEED_FULL_RESET, &reset_context->flags); skip_hw_reset = test_bit(AMDGPU_SKIP_HW_RESET, &reset_context->flags); - gpu_reset_for_dev_remove = - test_bit(AMDGPU_RESET_FOR_DEVICE_REMOVE, &reset_context->flags) && - test_bit(AMDGPU_NEED_FULL_RESET, &reset_context->flags); - /* * ASIC reset has to be done on all XGMI hive nodes ASAP * to allow proper links negotiation in FW (within 1 sec) @@ -5311,18 +5297,6 @@ int amdgpu_do_asic_reset(struct list_head *device_list_handle, amdgpu_ras_intr_cleared(); } - /* Since the mode1 reset affects base ip blocks, the - * phase1 ip blocks need to be resumed. Otherwise there - * will be a BIOS signature error and the psp bootloader - * can't load kdb on the next amdgpu install. - */ - if (gpu_reset_for_dev_remove) { - list_for_each_entry(tmp_adev, device_list_handle, reset_list) - amdgpu_device_ip_resume_phase1(tmp_adev); - - goto end; - } - list_for_each_entry(tmp_adev, device_list_handle, reset_list) { if (need_full_reset) { /* post card */ @@ -5559,11 +5533,6 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, int i, r = 0; bool need_emergency_restart = false; bool audio_suspended = false; - bool gpu_reset_for_dev_remove = false; - - gpu_reset_for_dev_remove = - test_bit(AMDGPU_RESET_FOR_DEVICE_REMOVE, &reset_context->flags) && - test_bit(AMDGPU_NEED_FULL_RESET, &reset_context->flags); /* * Special case: RAS triggered and full reset isn't supported @@ -5601,7 +5570,7 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, if (!amdgpu_sriov_vf(adev) && (adev->gmc.xgmi.num_physical_nodes > 1)) { list_for_each_entry(tmp_adev, &hive->device_list, gmc.xgmi.head) { list_add_tail(&tmp_adev->reset_list, &device_list); - if (gpu_reset_for_dev_remove && adev->shutdown) + if (adev->shutdown) tmp_adev->shutdown = true; } if (!list_is_first(&adev->reset_list, &device_list)) @@ -5658,7 +5627,7 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = tmp_adev->rings[i]; - if (!ring || !drm_sched_wqueue_ready(&ring->sched)) + if (!amdgpu_ring_sched_ready(ring)) continue; drm_sched_stop(&ring->sched, job ? &job->base : NULL); @@ -5686,10 +5655,6 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, retry: /* Rest of adevs pre asic reset from XGMI hive. */ list_for_each_entry(tmp_adev, device_list_handle, reset_list) { - if (gpu_reset_for_dev_remove) { - /* Workaroud for ASICs need to disable SMC first */ - amdgpu_device_smu_fini_early(tmp_adev); - } r = amdgpu_device_pre_asic_reset(tmp_adev, reset_context); /*TODO Should we stop ?*/ if (r) { @@ -5721,9 +5686,6 @@ retry: /* Rest of adevs pre asic reset from XGMI hive. */ r = amdgpu_do_asic_reset(device_list_handle, reset_context); if (r && r == -EAGAIN) goto retry; - - if (!r && gpu_reset_for_dev_remove) - goto recover_end; } skip_hw_reset: @@ -5734,7 +5696,7 @@ skip_hw_reset: for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = tmp_adev->rings[i]; - if (!ring || !drm_sched_wqueue_ready(&ring->sched)) + if (!amdgpu_ring_sched_ready(ring)) continue; drm_sched_start(&ring->sched, true); @@ -5779,7 +5741,6 @@ skip_sched_resume: amdgpu_ras_set_error_query_ready(tmp_adev, true); } -recover_end: tmp_adev = list_first_entry(device_list_handle, struct amdgpu_device, reset_list); amdgpu_device_unlock_reset_domain(tmp_adev->reset_domain); @@ -6090,7 +6051,7 @@ pci_ers_result_t amdgpu_pci_error_detected(struct pci_dev *pdev, pci_channel_sta for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = adev->rings[i]; - if (!ring || !drm_sched_wqueue_ready(&ring->sched)) + if (!amdgpu_ring_sched_ready(ring)) continue; drm_sched_stop(&ring->sched, NULL); @@ -6218,7 +6179,7 @@ void amdgpu_pci_resume(struct pci_dev *pdev) for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = adev->rings[i]; - if (!ring || !drm_sched_wqueue_ready(&ring->sched)) + if (!amdgpu_ring_sched_ready(ring)) continue; drm_sched_start(&ring->sched, true); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 0431eafa86b5..c7d60dd0fb97 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -1963,8 +1963,6 @@ static int amdgpu_discovery_set_gc_ip_blocks(struct amdgpu_device *adev) amdgpu_device_ip_block_add(adev, &gfx_v9_0_ip_block); break; case IP_VERSION(9, 4, 3): - if (!amdgpu_exp_hw_support) - return -EINVAL; amdgpu_device_ip_block_add(adev, &gfx_v9_4_3_ip_block); break; case IP_VERSION(10, 1, 10): diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 852cec98ff26..211501ea9169 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -128,6 +128,7 @@ enum AMDGPU_DEBUG_MASK { AMDGPU_DEBUG_VM = BIT(0), AMDGPU_DEBUG_LARGEBAR = BIT(1), AMDGPU_DEBUG_DISABLE_GPU_SOFT_RECOVERY = BIT(2), + AMDGPU_DEBUG_USE_VRAM_FW_BUF = BIT(3), }; unsigned int amdgpu_vram_limit = UINT_MAX; @@ -210,7 +211,6 @@ int amdgpu_seamless = -1; /* auto */ uint amdgpu_debug_mask; int amdgpu_agp = -1; /* auto */ int amdgpu_wbrf = -1; -int fw_bo_location = -1; static void amdgpu_drv_delayed_reset_work_handler(struct work_struct *work); @@ -990,10 +990,6 @@ MODULE_PARM_DESC(wbrf, "Enable Wifi RFI interference mitigation (0 = disabled, 1 = enabled, -1 = auto(default)"); module_param_named(wbrf, amdgpu_wbrf, int, 0444); -MODULE_PARM_DESC(fw_bo_location, - "location to put firmware bo for frontdoor loading (-1 = auto (default), 0 = on ram, 1 = on vram"); -module_param(fw_bo_location, int, 0644); - /* These devices are not supported by amdgpu. * They are supported by the mach64, r128, radeon drivers */ @@ -2122,6 +2118,11 @@ static void amdgpu_init_debug_options(struct amdgpu_device *adev) pr_info("debug: soft reset for GPU recovery disabled\n"); adev->debug_disable_soft_recovery = true; } + + if (amdgpu_debug_mask & AMDGPU_DEBUG_USE_VRAM_FW_BUF) { + pr_info("debug: place fw in vram for frontdoor loading\n"); + adev->debug_use_vram_fw_buf = true; + } } static unsigned long amdgpu_fix_asic_type(struct pci_dev *pdev, unsigned long flags) @@ -2233,6 +2234,8 @@ static int amdgpu_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, ddev); + amdgpu_init_debug_options(adev); + ret = amdgpu_driver_load_kms(adev, flags); if (ret) goto err_pci; @@ -2252,6 +2255,10 @@ retry_init: if (ret) goto err_pci; + ret = amdgpu_amdkfd_drm_client_create(adev); + if (ret) + goto err_pci; + /* * 1. don't init fbdev on hw without DCE * 2. don't init fbdev if there are no connectors @@ -2313,8 +2320,6 @@ retry_init: amdgpu_get_secondary_funcs(adev); } - amdgpu_init_debug_options(adev); - return 0; err_pci: @@ -2336,38 +2341,6 @@ amdgpu_pci_remove(struct pci_dev *pdev) pm_runtime_forbid(dev->dev); } - if (amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 2) && - !amdgpu_sriov_vf(adev)) { - bool need_to_reset_gpu = false; - - if (adev->gmc.xgmi.num_physical_nodes > 1) { - struct amdgpu_hive_info *hive; - - hive = amdgpu_get_xgmi_hive(adev); - if (hive->device_remove_count == 0) - need_to_reset_gpu = true; - hive->device_remove_count++; - amdgpu_put_xgmi_hive(hive); - } else { - need_to_reset_gpu = true; - } - - /* Workaround for ASICs need to reset SMU. - * Called only when the first device is removed. - */ - if (need_to_reset_gpu) { - struct amdgpu_reset_context reset_context; - - adev->shutdown = true; - memset(&reset_context, 0, sizeof(reset_context)); - reset_context.method = AMD_RESET_METHOD_NONE; - reset_context.reset_req_dev = adev; - set_bit(AMDGPU_NEED_FULL_RESET, &reset_context.flags); - set_bit(AMDGPU_RESET_FOR_DEVICE_REMOVE, &reset_context.flags); - amdgpu_device_gpu_recover(adev, NULL, &reset_context); - } - } - amdgpu_driver_unload_kms(dev); /* @@ -2503,6 +2476,7 @@ static int amdgpu_pmops_suspend(struct device *dev) struct drm_device *drm_dev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(drm_dev); + adev->suspend_complete = false; if (amdgpu_acpi_is_s0ix_active(adev)) adev->in_s0ix = true; else if (amdgpu_acpi_is_s3_active(adev)) @@ -2517,6 +2491,7 @@ static int amdgpu_pmops_suspend_noirq(struct device *dev) struct drm_device *drm_dev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(drm_dev); + adev->suspend_complete = true; if (amdgpu_acpi_should_gpu_reset(adev)) return amdgpu_asic_reset(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c index 73b8cca35bab..c623e23049d1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c @@ -121,6 +121,7 @@ int amdgpu_gart_table_ram_alloc(struct amdgpu_device *adev) struct amdgpu_bo_param bp; dma_addr_t dma_addr; struct page *p; + unsigned long x; int ret; if (adev->gart.bo != NULL) @@ -130,6 +131,10 @@ int amdgpu_gart_table_ram_alloc(struct amdgpu_device *adev) if (!p) return -ENOMEM; + /* assign pages to this device */ + for (x = 0; x < (1UL << order); x++) + p[x].mapping = adev->mman.bdev.dev_mapping; + /* If the hardware does not support UTCL2 snooping of the CPU caches * then set_memory_wc() could be used as a workaround to mark the pages * as write combine memory. @@ -223,6 +228,7 @@ void amdgpu_gart_table_ram_free(struct amdgpu_device *adev) unsigned int order = get_order(adev->gart.table_size); struct sg_table *sg = adev->gart.bo->tbo.sg; struct page *p; + unsigned long x; int ret; ret = amdgpu_bo_reserve(adev->gart.bo, false); @@ -234,6 +240,8 @@ void amdgpu_gart_table_ram_free(struct amdgpu_device *adev) sg_free_table(sg); kfree(sg); p = virt_to_page(adev->gart.ptr); + for (x = 0; x < (1UL << order); x++) + p[x].mapping = NULL; __free_pages(p, order); adev->gart.ptr = NULL; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index d2f273d77e59..55784a9f26c4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -1045,21 +1045,28 @@ int amdgpu_gmc_vram_checking(struct amdgpu_device *adev) * seconds, so here, we just pick up three parts for emulation. */ ret = memcmp(vram_ptr, cptr, 10); - if (ret) - return ret; + if (ret) { + ret = -EIO; + goto release_buffer; + } ret = memcmp(vram_ptr + (size / 2), cptr, 10); - if (ret) - return ret; + if (ret) { + ret = -EIO; + goto release_buffer; + } ret = memcmp(vram_ptr + size - 10, cptr, 10); - if (ret) - return ret; + if (ret) { + ret = -EIO; + goto release_buffer; + } +release_buffer: amdgpu_bo_free_kernel(&vram_bo, &vram_gpu, &vram_ptr); - return 0; + return ret; } static ssize_t current_memory_partition_show( diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c index 82608df43396..d79cb13e1aa8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c @@ -175,7 +175,6 @@ struct amdgpu_i2c_chan *amdgpu_i2c_create(struct drm_device *dev, i2c->rec = *rec; i2c->adapter.owner = THIS_MODULE; - i2c->adapter.class = I2C_CLASS_DDC; i2c->adapter.dev.parent = dev->dev; i2c->dev = dev; i2c_set_adapdata(&i2c->adapter, i2c); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index b5ebafd4a3ad..bf4f48fe438d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -1105,7 +1105,12 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) if (amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&ui32, &ui32_size)) { - return -EINVAL; + /* fall back to input power for backwards compat */ + if (amdgpu_dpm_read_sensor(adev, + AMDGPU_PP_SENSOR_GPU_INPUT_POWER, + (void *)&ui32, &ui32_size)) { + return -EINVAL; + } } ui32 >>= 8; break; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index 2addbdf88394..0328616473f8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -466,7 +466,7 @@ static int psp_sw_init(void *handle) } ret = amdgpu_bo_create_kernel(adev, PSP_1_MEG, PSP_1_MEG, - (amdgpu_sriov_vf(adev) || fw_bo_location == 1) ? + (amdgpu_sriov_vf(adev) || adev->debug_use_vram_fw_buf) ? AMDGPU_GEM_DOMAIN_VRAM : AMDGPU_GEM_DOMAIN_GTT, &psp->fw_pri_bo, &psp->fw_pri_mc_addr, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c index 468a67b302d4..ca5c86e5f7cd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c @@ -362,7 +362,7 @@ static ssize_t ta_if_invoke_debugfs_write(struct file *fp, const char *buf, size } } - if (copy_to_user((char *)buf, context->mem_context.shared_buf, shared_buf_len)) + if (copy_to_user((char *)&buf[copy_pos], context->mem_context.shared_buf, shared_buf_len)) ret = -EFAULT; err_free_shared_buf: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index fc42fb6ee191..31823a30dea2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -305,11 +305,13 @@ static int amdgpu_ras_debugfs_ctrl_parse_data(struct file *f, return -EINVAL; data->head.block = block_id; - /* only ue and ce errors are supported */ + /* only ue, ce and poison errors are supported */ if (!memcmp("ue", err, 2)) data->head.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE; else if (!memcmp("ce", err, 2)) data->head.type = AMDGPU_RAS_ERROR__SINGLE_CORRECTABLE; + else if (!memcmp("poison", err, 6)) + data->head.type = AMDGPU_RAS_ERROR__POISON; else return -EINVAL; @@ -431,9 +433,10 @@ static void amdgpu_ras_instance_mask_check(struct amdgpu_device *adev, * The block is one of: umc, sdma, gfx, etc. * see ras_block_string[] for details * - * The error type is one of: ue, ce, where, + * The error type is one of: ue, ce and poison where, * ue is multi-uncorrectable * ce is single-correctable + * poison is poison * * The sub-block is a the sub-block index, pass 0 if there is no sub-block. * The address and value are hexadecimal numbers, leading 0x is optional. @@ -1067,8 +1070,7 @@ static void amdgpu_ras_error_print_error_data(struct amdgpu_device *adev, mcm_info = &err_info->mcm_info; if (err_info->ce_count) { dev_info(adev->dev, "socket: %d, die: %d, " - "%lld new correctable hardware errors detected in %s block, " - "no user action is needed\n", + "%lld new correctable hardware errors detected in %s block\n", mcm_info->socket_id, mcm_info->die_id, err_info->ce_count, @@ -1080,8 +1082,7 @@ static void amdgpu_ras_error_print_error_data(struct amdgpu_device *adev, err_info = &err_node->err_info; mcm_info = &err_info->mcm_info; dev_info(adev->dev, "socket: %d, die: %d, " - "%lld correctable hardware errors detected in total in %s block, " - "no user action is needed\n", + "%lld correctable hardware errors detected in total in %s block\n", mcm_info->socket_id, mcm_info->die_id, err_info->ce_count, blk_name); } } @@ -1108,16 +1109,14 @@ static void amdgpu_ras_error_generate_report(struct amdgpu_device *adev, adev->smuio.funcs->get_die_id) { dev_info(adev->dev, "socket: %d, die: %d " "%ld correctable hardware errors " - "detected in %s block, no user " - "action is needed.\n", + "detected in %s block\n", adev->smuio.funcs->get_socket_id(adev), adev->smuio.funcs->get_die_id(adev), ras_mgr->err_data.ce_count, blk_name); } else { dev_info(adev->dev, "%ld correctable hardware errors " - "detected in %s block, no user " - "action is needed.\n", + "detected in %s block\n", ras_mgr->err_data.ce_count, blk_name); } @@ -1920,7 +1919,7 @@ static void amdgpu_ras_interrupt_poison_creation_handler(struct ras_manager *obj struct amdgpu_iv_entry *entry) { dev_info(obj->adev->dev, - "Poison is created, no user action is needed.\n"); + "Poison is created\n"); } static void amdgpu_ras_interrupt_umc_handler(struct ras_manager *obj, @@ -2920,6 +2919,11 @@ int amdgpu_ras_init(struct amdgpu_device *adev) amdgpu_ras_query_poison_mode(adev); + /* Packed socket_id to ras feature mask bits[31:29] */ + if (adev->smuio.funcs && + adev->smuio.funcs->get_socket_id) + con->features |= ((adev->smuio.funcs->get_socket_id(adev)) << 29); + /* Get RAS schema for particular SOC */ con->schema = amdgpu_get_ras_schema(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h index b0335a1c5e90..19899f6b9b2b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.h @@ -32,7 +32,6 @@ enum AMDGPU_RESET_FLAGS { AMDGPU_NEED_FULL_RESET = 0, AMDGPU_SKIP_HW_RESET = 1, - AMDGPU_RESET_FOR_DEVICE_REMOVE = 2, }; struct amdgpu_reset_context { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c index 45424ebf9681..5505d646f43a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c @@ -635,6 +635,7 @@ int amdgpu_ring_test_helper(struct amdgpu_ring *ring) ring->name); ring->sched.ready = !r; + return r; } @@ -717,3 +718,14 @@ void amdgpu_ring_ib_on_emit_de(struct amdgpu_ring *ring) if (ring->is_sw_ring) amdgpu_sw_ring_ib_mark_offset(ring, AMDGPU_MUX_OFFSET_TYPE_DE); } + +bool amdgpu_ring_sched_ready(struct amdgpu_ring *ring) +{ + if (!ring) + return false; + + if (ring->no_scheduler || !drm_sched_wqueue_ready(&ring->sched)) + return false; + + return true; +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h index bbb53720a018..fe1a61eb6e4c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h @@ -450,5 +450,5 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned num_ibs, int amdgpu_ib_pool_init(struct amdgpu_device *adev); void amdgpu_ib_pool_fini(struct amdgpu_device *adev); int amdgpu_ib_ring_tests(struct amdgpu_device *adev); - +bool amdgpu_ring_sched_ready(struct amdgpu_ring *ring); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c index d334e42fe0eb..3e12763e477a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c @@ -1062,7 +1062,7 @@ int amdgpu_ucode_create_bo(struct amdgpu_device *adev) { if (adev->firmware.load_type != AMDGPU_FW_LOAD_DIRECT) { amdgpu_bo_create_kernel(adev, adev->firmware.fw_size, PAGE_SIZE, - (amdgpu_sriov_vf(adev) || fw_bo_location == 1) ? + (amdgpu_sriov_vf(adev) || adev->debug_use_vram_fw_buf) ? AMDGPU_GEM_DOMAIN_VRAM : AMDGPU_GEM_DOMAIN_GTT, &adev->firmware.fw_buf, &adev->firmware.fw_buf_mc, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index b6cd565562ad..4740dd65b99d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -116,7 +116,7 @@ struct amdgpu_mem_stats; #define AMDGPU_VM_FAULT_STOP_FIRST 1 #define AMDGPU_VM_FAULT_STOP_ALWAYS 2 -/* Reserve 4MB VRAM for page tables */ +/* How much VRAM be reserved for page tables */ #define AMDGPU_VM_RESERVED_VRAM (8ULL << 20) /* diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c index 6f149b54d4d3..b9a15d51eb5c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c @@ -59,11 +59,8 @@ static inline uint16_t complete_integer_division_u16( static uint16_t vpe_u1_8_from_fraction(uint16_t numerator, uint16_t denominator) { - bool arg1_negative = numerator < 0; - bool arg2_negative = denominator < 0; - - uint16_t arg1_value = (uint16_t)(arg1_negative ? -numerator : numerator); - uint16_t arg2_value = (uint16_t)(arg2_negative ? -denominator : denominator); + u16 arg1_value = numerator; + u16 arg2_value = denominator; uint16_t remainder; @@ -100,9 +97,6 @@ static uint16_t vpe_u1_8_from_fraction(uint16_t numerator, uint16_t denominator) res_value += summand; } - if (arg1_negative ^ arg2_negative) - res_value = -res_value; - return res_value; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 08916538a615..8db880244324 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -221,8 +221,23 @@ static struct attribute *amdgpu_vram_mgr_attributes[] = { NULL }; +static umode_t amdgpu_vram_attrs_is_visible(struct kobject *kobj, + struct attribute *attr, int i) +{ + struct device *dev = kobj_to_dev(kobj); + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + + if (attr == &dev_attr_mem_info_vram_vendor.attr && + !adev->gmc.vram_vendor) + return 0; + + return attr->mode; +} + const struct attribute_group amdgpu_vram_mgr_attr_group = { - .attrs = amdgpu_vram_mgr_attributes + .attrs = amdgpu_vram_mgr_attributes, + .is_visible = amdgpu_vram_attrs_is_visible }; /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.h index 6cab882e8061..1592c63b3099 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.h @@ -43,7 +43,6 @@ struct amdgpu_hive_info { } pstate; struct amdgpu_reset_domain *reset_domain; - uint32_t device_remove_count; atomic_t ras_recovery; }; diff --git a/drivers/gpu/drm/amd/amdgpu/athub_v3_0.c b/drivers/gpu/drm/amd/amdgpu/athub_v3_0.c index f0737fb3a999..d1bba9c64e16 100644 --- a/drivers/gpu/drm/amd/amdgpu/athub_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/athub_v3_0.c @@ -30,6 +30,8 @@ #define regATHUB_MISC_CNTL_V3_0_1 0x00d7 #define regATHUB_MISC_CNTL_V3_0_1_BASE_IDX 0 +#define regATHUB_MISC_CNTL_V3_3_0 0x00d8 +#define regATHUB_MISC_CNTL_V3_3_0_BASE_IDX 0 static uint32_t athub_v3_0_get_cg_cntl(struct amdgpu_device *adev) @@ -40,6 +42,9 @@ static uint32_t athub_v3_0_get_cg_cntl(struct amdgpu_device *adev) case IP_VERSION(3, 0, 1): data = RREG32_SOC15(ATHUB, 0, regATHUB_MISC_CNTL_V3_0_1); break; + case IP_VERSION(3, 3, 0): + data = RREG32_SOC15(ATHUB, 0, regATHUB_MISC_CNTL_V3_3_0); + break; default: data = RREG32_SOC15(ATHUB, 0, regATHUB_MISC_CNTL); break; @@ -53,6 +58,9 @@ static void athub_v3_0_set_cg_cntl(struct amdgpu_device *adev, uint32_t data) case IP_VERSION(3, 0, 1): WREG32_SOC15(ATHUB, 0, regATHUB_MISC_CNTL_V3_0_1, data); break; + case IP_VERSION(3, 3, 0): + WREG32_SOC15(ATHUB, 0, regATHUB_MISC_CNTL_V3_3_0, data); + break; default: WREG32_SOC15(ATHUB, 0, regATHUB_MISC_CNTL, data); break; diff --git a/drivers/gpu/drm/amd/amdgpu/cik_ih.c b/drivers/gpu/drm/amd/amdgpu/cik_ih.c index 6f7c031dd197..f24e34dc33d1 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik_ih.c +++ b/drivers/gpu/drm/amd/amdgpu/cik_ih.c @@ -204,6 +204,12 @@ static u32 cik_ih_get_wptr(struct amdgpu_device *adev, tmp = RREG32(mmIH_RB_CNTL); tmp |= IH_RB_CNTL__WPTR_OVERFLOW_CLEAR_MASK; WREG32(mmIH_RB_CNTL, tmp); + + /* Unset the CLEAR_OVERFLOW bit immediately so new overflows + * can be detected. + */ + tmp &= ~IH_RB_CNTL__WPTR_OVERFLOW_CLEAR_MASK; + WREG32(mmIH_RB_CNTL, tmp); } return (wptr & ih->ptr_mask); } diff --git a/drivers/gpu/drm/amd/amdgpu/cz_ih.c b/drivers/gpu/drm/amd/amdgpu/cz_ih.c index b8c47e0cf37a..c19681492efa 100644 --- a/drivers/gpu/drm/amd/amdgpu/cz_ih.c +++ b/drivers/gpu/drm/amd/amdgpu/cz_ih.c @@ -216,6 +216,11 @@ static u32 cz_ih_get_wptr(struct amdgpu_device *adev, tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1); WREG32(mmIH_RB_CNTL, tmp); + /* Unset the CLEAR_OVERFLOW bit immediately so new overflows + * can be detected. + */ + tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 0); + WREG32(mmIH_RB_CNTL, tmp); out: return (wptr & ih->ptr_mask); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 73f6d7e72c73..dcdecb18b230 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -3996,16 +3996,13 @@ static int gfx_v10_0_init_microcode(struct amdgpu_device *adev) if (!amdgpu_sriov_vf(adev)) { snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_rlc.bin", ucode_prefix); - err = amdgpu_ucode_request(adev, &adev->gfx.rlc_fw, fw_name); - /* don't check this. There are apparently firmwares in the wild with - * incorrect size in the header - */ - if (err == -ENODEV) - goto out; + err = request_firmware(&adev->gfx.rlc_fw, fw_name, adev->dev); if (err) - dev_dbg(adev->dev, - "gfx10: amdgpu_ucode_request() failed \"%s\"\n", - fw_name); + goto out; + + /* don't validate this firmware. There are apparently firmwares + * in the wild with incorrect size in the header + */ rlc_hdr = (const struct rlc_firmware_header_v2_0 *)adev->gfx.rlc_fw->data; version_major = le16_to_cpu(rlc_hdr->header.header_version_major); version_minor = le16_to_cpu(rlc_hdr->header.header_version_minor); @@ -4030,8 +4027,6 @@ static int gfx_v10_0_init_microcode(struct amdgpu_device *adev) err = 0; adev->gfx.mec2_fw = NULL; } - amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_MEC2); - amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_MEC2_JT); gfx_v10_0_check_fw_write_wait(adev); out: @@ -6592,7 +6587,7 @@ static int gfx_v10_0_compute_mqd_init(struct amdgpu_device *adev, void *m, #ifdef __BIG_ENDIAN tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, ENDIAN_SWAP, 1); #endif - tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, UNORD_DISPATCH, 0); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, UNORD_DISPATCH, 1); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, TUNNEL_DISPATCH, prop->allow_tunneling); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, PRIV_STATE, 1); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 2fbcd9765980..4f3bfdc75b37 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -107,23 +107,6 @@ static const struct soc15_reg_golden golden_settings_gc_11_0_1[] = SOC15_REG_GOLDEN_VALUE(GC, 0, regTCP_CNTL2, 0xfcffffff, 0x0000000a) }; -static const struct soc15_reg_golden golden_settings_gc_11_5_0[] = { - SOC15_REG_GOLDEN_VALUE(GC, 0, regDB_DEBUG5, 0xffffffff, 0x00000800), - SOC15_REG_GOLDEN_VALUE(GC, 0, regGB_ADDR_CONFIG, 0x0c1807ff, 0x00000242), - SOC15_REG_GOLDEN_VALUE(GC, 0, regGCR_GENERAL_CNTL, 0x1ff1ffff, 0x00000500), - SOC15_REG_GOLDEN_VALUE(GC, 0, regGL2A_ADDR_MATCH_MASK, 0xffffffff, 0xfffffff3), - SOC15_REG_GOLDEN_VALUE(GC, 0, regGL2C_ADDR_MATCH_MASK, 0xffffffff, 0xfffffff3), - SOC15_REG_GOLDEN_VALUE(GC, 0, regGL2C_CTRL, 0xffffffff, 0xf37fff3f), - SOC15_REG_GOLDEN_VALUE(GC, 0, regGL2C_CTRL3, 0xfffffffb, 0x00f40188), - SOC15_REG_GOLDEN_VALUE(GC, 0, regGL2C_CTRL4, 0xf0ffffff, 0x8000b007), - SOC15_REG_GOLDEN_VALUE(GC, 0, regPA_CL_ENHANCE, 0xf1ffffff, 0x00880007), - SOC15_REG_GOLDEN_VALUE(GC, 0, regPC_CONFIG_CNTL_1, 0xffffffff, 0x00010000), - SOC15_REG_GOLDEN_VALUE(GC, 0, regTA_CNTL_AUX, 0xf7f7ffff, 0x01030000), - SOC15_REG_GOLDEN_VALUE(GC, 0, regTA_CNTL2, 0x007f0000, 0x00000000), - SOC15_REG_GOLDEN_VALUE(GC, 0, regTCP_CNTL2, 0xffcfffff, 0x0000200a), - SOC15_REG_GOLDEN_VALUE(GC, 0, regUTCL1_CTRL_2, 0xffffffff, 0x0000048f) -}; - #define DEFAULT_SH_MEM_CONFIG \ ((SH_MEM_ADDRESS_MODE_64 << SH_MEM_CONFIG__ADDRESS_MODE__SHIFT) | \ (SH_MEM_ALIGNMENT_MODE_UNALIGNED << SH_MEM_CONFIG__ALIGNMENT_MODE__SHIFT) | \ @@ -304,11 +287,6 @@ static void gfx_v11_0_init_golden_registers(struct amdgpu_device *adev) golden_settings_gc_11_0_1, (const u32)ARRAY_SIZE(golden_settings_gc_11_0_1)); break; - case IP_VERSION(11, 5, 0): - soc15_program_register_sequence(adev, - golden_settings_gc_11_5_0, - (const u32)ARRAY_SIZE(golden_settings_gc_11_5_0)); - break; default: break; } @@ -3846,7 +3824,7 @@ static int gfx_v11_0_compute_mqd_init(struct amdgpu_device *adev, void *m, (order_base_2(prop->queue_size / 4) - 1)); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, RPTR_BLOCK_SIZE, (order_base_2(AMDGPU_GPU_PAGE_SIZE / 4) - 1)); - tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, UNORD_DISPATCH, 0); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, UNORD_DISPATCH, 1); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, TUNNEL_DISPATCH, prop->allow_tunneling); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, PRIV_STATE, 1); @@ -6383,6 +6361,9 @@ static int gfx_v11_0_get_cu_info(struct amdgpu_device *adev, mutex_lock(&adev->grbm_idx_mutex); for (i = 0; i < adev->gfx.config.max_shader_engines; i++) { for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) { + bitmap = i * adev->gfx.config.max_sh_per_se + j; + if (!((gfx_v11_0_get_sa_active_bitmap(adev) >> bitmap) & 1)) + continue; mask = 1; counter = 0; gfx_v11_0_select_se_sh(adev, i, j, 0xffffffff, 0); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 69c500910746..3bc6943365a4 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -3034,6 +3034,14 @@ static int gfx_v9_0_cp_gfx_start(struct amdgpu_device *adev) gfx_v9_0_cp_gfx_enable(adev, true); + /* Now only limit the quirk on the APU gfx9 series and already + * confirmed that the APU gfx10/gfx11 needn't such update. + */ + if (adev->flags & AMD_IS_APU && + adev->in_s3 && !adev->suspend_complete) { + DRM_INFO(" Will skip the CSB packet resubmit\n"); + return 0; + } r = amdgpu_ring_alloc(ring, gfx_v9_0_get_csb_size(adev) + 4 + 3); if (r) { DRM_ERROR("amdgpu: cp failed to lock ring (%d).\n", r); diff --git a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c index 95d06da544e2..49aecdcee006 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c +++ b/drivers/gpu/drm/amd/amdgpu/gfxhub_v1_2.c @@ -456,10 +456,12 @@ static void gfxhub_v1_2_xcc_gart_disable(struct amdgpu_device *adev, WREG32_SOC15_RLC(GC, GET_INST(GC, j), regMC_VM_MX_L1_TLB_CNTL, tmp); /* Setup L2 cache */ - tmp = RREG32_SOC15(GC, GET_INST(GC, j), regVM_L2_CNTL); - tmp = REG_SET_FIELD(tmp, VM_L2_CNTL, ENABLE_L2_CACHE, 0); - WREG32_SOC15(GC, GET_INST(GC, j), regVM_L2_CNTL, tmp); - WREG32_SOC15(GC, GET_INST(GC, j), regVM_L2_CNTL3, 0); + if (!amdgpu_sriov_vf(adev)) { + tmp = RREG32_SOC15(GC, GET_INST(GC, j), regVM_L2_CNTL); + tmp = REG_SET_FIELD(tmp, VM_L2_CNTL, ENABLE_L2_CACHE, 0); + WREG32_SOC15(GC, GET_INST(GC, j), regVM_L2_CNTL, tmp); + WREG32_SOC15(GC, GET_INST(GC, j), regVM_L2_CNTL3, 0); + } } } diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c index 42e103d7077d..59d9215e5556 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c @@ -915,8 +915,8 @@ static int gmc_v6_0_hw_init(void *handle) if (amdgpu_emu_mode == 1) return amdgpu_gmc_vram_checking(adev); - else - return r; + + return 0; } static int gmc_v6_0_hw_fini(void *handle) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index efc16e580f1e..45a2f8e031a2 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -1099,8 +1099,8 @@ static int gmc_v7_0_hw_init(void *handle) if (amdgpu_emu_mode == 1) return amdgpu_gmc_vram_checking(adev); - else - return r; + + return 0; } static int gmc_v7_0_hw_fini(void *handle) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index ff4ae73d27ec..4422b27a3cc2 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -1219,8 +1219,8 @@ static int gmc_v8_0_hw_init(void *handle) if (amdgpu_emu_mode == 1) return amdgpu_gmc_vram_checking(adev); - else - return r; + + return 0; } static int gmc_v8_0_hw_fini(void *handle) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index f9039d64ff2d..e67a62db9e12 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -1947,13 +1947,6 @@ static int gmc_v9_0_init_mem_ranges(struct amdgpu_device *adev) static void gmc_v9_4_3_init_vram_info(struct amdgpu_device *adev) { - static const u32 regBIF_BIOS_SCRATCH_4 = 0x50; - u32 vram_info; - - if (!amdgpu_sriov_vf(adev)) { - vram_info = RREG32(regBIF_BIOS_SCRATCH_4); - adev->gmc.vram_vendor = vram_info & 0xF; - } adev->gmc.vram_type = AMDGPU_VRAM_TYPE_HBM; adev->gmc.vram_width = 128 * 64; } @@ -2340,8 +2333,8 @@ static int gmc_v9_0_hw_init(void *handle) if (amdgpu_emu_mode == 1) return amdgpu_gmc_vram_checking(adev); - else - return r; + + return 0; } /** diff --git a/drivers/gpu/drm/amd/amdgpu/iceland_ih.c b/drivers/gpu/drm/amd/amdgpu/iceland_ih.c index aecad530b10a..2c02ae69883d 100644 --- a/drivers/gpu/drm/amd/amdgpu/iceland_ih.c +++ b/drivers/gpu/drm/amd/amdgpu/iceland_ih.c @@ -215,6 +215,11 @@ static u32 iceland_ih_get_wptr(struct amdgpu_device *adev, tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1); WREG32(mmIH_RB_CNTL, tmp); + /* Unset the CLEAR_OVERFLOW bit immediately so new overflows + * can be detected. + */ + tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 0); + WREG32(mmIH_RB_CNTL, tmp); out: return (wptr & ih->ptr_mask); diff --git a/drivers/gpu/drm/amd/amdgpu/ih_v6_0.c b/drivers/gpu/drm/amd/amdgpu/ih_v6_0.c index d9ed7332d805..ad4ad39f128f 100644 --- a/drivers/gpu/drm/amd/amdgpu/ih_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/ih_v6_0.c @@ -418,6 +418,12 @@ static u32 ih_v6_0_get_wptr(struct amdgpu_device *adev, tmp = RREG32_NO_KIQ(ih_regs->ih_rb_cntl); tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1); WREG32_NO_KIQ(ih_regs->ih_rb_cntl, tmp); + + /* Unset the CLEAR_OVERFLOW bit immediately so new overflows + * can be detected. + */ + tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 0); + WREG32_NO_KIQ(ih_regs->ih_rb_cntl, tmp); out: return (wptr & ih->ptr_mask); } diff --git a/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c b/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c index 8fb05eae340a..b8da0fc29378 100644 --- a/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c +++ b/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c @@ -418,6 +418,13 @@ static u32 ih_v6_1_get_wptr(struct amdgpu_device *adev, tmp = RREG32_NO_KIQ(ih_regs->ih_rb_cntl); tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1); WREG32_NO_KIQ(ih_regs->ih_rb_cntl, tmp); + + /* Unset the CLEAR_OVERFLOW bit immediately so new overflows + * can be detected. + */ + tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 0); + WREG32_NO_KIQ(ih_regs->ih_rb_cntl, tmp); + out: return (wptr & ih->ptr_mask); } diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c index bc38b90f8cf8..88ea58d5c4ab 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c @@ -674,14 +674,6 @@ static int jpeg_v4_0_set_powergating_state(void *handle, return ret; } -static int jpeg_v4_0_set_interrupt_state(struct amdgpu_device *adev, - struct amdgpu_irq_src *source, - unsigned type, - enum amdgpu_interrupt_state state) -{ - return 0; -} - static int jpeg_v4_0_set_ras_interrupt_state(struct amdgpu_device *adev, struct amdgpu_irq_src *source, unsigned int type, @@ -765,7 +757,6 @@ static void jpeg_v4_0_set_dec_ring_funcs(struct amdgpu_device *adev) } static const struct amdgpu_irq_src_funcs jpeg_v4_0_irq_funcs = { - .set = jpeg_v4_0_set_interrupt_state, .process = jpeg_v4_0_process_interrupt, }; diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c index 6ede85b28cc8..78b74daf4eeb 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c @@ -181,7 +181,6 @@ static int jpeg_v4_0_5_hw_fini(void *handle) RREG32_SOC15(JPEG, 0, regUVD_JRBC_STATUS)) jpeg_v4_0_5_set_powergating_state(adev, AMD_PG_STATE_GATE); } - amdgpu_irq_put(adev, &adev->jpeg.inst->irq, 0); return 0; } @@ -516,14 +515,6 @@ static int jpeg_v4_0_5_set_powergating_state(void *handle, return ret; } -static int jpeg_v4_0_5_set_interrupt_state(struct amdgpu_device *adev, - struct amdgpu_irq_src *source, - unsigned type, - enum amdgpu_interrupt_state state) -{ - return 0; -} - static int jpeg_v4_0_5_process_interrupt(struct amdgpu_device *adev, struct amdgpu_irq_src *source, struct amdgpu_iv_entry *entry) @@ -603,7 +594,6 @@ static void jpeg_v4_0_5_set_dec_ring_funcs(struct amdgpu_device *adev) } static const struct amdgpu_irq_src_funcs jpeg_v4_0_5_irq_funcs = { - .set = jpeg_v4_0_5_set_interrupt_state, .process = jpeg_v4_0_5_process_interrupt, }; diff --git a/drivers/gpu/drm/amd/amdgpu/navi10_ih.c b/drivers/gpu/drm/amd/amdgpu/navi10_ih.c index e64b33115848..de93614726c9 100644 --- a/drivers/gpu/drm/amd/amdgpu/navi10_ih.c +++ b/drivers/gpu/drm/amd/amdgpu/navi10_ih.c @@ -442,6 +442,12 @@ static u32 navi10_ih_get_wptr(struct amdgpu_device *adev, tmp = RREG32_NO_KIQ(ih_regs->ih_rb_cntl); tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1); WREG32_NO_KIQ(ih_regs->ih_rb_cntl, tmp); + + /* Unset the CLEAR_OVERFLOW bit immediately so new overflows + * can be detected. + */ + tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 0); + WREG32_NO_KIQ(ih_regs->ih_rb_cntl, tmp); out: return (wptr & ih->ptr_mask); } diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c b/drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c index 6d24c84924cb..19986ff6a48d 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c @@ -401,8 +401,7 @@ static void nbio_v7_4_handle_ras_controller_intr_no_bifring(struct amdgpu_device if (err_data.ce_count) dev_info(adev->dev, "%ld correctable hardware " - "errors detected in %s block, " - "no user action is needed.\n", + "errors detected in %s block\n", obj->err_data.ce_count, get_ras_block_str(adev->nbio.ras_if)); diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c b/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c index 25a3da83e0fb..b4723d68eab0 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c @@ -431,6 +431,12 @@ static void nbio_v7_9_init_registers(struct amdgpu_device *adev) u32 inst_mask; int i; + if (amdgpu_sriov_vf(adev)) + adev->rmmio_remap.reg_offset = + SOC15_REG_OFFSET( + NBIO, 0, + regBIF_BX_DEV0_EPF0_VF0_HDP_MEM_COHERENCY_FLUSH_CNTL) + << 2; WREG32_SOC15(NBIO, 0, regXCC_DOORBELL_FENCE, 0xff & ~(adev->gfx.xcc_mask)); @@ -597,8 +603,7 @@ static void nbio_v7_9_handle_ras_controller_intr_no_bifring(struct amdgpu_device if (err_data.ce_count) dev_info(adev->dev, "%ld correctable hardware " - "errors detected in %s block, " - "no user action is needed.\n", + "errors detected in %s block\n", obj->err_data.ce_count, get_ras_block_str(adev->nbio.ras_if)); diff --git a/drivers/gpu/drm/amd/amdgpu/si_ih.c b/drivers/gpu/drm/amd/amdgpu/si_ih.c index 9a24f17a5750..cada9f300a7f 100644 --- a/drivers/gpu/drm/amd/amdgpu/si_ih.c +++ b/drivers/gpu/drm/amd/amdgpu/si_ih.c @@ -119,6 +119,12 @@ static u32 si_ih_get_wptr(struct amdgpu_device *adev, tmp = RREG32(IH_RB_CNTL); tmp |= IH_RB_CNTL__WPTR_OVERFLOW_CLEAR_MASK; WREG32(IH_RB_CNTL, tmp); + + /* Unset the CLEAR_OVERFLOW bit immediately so new overflows + * can be detected. + */ + tmp &= ~IH_RB_CNTL__WPTR_OVERFLOW_CLEAR_MASK; + WREG32(IH_RB_CNTL, tmp); } return (wptr & ih->ptr_mask); } diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c index 15033efec2ba..c64c01e2944a 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15.c +++ b/drivers/gpu/drm/amd/amdgpu/soc15.c @@ -1298,10 +1298,32 @@ static int soc15_common_suspend(void *handle) return soc15_common_hw_fini(adev); } +static bool soc15_need_reset_on_resume(struct amdgpu_device *adev) +{ + u32 sol_reg; + + sol_reg = RREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_81); + + /* Will reset for the following suspend abort cases. + * 1) Only reset limit on APU side, dGPU hasn't checked yet. + * 2) S3 suspend abort and TOS already launched. + */ + if (adev->flags & AMD_IS_APU && adev->in_s3 && + !adev->suspend_complete && + sol_reg) + return true; + + return false; +} + static int soc15_common_resume(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; + if (soc15_need_reset_on_resume(adev)) { + dev_info(adev->dev, "S3 suspend abort case, let's reset ASIC.\n"); + soc15_asic_reset(adev); + } return soc15_common_hw_init(adev); } diff --git a/drivers/gpu/drm/amd/amdgpu/tonga_ih.c b/drivers/gpu/drm/amd/amdgpu/tonga_ih.c index 917707bba7f3..450b6e831509 100644 --- a/drivers/gpu/drm/amd/amdgpu/tonga_ih.c +++ b/drivers/gpu/drm/amd/amdgpu/tonga_ih.c @@ -219,6 +219,12 @@ static u32 tonga_ih_get_wptr(struct amdgpu_device *adev, tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1); WREG32(mmIH_RB_CNTL, tmp); + /* Unset the CLEAR_OVERFLOW bit immediately so new overflows + * can be detected. + */ + tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 0); + WREG32(mmIH_RB_CNTL, tmp); + out: return (wptr & ih->ptr_mask); } diff --git a/drivers/gpu/drm/amd/amdgpu/umc_v6_7.c b/drivers/gpu/drm/amd/amdgpu/umc_v6_7.c index 530549314ce4..a3ee3c4c650f 100644 --- a/drivers/gpu/drm/amd/amdgpu/umc_v6_7.c +++ b/drivers/gpu/drm/amd/amdgpu/umc_v6_7.c @@ -64,7 +64,7 @@ static void umc_v6_7_query_error_status_helper(struct amdgpu_device *adev, uint64_t reg_value; if (REG_GET_FIELD(mc_umc_status, MCA_UMC_UMC0_MCUMC_STATUST0, Deferred) == 1) - dev_info(adev->dev, "Deferred error, no user action is needed.\n"); + dev_info(adev->dev, "Deferred error\n"); if (mc_umc_status) dev_info(adev->dev, "MCA STATUS 0x%llx, umc_reg_offset 0x%x\n", mc_umc_status, umc_reg_offset); diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c index 169ed400ee7b..8ab01ae919d2 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c @@ -2018,22 +2018,6 @@ static int vcn_v4_0_set_powergating_state(void *handle, enum amd_powergating_sta } /** - * vcn_v4_0_set_interrupt_state - set VCN block interrupt state - * - * @adev: amdgpu_device pointer - * @source: interrupt sources - * @type: interrupt types - * @state: interrupt states - * - * Set VCN block interrupt state - */ -static int vcn_v4_0_set_interrupt_state(struct amdgpu_device *adev, struct amdgpu_irq_src *source, - unsigned type, enum amdgpu_interrupt_state state) -{ - return 0; -} - -/** * vcn_v4_0_set_ras_interrupt_state - set VCN block RAS interrupt state * * @adev: amdgpu_device pointer @@ -2097,7 +2081,6 @@ static int vcn_v4_0_process_interrupt(struct amdgpu_device *adev, struct amdgpu_ } static const struct amdgpu_irq_src_funcs vcn_v4_0_irq_funcs = { - .set = vcn_v4_0_set_interrupt_state, .process = vcn_v4_0_process_interrupt, }; diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c index 2eda30e78f61..49e4c3c09aca 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c @@ -269,8 +269,6 @@ static int vcn_v4_0_5_hw_fini(void *handle) vcn_v4_0_5_set_powergating_state(adev, AMD_PG_STATE_GATE); } } - - amdgpu_irq_put(adev, &adev->vcn.inst[i].irq, 0); } return 0; @@ -1669,22 +1667,6 @@ static int vcn_v4_0_5_set_powergating_state(void *handle, enum amd_powergating_s } /** - * vcn_v4_0_5_set_interrupt_state - set VCN block interrupt state - * - * @adev: amdgpu_device pointer - * @source: interrupt sources - * @type: interrupt types - * @state: interrupt states - * - * Set VCN block interrupt state - */ -static int vcn_v4_0_5_set_interrupt_state(struct amdgpu_device *adev, struct amdgpu_irq_src *source, - unsigned type, enum amdgpu_interrupt_state state) -{ - return 0; -} - -/** * vcn_v4_0_5_process_interrupt - process VCN block interrupt * * @adev: amdgpu_device pointer @@ -1726,7 +1708,6 @@ static int vcn_v4_0_5_process_interrupt(struct amdgpu_device *adev, struct amdgp } static const struct amdgpu_irq_src_funcs vcn_v4_0_5_irq_funcs = { - .set = vcn_v4_0_5_set_interrupt_state, .process = vcn_v4_0_5_process_interrupt, }; diff --git a/drivers/gpu/drm/amd/amdgpu/vega10_ih.c b/drivers/gpu/drm/amd/amdgpu/vega10_ih.c index d364c6dd152c..bf68e18e3824 100644 --- a/drivers/gpu/drm/amd/amdgpu/vega10_ih.c +++ b/drivers/gpu/drm/amd/amdgpu/vega10_ih.c @@ -373,6 +373,12 @@ static u32 vega10_ih_get_wptr(struct amdgpu_device *adev, tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1); WREG32_NO_KIQ(ih_regs->ih_rb_cntl, tmp); + /* Unset the CLEAR_OVERFLOW bit immediately so new overflows + * can be detected. + */ + tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 0); + WREG32_NO_KIQ(ih_regs->ih_rb_cntl, tmp); + out: return (wptr & ih->ptr_mask); } diff --git a/drivers/gpu/drm/amd/amdgpu/vega20_ih.c b/drivers/gpu/drm/amd/amdgpu/vega20_ih.c index ddfc6941f9d5..db66e6cccaf2 100644 --- a/drivers/gpu/drm/amd/amdgpu/vega20_ih.c +++ b/drivers/gpu/drm/amd/amdgpu/vega20_ih.c @@ -421,6 +421,12 @@ static u32 vega20_ih_get_wptr(struct amdgpu_device *adev, tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1); WREG32_NO_KIQ(ih_regs->ih_rb_cntl, tmp); + /* Unset the CLEAR_OVERFLOW bit immediately so new overflows + * can be detected. + */ + tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 0); + WREG32_NO_KIQ(ih_regs->ih_rb_cntl, tmp); + out: return (wptr & ih->ptr_mask); } diff --git a/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler.h b/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler.h index df75863393fc..d1caaf0e6a7c 100644 --- a/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler.h +++ b/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler.h @@ -674,7 +674,7 @@ static const uint32_t cwsr_trap_gfx9_hex[] = { 0x86ea6a6a, 0x8f6e837a, 0xb96ee0c2, 0xbf800002, 0xb97a0002, 0xbf8a0000, - 0xbe801f6c, 0xbf810000, + 0xbe801f6c, 0xbf9b0000, }; static const uint32_t cwsr_trap_nv1x_hex[] = { @@ -1091,7 +1091,7 @@ static const uint32_t cwsr_trap_nv1x_hex[] = { 0xb9eef807, 0x876dff6d, 0x0000ffff, 0x87fe7e7e, 0x87ea6a6a, 0xb9faf802, - 0xbe80226c, 0xbf810000, + 0xbe80226c, 0xbf9b0000, 0xbf9f0000, 0xbf9f0000, 0xbf9f0000, 0xbf9f0000, 0xbf9f0000, 0x00000000, @@ -1574,7 +1574,7 @@ static const uint32_t cwsr_trap_arcturus_hex[] = { 0x86ea6a6a, 0x8f6e837a, 0xb96ee0c2, 0xbf800002, 0xb97a0002, 0xbf8a0000, - 0xbe801f6c, 0xbf810000, + 0xbe801f6c, 0xbf9b0000, }; static const uint32_t cwsr_trap_aldebaran_hex[] = { @@ -2065,7 +2065,7 @@ static const uint32_t cwsr_trap_aldebaran_hex[] = { 0x86ea6a6a, 0x8f6e837a, 0xb96ee0c2, 0xbf800002, 0xb97a0002, 0xbf8a0000, - 0xbe801f6c, 0xbf810000, + 0xbe801f6c, 0xbf9b0000, }; static const uint32_t cwsr_trap_gfx10_hex[] = { @@ -2500,7 +2500,7 @@ static const uint32_t cwsr_trap_gfx10_hex[] = { 0x876dff6d, 0x0000ffff, 0x87fe7e7e, 0x87ea6a6a, 0xb9faf802, 0xbe80226c, - 0xbf810000, 0xbf9f0000, + 0xbf9b0000, 0xbf9f0000, 0xbf9f0000, 0xbf9f0000, 0xbf9f0000, 0xbf9f0000, }; @@ -2944,7 +2944,7 @@ static const uint32_t cwsr_trap_gfx11_hex[] = { 0xb8eef802, 0xbf0d866e, 0xbfa20002, 0xb97af802, 0xbe80486c, 0xb97af802, - 0xbe804a6c, 0xbfb00000, + 0xbe804a6c, 0xbfb10000, 0xbf9f0000, 0xbf9f0000, 0xbf9f0000, 0xbf9f0000, 0xbf9f0000, 0x00000000, @@ -3436,5 +3436,5 @@ static const uint32_t cwsr_trap_gfx9_4_3_hex[] = { 0x86ea6a6a, 0x8f6e837a, 0xb96ee0c2, 0xbf800002, 0xb97a0002, 0xbf8a0000, - 0xbe801f6c, 0xbf810000, + 0xbe801f6c, 0xbf9b0000, }; diff --git a/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx10.asm b/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx10.asm index e0140df0b0ec..71b3dc0c7363 100644 --- a/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx10.asm +++ b/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx10.asm @@ -1104,7 +1104,7 @@ L_RETURN_WITHOUT_PRIV: s_rfe_b64 s_restore_pc_lo //Return to the main shader program and resume execution L_END_PGM: - s_endpgm + s_endpgm_saved end function write_hwreg_to_mem(s, s_rsrc, s_mem_offset) diff --git a/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx9.asm b/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx9.asm index e506411ad28a..bb26338204f4 100644 --- a/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx9.asm +++ b/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx9.asm @@ -921,7 +921,7 @@ L_RESTORE: /* the END */ /**************************************************************************/ L_END_PGM: - s_endpgm + s_endpgm_saved end diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index ce4c52ec34d8..80e90fdef291 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1442,7 +1442,9 @@ static int kfd_ioctl_unmap_memory_from_gpu(struct file *filep, kfd_flush_tlb(peer_pdd, TLB_FLUSH_HEAVYWEIGHT); /* Remove dma mapping after tlb flush to avoid IO_PAGE_FAULT */ - amdgpu_amdkfd_gpuvm_dmaunmap_mem(mem, peer_pdd->drm_priv); + err = amdgpu_amdkfd_gpuvm_dmaunmap_mem(mem, peer_pdd->drm_priv); + if (err) + goto sync_memory_failed; } mutex_unlock(&p->mutex); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index d630100b9e91..bdc01ca9609a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -574,7 +574,7 @@ svm_migrate_copy_to_ram(struct amdgpu_device *adev, struct svm_range *prange, pr_debug("svms 0x%p [0x%lx 0x%lx]\n", prange->svms, prange->start, prange->last); - addr = prange->start << PAGE_SHIFT; + addr = migrate->start; src = (uint64_t *)(scratch + npages); dst = scratch; @@ -1026,7 +1026,7 @@ int kgd2kfd_init_zone_device(struct amdgpu_device *adev) } else { res = devm_request_free_mem_region(adev->dev, &iomem_resource, size); if (IS_ERR(res)) - return -ENOMEM; + return PTR_ERR(res); pgmap->range.start = res->start; pgmap->range.end = res->end; pgmap->type = MEMORY_DEVICE_PRIVATE; @@ -1042,10 +1042,10 @@ int kgd2kfd_init_zone_device(struct amdgpu_device *adev) r = devm_memremap_pages(adev->dev, pgmap); if (IS_ERR(r)) { pr_err("failed to register HMM device memory\n"); - /* Disable SVM support capability */ - pgmap->type = 0; if (pgmap->type == MEMORY_DEVICE_PRIVATE) devm_release_mem_region(adev->dev, res->start, resource_size(res)); + /* Disable SVM support capability */ + pgmap->type = 0; return PTR_ERR(r); } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c index 8b7fed913526..22cbfa1bdadd 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c @@ -170,6 +170,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, m->cp_hqd_pq_control = 5 << CP_HQD_PQ_CONTROL__RPTR_BLOCK_SIZE__SHIFT; m->cp_hqd_pq_control |= ffs(q->queue_size / sizeof(unsigned int)) - 1 - 1; + m->cp_hqd_pq_control |= CP_HQD_PQ_CONTROL__UNORD_DISPATCH_MASK; pr_debug("cp_hqd_pq_control 0x%x\n", m->cp_hqd_pq_control); m->cp_hqd_pq_base_lo = lower_32_bits((uint64_t)q->queue_address >> 8); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c index 15277f1d5cf0..d722cbd31783 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c @@ -224,6 +224,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, m->cp_hqd_pq_control = 5 << CP_HQD_PQ_CONTROL__RPTR_BLOCK_SIZE__SHIFT; m->cp_hqd_pq_control |= ffs(q->queue_size / sizeof(unsigned int)) - 1 - 1; + m->cp_hqd_pq_control |= CP_HQD_PQ_CONTROL__UNORD_DISPATCH_MASK; pr_debug("cp_hqd_pq_control 0x%x\n", m->cp_hqd_pq_control); m->cp_hqd_pq_base_lo = lower_32_bits((uint64_t)q->queue_address >> 8); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index 745024b31340..677281c0793e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -917,7 +917,7 @@ struct kfd_process { * fence will be triggered during eviction and new one will be created * during restore */ - struct dma_fence *ef; + struct dma_fence __rcu *ef; /* Work items for evicting and restoring BOs */ struct delayed_work eviction_work; @@ -1488,10 +1488,15 @@ void kfd_dec_compute_active(struct kfd_node *dev); /* Cgroup Support */ /* Check with device cgroup if @kfd device is accessible */ -static inline int kfd_devcgroup_check_permission(struct kfd_node *kfd) +static inline int kfd_devcgroup_check_permission(struct kfd_node *node) { #if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF) - struct drm_device *ddev = adev_to_drm(kfd->adev); + struct drm_device *ddev; + + if (node->xcp) + ddev = node->xcp->ddev; + else + ddev = adev_to_drm(node->adev); return devcgroup_check_permission(DEVCG_DEV_CHAR, DRM_MAJOR, ddev->render->index, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index 71df51fcc1b0..717a60d7a4ea 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -1110,6 +1110,7 @@ static void kfd_process_wq_release(struct work_struct *work) { struct kfd_process *p = container_of(work, struct kfd_process, release_work); + struct dma_fence *ef; kfd_process_dequeue_from_all_devices(p); pqm_uninit(&p->pqm); @@ -1118,7 +1119,9 @@ static void kfd_process_wq_release(struct work_struct *work) * destroyed. This allows any BOs to be freed without * triggering pointless evictions or waiting for fences. */ - dma_fence_signal(p->ef); + synchronize_rcu(); + ef = rcu_access_pointer(p->ef); + dma_fence_signal(ef); kfd_process_remove_sysfs(p); @@ -1127,7 +1130,7 @@ static void kfd_process_wq_release(struct work_struct *work) svm_range_list_fini(p); kfd_process_destroy_pdds(p); - dma_fence_put(p->ef); + dma_fence_put(ef); kfd_event_free_process(p); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index ac84c4a2ca07..c50a0dc9c9c0 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -404,14 +404,9 @@ static void svm_range_bo_release(struct kref *kref) spin_lock(&svm_bo->list_lock); } spin_unlock(&svm_bo->list_lock); - if (!dma_fence_is_signaled(&svm_bo->eviction_fence->base)) { - /* We're not in the eviction worker. - * Signal the fence and synchronize with any - * pending eviction work. - */ + if (!dma_fence_is_signaled(&svm_bo->eviction_fence->base)) + /* We're not in the eviction worker. Signal the fence. */ dma_fence_signal(&svm_bo->eviction_fence->base); - cancel_work_sync(&svm_bo->eviction_work); - } dma_fence_put(&svm_bo->eviction_fence->base); amdgpu_bo_unref(&svm_bo->bo); kfree(svm_bo); @@ -2345,8 +2340,10 @@ retry: mutex_unlock(&svms->lock); mmap_write_unlock(mm); - /* Pairs with mmget in svm_range_add_list_work */ - mmput(mm); + /* Pairs with mmget in svm_range_add_list_work. If dropping the + * last mm refcount, schedule release work to avoid circular locking + */ + mmput_async(mm); spin_lock(&svms->deferred_list_lock); } @@ -2657,6 +2654,7 @@ svm_range_get_range_boundaries(struct kfd_process *p, int64_t addr, { struct vm_area_struct *vma; struct interval_tree_node *node; + struct rb_node *rb_node; unsigned long start_limit, end_limit; vma = vma_lookup(p->mm, addr << PAGE_SHIFT); @@ -2676,16 +2674,15 @@ svm_range_get_range_boundaries(struct kfd_process *p, int64_t addr, if (node) { end_limit = min(end_limit, node->start); /* Last range that ends before the fault address */ - node = container_of(rb_prev(&node->rb), - struct interval_tree_node, rb); + rb_node = rb_prev(&node->rb); } else { /* Last range must end before addr because * there was no range after addr */ - node = container_of(rb_last(&p->svms.objects.rb_root), - struct interval_tree_node, rb); + rb_node = rb_last(&p->svms.objects.rb_root); } - if (node) { + if (rb_node) { + node = container_of(rb_node, struct interval_tree_node, rb); if (node->last >= addr) { WARN(1, "Overlap with prev node and page fault addr\n"); return -EFAULT; @@ -3432,13 +3429,14 @@ svm_range_trigger_migration(struct mm_struct *mm, struct svm_range *prange, int svm_range_schedule_evict_svm_bo(struct amdgpu_amdkfd_fence *fence) { - if (!fence) - return -EINVAL; - - if (dma_fence_is_signaled(&fence->base)) - return 0; - - if (fence->svm_bo) { + /* Dereferencing fence->svm_bo is safe here because the fence hasn't + * signaled yet and we're under the protection of the fence->lock. + * After the fence is signaled in svm_range_bo_release, we cannot get + * here any more. + * + * Reference is dropped in svm_range_evict_svm_bo_worker. + */ + if (svm_bo_ref_unless_zero(fence->svm_bo)) { WRITE_ONCE(fence->svm_bo->evicting, 1); schedule_work(&fence->svm_bo->eviction_work); } @@ -3453,8 +3451,6 @@ static void svm_range_evict_svm_bo_worker(struct work_struct *work) int r = 0; svm_bo = container_of(work, struct svm_range_bo, eviction_work); - if (!svm_bo_ref_unless_zero(svm_bo)) - return; /* svm_bo was freed while eviction was pending */ if (mmget_not_zero(svm_bo->eviction_fence->mm)) { mm = svm_bo->eviction_fence->mm; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index f6575d7dee97..59d2eee72a32 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -272,6 +272,7 @@ static int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc, { u32 v_blank_start, v_blank_end, h_position, v_position; struct amdgpu_crtc *acrtc = NULL; + struct dc *dc = adev->dm.dc; if ((crtc < 0) || (crtc >= adev->mode_info.num_crtc)) return -EINVAL; @@ -284,6 +285,9 @@ static int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc, return 0; } + if (dc && dc->caps.ips_support && dc->idle_optimizations_allowed) + dc_allow_idle_optimizations(dc, false); + /* * TODO rework base driver to use values directly. * for now parse it back into reg-format @@ -1715,7 +1719,10 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) init_data.nbio_reg_offsets = adev->reg_offset[NBIO_HWIP][0]; init_data.clk_reg_offsets = adev->reg_offset[CLK_HWIP][0]; - init_data.flags.disable_ips = DMUB_IPS_DISABLE_ALL; + if (amdgpu_dc_debug_mask & DC_DISABLE_IPS) + init_data.flags.disable_ips = DMUB_IPS_DISABLE_ALL; + + init_data.flags.disable_ips_in_vpb = 1; /* Enable DWB for tested platforms only */ if (amdgpu_ip_version(adev, DCE_HWIP, 0) >= IP_VERSION(3, 0, 0)) @@ -7615,7 +7622,6 @@ create_i2c(struct ddc_service *ddc_service, if (!i2c) return NULL; i2c->base.owner = THIS_MODULE; - i2c->base.class = I2C_CLASS_DDC; i2c->base.dev.parent = &adev->pdev->dev; i2c->base.algo = &amdgpu_dm_i2c_algo; snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c hw bus %d", link_index); @@ -8977,16 +8983,8 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) trace_amdgpu_dm_atomic_commit_tail_begin(state); - if (dm->dc->caps.ips_support) { - for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) { - if (new_con_state->crtc && - new_con_state->crtc->state->active && - drm_atomic_crtc_needs_modeset(new_con_state->crtc->state)) { - dc_dmub_srv_apply_idle_power_optimizations(dm->dc, false); - break; - } - } - } + if (dm->dc->caps.ips_support && dm->dc->idle_optimizations_allowed) + dc_allow_idle_optimizations(dm->dc, false); drm_atomic_helper_update_legacy_modeset_state(dev, state); drm_dp_mst_atomic_wait_for_dependencies(state); @@ -9189,6 +9187,10 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) * To fix this, DC should permit updating only stream properties. */ dummy_updates = kzalloc(sizeof(struct dc_surface_update) * MAX_SURFACES, GFP_ATOMIC); + if (!dummy_updates) { + DRM_ERROR("Failed to allocate memory for dummy_updates.\n"); + continue; + } for (j = 0; j < status->plane_count; j++) dummy_updates[j].surface = status->plane_states[0]; @@ -9293,10 +9295,10 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) if (!new_con_state->writeback_job) continue; - new_crtc_state = NULL; + new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base); - if (acrtc) - new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base); + if (!new_crtc_state) + continue; if (acrtc->wb_enabled) continue; @@ -10729,11 +10731,13 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, goto fail; } - ret = compute_mst_dsc_configs_for_state(state, dm_state->context, vars); - if (ret) { - DRM_DEBUG_DRIVER("compute_mst_dsc_configs_for_state() failed\n"); - ret = -EINVAL; - goto fail; + if (dc_resource_is_dsc_encoding_supported(dc)) { + ret = compute_mst_dsc_configs_for_state(state, dm_state->context, vars); + if (ret) { + DRM_DEBUG_DRIVER("compute_mst_dsc_configs_for_state() failed\n"); + ret = -EINVAL; + goto fail; + } } ret = dm_update_mst_vcpi_slots_for_dsc(state, dm_state->context, vars); @@ -10753,7 +10757,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, DRM_DEBUG_DRIVER("drm_dp_mst_atomic_check() failed\n"); goto fail; } - status = dc_validate_global_state(dc, dm_state->context, false); + status = dc_validate_global_state(dc, dm_state->context, true); if (status != DC_OK) { DRM_DEBUG_DRIVER("DC global validation failure: %s (%d)", dc_status_to_str(status), status); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c index 9b527bffe11a..c87b64e464ed 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c @@ -1239,7 +1239,7 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, if (has_crtc_cm_degamma && ret != -EINVAL) { drm_dbg_kms(crtc->base.crtc->dev, "doesn't support plane and CRTC degamma at the same time\n"); - return -EINVAL; + return -EINVAL; } /* If we are here, it means we don't have plane degamma settings, check diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index eaf8d9f48244..85b7f58a7f35 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -979,6 +979,11 @@ int dm_helper_dmub_aux_transfer_sync( struct aux_payload *payload, enum aux_return_code_type *operation_result) { + if (!link->hpd_status) { + *operation_result = AUX_RET_ERROR_HPD_DISCON; + return -1; + } + return amdgpu_dm_process_dmub_aux_transfer_sync(ctx, link->link_index, payload, operation_result); } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c index 58b880acb087..3390f0d8420a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c @@ -711,7 +711,7 @@ static inline int dm_irq_state(struct amdgpu_device *adev, { bool st; enum dc_irq_source irq_source; - + struct dc *dc = adev->dm.dc; struct amdgpu_crtc *acrtc = adev->mode_info.crtcs[crtc_id]; if (!acrtc) { @@ -729,6 +729,9 @@ static inline int dm_irq_state(struct amdgpu_device *adev, st = (state == AMDGPU_IRQ_STATE_ENABLE); + if (dc && dc->caps.ips_support && dc->idle_optimizations_allowed) + dc_allow_idle_optimizations(dc, false); + dc_interrupt_set(adev->dm.dc, irq_source, st); return 0; } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c index 757528256326..a84f1e376dee 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c @@ -87,6 +87,20 @@ static const struct IP_BASE CLK_BASE = { { { { 0x00016C00, 0x02401800, 0, 0, 0, #define CLK1_CLK_PLL_REQ__PllSpineDiv_MASK 0x0000F000L #define CLK1_CLK_PLL_REQ__FbMult_frac_MASK 0xFFFF0000L +#define regCLK1_CLK2_BYPASS_CNTL 0x029c +#define regCLK1_CLK2_BYPASS_CNTL_BASE_IDX 0 + +#define CLK1_CLK2_BYPASS_CNTL__CLK2_BYPASS_SEL__SHIFT 0x0 +#define CLK1_CLK2_BYPASS_CNTL__CLK2_BYPASS_DIV__SHIFT 0x10 +#define CLK1_CLK2_BYPASS_CNTL__CLK2_BYPASS_SEL_MASK 0x00000007L +#define CLK1_CLK2_BYPASS_CNTL__CLK2_BYPASS_DIV_MASK 0x000F0000L + +#define regCLK6_0_CLK6_spll_field_8 0x464b +#define regCLK6_0_CLK6_spll_field_8_BASE_IDX 0 + +#define CLK6_0_CLK6_spll_field_8__spll_ssc_en__SHIFT 0xd +#define CLK6_0_CLK6_spll_field_8__spll_ssc_en_MASK 0x00002000L + #define REG(reg_name) \ (CLK_BASE.instance[0].segment[reg ## reg_name ## _BASE_IDX] + reg ## reg_name) @@ -131,35 +145,63 @@ static int dcn314_get_active_display_cnt_wa( return display_count; } -static void dcn314_disable_otg_wa(struct clk_mgr *clk_mgr_base, struct dc_state *context, bool disable) +static void dcn314_disable_otg_wa(struct clk_mgr *clk_mgr_base, struct dc_state *context, + bool safe_to_lower, bool disable) { struct dc *dc = clk_mgr_base->ctx->dc; int i; for (i = 0; i < dc->res_pool->pipe_count; ++i) { - struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe = safe_to_lower + ? &context->res_ctx.pipe_ctx[i] + : &dc->current_state->res_ctx.pipe_ctx[i]; if (pipe->top_pipe || pipe->prev_odm_pipe) continue; if (pipe->stream && (pipe->stream->dpms_off || dc_is_virtual_signal(pipe->stream->signal))) { - struct stream_encoder *stream_enc = pipe->stream_res.stream_enc; - if (disable) { - if (stream_enc && stream_enc->funcs->disable_fifo) - pipe->stream_res.stream_enc->funcs->disable_fifo(stream_enc); + if (pipe->stream_res.tg && pipe->stream_res.tg->funcs->immediate_disable_crtc) + pipe->stream_res.tg->funcs->immediate_disable_crtc(pipe->stream_res.tg); - pipe->stream_res.tg->funcs->immediate_disable_crtc(pipe->stream_res.tg); reset_sync_context_for_pipe(dc, context, i); } else { pipe->stream_res.tg->funcs->enable_crtc(pipe->stream_res.tg); - - if (stream_enc && stream_enc->funcs->enable_fifo) - pipe->stream_res.stream_enc->funcs->enable_fifo(stream_enc); } } } } +bool dcn314_is_spll_ssc_enabled(struct clk_mgr *clk_mgr_base) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + uint32_t ssc_enable; + + REG_GET(CLK6_0_CLK6_spll_field_8, spll_ssc_en, &ssc_enable); + + return ssc_enable == 1; +} + +void dcn314_init_clocks(struct clk_mgr *clk_mgr) +{ + struct clk_mgr_internal *clk_mgr_int = TO_CLK_MGR_INTERNAL(clk_mgr); + uint32_t ref_dtbclk = clk_mgr->clks.ref_dtbclk_khz; + + memset(&(clk_mgr->clks), 0, sizeof(struct dc_clocks)); + // Assumption is that boot state always supports pstate + clk_mgr->clks.ref_dtbclk_khz = ref_dtbclk; // restore ref_dtbclk + clk_mgr->clks.p_state_change_support = true; + clk_mgr->clks.prev_p_state_change_support = true; + clk_mgr->clks.pwr_state = DCN_PWR_STATE_UNKNOWN; + clk_mgr->clks.zstate_support = DCN_ZSTATE_SUPPORT_UNKNOWN; + + // to adjust dp_dto reference clock if ssc is enable otherwise to apply dprefclk + if (dcn314_is_spll_ssc_enabled(clk_mgr)) + clk_mgr->dp_dto_source_clock_in_khz = + dce_adjust_dp_ref_freq_for_ss(clk_mgr_int, clk_mgr->dprefclk_khz); + else + clk_mgr->dp_dto_source_clock_in_khz = clk_mgr->dprefclk_khz; +} + void dcn314_update_clocks(struct clk_mgr *clk_mgr_base, struct dc_state *context, bool safe_to_lower) @@ -252,11 +294,11 @@ void dcn314_update_clocks(struct clk_mgr *clk_mgr_base, } if (should_set_clock(safe_to_lower, new_clocks->dispclk_khz, clk_mgr_base->clks.dispclk_khz)) { - dcn314_disable_otg_wa(clk_mgr_base, context, true); + dcn314_disable_otg_wa(clk_mgr_base, context, safe_to_lower, true); clk_mgr_base->clks.dispclk_khz = new_clocks->dispclk_khz; dcn314_smu_set_dispclk(clk_mgr, clk_mgr_base->clks.dispclk_khz); - dcn314_disable_otg_wa(clk_mgr_base, context, false); + dcn314_disable_otg_wa(clk_mgr_base, context, safe_to_lower, false); update_dispclk = true; } @@ -436,6 +478,11 @@ static DpmClocks314_t dummy_clocks; static struct dcn314_watermarks dummy_wms = { 0 }; +static struct dcn314_ss_info_table ss_info_table = { + .ss_divider = 1000, + .ss_percentage = {0, 0, 375, 375, 375} +}; + static void dcn314_build_watermark_ranges(struct clk_bw_params *bw_params, struct dcn314_watermarks *table) { int i, num_valid_sets; @@ -708,13 +755,31 @@ static struct clk_mgr_funcs dcn314_funcs = { .get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz, .get_dtb_ref_clk_frequency = dcn31_get_dtb_ref_freq_khz, .update_clocks = dcn314_update_clocks, - .init_clocks = dcn31_init_clocks, + .init_clocks = dcn314_init_clocks, .enable_pme_wa = dcn314_enable_pme_wa, .are_clock_states_equal = dcn314_are_clock_states_equal, .notify_wm_ranges = dcn314_notify_wm_ranges }; extern struct clk_mgr_funcs dcn3_fpga_funcs; +static void dcn314_read_ss_info_from_lut(struct clk_mgr_internal *clk_mgr) +{ + uint32_t clock_source; + //uint32_t ssc_enable; + + REG_GET(CLK1_CLK2_BYPASS_CNTL, CLK2_BYPASS_SEL, &clock_source); + //REG_GET(CLK6_0_CLK6_spll_field_8, spll_ssc_en, &ssc_enable); + + if (dcn314_is_spll_ssc_enabled(&clk_mgr->base) && (clock_source < ARRAY_SIZE(ss_info_table.ss_percentage))) { + clk_mgr->dprefclk_ss_percentage = ss_info_table.ss_percentage[clock_source]; + + if (clk_mgr->dprefclk_ss_percentage != 0) { + clk_mgr->ss_on_dprefclk = true; + clk_mgr->dprefclk_ss_divider = ss_info_table.ss_divider; + } + } +} + void dcn314_clk_mgr_construct( struct dc_context *ctx, struct clk_mgr_dcn314 *clk_mgr, @@ -782,6 +847,7 @@ void dcn314_clk_mgr_construct( clk_mgr->base.base.dprefclk_khz = 600000; clk_mgr->base.base.clks.ref_dtbclk_khz = 600000; dce_clock_read_ss_info(&clk_mgr->base); + dcn314_read_ss_info_from_lut(&clk_mgr->base); /*if bios enabled SS, driver needs to adjust dtb clock, only enable with correct bios*/ clk_mgr->base.base.bw_params = &dcn314_bw_params; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.h index 171f84340eb2..002c28e80720 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.h @@ -28,6 +28,8 @@ #define __DCN314_CLK_MGR_H__ #include "clk_mgr_internal.h" +#define DCN314_NUM_CLOCK_SOURCES 5 + struct dcn314_watermarks; struct dcn314_smu_watermark_set { @@ -40,9 +42,18 @@ struct clk_mgr_dcn314 { struct dcn314_smu_watermark_set smu_wm_set; }; +struct dcn314_ss_info_table { + uint32_t ss_divider; + uint32_t ss_percentage[DCN314_NUM_CLOCK_SOURCES]; +}; + bool dcn314_are_clock_states_equal(struct dc_clocks *a, struct dc_clocks *b); +bool dcn314_is_spll_ssc_enabled(struct clk_mgr *clk_mgr_base); + +void dcn314_init_clocks(struct clk_mgr *clk_mgr); + void dcn314_update_clocks(struct clk_mgr *clk_mgr_base, struct dc_state *context, bool safe_to_lower); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c index 9c660d1facc7..14cec1c7b718 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c @@ -437,32 +437,32 @@ static struct wm_table ddr5_wm_table = { .wm_inst = WM_A, .wm_type = WM_TYPE_PSTATE_CHG, .pstate_latency_us = 11.72, - .sr_exit_time_us = 14.0, - .sr_enter_plus_exit_time_us = 16.0, + .sr_exit_time_us = 28.0, + .sr_enter_plus_exit_time_us = 30.0, .valid = true, }, { .wm_inst = WM_B, .wm_type = WM_TYPE_PSTATE_CHG, .pstate_latency_us = 11.72, - .sr_exit_time_us = 14.0, - .sr_enter_plus_exit_time_us = 16.0, + .sr_exit_time_us = 28.0, + .sr_enter_plus_exit_time_us = 30.0, .valid = true, }, { .wm_inst = WM_C, .wm_type = WM_TYPE_PSTATE_CHG, .pstate_latency_us = 11.72, - .sr_exit_time_us = 14.0, - .sr_enter_plus_exit_time_us = 16.0, + .sr_exit_time_us = 28.0, + .sr_enter_plus_exit_time_us = 30.0, .valid = true, }, { .wm_inst = WM_D, .wm_type = WM_TYPE_PSTATE_CHG, .pstate_latency_us = 11.72, - .sr_exit_time_us = 14.0, - .sr_enter_plus_exit_time_us = 16.0, + .sr_exit_time_us = 28.0, + .sr_enter_plus_exit_time_us = 30.0, .valid = true, }, } @@ -474,32 +474,32 @@ static struct wm_table lpddr5_wm_table = { .wm_inst = WM_A, .wm_type = WM_TYPE_PSTATE_CHG, .pstate_latency_us = 11.65333, - .sr_exit_time_us = 14.0, - .sr_enter_plus_exit_time_us = 16.0, + .sr_exit_time_us = 28.0, + .sr_enter_plus_exit_time_us = 30.0, .valid = true, }, { .wm_inst = WM_B, .wm_type = WM_TYPE_PSTATE_CHG, .pstate_latency_us = 11.65333, - .sr_exit_time_us = 14.0, - .sr_enter_plus_exit_time_us = 16.0, + .sr_exit_time_us = 28.0, + .sr_enter_plus_exit_time_us = 30.0, .valid = true, }, { .wm_inst = WM_C, .wm_type = WM_TYPE_PSTATE_CHG, .pstate_latency_us = 11.65333, - .sr_exit_time_us = 14.0, - .sr_enter_plus_exit_time_us = 16.0, + .sr_exit_time_us = 28.0, + .sr_enter_plus_exit_time_us = 30.0, .valid = true, }, { .wm_inst = WM_D, .wm_type = WM_TYPE_PSTATE_CHG, .pstate_latency_us = 11.65333, - .sr_exit_time_us = 14.0, - .sr_enter_plus_exit_time_us = 16.0, + .sr_exit_time_us = 28.0, + .sr_enter_plus_exit_time_us = 30.0, .valid = true, }, } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 2d7205058c64..2c424e435962 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -411,12 +411,9 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc, * avoid conflicting with firmware updates. */ if (dc->ctx->dce_version > DCE_VERSION_MAX) - if (dc->optimized_required) + if (dc->optimized_required || dc->wm_optimized_required) return false; - if (!memcmp(&stream->adjust, adjust, sizeof(*adjust))) - return true; - stream->adjust.v_total_max = adjust->v_total_max; stream->adjust.v_total_mid = adjust->v_total_mid; stream->adjust.v_total_mid_frame_num = adjust->v_total_mid_frame_num; @@ -2230,6 +2227,7 @@ void dc_post_update_surfaces_to_stream(struct dc *dc) } dc->optimized_required = false; + dc->wm_optimized_required = false; } bool dc_set_generic_gpio_for_stereo(bool enable, @@ -2652,6 +2650,8 @@ enum surface_update_type dc_check_update_surfaces_for_stream( } else if (memcmp(&dc->current_state->bw_ctx.bw.dcn.clk, &dc->clk_mgr->clks, offsetof(struct dc_clocks, prev_p_state_change_support)) != 0) { dc->optimized_required = true; } + + dc->optimized_required |= dc->wm_optimized_required; } return type; @@ -2859,6 +2859,9 @@ static void copy_stream_update_to_stream(struct dc *dc, if (update->vrr_active_fixed) stream->vrr_active_fixed = *update->vrr_active_fixed; + if (update->crtc_timing_adjust) + stream->adjust = *update->crtc_timing_adjust; + if (update->dpms_off) stream->dpms_off = *update->dpms_off; @@ -3519,7 +3522,7 @@ static void commit_planes_for_stream(struct dc *dc, top_pipe_to_program = resource_get_otg_master_for_stream( &context->res_ctx, stream); - + ASSERT(top_pipe_to_program != NULL); for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i]; @@ -3814,7 +3817,9 @@ static void commit_planes_for_stream(struct dc *dc, * programming has completed (we turn on phantom OTG in order * to complete the plane disable for phantom pipes). */ - dc->hwss.apply_ctx_to_hw(dc, context); + + if (dc->hwss.disable_phantom_streams) + dc->hwss.disable_phantom_streams(dc, context); } if (update_type != UPDATE_TYPE_FAST) @@ -4288,7 +4293,8 @@ static bool full_update_required(struct dc *dc, stream_update->mst_bw_update || stream_update->func_shaper || stream_update->lut3d_func || - stream_update->pending_test_pattern)) + stream_update->pending_test_pattern || + stream_update->crtc_timing_adjust)) return true; if (stream) { @@ -4341,6 +4347,8 @@ static bool should_commit_minimal_transition_for_windowed_mpo_odm(struct dc *dc, cur_pipe = resource_get_otg_master_for_stream(&dc->current_state->res_ctx, stream); new_pipe = resource_get_otg_master_for_stream(&context->res_ctx, stream); + if (!cur_pipe || !new_pipe) + return false; cur_is_odm_in_use = resource_get_odm_slice_count(cur_pipe) > 1; new_is_odm_in_use = resource_get_odm_slice_count(new_pipe) > 1; if (cur_is_odm_in_use == new_is_odm_in_use) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index 57f0ddd15923..9fbdb09697fd 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -2194,6 +2194,10 @@ void resource_log_pipe_topology_update(struct dc *dc, struct dc_state *state) for (stream_idx = 0; stream_idx < state->stream_count; stream_idx++) { otg_master = resource_get_otg_master_for_stream( &state->res_ctx, state->streams[stream_idx]); + if (!otg_master || otg_master->stream_res.tg == NULL) { + DC_LOG_DC("topology update: otg_master NULL stream_idx %d!\n", stream_idx); + return; + } slice_count = resource_get_opp_heads_for_otg_master(otg_master, &state->res_ctx, opp_heads); for (slice_idx = 0; slice_idx < slice_count; slice_idx++) { @@ -4986,20 +4990,6 @@ enum dc_status update_dp_encoder_resources_for_test_harness(const struct dc *dc, return DC_OK; } -bool resource_subvp_in_use(struct dc *dc, - struct dc_state *context) -{ - uint32_t i; - - for (i = 0; i < dc->res_pool->pipe_count; i++) { - struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; - - if (dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_NONE) - return true; - } - return false; -} - bool check_subvp_sw_cursor_fallback_req(const struct dc *dc, struct dc_stream_state *stream) { if (!dc->debug.disable_subvp_high_refresh && is_subvp_high_refresh_candidate(stream)) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_state.c b/drivers/gpu/drm/amd/display/dc/core/dc_state.c index 460a8010c79f..180ac47868c2 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_state.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_state.c @@ -267,7 +267,8 @@ void dc_state_construct(struct dc *dc, struct dc_state *state) state->clk_mgr = dc->clk_mgr; /* Initialise DIG link encoder resource tracking variables. */ - link_enc_cfg_init(dc, state); + if (dc->res_pool) + link_enc_cfg_init(dc, state); } void dc_state_destruct(struct dc_state *state) @@ -290,11 +291,14 @@ void dc_state_destruct(struct dc_state *state) dc_stream_release(state->phantom_streams[i]); state->phantom_streams[i] = NULL; } + state->phantom_stream_count = 0; for (i = 0; i < state->phantom_plane_count; i++) { dc_plane_state_release(state->phantom_planes[i]); state->phantom_planes[i] = NULL; } + state->phantom_plane_count = 0; + state->stream_mask = 0; memset(&state->res_ctx, 0, sizeof(state->res_ctx)); memset(&state->pp_display_cfg, 0, sizeof(state->pp_display_cfg)); @@ -433,8 +437,9 @@ bool dc_state_add_plane( otg_master_pipe = resource_get_otg_master_for_stream( &state->res_ctx, stream); - added = resource_append_dpp_pipes_for_plane_composition(state, - dc->current_state, pool, otg_master_pipe, plane_state); + if (otg_master_pipe) + added = resource_append_dpp_pipes_for_plane_composition(state, + dc->current_state, pool, otg_master_pipe, plane_state); if (added) { stream_status->plane_states[stream_status->plane_count] = diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index f30a341bc090..c9317ea0258e 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -51,7 +51,7 @@ struct aux_payload; struct set_config_cmd_payload; struct dmub_notification; -#define DC_VER "3.2.265" +#define DC_VER "3.2.266" #define MAX_SURFACES 3 #define MAX_PLANES 6 @@ -434,6 +434,7 @@ struct dc_config { bool EnableMinDispClkODM; bool enable_auto_dpm_test_logs; unsigned int disable_ips; + unsigned int disable_ips_in_vpb; }; enum visual_confirm { @@ -1036,6 +1037,7 @@ struct dc { /* Require to optimize clocks and bandwidth for added/removed planes */ bool optimized_required; + bool wm_optimized_required; bool idle_optimizations_allowed; bool enable_c20_dtm_b0; diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h index a23eebd9933b..ee10941caa59 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_stream.h +++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h @@ -139,6 +139,7 @@ union stream_update_flags { uint32_t wb_update:1; uint32_t dsc_changed : 1; uint32_t mst_bw : 1; + uint32_t crtc_timing_adjust : 1; uint32_t fams_changed : 1; } bits; @@ -325,6 +326,7 @@ struct dc_stream_update { struct dc_3dlut *lut3d_func; struct test_pattern *pending_test_pattern; + struct dc_crtc_timing_adjust *crtc_timing_adjust; }; bool dc_is_stream_unchanged( diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index 4f276169e05a..9900dda2eef5 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -1034,6 +1034,7 @@ enum replay_FW_Message_type { Replay_Msg_Not_Support = -1, Replay_Set_Timing_Sync_Supported, Replay_Set_Residency_Frameupdate_Timer, + Replay_Set_Pseudo_VTotal, }; union replay_error_status { @@ -1089,6 +1090,10 @@ struct replay_settings { uint16_t coasting_vtotal_table[PR_COASTING_TYPE_NUM]; /* Maximum link off frame count */ enum replay_link_off_frame_count_level link_off_frame_count_level; + /* Replay pseudo vtotal for abm + ips on full screen video which can improve ips residency */ + uint16_t abm_with_ips_on_full_screen_video_pseudo_vtotal; + /* Replay last pseudo vtotal set to DMUB */ + uint16_t last_pseudo_vtotal; }; /* To split out "global" and "per-panel" config settings. @@ -1140,23 +1145,25 @@ struct dc_panel_config { } ilr; }; +#define MAX_SINKS_PER_LINK 4 + /* * USB4 DPIA BW ALLOCATION STRUCTS */ struct dc_dpia_bw_alloc { - int sink_verified_bw; // The Verified BW that sink can allocated and use that has been verified already - int sink_allocated_bw; // The Actual Allocated BW that sink currently allocated - int sink_max_bw; // The Max BW that sink can require/support + int remote_sink_req_bw[MAX_SINKS_PER_LINK]; // BW requested by remote sinks + int link_verified_bw; // The Verified BW that link can allocated and use that has been verified already + int link_max_bw; // The Max BW that link can require/support + int allocated_bw; // The Actual Allocated BW for this DPIA int estimated_bw; // The estimated available BW for this DPIA int bw_granularity; // BW Granularity + int dp_overhead; // DP overhead in dp tunneling bool bw_alloc_enabled; // The BW Alloc Mode Support is turned ON for all 3: DP-Tx & Dpia & CM bool response_ready; // Response ready from the CM side uint8_t nrd_max_lane_count; // Non-reduced max lane count uint8_t nrd_max_link_rate; // Non-reduced max link rate }; -#define MAX_SINKS_PER_LINK 4 - enum dc_hpd_enable_select { HPD_EN_FOR_ALL_EDP = 0, HPD_EN_FOR_PRIMARY_EDP_ONLY, diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c index 140598f18bbd..f0458b8f00af 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c @@ -782,7 +782,7 @@ static void get_azalia_clock_info_dp( /*audio_dto_module = dpDtoSourceClockInkhz * 10,000; * [khz] ->[100Hz] */ azalia_clock_info->audio_dto_module = - pll_info->dp_dto_source_clock_in_khz * 10; + pll_info->audio_dto_source_clock_in_khz * 10; } void dce_aud_wall_dto_setup( diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index 5d3f6fa1011e..970644b695cd 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -975,6 +975,9 @@ static bool dcn31_program_pix_clk( look_up_in_video_optimized_rate_tlb(pix_clk_params->requested_pix_clk_100hz / 10); struct bp_pixel_clock_parameters bp_pc_params = {0}; enum transmitter_color_depth bp_pc_colour_depth = TRANSMITTER_COLOR_DEPTH_24; + + if (clock_source->ctx->dc->clk_mgr->dp_dto_source_clock_in_khz != 0) + dp_dto_ref_khz = clock_source->ctx->dc->clk_mgr->dp_dto_source_clock_in_khz; // For these signal types Driver to program DP_DTO without calling VBIOS Command table if (dc_is_dp_signal(pix_clk_params->signal_type) || dc_is_virtual_signal(pix_clk_params->signal_type)) { if (e) { @@ -1088,6 +1091,10 @@ static bool get_pixel_clk_frequency_100hz( struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); unsigned int clock_hz = 0; unsigned int modulo_hz = 0; + unsigned int dp_dto_ref_khz = clock_source->ctx->dc->clk_mgr->dprefclk_khz; + + if (clock_source->ctx->dc->clk_mgr->dp_dto_source_clock_in_khz != 0) + dp_dto_ref_khz = clock_source->ctx->dc->clk_mgr->dp_dto_source_clock_in_khz; if (clock_source->id == CLOCK_SOURCE_ID_DP_DTO) { clock_hz = REG_READ(PHASE[inst]); @@ -1100,7 +1107,7 @@ static bool get_pixel_clk_frequency_100hz( modulo_hz = REG_READ(MODULO[inst]); if (modulo_hz) *pixel_clk_khz = div_u64((uint64_t)clock_hz* - clock_source->ctx->dc->clk_mgr->dprefclk_khz*10, + dp_dto_ref_khz*10, modulo_hz); else *pixel_clk_khz = 0; diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_dio_link_encoder.c index 501388014855..d761b0df2878 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_dio_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_dio_link_encoder.c @@ -203,12 +203,12 @@ void dcn32_link_encoder_construct( enc10->base.hpd_source = init_data->hpd_source; enc10->base.connector = init_data->connector; - if (enc10->base.connector.id == CONNECTOR_ID_USBC) - enc10->base.features.flags.bits.DP_IS_USB_C = 1; enc10->base.preferred_engine = ENGINE_ID_UNKNOWN; enc10->base.features = *enc_features; + if (enc10->base.connector.id == CONNECTOR_ID_USBC) + enc10->base.features.flags.bits.DP_IS_USB_C = 1; enc10->base.transmitter = init_data->transmitter; diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c index e4a328b45c8a..87760600e154 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c @@ -183,6 +183,20 @@ bool dcn32_all_pipes_have_stream_and_plane(struct dc *dc, return true; } +bool dcn32_subvp_in_use(struct dc *dc, + struct dc_state *context) +{ + uint32_t i; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; + + if (dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_NONE) + return true; + } + return false; +} + bool dcn32_mpo_in_use(struct dc_state *context) { uint32_t i; diff --git a/drivers/gpu/drm/amd/display/dc/dcn35/dcn35_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dcn35/dcn35_dio_link_encoder.c index da94e5309fba..81e349d5835b 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn35/dcn35_dio_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dcn35/dcn35_dio_link_encoder.c @@ -184,8 +184,6 @@ void dcn35_link_encoder_construct( enc10->base.hpd_source = init_data->hpd_source; enc10->base.connector = init_data->connector; - if (enc10->base.connector.id == CONNECTOR_ID_USBC) - enc10->base.features.flags.bits.DP_IS_USB_C = 1; enc10->base.preferred_engine = ENGINE_ID_UNKNOWN; @@ -240,6 +238,8 @@ void dcn35_link_encoder_construct( } enc10->base.features.flags.bits.HDMI_6GB_EN = 1; + if (enc10->base.connector.id == CONNECTOR_ID_USBC) + enc10->base.features.flags.bits.DP_IS_USB_C = 1; if (bp_funcs->get_connector_speed_cap_info) result = bp_funcs->get_connector_speed_cap_info(enc10->base.ctx->dc_bios, diff --git a/drivers/gpu/drm/amd/display/dc/dml/Makefile b/drivers/gpu/drm/amd/display/dc/dml/Makefile index 6042a5a6a44f..59ade76ffb18 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dml/Makefile @@ -72,11 +72,11 @@ CFLAGS_$(AMDDALPATH)/dc/dml/display_mode_lib.o := $(dml_ccflags) CFLAGS_$(AMDDALPATH)/dc/dml/display_mode_vba.o := $(dml_ccflags) CFLAGS_$(AMDDALPATH)/dc/dml/dcn10/dcn10_fpu.o := $(dml_ccflags) CFLAGS_$(AMDDALPATH)/dc/dml/dcn20/dcn20_fpu.o := $(dml_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml/dcn20/display_mode_vba_20.o := $(dml_ccflags) +CFLAGS_$(AMDDALPATH)/dc/dml/dcn20/display_mode_vba_20.o := $(dml_ccflags) $(frame_warn_flag) CFLAGS_$(AMDDALPATH)/dc/dml/dcn20/display_rq_dlg_calc_20.o := $(dml_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml/dcn20/display_mode_vba_20v2.o := $(dml_ccflags) +CFLAGS_$(AMDDALPATH)/dc/dml/dcn20/display_mode_vba_20v2.o := $(dml_ccflags) $(frame_warn_flag) CFLAGS_$(AMDDALPATH)/dc/dml/dcn20/display_rq_dlg_calc_20v2.o := $(dml_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dml/dcn21/display_mode_vba_21.o := $(dml_ccflags) +CFLAGS_$(AMDDALPATH)/dc/dml/dcn21/display_mode_vba_21.o := $(dml_ccflags) $(frame_warn_flag) CFLAGS_$(AMDDALPATH)/dc/dml/dcn21/display_rq_dlg_calc_21.o := $(dml_ccflags) CFLAGS_$(AMDDALPATH)/dc/dml/dcn30/display_mode_vba_30.o := $(dml_ccflags) $(frame_warn_flag) CFLAGS_$(AMDDALPATH)/dc/dml/dcn30/display_rq_dlg_calc_30.o := $(dml_ccflags) diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c index aa68d010cbfd..ba76dd4a2ce2 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c @@ -33,7 +33,6 @@ #include "dcn30/dcn30_resource.h" #include "link.h" #include "dc_state_priv.h" -#include "resource.h" #define DC_LOGGER_INIT(logger) @@ -292,7 +291,7 @@ int dcn32_find_dummy_latency_index_for_fw_based_mclk_switch(struct dc *dc, /* for subvp + DRR case, if subvp pipes are still present we support pstate */ if (vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported && - resource_subvp_in_use(dc, context)) + dcn32_subvp_in_use(dc, context)) vba->DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] = temp_clock_change_support; if (vlevel < context->bw_ctx.dml.vba.soc.num_states && @@ -1113,7 +1112,7 @@ struct pipe_slice_table { struct pipe_ctx *pri_pipe; struct dc_plane_state *plane; int slice_count; - } mpc_combines[MAX_SURFACES]; + } mpc_combines[MAX_PLANES]; int mpc_combine_count; }; @@ -1289,7 +1288,7 @@ static bool update_pipes_with_split_flags(struct dc *dc, struct dc_state *contex return updated; } -static bool should_allow_odm_power_optimization(struct dc *dc, +static bool should_apply_odm_power_optimization(struct dc *dc, struct dc_state *context, struct vba_vars_st *v, int *split, bool *merge) { @@ -1393,9 +1392,12 @@ static void try_odm_power_optimization_and_revalidate( { int i; unsigned int new_vlevel; + unsigned int cur_policy[MAX_PIPES]; - for (i = 0; i < pipe_cnt; i++) + for (i = 0; i < pipe_cnt; i++) { + cur_policy[i] = pipes[i].pipe.dest.odm_combine_policy; pipes[i].pipe.dest.odm_combine_policy = dm_odm_combine_policy_2to1; + } new_vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt); @@ -1404,6 +1406,9 @@ static void try_odm_power_optimization_and_revalidate( memset(merge, 0, MAX_PIPES * sizeof(bool)); *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, new_vlevel, split, merge); context->bw_ctx.dml.vba.VoltageLevel = *vlevel; + } else { + for (i = 0; i < pipe_cnt; i++) + pipes[i].pipe.dest.odm_combine_policy = cur_policy[i]; } } @@ -1581,7 +1586,7 @@ static void dcn32_full_validate_bw_helper(struct dc *dc, } } - if (should_allow_odm_power_optimization(dc, context, vba, split, merge)) + if (should_apply_odm_power_optimization(dc, context, vba, split, merge)) try_odm_power_optimization_and_revalidate( dc, context, pipes, split, merge, vlevel, *pipe_cnt); @@ -2210,7 +2215,8 @@ bool dcn32_internal_validate_bw(struct dc *dc, int i; pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, fast_validate); - dcn32_update_dml_pipes_odm_policy_based_on_context(dc, context, pipes); + if (!dc->config.enable_windowed_mpo_odm) + dcn32_update_dml_pipes_odm_policy_based_on_context(dc, context, pipes); /* repopulate_pipes = 1 means the pipes were either split or merged. In this case * we have to re-calculate the DET allocation and run through DML once more to @@ -2273,7 +2279,7 @@ void dcn32_calculate_wm_and_dlg_fpu(struct dc *dc, struct dc_state *context, unsigned int dummy_latency_index = 0; int maxMpcComb = context->bw_ctx.dml.vba.maxMpcComb; unsigned int min_dram_speed_mts = context->bw_ctx.dml.vba.DRAMSpeed; - bool subvp_active = resource_subvp_in_use(dc, context); + bool subvp_in_use = dcn32_subvp_in_use(dc, context); unsigned int min_dram_speed_mts_margin; bool need_fclk_lat_as_dummy = false; bool is_subvp_p_drr = false; @@ -2282,7 +2288,7 @@ void dcn32_calculate_wm_and_dlg_fpu(struct dc *dc, struct dc_state *context, dc_assert_fp_enabled(); /* need to find dummy latency index for subvp */ - if (subvp_active) { + if (subvp_in_use) { /* Override DRAMClockChangeSupport for SubVP + DRR case where the DRR cannot switch without stretching it's VBLANK */ if (!pstate_en) { context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][maxMpcComb] = dm_dram_clock_change_vblank_w_mall_sub_vp; @@ -2468,7 +2474,7 @@ void dcn32_calculate_wm_and_dlg_fpu(struct dc *dc, struct dc_state *context, dc->clk_mgr->bw_params->clk_table.entries[min_dram_speed_mts_offset].memclk_mhz * 16; } - if (!context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching && !subvp_active) { + if (!context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching && !subvp_in_use) { /* find largest table entry that is lower than dram speed, * but lower than DPM0 still uses DPM0 */ @@ -2754,7 +2760,7 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk struct _vcs_dpi_voltage_scaling_st entry = {0}; struct clk_limit_table_entry max_clk_data = {0}; - unsigned int min_dcfclk_mhz = 199, min_fclk_mhz = 299; + unsigned int min_dcfclk_mhz = 399, min_fclk_mhz = 599; static const unsigned int num_dcfclk_stas = 5; unsigned int dcfclk_sta_targets[DC__VOLTAGE_STATES] = {199, 615, 906, 1324, 1564}; @@ -3528,7 +3534,7 @@ void dcn32_set_clock_limits(const struct _vcs_dpi_soc_bounding_box_st *soc_bb) void dcn32_override_min_req_memclk(struct dc *dc, struct dc_state *context) { // WA: restrict FPO and SubVP to use first non-strobe mode (DCN32 BW issue) - if ((context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching || resource_subvp_in_use(dc, context)) && + if ((context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching || dcn32_subvp_in_use(dc, context)) && dc->dml.soc.num_chans <= 8) { int num_mclk_levels = dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_memclk_levels; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c index 3d12dabd39e4..7ea2bd5374d5 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn35/dcn35_fpu.c @@ -164,11 +164,11 @@ struct _vcs_dpi_soc_bounding_box_st dcn3_5_soc = { }, }, .num_states = 5, - .sr_exit_time_us = 14.0, - .sr_enter_plus_exit_time_us = 16.0, - .sr_exit_z8_time_us = 525.0, - .sr_enter_plus_exit_z8_time_us = 715.0, - .fclk_change_latency_us = 20.0, + .sr_exit_time_us = 28.0, + .sr_enter_plus_exit_time_us = 30.0, + .sr_exit_z8_time_us = 210.0, + .sr_enter_plus_exit_z8_time_us = 320.0, + .fclk_change_latency_us = 24.0, .usr_retraining_latency_us = 2, .writeback_latency_us = 12.0, diff --git a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_core.c b/drivers/gpu/drm/amd/display/dc/dml2/display_mode_core.c index b95bf27f2fe2..9be5ebf3a8c0 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_core.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/display_mode_core.c @@ -6229,7 +6229,7 @@ static void set_calculate_prefetch_schedule_params(struct display_mode_lib_st *m CalculatePrefetchSchedule_params->GPUVMEnable = mode_lib->ms.cache_display_cfg.plane.GPUVMEnable; CalculatePrefetchSchedule_params->HostVMEnable = mode_lib->ms.cache_display_cfg.plane.HostVMEnable; CalculatePrefetchSchedule_params->HostVMMaxNonCachedPageTableLevels = mode_lib->ms.cache_display_cfg.plane.HostVMMaxPageTableLevels; - CalculatePrefetchSchedule_params->HostVMMinPageSize = mode_lib->ms.soc.hostvm_min_page_size_kbytes; + CalculatePrefetchSchedule_params->HostVMMinPageSize = mode_lib->ms.soc.hostvm_min_page_size_kbytes * 1024; CalculatePrefetchSchedule_params->DynamicMetadataEnable = mode_lib->ms.cache_display_cfg.plane.DynamicMetadataEnable[k]; CalculatePrefetchSchedule_params->DynamicMetadataVMEnabled = mode_lib->ms.ip.dynamic_metadata_vm_enabled; CalculatePrefetchSchedule_params->DynamicMetadataLinesBeforeActiveRequired = mode_lib->ms.cache_display_cfg.plane.DynamicMetadataLinesBeforeActiveRequired[k]; @@ -6329,7 +6329,7 @@ static void dml_prefetch_check(struct display_mode_lib_st *mode_lib) mode_lib->ms.NoOfDPPThisState, mode_lib->ms.dpte_group_bytes, s->HostVMInefficiencyFactor, - mode_lib->ms.soc.hostvm_min_page_size_kbytes, + mode_lib->ms.soc.hostvm_min_page_size_kbytes * 1024, mode_lib->ms.cache_display_cfg.plane.HostVMMaxPageTableLevels); s->NextMaxVStartup = s->MaxVStartupAllPlanes[j]; @@ -6542,7 +6542,7 @@ static void dml_prefetch_check(struct display_mode_lib_st *mode_lib) mode_lib->ms.cache_display_cfg.plane.HostVMEnable, mode_lib->ms.cache_display_cfg.plane.HostVMMaxPageTableLevels, mode_lib->ms.cache_display_cfg.plane.GPUVMEnable, - mode_lib->ms.soc.hostvm_min_page_size_kbytes, + mode_lib->ms.soc.hostvm_min_page_size_kbytes * 1024, mode_lib->ms.PDEAndMetaPTEBytesPerFrame[j][k], mode_lib->ms.MetaRowBytes[j][k], mode_lib->ms.DPTEBytesPerRow[j][k], @@ -7687,7 +7687,7 @@ dml_bool_t dml_core_mode_support(struct display_mode_lib_st *mode_lib) CalculateVMRowAndSwath_params->HostVMMaxNonCachedPageTableLevels = mode_lib->ms.cache_display_cfg.plane.HostVMMaxPageTableLevels; CalculateVMRowAndSwath_params->GPUVMMaxPageTableLevels = mode_lib->ms.cache_display_cfg.plane.GPUVMMaxPageTableLevels; CalculateVMRowAndSwath_params->GPUVMMinPageSizeKBytes = mode_lib->ms.cache_display_cfg.plane.GPUVMMinPageSizeKBytes; - CalculateVMRowAndSwath_params->HostVMMinPageSize = mode_lib->ms.soc.hostvm_min_page_size_kbytes; + CalculateVMRowAndSwath_params->HostVMMinPageSize = mode_lib->ms.soc.hostvm_min_page_size_kbytes * 1024; CalculateVMRowAndSwath_params->PTEBufferModeOverrideEn = mode_lib->ms.cache_display_cfg.plane.PTEBufferModeOverrideEn; CalculateVMRowAndSwath_params->PTEBufferModeOverrideVal = mode_lib->ms.cache_display_cfg.plane.PTEBufferMode; CalculateVMRowAndSwath_params->PTEBufferSizeNotExceeded = mode_lib->ms.PTEBufferSizeNotExceededPerState; @@ -7957,7 +7957,7 @@ dml_bool_t dml_core_mode_support(struct display_mode_lib_st *mode_lib) UseMinimumDCFCLK_params->GPUVMMaxPageTableLevels = mode_lib->ms.cache_display_cfg.plane.GPUVMMaxPageTableLevels; UseMinimumDCFCLK_params->HostVMEnable = mode_lib->ms.cache_display_cfg.plane.HostVMEnable; UseMinimumDCFCLK_params->NumberOfActiveSurfaces = mode_lib->ms.num_active_planes; - UseMinimumDCFCLK_params->HostVMMinPageSize = mode_lib->ms.soc.hostvm_min_page_size_kbytes; + UseMinimumDCFCLK_params->HostVMMinPageSize = mode_lib->ms.soc.hostvm_min_page_size_kbytes * 1024; UseMinimumDCFCLK_params->HostVMMaxNonCachedPageTableLevels = mode_lib->ms.cache_display_cfg.plane.HostVMMaxPageTableLevels; UseMinimumDCFCLK_params->DynamicMetadataVMEnabled = mode_lib->ms.ip.dynamic_metadata_vm_enabled; UseMinimumDCFCLK_params->ImmediateFlipRequirement = s->ImmediateFlipRequiredFinal; @@ -8699,7 +8699,7 @@ void dml_core_mode_programming(struct display_mode_lib_st *mode_lib, const struc CalculateVMRowAndSwath_params->HostVMMaxNonCachedPageTableLevels = mode_lib->ms.cache_display_cfg.plane.HostVMMaxPageTableLevels; CalculateVMRowAndSwath_params->GPUVMMaxPageTableLevels = mode_lib->ms.cache_display_cfg.plane.GPUVMMaxPageTableLevels; CalculateVMRowAndSwath_params->GPUVMMinPageSizeKBytes = mode_lib->ms.cache_display_cfg.plane.GPUVMMinPageSizeKBytes; - CalculateVMRowAndSwath_params->HostVMMinPageSize = mode_lib->ms.soc.hostvm_min_page_size_kbytes; + CalculateVMRowAndSwath_params->HostVMMinPageSize = mode_lib->ms.soc.hostvm_min_page_size_kbytes * 1024; CalculateVMRowAndSwath_params->PTEBufferModeOverrideEn = mode_lib->ms.cache_display_cfg.plane.PTEBufferModeOverrideEn; CalculateVMRowAndSwath_params->PTEBufferModeOverrideVal = mode_lib->ms.cache_display_cfg.plane.PTEBufferMode; CalculateVMRowAndSwath_params->PTEBufferSizeNotExceeded = s->dummy_boolean_array[0]; @@ -8805,7 +8805,7 @@ void dml_core_mode_programming(struct display_mode_lib_st *mode_lib, const struc mode_lib->ms.cache_display_cfg.hw.DPPPerSurface, locals->dpte_group_bytes, s->HostVMInefficiencyFactor, - mode_lib->ms.soc.hostvm_min_page_size_kbytes, + mode_lib->ms.soc.hostvm_min_page_size_kbytes * 1024, mode_lib->ms.cache_display_cfg.plane.HostVMMaxPageTableLevels); locals->TCalc = 24.0 / locals->DCFCLKDeepSleep; @@ -8995,7 +8995,7 @@ void dml_core_mode_programming(struct display_mode_lib_st *mode_lib, const struc CalculatePrefetchSchedule_params->GPUVMEnable = mode_lib->ms.cache_display_cfg.plane.GPUVMEnable; CalculatePrefetchSchedule_params->HostVMEnable = mode_lib->ms.cache_display_cfg.plane.HostVMEnable; CalculatePrefetchSchedule_params->HostVMMaxNonCachedPageTableLevels = mode_lib->ms.cache_display_cfg.plane.HostVMMaxPageTableLevels; - CalculatePrefetchSchedule_params->HostVMMinPageSize = mode_lib->ms.soc.hostvm_min_page_size_kbytes; + CalculatePrefetchSchedule_params->HostVMMinPageSize = mode_lib->ms.soc.hostvm_min_page_size_kbytes * 1024; CalculatePrefetchSchedule_params->DynamicMetadataEnable = mode_lib->ms.cache_display_cfg.plane.DynamicMetadataEnable[k]; CalculatePrefetchSchedule_params->DynamicMetadataVMEnabled = mode_lib->ms.ip.dynamic_metadata_vm_enabled; CalculatePrefetchSchedule_params->DynamicMetadataLinesBeforeActiveRequired = mode_lib->ms.cache_display_cfg.plane.DynamicMetadataLinesBeforeActiveRequired[k]; @@ -9240,7 +9240,7 @@ void dml_core_mode_programming(struct display_mode_lib_st *mode_lib, const struc mode_lib->ms.cache_display_cfg.plane.HostVMEnable, mode_lib->ms.cache_display_cfg.plane.HostVMMaxPageTableLevels, mode_lib->ms.cache_display_cfg.plane.GPUVMEnable, - mode_lib->ms.soc.hostvm_min_page_size_kbytes, + mode_lib->ms.soc.hostvm_min_page_size_kbytes * 1024, locals->PDEAndMetaPTEBytesFrame[k], locals->MetaRowByte[k], locals->PixelPTEBytesPerRow[k], @@ -9446,13 +9446,13 @@ void dml_core_mode_programming(struct display_mode_lib_st *mode_lib, const struc CalculateWatermarks_params->CompressedBufferSizeInkByte = locals->CompressedBufferSizeInkByte; // Output - CalculateWatermarks_params->Watermark = &s->dummy_watermark; // Watermarks *Watermark - CalculateWatermarks_params->DRAMClockChangeSupport = &mode_lib->ms.support.DRAMClockChangeSupport[0]; - CalculateWatermarks_params->MaxActiveDRAMClockChangeLatencySupported = &s->dummy_single_array[0][0]; // dml_float_t *MaxActiveDRAMClockChangeLatencySupported[] - CalculateWatermarks_params->SubViewportLinesNeededInMALL = &mode_lib->ms.SubViewportLinesNeededInMALL[j]; // dml_uint_t SubViewportLinesNeededInMALL[] - CalculateWatermarks_params->FCLKChangeSupport = &mode_lib->ms.support.FCLKChangeSupport[0]; - CalculateWatermarks_params->MaxActiveFCLKChangeLatencySupported = &s->dummy_single[0]; // dml_float_t *MaxActiveFCLKChangeLatencySupported - CalculateWatermarks_params->USRRetrainingSupport = &mode_lib->ms.support.USRRetrainingSupport[0]; + CalculateWatermarks_params->Watermark = &locals->Watermark; // Watermarks *Watermark + CalculateWatermarks_params->DRAMClockChangeSupport = &locals->DRAMClockChangeSupport; + CalculateWatermarks_params->MaxActiveDRAMClockChangeLatencySupported = locals->MaxActiveDRAMClockChangeLatencySupported; // dml_float_t *MaxActiveDRAMClockChangeLatencySupported[] + CalculateWatermarks_params->SubViewportLinesNeededInMALL = locals->SubViewportLinesNeededInMALL; // dml_uint_t SubViewportLinesNeededInMALL[] + CalculateWatermarks_params->FCLKChangeSupport = &locals->FCLKChangeSupport; + CalculateWatermarks_params->MaxActiveFCLKChangeLatencySupported = &locals->MaxActiveFCLKChangeLatencySupported; // dml_float_t *MaxActiveFCLKChangeLatencySupported + CalculateWatermarks_params->USRRetrainingSupport = &locals->USRRetrainingSupport; CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport( &mode_lib->scratch, diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.c index fa6a93dd9629..23a608274096 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.c @@ -341,9 +341,6 @@ void dml2_init_soc_states(struct dml2_context *dml2, const struct dc *in_dc, break; } - if (dml2->config.bbox_overrides.clks_table.num_states) - p->in_states->num_states = dml2->config.bbox_overrides.clks_table.num_states; - /* Override from passed values, if available */ for (i = 0; i < p->in_states->num_states; i++) { if (dml2->config.bbox_overrides.sr_exit_latency_us) { @@ -400,6 +397,7 @@ void dml2_init_soc_states(struct dml2_context *dml2, const struct dc *in_dc, } /* Copy clocks tables entries, if available */ if (dml2->config.bbox_overrides.clks_table.num_states) { + p->in_states->num_states = dml2->config.bbox_overrides.clks_table.num_states; for (i = 0; i < dml2->config.bbox_overrides.clks_table.num_entries_per_clk.num_dcfclk_levels; i++) { p->in_states->state_array[i].dcfclk_mhz = dml2->config.bbox_overrides.clks_table.clk_entries[i].dcfclk_mhz; @@ -626,8 +624,8 @@ static void populate_dml_output_cfg_from_stream_state(struct dml_output_cfg_st * if (is_dp2p0_output_encoder(pipe)) out->OutputEncoder[location] = dml_dp2p0; break; - out->OutputEncoder[location] = dml_edp; case SIGNAL_TYPE_EDP: + out->OutputEncoder[location] = dml_edp; break; case SIGNAL_TYPE_HDMI_TYPE_A: case SIGNAL_TYPE_DVI_SINGLE_LINK: @@ -793,35 +791,28 @@ static void populate_dml_surface_cfg_from_plane_state(enum dml_project_id dml2_p } } -/*TODO no support for mpc combine, need rework - should calculate scaling params based on plane+stream*/ -static struct scaler_data get_scaler_data_for_plane(const struct dc_plane_state *in, const struct dc_state *context) +static struct scaler_data get_scaler_data_for_plane(const struct dc_plane_state *in, struct dc_state *context) { int i; - struct scaler_data data = { 0 }; + struct pipe_ctx *temp_pipe = &context->res_ctx.temp_pipe; + + memset(temp_pipe, 0, sizeof(struct pipe_ctx)); for (i = 0; i < MAX_PIPES; i++) { const struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; if (pipe->plane_state == in && !pipe->prev_odm_pipe) { - const struct pipe_ctx *next_pipe = pipe->next_odm_pipe; - - data = context->res_ctx.pipe_ctx[i].plane_res.scl_data; - while (next_pipe) { - data.h_active += next_pipe->plane_res.scl_data.h_active; - data.recout.width += next_pipe->plane_res.scl_data.recout.width; - if (in->rotation == ROTATION_ANGLE_0 || in->rotation == ROTATION_ANGLE_180) { - data.viewport.width += next_pipe->plane_res.scl_data.viewport.width; - } else { - data.viewport.height += next_pipe->plane_res.scl_data.viewport.height; - } - next_pipe = next_pipe->next_odm_pipe; - } + temp_pipe->stream = pipe->stream; + temp_pipe->plane_state = pipe->plane_state; + temp_pipe->plane_res.scl_data.taps = pipe->plane_res.scl_data.taps; + + resource_build_scaling_params(temp_pipe); break; } } ASSERT(i < MAX_PIPES); - return data; + return temp_pipe->plane_res.scl_data; } static void populate_dummy_dml_plane_cfg(struct dml_plane_cfg_st *out, unsigned int location, const struct dc_stream_state *in) @@ -866,7 +857,7 @@ static void populate_dummy_dml_plane_cfg(struct dml_plane_cfg_st *out, unsigned out->ScalerEnabled[location] = false; } -static void populate_dml_plane_cfg_from_plane_state(struct dml_plane_cfg_st *out, unsigned int location, const struct dc_plane_state *in, const struct dc_state *context) +static void populate_dml_plane_cfg_from_plane_state(struct dml_plane_cfg_st *out, unsigned int location, const struct dc_plane_state *in, struct dc_state *context) { const struct scaler_data scaler_data = get_scaler_data_for_plane(in, context); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c index fb328cd06cea..01493c49bd7a 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c @@ -1183,9 +1183,9 @@ void dce110_disable_stream(struct pipe_ctx *pipe_ctx) dto_params.timing = &pipe_ctx->stream->timing; dp_hpo_inst = pipe_ctx->stream_res.hpo_dp_stream_enc->inst; if (dccg) { - dccg->funcs->set_dtbclk_dto(dccg, &dto_params); dccg->funcs->disable_symclk32_se(dccg, dp_hpo_inst); dccg->funcs->set_dpstreamclk(dccg, REFCLK, tg->inst, dp_hpo_inst); + dccg->funcs->set_dtbclk_dto(dccg, &dto_params); } } else if (dccg && dccg->funcs->disable_symclk_se) { dccg->funcs->disable_symclk_se(dccg, stream_enc->stream_enc_inst, @@ -1354,7 +1354,7 @@ static void build_audio_output( if (state->clk_mgr && (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT || pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)) { - audio_output->pll_info.dp_dto_source_clock_in_khz = + audio_output->pll_info.audio_dto_source_clock_in_khz = state->clk_mgr->funcs->get_dp_ref_clk_frequency( state->clk_mgr); } @@ -1476,7 +1476,7 @@ static enum dc_status dce110_enable_stream_timing( return DC_OK; } -static enum dc_status apply_single_controller_ctx_to_hw( +enum dc_status dce110_apply_single_controller_ctx_to_hw( struct pipe_ctx *pipe_ctx, struct dc_state *context, struct dc *dc) @@ -2302,7 +2302,7 @@ enum dc_status dce110_apply_ctx_to_hw( if (pipe_ctx->top_pipe || pipe_ctx->prev_odm_pipe) continue; - status = apply_single_controller_ctx_to_hw( + status = dce110_apply_single_controller_ctx_to_hw( pipe_ctx, context, dc); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.h index 08028a1779ae..ed3cc3648e8e 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.h @@ -39,6 +39,10 @@ enum dc_status dce110_apply_ctx_to_hw( struct dc *dc, struct dc_state *context); +enum dc_status dce110_apply_single_controller_ctx_to_hw( + struct pipe_ctx *pipe_ctx, + struct dc_state *context, + struct dc *dc); void dce110_enable_stream(struct pipe_ctx *pipe_ctx); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index 51dd2ae09b2a..6dd479e8a348 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -3076,7 +3076,7 @@ void dcn10_prepare_bandwidth( context, false); - dc->optimized_required |= hubbub->funcs->program_watermarks(hubbub, + dc->wm_optimized_required = hubbub->funcs->program_watermarks(hubbub, &context->bw_ctx.bw.dcn.watermarks, dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, true); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c index bc71a9b058fe..931ac8ed7069 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c @@ -1882,42 +1882,6 @@ static void dcn20_program_pipe( } } -static void update_vmin_vmax_fams(struct dc *dc, - struct dc_state *context) -{ - uint32_t i; - struct drr_params params = {0}; - bool subvp_in_use = resource_subvp_in_use(dc, context); - - for (i = 0; i < dc->res_pool->pipe_count; i++) { - struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; - - if (resource_is_pipe_type(pipe, OTG_MASTER) && - ((subvp_in_use && dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_PHANTOM && - pipe->stream->allow_freesync) || (context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching && pipe->stream->fpo_in_use))) { - if (!pipe->stream->vrr_active_variable && !pipe->stream->vrr_active_fixed) { - struct timing_generator *tg = context->res_ctx.pipe_ctx[i].stream_res.tg; - - /* DRR should be configured already if we're in active variable - * or active fixed, so only program if we're not in this state - */ - params.vertical_total_min = pipe->stream->timing.v_total; - params.vertical_total_max = pipe->stream->timing.v_total; - tg->funcs->set_drr(tg, ¶ms); - } - } else { - if (resource_is_pipe_type(pipe, OTG_MASTER) && - !pipe->stream->vrr_active_variable && - !pipe->stream->vrr_active_fixed) { - struct timing_generator *tg = context->res_ctx.pipe_ctx[i].stream_res.tg; - params.vertical_total_min = 0; - params.vertical_total_max = 0; - tg->funcs->set_drr(tg, ¶ms); - } - } - } -} - void dcn20_program_front_end_for_ctx( struct dc *dc, struct dc_state *context) @@ -1994,7 +1958,6 @@ void dcn20_program_front_end_for_ctx( && context->res_ctx.pipe_ctx[i].stream) hws->funcs.blank_pixel_data(dc, &context->res_ctx.pipe_ctx[i], true); - update_vmin_vmax_fams(dc, context); /* Disconnect mpcc */ for (i = 0; i < dc->res_pool->pipe_count; i++) @@ -2196,10 +2159,10 @@ void dcn20_prepare_bandwidth( } /* program dchubbub watermarks: - * For assigning optimized_required, use |= operator since we don't want + * For assigning wm_optimized_required, use |= operator since we don't want * to clear the value if the optimize has not happened yet */ - dc->optimized_required |= hubbub->funcs->program_watermarks(hubbub, + dc->wm_optimized_required |= hubbub->funcs->program_watermarks(hubbub, &context->bw_ctx.bw.dcn.watermarks, dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, false); @@ -2212,10 +2175,10 @@ void dcn20_prepare_bandwidth( if (hubbub->funcs->program_compbuf_size) { if (context->bw_ctx.dml.ip.min_comp_buffer_size_kbytes) { compbuf_size_kb = context->bw_ctx.dml.ip.min_comp_buffer_size_kbytes; - dc->optimized_required |= (compbuf_size_kb != dc->current_state->bw_ctx.dml.ip.min_comp_buffer_size_kbytes); + dc->wm_optimized_required |= (compbuf_size_kb != dc->current_state->bw_ctx.dml.ip.min_comp_buffer_size_kbytes); } else { compbuf_size_kb = context->bw_ctx.bw.dcn.compbuf_size_kb; - dc->optimized_required |= (compbuf_size_kb != dc->current_state->bw_ctx.bw.dcn.compbuf_size_kb); + dc->wm_optimized_required |= (compbuf_size_kb != dc->current_state->bw_ctx.bw.dcn.compbuf_size_kb); } hubbub->funcs->program_compbuf_size(hubbub, compbuf_size_kb, false); @@ -2598,7 +2561,7 @@ void dcn20_setup_vupdate_interrupt(struct dc *dc, struct pipe_ctx *pipe_ctx) tg->funcs->setup_vertical_interrupt2(tg, start_line); } -static void dcn20_reset_back_end_for_pipe( +void dcn20_reset_back_end_for_pipe( struct dc *dc, struct pipe_ctx *pipe_ctx, struct dc_state *context) @@ -2827,18 +2790,17 @@ void dcn20_enable_stream(struct pipe_ctx *pipe_ctx) } if (dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { - dp_hpo_inst = pipe_ctx->stream_res.hpo_dp_stream_enc->inst; - dccg->funcs->set_dpstreamclk(dccg, DTBCLK0, tg->inst, dp_hpo_inst); - - phyd32clk = get_phyd32clk_src(link); - dccg->funcs->enable_symclk32_se(dccg, dp_hpo_inst, phyd32clk); - dto_params.otg_inst = tg->inst; dto_params.pixclk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10; dto_params.num_odm_segments = get_odm_segment_count(pipe_ctx); dto_params.timing = &pipe_ctx->stream->timing; dto_params.ref_dtbclk_khz = dc->clk_mgr->funcs->get_dtb_ref_clk_frequency(dc->clk_mgr); dccg->funcs->set_dtbclk_dto(dccg, &dto_params); + dp_hpo_inst = pipe_ctx->stream_res.hpo_dp_stream_enc->inst; + dccg->funcs->set_dpstreamclk(dccg, DTBCLK0, tg->inst, dp_hpo_inst); + + phyd32clk = get_phyd32clk_src(link); + dccg->funcs->enable_symclk32_se(dccg, dp_hpo_inst, phyd32clk); } else { if (dccg->funcs->enable_symclk_se) dccg->funcs->enable_symclk_se(dccg, stream_enc->stream_enc_inst, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h index b94c85340abf..d950b3e54ec2 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h @@ -84,6 +84,10 @@ enum dc_status dcn20_enable_stream_timing( void dcn20_disable_stream_gating(struct dc *dc, struct pipe_ctx *pipe_ctx); void dcn20_enable_stream_gating(struct dc *dc, struct pipe_ctx *pipe_ctx); void dcn20_setup_vupdate_interrupt(struct dc *dc, struct pipe_ctx *pipe_ctx); +void dcn20_reset_back_end_for_pipe( + struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_state *context); void dcn20_init_blank( struct dc *dc, struct timing_generator *tg); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn21/dcn21_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn21/dcn21_hwseq.c index 8e88dcaf88f5..5c7f380a84f9 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn21/dcn21_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn21/dcn21_hwseq.c @@ -206,28 +206,32 @@ void dcn21_set_abm_immediate_disable(struct pipe_ctx *pipe_ctx) void dcn21_set_pipe(struct pipe_ctx *pipe_ctx) { struct abm *abm = pipe_ctx->stream_res.abm; - uint32_t otg_inst = pipe_ctx->stream_res.tg->inst; + struct timing_generator *tg = pipe_ctx->stream_res.tg; struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl; struct dmcu *dmcu = pipe_ctx->stream->ctx->dc->res_pool->dmcu; + uint32_t otg_inst; + + if (!abm && !tg && !panel_cntl) + return; + + otg_inst = tg->inst; if (dmcu) { dce110_set_pipe(pipe_ctx); return; } - if (abm && panel_cntl) { - if (abm->funcs && abm->funcs->set_pipe_ex) { - abm->funcs->set_pipe_ex(abm, + if (abm->funcs && abm->funcs->set_pipe_ex) { + abm->funcs->set_pipe_ex(abm, otg_inst, SET_ABM_PIPE_NORMAL, panel_cntl->inst, panel_cntl->pwrseq_inst); - } else { - dmub_abm_set_pipe(abm, otg_inst, - SET_ABM_PIPE_NORMAL, - panel_cntl->inst, - panel_cntl->pwrseq_inst); - } + } else { + dmub_abm_set_pipe(abm, otg_inst, + SET_ABM_PIPE_NORMAL, + panel_cntl->inst, + panel_cntl->pwrseq_inst); } } @@ -237,34 +241,35 @@ bool dcn21_set_backlight_level(struct pipe_ctx *pipe_ctx, { struct dc_context *dc = pipe_ctx->stream->ctx; struct abm *abm = pipe_ctx->stream_res.abm; + struct timing_generator *tg = pipe_ctx->stream_res.tg; struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl; + uint32_t otg_inst; + + if (!abm && !tg && !panel_cntl) + return false; + + otg_inst = tg->inst; if (dc->dc->res_pool->dmcu) { dce110_set_backlight_level(pipe_ctx, backlight_pwm_u16_16, frame_ramp); return true; } - if (abm != NULL) { - uint32_t otg_inst = pipe_ctx->stream_res.tg->inst; - - if (abm && panel_cntl) { - if (abm->funcs && abm->funcs->set_pipe_ex) { - abm->funcs->set_pipe_ex(abm, - otg_inst, - SET_ABM_PIPE_NORMAL, - panel_cntl->inst, - panel_cntl->pwrseq_inst); - } else { - dmub_abm_set_pipe(abm, - otg_inst, - SET_ABM_PIPE_NORMAL, - panel_cntl->inst, - panel_cntl->pwrseq_inst); - } - } + if (abm->funcs && abm->funcs->set_pipe_ex) { + abm->funcs->set_pipe_ex(abm, + otg_inst, + SET_ABM_PIPE_NORMAL, + panel_cntl->inst, + panel_cntl->pwrseq_inst); + } else { + dmub_abm_set_pipe(abm, + otg_inst, + SET_ABM_PIPE_NORMAL, + panel_cntl->inst, + panel_cntl->pwrseq_inst); } - if (abm && abm->funcs && abm->funcs->set_backlight_level_pwm) + if (abm->funcs && abm->funcs->set_backlight_level_pwm) abm->funcs->set_backlight_level_pwm(abm, backlight_pwm_u16_16, frame_ramp, 0, panel_cntl->inst); else diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c index 6c9299c7683d..aa36d7a56ca8 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c @@ -1474,9 +1474,44 @@ void dcn32_update_dsc_pg(struct dc *dc, } } +void dcn32_disable_phantom_streams(struct dc *dc, struct dc_state *context) +{ + struct dce_hwseq *hws = dc->hwseq; + int i; + + for (i = dc->res_pool->pipe_count - 1; i >= 0 ; i--) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (!pipe_ctx_old->stream) + continue; + + if (dc_state_get_pipe_subvp_type(dc->current_state, pipe_ctx_old) != SUBVP_PHANTOM) + continue; + + if (pipe_ctx_old->top_pipe || pipe_ctx_old->prev_odm_pipe) + continue; + + if (!pipe_ctx->stream || pipe_need_reprogram(pipe_ctx_old, pipe_ctx) || + (pipe_ctx->stream && dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM)) { + struct clock_source *old_clk = pipe_ctx_old->clock_source; + + if (hws->funcs.reset_back_end_for_pipe) + hws->funcs.reset_back_end_for_pipe(dc, pipe_ctx_old, dc->current_state); + if (hws->funcs.enable_stream_gating) + hws->funcs.enable_stream_gating(dc, pipe_ctx_old); + if (old_clk) + old_clk->funcs->cs_power_down(old_clk); + } + } +} + void dcn32_enable_phantom_streams(struct dc *dc, struct dc_state *context) { unsigned int i; + enum dc_status status = DC_OK; + struct dce_hwseq *hws = dc->hwseq; for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; @@ -1497,16 +1532,39 @@ void dcn32_enable_phantom_streams(struct dc *dc, struct dc_state *context) } } for (i = 0; i < dc->res_pool->pipe_count; i++) { - struct pipe_ctx *new_pipe = &context->res_ctx.pipe_ctx[i]; - - if (new_pipe->stream && dc_state_get_pipe_subvp_type(context, new_pipe) == SUBVP_PHANTOM) { - // If old context or new context has phantom pipes, apply - // the phantom timings now. We can't change the phantom - // pipe configuration safely without driver acquiring - // the DMCUB lock first. - dc->hwss.apply_ctx_to_hw(dc, context); - break; + struct pipe_ctx *pipe_ctx_old = + &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM) + continue; + + if (pipe_ctx->stream == pipe_ctx_old->stream && + pipe_ctx->stream->link->link_state_valid) { + continue; } + + if (pipe_ctx_old->stream && !pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) + continue; + + if (pipe_ctx->top_pipe || pipe_ctx->prev_odm_pipe) + continue; + + if (hws->funcs.apply_single_controller_ctx_to_hw) + status = hws->funcs.apply_single_controller_ctx_to_hw( + pipe_ctx, + context, + dc); + + ASSERT(status == DC_OK); + +#ifdef CONFIG_DRM_AMD_DC_FP + if (hws->funcs.resync_fifo_dccg_dio) + hws->funcs.resync_fifo_dccg_dio(hws, dc, context); +#endif } } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h index cecf7f0f5671..069e20bc87c0 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h @@ -111,6 +111,8 @@ void dcn32_update_dsc_pg(struct dc *dc, void dcn32_enable_phantom_streams(struct dc *dc, struct dc_state *context); +void dcn32_disable_phantom_streams(struct dc *dc, struct dc_state *context); + void dcn32_init_blank( struct dc *dc, struct timing_generator *tg); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c index 427cfc8c24a4..e8ac94a005b8 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c @@ -109,6 +109,7 @@ static const struct hw_sequencer_funcs dcn32_funcs = { .get_dcc_en_bits = dcn10_get_dcc_en_bits, .commit_subvp_config = dcn32_commit_subvp_config, .enable_phantom_streams = dcn32_enable_phantom_streams, + .disable_phantom_streams = dcn32_disable_phantom_streams, .subvp_pipe_control_lock = dcn32_subvp_pipe_control_lock, .update_visual_confirm_color = dcn10_update_visual_confirm_color, .subvp_pipe_control_lock_fast = dcn32_subvp_pipe_control_lock_fast, @@ -159,6 +160,8 @@ static const struct hwseq_private_funcs dcn32_private_funcs = { .set_pixels_per_cycle = dcn32_set_pixels_per_cycle, .resync_fifo_dccg_dio = dcn32_resync_fifo_dccg_dio, .is_dp_dig_pixel_rate_div_policy = dcn32_is_dp_dig_pixel_rate_div_policy, + .apply_single_controller_ctx_to_hw = dce110_apply_single_controller_ctx_to_hw, + .reset_back_end_for_pipe = dcn20_reset_back_end_for_pipe, }; void dcn32_hw_sequencer_init_functions(struct dc *dc) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c index 9c806385ecbd..8b6c49622f3b 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c @@ -680,7 +680,7 @@ void dcn35_power_down_on_boot(struct dc *dc) bool dcn35_apply_idle_power_optimizations(struct dc *dc, bool enable) { struct dc_link *edp_links[MAX_NUM_EDP]; - int edp_num; + int i, edp_num; if (dc->debug.dmcub_emulation) return true; @@ -688,6 +688,13 @@ bool dcn35_apply_idle_power_optimizations(struct dc *dc, bool enable) dc_get_edp_links(dc, edp_links, &edp_num); if (edp_num == 0 || edp_num > 1) return false; + + for (i = 0; i < dc->current_state->stream_count; ++i) { + struct dc_stream_state *stream = dc->current_state->streams[i]; + + if (!stream->dpms_off && !dc_is_embedded_signal(stream->signal)) + return false; + } } // TODO: review other cases when idle optimization is allowed diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h index a54399383318..64ca7c66509b 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h @@ -379,6 +379,7 @@ struct hw_sequencer_funcs { struct dc_cursor_attributes *cursor_attr); void (*commit_subvp_config)(struct dc *dc, struct dc_state *context); void (*enable_phantom_streams)(struct dc *dc, struct dc_state *context); + void (*disable_phantom_streams)(struct dc *dc, struct dc_state *context); void (*subvp_pipe_control_lock)(struct dc *dc, struct dc_state *context, bool lock, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h index 6137cf09aa54..b3c62a82cb1c 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h @@ -165,8 +165,15 @@ struct hwseq_private_funcs { void (*set_pixels_per_cycle)(struct pipe_ctx *pipe_ctx); void (*resync_fifo_dccg_dio)(struct dce_hwseq *hws, struct dc *dc, struct dc_state *context); + enum dc_status (*apply_single_controller_ctx_to_hw)( + struct pipe_ctx *pipe_ctx, + struct dc_state *context, + struct dc *dc); bool (*is_dp_dig_pixel_rate_div_policy)(struct pipe_ctx *pipe_ctx); #endif + void (*reset_back_end_for_pipe)(struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_state *context); }; struct dce_hwseq { diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h index f74ae0d41d3c..3a6bf77a6873 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h +++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h @@ -469,6 +469,8 @@ struct resource_context { unsigned int hpo_dp_link_enc_to_link_idx[MAX_HPO_DP2_LINK_ENCODERS]; int hpo_dp_link_enc_ref_cnts[MAX_HPO_DP2_LINK_ENCODERS]; bool is_mpc_3dlut_acquired[MAX_PIPES]; + /* solely used for build scalar data in dml2 */ + struct pipe_ctx temp_pipe; }; struct dce_bw_output { diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h index cbba39d251e5..17e014d3bdc8 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h @@ -333,6 +333,7 @@ struct clk_mgr { bool force_smu_not_present; bool dc_mode_softmax_enabled; int dprefclk_khz; // Used by program pixel clock in clock source funcs, need to figureout where this goes + int dp_dto_source_clock_in_khz; // Used to program DP DTO with ss adjustment on DCN314 int dentist_vco_freq_khz; struct clk_state_registers_and_bypass boot_snapshot; struct clk_bw_params *bw_params; diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h index 1d51fed12e20..77a60aa9f27b 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/resource.h +++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h @@ -427,22 +427,18 @@ struct pipe_ctx *resource_get_primary_dpp_pipe(const struct pipe_ctx *dpp_pipe); int resource_get_mpc_slice_index(const struct pipe_ctx *dpp_pipe); /* - * Get number of MPC "cuts" of the plane associated with the pipe. MPC slice - * count is equal to MPC splits + 1. For example if a plane is cut 3 times, it - * will have 4 pieces of slice. - * return - 0 if pipe is not used for a plane with MPCC combine. otherwise - * the number of MPC "cuts" for the plane. + * Get the number of MPC slices associated with the pipe. + * The function returns 0 if the pipe is not associated with an MPC combine + * pipe topology. */ -int resource_get_mpc_slice_count(const struct pipe_ctx *opp_head); +int resource_get_mpc_slice_count(const struct pipe_ctx *pipe); /* - * Get number of ODM "cuts" of the timing associated with the pipe. ODM slice - * count is equal to ODM splits + 1. For example if a timing is cut 3 times, it - * will have 4 pieces of slice. - * return - 0 if pipe is not used for ODM combine. otherwise - * the number of ODM "cuts" for the timing. + * Get the number of ODM slices associated with the pipe. + * The function returns 0 if the pipe is not associated with an ODM combine + * pipe topology. */ -int resource_get_odm_slice_count(const struct pipe_ctx *otg_master); +int resource_get_odm_slice_count(const struct pipe_ctx *pipe); /* Get the ODM slice index counting from 0 from left most slice */ int resource_get_odm_slice_index(const struct pipe_ctx *opp_head); @@ -609,9 +605,6 @@ bool dc_resource_acquire_secondary_pipe_for_mpc_odm_legacy( struct pipe_ctx *sec_pipe, bool odm); -bool resource_subvp_in_use(struct dc *dc, - struct dc_state *context); - /* A test harness interface that modifies dp encoder resources in the given dc * state and bypasses the need to revalidate. The interface assumes that the * test harness interface is called with pre-validated link config stored in the diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c index 5fe8b4871c77..3cbfbf8d107e 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c @@ -900,11 +900,15 @@ bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx, bool enable, bool immedi { struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc; struct dc_stream_state *stream = pipe_ctx->stream; - DC_LOGGER_INIT(dsc->ctx->logger); - if (!pipe_ctx->stream->timing.flags.DSC || !dsc) + if (!pipe_ctx->stream->timing.flags.DSC) return false; + if (!dsc) + return false; + + DC_LOGGER_INIT(dsc->ctx->logger); + if (enable) { struct dsc_config dsc_cfg; uint8_t dsc_packed_pps[128]; @@ -2005,17 +2009,11 @@ static enum dc_status enable_link_dp(struct dc_state *state, } } - /* - * If the link is DP-over-USB4 do the following: - * - Train with fallback when enabling DPIA link. Conventional links are + /* Train with fallback when enabling DPIA link. Conventional links are * trained with fallback during sink detection. - * - Allocate only what the stream needs for bw in Gbps. Inform the CM - * in case stream needs more or less bw from what has been allocated - * earlier at plug time. */ - if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) { + if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) do_fallback = true; - } /* * Temporary w/a to get DP2.0 link rates to work with SST. @@ -2197,6 +2195,32 @@ static enum dc_status enable_link( return status; } +static bool allocate_usb4_bandwidth_for_stream(struct dc_stream_state *stream, int bw) +{ + return true; +} + +static bool allocate_usb4_bandwidth(struct dc_stream_state *stream) +{ + bool ret; + + int bw = dc_bandwidth_in_kbps_from_timing(&stream->timing, + dc_link_get_highest_encoding_format(stream->sink->link)); + + ret = allocate_usb4_bandwidth_for_stream(stream, bw); + + return ret; +} + +static bool deallocate_usb4_bandwidth(struct dc_stream_state *stream) +{ + bool ret; + + ret = allocate_usb4_bandwidth_for_stream(stream, 0); + + return ret; +} + void link_set_dpms_off(struct pipe_ctx *pipe_ctx) { struct dc *dc = pipe_ctx->stream->ctx->dc; @@ -2232,6 +2256,9 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx) update_psp_stream_config(pipe_ctx, true); dc->hwss.blank_stream(pipe_ctx); + if (pipe_ctx->stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) + deallocate_usb4_bandwidth(pipe_ctx->stream); + if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) deallocate_mst_payload(pipe_ctx); else if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT && @@ -2474,6 +2501,9 @@ void link_set_dpms_on( } } + if (pipe_ctx->stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) + allocate_usb4_bandwidth(pipe_ctx->stream); + if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) allocate_mst_payload(pipe_ctx); else if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT && diff --git a/drivers/gpu/drm/amd/display/dc/link/link_validation.c b/drivers/gpu/drm/amd/display/dc/link/link_validation.c index b45fda96eaf6..8fe66c367850 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_validation.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_validation.c @@ -346,23 +346,61 @@ enum dc_status link_validate_mode_timing( return DC_OK; } +/* + * This function calculates the bandwidth required for the stream timing + * and aggregates the stream bandwidth for the respective dpia link + * + * @stream: pointer to the dc_stream_state struct instance + * @num_streams: number of streams to be validated + * + * return: true if validation is succeeded + */ bool link_validate_dpia_bandwidth(const struct dc_stream_state *stream, const unsigned int num_streams) { - bool ret = true; - int bw_needed[MAX_DPIA_NUM]; - struct dc_link *link[MAX_DPIA_NUM]; - - if (!num_streams || num_streams > MAX_DPIA_NUM) - return ret; + int bw_needed[MAX_DPIA_NUM] = {0}; + struct dc_link *dpia_link[MAX_DPIA_NUM] = {0}; + int num_dpias = 0; for (uint8_t i = 0; i < num_streams; ++i) { + if (stream[i].signal == SIGNAL_TYPE_DISPLAY_PORT) { + /* new dpia sst stream, check whether it exceeds max dpia */ + if (num_dpias >= MAX_DPIA_NUM) + return false; - link[i] = stream[i].link; - bw_needed[i] = dc_bandwidth_in_kbps_from_timing(&stream[i].timing, - dc_link_get_highest_encoding_format(link[i])); + dpia_link[num_dpias] = stream[i].link; + bw_needed[num_dpias] = dc_bandwidth_in_kbps_from_timing(&stream[i].timing, + dc_link_get_highest_encoding_format(dpia_link[num_dpias])); + num_dpias++; + } else if (stream[i].signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + uint8_t j = 0; + /* check whether its a known dpia link */ + for (; j < num_dpias; ++j) { + if (dpia_link[j] == stream[i].link) + break; + } + + if (j == num_dpias) { + /* new dpia mst stream, check whether it exceeds max dpia */ + if (num_dpias >= MAX_DPIA_NUM) + return false; + else { + dpia_link[j] = stream[i].link; + num_dpias++; + } + } + + bw_needed[j] += dc_bandwidth_in_kbps_from_timing(&stream[i].timing, + dc_link_get_highest_encoding_format(dpia_link[j])); + } } - ret = dpia_validate_usb4_bw(link, bw_needed, num_streams); + /* Include dp overheads */ + for (uint8_t i = 0; i < num_dpias; ++i) { + int dp_overhead = 0; + + dp_overhead = link_dp_dpia_get_dp_overhead_in_dp_tunneling(dpia_link[i]); + bw_needed[i] += dp_overhead; + } - return ret; + return dpia_validate_usb4_bw(dpia_link, bw_needed, num_dpias); } diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c index 982eda3c46f5..6af42ba9885c 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia.c @@ -82,25 +82,33 @@ bool dpia_query_hpd_status(struct dc_link *link) { union dmub_rb_cmd cmd = {0}; struct dc_dmub_srv *dmub_srv = link->ctx->dmub_srv; - bool is_hpd_high = false; /* prepare QUERY_HPD command */ cmd.query_hpd.header.type = DMUB_CMD__QUERY_HPD_STATE; cmd.query_hpd.data.instance = link->link_id.enum_id - ENUM_ID_1; cmd.query_hpd.data.ch_type = AUX_CHANNEL_DPIA; - /* Return HPD status reported by DMUB if query successfully executed. */ - if (dc_wake_and_execute_dmub_cmd(dmub_srv->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) && - cmd.query_hpd.data.status == AUX_RET_SUCCESS) - is_hpd_high = cmd.query_hpd.data.result; - - DC_LOG_DEBUG("%s: link(%d) dpia(%d) cmd_status(%d) result(%d)\n", - __func__, - link->link_index, - link->link_id.enum_id - ENUM_ID_1, - cmd.query_hpd.data.status, - cmd.query_hpd.data.result); - - return is_hpd_high; + /* Query dpia hpd status from dmub */ + if (dc_wake_and_execute_dmub_cmd(dmub_srv->ctx, &cmd, + DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) && + cmd.query_hpd.data.status == AUX_RET_SUCCESS) { + DC_LOG_DEBUG("%s: for link(%d) dpia(%d) success, current_hpd_status(%d) new_hpd_status(%d)\n", + __func__, + link->link_index, + link->link_id.enum_id - ENUM_ID_1, + link->hpd_status, + cmd.query_hpd.data.result); + link->hpd_status = cmd.query_hpd.data.result; + } else { + DC_LOG_ERROR("%s: for link(%d) dpia(%d) failed with status(%d), current_hpd_status(%d) new_hpd_status(0)\n", + __func__, + link->link_index, + link->link_id.enum_id - ENUM_ID_1, + cmd.query_hpd.data.status, + link->hpd_status); + link->hpd_status = false; + } + + return link->hpd_status; } diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c index a7aa8c9da868..5491b707cec8 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c @@ -54,12 +54,18 @@ static bool get_bw_alloc_proceed_flag(struct dc_link *tmp) static void reset_bw_alloc_struct(struct dc_link *link) { link->dpia_bw_alloc_config.bw_alloc_enabled = false; - link->dpia_bw_alloc_config.sink_verified_bw = 0; - link->dpia_bw_alloc_config.sink_max_bw = 0; + link->dpia_bw_alloc_config.link_verified_bw = 0; + link->dpia_bw_alloc_config.link_max_bw = 0; + link->dpia_bw_alloc_config.allocated_bw = 0; link->dpia_bw_alloc_config.estimated_bw = 0; link->dpia_bw_alloc_config.bw_granularity = 0; + link->dpia_bw_alloc_config.dp_overhead = 0; link->dpia_bw_alloc_config.response_ready = false; - link->dpia_bw_alloc_config.sink_allocated_bw = 0; + link->dpia_bw_alloc_config.nrd_max_lane_count = 0; + link->dpia_bw_alloc_config.nrd_max_link_rate = 0; + for (int i = 0; i < MAX_SINKS_PER_LINK; i++) + link->dpia_bw_alloc_config.remote_sink_req_bw[i] = 0; + DC_LOG_DEBUG("reset usb4 bw alloc of link(%d)\n", link->link_index); } #define BW_GRANULARITY_0 4 // 0.25 Gbps @@ -190,7 +196,7 @@ static int get_host_router_total_dp_tunnel_bw(const struct dc *dc, uint8_t hr_in struct dc_link *link_dpia_primary, *link_dpia_secondary; int total_bw = 0; - for (uint8_t i = 0; i < MAX_PIPES * 2; ++i) { + for (uint8_t i = 0; i < (MAX_PIPES * 2) - 1; ++i) { if (!dc->links[i] || dc->links[i]->ep_type != DISPLAY_ENDPOINT_USB4_DPIA) continue; @@ -210,8 +216,8 @@ static int get_host_router_total_dp_tunnel_bw(const struct dc *dc, uint8_t hr_in link_dpia_primary->dpia_bw_alloc_config.bw_alloc_enabled) && (link_dpia_secondary->hpd_status && link_dpia_secondary->dpia_bw_alloc_config.bw_alloc_enabled)) { - total_bw += link_dpia_primary->dpia_bw_alloc_config.estimated_bw + - link_dpia_secondary->dpia_bw_alloc_config.sink_allocated_bw; + total_bw += link_dpia_primary->dpia_bw_alloc_config.estimated_bw + + link_dpia_secondary->dpia_bw_alloc_config.allocated_bw; } else if (link_dpia_primary->hpd_status && link_dpia_primary->dpia_bw_alloc_config.bw_alloc_enabled) { total_bw = link_dpia_primary->dpia_bw_alloc_config.estimated_bw; @@ -264,7 +270,7 @@ static void set_usb4_req_bw_req(struct dc_link *link, int req_bw) /* Error check whether requested and allocated are equal */ req_bw = requested_bw * (Kbps_TO_Gbps / link->dpia_bw_alloc_config.bw_granularity); - if (req_bw == link->dpia_bw_alloc_config.sink_allocated_bw) { + if (req_bw == link->dpia_bw_alloc_config.allocated_bw) { DC_LOG_ERROR("%s: Request bw equals to allocated bw for link(%d)\n", __func__, link->link_index); } @@ -387,9 +393,9 @@ void dpia_handle_bw_alloc_response(struct dc_link *link, uint8_t bw, uint8_t res DC_LOG_DEBUG("%s: BW REQ SUCCESS for DP-TX Request for link(%d)\n", __func__, link->link_index); DC_LOG_DEBUG("%s: current allocated_bw(%d), new allocated_bw(%d)\n", - __func__, link->dpia_bw_alloc_config.sink_allocated_bw, bw_needed); + __func__, link->dpia_bw_alloc_config.allocated_bw, bw_needed); - link->dpia_bw_alloc_config.sink_allocated_bw = bw_needed; + link->dpia_bw_alloc_config.allocated_bw = bw_needed; link->dpia_bw_alloc_config.response_ready = true; break; @@ -427,8 +433,8 @@ int dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int pea if (link->hpd_status && peak_bw > 0) { // If DP over USB4 then we need to check BW allocation - link->dpia_bw_alloc_config.sink_max_bw = peak_bw; - set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.sink_max_bw); + link->dpia_bw_alloc_config.link_max_bw = peak_bw; + set_usb4_req_bw_req(link, link->dpia_bw_alloc_config.link_max_bw); do { if (timeout > 0) @@ -440,8 +446,8 @@ int dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int pea if (!timeout) ret = 0;// ERROR TIMEOUT waiting for response for allocating bw - else if (link->dpia_bw_alloc_config.sink_allocated_bw > 0) - ret = link->dpia_bw_alloc_config.sink_allocated_bw; + else if (link->dpia_bw_alloc_config.allocated_bw > 0) + ret = link->dpia_bw_alloc_config.allocated_bw; } //2. Cold Unplug else if (!link->hpd_status) @@ -450,7 +456,6 @@ int dpia_handle_usb4_bandwidth_allocation_for_link(struct dc_link *link, int pea out: return ret; } - bool link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int req_bw) { bool ret = false; @@ -458,7 +463,7 @@ bool link_dp_dpia_allocate_usb4_bandwidth_for_stream(struct dc_link *link, int r DC_LOG_DEBUG("%s: ENTER: link(%d), hpd_status(%d), current allocated_bw(%d), req_bw(%d)\n", __func__, link->link_index, link->hpd_status, - link->dpia_bw_alloc_config.sink_allocated_bw, req_bw); + link->dpia_bw_alloc_config.allocated_bw, req_bw); if (!get_bw_alloc_proceed_flag(link)) goto out; @@ -523,3 +528,30 @@ bool dpia_validate_usb4_bw(struct dc_link **link, int *bw_needed_per_dpia, const return ret; } + +int link_dp_dpia_get_dp_overhead_in_dp_tunneling(struct dc_link *link) +{ + int dp_overhead = 0, link_mst_overhead = 0; + + if (!get_bw_alloc_proceed_flag((link))) + return dp_overhead; + + /* if its mst link, add MTPH overhead */ + if ((link->type == dc_connection_mst_branch) && + !link->dpcd_caps.channel_coding_cap.bits.DP_128b_132b_SUPPORTED) { + /* For 8b/10b encoding: MTP is 64 time slots long, slot 0 is used for MTPH + * MST overhead is 1/64 of link bandwidth (excluding any overhead) + */ + const struct dc_link_settings *link_cap = + dc_link_get_link_cap(link); + uint32_t link_bw_in_kbps = (uint32_t)link_cap->link_rate * + (uint32_t)link_cap->lane_count * + LINK_RATE_REF_FREQ_IN_KHZ * 8; + link_mst_overhead = (link_bw_in_kbps / 64) + ((link_bw_in_kbps % 64) ? 1 : 0); + } + + /* add all the overheads */ + dp_overhead = link_mst_overhead; + + return dp_overhead; +} diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.h index 981bc4eb6120..3b6d8494f9d5 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.h @@ -99,4 +99,13 @@ void dpia_handle_bw_alloc_response(struct dc_link *link, uint8_t bw, uint8_t res */ bool dpia_validate_usb4_bw(struct dc_link **link, int *bw_needed, const unsigned int num_dpias); +/* + * Obtain all the DP overheads in dp tunneling for the dpia link + * + * @link: pointer to the dc_link struct instance + * + * return: DP overheads in DP tunneling + */ +int link_dp_dpia_get_dp_overhead_in_dp_tunneling(struct dc_link *link); + #endif /* DC_INC_LINK_DP_DPIA_BW_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dpcd.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dpcd.c index 5c9a30211c10..fc50931c2aec 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dpcd.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dpcd.c @@ -205,7 +205,7 @@ enum dc_status core_link_read_dpcd( uint32_t extended_size; /* size of the remaining partitioned address space */ uint32_t size_left_to_read; - enum dc_status status; + enum dc_status status = DC_ERROR_UNEXPECTED; /* size of the next partition to be read from */ uint32_t partition_size; uint32_t data_index = 0; @@ -234,7 +234,7 @@ enum dc_status core_link_write_dpcd( { uint32_t partition_size; uint32_t data_index = 0; - enum dc_status status; + enum dc_status status = DC_ERROR_UNEXPECTED; while (size) { partition_size = dpcd_get_next_partition_size(address, size); diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c index 7f1196528218..046d3e205415 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c @@ -930,8 +930,8 @@ bool edp_get_replay_state(const struct dc_link *link, uint64_t *state) bool edp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream) { /* To-do: Setup Replay */ - struct dc *dc = link->ctx->dc; - struct dmub_replay *replay = dc->res_pool->replay; + struct dc *dc; + struct dmub_replay *replay; int i; unsigned int panel_inst; struct replay_context replay_context = { 0 }; @@ -947,6 +947,10 @@ bool edp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream if (!link) return false; + dc = link->ctx->dc; + + replay = dc->res_pool->replay; + if (!replay) return false; @@ -975,8 +979,7 @@ bool edp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream replay_context.line_time_in_ns = lineTimeInNs; - if (replay) - link->replay_settings.replay_feature_enabled = + link->replay_settings.replay_feature_enabled = replay->funcs->replay_copy_settings(replay, link, &replay_context, panel_inst); if (link->replay_settings.replay_feature_enabled) { diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c index 91ea0d4da06a..823493543325 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c @@ -166,12 +166,6 @@ static bool optc32_disable_crtc(struct timing_generator *optc) { struct optc *optc1 = DCN10TG_FROM_TG(optc); - /* disable otg request until end of the first line - * in the vertical blank region - */ - REG_UPDATE(OTG_CONTROL, - OTG_MASTER_EN, 0); - REG_UPDATE_5(OPTC_DATA_SOURCE_SELECT, OPTC_SEG0_SRC_SEL, 0xf, OPTC_SEG1_SRC_SEL, 0xf, @@ -179,6 +173,15 @@ static bool optc32_disable_crtc(struct timing_generator *optc) OPTC_SEG3_SRC_SEL, 0xf, OPTC_NUM_OF_INPUT_SEGMENT, 0); + REG_UPDATE(OPTC_MEMORY_CONFIG, + OPTC_MEM_SEL, 0); + + /* disable otg request until end of the first line + * in the vertical blank region + */ + REG_UPDATE(OTG_CONTROL, + OTG_MASTER_EN, 0); + REG_UPDATE(CONTROL, VTG0_ENABLE, 0); @@ -205,6 +208,13 @@ static void optc32_disable_phantom_otg(struct timing_generator *optc) { struct optc *optc1 = DCN10TG_FROM_TG(optc); + REG_UPDATE_5(OPTC_DATA_SOURCE_SELECT, + OPTC_SEG0_SRC_SEL, 0xf, + OPTC_SEG1_SRC_SEL, 0xf, + OPTC_SEG2_SRC_SEL, 0xf, + OPTC_SEG3_SRC_SEL, 0xf, + OPTC_NUM_OF_INPUT_SEGMENT, 0); + REG_UPDATE(OTG_CONTROL, OTG_MASTER_EN, 0); } diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c index 08a59cf449ca..5b1547508850 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c @@ -138,12 +138,6 @@ static bool optc35_disable_crtc(struct timing_generator *optc) { struct optc *optc1 = DCN10TG_FROM_TG(optc); - /* disable otg request until end of the first line - * in the vertical blank region - */ - REG_UPDATE(OTG_CONTROL, - OTG_MASTER_EN, 0); - REG_UPDATE_5(OPTC_DATA_SOURCE_SELECT, OPTC_SEG0_SRC_SEL, 0xf, OPTC_SEG1_SRC_SEL, 0xf, @@ -151,6 +145,15 @@ static bool optc35_disable_crtc(struct timing_generator *optc) OPTC_SEG3_SRC_SEL, 0xf, OPTC_NUM_OF_INPUT_SEGMENT, 0); + REG_UPDATE(OPTC_MEMORY_CONFIG, + OPTC_MEM_SEL, 0); + + /* disable otg request until end of the first line + * in the vertical blank region + */ + REG_UPDATE(OTG_CONTROL, + OTG_MASTER_EN, 0); + REG_UPDATE(CONTROL, VTG0_ENABLE, 0); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c index 511ff6b5b985..7538b548c572 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c @@ -999,7 +999,7 @@ static struct stream_encoder *dcn301_stream_encoder_create(enum engine_id eng_id vpg = dcn301_vpg_create(ctx, vpg_inst); afmt = dcn301_afmt_create(ctx, afmt_inst); - if (!enc1 || !vpg || !afmt) { + if (!enc1 || !vpg || !afmt || eng_id >= ARRAY_SIZE(stream_enc_regs)) { kfree(enc1); kfree(vpg); kfree(afmt); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c index ac04a9c9a3d8..6f10052caeef 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c @@ -1829,7 +1829,21 @@ int dcn32_populate_dml_pipes_from_context( dcn32_zero_pipe_dcc_fraction(pipes, pipe_cnt); DC_FP_END(); pipes[pipe_cnt].pipe.dest.vfront_porch = timing->v_front_porch; - pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_dal; + if (dc->config.enable_windowed_mpo_odm && + dc->debug.enable_single_display_2to1_odm_policy) { + switch (resource_get_odm_slice_count(pipe)) { + case 2: + pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_2to1; + break; + case 4: + pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_4to1; + break; + default: + pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_dal; + } + } else { + pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_dal; + } pipes[pipe_cnt].pipe.src.gpuvm_min_page_size_kbytes = 256; // according to spreadsheet pipes[pipe_cnt].pipe.src.unbounded_req_mode = false; pipes[pipe_cnt].pipe.scale_ratio_depth.lb_depth = dm_lb_19; @@ -1899,7 +1913,7 @@ int dcn32_populate_dml_pipes_from_context( static struct dc_cap_funcs cap_funcs = { .get_dcc_compression_cap = dcn20_get_dcc_compression_cap, - .get_subvp_en = resource_subvp_in_use, + .get_subvp_en = dcn32_subvp_in_use, }; void dcn32_calculate_wm_and_dlg(struct dc *dc, struct dc_state *context, diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h index 62611acd4bcb..0c87b0fabba7 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h @@ -131,6 +131,9 @@ void dcn32_merge_pipes_for_subvp(struct dc *dc, bool dcn32_all_pipes_have_stream_and_plane(struct dc *dc, struct dc_state *context); +bool dcn32_subvp_in_use(struct dc *dc, + struct dc_state *context); + bool dcn32_mpo_in_use(struct dc_state *context); bool dcn32_any_surfaces_rotated(struct dc *dc, struct dc_state *context); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c index e1ab207c46f1..74412e5f03fe 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c @@ -1574,7 +1574,7 @@ static void dcn321_destroy_resource_pool(struct resource_pool **pool) static struct dc_cap_funcs cap_funcs = { .get_dcc_compression_cap = dcn20_get_dcc_compression_cap, - .get_subvp_en = resource_subvp_in_use, + .get_subvp_en = dcn32_subvp_in_use, }; static void dcn321_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c index 761ec9891875..1c3d89264ef7 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c @@ -780,8 +780,8 @@ static const struct dc_debug_options debug_defaults_drv = { .disable_z10 = false, .ignore_pg = true, .psp_disabled_wa = true, - .ips2_eval_delay_us = 200, - .ips2_entry_delay_us = 400, + .ips2_eval_delay_us = 1650, + .ips2_entry_delay_us = 800, .static_screen_wait_frames = 2, }; @@ -2130,6 +2130,7 @@ static bool dcn35_resource_construct( dc->dml2_options.dcn_pipe_count = pool->base.pipe_count; dc->dml2_options.use_native_pstate_optimization = true; dc->dml2_options.use_native_soc_bb_construction = true; + dc->dml2_options.minimize_dispclk_using_odm = false; if (dc->config.EnableMinDispClkODM) dc->dml2_options.minimize_dispclk_using_odm = true; dc->dml2_options.enable_windowed_mpo_odm = dc->config.enable_windowed_mpo_odm; diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h index c64b6c848ef7..e699731ee68e 100644 --- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h +++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h @@ -2832,6 +2832,7 @@ struct dmub_rb_cmd_psr_set_power_opt { #define REPLAY_RESIDENCY_MODE_MASK (0x1 << REPLAY_RESIDENCY_MODE_SHIFT) # define REPLAY_RESIDENCY_MODE_PHY (0x0 << REPLAY_RESIDENCY_MODE_SHIFT) # define REPLAY_RESIDENCY_MODE_ALPM (0x1 << REPLAY_RESIDENCY_MODE_SHIFT) +# define REPLAY_RESIDENCY_MODE_IPS 0x10 #define REPLAY_RESIDENCY_ENABLE_MASK (0x1 << REPLAY_RESIDENCY_ENABLE_SHIFT) # define REPLAY_RESIDENCY_DISABLE (0x0 << REPLAY_RESIDENCY_ENABLE_SHIFT) @@ -2894,6 +2895,10 @@ enum dmub_cmd_replay_type { * Set Residency Frameupdate Timer. */ DMUB_CMD__REPLAY_SET_RESIDENCY_FRAMEUPDATE_TIMER = 6, + /** + * Set pseudo vtotal + */ + DMUB_CMD__REPLAY_SET_PSEUDO_VTOTAL = 7, }; /** @@ -3077,6 +3082,26 @@ struct dmub_cmd_replay_set_timing_sync_data { }; /** + * Data passed from driver to FW in a DMUB_CMD__REPLAY_SET_PSEUDO_VTOTAL command. + */ +struct dmub_cmd_replay_set_pseudo_vtotal { + /** + * Panel Instance. + * Panel isntance to identify which replay_state to use + * Currently the support is only for 0 or 1 + */ + uint8_t panel_inst; + /** + * Source Vtotal that Replay + IPS + ABM full screen video src vtotal + */ + uint16_t vtotal; + /** + * Explicit padding to 4 byte boundary. + */ + uint8_t pad; +}; + +/** * Definition of a DMUB_CMD__SET_REPLAY_POWER_OPT command. */ struct dmub_rb_cmd_replay_set_power_opt { @@ -3157,6 +3182,20 @@ struct dmub_rb_cmd_replay_set_timing_sync { }; /** + * Definition of a DMUB_CMD__REPLAY_SET_PSEUDO_VTOTAL command. + */ +struct dmub_rb_cmd_replay_set_pseudo_vtotal { + /** + * Command header. + */ + struct dmub_cmd_header header; + /** + * Definition of DMUB_CMD__REPLAY_SET_PSEUDO_VTOTAL command. + */ + struct dmub_cmd_replay_set_pseudo_vtotal data; +}; + +/** * Data passed from driver to FW in DMUB_CMD__REPLAY_SET_RESIDENCY_FRAMEUPDATE_TIMER command. */ struct dmub_cmd_replay_frameupdate_timer_data { @@ -3207,6 +3246,10 @@ union dmub_replay_cmd_set { * Definition of DMUB_CMD__REPLAY_SET_RESIDENCY_FRAMEUPDATE_TIMER command data. */ struct dmub_cmd_replay_frameupdate_timer_data timer_data; + /** + * Definition of DMUB_CMD__REPLAY_SET_PSEUDO_VTOTAL command data. + */ + struct dmub_cmd_replay_set_pseudo_vtotal pseudo_vtotal_data; }; /** @@ -4358,6 +4401,10 @@ union dmub_rb_cmd { * Definition of a DMUB_CMD__REPLAY_SET_RESIDENCY_FRAMEUPDATE_TIMER command. */ struct dmub_rb_cmd_replay_set_frameupdate_timer replay_set_frameupdate_timer; + /** + * Definition of a DMUB_CMD__REPLAY_SET_PSEUDO_VTOTAL command. + */ + struct dmub_rb_cmd_replay_set_pseudo_vtotal replay_set_pseudo_vtotal; }; /** diff --git a/drivers/gpu/drm/amd/display/include/audio_types.h b/drivers/gpu/drm/amd/display/include/audio_types.h index 66a54da0641c..915a031a43cb 100644 --- a/drivers/gpu/drm/amd/display/include/audio_types.h +++ b/drivers/gpu/drm/amd/display/include/audio_types.h @@ -64,7 +64,7 @@ enum audio_dto_source { /* PLL information required for AZALIA DTO calculation */ struct audio_pll_info { - uint32_t dp_dto_source_clock_in_khz; + uint32_t audio_dto_source_clock_in_khz; uint32_t feed_back_divider; enum audio_dto_source dto_source; bool ss_enabled; diff --git a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c b/drivers/gpu/drm/amd/display/modules/power/power_helpers.c index ad98e504c00d..e304e8435fb8 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c +++ b/drivers/gpu/drm/amd/display/modules/power/power_helpers.c @@ -980,6 +980,11 @@ void set_replay_coasting_vtotal(struct dc_link *link, link->replay_settings.coasting_vtotal_table[type] = vtotal; } +void set_replay_ips_full_screen_video_src_vtotal(struct dc_link *link, uint16_t vtotal) +{ + link->replay_settings.abm_with_ips_on_full_screen_video_pseudo_vtotal = vtotal; +} + void calculate_replay_link_off_frame_count(struct dc_link *link, uint16_t vtotal, uint16_t htotal) { diff --git a/drivers/gpu/drm/amd/display/modules/power/power_helpers.h b/drivers/gpu/drm/amd/display/modules/power/power_helpers.h index c17bbc6fb38c..bef4815e1703 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_helpers.h +++ b/drivers/gpu/drm/amd/display/modules/power/power_helpers.h @@ -57,6 +57,7 @@ void init_replay_config(struct dc_link *link, struct replay_config *pr_config); void set_replay_coasting_vtotal(struct dc_link *link, enum replay_coasting_vtotal_type type, uint16_t vtotal); +void set_replay_ips_full_screen_video_src_vtotal(struct dc_link *link, uint16_t vtotal); void calculate_replay_link_off_frame_count(struct dc_link *link, uint16_t vtotal, uint16_t htotal); diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h index 1dc5dd9b7bf7..df2c7ffe190f 100644 --- a/drivers/gpu/drm/amd/include/amd_shared.h +++ b/drivers/gpu/drm/amd/include/amd_shared.h @@ -258,6 +258,7 @@ enum DC_DEBUG_MASK { DC_ENABLE_DML2 = 0x100, DC_DISABLE_PSR_SU = 0x200, DC_DISABLE_REPLAY = 0x400, + DC_DISABLE_IPS = 0x800, }; enum amd_dpm_forced_level; diff --git a/drivers/gpu/drm/amd/include/amdgpu_reg_state.h b/drivers/gpu/drm/amd/include/amdgpu_reg_state.h index be519c8edf49..335980e2afbf 100644 --- a/drivers/gpu/drm/amd/include/amdgpu_reg_state.h +++ b/drivers/gpu/drm/amd/include/amdgpu_reg_state.h @@ -138,7 +138,7 @@ static inline size_t amdgpu_reginst_size(uint16_t num_inst, size_t inst_size, } #define amdgpu_asic_get_reg_state_supported(adev) \ - ((adev)->asic_funcs->get_reg_state ? 1 : 0) + (((adev)->asic_funcs && (adev)->asic_funcs->get_reg_state) ? 1 : 0) #define amdgpu_asic_get_reg_state(adev, state, buf, size) \ ((adev)->asic_funcs->get_reg_state ? \ diff --git a/drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_offset.h b/drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_offset.h index 7ee3d291120d..6f80bfa7e41a 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_offset.h +++ b/drivers/gpu/drm/amd/include/asic_reg/nbio/nbio_7_11_0_offset.h @@ -8707,10 +8707,10 @@ #define regBIF_BX1_MM_CFGREGS_CNTL_BASE_IDX 2 #define regBIF_BX1_BX_RESET_CNTL 0x00f0 #define regBIF_BX1_BX_RESET_CNTL_BASE_IDX 2 -#define regBIF_BX1_INTERRUPT_CNTL 0x8e11 -#define regBIF_BX1_INTERRUPT_CNTL_BASE_IDX 5 -#define regBIF_BX1_INTERRUPT_CNTL2 0x8e12 -#define regBIF_BX1_INTERRUPT_CNTL2_BASE_IDX 5 +#define regBIF_BX1_INTERRUPT_CNTL 0x00f1 +#define regBIF_BX1_INTERRUPT_CNTL_BASE_IDX 2 +#define regBIF_BX1_INTERRUPT_CNTL2 0x00f2 +#define regBIF_BX1_INTERRUPT_CNTL2_BASE_IDX 2 #define regBIF_BX1_CLKREQB_PAD_CNTL 0x00f8 #define regBIF_BX1_CLKREQB_PAD_CNTL_BASE_IDX 2 #define regBIF_BX1_BIF_FEATURES_CONTROL_MISC 0x00fb diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index f3cb490fe79b..087d57850304 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -4349,11 +4349,19 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_VDDNB, (void *)&value, &size)) seq_printf(m, "\t%u mV (VDDNB)\n", value); size = sizeof(uint32_t); - if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) - seq_printf(m, "\t%u.%02u W (average GPU)\n", query >> 8, query & 0xff); + if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) { + if (adev->flags & AMD_IS_APU) + seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", query >> 8, query & 0xff); + else + seq_printf(m, "\t%u.%02u W (average SoC)\n", query >> 8, query & 0xff); + } size = sizeof(uint32_t); - if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) - seq_printf(m, "\t%u.%02u W (current GPU)\n", query >> 8, query & 0xff); + if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) { + if (adev->flags & AMD_IS_APU) + seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", query >> 8, query & 0xff); + else + seq_printf(m, "\t%u.%02u W (current SoC)\n", query >> 8, query & 0xff); + } size = sizeof(value); seq_printf(m, "\n"); @@ -4379,9 +4387,9 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a /* VCN clocks */ if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_VCN_POWER_STATE, (void *)&value, &size)) { if (!value) { - seq_printf(m, "VCN: Disabled\n"); + seq_printf(m, "VCN: Powered down\n"); } else { - seq_printf(m, "VCN: Enabled\n"); + seq_printf(m, "VCN: Powered up\n"); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_UVD_DCLK, (void *)&value, &size)) seq_printf(m, "\t%u MHz (DCLK)\n", value/100); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_UVD_VCLK, (void *)&value, &size)) @@ -4393,9 +4401,9 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a /* UVD clocks */ if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_UVD_POWER, (void *)&value, &size)) { if (!value) { - seq_printf(m, "UVD: Disabled\n"); + seq_printf(m, "UVD: Powered down\n"); } else { - seq_printf(m, "UVD: Enabled\n"); + seq_printf(m, "UVD: Powered up\n"); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_UVD_DCLK, (void *)&value, &size)) seq_printf(m, "\t%u MHz (DCLK)\n", value/100); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_UVD_VCLK, (void *)&value, &size)) @@ -4407,9 +4415,9 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a /* VCE clocks */ if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_VCE_POWER, (void *)&value, &size)) { if (!value) { - seq_printf(m, "VCE: Disabled\n"); + seq_printf(m, "VCE: Powered down\n"); } else { - seq_printf(m, "VCE: Enabled\n"); + seq_printf(m, "VCE: Powered up\n"); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_VCE_ECCLK, (void *)&value, &size)) seq_printf(m, "\t%u MHz (ECCLK)\n", value/100); } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c index f2a55c1413f5..17882f8dfdd3 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c @@ -200,7 +200,7 @@ static int get_platform_power_management_table( struct pp_hwmgr *hwmgr, ATOM_Tonga_PPM_Table *atom_ppm_table) { - struct phm_ppm_table *ptr = kzalloc(sizeof(ATOM_Tonga_PPM_Table), GFP_KERNEL); + struct phm_ppm_table *ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); struct phm_ppt_v1_information *pp_table_information = (struct phm_ppt_v1_information *)(hwmgr->pptable); diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index b1a8799e2dee..aa91730e4eaf 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -3999,6 +3999,7 @@ static int smu7_read_sensor(struct pp_hwmgr *hwmgr, int idx, uint32_t sclk, mclk, activity_percent; uint32_t offset, val_vid; struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend); + struct amdgpu_device *adev = hwmgr->adev; /* size must be at least 4 bytes for all sensors */ if (*size < 4) @@ -4042,7 +4043,21 @@ static int smu7_read_sensor(struct pp_hwmgr *hwmgr, int idx, *size = 4; return 0; case AMDGPU_PP_SENSOR_GPU_INPUT_POWER: - return smu7_get_gpu_power(hwmgr, (uint32_t *)value); + if ((adev->asic_type != CHIP_HAWAII) && + (adev->asic_type != CHIP_BONAIRE) && + (adev->asic_type != CHIP_FIJI) && + (adev->asic_type != CHIP_TONGA)) + return smu7_get_gpu_power(hwmgr, (uint32_t *)value); + else + return -EOPNOTSUPP; + case AMDGPU_PP_SENSOR_GPU_AVG_POWER: + if ((adev->asic_type != CHIP_HAWAII) && + (adev->asic_type != CHIP_BONAIRE) && + (adev->asic_type != CHIP_FIJI) && + (adev->asic_type != CHIP_TONGA)) + return -EOPNOTSUPP; + else + return smu7_get_gpu_power(hwmgr, (uint32_t *)value); case AMDGPU_PP_SENSOR_VDDGFX: if ((data->vr_config & VRCONF_VDDGFX_MASK) == (VR_SVI2_PLANE_2 << VRCONF_VDDGFX_SHIFT)) diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c index c16703868e5c..0ad947df777a 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c @@ -24,6 +24,7 @@ #include <linux/firmware.h> #include <linux/pci.h> +#include <linux/power_supply.h> #include <linux/reboot.h> #include "amdgpu.h" @@ -733,7 +734,7 @@ static int smu_early_init(void *handle) smu->adev = adev; smu->pm_enabled = !!amdgpu_dpm; smu->is_apu = false; - smu->smu_baco.state = SMU_BACO_STATE_NONE; + smu->smu_baco.state = SMU_BACO_STATE_EXIT; smu->smu_baco.platform_support = false; smu->user_dpm_profile.fan_mode = -1; @@ -817,16 +818,8 @@ static int smu_late_init(void *handle) * handle the switch automatically. Driver involvement * is unnecessary. */ - if (!smu->dc_controlled_by_gpio) { - ret = smu_set_power_source(smu, - adev->pm.ac_power ? SMU_POWER_SOURCE_AC : - SMU_POWER_SOURCE_DC); - if (ret) { - dev_err(adev->dev, "Failed to switch to %s mode!\n", - adev->pm.ac_power ? "AC" : "DC"); - return ret; - } - } + adev->pm.ac_power = power_supply_is_system_supplied() > 0; + smu_set_ac_dc(smu); if ((amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 1)) || (amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 3))) @@ -1961,31 +1954,10 @@ static int smu_smc_hw_cleanup(struct smu_context *smu) return 0; } -static int smu_reset_mp1_state(struct smu_context *smu) -{ - struct amdgpu_device *adev = smu->adev; - int ret = 0; - - if ((!adev->in_runpm) && (!adev->in_suspend) && - (!amdgpu_in_reset(adev))) - switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { - case IP_VERSION(13, 0, 0): - case IP_VERSION(13, 0, 7): - case IP_VERSION(13, 0, 10): - ret = smu_set_mp1_state(smu, PP_MP1_STATE_UNLOAD); - break; - default: - break; - } - - return ret; -} - static int smu_hw_fini(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; struct smu_context *smu = adev->powerplay.pp_handle; - int ret; if (amdgpu_sriov_vf(adev) && !amdgpu_sriov_is_pp_one_vf(adev)) return 0; @@ -2003,15 +1975,7 @@ static int smu_hw_fini(void *handle) adev->pm.dpm_enabled = false; - ret = smu_smc_hw_cleanup(smu); - if (ret) - return ret; - - ret = smu_reset_mp1_state(smu); - if (ret) - return ret; - - return 0; + return smu_smc_hw_cleanup(smu); } static void smu_late_fini(void *handle) @@ -2710,6 +2674,7 @@ int smu_get_power_limit(void *handle, case SMU_PPT_LIMIT_CURRENT: switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { case IP_VERSION(13, 0, 2): + case IP_VERSION(13, 0, 6): case IP_VERSION(11, 0, 7): case IP_VERSION(11, 0, 11): case IP_VERSION(11, 0, 12): diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h index 2aa4fea87314..66e84defd0b6 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h @@ -424,7 +424,6 @@ enum smu_reset_mode { enum smu_baco_state { SMU_BACO_STATE_ENTER = 0, SMU_BACO_STATE_EXIT, - SMU_BACO_STATE_NONE, }; struct smu_baco_context { diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c index 5a314d0316c1..c7bfa68bf00f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c @@ -1442,10 +1442,12 @@ static int smu_v11_0_irq_process(struct amdgpu_device *adev, case 0x3: dev_dbg(adev->dev, "Switched to AC mode!\n"); schedule_work(&smu->interrupt_work); + adev->pm.ac_power = true; break; case 0x4: dev_dbg(adev->dev, "Switched to DC mode!\n"); schedule_work(&smu->interrupt_work); + adev->pm.ac_power = false; break; case 0x7: /* diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c index f1440869d1ce..dd9bcbd630a1 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c @@ -1530,7 +1530,6 @@ static int aldebaran_i2c_control_init(struct smu_context *smu) smu_i2c->port = 0; mutex_init(&smu_i2c->mutex); control->owner = THIS_MODULE; - control->class = I2C_CLASS_SPD; control->dev.parent = &adev->pdev->dev; control->algo = &aldebaran_i2c_algo; snprintf(control->name, sizeof(control->name), "AMDGPU SMU 0"); diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c index 771a3d457c33..c486182ff275 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c @@ -1379,10 +1379,12 @@ static int smu_v13_0_irq_process(struct amdgpu_device *adev, case 0x3: dev_dbg(adev->dev, "Switched to AC mode!\n"); smu_v13_0_ack_ac_dc_interrupt(smu); + adev->pm.ac_power = true; break; case 0x4: dev_dbg(adev->dev, "Switched to DC mode!\n"); smu_v13_0_ack_ac_dc_interrupt(smu); + adev->pm.ac_power = false; break; case 0x7: /* diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index 231122622a9c..a9954ffc02c5 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -2357,6 +2357,7 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu, PPTable_t *pptable = table_context->driver_pptable; SkuTable_t *skutable = &pptable->SkuTable; uint32_t power_limit, od_percent_upper, od_percent_lower; + uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; if (smu_v13_0_get_current_power_limit(smu, &power_limit)) power_limit = smu->adev->pm.ac_power ? @@ -2380,7 +2381,7 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu, od_percent_upper, od_percent_lower, power_limit); if (max_power_limit) { - *max_power_limit = power_limit * (100 + od_percent_upper); + *max_power_limit = msg_limit * (100 + od_percent_upper); *max_power_limit /= 100; } @@ -2696,7 +2697,6 @@ static int smu_v13_0_0_i2c_control_init(struct smu_context *smu) smu_i2c->port = i; mutex_init(&smu_i2c->mutex); control->owner = THIS_MODULE; - control->class = I2C_CLASS_SPD; control->dev.parent = &adev->pdev->dev; control->algo = &smu_v13_0_0_i2c_algo; snprintf(control->name, sizeof(control->name), "AMDGPU SMU %d", i); @@ -2748,13 +2748,7 @@ static int smu_v13_0_0_set_mp1_state(struct smu_context *smu, switch (mp1_state) { case PP_MP1_STATE_UNLOAD: - ret = smu_cmn_send_smc_msg_with_param(smu, - SMU_MSG_PrepareMp1ForUnload, - 0x55, NULL); - - if (!ret && smu->smu_baco.state == SMU_BACO_STATE_EXIT) - ret = smu_v13_0_disable_pmfw_state(smu); - + ret = smu_cmn_set_mp1_state(smu, mp1_state); break; default: /* Ignore others */ @@ -2950,7 +2944,7 @@ static bool smu_v13_0_0_wbrf_support_check(struct smu_context *smu) { struct amdgpu_device *adev = smu->adev; - switch (adev->ip_versions[MP1_HWIP][0]) { + switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { case IP_VERSION(13, 0, 0): return smu->smc_fw_version >= 0x004e6300; case IP_VERSION(13, 0, 10): @@ -2960,6 +2954,55 @@ static bool smu_v13_0_0_wbrf_support_check(struct smu_context *smu) } } +static int smu_v13_0_0_set_power_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit) +{ + PPTable_t *pptable = smu->smu_table.driver_pptable; + SkuTable_t *skutable = &pptable->SkuTable; + uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; + struct smu_table_context *table_context = &smu->smu_table; + OverDriveTableExternal_t *od_table = + (OverDriveTableExternal_t *)table_context->overdrive_table; + int ret = 0; + + if (limit_type != SMU_DEFAULT_PPT_LIMIT) + return -EINVAL; + + if (limit <= msg_limit) { + if (smu->current_power_limit > msg_limit) { + od_table->OverDriveTable.Ppt = 0; + od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT; + + ret = smu_v13_0_0_upload_overdrive_table(smu, od_table); + if (ret) { + dev_err(smu->adev->dev, "Failed to upload overdrive table!\n"); + return ret; + } + } + return smu_v13_0_set_power_limit(smu, limit_type, limit); + } else if (smu->od_enabled) { + ret = smu_v13_0_set_power_limit(smu, limit_type, msg_limit); + if (ret) + return ret; + + od_table->OverDriveTable.Ppt = (limit * 100) / msg_limit - 100; + od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT; + + ret = smu_v13_0_0_upload_overdrive_table(smu, od_table); + if (ret) { + dev_err(smu->adev->dev, "Failed to upload overdrive table!\n"); + return ret; + } + + smu->current_power_limit = limit; + } else { + return -EINVAL; + } + + return 0; +} + static const struct pptable_funcs smu_v13_0_0_ppt_funcs = { .get_allowed_feature_mask = smu_v13_0_0_get_allowed_feature_mask, .set_default_dpm_table = smu_v13_0_0_set_default_dpm_table, @@ -3014,7 +3057,7 @@ static const struct pptable_funcs smu_v13_0_0_ppt_funcs = { .set_fan_control_mode = smu_v13_0_set_fan_control_mode, .enable_mgpu_fan_boost = smu_v13_0_0_enable_mgpu_fan_boost, .get_power_limit = smu_v13_0_0_get_power_limit, - .set_power_limit = smu_v13_0_set_power_limit, + .set_power_limit = smu_v13_0_0_set_power_limit, .set_power_source = smu_v13_0_set_power_source, .get_power_profile_mode = smu_v13_0_0_get_power_profile_mode, .set_power_profile_mode = smu_v13_0_0_set_power_profile_mode, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index 4ebc6b421c2c..7e1941cf1796 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -160,8 +160,8 @@ static const struct cmn2asic_msg_mapping smu_v13_0_6_message_map[SMU_MSG_MAX_COU MSG_MAP(GfxDriverResetRecovery, PPSMC_MSG_GfxDriverResetRecovery, 0), MSG_MAP(GetMinGfxclkFrequency, PPSMC_MSG_GetMinGfxDpmFreq, 1), MSG_MAP(GetMaxGfxclkFrequency, PPSMC_MSG_GetMaxGfxDpmFreq, 1), - MSG_MAP(SetSoftMinGfxclk, PPSMC_MSG_SetSoftMinGfxClk, 0), - MSG_MAP(SetSoftMaxGfxClk, PPSMC_MSG_SetSoftMaxGfxClk, 0), + MSG_MAP(SetSoftMinGfxclk, PPSMC_MSG_SetSoftMinGfxClk, 1), + MSG_MAP(SetSoftMaxGfxClk, PPSMC_MSG_SetSoftMaxGfxClk, 1), MSG_MAP(PrepareMp1ForUnload, PPSMC_MSG_PrepareForDriverUnload, 0), MSG_MAP(GetCTFLimit, PPSMC_MSG_GetCTFLimit, 0), MSG_MAP(GetThermalLimit, PPSMC_MSG_ReadThrottlerLimit, 0), @@ -970,7 +970,9 @@ static int smu_v13_0_6_print_clks(struct smu_context *smu, char *buf, int size, if (i < (clocks.num_levels - 1)) clk2 = clocks.data[i + 1].clocks_in_khz / 1000; - if (curr_clk >= clk1 && curr_clk < clk2) { + if (curr_clk == clk1) { + level = i; + } else if (curr_clk >= clk1 && curr_clk < clk2) { level = (curr_clk - clk1) <= (clk2 - curr_clk) ? i : i + 1; @@ -1936,7 +1938,6 @@ static int smu_v13_0_6_i2c_control_init(struct smu_context *smu) smu_i2c->port = i; mutex_init(&smu_i2c->mutex); control->owner = THIS_MODULE; - control->class = I2C_CLASS_SPD; control->dev.parent = &adev->pdev->dev; control->algo = &smu_v13_0_6_i2c_algo; snprintf(control->name, sizeof(control->name), "AMDGPU SMU %d", i); @@ -2235,17 +2236,18 @@ static int smu_v13_0_6_mode2_reset(struct smu_context *smu) continue; } - if (ret) { - dev_err(adev->dev, - "failed to send mode2 message \tparam: 0x%08x error code %d\n", - SMU_RESET_MODE_2, ret); + if (ret) goto out; - } + } while (ret == -ETIME && timeout); out: mutex_unlock(&smu->message_lock); + if (ret) + dev_err(adev->dev, "failed to send mode2 reset, error code %d", + ret); + return ret; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index 59606a19e3d2..0ffdb58af74e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -2321,6 +2321,7 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu, PPTable_t *pptable = table_context->driver_pptable; SkuTable_t *skutable = &pptable->SkuTable; uint32_t power_limit, od_percent_upper, od_percent_lower; + uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; if (smu_v13_0_get_current_power_limit(smu, &power_limit)) power_limit = smu->adev->pm.ac_power ? @@ -2344,7 +2345,7 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu, od_percent_upper, od_percent_lower, power_limit); if (max_power_limit) { - *max_power_limit = power_limit * (100 + od_percent_upper); + *max_power_limit = msg_limit * (100 + od_percent_upper); *max_power_limit /= 100; } @@ -2504,13 +2505,7 @@ static int smu_v13_0_7_set_mp1_state(struct smu_context *smu, switch (mp1_state) { case PP_MP1_STATE_UNLOAD: - ret = smu_cmn_send_smc_msg_with_param(smu, - SMU_MSG_PrepareMp1ForUnload, - 0x55, NULL); - - if (!ret && smu->smu_baco.state == SMU_BACO_STATE_EXIT) - ret = smu_v13_0_disable_pmfw_state(smu); - + ret = smu_cmn_set_mp1_state(smu, mp1_state); break; default: /* Ignore others */ @@ -2545,6 +2540,55 @@ static bool smu_v13_0_7_wbrf_support_check(struct smu_context *smu) return smu->smc_fw_version > 0x00524600; } +static int smu_v13_0_7_set_power_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit) +{ + PPTable_t *pptable = smu->smu_table.driver_pptable; + SkuTable_t *skutable = &pptable->SkuTable; + uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; + struct smu_table_context *table_context = &smu->smu_table; + OverDriveTableExternal_t *od_table = + (OverDriveTableExternal_t *)table_context->overdrive_table; + int ret = 0; + + if (limit_type != SMU_DEFAULT_PPT_LIMIT) + return -EINVAL; + + if (limit <= msg_limit) { + if (smu->current_power_limit > msg_limit) { + od_table->OverDriveTable.Ppt = 0; + od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT; + + ret = smu_v13_0_7_upload_overdrive_table(smu, od_table); + if (ret) { + dev_err(smu->adev->dev, "Failed to upload overdrive table!\n"); + return ret; + } + } + return smu_v13_0_set_power_limit(smu, limit_type, limit); + } else if (smu->od_enabled) { + ret = smu_v13_0_set_power_limit(smu, limit_type, msg_limit); + if (ret) + return ret; + + od_table->OverDriveTable.Ppt = (limit * 100) / msg_limit - 100; + od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT; + + ret = smu_v13_0_7_upload_overdrive_table(smu, od_table); + if (ret) { + dev_err(smu->adev->dev, "Failed to upload overdrive table!\n"); + return ret; + } + + smu->current_power_limit = limit; + } else { + return -EINVAL; + } + + return 0; +} + static const struct pptable_funcs smu_v13_0_7_ppt_funcs = { .get_allowed_feature_mask = smu_v13_0_7_get_allowed_feature_mask, .set_default_dpm_table = smu_v13_0_7_set_default_dpm_table, @@ -2596,7 +2640,7 @@ static const struct pptable_funcs smu_v13_0_7_ppt_funcs = { .set_fan_control_mode = smu_v13_0_set_fan_control_mode, .enable_mgpu_fan_boost = smu_v13_0_7_enable_mgpu_fan_boost, .get_power_limit = smu_v13_0_7_get_power_limit, - .set_power_limit = smu_v13_0_set_power_limit, + .set_power_limit = smu_v13_0_7_set_power_limit, .set_power_source = smu_v13_0_set_power_source, .get_power_profile_mode = smu_v13_0_7_get_power_profile_mode, .set_power_profile_mode = smu_v13_0_7_set_power_profile_mode, diff --git a/drivers/gpu/drm/ast/ast_i2c.c b/drivers/gpu/drm/ast/ast_i2c.c index 0e845e7acd9b..e5d3f7121de4 100644 --- a/drivers/gpu/drm/ast/ast_i2c.c +++ b/drivers/gpu/drm/ast/ast_i2c.c @@ -120,7 +120,6 @@ struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev) return NULL; i2c->adapter.owner = THIS_MODULE; - i2c->adapter.class = I2C_CLASS_DDC; i2c->adapter.dev.parent = dev->dev; i2c->dev = dev; i2c_set_adapdata(&i2c->adapter, i2c); diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index ef31033439bc..29d91493b101 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -1762,6 +1762,7 @@ static ssize_t anx7625_aux_transfer(struct drm_dp_aux *aux, u8 request = msg->request & ~DP_AUX_I2C_MOT; int ret = 0; + mutex_lock(&ctx->aux_lock); pm_runtime_get_sync(dev); msg->reply = 0; switch (request) { @@ -1778,6 +1779,7 @@ static ssize_t anx7625_aux_transfer(struct drm_dp_aux *aux, msg->size, msg->buffer); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); + mutex_unlock(&ctx->aux_lock); return ret; } @@ -2474,7 +2476,9 @@ static void anx7625_bridge_atomic_disable(struct drm_bridge *bridge, ctx->connector = NULL; anx7625_dp_stop(ctx); - pm_runtime_put_sync(dev); + mutex_lock(&ctx->aux_lock); + pm_runtime_put_sync_suspend(dev); + mutex_unlock(&ctx->aux_lock); } static enum drm_connector_status @@ -2668,6 +2672,7 @@ static int anx7625_i2c_probe(struct i2c_client *client) mutex_init(&platform->lock); mutex_init(&platform->hdcp_wq_lock); + mutex_init(&platform->aux_lock); INIT_DELAYED_WORK(&platform->hdcp_work, hdcp_check_work_func); platform->hdcp_workqueue = create_workqueue("hdcp workqueue"); diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h index 66ebee7f3d83..39ed35d33836 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.h +++ b/drivers/gpu/drm/bridge/analogix/anx7625.h @@ -475,6 +475,8 @@ struct anx7625_data { struct workqueue_struct *hdcp_workqueue; /* Lock for hdcp work queue */ struct mutex hdcp_wq_lock; + /* Lock for aux transfer and disable */ + struct mutex aux_lock; char edid_block; struct display_timing dt; u8 display_timing_valid; diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index 541e4f5afc4c..14d4dcf239da 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -107,6 +107,7 @@ struct ps8640 { struct device_link *link; bool pre_enabled; bool need_post_hpd_delay; + struct mutex aux_lock; }; static const struct regmap_config ps8640_regmap_config[] = { @@ -345,11 +346,20 @@ static ssize_t ps8640_aux_transfer(struct drm_dp_aux *aux, struct device *dev = &ps_bridge->page[PAGE0_DP_CNTL]->dev; int ret; + mutex_lock(&ps_bridge->aux_lock); pm_runtime_get_sync(dev); + ret = _ps8640_wait_hpd_asserted(ps_bridge, 200 * 1000); + if (ret) { + pm_runtime_put_sync_suspend(dev); + goto exit; + } ret = ps8640_aux_transfer_msg(aux, msg); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); +exit: + mutex_unlock(&ps_bridge->aux_lock); + return ret; } @@ -470,7 +480,18 @@ static void ps8640_atomic_post_disable(struct drm_bridge *bridge, ps_bridge->pre_enabled = false; ps8640_bridge_vdo_control(ps_bridge, DISABLE); + + /* + * The bridge seems to expect everything to be power cycled at the + * disable process, so grab a lock here to make sure + * ps8640_aux_transfer() is not holding a runtime PM reference and + * preventing the bridge from suspend. + */ + mutex_lock(&ps_bridge->aux_lock); + pm_runtime_put_sync_suspend(&ps_bridge->page[PAGE0_DP_CNTL]->dev); + + mutex_unlock(&ps_bridge->aux_lock); } static int ps8640_bridge_attach(struct drm_bridge *bridge, @@ -619,6 +640,8 @@ static int ps8640_probe(struct i2c_client *client) if (!ps_bridge) return -ENOMEM; + mutex_init(&ps_bridge->aux_lock); + ps_bridge->supplies[0].supply = "vdd12"; ps_bridge->supplies[1].supply = "vdd33"; ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ps_bridge->supplies), diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c index be5914caa17d..63a1a0c88be4 100644 --- a/drivers/gpu/drm/bridge/samsung-dsim.c +++ b/drivers/gpu/drm/bridge/samsung-dsim.c @@ -969,10 +969,6 @@ static int samsung_dsim_init_link(struct samsung_dsim *dsi) reg = samsung_dsim_read(dsi, DSIM_ESCMODE_REG); reg &= ~DSIM_STOP_STATE_CNT_MASK; reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]); - - if (!samsung_dsim_hw_is_exynos(dsi->plat_data->hw_type)) - reg |= DSIM_FORCE_STOP_STATE; - samsung_dsim_write(dsi, DSIM_ESCMODE_REG, reg); reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff); @@ -1431,18 +1427,6 @@ static void samsung_dsim_disable_irq(struct samsung_dsim *dsi) disable_irq(dsi->irq); } -static void samsung_dsim_set_stop_state(struct samsung_dsim *dsi, bool enable) -{ - u32 reg = samsung_dsim_read(dsi, DSIM_ESCMODE_REG); - - if (enable) - reg |= DSIM_FORCE_STOP_STATE; - else - reg &= ~DSIM_FORCE_STOP_STATE; - - samsung_dsim_write(dsi, DSIM_ESCMODE_REG, reg); -} - static int samsung_dsim_init(struct samsung_dsim *dsi) { const struct samsung_dsim_driver_data *driver_data = dsi->driver_data; @@ -1492,9 +1476,6 @@ static void samsung_dsim_atomic_pre_enable(struct drm_bridge *bridge, ret = samsung_dsim_init(dsi); if (ret) return; - - samsung_dsim_set_display_mode(dsi); - samsung_dsim_set_display_enable(dsi, true); } } @@ -1503,12 +1484,8 @@ static void samsung_dsim_atomic_enable(struct drm_bridge *bridge, { struct samsung_dsim *dsi = bridge_to_dsi(bridge); - if (samsung_dsim_hw_is_exynos(dsi->plat_data->hw_type)) { - samsung_dsim_set_display_mode(dsi); - samsung_dsim_set_display_enable(dsi, true); - } else { - samsung_dsim_set_stop_state(dsi, false); - } + samsung_dsim_set_display_mode(dsi); + samsung_dsim_set_display_enable(dsi, true); dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE; } @@ -1521,9 +1498,6 @@ static void samsung_dsim_atomic_disable(struct drm_bridge *bridge, if (!(dsi->state & DSIM_STATE_ENABLED)) return; - if (!samsung_dsim_hw_is_exynos(dsi->plat_data->hw_type)) - samsung_dsim_set_stop_state(dsi, true); - dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE; } @@ -1828,8 +1802,6 @@ static ssize_t samsung_dsim_host_transfer(struct mipi_dsi_host *host, if (ret) return ret; - samsung_dsim_set_stop_state(dsi, false); - ret = mipi_dsi_create_packet(&xfer.packet, msg); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index 2bdc5b439beb..4560ae9cbce1 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -1080,6 +1080,26 @@ static int sii902x_init(struct sii902x *sii902x) return ret; } + ret = sii902x_audio_codec_init(sii902x, dev); + if (ret) + return ret; + + i2c_set_clientdata(sii902x->i2c, sii902x); + + sii902x->i2cmux = i2c_mux_alloc(sii902x->i2c->adapter, dev, + 1, 0, I2C_MUX_GATE, + sii902x_i2c_bypass_select, + sii902x_i2c_bypass_deselect); + if (!sii902x->i2cmux) { + ret = -ENOMEM; + goto err_unreg_audio; + } + + sii902x->i2cmux->priv = sii902x; + ret = i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0); + if (ret) + goto err_unreg_audio; + sii902x->bridge.funcs = &sii902x_bridge_funcs; sii902x->bridge.of_node = dev->of_node; sii902x->bridge.timings = &default_sii902x_timings; @@ -1090,19 +1110,13 @@ static int sii902x_init(struct sii902x *sii902x) drm_bridge_add(&sii902x->bridge); - sii902x_audio_codec_init(sii902x, dev); - - i2c_set_clientdata(sii902x->i2c, sii902x); + return 0; - sii902x->i2cmux = i2c_mux_alloc(sii902x->i2c->adapter, dev, - 1, 0, I2C_MUX_GATE, - sii902x_i2c_bypass_select, - sii902x_i2c_bypass_deselect); - if (!sii902x->i2cmux) - return -ENOMEM; +err_unreg_audio: + if (!PTR_ERR_OR_ZERO(sii902x->audio.pdev)) + platform_device_unregister(sii902x->audio.pdev); - sii902x->i2cmux->priv = sii902x; - return i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0); + return ret; } static int sii902x_probe(struct i2c_client *client) @@ -1170,12 +1184,14 @@ static int sii902x_probe(struct i2c_client *client) } static void sii902x_remove(struct i2c_client *client) - { struct sii902x *sii902x = i2c_get_clientdata(client); - i2c_mux_del_adapters(sii902x->i2cmux); drm_bridge_remove(&sii902x->bridge); + i2c_mux_del_adapters(sii902x->i2cmux); + + if (!PTR_ERR_OR_ZERO(sii902x->audio.pdev)) + platform_device_unregister(sii902x->audio.pdev); } static const struct of_device_id sii902x_dt_ids[] = { diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 52d91a0df85e..aca5bb0866f8 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -515,7 +515,6 @@ static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi *hdmi) init_completion(&i2c->cmp); adap = &i2c->adap; - adap->class = I2C_CLASS_DDC; adap->owner = THIS_MODULE; adap->dev.parent = hdmi->dev; adap->algo = &dw_hdmi_algorithm; diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index d72b6f9a352c..b1ca3a1100da 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -2102,7 +2102,6 @@ int drm_dp_aux_register(struct drm_dp_aux *aux) if (!aux->ddc.algo) drm_dp_aux_init(aux); - aux->ddc.class = I2C_CLASS_DDC; aux->ddc.owner = THIS_MODULE; aux->ddc.dev.parent = aux->dev; diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index 8ca01a6bf645..f7c6b60629c2 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -5491,6 +5491,7 @@ EXPORT_SYMBOL(drm_dp_mst_atomic_enable_dsc); * - 0 if the new state is valid * - %-ENOSPC, if the new state is invalid, because of BW limitation * @failing_port is set to: + * * - The non-root port where a BW limit check failed * with all the ports downstream of @failing_port passing * the BW limit check. @@ -5499,6 +5500,7 @@ EXPORT_SYMBOL(drm_dp_mst_atomic_enable_dsc); * - %NULL if the BW limit check failed at the root port * with all the ports downstream of the root port passing * the BW limit check. + * * - %-EINVAL, if the new state is invalid, because the root port has * too many payloads. */ @@ -5926,7 +5928,6 @@ static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port) aux->ddc.algo_data = aux; aux->ddc.retries = 3; - aux->ddc.class = I2C_CLASS_DDC; aux->ddc.owner = THIS_MODULE; /* FIXME: set the kdev of the port's connector as parent */ aux->ddc.dev.parent = parent_dev; diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c index 776f2f0b602d..0ef7bc8848b0 100644 --- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c @@ -319,9 +319,9 @@ static void decon_win_set_bldmod(struct decon_context *ctx, unsigned int win, static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, struct drm_framebuffer *fb) { - struct exynos_drm_plane plane = ctx->planes[win]; + struct exynos_drm_plane *plane = &ctx->planes[win]; struct exynos_drm_plane_state *state = - to_exynos_plane_state(plane.base.state); + to_exynos_plane_state(plane->base.state); unsigned int alpha = state->base.alpha; unsigned int pixel_alpha; unsigned long val; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index a9f1c5c05894..f2145227a1e0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -480,7 +480,7 @@ static void fimd_commit(struct exynos_drm_crtc *crtc) struct fimd_context *ctx = crtc->ctx; struct drm_display_mode *mode = &crtc->base.state->adjusted_mode; const struct fimd_driver_data *driver_data = ctx->driver_data; - void *timing_base = ctx->regs + driver_data->timing_base; + void __iomem *timing_base = ctx->regs + driver_data->timing_base; u32 val; if (ctx->suspended) @@ -661,9 +661,9 @@ static void fimd_win_set_bldmod(struct fimd_context *ctx, unsigned int win, static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win, struct drm_framebuffer *fb, int width) { - struct exynos_drm_plane plane = ctx->planes[win]; + struct exynos_drm_plane *plane = &ctx->planes[win]; struct exynos_drm_plane_state *state = - to_exynos_plane_state(plane.base.state); + to_exynos_plane_state(plane->base.state); uint32_t pixel_format = fb->format->format; unsigned int alpha = state->base.alpha; u32 val = WINCONx_ENWIN; diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index e9a769590415..180507a47700 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -1341,7 +1341,7 @@ static int __maybe_unused gsc_runtime_resume(struct device *dev) for (i = 0; i < ctx->num_clocks; i++) { ret = clk_prepare_enable(ctx->clocks[i]); if (ret) { - while (--i > 0) + while (--i >= 0) clk_disable_unprepare(ctx->clocks[i]); return ret; } diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 8992a95076f2..dd1eb7e9877d 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -855,7 +855,6 @@ cdv_intel_dp_i2c_init(struct gma_connector *connector, memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter)); intel_dp->adapter.owner = THIS_MODULE; - intel_dp->adapter.class = I2C_CLASS_DDC; strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1); intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0'; intel_dp->adapter.algo_data = &intel_dp->algo; diff --git a/drivers/gpu/drm/gma500/intel_gmbus.c b/drivers/gpu/drm/gma500/intel_gmbus.c index 09cedabf4776..aa45509859f2 100644 --- a/drivers/gpu/drm/gma500/intel_gmbus.c +++ b/drivers/gpu/drm/gma500/intel_gmbus.c @@ -411,7 +411,6 @@ int gma_intel_setup_gmbus(struct drm_device *dev) struct intel_gmbus *bus = &dev_priv->gmbus[i]; bus->adapter.owner = THIS_MODULE; - bus->adapter.class = I2C_CLASS_DDC; snprintf(bus->adapter.name, sizeof(bus->adapter.name), "gma500 gmbus %s", diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c index fc9a34ed58bd..6daa6669ed23 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c @@ -168,7 +168,6 @@ static struct i2c_adapter oaktrail_hdmi_i2c_adapter = { .name = "oaktrail_hdmi_i2c", .nr = 3, .owner = THIS_MODULE, - .class = I2C_CLASS_DDC, .algo = &oaktrail_hdmi_i2c_algorithm, }; diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index d6fd5d726216..e4f914deceba 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -2426,7 +2426,6 @@ psb_intel_sdvo_init_ddc_proxy(struct psb_intel_sdvo *sdvo, struct drm_device *dev) { sdvo->ddc.owner = THIS_MODULE; - sdvo->ddc.class = I2C_CLASS_DDC; snprintf(sdvo->ddc.name, I2C_NAME_SIZE, "SDVO DDC proxy"); sdvo->ddc.dev.parent = dev->dev; sdvo->ddc.algo_data = sdvo; diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c index 410bd019bb35..e6e48651c15c 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c @@ -81,7 +81,6 @@ int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_connector *connector) { connector->adapter.owner = THIS_MODULE; - connector->adapter.class = I2C_CLASS_DDC; snprintf(connector->adapter.name, I2C_NAME_SIZE, "HIS i2c bit bus"); connector->adapter.dev.parent = drm_dev->dev; i2c_set_adapdata(&connector->adapter, connector); diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index b5d6e3352071..3089029abba4 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -140,7 +140,7 @@ config DRM_I915_GVT_KVMGT Note that this driver only supports newer device from Broadwell on. For further information and setup guide, you can visit: - http://01.org/igvt-g. + https://github.com/intel/gvt-linux/wiki. If in doubt, say "N". diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index e777686190ca..c13f14edb508 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -17,7 +17,6 @@ subdir-ccflags-y += $(call cc-option, -Wunused-const-variable) subdir-ccflags-y += $(call cc-option, -Wpacked-not-aligned) subdir-ccflags-y += $(call cc-option, -Wformat-overflow) subdir-ccflags-y += $(call cc-option, -Wformat-truncation) -subdir-ccflags-y += $(call cc-option, -Wstringop-overflow) subdir-ccflags-y += $(call cc-option, -Wstringop-truncation) # The following turn off the warnings enabled by -Wextra ifeq ($(findstring 2, $(KBUILD_EXTRA_WARN)),) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index ac456a2275db..eda4a8b88590 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -1155,6 +1155,7 @@ static void gen11_dsi_powerup_panel(struct intel_encoder *encoder) } intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_INIT_OTP); + intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_DISPLAY_ON); /* ensure all panel commands dispatched before enabling transcoder */ wait_for_cmds_dispatched_to_panel(encoder); @@ -1255,8 +1256,6 @@ static void gen11_dsi_enable(struct intel_atomic_state *state, /* step6d: enable dsi transcoder */ gen11_dsi_enable_transcoder(encoder); - intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_DISPLAY_ON); - /* step7: enable backlight */ intel_backlight_enable(crtc_state, conn_state); intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_BACKLIGHT_ON); diff --git a/drivers/gpu/drm/i915/display/intel_backlight.c b/drivers/gpu/drm/i915/display/intel_backlight.c index 612d4cd9dacb..3f3cd944a1c5 100644 --- a/drivers/gpu/drm/i915/display/intel_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_backlight.c @@ -275,7 +275,7 @@ static void ext_pwm_set_backlight(const struct drm_connector_state *conn_state, struct intel_panel *panel = &to_intel_connector(conn_state->connector)->panel; pwm_set_relative_duty_cycle(&panel->backlight.pwm_state, level, 100); - pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state); + pwm_apply_might_sleep(panel->backlight.pwm, &panel->backlight.pwm_state); } static void @@ -428,7 +428,7 @@ static void ext_pwm_disable_backlight(const struct drm_connector_state *old_conn intel_backlight_set_pwm_level(old_conn_state, level); panel->backlight.pwm_state.enabled = false; - pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state); + pwm_apply_might_sleep(panel->backlight.pwm, &panel->backlight.pwm_state); } void intel_backlight_disable(const struct drm_connector_state *old_conn_state) @@ -750,7 +750,7 @@ static void ext_pwm_enable_backlight(const struct intel_crtc_state *crtc_state, pwm_set_relative_duty_cycle(&panel->backlight.pwm_state, level, 100); panel->backlight.pwm_state.enabled = true; - pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state); + pwm_apply_might_sleep(panel->backlight.pwm, &panel->backlight.pwm_state); } static void __intel_backlight_enable(const struct intel_crtc_state *crtc_state, diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 884a1da36089..6b25e195232f 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -3067,24 +3067,29 @@ static void intel_c20pll_state_verify(const struct intel_crtc_state *state, { struct drm_i915_private *i915 = to_i915(crtc->base.dev); const struct intel_c20pll_state *mpll_sw_state = &state->cx0pll_state.c20; - bool use_mplla; + bool sw_use_mpllb = mpll_sw_state->tx[0] & C20_PHY_USE_MPLLB; + bool hw_use_mpllb = mpll_hw_state->tx[0] & C20_PHY_USE_MPLLB; int i; - use_mplla = intel_c20_use_mplla(mpll_hw_state->clock); - if (use_mplla) { - for (i = 0; i < ARRAY_SIZE(mpll_sw_state->mplla); i++) { - I915_STATE_WARN(i915, mpll_hw_state->mplla[i] != mpll_sw_state->mplla[i], - "[CRTC:%d:%s] mismatch in C20MPLLA: Register[%d] (expected 0x%04x, found 0x%04x)", - crtc->base.base.id, crtc->base.name, i, - mpll_sw_state->mplla[i], mpll_hw_state->mplla[i]); - } - } else { + I915_STATE_WARN(i915, sw_use_mpllb != hw_use_mpllb, + "[CRTC:%d:%s] mismatch in C20: Register MPLLB selection (expected %d, found %d)", + crtc->base.base.id, crtc->base.name, + sw_use_mpllb, hw_use_mpllb); + + if (hw_use_mpllb) { for (i = 0; i < ARRAY_SIZE(mpll_sw_state->mpllb); i++) { I915_STATE_WARN(i915, mpll_hw_state->mpllb[i] != mpll_sw_state->mpllb[i], "[CRTC:%d:%s] mismatch in C20MPLLB: Register[%d] (expected 0x%04x, found 0x%04x)", crtc->base.base.id, crtc->base.name, i, mpll_sw_state->mpllb[i], mpll_hw_state->mpllb[i]); } + } else { + for (i = 0; i < ARRAY_SIZE(mpll_sw_state->mplla); i++) { + I915_STATE_WARN(i915, mpll_hw_state->mplla[i] != mpll_sw_state->mplla[i], + "[CRTC:%d:%s] mismatch in C20MPLLA: Register[%d] (expected 0x%04x, found 0x%04x)", + crtc->base.base.id, crtc->base.name, i, + mpll_sw_state->mplla[i], mpll_hw_state->mplla[i]); + } } for (i = 0; i < ARRAY_SIZE(mpll_sw_state->tx); i++) { diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index 5f091502719b..6fd4fa52253a 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -405,8 +405,8 @@ print_async_put_domains_state(struct i915_power_domains *power_domains) struct drm_i915_private, display.power.domains); - drm_dbg(&i915->drm, "async_put_wakeref %lu\n", - power_domains->async_put_wakeref); + drm_dbg(&i915->drm, "async_put_wakeref: %s\n", + str_yes_no(power_domains->async_put_wakeref)); print_power_domains(power_domains, "async_put_domains[0]", &power_domains->async_put_domains[0]); diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 7d2b8ce48fda..f5ef95da5534 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -2101,7 +2101,7 @@ static int intel_dp_dsc_compute_pipe_bpp(struct intel_dp *intel_dp, } } - dsc_max_bpc = intel_dp_dsc_min_src_input_bpc(i915); + dsc_max_bpc = intel_dp_dsc_max_src_input_bpc(i915); if (!dsc_max_bpc) return -EINVAL; diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index 40d7b6f3f489..e9e4dcf345f9 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -899,7 +899,6 @@ int intel_gmbus_setup(struct drm_i915_private *i915) } bus->adapter.owner = THIS_MODULE; - bus->adapter.class = I2C_CLASS_DDC; snprintf(bus->adapter.name, sizeof(bus->adapter.name), "i915 gmbus %s", gmbus_pin->name); diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index b6e2e70e1290..57bbf3e3af92 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1525,8 +1525,18 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp, * can rely on frontbuffer tracking. */ mask = EDP_PSR_DEBUG_MASK_MEMUP | - EDP_PSR_DEBUG_MASK_HPD | - EDP_PSR_DEBUG_MASK_LPSP; + EDP_PSR_DEBUG_MASK_HPD; + + /* + * For some unknown reason on HSW non-ULT (or at least on + * Dell Latitude E6540) external displays start to flicker + * when PSR is enabled on the eDP. SR/PC6 residency is much + * higher than should be possible with an external display. + * As a workaround leave LPSP unmasked to prevent PSR entry + * when external displays are active. + */ + if (DISPLAY_VER(dev_priv) >= 8 || IS_HASWELL_ULT(dev_priv)) + mask |= EDP_PSR_DEBUG_MASK_LPSP; if (DISPLAY_VER(dev_priv) < 20) mask |= EDP_PSR_DEBUG_MASK_MAX_SLEEP; @@ -3319,11 +3329,11 @@ void intel_psr_connector_debugfs_add(struct intel_connector *connector) struct drm_i915_private *i915 = to_i915(connector->base.dev); struct dentry *root = connector->base.debugfs_entry; - if (connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) { - if (!(HAS_DP20(i915) && - connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort)) - return; - } + /* TODO: Add support for MST connectors as well. */ + if ((connector->base.connector_type != DRM_MODE_CONNECTOR_eDP && + connector->base.connector_type != DRM_MODE_CONNECTOR_DisplayPort) || + connector->mst_port) + return; debugfs_create_file("i915_psr_sink_status", 0444, root, connector, &i915_psr_sink_status_fops); diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index 9218047495fb..acc6b6804105 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -3327,7 +3327,6 @@ intel_sdvo_init_ddc_proxy(struct intel_sdvo_ddc *ddc, ddc->ddc_bus = ddc_bus; ddc->ddc.owner = THIS_MODULE; - ddc->ddc.class = I2C_CLASS_DDC; snprintf(ddc->ddc.name, I2C_NAME_SIZE, "SDVO %c DDC%d", port_name(sdvo->base.port), ddc_bus); ddc->ddc.dev.parent = &pdev->dev; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h index c573c067779f..03bc7f9d191b 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h @@ -412,9 +412,9 @@ struct i915_gem_context { /** @stale: tracks stale engines to be destroyed */ struct { - /** @lock: guards engines */ + /** @stale.lock: guards engines */ spinlock_t lock; - /** @engines: list of stale engines */ + /** @stale.engines: list of stale engines */ struct list_head engines; } stale; }; diff --git a/drivers/gpu/drm/i915/gt/intel_gsc.h b/drivers/gpu/drm/i915/gt/intel_gsc.h index 7ab3ca0f9f26..013c64251448 100644 --- a/drivers/gpu/drm/i915/gt/intel_gsc.h +++ b/drivers/gpu/drm/i915/gt/intel_gsc.h @@ -21,8 +21,11 @@ struct mei_aux_device; /** * struct intel_gsc - graphics security controller * - * @gem_obj: scratch memory GSC operations - * @intf : gsc interface + * @intf: gsc interface + * @intf.adev: MEI aux. device for this @intf + * @intf.gem_obj: scratch memory GSC operations + * @intf.irq: IRQ for this device (%-1 for no IRQ) + * @intf.id: this interface's id number/index */ struct intel_gsc { struct intel_gsc_intf { diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h index e22c12ce245a..813cc888e6fa 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h @@ -105,61 +105,67 @@ struct intel_guc { */ struct { /** - * @lock: protects everything in submission_state, - * ce->guc_id.id, and ce->guc_id.ref when transitioning in and - * out of zero + * @submission_state.lock: protects everything in + * submission_state, ce->guc_id.id, and ce->guc_id.ref + * when transitioning in and out of zero */ spinlock_t lock; /** - * @guc_ids: used to allocate new guc_ids, single-lrc + * @submission_state.guc_ids: used to allocate new + * guc_ids, single-lrc */ struct ida guc_ids; /** - * @num_guc_ids: Number of guc_ids, selftest feature to be able - * to reduce this number while testing. + * @submission_state.num_guc_ids: Number of guc_ids, selftest + * feature to be able to reduce this number while testing. */ int num_guc_ids; /** - * @guc_ids_bitmap: used to allocate new guc_ids, multi-lrc + * @submission_state.guc_ids_bitmap: used to allocate + * new guc_ids, multi-lrc */ unsigned long *guc_ids_bitmap; /** - * @guc_id_list: list of intel_context with valid guc_ids but no - * refs + * @submission_state.guc_id_list: list of intel_context + * with valid guc_ids but no refs */ struct list_head guc_id_list; /** - * @guc_ids_in_use: Number single-lrc guc_ids in use + * @submission_state.guc_ids_in_use: Number single-lrc + * guc_ids in use */ unsigned int guc_ids_in_use; /** - * @destroyed_contexts: list of contexts waiting to be destroyed - * (deregistered with the GuC) + * @submission_state.destroyed_contexts: list of contexts + * waiting to be destroyed (deregistered with the GuC) */ struct list_head destroyed_contexts; /** - * @destroyed_worker: worker to deregister contexts, need as we - * need to take a GT PM reference and can't from destroy - * function as it might be in an atomic context (no sleeping) + * @submission_state.destroyed_worker: worker to deregister + * contexts, need as we need to take a GT PM reference and + * can't from destroy function as it might be in an atomic + * context (no sleeping) */ struct work_struct destroyed_worker; /** - * @reset_fail_worker: worker to trigger a GT reset after an - * engine reset fails + * @submission_state.reset_fail_worker: worker to trigger + * a GT reset after an engine reset fails */ struct work_struct reset_fail_worker; /** - * @reset_fail_mask: mask of engines that failed to reset + * @submission_state.reset_fail_mask: mask of engines that + * failed to reset */ intel_engine_mask_t reset_fail_mask; /** - * @sched_disable_delay_ms: schedule disable delay, in ms, for - * contexts + * @submission_state.sched_disable_delay_ms: schedule + * disable delay, in ms, for contexts */ unsigned int sched_disable_delay_ms; /** - * @sched_disable_gucid_threshold: threshold of min remaining available - * guc_ids before we start bypassing the schedule disable delay + * @submission_state.sched_disable_gucid_threshold: + * threshold of min remaining available guc_ids before + * we start bypassing the schedule disable delay */ unsigned int sched_disable_gucid_threshold; } submission_state; @@ -243,37 +249,40 @@ struct intel_guc { */ struct { /** - * @lock: Lock protecting the below fields and the engine stats. + * @timestamp.lock: Lock protecting the below fields and + * the engine stats. */ spinlock_t lock; /** - * @gt_stamp: 64 bit extended value of the GT timestamp. + * @timestamp.gt_stamp: 64-bit extended value of the GT + * timestamp. */ u64 gt_stamp; /** - * @ping_delay: Period for polling the GT timestamp for - * overflow. + * @timestamp.ping_delay: Period for polling the GT + * timestamp for overflow. */ unsigned long ping_delay; /** - * @work: Periodic work to adjust GT timestamp, engine and - * context usage for overflows. + * @timestamp.work: Periodic work to adjust GT timestamp, + * engine and context usage for overflows. */ struct delayed_work work; /** - * @shift: Right shift value for the gpm timestamp + * @timestamp.shift: Right shift value for the gpm timestamp */ u32 shift; /** - * @last_stat_jiffies: jiffies at last actual stats collection time - * We use this timestamp to ensure we don't oversample the - * stats because runtime power management events can trigger - * stats collection at much higher rates than required. + * @timestamp.last_stat_jiffies: jiffies at last actual + * stats collection time. We use this timestamp to ensure + * we don't oversample the stats because runtime power + * management events can trigger stats collection at much + * higher rates than required. */ unsigned long last_stat_jiffies; } timestamp; diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 90f6c1ece57d..efcb00472be2 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -2849,8 +2849,7 @@ static int handle_mmio(struct intel_gvt_mmio_table_iter *iter, u32 offset, for (i = start; i < end; i += 4) { p = intel_gvt_find_mmio_info(gvt, i); if (p) { - WARN(1, "dup mmio definition offset %x\n", - info->offset); + WARN(1, "dup mmio definition offset %x\n", i); /* We return -EEXIST here to make GVT-g load fail. * So duplicated MMIO can be found as soon as diff --git a/drivers/gpu/drm/i915/i915_perf_types.h b/drivers/gpu/drm/i915/i915_perf_types.h index 13b1ae9b96c7..46445248d193 100644 --- a/drivers/gpu/drm/i915/i915_perf_types.h +++ b/drivers/gpu/drm/i915/i915_perf_types.h @@ -291,7 +291,8 @@ struct i915_perf_stream { int size_exponent; /** - * @ptr_lock: Locks reads and writes to all head/tail state + * @oa_buffer.ptr_lock: Locks reads and writes to all + * head/tail state * * Consider: the head and tail pointer state needs to be read * consistently from a hrtimer callback (atomic context) and @@ -313,7 +314,8 @@ struct i915_perf_stream { spinlock_t ptr_lock; /** - * @head: Although we can always read back the head pointer register, + * @oa_buffer.head: Although we can always read back + * the head pointer register, * we prefer to avoid trusting the HW state, just to avoid any * risk that some hardware condition could * somehow bump the * head pointer unpredictably and cause us to forward the wrong @@ -322,7 +324,8 @@ struct i915_perf_stream { u32 head; /** - * @tail: The last verified tail that can be read by userspace. + * @oa_buffer.tail: The last verified tail that can be + * read by userspace. */ u32 tail; } oa_buffer; diff --git a/drivers/gpu/drm/i915/intel_gvt.c b/drivers/gpu/drm/i915/intel_gvt.c index e98b6d69a91a..9b6d87c8b583 100644 --- a/drivers/gpu/drm/i915/intel_gvt.c +++ b/drivers/gpu/drm/i915/intel_gvt.c @@ -41,7 +41,7 @@ * To virtualize GPU resources GVT-g driver depends on hypervisor technology * e.g KVM/VFIO/mdev, Xen, etc. to provide resource access trapping capability * and be virtualized within GVT-g device module. More architectural design - * doc is available on https://01.org/group/2230/documentation-list. + * doc is available on https://github.com/intel/gvt-linux/wiki. */ static LIST_HEAD(intel_gvt_devices); diff --git a/drivers/gpu/drm/loongson/lsdc_i2c.c b/drivers/gpu/drm/loongson/lsdc_i2c.c index 9625d0b1d0b4..ce90c25536d2 100644 --- a/drivers/gpu/drm/loongson/lsdc_i2c.c +++ b/drivers/gpu/drm/loongson/lsdc_i2c.c @@ -154,7 +154,6 @@ int lsdc_create_i2c_chan(struct drm_device *ddev, adapter = &li2c->adapter; adapter->algo_data = &li2c->bit; adapter->owner = THIS_MODULE; - adapter->class = I2C_CLASS_DDC; adapter->dev.parent = ddev->dev; adapter->nr = -1; diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c index d675c954befe..54e46e440e0f 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c @@ -297,7 +297,6 @@ static int mtk_hdmi_ddc_probe(struct platform_device *pdev) strscpy(ddc->adap.name, "mediatek-hdmi-ddc", sizeof(ddc->adap.name)); ddc->adap.owner = THIS_MODULE; - ddc->adap.class = I2C_CLASS_DDC; ddc->adap.algo = &mtk_hdmi_ddc_algorithm; ddc->adap.retries = 3; ddc->adap.dev.of_node = dev->of_node; diff --git a/drivers/gpu/drm/mgag200/mgag200_i2c.c b/drivers/gpu/drm/mgag200/mgag200_i2c.c index 0c48bdf3e7f8..423eb302be7e 100644 --- a/drivers/gpu/drm/mgag200/mgag200_i2c.c +++ b/drivers/gpu/drm/mgag200/mgag200_i2c.c @@ -106,7 +106,6 @@ int mgag200_i2c_init(struct mga_device *mdev, struct mga_i2c_chan *i2c) i2c->data = BIT(info->i2c.data_bit); i2c->clock = BIT(info->i2c.clock_bit); i2c->adapter.owner = THIS_MODULE; - i2c->adapter.class = I2C_CLASS_DDC; i2c->adapter.dev.parent = dev->dev; i2c->dev = dev; i2c_set_adapdata(&i2c->adapter, i2c); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 83380bc92a00..6a4b489d44e5 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -144,10 +144,6 @@ enum dpu_enc_rc_states { * to track crtc in the disable() hook which is called * _after_ encoder_mask is cleared. * @connector: If a mode is set, cached pointer to the active connector - * @crtc_kickoff_cb: Callback into CRTC that will flush & start - * all CTL paths - * @crtc_kickoff_cb_data: Opaque user data given to crtc_kickoff_cb - * @debugfs_root: Debug file system root file node * @enc_lock: Lock around physical encoder * create/destroy/enable/disable * @frame_busy_mask: Bitmask tracking which phys_enc we are still @@ -2072,7 +2068,7 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) } /* reset the merge 3D HW block */ - if (phys_enc->hw_pp->merge_3d) { + if (phys_enc->hw_pp && phys_enc->hw_pp->merge_3d) { phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d, BLEND_3D_NONE); if (phys_enc->hw_ctl->ops.update_pending_flush_merge_3d) @@ -2103,7 +2099,7 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) if (phys_enc->hw_wb) intf_cfg.wb = phys_enc->hw_wb->idx; - if (phys_enc->hw_pp->merge_3d) + if (phys_enc->hw_pp && phys_enc->hw_pp->merge_3d) intf_cfg.merge_3d = phys_enc->hw_pp->merge_3d->idx; if (ctl->ops.reset_intf_cfg) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c index b58a9c2ae326..724537ab776d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c @@ -29,7 +29,6 @@ static inline bool reserved_by_other(uint32_t *res_map, int idx, /** * struct dpu_rm_requirements - Reservation requirements parameter bundle * @topology: selected topology for the display - * @hw_res: Hardware resources required as reported by the encoders */ struct dpu_rm_requirements { struct msm_display_topology topology; @@ -204,6 +203,8 @@ static bool _dpu_rm_needs_split_display(const struct msm_display_topology *top) * _dpu_rm_get_lm_peer - get the id of a mixer which is a peer of the primary * @rm: dpu resource manager handle * @primary_idx: index of primary mixer in rm->mixer_blks[] + * + * Returns: lm peer mixed id on success or %-EINVAL on error */ static int _dpu_rm_get_lm_peer(struct dpu_rm *rm, int primary_idx) { diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 77a8d9366ed7..fb588fde298a 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -135,11 +135,6 @@ static void dp_ctrl_config_ctrl(struct dp_ctrl_private *ctrl) tbd = dp_link_get_test_bits_depth(ctrl->link, ctrl->panel->dp_mode.bpp); - if (tbd == DP_TEST_BIT_DEPTH_UNKNOWN) { - pr_debug("BIT_DEPTH not set. Configure default\n"); - tbd = DP_TEST_BIT_DEPTH_8; - } - config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT; /* Num of Lanes */ diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c index 98427d45e9a7..49dfac1fd1ef 100644 --- a/drivers/gpu/drm/msm/dp/dp_link.c +++ b/drivers/gpu/drm/msm/dp/dp_link.c @@ -7,6 +7,7 @@ #include <drm/drm_print.h> +#include "dp_reg.h" #include "dp_link.h" #include "dp_panel.h" @@ -1082,7 +1083,7 @@ int dp_link_process_request(struct dp_link *dp_link) int dp_link_get_colorimetry_config(struct dp_link *dp_link) { - u32 cc; + u32 cc = DP_MISC0_COLORIMERY_CFG_LEGACY_RGB; struct dp_link_private *link; if (!dp_link) { @@ -1096,10 +1097,11 @@ int dp_link_get_colorimetry_config(struct dp_link *dp_link) * Unless a video pattern CTS test is ongoing, use RGB_VESA * Only RGB_VESA and RGB_CEA supported for now */ - if (dp_link_is_video_pattern_requested(link)) - cc = link->dp_link.test_video.test_dyn_range; - else - cc = DP_TEST_DYNAMIC_RANGE_VESA; + if (dp_link_is_video_pattern_requested(link)) { + if (link->dp_link.test_video.test_dyn_range & + DP_TEST_DYNAMIC_RANGE_CEA) + cc = DP_MISC0_COLORIMERY_CFG_CEA_RGB; + } return cc; } @@ -1179,6 +1181,9 @@ void dp_link_reset_phy_params_vx_px(struct dp_link *dp_link) u32 dp_link_get_test_bits_depth(struct dp_link *dp_link, u32 bpp) { u32 tbd; + struct dp_link_private *link; + + link = container_of(dp_link, struct dp_link_private, dp_link); /* * Few simplistic rules and assumptions made here: @@ -1196,12 +1201,13 @@ u32 dp_link_get_test_bits_depth(struct dp_link *dp_link, u32 bpp) tbd = DP_TEST_BIT_DEPTH_10; break; default: - tbd = DP_TEST_BIT_DEPTH_UNKNOWN; + drm_dbg_dp(link->drm_dev, "bpp=%d not supported, use bpc=8\n", + bpp); + tbd = DP_TEST_BIT_DEPTH_8; break; } - if (tbd != DP_TEST_BIT_DEPTH_UNKNOWN) - tbd = (tbd >> DP_TEST_BIT_DEPTH_SHIFT); + tbd = (tbd >> DP_TEST_BIT_DEPTH_SHIFT); return tbd; } diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h index ea85a691e72b..78785ed4b40c 100644 --- a/drivers/gpu/drm/msm/dp/dp_reg.h +++ b/drivers/gpu/drm/msm/dp/dp_reg.h @@ -143,6 +143,9 @@ #define DP_MISC0_COLORIMETRY_CFG_SHIFT (0x00000001) #define DP_MISC0_TEST_BITS_DEPTH_SHIFT (0x00000005) +#define DP_MISC0_COLORIMERY_CFG_LEGACY_RGB (0) +#define DP_MISC0_COLORIMERY_CFG_CEA_RGB (0x04) + #define REG_DP_VALID_BOUNDARY (0x00000030) #define REG_DP_VALID_BOUNDARY_2 (0x00000034) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c index de182c004843..7aa500d24240 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c @@ -249,7 +249,6 @@ struct i2c_adapter *msm_hdmi_i2c_init(struct hdmi *hdmi) i2c->owner = THIS_MODULE; - i2c->class = I2C_CLASS_DDC; snprintf(i2c->name, sizeof(i2c->name), "msm hdmi i2c"); i2c->dev.parent = &hdmi->pdev->dev; i2c->algo = &msm_hdmi_i2c_algorithm; diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index 455b2e3a0cdd..35423d10aafa 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -562,6 +562,7 @@ static const struct msm_mdss_data sdm670_data = { .ubwc_enc_version = UBWC_2_0, .ubwc_dec_version = UBWC_2_0, .highest_bank_bit = 1, + .reg_bus_bw = 76800, }; static const struct msm_mdss_data sdm845_data = { diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h index d1437c08645f..6f5d376d8fcc 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h @@ -9,7 +9,7 @@ #define GSP_PAGE_SIZE BIT(GSP_PAGE_SHIFT) struct nvkm_gsp_mem { - u32 size; + size_t size; void *data; dma_addr_t addr; }; diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 5057d976fa57..93f08f9479d8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -62,7 +62,7 @@ nouveau_fence_signal(struct nouveau_fence *fence) if (test_bit(DMA_FENCE_FLAG_USER_BITS, &fence->base.flags)) { struct nouveau_fence_chan *fctx = nouveau_fctx(fence); - if (atomic_dec_and_test(&fctx->notify_ref)) + if (!--fctx->notify_ref) drop = 1; } @@ -103,7 +103,7 @@ nouveau_fence_context_kill(struct nouveau_fence_chan *fctx, int error) void nouveau_fence_context_del(struct nouveau_fence_chan *fctx) { - cancel_work_sync(&fctx->allow_block_work); + cancel_work_sync(&fctx->uevent_work); nouveau_fence_context_kill(fctx, 0); nvif_event_dtor(&fctx->event); fctx->dead = 1; @@ -146,12 +146,13 @@ nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fc return drop; } -static int -nouveau_fence_wait_uevent_handler(struct nvif_event *event, void *repv, u32 repc) +static void +nouveau_fence_uevent_work(struct work_struct *work) { - struct nouveau_fence_chan *fctx = container_of(event, typeof(*fctx), event); + struct nouveau_fence_chan *fctx = container_of(work, struct nouveau_fence_chan, + uevent_work); unsigned long flags; - int ret = NVIF_EVENT_KEEP; + int drop = 0; spin_lock_irqsave(&fctx->lock, flags); if (!list_empty(&fctx->pending)) { @@ -161,23 +162,20 @@ nouveau_fence_wait_uevent_handler(struct nvif_event *event, void *repv, u32 repc fence = list_entry(fctx->pending.next, typeof(*fence), head); chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); if (nouveau_fence_update(chan, fctx)) - ret = NVIF_EVENT_DROP; + drop = 1; } - spin_unlock_irqrestore(&fctx->lock, flags); + if (drop) + nvif_event_block(&fctx->event); - return ret; + spin_unlock_irqrestore(&fctx->lock, flags); } -static void -nouveau_fence_work_allow_block(struct work_struct *work) +static int +nouveau_fence_wait_uevent_handler(struct nvif_event *event, void *repv, u32 repc) { - struct nouveau_fence_chan *fctx = container_of(work, struct nouveau_fence_chan, - allow_block_work); - - if (atomic_read(&fctx->notify_ref) == 0) - nvif_event_block(&fctx->event); - else - nvif_event_allow(&fctx->event); + struct nouveau_fence_chan *fctx = container_of(event, typeof(*fctx), event); + schedule_work(&fctx->uevent_work); + return NVIF_EVENT_KEEP; } void @@ -191,7 +189,7 @@ nouveau_fence_context_new(struct nouveau_channel *chan, struct nouveau_fence_cha } args; int ret; - INIT_WORK(&fctx->allow_block_work, nouveau_fence_work_allow_block); + INIT_WORK(&fctx->uevent_work, nouveau_fence_uevent_work); INIT_LIST_HEAD(&fctx->flip); INIT_LIST_HEAD(&fctx->pending); spin_lock_init(&fctx->lock); @@ -535,19 +533,15 @@ static bool nouveau_fence_enable_signaling(struct dma_fence *f) struct nouveau_fence *fence = from_fence(f); struct nouveau_fence_chan *fctx = nouveau_fctx(fence); bool ret; - bool do_work; - if (atomic_inc_return(&fctx->notify_ref) == 0) - do_work = true; + if (!fctx->notify_ref++) + nvif_event_allow(&fctx->event); ret = nouveau_fence_no_signaling(f); if (ret) set_bit(DMA_FENCE_FLAG_USER_BITS, &fence->base.flags); - else if (atomic_dec_and_test(&fctx->notify_ref)) - do_work = true; - - if (do_work) - schedule_work(&fctx->allow_block_work); + else if (!--fctx->notify_ref) + nvif_event_block(&fctx->event); return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h index 28f5cf013b89..8bc065acfe35 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.h +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -3,7 +3,6 @@ #define __NOUVEAU_FENCE_H__ #include <linux/dma-fence.h> -#include <linux/workqueue.h> #include <nvif/event.h> struct nouveau_drm; @@ -45,10 +44,9 @@ struct nouveau_fence_chan { u32 context; char name[32]; + struct work_struct uevent_work; struct nvif_event event; - struct work_struct allow_block_work; - atomic_t notify_ref; - int dead, killed; + int notify_ref, dead, killed; }; struct nouveau_fence_priv { diff --git a/drivers/gpu/drm/nouveau/nouveau_vmm.c b/drivers/gpu/drm/nouveau/nouveau_vmm.c index a6602c012671..3dda885df5b2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vmm.c +++ b/drivers/gpu/drm/nouveau/nouveau_vmm.c @@ -108,6 +108,9 @@ nouveau_vma_new(struct nouveau_bo *nvbo, struct nouveau_vmm *vmm, } else { ret = nvif_vmm_get(&vmm->vmm, PTES, false, mem->mem.page, 0, mem->mem.size, &tmp); + if (ret) + goto done; + vma->addr = tmp.addr; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga100.c index c8ce7ff18713..e74493a4569e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/ga100.c @@ -550,6 +550,10 @@ ga100_fifo_nonstall_ctor(struct nvkm_fifo *fifo) struct nvkm_engn *engn = list_first_entry(&runl->engns, typeof(*engn), head); runl->nonstall.vector = engn->func->nonstall(engn); + + /* if no nonstall vector just keep going */ + if (runl->nonstall.vector == -1) + continue; if (runl->nonstall.vector < 0) { RUNL_ERROR(runl, "nonstall %d", runl->nonstall.vector); return runl->nonstall.vector; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/r535.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/r535.c index b903785056b5..3454c7d29502 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/r535.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/r535.c @@ -351,7 +351,7 @@ r535_engn_nonstall(struct nvkm_engn *engn) int ret; ret = nvkm_gsp_intr_nonstall(subdev->device->gsp, subdev->type, subdev->inst); - WARN_ON(ret < 0); + WARN_ON(ret == -ENOENT); return ret; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/base.c index 04bceaa28a19..da1bebb896f7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/base.c @@ -25,12 +25,8 @@ int nvkm_gsp_intr_nonstall(struct nvkm_gsp *gsp, enum nvkm_subdev_type type, int inst) { for (int i = 0; i < gsp->intr_nr; i++) { - if (gsp->intr[i].type == type && gsp->intr[i].inst == inst) { - if (gsp->intr[i].nonstall != ~0) - return gsp->intr[i].nonstall; - - return -EINVAL; - } + if (gsp->intr[i].type == type && gsp->intr[i].inst == inst) + return gsp->intr[i].nonstall; } return -ENOENT; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c index 9ee58e2a0eb2..a41735ab6068 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c @@ -997,6 +997,32 @@ r535_gsp_rpc_get_gsp_static_info(struct nvkm_gsp *gsp) return 0; } +static void +nvkm_gsp_mem_dtor(struct nvkm_gsp *gsp, struct nvkm_gsp_mem *mem) +{ + if (mem->data) { + /* + * Poison the buffer to catch any unexpected access from + * GSP-RM if the buffer was prematurely freed. + */ + memset(mem->data, 0xFF, mem->size); + + dma_free_coherent(gsp->subdev.device->dev, mem->size, mem->data, mem->addr); + memset(mem, 0, sizeof(*mem)); + } +} + +static int +nvkm_gsp_mem_ctor(struct nvkm_gsp *gsp, size_t size, struct nvkm_gsp_mem *mem) +{ + mem->size = size; + mem->data = dma_alloc_coherent(gsp->subdev.device->dev, size, &mem->addr, GFP_KERNEL); + if (WARN_ON(!mem->data)) + return -ENOMEM; + + return 0; +} + static int r535_gsp_postinit(struct nvkm_gsp *gsp) { @@ -1024,6 +1050,13 @@ r535_gsp_postinit(struct nvkm_gsp *gsp) nvkm_inth_allow(&gsp->subdev.inth); nvkm_wr32(device, 0x110004, 0x00000040); + + /* Release the DMA buffers that were needed only for boot and init */ + nvkm_gsp_mem_dtor(gsp, &gsp->boot.fw); + nvkm_gsp_mem_dtor(gsp, &gsp->libos); + nvkm_gsp_mem_dtor(gsp, &gsp->rmargs); + nvkm_gsp_mem_dtor(gsp, &gsp->wpr_meta); + return ret; } @@ -1078,7 +1111,6 @@ r535_gsp_rpc_set_registry(struct nvkm_gsp *gsp) if (IS_ERR(rpc)) return PTR_ERR(rpc); - rpc->size = sizeof(*rpc); rpc->numEntries = NV_GSP_REG_NUM_ENTRIES; str_offset = offsetof(typeof(*rpc), entries[NV_GSP_REG_NUM_ENTRIES]); @@ -1094,6 +1126,7 @@ r535_gsp_rpc_set_registry(struct nvkm_gsp *gsp) strings += name_len; str_offset += name_len; } + rpc->size = str_offset; return nvkm_gsp_rpc_wr(gsp, rpc, false); } @@ -1532,27 +1565,6 @@ r535_gsp_msg_run_cpu_sequencer(void *priv, u32 fn, void *repv, u32 repc) return 0; } -static void -nvkm_gsp_mem_dtor(struct nvkm_gsp *gsp, struct nvkm_gsp_mem *mem) -{ - if (mem->data) { - dma_free_coherent(gsp->subdev.device->dev, mem->size, mem->data, mem->addr); - mem->data = NULL; - } -} - -static int -nvkm_gsp_mem_ctor(struct nvkm_gsp *gsp, u32 size, struct nvkm_gsp_mem *mem) -{ - mem->size = size; - mem->data = dma_alloc_coherent(gsp->subdev.device->dev, size, &mem->addr, GFP_KERNEL); - if (WARN_ON(!mem->data)) - return -ENOMEM; - - return 0; -} - - static int r535_gsp_booter_unload(struct nvkm_gsp *gsp, u32 mbox0, u32 mbox1) { @@ -1938,20 +1950,20 @@ nvkm_gsp_radix3_dtor(struct nvkm_gsp *gsp, struct nvkm_gsp_radix3 *rx3) * See kgspCreateRadix3_IMPL */ static int -nvkm_gsp_radix3_sg(struct nvkm_device *device, struct sg_table *sgt, u64 size, +nvkm_gsp_radix3_sg(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, struct nvkm_gsp_radix3 *rx3) { u64 addr; for (int i = ARRAY_SIZE(rx3->mem) - 1; i >= 0; i--) { u64 *ptes; - int idx; + size_t bufsize; + int ret, idx; - rx3->mem[i].size = ALIGN((size / GSP_PAGE_SIZE) * sizeof(u64), GSP_PAGE_SIZE); - rx3->mem[i].data = dma_alloc_coherent(device->dev, rx3->mem[i].size, - &rx3->mem[i].addr, GFP_KERNEL); - if (WARN_ON(!rx3->mem[i].data)) - return -ENOMEM; + bufsize = ALIGN((size / GSP_PAGE_SIZE) * sizeof(u64), GSP_PAGE_SIZE); + ret = nvkm_gsp_mem_ctor(gsp, bufsize, &rx3->mem[i]); + if (ret) + return ret; ptes = rx3->mem[i].data; if (i == 2) { @@ -1991,7 +2003,7 @@ r535_gsp_fini(struct nvkm_gsp *gsp, bool suspend) if (ret) return ret; - ret = nvkm_gsp_radix3_sg(gsp->subdev.device, &gsp->sr.sgt, len, &gsp->sr.radix3); + ret = nvkm_gsp_radix3_sg(gsp, &gsp->sr.sgt, len, &gsp->sr.radix3); if (ret) return ret; @@ -2150,6 +2162,11 @@ r535_gsp_dtor(struct nvkm_gsp *gsp) mutex_destroy(&gsp->cmdq.mutex); r535_gsp_dtor_fws(gsp); + + nvkm_gsp_mem_dtor(gsp, &gsp->shm.mem); + nvkm_gsp_mem_dtor(gsp, &gsp->loginit); + nvkm_gsp_mem_dtor(gsp, &gsp->logintr); + nvkm_gsp_mem_dtor(gsp, &gsp->logrm); } int @@ -2194,7 +2211,7 @@ r535_gsp_oneinit(struct nvkm_gsp *gsp) memcpy(gsp->sig.data, data, size); /* Build radix3 page table for ELF image. */ - ret = nvkm_gsp_radix3_sg(device, &gsp->fw.mem.sgt, gsp->fw.len, &gsp->radix3); + ret = nvkm_gsp_radix3_sg(gsp, &gsp->fw.mem.sgt, gsp->fw.len, &gsp->radix3); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp10b.c index e7e8fdf3adab..29682722b0b3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp10b.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/gp10b.c @@ -28,19 +28,14 @@ static void gp10b_ltc_init(struct nvkm_ltc *ltc) { struct nvkm_device *device = ltc->subdev.device; - struct iommu_fwspec *spec; + u32 sid; nvkm_wr32(device, 0x17e27c, ltc->ltc_nr); nvkm_wr32(device, 0x17e000, ltc->ltc_nr); nvkm_wr32(device, 0x100800, ltc->ltc_nr); - spec = dev_iommu_fwspec_get(device->dev); - if (spec) { - u32 sid = spec->ids[0] & 0xffff; - - /* stream ID */ + if (tegra_dev_iommu_get_stream_id(device->dev, &sid)) nvkm_wr32(device, 0x160000, sid << 2); - } } static const struct nvkm_ltc_func diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index dad938cf6dec..8f3783742208 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -539,6 +539,8 @@ config DRM_PANEL_RAYDIUM_RM692E5 depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_DP_HELPER + select DRM_DISPLAY_HELPER help Say Y here if you want to enable support for Raydium RM692E5-based display panels, such as the one found in the Fairphone 5 smartphone. diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c index ea5a85779382..f23d8832a1ad 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c @@ -309,7 +309,7 @@ static const struct s6d7aa0_panel_desc s6d7aa0_lsl080al02_desc = { .off_func = s6d7aa0_lsl080al02_off, .drm_mode = &s6d7aa0_lsl080al02_mode, .mode_flags = MIPI_DSI_MODE_VSYNC_FLUSH | MIPI_DSI_MODE_VIDEO_NO_HFP, - .bus_flags = DRM_BUS_FLAG_DE_HIGH, + .bus_flags = 0, .has_backlight = false, .use_passwd3 = false, diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 2214cb09678c..d493ee735c73 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -3948,6 +3948,7 @@ static const struct panel_desc tianma_tm070jdhg30 = { }, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, .connector_type = DRM_MODE_CONNECTOR_LVDS, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, }; static const struct panel_desc tianma_tm070jvhg33 = { @@ -3960,6 +3961,7 @@ static const struct panel_desc tianma_tm070jvhg33 = { }, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, .connector_type = DRM_MODE_CONNECTOR_LVDS, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, }; static const struct display_timing tianma_tm070rvhg71_timing = { diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c index 314d066e68e9..3d174390a8af 100644 --- a/drivers/gpu/drm/radeon/radeon_i2c.c +++ b/drivers/gpu/drm/radeon/radeon_i2c.c @@ -918,7 +918,6 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev, i2c->rec = *rec; i2c->adapter.owner = THIS_MODULE; - i2c->adapter.class = I2C_CLASS_DDC; i2c->adapter.dev.parent = dev->dev; i2c->dev = dev; i2c_set_adapdata(&i2c->adapter, i2c); diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c index f6d819803c0e..e6fbe040ccf6 100644 --- a/drivers/gpu/drm/rockchip/inno_hdmi.c +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c @@ -792,7 +792,6 @@ static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi) init_completion(&i2c->cmp); adap = &i2c->adap; - adap->class = I2C_CLASS_DDC; adap->owner = THIS_MODULE; adap->dev.parent = hdmi->dev; adap->dev.of_node = hdmi->dev->of_node; diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c index 62e6d8187de7..95cd1b49eda8 100644 --- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c +++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c @@ -715,7 +715,6 @@ static struct i2c_adapter *rk3066_hdmi_i2c_adapter(struct rk3066_hdmi *hdmi) init_completion(&i2c->cmpltn); adap = &i2c->adap; - adap->class = I2C_CLASS_DDC; adap->owner = THIS_MODULE; adap->dev.parent = hdmi->dev; adap->dev.of_node = hdmi->dev->of_node; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 574103fc79f9..85b3b4871a1d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -35,7 +35,6 @@ #include "rockchip_drm_drv.h" #include "rockchip_drm_gem.h" -#include "rockchip_drm_fb.h" #include "rockchip_drm_vop2.h" #include "rockchip_rgb.h" @@ -1681,7 +1680,6 @@ static unsigned long rk3588_calc_cru_cfg(struct vop2_video_port *vp, int id, unsigned long dclk_core_rate = v_pixclk >> 2; unsigned long dclk_rate = v_pixclk; unsigned long dclk_out_rate; - unsigned long if_dclk_rate; unsigned long if_pixclk_rate; int K = 1; @@ -1696,8 +1694,8 @@ static unsigned long rk3588_calc_cru_cfg(struct vop2_video_port *vp, int id, } if_pixclk_rate = (dclk_core_rate << 1) / K; - if_dclk_rate = dclk_core_rate / K; /* + * if_dclk_rate = dclk_core_rate / K; * *if_pixclk_div = dclk_rate / if_pixclk_rate; * *if_dclk_div = dclk_rate / if_dclk_rate; */ diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 550492a7a031..d442b893275b 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -1184,14 +1184,16 @@ static void drm_sched_run_job_work(struct work_struct *w) if (READ_ONCE(sched->pause_submit)) return; + /* Find entity with a ready job */ entity = drm_sched_select_entity(sched); if (!entity) - return; + return; /* No more work */ sched_job = drm_sched_entity_pop_job(entity); if (!sched_job) { complete_all(&entity->entity_idle); - return; /* No more work */ + drm_sched_run_job_queue(sched); + return; } s_fence = sched_job->s_fence; diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index bef293922b98..3d0e093a7e6e 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -319,7 +319,7 @@ static int ssd130x_pwm_enable(struct ssd130x_device *ssd130x) pwm_init_state(ssd130x->pwm, &pwmstate); pwm_set_relative_duty_cycle(&pwmstate, 50, 100); - pwm_apply_state(ssd130x->pwm, &pwmstate); + pwm_apply_might_sleep(ssd130x->pwm, &pwmstate); /* Enable the PWM */ pwm_enable(ssd130x->pwm); diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c index d1a65a921f5a..f5f62eb0eeca 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c @@ -302,7 +302,6 @@ int sun4i_hdmi_i2c_create(struct device *dev, struct sun4i_hdmi *hdmi) return -ENOMEM; adap->owner = THIS_MODULE; - adap->class = I2C_CLASS_DDC; adap->algo = &sun4i_hdmi_i2c_algorithm; strscpy(adap->name, "sun4i_hdmi_i2c adapter", sizeof(adap->name)); i2c_set_adapdata(adap, hdmi); diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index ff36171c8fb7..a73cff7a3070 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -960,7 +960,8 @@ int host1x_client_iommu_attach(struct host1x_client *client) * not the shared IOMMU domain, don't try to attach it to a different * domain. This allows using the IOMMU-backed DMA API. */ - if (domain && domain != tegra->domain) + if (domain && domain->type != IOMMU_DOMAIN_IDENTITY && + domain != tegra->domain) return 0; if (tegra->domain) { diff --git a/drivers/gpu/drm/tests/drm_mm_test.c b/drivers/gpu/drm/tests/drm_mm_test.c index 4e9247cf9977..1eb0c304f960 100644 --- a/drivers/gpu/drm/tests/drm_mm_test.c +++ b/drivers/gpu/drm/tests/drm_mm_test.c @@ -188,13 +188,13 @@ out: static void drm_test_mm_debug(struct kunit *test) { + struct drm_printer p = drm_debug_printer(test->name); struct drm_mm mm; struct drm_mm_node nodes[2]; /* Create a small drm_mm with a couple of nodes and a few holes, and * check that the debug iterator doesn't explode over a trivial drm_mm. */ - drm_mm_init(&mm, 0, 4096); memset(nodes, 0, sizeof(nodes)); @@ -209,6 +209,9 @@ static void drm_test_mm_debug(struct kunit *test) KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[1]), "failed to reserve node[0] {start=%lld, size=%lld)\n", nodes[0].start, nodes[0].size); + + drm_mm_print(&mm, &p); + KUNIT_SUCCEED(test); } static bool expect_insert(struct kunit *test, struct drm_mm *mm, diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c index f5187b384ae9..76027960054f 100644 --- a/drivers/gpu/drm/ttm/ttm_device.c +++ b/drivers/gpu/drm/ttm/ttm_device.c @@ -95,11 +95,17 @@ static int ttm_global_init(void) ttm_pool_mgr_init(num_pages); ttm_tt_mgr_init(num_pages, num_dma32); - glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32); + glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32 | + __GFP_NOWARN); + /* Retry without GFP_DMA32 for platforms DMA32 is not available */ if (unlikely(glob->dummy_read_page == NULL)) { - ret = -ENOMEM; - goto out; + glob->dummy_read_page = alloc_page(__GFP_ZERO); + if (unlikely(glob->dummy_read_page == NULL)) { + ret = -ENOMEM; + goto out; + } + pr_warn("Using GFP_DMA32 fallback for dummy_read_page\n"); } INIT_LIST_HEAD(&glob->device_list); @@ -195,7 +201,7 @@ int ttm_device_init(struct ttm_device *bdev, const struct ttm_device_funcs *func bool use_dma_alloc, bool use_dma32) { struct ttm_global *glob = &ttm_glob; - int ret; + int ret, nid; if (WARN_ON(vma_manager == NULL)) return -EINVAL; @@ -215,7 +221,12 @@ int ttm_device_init(struct ttm_device *bdev, const struct ttm_device_funcs *func ttm_sys_man_init(bdev); - ttm_pool_init(&bdev->pool, dev, dev_to_node(dev), use_dma_alloc, use_dma32); + if (dev) + nid = dev_to_node(dev); + else + nid = NUMA_NO_NODE; + + ttm_pool_init(&bdev->pool, dev, nid, use_dma_alloc, use_dma32); bdev->vma_manager = vma_manager; spin_lock_init(&bdev->lru_lock); diff --git a/drivers/gpu/drm/v3d/v3d_debugfs.c b/drivers/gpu/drm/v3d/v3d_debugfs.c index f843a50d5dce..94eafcecc65b 100644 --- a/drivers/gpu/drm/v3d/v3d_debugfs.c +++ b/drivers/gpu/drm/v3d/v3d_debugfs.c @@ -62,9 +62,9 @@ static const struct v3d_reg_def v3d_core_reg_defs[] = { REGDEF(33, 71, V3D_PTB_BPCA), REGDEF(33, 71, V3D_PTB_BPCS), - REGDEF(33, 41, V3D_GMP_STATUS(33)), - REGDEF(33, 41, V3D_GMP_CFG(33)), - REGDEF(33, 41, V3D_GMP_VIO_ADDR(33)), + REGDEF(33, 42, V3D_GMP_STATUS(33)), + REGDEF(33, 42, V3D_GMP_CFG(33)), + REGDEF(33, 42, V3D_GMP_VIO_ADDR(33)), REGDEF(33, 71, V3D_ERR_FDBGO), REGDEF(33, 71, V3D_ERR_FDBGB), @@ -74,13 +74,13 @@ static const struct v3d_reg_def v3d_core_reg_defs[] = { static const struct v3d_reg_def v3d_csd_reg_defs[] = { REGDEF(41, 71, V3D_CSD_STATUS), - REGDEF(41, 41, V3D_CSD_CURRENT_CFG0(41)), - REGDEF(41, 41, V3D_CSD_CURRENT_CFG1(41)), - REGDEF(41, 41, V3D_CSD_CURRENT_CFG2(41)), - REGDEF(41, 41, V3D_CSD_CURRENT_CFG3(41)), - REGDEF(41, 41, V3D_CSD_CURRENT_CFG4(41)), - REGDEF(41, 41, V3D_CSD_CURRENT_CFG5(41)), - REGDEF(41, 41, V3D_CSD_CURRENT_CFG6(41)), + REGDEF(41, 42, V3D_CSD_CURRENT_CFG0(41)), + REGDEF(41, 42, V3D_CSD_CURRENT_CFG1(41)), + REGDEF(41, 42, V3D_CSD_CURRENT_CFG2(41)), + REGDEF(41, 42, V3D_CSD_CURRENT_CFG3(41)), + REGDEF(41, 42, V3D_CSD_CURRENT_CFG4(41)), + REGDEF(41, 42, V3D_CSD_CURRENT_CFG5(41)), + REGDEF(41, 42, V3D_CSD_CURRENT_CFG6(41)), REGDEF(71, 71, V3D_CSD_CURRENT_CFG0(71)), REGDEF(71, 71, V3D_CSD_CURRENT_CFG1(71)), REGDEF(71, 71, V3D_CSD_CURRENT_CFG2(71)), diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index fcff41dd2315..88f63d526b22 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -147,6 +147,13 @@ v3d_job_allocate(void **container, size_t size) return 0; } +static void +v3d_job_deallocate(void **container) +{ + kfree(*container); + *container = NULL; +} + static int v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, struct v3d_job *job, void (*free)(struct kref *ref), @@ -273,8 +280,10 @@ v3d_setup_csd_jobs_and_bos(struct drm_file *file_priv, ret = v3d_job_init(v3d, file_priv, &(*job)->base, v3d_job_free, args->in_sync, se, V3D_CSD); - if (ret) + if (ret) { + v3d_job_deallocate((void *)job); return ret; + } ret = v3d_job_allocate((void *)clean_job, sizeof(**clean_job)); if (ret) @@ -282,8 +291,10 @@ v3d_setup_csd_jobs_and_bos(struct drm_file *file_priv, ret = v3d_job_init(v3d, file_priv, *clean_job, v3d_job_free, 0, NULL, V3D_CACHE_CLEAN); - if (ret) + if (ret) { + v3d_job_deallocate((void *)clean_job); return ret; + } (*job)->args = *args; @@ -860,8 +871,10 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, ret = v3d_job_init(v3d, file_priv, &render->base, v3d_render_job_free, args->in_sync_rcl, &se, V3D_RENDER); - if (ret) + if (ret) { + v3d_job_deallocate((void *)&render); goto fail; + } render->start = args->rcl_start; render->end = args->rcl_end; @@ -874,8 +887,10 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, ret = v3d_job_init(v3d, file_priv, &bin->base, v3d_job_free, args->in_sync_bcl, &se, V3D_BIN); - if (ret) + if (ret) { + v3d_job_deallocate((void *)&bin); goto fail; + } bin->start = args->bcl_start; bin->end = args->bcl_end; @@ -892,8 +907,10 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, ret = v3d_job_init(v3d, file_priv, clean_job, v3d_job_free, 0, NULL, V3D_CACHE_CLEAN); - if (ret) + if (ret) { + v3d_job_deallocate((void *)&clean_job); goto fail; + } last_job = clean_job; } else { @@ -1015,8 +1032,10 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, ret = v3d_job_init(v3d, file_priv, &job->base, v3d_job_free, args->in_sync, &se, V3D_TFU); - if (ret) + if (ret) { + v3d_job_deallocate((void *)&job); goto fail; + } job->base.bo = kcalloc(ARRAY_SIZE(args->bo_handles), sizeof(*job->base.bo), GFP_KERNEL); @@ -1233,8 +1252,10 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data, ret = v3d_job_init(v3d, file_priv, &cpu_job->base, v3d_job_free, 0, &se, V3D_CPU); - if (ret) + if (ret) { + v3d_job_deallocate((void *)&cpu_job); goto fail; + } clean_job = cpu_job->indirect_csd.clean_job; csd_job = cpu_job->indirect_csd.job; diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index f8e9abe647b9..9539aa28937f 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -94,6 +94,7 @@ static int virtio_gpu_probe(struct virtio_device *vdev) goto err_free; } + dma_set_max_seg_size(dev->dev, dma_max_mapping_size(dev->dev) ?: UINT_MAX); ret = virtio_gpu_init(vdev, dev); if (ret) goto err_free; diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig index 1cced50d8d8c..e36ae1f0d885 100644 --- a/drivers/gpu/drm/xe/Kconfig +++ b/drivers/gpu/drm/xe/Kconfig @@ -47,7 +47,7 @@ config DRM_XE config DRM_XE_DISPLAY bool "Enable display support" - depends on DRM_XE && EXPERT && DRM_XE=m + depends on DRM_XE && DRM_XE=m select FB_IOMEM_HELPERS select I2C select I2C_ALGOBIT diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 53bd2a8ba1ae..efcf0ab7a1a6 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -17,7 +17,6 @@ subdir-ccflags-y += $(call cc-option, -Wunused-const-variable) subdir-ccflags-y += $(call cc-option, -Wpacked-not-aligned) subdir-ccflags-y += $(call cc-option, -Wformat-overflow) subdir-ccflags-y += $(call cc-option, -Wformat-truncation) -subdir-ccflags-y += $(call cc-option, -Wstringop-overflow) subdir-ccflags-y += $(call cc-option, -Wstringop-truncation) # The following turn off the warnings enabled by -Wextra ifeq ($(findstring 2, $(KBUILD_EXTRA_WARN)),) diff --git a/drivers/gpu/drm/xe/abi/guc_actions_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_abi.h index 3062e0e0d467..79ba98a169f9 100644 --- a/drivers/gpu/drm/xe/abi/guc_actions_abi.h +++ b/drivers/gpu/drm/xe/abi/guc_actions_abi.h @@ -50,8 +50,8 @@ #define HOST2GUC_SELF_CFG_REQUEST_MSG_LEN (GUC_HXG_REQUEST_MSG_MIN_LEN + 3u) #define HOST2GUC_SELF_CFG_REQUEST_MSG_0_MBZ GUC_HXG_REQUEST_MSG_0_DATA0 -#define HOST2GUC_SELF_CFG_REQUEST_MSG_1_KLV_KEY (0xffff << 16) -#define HOST2GUC_SELF_CFG_REQUEST_MSG_1_KLV_LEN (0xffff << 0) +#define HOST2GUC_SELF_CFG_REQUEST_MSG_1_KLV_KEY (0xffffu << 16) +#define HOST2GUC_SELF_CFG_REQUEST_MSG_1_KLV_LEN (0xffffu << 0) #define HOST2GUC_SELF_CFG_REQUEST_MSG_2_VALUE32 GUC_HXG_REQUEST_MSG_n_DATAn #define HOST2GUC_SELF_CFG_REQUEST_MSG_3_VALUE64 GUC_HXG_REQUEST_MSG_n_DATAn diff --git a/drivers/gpu/drm/xe/abi/guc_actions_slpc_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_slpc_abi.h index 811add10c30d..c165e26c0976 100644 --- a/drivers/gpu/drm/xe/abi/guc_actions_slpc_abi.h +++ b/drivers/gpu/drm/xe/abi/guc_actions_slpc_abi.h @@ -242,8 +242,8 @@ struct slpc_shared_data { (HOST2GUC_PC_SLPC_REQUEST_REQUEST_MSG_MIN_LEN + \ HOST2GUC_PC_SLPC_EVENT_MAX_INPUT_ARGS) #define HOST2GUC_PC_SLPC_REQUEST_MSG_0_MBZ GUC_HXG_REQUEST_MSG_0_DATA0 -#define HOST2GUC_PC_SLPC_REQUEST_MSG_1_EVENT_ID (0xff << 8) -#define HOST2GUC_PC_SLPC_REQUEST_MSG_1_EVENT_ARGC (0xff << 0) +#define HOST2GUC_PC_SLPC_REQUEST_MSG_1_EVENT_ID (0xffu << 8) +#define HOST2GUC_PC_SLPC_REQUEST_MSG_1_EVENT_ARGC (0xffu << 0) #define HOST2GUC_PC_SLPC_REQUEST_MSG_N_EVENT_DATA_N GUC_HXG_REQUEST_MSG_n_DATAn #endif diff --git a/drivers/gpu/drm/xe/abi/guc_communication_ctb_abi.h b/drivers/gpu/drm/xe/abi/guc_communication_ctb_abi.h index 3b83f907ece4..0b1146d0c997 100644 --- a/drivers/gpu/drm/xe/abi/guc_communication_ctb_abi.h +++ b/drivers/gpu/drm/xe/abi/guc_communication_ctb_abi.h @@ -82,11 +82,11 @@ static_assert(sizeof(struct guc_ct_buffer_desc) == 64); #define GUC_CTB_HDR_LEN 1u #define GUC_CTB_MSG_MIN_LEN GUC_CTB_HDR_LEN #define GUC_CTB_MSG_MAX_LEN 256u -#define GUC_CTB_MSG_0_FENCE (0xffff << 16) -#define GUC_CTB_MSG_0_FORMAT (0xf << 12) +#define GUC_CTB_MSG_0_FENCE (0xffffu << 16) +#define GUC_CTB_MSG_0_FORMAT (0xfu << 12) #define GUC_CTB_FORMAT_HXG 0u -#define GUC_CTB_MSG_0_RESERVED (0xf << 8) -#define GUC_CTB_MSG_0_NUM_DWORDS (0xff << 0) +#define GUC_CTB_MSG_0_RESERVED (0xfu << 8) +#define GUC_CTB_MSG_0_NUM_DWORDS (0xffu << 0) /** * DOC: CTB HXG Message diff --git a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h index 47094b9b044c..0400bc0fccdc 100644 --- a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h +++ b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h @@ -31,9 +31,9 @@ */ #define GUC_KLV_LEN_MIN 1u -#define GUC_KLV_0_KEY (0xffff << 16) -#define GUC_KLV_0_LEN (0xffff << 0) -#define GUC_KLV_n_VALUE (0xffffffff << 0) +#define GUC_KLV_0_KEY (0xffffu << 16) +#define GUC_KLV_0_LEN (0xffffu << 0) +#define GUC_KLV_n_VALUE (0xffffffffu << 0) /** * DOC: GuC Self Config KLVs diff --git a/drivers/gpu/drm/xe/abi/guc_messages_abi.h b/drivers/gpu/drm/xe/abi/guc_messages_abi.h index 3d199016cf88..29e414c82d56 100644 --- a/drivers/gpu/drm/xe/abi/guc_messages_abi.h +++ b/drivers/gpu/drm/xe/abi/guc_messages_abi.h @@ -40,18 +40,18 @@ */ #define GUC_HXG_MSG_MIN_LEN 1u -#define GUC_HXG_MSG_0_ORIGIN (0x1 << 31) +#define GUC_HXG_MSG_0_ORIGIN (0x1u << 31) #define GUC_HXG_ORIGIN_HOST 0u #define GUC_HXG_ORIGIN_GUC 1u -#define GUC_HXG_MSG_0_TYPE (0x7 << 28) +#define GUC_HXG_MSG_0_TYPE (0x7u << 28) #define GUC_HXG_TYPE_REQUEST 0u #define GUC_HXG_TYPE_EVENT 1u #define GUC_HXG_TYPE_NO_RESPONSE_BUSY 3u #define GUC_HXG_TYPE_NO_RESPONSE_RETRY 5u #define GUC_HXG_TYPE_RESPONSE_FAILURE 6u #define GUC_HXG_TYPE_RESPONSE_SUCCESS 7u -#define GUC_HXG_MSG_0_AUX (0xfffffff << 0) -#define GUC_HXG_MSG_n_PAYLOAD (0xffffffff << 0) +#define GUC_HXG_MSG_0_AUX (0xfffffffu << 0) +#define GUC_HXG_MSG_n_PAYLOAD (0xffffffffu << 0) /** * DOC: HXG Request @@ -85,8 +85,8 @@ */ #define GUC_HXG_REQUEST_MSG_MIN_LEN GUC_HXG_MSG_MIN_LEN -#define GUC_HXG_REQUEST_MSG_0_DATA0 (0xfff << 16) -#define GUC_HXG_REQUEST_MSG_0_ACTION (0xffff << 0) +#define GUC_HXG_REQUEST_MSG_0_DATA0 (0xfffu << 16) +#define GUC_HXG_REQUEST_MSG_0_ACTION (0xffffu << 0) #define GUC_HXG_REQUEST_MSG_n_DATAn GUC_HXG_MSG_n_PAYLOAD /** @@ -117,8 +117,8 @@ */ #define GUC_HXG_EVENT_MSG_MIN_LEN GUC_HXG_MSG_MIN_LEN -#define GUC_HXG_EVENT_MSG_0_DATA0 (0xfff << 16) -#define GUC_HXG_EVENT_MSG_0_ACTION (0xffff << 0) +#define GUC_HXG_EVENT_MSG_0_DATA0 (0xfffu << 16) +#define GUC_HXG_EVENT_MSG_0_ACTION (0xffffu << 0) #define GUC_HXG_EVENT_MSG_n_DATAn GUC_HXG_MSG_n_PAYLOAD /** @@ -188,8 +188,8 @@ */ #define GUC_HXG_FAILURE_MSG_LEN GUC_HXG_MSG_MIN_LEN -#define GUC_HXG_FAILURE_MSG_0_HINT (0xfff << 16) -#define GUC_HXG_FAILURE_MSG_0_ERROR (0xffff << 0) +#define GUC_HXG_FAILURE_MSG_0_HINT (0xfffu << 16) +#define GUC_HXG_FAILURE_MSG_0_ERROR (0xffffu << 0) /** * DOC: HXG Response diff --git a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h index 5f19550cc845..68d9f6116bdf 100644 --- a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h +++ b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h @@ -35,12 +35,10 @@ static inline int i915_gem_object_read_from_page(struct xe_bo *bo, u32 ofs, u64 *ptr, u32 size) { struct ttm_bo_kmap_obj map; - void *virtual; + void *src; bool is_iomem; int ret; - XE_WARN_ON(size != 8); - ret = xe_bo_lock(bo, true); if (ret) return ret; @@ -50,11 +48,12 @@ static inline int i915_gem_object_read_from_page(struct xe_bo *bo, goto out_unlock; ofs &= ~PAGE_MASK; - virtual = ttm_kmap_obj_virtual(&map, &is_iomem); + src = ttm_kmap_obj_virtual(&map, &is_iomem); + src += ofs; if (is_iomem) - *ptr = readq((void __iomem *)(virtual + ofs)); + memcpy_fromio(ptr, (void __iomem *)src, size); else - *ptr = *(u64 *)(virtual + ofs); + memcpy(ptr, src, size); ttm_bo_kunmap(&map); out_unlock: diff --git a/drivers/gpu/drm/xe/tests/xe_bo.c b/drivers/gpu/drm/xe/tests/xe_bo.c index 412b2e7ce40c..3436fd9cf2b2 100644 --- a/drivers/gpu/drm/xe/tests/xe_bo.c +++ b/drivers/gpu/drm/xe/tests/xe_bo.c @@ -125,14 +125,13 @@ static void ccs_test_run_tile(struct xe_device *xe, struct xe_tile *tile, bo = xe_bo_create_user(xe, NULL, NULL, SZ_1M, DRM_XE_GEM_CPU_CACHING_WC, ttm_bo_type_device, bo_flags); - - xe_bo_lock(bo, false); - if (IS_ERR(bo)) { KUNIT_FAIL(test, "Failed to create bo.\n"); return; } + xe_bo_lock(bo, false); + kunit_info(test, "Verifying that CCS data is cleared on creation.\n"); ret = ccs_test_migrate(tile, bo, false, 0ULL, 0xdeadbeefdeadbeefULL, test); diff --git a/drivers/gpu/drm/xe/tests/xe_migrate.c b/drivers/gpu/drm/xe/tests/xe_migrate.c index 7a32faa2f688..a6523df0f1d3 100644 --- a/drivers/gpu/drm/xe/tests/xe_migrate.c +++ b/drivers/gpu/drm/xe/tests/xe_migrate.c @@ -331,7 +331,7 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test) xe_res_first_sg(xe_bo_sg(pt), 0, pt->size, &src_it); emit_pte(m, bb, NUM_KERNEL_PDE - 1, xe_bo_is_vram(pt), false, - &src_it, XE_PAGE_SIZE, pt); + &src_it, XE_PAGE_SIZE, pt->ttm.resource); run_sanity_job(m, xe, bb, bb->len, "Writing PTE for our fake PT", test); diff --git a/drivers/gpu/drm/xe/tests/xe_wa_test.c b/drivers/gpu/drm/xe/tests/xe_wa_test.c index a53c22a19582..b4715b78ef3b 100644 --- a/drivers/gpu/drm/xe/tests/xe_wa_test.c +++ b/drivers/gpu/drm/xe/tests/xe_wa_test.c @@ -74,9 +74,6 @@ static const struct platform_test_case cases[] = { SUBPLATFORM_CASE(DG2, G11, B1), SUBPLATFORM_CASE(DG2, G12, A0), SUBPLATFORM_CASE(DG2, G12, A1), - PLATFORM_CASE(PVC, B0), - PLATFORM_CASE(PVC, B1), - PLATFORM_CASE(PVC, C0), GMDID_CASE(METEORLAKE, 1270, A0, 1300, A0), GMDID_CASE(METEORLAKE, 1271, A0, 1300, A0), GMDID_CASE(LUNARLAKE, 2004, A0, 2000, A0), diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index 8e4a3b1f6b93..0b0e262e2166 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -125,9 +125,9 @@ static struct xe_mem_region *res_to_mem_region(struct ttm_resource *res) static void try_add_system(struct xe_device *xe, struct xe_bo *bo, u32 bo_flags, u32 *c) { - xe_assert(xe, *c < ARRAY_SIZE(bo->placements)); - if (bo_flags & XE_BO_CREATE_SYSTEM_BIT) { + xe_assert(xe, *c < ARRAY_SIZE(bo->placements)); + bo->placements[*c] = (struct ttm_place) { .mem_type = XE_PL_TT, }; @@ -145,6 +145,8 @@ static void add_vram(struct xe_device *xe, struct xe_bo *bo, struct xe_mem_region *vram; u64 io_size; + xe_assert(xe, *c < ARRAY_SIZE(bo->placements)); + vram = to_xe_ttm_vram_mgr(ttm_manager_type(&xe->ttm, mem_type))->vram; xe_assert(xe, vram && vram->usable_size); io_size = vram->io_size; @@ -175,8 +177,6 @@ static void add_vram(struct xe_device *xe, struct xe_bo *bo, static void try_add_vram(struct xe_device *xe, struct xe_bo *bo, u32 bo_flags, u32 *c) { - xe_assert(xe, *c < ARRAY_SIZE(bo->placements)); - if (bo->props.preferred_gt == XE_GT1) { if (bo_flags & XE_BO_CREATE_VRAM1_BIT) add_vram(xe, bo, bo->placements, bo_flags, XE_PL_VRAM1, c); @@ -193,9 +193,9 @@ static void try_add_vram(struct xe_device *xe, struct xe_bo *bo, static void try_add_stolen(struct xe_device *xe, struct xe_bo *bo, u32 bo_flags, u32 *c) { - xe_assert(xe, *c < ARRAY_SIZE(bo->placements)); - if (bo_flags & XE_BO_CREATE_STOLEN_BIT) { + xe_assert(xe, *c < ARRAY_SIZE(bo->placements)); + bo->placements[*c] = (struct ttm_place) { .mem_type = XE_PL_STOLEN, .flags = bo_flags & (XE_BO_CREATE_PINNED_BIT | @@ -442,7 +442,7 @@ static int xe_ttm_io_mem_reserve(struct ttm_device *bdev, if (vram->mapping && mem->placement & TTM_PL_FLAG_CONTIGUOUS) - mem->bus.addr = (u8 *)vram->mapping + + mem->bus.addr = (u8 __force *)vram->mapping + mem->bus.offset; mem->bus.offset += vram->io_start; @@ -734,7 +734,7 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict, /* Create a new VMAP once kernel BO back in VRAM */ if (!ret && resource_is_vram(new_mem)) { struct xe_mem_region *vram = res_to_mem_region(new_mem); - void *new_addr = vram->mapping + + void __iomem *new_addr = vram->mapping + (new_mem->start << PAGE_SHIFT); if (XE_WARN_ON(new_mem->start == XE_BO_INVALID_OFFSET)) { diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index d9ae77fe7382..1f0b4b9ce84f 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -484,7 +484,7 @@ int xe_device_probe(struct xe_device *xe) err = xe_device_set_has_flat_ccs(xe); if (err) - return err; + goto err_irq_shutdown; err = xe_mmio_probe_vram(xe); if (err) @@ -613,7 +613,7 @@ void xe_device_wmb(struct xe_device *xe) u32 xe_device_ccs_bytes(struct xe_device *xe, u64 size) { return xe_device_has_flat_ccs(xe) ? - DIV_ROUND_UP(size, NUM_BYTES_PER_CCS_BYTE(xe)) : 0; + DIV_ROUND_UP_ULL(size, NUM_BYTES_PER_CCS_BYTE(xe)) : 0; } bool xe_device_mem_access_ongoing(struct xe_device *xe) diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h index c45ef17b3473..5dc9127a2029 100644 --- a/drivers/gpu/drm/xe/xe_device_types.h +++ b/drivers/gpu/drm/xe/xe_device_types.h @@ -97,7 +97,7 @@ struct xe_mem_region { */ resource_size_t actual_physical_size; /** @mapping: pointer to VRAM mappable space */ - void *__iomem mapping; + void __iomem *mapping; }; /** @@ -146,7 +146,7 @@ struct xe_tile { size_t size; /** @regs: pointer to tile's MMIO space (starting with registers) */ - void *regs; + void __iomem *regs; } mmio; /** @@ -159,7 +159,7 @@ struct xe_tile { size_t size; /** @regs: pointer to tile's additional MMIO-extension space */ - void *regs; + void __iomem *regs; } mmio_ext; /** @mem: memory management info for tile */ @@ -301,7 +301,7 @@ struct xe_device { /** @size: size of MMIO space for device */ size_t size; /** @regs: pointer to MMIO space for device */ - void *regs; + void __iomem *regs; } mmio; /** @mem: memory info for device */ diff --git a/drivers/gpu/drm/xe/xe_display.c b/drivers/gpu/drm/xe/xe_display.c index 74391d9b11ae..e4db069f0db3 100644 --- a/drivers/gpu/drm/xe/xe_display.c +++ b/drivers/gpu/drm/xe/xe_display.c @@ -134,8 +134,6 @@ static void xe_display_fini_nommio(struct drm_device *dev, void *dummy) int xe_display_init_nommio(struct xe_device *xe) { - int err; - if (!xe->info.enable_display) return 0; @@ -145,10 +143,6 @@ int xe_display_init_nommio(struct xe_device *xe) /* This must be called before any calls to HAS_PCH_* */ intel_detect_pch(xe); - err = intel_power_domains_init(xe); - if (err) - return err; - return drmm_add_action_or_reset(&xe->drm, xe_display_fini_nommio, xe); } diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c index 64ed303728fd..da2627ed6ae7 100644 --- a/drivers/gpu/drm/xe/xe_dma_buf.c +++ b/drivers/gpu/drm/xe/xe_dma_buf.c @@ -175,7 +175,7 @@ static int xe_dma_buf_begin_cpu_access(struct dma_buf *dma_buf, return 0; } -const struct dma_buf_ops xe_dmabuf_ops = { +static const struct dma_buf_ops xe_dmabuf_ops = { .attach = xe_dma_buf_attach, .detach = xe_dma_buf_detach, .pin = xe_dma_buf_pin, diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c index d30c0d0689bc..17f26952e665 100644 --- a/drivers/gpu/drm/xe/xe_exec.c +++ b/drivers/gpu/drm/xe/xe_exec.c @@ -111,11 +111,11 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file) u64 addresses[XE_HW_ENGINE_MAX_INSTANCE]; struct drm_gpuvm_exec vm_exec = {.extra.fn = xe_exec_fn}; struct drm_exec *exec = &vm_exec.exec; - u32 i, num_syncs = 0; + u32 i, num_syncs = 0, num_ufence = 0; struct xe_sched_job *job; struct dma_fence *rebind_fence; struct xe_vm *vm; - bool write_locked; + bool write_locked, skip_retry = false; ktime_t end = 0; int err = 0; @@ -157,6 +157,14 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file) SYNC_PARSE_FLAG_LR_MODE : 0)); if (err) goto err_syncs; + + if (xe_sync_is_ufence(&syncs[i])) + num_ufence++; + } + + if (XE_IOCTL_DBG(xe, num_ufence > 1)) { + err = -EINVAL; + goto err_syncs; } if (xe_exec_queue_is_parallel(q)) { @@ -227,7 +235,8 @@ retry: } if (xe_exec_queue_is_lr(q) && xe_exec_queue_ring_full(q)) { - err = -EWOULDBLOCK; + err = -EWOULDBLOCK; /* Aliased to -EAGAIN */ + skip_retry = true; goto err_exec; } @@ -337,7 +346,7 @@ err_unlock_list: up_write(&vm->lock); else up_read(&vm->lock); - if (err == -EAGAIN) + if (err == -EAGAIN && !skip_retry) goto retry; err_syncs: for (i = 0; i < num_syncs; i++) diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c index 44fe8097b7cd..254b1d3af4cb 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.c +++ b/drivers/gpu/drm/xe/xe_exec_queue.c @@ -67,6 +67,11 @@ static struct xe_exec_queue *__xe_exec_queue_create(struct xe_device *xe, q->sched_props.timeslice_us = hwe->eclass->sched_props.timeslice_us; q->sched_props.preempt_timeout_us = hwe->eclass->sched_props.preempt_timeout_us; + if (q->flags & EXEC_QUEUE_FLAG_KERNEL && + q->flags & EXEC_QUEUE_FLAG_HIGH_PRIORITY) + q->sched_props.priority = XE_EXEC_QUEUE_PRIORITY_KERNEL; + else + q->sched_props.priority = XE_EXEC_QUEUE_PRIORITY_NORMAL; if (xe_exec_queue_is_parallel(q)) { q->parallel.composite_fence_ctx = dma_fence_context_alloc(1); @@ -921,20 +926,24 @@ void xe_exec_queue_last_fence_put_unlocked(struct xe_exec_queue *q) * @q: The exec queue * @vm: The VM the engine does a bind or exec for * - * Get last fence, does not take a ref + * Get last fence, takes a ref * * Returns: last fence if not signaled, dma fence stub if signaled */ struct dma_fence *xe_exec_queue_last_fence_get(struct xe_exec_queue *q, struct xe_vm *vm) { + struct dma_fence *fence; + xe_exec_queue_last_fence_lockdep_assert(q, vm); if (q->last_fence && test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &q->last_fence->flags)) xe_exec_queue_last_fence_put(q, vm); - return q->last_fence ? q->last_fence : dma_fence_get_stub(); + fence = q->last_fence ? q->last_fence : dma_fence_get_stub(); + dma_fence_get(fence); + return fence; } /** diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h index 3d7e704ec3d9..8d4b7feb8c30 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h @@ -52,8 +52,6 @@ struct xe_exec_queue { struct xe_vm *vm; /** @class: class of this exec queue */ enum xe_engine_class class; - /** @priority: priority of this exec queue */ - enum xe_exec_queue_priority priority; /** * @logical_mask: logical mask of where job submitted to exec queue can run */ @@ -84,6 +82,8 @@ struct xe_exec_queue { #define EXEC_QUEUE_FLAG_VM BIT(4) /* child of VM queue for multi-tile VM jobs */ #define EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD BIT(5) +/* kernel exec_queue only, set priority to highest level */ +#define EXEC_QUEUE_FLAG_HIGH_PRIORITY BIT(6) /** * @flags: flags for this exec queue, should statically setup aside from ban @@ -142,6 +142,8 @@ struct xe_exec_queue { u32 timeslice_us; /** @preempt_timeout_us: preemption timeout in micro-seconds */ u32 preempt_timeout_us; + /** @priority: priority of this exec queue */ + enum xe_exec_queue_priority priority; } sched_props; /** @compute: compute exec queue state */ diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c index 3af2adec1295..35474ddbaf97 100644 --- a/drivers/gpu/drm/xe/xe_gt.c +++ b/drivers/gpu/drm/xe/xe_gt.c @@ -437,7 +437,10 @@ static int all_fw_domain_init(struct xe_gt *gt) * USM has its only SA pool to non-block behind user operations */ if (gt_to_xe(gt)->info.has_usm) { - gt->usm.bb_pool = xe_sa_bo_manager_init(gt_to_tile(gt), SZ_1M, 16); + struct xe_device *xe = gt_to_xe(gt); + + gt->usm.bb_pool = xe_sa_bo_manager_init(gt_to_tile(gt), + IS_DGFX(xe) ? SZ_1M : SZ_512K, 16); if (IS_ERR(gt->usm.bb_pool)) { err = PTR_ERR(gt->usm.bb_pool); goto err_force_wake; diff --git a/drivers/gpu/drm/xe/xe_gt_freq.c b/drivers/gpu/drm/xe/xe_gt_freq.c index 3adfa6686e7c..e5b0f4ecdbe8 100644 --- a/drivers/gpu/drm/xe/xe_gt_freq.c +++ b/drivers/gpu/drm/xe/xe_gt_freq.c @@ -196,6 +196,9 @@ void xe_gt_freq_init(struct xe_gt *gt) struct xe_device *xe = gt_to_xe(gt); int err; + if (xe->info.skip_guc_pc) + return; + gt->freq = kobject_create_and_add("freq0", gt->sysfs); if (!gt->freq) { drm_warn(&xe->drm, "failed to add freq0 directory to %s\n", diff --git a/drivers/gpu/drm/xe/xe_gt_mcr.c b/drivers/gpu/drm/xe/xe_gt_mcr.c index 77925b35cf8d..8546cd3cc50d 100644 --- a/drivers/gpu/drm/xe/xe_gt_mcr.c +++ b/drivers/gpu/drm/xe/xe_gt_mcr.c @@ -480,7 +480,7 @@ static bool xe_gt_mcr_get_nonterminated_steering(struct xe_gt *gt, * to synchronize with external clients (e.g., firmware), so a semaphore * register will also need to be taken. */ -static void mcr_lock(struct xe_gt *gt) +static void mcr_lock(struct xe_gt *gt) __acquires(>->mcr_lock) { struct xe_device *xe = gt_to_xe(gt); int ret = 0; @@ -500,7 +500,7 @@ static void mcr_lock(struct xe_gt *gt) drm_WARN_ON_ONCE(&xe->drm, ret == -ETIMEDOUT); } -static void mcr_unlock(struct xe_gt *gt) +static void mcr_unlock(struct xe_gt *gt) __releases(>->mcr_lock) { /* Release hardware semaphore - this is done by writing 1 to the register */ if (GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270) diff --git a/drivers/gpu/drm/xe/xe_gt_pagefault.c b/drivers/gpu/drm/xe/xe_gt_pagefault.c index 59a70d2e0a7a..73f08f1924df 100644 --- a/drivers/gpu/drm/xe/xe_gt_pagefault.c +++ b/drivers/gpu/drm/xe/xe_gt_pagefault.c @@ -165,7 +165,8 @@ retry_userptr: goto unlock_vm; } - if (!xe_vma_is_userptr(vma) || !xe_vma_userptr_check_repin(vma)) { + if (!xe_vma_is_userptr(vma) || + !xe_vma_userptr_check_repin(to_userptr_vma(vma))) { downgrade_write(&vm->lock); write_locked = false; } @@ -181,11 +182,13 @@ retry_userptr: /* TODO: Validate fault */ if (xe_vma_is_userptr(vma) && write_locked) { + struct xe_userptr_vma *uvma = to_userptr_vma(vma); + spin_lock(&vm->userptr.invalidated_lock); - list_del_init(&vma->userptr.invalidate_link); + list_del_init(&uvma->userptr.invalidate_link); spin_unlock(&vm->userptr.invalidated_lock); - ret = xe_vma_userptr_pin_pages(vma); + ret = xe_vma_userptr_pin_pages(uvma); if (ret) goto unlock_vm; @@ -220,7 +223,7 @@ retry_userptr: dma_fence_put(fence); if (xe_vma_is_userptr(vma)) - ret = xe_vma_userptr_check_repin(vma); + ret = xe_vma_userptr_check_repin(to_userptr_vma(vma)); vma->usm.tile_invalidated &= ~BIT(tile->id); unlock_dma_resv: @@ -332,7 +335,7 @@ int xe_guc_pagefault_handler(struct xe_guc *guc, u32 *msg, u32 len) return -EPROTO; asid = FIELD_GET(PFD_ASID, msg[1]); - pf_queue = >->usm.pf_queue[asid % NUM_PF_QUEUE]; + pf_queue = gt->usm.pf_queue + (asid % NUM_PF_QUEUE); spin_lock_irqsave(&pf_queue->lock, flags); full = pf_queue_full(pf_queue); diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index 482cb0df9f15..0a61390c64a7 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -60,7 +60,12 @@ static u32 guc_ctl_debug_flags(struct xe_guc *guc) static u32 guc_ctl_feature_flags(struct xe_guc *guc) { - return GUC_CTL_ENABLE_SLPC; + u32 flags = 0; + + if (!guc_to_xe(guc)->info.skip_guc_pc) + flags |= GUC_CTL_ENABLE_SLPC; + + return flags; } static u32 guc_ctl_log_params_flags(struct xe_guc *guc) diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c index f71085228cb3..d91702592520 100644 --- a/drivers/gpu/drm/xe/xe_guc_pc.c +++ b/drivers/gpu/drm/xe/xe_guc_pc.c @@ -963,7 +963,9 @@ void xe_guc_pc_fini(struct xe_guc_pc *pc) struct xe_device *xe = pc_to_xe(pc); if (xe->info.skip_guc_pc) { + xe_device_mem_access_get(xe); xe_gt_idle_disable_c6(pc_to_gt(pc)); + xe_device_mem_access_put(xe); return; } diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 21ac68e3246f..54ffcfcdd41f 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -421,7 +421,7 @@ static void init_policies(struct xe_guc *guc, struct xe_exec_queue *q) { struct exec_queue_policy policy; struct xe_device *xe = guc_to_xe(guc); - enum xe_exec_queue_priority prio = q->priority; + enum xe_exec_queue_priority prio = q->sched_props.priority; u32 timeslice_us = q->sched_props.timeslice_us; u32 preempt_timeout_us = q->sched_props.preempt_timeout_us; @@ -1231,7 +1231,6 @@ static int guc_exec_queue_init(struct xe_exec_queue *q) err = xe_sched_entity_init(&ge->entity, sched); if (err) goto err_sched; - q->priority = XE_EXEC_QUEUE_PRIORITY_NORMAL; if (xe_exec_queue_is_lr(q)) INIT_WORK(&q->guc->lr_tdr, xe_guc_exec_queue_lr_cleanup); @@ -1301,15 +1300,15 @@ static int guc_exec_queue_set_priority(struct xe_exec_queue *q, { struct xe_sched_msg *msg; - if (q->priority == priority || exec_queue_killed_or_banned(q)) + if (q->sched_props.priority == priority || exec_queue_killed_or_banned(q)) return 0; msg = kmalloc(sizeof(*msg), GFP_KERNEL); if (!msg) return -ENOMEM; + q->sched_props.priority = priority; guc_exec_queue_add_msg(q, msg, SET_SCHED_PROPS); - q->priority = priority; return 0; } diff --git a/drivers/gpu/drm/xe/xe_hw_fence.c b/drivers/gpu/drm/xe/xe_hw_fence.c index a6094c81f2ad..a5de3e7b0bd6 100644 --- a/drivers/gpu/drm/xe/xe_hw_fence.c +++ b/drivers/gpu/drm/xe/xe_hw_fence.c @@ -217,13 +217,13 @@ struct xe_hw_fence *xe_hw_fence_create(struct xe_hw_fence_ctx *ctx, if (!fence) return ERR_PTR(-ENOMEM); - dma_fence_init(&fence->dma, &xe_hw_fence_ops, &ctx->irq->lock, - ctx->dma_fence_ctx, ctx->next_seqno++); - fence->ctx = ctx; fence->seqno_map = seqno_map; INIT_LIST_HEAD(&fence->irq_link); + dma_fence_init(&fence->dma, &xe_hw_fence_ops, &ctx->irq->lock, + ctx->dma_fence_ctx, ctx->next_seqno++); + trace_xe_hw_fence_create(fence); return fence; diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c index 6ef2aa1eae8b..174ed2185481 100644 --- a/drivers/gpu/drm/xe/xe_hwmon.c +++ b/drivers/gpu/drm/xe/xe_hwmon.c @@ -419,7 +419,7 @@ static int xe_hwmon_pcode_read_i1(struct xe_gt *gt, u32 *uval) return xe_pcode_read(gt, PCODE_MBOX(PCODE_POWER_SETUP, POWER_SETUP_SUBCOMMAND_READ_I1, 0), - uval, 0); + uval, NULL); } static int xe_hwmon_pcode_write_i1(struct xe_gt *gt, u32 uval) diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index b7fa3831b684..0ec5ad2539f1 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -21,10 +21,10 @@ #include "xe_map.h" #include "xe_vm.h" -#define CTX_VALID (1 << 0) -#define CTX_PRIVILEGE (1 << 8) -#define CTX_ADDRESSING_MODE_SHIFT 3 -#define LEGACY_64B_CONTEXT 3 +#define LRC_VALID (1 << 0) +#define LRC_PRIVILEGE (1 << 8) +#define LRC_ADDRESSING_MODE_SHIFT 3 +#define LRC_LEGACY_64B_CONTEXT 3 #define ENGINE_CLASS_SHIFT 61 #define ENGINE_INSTANCE_SHIFT 48 @@ -762,15 +762,15 @@ int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, (q->usm.acc_notify << ACC_NOTIFY_S) | q->usm.acc_trigger); - lrc->desc = CTX_VALID; - lrc->desc |= LEGACY_64B_CONTEXT << CTX_ADDRESSING_MODE_SHIFT; + lrc->desc = LRC_VALID; + lrc->desc |= LRC_LEGACY_64B_CONTEXT << LRC_ADDRESSING_MODE_SHIFT; /* TODO: Priority */ /* While this appears to have something about privileged batches or * some such, it really just means PPGTT mode. */ if (vm) - lrc->desc |= CTX_PRIVILEGE; + lrc->desc |= LRC_PRIVILEGE; if (GRAPHICS_VERx100(xe) < 1250) { lrc->desc |= (u64)hwe->instance << ENGINE_INSTANCE_SHIFT; diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index adf1dab5eba2..70480c305602 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -62,6 +62,8 @@ struct xe_migrate { * out of the pt_bo. */ struct drm_suballoc_manager vm_update_sa; + /** @min_chunk_size: For dgfx, Minimum chunk size */ + u64 min_chunk_size; }; #define MAX_PREEMPTDISABLE_TRANSFER SZ_8M /* Around 1ms. */ @@ -168,11 +170,6 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m, if (!IS_DGFX(xe)) { /* Write out batch too */ m->batch_base_ofs = NUM_PT_SLOTS * XE_PAGE_SIZE; - if (xe->info.has_usm) { - batch = tile->primary_gt->usm.bb_pool->bo; - m->usm_batch_base_ofs = m->batch_base_ofs; - } - for (i = 0; i < batch->size; i += vm->flags & XE_VM_FLAG_64K ? XE_64K_PAGE_SIZE : XE_PAGE_SIZE) { @@ -183,6 +180,24 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m, entry); level++; } + if (xe->info.has_usm) { + xe_tile_assert(tile, batch->size == SZ_1M); + + batch = tile->primary_gt->usm.bb_pool->bo; + m->usm_batch_base_ofs = m->batch_base_ofs + SZ_1M; + xe_tile_assert(tile, batch->size == SZ_512K); + + for (i = 0; i < batch->size; + i += vm->flags & XE_VM_FLAG_64K ? XE_64K_PAGE_SIZE : + XE_PAGE_SIZE) { + entry = vm->pt_ops->pte_encode_bo(batch, i, + pat_index, 0); + + xe_map_wr(xe, &bo->vmap, map_ofs + level * 8, u64, + entry); + level++; + } + } } else { u64 batch_addr = xe_bo_addr(batch, 0, XE_PAGE_SIZE); @@ -344,7 +359,8 @@ struct xe_migrate *xe_migrate_init(struct xe_tile *tile) m->q = xe_exec_queue_create(xe, vm, logical_mask, 1, hwe, EXEC_QUEUE_FLAG_KERNEL | - EXEC_QUEUE_FLAG_PERMANENT); + EXEC_QUEUE_FLAG_PERMANENT | + EXEC_QUEUE_FLAG_HIGH_PRIORITY); } else { m->q = xe_exec_queue_create_class(xe, primary_gt, vm, XE_ENGINE_CLASS_COPY, @@ -355,8 +371,6 @@ struct xe_migrate *xe_migrate_init(struct xe_tile *tile) xe_vm_close_and_put(vm); return ERR_CAST(m->q); } - if (xe->info.has_usm) - m->q->priority = XE_EXEC_QUEUE_PRIORITY_KERNEL; mutex_init(&m->job_mutex); @@ -364,6 +378,19 @@ struct xe_migrate *xe_migrate_init(struct xe_tile *tile) if (err) return ERR_PTR(err); + if (IS_DGFX(xe)) { + if (xe_device_has_flat_ccs(xe)) + /* min chunk size corresponds to 4K of CCS Metadata */ + m->min_chunk_size = SZ_4K * SZ_64K / + xe_device_ccs_bytes(xe, SZ_64K); + else + /* Somewhat arbitrary to avoid a huge amount of blits */ + m->min_chunk_size = SZ_64K; + m->min_chunk_size = roundup_pow_of_two(m->min_chunk_size); + drm_dbg(&xe->drm, "Migrate min chunk size is 0x%08llx\n", + (unsigned long long)m->min_chunk_size); + } + return m; } @@ -375,16 +402,35 @@ static u64 max_mem_transfer_per_pass(struct xe_device *xe) return MAX_PREEMPTDISABLE_TRANSFER; } -static u64 xe_migrate_res_sizes(struct xe_device *xe, struct xe_res_cursor *cur) +static u64 xe_migrate_res_sizes(struct xe_migrate *m, struct xe_res_cursor *cur) { - /* - * For VRAM we use identity mapped pages so we are limited to current - * cursor size. For system we program the pages ourselves so we have no - * such limitation. - */ - return min_t(u64, max_mem_transfer_per_pass(xe), - mem_type_is_vram(cur->mem_type) ? cur->size : - cur->remaining); + struct xe_device *xe = tile_to_xe(m->tile); + u64 size = min_t(u64, max_mem_transfer_per_pass(xe), cur->remaining); + + if (mem_type_is_vram(cur->mem_type)) { + /* + * VRAM we want to blit in chunks with sizes aligned to + * min_chunk_size in order for the offset to CCS metadata to be + * page-aligned. If it's the last chunk it may be smaller. + * + * Another constraint is that we need to limit the blit to + * the VRAM block size, unless size is smaller than + * min_chunk_size. + */ + u64 chunk = max_t(u64, cur->size, m->min_chunk_size); + + size = min_t(u64, size, chunk); + if (size > m->min_chunk_size) + size = round_down(size, m->min_chunk_size); + } + + return size; +} + +static bool xe_migrate_allow_identity(u64 size, const struct xe_res_cursor *cur) +{ + /* If the chunk is not fragmented, allow identity map. */ + return cur->size >= size; } static u32 pte_update_size(struct xe_migrate *m, @@ -397,7 +443,12 @@ static u32 pte_update_size(struct xe_migrate *m, u32 cmds = 0; *L0_pt = pt_ofs; - if (!is_vram) { + if (is_vram && xe_migrate_allow_identity(*L0, cur)) { + /* Offset into identity map. */ + *L0_ofs = xe_migrate_vram_ofs(tile_to_xe(m->tile), + cur->start + vram_region_gpu_offset(res)); + cmds += cmd_size; + } else { /* Clip L0 to available size */ u64 size = min(*L0, (u64)avail_pts * SZ_2M); u64 num_4k_pages = DIV_ROUND_UP(size, XE_PAGE_SIZE); @@ -413,11 +464,6 @@ static u32 pte_update_size(struct xe_migrate *m, /* Each chunk has a single blit command */ cmds += cmd_size; - } else { - /* Offset into identity map. */ - *L0_ofs = xe_migrate_vram_ofs(tile_to_xe(m->tile), - cur->start + vram_region_gpu_offset(res)); - cmds += cmd_size; } return cmds; @@ -427,10 +473,10 @@ static void emit_pte(struct xe_migrate *m, struct xe_bb *bb, u32 at_pt, bool is_vram, bool is_comp_pte, struct xe_res_cursor *cur, - u32 size, struct xe_bo *bo) + u32 size, struct ttm_resource *res) { struct xe_device *xe = tile_to_xe(m->tile); - + struct xe_vm *vm = m->q->vm; u16 pat_index; u32 ptes; u64 ofs = at_pt * XE_PAGE_SIZE; @@ -439,17 +485,10 @@ static void emit_pte(struct xe_migrate *m, /* Indirect access needs compression enabled uncached PAT index */ if (GRAPHICS_VERx100(xe) >= 2000) pat_index = is_comp_pte ? xe->pat.idx[XE_CACHE_NONE_COMPRESSION] : - xe->pat.idx[XE_CACHE_NONE]; + xe->pat.idx[XE_CACHE_WB]; else pat_index = xe->pat.idx[XE_CACHE_WB]; - /* - * FIXME: Emitting VRAM PTEs to L0 PTs is forbidden. Currently - * we're only emitting VRAM PTEs during sanity tests, so when - * that's moved to a Kunit test, we should condition VRAM PTEs - * on running tests. - */ - ptes = DIV_ROUND_UP(size, XE_PAGE_SIZE); while (ptes) { @@ -469,20 +508,22 @@ static void emit_pte(struct xe_migrate *m, addr = xe_res_dma(cur) & PAGE_MASK; if (is_vram) { - /* Is this a 64K PTE entry? */ - if ((m->q->vm->flags & XE_VM_FLAG_64K) && - !(cur_ofs & (16 * 8 - 1))) { - xe_tile_assert(m->tile, IS_ALIGNED(addr, SZ_64K)); + if (vm->flags & XE_VM_FLAG_64K) { + u64 va = cur_ofs * XE_PAGE_SIZE / 8; + + xe_assert(xe, (va & (SZ_64K - 1)) == + (addr & (SZ_64K - 1))); + flags |= XE_PTE_PS64; } - addr += vram_region_gpu_offset(bo->ttm.resource); + addr += vram_region_gpu_offset(res); devmem = true; } - addr = m->q->vm->pt_ops->pte_encode_addr(m->tile->xe, - addr, pat_index, - 0, devmem, flags); + addr = vm->pt_ops->pte_encode_addr(m->tile->xe, + addr, pat_index, + 0, devmem, flags); bb->cs[bb->len++] = lower_32_bits(addr); bb->cs[bb->len++] = upper_32_bits(addr); @@ -694,8 +735,8 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m, bool usm = xe->info.has_usm; u32 avail_pts = max_mem_transfer_per_pass(xe) / LEVEL0_PAGE_TABLE_ENCODE_SIZE; - src_L0 = xe_migrate_res_sizes(xe, &src_it); - dst_L0 = xe_migrate_res_sizes(xe, &dst_it); + src_L0 = xe_migrate_res_sizes(m, &src_it); + dst_L0 = xe_migrate_res_sizes(m, &dst_it); drm_dbg(&xe->drm, "Pass %u, sizes: %llu & %llu\n", pass++, src_L0, dst_L0); @@ -716,6 +757,7 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m, &ccs_ofs, &ccs_pt, 0, 2 * avail_pts, avail_pts); + xe_assert(xe, IS_ALIGNED(ccs_it.start, PAGE_SIZE)); } /* Add copy commands size here */ @@ -728,20 +770,20 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m, goto err_sync; } - if (!src_is_vram) - emit_pte(m, bb, src_L0_pt, src_is_vram, true, &src_it, src_L0, - src_bo); - else + if (src_is_vram && xe_migrate_allow_identity(src_L0, &src_it)) xe_res_next(&src_it, src_L0); - - if (!dst_is_vram) - emit_pte(m, bb, dst_L0_pt, dst_is_vram, true, &dst_it, src_L0, - dst_bo); else + emit_pte(m, bb, src_L0_pt, src_is_vram, copy_system_ccs, + &src_it, src_L0, src); + + if (dst_is_vram && xe_migrate_allow_identity(src_L0, &dst_it)) xe_res_next(&dst_it, src_L0); + else + emit_pte(m, bb, dst_L0_pt, dst_is_vram, copy_system_ccs, + &dst_it, src_L0, dst); if (copy_system_ccs) - emit_pte(m, bb, ccs_pt, false, false, &ccs_it, ccs_size, src_bo); + emit_pte(m, bb, ccs_pt, false, false, &ccs_it, ccs_size, src); bb->cs[bb->len++] = MI_BATCH_BUFFER_END; update_idx = bb->len; @@ -950,7 +992,7 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m, bool usm = xe->info.has_usm; u32 avail_pts = max_mem_transfer_per_pass(xe) / LEVEL0_PAGE_TABLE_ENCODE_SIZE; - clear_L0 = xe_migrate_res_sizes(xe, &src_it); + clear_L0 = xe_migrate_res_sizes(m, &src_it); drm_dbg(&xe->drm, "Pass %u, size: %llu\n", pass++, clear_L0); @@ -977,12 +1019,12 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m, size -= clear_L0; /* Preemption is enabled again by the ring ops. */ - if (!clear_vram) { - emit_pte(m, bb, clear_L0_pt, clear_vram, true, &src_it, clear_L0, - bo); - } else { + if (clear_vram && xe_migrate_allow_identity(clear_L0, &src_it)) xe_res_next(&src_it, clear_L0); - } + else + emit_pte(m, bb, clear_L0_pt, clear_vram, clear_system_ccs, + &src_it, clear_L0, dst); + bb->cs[bb->len++] = MI_BATCH_BUFFER_END; update_idx = bb->len; @@ -1175,8 +1217,11 @@ static bool no_in_syncs(struct xe_vm *vm, struct xe_exec_queue *q, } if (q) { fence = xe_exec_queue_last_fence_get(q, vm); - if (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) + if (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) { + dma_fence_put(fence); return false; + } + dma_fence_put(fence); } return true; diff --git a/drivers/gpu/drm/xe/xe_mmio.c b/drivers/gpu/drm/xe/xe_mmio.c index f660cfb79f50..5f6b53ea5528 100644 --- a/drivers/gpu/drm/xe/xe_mmio.c +++ b/drivers/gpu/drm/xe/xe_mmio.c @@ -272,8 +272,8 @@ int xe_mmio_probe_vram(struct xe_device *xe) drm_info(&xe->drm, "VRAM[%u, %u]: Actual physical size %pa, usable size exclude stolen %pa, CPU accessible size %pa\n", id, tile->id, &tile->mem.vram.actual_physical_size, &tile->mem.vram.usable_size, &tile->mem.vram.io_size); drm_info(&xe->drm, "VRAM[%u, %u]: DPA range: [%pa-%llx], io range: [%pa-%llx]\n", id, tile->id, - &tile->mem.vram.dpa_base, tile->mem.vram.dpa_base + tile->mem.vram.actual_physical_size, - &tile->mem.vram.io_start, tile->mem.vram.io_start + tile->mem.vram.io_size); + &tile->mem.vram.dpa_base, tile->mem.vram.dpa_base + (u64)tile->mem.vram.actual_physical_size, + &tile->mem.vram.io_start, tile->mem.vram.io_start + (u64)tile->mem.vram.io_size); /* calculate total size using tile size to get the correct HW sizing */ total_size += tile_size; @@ -303,7 +303,7 @@ void xe_mmio_probe_tiles(struct xe_device *xe) u8 id, tile_count = xe->info.tile_count; struct xe_gt *gt = xe_root_mmio_gt(xe); struct xe_tile *tile; - void *regs; + void __iomem *regs; u32 mtcfg; if (tile_count == 1) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index de1030a47588..e45b37c3f0c2 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -618,8 +618,8 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, if (!xe_vma_is_null(vma)) { if (xe_vma_is_userptr(vma)) - xe_res_first_sg(vma->userptr.sg, 0, xe_vma_size(vma), - &curs); + xe_res_first_sg(to_userptr_vma(vma)->userptr.sg, 0, + xe_vma_size(vma), &curs); else if (xe_bo_is_vram(bo) || xe_bo_is_stolen(bo)) xe_res_first(bo->ttm.resource, xe_vma_bo_offset(vma), xe_vma_size(vma), &curs); @@ -906,17 +906,17 @@ static void xe_vm_dbg_print_entries(struct xe_device *xe, #ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT -static int xe_pt_userptr_inject_eagain(struct xe_vma *vma) +static int xe_pt_userptr_inject_eagain(struct xe_userptr_vma *uvma) { - u32 divisor = vma->userptr.divisor ? vma->userptr.divisor : 2; + u32 divisor = uvma->userptr.divisor ? uvma->userptr.divisor : 2; static u32 count; if (count++ % divisor == divisor - 1) { - struct xe_vm *vm = xe_vma_vm(vma); + struct xe_vm *vm = xe_vma_vm(&uvma->vma); - vma->userptr.divisor = divisor << 1; + uvma->userptr.divisor = divisor << 1; spin_lock(&vm->userptr.invalidated_lock); - list_move_tail(&vma->userptr.invalidate_link, + list_move_tail(&uvma->userptr.invalidate_link, &vm->userptr.invalidated); spin_unlock(&vm->userptr.invalidated_lock); return true; @@ -927,7 +927,7 @@ static int xe_pt_userptr_inject_eagain(struct xe_vma *vma) #else -static bool xe_pt_userptr_inject_eagain(struct xe_vma *vma) +static bool xe_pt_userptr_inject_eagain(struct xe_userptr_vma *uvma) { return false; } @@ -1000,9 +1000,9 @@ static int xe_pt_userptr_pre_commit(struct xe_migrate_pt_update *pt_update) { struct xe_pt_migrate_pt_update *userptr_update = container_of(pt_update, typeof(*userptr_update), base); - struct xe_vma *vma = pt_update->vma; - unsigned long notifier_seq = vma->userptr.notifier_seq; - struct xe_vm *vm = xe_vma_vm(vma); + struct xe_userptr_vma *uvma = to_userptr_vma(pt_update->vma); + unsigned long notifier_seq = uvma->userptr.notifier_seq; + struct xe_vm *vm = xe_vma_vm(&uvma->vma); int err = xe_pt_vm_dependencies(pt_update->job, &vm->rftree[pt_update->tile_id], pt_update->start, @@ -1023,7 +1023,7 @@ static int xe_pt_userptr_pre_commit(struct xe_migrate_pt_update *pt_update) */ do { down_read(&vm->userptr.notifier_lock); - if (!mmu_interval_read_retry(&vma->userptr.notifier, + if (!mmu_interval_read_retry(&uvma->userptr.notifier, notifier_seq)) break; @@ -1032,11 +1032,11 @@ static int xe_pt_userptr_pre_commit(struct xe_migrate_pt_update *pt_update) if (userptr_update->bind) return -EAGAIN; - notifier_seq = mmu_interval_read_begin(&vma->userptr.notifier); + notifier_seq = mmu_interval_read_begin(&uvma->userptr.notifier); } while (true); /* Inject errors to test_whether they are handled correctly */ - if (userptr_update->bind && xe_pt_userptr_inject_eagain(vma)) { + if (userptr_update->bind && xe_pt_userptr_inject_eagain(uvma)) { up_read(&vm->userptr.notifier_lock); return -EAGAIN; } @@ -1297,7 +1297,7 @@ __xe_pt_bind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_exec_queue vma->tile_present |= BIT(tile->id); if (bind_pt_update.locked) { - vma->userptr.initial_bind = true; + to_userptr_vma(vma)->userptr.initial_bind = true; up_read(&vm->userptr.notifier_lock); xe_bo_put_commit(&deferred); } @@ -1642,7 +1642,7 @@ __xe_pt_unbind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_exec_queu if (!vma->tile_present) { spin_lock(&vm->userptr.invalidated_lock); - list_del_init(&vma->userptr.invalidate_link); + list_del_init(&to_userptr_vma(vma)->userptr.invalidate_link); spin_unlock(&vm->userptr.invalidated_lock); } up_read(&vm->userptr.notifier_lock); diff --git a/drivers/gpu/drm/xe/xe_query.c b/drivers/gpu/drm/xe/xe_query.c index 9b35673b286c..7e924faeeea0 100644 --- a/drivers/gpu/drm/xe/xe_query.c +++ b/drivers/gpu/drm/xe/xe_query.c @@ -459,21 +459,21 @@ static size_t calc_topo_query_size(struct xe_device *xe) sizeof_field(struct xe_gt, fuse_topo.eu_mask_per_dss)); } -static void __user *copy_mask(void __user *ptr, - struct drm_xe_query_topology_mask *topo, - void *mask, size_t mask_size) +static int copy_mask(void __user **ptr, + struct drm_xe_query_topology_mask *topo, + void *mask, size_t mask_size) { topo->num_bytes = mask_size; - if (copy_to_user(ptr, topo, sizeof(*topo))) - return ERR_PTR(-EFAULT); - ptr += sizeof(topo); + if (copy_to_user(*ptr, topo, sizeof(*topo))) + return -EFAULT; + *ptr += sizeof(topo); - if (copy_to_user(ptr, mask, mask_size)) - return ERR_PTR(-EFAULT); - ptr += mask_size; + if (copy_to_user(*ptr, mask, mask_size)) + return -EFAULT; + *ptr += mask_size; - return ptr; + return 0; } static int query_gt_topology(struct xe_device *xe, @@ -493,28 +493,28 @@ static int query_gt_topology(struct xe_device *xe, } for_each_gt(gt, xe, id) { + int err; + topo.gt_id = id; topo.type = DRM_XE_TOPO_DSS_GEOMETRY; - query_ptr = copy_mask(query_ptr, &topo, - gt->fuse_topo.g_dss_mask, - sizeof(gt->fuse_topo.g_dss_mask)); - if (IS_ERR(query_ptr)) - return PTR_ERR(query_ptr); + err = copy_mask(&query_ptr, &topo, gt->fuse_topo.g_dss_mask, + sizeof(gt->fuse_topo.g_dss_mask)); + if (err) + return err; topo.type = DRM_XE_TOPO_DSS_COMPUTE; - query_ptr = copy_mask(query_ptr, &topo, - gt->fuse_topo.c_dss_mask, - sizeof(gt->fuse_topo.c_dss_mask)); - if (IS_ERR(query_ptr)) - return PTR_ERR(query_ptr); + err = copy_mask(&query_ptr, &topo, gt->fuse_topo.c_dss_mask, + sizeof(gt->fuse_topo.c_dss_mask)); + if (err) + return err; topo.type = DRM_XE_TOPO_EU_PER_DSS; - query_ptr = copy_mask(query_ptr, &topo, - gt->fuse_topo.eu_mask_per_dss, - sizeof(gt->fuse_topo.eu_mask_per_dss)); - if (IS_ERR(query_ptr)) - return PTR_ERR(query_ptr); + err = copy_mask(&query_ptr, &topo, + gt->fuse_topo.eu_mask_per_dss, + sizeof(gt->fuse_topo.eu_mask_per_dss)); + if (err) + return err; } return 0; diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c index 01106a1156ad..4e2ccad0e52f 100644 --- a/drivers/gpu/drm/xe/xe_sched_job.c +++ b/drivers/gpu/drm/xe/xe_sched_job.c @@ -274,7 +274,6 @@ int xe_sched_job_last_fence_add_dep(struct xe_sched_job *job, struct xe_vm *vm) struct dma_fence *fence; fence = xe_exec_queue_last_fence_get(job->q, vm); - dma_fence_get(fence); return drm_sched_job_add_dependency(&job->drm, fence); } diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c index e4c220cf9115..aab92bee1d7c 100644 --- a/drivers/gpu/drm/xe/xe_sync.c +++ b/drivers/gpu/drm/xe/xe_sync.c @@ -307,7 +307,6 @@ xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync, /* Easy case... */ if (!num_in_fence) { fence = xe_exec_queue_last_fence_get(q, vm); - dma_fence_get(fence); return fence; } @@ -322,7 +321,6 @@ xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync, } } fences[current_fence++] = xe_exec_queue_last_fence_get(q, vm); - dma_fence_get(fences[current_fence - 1]); cf = dma_fence_array_create(num_in_fence, fences, vm->composite_fence_ctx, vm->composite_fence_seqno++, diff --git a/drivers/gpu/drm/xe/xe_sync.h b/drivers/gpu/drm/xe/xe_sync.h index d284afbe917c..f43cdcaca6c5 100644 --- a/drivers/gpu/drm/xe/xe_sync.h +++ b/drivers/gpu/drm/xe/xe_sync.h @@ -33,4 +33,9 @@ struct dma_fence * xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync, struct xe_exec_queue *q, struct xe_vm *vm); +static inline bool xe_sync_is_ufence(struct xe_sync_entry *sync) +{ + return !!sync->ufence; +} + #endif diff --git a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c index d2b00d0bf1e2..e5d7d5e2bec1 100644 --- a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c +++ b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c @@ -31,7 +31,7 @@ struct xe_ttm_stolen_mgr { /* GPU base offset */ resource_size_t stolen_base; - void *__iomem mapping; + void __iomem *mapping; }; static inline struct xe_ttm_stolen_mgr * @@ -275,7 +275,7 @@ static int __xe_ttm_stolen_io_mem_reserve_bar2(struct xe_device *xe, drm_WARN_ON(&xe->drm, !(mem->placement & TTM_PL_FLAG_CONTIGUOUS)); if (mem->placement & TTM_PL_FLAG_CONTIGUOUS && mgr->mapping) - mem->bus.addr = (u8 *)mgr->mapping + mem->bus.offset; + mem->bus.addr = (u8 __force *)mgr->mapping + mem->bus.offset; mem->bus.offset += mgr->io_base; mem->bus.is_iomem = true; diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 0cfe7289b97e..865e10d0a06a 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -37,8 +37,6 @@ #include "generated/xe_wa_oob.h" #include "xe_wa.h" -#define TEST_VM_ASYNC_OPS_ERROR - static struct drm_gem_object *xe_vm_obj(struct xe_vm *vm) { return vm->gpuvm.r_obj; @@ -46,7 +44,7 @@ static struct drm_gem_object *xe_vm_obj(struct xe_vm *vm) /** * xe_vma_userptr_check_repin() - Advisory check for repin needed - * @vma: The userptr vma + * @uvma: The userptr vma * * Check if the userptr vma has been invalidated since last successful * repin. The check is advisory only and can the function can be called @@ -56,15 +54,17 @@ static struct drm_gem_object *xe_vm_obj(struct xe_vm *vm) * * Return: 0 if userptr vma is valid, -EAGAIN otherwise; repin recommended. */ -int xe_vma_userptr_check_repin(struct xe_vma *vma) +int xe_vma_userptr_check_repin(struct xe_userptr_vma *uvma) { - return mmu_interval_check_retry(&vma->userptr.notifier, - vma->userptr.notifier_seq) ? + return mmu_interval_check_retry(&uvma->userptr.notifier, + uvma->userptr.notifier_seq) ? -EAGAIN : 0; } -int xe_vma_userptr_pin_pages(struct xe_vma *vma) +int xe_vma_userptr_pin_pages(struct xe_userptr_vma *uvma) { + struct xe_userptr *userptr = &uvma->userptr; + struct xe_vma *vma = &uvma->vma; struct xe_vm *vm = xe_vma_vm(vma); struct xe_device *xe = vm->xe; const unsigned long num_pages = xe_vma_size(vma) >> PAGE_SHIFT; @@ -80,30 +80,30 @@ retry: if (vma->gpuva.flags & XE_VMA_DESTROYED) return 0; - notifier_seq = mmu_interval_read_begin(&vma->userptr.notifier); - if (notifier_seq == vma->userptr.notifier_seq) + notifier_seq = mmu_interval_read_begin(&userptr->notifier); + if (notifier_seq == userptr->notifier_seq) return 0; pages = kvmalloc_array(num_pages, sizeof(*pages), GFP_KERNEL); if (!pages) return -ENOMEM; - if (vma->userptr.sg) { + if (userptr->sg) { dma_unmap_sgtable(xe->drm.dev, - vma->userptr.sg, + userptr->sg, read_only ? DMA_TO_DEVICE : DMA_BIDIRECTIONAL, 0); - sg_free_table(vma->userptr.sg); - vma->userptr.sg = NULL; + sg_free_table(userptr->sg); + userptr->sg = NULL; } pinned = ret = 0; if (in_kthread) { - if (!mmget_not_zero(vma->userptr.notifier.mm)) { + if (!mmget_not_zero(userptr->notifier.mm)) { ret = -EFAULT; goto mm_closed; } - kthread_use_mm(vma->userptr.notifier.mm); + kthread_use_mm(userptr->notifier.mm); } while (pinned < num_pages) { @@ -112,43 +112,40 @@ retry: num_pages - pinned, read_only ? 0 : FOLL_WRITE, &pages[pinned]); - if (ret < 0) { - if (in_kthread) - ret = 0; + if (ret < 0) break; - } pinned += ret; ret = 0; } if (in_kthread) { - kthread_unuse_mm(vma->userptr.notifier.mm); - mmput(vma->userptr.notifier.mm); + kthread_unuse_mm(userptr->notifier.mm); + mmput(userptr->notifier.mm); } mm_closed: if (ret) goto out; - ret = sg_alloc_table_from_pages_segment(&vma->userptr.sgt, pages, + ret = sg_alloc_table_from_pages_segment(&userptr->sgt, pages, pinned, 0, (u64)pinned << PAGE_SHIFT, xe_sg_segment_size(xe->drm.dev), GFP_KERNEL); if (ret) { - vma->userptr.sg = NULL; + userptr->sg = NULL; goto out; } - vma->userptr.sg = &vma->userptr.sgt; + userptr->sg = &userptr->sgt; - ret = dma_map_sgtable(xe->drm.dev, vma->userptr.sg, + ret = dma_map_sgtable(xe->drm.dev, userptr->sg, read_only ? DMA_TO_DEVICE : DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_NO_KERNEL_MAPPING); if (ret) { - sg_free_table(vma->userptr.sg); - vma->userptr.sg = NULL; + sg_free_table(userptr->sg); + userptr->sg = NULL; goto out; } @@ -167,8 +164,8 @@ out: kvfree(pages); if (!(ret < 0)) { - vma->userptr.notifier_seq = notifier_seq; - if (xe_vma_userptr_check_repin(vma) == -EAGAIN) + userptr->notifier_seq = notifier_seq; + if (xe_vma_userptr_check_repin(uvma) == -EAGAIN) goto retry; } @@ -335,13 +332,13 @@ int xe_vm_add_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q) down_write(&vm->lock); err = drm_gpuvm_exec_lock(&vm_exec); if (err) - return err; + goto out_up_write; pfence = xe_preempt_fence_create(q, q->compute.context, ++q->compute.seqno); if (!pfence) { err = -ENOMEM; - goto out_unlock; + goto out_fini; } list_add(&q->compute.link, &vm->preempt.exec_queues); @@ -364,8 +361,9 @@ int xe_vm_add_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q) up_read(&vm->userptr.notifier_lock); -out_unlock: +out_fini: drm_exec_fini(exec); +out_up_write: up_write(&vm->lock); return err; @@ -634,7 +632,9 @@ static bool vma_userptr_invalidate(struct mmu_interval_notifier *mni, const struct mmu_notifier_range *range, unsigned long cur_seq) { - struct xe_vma *vma = container_of(mni, struct xe_vma, userptr.notifier); + struct xe_userptr *userptr = container_of(mni, typeof(*userptr), notifier); + struct xe_userptr_vma *uvma = container_of(userptr, typeof(*uvma), userptr); + struct xe_vma *vma = &uvma->vma; struct xe_vm *vm = xe_vma_vm(vma); struct dma_resv_iter cursor; struct dma_fence *fence; @@ -650,7 +650,7 @@ static bool vma_userptr_invalidate(struct mmu_interval_notifier *mni, mmu_interval_set_seq(mni, cur_seq); /* No need to stop gpu access if the userptr is not yet bound. */ - if (!vma->userptr.initial_bind) { + if (!userptr->initial_bind) { up_write(&vm->userptr.notifier_lock); return true; } @@ -662,7 +662,7 @@ static bool vma_userptr_invalidate(struct mmu_interval_notifier *mni, if (!xe_vm_in_fault_mode(vm) && !(vma->gpuva.flags & XE_VMA_DESTROYED) && vma->tile_present) { spin_lock(&vm->userptr.invalidated_lock); - list_move_tail(&vma->userptr.invalidate_link, + list_move_tail(&userptr->invalidate_link, &vm->userptr.invalidated); spin_unlock(&vm->userptr.invalidated_lock); } @@ -702,7 +702,7 @@ static const struct mmu_interval_notifier_ops vma_userptr_notifier_ops = { int xe_vm_userptr_pin(struct xe_vm *vm) { - struct xe_vma *vma, *next; + struct xe_userptr_vma *uvma, *next; int err = 0; LIST_HEAD(tmp_evict); @@ -710,22 +710,23 @@ int xe_vm_userptr_pin(struct xe_vm *vm) /* Collect invalidated userptrs */ spin_lock(&vm->userptr.invalidated_lock); - list_for_each_entry_safe(vma, next, &vm->userptr.invalidated, + list_for_each_entry_safe(uvma, next, &vm->userptr.invalidated, userptr.invalidate_link) { - list_del_init(&vma->userptr.invalidate_link); - list_move_tail(&vma->combined_links.userptr, + list_del_init(&uvma->userptr.invalidate_link); + list_move_tail(&uvma->userptr.repin_link, &vm->userptr.repin_list); } spin_unlock(&vm->userptr.invalidated_lock); /* Pin and move to temporary list */ - list_for_each_entry_safe(vma, next, &vm->userptr.repin_list, - combined_links.userptr) { - err = xe_vma_userptr_pin_pages(vma); + list_for_each_entry_safe(uvma, next, &vm->userptr.repin_list, + userptr.repin_link) { + err = xe_vma_userptr_pin_pages(uvma); if (err < 0) return err; - list_move_tail(&vma->combined_links.userptr, &vm->rebind_list); + list_del_init(&uvma->userptr.repin_link); + list_move_tail(&uvma->vma.combined_links.rebind, &vm->rebind_list); } return 0; @@ -781,6 +782,14 @@ struct dma_fence *xe_vm_rebind(struct xe_vm *vm, bool rebind_worker) return fence; } +static void xe_vma_free(struct xe_vma *vma) +{ + if (xe_vma_is_userptr(vma)) + kfree(to_userptr_vma(vma)); + else + kfree(vma); +} + #define VMA_CREATE_FLAG_READ_ONLY BIT(0) #define VMA_CREATE_FLAG_IS_NULL BIT(1) @@ -799,14 +808,26 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm, xe_assert(vm->xe, start < end); xe_assert(vm->xe, end < vm->size); - if (!bo && !is_null) /* userptr */ + /* + * Allocate and ensure that the xe_vma_is_userptr() return + * matches what was allocated. + */ + if (!bo && !is_null) { + struct xe_userptr_vma *uvma = kzalloc(sizeof(*uvma), GFP_KERNEL); + + if (!uvma) + return ERR_PTR(-ENOMEM); + + vma = &uvma->vma; + } else { vma = kzalloc(sizeof(*vma), GFP_KERNEL); - else - vma = kzalloc(sizeof(*vma) - sizeof(struct xe_userptr), - GFP_KERNEL); - if (!vma) { - vma = ERR_PTR(-ENOMEM); - return vma; + if (!vma) + return ERR_PTR(-ENOMEM); + + if (is_null) + vma->gpuva.flags |= DRM_GPUVA_SPARSE; + if (bo) + vma->gpuva.gem.obj = &bo->ttm.base; } INIT_LIST_HEAD(&vma->combined_links.rebind); @@ -817,8 +838,6 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm, vma->gpuva.va.range = end - start + 1; if (read_only) vma->gpuva.flags |= XE_VMA_READ_ONLY; - if (is_null) - vma->gpuva.flags |= DRM_GPUVA_SPARSE; for_each_tile(tile, vm->xe, id) vma->tile_mask |= 0x1 << id; @@ -835,35 +854,35 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm, vm_bo = drm_gpuvm_bo_obtain(vma->gpuva.vm, &bo->ttm.base); if (IS_ERR(vm_bo)) { - kfree(vma); + xe_vma_free(vma); return ERR_CAST(vm_bo); } drm_gpuvm_bo_extobj_add(vm_bo); drm_gem_object_get(&bo->ttm.base); - vma->gpuva.gem.obj = &bo->ttm.base; vma->gpuva.gem.offset = bo_offset_or_userptr; drm_gpuva_link(&vma->gpuva, vm_bo); drm_gpuvm_bo_put(vm_bo); } else /* userptr or null */ { if (!is_null) { + struct xe_userptr *userptr = &to_userptr_vma(vma)->userptr; u64 size = end - start + 1; int err; - INIT_LIST_HEAD(&vma->userptr.invalidate_link); + INIT_LIST_HEAD(&userptr->invalidate_link); + INIT_LIST_HEAD(&userptr->repin_link); vma->gpuva.gem.offset = bo_offset_or_userptr; - err = mmu_interval_notifier_insert(&vma->userptr.notifier, + err = mmu_interval_notifier_insert(&userptr->notifier, current->mm, xe_vma_userptr(vma), size, &vma_userptr_notifier_ops); if (err) { - kfree(vma); - vma = ERR_PTR(err); - return vma; + xe_vma_free(vma); + return ERR_PTR(err); } - vma->userptr.notifier_seq = LONG_MAX; + userptr->notifier_seq = LONG_MAX; } xe_vm_get(vm); @@ -879,13 +898,15 @@ static void xe_vma_destroy_late(struct xe_vma *vma) bool read_only = xe_vma_read_only(vma); if (xe_vma_is_userptr(vma)) { - if (vma->userptr.sg) { + struct xe_userptr *userptr = &to_userptr_vma(vma)->userptr; + + if (userptr->sg) { dma_unmap_sgtable(xe->drm.dev, - vma->userptr.sg, + userptr->sg, read_only ? DMA_TO_DEVICE : DMA_BIDIRECTIONAL, 0); - sg_free_table(vma->userptr.sg); - vma->userptr.sg = NULL; + sg_free_table(userptr->sg); + userptr->sg = NULL; } /* @@ -893,7 +914,7 @@ static void xe_vma_destroy_late(struct xe_vma *vma) * the notifer until we're sure the GPU is not accessing * them anymore */ - mmu_interval_notifier_remove(&vma->userptr.notifier); + mmu_interval_notifier_remove(&userptr->notifier); xe_vm_put(vm); } else if (xe_vma_is_null(vma)) { xe_vm_put(vm); @@ -901,7 +922,7 @@ static void xe_vma_destroy_late(struct xe_vma *vma) xe_bo_put(xe_vma_bo(vma)); } - kfree(vma); + xe_vma_free(vma); } static void vma_destroy_work_func(struct work_struct *w) @@ -932,7 +953,7 @@ static void xe_vma_destroy(struct xe_vma *vma, struct dma_fence *fence) xe_assert(vm->xe, vma->gpuva.flags & XE_VMA_DESTROYED); spin_lock(&vm->userptr.invalidated_lock); - list_del(&vma->userptr.invalidate_link); + list_del(&to_userptr_vma(vma)->userptr.invalidate_link); spin_unlock(&vm->userptr.invalidated_lock); } else if (!xe_vma_is_null(vma)) { xe_bo_assert_held(xe_vma_bo(vma)); @@ -1854,10 +1875,8 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *data, mutex_lock(&xef->vm.lock); err = xa_alloc(&xef->vm.xa, &id, vm, xa_limit_32b, GFP_KERNEL); mutex_unlock(&xef->vm.lock); - if (err) { - xe_vm_close_and_put(vm); - return err; - } + if (err) + goto err_close_and_put; if (xe->info.has_asid) { mutex_lock(&xe->usm.lock); @@ -1865,11 +1884,9 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *data, XA_LIMIT(1, XE_MAX_ASID - 1), &xe->usm.next_asid, GFP_KERNEL); mutex_unlock(&xe->usm.lock); - if (err < 0) { - xe_vm_close_and_put(vm); - return err; - } - err = 0; + if (err < 0) + goto err_free_id; + vm->usm.asid = asid; } @@ -1887,6 +1904,15 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *data, #endif return 0; + +err_free_id: + mutex_lock(&xef->vm.lock); + xa_erase(&xef->vm.xa, id); + mutex_unlock(&xef->vm.lock); +err_close_and_put: + xe_vm_close_and_put(vm); + + return err; } int xe_vm_destroy_ioctl(struct drm_device *dev, void *data, @@ -1953,6 +1979,7 @@ static int xe_vm_prefetch(struct xe_vm *vm, struct xe_vma *vma, xe_exec_queue_last_fence_get(wait_exec_queue, vm); xe_sync_entry_signal(&syncs[i], NULL, fence); + dma_fence_put(fence); } } @@ -2033,7 +2060,6 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_bo *bo, struct drm_gem_object *obj = bo ? &bo->ttm.base : NULL; struct drm_gpuva_ops *ops; struct drm_gpuva_op *__op; - struct xe_vma_op *op; struct drm_gpuvm_bo *vm_bo; int err; @@ -2063,9 +2089,11 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_bo *bo, if (err) return ERR_PTR(err); - vm_bo = drm_gpuvm_bo_find(&vm->gpuvm, obj); - if (!vm_bo) - break; + vm_bo = drm_gpuvm_bo_obtain(&vm->gpuvm, obj); + if (IS_ERR(vm_bo)) { + xe_bo_unlock(bo); + return ERR_CAST(vm_bo); + } ops = drm_gpuvm_bo_unmap_ops_create(vm_bo); drm_gpuvm_bo_put(vm_bo); @@ -2078,15 +2106,6 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_bo *bo, if (IS_ERR(ops)) return ops; -#ifdef TEST_VM_ASYNC_OPS_ERROR - if (operation & FORCE_ASYNC_OP_ERROR) { - op = list_first_entry_or_null(&ops->list, struct xe_vma_op, - base.entry); - if (op) - op->inject_error = true; - } -#endif - drm_gpuva_for_each_op(__op, ops) { struct xe_vma_op *op = gpuva_op_to_vma_op(__op); @@ -2142,7 +2161,7 @@ static struct xe_vma *new_vma(struct xe_vm *vm, struct drm_gpuva_op_map *op, drm_exec_fini(&exec); if (xe_vma_is_userptr(vma)) { - err = xe_vma_userptr_pin_pages(vma); + err = xe_vma_userptr_pin_pages(to_userptr_vma(vma)); if (err) { prep_vma_destroy(vm, vma, false); xe_vma_destroy_unlocked(vma); @@ -2166,8 +2185,10 @@ static u64 xe_vma_max_pte_size(struct xe_vma *vma) return SZ_1G; else if (vma->gpuva.flags & XE_VMA_PTE_2M) return SZ_2M; + else if (vma->gpuva.flags & XE_VMA_PTE_4K) + return SZ_4K; - return SZ_4K; + return SZ_1G; /* Uninitialized, used max size */ } static u64 xe_vma_set_pte_size(struct xe_vma *vma, u64 size) @@ -2497,13 +2518,25 @@ retry_userptr: } drm_exec_fini(&exec); - if (err == -EAGAIN && xe_vma_is_userptr(vma)) { + if (err == -EAGAIN) { lockdep_assert_held_write(&vm->lock); - err = xe_vma_userptr_pin_pages(vma); - if (!err) - goto retry_userptr; - trace_xe_vma_fail(vma); + if (op->base.op == DRM_GPUVA_OP_REMAP) { + if (!op->remap.unmap_done) + vma = gpuva_to_vma(op->base.remap.unmap->va); + else if (op->remap.prev) + vma = op->remap.prev; + else + vma = op->remap.next; + } + + if (xe_vma_is_userptr(vma)) { + err = xe_vma_userptr_pin_pages(to_userptr_vma(vma)); + if (!err) + goto retry_userptr; + + trace_xe_vma_fail(vma); + } } return err; @@ -2515,13 +2548,6 @@ static int xe_vma_op_execute(struct xe_vm *vm, struct xe_vma_op *op) lockdep_assert_held_write(&vm->lock); -#ifdef TEST_VM_ASYNC_OPS_ERROR - if (op->inject_error) { - op->inject_error = false; - return -ENOMEM; - } -#endif - switch (op->base.op) { case DRM_GPUVA_OP_MAP: ret = __xe_vma_op_execute(vm, op->map.vma, op); @@ -2636,7 +2662,7 @@ static void vm_bind_ioctl_ops_unwind(struct xe_vm *vm, { int i; - for (i = num_ops_list - 1; i; ++i) { + for (i = num_ops_list - 1; i >= 0; --i) { struct drm_gpuva_ops *__ops = ops[i]; struct drm_gpuva_op *__op; @@ -2681,16 +2707,9 @@ static int vm_bind_ioctl_ops_execute(struct xe_vm *vm, return 0; } -#ifdef TEST_VM_ASYNC_OPS_ERROR -#define SUPPORTED_FLAGS \ - (FORCE_ASYNC_OP_ERROR | DRM_XE_VM_BIND_FLAG_READONLY | \ - DRM_XE_VM_BIND_FLAG_IMMEDIATE | DRM_XE_VM_BIND_FLAG_NULL | 0xffff) -#else #define SUPPORTED_FLAGS \ (DRM_XE_VM_BIND_FLAG_READONLY | \ - DRM_XE_VM_BIND_FLAG_IMMEDIATE | DRM_XE_VM_BIND_FLAG_NULL | \ - 0xffff) -#endif + DRM_XE_VM_BIND_FLAG_IMMEDIATE | DRM_XE_VM_BIND_FLAG_NULL) #define XE_64K_PAGE_MASK 0xffffull #define ALL_DRM_XE_SYNCS_FLAGS (DRM_XE_SYNCS_FLAG_WAIT_FOR_OP) @@ -2843,7 +2862,7 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file) struct drm_gpuva_ops **ops = NULL; struct xe_vm *vm; struct xe_exec_queue *q = NULL; - u32 num_syncs; + u32 num_syncs, num_ufence = 0; struct xe_sync_entry *syncs = NULL; struct drm_xe_vm_bind_op *bind_ops; LIST_HEAD(ops_list); @@ -2980,6 +2999,14 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file) SYNC_PARSE_FLAG_DISALLOW_USER_FENCE : 0)); if (err) goto free_syncs; + + if (xe_sync_is_ufence(&syncs[num_syncs])) + num_ufence++; + } + + if (XE_IOCTL_DBG(xe, num_ufence > 1)) { + err = -EINVAL; + goto free_syncs; } if (!args->num_binds) { @@ -3122,8 +3149,8 @@ int xe_vm_invalidate_vma(struct xe_vma *vma) if (IS_ENABLED(CONFIG_PROVE_LOCKING)) { if (xe_vma_is_userptr(vma)) { WARN_ON_ONCE(!mmu_interval_check_retry - (&vma->userptr.notifier, - vma->userptr.notifier_seq)); + (&to_userptr_vma(vma)->userptr.notifier, + to_userptr_vma(vma)->userptr.notifier_seq)); WARN_ON_ONCE(!dma_resv_test_signaled(xe_vm_resv(xe_vma_vm(vma)), DMA_RESV_USAGE_BOOKKEEP)); @@ -3184,11 +3211,11 @@ int xe_analyze_vm(struct drm_printer *p, struct xe_vm *vm, int gt_id) if (is_null) { addr = 0; } else if (is_userptr) { + struct sg_table *sg = to_userptr_vma(vma)->userptr.sg; struct xe_res_cursor cur; - if (vma->userptr.sg) { - xe_res_first_sg(vma->userptr.sg, 0, XE_PAGE_SIZE, - &cur); + if (sg) { + xe_res_first_sg(sg, 0, XE_PAGE_SIZE, &cur); addr = xe_res_dma(&cur); } else { addr = 0; diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h index cf2f96e8c1ab..9654a0612fc2 100644 --- a/drivers/gpu/drm/xe/xe_vm.h +++ b/drivers/gpu/drm/xe/xe_vm.h @@ -160,6 +160,18 @@ static inline bool xe_vma_is_userptr(struct xe_vma *vma) return xe_vma_has_no_bo(vma) && !xe_vma_is_null(vma); } +/** + * to_userptr_vma() - Return a pointer to an embedding userptr vma + * @vma: Pointer to the embedded struct xe_vma + * + * Return: Pointer to the embedding userptr vma + */ +static inline struct xe_userptr_vma *to_userptr_vma(struct xe_vma *vma) +{ + xe_assert(xe_vma_vm(vma)->xe, xe_vma_is_userptr(vma)); + return container_of(vma, struct xe_userptr_vma, vma); +} + u64 xe_vm_pdp4_descriptor(struct xe_vm *vm, struct xe_tile *tile); int xe_vm_create_ioctl(struct drm_device *dev, void *data, @@ -224,9 +236,9 @@ static inline void xe_vm_reactivate_rebind(struct xe_vm *vm) } } -int xe_vma_userptr_pin_pages(struct xe_vma *vma); +int xe_vma_userptr_pin_pages(struct xe_userptr_vma *uvma); -int xe_vma_userptr_check_repin(struct xe_vma *vma); +int xe_vma_userptr_check_repin(struct xe_userptr_vma *uvma); bool xe_vm_validate_should_retry(struct drm_exec *exec, int err, ktime_t *end); diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h index 63e8a50b88e9..5ac9c5bebabc 100644 --- a/drivers/gpu/drm/xe/xe_vm_types.h +++ b/drivers/gpu/drm/xe/xe_vm_types.h @@ -21,9 +21,6 @@ struct xe_bo; struct xe_sync_entry; struct xe_vm; -#define TEST_VM_ASYNC_OPS_ERROR -#define FORCE_ASYNC_OP_ERROR BIT(31) - #define XE_VMA_READ_ONLY DRM_GPUVA_USERBITS #define XE_VMA_DESTROYED (DRM_GPUVA_USERBITS << 1) #define XE_VMA_ATOMIC_PTE_BIT (DRM_GPUVA_USERBITS << 2) @@ -37,6 +34,8 @@ struct xe_vm; struct xe_userptr { /** @invalidate_link: Link for the vm::userptr.invalidated list */ struct list_head invalidate_link; + /** @userptr: link into VM repin list if userptr. */ + struct list_head repin_link; /** * @notifier: MMU notifier for user pointer (invalidation call back) */ @@ -68,8 +67,6 @@ struct xe_vma { * resv. */ union { - /** @userptr: link into VM repin list if userptr. */ - struct list_head userptr; /** @rebind: link into VM if this VMA needs rebinding. */ struct list_head rebind; /** @destroy: link to contested list when VM is being closed. */ @@ -105,11 +102,15 @@ struct xe_vma { * @pat_index: The pat index to use when encoding the PTEs for this vma. */ u16 pat_index; +}; - /** - * @userptr: user pointer state, only allocated for VMAs that are - * user pointers - */ +/** + * struct xe_userptr_vma - A userptr vma subclass + * @vma: The vma. + * @userptr: Additional userptr information. + */ +struct xe_userptr_vma { + struct xe_vma vma; struct xe_userptr userptr; }; @@ -356,11 +357,6 @@ struct xe_vma_op { /** @flags: operation flags */ enum xe_vma_op_flags flags; -#ifdef TEST_VM_ASYNC_OPS_ERROR - /** @inject_error: inject error to test async op error handling */ - bool inject_error; -#endif - union { /** @map: VMA map operation specific data */ struct xe_vma_op_map map; diff --git a/drivers/greybus/gb-beagleplay.c b/drivers/greybus/gb-beagleplay.c index 43318c1993ba..c3e90025064b 100644 --- a/drivers/greybus/gb-beagleplay.c +++ b/drivers/greybus/gb-beagleplay.c @@ -85,17 +85,31 @@ struct hdlc_payload { void *buf; }; +/** + * struct hdlc_greybus_frame - Structure to represent greybus HDLC frame payload + * + * @cport: cport id + * @hdr: greybus operation header + * @payload: greybus message payload + * + * The HDLC payload sent over UART for greybus address has cport preappended to greybus message + */ +struct hdlc_greybus_frame { + __le16 cport; + struct gb_operation_msg_hdr hdr; + u8 payload[]; +} __packed; + static void hdlc_rx_greybus_frame(struct gb_beagleplay *bg, u8 *buf, u16 len) { - u16 cport_id; - struct gb_operation_msg_hdr *hdr = (struct gb_operation_msg_hdr *)buf; - - memcpy(&cport_id, hdr->pad, sizeof(cport_id)); + struct hdlc_greybus_frame *gb_frame = (struct hdlc_greybus_frame *)buf; + u16 cport_id = le16_to_cpu(gb_frame->cport); + u16 gb_msg_len = le16_to_cpu(gb_frame->hdr.size); dev_dbg(&bg->sd->dev, "Greybus Operation %u type %X cport %u status %u received", - hdr->operation_id, hdr->type, cport_id, hdr->result); + gb_frame->hdr.operation_id, gb_frame->hdr.type, cport_id, gb_frame->hdr.result); - greybus_data_rcvd(bg->gb_hd, cport_id, buf, len); + greybus_data_rcvd(bg->gb_hd, cport_id, (u8 *)&gb_frame->hdr, gb_msg_len); } static void hdlc_rx_dbg_frame(const struct gb_beagleplay *bg, const char *buf, u16 len) @@ -257,7 +271,7 @@ static void hdlc_rx_frame(struct gb_beagleplay *bg) } } -static int hdlc_rx(struct gb_beagleplay *bg, const u8 *data, size_t count) +static ssize_t hdlc_rx(struct gb_beagleplay *bg, const u8 *data, size_t count) { size_t i; u8 c; @@ -317,7 +331,8 @@ static void hdlc_deinit(struct gb_beagleplay *bg) flush_work(&bg->tx_work); } -static int gb_tty_receive(struct serdev_device *sd, const unsigned char *data, size_t count) +static ssize_t gb_tty_receive(struct serdev_device *sd, const u8 *data, + size_t count) { struct gb_beagleplay *bg = serdev_device_get_drvdata(sd); @@ -336,25 +351,39 @@ static struct serdev_device_ops gb_beagleplay_ops = { .write_wakeup = gb_tty_wakeup, }; +/** + * gb_message_send() - Send greybus message using HDLC over UART + * + * @hd: pointer to greybus host device + * @cport: AP cport where message originates + * @msg: greybus message to send + * @mask: gfp mask + * + * Greybus HDLC frame has the following payload: + * 1. le16 cport + * 2. gb_operation_msg_hdr msg_header + * 3. u8 *msg_payload + */ static int gb_message_send(struct gb_host_device *hd, u16 cport, struct gb_message *msg, gfp_t mask) { struct gb_beagleplay *bg = dev_get_drvdata(&hd->dev); - struct hdlc_payload payloads[2]; + struct hdlc_payload payloads[3]; + __le16 cport_id = cpu_to_le16(cport); dev_dbg(&hd->dev, "Sending greybus message with Operation %u, Type: %X on Cport %u", msg->header->operation_id, msg->header->type, cport); - if (msg->header->size > RX_HDLC_PAYLOAD) + if (le16_to_cpu(msg->header->size) > RX_HDLC_PAYLOAD) return dev_err_probe(&hd->dev, -E2BIG, "Greybus message too big"); - memcpy(msg->header->pad, &cport, sizeof(cport)); - - payloads[0].buf = msg->header; - payloads[0].len = sizeof(*msg->header); - payloads[1].buf = msg->payload; - payloads[1].len = msg->payload_size; + payloads[0].buf = &cport_id; + payloads[0].len = sizeof(cport_id); + payloads[1].buf = msg->header; + payloads[1].len = sizeof(*msg->header); + payloads[2].buf = msg->payload; + payloads[2].len = msg->payload_size; - hdlc_tx_frames(bg, ADDRESS_GREYBUS, 0x03, payloads, 2); + hdlc_tx_frames(bg, ADDRESS_GREYBUS, 0x03, payloads, 3); greybus_message_sent(bg->gb_hd, msg, 0); return 0; diff --git a/drivers/hid/amd-sfh-hid/Kconfig b/drivers/hid/amd-sfh-hid/Kconfig index af752dd3a340..329de5e12c1a 100644 --- a/drivers/hid/amd-sfh-hid/Kconfig +++ b/drivers/hid/amd-sfh-hid/Kconfig @@ -6,6 +6,7 @@ menu "AMD SFH HID Support" config AMD_SFH_HID tristate "AMD Sensor Fusion Hub" depends on HID + depends on X86 help If you say yes to this option, support will be included for the AMD Sensor Fusion Hub. diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_common.h b/drivers/hid/amd-sfh-hid/amd_sfh_common.h index a1950bc6e6ce..e5620d7db569 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_common.h +++ b/drivers/hid/amd-sfh-hid/amd_sfh_common.h @@ -19,6 +19,9 @@ #define AMD_C2P_MSG(regno) (0x10500 + ((regno) * 4)) #define AMD_P2C_MSG(regno) (0x10680 + ((regno) * 4)) +#define AMD_C2P_MSG_V1(regno) (0x10900 + ((regno) * 4)) +#define AMD_P2C_MSG_V1(regno) (0x10500 + ((regno) * 4)) + #define SENSOR_ENABLED 4 #define SENSOR_DISABLED 5 @@ -53,6 +56,9 @@ struct amd_mp2_dev { /* mp2 active control status */ u32 mp2_acs; struct sfh_dev_status dev_en; + struct work_struct work; + u8 init_done; + u8 rver; }; struct amd_mp2_ops { @@ -79,4 +85,14 @@ void amd_sfh_clear_intr_v2(struct amd_mp2_dev *privdata); int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata); void amd_sfh_clear_intr(struct amd_mp2_dev *privdata); int amd_sfh_irq_init(struct amd_mp2_dev *privdata); + +static inline u64 amd_get_c2p_val(struct amd_mp2_dev *mp2, u32 idx) +{ + return mp2->rver == 1 ? AMD_C2P_MSG_V1(idx) : AMD_C2P_MSG(idx); +} + +static inline u64 amd_get_p2c_val(struct amd_mp2_dev *mp2, u32 idx) +{ + return mp2->rver == 1 ? AMD_P2C_MSG_V1(idx) : AMD_P2C_MSG(idx); +} #endif diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c index 2530fa98b568..9e97c26c4482 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c @@ -10,6 +10,7 @@ #include <linux/bitops.h> #include <linux/delay.h> +#include <linux/devm-helpers.h> #include <linux/dma-mapping.h> #include <linux/dmi.h> #include <linux/interrupt.h> @@ -35,15 +36,17 @@ static int sensor_mask_override = -1; module_param_named(sensor_mask, sensor_mask_override, int, 0444); MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask"); +static bool intr_disable = true; + static int amd_sfh_wait_response_v2(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts) { union cmd_response cmd_resp; - /* Get response with status within a max of 1600 ms timeout */ + /* Get response with status within a max of 10 seconds timeout */ if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp, (cmd_resp.response_v2.response == sensor_sts && cmd_resp.response_v2.status == 0 && (sid == 0xff || - cmd_resp.response_v2.sensor_id == sid)), 500, 1600000)) + cmd_resp.response_v2.sensor_id == sid)), 500, 10000000)) return cmd_resp.response_v2.response; return SENSOR_DISABLED; @@ -55,7 +58,7 @@ static void amd_start_sensor_v2(struct amd_mp2_dev *privdata, struct amd_mp2_sen cmd_base.ul = 0; cmd_base.cmd_v2.cmd_id = ENABLE_SENSOR; - cmd_base.cmd_v2.intr_disable = 1; + cmd_base.cmd_v2.intr_disable = intr_disable; cmd_base.cmd_v2.period = info.period; cmd_base.cmd_v2.sensor_id = info.sensor_idx; cmd_base.cmd_v2.length = 16; @@ -73,7 +76,7 @@ static void amd_stop_sensor_v2(struct amd_mp2_dev *privdata, u16 sensor_idx) cmd_base.ul = 0; cmd_base.cmd_v2.cmd_id = DISABLE_SENSOR; - cmd_base.cmd_v2.intr_disable = 1; + cmd_base.cmd_v2.intr_disable = intr_disable; cmd_base.cmd_v2.period = 0; cmd_base.cmd_v2.sensor_id = sensor_idx; cmd_base.cmd_v2.length = 16; @@ -87,7 +90,7 @@ static void amd_stop_all_sensor_v2(struct amd_mp2_dev *privdata) union sfh_cmd_base cmd_base; cmd_base.cmd_v2.cmd_id = STOP_ALL_SENSORS; - cmd_base.cmd_v2.intr_disable = 1; + cmd_base.cmd_v2.intr_disable = intr_disable; cmd_base.cmd_v2.period = 0; cmd_base.cmd_v2.sensor_id = 0; @@ -96,9 +99,9 @@ static void amd_stop_all_sensor_v2(struct amd_mp2_dev *privdata) void amd_sfh_clear_intr_v2(struct amd_mp2_dev *privdata) { - if (readl(privdata->mmio + AMD_P2C_MSG(4))) { - writel(0, privdata->mmio + AMD_P2C_MSG(4)); - writel(0xf, privdata->mmio + AMD_P2C_MSG(5)); + if (readl(privdata->mmio + amd_get_p2c_val(privdata, 4))) { + writel(0, privdata->mmio + amd_get_p2c_val(privdata, 4)); + writel(0xf, privdata->mmio + amd_get_p2c_val(privdata, 5)); } } @@ -292,6 +295,26 @@ int amd_sfh_irq_init(struct amd_mp2_dev *privdata) return 0; } +static int mp2_disable_intr(const struct dmi_system_id *id) +{ + intr_disable = false; + return 0; +} + +static const struct dmi_system_id dmi_sfh_table[] = { + { + /* + * https://bugzilla.kernel.org/show_bug.cgi?id=218104 + */ + .callback = mp2_disable_intr, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook x360 435 G7"), + }, + }, + {} +}; + static const struct dmi_system_id dmi_nodevs[] = { { /* @@ -307,6 +330,48 @@ static const struct dmi_system_id dmi_nodevs[] = { { } }; +static void sfh1_1_init_work(struct work_struct *work) +{ + struct amd_mp2_dev *mp2 = container_of(work, struct amd_mp2_dev, work); + struct pci_dev *pdev = mp2->pdev; + int rc; + + rc = mp2->sfh1_1_ops->init(mp2); + if (rc) { + dev_err(&pdev->dev, "sfh1_1_init failed err %d\n", rc); + return; + } + + amd_sfh_clear_intr(mp2); + mp2->init_done = 1; +} + +static void sfh_init_work(struct work_struct *work) +{ + struct amd_mp2_dev *mp2 = container_of(work, struct amd_mp2_dev, work); + struct pci_dev *pdev = mp2->pdev; + int rc; + + rc = amd_sfh_hid_client_init(mp2); + if (rc) { + amd_sfh_clear_intr(mp2); + dev_err(&pdev->dev, "amd_sfh_hid_client_init failed err %d\n", rc); + return; + } + + amd_sfh_clear_intr(mp2); + mp2->init_done = 1; +} + +static void amd_sfh_remove(struct pci_dev *pdev) +{ + struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev); + + flush_work(&mp2->work); + if (mp2->init_done) + mp2->mp2_ops->remove(mp2); +} + static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct amd_mp2_dev *privdata; @@ -315,6 +380,8 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i if (dmi_first_match(dmi_nodevs)) return -ENODEV; + dmi_check_system(dmi_sfh_table); + privdata = devm_kzalloc(&pdev->dev, sizeof(*privdata), GFP_KERNEL); if (!privdata) return -ENOMEM; @@ -343,10 +410,15 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i privdata->sfh1_1_ops = (const struct amd_sfh1_1_ops *)id->driver_data; if (privdata->sfh1_1_ops) { - rc = privdata->sfh1_1_ops->init(privdata); + if (boot_cpu_data.x86 >= 0x1A) + privdata->rver = 1; + + rc = devm_work_autocancel(&pdev->dev, &privdata->work, sfh1_1_init_work); if (rc) return rc; - goto init_done; + + schedule_work(&privdata->work); + return 0; } mp2_select_ops(privdata); @@ -357,33 +429,34 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i return rc; } - rc = amd_sfh_hid_client_init(privdata); + rc = devm_work_autocancel(&pdev->dev, &privdata->work, sfh_init_work); if (rc) { amd_sfh_clear_intr(privdata); - if (rc != -EOPNOTSUPP) - dev_err(&pdev->dev, "amd_sfh_hid_client_init failed\n"); return rc; } -init_done: - amd_sfh_clear_intr(privdata); - - return devm_add_action_or_reset(&pdev->dev, privdata->mp2_ops->remove, privdata); + schedule_work(&privdata->work); + return 0; } static void amd_sfh_shutdown(struct pci_dev *pdev) { struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev); - if (mp2 && mp2->mp2_ops) - mp2->mp2_ops->stop_all(mp2); + if (mp2) { + flush_work(&mp2->work); + if (mp2->init_done) + mp2->mp2_ops->stop_all(mp2); + } } static int __maybe_unused amd_mp2_pci_resume(struct device *dev) { struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); - mp2->mp2_ops->resume(mp2); + flush_work(&mp2->work); + if (mp2->init_done) + mp2->mp2_ops->resume(mp2); return 0; } @@ -392,7 +465,9 @@ static int __maybe_unused amd_mp2_pci_suspend(struct device *dev) { struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); - mp2->mp2_ops->suspend(mp2); + flush_work(&mp2->work); + if (mp2->init_done) + mp2->mp2_ops->suspend(mp2); return 0; } @@ -414,6 +489,7 @@ static struct pci_driver amd_mp2_pci_driver = { .probe = amd_mp2_pci_probe, .driver.pm = &amd_mp2_pm_ops, .shutdown = amd_sfh_shutdown, + .remove = amd_sfh_remove, }; module_pci_driver(amd_mp2_pci_driver); diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h index 70add75fc506..05e400a4a83e 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h @@ -90,10 +90,10 @@ enum mem_use_type { struct hpd_status { union { struct { - u32 human_presence_report : 4; - u32 human_presence_actual : 4; - u32 probablity : 8; u32 object_distance : 16; + u32 probablity : 8; + u32 human_presence_actual : 4; + u32 human_presence_report : 4; } shpd; u32 val; }; diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c index 33fbdde8aff0..c8916afefa62 100644 --- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c @@ -251,7 +251,7 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id, break; case HPD_IDX: get_common_inputs(&hpd_input.common_property, report_id); - hpdstatus.val = readl(mp2->mmio + AMD_C2P_MSG(4)); + hpdstatus.val = readl(mp2->mmio + amd_get_c2p_val(mp2, 4)); hpd_input.human_presence = hpdstatus.shpd.presence; report_size = sizeof(hpd_input); memcpy(input_report, &hpd_input, sizeof(hpd_input)); diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c index 9dbe6f4cb294..5b24d5f63701 100644 --- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c @@ -172,7 +172,7 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata) if (rc) goto cleanup; - writel(0, privdata->mmio + AMD_P2C_MSG(0)); + writel(0, privdata->mmio + amd_get_p2c_val(privdata, 0)); mp2_ops->start(privdata, info); status = amd_sfh_wait_for_response (privdata, cl_data->sensor_idx[i], ENABLE_SENSOR); @@ -298,7 +298,7 @@ static void amd_sfh_set_ops(struct amd_mp2_dev *mp2) int amd_sfh1_1_init(struct amd_mp2_dev *mp2) { - u32 phy_base = readl(mp2->mmio + AMD_C2P_MSG(22)); + u32 phy_base = readl(mp2->mmio + amd_get_c2p_val(mp2, 22)); struct device *dev = &mp2->pdev->dev; struct sfh_base_info binfo; int rc; diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c index ae36312bc236..2de2668a0277 100644 --- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c @@ -20,7 +20,7 @@ static int amd_sfh_wait_response(struct amd_mp2_dev *mp2, u8 sid, u32 cmd_id) struct sfh_cmd_response cmd_resp; /* Get response with status within a max of 10000 ms timeout */ - if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp, + if (!readl_poll_timeout(mp2->mmio + amd_get_p2c_val(mp2, 0), cmd_resp.resp, (cmd_resp.response.response == 0 && cmd_resp.response.cmd_id == cmd_id && (sid == 0xff || cmd_resp.response.sensor_id == sid)), 500, 10000000)) @@ -39,7 +39,7 @@ static void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor cmd_base.cmd.sub_cmd_value = 1; cmd_base.cmd.sensor_id = info.sensor_idx; - writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG(0)); + writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0)); } static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx) @@ -52,8 +52,8 @@ static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx) cmd_base.cmd.sub_cmd_value = 1; cmd_base.cmd.sensor_id = sensor_idx; - writeq(0x0, privdata->mmio + AMD_C2P_MSG(1)); - writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG(0)); + writeq(0x0, privdata->mmio + amd_get_c2p_val(privdata, 1)); + writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0)); } static void amd_stop_all_sensor(struct amd_mp2_dev *privdata) @@ -66,7 +66,7 @@ static void amd_stop_all_sensor(struct amd_mp2_dev *privdata) /* 0xf indicates all sensors */ cmd_base.cmd.sensor_id = 0xf; - writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG(0)); + writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0)); } static struct amd_mp2_ops amd_sfh_ops = { diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c index 4c1a00f9929e..6802efb4d6cd 100644 --- a/drivers/hsi/controllers/omap_ssi_core.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -355,7 +355,7 @@ static int ssi_add_controller(struct hsi_controller *ssi, if (!omap_ssi) return -ENOMEM; - err = ida_simple_get(&platform_omap_ssi_ida, 0, 0, GFP_KERNEL); + err = ida_alloc(&platform_omap_ssi_ida, GFP_KERNEL); if (err < 0) return err; ssi->id = err; @@ -417,7 +417,7 @@ static int ssi_add_controller(struct hsi_controller *ssi, return 0; out_err: - ida_simple_remove(&platform_omap_ssi_ida, ssi->id); + ida_free(&platform_omap_ssi_ida, ssi->id); return err; } @@ -451,7 +451,7 @@ static void ssi_remove_controller(struct hsi_controller *ssi) tasklet_kill(&omap_ssi->gdd_tasklet); hsi_unregister_controller(ssi); clk_notifier_unregister(omap_ssi->fck, &omap_ssi->fck_nb); - ida_simple_remove(&platform_omap_ssi_ida, id); + ida_free(&platform_omap_ssi_ida, id); } static inline int ssi_of_get_available_ports_count(const struct device_node *np) diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index 4372f5d146ab..0285a74363b3 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -488,7 +488,7 @@ void hv_setup_dma_ops(struct device *dev, bool coherent) * Hyper-V does not offer a vIOMMU in the guest * VM, so pass 0/NULL for the IOMMU settings */ - arch_setup_dma_ops(dev, 0, 0, NULL, coherent); + arch_setup_dma_ops(dev, 0, 0, coherent); } EXPORT_SYMBOL_GPL(hv_setup_dma_ops); diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index f6e1e55e8292..4acc1858d8ac 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -195,6 +195,8 @@ struct aspeed_pwm_tacho_data { u8 fan_tach_ch_source[MAX_ASPEED_FAN_TACH_CHANNELS]; struct aspeed_cooling_device *cdev[8]; const struct attribute_group *groups[3]; + /* protects access to shared ASPEED_PTCR_RESULT */ + struct mutex tach_lock; }; enum type { TYPEM, TYPEN, TYPEO }; @@ -529,6 +531,8 @@ static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv, u8 fan_tach_ch_source, type, mode, both; int ret; + mutex_lock(&priv->tach_lock); + regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0); regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0x1 << fan_tach_ch); @@ -546,6 +550,8 @@ static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv, ASPEED_RPM_STATUS_SLEEP_USEC, usec); + mutex_unlock(&priv->tach_lock); + /* return -ETIMEDOUT if we didn't get an answer. */ if (ret) return ret; @@ -915,6 +921,7 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev) priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + mutex_init(&priv->tach_lock); priv->regmap = devm_regmap_init(dev, NULL, (__force void *)regs, &aspeed_pwm_tacho_regmap_config); if (IS_ERR(priv->regmap)) diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index ba82d1e79c13..b8fc8d1ef20d 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -41,7 +41,7 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); #define PKG_SYSFS_ATTR_NO 1 /* Sysfs attribute for package temp */ #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ -#define NUM_REAL_CORES 128 /* Number of Real cores per cpu */ +#define NUM_REAL_CORES 512 /* Number of Real cores per cpu */ #define CORETEMP_NAME_LENGTH 28 /* String Length of attrs */ #define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */ #define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) @@ -419,7 +419,7 @@ static ssize_t show_temp(struct device *dev, } static int create_core_attrs(struct temp_data *tdata, struct device *dev, - int attr_no) + int index) { int i; static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev, @@ -431,13 +431,20 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev, }; for (i = 0; i < tdata->attr_size; i++) { + /* + * We map the attr number to core id of the CPU + * The attr number is always core id + 2 + * The Pkgtemp will always show up as temp1_*, if available + */ + int attr_no = tdata->is_pkg_data ? 1 : tdata->cpu_core_id + 2; + snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, "temp%d_%s", attr_no, suffixes[i]); sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; tdata->sd_attrs[i].dev_attr.attr.mode = 0444; tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; - tdata->sd_attrs[i].index = attr_no; + tdata->sd_attrs[i].index = index; tdata->attrs[i] = &tdata->sd_attrs[i].dev_attr.attr; } tdata->attr_group.attrs = tdata->attrs; @@ -495,30 +502,25 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, struct platform_data *pdata = platform_get_drvdata(pdev); struct cpuinfo_x86 *c = &cpu_data(cpu); u32 eax, edx; - int err, index, attr_no; + int err, index; if (!housekeeping_cpu(cpu, HK_TYPE_MISC)) return 0; /* - * Find attr number for sysfs: - * We map the attr number to core id of the CPU - * The attr number is always core id + 2 - * The Pkgtemp will always show up as temp1_*, if available + * Get the index of tdata in pdata->core_data[] + * tdata for package: pdata->core_data[1] + * tdata for core: pdata->core_data[2] .. pdata->core_data[NUM_REAL_CORES + 1] */ if (pkg_flag) { - attr_no = PKG_SYSFS_ATTR_NO; + index = PKG_SYSFS_ATTR_NO; } else { - index = ida_alloc(&pdata->ida, GFP_KERNEL); + index = ida_alloc_max(&pdata->ida, NUM_REAL_CORES - 1, GFP_KERNEL); if (index < 0) return index; - pdata->cpu_map[index] = topology_core_id(cpu); - attr_no = index + BASE_SYSFS_ATTR_NO; - } - if (attr_no > MAX_CORE_DATA - 1) { - err = -ERANGE; - goto ida_free; + pdata->cpu_map[index] = topology_core_id(cpu); + index += BASE_SYSFS_ATTR_NO; } tdata = init_temp_data(cpu, pkg_flag); @@ -544,20 +546,20 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, if (get_ttarget(tdata, &pdev->dev) >= 0) tdata->attr_size++; - pdata->core_data[attr_no] = tdata; + pdata->core_data[index] = tdata; /* Create sysfs interfaces */ - err = create_core_attrs(tdata, pdata->hwmon_dev, attr_no); + err = create_core_attrs(tdata, pdata->hwmon_dev, index); if (err) goto exit_free; return 0; exit_free: - pdata->core_data[attr_no] = NULL; + pdata->core_data[index] = NULL; kfree(tdata); ida_free: if (!pkg_flag) - ida_free(&pdata->ida, index); + ida_free(&pdata->ida, index - BASE_SYSFS_ATTR_NO); return err; } diff --git a/drivers/hwmon/gigabyte_waterforce.c b/drivers/hwmon/gigabyte_waterforce.c index 85e523775714..8129d7b3ceaf 100644 --- a/drivers/hwmon/gigabyte_waterforce.c +++ b/drivers/hwmon/gigabyte_waterforce.c @@ -146,7 +146,7 @@ static int waterforce_get_status(struct waterforce_data *priv) /* Send command for getting status */ ret = waterforce_write_expanded(priv, get_status_cmd, GET_STATUS_CMD_LENGTH); if (ret < 0) - return ret; + goto unlock_and_return; ret = wait_for_completion_interruptible_timeout(&priv->status_report_received, msecs_to_jiffies(STATUS_VALIDITY)); diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index d9733da8ea34..904816abb7c4 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -195,6 +195,7 @@ struct npcm7xx_cooling_device { struct npcm7xx_pwm_fan_data { void __iomem *pwm_base; void __iomem *fan_base; + int pwm_modules; unsigned long pwm_clk_freq; unsigned long fan_clk_freq; struct clk *pwm_clk; @@ -710,7 +711,7 @@ static u32 npcm7xx_pwm_init(struct npcm7xx_pwm_fan_data *data) /* Setting PWM Prescale Register value register to both modules */ prescale_val |= (prescale_val << NPCM7XX_PWM_PRESCALE_SHIFT_CH01); - for (m = 0; m < NPCM7XX_PWM_MAX_MODULES ; m++) { + for (m = 0; m < data->pwm_modules; m++) { iowrite32(prescale_val, NPCM7XX_PWM_REG_PR(data->pwm_base, m)); iowrite32(NPCM7XX_PWM_PRESCALE2_DEFAULT, NPCM7XX_PWM_REG_CSR(data->pwm_base, m)); @@ -946,6 +947,8 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) if (!data->info) return -EINVAL; + data->pwm_modules = data->info->pwm_max_channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm"); if (!res) { dev_err(dev, "pwm resource not found\n"); @@ -983,7 +986,7 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) output_freq = npcm7xx_pwm_init(data); npcm7xx_fan_init(data); - for (cnt = 0; cnt < NPCM7XX_PWM_MAX_MODULES ; cnt++) + for (cnt = 0; cnt < data->pwm_modules; cnt++) mutex_init(&data->pwm_lock[cnt]); for (i = 0; i < NPCM7XX_FAN_MAX_MODULE; i++) { diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c index b9bb469e2d8f..e5fa10b3b8bc 100644 --- a/drivers/hwmon/pmbus/mp2975.c +++ b/drivers/hwmon/pmbus/mp2975.c @@ -126,6 +126,21 @@ static const struct regulator_desc __maybe_unused mp2975_reg_desc[] = { #define to_mp2975_data(x) container_of(x, struct mp2975_data, info) +static int mp2975_read_byte_data(struct i2c_client *client, int page, int reg) +{ + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * Report direct format as configured by MFR_DC_LOOP_CTRL. + * Unlike on MP2971/MP2973 the reported VOUT_MODE isn't automatically + * internally updated, but always reads as PB_VOUT_MODE_VID. + */ + return PB_VOUT_MODE_DIRECT; + default: + return -ENODATA; + } +} + static int mp2975_read_word_helper(struct i2c_client *client, int page, int phase, u8 reg, u16 mask) @@ -869,6 +884,7 @@ static struct pmbus_driver_info mp2975_info = { PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | PMBUS_PHASE_VIRTUAL, + .read_byte_data = mp2975_read_byte_data, .read_word_data = mp2975_read_word_data, #if IS_ENABLED(CONFIG_SENSORS_MP2975_REGULATOR) .num_regulators = 1, diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 6e4516c2ab89..b67bc9e833c0 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -151,7 +151,7 @@ static int pwm_fan_power_on(struct pwm_fan_ctx *ctx) } state->enabled = true; - ret = pwm_apply_state(ctx->pwm, state); + ret = pwm_apply_might_sleep(ctx->pwm, state); if (ret) { dev_err(ctx->dev, "failed to enable PWM\n"); goto disable_regulator; @@ -181,7 +181,7 @@ static int pwm_fan_power_off(struct pwm_fan_ctx *ctx) state->enabled = false; state->duty_cycle = 0; - ret = pwm_apply_state(ctx->pwm, state); + ret = pwm_apply_might_sleep(ctx->pwm, state); if (ret) { dev_err(ctx->dev, "failed to disable PWM\n"); return ret; @@ -207,7 +207,7 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) period = state->period; state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); - ret = pwm_apply_state(ctx->pwm, state); + ret = pwm_apply_might_sleep(ctx->pwm, state); if (ret) return ret; ret = pwm_fan_power_on(ctx); @@ -278,7 +278,7 @@ static int pwm_fan_update_enable(struct pwm_fan_ctx *ctx, long val) state, &enable_regulator); - pwm_apply_state(ctx->pwm, state); + pwm_apply_might_sleep(ctx->pwm, state); pwm_fan_switch_power(ctx, enable_regulator); pwm_fan_update_state(ctx, 0); } diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index ada694ba9f95..0c0a932c00f3 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -84,8 +84,9 @@ static DEFINE_MUTEX(hwspinlock_tree_lock); * should decide between spin_trylock, spin_trylock_irq and * spin_trylock_irqsave. * - * Returns 0 if we successfully locked the hwspinlock or -EBUSY if + * Returns: %0 if we successfully locked the hwspinlock or -EBUSY if * the hwspinlock was already taken. + * * This function will never sleep. */ int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags) @@ -171,7 +172,7 @@ EXPORT_SYMBOL_GPL(__hwspin_trylock); /** * __hwspin_lock_timeout() - lock an hwspinlock with timeout limit * @hwlock: the hwspinlock to be locked - * @timeout: timeout value in msecs + * @to: timeout value in msecs * @mode: mode which controls whether local interrupts are disabled or not * @flags: a pointer to where the caller's interrupt state will be saved at (if * requested) @@ -199,9 +200,11 @@ EXPORT_SYMBOL_GPL(__hwspin_trylock); * to choose the appropriate @mode of operation, exactly the same way users * should decide between spin_lock, spin_lock_irq and spin_lock_irqsave. * - * Returns 0 when the @hwlock was successfully taken, and an appropriate + * Returns: %0 when the @hwlock was successfully taken, and an appropriate * error code otherwise (most notably -ETIMEDOUT if the @hwlock is still - * busy after @timeout msecs). The function will never sleep. + * busy after @timeout msecs). + * + * The function will never sleep. */ int __hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int to, int mode, unsigned long *flags) @@ -304,13 +307,12 @@ EXPORT_SYMBOL_GPL(__hwspin_unlock); /** * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id - * @bank: the hwspinlock device bank * @hwlock_spec: hwlock specifier as found in the device tree * * This is a simple translation function, suitable for hwspinlock platform * drivers that only has a lock specifier length of 1. * - * Returns a relative index of the lock within a specified bank on success, + * Returns: a relative index of the lock within a specified bank on success, * or -EINVAL on invalid specifier cell count. */ static inline int @@ -332,9 +334,10 @@ of_hwspin_lock_simple_xlate(const struct of_phandle_args *hwlock_spec) * hwspinlock device, so that it can be requested using the normal * hwspin_lock_request_specific() API. * - * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock - * device is not yet registered, -EINVAL on invalid args specifier value or an - * appropriate error as returned from the OF parsing of the DT client node. + * Returns: the global lock id number on success, -EPROBE_DEFER if the + * hwspinlock device is not yet registered, -EINVAL on invalid args + * specifier value or an appropriate error as returned from the OF parsing + * of the DT client node. */ int of_hwspin_lock_get_id(struct device_node *np, int index) { @@ -399,9 +402,10 @@ EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id); * the hwspinlock device, so that it can be requested using the normal * hwspin_lock_request_specific() API. * - * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock - * device is not yet registered, -EINVAL on invalid args specifier value or an - * appropriate error as returned from the OF parsing of the DT client node. + * Returns: the global lock id number on success, -EPROBE_DEFER if the + * hwspinlock device is not yet registered, -EINVAL on invalid args + * specifier value or an appropriate error as returned from the OF parsing + * of the DT client node. */ int of_hwspin_lock_get_id_byname(struct device_node *np, const char *name) { @@ -481,7 +485,7 @@ out: * * Should be called from a process context (might sleep) * - * Returns 0 on success, or an appropriate error code on failure + * Returns: %0 on success, or an appropriate error code on failure */ int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev, const struct hwspinlock_ops *ops, int base_id, int num_locks) @@ -529,7 +533,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_register); * * Should be called from a process context (might sleep) * - * Returns 0 on success, or an appropriate error code on failure + * Returns: %0 on success, or an appropriate error code on failure */ int hwspin_lock_unregister(struct hwspinlock_device *bank) { @@ -578,7 +582,7 @@ static int devm_hwspin_lock_device_match(struct device *dev, void *res, * * Should be called from a process context (might sleep) * - * Returns 0 on success, or an appropriate error code on failure + * Returns: %0 on success, or an appropriate error code on failure */ int devm_hwspin_lock_unregister(struct device *dev, struct hwspinlock_device *bank) @@ -607,7 +611,7 @@ EXPORT_SYMBOL_GPL(devm_hwspin_lock_unregister); * * Should be called from a process context (might sleep) * - * Returns 0 on success, or an appropriate error code on failure + * Returns: %0 on success, or an appropriate error code on failure */ int devm_hwspin_lock_register(struct device *dev, struct hwspinlock_device *bank, @@ -635,12 +639,13 @@ EXPORT_SYMBOL_GPL(devm_hwspin_lock_register); /** * __hwspin_lock_request() - tag an hwspinlock as used and power it up + * @hwlock: the target hwspinlock * * This is an internal function that prepares an hwspinlock instance * before it is given to the user. The function assumes that * hwspinlock_tree_lock is taken. * - * Returns 0 or positive to indicate success, and a negative value to + * Returns: %0 or positive to indicate success, and a negative value to * indicate an error (with the appropriate error code) */ static int __hwspin_lock_request(struct hwspinlock *hwlock) @@ -680,7 +685,7 @@ static int __hwspin_lock_request(struct hwspinlock *hwlock) * hwspin_lock_get_id() - retrieve id number of a given hwspinlock * @hwlock: a valid hwspinlock instance * - * Returns the id number of a given @hwlock, or -EINVAL if @hwlock is invalid. + * Returns: the id number of a given @hwlock, or -EINVAL if @hwlock is invalid. */ int hwspin_lock_get_id(struct hwspinlock *hwlock) { @@ -704,7 +709,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_get_id); * * Should be called from a process context (might sleep) * - * Returns the address of the assigned hwspinlock, or NULL on error + * Returns: the address of the assigned hwspinlock, or %NULL on error */ struct hwspinlock *hwspin_lock_request(void) { @@ -747,7 +752,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request); * * Should be called from a process context (might sleep) * - * Returns the address of the assigned hwspinlock, or NULL on error + * Returns: the address of the assigned hwspinlock, or %NULL on error */ struct hwspinlock *hwspin_lock_request_specific(unsigned int id) { @@ -795,7 +800,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request_specific); * * Should be called from a process context (might sleep) * - * Returns 0 on success, or an appropriate error code on failure + * Returns: %0 on success, or an appropriate error code on failure */ int hwspin_lock_free(struct hwspinlock *hwlock) { @@ -865,7 +870,7 @@ static void devm_hwspin_lock_release(struct device *dev, void *res) * * Should be called from a process context (might sleep) * - * Returns 0 on success, or an appropriate error code on failure + * Returns: %0 on success, or an appropriate error code on failure */ int devm_hwspin_lock_free(struct device *dev, struct hwspinlock *hwlock) { @@ -891,7 +896,7 @@ EXPORT_SYMBOL_GPL(devm_hwspin_lock_free); * * Should be called from a process context (might sleep) * - * Returns the address of the assigned hwspinlock, or NULL on error + * Returns: the address of the assigned hwspinlock, or %NULL on error */ struct hwspinlock *devm_hwspin_lock_request(struct device *dev) { @@ -926,7 +931,7 @@ EXPORT_SYMBOL_GPL(devm_hwspin_lock_request); * * Should be called from a process context (might sleep) * - * Returns the address of the assigned hwspinlock, or NULL on error + * Returns: the address of the assigned hwspinlock, or %NULL on error */ struct hwspinlock *devm_hwspin_lock_request_specific(struct device *dev, unsigned int id) diff --git a/drivers/hwspinlock/qcom_hwspinlock.c b/drivers/hwspinlock/qcom_hwspinlock.c index a0fd67fd2934..814dfe8697bf 100644 --- a/drivers/hwspinlock/qcom_hwspinlock.c +++ b/drivers/hwspinlock/qcom_hwspinlock.c @@ -115,7 +115,6 @@ static const struct of_device_id qcom_hwspinlock_of_match[] = { { .compatible = "qcom,sfpb-mutex", .data = &of_sfpb_mutex }, { .compatible = "qcom,tcsr-mutex", .data = &of_tcsr_mutex }, { .compatible = "qcom,apq8084-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, - { .compatible = "qcom,ipq6018-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, { .compatible = "qcom,msm8226-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, { .compatible = "qcom,msm8974-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, { .compatible = "qcom,msm8994-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 9fabe00a40d6..d7f0e231feb9 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1093,6 +1093,7 @@ static int coresight_validate_source(struct coresight_device *csdev, if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC && subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE && + subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM && subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) { dev_err(&csdev->dev, "wrong device subtype in %s\n", function); return -EINVAL; @@ -1162,6 +1163,7 @@ int coresight_enable(struct coresight_device *csdev) per_cpu(tracer_path, cpu) = path; break; case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: + case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: /* * Use the hash of source's device name as ID @@ -1212,6 +1214,7 @@ void coresight_disable(struct coresight_device *csdev) per_cpu(tracer_path, cpu) = NULL; break; case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: + case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); /* Find the path by the hash. */ diff --git a/drivers/hwtracing/coresight/coresight-dummy.c b/drivers/hwtracing/coresight/coresight-dummy.c index e4deafae7bc2..ac70c0b491be 100644 --- a/drivers/hwtracing/coresight/coresight-dummy.c +++ b/drivers/hwtracing/coresight/coresight-dummy.c @@ -122,14 +122,13 @@ static int dummy_probe(struct platform_device *pdev) return 0; } -static int dummy_remove(struct platform_device *pdev) +static void dummy_remove(struct platform_device *pdev) { struct dummy_drvdata *drvdata = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; pm_runtime_disable(dev); coresight_unregister(drvdata->csdev); - return 0; } static const struct of_device_id dummy_match[] = { @@ -140,7 +139,7 @@ static const struct of_device_id dummy_match[] = { static struct platform_driver dummy_driver = { .probe = dummy_probe, - .remove = dummy_remove, + .remove_new = dummy_remove, .driver = { .name = "coresight-dummy", .of_match_table = dummy_match, diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 89e8ed214ea4..a52cfcce25d6 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -68,6 +68,7 @@ PMU_FORMAT_ATTR(preset, "config:0-3"); PMU_FORMAT_ATTR(sinkid, "config2:0-31"); /* config ID - set if a system configuration is selected */ PMU_FORMAT_ATTR(configid, "config2:32-63"); +PMU_FORMAT_ATTR(cc_threshold, "config3:0-11"); /* @@ -101,6 +102,7 @@ static struct attribute *etm_config_formats_attr[] = { &format_attr_preset.attr, &format_attr_configid.attr, &format_attr_branch_broadcast.attr, + &format_attr_cc_threshold.attr, NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 34aee59dd147..ce1995a2827f 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -644,7 +644,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev, struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr; unsigned long cfg_hash; - int preset; + int preset, cc_threshold; /* Clear configuration from previous run */ memset(config, 0, sizeof(struct etmv4_config)); @@ -667,7 +667,12 @@ static int etm4_parse_event_config(struct coresight_device *csdev, if (attr->config & BIT(ETM_OPT_CYCACC)) { config->cfg |= TRCCONFIGR_CCI; /* TRM: Must program this for cycacc to work */ - config->ccctlr = ETM_CYC_THRESHOLD_DEFAULT; + cc_threshold = attr->config3 & ETM_CYC_THRESHOLD_MASK; + if (!cc_threshold) + cc_threshold = ETM_CYC_THRESHOLD_DEFAULT; + if (cc_threshold < drvdata->ccitmin) + cc_threshold = drvdata->ccitmin; + config->ccctlr = cc_threshold; } if (attr->config & BIT(ETM_OPT_TS)) { /* @@ -1150,6 +1155,41 @@ static void cpu_detect_trace_filtering(struct etmv4_drvdata *drvdata) drvdata->trfcr = trfcr; } +/* + * The following errata on applicable cpu ranges, affect the CCITMIN filed + * in TCRIDR3 register. Software read for the field returns 0x100 limiting + * the cycle threshold granularity, whereas the right value should have + * been 0x4, which is well supported in the hardware. + */ +static struct midr_range etm_wrong_ccitmin_cpus[] = { + /* Erratum #1490853 - Cortex-A76 */ + MIDR_RANGE(MIDR_CORTEX_A76, 0, 0, 4, 0), + /* Erratum #1490853 - Neoverse-N1 */ + MIDR_RANGE(MIDR_NEOVERSE_N1, 0, 0, 4, 0), + /* Erratum #1491015 - Cortex-A77 */ + MIDR_RANGE(MIDR_CORTEX_A77, 0, 0, 1, 0), + /* Erratum #1502854 - Cortex-X1 */ + MIDR_REV(MIDR_CORTEX_X1, 0, 0), + /* Erratum #1619801 - Neoverse-V1 */ + MIDR_REV(MIDR_NEOVERSE_V1, 0, 0), + {}, +}; + +static void etm4_fixup_wrong_ccitmin(struct etmv4_drvdata *drvdata) +{ + /* + * Erratum affected cpus will read 256 as the minimum + * instruction trace cycle counting threshold whereas + * the correct value should be 4 instead. Override the + * recorded value for 'drvdata->ccitmin' to workaround + * this problem. + */ + if (is_midr_in_range_list(read_cpuid_id(), etm_wrong_ccitmin_cpus)) { + if (drvdata->ccitmin == 256) + drvdata->ccitmin = 4; + } +} + static void etm4_init_arch_data(void *info) { u32 etmidr0; @@ -1214,6 +1254,8 @@ static void etm4_init_arch_data(void *info) etmidr3 = etm4x_relaxed_read32(csa, TRCIDR3); /* CCITMIN, bits[11:0] minimum threshold value that can be programmed */ drvdata->ccitmin = FIELD_GET(TRCIDR3_CCITMIN_MASK, etmidr3); + etm4_fixup_wrong_ccitmin(drvdata); + /* EXLEVEL_S, bits[19:16] Secure state instruction tracing */ drvdata->s_ex_level = FIELD_GET(TRCIDR3_EXLEVEL_S_MASK, etmidr3); drvdata->config.s_ex_level = drvdata->s_ex_level; @@ -2261,7 +2303,7 @@ static void etm4_remove_amba(struct amba_device *adev) etm4_remove_dev(drvdata); } -static int etm4_remove_platform_dev(struct platform_device *pdev) +static void etm4_remove_platform_dev(struct platform_device *pdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(&pdev->dev); @@ -2271,8 +2313,6 @@ static int etm4_remove_platform_dev(struct platform_device *pdev) if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk)) clk_put(drvdata->pclk); - - return 0; } static const struct amba_id etm4_ids[] = { @@ -2358,7 +2398,7 @@ MODULE_DEVICE_TABLE(acpi, etm4x_acpi_ids); static struct platform_driver etm4_platform_driver = { .probe = etm4_probe_platform_dev, - .remove = etm4_remove_platform_dev, + .remove_new = etm4_remove_platform_dev, .driver = { .name = "coresight-etm4x", .of_match_table = etm4_sysreg_match, diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 20e2e4cb7614..da17b6c49b0f 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -1036,7 +1036,7 @@ struct etmv4_drvdata { u8 ctxid_size; u8 vmid_size; u8 ccsize; - u8 ccitmin; + u16 ccitmin; u8 s_ex_level; u8 ns_ex_level; u8 q_support; diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index b8e150e45b27..a5b1fc787766 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -335,11 +335,10 @@ static int static_funnel_probe(struct platform_device *pdev) return ret; } -static int static_funnel_remove(struct platform_device *pdev) +static void static_funnel_remove(struct platform_device *pdev) { funnel_remove(&pdev->dev); pm_runtime_disable(&pdev->dev); - return 0; } static const struct of_device_id static_funnel_match[] = { @@ -360,7 +359,7 @@ MODULE_DEVICE_TABLE(acpi, static_funnel_ids); static struct platform_driver static_funnel_driver = { .probe = static_funnel_probe, - .remove = static_funnel_remove, + .remove_new = static_funnel_remove, .driver = { .name = "coresight-static-funnel", /* THIS_MODULE is taken care of by platform_driver_register() */ diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index b6be73034996..91d93060dda5 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -320,11 +320,10 @@ static int static_replicator_probe(struct platform_device *pdev) return ret; } -static int static_replicator_remove(struct platform_device *pdev) +static void static_replicator_remove(struct platform_device *pdev) { replicator_remove(&pdev->dev); pm_runtime_disable(&pdev->dev); - return 0; } #ifdef CONFIG_PM @@ -373,7 +372,7 @@ MODULE_DEVICE_TABLE(acpi, static_replicator_acpi_ids); static struct platform_driver static_replicator_driver = { .probe = static_replicator_probe, - .remove = static_replicator_remove, + .remove_new = static_replicator_remove, .driver = { .name = "coresight-static-replicator", /* THIS_MODULE is taken care of by platform_driver_register() */ diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index c106d142e632..7ec5365e2b64 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -10,6 +10,7 @@ #include <linux/device.h> #include <linux/idr.h> #include <linux/io.h> +#include <linux/iommu.h> #include <linux/err.h> #include <linux/fs.h> #include <linux/miscdevice.h> @@ -344,7 +345,14 @@ static const struct attribute_group coresight_tmc_mgmt_group = { .name = "mgmt", }; -static const struct attribute_group *coresight_tmc_groups[] = { +static const struct attribute_group *coresight_etf_groups[] = { + &coresight_tmc_group, + &coresight_tmc_mgmt_group, + NULL, +}; + +static const struct attribute_group *coresight_etr_groups[] = { + &coresight_etr_group, &coresight_tmc_group, &coresight_tmc_mgmt_group, NULL, @@ -465,6 +473,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) drvdata->memwidth = tmc_get_memwidth(devid); /* This device is not associated with a session */ drvdata->pid = -1; + drvdata->etr_mode = ETR_MODE_AUTO; if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { drvdata->size = tmc_etr_get_default_buffer_size(dev); @@ -474,16 +483,17 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) } desc.dev = dev; - desc.groups = coresight_tmc_groups; switch (drvdata->config_type) { case TMC_CONFIG_TYPE_ETB: + desc.groups = coresight_etf_groups; desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.ops = &tmc_etb_cs_ops; dev_list = &etb_devs; break; case TMC_CONFIG_TYPE_ETR: + desc.groups = coresight_etr_groups; desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM; desc.ops = &tmc_etr_cs_ops; @@ -496,6 +506,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) dev_list = &etr_devs; break; case TMC_CONFIG_TYPE_ETF: + desc.groups = coresight_etf_groups; desc.type = CORESIGHT_DEV_TYPE_LINKSINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 8311e1028ddb..af02ba5d5f15 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -26,6 +26,12 @@ struct etr_flat_buf { size_t size; }; +struct etr_buf_hw { + bool has_iommu; + bool has_etr_sg; + bool has_catu; +}; + /* * etr_perf_buffer - Perf buffer used for ETR * @drvdata - The ETR drvdaga this buffer has been allocated for. @@ -830,6 +836,22 @@ static inline int tmc_etr_mode_alloc_buf(int mode, } } +static void get_etr_buf_hw(struct device *dev, struct etr_buf_hw *buf_hw) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); + + buf_hw->has_iommu = iommu_get_domain_for_dev(dev->parent); + buf_hw->has_etr_sg = tmc_etr_has_cap(drvdata, TMC_ETR_SG); + buf_hw->has_catu = !!tmc_etr_get_catu_device(drvdata); +} + +static bool etr_can_use_flat_mode(struct etr_buf_hw *buf_hw, ssize_t etr_buf_size) +{ + bool has_sg = buf_hw->has_catu || buf_hw->has_etr_sg; + + return !has_sg || buf_hw->has_iommu || etr_buf_size < SZ_1M; +} + /* * tmc_alloc_etr_buf: Allocate a buffer use by ETR. * @drvdata : ETR device details. @@ -843,23 +865,22 @@ static struct etr_buf *tmc_alloc_etr_buf(struct tmc_drvdata *drvdata, int node, void **pages) { int rc = -ENOMEM; - bool has_etr_sg, has_iommu; - bool has_sg, has_catu; struct etr_buf *etr_buf; + struct etr_buf_hw buf_hw; struct device *dev = &drvdata->csdev->dev; - has_etr_sg = tmc_etr_has_cap(drvdata, TMC_ETR_SG); - has_iommu = iommu_get_domain_for_dev(dev->parent); - has_catu = !!tmc_etr_get_catu_device(drvdata); - - has_sg = has_catu || has_etr_sg; - + get_etr_buf_hw(dev, &buf_hw); etr_buf = kzalloc(sizeof(*etr_buf), GFP_KERNEL); if (!etr_buf) return ERR_PTR(-ENOMEM); etr_buf->size = size; + /* If there is user directive for buffer mode, try that first */ + if (drvdata->etr_mode != ETR_MODE_AUTO) + rc = tmc_etr_mode_alloc_buf(drvdata->etr_mode, drvdata, + etr_buf, node, pages); + /* * If we have to use an existing list of pages, we cannot reliably * use a contiguous DMA memory (even if we have an IOMMU). Otherwise, @@ -872,14 +893,13 @@ static struct etr_buf *tmc_alloc_etr_buf(struct tmc_drvdata *drvdata, * Fallback to available mechanisms. * */ - if (!pages && - (!has_sg || has_iommu || size < SZ_1M)) + if (rc && !pages && etr_can_use_flat_mode(&buf_hw, size)) rc = tmc_etr_mode_alloc_buf(ETR_MODE_FLAT, drvdata, etr_buf, node, pages); - if (rc && has_etr_sg) + if (rc && buf_hw.has_etr_sg) rc = tmc_etr_mode_alloc_buf(ETR_MODE_ETR_SG, drvdata, etr_buf, node, pages); - if (rc && has_catu) + if (rc && buf_hw.has_catu) rc = tmc_etr_mode_alloc_buf(ETR_MODE_CATU, drvdata, etr_buf, node, pages); if (rc) { @@ -1804,3 +1824,70 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) return 0; } + +static const char *const buf_modes_str[] = { + [ETR_MODE_FLAT] = "flat", + [ETR_MODE_ETR_SG] = "tmc-sg", + [ETR_MODE_CATU] = "catu", + [ETR_MODE_AUTO] = "auto", +}; + +static ssize_t buf_modes_available_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct etr_buf_hw buf_hw; + ssize_t size = 0; + + get_etr_buf_hw(dev, &buf_hw); + size += sysfs_emit(buf, "%s ", buf_modes_str[ETR_MODE_AUTO]); + size += sysfs_emit_at(buf, size, "%s ", buf_modes_str[ETR_MODE_FLAT]); + if (buf_hw.has_etr_sg) + size += sysfs_emit_at(buf, size, "%s ", buf_modes_str[ETR_MODE_ETR_SG]); + + if (buf_hw.has_catu) + size += sysfs_emit_at(buf, size, "%s ", buf_modes_str[ETR_MODE_CATU]); + + size += sysfs_emit_at(buf, size, "\n"); + return size; +} +static DEVICE_ATTR_RO(buf_modes_available); + +static ssize_t buf_mode_preferred_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return sysfs_emit(buf, "%s\n", buf_modes_str[drvdata->etr_mode]); +} + +static ssize_t buf_mode_preferred_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etr_buf_hw buf_hw; + + get_etr_buf_hw(dev, &buf_hw); + if (sysfs_streq(buf, buf_modes_str[ETR_MODE_FLAT])) + drvdata->etr_mode = ETR_MODE_FLAT; + else if (sysfs_streq(buf, buf_modes_str[ETR_MODE_ETR_SG]) && buf_hw.has_etr_sg) + drvdata->etr_mode = ETR_MODE_ETR_SG; + else if (sysfs_streq(buf, buf_modes_str[ETR_MODE_CATU]) && buf_hw.has_catu) + drvdata->etr_mode = ETR_MODE_CATU; + else if (sysfs_streq(buf, buf_modes_str[ETR_MODE_AUTO])) + drvdata->etr_mode = ETR_MODE_AUTO; + else + return -EINVAL; + return size; +} +static DEVICE_ATTR_RW(buf_mode_preferred); + +static struct attribute *coresight_etr_attrs[] = { + &dev_attr_buf_modes_available.attr, + &dev_attr_buf_mode_preferred.attr, + NULL, +}; + +const struct attribute_group coresight_etr_group = { + .attrs = coresight_etr_attrs, +}; diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 0ee48c5ba764..8dcb426ac3e7 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -135,6 +135,7 @@ enum etr_mode { ETR_MODE_FLAT, /* Uses contiguous flat buffer */ ETR_MODE_ETR_SG, /* Uses in-built TMC ETR SG mechanism */ ETR_MODE_CATU, /* Use SG mechanism in CATU */ + ETR_MODE_AUTO, /* Use the default mechanism */ }; struct etr_buf_operations; @@ -207,6 +208,7 @@ struct tmc_drvdata { enum tmc_mem_intf_width memwidth; u32 trigger_cntr; u32 etr_caps; + enum etr_mode etr_mode; struct idr idr; struct mutex idr_mutex; struct etr_buf *sysfs_buf; @@ -334,5 +336,6 @@ void tmc_etr_set_catu_ops(const struct etr_buf_operations *catu); void tmc_etr_remove_catu_ops(void); struct etr_buf *tmc_etr_get_buffer(struct coresight_device *csdev, enum cs_mode mode, void *data); +extern const struct attribute_group coresight_etr_group; #endif diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c index 8d2b9d29237d..5f82737c37bb 100644 --- a/drivers/hwtracing/coresight/coresight-tpda.c +++ b/drivers/hwtracing/coresight/coresight-tpda.c @@ -21,6 +21,80 @@ DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda"); +static bool coresight_device_is_tpdm(struct coresight_device *csdev) +{ + return (csdev->type == CORESIGHT_DEV_TYPE_SOURCE) && + (csdev->subtype.source_subtype == + CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM); +} + +/* + * Read the DSB element size from the TPDM device + * Returns + * The dsb element size read from the devicetree if available. + * 0 - Otherwise, with a warning once. + */ +static int tpdm_read_dsb_element_size(struct coresight_device *csdev) +{ + int rc = 0; + u8 size = 0; + + rc = fwnode_property_read_u8(dev_fwnode(csdev->dev.parent), + "qcom,dsb-element-size", &size); + if (rc) + dev_warn_once(&csdev->dev, + "Failed to read TPDM DSB Element size: %d\n", rc); + + return size; +} + +/* + * Search and read element data size from the TPDM node in + * the devicetree. Each input port of TPDA is connected to + * a TPDM. Different TPDM supports different types of dataset, + * and some may support more than one type of dataset. + * Parameter "inport" is used to pass in the input port number + * of TPDA, and it is set to -1 in the recursize call. + */ +static int tpda_get_element_size(struct coresight_device *csdev, + int inport) +{ + int dsb_size = -ENOENT; + int i, size; + struct coresight_device *in; + + for (i = 0; i < csdev->pdata->nr_inconns; i++) { + in = csdev->pdata->in_conns[i]->src_dev; + if (!in) + continue; + + /* Ignore the paths that do not match port */ + if (inport > 0 && + csdev->pdata->in_conns[i]->dest_port != inport) + continue; + + if (coresight_device_is_tpdm(in)) { + size = tpdm_read_dsb_element_size(in); + } else { + /* Recurse down the path */ + size = tpda_get_element_size(in, -1); + } + + if (size < 0) + return size; + + if (dsb_size < 0) { + /* Found a size, save it. */ + dsb_size = size; + } else { + /* Found duplicate TPDMs */ + return -EEXIST; + } + } + + return dsb_size; +} + /* Settings pre enabling port control register */ static void tpda_enable_pre_port(struct tpda_drvdata *drvdata) { @@ -32,26 +106,55 @@ static void tpda_enable_pre_port(struct tpda_drvdata *drvdata) writel_relaxed(val, drvdata->base + TPDA_CR); } -static void tpda_enable_port(struct tpda_drvdata *drvdata, int port) +static int tpda_enable_port(struct tpda_drvdata *drvdata, int port) { u32 val; + int size; val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port)); + /* + * Configure aggregator port n DSB data set element size + * Set the bit to 0 if the size is 32 + * Set the bit to 1 if the size is 64 + */ + size = tpda_get_element_size(drvdata->csdev, port); + switch (size) { + case 32: + val &= ~TPDA_Pn_CR_DSBSIZE; + break; + case 64: + val |= TPDA_Pn_CR_DSBSIZE; + break; + case 0: + return -EEXIST; + case -EEXIST: + dev_warn_once(&drvdata->csdev->dev, + "Detected multiple TPDMs on port %d", -EEXIST); + return -EEXIST; + default: + return -EINVAL; + } + /* Enable the port */ val |= TPDA_Pn_CR_ENA; writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port)); + + return 0; } -static void __tpda_enable(struct tpda_drvdata *drvdata, int port) +static int __tpda_enable(struct tpda_drvdata *drvdata, int port) { + int ret; + CS_UNLOCK(drvdata->base); if (!drvdata->csdev->enable) tpda_enable_pre_port(drvdata); - tpda_enable_port(drvdata, port); - + ret = tpda_enable_port(drvdata, port); CS_LOCK(drvdata->base); + + return ret; } static int tpda_enable(struct coresight_device *csdev, @@ -59,16 +162,19 @@ static int tpda_enable(struct coresight_device *csdev, struct coresight_connection *out) { struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + int ret = 0; spin_lock(&drvdata->spinlock); - if (atomic_read(&in->dest_refcnt) == 0) - __tpda_enable(drvdata, in->dest_port); + if (atomic_read(&in->dest_refcnt) == 0) { + ret = __tpda_enable(drvdata, in->dest_port); + if (!ret) { + atomic_inc(&in->dest_refcnt); + dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port); + } + } - atomic_inc(&in->dest_refcnt); spin_unlock(&drvdata->spinlock); - - dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port); - return 0; + return ret; } static void __tpda_disable(struct tpda_drvdata *drvdata, int port) diff --git a/drivers/hwtracing/coresight/coresight-tpda.h b/drivers/hwtracing/coresight/coresight-tpda.h index 0399678df312..b3b38fd41b64 100644 --- a/drivers/hwtracing/coresight/coresight-tpda.h +++ b/drivers/hwtracing/coresight/coresight-tpda.h @@ -10,6 +10,8 @@ #define TPDA_Pn_CR(n) (0x004 + (n * 4)) /* Aggregator port enable bit */ #define TPDA_Pn_CR_ENA BIT(0) +/* Aggregator port DSB data set element size bit */ +#define TPDA_Pn_CR_DSBSIZE BIT(8) #define TPDA_MAX_INPORTS 32 diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index f4854af0431e..97654aa4b772 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -4,6 +4,7 @@ */ #include <linux/amba/bus.h> +#include <linux/bitfield.h> #include <linux/bitmap.h> #include <linux/coresight.h> #include <linux/coresight-pmu.h> @@ -20,23 +21,265 @@ DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm"); -static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata) +/* Read dataset array member with the index number */ +static ssize_t tpdm_simple_dataset_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct tpdm_dataset_attribute *tpdm_attr = + container_of(attr, struct tpdm_dataset_attribute, attr); + + switch (tpdm_attr->mem) { + case DSB_EDGE_CTRL: + if (tpdm_attr->idx >= TPDM_DSB_MAX_EDCR) + return -EINVAL; + return sysfs_emit(buf, "0x%x\n", + drvdata->dsb->edge_ctrl[tpdm_attr->idx]); + case DSB_EDGE_CTRL_MASK: + if (tpdm_attr->idx >= TPDM_DSB_MAX_EDCMR) + return -EINVAL; + return sysfs_emit(buf, "0x%x\n", + drvdata->dsb->edge_ctrl_mask[tpdm_attr->idx]); + case DSB_TRIG_PATT: + if (tpdm_attr->idx >= TPDM_DSB_MAX_PATT) + return -EINVAL; + return sysfs_emit(buf, "0x%x\n", + drvdata->dsb->trig_patt[tpdm_attr->idx]); + case DSB_TRIG_PATT_MASK: + if (tpdm_attr->idx >= TPDM_DSB_MAX_PATT) + return -EINVAL; + return sysfs_emit(buf, "0x%x\n", + drvdata->dsb->trig_patt_mask[tpdm_attr->idx]); + case DSB_PATT: + if (tpdm_attr->idx >= TPDM_DSB_MAX_PATT) + return -EINVAL; + return sysfs_emit(buf, "0x%x\n", + drvdata->dsb->patt_val[tpdm_attr->idx]); + case DSB_PATT_MASK: + if (tpdm_attr->idx >= TPDM_DSB_MAX_PATT) + return -EINVAL; + return sysfs_emit(buf, "0x%x\n", + drvdata->dsb->patt_mask[tpdm_attr->idx]); + case DSB_MSR: + if (tpdm_attr->idx >= drvdata->dsb_msr_num) + return -EINVAL; + return sysfs_emit(buf, "0x%x\n", + drvdata->dsb->msr[tpdm_attr->idx]); + } + return -EINVAL; +} + +/* Write dataset array member with the index number */ +static ssize_t tpdm_simple_dataset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + unsigned long val; + ssize_t ret = size; + + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct tpdm_dataset_attribute *tpdm_attr = + container_of(attr, struct tpdm_dataset_attribute, attr); + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + switch (tpdm_attr->mem) { + case DSB_TRIG_PATT: + if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) + drvdata->dsb->trig_patt[tpdm_attr->idx] = val; + else + ret = -EINVAL; + break; + case DSB_TRIG_PATT_MASK: + if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) + drvdata->dsb->trig_patt_mask[tpdm_attr->idx] = val; + else + ret = -EINVAL; + break; + case DSB_PATT: + if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) + drvdata->dsb->patt_val[tpdm_attr->idx] = val; + else + ret = -EINVAL; + break; + case DSB_PATT_MASK: + if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) + drvdata->dsb->patt_mask[tpdm_attr->idx] = val; + else + ret = -EINVAL; + break; + case DSB_MSR: + if (tpdm_attr->idx < drvdata->dsb_msr_num) + drvdata->dsb->msr[tpdm_attr->idx] = val; + else + ret = -EINVAL; + break; + default: + ret = -EINVAL; + } + spin_unlock(&drvdata->spinlock); + + return ret; +} + +static bool tpdm_has_dsb_dataset(struct tpdm_drvdata *drvdata) +{ + return (drvdata->datasets & TPDM_PIDR0_DS_DSB); +} + +static umode_t tpdm_dsb_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (drvdata && tpdm_has_dsb_dataset(drvdata)) + return attr->mode; + + return 0; +} + +static umode_t tpdm_dsb_msr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct device_attribute *dev_attr = + container_of(attr, struct device_attribute, attr); + struct tpdm_dataset_attribute *tpdm_attr = + container_of(dev_attr, struct tpdm_dataset_attribute, attr); + + if (tpdm_attr->idx < drvdata->dsb_msr_num) + return attr->mode; + + return 0; +} + +static void tpdm_reset_datasets(struct tpdm_drvdata *drvdata) +{ + if (tpdm_has_dsb_dataset(drvdata)) { + memset(drvdata->dsb, 0, sizeof(struct dsb_dataset)); + + drvdata->dsb->trig_ts = true; + drvdata->dsb->trig_type = false; + } +} + +static void set_dsb_mode(struct tpdm_drvdata *drvdata, u32 *val) +{ + u32 mode; + + /* Set the test accurate mode */ + mode = TPDM_DSB_MODE_TEST(drvdata->dsb->mode); + *val &= ~TPDM_DSB_CR_TEST_MODE; + *val |= FIELD_PREP(TPDM_DSB_CR_TEST_MODE, mode); + + /* Set the byte lane for high-performance mode */ + mode = TPDM_DSB_MODE_HPBYTESEL(drvdata->dsb->mode); + *val &= ~TPDM_DSB_CR_HPSEL; + *val |= FIELD_PREP(TPDM_DSB_CR_HPSEL, mode); + + /* Set the performance mode */ + if (drvdata->dsb->mode & TPDM_DSB_MODE_PERF) + *val |= TPDM_DSB_CR_MODE; + else + *val &= ~TPDM_DSB_CR_MODE; +} + +static void set_dsb_tier(struct tpdm_drvdata *drvdata) { u32 val; - /* Set the enable bit of DSB control register to 1 */ + val = readl_relaxed(drvdata->base + TPDM_DSB_TIER); + + /* Clear all relevant fields */ + val &= ~(TPDM_DSB_TIER_PATT_TSENAB | TPDM_DSB_TIER_PATT_TYPE | + TPDM_DSB_TIER_XTRIG_TSENAB); + + /* Set pattern timestamp type and enablement */ + if (drvdata->dsb->patt_ts) { + val |= TPDM_DSB_TIER_PATT_TSENAB; + if (drvdata->dsb->patt_type) + val |= TPDM_DSB_TIER_PATT_TYPE; + else + val &= ~TPDM_DSB_TIER_PATT_TYPE; + } else { + val &= ~TPDM_DSB_TIER_PATT_TSENAB; + } + + /* Set trigger timestamp */ + if (drvdata->dsb->trig_ts) + val |= TPDM_DSB_TIER_XTRIG_TSENAB; + else + val &= ~TPDM_DSB_TIER_XTRIG_TSENAB; + + writel_relaxed(val, drvdata->base + TPDM_DSB_TIER); +} + +static void set_dsb_msr(struct tpdm_drvdata *drvdata) +{ + int i; + + for (i = 0; i < drvdata->dsb_msr_num; i++) + writel_relaxed(drvdata->dsb->msr[i], + drvdata->base + TPDM_DSB_MSR(i)); +} + +static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata) +{ + u32 val, i; + + for (i = 0; i < TPDM_DSB_MAX_EDCR; i++) + writel_relaxed(drvdata->dsb->edge_ctrl[i], + drvdata->base + TPDM_DSB_EDCR(i)); + for (i = 0; i < TPDM_DSB_MAX_EDCMR; i++) + writel_relaxed(drvdata->dsb->edge_ctrl_mask[i], + drvdata->base + TPDM_DSB_EDCMR(i)); + for (i = 0; i < TPDM_DSB_MAX_PATT; i++) { + writel_relaxed(drvdata->dsb->patt_val[i], + drvdata->base + TPDM_DSB_TPR(i)); + writel_relaxed(drvdata->dsb->patt_mask[i], + drvdata->base + TPDM_DSB_TPMR(i)); + writel_relaxed(drvdata->dsb->trig_patt[i], + drvdata->base + TPDM_DSB_XPR(i)); + writel_relaxed(drvdata->dsb->trig_patt_mask[i], + drvdata->base + TPDM_DSB_XPMR(i)); + } + + set_dsb_tier(drvdata); + + set_dsb_msr(drvdata); + val = readl_relaxed(drvdata->base + TPDM_DSB_CR); + /* Set the mode of DSB dataset */ + set_dsb_mode(drvdata, &val); + /* Set trigger type */ + if (drvdata->dsb->trig_type) + val |= TPDM_DSB_CR_TRIG_TYPE; + else + val &= ~TPDM_DSB_CR_TRIG_TYPE; + /* Set the enable bit of DSB control register to 1 */ val |= TPDM_DSB_CR_ENA; writel_relaxed(val, drvdata->base + TPDM_DSB_CR); } -/* TPDM enable operations */ +/* + * TPDM enable operations + * The TPDM or Monitor serves as data collection component for various + * dataset types. It covers Basic Counts(BC), Tenure Counts(TC), + * Continuous Multi-Bit(CMB), Multi-lane CMB(MCMB) and Discrete Single + * Bit(DSB). This function will initialize the configuration according + * to the dataset type supported by the TPDM. + */ static void __tpdm_enable(struct tpdm_drvdata *drvdata) { CS_UNLOCK(drvdata->base); - /* Check if DSB datasets is present for TPDM. */ - if (drvdata->datasets & TPDM_PIDR0_DS_DSB) + if (tpdm_has_dsb_dataset(drvdata)) tpdm_enable_dsb(drvdata); CS_LOCK(drvdata->base); @@ -76,8 +319,7 @@ static void __tpdm_disable(struct tpdm_drvdata *drvdata) { CS_UNLOCK(drvdata->base); - /* Check if DSB datasets is present for TPDM. */ - if (drvdata->datasets & TPDM_PIDR0_DS_DSB) + if (tpdm_has_dsb_dataset(drvdata)) tpdm_disable_dsb(drvdata); CS_LOCK(drvdata->base); @@ -110,16 +352,45 @@ static const struct coresight_ops tpdm_cs_ops = { .source_ops = &tpdm_source_ops, }; -static void tpdm_init_default_data(struct tpdm_drvdata *drvdata) +static int tpdm_datasets_setup(struct tpdm_drvdata *drvdata) { u32 pidr; - CS_UNLOCK(drvdata->base); /* Get the datasets present on the TPDM. */ pidr = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR0); drvdata->datasets |= pidr & GENMASK(TPDM_DATASETS - 1, 0); - CS_LOCK(drvdata->base); + + if (tpdm_has_dsb_dataset(drvdata) && (!drvdata->dsb)) { + drvdata->dsb = devm_kzalloc(drvdata->dev, + sizeof(*drvdata->dsb), GFP_KERNEL); + if (!drvdata->dsb) + return -ENOMEM; + } + tpdm_reset_datasets(drvdata); + + return 0; +} + +static ssize_t reset_dataset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + int ret = 0; + unsigned long val; + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 0, &val); + if (ret || val != 1) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + tpdm_reset_datasets(drvdata); + spin_unlock(&drvdata->spinlock); + + return size; } +static DEVICE_ATTR_WO(reset_dataset); /* * value 1: 64 bits test data @@ -161,6 +432,7 @@ static ssize_t integration_test_store(struct device *dev, static DEVICE_ATTR_WO(integration_test); static struct attribute *tpdm_attrs[] = { + &dev_attr_reset_dataset.attr, &dev_attr_integration_test.attr, NULL, }; @@ -169,8 +441,421 @@ static struct attribute_group tpdm_attr_grp = { .attrs = tpdm_attrs, }; +static ssize_t dsb_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return sysfs_emit(buf, "%x\n", drvdata->dsb->mode); +} + +static ssize_t dsb_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + + if ((kstrtoul(buf, 0, &val)) || (val < 0) || + (val & ~TPDM_DSB_MODE_MASK)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->dsb->mode = val & TPDM_DSB_MODE_MASK; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(dsb_mode); + +static ssize_t ctrl_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return sysfs_emit(buf, "%u\n", + (unsigned int)drvdata->dsb->edge_ctrl_idx); +} + +/* + * The EDCR registers can include up to 16 32-bit registers, and each + * one can be configured to control up to 16 edge detections(2 bits + * control one edge detection). So a total 256 edge detections can be + * configured. This function provides a way to set the index number of + * the edge detection which needs to be configured. + */ +static ssize_t ctrl_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + + if ((kstrtoul(buf, 0, &val)) || (val >= TPDM_DSB_MAX_LINES)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->dsb->edge_ctrl_idx = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(ctrl_idx); + +/* + * This function is used to control the edge detection according + * to the index number that has been set. + * "edge_ctrl" should be one of the following values. + * 0 - Rising edge detection + * 1 - Falling edge detection + * 2 - Rising and falling edge detection (toggle detection) + */ +static ssize_t ctrl_val_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val, edge_ctrl; + int reg; + + if ((kstrtoul(buf, 0, &edge_ctrl)) || (edge_ctrl > 0x2)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* + * There are 2 bit per DSB Edge Control line. + * Thus we have 16 lines in a 32bit word. + */ + reg = EDCR_TO_WORD_IDX(drvdata->dsb->edge_ctrl_idx); + val = drvdata->dsb->edge_ctrl[reg]; + val &= ~EDCR_TO_WORD_MASK(drvdata->dsb->edge_ctrl_idx); + val |= EDCR_TO_WORD_VAL(edge_ctrl, drvdata->dsb->edge_ctrl_idx); + drvdata->dsb->edge_ctrl[reg] = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_WO(ctrl_val); + +static ssize_t ctrl_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + u32 set; + int reg; + + if ((kstrtoul(buf, 0, &val)) || (val & ~1UL)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* + * There is 1 bit per DSB Edge Control Mark line. + * Thus we have 32 lines in a 32bit word. + */ + reg = EDCMR_TO_WORD_IDX(drvdata->dsb->edge_ctrl_idx); + set = drvdata->dsb->edge_ctrl_mask[reg]; + if (val) + set |= BIT(EDCMR_TO_WORD_SHIFT(drvdata->dsb->edge_ctrl_idx)); + else + set &= ~BIT(EDCMR_TO_WORD_SHIFT(drvdata->dsb->edge_ctrl_idx)); + drvdata->dsb->edge_ctrl_mask[reg] = set; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_WO(ctrl_mask); + +static ssize_t enable_ts_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return sysfs_emit(buf, "%u\n", + (unsigned int)drvdata->dsb->patt_ts); +} + +/* + * value 1: Enable/Disable DSB pattern timestamp + */ +static ssize_t enable_ts_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + + if ((kstrtoul(buf, 0, &val)) || (val & ~1UL)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->dsb->patt_ts = !!val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(enable_ts); + +static ssize_t set_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return sysfs_emit(buf, "%u\n", + (unsigned int)drvdata->dsb->patt_type); +} + +/* + * value 1: Set DSB pattern type + */ +static ssize_t set_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + + if ((kstrtoul(buf, 0, &val)) || (val & ~1UL)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->dsb->patt_type = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(set_type); + +static ssize_t dsb_trig_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return sysfs_emit(buf, "%u\n", + (unsigned int)drvdata->dsb->trig_type); +} + +/* + * Trigger type (boolean): + * false - Disable trigger type. + * true - Enable trigger type. + */ +static ssize_t dsb_trig_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + + if ((kstrtoul(buf, 0, &val)) || (val & ~1UL)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + if (val) + drvdata->dsb->trig_type = true; + else + drvdata->dsb->trig_type = false; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(dsb_trig_type); + +static ssize_t dsb_trig_ts_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return sysfs_emit(buf, "%u\n", + (unsigned int)drvdata->dsb->trig_ts); +} + +/* + * Trigger timestamp (boolean): + * false - Disable trigger timestamp. + * true - Enable trigger timestamp. + */ +static ssize_t dsb_trig_ts_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + + if ((kstrtoul(buf, 0, &val)) || (val & ~1UL)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + if (val) + drvdata->dsb->trig_ts = true; + else + drvdata->dsb->trig_ts = false; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(dsb_trig_ts); + +static struct attribute *tpdm_dsb_edge_attrs[] = { + &dev_attr_ctrl_idx.attr, + &dev_attr_ctrl_val.attr, + &dev_attr_ctrl_mask.attr, + DSB_EDGE_CTRL_ATTR(0), + DSB_EDGE_CTRL_ATTR(1), + DSB_EDGE_CTRL_ATTR(2), + DSB_EDGE_CTRL_ATTR(3), + DSB_EDGE_CTRL_ATTR(4), + DSB_EDGE_CTRL_ATTR(5), + DSB_EDGE_CTRL_ATTR(6), + DSB_EDGE_CTRL_ATTR(7), + DSB_EDGE_CTRL_ATTR(8), + DSB_EDGE_CTRL_ATTR(9), + DSB_EDGE_CTRL_ATTR(10), + DSB_EDGE_CTRL_ATTR(11), + DSB_EDGE_CTRL_ATTR(12), + DSB_EDGE_CTRL_ATTR(13), + DSB_EDGE_CTRL_ATTR(14), + DSB_EDGE_CTRL_ATTR(15), + DSB_EDGE_CTRL_MASK_ATTR(0), + DSB_EDGE_CTRL_MASK_ATTR(1), + DSB_EDGE_CTRL_MASK_ATTR(2), + DSB_EDGE_CTRL_MASK_ATTR(3), + DSB_EDGE_CTRL_MASK_ATTR(4), + DSB_EDGE_CTRL_MASK_ATTR(5), + DSB_EDGE_CTRL_MASK_ATTR(6), + DSB_EDGE_CTRL_MASK_ATTR(7), + NULL, +}; + +static struct attribute *tpdm_dsb_trig_patt_attrs[] = { + DSB_TRIG_PATT_ATTR(0), + DSB_TRIG_PATT_ATTR(1), + DSB_TRIG_PATT_ATTR(2), + DSB_TRIG_PATT_ATTR(3), + DSB_TRIG_PATT_ATTR(4), + DSB_TRIG_PATT_ATTR(5), + DSB_TRIG_PATT_ATTR(6), + DSB_TRIG_PATT_ATTR(7), + DSB_TRIG_PATT_MASK_ATTR(0), + DSB_TRIG_PATT_MASK_ATTR(1), + DSB_TRIG_PATT_MASK_ATTR(2), + DSB_TRIG_PATT_MASK_ATTR(3), + DSB_TRIG_PATT_MASK_ATTR(4), + DSB_TRIG_PATT_MASK_ATTR(5), + DSB_TRIG_PATT_MASK_ATTR(6), + DSB_TRIG_PATT_MASK_ATTR(7), + NULL, +}; + +static struct attribute *tpdm_dsb_patt_attrs[] = { + DSB_PATT_ATTR(0), + DSB_PATT_ATTR(1), + DSB_PATT_ATTR(2), + DSB_PATT_ATTR(3), + DSB_PATT_ATTR(4), + DSB_PATT_ATTR(5), + DSB_PATT_ATTR(6), + DSB_PATT_ATTR(7), + DSB_PATT_MASK_ATTR(0), + DSB_PATT_MASK_ATTR(1), + DSB_PATT_MASK_ATTR(2), + DSB_PATT_MASK_ATTR(3), + DSB_PATT_MASK_ATTR(4), + DSB_PATT_MASK_ATTR(5), + DSB_PATT_MASK_ATTR(6), + DSB_PATT_MASK_ATTR(7), + &dev_attr_enable_ts.attr, + &dev_attr_set_type.attr, + NULL, +}; + +static struct attribute *tpdm_dsb_msr_attrs[] = { + DSB_MSR_ATTR(0), + DSB_MSR_ATTR(1), + DSB_MSR_ATTR(2), + DSB_MSR_ATTR(3), + DSB_MSR_ATTR(4), + DSB_MSR_ATTR(5), + DSB_MSR_ATTR(6), + DSB_MSR_ATTR(7), + DSB_MSR_ATTR(8), + DSB_MSR_ATTR(9), + DSB_MSR_ATTR(10), + DSB_MSR_ATTR(11), + DSB_MSR_ATTR(12), + DSB_MSR_ATTR(13), + DSB_MSR_ATTR(14), + DSB_MSR_ATTR(15), + DSB_MSR_ATTR(16), + DSB_MSR_ATTR(17), + DSB_MSR_ATTR(18), + DSB_MSR_ATTR(19), + DSB_MSR_ATTR(20), + DSB_MSR_ATTR(21), + DSB_MSR_ATTR(22), + DSB_MSR_ATTR(23), + DSB_MSR_ATTR(24), + DSB_MSR_ATTR(25), + DSB_MSR_ATTR(26), + DSB_MSR_ATTR(27), + DSB_MSR_ATTR(28), + DSB_MSR_ATTR(29), + DSB_MSR_ATTR(30), + DSB_MSR_ATTR(31), + NULL, +}; + +static struct attribute *tpdm_dsb_attrs[] = { + &dev_attr_dsb_mode.attr, + &dev_attr_dsb_trig_ts.attr, + &dev_attr_dsb_trig_type.attr, + NULL, +}; + +static struct attribute_group tpdm_dsb_attr_grp = { + .attrs = tpdm_dsb_attrs, + .is_visible = tpdm_dsb_is_visible, +}; + +static struct attribute_group tpdm_dsb_edge_grp = { + .attrs = tpdm_dsb_edge_attrs, + .is_visible = tpdm_dsb_is_visible, + .name = "dsb_edge", +}; + +static struct attribute_group tpdm_dsb_trig_patt_grp = { + .attrs = tpdm_dsb_trig_patt_attrs, + .is_visible = tpdm_dsb_is_visible, + .name = "dsb_trig_patt", +}; + +static struct attribute_group tpdm_dsb_patt_grp = { + .attrs = tpdm_dsb_patt_attrs, + .is_visible = tpdm_dsb_is_visible, + .name = "dsb_patt", +}; + +static struct attribute_group tpdm_dsb_msr_grp = { + .attrs = tpdm_dsb_msr_attrs, + .is_visible = tpdm_dsb_msr_is_visible, + .name = "dsb_msr", +}; + static const struct attribute_group *tpdm_attr_grps[] = { &tpdm_attr_grp, + &tpdm_dsb_attr_grp, + &tpdm_dsb_edge_grp, + &tpdm_dsb_trig_patt_grp, + &tpdm_dsb_patt_grp, + &tpdm_dsb_msr_grp, NULL, }; @@ -181,6 +866,7 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id) struct coresight_platform_data *pdata; struct tpdm_drvdata *drvdata; struct coresight_desc desc = { 0 }; + int ret; pdata = coresight_get_platform_data(dev); if (IS_ERR(pdata)) @@ -200,12 +886,20 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id) drvdata->base = base; + ret = tpdm_datasets_setup(drvdata); + if (ret) + return ret; + + if (drvdata && tpdm_has_dsb_dataset(drvdata)) + of_property_read_u32(drvdata->dev->of_node, + "qcom,dsb-msrs-num", &drvdata->dsb_msr_num); + /* Set up coresight component description */ desc.name = coresight_alloc_device_name(&tpdm_devs, dev); if (!desc.name) return -ENOMEM; desc.type = CORESIGHT_DEV_TYPE_SOURCE; - desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS; + desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM; desc.ops = &tpdm_cs_ops; desc.pdata = adev->dev.platform_data; desc.dev = &adev->dev; @@ -216,7 +910,7 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id) return PTR_ERR(drvdata->csdev); spin_lock_init(&drvdata->spinlock); - tpdm_init_default_data(drvdata); + /* Decrease pm refcount when probe is done.*/ pm_runtime_put(&adev->dev); diff --git a/drivers/hwtracing/coresight/coresight-tpdm.h b/drivers/hwtracing/coresight/coresight-tpdm.h index 543854043a2d..4115b2a17b8d 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.h +++ b/drivers/hwtracing/coresight/coresight-tpdm.h @@ -11,8 +11,52 @@ /* DSB Subunit Registers */ #define TPDM_DSB_CR (0x780) +#define TPDM_DSB_TIER (0x784) +#define TPDM_DSB_TPR(n) (0x788 + (n * 4)) +#define TPDM_DSB_TPMR(n) (0x7A8 + (n * 4)) +#define TPDM_DSB_XPR(n) (0x7C8 + (n * 4)) +#define TPDM_DSB_XPMR(n) (0x7E8 + (n * 4)) +#define TPDM_DSB_EDCR(n) (0x808 + (n * 4)) +#define TPDM_DSB_EDCMR(n) (0x848 + (n * 4)) +#define TPDM_DSB_MSR(n) (0x980 + (n * 4)) + /* Enable bit for DSB subunit */ #define TPDM_DSB_CR_ENA BIT(0) +/* Enable bit for DSB subunit perfmance mode */ +#define TPDM_DSB_CR_MODE BIT(1) +/* Enable bit for DSB subunit trigger type */ +#define TPDM_DSB_CR_TRIG_TYPE BIT(12) +/* Data bits for DSB high performace mode */ +#define TPDM_DSB_CR_HPSEL GENMASK(6, 2) +/* Data bits for DSB test mode */ +#define TPDM_DSB_CR_TEST_MODE GENMASK(10, 9) + +/* Enable bit for DSB subunit pattern timestamp */ +#define TPDM_DSB_TIER_PATT_TSENAB BIT(0) +/* Enable bit for DSB subunit trigger timestamp */ +#define TPDM_DSB_TIER_XTRIG_TSENAB BIT(1) +/* Bit for DSB subunit pattern type */ +#define TPDM_DSB_TIER_PATT_TYPE BIT(2) + +/* DSB programming modes */ +/* DSB mode bits mask */ +#define TPDM_DSB_MODE_MASK GENMASK(8, 0) +/* Test mode control bit*/ +#define TPDM_DSB_MODE_TEST(val) (val & GENMASK(1, 0)) +/* Performance mode */ +#define TPDM_DSB_MODE_PERF BIT(3) +/* High performance mode */ +#define TPDM_DSB_MODE_HPBYTESEL(val) (val & GENMASK(8, 4)) + +#define EDCRS_PER_WORD 16 +#define EDCR_TO_WORD_IDX(r) ((r) / EDCRS_PER_WORD) +#define EDCR_TO_WORD_SHIFT(r) ((r % EDCRS_PER_WORD) * 2) +#define EDCR_TO_WORD_VAL(val, r) (val << EDCR_TO_WORD_SHIFT(r)) +#define EDCR_TO_WORD_MASK(r) EDCR_TO_WORD_VAL(0x3, r) + +#define EDCMRS_PER_WORD 32 +#define EDCMR_TO_WORD_IDX(r) ((r) / EDCMRS_PER_WORD) +#define EDCMR_TO_WORD_SHIFT(r) ((r) % EDCMRS_PER_WORD) /* TPDM integration test registers */ #define TPDM_ITATBCNTRL (0xEF0) @@ -40,6 +84,95 @@ #define TPDM_PIDR0_DS_IMPDEF BIT(0) #define TPDM_PIDR0_DS_DSB BIT(1) +#define TPDM_DSB_MAX_LINES 256 +/* MAX number of EDCR registers */ +#define TPDM_DSB_MAX_EDCR 16 +/* MAX number of EDCMR registers */ +#define TPDM_DSB_MAX_EDCMR 8 +/* MAX number of DSB pattern */ +#define TPDM_DSB_MAX_PATT 8 +/* MAX number of DSB MSR */ +#define TPDM_DSB_MAX_MSR 32 + +#define tpdm_simple_dataset_ro(name, mem, idx) \ + (&((struct tpdm_dataset_attribute[]) { \ + { \ + __ATTR(name, 0444, tpdm_simple_dataset_show, NULL), \ + mem, \ + idx, \ + } \ + })[0].attr.attr) + +#define tpdm_simple_dataset_rw(name, mem, idx) \ + (&((struct tpdm_dataset_attribute[]) { \ + { \ + __ATTR(name, 0644, tpdm_simple_dataset_show, \ + tpdm_simple_dataset_store), \ + mem, \ + idx, \ + } \ + })[0].attr.attr) + +#define DSB_EDGE_CTRL_ATTR(nr) \ + tpdm_simple_dataset_ro(edcr##nr, \ + DSB_EDGE_CTRL, nr) + +#define DSB_EDGE_CTRL_MASK_ATTR(nr) \ + tpdm_simple_dataset_ro(edcmr##nr, \ + DSB_EDGE_CTRL_MASK, nr) + +#define DSB_TRIG_PATT_ATTR(nr) \ + tpdm_simple_dataset_rw(xpr##nr, \ + DSB_TRIG_PATT, nr) + +#define DSB_TRIG_PATT_MASK_ATTR(nr) \ + tpdm_simple_dataset_rw(xpmr##nr, \ + DSB_TRIG_PATT_MASK, nr) + +#define DSB_PATT_ATTR(nr) \ + tpdm_simple_dataset_rw(tpr##nr, \ + DSB_PATT, nr) + +#define DSB_PATT_MASK_ATTR(nr) \ + tpdm_simple_dataset_rw(tpmr##nr, \ + DSB_PATT_MASK, nr) + +#define DSB_MSR_ATTR(nr) \ + tpdm_simple_dataset_rw(msr##nr, \ + DSB_MSR, nr) + +/** + * struct dsb_dataset - specifics associated to dsb dataset + * @mode: DSB programming mode + * @edge_ctrl_idx Index number of the edge control + * @edge_ctrl: Save value for edge control + * @edge_ctrl_mask: Save value for edge control mask + * @patt_val: Save value for pattern + * @patt_mask: Save value for pattern mask + * @trig_patt: Save value for trigger pattern + * @trig_patt_mask: Save value for trigger pattern mask + * @msr Save value for MSR + * @patt_ts: Enable/Disable pattern timestamp + * @patt_type: Set pattern type + * @trig_ts: Enable/Disable trigger timestamp. + * @trig_type: Enable/Disable trigger type. + */ +struct dsb_dataset { + u32 mode; + u32 edge_ctrl_idx; + u32 edge_ctrl[TPDM_DSB_MAX_EDCR]; + u32 edge_ctrl_mask[TPDM_DSB_MAX_EDCMR]; + u32 patt_val[TPDM_DSB_MAX_PATT]; + u32 patt_mask[TPDM_DSB_MAX_PATT]; + u32 trig_patt[TPDM_DSB_MAX_PATT]; + u32 trig_patt_mask[TPDM_DSB_MAX_PATT]; + u32 msr[TPDM_DSB_MAX_MSR]; + bool patt_ts; + bool patt_type; + bool trig_ts; + bool trig_type; +}; + /** * struct tpdm_drvdata - specifics associated to an TPDM component * @base: memory mapped base address for this component. @@ -48,6 +181,8 @@ * @spinlock: lock for the drvdata value. * @enable: enable status of the component. * @datasets: The datasets types present of the TPDM. + * @dsb Specifics associated to TPDM DSB. + * @dsb_msr_num Number of MSR supported by DSB TPDM */ struct tpdm_drvdata { @@ -57,6 +192,32 @@ struct tpdm_drvdata { spinlock_t spinlock; bool enable; unsigned long datasets; + struct dsb_dataset *dsb; + u32 dsb_msr_num; +}; + +/* Enumerate members of various datasets */ +enum dataset_mem { + DSB_EDGE_CTRL, + DSB_EDGE_CTRL_MASK, + DSB_TRIG_PATT, + DSB_TRIG_PATT_MASK, + DSB_PATT, + DSB_PATT_MASK, + DSB_MSR, +}; + +/** + * struct tpdm_dataset_attribute - Record the member variables and + * index number of datasets that need to be operated by sysfs file + * @attr: The device attribute + * @mem: The member in the dataset data structure + * @idx: The index number of the array data + */ +struct tpdm_dataset_attribute { + struct device_attribute attr; + enum dataset_mem mem; + u32 idx; }; #endif /* _CORESIGHT_CORESIGHT_TPDM_H */ diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index e20c1c6acc73..6136776482e6 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -1253,8 +1253,18 @@ static void arm_trbe_register_coresight_cpu(struct trbe_drvdata *drvdata, int cp desc.name = devm_kasprintf(dev, GFP_KERNEL, "trbe%d", cpu); if (!desc.name) goto cpu_clear; - - desc.pdata = coresight_get_platform_data(dev); + /* + * TRBE coresight devices do not need regular connections + * information, as the paths get built between all percpu + * source and their respective percpu sink devices. Though + * coresight_register() expect device connections via the + * platform_data, which TRBE devices do not have. As they + * are not real ACPI devices, coresight_get_platform_data() + * ends up failing. Instead let's allocate a dummy zeroed + * coresight_platform_data structure and assign that back + * into the device for that purpose. + */ + desc.pdata = devm_kzalloc(dev, sizeof(*desc.pdata), GFP_KERNEL); if (IS_ERR(desc.pdata)) goto cpu_clear; @@ -1520,14 +1530,13 @@ probe_failed: return ret; } -static int arm_trbe_device_remove(struct platform_device *pdev) +static void arm_trbe_device_remove(struct platform_device *pdev) { struct trbe_drvdata *drvdata = platform_get_drvdata(pdev); arm_trbe_remove_cpuhp(drvdata); arm_trbe_remove_coresight(drvdata); arm_trbe_remove_irq(drvdata); - return 0; } static const struct of_device_id arm_trbe_of_match[] = { @@ -1536,14 +1545,23 @@ static const struct of_device_id arm_trbe_of_match[] = { }; MODULE_DEVICE_TABLE(of, arm_trbe_of_match); +#ifdef CONFIG_ACPI +static const struct platform_device_id arm_trbe_acpi_match[] = { + { ARMV8_TRBE_PDEV_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(platform, arm_trbe_acpi_match); +#endif + static struct platform_driver arm_trbe_driver = { + .id_table = ACPI_PTR(arm_trbe_acpi_match), .driver = { .name = DRVNAME, .of_match_table = of_match_ptr(arm_trbe_of_match), .suppress_bind_attrs = true, }, .probe = arm_trbe_device_probe, - .remove = arm_trbe_device_remove, + .remove_new = arm_trbe_device_remove, }; static int __init arm_trbe_init(void) diff --git a/drivers/hwtracing/coresight/coresight-trbe.h b/drivers/hwtracing/coresight/coresight-trbe.h index e915e749be55..45202c48acce 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.h +++ b/drivers/hwtracing/coresight/coresight-trbe.h @@ -7,11 +7,13 @@ * * Author: Anshuman Khandual <anshuman.khandual@arm.com> */ +#include <linux/acpi.h> #include <linux/coresight.h> #include <linux/device.h> #include <linux/irq.h> #include <linux/kernel.h> #include <linux/of.h> +#include <linux/perf/arm_pmu.h> #include <linux/platform_device.h> #include <linux/smp.h> diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index 6e32d31a95fe..10e886455b8b 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -97,27 +97,19 @@ static int smb_open(struct inode *inode, struct file *file) { struct smb_drv_data *drvdata = container_of(file->private_data, struct smb_drv_data, miscdev); - int ret = 0; - spin_lock(&drvdata->spinlock); + guard(spinlock)(&drvdata->spinlock); - if (drvdata->reading) { - ret = -EBUSY; - goto out; - } + if (drvdata->reading) + return -EBUSY; - if (atomic_read(&drvdata->csdev->refcnt)) { - ret = -EBUSY; - goto out; - } + if (atomic_read(&drvdata->csdev->refcnt)) + return -EBUSY; smb_update_data_size(drvdata); - drvdata->reading = true; -out: - spin_unlock(&drvdata->spinlock); - return ret; + return 0; } static ssize_t smb_read(struct file *file, char __user *data, size_t len, @@ -160,9 +152,8 @@ static int smb_release(struct inode *inode, struct file *file) struct smb_drv_data *drvdata = container_of(file->private_data, struct smb_drv_data, miscdev); - spin_lock(&drvdata->spinlock); + guard(spinlock)(&drvdata->spinlock); drvdata->reading = false; - spin_unlock(&drvdata->spinlock); return 0; } @@ -255,19 +246,15 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode, struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); int ret = 0; - spin_lock(&drvdata->spinlock); + guard(spinlock)(&drvdata->spinlock); /* Do nothing, the trace data is reading by other interface now */ - if (drvdata->reading) { - ret = -EBUSY; - goto out; - } + if (drvdata->reading) + return -EBUSY; /* Do nothing, the SMB is already enabled as other mode */ - if (drvdata->mode != CS_MODE_DISABLED && drvdata->mode != mode) { - ret = -EBUSY; - goto out; - } + if (drvdata->mode != CS_MODE_DISABLED && drvdata->mode != mode) + return -EBUSY; switch (mode) { case CS_MODE_SYSFS: @@ -281,13 +268,10 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode, } if (ret) - goto out; + return ret; atomic_inc(&csdev->refcnt); - dev_dbg(&csdev->dev, "Ultrasoc SMB enabled\n"); -out: - spin_unlock(&drvdata->spinlock); return ret; } @@ -295,19 +279,14 @@ out: static int smb_disable(struct coresight_device *csdev) { struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); - int ret = 0; - spin_lock(&drvdata->spinlock); + guard(spinlock)(&drvdata->spinlock); - if (drvdata->reading) { - ret = -EBUSY; - goto out; - } + if (drvdata->reading) + return -EBUSY; - if (atomic_dec_return(&csdev->refcnt)) { - ret = -EBUSY; - goto out; - } + if (atomic_dec_return(&csdev->refcnt)) + return -EBUSY; /* Complain if we (somehow) got out of sync */ WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); @@ -317,12 +296,9 @@ static int smb_disable(struct coresight_device *csdev) /* Dissociate from the target process. */ drvdata->pid = -1; drvdata->mode = CS_MODE_DISABLED; - dev_dbg(&csdev->dev, "Ultrasoc SMB disabled\n"); -out: - spin_unlock(&drvdata->spinlock); - return ret; + return 0; } static void *smb_alloc_buffer(struct coresight_device *csdev, @@ -395,17 +371,17 @@ static unsigned long smb_update_buffer(struct coresight_device *csdev, struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); struct smb_data_buffer *sdb = &drvdata->sdb; struct cs_buffers *buf = sink_config; - unsigned long data_size = 0; + unsigned long data_size; bool lost = false; if (!buf) return 0; - spin_lock(&drvdata->spinlock); + guard(spinlock)(&drvdata->spinlock); /* Don't do anything if another tracer is using this sink. */ if (atomic_read(&csdev->refcnt) != 1) - goto out; + return 0; smb_disable_hw(drvdata); smb_update_data_size(drvdata); @@ -424,8 +400,6 @@ static unsigned long smb_update_buffer(struct coresight_device *csdev, smb_sync_perf_buffer(drvdata, buf, handle->head); if (!buf->snapshot && lost) perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); -out: - spin_unlock(&drvdata->spinlock); return data_size; } @@ -601,15 +575,13 @@ static int smb_probe(struct platform_device *pdev) return 0; } -static int smb_remove(struct platform_device *pdev) +static void smb_remove(struct platform_device *pdev) { struct smb_drv_data *drvdata = platform_get_drvdata(pdev); smb_unregister_sink(drvdata); smb_config_inport(&pdev->dev, false); - - return 0; } #ifdef CONFIG_ACPI @@ -627,7 +599,7 @@ static struct platform_driver smb_driver = { .suppress_bind_attrs = true, }, .probe = smb_probe, - .remove = smb_remove, + .remove_new = smb_remove, }; module_platform_driver(smb_driver); diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index a991ecb7515a..c1b5fd2b8974 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -183,6 +183,10 @@ static void hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt) static void hisi_ptt_trace_end(struct hisi_ptt *hisi_ptt) { writel(0, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + + /* Mask the interrupt on the end */ + writel(HISI_PTT_TRACE_INT_MASK_ALL, hisi_ptt->iobase + HISI_PTT_TRACE_INT_MASK); + hisi_ptt->trace_ctrl.started = false; } @@ -270,15 +274,14 @@ static int hisi_ptt_update_aux(struct hisi_ptt *hisi_ptt, int index, bool stop) buf->pos += size; /* - * Just commit the traced data if we're going to stop. Otherwise if the - * resident AUX buffer cannot contain the data of next trace buffer, - * apply a new one. + * Always commit the data to the AUX buffer in time to make sure + * userspace got enough time to consume the data. + * + * If we're not going to stop, apply a new one and check whether + * there's enough room for the next trace. */ - if (stop) { - perf_aux_output_end(handle, buf->pos); - } else if (buf->length - buf->pos < HISI_PTT_TRACE_BUF_SIZE) { - perf_aux_output_end(handle, buf->pos); - + perf_aux_output_end(handle, size); + if (!stop) { buf = perf_aux_output_begin(handle, event); if (!buf) return -EINVAL; diff --git a/drivers/hwtracing/ptt/hisi_ptt.h b/drivers/hwtracing/ptt/hisi_ptt.h index e17f045d7e72..46030aa88081 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.h +++ b/drivers/hwtracing/ptt/hisi_ptt.h @@ -47,6 +47,7 @@ #define HISI_PTT_TRACE_INT_STAT 0x0890 #define HISI_PTT_TRACE_INT_STAT_MASK GENMASK(3, 0) #define HISI_PTT_TRACE_INT_MASK 0x0894 +#define HISI_PTT_TRACE_INT_MASK_ALL GENMASK(3, 0) #define HISI_PTT_TUNING_INT_STAT 0x0898 #define HISI_PTT_TUNING_INT_STAT_MASK BIT(0) #define HISI_PTT_TRACE_WR_STS 0x08a0 diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c index ee83c4581bce..461eb23f9d47 100644 --- a/drivers/i2c/busses/i2c-ali1535.c +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -477,7 +477,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter ali1535_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c index 55a9e93fbfeb..307fb0666ecb 100644 --- a/drivers/i2c/busses/i2c-ali1563.c +++ b/drivers/i2c/busses/i2c-ali1563.c @@ -390,7 +390,7 @@ static const struct i2c_algorithm ali1563_algorithm = { static struct i2c_adapter ali1563_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &ali1563_algorithm, }; diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index 0231c5be6354..d2fa30deb054 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -461,7 +461,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter ali15x3_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index ef1307a258e9..208310db906d 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -285,7 +285,7 @@ static const struct i2c_algorithm smbus_algorithm = { struct i2c_adapter amd756_smbus = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index 1ed7e945bb6d..42a9b1221065 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -449,7 +449,7 @@ static int amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id) smbus->adapter.owner = THIS_MODULE; snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), "SMBus2 AMD8111 adapter at %04x", smbus->base); - smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + smbus->adapter.class = I2C_CLASS_HWMON; smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index 9a664abf734d..4404b4aac676 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -658,7 +658,7 @@ static int cpm_i2c_probe(struct platform_device *ofdev) /* register new adapter to i2c module... */ data = of_get_property(ofdev->dev.of_node, "linux,i2c-index", &len); - cpm->adap.nr = (data && len == 4) ? be32_to_cpup(data) : -1; + cpm->adap.nr = (data && len == 4) ? *data : -1; result = i2c_add_numbered_adapter(&cpm->adap); if (result < 0) diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c index b0f50dce9d0f..cfe8665cacd2 100644 --- a/drivers/i2c/busses/i2c-elektor.c +++ b/drivers/i2c/busses/i2c-elektor.c @@ -188,7 +188,7 @@ static struct i2c_algo_pcf_data pcf_isa_data = { static struct i2c_adapter pcf_isa_ops = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo_data = &pcf_isa_data, .name = "i2c-elektor", }; diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index fb35a75fe0e3..4f1411b1a775 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -25,7 +25,6 @@ struct i2c_gpio_private_data { struct i2c_algo_bit_data bit_data; struct i2c_gpio_platform_data pdata; #ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR - struct dentry *debug_dir; /* these must be protected by bus lock */ struct completion scl_irq_completion; u64 scl_irq_data; @@ -72,7 +71,6 @@ static int i2c_gpio_getscl(void *data) } #ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR -static struct dentry *i2c_gpio_debug_dir; #define setsda(bd, val) ((bd)->setsda((bd)->data, val)) #define setscl(bd, val) ((bd)->setscl((bd)->data, val)) @@ -258,41 +256,23 @@ static void i2c_gpio_fault_injector_init(struct platform_device *pdev) { struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev); - /* - * If there will be a debugfs-dir per i2c adapter somewhen, put the - * 'fault-injector' dir there. Until then, we have a global dir with - * all adapters as subdirs. - */ - if (!i2c_gpio_debug_dir) - i2c_gpio_debug_dir = debugfs_create_dir("i2c-fault-injector", NULL); - - priv->debug_dir = debugfs_create_dir(pdev->name, i2c_gpio_debug_dir); - init_completion(&priv->scl_irq_completion); - debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir, + debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->adap.debugfs, priv, &fops_incomplete_addr_phase); - debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir, + debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->adap.debugfs, priv, &fops_incomplete_write_byte); if (priv->bit_data.getscl) { - debugfs_create_file_unsafe("inject_panic", 0200, priv->debug_dir, + debugfs_create_file_unsafe("inject_panic", 0200, priv->adap.debugfs, priv, &fops_inject_panic); - debugfs_create_file_unsafe("lose_arbitration", 0200, priv->debug_dir, + debugfs_create_file_unsafe("lose_arbitration", 0200, priv->adap.debugfs, priv, &fops_lose_arbitration); } - debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl); - debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda); -} - -static void i2c_gpio_fault_injector_exit(struct platform_device *pdev) -{ - struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev); - - debugfs_remove_recursive(priv->debug_dir); + debugfs_create_file_unsafe("scl", 0600, priv->adap.debugfs, priv, &fops_scl); + debugfs_create_file_unsafe("sda", 0600, priv->adap.debugfs, priv, &fops_sda); } #else static inline void i2c_gpio_fault_injector_init(struct platform_device *pdev) {} -static inline void i2c_gpio_fault_injector_exit(struct platform_device *pdev) {} #endif /* CONFIG_I2C_GPIO_FAULT_INJECTOR*/ /* Get i2c-gpio properties from DT or ACPI table */ @@ -444,7 +424,7 @@ static int i2c_gpio_probe(struct platform_device *pdev) snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id); adap->algo_data = bit_data; - adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adap->class = I2C_CLASS_HWMON; adap->dev.parent = dev; device_set_node(&adap->dev, fwnode); @@ -475,8 +455,6 @@ static void i2c_gpio_remove(struct platform_device *pdev) struct i2c_gpio_private_data *priv; struct i2c_adapter *adap; - i2c_gpio_fault_injector_exit(pdev); - priv = platform_get_drvdata(pdev); adap = &priv->adap; diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 070999139c6d..3932e8d96a17 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1230,8 +1230,10 @@ static const struct { * Additional individual entries were added after verification. */ { "Latitude 5480", 0x29 }, + { "Precision 3540", 0x29 }, { "Vostro V131", 0x1d }, { "Vostro 5568", 0x29 }, + { "XPS 15 7590", 0x29 }, }; static void register_dell_lis3lv02d_i2c_device(struct i801_priv *priv) diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index 408820319ec4..7fb87b78923e 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -739,7 +739,7 @@ static int iic_probe(struct platform_device *ofdev) adap->dev.of_node = of_node_get(np); strscpy(adap->name, "IBM IIC", sizeof(adap->name)); i2c_set_adapdata(adap, dev); - adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adap->class = I2C_CLASS_HWMON; adap->algo = &iic_algo; adap->timeout = HZ; diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 1775a79aeba2..88a053987403 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1401,7 +1401,7 @@ static int i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx, PINCTRL_STATE_DEFAULT); i2c_imx->pinctrl_pins_gpio = pinctrl_lookup_state(i2c_imx->pinctrl, "gpio"); - rinfo->sda_gpiod = devm_gpiod_get(&pdev->dev, "sda", GPIOD_IN); + rinfo->sda_gpiod = devm_gpiod_get_optional(&pdev->dev, "sda", GPIOD_IN); rinfo->scl_gpiod = devm_gpiod_get(&pdev->dev, "scl", GPIOD_OUT_HIGH_OPEN_DRAIN); if (PTR_ERR(rinfo->sda_gpiod) == -EPROBE_DEFER || diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index f2f7ebeeaecb..2e5f0165c3d3 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -478,7 +478,7 @@ iop3xx_i2c_probe(struct platform_device *pdev) memcpy(new_adapter->name, pdev->name, strlen(pdev->name)); new_adapter->owner = THIS_MODULE; - new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + new_adapter->class = I2C_CLASS_HWMON; new_adapter->dev.parent = &pdev->dev; new_adapter->dev.of_node = pdev->dev.of_node; new_adapter->nr = pdev->id; diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c index 1dc1ceaa4443..416a9968ed28 100644 --- a/drivers/i2c/busses/i2c-isch.c +++ b/drivers/i2c/busses/i2c-isch.c @@ -249,7 +249,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter sch_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-kempld.c b/drivers/i2c/busses/i2c-kempld.c index e01d75308288..c3a529a73b5b 100644 --- a/drivers/i2c/busses/i2c-kempld.c +++ b/drivers/i2c/busses/i2c-kempld.c @@ -283,8 +283,7 @@ static const struct i2c_algorithm kempld_i2c_algorithm = { static const struct i2c_adapter kempld_i2c_adapter = { .owner = THIS_MODULE, .name = "i2c-kempld", - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD | - I2C_CLASS_DEPRECATED, + .class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED, .algo = &kempld_i2c_algorithm, }; diff --git a/drivers/i2c/busses/i2c-mlxcpld.c b/drivers/i2c/busses/i2c-mlxcpld.c index 6fec64ea67fb..099291a0411d 100644 --- a/drivers/i2c/busses/i2c-mlxcpld.c +++ b/drivers/i2c/busses/i2c-mlxcpld.c @@ -477,7 +477,7 @@ static const struct i2c_adapter_quirks mlxcpld_i2c_quirks_ext2 = { static struct i2c_adapter mlxcpld_i2c_adapter = { .owner = THIS_MODULE, .name = "i2c-mlxcpld", - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &mlxcpld_i2c_algo, .quirks = &mlxcpld_i2c_quirks, .retries = MLXCPLD_I2C_RETR_NUM, diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 38d203d93eee..fab662e6bc08 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -349,7 +349,7 @@ static int nforce2_probe_smb(struct pci_dev *dev, int bar, int alt_reg, return -EBUSY; } smbus->adapter.owner = THIS_MODULE; - smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + smbus->adapter.class = I2C_CLASS_HWMON; smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; smbus->adapter.dev.parent = &dev->dev; diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c index ae4bae63ad4f..54181b3f1919 100644 --- a/drivers/i2c/busses/i2c-npcm7xx.c +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -326,7 +326,6 @@ struct npcm_i2c { u8 slv_rd_buf[MAX_I2C_HW_FIFO_SIZE]; u8 slv_wr_buf[MAX_I2C_HW_FIFO_SIZE]; #endif - struct dentry *debugfs; /* debugfs device directory */ u64 ber_cnt; u64 rec_succ_cnt; u64 rec_fail_cnt; @@ -2250,27 +2249,15 @@ static const struct i2c_algorithm npcm_i2c_algo = { #endif }; -/* i2c debugfs directory: used to keep health monitor of i2c devices */ -static struct dentry *npcm_i2c_debugfs_dir; - static void npcm_i2c_init_debugfs(struct platform_device *pdev, struct npcm_i2c *bus) { - struct dentry *d; - - if (!npcm_i2c_debugfs_dir) - return; - d = debugfs_create_dir(dev_name(&pdev->dev), npcm_i2c_debugfs_dir); - if (IS_ERR_OR_NULL(d)) - return; - debugfs_create_u64("ber_cnt", 0444, d, &bus->ber_cnt); - debugfs_create_u64("nack_cnt", 0444, d, &bus->nack_cnt); - debugfs_create_u64("rec_succ_cnt", 0444, d, &bus->rec_succ_cnt); - debugfs_create_u64("rec_fail_cnt", 0444, d, &bus->rec_fail_cnt); - debugfs_create_u64("timeout_cnt", 0444, d, &bus->timeout_cnt); - debugfs_create_u64("tx_complete_cnt", 0444, d, &bus->tx_complete_cnt); - - bus->debugfs = d; + debugfs_create_u64("ber_cnt", 0444, bus->adap.debugfs, &bus->ber_cnt); + debugfs_create_u64("nack_cnt", 0444, bus->adap.debugfs, &bus->nack_cnt); + debugfs_create_u64("rec_succ_cnt", 0444, bus->adap.debugfs, &bus->rec_succ_cnt); + debugfs_create_u64("rec_fail_cnt", 0444, bus->adap.debugfs, &bus->rec_fail_cnt); + debugfs_create_u64("timeout_cnt", 0444, bus->adap.debugfs, &bus->timeout_cnt); + debugfs_create_u64("tx_complete_cnt", 0444, bus->adap.debugfs, &bus->tx_complete_cnt); } static int npcm_i2c_probe_bus(struct platform_device *pdev) @@ -2362,7 +2349,6 @@ static void npcm_i2c_remove_bus(struct platform_device *pdev) unsigned long lock_flags; struct npcm_i2c *bus = platform_get_drvdata(pdev); - debugfs_remove_recursive(bus->debugfs); spin_lock_irqsave(&bus->lock, lock_flags); npcm_i2c_disable(bus); spin_unlock_irqrestore(&bus->lock, lock_flags); @@ -2385,28 +2371,7 @@ static struct platform_driver npcm_i2c_bus_driver = { } }; -static int __init npcm_i2c_init(void) -{ - int ret; - - npcm_i2c_debugfs_dir = debugfs_create_dir("npcm_i2c", NULL); - - ret = platform_driver_register(&npcm_i2c_bus_driver); - if (ret) { - debugfs_remove_recursive(npcm_i2c_debugfs_dir); - return ret; - } - - return 0; -} -module_init(npcm_i2c_init); - -static void __exit npcm_i2c_exit(void) -{ - platform_driver_unregister(&npcm_i2c_bus_driver); - debugfs_remove_recursive(npcm_i2c_debugfs_dir); -} -module_exit(npcm_i2c_exit); +module_platform_driver(npcm_i2c_bus_driver); MODULE_AUTHOR("Avi Fishman <avi.fishman@gmail.com>"); MODULE_AUTHOR("Tali Perry <tali.perry@nuvoton.com>"); diff --git a/drivers/i2c/busses/i2c-pasemi-pci.c b/drivers/i2c/busses/i2c-pasemi-pci.c index cfc89e04eb94..77f90c7436ed 100644 --- a/drivers/i2c/busses/i2c-pasemi-pci.c +++ b/drivers/i2c/busses/i2c-pasemi-pci.c @@ -56,7 +56,7 @@ static int pasemi_smb_pci_probe(struct pci_dev *dev, if (!smbus->ioaddr) return -EBUSY; - smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + smbus->adapter.class = I2C_CLASS_HWMON; error = pasemi_i2c_common_probe(smbus); if (error) return error; diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 809fbd014cd6..6a0392172b2f 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -943,7 +943,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, } adap->owner = THIS_MODULE; - adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adap->class = I2C_CLASS_HWMON; adap->algo = sb800_main ? &piix4_smbus_algorithm_sb800 : &smbus_algorithm; diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 829ac053bbb7..828aa2ea0fe4 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -89,6 +89,7 @@ #define TMDMAE BIT(0) /* DMA Master Transmitted Enable */ /* ICCCR2 */ +#define FMPE BIT(7) /* Fast Mode Plus Enable */ #define CDFD BIT(2) /* CDF Disable */ #define HLSE BIT(1) /* HIGH/LOW Separate Control Enable */ #define SME BIT(0) /* SCL Mask Enable */ @@ -122,16 +123,18 @@ #define ID_NACK BIT(4) #define ID_EPROTO BIT(5) /* persistent flags */ +#define ID_P_FMPLUS BIT(27) #define ID_P_NOT_ATOMIC BIT(28) #define ID_P_HOST_NOTIFY BIT(29) #define ID_P_NO_RXDMA BIT(30) /* HW forbids RXDMA sometimes */ #define ID_P_PM_BLOCKED BIT(31) -#define ID_P_MASK GENMASK(31, 28) +#define ID_P_MASK GENMASK(31, 27) enum rcar_i2c_type { I2C_RCAR_GEN1, I2C_RCAR_GEN2, I2C_RCAR_GEN3, + I2C_RCAR_GEN4, }; struct rcar_i2c_priv { @@ -148,6 +151,7 @@ struct rcar_i2c_priv { u32 icccr; u16 schd; u16 scld; + u8 smd; u8 recovery_icmcr; /* protected by adapter lock */ enum rcar_i2c_type devtype; struct i2c_client *slave; @@ -239,9 +243,14 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv) if (priv->devtype < I2C_RCAR_GEN3) { rcar_i2c_write(priv, ICCCR, priv->icccr); } else { - rcar_i2c_write(priv, ICCCR2, CDFD | HLSE | SME); + u32 icccr2 = CDFD | HLSE | SME; + + if (priv->flags & ID_P_FMPLUS) + icccr2 |= FMPE; + + rcar_i2c_write(priv, ICCCR2, icccr2); rcar_i2c_write(priv, ICCCR, priv->icccr); - rcar_i2c_write(priv, ICMPR, RCAR_DEFAULT_SMD); + rcar_i2c_write(priv, ICMPR, priv->smd); rcar_i2c_write(priv, ICHPR, priv->schd); rcar_i2c_write(priv, ICLPR, priv->scld); rcar_i2c_write(priv, ICFBSCR, TCYC17); @@ -278,6 +287,7 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv) /* Fall back to previously used values if not supplied */ i2c_parse_fw_timings(dev, &t, false); + priv->smd = RCAR_DEFAULT_SMD; /* * calculate SCL clock @@ -303,6 +313,11 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv) if (cdf >= 1U << cdf_width) goto err_no_val; + if (t.bus_freq_hz > I2C_MAX_FAST_MODE_FREQ && priv->devtype >= I2C_RCAR_GEN4) + priv->flags |= ID_P_FMPLUS; + else + priv->flags &= ~ID_P_FMPLUS; + /* On Gen3+, we use cdf only for the filters, not as a SCL divider */ ick = rate / (priv->devtype < I2C_RCAR_GEN3 ? (cdf + 1) : 1); @@ -344,30 +359,30 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv) * x as a base value for the SCLD/SCHD ratio: * * SCL = clkp / (8 + 2 * SMD + SCLD + SCHD + F[(ticf + tr + intd) * clkp]) - * SCL = clkp / (8 + 2 * RCAR_DEFAULT_SMD + RCAR_SCLD_RATIO * x + * SCL = clkp / (8 + 2 * SMD + RCAR_SCLD_RATIO * x * + RCAR_SCHD_RATIO * x + F[...]) * * with: sum_ratio = RCAR_SCLD_RATIO + RCAR_SCHD_RATIO - * and: smd = RCAR_DEFAULT_SMD * * SCL = clkp / (8 + 2 * smd + sum_ratio * x + F[...]) * 8 + 2 * smd + sum_ratio * x + F[...] = clkp / SCL * x = ((clkp / SCL) - 8 - 2 * smd - F[...]) / sum_ratio */ x = DIV_ROUND_UP(rate, t.bus_freq_hz ?: 1); - x = DIV_ROUND_UP(x - 8 - 2 * RCAR_DEFAULT_SMD - round, sum_ratio); - scl = rate / (8 + 2 * RCAR_DEFAULT_SMD + sum_ratio * x + round); + x = DIV_ROUND_UP(x - 8 - 2 * priv->smd - round, sum_ratio); + scl = rate / (8 + 2 * priv->smd + sum_ratio * x + round); - /* Bail out if values don't fit into 16 bit or SMD became too large */ - if (x * RCAR_SCLD_RATIO > 0xffff || RCAR_DEFAULT_SMD > x * RCAR_SCHD_RATIO) + if (x == 0 || x * RCAR_SCLD_RATIO > 0xffff) goto err_no_val; priv->icccr = cdf; priv->schd = RCAR_SCHD_RATIO * x; priv->scld = RCAR_SCLD_RATIO * x; + if (priv->smd >= priv->schd) + priv->smd = priv->schd - 1; - dev_dbg(dev, "clk %u/%u(%lu), round %u, CDF: %u SCHD %u SCLD %u\n", - scl, t.bus_freq_hz, rate, round, cdf, priv->schd, priv->scld); + dev_dbg(dev, "clk %u/%u(%lu), round %u, CDF: %u SCHD %u SCLD %u SMD %u\n", + scl, t.bus_freq_hz, rate, round, cdf, priv->schd, priv->scld, priv->smd); } return 0; @@ -431,8 +446,8 @@ static void rcar_i2c_cleanup_dma(struct rcar_i2c_priv *priv, bool terminate) dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg), sg_dma_len(&priv->sg), priv->dma_direction); - /* Gen3 can only do one RXDMA per transfer and we just completed it */ - if (priv->devtype == I2C_RCAR_GEN3 && + /* Gen3+ can only do one RXDMA per transfer and we just completed it */ + if (priv->devtype >= I2C_RCAR_GEN3 && priv->dma_direction == DMA_FROM_DEVICE) priv->flags |= ID_P_NO_RXDMA; @@ -886,8 +901,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, if (ret < 0) goto out; - /* Gen3 needs a reset before allowing RXDMA once */ - if (priv->devtype == I2C_RCAR_GEN3) { + /* Gen3+ needs a reset. That also allows RXDMA once */ + if (priv->devtype >= I2C_RCAR_GEN3) { priv->flags &= ~ID_P_NO_RXDMA; ret = rcar_i2c_do_reset(priv); if (ret) @@ -1072,10 +1087,12 @@ static const struct of_device_id rcar_i2c_dt_ids[] = { { .compatible = "renesas,i2c-r8a7794", .data = (void *)I2C_RCAR_GEN2 }, { .compatible = "renesas,i2c-r8a7795", .data = (void *)I2C_RCAR_GEN3 }, { .compatible = "renesas,i2c-r8a7796", .data = (void *)I2C_RCAR_GEN3 }, + /* S4 has no FM+ bit */ + { .compatible = "renesas,i2c-r8a779f0", .data = (void *)I2C_RCAR_GEN3 }, { .compatible = "renesas,rcar-gen1-i2c", .data = (void *)I2C_RCAR_GEN1 }, { .compatible = "renesas,rcar-gen2-i2c", .data = (void *)I2C_RCAR_GEN2 }, { .compatible = "renesas,rcar-gen3-i2c", .data = (void *)I2C_RCAR_GEN3 }, - { .compatible = "renesas,rcar-gen4-i2c", .data = (void *)I2C_RCAR_GEN3 }, + { .compatible = "renesas,rcar-gen4-i2c", .data = (void *)I2C_RCAR_GEN4 }, {}, }; MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids); @@ -1151,7 +1168,7 @@ static int rcar_i2c_probe(struct platform_device *pdev) if (of_property_read_bool(dev->of_node, "smbus")) priv->flags |= ID_P_HOST_NOTIFY; - if (priv->devtype == I2C_RCAR_GEN3) { + if (priv->devtype >= I2C_RCAR_GEN3) { priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(priv->rstc)) { ret = PTR_ERR(priv->rstc); diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 4362db7c5789..086fdf262e7b 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -1295,8 +1295,12 @@ static int rk3x_i2c_probe(struct platform_device *pdev) return -EINVAL; } - /* 27+i: write mask, 11+i: value */ - value = BIT(27 + bus_nr) | BIT(11 + bus_nr); + /* rv1126 i2c2 uses non-sequential write mask 20, value 4 */ + if (i2c->soc_data == &rv1126_soc_data && bus_nr == 2) + value = BIT(20) | BIT(4); + else + /* 27+i: write mask, 11+i: value */ + value = BIT(27 + bus_nr) | BIT(11 + bus_nr); ret = regmap_write(grf, i2c->soc_data->grf_offset, value); if (ret != 0) { diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index c56886af724e..275f7c42165c 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -76,6 +76,7 @@ #define QUIRK_HDMIPHY (1 << 1) #define QUIRK_NO_GPIO (1 << 2) #define QUIRK_POLL (1 << 3) +#define QUIRK_ATOMIC (1 << 4) /* Max time to wait for bus to become idle after a xfer (in us) */ #define S3C2410_IDLE_TIMEOUT 5000 @@ -174,7 +175,7 @@ static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret) if (ret) i2c->msg_idx = ret; - if (!(i2c->quirks & QUIRK_POLL)) + if (!(i2c->quirks & (QUIRK_POLL | QUIRK_ATOMIC))) wake_up(&i2c->wait); } @@ -216,8 +217,17 @@ static bool is_ack(struct s3c24xx_i2c *i2c) int tries; for (tries = 50; tries; --tries) { - if (readl(i2c->regs + S3C2410_IICCON) - & S3C2410_IICCON_IRQPEND) { + unsigned long tmp = readl(i2c->regs + S3C2410_IICCON); + + if (!(tmp & S3C2410_IICCON_ACKEN)) { + /* + * Wait a bit for the bus to stabilize, + * delay estimated experimentally. + */ + usleep_range(100, 200); + return true; + } + if (tmp & S3C2410_IICCON_IRQPEND) { if (!(readl(i2c->regs + S3C2410_IICSTAT) & S3C2410_IICSTAT_LASTBIT)) return true; @@ -270,16 +280,6 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, stat |= S3C2410_IICSTAT_START; writel(stat, i2c->regs + S3C2410_IICSTAT); - - if (i2c->quirks & QUIRK_POLL) { - while ((i2c->msg_num != 0) && is_ack(i2c)) { - i2c_s3c_irq_nextbyte(i2c, stat); - stat = readl(i2c->regs + S3C2410_IICSTAT); - - if (stat & S3C2410_IICSTAT_ARBITR) - dev_err(i2c->dev, "deal with arbitration loss\n"); - } - } } static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) @@ -685,7 +685,7 @@ static void s3c24xx_i2c_wait_idle(struct s3c24xx_i2c *i2c) static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num) { - unsigned long timeout; + unsigned long timeout = 0; int ret; ret = s3c24xx_i2c_set_master(i2c); @@ -704,17 +704,20 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, s3c24xx_i2c_enable_irq(i2c); s3c24xx_i2c_message_start(i2c, msgs); - if (i2c->quirks & QUIRK_POLL) { - ret = i2c->msg_idx; + if (i2c->quirks & (QUIRK_POLL | QUIRK_ATOMIC)) { + while ((i2c->msg_num != 0) && is_ack(i2c)) { + unsigned long stat = readl(i2c->regs + S3C2410_IICSTAT); - if (ret != num) - dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); + i2c_s3c_irq_nextbyte(i2c, stat); - goto out; + stat = readl(i2c->regs + S3C2410_IICSTAT); + if (stat & S3C2410_IICSTAT_ARBITR) + dev_err(i2c->dev, "deal with arbitration loss\n"); + } + } else { + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); } - timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); - ret = i2c->msg_idx; /* @@ -773,6 +776,21 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, return -EREMOTEIO; } +static int s3c24xx_i2c_xfer_atomic(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data; + int ret; + + disable_irq(i2c->irq); + i2c->quirks |= QUIRK_ATOMIC; + ret = s3c24xx_i2c_xfer(adap, msgs, num); + i2c->quirks &= ~QUIRK_ATOMIC; + enable_irq(i2c->irq); + + return ret; +} + /* declare our i2c functionality */ static u32 s3c24xx_i2c_func(struct i2c_adapter *adap) { @@ -783,6 +801,7 @@ static u32 s3c24xx_i2c_func(struct i2c_adapter *adap) /* i2c bus registration info */ static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, + .master_xfer_atomic = s3c24xx_i2c_xfer_atomic, .functionality = s3c24xx_i2c_func, }; diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c index 421735acfa14..d7af8e0d7599 100644 --- a/drivers/i2c/busses/i2c-scmi.c +++ b/drivers/i2c/busses/i2c-scmi.c @@ -385,7 +385,7 @@ static int smbus_cmi_probe(struct platform_device *device) smbus_cmi->adapter.owner = THIS_MODULE; smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm; smbus_cmi->adapter.algo_data = smbus_cmi; - smbus_cmi->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + smbus_cmi->adapter.class = I2C_CLASS_HWMON; smbus_cmi->adapter.dev.parent = &device->dev; ret = i2c_add_adapter(&smbus_cmi->adapter); diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c index 1ad2a26156d1..8a043f5fca1e 100644 --- a/drivers/i2c/busses/i2c-sh7760.c +++ b/drivers/i2c/busses/i2c-sh7760.c @@ -477,7 +477,7 @@ static int sh7760_i2c_probe(struct platform_device *pdev) id->adap.nr = pdev->id; id->adap.algo = &sh7760_i2c_algo; - id->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + id->adap.class = I2C_CLASS_HWMON; id->adap.retries = 3; id->adap.algo_data = id; id->adap.dev.parent = &pdev->dev; diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c index 8f71f01cb169..49f8f4f1b0f0 100644 --- a/drivers/i2c/busses/i2c-sibyte.c +++ b/drivers/i2c/busses/i2c-sibyte.c @@ -142,7 +142,7 @@ static struct i2c_algo_sibyte_data sibyte_board_data[2] = { static struct i2c_adapter sibyte_board_adapter[2] = { { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = NULL, .algo_data = &sibyte_board_data[0], .nr = 0, @@ -150,7 +150,7 @@ static struct i2c_adapter sibyte_board_adapter[2] = { }, { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = NULL, .algo_data = &sibyte_board_data[1], .nr = 1, diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c index 486f1e9dfb74..32476dc10ad6 100644 --- a/drivers/i2c/busses/i2c-sis5595.c +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -353,7 +353,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter sis5595_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index 87d56250d78a..3505cf29cedd 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -493,7 +493,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter sis630_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &smbus_algorithm, .retries = 3 }; diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c index cde8003985a5..77529dda6fcd 100644 --- a/drivers/i2c/busses/i2c-sis96x.c +++ b/drivers/i2c/busses/i2c-sis96x.c @@ -228,7 +228,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter sis96x_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 983509936727..01210452216b 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -50,6 +50,7 @@ #define STM32F7_I2C_TXDR 0x28 /* STM32F7 I2C control 1 */ +#define STM32_I2C_CR1_FMP BIT(24) #define STM32F7_I2C_CR1_PECEN BIT(23) #define STM32F7_I2C_CR1_ALERTEN BIT(22) #define STM32F7_I2C_CR1_SMBHEN BIT(20) @@ -226,6 +227,8 @@ struct stm32f7_i2c_spec { * @rise_time: Rise time (ns) * @fall_time: Fall time (ns) * @fmp_clr_offset: Fast Mode Plus clear register offset from set register + * @single_it_line: Only a single IT line is used for both events/errors + * @fmp_cr1_bit: Fast Mode Plus control is done via a bit in CR1 */ struct stm32f7_i2c_setup { u32 speed_freq; @@ -233,6 +236,8 @@ struct stm32f7_i2c_setup { u32 rise_time; u32 fall_time; u32 fmp_clr_offset; + bool single_it_line; + bool fmp_cr1_bit; }; /** @@ -418,6 +423,13 @@ static const struct stm32f7_i2c_setup stm32mp13_setup = { .fmp_clr_offset = 0x4, }; +static const struct stm32f7_i2c_setup stm32mp25_setup = { + .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT, + .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT, + .single_it_line = true, + .fmp_cr1_bit = true, +}; + static inline void stm32f7_i2c_set_bits(void __iomem *reg, u32 mask) { writel_relaxed(readl_relaxed(reg) | mask, reg); @@ -1419,15 +1431,13 @@ static bool stm32f7_i2c_is_slave_busy(struct stm32f7_i2c_dev *i2c_dev) return i == busy; } -static irqreturn_t stm32f7_i2c_slave_isr_event(struct stm32f7_i2c_dev *i2c_dev) +static irqreturn_t stm32f7_i2c_slave_isr_event(struct stm32f7_i2c_dev *i2c_dev, u32 status) { void __iomem *base = i2c_dev->base; - u32 cr2, status, mask; + u32 cr2, mask; u8 val; int ret; - status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR); - /* Slave transmitter mode */ if (status & STM32F7_I2C_ISR_TXIS) { i2c_slave_event(i2c_dev->slave_running, @@ -1494,23 +1504,81 @@ static irqreturn_t stm32f7_i2c_slave_isr_event(struct stm32f7_i2c_dev *i2c_dev) return IRQ_HANDLED; } -static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data) +static irqreturn_t stm32f7_i2c_handle_isr_errs(struct stm32f7_i2c_dev *i2c_dev, u32 status) { - struct stm32f7_i2c_dev *i2c_dev = data; struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; - struct stm32_i2c_dma *dma = i2c_dev->dma; + u16 addr = f7_msg->addr; void __iomem *base = i2c_dev->base; - u32 status, mask; - int ret = IRQ_HANDLED; + struct device *dev = i2c_dev->dev; + struct stm32_i2c_dma *dma = i2c_dev->dma; - /* Check if the interrupt if for a slave device */ - if (!i2c_dev->master_mode) { - ret = stm32f7_i2c_slave_isr_event(i2c_dev); - return ret; + /* Bus error */ + if (status & STM32F7_I2C_ISR_BERR) { + dev_err(dev, "Bus error accessing addr 0x%x\n", addr); + writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR); + stm32f7_i2c_release_bus(&i2c_dev->adap); + f7_msg->result = -EIO; + } + + /* Arbitration loss */ + if (status & STM32F7_I2C_ISR_ARLO) { + dev_dbg(dev, "Arbitration loss accessing addr 0x%x\n", addr); + writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR); + f7_msg->result = -EAGAIN; + } + + if (status & STM32F7_I2C_ISR_PECERR) { + dev_err(dev, "PEC error in reception accessing addr 0x%x\n", addr); + writel_relaxed(STM32F7_I2C_ICR_PECCF, base + STM32F7_I2C_ICR); + f7_msg->result = -EINVAL; } + if (status & STM32F7_I2C_ISR_ALERT) { + dev_dbg(dev, "SMBus alert received\n"); + writel_relaxed(STM32F7_I2C_ICR_ALERTCF, base + STM32F7_I2C_ICR); + i2c_handle_smbus_alert(i2c_dev->alert->ara); + return IRQ_HANDLED; + } + + if (!i2c_dev->slave_running) { + u32 mask; + /* Disable interrupts */ + if (stm32f7_i2c_is_slave_registered(i2c_dev)) + mask = STM32F7_I2C_XFER_IRQ_MASK; + else + mask = STM32F7_I2C_ALL_IRQ_MASK; + stm32f7_i2c_disable_irq(i2c_dev, mask); + } + + /* Disable dma */ + if (i2c_dev->use_dma) { + stm32f7_i2c_disable_dma_req(i2c_dev); + dmaengine_terminate_async(dma->chan_using); + } + + i2c_dev->master_mode = false; + complete(&i2c_dev->complete); + + return IRQ_HANDLED; +} + +#define STM32F7_ERR_EVENTS (STM32F7_I2C_ISR_BERR | STM32F7_I2C_ISR_ARLO |\ + STM32F7_I2C_ISR_PECERR | STM32F7_I2C_ISR_ALERT) +static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data) +{ + struct stm32f7_i2c_dev *i2c_dev = data; + u32 status; + status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR); + /* + * Check if the interrupt is for a slave device or related + * to errors flags (in case of single it line mode) + */ + if (!i2c_dev->master_mode || + (i2c_dev->setup.single_it_line && (status & STM32F7_ERR_EVENTS))) + return IRQ_WAKE_THREAD; + /* Tx empty */ if (status & STM32F7_I2C_ISR_TXIS) stm32f7_i2c_write_tx_data(i2c_dev); @@ -1519,6 +1587,33 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data) if (status & STM32F7_I2C_ISR_RXNE) stm32f7_i2c_read_rx_data(i2c_dev); + /* Wake up the thread if other flags are raised */ + if (status & + (STM32F7_I2C_ISR_NACKF | STM32F7_I2C_ISR_STOPF | + STM32F7_I2C_ISR_TC | STM32F7_I2C_ISR_TCR)) + return IRQ_WAKE_THREAD; + + return IRQ_HANDLED; +} + +static irqreturn_t stm32f7_i2c_isr_event_thread(int irq, void *data) +{ + struct stm32f7_i2c_dev *i2c_dev = data; + struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; + struct stm32_i2c_dma *dma = i2c_dev->dma; + void __iomem *base = i2c_dev->base; + u32 status, mask; + int ret; + + status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR); + + if (!i2c_dev->master_mode) + return stm32f7_i2c_slave_isr_event(i2c_dev, status); + + /* Handle errors in case of this handler is used for events/errors */ + if (i2c_dev->setup.single_it_line && (status & STM32F7_ERR_EVENTS)) + return stm32f7_i2c_handle_isr_errs(i2c_dev, status); + /* NACK received */ if (status & STM32F7_I2C_ISR_NACKF) { dev_dbg(i2c_dev->dev, "<%s>: Receive NACK (addr %x)\n", @@ -1531,33 +1626,28 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data) f7_msg->result = -ENXIO; } - /* STOP detection flag */ - if (status & STM32F7_I2C_ISR_STOPF) { - /* Disable interrupts */ - if (stm32f7_i2c_is_slave_registered(i2c_dev)) - mask = STM32F7_I2C_XFER_IRQ_MASK; + if (status & STM32F7_I2C_ISR_TCR) { + if (f7_msg->smbus) + stm32f7_i2c_smbus_reload(i2c_dev); else - mask = STM32F7_I2C_ALL_IRQ_MASK; - stm32f7_i2c_disable_irq(i2c_dev, mask); - - /* Clear STOP flag */ - writel_relaxed(STM32F7_I2C_ICR_STOPCF, base + STM32F7_I2C_ICR); - - if (i2c_dev->use_dma && !f7_msg->result) { - ret = IRQ_WAKE_THREAD; - } else { - i2c_dev->master_mode = false; - complete(&i2c_dev->complete); - } + stm32f7_i2c_reload(i2c_dev); } /* Transfer complete */ if (status & STM32F7_I2C_ISR_TC) { + /* Wait for dma transfer completion before sending next message */ + if (i2c_dev->use_dma && !f7_msg->result) { + ret = wait_for_completion_timeout(&i2c_dev->dma->dma_complete, HZ); + if (!ret) { + dev_dbg(i2c_dev->dev, "<%s>: Timed out\n", __func__); + stm32f7_i2c_disable_dma_req(i2c_dev); + dmaengine_terminate_async(dma->chan_using); + f7_msg->result = -ETIMEDOUT; + } + } if (f7_msg->stop) { mask = STM32F7_I2C_CR2_STOP; stm32f7_i2c_set_bits(base + STM32F7_I2C_CR2, mask); - } else if (i2c_dev->use_dma && !f7_msg->result) { - ret = IRQ_WAKE_THREAD; } else if (f7_msg->smbus) { stm32f7_i2c_smbus_rep_start(i2c_dev); } else { @@ -1567,47 +1657,18 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data) } } - if (status & STM32F7_I2C_ISR_TCR) { - if (f7_msg->smbus) - stm32f7_i2c_smbus_reload(i2c_dev); + /* STOP detection flag */ + if (status & STM32F7_I2C_ISR_STOPF) { + /* Disable interrupts */ + if (stm32f7_i2c_is_slave_registered(i2c_dev)) + mask = STM32F7_I2C_XFER_IRQ_MASK; else - stm32f7_i2c_reload(i2c_dev); - } - - return ret; -} - -static irqreturn_t stm32f7_i2c_isr_event_thread(int irq, void *data) -{ - struct stm32f7_i2c_dev *i2c_dev = data; - struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; - struct stm32_i2c_dma *dma = i2c_dev->dma; - u32 status; - int ret; - - /* - * Wait for dma transfer completion before sending next message or - * notity the end of xfer to the client - */ - ret = wait_for_completion_timeout(&i2c_dev->dma->dma_complete, HZ); - if (!ret) { - dev_dbg(i2c_dev->dev, "<%s>: Timed out\n", __func__); - stm32f7_i2c_disable_dma_req(i2c_dev); - dmaengine_terminate_async(dma->chan_using); - f7_msg->result = -ETIMEDOUT; - } + mask = STM32F7_I2C_ALL_IRQ_MASK; + stm32f7_i2c_disable_irq(i2c_dev, mask); - status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR); + /* Clear STOP flag */ + writel_relaxed(STM32F7_I2C_ICR_STOPCF, base + STM32F7_I2C_ICR); - if (status & STM32F7_I2C_ISR_TC) { - if (f7_msg->smbus) { - stm32f7_i2c_smbus_rep_start(i2c_dev); - } else { - i2c_dev->msg_id++; - i2c_dev->msg++; - stm32f7_i2c_xfer_msg(i2c_dev, i2c_dev->msg); - } - } else { i2c_dev->master_mode = false; complete(&i2c_dev->complete); } @@ -1615,68 +1676,14 @@ static irqreturn_t stm32f7_i2c_isr_event_thread(int irq, void *data) return IRQ_HANDLED; } -static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data) +static irqreturn_t stm32f7_i2c_isr_error_thread(int irq, void *data) { struct stm32f7_i2c_dev *i2c_dev = data; - struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg; - void __iomem *base = i2c_dev->base; - struct device *dev = i2c_dev->dev; - struct stm32_i2c_dma *dma = i2c_dev->dma; u32 status; status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR); - /* Bus error */ - if (status & STM32F7_I2C_ISR_BERR) { - dev_err(dev, "<%s>: Bus error accessing addr 0x%x\n", - __func__, f7_msg->addr); - writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR); - stm32f7_i2c_release_bus(&i2c_dev->adap); - f7_msg->result = -EIO; - } - - /* Arbitration loss */ - if (status & STM32F7_I2C_ISR_ARLO) { - dev_dbg(dev, "<%s>: Arbitration loss accessing addr 0x%x\n", - __func__, f7_msg->addr); - writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR); - f7_msg->result = -EAGAIN; - } - - if (status & STM32F7_I2C_ISR_PECERR) { - dev_err(dev, "<%s>: PEC error in reception accessing addr 0x%x\n", - __func__, f7_msg->addr); - writel_relaxed(STM32F7_I2C_ICR_PECCF, base + STM32F7_I2C_ICR); - f7_msg->result = -EINVAL; - } - - if (status & STM32F7_I2C_ISR_ALERT) { - dev_dbg(dev, "<%s>: SMBus alert received\n", __func__); - writel_relaxed(STM32F7_I2C_ICR_ALERTCF, base + STM32F7_I2C_ICR); - i2c_handle_smbus_alert(i2c_dev->alert->ara); - return IRQ_HANDLED; - } - - if (!i2c_dev->slave_running) { - u32 mask; - /* Disable interrupts */ - if (stm32f7_i2c_is_slave_registered(i2c_dev)) - mask = STM32F7_I2C_XFER_IRQ_MASK; - else - mask = STM32F7_I2C_ALL_IRQ_MASK; - stm32f7_i2c_disable_irq(i2c_dev, mask); - } - - /* Disable dma */ - if (i2c_dev->use_dma) { - stm32f7_i2c_disable_dma_req(i2c_dev); - dmaengine_terminate_async(dma->chan_using); - } - - i2c_dev->master_mode = false; - complete(&i2c_dev->complete); - - return IRQ_HANDLED; + return stm32f7_i2c_handle_isr_errs(i2c_dev, status); } static int stm32f7_i2c_wait_polling(struct stm32f7_i2c_dev *i2c_dev) @@ -2012,23 +2019,27 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave) static int stm32f7_i2c_write_fm_plus_bits(struct stm32f7_i2c_dev *i2c_dev, bool enable) { - int ret; + int ret = 0; if (i2c_dev->bus_rate <= I2C_MAX_FAST_MODE_FREQ || - IS_ERR_OR_NULL(i2c_dev->regmap)) + (!i2c_dev->setup.fmp_cr1_bit && IS_ERR_OR_NULL(i2c_dev->regmap))) /* Optional */ return 0; - if (i2c_dev->fmp_sreg == i2c_dev->fmp_creg) - ret = regmap_update_bits(i2c_dev->regmap, - i2c_dev->fmp_sreg, - i2c_dev->fmp_mask, - enable ? i2c_dev->fmp_mask : 0); - else - ret = regmap_write(i2c_dev->regmap, - enable ? i2c_dev->fmp_sreg : - i2c_dev->fmp_creg, - i2c_dev->fmp_mask); + if (i2c_dev->setup.fmp_cr1_bit) { + if (enable) + stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1, STM32_I2C_CR1_FMP); + else + stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, STM32_I2C_CR1_FMP); + } else { + if (i2c_dev->fmp_sreg == i2c_dev->fmp_creg) + ret = regmap_update_bits(i2c_dev->regmap, i2c_dev->fmp_sreg, + i2c_dev->fmp_mask, enable ? i2c_dev->fmp_mask : 0); + else + ret = regmap_write(i2c_dev->regmap, + enable ? i2c_dev->fmp_sreg : i2c_dev->fmp_creg, + i2c_dev->fmp_mask); + } return ret; } @@ -2162,6 +2173,13 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) if (!i2c_dev) return -ENOMEM; + setup = of_device_get_match_data(&pdev->dev); + if (!setup) { + dev_err(&pdev->dev, "Can't get device data\n"); + return -ENODEV; + } + i2c_dev->setup = *setup; + i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(i2c_dev->base)) return PTR_ERR(i2c_dev->base); @@ -2171,10 +2189,6 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) if (irq_event < 0) return irq_event; - irq_error = platform_get_irq(pdev, 1); - if (irq_error < 0) - return irq_error; - i2c_dev->wakeup_src = of_property_read_bool(pdev->dev.of_node, "wakeup-source"); @@ -2199,26 +2213,22 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) stm32f7_i2c_isr_event_thread, IRQF_ONESHOT, pdev->name, i2c_dev); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq event %i\n", - irq_event); - return ret; - } - - ret = devm_request_irq(&pdev->dev, irq_error, stm32f7_i2c_isr_error, 0, - pdev->name, i2c_dev); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq error %i\n", - irq_error); - return ret; - } - - setup = of_device_get_match_data(&pdev->dev); - if (!setup) { - dev_err(&pdev->dev, "Can't get device data\n"); - return -ENODEV; + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to request irq event\n"); + + if (!i2c_dev->setup.single_it_line) { + irq_error = platform_get_irq(pdev, 1); + if (irq_error < 0) + return irq_error; + + ret = devm_request_threaded_irq(&pdev->dev, irq_error, + NULL, + stm32f7_i2c_isr_error_thread, + IRQF_ONESHOT, + pdev->name, i2c_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to request irq error\n"); } - i2c_dev->setup = *setup; ret = stm32f7_i2c_setup_timing(i2c_dev, &i2c_dev->setup); if (ret) @@ -2226,9 +2236,12 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) /* Setup Fast mode plus if necessary */ if (i2c_dev->bus_rate > I2C_MAX_FAST_MODE_FREQ) { - ret = stm32f7_i2c_setup_fm_plus_bits(pdev, i2c_dev); - if (ret) - return ret; + if (!i2c_dev->setup.fmp_cr1_bit) { + ret = stm32f7_i2c_setup_fm_plus_bits(pdev, i2c_dev); + if (ret) + return ret; + } + ret = stm32f7_i2c_write_fm_plus_bits(i2c_dev, true); if (ret) return ret; @@ -2507,6 +2520,7 @@ static const struct of_device_id stm32f7_i2c_match[] = { { .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup}, { .compatible = "st,stm32mp15-i2c", .data = &stm32mp15_setup}, { .compatible = "st,stm32mp13-i2c", .data = &stm32mp13_setup}, + { .compatible = "st,stm32mp25-i2c", .data = &stm32mp25_setup}, {}, }; MODULE_DEVICE_TABLE(of, stm32f7_i2c_match); diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c index ad4f09c7f027..7ed29992a97f 100644 --- a/drivers/i2c/busses/i2c-via.c +++ b/drivers/i2c/busses/i2c-via.c @@ -70,7 +70,7 @@ static struct i2c_algo_bit_data bit_data = { static struct i2c_adapter vt586b_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .name = "VIA i2c", .algo_data = &bit_data, }; diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c index 970ccdcbb889..2cc7bba3b8bf 100644 --- a/drivers/i2c/busses/i2c-viapro.c +++ b/drivers/i2c/busses/i2c-viapro.c @@ -304,7 +304,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter vt596_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c index 76118abc6e10..ec2a8da134e5 100644 --- a/drivers/i2c/busses/i2c-wmt.c +++ b/drivers/i2c/busses/i2c-wmt.c @@ -74,9 +74,6 @@ #define MCR_APB_96M 7 #define MCR_APB_166M 12 -#define I2C_MODE_STANDARD 0 -#define I2C_MODE_FAST 1 - #define WMT_I2C_TIMEOUT (msecs_to_jiffies(1000)) struct wmt_i2c_dev { @@ -85,7 +82,7 @@ struct wmt_i2c_dev { struct device *dev; void __iomem *base; struct clk *clk; - int mode; + u16 tcr; int irq; u16 cmd_status; }; @@ -109,6 +106,12 @@ static int wmt_i2c_wait_bus_not_busy(struct wmt_i2c_dev *i2c_dev) static int wmt_check_status(struct wmt_i2c_dev *i2c_dev) { int ret = 0; + unsigned long wait_result; + + wait_result = wait_for_completion_timeout(&i2c_dev->complete, + msecs_to_jiffies(500)); + if (!wait_result) + return -ETIMEDOUT; if (i2c_dev->cmd_status & ISR_NACK_ADDR) ret = -EIO; @@ -119,21 +122,13 @@ static int wmt_check_status(struct wmt_i2c_dev *i2c_dev) return ret; } -static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg, +static int wmt_i2c_write(struct wmt_i2c_dev *i2c_dev, struct i2c_msg *pmsg, int last) { - struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap); - u16 val, tcr_val; + u16 val, tcr_val = i2c_dev->tcr; int ret; - unsigned long wait_result; int xfer_len = 0; - if (!(pmsg->flags & I2C_M_NOSTART)) { - ret = wmt_i2c_wait_bus_not_busy(i2c_dev); - if (ret < 0) - return ret; - } - if (pmsg->len == 0) { /* * We still need to run through the while (..) once, so @@ -148,20 +143,12 @@ static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg, if (!(pmsg->flags & I2C_M_NOSTART)) { val = readw(i2c_dev->base + REG_CR); val &= ~CR_TX_END; - writew(val, i2c_dev->base + REG_CR); - - val = readw(i2c_dev->base + REG_CR); val |= CR_CPU_RDY; writew(val, i2c_dev->base + REG_CR); } reinit_completion(&i2c_dev->complete); - if (i2c_dev->mode == I2C_MODE_STANDARD) - tcr_val = TCR_STANDARD_MODE; - else - tcr_val = TCR_FAST_MODE; - tcr_val |= (TCR_MASTER_WRITE | (pmsg->addr & TCR_SLAVE_ADDR_MASK)); writew(tcr_val, i2c_dev->base + REG_TCR); @@ -173,12 +160,6 @@ static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg, } while (xfer_len < pmsg->len) { - wait_result = wait_for_completion_timeout(&i2c_dev->complete, - msecs_to_jiffies(500)); - - if (wait_result == 0) - return -ETIMEDOUT; - ret = wmt_check_status(i2c_dev); if (ret) return ret; @@ -210,47 +191,24 @@ static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg, return 0; } -static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg, - int last) +static int wmt_i2c_read(struct wmt_i2c_dev *i2c_dev, struct i2c_msg *pmsg) { - struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap); - u16 val, tcr_val; + u16 val, tcr_val = i2c_dev->tcr; int ret; - unsigned long wait_result; u32 xfer_len = 0; - if (!(pmsg->flags & I2C_M_NOSTART)) { - ret = wmt_i2c_wait_bus_not_busy(i2c_dev); - if (ret < 0) - return ret; - } - - val = readw(i2c_dev->base + REG_CR); - val &= ~CR_TX_END; - writew(val, i2c_dev->base + REG_CR); - val = readw(i2c_dev->base + REG_CR); - val &= ~CR_TX_NEXT_NO_ACK; - writew(val, i2c_dev->base + REG_CR); + val &= ~(CR_TX_END | CR_TX_NEXT_NO_ACK); - if (!(pmsg->flags & I2C_M_NOSTART)) { - val = readw(i2c_dev->base + REG_CR); + if (!(pmsg->flags & I2C_M_NOSTART)) val |= CR_CPU_RDY; - writew(val, i2c_dev->base + REG_CR); - } - if (pmsg->len == 1) { - val = readw(i2c_dev->base + REG_CR); + if (pmsg->len == 1) val |= CR_TX_NEXT_NO_ACK; - writew(val, i2c_dev->base + REG_CR); - } - reinit_completion(&i2c_dev->complete); + writew(val, i2c_dev->base + REG_CR); - if (i2c_dev->mode == I2C_MODE_STANDARD) - tcr_val = TCR_STANDARD_MODE; - else - tcr_val = TCR_FAST_MODE; + reinit_completion(&i2c_dev->complete); tcr_val |= TCR_MASTER_READ | (pmsg->addr & TCR_SLAVE_ADDR_MASK); @@ -263,12 +221,6 @@ static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg, } while (xfer_len < pmsg->len) { - wait_result = wait_for_completion_timeout(&i2c_dev->complete, - msecs_to_jiffies(500)); - - if (!wait_result) - return -ETIMEDOUT; - ret = wmt_check_status(i2c_dev); if (ret) return ret; @@ -276,15 +228,10 @@ static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg, pmsg->buf[xfer_len] = readw(i2c_dev->base + REG_CDR) >> 8; xfer_len++; - if (xfer_len == pmsg->len - 1) { - val = readw(i2c_dev->base + REG_CR); - val |= (CR_TX_NEXT_NO_ACK | CR_CPU_RDY); - writew(val, i2c_dev->base + REG_CR); - } else { - val = readw(i2c_dev->base + REG_CR); - val |= CR_CPU_RDY; - writew(val, i2c_dev->base + REG_CR); - } + val = readw(i2c_dev->base + REG_CR) | CR_CPU_RDY; + if (xfer_len == pmsg->len - 1) + val |= CR_TX_NEXT_NO_ACK; + writew(val, i2c_dev->base + REG_CR); } return 0; @@ -295,17 +242,22 @@ static int wmt_i2c_xfer(struct i2c_adapter *adap, int num) { struct i2c_msg *pmsg; - int i, is_last; + int i; int ret = 0; + struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap); for (i = 0; ret >= 0 && i < num; i++) { - is_last = ((i + 1) == num); - pmsg = &msgs[i]; + if (!(pmsg->flags & I2C_M_NOSTART)) { + ret = wmt_i2c_wait_bus_not_busy(i2c_dev); + if (ret < 0) + return ret; + } + if (pmsg->flags & I2C_M_RD) - ret = wmt_i2c_read(adap, pmsg, is_last); + ret = wmt_i2c_read(i2c_dev, pmsg); else - ret = wmt_i2c_write(adap, pmsg, is_last); + ret = wmt_i2c_write(i2c_dev, pmsg, (i + 1) == num); } return (ret < 0) ? ret : i; @@ -359,10 +311,10 @@ static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev) readw(i2c_dev->base + REG_CSR); /* read clear */ writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR); - if (i2c_dev->mode == I2C_MODE_STANDARD) - writew(SCL_TIMEOUT(128) | TR_STD, i2c_dev->base + REG_TR); - else + if (i2c_dev->tcr == TCR_FAST_MODE) writew(SCL_TIMEOUT(128) | TR_HS, i2c_dev->base + REG_TR); + else + writew(SCL_TIMEOUT(128) | TR_STD, i2c_dev->base + REG_TR); return 0; } @@ -395,10 +347,9 @@ static int wmt_i2c_probe(struct platform_device *pdev) return PTR_ERR(i2c_dev->clk); } - i2c_dev->mode = I2C_MODE_STANDARD; err = of_property_read_u32(np, "clock-frequency", &clk_rate); if (!err && (clk_rate == I2C_MAX_FAST_MODE_FREQ)) - i2c_dev->mode = I2C_MODE_FAST; + i2c_dev->tcr = TCR_FAST_MODE; i2c_dev->dev = &pdev->dev; diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c index 83c1db610f54..3648382b885a 100644 --- a/drivers/i2c/busses/scx200_acb.c +++ b/drivers/i2c/busses/scx200_acb.c @@ -427,7 +427,7 @@ static struct scx200_acb_iface *scx200_create_iface(const char *text, snprintf(adapter->name, sizeof(adapter->name), "%s ACB%d", text, index); adapter->owner = THIS_MODULE; adapter->algo = &scx200_acb_algorithm; - adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adapter->class = I2C_CLASS_HWMON; adapter->dev.parent = dev; mutex_init(&iface->mutex); diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index eac90a3cf61a..3bd48d4b6318 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -16,6 +16,7 @@ #include <linux/acpi.h> #include <linux/clk/clk-conf.h> #include <linux/completion.h> +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/errno.h> @@ -67,6 +68,8 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key); static bool is_registered; +static struct dentry *i2c_debugfs_root; + int i2c_transfer_trace_reg(void) { static_branch_inc(&i2c_trace_msg_key); @@ -689,7 +692,7 @@ static struct attribute *i2c_dev_attrs[] = { }; ATTRIBUTE_GROUPS(i2c_dev); -struct bus_type i2c_bus_type = { +const struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match, .probe = i2c_device_probe, @@ -1524,6 +1527,8 @@ static int i2c_register_adapter(struct i2c_adapter *adap) goto out_list; } + adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root); + res = i2c_setup_smbus_alert(adap); if (res) goto out_reg; @@ -1563,6 +1568,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) return 0; out_reg: + debugfs_remove_recursive(adap->debugfs); init_completion(&adap->dev_released); device_unregister(&adap->dev); wait_for_completion(&adap->dev_released); @@ -1764,6 +1770,8 @@ void i2c_del_adapter(struct i2c_adapter *adap) i2c_host_notify_irq_teardown(adap); + debugfs_remove_recursive(adap->debugfs); + /* wait until all references to the device are gone * * FIXME: This is old code and should ideally be replaced by an @@ -2061,6 +2069,8 @@ static int __init i2c_init(void) is_registered = true; + i2c_debugfs_root = debugfs_create_dir("i2c", NULL); + #ifdef CONFIG_I2C_COMPAT i2c_adapter_compat_class = class_compat_register("i2c-adapter"); if (!i2c_adapter_compat_class) { @@ -2099,6 +2109,7 @@ static void __exit i2c_exit(void) #ifdef CONFIG_I2C_COMPAT class_compat_unregister(i2c_adapter_compat_class); #endif + debugfs_remove_recursive(i2c_debugfs_root); bus_unregister(&i2c_bus_type); tracepoint_synchronize_unregister(); } diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index 138c3f5e0093..74807c6db596 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -308,8 +308,8 @@ EXPORT_SYMBOL_GPL(i2c_free_slave_host_notify_device); * target systems are the same. * Restrictions to automatic SPD instantiation: * - Only works if all filled slots have the same memory type - * - Only works for DDR2, DDR3 and DDR4 for now - * - Only works on systems with 1 to 4 memory slots + * - Only works for DDR, DDR2, DDR3 and DDR4 for now + * - Only works on systems with 1 to 8 memory slots */ #if IS_ENABLED(CONFIG_DMI) void i2c_register_spd(struct i2c_adapter *adap) @@ -354,9 +354,9 @@ void i2c_register_spd(struct i2c_adapter *adap) dev_info(&adap->dev, "%d/%d memory slots populated (from DMI)\n", dimm_count, slot_count); - if (slot_count > 4) { + if (slot_count > 8) { dev_warn(&adap->dev, - "Systems with more than 4 memory slots not supported yet, not instantiating SPD\n"); + "Systems with more than 8 memory slots not supported yet, not instantiating SPD\n"); return; } diff --git a/drivers/i2c/i2c-stub.c b/drivers/i2c/i2c-stub.c index d642cad219d9..09e7b7bf4c5f 100644 --- a/drivers/i2c/i2c-stub.c +++ b/drivers/i2c/i2c-stub.c @@ -308,7 +308,7 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter stub_adapter = { .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &smbus_algorithm, .name = "SMBus stub driver", }; diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c index 9efc1ed01577..8489971babd3 100644 --- a/drivers/i2c/muxes/i2c-mux-reg.c +++ b/drivers/i2c/muxes/i2c-mux-reg.c @@ -159,7 +159,6 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) struct regmux *mux; struct i2c_adapter *parent; struct resource *res; - unsigned int class; int i, ret, nr; mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); @@ -213,9 +212,8 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) for (i = 0; i < mux->data.n_values; i++) { nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; - class = mux->data.classes ? mux->data.classes[i] : 0; - ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class); + ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], 0); if (ret) goto err_del_mux_adapters; } diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 95caa162706f..3afa530c5e32 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -557,6 +557,88 @@ static ssize_t i2c_scl_frequency_show(struct device *dev, } static DEVICE_ATTR_RO(i2c_scl_frequency); +static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) +{ + int ret; + + if (!master || !master->ops) + return -EINVAL; + + if (!master->ops->enable_hotjoin || !master->ops->disable_hotjoin) + return -EINVAL; + + i3c_bus_normaluse_lock(&master->bus); + + if (enable) + ret = master->ops->enable_hotjoin(master); + else + ret = master->ops->disable_hotjoin(master); + + master->hotjoin = enable; + + i3c_bus_normaluse_unlock(&master->bus); + + return ret; +} + +static ssize_t hotjoin_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i3c_bus *i3cbus = dev_to_i3cbus(dev); + int ret; + bool res; + + if (!i3cbus->cur_master) + return -EINVAL; + + if (kstrtobool(buf, &res)) + return -EINVAL; + + ret = i3c_set_hotjoin(i3cbus->cur_master->common.master, res); + if (ret) + return ret; + + return count; +} + +/* + * i3c_master_enable_hotjoin - Enable hotjoin + * @master: I3C master object + * + * Return: a 0 in case of success, an negative error code otherwise. + */ +int i3c_master_enable_hotjoin(struct i3c_master_controller *master) +{ + return i3c_set_hotjoin(master, true); +} +EXPORT_SYMBOL_GPL(i3c_master_enable_hotjoin); + +/* + * i3c_master_disable_hotjoin - Disable hotjoin + * @master: I3C master object + * + * Return: a 0 in case of success, an negative error code otherwise. + */ +int i3c_master_disable_hotjoin(struct i3c_master_controller *master) +{ + return i3c_set_hotjoin(master, false); +} +EXPORT_SYMBOL_GPL(i3c_master_disable_hotjoin); + +static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, char *buf) +{ + struct i3c_bus *i3cbus = dev_to_i3cbus(dev); + ssize_t ret; + + i3c_bus_normaluse_lock(i3cbus); + ret = sysfs_emit(buf, "%d\n", i3cbus->cur_master->common.master->hotjoin); + i3c_bus_normaluse_unlock(i3cbus); + + return ret; +} + +static DEVICE_ATTR_RW(hotjoin); + static struct attribute *i3c_masterdev_attrs[] = { &dev_attr_mode.attr, &dev_attr_current_master.attr, @@ -567,6 +649,7 @@ static struct attribute *i3c_masterdev_attrs[] = { &dev_attr_pid.attr, &dev_attr_dynamic_address.attr, &dev_attr_hdrcap.attr, + &dev_attr_hotjoin.attr, NULL, }; ATTRIBUTE_GROUPS(i3c_masterdev); @@ -1130,8 +1213,16 @@ static int i3c_master_getmxds_locked(struct i3c_master_controller *master, i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1); ret = i3c_master_send_ccc_cmd_locked(master, &cmd); - if (ret) - goto out; + if (ret) { + /* + * Retry when the device does not support max read turnaround + * while expecting shorter length from this CCC command. + */ + dest.payload.len -= 3; + ret = i3c_master_send_ccc_cmd_locked(master, &cmd); + if (ret) + goto out; + } if (dest.payload.len != 2 && dest.payload.len != 5) { ret = -EIO; diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index bcbe8f914149..c1627f3552ce 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -76,7 +76,8 @@ #define PRESCL_CTRL0 0x14 #define PRESCL_CTRL0_I2C(x) ((x) << 16) #define PRESCL_CTRL0_I3C(x) (x) -#define PRESCL_CTRL0_MAX GENMASK(9, 0) +#define PRESCL_CTRL0_I3C_MAX GENMASK(9, 0) +#define PRESCL_CTRL0_I2C_MAX GENMASK(15, 0) #define PRESCL_CTRL1 0x18 #define PRESCL_CTRL1_PP_LOW_MASK GENMASK(15, 8) @@ -1233,7 +1234,7 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m) return -EINVAL; pres = DIV_ROUND_UP(sysclk_rate, (bus->scl_rate.i3c * 4)) - 1; - if (pres > PRESCL_CTRL0_MAX) + if (pres > PRESCL_CTRL0_I3C_MAX) return -ERANGE; bus->scl_rate.i3c = sysclk_rate / ((pres + 1) * 4); @@ -1246,7 +1247,7 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m) max_i2cfreq = bus->scl_rate.i2c; pres = (sysclk_rate / (max_i2cfreq * 5)) - 1; - if (pres > PRESCL_CTRL0_MAX) + if (pres > PRESCL_CTRL0_I2C_MAX) return -ERANGE; bus->scl_rate.i2c = sysclk_rate / ((pres + 1) * 5); diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c index 2b2323aa6714..638b054d6c92 100644 --- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c +++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c @@ -298,7 +298,7 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci) unsigned int dcr, bcr; DECLARE_COMPLETION_ONSTACK(done); - xfer = hci_alloc_xfer(2); + xfer = hci_alloc_xfer(1); if (!xfer) return -ENOMEM; @@ -339,12 +339,13 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci) ret = -ETIME; break; } - if (RESP_STATUS(xfer[0].response) == RESP_ERR_NACK && + if ((RESP_STATUS(xfer->response) == RESP_ERR_ADDR_HEADER || + RESP_STATUS(xfer->response) == RESP_ERR_NACK) && RESP_DATA_LENGTH(xfer->response) == 1) { ret = 0; /* no more devices to be assigned */ break; } - if (RESP_STATUS(xfer[0].response) != RESP_SUCCESS) { + if (RESP_STATUS(xfer->response) != RESP_SUCCESS) { ret = -EIO; break; } diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 1ae56a5699c6..d7e966a25583 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -245,7 +245,14 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m, if (ccc->rnw) ccc->dests[i - prefixed].payload.len = RESP_DATA_LENGTH(xfer[i].response); - if (RESP_STATUS(xfer[i].response) != RESP_SUCCESS) { + switch (RESP_STATUS(xfer[i].response)) { + case RESP_SUCCESS: + continue; + case RESP_ERR_ADDR_HEADER: + case RESP_ERR_NACK: + ccc->err = I3C_ERROR_M2; + fallthrough; + default: ret = -EIO; goto out; } @@ -269,6 +276,34 @@ static int i3c_hci_daa(struct i3c_master_controller *m) return hci->cmd->perform_daa(hci); } +static int i3c_hci_alloc_safe_xfer_buf(struct i3c_hci *hci, + struct hci_xfer *xfer) +{ + if (hci->io != &mipi_i3c_hci_dma || + xfer->data == NULL || !is_vmalloc_addr(xfer->data)) + return 0; + + if (xfer->rnw) + xfer->bounce_buf = kzalloc(xfer->data_len, GFP_KERNEL); + else + xfer->bounce_buf = kmemdup(xfer->data, + xfer->data_len, GFP_KERNEL); + + return xfer->bounce_buf == NULL ? -ENOMEM : 0; +} + +static void i3c_hci_free_safe_xfer_buf(struct i3c_hci *hci, + struct hci_xfer *xfer) +{ + if (hci->io != &mipi_i3c_hci_dma || xfer->bounce_buf == NULL) + return; + + if (xfer->rnw) + memcpy(xfer->data, xfer->bounce_buf, xfer->data_len); + + kfree(xfer->bounce_buf); +} + static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, struct i3c_priv_xfer *i3c_xfers, int nxfers) @@ -302,6 +337,9 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, } hci->cmd->prep_i3c_xfer(hci, dev, &xfer[i]); xfer[i].cmd_desc[0] |= CMD_0_ROC; + ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]); + if (ret) + goto out; } last = i - 1; xfer[last].cmd_desc[0] |= CMD_0_TOC; @@ -325,6 +363,9 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, } out: + for (i = 0; i < nxfers; i++) + i3c_hci_free_safe_xfer_buf(hci, &xfer[i]); + hci_free_xfer(xfer, nxfers); return ret; } @@ -350,6 +391,9 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD; hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]); xfer[i].cmd_desc[0] |= CMD_0_ROC; + ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]); + if (ret) + goto out; } last = i - 1; xfer[last].cmd_desc[0] |= CMD_0_TOC; @@ -371,6 +415,9 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, } out: + for (i = 0; i < nxfers; i++) + i3c_hci_free_safe_xfer_buf(hci, &xfer[i]); + hci_free_xfer(xfer, nxfers); return ret; } diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index c805a8497319..4e01a95cc4d0 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -362,6 +362,7 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, struct hci_rh_data *rh; unsigned int i, ring, enqueue_ptr; u32 op1_val, op2_val; + void *buf; /* For now we only use ring 0 */ ring = 0; @@ -390,9 +391,10 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, /* 2nd and 3rd words of Data Buffer Descriptor Structure */ if (xfer->data) { + buf = xfer->bounce_buf ? xfer->bounce_buf : xfer->data; xfer->data_dma = dma_map_single(&hci->master.dev, - xfer->data, + buf, xfer->data_len, xfer->rnw ? DMA_FROM_DEVICE : diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index f109923f6c3f..f94d95e024be 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -90,6 +90,7 @@ struct hci_xfer { struct { /* DMA specific */ dma_addr_t data_dma; + void *bounce_buf; int ring_number; int ring_entry; }; diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index cf703c00f633..5ee4db68988e 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -128,13 +128,17 @@ /* This parameter depends on the implementation and may be tuned */ #define SVC_I3C_FIFO_SIZE 16 +#define SVC_I3C_EVENT_IBI BIT(0) +#define SVC_I3C_EVENT_HOTJOIN BIT(1) + struct svc_i3c_cmd { u8 addr; bool rnw; u8 *in; const void *out; unsigned int len; - unsigned int read_len; + unsigned int actual_len; + struct i3c_priv_xfer *xfer; bool continued; }; @@ -177,6 +181,7 @@ struct svc_i3c_regs_save { * @ibi.tbq_slot: To be queued IBI slot * @ibi.lock: IBI lock * @lock: Transfer lock, protect between IBI work thread and callbacks from master + * @enabled_events: Bit masks for enable events (IBI, HotJoin). */ struct svc_i3c_master { struct i3c_master_controller base; @@ -206,6 +211,7 @@ struct svc_i3c_master { spinlock_t lock; } ibi; struct mutex lock; + int enabled_events; }; /** @@ -220,6 +226,11 @@ struct svc_i3c_i2c_dev_data { struct i3c_generic_ibi_pool *ibi_pool; }; +static inline bool is_events_enabled(struct svc_i3c_master *master, u32 mask) +{ + return !!(master->enabled_events & mask); +} + static bool svc_i3c_master_error(struct svc_i3c_master *master) { u32 mstatus, merrwarn; @@ -429,13 +440,16 @@ static void svc_i3c_master_ibi_work(struct work_struct *work) switch (ibitype) { case SVC_I3C_MSTATUS_IBITYPE_IBI: dev = svc_i3c_master_dev_from_addr(master, ibiaddr); - if (!dev) + if (!dev || !is_events_enabled(master, SVC_I3C_EVENT_IBI)) svc_i3c_master_nack_ibi(master); else svc_i3c_master_handle_ibi(master, dev); break; case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN: - svc_i3c_master_ack_ibi(master, false); + if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN)) + svc_i3c_master_ack_ibi(master, false); + else + svc_i3c_master_nack_ibi(master); break; case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST: svc_i3c_master_nack_ibi(master); @@ -472,7 +486,9 @@ static void svc_i3c_master_ibi_work(struct work_struct *work) svc_i3c_master_emit_stop(master); break; case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN: - queue_work(master->base.wq, &master->hj_work); + svc_i3c_master_emit_stop(master); + if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN)) + queue_work(master->base.wq, &master->hj_work); break; case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST: default: @@ -1024,7 +1040,7 @@ static int svc_i3c_master_write(struct svc_i3c_master *master, static int svc_i3c_master_xfer(struct svc_i3c_master *master, bool rnw, unsigned int xfer_type, u8 addr, u8 *in, const u8 *out, unsigned int xfer_len, - unsigned int *read_len, bool continued) + unsigned int *actual_len, bool continued) { u32 reg; int ret; @@ -1037,7 +1053,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, SVC_I3C_MCTRL_IBIRESP_NACK | SVC_I3C_MCTRL_DIR(rnw) | SVC_I3C_MCTRL_ADDR(addr) | - SVC_I3C_MCTRL_RDTERM(*read_len), + SVC_I3C_MCTRL_RDTERM(*actual_len), master->regs + SVC_I3C_MCTRL); ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg, @@ -1047,6 +1063,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) { ret = -ENXIO; + *actual_len = 0; goto emit_stop; } @@ -1064,6 +1081,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, */ if (SVC_I3C_MSTATUS_IBIWON(reg)) { ret = -ENXIO; + *actual_len = 0; goto emit_stop; } @@ -1075,7 +1093,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, goto emit_stop; if (rnw) - *read_len = ret; + *actual_len = ret; ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg, SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000); @@ -1157,8 +1175,12 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master) ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type, cmd->addr, cmd->in, cmd->out, - cmd->len, &cmd->read_len, + cmd->len, &cmd->actual_len, cmd->continued); + /* cmd->xfer is NULL if I2C or CCC transfer */ + if (cmd->xfer) + cmd->xfer->actual_len = cmd->actual_len; + if (ret) break; } @@ -1243,7 +1265,7 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master, cmd->in = NULL; cmd->out = buf; cmd->len = xfer_len; - cmd->read_len = 0; + cmd->actual_len = 0; cmd->continued = false; mutex_lock(&master->lock); @@ -1263,7 +1285,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master, struct i3c_ccc_cmd *ccc) { unsigned int xfer_len = ccc->dests[0].payload.len; - unsigned int read_len = ccc->rnw ? xfer_len : 0; + unsigned int actual_len = ccc->rnw ? xfer_len : 0; struct svc_i3c_xfer *xfer; struct svc_i3c_cmd *cmd; int ret; @@ -1281,7 +1303,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master, cmd->in = NULL; cmd->out = &ccc->id; cmd->len = 1; - cmd->read_len = 0; + cmd->actual_len = 0; cmd->continued = true; /* Directed message */ @@ -1291,7 +1313,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master, cmd->in = ccc->rnw ? ccc->dests[0].payload.data : NULL; cmd->out = ccc->rnw ? NULL : ccc->dests[0].payload.data, cmd->len = xfer_len; - cmd->read_len = read_len; + cmd->actual_len = actual_len; cmd->continued = false; mutex_lock(&master->lock); @@ -1300,8 +1322,8 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master, svc_i3c_master_dequeue_xfer(master, xfer); mutex_unlock(&master->lock); - if (cmd->read_len != xfer_len) - ccc->dests[0].payload.len = cmd->read_len; + if (cmd->actual_len != xfer_len) + ccc->dests[0].payload.len = cmd->actual_len; ret = xfer->ret; svc_i3c_master_free_xfer(xfer); @@ -1346,12 +1368,13 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev, for (i = 0; i < nxfers; i++) { struct svc_i3c_cmd *cmd = &xfer->cmds[i]; + cmd->xfer = &xfers[i]; cmd->addr = master->addrs[data->index]; cmd->rnw = xfers[i].rnw; cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL; cmd->out = xfers[i].rnw ? NULL : xfers[i].data.out; cmd->len = xfers[i].len; - cmd->read_len = xfers[i].rnw ? xfers[i].len : 0; + cmd->actual_len = xfers[i].rnw ? xfers[i].len : 0; cmd->continued = (i + 1) < nxfers; } @@ -1391,7 +1414,7 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, cmd->in = cmd->rnw ? xfers[i].buf : NULL; cmd->out = cmd->rnw ? NULL : xfers[i].buf; cmd->len = xfers[i].len; - cmd->read_len = cmd->rnw ? xfers[i].len : 0; + cmd->actual_len = cmd->rnw ? xfers[i].len : 0; cmd->continued = (i + 1 < nxfers); } @@ -1472,6 +1495,7 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev) return ret; } + master->enabled_events |= SVC_I3C_EVENT_IBI; svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART); return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); @@ -1483,7 +1507,9 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev) struct svc_i3c_master *master = to_svc_i3c_master(m); int ret; - svc_i3c_master_disable_interrupts(master); + master->enabled_events &= ~SVC_I3C_EVENT_IBI; + if (!master->enabled_events) + svc_i3c_master_disable_interrupts(master); ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); @@ -1493,6 +1519,39 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev) return ret; } +static int svc_i3c_master_enable_hotjoin(struct i3c_master_controller *m) +{ + struct svc_i3c_master *master = to_svc_i3c_master(m); + int ret; + + ret = pm_runtime_resume_and_get(master->dev); + if (ret < 0) { + dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__); + return ret; + } + + master->enabled_events |= SVC_I3C_EVENT_HOTJOIN; + + svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART); + + return 0; +} + +static int svc_i3c_master_disable_hotjoin(struct i3c_master_controller *m) +{ + struct svc_i3c_master *master = to_svc_i3c_master(m); + + master->enabled_events &= ~SVC_I3C_EVENT_HOTJOIN; + + if (!master->enabled_events) + svc_i3c_master_disable_interrupts(master); + + pm_runtime_mark_last_busy(master->dev); + pm_runtime_put_autosuspend(master->dev); + + return 0; +} + static void svc_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot) { @@ -1519,6 +1578,8 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = { .recycle_ibi_slot = svc_i3c_master_recycle_ibi_slot, .enable_ibi = svc_i3c_master_enable_ibi, .disable_ibi = svc_i3c_master_disable_ibi, + .enable_hotjoin = svc_i3c_master_enable_hotjoin, + .disable_hotjoin = svc_i3c_master_disable_hotjoin, }; static int svc_i3c_master_prepare_clks(struct svc_i3c_master *master) diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index f113dae59048..91adcac875a4 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -260,10 +260,11 @@ config BMI088_ACCEL select REGMAP select BMI088_ACCEL_SPI help - Say yes here to build support for the Bosch BMI088 accelerometer. + Say yes here to build support for the following Bosch accelerometers: + BMI088, BMI085, BMI090L. Note that all of these are combo module that + include both accelerometer and gyroscope. - This is a combo module with both accelerometer and gyroscope. This - driver only implements the accelerometer part, which has its own + This driver only implements the accelerometer part, which has its own address and register map. BMG160 provides the gyroscope driver. config BMI088_ACCEL_SPI diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c index 84edcc78d796..4d989708e6c3 100644 --- a/drivers/iio/accel/bmi088-accel-core.c +++ b/drivers/iio/accel/bmi088-accel-core.c @@ -2,6 +2,8 @@ /* * 3-axis accelerometer driver supporting following Bosch-Sensortec chips: * - BMI088 + * - BMI085 + * - BMI090L * * Copyright (c) 2018-2021, Topic Embedded Products */ diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c index ee540edd8412..7b419a7b2478 100644 --- a/drivers/iio/accel/bmi088-accel-spi.c +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -2,6 +2,8 @@ /* * 3-axis accelerometer driver supporting following Bosch-Sensortec chips: * - BMI088 + * - BMI085 + * - BMI090L * * Copyright (c) 2018-2020, Topic Embedded Products */ diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 35f9867da12c..3b73c509bd68 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -36,13 +36,29 @@ config AD4130 To compile this driver as a module, choose M here: the module will be called ad4130. +config AD7091R + tristate + config AD7091R5 tristate "Analog Devices AD7091R5 ADC Driver" depends on I2C + select AD7091R select REGMAP_I2C help Say yes here to build support for Analog Devices AD7091R-5 ADC. +config AD7091R8 + tristate "Analog Devices AD7091R8 ADC Driver" + depends on SPI + select AD7091R + select REGMAP_SPI + help + Say yes here to build support for Analog Devices AD7091R-2, AD7091R-4, + and AD7091R-8 ADC. + + To compile this driver as a module, choose M here: the module will be + called ad7091r8. + config AD7124 tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver" depends on SPI_MASTER @@ -292,7 +308,7 @@ config ADI_AXI_ADC select IIO_BUFFER select IIO_BUFFER_HW_CONSUMER select IIO_BUFFER_DMAENGINE - depends on HAS_IOMEM + select REGMAP_MMIO depends on OF help Say yes here to build support for Analog Devices Generic @@ -745,6 +761,17 @@ config MAX1363 To compile this driver as a module, choose M here: the module will be called max1363. +config MAX34408 + tristate "Maxim max34408/max344089 ADC driver" + depends on I2C + help + Say yes here to build support for Maxim max34408/max34409 current sense + monitor with 8-bits ADC interface with overcurrent delay/threshold and + shutdown delay. + + To compile this driver as a module, choose M here: the module will be + called max34408. + config MAX77541_ADC tristate "Analog Devices MAX77541 ADC driver" depends on MFD_MAX77541 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index bee11d442af4..d2fda54a3259 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -7,7 +7,9 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD4130) += ad4130.o -obj-$(CONFIG_AD7091R5) += ad7091r5.o ad7091r-base.o +obj-$(CONFIG_AD7091R) += ad7091r-base.o +obj-$(CONFIG_AD7091R5) += ad7091r5.o +obj-$(CONFIG_AD7091R8) += ad7091r8.o obj-$(CONFIG_AD7124) += ad7124.o obj-$(CONFIG_AD7192) += ad7192.o obj-$(CONFIG_AD7266) += ad7266.o @@ -68,6 +70,7 @@ obj-$(CONFIG_MAX11205) += max11205.o obj-$(CONFIG_MAX11410) += max11410.o obj-$(CONFIG_MAX1241) += max1241.o obj-$(CONFIG_MAX1363) += max1363.o +obj-$(CONFIG_MAX34408) += max34408.o obj-$(CONFIG_MAX77541_ADC) += max77541-adc.o obj-$(CONFIG_MAX9611) += max9611.o obj-$(CONFIG_MCP320X) += mcp320x.o diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index 8e252cde735b..f4255b91acfc 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -6,6 +6,7 @@ */ #include <linux/bitops.h> +#include <linux/bitfield.h> #include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/interrupt.h> @@ -15,67 +16,26 @@ #include "ad7091r-base.h" -#define AD7091R_REG_RESULT 0 -#define AD7091R_REG_CHANNEL 1 -#define AD7091R_REG_CONF 2 -#define AD7091R_REG_ALERT 3 -#define AD7091R_REG_CH_LOW_LIMIT(ch) ((ch) * 3 + 4) -#define AD7091R_REG_CH_HIGH_LIMIT(ch) ((ch) * 3 + 5) -#define AD7091R_REG_CH_HYSTERESIS(ch) ((ch) * 3 + 6) - -/* AD7091R_REG_RESULT */ -#define AD7091R_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x3) -#define AD7091R_REG_RESULT_CONV_RESULT(x) ((x) & 0xfff) - -/* AD7091R_REG_CONF */ -#define AD7091R_REG_CONF_AUTO BIT(8) -#define AD7091R_REG_CONF_CMD BIT(10) - -#define AD7091R_REG_CONF_MODE_MASK \ - (AD7091R_REG_CONF_AUTO | AD7091R_REG_CONF_CMD) - -enum ad7091r_mode { - AD7091R_MODE_SAMPLE, - AD7091R_MODE_COMMAND, - AD7091R_MODE_AUTOCYCLE, +const struct iio_event_spec ad7091r_events[] = { + { + .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_HYSTERESIS), + }, }; - -struct ad7091r_state { - struct device *dev; - struct regmap *map; - struct regulator *vref; - const struct ad7091r_chip_info *chip_info; - enum ad7091r_mode mode; - struct mutex lock; /*lock to prevent concurent reads */ -}; - -static int ad7091r_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode) -{ - int ret, conf; - - switch (mode) { - case AD7091R_MODE_SAMPLE: - conf = 0; - break; - case AD7091R_MODE_COMMAND: - conf = AD7091R_REG_CONF_CMD; - break; - case AD7091R_MODE_AUTOCYCLE: - conf = AD7091R_REG_CONF_AUTO; - break; - default: - return -EINVAL; - } - - ret = regmap_update_bits(st->map, AD7091R_REG_CONF, - AD7091R_REG_CONF_MODE_MASK, conf); - if (ret) - return ret; - - st->mode = mode; - - return 0; -} +EXPORT_SYMBOL_NS_GPL(ad7091r_events, IIO_AD7091R); static int ad7091r_set_channel(struct ad7091r_state *st, unsigned int channel) { @@ -110,7 +70,7 @@ static int ad7091r_read_one(struct iio_dev *iio_dev, if (ret) return ret; - if (AD7091R_REG_RESULT_CH_ID(val) != channel) + if (st->chip_info->reg_result_chan_id(val) != channel) return -EIO; *read_val = AD7091R_REG_RESULT_CONV_RESULT(val); @@ -168,14 +128,148 @@ unlock: return ret; } +static int ad7091r_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7091r_state *st = iio_priv(indio_dev); + int val, ret; + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = regmap_read(st->map, + AD7091R_REG_CH_HIGH_LIMIT(chan->channel), + &val); + if (ret) + return ret; + return val != AD7091R_HIGH_LIMIT; + case IIO_EV_DIR_FALLING: + ret = regmap_read(st->map, + AD7091R_REG_CH_LOW_LIMIT(chan->channel), + &val); + if (ret) + return ret; + return val != AD7091R_LOW_LIMIT; + default: + return -EINVAL; + } +} + +static int ad7091r_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct ad7091r_state *st = iio_priv(indio_dev); + + if (state) { + return regmap_set_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_ALERT_EN); + } else { + /* + * Set thresholds either to 0 or to 2^12 - 1 as appropriate to + * prevent alerts and thus disable event generation. + */ + switch (dir) { + case IIO_EV_DIR_RISING: + return regmap_write(st->map, + AD7091R_REG_CH_HIGH_LIMIT(chan->channel), + AD7091R_HIGH_LIMIT); + case IIO_EV_DIR_FALLING: + return regmap_write(st->map, + AD7091R_REG_CH_LOW_LIMIT(chan->channel), + AD7091R_LOW_LIMIT); + default: + return -EINVAL; + } + } +} + +static int ad7091r_read_event_value(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 ad7091r_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + ret = regmap_read(st->map, + AD7091R_REG_CH_HIGH_LIMIT(chan->channel), + val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + ret = regmap_read(st->map, + AD7091R_REG_CH_LOW_LIMIT(chan->channel), + val); + if (ret) + return ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + ret = regmap_read(st->map, + AD7091R_REG_CH_HYSTERESIS(chan->channel), + val); + if (ret) + return ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7091r_write_event_value(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 ad7091r_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + return regmap_write(st->map, + AD7091R_REG_CH_HIGH_LIMIT(chan->channel), + val); + case IIO_EV_DIR_FALLING: + return regmap_write(st->map, + AD7091R_REG_CH_LOW_LIMIT(chan->channel), + val); + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + return regmap_write(st->map, + AD7091R_REG_CH_HYSTERESIS(chan->channel), + val); + default: + return -EINVAL; + } +} + static const struct iio_info ad7091r_info = { .read_raw = ad7091r_read_raw, + .read_event_config = &ad7091r_read_event_config, + .write_event_config = &ad7091r_write_event_config, + .read_event_value = &ad7091r_read_event_value, + .write_event_value = &ad7091r_write_event_value, }; static irqreturn_t ad7091r_event_handler(int irq, void *private) { - struct ad7091r_state *st = (struct ad7091r_state *) private; - struct iio_dev *iio_dev = dev_get_drvdata(st->dev); + struct iio_dev *iio_dev = private; + struct ad7091r_state *st = iio_priv(iio_dev); unsigned int i, read_val; int ret; s64 timestamp = iio_get_time_ns(iio_dev); @@ -207,9 +301,8 @@ static void ad7091r_remove(void *data) regulator_disable(st->vref); } -int ad7091r_probe(struct device *dev, const char *name, - const struct ad7091r_chip_info *chip_info, - struct regmap *map, int irq) +int ad7091r_probe(struct device *dev, const struct ad7091r_init_info *init_info, + int irq) { struct iio_dev *iio_dev; struct ad7091r_state *st; @@ -221,29 +314,54 @@ int ad7091r_probe(struct device *dev, const char *name, st = iio_priv(iio_dev); st->dev = dev; - st->chip_info = chip_info; - st->map = map; + init_info->init_adc_regmap(st, init_info->regmap_config); + if (IS_ERR(st->map)) + return dev_err_probe(st->dev, PTR_ERR(st->map), + "Error initializing regmap\n"); - iio_dev->name = name; iio_dev->info = &ad7091r_info; iio_dev->modes = INDIO_DIRECT_MODE; - iio_dev->num_channels = chip_info->num_channels; - iio_dev->channels = chip_info->channels; + if (init_info->setup) { + ret = init_info->setup(st); + if (ret < 0) + return ret; + } if (irq) { + st->chip_info = init_info->info_irq; + ret = regmap_update_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_ALERT_EN, BIT(4)); + if (ret) + return ret; + ret = devm_request_threaded_irq(dev, irq, NULL, - ad7091r_event_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name, st); + ad7091r_event_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + st->chip_info->name, iio_dev); if (ret) return ret; + } else { + st->chip_info = init_info->info_no_irq; } + iio_dev->name = st->chip_info->name; + iio_dev->num_channels = st->chip_info->num_channels; + iio_dev->channels = st->chip_info->channels; + st->vref = devm_regulator_get_optional(dev, "vref"); if (IS_ERR(st->vref)) { if (PTR_ERR(st->vref) == -EPROBE_DEFER) return -EPROBE_DEFER; + st->vref = NULL; + /* Enable internal vref */ + ret = regmap_set_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_INT_VREF); + if (ret) + return dev_err_probe(st->dev, ret, + "Error on enable internal reference\n"); } else { ret = regulator_enable(st->vref); if (ret) @@ -254,7 +372,7 @@ int ad7091r_probe(struct device *dev, const char *name, } /* Use command mode by default to convert only desired channels*/ - ret = ad7091r_set_mode(st, AD7091R_MODE_COMMAND); + ret = st->chip_info->set_mode(st, AD7091R_MODE_COMMAND); if (ret) return ret; @@ -262,7 +380,7 @@ int ad7091r_probe(struct device *dev, const char *name, } EXPORT_SYMBOL_NS_GPL(ad7091r_probe, IIO_AD7091R); -static bool ad7091r_writeable_reg(struct device *dev, unsigned int reg) +bool ad7091r_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { case AD7091R_REG_RESULT: @@ -272,8 +390,9 @@ static bool ad7091r_writeable_reg(struct device *dev, unsigned int reg) return true; } } +EXPORT_SYMBOL_NS_GPL(ad7091r_writeable_reg, IIO_AD7091R); -static bool ad7091r_volatile_reg(struct device *dev, unsigned int reg) +bool ad7091r_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case AD7091R_REG_RESULT: @@ -283,14 +402,7 @@ static bool ad7091r_volatile_reg(struct device *dev, unsigned int reg) return false; } } - -const struct regmap_config ad7091r_regmap_config = { - .reg_bits = 8, - .val_bits = 16, - .writeable_reg = ad7091r_writeable_reg, - .volatile_reg = ad7091r_volatile_reg, -}; -EXPORT_SYMBOL_NS_GPL(ad7091r_regmap_config, IIO_AD7091R); +EXPORT_SYMBOL_NS_GPL(ad7091r_volatile_reg, IIO_AD7091R); MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7091Rx multi-channel converters"); diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h index 509748aef9b1..696bf7a897bb 100644 --- a/drivers/iio/adc/ad7091r-base.h +++ b/drivers/iio/adc/ad7091r-base.h @@ -8,19 +8,92 @@ #ifndef __DRIVERS_IIO_ADC_AD7091R_BASE_H__ #define __DRIVERS_IIO_ADC_AD7091R_BASE_H__ +#include <linux/regmap.h> + +#define AD7091R_REG_RESULT 0 +#define AD7091R_REG_CHANNEL 1 +#define AD7091R_REG_CONF 2 +#define AD7091R_REG_ALERT 3 +#define AD7091R_REG_CH_LOW_LIMIT(ch) ((ch) * 3 + 4) +#define AD7091R_REG_CH_HIGH_LIMIT(ch) ((ch) * 3 + 5) +#define AD7091R_REG_CH_HYSTERESIS(ch) ((ch) * 3 + 6) + +/* AD7091R_REG_RESULT */ +#define AD7091R5_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x3) +#define AD7091R8_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x7) +#define AD7091R_REG_RESULT_CONV_RESULT(x) ((x) & 0xfff) + +/* AD7091R_REG_CONF */ +#define AD7091R_REG_CONF_INT_VREF BIT(0) +#define AD7091R_REG_CONF_ALERT_EN BIT(4) +#define AD7091R_REG_CONF_AUTO BIT(8) +#define AD7091R_REG_CONF_CMD BIT(10) + +#define AD7091R_REG_CONF_MODE_MASK \ + (AD7091R_REG_CONF_AUTO | AD7091R_REG_CONF_CMD) + +/* AD7091R_REG_CH_LIMIT */ +#define AD7091R_HIGH_LIMIT 0xFFF +#define AD7091R_LOW_LIMIT 0x0 + +#define AD7091R_CHANNEL(idx, bits, ev, num_ev) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .channel = idx, \ + .event_spec = ev, \ + .num_event_specs = num_ev, \ + .scan_type.storagebits = 16, \ + .scan_type.realbits = bits, \ +} + struct device; -struct ad7091r_state; +struct gpio_desc; + +enum ad7091r_mode { + AD7091R_MODE_SAMPLE, + AD7091R_MODE_COMMAND, + AD7091R_MODE_AUTOCYCLE, +}; + +struct ad7091r_state { + struct device *dev; + struct regmap *map; + struct gpio_desc *convst_gpio; + struct gpio_desc *reset_gpio; + struct regulator *vref; + const struct ad7091r_chip_info *chip_info; + enum ad7091r_mode mode; + struct mutex lock; /*lock to prevent concurent reads */ + __be16 tx_buf __aligned(IIO_DMA_MINALIGN); + __be16 rx_buf; +}; struct ad7091r_chip_info { + const char *name; unsigned int num_channels; const struct iio_chan_spec *channels; unsigned int vref_mV; + unsigned int (*reg_result_chan_id)(unsigned int val); + int (*set_mode)(struct ad7091r_state *st, enum ad7091r_mode mode); }; -extern const struct regmap_config ad7091r_regmap_config; +struct ad7091r_init_info { + const struct ad7091r_chip_info *info_irq; + const struct ad7091r_chip_info *info_no_irq; + const struct regmap_config *regmap_config; + void (*init_adc_regmap)(struct ad7091r_state *st, + const struct regmap_config *regmap_conf); + int (*setup)(struct ad7091r_state *st); +}; + +extern const struct iio_event_spec ad7091r_events[3]; + +int ad7091r_probe(struct device *dev, const struct ad7091r_init_info *init_info, + int irq); -int ad7091r_probe(struct device *dev, const char *name, - const struct ad7091r_chip_info *chip_info, - struct regmap *map, int irq); +bool ad7091r_volatile_reg(struct device *dev, unsigned int reg); +bool ad7091r_writeable_reg(struct device *dev, unsigned int reg); #endif /* __DRIVERS_IIO_ADC_AD7091R_BASE_H__ */ diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c index 2f048527b7b7..a75837334157 100644 --- a/drivers/iio/adc/ad7091r5.c +++ b/drivers/iio/adc/ad7091r5.c @@ -12,42 +12,11 @@ #include "ad7091r-base.h" -static const struct iio_event_spec ad7091r5_events[] = { - { - .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_HYSTERESIS), - }, -}; - -#define AD7091R_CHANNEL(idx, bits, ev, num_ev) { \ - .type = IIO_VOLTAGE, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .indexed = 1, \ - .channel = idx, \ - .event_spec = ev, \ - .num_event_specs = num_ev, \ - .scan_type.storagebits = 16, \ - .scan_type.realbits = bits, \ -} static const struct iio_chan_spec ad7091r5_channels_irq[] = { - AD7091R_CHANNEL(0, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), - AD7091R_CHANNEL(1, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), - AD7091R_CHANNEL(2, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), - AD7091R_CHANNEL(3, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), + AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(2, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(3, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), }; static const struct iio_chan_spec ad7091r5_channels_noirq[] = { @@ -57,43 +26,98 @@ static const struct iio_chan_spec ad7091r5_channels_noirq[] = { AD7091R_CHANNEL(3, 12, NULL, 0), }; +static int ad7091r5_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode) +{ + int ret, conf; + + switch (mode) { + case AD7091R_MODE_SAMPLE: + conf = 0; + break; + case AD7091R_MODE_COMMAND: + conf = AD7091R_REG_CONF_CMD; + break; + case AD7091R_MODE_AUTOCYCLE: + conf = AD7091R_REG_CONF_AUTO; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_MODE_MASK, conf); + if (ret) + return ret; + + st->mode = mode; + + return 0; +} + +static unsigned int ad7091r5_reg_result_chan_id(unsigned int val) +{ + return AD7091R5_REG_RESULT_CH_ID(val); +} + static const struct ad7091r_chip_info ad7091r5_chip_info_irq = { + .name = "ad7091r-5", .channels = ad7091r5_channels_irq, .num_channels = ARRAY_SIZE(ad7091r5_channels_irq), .vref_mV = 2500, + .reg_result_chan_id = &ad7091r5_reg_result_chan_id, + .set_mode = &ad7091r5_set_mode, }; static const struct ad7091r_chip_info ad7091r5_chip_info_noirq = { + .name = "ad7091r-5", .channels = ad7091r5_channels_noirq, .num_channels = ARRAY_SIZE(ad7091r5_channels_noirq), .vref_mV = 2500, + .reg_result_chan_id = &ad7091r5_reg_result_chan_id, + .set_mode = &ad7091r5_set_mode, }; -static int ad7091r5_i2c_probe(struct i2c_client *i2c) +static const struct regmap_config ad7091r_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .writeable_reg = ad7091r_writeable_reg, + .volatile_reg = ad7091r_volatile_reg, +}; + +static void ad7091r5_regmap_init(struct ad7091r_state *st, + const struct regmap_config *regmap_conf) { - const struct i2c_device_id *id = i2c_client_get_device_id(i2c); - const struct ad7091r_chip_info *chip_info; - struct regmap *map = devm_regmap_init_i2c(i2c, &ad7091r_regmap_config); + struct i2c_client *i2c = container_of(st->dev, struct i2c_client, dev); - if (IS_ERR(map)) - return PTR_ERR(map); + st->map = devm_regmap_init_i2c(i2c, regmap_conf); +} + +static struct ad7091r_init_info ad7091r5_init_info = { + .info_irq = &ad7091r5_chip_info_irq, + .info_no_irq = &ad7091r5_chip_info_noirq, + .regmap_config = &ad7091r_regmap_config, + .init_adc_regmap = &ad7091r5_regmap_init +}; + +static int ad7091r5_i2c_probe(struct i2c_client *i2c) +{ + const struct ad7091r_init_info *init_info; - if (i2c->irq) - chip_info = &ad7091r5_chip_info_irq; - else - chip_info = &ad7091r5_chip_info_noirq; + init_info = i2c_get_match_data(i2c); + if (!init_info) + return -EINVAL; - return ad7091r_probe(&i2c->dev, id->name, chip_info, map, i2c->irq); + return ad7091r_probe(&i2c->dev, init_info, i2c->irq); } static const struct of_device_id ad7091r5_dt_ids[] = { - { .compatible = "adi,ad7091r5" }, + { .compatible = "adi,ad7091r5", .data = &ad7091r5_init_info }, {}, }; MODULE_DEVICE_TABLE(of, ad7091r5_dt_ids); static const struct i2c_device_id ad7091r5_i2c_ids[] = { - {"ad7091r5", 0}, + {"ad7091r5", (kernel_ulong_t)&ad7091r5_init_info }, {} }; MODULE_DEVICE_TABLE(i2c, ad7091r5_i2c_ids); diff --git a/drivers/iio/adc/ad7091r8.c b/drivers/iio/adc/ad7091r8.c new file mode 100644 index 000000000000..57700f124803 --- /dev/null +++ b/drivers/iio/adc/ad7091r8.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7091R8 12-bit SAR ADC driver + * + * Copyright 2023 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/gpio/consumer.h> +#include <linux/spi/spi.h> + +#include "ad7091r-base.h" + +#define AD7091R8_REG_ADDR_MSK GENMASK(15, 11) +#define AD7091R8_RD_WR_FLAG_MSK BIT(10) +#define AD7091R8_REG_DATA_MSK GENMASK(9, 0) + +#define AD7091R_SPI_REGMAP_CONFIG(n) { \ + .reg_bits = 8, \ + .val_bits = 16, \ + .volatile_reg = ad7091r_volatile_reg, \ + .writeable_reg = ad7091r_writeable_reg, \ + .max_register = AD7091R_REG_CH_HYSTERESIS(n), \ +} + +static int ad7091r8_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode) +{ + /* AD7091R-2/-4/-8 don't set sample/command/autocycle mode in conf reg */ + st->mode = mode; + return 0; +} + +static unsigned int ad7091r8_reg_result_chan_id(unsigned int val) +{ + return AD7091R8_REG_RESULT_CH_ID(val); +} + +#define AD7091R_SPI_CHIP_INFO(_n, _name) { \ + .name = _name, \ + .channels = ad7091r##_n##_channels, \ + .num_channels = ARRAY_SIZE(ad7091r##_n##_channels), \ + .vref_mV = 2500, \ + .reg_result_chan_id = &ad7091r8_reg_result_chan_id, \ + .set_mode = &ad7091r8_set_mode, \ +} + +#define AD7091R_SPI_CHIP_INFO_IRQ(_n, _name) { \ + .name = _name, \ + .channels = ad7091r##_n##_channels_irq, \ + .num_channels = ARRAY_SIZE(ad7091r##_n##_channels_irq), \ + .vref_mV = 2500, \ + .reg_result_chan_id = &ad7091r8_reg_result_chan_id, \ + .set_mode = &ad7091r8_set_mode, \ +} + +enum ad7091r8_info_ids { + AD7091R2_INFO, + AD7091R4_INFO, + AD7091R4_INFO_IRQ, + AD7091R8_INFO, + AD7091R8_INFO_IRQ, +}; + +static const struct iio_chan_spec ad7091r2_channels[] = { + AD7091R_CHANNEL(0, 12, NULL, 0), + AD7091R_CHANNEL(1, 12, NULL, 0), +}; + +static const struct iio_chan_spec ad7091r4_channels[] = { + AD7091R_CHANNEL(0, 12, NULL, 0), + AD7091R_CHANNEL(1, 12, NULL, 0), + AD7091R_CHANNEL(2, 12, NULL, 0), + AD7091R_CHANNEL(3, 12, NULL, 0), +}; + +static const struct iio_chan_spec ad7091r4_channels_irq[] = { + AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(2, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(3, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), +}; + +static const struct iio_chan_spec ad7091r8_channels[] = { + AD7091R_CHANNEL(0, 12, NULL, 0), + AD7091R_CHANNEL(1, 12, NULL, 0), + AD7091R_CHANNEL(2, 12, NULL, 0), + AD7091R_CHANNEL(3, 12, NULL, 0), + AD7091R_CHANNEL(4, 12, NULL, 0), + AD7091R_CHANNEL(5, 12, NULL, 0), + AD7091R_CHANNEL(6, 12, NULL, 0), + AD7091R_CHANNEL(7, 12, NULL, 0), +}; + +static const struct iio_chan_spec ad7091r8_channels_irq[] = { + AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(2, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(3, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(4, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(5, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(6, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(7, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), +}; + +static void ad7091r_pulse_convst(struct ad7091r_state *st) +{ + gpiod_set_value_cansleep(st->convst_gpio, 1); + gpiod_set_value_cansleep(st->convst_gpio, 0); +} + +static int ad7091r_regmap_bus_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct ad7091r_state *st = context; + struct spi_device *spi = container_of(st->dev, struct spi_device, dev); + int ret; + + struct spi_transfer t[] = { + { + .tx_buf = &st->tx_buf, + .len = 2, + .cs_change = 1, + }, { + .rx_buf = &st->rx_buf, + .len = 2, + } + }; + + if (reg == AD7091R_REG_RESULT) + ad7091r_pulse_convst(st); + + st->tx_buf = cpu_to_be16(reg << 11); + + ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + *val = be16_to_cpu(st->rx_buf); + return 0; +} + +static int ad7091r_regmap_bus_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct ad7091r_state *st = context; + struct spi_device *spi = container_of(st->dev, struct spi_device, dev); + + /* + * AD7091R-2/-4/-8 protocol (datasheet page 31) is to do a single SPI + * transfer with reg address set in bits B15:B11 and value set in B9:B0. + */ + st->tx_buf = cpu_to_be16(FIELD_PREP(AD7091R8_REG_DATA_MSK, val) | + FIELD_PREP(AD7091R8_RD_WR_FLAG_MSK, 1) | + FIELD_PREP(AD7091R8_REG_ADDR_MSK, reg)); + + return spi_write(spi, &st->tx_buf, 2); +} + +static struct regmap_bus ad7091r8_regmap_bus = { + .reg_read = ad7091r_regmap_bus_reg_read, + .reg_write = ad7091r_regmap_bus_reg_write, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static const struct ad7091r_chip_info ad7091r8_infos[] = { + [AD7091R2_INFO] = AD7091R_SPI_CHIP_INFO(2, "ad7091r-2"), + [AD7091R4_INFO] = AD7091R_SPI_CHIP_INFO(4, "ad7091r-4"), + [AD7091R4_INFO_IRQ] = AD7091R_SPI_CHIP_INFO_IRQ(4, "ad7091r-4"), + [AD7091R8_INFO] = AD7091R_SPI_CHIP_INFO(8, "ad7091r-8"), + [AD7091R8_INFO_IRQ] = AD7091R_SPI_CHIP_INFO_IRQ(8, "ad7091r-8") +}; + +static const struct regmap_config ad7091r2_reg_conf = AD7091R_SPI_REGMAP_CONFIG(2); +static const struct regmap_config ad7091r4_reg_conf = AD7091R_SPI_REGMAP_CONFIG(4); +static const struct regmap_config ad7091r8_reg_conf = AD7091R_SPI_REGMAP_CONFIG(8); + +static void ad7091r8_regmap_init(struct ad7091r_state *st, + const struct regmap_config *regmap_conf) +{ + st->map = devm_regmap_init(st->dev, &ad7091r8_regmap_bus, st, + regmap_conf); +} + +static int ad7091r8_gpio_setup(struct ad7091r_state *st) +{ + st->convst_gpio = devm_gpiod_get(st->dev, "convst", GPIOD_OUT_LOW); + if (IS_ERR(st->convst_gpio)) + return dev_err_probe(st->dev, PTR_ERR(st->convst_gpio), + "Error getting convst GPIO\n"); + + st->reset_gpio = devm_gpiod_get_optional(st->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(st->reset_gpio)) + return dev_err_probe(st->dev, PTR_ERR(st->convst_gpio), + "Error on requesting reset GPIO\n"); + + if (st->reset_gpio) { + fsleep(20); + gpiod_set_value_cansleep(st->reset_gpio, 0); + } + + return 0; +} + +static struct ad7091r_init_info ad7091r2_init_info = { + .info_no_irq = &ad7091r8_infos[AD7091R2_INFO], + .regmap_config = &ad7091r2_reg_conf, + .init_adc_regmap = &ad7091r8_regmap_init, + .setup = &ad7091r8_gpio_setup +}; + +static struct ad7091r_init_info ad7091r4_init_info = { + .info_no_irq = &ad7091r8_infos[AD7091R4_INFO], + .info_irq = &ad7091r8_infos[AD7091R4_INFO_IRQ], + .regmap_config = &ad7091r4_reg_conf, + .init_adc_regmap = &ad7091r8_regmap_init, + .setup = &ad7091r8_gpio_setup +}; + +static struct ad7091r_init_info ad7091r8_init_info = { + .info_no_irq = &ad7091r8_infos[AD7091R8_INFO], + .info_irq = &ad7091r8_infos[AD7091R8_INFO_IRQ], + .regmap_config = &ad7091r8_reg_conf, + .init_adc_regmap = &ad7091r8_regmap_init, + .setup = &ad7091r8_gpio_setup +}; + +static int ad7091r8_spi_probe(struct spi_device *spi) +{ + const struct ad7091r_init_info *init_info; + + init_info = spi_get_device_match_data(spi); + if (!init_info) + return -EINVAL; + + return ad7091r_probe(&spi->dev, init_info, spi->irq); +} + +static const struct of_device_id ad7091r8_of_match[] = { + { .compatible = "adi,ad7091r2", .data = &ad7091r2_init_info }, + { .compatible = "adi,ad7091r4", .data = &ad7091r4_init_info }, + { .compatible = "adi,ad7091r8", .data = &ad7091r8_init_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7091r8_of_match); + +static const struct spi_device_id ad7091r8_spi_id[] = { + { "ad7091r2", (kernel_ulong_t)&ad7091r2_init_info }, + { "ad7091r4", (kernel_ulong_t)&ad7091r4_init_info }, + { "ad7091r8", (kernel_ulong_t)&ad7091r8_init_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7091r8_spi_id); + +static struct spi_driver ad7091r8_driver = { + .driver = { + .name = "ad7091r8", + .of_match_table = ad7091r8_of_match, + }, + .probe = ad7091r8_spi_probe, + .id_table = ad7091r8_spi_id, +}; +module_spi_driver(ad7091r8_driver); + +MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7091R8 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_AD7091R); diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index 39eccc28debe..6581fce4ba95 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -4,8 +4,9 @@ * * Copyright 2012-2020 Analog Devices Inc. */ - +#include <linux/cleanup.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -100,12 +101,6 @@ #define AD9467_DEF_OUTPUT_MODE 0x08 #define AD9467_REG_VREF_MASK 0x0F -enum { - ID_AD9265, - ID_AD9434, - ID_AD9467, -}; - struct ad9467_chip_info { struct adi_axi_adc_chip_info axi_adc_info; unsigned int default_output_mode; @@ -119,9 +114,11 @@ struct ad9467_state { struct spi_device *spi; struct clk *clk; unsigned int output_mode; + unsigned int (*scales)[2]; struct gpio_desc *pwrdown_gpio; - struct gpio_desc *reset_gpio; + /* ensure consistent state obtained on multiple related accesses */ + struct mutex lock; }; static int ad9467_spi_read(struct spi_device *spi, unsigned int reg) @@ -161,11 +158,13 @@ static int ad9467_reg_access(struct adi_axi_adc_conv *conv, unsigned int reg, struct spi_device *spi = st->spi; int ret; - if (readval == NULL) { + if (!readval) { + guard(mutex)(&st->lock); ret = ad9467_spi_write(spi, reg, writeval); - ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER, - AN877_ADC_TRANSFER_SYNC); - return ret; + if (ret) + return ret; + return ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); } ret = ad9467_spi_read(spi, reg); @@ -212,6 +211,7 @@ static void __ad9467_get_scale(struct adi_axi_adc_conv *conv, int index, .channel = _chan, \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ .scan_index = _si, \ .scan_type = { \ .sign = _sign, \ @@ -228,43 +228,46 @@ static const struct iio_chan_spec ad9467_channels[] = { AD9467_CHAN(0, 0, 16, 'S'), }; -static const struct ad9467_chip_info ad9467_chip_tbl[] = { - [ID_AD9265] = { - .axi_adc_info = { - .id = CHIPID_AD9265, - .max_rate = 125000000UL, - .scale_table = ad9265_scale_table, - .num_scales = ARRAY_SIZE(ad9265_scale_table), - .channels = ad9467_channels, - .num_channels = ARRAY_SIZE(ad9467_channels), - }, - .default_output_mode = AD9265_DEF_OUTPUT_MODE, - .vref_mask = AD9265_REG_VREF_MASK, +static const struct ad9467_chip_info ad9467_chip_tbl = { + .axi_adc_info = { + .name = "ad9467", + .id = CHIPID_AD9467, + .max_rate = 250000000UL, + .scale_table = ad9467_scale_table, + .num_scales = ARRAY_SIZE(ad9467_scale_table), + .channels = ad9467_channels, + .num_channels = ARRAY_SIZE(ad9467_channels), }, - [ID_AD9434] = { - .axi_adc_info = { - .id = CHIPID_AD9434, - .max_rate = 500000000UL, - .scale_table = ad9434_scale_table, - .num_scales = ARRAY_SIZE(ad9434_scale_table), - .channels = ad9434_channels, - .num_channels = ARRAY_SIZE(ad9434_channels), - }, - .default_output_mode = AD9434_DEF_OUTPUT_MODE, - .vref_mask = AD9434_REG_VREF_MASK, + .default_output_mode = AD9467_DEF_OUTPUT_MODE, + .vref_mask = AD9467_REG_VREF_MASK, +}; + +static const struct ad9467_chip_info ad9434_chip_tbl = { + .axi_adc_info = { + .name = "ad9434", + .id = CHIPID_AD9434, + .max_rate = 500000000UL, + .scale_table = ad9434_scale_table, + .num_scales = ARRAY_SIZE(ad9434_scale_table), + .channels = ad9434_channels, + .num_channels = ARRAY_SIZE(ad9434_channels), }, - [ID_AD9467] = { - .axi_adc_info = { - .id = CHIPID_AD9467, - .max_rate = 250000000UL, - .scale_table = ad9467_scale_table, - .num_scales = ARRAY_SIZE(ad9467_scale_table), - .channels = ad9467_channels, - .num_channels = ARRAY_SIZE(ad9467_channels), - }, - .default_output_mode = AD9467_DEF_OUTPUT_MODE, - .vref_mask = AD9467_REG_VREF_MASK, + .default_output_mode = AD9434_DEF_OUTPUT_MODE, + .vref_mask = AD9434_REG_VREF_MASK, +}; + +static const struct ad9467_chip_info ad9265_chip_tbl = { + .axi_adc_info = { + .name = "ad9265", + .id = CHIPID_AD9265, + .max_rate = 125000000UL, + .scale_table = ad9265_scale_table, + .num_scales = ARRAY_SIZE(ad9265_scale_table), + .channels = ad9467_channels, + .num_channels = ARRAY_SIZE(ad9467_channels), }, + .default_output_mode = AD9265_DEF_OUTPUT_MODE, + .vref_mask = AD9265_REG_VREF_MASK, }; static int ad9467_get_scale(struct adi_axi_adc_conv *conv, int *val, int *val2) @@ -273,10 +276,13 @@ static int ad9467_get_scale(struct adi_axi_adc_conv *conv, int *val, int *val2) const struct ad9467_chip_info *info1 = to_ad9467_chip_info(info); struct ad9467_state *st = adi_axi_adc_conv_priv(conv); unsigned int i, vref_val; + int ret; - vref_val = ad9467_spi_read(st->spi, AN877_ADC_REG_VREF); + ret = ad9467_spi_read(st->spi, AN877_ADC_REG_VREF); + if (ret < 0) + return ret; - vref_val &= info1->vref_mask; + vref_val = ret & info1->vref_mask; for (i = 0; i < info->num_scales; i++) { if (vref_val == info->scale_table[i][1]) @@ -297,6 +303,7 @@ static int ad9467_set_scale(struct adi_axi_adc_conv *conv, int val, int val2) struct ad9467_state *st = adi_axi_adc_conv_priv(conv); unsigned int scale_val[2]; unsigned int i; + int ret; if (val != 0) return -EINVAL; @@ -306,11 +313,14 @@ static int ad9467_set_scale(struct adi_axi_adc_conv *conv, int val, int val2) if (scale_val[0] != val || scale_val[1] != val2) continue; - ad9467_spi_write(st->spi, AN877_ADC_REG_VREF, - info->scale_table[i][1]); - ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER, - AN877_ADC_TRANSFER_SYNC); - return 0; + guard(mutex)(&st->lock); + ret = ad9467_spi_write(st->spi, AN877_ADC_REG_VREF, + info->scale_table[i][1]); + if (ret < 0) + return ret; + + return ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); } return -EINVAL; @@ -359,6 +369,26 @@ static int ad9467_write_raw(struct adi_axi_adc_conv *conv, } } +static int ad9467_read_avail(struct adi_axi_adc_conv *conv, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + const struct adi_axi_adc_chip_info *info = conv->chip_info; + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)st->scales; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = info->num_scales * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode) { int ret; @@ -371,6 +401,26 @@ static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode) AN877_ADC_TRANSFER_SYNC); } +static int ad9467_scale_fill(struct adi_axi_adc_conv *conv) +{ + const struct adi_axi_adc_chip_info *info = conv->chip_info; + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + unsigned int i, val1, val2; + + st->scales = devm_kmalloc_array(&st->spi->dev, info->num_scales, + sizeof(*st->scales), GFP_KERNEL); + if (!st->scales) + return -ENOMEM; + + for (i = 0; i < info->num_scales; i++) { + __ad9467_get_scale(conv, i, &val1, &val2); + st->scales[i][0] = val1; + st->scales[i][1] = val2; + } + + return 0; +} + static int ad9467_preenable_setup(struct adi_axi_adc_conv *conv) { struct ad9467_state *st = adi_axi_adc_conv_priv(conv); @@ -378,6 +428,21 @@ static int ad9467_preenable_setup(struct adi_axi_adc_conv *conv) return ad9467_outputmode_set(st->spi, st->output_mode); } +static int ad9467_reset(struct device *dev) +{ + struct gpio_desc *gpio; + + gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR_OR_NULL(gpio)) + return PTR_ERR_OR_ZERO(gpio); + + fsleep(1); + gpiod_set_value_cansleep(gpio, 0); + fsleep(10 * USEC_PER_MSEC); + + return 0; +} + static int ad9467_probe(struct spi_device *spi) { const struct ad9467_chip_info *info; @@ -386,9 +451,7 @@ static int ad9467_probe(struct spi_device *spi) unsigned int id; int ret; - info = of_device_get_match_data(&spi->dev); - if (!info) - info = (void *)spi_get_device_id(spi)->driver_data; + info = spi_get_device_match_data(spi); if (!info) return -ENODEV; @@ -408,21 +471,16 @@ static int ad9467_probe(struct spi_device *spi) if (IS_ERR(st->pwrdown_gpio)) return PTR_ERR(st->pwrdown_gpio); - st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", - GPIOD_OUT_LOW); - if (IS_ERR(st->reset_gpio)) - return PTR_ERR(st->reset_gpio); - - if (st->reset_gpio) { - udelay(1); - ret = gpiod_direction_output(st->reset_gpio, 1); - if (ret) - return ret; - mdelay(10); - } + ret = ad9467_reset(&spi->dev); + if (ret) + return ret; conv->chip_info = &info->axi_adc_info; + ret = ad9467_scale_fill(conv); + if (ret) + return ret; + id = ad9467_spi_read(spi, AN877_ADC_REG_CHIP_ID); if (id != conv->chip_info->id) { dev_err(&spi->dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n", @@ -433,6 +491,7 @@ static int ad9467_probe(struct spi_device *spi) conv->reg_access = ad9467_reg_access; conv->write_raw = ad9467_write_raw; conv->read_raw = ad9467_read_raw; + conv->read_avail = ad9467_read_avail; conv->preenable_setup = ad9467_preenable_setup; st->output_mode = info->default_output_mode | @@ -442,17 +501,17 @@ static int ad9467_probe(struct spi_device *spi) } static const struct of_device_id ad9467_of_match[] = { - { .compatible = "adi,ad9265", .data = &ad9467_chip_tbl[ID_AD9265], }, - { .compatible = "adi,ad9434", .data = &ad9467_chip_tbl[ID_AD9434], }, - { .compatible = "adi,ad9467", .data = &ad9467_chip_tbl[ID_AD9467], }, + { .compatible = "adi,ad9265", .data = &ad9265_chip_tbl, }, + { .compatible = "adi,ad9434", .data = &ad9434_chip_tbl, }, + { .compatible = "adi,ad9467", .data = &ad9467_chip_tbl, }, {} }; MODULE_DEVICE_TABLE(of, ad9467_of_match); static const struct spi_device_id ad9467_ids[] = { - { "ad9265", (kernel_ulong_t)&ad9467_chip_tbl[ID_AD9265] }, - { "ad9434", (kernel_ulong_t)&ad9467_chip_tbl[ID_AD9434] }, - { "ad9467", (kernel_ulong_t)&ad9467_chip_tbl[ID_AD9467] }, + { "ad9265", (kernel_ulong_t)&ad9265_chip_tbl }, + { "ad9434", (kernel_ulong_t)&ad9434_chip_tbl }, + { "ad9467", (kernel_ulong_t)&ad9467_chip_tbl }, {} }; MODULE_DEVICE_TABLE(spi, ad9467_ids); diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index aff0532a974a..c247ff1541d2 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -14,6 +14,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/property.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <linux/iio/iio.h> @@ -62,7 +63,7 @@ struct adi_axi_adc_state { struct mutex lock; struct adi_axi_adc_client *client; - void __iomem *regs; + struct regmap *regmap; }; struct adi_axi_adc_client { @@ -90,19 +91,6 @@ void *adi_axi_adc_conv_priv(struct adi_axi_adc_conv *conv) } EXPORT_SYMBOL_NS_GPL(adi_axi_adc_conv_priv, IIO_ADI_AXI); -static void adi_axi_adc_write(struct adi_axi_adc_state *st, - unsigned int reg, - unsigned int val) -{ - iowrite32(val, st->regs + reg); -} - -static unsigned int adi_axi_adc_read(struct adi_axi_adc_state *st, - unsigned int reg) -{ - return ioread32(st->regs + reg); -} - static int adi_axi_adc_config_dma_buffer(struct device *dev, struct iio_dev *indio_dev) { @@ -144,22 +132,39 @@ static int adi_axi_adc_write_raw(struct iio_dev *indio_dev, return conv->write_raw(conv, chan, val, val2, mask); } +static int adi_axi_adc_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct adi_axi_adc_state *st = iio_priv(indio_dev); + struct adi_axi_adc_conv *conv = &st->client->conv; + + if (!conv->read_avail) + return -EOPNOTSUPP; + + return conv->read_avail(conv, chan, vals, type, length, mask); +} + static int adi_axi_adc_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct adi_axi_adc_state *st = iio_priv(indio_dev); struct adi_axi_adc_conv *conv = &st->client->conv; - unsigned int i, ctrl; + unsigned int i; + int ret; for (i = 0; i < conv->chip_info->num_channels; i++) { - ctrl = adi_axi_adc_read(st, ADI_AXI_REG_CHAN_CTRL(i)); - if (test_bit(i, scan_mask)) - ctrl |= ADI_AXI_REG_CHAN_CTRL_ENABLE; + ret = regmap_set_bits(st->regmap, + ADI_AXI_REG_CHAN_CTRL(i), + ADI_AXI_REG_CHAN_CTRL_ENABLE); else - ctrl &= ~ADI_AXI_REG_CHAN_CTRL_ENABLE; - - adi_axi_adc_write(st, ADI_AXI_REG_CHAN_CTRL(i), ctrl); + ret = regmap_clear_bits(st->regmap, + ADI_AXI_REG_CHAN_CTRL(i), + ADI_AXI_REG_CHAN_CTRL_ENABLE); + if (ret) + return ret; } return 0; @@ -228,69 +233,11 @@ struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev, } EXPORT_SYMBOL_NS_GPL(devm_adi_axi_adc_conv_register, IIO_ADI_AXI); -static ssize_t in_voltage_scale_available_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct adi_axi_adc_state *st = iio_priv(indio_dev); - struct adi_axi_adc_conv *conv = &st->client->conv; - size_t len = 0; - int i; - - for (i = 0; i < conv->chip_info->num_scales; i++) { - const unsigned int *s = conv->chip_info->scale_table[i]; - - len += scnprintf(buf + len, PAGE_SIZE - len, - "%u.%06u ", s[0], s[1]); - } - buf[len - 1] = '\n'; - - return len; -} - -static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0); - -enum { - ADI_AXI_ATTR_SCALE_AVAIL, -}; - -#define ADI_AXI_ATTR(_en_, _file_) \ - [ADI_AXI_ATTR_##_en_] = &iio_dev_attr_##_file_.dev_attr.attr - -static struct attribute *adi_axi_adc_attributes[] = { - ADI_AXI_ATTR(SCALE_AVAIL, in_voltage_scale_available), - NULL -}; - -static umode_t axi_adc_attr_is_visible(struct kobject *kobj, - struct attribute *attr, int n) -{ - struct device *dev = kobj_to_dev(kobj); - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct adi_axi_adc_state *st = iio_priv(indio_dev); - struct adi_axi_adc_conv *conv = &st->client->conv; - - switch (n) { - case ADI_AXI_ATTR_SCALE_AVAIL: - if (!conv->chip_info->num_scales) - return 0; - return attr->mode; - default: - return attr->mode; - } -} - -static const struct attribute_group adi_axi_adc_attribute_group = { - .attrs = adi_axi_adc_attributes, - .is_visible = axi_adc_attr_is_visible, -}; - static const struct iio_info adi_axi_adc_info = { .read_raw = &adi_axi_adc_read_raw, .write_raw = &adi_axi_adc_write_raw, - .attrs = &adi_axi_adc_attribute_group, .update_scan_mode = &adi_axi_adc_update_scan_mode, + .read_avail = &adi_axi_adc_read_avail, }; static const struct adi_axi_adc_core_info adi_axi_adc_10_0_a_info = { @@ -354,21 +301,32 @@ static int adi_axi_adc_setup_channels(struct device *dev, } for (i = 0; i < conv->chip_info->num_channels; i++) { - adi_axi_adc_write(st, ADI_AXI_REG_CHAN_CTRL(i), - ADI_AXI_REG_CHAN_CTRL_DEFAULTS); + ret = regmap_write(st->regmap, ADI_AXI_REG_CHAN_CTRL(i), + ADI_AXI_REG_CHAN_CTRL_DEFAULTS); + if (ret) + return ret; } return 0; } -static void axi_adc_reset(struct adi_axi_adc_state *st) +static int axi_adc_reset(struct adi_axi_adc_state *st) { - adi_axi_adc_write(st, ADI_AXI_REG_RSTN, 0); + int ret; + + ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0); + if (ret) + return ret; + mdelay(10); - adi_axi_adc_write(st, ADI_AXI_REG_RSTN, ADI_AXI_REG_RSTN_MMCM_RSTN); + ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN, + ADI_AXI_REG_RSTN_MMCM_RSTN); + if (ret) + return ret; + mdelay(10); - adi_axi_adc_write(st, ADI_AXI_REG_RSTN, - ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN); + return regmap_write(st->regmap, ADI_AXI_REG_RSTN, + ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN); } static void adi_axi_adc_cleanup(void *data) @@ -379,12 +337,20 @@ static void adi_axi_adc_cleanup(void *data) module_put(cl->dev->driver->owner); } +static const struct regmap_config axi_adc_regmap_config = { + .val_bits = 32, + .reg_bits = 32, + .reg_stride = 4, + .max_register = 0x0800, +}; + static int adi_axi_adc_probe(struct platform_device *pdev) { struct adi_axi_adc_conv *conv; struct iio_dev *indio_dev; struct adi_axi_adc_client *cl; struct adi_axi_adc_state *st; + void __iomem *base; unsigned int ver; int ret; @@ -405,15 +371,24 @@ static int adi_axi_adc_probe(struct platform_device *pdev) cl->state = st; mutex_init(&st->lock); - st->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(st->regs)) - return PTR_ERR(st->regs); + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + st->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &axi_adc_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); conv = &st->client->conv; - axi_adc_reset(st); + ret = axi_adc_reset(st); + if (ret) + return ret; - ver = adi_axi_adc_read(st, ADI_AXI_REG_VERSION); + ret = regmap_read(st->regmap, ADI_AXI_REG_VERSION, &ver); + if (ret) + return ret; if (cl->info->version > ver) { dev_err(&pdev->dev, diff --git a/drivers/iio/adc/max34408.c b/drivers/iio/adc/max34408.c new file mode 100644 index 000000000000..6c2ea2bc52c6 --- /dev/null +++ b/drivers/iio/adc/max34408.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IIO driver for Maxim MAX34409/34408 ADC, 4-Channels/2-Channels, 8bits, I2C + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX34408-MAX34409.pdf + * + * TODO: ALERT interrupt, Overcurrent delay, Shutdown delay + */ + +#include <linux/bitfield.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> +#include <linux/regmap.h> + +#include <linux/iio/iio.h> +#include <linux/iio/types.h> + +#define MAX34408_STATUS_REG 0x0 +#define MAX34408_CONTROL_REG 0x1 +#define MAX34408_OCDELAY_REG 0x2 +#define MAX34408_SDDELAY_REG 0x3 + +#define MAX34408_ADC1_REG 0x4 +#define MAX34408_ADC2_REG 0x5 +/* ADC3 & ADC4 always returns 0x0 on 34408 */ +#define MAX34409_ADC3_REG 0x6 +#define MAX34409_ADC4_REG 0x7 + +#define MAX34408_OCT1_REG 0x8 +#define MAX34408_OCT2_REG 0x9 +#define MAX34409_OCT3_REG 0xA +#define MAX34409_OCT4_REG 0xB + +#define MAX34408_DID_REG 0xC +#define MAX34408_DCYY_REG 0xD +#define MAX34408_DCWW_REG 0xE + +/* Bit masks for status register */ +#define MAX34408_STATUS_OC_MSK GENMASK(1, 0) +#define MAX34409_STATUS_OC_MSK GENMASK(3, 0) +#define MAX34408_STATUS_SHTDN BIT(4) +#define MAX34408_STATUS_ENA BIT(5) + +/* Bit masks for control register */ +#define MAX34408_CONTROL_AVG0 BIT(0) +#define MAX34408_CONTROL_AVG1 BIT(1) +#define MAX34408_CONTROL_AVG2 BIT(2) +#define MAX34408_CONTROL_ALERT BIT(3) + +#define MAX34408_DEFAULT_AVG 0x4 + +/* Bit masks for over current delay */ +#define MAX34408_OCDELAY_OCD_MSK GENMASK(6, 0) +#define MAX34408_OCDELAY_RESET BIT(7) + +/* Bit masks for shutdown delay */ +#define MAX34408_SDDELAY_SHD_MSK GENMASK(6, 0) +#define MAX34408_SDDELAY_RESET BIT(7) + +#define MAX34408_DEFAULT_RSENSE 1000 + +/** + * struct max34408_data - max34408/max34409 specific data. + * @regmap: device register map. + * @dev: max34408 device. + * @lock: lock for protecting access to device hardware registers, mostly + * for read modify write cycles for control registers. + * @input_rsense: Rsense values in uOhm, will be overwritten by + * values from channel nodes. + */ +struct max34408_data { + struct regmap *regmap; + struct device *dev; + struct mutex lock; + u32 input_rsense[4]; +}; + +static const struct regmap_config max34408_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX34408_DCWW_REG, +}; + +struct max34408_adc_model_data { + const char *model_name; + const struct iio_chan_spec *channels; + const int num_channels; +}; + +#define MAX34008_CHANNEL(_index, _address) \ + { \ + .type = IIO_CURRENT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .channel = (_index), \ + .address = (_address), \ + .indexed = 1, \ + } + +static const struct iio_chan_spec max34408_channels[] = { + MAX34008_CHANNEL(0, MAX34408_ADC1_REG), + MAX34008_CHANNEL(1, MAX34408_ADC2_REG), +}; + +static const struct iio_chan_spec max34409_channels[] = { + MAX34008_CHANNEL(0, MAX34408_ADC1_REG), + MAX34008_CHANNEL(1, MAX34408_ADC2_REG), + MAX34008_CHANNEL(2, MAX34409_ADC3_REG), + MAX34008_CHANNEL(3, MAX34409_ADC4_REG), +}; + +static int max34408_read_adc_avg(struct max34408_data *max34408, + const struct iio_chan_spec *chan, int *val) +{ + unsigned int ctrl; + int rc; + + guard(mutex)(&max34408->lock); + rc = regmap_read(max34408->regmap, MAX34408_CONTROL_REG, (u32 *)&ctrl); + if (rc) + return rc; + + /* set averaging (0b100) default values*/ + rc = regmap_write(max34408->regmap, MAX34408_CONTROL_REG, + MAX34408_DEFAULT_AVG); + if (rc) { + dev_err(max34408->dev, + "Error (%d) writing control register\n", rc); + return rc; + } + + rc = regmap_read(max34408->regmap, chan->address, val); + if (rc) + return rc; + + /* back to old values */ + rc = regmap_write(max34408->regmap, MAX34408_CONTROL_REG, ctrl); + if (rc) + dev_err(max34408->dev, + "Error (%d) writing control register\n", rc); + + return rc; +} + +static int max34408_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max34408_data *max34408 = iio_priv(indio_dev); + int rc; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + rc = max34408_read_adc_avg(max34408, chan, val); + if (rc) + return rc; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * calcluate current for 8bit ADC with Rsense + * value. + * 10 mV * 1000 / Rsense uOhm = max current + * (max current * adc val * 1000) / (2^8 - 1) mA + */ + *val = 10000 / max34408->input_rsense[chan->channel]; + *val2 = 8; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info max34408_info = { + .read_raw = max34408_read_raw, +}; + +static const struct max34408_adc_model_data max34408_model_data = { + .model_name = "max34408", + .channels = max34408_channels, + .num_channels = 2, +}; + +static const struct max34408_adc_model_data max34409_model_data = { + .model_name = "max34409", + .channels = max34409_channels, + .num_channels = 4, +}; + +static int max34408_probe(struct i2c_client *client) +{ + const struct max34408_adc_model_data *model_data; + struct device *dev = &client->dev; + struct max34408_data *max34408; + struct fwnode_handle *node; + struct iio_dev *indio_dev; + struct regmap *regmap; + int rc, i = 0; + + model_data = i2c_get_match_data(client); + if (!model_data) + return -EINVAL; + + regmap = devm_regmap_init_i2c(client, &max34408_regmap_config); + if (IS_ERR(regmap)) { + dev_err_probe(dev, PTR_ERR(regmap), + "regmap_init failed\n"); + return PTR_ERR(regmap); + } + + indio_dev = devm_iio_device_alloc(dev, sizeof(*max34408)); + if (!indio_dev) + return -ENOMEM; + + max34408 = iio_priv(indio_dev); + max34408->regmap = regmap; + max34408->dev = dev; + mutex_init(&max34408->lock); + + device_for_each_child_node(dev, node) { + fwnode_property_read_u32(node, "maxim,rsense-val-micro-ohms", + &max34408->input_rsense[i]); + i++; + } + + /* disable ALERT and averaging */ + rc = regmap_write(max34408->regmap, MAX34408_CONTROL_REG, 0x0); + if (rc) + return rc; + + indio_dev->channels = model_data->channels; + indio_dev->num_channels = model_data->num_channels; + indio_dev->name = model_data->model_name; + + indio_dev->info = &max34408_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id max34408_of_match[] = { + { + .compatible = "maxim,max34408", + .data = &max34408_model_data, + }, + { + .compatible = "maxim,max34409", + .data = &max34409_model_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, max34408_of_match); + +static const struct i2c_device_id max34408_id[] = { + { "max34408", (kernel_ulong_t)&max34408_model_data }, + { "max34409", (kernel_ulong_t)&max34409_model_data }, + {} +}; +MODULE_DEVICE_TABLE(i2c, max34408_id); + +static struct i2c_driver max34408_driver = { + .driver = { + .name = "max34408", + .of_match_table = max34408_of_match, + }, + .probe = max34408_probe, + .id_table = max34408_id, +}; +module_i2c_driver(max34408_driver); + +MODULE_AUTHOR("Ivan Mikhaylov <fr0st61te@gmail.com>"); +MODULE_DESCRIPTION("Maxim MAX34408/34409 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index d864558bc087..7a32e7a1be9d 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -7,6 +7,7 @@ */ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> @@ -316,47 +317,37 @@ static int mcp3911_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct mcp3911 *adc = iio_priv(indio_dev); - int ret = -EINVAL; + int ret; - mutex_lock(&adc->lock); + guard(mutex)(&adc->lock); switch (mask) { case IIO_CHAN_INFO_RAW: ret = mcp3911_read(adc, MCP3911_CHANNEL(channel->channel), val, 3); if (ret) - goto out; + return ret; *val = sign_extend32(*val, 23); - - ret = IIO_VAL_INT; - break; - + return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: - ret = adc->chip->get_offset(adc, channel->channel, val); if (ret) - goto out; + return ret; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: ret = adc->chip->get_osr(adc, val); if (ret) - goto out; - - ret = IIO_VAL_INT; - break; + return ret; + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = mcp3911_scale_table[ilog2(adc->gain[channel->channel])][0]; *val2 = mcp3911_scale_table[ilog2(adc->gain[channel->channel])][1]; - ret = IIO_VAL_INT_PLUS_NANO; - break; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; } - -out: - mutex_unlock(&adc->lock); - return ret; } static int mcp3911_write_raw(struct iio_dev *indio_dev, @@ -364,9 +355,8 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev, int val2, long mask) { struct mcp3911 *adc = iio_priv(indio_dev); - int ret = -EINVAL; - mutex_lock(&adc->lock); + guard(mutex)(&adc->lock); switch (mask) { case IIO_CHAN_INFO_SCALE: for (int i = 0; i < MCP3911_NUM_SCALES; i++) { @@ -374,32 +364,25 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev, val2 == mcp3911_scale_table[i][1]) { adc->gain[channel->channel] = BIT(i); - ret = adc->chip->set_scale(adc, channel->channel, i); + return adc->chip->set_scale(adc, channel->channel, i); } } - break; + return -EINVAL; case IIO_CHAN_INFO_OFFSET: - if (val2 != 0) { - ret = -EINVAL; - goto out; - } - - ret = adc->chip->set_offset(adc, channel->channel, val); - break; + if (val2 != 0) + return -EINVAL; + return adc->chip->set_offset(adc, channel->channel, val); case IIO_CHAN_INFO_OVERSAMPLING_RATIO: for (int i = 0; i < ARRAY_SIZE(mcp3911_osr_table); i++) { if (val == mcp3911_osr_table[i]) { - ret = adc->chip->set_osr(adc, i); - break; + return adc->chip->set_osr(adc, i); } } - break; + return -EINVAL; + default: + return -EINVAL; } - -out: - mutex_unlock(&adc->lock); - return ret; } static int mcp3911_calc_scale_table(struct mcp3911 *adc) @@ -532,7 +515,7 @@ static irqreturn_t mcp3911_trigger_handler(int irq, void *p) int i = 0; int ret; - mutex_lock(&adc->lock); + guard(mutex)(&adc->lock); adc->tx_buf = MCP3911_REG_READ(MCP3911_CHANNEL(0), adc->dev_addr); ret = spi_sync_transfer(adc->spi, xfer, ARRAY_SIZE(xfer)); if (ret < 0) { @@ -549,7 +532,6 @@ static irqreturn_t mcp3911_trigger_handler(int irq, void *p) iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan, iio_get_time_ns(indio_dev)); out: - mutex_unlock(&adc->lock); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c index e87d35d50a95..ed4d72922696 100644 --- a/drivers/iio/amplifiers/hmc425a.c +++ b/drivers/iio/amplifiers/hmc425a.c @@ -5,6 +5,7 @@ * Copyright 2020 Analog Devices Inc. */ +#include <linux/bitops.h> #include <linux/device.h> #include <linux/err.h> #include <linux/gpio/consumer.h> @@ -22,6 +23,7 @@ enum hmc425a_type { ID_HMC425A, ID_HMC540S, + ID_ADRF5740 }; struct hmc425a_chip_info { @@ -74,6 +76,10 @@ static int hmc425a_read_raw(struct iio_dev *indio_dev, case ID_HMC540S: gain = ~code * -1000; break; + case ID_ADRF5740: + code = code & BIT(3) ? code & ~BIT(2) : code; + gain = code * -2000; + break; } *val = gain / 1000; @@ -113,6 +119,10 @@ static int hmc425a_write_raw(struct iio_dev *indio_dev, case ID_HMC540S: code = ~((abs(gain) / 1000) & 0xF); break; + case ID_ADRF5740: + code = (abs(gain) / 2000) & 0xF; + code = code & BIT(3) ? code | BIT(2) : code; + break; } mutex_lock(&st->lock); @@ -165,6 +175,7 @@ static const struct iio_chan_spec hmc425a_channels[] = { static const struct of_device_id hmc425a_of_match[] = { { .compatible = "adi,hmc425a", .data = (void *)ID_HMC425A }, { .compatible = "adi,hmc540s", .data = (void *)ID_HMC540S }, + { .compatible = "adi,adrf5740", .data = (void *)ID_ADRF5740 }, {}, }; MODULE_DEVICE_TABLE(of, hmc425a_of_match); @@ -188,6 +199,15 @@ static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = { .gain_max = 0, .default_gain = -0x10, /* set default gain -15.0db*/ }, + [ID_ADRF5740] = { + .name = "adrf5740", + .channels = hmc425a_channels, + .num_channels = ARRAY_SIZE(hmc425a_channels), + .num_gpios = 4, + .gain_min = -22000, + .gain_max = 0, + .default_gain = 0xF, /* set default gain -22.0db*/ + }, }; static int hmc425a_probe(struct platform_device *pdev) @@ -229,6 +249,9 @@ static int hmc425a_probe(struct platform_device *pdev) indio_dev->info = &hmc425a_info; indio_dev->modes = INDIO_DIRECT_MODE; + /* Set default gain */ + hmc425a_write(indio_dev, st->gain); + return devm_iio_device_register(&pdev->dev, indio_dev); } diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index d348af8b9705..5610ba67925e 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -179,7 +179,7 @@ static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block( } block->size = size; - block->state = IIO_BLOCK_STATE_DEQUEUED; + block->state = IIO_BLOCK_STATE_DONE; block->queue = queue; INIT_LIST_HEAD(&block->head); kref_init(&block->kref); @@ -191,16 +191,8 @@ static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block( static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block) { - struct iio_dma_buffer_queue *queue = block->queue; - - /* - * The buffer has already been freed by the application, just drop the - * reference. - */ - if (block->state != IIO_BLOCK_STATE_DEAD) { + if (block->state != IIO_BLOCK_STATE_DEAD) block->state = IIO_BLOCK_STATE_DONE; - list_add_tail(&block->head, &queue->outgoing); - } } /** @@ -261,7 +253,6 @@ static bool iio_dma_block_reusable(struct iio_dma_buffer_block *block) * not support abort and has not given back the block yet. */ switch (block->state) { - case IIO_BLOCK_STATE_DEQUEUED: case IIO_BLOCK_STATE_QUEUED: case IIO_BLOCK_STATE_DONE: return true; @@ -317,7 +308,6 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer) * dead. This means we can reset the lists without having to fear * corrution. */ - INIT_LIST_HEAD(&queue->outgoing); spin_unlock_irq(&queue->list_lock); INIT_LIST_HEAD(&queue->incoming); @@ -356,6 +346,29 @@ out_unlock: } EXPORT_SYMBOL_GPL(iio_dma_buffer_request_update); +static void iio_dma_buffer_fileio_free(struct iio_dma_buffer_queue *queue) +{ + unsigned int i; + + spin_lock_irq(&queue->list_lock); + for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { + if (!queue->fileio.blocks[i]) + continue; + queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD; + } + spin_unlock_irq(&queue->list_lock); + + INIT_LIST_HEAD(&queue->incoming); + + for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { + if (!queue->fileio.blocks[i]) + continue; + iio_buffer_block_put(queue->fileio.blocks[i]); + queue->fileio.blocks[i] = NULL; + } + queue->fileio.active_block = NULL; +} + static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue, struct iio_dma_buffer_block *block) { @@ -456,14 +469,20 @@ static struct iio_dma_buffer_block *iio_dma_buffer_dequeue( struct iio_dma_buffer_queue *queue) { struct iio_dma_buffer_block *block; + unsigned int idx; spin_lock_irq(&queue->list_lock); - block = list_first_entry_or_null(&queue->outgoing, struct - iio_dma_buffer_block, head); - if (block != NULL) { - list_del(&block->head); - block->state = IIO_BLOCK_STATE_DEQUEUED; + + idx = queue->fileio.next_dequeue; + block = queue->fileio.blocks[idx]; + + if (block->state == IIO_BLOCK_STATE_DONE) { + idx = (idx + 1) % ARRAY_SIZE(queue->fileio.blocks); + queue->fileio.next_dequeue = idx; + } else { + block = NULL; } + spin_unlock_irq(&queue->list_lock); return block; @@ -539,6 +558,7 @@ size_t iio_dma_buffer_data_available(struct iio_buffer *buf) struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buf); struct iio_dma_buffer_block *block; size_t data_available = 0; + unsigned int i; /* * For counting the available bytes we'll use the size of the block not @@ -552,8 +572,15 @@ size_t iio_dma_buffer_data_available(struct iio_buffer *buf) data_available += queue->fileio.active_block->size; spin_lock_irq(&queue->list_lock); - list_for_each_entry(block, &queue->outgoing, head) - data_available += block->size; + + for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { + block = queue->fileio.blocks[i]; + + if (block != queue->fileio.active_block + && block->state == IIO_BLOCK_STATE_DONE) + data_available += block->size; + } + spin_unlock_irq(&queue->list_lock); mutex_unlock(&queue->lock); @@ -617,7 +644,6 @@ int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, queue->ops = ops; INIT_LIST_HEAD(&queue->incoming); - INIT_LIST_HEAD(&queue->outgoing); mutex_init(&queue->lock); spin_lock_init(&queue->list_lock); @@ -635,28 +661,9 @@ EXPORT_SYMBOL_GPL(iio_dma_buffer_init); */ void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue) { - unsigned int i; - mutex_lock(&queue->lock); - spin_lock_irq(&queue->list_lock); - for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { - if (!queue->fileio.blocks[i]) - continue; - queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD; - } - INIT_LIST_HEAD(&queue->outgoing); - spin_unlock_irq(&queue->list_lock); - - INIT_LIST_HEAD(&queue->incoming); - - for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { - if (!queue->fileio.blocks[i]) - continue; - iio_buffer_block_put(queue->fileio.blocks[i]); - queue->fileio.blocks[i] = NULL; - } - queue->fileio.active_block = NULL; + iio_dma_buffer_fileio_free(queue); queue->ops = NULL; mutex_unlock(&queue->lock); diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index c30657e10ee1..02649ab81b3c 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -5,6 +5,17 @@ menu "Chemical Sensors" +config AOSONG_AGS02MA + tristate "Aosong AGS02MA TVOC sensor driver" + depends on I2C + select CRC8 + help + Say Y here to build support for Aosong AGS02MA TVOC (Total Volatile + Organic Compounds) sensor. + + To compile this driver as module, choose M here: the module will be + called ags02ma. + config ATLAS_PH_SENSOR tristate "Atlas Scientific OEM SM sensors" depends on I2C diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile index a11e777a7a00..2f3dee8bb779 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -4,6 +4,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AOSONG_AGS02MA) += ags02ma.o obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-sensor.o obj-$(CONFIG_ATLAS_EZO_SENSOR) += atlas-ezo-sensor.o obj-$(CONFIG_BME680) += bme680_core.o diff --git a/drivers/iio/chemical/ags02ma.c b/drivers/iio/chemical/ags02ma.c new file mode 100644 index 000000000000..8fcd80946543 --- /dev/null +++ b/drivers/iio/chemical/ags02ma.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com> + * + * Driver for Aosong AGS02MA + * + * Datasheet: + * https://asairsensors.com/wp-content/uploads/2021/09/AGS02MA.pdf + * Product Page: + * http://www.aosong.com/m/en/products-33.html + */ + +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> + +#define AGS02MA_TVOC_READ_REG 0x00 +#define AGS02MA_VERSION_REG 0x11 + +#define AGS02MA_VERSION_PROCESSING_DELAY 30 +#define AGS02MA_TVOC_READ_PROCESSING_DELAY 1500 + +#define AGS02MA_CRC8_INIT 0xff +#define AGS02MA_CRC8_POLYNOMIAL 0x31 + +DECLARE_CRC8_TABLE(ags02ma_crc8_table); + +struct ags02ma_data { + struct i2c_client *client; +}; + +struct ags02ma_reading { + __be32 data; + u8 crc; +} __packed; + +static int ags02ma_register_read(struct i2c_client *client, u8 reg, u16 delay, + u32 *val) +{ + int ret; + u8 crc; + struct ags02ma_reading read_buffer; + + ret = i2c_master_send(client, ®, sizeof(reg)); + if (ret < 0) { + dev_err(&client->dev, + "Failed to send data to register 0x%x: %d", reg, ret); + return ret; + } + + /* Processing Delay, Check Table 7.7 in the datasheet */ + msleep_interruptible(delay); + + ret = i2c_master_recv(client, (u8 *)&read_buffer, sizeof(read_buffer)); + if (ret < 0) { + dev_err(&client->dev, + "Failed to receive from register 0x%x: %d", reg, ret); + return ret; + } + + crc = crc8(ags02ma_crc8_table, (u8 *)&read_buffer.data, + sizeof(read_buffer.data), AGS02MA_CRC8_INIT); + if (crc != read_buffer.crc) { + dev_err(&client->dev, "CRC error\n"); + return -EIO; + } + + *val = be32_to_cpu(read_buffer.data); + return 0; +} + +static int ags02ma_read_raw(struct iio_dev *iio_device, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + struct ags02ma_data *data = iio_priv(iio_device); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = ags02ma_register_read(data->client, AGS02MA_TVOC_READ_REG, + AGS02MA_TVOC_READ_PROCESSING_DELAY, + val); + if (ret < 0) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* The sensor reads data as ppb */ + *val = 0; + *val2 = 100; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static const struct iio_info ags02ma_info = { + .read_raw = ags02ma_read_raw, +}; + +static const struct iio_chan_spec ags02ma_channel = { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_VOC, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), +}; + +static int ags02ma_probe(struct i2c_client *client) +{ + int ret; + struct ags02ma_data *data; + struct iio_dev *indio_dev; + u32 version; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + crc8_populate_msb(ags02ma_crc8_table, AGS02MA_CRC8_POLYNOMIAL); + + ret = ags02ma_register_read(client, AGS02MA_VERSION_REG, + AGS02MA_VERSION_PROCESSING_DELAY, &version); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read device version\n"); + dev_dbg(&client->dev, "Aosong AGS02MA, Version: 0x%x", version); + + data = iio_priv(indio_dev); + data->client = client; + indio_dev->info = &ags02ma_info; + indio_dev->channels = &ags02ma_channel; + indio_dev->num_channels = 1; + indio_dev->name = "ags02ma"; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id ags02ma_id_table[] = { + { "ags02ma" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, ags02ma_id_table); + +static const struct of_device_id ags02ma_of_table[] = { + { .compatible = "aosong,ags02ma" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ags02ma_of_table); + +static struct i2c_driver ags02ma_driver = { + .driver = { + .name = "ags02ma", + .of_match_table = ags02ma_of_table, + }, + .id_table = ags02ma_id_table, + .probe = ags02ma_probe, +}; +module_i2c_driver(ags02ma_driver); + +MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>"); +MODULE_DESCRIPTION("Aosong AGS02MA TVOC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/chemical/pms7003.c b/drivers/iio/chemical/pms7003.c index e9857d93b307..b5cf15a515d2 100644 --- a/drivers/iio/chemical/pms7003.c +++ b/drivers/iio/chemical/pms7003.c @@ -211,13 +211,13 @@ static bool pms7003_frame_is_okay(struct pms7003_frame *frame) return checksum == pms7003_calc_checksum(frame); } -static int pms7003_receive_buf(struct serdev_device *serdev, - const unsigned char *buf, size_t size) +static ssize_t pms7003_receive_buf(struct serdev_device *serdev, const u8 *buf, + size_t size) { struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev); struct pms7003_state *state = iio_priv(indio_dev); struct pms7003_frame *frame = &state->frame; - int num; + size_t num; if (!frame->expected_length) { u16 magic; diff --git a/drivers/iio/chemical/scd30_serial.c b/drivers/iio/chemical/scd30_serial.c index 3c519103d30b..a47654591e55 100644 --- a/drivers/iio/chemical/scd30_serial.c +++ b/drivers/iio/chemical/scd30_serial.c @@ -174,13 +174,13 @@ static int scd30_serdev_command(struct scd30_state *state, enum scd30_cmd cmd, u return 0; } -static int scd30_serdev_receive_buf(struct serdev_device *serdev, - const unsigned char *buf, size_t size) +static ssize_t scd30_serdev_receive_buf(struct serdev_device *serdev, + const u8 *buf, size_t size) { struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev); struct scd30_serdev_priv *priv; struct scd30_state *state; - int num; + size_t num; if (!indio_dev) return 0; diff --git a/drivers/iio/chemical/sps30_serial.c b/drivers/iio/chemical/sps30_serial.c index 164f4b3e025c..3afa89f8acc3 100644 --- a/drivers/iio/chemical/sps30_serial.c +++ b/drivers/iio/chemical/sps30_serial.c @@ -74,8 +74,8 @@ static int sps30_serial_xfer(struct sps30_state *state, const unsigned char *buf } static const struct { - unsigned char byte; - unsigned char byte2; + u8 byte; + u8 byte2; } sps30_serial_bytes[] = { { 0x11, 0x31 }, { 0x13, 0x33 }, @@ -83,7 +83,7 @@ static const struct { { 0x7d, 0x5d }, }; -static int sps30_serial_put_byte(unsigned char *buf, unsigned char byte) +static int sps30_serial_put_byte(u8 *buf, u8 byte) { int i; @@ -102,7 +102,7 @@ static int sps30_serial_put_byte(unsigned char *buf, unsigned char byte) return 1; } -static char sps30_serial_get_byte(bool escaped, unsigned char byte2) +static u8 sps30_serial_get_byte(bool escaped, u8 byte2) { int i; @@ -130,8 +130,8 @@ static unsigned char sps30_serial_calc_chksum(const unsigned char *buf, size_t n return ~chksum; } -static int sps30_serial_prep_frame(unsigned char *buf, unsigned char cmd, - const unsigned char *arg, size_t arg_size) +static int sps30_serial_prep_frame(u8 *buf, u8 cmd, const u8 *arg, + size_t arg_size) { unsigned char chksum; int num = 0; @@ -210,14 +210,14 @@ static int sps30_serial_command(struct sps30_state *state, unsigned char cmd, return rsp_size; } -static int sps30_serial_receive_buf(struct serdev_device *serdev, - const unsigned char *buf, size_t size) +static ssize_t sps30_serial_receive_buf(struct serdev_device *serdev, + const u8 *buf, size_t size) { struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev); struct sps30_serial_priv *priv; struct sps30_state *state; - unsigned char byte; size_t i; + u8 byte; if (!indio_dev) return 0; diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 93b8be183de6..34eb40bb9529 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -400,6 +400,16 @@ config MCP4728 To compile this driver as a module, choose M here: the module will be called mcp4728. +config MCP4821 + tristate "MCP4801/02/11/12/21/22 DAC driver" + depends on SPI + help + Say yes here to build the driver for the Microchip MCP4801 + MCP4802, MCP4811, MCP4812, MCP4821 and MCP4822 DAC devices. + + To compile this driver as a module, choose M here: the module + will be called mcp4821. + config MCP4922 tristate "MCP4902, MCP4912, MCP4922 DAC driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 5b2bac900d5a..55bf89739d14 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_MAX5522) += max5522.o obj-$(CONFIG_MAX5821) += max5821.o obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4728) += mcp4728.o +obj-$(CONFIG_MCP4821) += mcp4821.o obj-$(CONFIG_MCP4922) += mcp4922.o obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o obj-$(CONFIG_STM32_DAC) += stm32-dac.o diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index a4167454da81..75b549827e15 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -345,6 +345,7 @@ static int ad5791_probe(struct spi_device *spi) struct iio_dev *indio_dev; struct ad5791_state *st; int ret, pos_voltage_uv = 0, neg_voltage_uv = 0; + bool use_rbuf_gain2; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (!indio_dev) @@ -379,6 +380,12 @@ static int ad5791_probe(struct spi_device *spi) st->pwr_down = true; st->spi = spi; + if (pdata) + use_rbuf_gain2 = pdata->use_rbuf_gain2; + else + use_rbuf_gain2 = device_property_read_bool(&spi->dev, + "adi,rbuf-gain2-en"); + if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) { st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000; st->vref_neg_mv = neg_voltage_uv / 1000; @@ -398,7 +405,7 @@ static int ad5791_probe(struct spi_device *spi) st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv)) - | ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) | + | (use_rbuf_gain2 ? 0 : AD5791_CTRL_RBUF) | AD5791_CTRL_BIN2SC; ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl | diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c new file mode 100644 index 000000000000..8a0480d33845 --- /dev/null +++ b/drivers/iio/dac/mcp4821.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com> + * + * Driver for Microchip MCP4801, MCP4802, MCP4811, MCP4812, MCP4821 and MCP4822 + * + * Based on the work of: + * Michael Welling (MCP4922 Driver) + * + * Datasheet: + * MCP48x1: https://ww1.microchip.com/downloads/en/DeviceDoc/22244B.pdf + * MCP48x2: https://ww1.microchip.com/downloads/en/DeviceDoc/20002249B.pdf + * + * TODO: + * - Configurable gain + * - Regulator control + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/spi/spi.h> + +#include <linux/iio/iio.h> +#include <linux/iio/types.h> + +#include <asm/unaligned.h> + +#define MCP4821_ACTIVE_MODE BIT(12) +#define MCP4802_SECOND_CHAN BIT(15) + +/* DAC uses an internal Voltage reference of 4.096V at a gain of 2x */ +#define MCP4821_2X_GAIN_VREF_MV 4096 + +enum mcp4821_supported_drvice_ids { + ID_MCP4801, + ID_MCP4802, + ID_MCP4811, + ID_MCP4812, + ID_MCP4821, + ID_MCP4822, +}; + +struct mcp4821_state { + struct spi_device *spi; + u16 dac_value[2]; +}; + +struct mcp4821_chip_info { + const char *name; + int num_channels; + const struct iio_chan_spec channels[2]; +}; + +#define MCP4821_CHAN(channel_id, resolution) \ + { \ + .type = IIO_VOLTAGE, .output = 1, .indexed = 1, \ + .channel = (channel_id), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .realbits = (resolution), \ + .shift = 12 - (resolution), \ + }, \ + } + +static const struct mcp4821_chip_info mcp4821_chip_info_table[6] = { + [ID_MCP4801] = { + .name = "mcp4801", + .num_channels = 1, + .channels = { + MCP4821_CHAN(0, 8), + }, + }, + [ID_MCP4802] = { + .name = "mcp4802", + .num_channels = 2, + .channels = { + MCP4821_CHAN(0, 8), + MCP4821_CHAN(1, 8), + }, + }, + [ID_MCP4811] = { + .name = "mcp4811", + .num_channels = 1, + .channels = { + MCP4821_CHAN(0, 10), + }, + }, + [ID_MCP4812] = { + .name = "mcp4812", + .num_channels = 2, + .channels = { + MCP4821_CHAN(0, 10), + MCP4821_CHAN(1, 10), + }, + }, + [ID_MCP4821] = { + .name = "mcp4821", + .num_channels = 1, + .channels = { + MCP4821_CHAN(0, 12), + }, + }, + [ID_MCP4822] = { + .name = "mcp4822", + .num_channels = 2, + .channels = { + MCP4821_CHAN(0, 12), + MCP4821_CHAN(1, 12), + }, + }, +}; + +static int mcp4821_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct mcp4821_state *state; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + state = iio_priv(indio_dev); + *val = state->dac_value[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = MCP4821_2X_GAIN_VREF_MV; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int mcp4821_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct mcp4821_state *state = iio_priv(indio_dev); + u16 write_val; + __be16 write_buffer; + int ret; + + if (val2 != 0) + return -EINVAL; + + if (val < 0 || val >= BIT(chan->scan_type.realbits)) + return -EINVAL; + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + write_val = MCP4821_ACTIVE_MODE | val << chan->scan_type.shift; + if (chan->channel) + write_val |= MCP4802_SECOND_CHAN; + + write_buffer = cpu_to_be16(write_val); + ret = spi_write(state->spi, &write_buffer, sizeof(write_buffer)); + if (ret) { + dev_err(&state->spi->dev, "Failed to write to device: %d", ret); + return ret; + } + + state->dac_value[chan->channel] = val; + + return 0; +} + +static const struct iio_info mcp4821_info = { + .read_raw = &mcp4821_read_raw, + .write_raw = &mcp4821_write_raw, +}; + +static int mcp4821_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct mcp4821_state *state; + const struct mcp4821_chip_info *info; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); + if (indio_dev == NULL) + return -ENOMEM; + + state = iio_priv(indio_dev); + state->spi = spi; + + info = spi_get_device_match_data(spi); + indio_dev->name = info->name; + indio_dev->info = &mcp4821_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = info->channels; + indio_dev->num_channels = info->num_channels; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +#define MCP4821_COMPATIBLE(of_compatible, id) \ + { \ + .compatible = of_compatible, \ + .data = &mcp4821_chip_info_table[id] \ + } + +static const struct of_device_id mcp4821_of_table[] = { + MCP4821_COMPATIBLE("microchip,mcp4801", ID_MCP4801), + MCP4821_COMPATIBLE("microchip,mcp4802", ID_MCP4802), + MCP4821_COMPATIBLE("microchip,mcp4811", ID_MCP4811), + MCP4821_COMPATIBLE("microchip,mcp4812", ID_MCP4812), + MCP4821_COMPATIBLE("microchip,mcp4821", ID_MCP4821), + MCP4821_COMPATIBLE("microchip,mcp4822", ID_MCP4822), + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mcp4821_of_table); + +static const struct spi_device_id mcp4821_id_table[] = { + { "mcp4801", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4801]}, + { "mcp4802", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4802]}, + { "mcp4811", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4811]}, + { "mcp4812", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4812]}, + { "mcp4821", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4821]}, + { "mcp4822", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4822]}, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, mcp4821_id_table); + +static struct spi_driver mcp4821_driver = { + .driver = { + .name = "mcp4821", + .of_match_table = mcp4821_of_table, + }, + .probe = mcp4821_probe, + .id_table = mcp4821_id_table, +}; +module_spi_driver(mcp4821_driver); + +MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>"); +MODULE_DESCRIPTION("Microchip MCP4821 DAC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c index 26abecbd51e0..9284c13f1abb 100644 --- a/drivers/iio/frequency/adf4377.c +++ b/drivers/iio/frequency/adf4377.c @@ -870,7 +870,6 @@ static const struct iio_chan_spec adf4377_channels[] = { static int adf4377_properties_parse(struct adf4377_state *st) { struct spi_device *spi = st->spi; - const char *str; int ret; st->clkin = devm_clk_get_enabled(&spi->dev, "ref_in"); @@ -896,16 +895,13 @@ static int adf4377_properties_parse(struct adf4377_state *st) return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_enclk2), "failed to get the CE GPIO\n"); - ret = device_property_read_string(&spi->dev, "adi,muxout-select", &str); - if (ret) { - st->muxout_select = ADF4377_MUXOUT_HIGH_Z; - } else { - ret = match_string(adf4377_muxout_modes, ARRAY_SIZE(adf4377_muxout_modes), str); - if (ret < 0) - return ret; - + ret = device_property_match_property_string(&spi->dev, "adi,muxout-select", + adf4377_muxout_modes, + ARRAY_SIZE(adf4377_muxout_modes)); + if (ret >= 0) st->muxout_select = ret; - } + else + st->muxout_select = ADF4377_MUXOUT_HIGH_Z; return 0; } diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c index bb5e1feef42b..b46b73b89eb7 100644 --- a/drivers/iio/frequency/admv1014.c +++ b/drivers/iio/frequency/admv1014.c @@ -710,7 +710,6 @@ static int admv1014_init(struct admv1014_state *st) static int admv1014_properties_parse(struct admv1014_state *st) { - const char *str; unsigned int i; struct spi_device *spi = st->spi; int ret; @@ -719,27 +718,21 @@ static int admv1014_properties_parse(struct admv1014_state *st) st->p1db_comp = device_property_read_bool(&spi->dev, "adi,p1db-compensation-enable"); - ret = device_property_read_string(&spi->dev, "adi,input-mode", &str); - if (ret) { - st->input_mode = ADMV1014_IQ_MODE; - } else { - ret = match_string(input_mode_names, ARRAY_SIZE(input_mode_names), str); - if (ret < 0) - return ret; - + ret = device_property_match_property_string(&spi->dev, "adi,input-mode", + input_mode_names, + ARRAY_SIZE(input_mode_names)); + if (ret >= 0) st->input_mode = ret; - } - - ret = device_property_read_string(&spi->dev, "adi,quad-se-mode", &str); - if (ret) { - st->quad_se_mode = ADMV1014_SE_MODE_POS; - } else { - ret = match_string(quad_se_mode_names, ARRAY_SIZE(quad_se_mode_names), str); - if (ret < 0) - return ret; + else + st->input_mode = ADMV1014_IQ_MODE; + ret = device_property_match_property_string(&spi->dev, "adi,quad-se-mode", + quad_se_mode_names, + ARRAY_SIZE(quad_se_mode_names)); + if (ret >= 0) st->quad_se_mode = ADMV1014_SE_MODE_POS + (ret * 3); - } + else + st->quad_se_mode = ADMV1014_SE_MODE_POS; for (i = 0; i < ADMV1014_NUM_REGULATORS; ++i) st->regulators[i].supply = admv1014_reg_name[i]; diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c new file mode 100644 index 000000000000..4e3311170725 --- /dev/null +++ b/drivers/iio/humidity/hdc3020.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * hdc3020.c - Support for the TI HDC3020,HDC3021 and HDC3022 + * temperature + relative humidity sensors + * + * Copyright (C) 2023 + * + * Datasheet: https://www.ti.com/lit/ds/symlink/hdc3020.pdf + */ + +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mutex.h> + +#include <asm/unaligned.h> + +#include <linux/iio/iio.h> + +#define HDC3020_HEATER_CMD_MSB 0x30 /* shared by all heater commands */ +#define HDC3020_HEATER_ENABLE 0x6D +#define HDC3020_HEATER_DISABLE 0x66 +#define HDC3020_HEATER_CONFIG 0x6E + +#define HDC3020_READ_RETRY_TIMES 10 +#define HDC3020_BUSY_DELAY_MS 10 + +#define HDC3020_CRC8_POLYNOMIAL 0x31 + +static const u8 HDC3020_S_AUTO_10HZ_MOD0[2] = { 0x27, 0x37 }; + +static const u8 HDC3020_EXIT_AUTO[2] = { 0x30, 0x93 }; + +static const u8 HDC3020_R_T_RH_AUTO[2] = { 0xE0, 0x00 }; +static const u8 HDC3020_R_T_LOW_AUTO[2] = { 0xE0, 0x02 }; +static const u8 HDC3020_R_T_HIGH_AUTO[2] = { 0xE0, 0x03 }; +static const u8 HDC3020_R_RH_LOW_AUTO[2] = { 0xE0, 0x04 }; +static const u8 HDC3020_R_RH_HIGH_AUTO[2] = { 0xE0, 0x05 }; + +struct hdc3020_data { + struct i2c_client *client; + /* + * Ensure that the sensor configuration (currently only heater is + * supported) will not be changed during the process of reading + * sensor data (this driver will try HDC3020_READ_RETRY_TIMES times + * if the device does not respond). + */ + struct mutex lock; +}; + +static const int hdc3020_heater_vals[] = {0, 1, 0x3FFF}; + +static const struct iio_chan_spec hdc3020_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) | + BIT(IIO_CHAN_INFO_TROUGH) | BIT(IIO_CHAN_INFO_OFFSET), + }, + { + .type = IIO_HUMIDITYRELATIVE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) | + BIT(IIO_CHAN_INFO_TROUGH), + }, + { + /* + * For setting the internal heater, which can be switched on to + * prevent or remove any condensation that may develop when the + * ambient environment approaches its dew point temperature. + */ + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), + .output = 1, + }, +}; + +DECLARE_CRC8_TABLE(hdc3020_crc8_table); + +static int hdc3020_write_bytes(struct hdc3020_data *data, const u8 *buf, u8 len) +{ + struct i2c_client *client = data->client; + struct i2c_msg msg; + int ret, cnt; + + msg.addr = client->addr; + msg.flags = 0; + msg.buf = (char *)buf; + msg.len = len; + + /* + * During the measurement process, HDC3020 will not return data. + * So wait for a while and try again + */ + for (cnt = 0; cnt < HDC3020_READ_RETRY_TIMES; cnt++) { + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret == 1) + return 0; + + mdelay(HDC3020_BUSY_DELAY_MS); + } + dev_err(&client->dev, "Could not write sensor command\n"); + + return -ETIMEDOUT; +} + +static int hdc3020_read_bytes(struct hdc3020_data *data, const u8 *buf, + void *val, int len) +{ + int ret, cnt; + struct i2c_client *client = data->client; + struct i2c_msg msg[2] = { + [0] = { + .addr = client->addr, + .flags = 0, + .buf = (char *)buf, + .len = 2, + }, + [1] = { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = val, + .len = len, + }, + }; + + /* + * During the measurement process, HDC3020 will not return data. + * So wait for a while and try again + */ + for (cnt = 0; cnt < HDC3020_READ_RETRY_TIMES; cnt++) { + ret = i2c_transfer(client->adapter, msg, 2); + if (ret == 2) + return 0; + + mdelay(HDC3020_BUSY_DELAY_MS); + } + dev_err(&client->dev, "Could not read sensor data\n"); + + return -ETIMEDOUT; +} + +static int hdc3020_read_measurement(struct hdc3020_data *data, + enum iio_chan_type type, int *val) +{ + u8 crc, buf[6]; + int ret; + + ret = hdc3020_read_bytes(data, HDC3020_R_T_RH_AUTO, buf, 6); + if (ret < 0) + return ret; + + /* CRC check of the temperature measurement */ + crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE); + if (crc != buf[2]) + return -EINVAL; + + /* CRC check of the relative humidity measurement */ + crc = crc8(hdc3020_crc8_table, buf + 3, 2, CRC8_INIT_VALUE); + if (crc != buf[5]) + return -EINVAL; + + if (type == IIO_TEMP) + *val = get_unaligned_be16(buf); + else if (type == IIO_HUMIDITYRELATIVE) + *val = get_unaligned_be16(&buf[3]); + else + return -EINVAL; + + return 0; +} + +/* + * After exiting the automatic measurement mode or resetting, the peak + * value will be reset to the default value + * This method is used to get the highest temp measured during automatic + * measurement + */ +static int hdc3020_read_high_peak_t(struct hdc3020_data *data, int *val) +{ + u8 crc, buf[3]; + int ret; + + ret = hdc3020_read_bytes(data, HDC3020_R_T_HIGH_AUTO, buf, 3); + if (ret < 0) + return ret; + + crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE); + if (crc != buf[2]) + return -EINVAL; + + *val = get_unaligned_be16(buf); + + return 0; +} + +/* + * This method is used to get the lowest temp measured during automatic + * measurement + */ +static int hdc3020_read_low_peak_t(struct hdc3020_data *data, int *val) +{ + u8 crc, buf[3]; + int ret; + + ret = hdc3020_read_bytes(data, HDC3020_R_T_LOW_AUTO, buf, 3); + if (ret < 0) + return ret; + + crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE); + if (crc != buf[2]) + return -EINVAL; + + *val = get_unaligned_be16(buf); + + return 0; +} + +/* + * This method is used to get the highest humidity measured during automatic + * measurement + */ +static int hdc3020_read_high_peak_rh(struct hdc3020_data *data, int *val) +{ + u8 crc, buf[3]; + int ret; + + ret = hdc3020_read_bytes(data, HDC3020_R_RH_HIGH_AUTO, buf, 3); + if (ret < 0) + return ret; + + crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE); + if (crc != buf[2]) + return -EINVAL; + + *val = get_unaligned_be16(buf); + + return 0; +} + +/* + * This method is used to get the lowest humidity measured during automatic + * measurement + */ +static int hdc3020_read_low_peak_rh(struct hdc3020_data *data, int *val) +{ + u8 crc, buf[3]; + int ret; + + ret = hdc3020_read_bytes(data, HDC3020_R_RH_LOW_AUTO, buf, 3); + if (ret < 0) + return ret; + + crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE); + if (crc != buf[2]) + return -EINVAL; + + *val = get_unaligned_be16(buf); + + return 0; +} + +static int hdc3020_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct hdc3020_data *data = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_TEMP && chan->type != IIO_HUMIDITYRELATIVE) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&data->lock); + ret = hdc3020_read_measurement(data, chan->type, val); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_PEAK: { + guard(mutex)(&data->lock); + if (chan->type == IIO_TEMP) { + ret = hdc3020_read_high_peak_t(data, val); + if (ret < 0) + return ret; + } else { + ret = hdc3020_read_high_peak_rh(data, val); + if (ret < 0) + return ret; + } + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_TROUGH: { + guard(mutex)(&data->lock); + if (chan->type == IIO_TEMP) { + ret = hdc3020_read_low_peak_t(data, val); + if (ret < 0) + return ret; + } else { + ret = hdc3020_read_low_peak_rh(data, val); + if (ret < 0) + return ret; + } + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + *val2 = 65536; + if (chan->type == IIO_TEMP) + *val = 175; + else + *val = 100; + return IIO_VAL_FRACTIONAL; + + case IIO_CHAN_INFO_OFFSET: + if (chan->type != IIO_TEMP) + return -EINVAL; + + *val = 16852; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int hdc3020_read_available(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, + int *type, int *length, long mask) +{ + if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_CURRENT) + return -EINVAL; + + *vals = hdc3020_heater_vals; + *type = IIO_VAL_INT; + + return IIO_AVAIL_RANGE; +} + +static int hdc3020_update_heater(struct hdc3020_data *data, int val) +{ + u8 buf[5]; + int ret; + + if (val < hdc3020_heater_vals[0] || val > hdc3020_heater_vals[2]) + return -EINVAL; + + buf[0] = HDC3020_HEATER_CMD_MSB; + + if (!val) { + buf[1] = HDC3020_HEATER_DISABLE; + return hdc3020_write_bytes(data, buf, 2); + } + + buf[1] = HDC3020_HEATER_CONFIG; + put_unaligned_be16(val & GENMASK(13, 0), &buf[2]); + buf[4] = crc8(hdc3020_crc8_table, buf + 2, 2, CRC8_INIT_VALUE); + ret = hdc3020_write_bytes(data, buf, 5); + if (ret < 0) + return ret; + + buf[1] = HDC3020_HEATER_ENABLE; + + return hdc3020_write_bytes(data, buf, 2); +} + +static int hdc3020_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct hdc3020_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_CURRENT) + return -EINVAL; + + guard(mutex)(&data->lock); + return hdc3020_update_heater(data, val); + } + + return -EINVAL; +} + +static const struct iio_info hdc3020_info = { + .read_raw = hdc3020_read_raw, + .write_raw = hdc3020_write_raw, + .read_avail = hdc3020_read_available, +}; + +static void hdc3020_stop(void *data) +{ + hdc3020_write_bytes((struct hdc3020_data *)data, HDC3020_EXIT_AUTO, 2); +} + +static int hdc3020_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct hdc3020_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + mutex_init(&data->lock); + + crc8_populate_msb(hdc3020_crc8_table, HDC3020_CRC8_POLYNOMIAL); + + indio_dev->name = "hdc3020"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &hdc3020_info; + indio_dev->channels = hdc3020_channels; + indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels); + + ret = hdc3020_write_bytes(data, HDC3020_S_AUTO_10HZ_MOD0, 2); + if (ret) + return dev_err_probe(&client->dev, ret, + "Unable to set up measurement\n"); + + ret = devm_add_action_or_reset(&data->client->dev, hdc3020_stop, data); + if (ret) + return ret; + + ret = devm_iio_device_register(&data->client->dev, indio_dev); + if (ret) + return dev_err_probe(&client->dev, ret, "Failed to add device"); + + return 0; +} + +static const struct i2c_device_id hdc3020_id[] = { + { "hdc3020" }, + { "hdc3021" }, + { "hdc3022" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, hdc3020_id); + +static const struct of_device_id hdc3020_dt_ids[] = { + { .compatible = "ti,hdc3020" }, + { .compatible = "ti,hdc3021" }, + { .compatible = "ti,hdc3022" }, + { } +}; +MODULE_DEVICE_TABLE(of, hdc3020_dt_ids); + +static struct i2c_driver hdc3020_driver = { + .driver = { + .name = "hdc3020", + .of_match_table = hdc3020_dt_ids, + }, + .probe = hdc3020_probe, + .id_table = hdc3020_id, +}; +module_i2c_driver(hdc3020_driver); + +MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>"); +MODULE_AUTHOR("Li peiyu <579lpy@gmail.com>"); +MODULE_DESCRIPTION("TI HDC3020 humidity and temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index c2f97629e9cd..52a155ff3250 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -53,6 +53,7 @@ config ADIS16480 ADIS16485, ADIS16488 inertial sensors. source "drivers/iio/imu/bmi160/Kconfig" +source "drivers/iio/imu/bmi323/Kconfig" source "drivers/iio/imu/bno055/Kconfig" config FXOS8700 diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index 6eb612034722..7e2d7d5c3b7b 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -15,6 +15,7 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o obj-y += bmi160/ +obj-y += bmi323/ obj-y += bno055/ obj-$(CONFIG_FXOS8700) += fxos8700_core.o diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index bc40240b29e2..495caf4ce87a 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -44,8 +44,6 @@ int __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value, .cs_change = 1, .delay.value = adis->data->write_delay, .delay.unit = SPI_DELAY_UNIT_USECS, - .cs_change_delay.value = adis->data->cs_change_delay, - .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 2, .bits_per_word = 8, @@ -53,8 +51,6 @@ int __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value, .cs_change = 1, .delay.value = adis->data->write_delay, .delay.unit = SPI_DELAY_UNIT_USECS, - .cs_change_delay.value = adis->data->cs_change_delay, - .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 4, .bits_per_word = 8, @@ -62,8 +58,6 @@ int __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value, .cs_change = 1, .delay.value = adis->data->write_delay, .delay.unit = SPI_DELAY_UNIT_USECS, - .cs_change_delay.value = adis->data->cs_change_delay, - .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 6, .bits_per_word = 8, @@ -144,8 +138,6 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val, .cs_change = 1, .delay.value = adis->data->write_delay, .delay.unit = SPI_DELAY_UNIT_USECS, - .cs_change_delay.value = adis->data->cs_change_delay, - .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 2, .bits_per_word = 8, @@ -153,8 +145,6 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val, .cs_change = 1, .delay.value = adis->data->read_delay, .delay.unit = SPI_DELAY_UNIT_USECS, - .cs_change_delay.value = adis->data->cs_change_delay, - .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 4, .rx_buf = adis->rx, @@ -163,8 +153,6 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val, .cs_change = 1, .delay.value = adis->data->read_delay, .delay.unit = SPI_DELAY_UNIT_USECS, - .cs_change_delay.value = adis->data->cs_change_delay, - .cs_change_delay.unit = SPI_DELAY_UNIT_USECS, }, { .rx_buf = adis->rx + 2, .bits_per_word = 8, @@ -524,6 +512,12 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev, } mutex_init(&adis->state_lock); + + if (!spi->cs_inactive.value) { + spi->cs_inactive.value = data->cs_change_delay; + spi->cs_inactive.unit = SPI_DELAY_UNIT_USECS; + } + adis->spi = spi; adis->data = data; iio_device_set_drvdata(indio_dev, adis); diff --git a/drivers/iio/imu/bmi323/Kconfig b/drivers/iio/imu/bmi323/Kconfig new file mode 100644 index 000000000000..ab37b285393c --- /dev/null +++ b/drivers/iio/imu/bmi323/Kconfig @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# BMI323 IMU driver +# + +config BMI323 + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config BMI323_I2C + tristate "Bosch BMI323 I2C driver" + depends on I2C + select BMI323 + select REGMAP_I2C + help + Enable support for the Bosch BMI323 6-Axis IMU connected to I2C + interface. + + This driver can also be built as a module. If so, the module will be + called bmi323_i2c. + +config BMI323_SPI + tristate "Bosch BMI323 SPI driver" + depends on SPI + select BMI323 + select REGMAP_SPI + help + Enable support for the Bosch BMI323 6-Axis IMU connected to SPI + interface. + + This driver can also be built as a module. If so, the module will be + called bmi323_spi. diff --git a/drivers/iio/imu/bmi323/Makefile b/drivers/iio/imu/bmi323/Makefile new file mode 100644 index 000000000000..a6a6dc0207c9 --- /dev/null +++ b/drivers/iio/imu/bmi323/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Bosch BMI323 IMU +# +obj-$(CONFIG_BMI323) += bmi323_core.o +obj-$(CONFIG_BMI323_I2C) += bmi323_i2c.o +obj-$(CONFIG_BMI323_SPI) += bmi323_spi.o diff --git a/drivers/iio/imu/bmi323/bmi323.h b/drivers/iio/imu/bmi323/bmi323.h new file mode 100644 index 000000000000..dff126d41658 --- /dev/null +++ b/drivers/iio/imu/bmi323/bmi323.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IIO driver for Bosch BMI323 6-Axis IMU + * + * Copyright (C) 2023, Jagath Jog J <jagathjog1996@gmail.com> + */ + +#ifndef _BMI323_H_ +#define _BMI323_H_ + +#include <linux/bits.h> +#include <linux/regmap.h> +#include <linux/units.h> + +#define BMI323_I2C_DUMMY 2 +#define BMI323_SPI_DUMMY 1 + +/* Register map */ + +#define BMI323_CHIP_ID_REG 0x00 +#define BMI323_CHIP_ID_VAL 0x0043 +#define BMI323_CHIP_ID_MSK GENMASK(7, 0) +#define BMI323_ERR_REG 0x01 +#define BMI323_STATUS_REG 0x02 +#define BMI323_STATUS_POR_MSK BIT(0) + +/* Accelero/Gyro/Temp data registers */ +#define BMI323_ACCEL_X_REG 0x03 +#define BMI323_GYRO_X_REG 0x06 +#define BMI323_TEMP_REG 0x09 +#define BMI323_ALL_CHAN_MSK GENMASK(5, 0) + +/* Status registers */ +#define BMI323_STATUS_INT1_REG 0x0D +#define BMI323_STATUS_INT2_REG 0x0E +#define BMI323_STATUS_NOMOTION_MSK BIT(0) +#define BMI323_STATUS_MOTION_MSK BIT(1) +#define BMI323_STATUS_STP_WTR_MSK BIT(5) +#define BMI323_STATUS_TAP_MSK BIT(8) +#define BMI323_STATUS_ERROR_MSK BIT(10) +#define BMI323_STATUS_TMP_DRDY_MSK BIT(11) +#define BMI323_STATUS_GYR_DRDY_MSK BIT(12) +#define BMI323_STATUS_ACC_DRDY_MSK BIT(13) +#define BMI323_STATUS_ACC_GYR_DRDY_MSK GENMASK(13, 12) +#define BMI323_STATUS_FIFO_WTRMRK_MSK BIT(14) +#define BMI323_STATUS_FIFO_FULL_MSK BIT(15) + +/* Feature registers */ +#define BMI323_FEAT_IO0_REG 0x10 +#define BMI323_FEAT_IO0_XYZ_NOMOTION_MSK GENMASK(2, 0) +#define BMI323_FEAT_IO0_XYZ_MOTION_MSK GENMASK(5, 3) +#define BMI323_FEAT_XYZ_MSK GENMASK(2, 0) +#define BMI323_FEAT_IO0_STP_CNT_MSK BIT(9) +#define BMI323_FEAT_IO0_S_TAP_MSK BIT(12) +#define BMI323_FEAT_IO0_D_TAP_MSK BIT(13) +#define BMI323_FEAT_IO1_REG 0x11 +#define BMI323_FEAT_IO1_ERR_MSK GENMASK(3, 0) +#define BMI323_FEAT_IO2_REG 0x12 +#define BMI323_FEAT_IO_STATUS_REG 0x14 +#define BMI323_FEAT_IO_STATUS_MSK BIT(0) +#define BMI323_FEAT_ENG_POLL 2000 +#define BMI323_FEAT_ENG_TIMEOUT 10000 + +/* FIFO registers */ +#define BMI323_FIFO_FILL_LEVEL_REG 0x15 +#define BMI323_FIFO_DATA_REG 0x16 + +/* Accelero/Gyro config registers */ +#define BMI323_ACC_CONF_REG 0x20 +#define BMI323_GYRO_CONF_REG 0x21 +#define BMI323_ACC_GYRO_CONF_MODE_MSK GENMASK(14, 12) +#define BMI323_ACC_GYRO_CONF_ODR_MSK GENMASK(3, 0) +#define BMI323_ACC_GYRO_CONF_SCL_MSK GENMASK(6, 4) +#define BMI323_ACC_GYRO_CONF_BW_MSK BIT(7) +#define BMI323_ACC_GYRO_CONF_AVG_MSK GENMASK(10, 8) + +/* FIFO registers */ +#define BMI323_FIFO_WTRMRK_REG 0x35 +#define BMI323_FIFO_CONF_REG 0x36 +#define BMI323_FIFO_CONF_STP_FUL_MSK BIT(0) +#define BMI323_FIFO_CONF_ACC_GYR_EN_MSK GENMASK(10, 9) +#define BMI323_FIFO_ACC_GYR_MSK GENMASK(1, 0) +#define BMI323_FIFO_CTRL_REG 0x37 +#define BMI323_FIFO_FLUSH_MSK BIT(0) + +/* Interrupt pin config registers */ +#define BMI323_IO_INT_CTR_REG 0x38 +#define BMI323_IO_INT1_LVL_MSK BIT(0) +#define BMI323_IO_INT1_OD_MSK BIT(1) +#define BMI323_IO_INT1_OP_EN_MSK BIT(2) +#define BMI323_IO_INT1_LVL_OD_OP_MSK GENMASK(2, 0) +#define BMI323_IO_INT2_LVL_MSK BIT(8) +#define BMI323_IO_INT2_OD_MSK BIT(9) +#define BMI323_IO_INT2_OP_EN_MSK BIT(10) +#define BMI323_IO_INT2_LVL_OD_OP_MSK GENMASK(10, 8) +#define BMI323_IO_INT_CONF_REG 0x39 +#define BMI323_IO_INT_LTCH_MSK BIT(0) +#define BMI323_INT_MAP1_REG 0x3A +#define BMI323_INT_MAP2_REG 0x3B +#define BMI323_NOMOTION_MSK GENMASK(1, 0) +#define BMI323_MOTION_MSK GENMASK(3, 2) +#define BMI323_STEP_CNT_MSK GENMASK(11, 10) +#define BMI323_TAP_MSK GENMASK(1, 0) +#define BMI323_TMP_DRDY_MSK GENMASK(7, 6) +#define BMI323_GYR_DRDY_MSK GENMASK(9, 8) +#define BMI323_ACC_DRDY_MSK GENMASK(11, 10) +#define BMI323_FIFO_WTRMRK_MSK GENMASK(13, 12) +#define BMI323_FIFO_FULL_MSK GENMASK(15, 14) + +/* Feature registers */ +#define BMI323_FEAT_CTRL_REG 0x40 +#define BMI323_FEAT_ENG_EN_MSK BIT(0) +#define BMI323_FEAT_DATA_ADDR 0x41 +#define BMI323_FEAT_DATA_TX 0x42 +#define BMI323_FEAT_DATA_STATUS 0x43 +#define BMI323_FEAT_DATA_TX_RDY_MSK BIT(1) +#define BMI323_FEAT_EVNT_EXT_REG 0x47 +#define BMI323_FEAT_EVNT_EXT_S_MSK BIT(3) +#define BMI323_FEAT_EVNT_EXT_D_MSK BIT(4) + +#define BMI323_CMD_REG 0x7E +#define BMI323_RST_VAL 0xDEAF +#define BMI323_CFG_RES_REG 0x7F + +/* Extended registers */ +#define BMI323_GEN_SET1_REG 0x02 +#define BMI323_GEN_SET1_MODE_MSK BIT(0) +#define BMI323_GEN_HOLD_DUR_MSK GENMASK(4, 1) + +/* Any Motion/No Motion config registers */ +#define BMI323_ANYMO1_REG 0x05 +#define BMI323_NOMO1_REG 0x08 +#define BMI323_MO2_OFFSET 0x01 +#define BMI323_MO3_OFFSET 0x02 +#define BMI323_MO1_REF_UP_MSK BIT(12) +#define BMI323_MO1_SLOPE_TH_MSK GENMASK(11, 0) +#define BMI323_MO2_HYSTR_MSK GENMASK(9, 0) +#define BMI323_MO3_DURA_MSK GENMASK(12, 0) + +/* Step counter config registers */ +#define BMI323_STEP_SC1_REG 0x10 +#define BMI323_STEP_SC1_WTRMRK_MSK GENMASK(9, 0) +#define BMI323_STEP_SC1_RST_CNT_MSK BIT(10) +#define BMI323_STEP_SC1_REG 0x10 +#define BMI323_STEP_LEN 2 + +/* Tap gesture config registers */ +#define BMI323_TAP1_REG 0x1E +#define BMI323_TAP1_AXIS_SEL_MSK GENMASK(1, 0) +#define BMI323_AXIS_XYZ_MSK GENMASK(1, 0) +#define BMI323_TAP1_TIMOUT_MSK BIT(2) +#define BMI323_TAP1_MAX_PEAKS_MSK GENMASK(5, 3) +#define BMI323_TAP1_MODE_MSK GENMASK(7, 6) +#define BMI323_TAP2_REG 0x1F +#define BMI323_TAP2_THRES_MSK GENMASK(9, 0) +#define BMI323_TAP2_MAX_DUR_MSK GENMASK(15, 10) +#define BMI323_TAP3_REG 0x20 +#define BMI323_TAP3_QUIET_TIM_MSK GENMASK(15, 12) +#define BMI323_TAP3_QT_BW_TAP_MSK GENMASK(11, 8) +#define BMI323_TAP3_QT_AFT_GES_MSK GENMASK(15, 12) + +#define BMI323_MOTION_THRES_SCALE 512 +#define BMI323_MOTION_HYSTR_SCALE 512 +#define BMI323_MOTION_DURAT_SCALE 50 +#define BMI323_TAP_THRES_SCALE 512 +#define BMI323_DUR_BW_TAP_SCALE 200 +#define BMI323_QUITE_TIM_GES_SCALE 25 +#define BMI323_MAX_GES_DUR_SCALE 25 + +/* + * The formula to calculate temperature in C. + * See datasheet section 6.1.1, Register Map Overview + * + * T_C = (temp_raw / 512) + 23 + */ +#define BMI323_TEMP_OFFSET 11776 +#define BMI323_TEMP_SCALE 1953125 + +/* + * The BMI323 features a FIFO with a capacity of 2048 bytes. Each frame + * consists of accelerometer (X, Y, Z) data and gyroscope (X, Y, Z) data, + * totaling 6 words or 12 bytes. The FIFO buffer can hold a total of + * 170 frames. + * + * If a watermark interrupt is configured for 170 frames, the interrupt will + * trigger when the FIFO reaches 169 frames, so limit the maximum watermark + * level to 169 frames. In terms of data, 169 frames would equal 1014 bytes, + * which is approximately 2 frames before the FIFO reaches its full capacity. + * See datasheet section 5.7.3 FIFO Buffer Interrupts + */ +#define BMI323_BYTES_PER_SAMPLE 2 +#define BMI323_FIFO_LENGTH_IN_BYTES 2048 +#define BMI323_FIFO_FRAME_LENGTH 6 +#define BMI323_FIFO_FULL_IN_FRAMES \ + ((BMI323_FIFO_LENGTH_IN_BYTES / \ + (BMI323_BYTES_PER_SAMPLE * BMI323_FIFO_FRAME_LENGTH)) - 1) +#define BMI323_FIFO_FULL_IN_WORDS \ + (BMI323_FIFO_FULL_IN_FRAMES * BMI323_FIFO_FRAME_LENGTH) + +#define BMI323_INT_MICRO_TO_RAW(val, val2, scale) ((val) * (scale) + \ + ((val2) * (scale)) / MEGA) + +#define BMI323_RAW_TO_MICRO(raw, scale) ((((raw) % (scale)) * MEGA) / scale) + +struct device; +int bmi323_core_probe(struct device *dev); +extern const struct regmap_config bmi323_regmap_config; + +#endif diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c new file mode 100644 index 000000000000..183af482828f --- /dev/null +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -0,0 +1,2139 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IIO core driver for Bosch BMI323 6-Axis IMU. + * + * Copyright (C) 2023, Jagath Jog J <jagathjog1996@gmail.com> + * + * Datasheet: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi323-ds000.pdf + */ + +#include <linux/bitfield.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/units.h> + +#include <asm/unaligned.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include "bmi323.h" + +enum bmi323_sensor_type { + BMI323_ACCEL, + BMI323_GYRO, + BMI323_SENSORS_CNT, +}; + +enum bmi323_opr_mode { + ACC_GYRO_MODE_DISABLE = 0x00, + GYRO_DRIVE_MODE_ENABLED = 0x01, + ACC_GYRO_MODE_DUTYCYCLE = 0x03, + ACC_GYRO_MODE_CONTINOUS = 0x04, + ACC_GYRO_MODE_HIGH_PERF = 0x07, +}; + +enum bmi323_state { + BMI323_IDLE, + BMI323_BUFFER_DRDY_TRIGGERED, + BMI323_BUFFER_FIFO, +}; + +enum bmi323_irq_pin { + BMI323_IRQ_DISABLED, + BMI323_IRQ_INT1, + BMI323_IRQ_INT2, +}; + +enum bmi323_3db_bw { + BMI323_BW_ODR_BY_2, + BMI323_BW_ODR_BY_4, +}; + +enum bmi323_scan { + BMI323_ACCEL_X, + BMI323_ACCEL_Y, + BMI323_ACCEL_Z, + BMI323_GYRO_X, + BMI323_GYRO_Y, + BMI323_GYRO_Z, + BMI323_CHAN_MAX +}; + +struct bmi323_hw { + u8 data; + u8 config; + const int (*scale_table)[2]; + int scale_table_len; +}; + +/* + * The accelerometer supports +-2G/4G/8G/16G ranges, and the resolution of + * each sample is 16 bits, signed. + * At +-8G the scale can calculated by + * ((8 + 8) * 9.80665 / (2^16 - 1)) * 10^6 = 2394.23819 scale in micro + * + */ +static const int bmi323_accel_scale[][2] = { + { 0, 598 }, + { 0, 1197 }, + { 0, 2394 }, + { 0, 4788 }, +}; + +static const int bmi323_gyro_scale[][2] = { + { 0, 66 }, + { 0, 133 }, + { 0, 266 }, + { 0, 532 }, + { 0, 1065 }, +}; + +static const int bmi323_accel_gyro_avrg[] = {0, 2, 4, 8, 16, 32, 64}; + +static const struct bmi323_hw bmi323_hw[2] = { + [BMI323_ACCEL] = { + .data = BMI323_ACCEL_X_REG, + .config = BMI323_ACC_CONF_REG, + .scale_table = bmi323_accel_scale, + .scale_table_len = ARRAY_SIZE(bmi323_accel_scale), + }, + [BMI323_GYRO] = { + .data = BMI323_GYRO_X_REG, + .config = BMI323_GYRO_CONF_REG, + .scale_table = bmi323_gyro_scale, + .scale_table_len = ARRAY_SIZE(bmi323_gyro_scale), + }, +}; + +struct bmi323_data { + struct device *dev; + struct regmap *regmap; + struct iio_mount_matrix orientation; + enum bmi323_irq_pin irq_pin; + struct iio_trigger *trig; + bool drdy_trigger_enabled; + enum bmi323_state state; + s64 fifo_tstamp, old_fifo_tstamp; + u32 odrns[BMI323_SENSORS_CNT]; + u32 odrhz[BMI323_SENSORS_CNT]; + unsigned int feature_events; + + /* + * Lock to protect the members of device's private data from concurrent + * access and also to serialize the access of extended registers. + * See bmi323_write_ext_reg(..) for more info. + */ + struct mutex mutex; + int watermark; + __le16 fifo_buff[BMI323_FIFO_FULL_IN_WORDS] __aligned(IIO_DMA_MINALIGN); + struct { + __le16 channels[BMI323_CHAN_MAX]; + s64 ts __aligned(8); + } buffer; + __le16 steps_count[BMI323_STEP_LEN]; +}; + +static const struct iio_mount_matrix * +bmi323_get_mount_matrix(const struct iio_dev *idev, + const struct iio_chan_spec *chan) +{ + struct bmi323_data *data = iio_priv(idev); + + return &data->orientation; +} + +static const struct iio_chan_spec_ext_info bmi323_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, bmi323_get_mount_matrix), + { } +}; + +static const struct iio_event_spec bmi323_step_wtrmrk_event = { + .type = IIO_EV_TYPE_CHANGE, + .dir = IIO_EV_DIR_NONE, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE), +}; + +static const struct iio_event_spec bmi323_accel_event[] = { + { + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_HYSTERESIS) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_HYSTERESIS) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_GESTURE, + .dir = IIO_EV_DIR_SINGLETAP, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_RESET_TIMEOUT), + }, + { + .type = IIO_EV_TYPE_GESTURE, + .dir = IIO_EV_DIR_DOUBLETAP, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_RESET_TIMEOUT) | + BIT(IIO_EV_INFO_TAP2_MIN_DELAY), + }, +}; + +#define BMI323_ACCEL_CHANNEL(_type, _axis, _index) { \ + .type = _type, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ + .ext_info = bmi323_ext_info, \ + .event_spec = bmi323_accel_event, \ + .num_event_specs = ARRAY_SIZE(bmi323_accel_event), \ +} + +#define BMI323_GYRO_CHANNEL(_type, _axis, _index) { \ + .type = _type, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ + .ext_info = bmi323_ext_info, \ +} + +static const struct iio_chan_spec bmi323_channels[] = { + BMI323_ACCEL_CHANNEL(IIO_ACCEL, X, BMI323_ACCEL_X), + BMI323_ACCEL_CHANNEL(IIO_ACCEL, Y, BMI323_ACCEL_Y), + BMI323_ACCEL_CHANNEL(IIO_ACCEL, Z, BMI323_ACCEL_Z), + BMI323_GYRO_CHANNEL(IIO_ANGL_VEL, X, BMI323_GYRO_X), + BMI323_GYRO_CHANNEL(IIO_ANGL_VEL, Y, BMI323_GYRO_Y), + BMI323_GYRO_CHANNEL(IIO_ANGL_VEL, Z, BMI323_GYRO_Z), + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .scan_index = -1, + }, + { + .type = IIO_STEPS, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_ENABLE), + .scan_index = -1, + .event_spec = &bmi323_step_wtrmrk_event, + .num_event_specs = 1, + + }, + IIO_CHAN_SOFT_TIMESTAMP(BMI323_CHAN_MAX), +}; + +static const int bmi323_acc_gyro_odr[][2] = { + { 0, 781250 }, + { 1, 562500 }, + { 3, 125000 }, + { 6, 250000 }, + { 12, 500000 }, + { 25, 0 }, + { 50, 0 }, + { 100, 0 }, + { 200, 0 }, + { 400, 0 }, + { 800, 0 }, +}; + +static const int bmi323_acc_gyro_odrns[] = { + 1280 * MEGA, + 640 * MEGA, + 320 * MEGA, + 160 * MEGA, + 80 * MEGA, + 40 * MEGA, + 20 * MEGA, + 10 * MEGA, + 5 * MEGA, + 2500 * KILO, + 1250 * KILO, +}; + +static enum bmi323_sensor_type bmi323_iio_to_sensor(enum iio_chan_type iio_type) +{ + switch (iio_type) { + case IIO_ACCEL: + return BMI323_ACCEL; + case IIO_ANGL_VEL: + return BMI323_GYRO; + default: + return -EINVAL; + } +} + +static int bmi323_set_mode(struct bmi323_data *data, + enum bmi323_sensor_type sensor, + enum bmi323_opr_mode mode) +{ + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, bmi323_hw[sensor].config, + BMI323_ACC_GYRO_CONF_MODE_MSK, + FIELD_PREP(BMI323_ACC_GYRO_CONF_MODE_MSK, + mode)); +} + +/* + * When writing data to extended register there must be no communication to + * any other register before write transaction is complete. + * See datasheet section 6.2 Extended Register Map Description. + */ +static int bmi323_write_ext_reg(struct bmi323_data *data, unsigned int ext_addr, + unsigned int ext_data) +{ + int ret, feature_status; + + ret = regmap_read(data->regmap, BMI323_FEAT_DATA_STATUS, + &feature_status); + if (ret) + return ret; + + if (!FIELD_GET(BMI323_FEAT_DATA_TX_RDY_MSK, feature_status)) + return -EBUSY; + + ret = regmap_write(data->regmap, BMI323_FEAT_DATA_ADDR, ext_addr); + if (ret) + return ret; + + return regmap_write(data->regmap, BMI323_FEAT_DATA_TX, ext_data); +} + +/* + * When reading data from extended register there must be no communication to + * any other register before read transaction is complete. + * See datasheet section 6.2 Extended Register Map Description. + */ +static int bmi323_read_ext_reg(struct bmi323_data *data, unsigned int ext_addr, + unsigned int *ext_data) +{ + int ret, feature_status; + + ret = regmap_read(data->regmap, BMI323_FEAT_DATA_STATUS, + &feature_status); + if (ret) + return ret; + + if (!FIELD_GET(BMI323_FEAT_DATA_TX_RDY_MSK, feature_status)) + return -EBUSY; + + ret = regmap_write(data->regmap, BMI323_FEAT_DATA_ADDR, ext_addr); + if (ret) + return ret; + + return regmap_read(data->regmap, BMI323_FEAT_DATA_TX, ext_data); +} + +static int bmi323_update_ext_reg(struct bmi323_data *data, + unsigned int ext_addr, + unsigned int mask, unsigned int ext_data) +{ + unsigned int value; + int ret; + + ret = bmi323_read_ext_reg(data, ext_addr, &value); + if (ret) + return ret; + + set_mask_bits(&value, mask, ext_data); + + return bmi323_write_ext_reg(data, ext_addr, value); +} + +static int bmi323_get_error_status(struct bmi323_data *data) +{ + int error, ret; + + guard(mutex)(&data->mutex); + ret = regmap_read(data->regmap, BMI323_ERR_REG, &error); + if (ret) + return ret; + + if (error) + dev_err(data->dev, "Sensor error 0x%x\n", error); + + return error; +} + +static int bmi323_feature_engine_events(struct bmi323_data *data, + const unsigned int event_mask, + bool state) +{ + unsigned int value; + int ret; + + ret = regmap_read(data->regmap, BMI323_FEAT_IO0_REG, &value); + if (ret) + return ret; + + /* Register must be cleared before changing an active config */ + ret = regmap_write(data->regmap, BMI323_FEAT_IO0_REG, 0); + if (ret) + return ret; + + if (state) + value |= event_mask; + else + value &= ~event_mask; + + ret = regmap_write(data->regmap, BMI323_FEAT_IO0_REG, value); + if (ret) + return ret; + + return regmap_write(data->regmap, BMI323_FEAT_IO_STATUS_REG, + BMI323_FEAT_IO_STATUS_MSK); +} + +static int bmi323_step_wtrmrk_en(struct bmi323_data *data, int state) +{ + enum bmi323_irq_pin step_irq; + int ret; + + guard(mutex)(&data->mutex); + if (!FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK, data->feature_events)) + return -EINVAL; + + if (state) + step_irq = data->irq_pin; + else + step_irq = BMI323_IRQ_DISABLED; + + ret = bmi323_update_ext_reg(data, BMI323_STEP_SC1_REG, + BMI323_STEP_SC1_WTRMRK_MSK, + FIELD_PREP(BMI323_STEP_SC1_WTRMRK_MSK, + state ? 1 : 0)); + if (ret) + return ret; + + return regmap_update_bits(data->regmap, BMI323_INT_MAP1_REG, + BMI323_STEP_CNT_MSK, + FIELD_PREP(BMI323_STEP_CNT_MSK, step_irq)); +} + +static int bmi323_motion_config_reg(enum iio_event_direction dir) +{ + switch (dir) { + case IIO_EV_DIR_RISING: + return BMI323_ANYMO1_REG; + case IIO_EV_DIR_FALLING: + return BMI323_NOMO1_REG; + default: + return -EINVAL; + } +} + +static int bmi323_motion_event_en(struct bmi323_data *data, + enum iio_event_direction dir, int state) +{ + unsigned int state_value = state ? BMI323_FEAT_XYZ_MSK : 0; + int config, ret, msk, raw, field_value; + enum bmi323_irq_pin motion_irq; + int irq_msk, irq_field_val; + + if (state) + motion_irq = data->irq_pin; + else + motion_irq = BMI323_IRQ_DISABLED; + + switch (dir) { + case IIO_EV_DIR_RISING: + msk = BMI323_FEAT_IO0_XYZ_MOTION_MSK; + raw = 512; + config = BMI323_ANYMO1_REG; + irq_msk = BMI323_MOTION_MSK; + irq_field_val = FIELD_PREP(BMI323_MOTION_MSK, motion_irq); + field_value = FIELD_PREP(BMI323_FEAT_IO0_XYZ_MOTION_MSK, + state_value); + break; + case IIO_EV_DIR_FALLING: + msk = BMI323_FEAT_IO0_XYZ_NOMOTION_MSK; + raw = 0; + config = BMI323_NOMO1_REG; + irq_msk = BMI323_NOMOTION_MSK; + irq_field_val = FIELD_PREP(BMI323_NOMOTION_MSK, motion_irq); + field_value = FIELD_PREP(BMI323_FEAT_IO0_XYZ_NOMOTION_MSK, + state_value); + break; + default: + return -EINVAL; + } + + guard(mutex)(&data->mutex); + ret = bmi323_feature_engine_events(data, msk, state); + if (ret) + return ret; + + ret = bmi323_update_ext_reg(data, config, + BMI323_MO1_REF_UP_MSK, + FIELD_PREP(BMI323_MO1_REF_UP_MSK, 0)); + if (ret) + return ret; + + /* Set initial value to avoid interrupts while enabling*/ + ret = bmi323_update_ext_reg(data, config, + BMI323_MO1_SLOPE_TH_MSK, + FIELD_PREP(BMI323_MO1_SLOPE_TH_MSK, raw)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, BMI323_INT_MAP1_REG, irq_msk, + irq_field_val); + if (ret) + return ret; + + set_mask_bits(&data->feature_events, msk, field_value); + + return 0; +} + +static int bmi323_tap_event_en(struct bmi323_data *data, + enum iio_event_direction dir, int state) +{ + enum bmi323_irq_pin tap_irq; + int ret, tap_enabled; + + guard(mutex)(&data->mutex); + + if (data->odrhz[BMI323_ACCEL] < 200) { + dev_err(data->dev, "Invalid accelerometer parameter\n"); + return -EINVAL; + } + + switch (dir) { + case IIO_EV_DIR_SINGLETAP: + ret = bmi323_feature_engine_events(data, + BMI323_FEAT_IO0_S_TAP_MSK, + state); + if (ret) + return ret; + + set_mask_bits(&data->feature_events, BMI323_FEAT_IO0_S_TAP_MSK, + FIELD_PREP(BMI323_FEAT_IO0_S_TAP_MSK, state)); + break; + case IIO_EV_DIR_DOUBLETAP: + ret = bmi323_feature_engine_events(data, + BMI323_FEAT_IO0_D_TAP_MSK, + state); + if (ret) + return ret; + + set_mask_bits(&data->feature_events, BMI323_FEAT_IO0_D_TAP_MSK, + FIELD_PREP(BMI323_FEAT_IO0_D_TAP_MSK, state)); + break; + default: + return -EINVAL; + } + + tap_enabled = FIELD_GET(BMI323_FEAT_IO0_S_TAP_MSK | + BMI323_FEAT_IO0_D_TAP_MSK, + data->feature_events); + + if (tap_enabled) + tap_irq = data->irq_pin; + else + tap_irq = BMI323_IRQ_DISABLED; + + ret = regmap_update_bits(data->regmap, BMI323_INT_MAP2_REG, + BMI323_TAP_MSK, + FIELD_PREP(BMI323_TAP_MSK, tap_irq)); + if (ret) + return ret; + + if (!state) + return 0; + + ret = bmi323_update_ext_reg(data, BMI323_TAP1_REG, + BMI323_TAP1_MAX_PEAKS_MSK, + FIELD_PREP(BMI323_TAP1_MAX_PEAKS_MSK, + 0x04)); + if (ret) + return ret; + + ret = bmi323_update_ext_reg(data, BMI323_TAP1_REG, + BMI323_TAP1_AXIS_SEL_MSK, + FIELD_PREP(BMI323_TAP1_AXIS_SEL_MSK, + BMI323_AXIS_XYZ_MSK)); + if (ret) + return ret; + + return bmi323_update_ext_reg(data, BMI323_TAP1_REG, + BMI323_TAP1_TIMOUT_MSK, + FIELD_PREP(BMI323_TAP1_TIMOUT_MSK, + 0)); +} + +static ssize_t in_accel_gesture_tap_wait_dur_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bmi323_data *data = iio_priv(indio_dev); + unsigned int reg_value, raw; + int ret, val[2]; + + scoped_guard(mutex, &data->mutex) { + ret = bmi323_read_ext_reg(data, BMI323_TAP2_REG, ®_value); + if (ret) + return ret; + } + + raw = FIELD_GET(BMI323_TAP2_MAX_DUR_MSK, reg_value); + val[0] = raw / BMI323_MAX_GES_DUR_SCALE; + val[1] = BMI323_RAW_TO_MICRO(raw, BMI323_MAX_GES_DUR_SCALE); + + return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(val), + val); +} + +static ssize_t in_accel_gesture_tap_wait_dur_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bmi323_data *data = iio_priv(indio_dev); + int ret, val_int, val_fract, raw; + + ret = iio_str_to_fixpoint(buf, 100000, &val_int, &val_fract); + if (ret) + return ret; + + raw = BMI323_INT_MICRO_TO_RAW(val_int, val_fract, + BMI323_MAX_GES_DUR_SCALE); + if (!in_range(raw, 0, 64)) + return -EINVAL; + + guard(mutex)(&data->mutex); + ret = bmi323_update_ext_reg(data, BMI323_TAP2_REG, + BMI323_TAP2_MAX_DUR_MSK, + FIELD_PREP(BMI323_TAP2_MAX_DUR_MSK, raw)); + if (ret) + return ret; + + return len; +} + +/* + * Maximum duration from first tap within the second tap is expected to happen. + * This timeout is applicable only if gesture_tap_wait_timeout is enabled. + */ +static IIO_DEVICE_ATTR_RW(in_accel_gesture_tap_wait_dur, 0); + +static ssize_t in_accel_gesture_tap_wait_timeout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bmi323_data *data = iio_priv(indio_dev); + unsigned int reg_value, raw; + int ret; + + scoped_guard(mutex, &data->mutex) { + ret = bmi323_read_ext_reg(data, BMI323_TAP1_REG, ®_value); + if (ret) + return ret; + } + + raw = FIELD_GET(BMI323_TAP1_TIMOUT_MSK, reg_value); + + return iio_format_value(buf, IIO_VAL_INT, 1, &raw); +} + +static ssize_t in_accel_gesture_tap_wait_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bmi323_data *data = iio_priv(indio_dev); + bool val; + int ret; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + guard(mutex)(&data->mutex); + ret = bmi323_update_ext_reg(data, BMI323_TAP1_REG, + BMI323_TAP1_TIMOUT_MSK, + FIELD_PREP(BMI323_TAP1_TIMOUT_MSK, val)); + if (ret) + return ret; + + return len; +} + +/* Enable/disable gesture confirmation with wait time */ +static IIO_DEVICE_ATTR_RW(in_accel_gesture_tap_wait_timeout, 0); + +static IIO_CONST_ATTR(in_accel_gesture_tap_wait_dur_available, + "[0.0 0.04 2.52]"); + +static IIO_CONST_ATTR(in_accel_gesture_doubletap_tap2_min_delay_available, + "[0.005 0.005 0.075]"); + +static IIO_CONST_ATTR(in_accel_gesture_tap_reset_timeout_available, + "[0.04 0.04 0.6]"); + +static IIO_CONST_ATTR(in_accel_gesture_tap_value_available, "[0.0 0.002 1.99]"); + +static IIO_CONST_ATTR(in_accel_mag_value_available, "[0.0 0.002 7.99]"); + +static IIO_CONST_ATTR(in_accel_mag_period_available, "[0.0 0.02 162.0]"); + +static IIO_CONST_ATTR(in_accel_mag_hysteresis_available, "[0.0 0.002 1.99]"); + +static struct attribute *bmi323_event_attributes[] = { + &iio_const_attr_in_accel_gesture_tap_value_available.dev_attr.attr, + &iio_const_attr_in_accel_gesture_tap_reset_timeout_available.dev_attr.attr, + &iio_const_attr_in_accel_gesture_doubletap_tap2_min_delay_available.dev_attr.attr, + &iio_const_attr_in_accel_gesture_tap_wait_dur_available.dev_attr.attr, + &iio_dev_attr_in_accel_gesture_tap_wait_timeout.dev_attr.attr, + &iio_dev_attr_in_accel_gesture_tap_wait_dur.dev_attr.attr, + &iio_const_attr_in_accel_mag_value_available.dev_attr.attr, + &iio_const_attr_in_accel_mag_period_available.dev_attr.attr, + &iio_const_attr_in_accel_mag_hysteresis_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group bmi323_event_attribute_group = { + .attrs = bmi323_event_attributes, +}; + +static int bmi323_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct bmi323_data *data = iio_priv(indio_dev); + + switch (type) { + case IIO_EV_TYPE_MAG: + return bmi323_motion_event_en(data, dir, state); + case IIO_EV_TYPE_GESTURE: + return bmi323_tap_event_en(data, dir, state); + case IIO_EV_TYPE_CHANGE: + return bmi323_step_wtrmrk_en(data, state); + default: + return -EINVAL; + } +} + +static int bmi323_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct bmi323_data *data = iio_priv(indio_dev); + int ret, value, reg_val; + + guard(mutex)(&data->mutex); + + switch (chan->type) { + case IIO_ACCEL: + switch (dir) { + case IIO_EV_DIR_SINGLETAP: + ret = FIELD_GET(BMI323_FEAT_IO0_S_TAP_MSK, + data->feature_events); + break; + case IIO_EV_DIR_DOUBLETAP: + ret = FIELD_GET(BMI323_FEAT_IO0_D_TAP_MSK, + data->feature_events); + break; + case IIO_EV_DIR_RISING: + value = FIELD_GET(BMI323_FEAT_IO0_XYZ_MOTION_MSK, + data->feature_events); + ret = value ? 1 : 0; + break; + case IIO_EV_DIR_FALLING: + value = FIELD_GET(BMI323_FEAT_IO0_XYZ_NOMOTION_MSK, + data->feature_events); + ret = value ? 1 : 0; + break; + default: + ret = -EINVAL; + break; + } + return ret; + case IIO_STEPS: + ret = regmap_read(data->regmap, BMI323_INT_MAP1_REG, ®_val); + if (ret) + return ret; + + return FIELD_GET(BMI323_STEP_CNT_MSK, reg_val) ? 1 : 0; + default: + return -EINVAL; + } +} + +static int bmi323_write_event_value(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 bmi323_data *data = iio_priv(indio_dev); + unsigned int raw; + int reg; + + guard(mutex)(&data->mutex); + + switch (type) { + case IIO_EV_TYPE_GESTURE: + switch (info) { + case IIO_EV_INFO_VALUE: + if (!in_range(val, 0, 2)) + return -EINVAL; + + raw = BMI323_INT_MICRO_TO_RAW(val, val2, + BMI323_TAP_THRES_SCALE); + + return bmi323_update_ext_reg(data, BMI323_TAP2_REG, + BMI323_TAP2_THRES_MSK, + FIELD_PREP(BMI323_TAP2_THRES_MSK, + raw)); + case IIO_EV_INFO_RESET_TIMEOUT: + if (val || !in_range(val2, 40000, 560001)) + return -EINVAL; + + raw = BMI323_INT_MICRO_TO_RAW(val, val2, + BMI323_QUITE_TIM_GES_SCALE); + + return bmi323_update_ext_reg(data, BMI323_TAP3_REG, + BMI323_TAP3_QT_AFT_GES_MSK, + FIELD_PREP(BMI323_TAP3_QT_AFT_GES_MSK, + raw)); + case IIO_EV_INFO_TAP2_MIN_DELAY: + if (val || !in_range(val2, 5000, 70001)) + return -EINVAL; + + raw = BMI323_INT_MICRO_TO_RAW(val, val2, + BMI323_DUR_BW_TAP_SCALE); + + return bmi323_update_ext_reg(data, BMI323_TAP3_REG, + BMI323_TAP3_QT_BW_TAP_MSK, + FIELD_PREP(BMI323_TAP3_QT_BW_TAP_MSK, + raw)); + default: + return -EINVAL; + } + case IIO_EV_TYPE_MAG: + reg = bmi323_motion_config_reg(dir); + if (reg < 0) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (!in_range(val, 0, 8)) + return -EINVAL; + + raw = BMI323_INT_MICRO_TO_RAW(val, val2, + BMI323_MOTION_THRES_SCALE); + + return bmi323_update_ext_reg(data, reg, + BMI323_MO1_SLOPE_TH_MSK, + FIELD_PREP(BMI323_MO1_SLOPE_TH_MSK, + raw)); + case IIO_EV_INFO_PERIOD: + if (!in_range(val, 0, 163)) + return -EINVAL; + + raw = BMI323_INT_MICRO_TO_RAW(val, val2, + BMI323_MOTION_DURAT_SCALE); + + return bmi323_update_ext_reg(data, + reg + BMI323_MO3_OFFSET, + BMI323_MO3_DURA_MSK, + FIELD_PREP(BMI323_MO3_DURA_MSK, + raw)); + case IIO_EV_INFO_HYSTERESIS: + if (!in_range(val, 0, 2)) + return -EINVAL; + + raw = BMI323_INT_MICRO_TO_RAW(val, val2, + BMI323_MOTION_HYSTR_SCALE); + + return bmi323_update_ext_reg(data, + reg + BMI323_MO2_OFFSET, + BMI323_MO2_HYSTR_MSK, + FIELD_PREP(BMI323_MO2_HYSTR_MSK, + raw)); + default: + return -EINVAL; + } + case IIO_EV_TYPE_CHANGE: + if (!in_range(val, 0, 20461)) + return -EINVAL; + + raw = val / 20; + return bmi323_update_ext_reg(data, BMI323_STEP_SC1_REG, + BMI323_STEP_SC1_WTRMRK_MSK, + FIELD_PREP(BMI323_STEP_SC1_WTRMRK_MSK, + raw)); + default: + return -EINVAL; + } +} + +static int bmi323_read_event_value(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 bmi323_data *data = iio_priv(indio_dev); + unsigned int raw, reg_value; + int ret, reg; + + guard(mutex)(&data->mutex); + + switch (type) { + case IIO_EV_TYPE_GESTURE: + switch (info) { + case IIO_EV_INFO_VALUE: + ret = bmi323_read_ext_reg(data, BMI323_TAP2_REG, + ®_value); + if (ret) + return ret; + + raw = FIELD_GET(BMI323_TAP2_THRES_MSK, reg_value); + *val = raw / BMI323_TAP_THRES_SCALE; + *val2 = BMI323_RAW_TO_MICRO(raw, BMI323_TAP_THRES_SCALE); + return IIO_VAL_INT_PLUS_MICRO; + case IIO_EV_INFO_RESET_TIMEOUT: + ret = bmi323_read_ext_reg(data, BMI323_TAP3_REG, + ®_value); + if (ret) + return ret; + + raw = FIELD_GET(BMI323_TAP3_QT_AFT_GES_MSK, reg_value); + *val = 0; + *val2 = BMI323_RAW_TO_MICRO(raw, + BMI323_QUITE_TIM_GES_SCALE); + return IIO_VAL_INT_PLUS_MICRO; + case IIO_EV_INFO_TAP2_MIN_DELAY: + ret = bmi323_read_ext_reg(data, BMI323_TAP3_REG, + ®_value); + if (ret) + return ret; + + raw = FIELD_GET(BMI323_TAP3_QT_BW_TAP_MSK, reg_value); + *val = 0; + *val2 = BMI323_RAW_TO_MICRO(raw, + BMI323_DUR_BW_TAP_SCALE); + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_EV_TYPE_MAG: + reg = bmi323_motion_config_reg(dir); + if (reg < 0) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + ret = bmi323_read_ext_reg(data, reg, ®_value); + if (ret) + return ret; + + raw = FIELD_GET(BMI323_MO1_SLOPE_TH_MSK, reg_value); + *val = raw / BMI323_MOTION_THRES_SCALE; + *val2 = BMI323_RAW_TO_MICRO(raw, + BMI323_MOTION_THRES_SCALE); + return IIO_VAL_INT_PLUS_MICRO; + case IIO_EV_INFO_PERIOD: + ret = bmi323_read_ext_reg(data, + reg + BMI323_MO3_OFFSET, + ®_value); + if (ret) + return ret; + + raw = FIELD_GET(BMI323_MO3_DURA_MSK, reg_value); + *val = raw / BMI323_MOTION_DURAT_SCALE; + *val2 = BMI323_RAW_TO_MICRO(raw, + BMI323_MOTION_DURAT_SCALE); + return IIO_VAL_INT_PLUS_MICRO; + case IIO_EV_INFO_HYSTERESIS: + ret = bmi323_read_ext_reg(data, + reg + BMI323_MO2_OFFSET, + ®_value); + if (ret) + return ret; + + raw = FIELD_GET(BMI323_MO2_HYSTR_MSK, reg_value); + *val = raw / BMI323_MOTION_HYSTR_SCALE; + *val2 = BMI323_RAW_TO_MICRO(raw, + BMI323_MOTION_HYSTR_SCALE); + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_EV_TYPE_CHANGE: + ret = bmi323_read_ext_reg(data, BMI323_STEP_SC1_REG, + ®_value); + if (ret) + return ret; + + raw = FIELD_GET(BMI323_STEP_SC1_WTRMRK_MSK, reg_value); + *val = raw * 20; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int __bmi323_fifo_flush(struct iio_dev *indio_dev) +{ + struct bmi323_data *data = iio_priv(indio_dev); + int i, ret, fifo_lvl, frame_count, bit, index; + __le16 *frame, *pchannels; + u64 sample_period; + s64 tstamp; + + guard(mutex)(&data->mutex); + ret = regmap_read(data->regmap, BMI323_FIFO_FILL_LEVEL_REG, &fifo_lvl); + if (ret) + return ret; + + fifo_lvl = min(fifo_lvl, BMI323_FIFO_FULL_IN_WORDS); + + frame_count = fifo_lvl / BMI323_FIFO_FRAME_LENGTH; + if (!frame_count) + return -EINVAL; + + if (fifo_lvl % BMI323_FIFO_FRAME_LENGTH) + dev_warn(data->dev, "Bad FIFO alignment\n"); + + /* + * Approximate timestamps for each of the sample based on the sampling + * frequency, timestamp for last sample and number of samples. + */ + if (data->old_fifo_tstamp) { + sample_period = data->fifo_tstamp - data->old_fifo_tstamp; + do_div(sample_period, frame_count); + } else { + sample_period = data->odrns[BMI323_ACCEL]; + } + + tstamp = data->fifo_tstamp - (frame_count - 1) * sample_period; + + ret = regmap_noinc_read(data->regmap, BMI323_FIFO_DATA_REG, + &data->fifo_buff[0], + fifo_lvl * BMI323_BYTES_PER_SAMPLE); + if (ret) + return ret; + + for (i = 0; i < frame_count; i++) { + frame = &data->fifo_buff[i * BMI323_FIFO_FRAME_LENGTH]; + pchannels = &data->buffer.channels[0]; + + index = 0; + for_each_set_bit(bit, indio_dev->active_scan_mask, + BMI323_CHAN_MAX) + pchannels[index++] = frame[bit]; + + iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, + tstamp); + + tstamp += sample_period; + } + + return frame_count; +} + +static int bmi323_set_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct bmi323_data *data = iio_priv(indio_dev); + + val = min(val, (u32)BMI323_FIFO_FULL_IN_FRAMES); + + guard(mutex)(&data->mutex); + data->watermark = val; + + return 0; +} + +static int bmi323_fifo_disable(struct bmi323_data *data) +{ + int ret; + + guard(mutex)(&data->mutex); + ret = regmap_write(data->regmap, BMI323_FIFO_CONF_REG, 0); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, BMI323_INT_MAP2_REG, + BMI323_FIFO_WTRMRK_MSK, + FIELD_PREP(BMI323_FIFO_WTRMRK_MSK, 0)); + if (ret) + return ret; + + data->fifo_tstamp = 0; + data->state = BMI323_IDLE; + + return 0; +} + +static int bmi323_buffer_predisable(struct iio_dev *indio_dev) +{ + struct bmi323_data *data = iio_priv(indio_dev); + + if (iio_device_get_current_mode(indio_dev) == INDIO_BUFFER_TRIGGERED) + return 0; + + return bmi323_fifo_disable(data); +} + +static int bmi323_update_watermark(struct bmi323_data *data) +{ + int wtrmrk; + + wtrmrk = data->watermark * BMI323_FIFO_FRAME_LENGTH; + + return regmap_write(data->regmap, BMI323_FIFO_WTRMRK_REG, wtrmrk); +} + +static int bmi323_fifo_enable(struct bmi323_data *data) +{ + int ret; + + guard(mutex)(&data->mutex); + ret = regmap_update_bits(data->regmap, BMI323_FIFO_CONF_REG, + BMI323_FIFO_CONF_ACC_GYR_EN_MSK, + FIELD_PREP(BMI323_FIFO_CONF_ACC_GYR_EN_MSK, + BMI323_FIFO_ACC_GYR_MSK)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, BMI323_INT_MAP2_REG, + BMI323_FIFO_WTRMRK_MSK, + FIELD_PREP(BMI323_FIFO_WTRMRK_MSK, + data->irq_pin)); + if (ret) + return ret; + + ret = bmi323_update_watermark(data); + if (ret) + return ret; + + ret = regmap_write(data->regmap, BMI323_FIFO_CTRL_REG, + BMI323_FIFO_FLUSH_MSK); + if (ret) + return ret; + + data->state = BMI323_BUFFER_FIFO; + + return 0; +} + +static int bmi323_buffer_preenable(struct iio_dev *indio_dev) +{ + struct bmi323_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->mutex); + /* + * When the ODR of the accelerometer and gyroscope do not match, the + * maximum ODR value between the accelerometer and gyroscope is used + * for FIFO and the signal with lower ODR will insert dummy frame. + * So allow buffer read only when ODR's of accelero and gyro are equal. + * See datasheet section 5.7 "FIFO Data Buffering". + */ + if (data->odrns[BMI323_ACCEL] != data->odrns[BMI323_GYRO]) { + dev_err(data->dev, "Accelero and Gyro ODR doesn't match\n"); + return -EINVAL; + } + + return 0; +} + +static int bmi323_buffer_postenable(struct iio_dev *indio_dev) +{ + struct bmi323_data *data = iio_priv(indio_dev); + + if (iio_device_get_current_mode(indio_dev) == INDIO_BUFFER_TRIGGERED) + return 0; + + return bmi323_fifo_enable(data); +} + +static ssize_t hwfifo_watermark_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bmi323_data *data = iio_priv(indio_dev); + int wm; + + scoped_guard(mutex, &data->mutex) + wm = data->watermark; + + return sysfs_emit(buf, "%d\n", wm); +} +static IIO_DEVICE_ATTR_RO(hwfifo_watermark, 0); + +static ssize_t hwfifo_enabled_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bmi323_data *data = iio_priv(indio_dev); + bool state; + + scoped_guard(mutex, &data->mutex) + state = data->state == BMI323_BUFFER_FIFO; + + return sysfs_emit(buf, "%d\n", state); +} +static IIO_DEVICE_ATTR_RO(hwfifo_enabled, 0); + +static const struct iio_dev_attr *bmi323_fifo_attributes[] = { + &iio_dev_attr_hwfifo_watermark, + &iio_dev_attr_hwfifo_enabled, + NULL +}; + +static const struct iio_buffer_setup_ops bmi323_buffer_ops = { + .preenable = bmi323_buffer_preenable, + .postenable = bmi323_buffer_postenable, + .predisable = bmi323_buffer_predisable, +}; + +static irqreturn_t bmi323_irq_thread_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct bmi323_data *data = iio_priv(indio_dev); + unsigned int status_addr, status, feature_event; + s64 timestamp = iio_get_time_ns(indio_dev); + int ret; + + if (data->irq_pin == BMI323_IRQ_INT1) + status_addr = BMI323_STATUS_INT1_REG; + else + status_addr = BMI323_STATUS_INT2_REG; + + scoped_guard(mutex, &data->mutex) { + ret = regmap_read(data->regmap, status_addr, &status); + if (ret) + return IRQ_NONE; + } + + if (!status || FIELD_GET(BMI323_STATUS_ERROR_MSK, status)) + return IRQ_NONE; + + if (FIELD_GET(BMI323_STATUS_FIFO_WTRMRK_MSK, status)) { + data->old_fifo_tstamp = data->fifo_tstamp; + data->fifo_tstamp = iio_get_time_ns(indio_dev); + ret = __bmi323_fifo_flush(indio_dev); + if (ret < 0) + return IRQ_NONE; + } + + if (FIELD_GET(BMI323_STATUS_ACC_GYR_DRDY_MSK, status)) + iio_trigger_poll_nested(data->trig); + + if (FIELD_GET(BMI323_STATUS_MOTION_MSK, status)) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_RISING), + timestamp); + + if (FIELD_GET(BMI323_STATUS_NOMOTION_MSK, status)) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_FALLING), + timestamp); + + if (FIELD_GET(BMI323_STATUS_STP_WTR_MSK, status)) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_STEPS, 0, + IIO_NO_MOD, + IIO_EV_TYPE_CHANGE, + IIO_EV_DIR_NONE), + timestamp); + + if (FIELD_GET(BMI323_STATUS_TAP_MSK, status)) { + scoped_guard(mutex, &data->mutex) { + ret = regmap_read(data->regmap, + BMI323_FEAT_EVNT_EXT_REG, + &feature_event); + if (ret) + return IRQ_NONE; + } + + if (FIELD_GET(BMI323_FEAT_EVNT_EXT_S_MSK, feature_event)) { + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_GESTURE, + IIO_EV_DIR_SINGLETAP), + timestamp); + } + + if (FIELD_GET(BMI323_FEAT_EVNT_EXT_D_MSK, feature_event)) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_GESTURE, + IIO_EV_DIR_DOUBLETAP), + timestamp); + } + + return IRQ_HANDLED; +} + +static int bmi323_set_drdy_irq(struct bmi323_data *data, + enum bmi323_irq_pin irq_pin) +{ + int ret; + + ret = regmap_update_bits(data->regmap, BMI323_INT_MAP2_REG, + BMI323_GYR_DRDY_MSK, + FIELD_PREP(BMI323_GYR_DRDY_MSK, irq_pin)); + if (ret) + return ret; + + return regmap_update_bits(data->regmap, BMI323_INT_MAP2_REG, + BMI323_ACC_DRDY_MSK, + FIELD_PREP(BMI323_ACC_DRDY_MSK, irq_pin)); +} + +static int bmi323_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct bmi323_data *data = iio_trigger_get_drvdata(trig); + enum bmi323_irq_pin irq_pin; + + guard(mutex)(&data->mutex); + + if (data->state == BMI323_BUFFER_FIFO) { + dev_warn(data->dev, "Can't set trigger when FIFO enabled\n"); + return -EBUSY; + } + + if (state) { + data->state = BMI323_BUFFER_DRDY_TRIGGERED; + irq_pin = data->irq_pin; + } else { + data->state = BMI323_IDLE; + irq_pin = BMI323_IRQ_DISABLED; + } + + return bmi323_set_drdy_irq(data, irq_pin); +} + +static const struct iio_trigger_ops bmi323_trigger_ops = { + .set_trigger_state = &bmi323_data_rdy_trigger_set_state, +}; + +static irqreturn_t bmi323_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bmi323_data *data = iio_priv(indio_dev); + int ret, bit, index = 0; + + /* Lock to protect the data->buffer */ + guard(mutex)(&data->mutex); + + if (*indio_dev->active_scan_mask == BMI323_ALL_CHAN_MSK) { + ret = regmap_bulk_read(data->regmap, BMI323_ACCEL_X_REG, + &data->buffer.channels, + ARRAY_SIZE(data->buffer.channels)); + if (ret) + return IRQ_NONE; + } else { + for_each_set_bit(bit, indio_dev->active_scan_mask, + BMI323_CHAN_MAX) { + ret = regmap_raw_read(data->regmap, + BMI323_ACCEL_X_REG + bit, + &data->buffer.channels[index++], + BMI323_BYTES_PER_SAMPLE); + if (ret) + return IRQ_NONE; + } + } + + iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int bmi323_set_average(struct bmi323_data *data, + enum bmi323_sensor_type sensor, int avg) +{ + int raw = ARRAY_SIZE(bmi323_accel_gyro_avrg); + + while (raw--) + if (avg == bmi323_accel_gyro_avrg[raw]) + break; + if (raw < 0) + return -EINVAL; + + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, bmi323_hw[sensor].config, + BMI323_ACC_GYRO_CONF_AVG_MSK, + FIELD_PREP(BMI323_ACC_GYRO_CONF_AVG_MSK, + raw)); +} + +static int bmi323_get_average(struct bmi323_data *data, + enum bmi323_sensor_type sensor, int *avg) +{ + int ret, value, raw; + + scoped_guard(mutex, &data->mutex) { + ret = regmap_read(data->regmap, bmi323_hw[sensor].config, &value); + if (ret) + return ret; + } + + raw = FIELD_GET(BMI323_ACC_GYRO_CONF_AVG_MSK, value); + *avg = bmi323_accel_gyro_avrg[raw]; + + return IIO_VAL_INT; +} + +static int bmi323_enable_steps(struct bmi323_data *data, int val) +{ + int ret; + + guard(mutex)(&data->mutex); + if (data->odrhz[BMI323_ACCEL] < 200) { + dev_err(data->dev, "Invalid accelerometer parameter\n"); + return -EINVAL; + } + + ret = bmi323_feature_engine_events(data, BMI323_FEAT_IO0_STP_CNT_MSK, + val ? 1 : 0); + if (ret) + return ret; + + set_mask_bits(&data->feature_events, BMI323_FEAT_IO0_STP_CNT_MSK, + FIELD_PREP(BMI323_FEAT_IO0_STP_CNT_MSK, val ? 1 : 0)); + + return 0; +} + +static int bmi323_read_steps(struct bmi323_data *data, int *val) +{ + int ret; + + guard(mutex)(&data->mutex); + if (!FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK, data->feature_events)) + return -EINVAL; + + ret = regmap_bulk_read(data->regmap, BMI323_FEAT_IO2_REG, + data->steps_count, + ARRAY_SIZE(data->steps_count)); + if (ret) + return ret; + + *val = get_unaligned_le32(data->steps_count); + + return IIO_VAL_INT; +} + +static int bmi323_read_axis(struct bmi323_data *data, + struct iio_chan_spec const *chan, int *val) +{ + enum bmi323_sensor_type sensor; + unsigned int value; + u8 addr; + int ret; + + ret = bmi323_get_error_status(data); + if (ret) + return -EINVAL; + + sensor = bmi323_iio_to_sensor(chan->type); + addr = bmi323_hw[sensor].data + (chan->channel2 - IIO_MOD_X); + + scoped_guard(mutex, &data->mutex) { + ret = regmap_read(data->regmap, addr, &value); + if (ret) + return ret; + } + + *val = sign_extend32(value, chan->scan_type.realbits - 1); + + return IIO_VAL_INT; +} + +static int bmi323_get_temp_data(struct bmi323_data *data, int *val) +{ + unsigned int value; + int ret; + + ret = bmi323_get_error_status(data); + if (ret) + return -EINVAL; + + scoped_guard(mutex, &data->mutex) { + ret = regmap_read(data->regmap, BMI323_TEMP_REG, &value); + if (ret) + return ret; + } + + *val = sign_extend32(value, 15); + + return IIO_VAL_INT; +} + +static int bmi323_get_odr(struct bmi323_data *data, + enum bmi323_sensor_type sensor, int *odr, int *uodr) +{ + int ret, value, odr_raw; + + scoped_guard(mutex, &data->mutex) { + ret = regmap_read(data->regmap, bmi323_hw[sensor].config, &value); + if (ret) + return ret; + } + + odr_raw = FIELD_GET(BMI323_ACC_GYRO_CONF_ODR_MSK, value); + *odr = bmi323_acc_gyro_odr[odr_raw - 1][0]; + *uodr = bmi323_acc_gyro_odr[odr_raw - 1][1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int bmi323_configure_power_mode(struct bmi323_data *data, + enum bmi323_sensor_type sensor, + int odr_index) +{ + enum bmi323_opr_mode mode; + + if (bmi323_acc_gyro_odr[odr_index][0] > 25) + mode = ACC_GYRO_MODE_CONTINOUS; + else + mode = ACC_GYRO_MODE_DUTYCYCLE; + + return bmi323_set_mode(data, sensor, mode); +} + +static int bmi323_set_odr(struct bmi323_data *data, + enum bmi323_sensor_type sensor, int odr, int uodr) +{ + int odr_raw, ret; + + odr_raw = ARRAY_SIZE(bmi323_acc_gyro_odr); + + while (odr_raw--) + if (odr == bmi323_acc_gyro_odr[odr_raw][0] && + uodr == bmi323_acc_gyro_odr[odr_raw][1]) + break; + if (odr_raw < 0) + return -EINVAL; + + ret = bmi323_configure_power_mode(data, sensor, odr_raw); + if (ret) + return -EINVAL; + + guard(mutex)(&data->mutex); + data->odrhz[sensor] = bmi323_acc_gyro_odr[odr_raw][0]; + data->odrns[sensor] = bmi323_acc_gyro_odrns[odr_raw]; + + odr_raw++; + + return regmap_update_bits(data->regmap, bmi323_hw[sensor].config, + BMI323_ACC_GYRO_CONF_ODR_MSK, + FIELD_PREP(BMI323_ACC_GYRO_CONF_ODR_MSK, + odr_raw)); +} + +static int bmi323_get_scale(struct bmi323_data *data, + enum bmi323_sensor_type sensor, int *val2) +{ + int ret, value, scale_raw; + + scoped_guard(mutex, &data->mutex) { + ret = regmap_read(data->regmap, bmi323_hw[sensor].config, + &value); + if (ret) + return ret; + } + + scale_raw = FIELD_GET(BMI323_ACC_GYRO_CONF_SCL_MSK, value); + *val2 = bmi323_hw[sensor].scale_table[scale_raw][1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int bmi323_set_scale(struct bmi323_data *data, + enum bmi323_sensor_type sensor, int val, int val2) +{ + int scale_raw; + + scale_raw = bmi323_hw[sensor].scale_table_len; + + while (scale_raw--) + if (val == bmi323_hw[sensor].scale_table[scale_raw][0] && + val2 == bmi323_hw[sensor].scale_table[scale_raw][1]) + break; + if (scale_raw < 0) + return -EINVAL; + + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, bmi323_hw[sensor].config, + BMI323_ACC_GYRO_CONF_SCL_MSK, + FIELD_PREP(BMI323_ACC_GYRO_CONF_SCL_MSK, + scale_raw)); +} + +static int bmi323_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + enum bmi323_sensor_type sensor; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = (const int *)bmi323_acc_gyro_odr; + *length = ARRAY_SIZE(bmi323_acc_gyro_odr) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + sensor = bmi323_iio_to_sensor(chan->type); + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = (const int *)bmi323_hw[sensor].scale_table; + *length = bmi323_hw[sensor].scale_table_len * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *type = IIO_VAL_INT; + *vals = (const int *)bmi323_accel_gyro_avrg; + *length = ARRAY_SIZE(bmi323_accel_gyro_avrg); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int bmi323_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct bmi323_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = bmi323_set_odr(data, bmi323_iio_to_sensor(chan->type), + val, val2); + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = bmi323_set_scale(data, bmi323_iio_to_sensor(chan->type), + val, val2); + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = bmi323_set_average(data, bmi323_iio_to_sensor(chan->type), + val); + + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_ENABLE: + return bmi323_enable_steps(data, val); + case IIO_CHAN_INFO_PROCESSED: + scoped_guard(mutex, &data->mutex) { + if (val || !FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK, + data->feature_events)) + return -EINVAL; + + /* Clear step counter value */ + ret = bmi323_update_ext_reg(data, BMI323_STEP_SC1_REG, + BMI323_STEP_SC1_RST_CNT_MSK, + FIELD_PREP(BMI323_STEP_SC1_RST_CNT_MSK, + 1)); + } + return ret; + default: + return -EINVAL; + } +} + +static int bmi323_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct bmi323_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + return bmi323_read_steps(data, val); + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_ACCEL: + case IIO_ANGL_VEL: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = bmi323_read_axis(data, chan, val); + + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_TEMP: + return bmi323_get_temp_data(data, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + return bmi323_get_odr(data, bmi323_iio_to_sensor(chan->type), + val, val2); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ACCEL: + case IIO_ANGL_VEL: + *val = 0; + return bmi323_get_scale(data, + bmi323_iio_to_sensor(chan->type), + val2); + case IIO_TEMP: + *val = BMI323_TEMP_SCALE / MEGA; + *val2 = BMI323_TEMP_SCALE % MEGA; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return bmi323_get_average(data, + bmi323_iio_to_sensor(chan->type), + val); + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + *val = BMI323_TEMP_OFFSET; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_ENABLE: + scoped_guard(mutex, &data->mutex) + *val = FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK, + data->feature_events); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info bmi323_info = { + .read_raw = bmi323_read_raw, + .write_raw = bmi323_write_raw, + .read_avail = bmi323_read_avail, + .hwfifo_set_watermark = bmi323_set_watermark, + .write_event_config = bmi323_write_event_config, + .read_event_config = bmi323_read_event_config, + .write_event_value = bmi323_write_event_value, + .read_event_value = bmi323_read_event_value, + .event_attrs = &bmi323_event_attribute_group, +}; + +#define BMI323_SCAN_MASK_ACCEL_3AXIS \ + (BIT(BMI323_ACCEL_X) | BIT(BMI323_ACCEL_Y) | BIT(BMI323_ACCEL_Z)) + +#define BMI323_SCAN_MASK_GYRO_3AXIS \ + (BIT(BMI323_GYRO_X) | BIT(BMI323_GYRO_Y) | BIT(BMI323_GYRO_Z)) + +static const unsigned long bmi323_avail_scan_masks[] = { + /* 3-axis accel */ + BMI323_SCAN_MASK_ACCEL_3AXIS, + /* 3-axis gyro */ + BMI323_SCAN_MASK_GYRO_3AXIS, + /* 3-axis accel + 3-axis gyro */ + BMI323_SCAN_MASK_ACCEL_3AXIS | BMI323_SCAN_MASK_GYRO_3AXIS, + 0 +}; + +static int bmi323_int_pin_config(struct bmi323_data *data, + enum bmi323_irq_pin irq_pin, + bool active_high, bool open_drain, bool latch) +{ + unsigned int mask, field_value; + int ret; + + ret = regmap_update_bits(data->regmap, BMI323_IO_INT_CONF_REG, + BMI323_IO_INT_LTCH_MSK, + FIELD_PREP(BMI323_IO_INT_LTCH_MSK, latch)); + if (ret) + return ret; + + ret = bmi323_update_ext_reg(data, BMI323_GEN_SET1_REG, + BMI323_GEN_HOLD_DUR_MSK, + FIELD_PREP(BMI323_GEN_HOLD_DUR_MSK, 0)); + if (ret) + return ret; + + switch (irq_pin) { + case BMI323_IRQ_INT1: + mask = BMI323_IO_INT1_LVL_OD_OP_MSK; + + field_value = FIELD_PREP(BMI323_IO_INT1_LVL_MSK, active_high) | + FIELD_PREP(BMI323_IO_INT1_OD_MSK, open_drain) | + FIELD_PREP(BMI323_IO_INT1_OP_EN_MSK, 1); + break; + case BMI323_IRQ_INT2: + mask = BMI323_IO_INT2_LVL_OD_OP_MSK; + + field_value = FIELD_PREP(BMI323_IO_INT2_LVL_MSK, active_high) | + FIELD_PREP(BMI323_IO_INT2_OD_MSK, open_drain) | + FIELD_PREP(BMI323_IO_INT2_OP_EN_MSK, 1); + break; + default: + return -EINVAL; + } + + return regmap_update_bits(data->regmap, BMI323_IO_INT_CTR_REG, mask, + field_value); +} + +static int bmi323_trigger_probe(struct bmi323_data *data, + struct iio_dev *indio_dev) +{ + bool open_drain, active_high, latch; + struct fwnode_handle *fwnode; + enum bmi323_irq_pin irq_pin; + int ret, irq, irq_type; + struct irq_data *desc; + + fwnode = dev_fwnode(data->dev); + if (!fwnode) + return -ENODEV; + + irq = fwnode_irq_get_byname(fwnode, "INT1"); + if (irq > 0) { + irq_pin = BMI323_IRQ_INT1; + } else { + irq = fwnode_irq_get_byname(fwnode, "INT2"); + if (irq < 0) + return 0; + + irq_pin = BMI323_IRQ_INT2; + } + + desc = irq_get_irq_data(irq); + if (!desc) + return dev_err_probe(data->dev, -EINVAL, + "Could not find IRQ %d\n", irq); + + irq_type = irqd_get_trigger_type(desc); + switch (irq_type) { + case IRQF_TRIGGER_RISING: + latch = false; + active_high = true; + break; + case IRQF_TRIGGER_HIGH: + latch = true; + active_high = true; + break; + case IRQF_TRIGGER_FALLING: + latch = false; + active_high = false; + break; + case IRQF_TRIGGER_LOW: + latch = true; + active_high = false; + break; + default: + return dev_err_probe(data->dev, -EINVAL, + "Invalid interrupt type 0x%x specified\n", + irq_type); + } + + open_drain = fwnode_property_read_bool(fwnode, "drive-open-drain"); + + ret = bmi323_int_pin_config(data, irq_pin, active_high, open_drain, + latch); + if (ret) + return dev_err_probe(data->dev, ret, + "Failed to configure irq line\n"); + + data->trig = devm_iio_trigger_alloc(data->dev, "%s-trig-%d", + indio_dev->name, irq_pin); + if (!data->trig) + return -ENOMEM; + + data->trig->ops = &bmi323_trigger_ops; + iio_trigger_set_drvdata(data->trig, data); + + ret = devm_request_threaded_irq(data->dev, irq, NULL, + bmi323_irq_thread_handler, + IRQF_ONESHOT, "bmi323-int", indio_dev); + if (ret) + return dev_err_probe(data->dev, ret, "Failed to request IRQ\n"); + + ret = devm_iio_trigger_register(data->dev, data->trig); + if (ret) + return dev_err_probe(data->dev, ret, + "Trigger registration failed\n"); + + data->irq_pin = irq_pin; + + return 0; +} + +static int bmi323_feature_engine_enable(struct bmi323_data *data, bool en) +{ + unsigned int feature_status; + int ret; + + if (!en) + return regmap_write(data->regmap, BMI323_FEAT_CTRL_REG, 0); + + ret = regmap_write(data->regmap, BMI323_FEAT_IO2_REG, 0x012c); + if (ret) + return ret; + + ret = regmap_write(data->regmap, BMI323_FEAT_IO_STATUS_REG, + BMI323_FEAT_IO_STATUS_MSK); + if (ret) + return ret; + + ret = regmap_write(data->regmap, BMI323_FEAT_CTRL_REG, + BMI323_FEAT_ENG_EN_MSK); + if (ret) + return ret; + + /* + * It takes around 4 msec to enable the Feature engine, so check + * the status of the feature engine every 2 msec for a maximum + * of 5 trials. + */ + ret = regmap_read_poll_timeout(data->regmap, BMI323_FEAT_IO1_REG, + feature_status, + FIELD_GET(BMI323_FEAT_IO1_ERR_MSK, + feature_status) == 1, + BMI323_FEAT_ENG_POLL, + BMI323_FEAT_ENG_TIMEOUT); + if (ret) + return dev_err_probe(data->dev, -EINVAL, + "Failed to enable feature engine\n"); + + return 0; +} + +static void bmi323_disable(void *data_ptr) +{ + struct bmi323_data *data = data_ptr; + + bmi323_set_mode(data, BMI323_ACCEL, ACC_GYRO_MODE_DISABLE); + bmi323_set_mode(data, BMI323_GYRO, ACC_GYRO_MODE_DISABLE); +} + +static int bmi323_set_bw(struct bmi323_data *data, + enum bmi323_sensor_type sensor, enum bmi323_3db_bw bw) +{ + return regmap_update_bits(data->regmap, bmi323_hw[sensor].config, + BMI323_ACC_GYRO_CONF_BW_MSK, + FIELD_PREP(BMI323_ACC_GYRO_CONF_BW_MSK, bw)); +} + +static int bmi323_init(struct bmi323_data *data) +{ + int ret, val; + + /* + * Perform soft reset to make sure the device is in a known state after + * start up. A delay of 1.5 ms is required after reset. + * See datasheet section 5.17 "Soft Reset". + */ + ret = regmap_write(data->regmap, BMI323_CMD_REG, BMI323_RST_VAL); + if (ret) + return ret; + + usleep_range(1500, 2000); + + /* + * Dummy read is required to enable SPI interface after reset. + * See datasheet section 7.2.1 "Protocol Selection". + */ + regmap_read(data->regmap, BMI323_CHIP_ID_REG, &val); + + ret = regmap_read(data->regmap, BMI323_STATUS_REG, &val); + if (ret) + return ret; + + if (!FIELD_GET(BMI323_STATUS_POR_MSK, val)) + return dev_err_probe(data->dev, -EINVAL, + "Sensor initialization error\n"); + + ret = regmap_read(data->regmap, BMI323_CHIP_ID_REG, &val); + if (ret) + return ret; + + if (FIELD_GET(BMI323_CHIP_ID_MSK, val) != BMI323_CHIP_ID_VAL) + return dev_err_probe(data->dev, -EINVAL, "Chip ID mismatch\n"); + + ret = bmi323_feature_engine_enable(data, true); + if (ret) + return ret; + + ret = regmap_read(data->regmap, BMI323_ERR_REG, &val); + if (ret) + return ret; + + if (val) + return dev_err_probe(data->dev, -EINVAL, + "Sensor power error = 0x%x\n", val); + + /* + * Set the Bandwidth coefficient which defines the 3 dB cutoff + * frequency in relation to the ODR. + */ + ret = bmi323_set_bw(data, BMI323_ACCEL, BMI323_BW_ODR_BY_2); + if (ret) + return ret; + + ret = bmi323_set_bw(data, BMI323_GYRO, BMI323_BW_ODR_BY_2); + if (ret) + return ret; + + ret = bmi323_set_odr(data, BMI323_ACCEL, 25, 0); + if (ret) + return ret; + + ret = bmi323_set_odr(data, BMI323_GYRO, 25, 0); + if (ret) + return ret; + + return devm_add_action_or_reset(data->dev, bmi323_disable, data); +} + +int bmi323_core_probe(struct device *dev) +{ + static const char * const regulator_names[] = { "vdd", "vddio" }; + struct iio_dev *indio_dev; + struct bmi323_data *data; + struct regmap *regmap; + int ret; + + regmap = dev_get_regmap(dev, NULL); + if (!regmap) + return dev_err_probe(dev, -ENODEV, "Failed to get regmap\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return dev_err_probe(dev, -ENOMEM, + "Failed to allocate device\n"); + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulator_names), + regulator_names); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + data = iio_priv(indio_dev); + data->dev = dev; + data->regmap = regmap; + mutex_init(&data->mutex); + + ret = bmi323_init(data); + if (ret) + return -EINVAL; + + ret = iio_read_mount_matrix(dev, &data->orientation); + if (ret) + return ret; + + indio_dev->name = "bmi323-imu"; + indio_dev->info = &bmi323_info; + indio_dev->channels = bmi323_channels; + indio_dev->num_channels = ARRAY_SIZE(bmi323_channels); + indio_dev->available_scan_masks = bmi323_avail_scan_masks; + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + dev_set_drvdata(data->dev, indio_dev); + + ret = bmi323_trigger_probe(data, indio_dev); + if (ret) + return -EINVAL; + + ret = devm_iio_triggered_buffer_setup_ext(data->dev, indio_dev, + &iio_pollfunc_store_time, + bmi323_trigger_handler, + IIO_BUFFER_DIRECTION_IN, + &bmi323_buffer_ops, + bmi323_fifo_attributes); + if (ret) + return dev_err_probe(data->dev, ret, + "Failed to setup trigger buffer\n"); + + ret = devm_iio_device_register(data->dev, indio_dev); + if (ret) + return dev_err_probe(data->dev, ret, + "Unable to register iio device\n"); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(bmi323_core_probe, IIO_BMI323); + +MODULE_DESCRIPTION("Bosch BMI323 IMU driver"); +MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/bmi323/bmi323_i2c.c b/drivers/iio/imu/bmi323/bmi323_i2c.c new file mode 100644 index 000000000000..20a8001b9956 --- /dev/null +++ b/drivers/iio/imu/bmi323/bmi323_i2c.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * I2C driver for Bosch BMI323 6-Axis IMU. + * + * Copyright (C) 2023, Jagath Jog J <jagathjog1996@gmail.com> + */ + +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "bmi323.h" + +struct bmi323_i2c_priv { + struct i2c_client *i2c; + u8 i2c_rx_buffer[BMI323_FIFO_LENGTH_IN_BYTES + BMI323_I2C_DUMMY]; +}; + +/* + * From BMI323 datasheet section 4: Notes on the Serial Interface Support. + * Each I2C register read operation requires to read two dummy bytes before + * the actual payload. + */ +static int bmi323_regmap_i2c_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, + size_t val_size) +{ + struct bmi323_i2c_priv *priv = context; + struct i2c_msg msgs[2]; + int ret; + + msgs[0].addr = priv->i2c->addr; + msgs[0].flags = priv->i2c->flags; + msgs[0].len = reg_size; + msgs[0].buf = (u8 *)reg_buf; + + msgs[1].addr = priv->i2c->addr; + msgs[1].len = val_size + BMI323_I2C_DUMMY; + msgs[1].buf = priv->i2c_rx_buffer; + msgs[1].flags = priv->i2c->flags | I2C_M_RD; + + ret = i2c_transfer(priv->i2c->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return -EIO; + + memcpy(val_buf, priv->i2c_rx_buffer + BMI323_I2C_DUMMY, val_size); + + return 0; +} + +static int bmi323_regmap_i2c_write(void *context, const void *data, + size_t count) +{ + struct bmi323_i2c_priv *priv = context; + u8 reg; + + reg = *(u8 *)data; + return i2c_smbus_write_i2c_block_data(priv->i2c, reg, + count - sizeof(u8), + data + sizeof(u8)); +} + +static struct regmap_bus bmi323_regmap_bus = { + .read = bmi323_regmap_i2c_read, + .write = bmi323_regmap_i2c_write, +}; + +static const struct regmap_config bmi323_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = BMI323_CFG_RES_REG, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static int bmi323_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct bmi323_i2c_priv *priv; + struct regmap *regmap; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->i2c = i2c; + regmap = devm_regmap_init(dev, &bmi323_regmap_bus, priv, + &bmi323_i2c_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to initialize I2C Regmap\n"); + + return bmi323_core_probe(dev); +} + +static const struct i2c_device_id bmi323_i2c_ids[] = { + { "bmi323" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bmi323_i2c_ids); + +static const struct of_device_id bmi323_of_i2c_match[] = { + { .compatible = "bosch,bmi323" }, + { } +}; +MODULE_DEVICE_TABLE(of, bmi323_of_i2c_match); + +static struct i2c_driver bmi323_i2c_driver = { + .driver = { + .name = "bmi323", + .of_match_table = bmi323_of_i2c_match, + }, + .probe = bmi323_i2c_probe, + .id_table = bmi323_i2c_ids, +}; +module_i2c_driver(bmi323_i2c_driver); + +MODULE_DESCRIPTION("Bosch BMI323 IMU driver"); +MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_BMI323); diff --git a/drivers/iio/imu/bmi323/bmi323_spi.c b/drivers/iio/imu/bmi323/bmi323_spi.c new file mode 100644 index 000000000000..7b1e8127d0dd --- /dev/null +++ b/drivers/iio/imu/bmi323/bmi323_spi.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SPI driver for Bosch BMI323 6-Axis IMU. + * + * Copyright (C) 2023, Jagath Jog J <jagathjog1996@gmail.com> + */ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "bmi323.h" + +/* + * From BMI323 datasheet section 4: Notes on the Serial Interface Support. + * Each SPI register read operation requires to read one dummy byte before + * the actual payload. + */ +static int bmi323_regmap_spi_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, + size_t val_size) +{ + struct spi_device *spi = context; + + return spi_write_then_read(spi, reg_buf, reg_size, val_buf, val_size); +} + +static int bmi323_regmap_spi_write(void *context, const void *data, + size_t count) +{ + struct spi_device *spi = context; + u8 *data_buff = (u8 *)data; + + data_buff[1] = data_buff[0]; + return spi_write(spi, data_buff + 1, count - 1); +} + +static struct regmap_bus bmi323_regmap_bus = { + .read = bmi323_regmap_spi_read, + .write = bmi323_regmap_spi_write, +}; + +static const struct regmap_config bmi323_spi_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .pad_bits = 8, + .read_flag_mask = BIT(7), + .max_register = BMI323_CFG_RES_REG, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static int bmi323_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct regmap *regmap; + + regmap = devm_regmap_init(dev, &bmi323_regmap_bus, dev, + &bmi323_spi_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to initialize SPI Regmap\n"); + + return bmi323_core_probe(dev); +} + +static const struct spi_device_id bmi323_spi_ids[] = { + { "bmi323" }, + { } +}; +MODULE_DEVICE_TABLE(spi, bmi323_spi_ids); + +static const struct of_device_id bmi323_of_spi_match[] = { + { .compatible = "bosch,bmi323" }, + { } +}; +MODULE_DEVICE_TABLE(of, bmi323_of_spi_match); + +static struct spi_driver bmi323_spi_driver = { + .driver = { + .name = "bmi323", + .of_match_table = bmi323_of_spi_match, + }, + .probe = bmi323_spi_probe, + .id_table = bmi323_spi_ids, +}; +module_spi_driver(bmi323_spi_driver); + +MODULE_DESCRIPTION("Bosch BMI323 IMU driver"); +MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_BMI323); diff --git a/drivers/iio/imu/bno055/bno055_ser_core.c b/drivers/iio/imu/bno055/bno055_ser_core.c index 57728a568471..5677bdf4f846 100644 --- a/drivers/iio/imu/bno055/bno055_ser_core.c +++ b/drivers/iio/imu/bno055/bno055_ser_core.c @@ -378,12 +378,12 @@ static void bno055_ser_handle_rx(struct bno055_ser_priv *priv, int status) * Also, we assume to RX one pkt per time (i.e. the HW doesn't send anything * unless we require to AND we don't queue more than one request per time). */ -static int bno055_ser_receive_buf(struct serdev_device *serdev, - const unsigned char *buf, size_t size) +static ssize_t bno055_ser_receive_buf(struct serdev_device *serdev, + const u8 *buf, size_t size) { int status; struct bno055_ser_priv *priv = serdev_device_get_drvdata(serdev); - int remaining = size; + size_t remaining = size; if (size == 0) return 0; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index b1e4fde27d25..f67bd5a39beb 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -137,10 +137,7 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev, out_unlock: mutex_unlock(&st->lock); /* sleep maximum required time */ - if (sleep_accel > sleep_temp) - sleep = sleep_accel; - else - sleep = sleep_temp; + sleep = max(sleep_accel, sleep_temp); if (sleep) msleep(sleep); return ret; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 6ef1df9d60b7..b52f328fd26c 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -424,10 +424,7 @@ out_unlock: mutex_unlock(&st->lock); /* sleep maximum required time */ - if (sleep_sensor > sleep_temp) - sleep = sleep_sensor; - else - sleep = sleep_temp; + sleep = max(sleep_sensor, sleep_temp); if (sleep) msleep(sleep); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index 3bf946e56e1d..3df0a715e885 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -137,10 +137,7 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev, out_unlock: mutex_unlock(&st->lock); /* sleep maximum required time */ - if (sleep_gyro > sleep_temp) - sleep = sleep_gyro; - else - sleep = sleep_temp; + sleep = max(sleep_gyro, sleep_temp); if (sleep) msleep(sleep); return ret; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 6b034dccc3b1..0e94e5335e93 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -567,15 +567,12 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) static int inv_mpu6050_sensor_set(struct inv_mpu6050_state *st, int reg, int axis, int val) { - int ind, result; + int ind; __be16 d = cpu_to_be16(val); ind = (axis - IIO_MOD_X) * 2; - result = regmap_bulk_write(st->map, reg + ind, &d, sizeof(d)); - if (result) - return -EINVAL; - return 0; + return regmap_bulk_write(st->map, reg + ind, &d, sizeof(d)); } static int inv_mpu6050_sensor_show(struct inv_mpu6050_state *st, int reg, @@ -587,7 +584,7 @@ static int inv_mpu6050_sensor_show(struct inv_mpu6050_state *st, int reg, ind = (axis - IIO_MOD_X) * 2; result = regmap_bulk_read(st->map, reg + ind, &d, sizeof(d)); if (result) - return -EINVAL; + return result; *val = (short)be16_to_cpup(&d); return IIO_VAL_INT; diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 176d31d9f9d8..b581a7e80566 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -413,6 +413,22 @@ static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks, { if (bitmap_empty(mask, masklength)) return NULL; + /* + * The condition here do not handle multi-long masks correctly. + * It only checks the first long to be zero, and will use such mask + * as a terminator even if there was bits set after the first long. + * + * Correct check would require using: + * while (!bitmap_empty(av_masks, masklength)) + * instead. This is potentially hazardous because the + * avaliable_scan_masks is a zero terminated array of longs - and + * using the proper bitmap_empty() check for multi-long wide masks + * would require the array to be terminated with multiple zero longs - + * which is not such an usual pattern. + * + * As writing of this no multi-long wide masks were found in-tree, so + * the simple while (*av_masks) check is working. + */ while (*av_masks) { if (strict) { if (bitmap_equal(mask, av_masks, masklength)) @@ -600,7 +616,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, &iio_show_fixed_type, NULL, 0, - 0, + IIO_SEPARATE, &indio_dev->dev, buffer, &buffer->buffer_attr_list); @@ -613,7 +629,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, &iio_scan_el_show, &iio_scan_el_store, chan->scan_index, - 0, + IIO_SEPARATE, &indio_dev->dev, buffer, &buffer->buffer_attr_list); @@ -623,7 +639,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, &iio_scan_el_ts_show, &iio_scan_el_ts_store, chan->scan_index, - 0, + IIO_SEPARATE, &indio_dev->dev, buffer, &buffer->buffer_attr_list); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index c77745b594bd..9a85752124dd 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -117,6 +117,8 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_LIGHT_GREEN] = "green", [IIO_MOD_LIGHT_BLUE] = "blue", [IIO_MOD_LIGHT_UV] = "uv", + [IIO_MOD_LIGHT_UVA] = "uva", + [IIO_MOD_LIGHT_UVB] = "uvb", [IIO_MOD_LIGHT_DUV] = "duv", [IIO_MOD_QUATERNION] = "quaternion", [IIO_MOD_TEMP_AMBIENT] = "ambient", @@ -182,6 +184,7 @@ static const char * const iio_chan_info_postfix[] = { [IIO_CHAN_INFO_THERMOCOUPLE_TYPE] = "thermocouple_type", [IIO_CHAN_INFO_CALIBAMBIENT] = "calibambient", [IIO_CHAN_INFO_ZEROPOINT] = "zeropoint", + [IIO_CHAN_INFO_TROUGH] = "trough_raw", }; /** * iio_device_id() - query the unique ID for the device @@ -1896,6 +1899,66 @@ static int iio_check_extended_name(const struct iio_dev *indio_dev) static const struct iio_buffer_setup_ops noop_ring_setup_ops; +static void iio_sanity_check_avail_scan_masks(struct iio_dev *indio_dev) +{ + unsigned int num_masks, masklength, longs_per_mask; + const unsigned long *av_masks; + int i; + + av_masks = indio_dev->available_scan_masks; + masklength = indio_dev->masklength; + longs_per_mask = BITS_TO_LONGS(masklength); + + /* + * The code determining how many available_scan_masks is in the array + * will be assuming the end of masks when first long with all bits + * zeroed is encountered. This is incorrect for masks where mask + * consists of more than one long, and where some of the available masks + * has long worth of bits zeroed (but has subsequent bit(s) set). This + * is a safety measure against bug where array of masks is terminated by + * a single zero while mask width is greater than width of a long. + */ + if (longs_per_mask > 1) + dev_warn(indio_dev->dev.parent, + "multi long available scan masks not fully supported\n"); + + if (bitmap_empty(av_masks, masklength)) + dev_warn(indio_dev->dev.parent, "empty scan mask\n"); + + for (num_masks = 0; *av_masks; num_masks++) + av_masks += longs_per_mask; + + if (num_masks < 2) + return; + + av_masks = indio_dev->available_scan_masks; + + /* + * Go through all the masks from first to one before the last, and see + * that no mask found later from the available_scan_masks array is a + * subset of mask found earlier. If this happens, then the mask found + * later will never get used because scanning the array is stopped when + * the first suitable mask is found. Drivers should order the array of + * available masks in the order of preference (presumably the least + * costy to access masks first). + */ + for (i = 0; i < num_masks - 1; i++) { + const unsigned long *mask1; + int j; + + mask1 = av_masks + i * longs_per_mask; + for (j = i + 1; j < num_masks; j++) { + const unsigned long *mask2; + + mask2 = av_masks + j * longs_per_mask; + if (bitmap_subset(mask2, mask1, masklength)) + dev_warn(indio_dev->dev.parent, + "available_scan_mask %d subset of %d. Never used\n", + j, i); + } + } +} + int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); @@ -1934,6 +1997,9 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) goto error_unreg_debugfs; } + if (indio_dev->available_scan_masks) + iio_sanity_check_avail_scan_masks(indio_dev); + ret = iio_device_register_sysfs(indio_dev); if (ret) { dev_err(indio_dev->dev.parent, diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 45edba797e4c..143003232d1c 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -252,6 +252,21 @@ config ISL29125 To compile this driver as a module, choose M here: the module will be called isl29125. +config ISL76682 + tristate "Intersil ISL76682 Light Sensor" + depends on I2C + select REGMAP_I2C + help + Say Y here if you want to build a driver for the Intersil ISL76682 + Ambient Light Sensor and IR Intensity sensor. This driver provides + the readouts via standard IIO sysfs and device interface. Both ALS + illuminance and IR illuminance are provided raw with separate scale + setting which can be configured via sysfs, the default scale is 1000 + lux, other options are 4000/16000/64000 lux. + + To compile this driver as a module, choose M here: the module will be + called isl76682. + config HID_SENSOR_ALS depends on HID_SENSOR_HUB select IIO_BUFFER @@ -347,6 +362,17 @@ config SENSORS_LM3533 changes. The ALS-control output values can be set per zone for the three current output channels. +config LTR390 + tristate "LTR-390UV-01 ambient light and UV sensor" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the Lite-On LTR-390UV-01 + ambient light and UV sensor. + + This driver can also be built as a module. If so, the module + will be called ltr390. + config LTR501 tristate "LTR-501ALS-01 light sensor" depends on I2C @@ -637,6 +663,17 @@ config VEML6070 To compile this driver as a module, choose M here: the module will be called veml6070. +config VEML6075 + tristate "VEML6075 UVA and UVB light sensor" + select REGMAP_I2C + depends on I2C + help + Say Y here if you want to build a driver for the Vishay VEML6075 UVA + and UVB light sensor. + + To compile this driver as a module, choose M here: the + module will be called veml6075. + config VL6180 tristate "VL6180 ALS, range and proximity sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index c0db4c4c36ec..2e5fdb33e0e9 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -28,8 +28,10 @@ obj-$(CONFIG_IQS621_ALS) += iqs621-als.o obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o obj-$(CONFIG_ISL29125) += isl29125.o +obj-$(CONFIG_ISL76682) += isl76682.o obj-$(CONFIG_JSA1212) += jsa1212.o obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o +obj-$(CONFIG_LTR390) += ltr390.o obj-$(CONFIG_LTR501) += ltr501.o obj-$(CONFIG_LTRF216A) += ltrf216a.o obj-$(CONFIG_LV0104CS) += lv0104cs.o @@ -60,5 +62,6 @@ obj-$(CONFIG_VCNL4000) += vcnl4000.o obj-$(CONFIG_VCNL4035) += vcnl4035.o obj-$(CONFIG_VEML6030) += veml6030.o obj-$(CONFIG_VEML6070) += veml6070.o +obj-$(CONFIG_VEML6075) += veml6075.o obj-$(CONFIG_VL6180) += vl6180.o obj-$(CONFIG_ZOPT2201) += zopt2201.o diff --git a/drivers/iio/light/isl76682.c b/drivers/iio/light/isl76682.c new file mode 100644 index 000000000000..cf6ddee44ffc --- /dev/null +++ b/drivers/iio/light/isl76682.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IIO driver for the light sensor ISL76682. + * ISL76682 is Ambient Light Sensor + * + * Copyright (c) 2023 Marek Vasut <marex@denx.de> + */ + +#include <linux/array_size.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#include <linux/iio/iio.h> + +#define ISL76682_REG_COMMAND 0x00 + +#define ISL76682_COMMAND_EN BIT(7) +#define ISL76682_COMMAND_MODE_CONTINUOUS BIT(6) +#define ISL76682_COMMAND_LIGHT_IR BIT(5) + +#define ISL76682_COMMAND_RANGE_LUX_1K 0x0 +#define ISL76682_COMMAND_RANGE_LUX_4K 0x1 +#define ISL76682_COMMAND_RANGE_LUX_16K 0x2 +#define ISL76682_COMMAND_RANGE_LUX_64K 0x3 +#define ISL76682_COMMAND_RANGE_LUX_MASK GENMASK(1, 0) + +#define ISL76682_REG_ALSIR_L 0x01 + +#define ISL76682_REG_ALSIR_U 0x02 + +#define ISL76682_NUM_REGS (ISL76682_REG_ALSIR_U + 1) + +#define ISL76682_CONV_TIME_MS 100 +#define ISL76682_INT_TIME_US 90000 + +#define ISL76682_ADC_MAX (BIT(16) - 1) + +struct isl76682_chip { + /* + * Lock to synchronize access to device command register + * and the content of range variable below. + */ + struct mutex lock; + struct regmap *regmap; + u8 range; + u8 command; +}; + +struct isl76682_range { + u8 range; + u32 als; + u32 ir; +}; + +static struct isl76682_range isl76682_range_table[] = { + { ISL76682_COMMAND_RANGE_LUX_1K, 15000, 10500 }, + { ISL76682_COMMAND_RANGE_LUX_4K, 60000, 42000 }, + { ISL76682_COMMAND_RANGE_LUX_16K, 240000, 168000 }, + { ISL76682_COMMAND_RANGE_LUX_64K, 960000, 673000 } +}; + +static int isl76682_get(struct isl76682_chip *chip, bool mode_ir, int *data) +{ + u8 command; + int ret; + + command = ISL76682_COMMAND_EN | ISL76682_COMMAND_MODE_CONTINUOUS | + chip->range; + + if (mode_ir) + command |= ISL76682_COMMAND_LIGHT_IR; + + if (command != chip->command) { + ret = regmap_write(chip->regmap, ISL76682_REG_COMMAND, command); + if (ret) + return ret; + + /* Need to wait for conversion time if ALS/IR mode enabled */ + msleep(ISL76682_CONV_TIME_MS); + + chip->command = command; + } + + ret = regmap_bulk_read(chip->regmap, ISL76682_REG_ALSIR_L, data, 2); + *data &= ISL76682_ADC_MAX; + return ret; +} + +static int isl76682_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct isl76682_chip *chip = iio_priv(indio_dev); + int i; + + if (mask != IIO_CHAN_INFO_SCALE) + return -EINVAL; + + if (val != 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(isl76682_range_table); i++) { + if (chan->type == IIO_LIGHT && val2 != isl76682_range_table[i].als) + continue; + if (chan->type == IIO_INTENSITY && val2 != isl76682_range_table[i].ir) + continue; + + scoped_guard(mutex, &chip->lock) + chip->range = isl76682_range_table[i].range; + return 0; + } + + return -EINVAL; +} + +static int isl76682_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct isl76682_chip *chip = iio_priv(indio_dev); + int ret; + int i; + + guard(mutex)(&chip->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_LIGHT: + ret = isl76682_get(chip, false, val); + return (ret < 0) ? ret : IIO_VAL_INT; + case IIO_INTENSITY: + ret = isl76682_get(chip, true, val); + return (ret < 0) ? ret : IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + for (i = 0; i < ARRAY_SIZE(isl76682_range_table); i++) { + if (chip->range != isl76682_range_table[i].range) + continue; + + *val = 0; + switch (chan->type) { + case IIO_LIGHT: + *val2 = isl76682_range_table[i].als; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_INTENSITY: + *val2 = isl76682_range_table[i].ir; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + } + return -EINVAL; + case IIO_CHAN_INFO_INT_TIME: + *val = 0; + *val2 = ISL76682_INT_TIME_US; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int illuminance_scale_available[] = { + 0, 15000, + 0, 60000, + 0, 240000, + 0, 960000, +}; + +static int intensity_scale_available[] = { + 0, 10500, + 0, 42000, + 0, 168000, + 0, 673000, +}; + +static int isl76682_read_avail(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_SCALE: + switch (chan->type) { + case IIO_LIGHT: + *vals = illuminance_scale_available; + *length = ARRAY_SIZE(illuminance_scale_available); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case IIO_INTENSITY: + *vals = intensity_scale_available; + *length = ARRAY_SIZE(intensity_scale_available); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec isl76682_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + }, { + .type = IIO_INTENSITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + } +}; + +static const struct iio_info isl76682_info = { + .read_avail = isl76682_read_avail, + .read_raw = isl76682_read_raw, + .write_raw = isl76682_write_raw, +}; + +static int isl76682_clear_configure_reg(struct isl76682_chip *chip) +{ + struct device *dev = regmap_get_device(chip->regmap); + int ret; + + ret = regmap_write(chip->regmap, ISL76682_REG_COMMAND, 0x0); + if (ret < 0) + dev_err(dev, "Error %d clearing the CONFIGURE register\n", ret); + + /* + * In the success case, the command register was zeroed out. + * + * In the error case, we do not know in which state the command + * register is, so we assume it is zeroed out, so that it would + * be reprogrammed at the next data read out, and at that time + * we hope it would be reprogrammed successfully. That is very + * much a best effort approach. + */ + chip->command = 0; + + return ret; +} + +static void isl76682_reset_action(void *chip) +{ + isl76682_clear_configure_reg(chip); +} + +static bool isl76682_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ISL76682_REG_ALSIR_L: + case ISL76682_REG_ALSIR_U: + return true; + default: + return false; + } +} + +static const struct regmap_config isl76682_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = isl76682_is_volatile_reg, + .max_register = ISL76682_NUM_REGS - 1, + .num_reg_defaults_raw = ISL76682_NUM_REGS, + .cache_type = REGCACHE_FLAT, +}; + +static int isl76682_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct isl76682_chip *chip; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + + mutex_init(&chip->lock); + + chip->regmap = devm_regmap_init_i2c(client, &isl76682_regmap_config); + ret = PTR_ERR_OR_ZERO(chip->regmap); + if (ret) + return dev_err_probe(dev, ret, "Error initializing regmap\n"); + + chip->range = ISL76682_COMMAND_RANGE_LUX_1K; + + ret = isl76682_clear_configure_reg(chip); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(dev, isl76682_reset_action, chip); + if (ret) + return ret; + + indio_dev->info = &isl76682_info; + indio_dev->channels = isl76682_channels; + indio_dev->num_channels = ARRAY_SIZE(isl76682_channels); + indio_dev->name = "isl76682"; + indio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct i2c_device_id isl76682_id[] = { + { "isl76682" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, isl76682_id); + +static const struct of_device_id isl76682_of_match[] = { + { .compatible = "isil,isl76682" }, + { } +}; +MODULE_DEVICE_TABLE(of, isl76682_of_match); + +static struct i2c_driver isl76682_driver = { + .driver = { + .name = "isl76682", + .of_match_table = isl76682_of_match, + }, + .probe = isl76682_probe, + .id_table = isl76682_id, +}; +module_i2c_driver(isl76682_driver); + +MODULE_DESCRIPTION("ISL76682 Ambient Light Sensor driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c new file mode 100644 index 000000000000..fff1e899097d --- /dev/null +++ b/drivers/iio/light/ltr390.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * IIO driver for Lite-On LTR390 ALS and UV sensor + * (7-bit I2C slave address 0x53) + * + * Based on the work of: + * Shreeya Patel and Shi Zhigang (LTRF216 Driver) + * + * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com> + * + * Datasheet: + * https://optoelectronics.liteon.com/upload/download/DS86-2015-0004/LTR-390UV_Final_%20DS_V1%201.pdf + * + * TODO: + * - Support for configurable gain and resolution + * - Sensor suspend/resume support + * - Add support for reading the ALS + * - Interrupt support + */ + +#include <linux/i2c.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> + +#include <linux/iio/iio.h> + +#include <asm/unaligned.h> + +#define LTR390_MAIN_CTRL 0x00 +#define LTR390_PART_ID 0x06 +#define LTR390_UVS_DATA 0x10 + +#define LTR390_SW_RESET BIT(4) +#define LTR390_UVS_MODE BIT(3) +#define LTR390_SENSOR_ENABLE BIT(1) + +#define LTR390_PART_NUMBER_ID 0xb + +/* + * At 20-bit resolution (integration time: 400ms) and 18x gain, 2300 counts of + * the sensor are equal to 1 UV Index [Datasheet Page#8]. + * + * For the default resolution of 18-bit (integration time: 100ms) and default + * gain of 3x, the counts/uvi are calculated as follows: + * 2300 / ((3/18) * (100/400)) = 95.83 + */ +#define LTR390_COUNTS_PER_UVI 96 + +/* + * Window Factor is needed when the device is under Window glass with coated + * tinted ink. This is to compensate for the light loss due to the lower + * transmission rate of the window glass and helps * in calculating lux. + */ +#define LTR390_WINDOW_FACTOR 1 + +struct ltr390_data { + struct regmap *regmap; + struct i2c_client *client; + /* Protects device from simulataneous reads */ + struct mutex lock; +}; + +static const struct regmap_config ltr390_regmap_config = { + .name = "ltr390", + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, +}; + +static int ltr390_register_read(struct ltr390_data *data, u8 register_address) +{ + struct device *dev = &data->client->dev; + int ret; + u8 recieve_buffer[3]; + + guard(mutex)(&data->lock); + + ret = regmap_bulk_read(data->regmap, register_address, recieve_buffer, + sizeof(recieve_buffer)); + if (ret) { + dev_err(dev, "failed to read measurement data"); + return ret; + } + + return get_unaligned_le24(recieve_buffer); +} + +static int ltr390_read_raw(struct iio_dev *iio_device, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + struct ltr390_data *data = iio_priv(iio_device); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = ltr390_register_read(data, LTR390_UVS_DATA); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = LTR390_WINDOW_FACTOR; + *val2 = LTR390_COUNTS_PER_UVI; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static const struct iio_info ltr390_info = { + .read_raw = ltr390_read_raw, +}; + +static const struct iio_chan_spec ltr390_channel = { + .type = IIO_UVINDEX, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) +}; + +static int ltr390_probe(struct i2c_client *client) +{ + struct ltr390_data *data; + struct iio_dev *indio_dev; + struct device *dev; + int ret, part_number; + + dev = &client->dev; + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + + data->regmap = devm_regmap_init_i2c(client, <r390_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "regmap initialization failed\n"); + + data->client = client; + mutex_init(&data->lock); + + indio_dev->info = <r390_info; + indio_dev->channels = <r390_channel; + indio_dev->num_channels = 1; + indio_dev->name = "ltr390"; + + ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number); + if (ret) + return dev_err_probe(dev, ret, + "failed to get sensor's part id\n"); + /* Lower 4 bits of `part_number` change with hardware revisions */ + if (part_number >> 4 != LTR390_PART_NUMBER_ID) + dev_info(dev, "received invalid product id: 0x%x", part_number); + dev_dbg(dev, "LTR390, product id: 0x%x\n", part_number); + + /* reset sensor, chip fails to respond to this, so ignore any errors */ + regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SW_RESET); + + /* Wait for the registers to reset before proceeding */ + usleep_range(1000, 2000); + + ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, + LTR390_SENSOR_ENABLE | LTR390_UVS_MODE); + if (ret) + return dev_err_probe(dev, ret, "failed to enable the sensor\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct i2c_device_id ltr390_id[] = { + { "ltr390" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, ltr390_id); + +static const struct of_device_id ltr390_of_table[] = { + { .compatible = "liteon,ltr390" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ltr390_of_table); + +static struct i2c_driver ltr390_driver = { + .driver = { + .name = "ltr390", + .of_match_table = ltr390_of_table, + }, + .probe = ltr390_probe, + .id_table = ltr390_id, +}; +module_i2c_driver(ltr390_driver); + +MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>"); +MODULE_DESCRIPTION("Lite-On LTR390 ALS and UV sensor Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/ltrf216a.c b/drivers/iio/light/ltrf216a.c index 8de4dd849936..68dc48420a88 100644 --- a/drivers/iio/light/ltrf216a.c +++ b/drivers/iio/light/ltrf216a.c @@ -234,7 +234,7 @@ static int ltrf216a_read_data(struct ltrf216a_data *data, u8 addr) static int ltrf216a_get_lux(struct ltrf216a_data *data) { int ret, greendata; - u64 lux, div; + u64 lux; ret = ltrf216a_set_power_state(data, true); if (ret) @@ -246,10 +246,9 @@ static int ltrf216a_get_lux(struct ltrf216a_data *data) ltrf216a_set_power_state(data, false); - lux = greendata * 45 * LTRF216A_WIN_FAC * 100; - div = data->als_gain_fac * data->int_time_fac * 100; + lux = greendata * 45 * LTRF216A_WIN_FAC; - return div_u64(lux, div); + return lux; } static int ltrf216a_read_raw(struct iio_dev *indio_dev, @@ -279,7 +278,8 @@ static int ltrf216a_read_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; *val = ret; - return IIO_VAL_INT; + *val2 = data->als_gain_fac * data->int_time_fac; + return IIO_VAL_FRACTIONAL; case IIO_CHAN_INFO_INT_TIME: mutex_lock(&data->lock); ret = ltrf216a_get_int_time(data, val, val2); diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c index ed241598aefb..636432c45651 100644 --- a/drivers/iio/light/pa12203001.c +++ b/drivers/iio/light/pa12203001.c @@ -472,7 +472,7 @@ static struct i2c_driver pa12203001_driver = { .driver = { .name = PA12203001_DRIVER_NAME, .pm = &pa12203001_pm_ops, - .acpi_match_table = ACPI_PTR(pa12203001_acpi_match), + .acpi_match_table = pa12203001_acpi_match, }, .probe = pa12203001_probe, .remove = pa12203001_remove, diff --git a/drivers/iio/light/rohm-bu27008.c b/drivers/iio/light/rohm-bu27008.c index 6a6d77805091..0f010eff1981 100644 --- a/drivers/iio/light/rohm-bu27008.c +++ b/drivers/iio/light/rohm-bu27008.c @@ -130,6 +130,7 @@ * @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 { @@ -138,6 +139,7 @@ enum bu27008_chan_type { BU27008_BLUE, BU27008_CLEAR, BU27008_IR, + BU27008_LUX, BU27008_NUM_CHANS }; @@ -172,6 +174,8 @@ static const unsigned long bu27008_scan_masks[] = { 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 }; @@ -331,6 +335,19 @@ static const struct iio_chan_spec bu27008_channels[] = { * 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), }; @@ -1004,6 +1021,169 @@ static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev, 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) @@ -1018,7 +1198,10 @@ static int bu27008_read_raw(struct iio_dev *idev, return -EBUSY; mutex_lock(&data->mutex); - ret = bu27008_read_one(data, idev, chan, val, val2); + 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); @@ -1026,6 +1209,11 @@ static int bu27008_read_raw(struct iio_dev *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) @@ -1236,10 +1424,7 @@ 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 { - __le16 chan[BU27008_NUM_HW_CHANS]; - s64 ts __aligned(8); - } raw; + struct bu27008_buf raw; int ret, dummy; memset(&raw, 0, sizeof(raw)); @@ -1257,6 +1442,12 @@ static irqreturn_t bu27008_trigger_handler(int irq, void *p) 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); diff --git a/drivers/iio/light/veml6075.c b/drivers/iio/light/veml6075.c new file mode 100644 index 000000000000..05d4c0e9015d --- /dev/null +++ b/drivers/iio/light/veml6075.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Vishay VEML6075 UVA and UVB light sensor + * + * Copyright 2023 Javier Carrasco <javier.carrasco.cruz@gmail.com> + * + * 7-bit I2C slave, address 0x10 + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/units.h> + +#include <linux/iio/iio.h> + +#define VEML6075_CMD_CONF 0x00 /* configuration register */ +#define VEML6075_CMD_UVA 0x07 /* UVA channel */ +#define VEML6075_CMD_UVB 0x09 /* UVB channel */ +#define VEML6075_CMD_COMP1 0x0A /* visible light compensation */ +#define VEML6075_CMD_COMP2 0x0B /* infrarred light compensation */ +#define VEML6075_CMD_ID 0x0C /* device ID */ + +#define VEML6075_CONF_IT GENMASK(6, 4) /* intregration time */ +#define VEML6075_CONF_HD BIT(3) /* dynamic setting */ +#define VEML6075_CONF_TRIG BIT(2) /* trigger */ +#define VEML6075_CONF_AF BIT(1) /* active force enable */ +#define VEML6075_CONF_SD BIT(0) /* shutdown */ + +#define VEML6075_IT_50_MS 0x00 +#define VEML6075_IT_100_MS 0x01 +#define VEML6075_IT_200_MS 0x02 +#define VEML6075_IT_400_MS 0x03 +#define VEML6075_IT_800_MS 0x04 + +#define VEML6075_AF_DISABLE 0x00 +#define VEML6075_AF_ENABLE 0x01 + +#define VEML6075_SD_DISABLE 0x00 +#define VEML6075_SD_ENABLE 0x01 + +/* Open-air coefficients and responsivity */ +#define VEML6075_A_COEF 2220 +#define VEML6075_B_COEF 1330 +#define VEML6075_C_COEF 2950 +#define VEML6075_D_COEF 1740 +#define VEML6075_UVA_RESP 1461 +#define VEML6075_UVB_RESP 2591 + +static const int veml6075_it_ms[] = { 50, 100, 200, 400, 800 }; + +struct veml6075_data { + struct i2c_client *client; + struct regmap *regmap; + /* + * prevent integration time modification and triggering + * measurements while a measurement is underway. + */ + struct mutex lock; +}; + +/* channel number */ +enum veml6075_chan { + CH_UVA, + CH_UVB, +}; + +static const struct iio_chan_spec veml6075_channels[] = { + { + .type = IIO_INTENSITY, + .channel = CH_UVA, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_UVA, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), + }, + { + .type = IIO_INTENSITY, + .channel = CH_UVB, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_UVB, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), + }, + { + .type = IIO_UVINDEX, + .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), + }, +}; + +static int veml6075_request_measurement(struct veml6075_data *data) +{ + int ret, conf, int_time; + + ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf); + if (ret < 0) + return ret; + + /* disable shutdown and trigger measurement */ + ret = regmap_write(data->regmap, VEML6075_CMD_CONF, + (conf | VEML6075_CONF_TRIG) & ~VEML6075_CONF_SD); + if (ret < 0) + return ret; + + /* + * A measurement requires between 1.30 and 1.40 times the integration + * time for all possible configurations. Using a 1.50 factor simplifies + * operations and ensures reliability under all circumstances. + */ + int_time = veml6075_it_ms[FIELD_GET(VEML6075_CONF_IT, conf)]; + msleep(int_time + (int_time / 2)); + + /* shutdown again, data registers are still accessible */ + return regmap_update_bits(data->regmap, VEML6075_CMD_CONF, + VEML6075_CONF_SD, VEML6075_CONF_SD); +} + +static int veml6075_uva_comp(int raw_uva, int comp1, int comp2) +{ + int comp1a_c, comp2a_c, uva_comp; + + comp1a_c = (comp1 * VEML6075_A_COEF) / 1000U; + comp2a_c = (comp2 * VEML6075_B_COEF) / 1000U; + uva_comp = raw_uva - comp1a_c - comp2a_c; + + return clamp_val(uva_comp, 0, U16_MAX); +} + +static int veml6075_uvb_comp(int raw_uvb, int comp1, int comp2) +{ + int comp1b_c, comp2b_c, uvb_comp; + + comp1b_c = (comp1 * VEML6075_C_COEF) / 1000U; + comp2b_c = (comp2 * VEML6075_D_COEF) / 1000U; + uvb_comp = raw_uvb - comp1b_c - comp2b_c; + + return clamp_val(uvb_comp, 0, U16_MAX); +} + +static int veml6075_read_comp(struct veml6075_data *data, int *c1, int *c2) +{ + int ret; + + ret = regmap_read(data->regmap, VEML6075_CMD_COMP1, c1); + if (ret < 0) + return ret; + + return regmap_read(data->regmap, VEML6075_CMD_COMP2, c2); +} + +static int veml6075_read_uv_direct(struct veml6075_data *data, int chan, + int *val) +{ + int c1, c2, ret; + + guard(mutex)(&data->lock); + + ret = veml6075_request_measurement(data); + if (ret < 0) + return ret; + + ret = veml6075_read_comp(data, &c1, &c2); + if (ret < 0) + return ret; + + switch (chan) { + case CH_UVA: + ret = regmap_read(data->regmap, VEML6075_CMD_UVA, val); + if (ret < 0) + return ret; + + *val = veml6075_uva_comp(*val, c1, c2); + return IIO_VAL_INT; + case CH_UVB: + ret = regmap_read(data->regmap, VEML6075_CMD_UVB, val); + if (ret < 0) + return ret; + + *val = veml6075_uvb_comp(*val, c1, c2); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int veml6075_read_int_time_index(struct veml6075_data *data) +{ + int ret, conf; + + ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf); + if (ret < 0) + return ret; + + return FIELD_GET(VEML6075_CONF_IT, conf); +} + +static int veml6075_read_int_time_ms(struct veml6075_data *data, int *val) +{ + int int_index; + + guard(mutex)(&data->lock); + int_index = veml6075_read_int_time_index(data); + if (int_index < 0) + return int_index; + + *val = veml6075_it_ms[int_index]; + + return IIO_VAL_INT; +} + +static int veml6075_get_uvi_micro(struct veml6075_data *data, int uva_comp, + int uvb_comp) +{ + int uvia_micro = uva_comp * VEML6075_UVA_RESP; + int uvib_micro = uvb_comp * VEML6075_UVB_RESP; + int int_index; + + int_index = veml6075_read_int_time_index(data); + if (int_index < 0) + return int_index; + + switch (int_index) { + case VEML6075_IT_50_MS: + return uvia_micro + uvib_micro; + case VEML6075_IT_100_MS: + case VEML6075_IT_200_MS: + case VEML6075_IT_400_MS: + case VEML6075_IT_800_MS: + return (uvia_micro + uvib_micro) / (2 << int_index); + default: + return -EINVAL; + } +} + +static int veml6075_read_uvi(struct veml6075_data *data, int *val, int *val2) +{ + int ret, c1, c2, uva, uvb, uvi_micro; + + guard(mutex)(&data->lock); + + ret = veml6075_request_measurement(data); + if (ret < 0) + return ret; + + ret = veml6075_read_comp(data, &c1, &c2); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, VEML6075_CMD_UVA, &uva); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, VEML6075_CMD_UVB, &uvb); + if (ret < 0) + return ret; + + uvi_micro = veml6075_get_uvi_micro(data, veml6075_uva_comp(uva, c1, c2), + veml6075_uvb_comp(uvb, c1, c2)); + if (uvi_micro < 0) + return uvi_micro; + + *val = uvi_micro / MICRO; + *val2 = uvi_micro % MICRO; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int veml6075_read_responsivity(int chan, int *val, int *val2) +{ + /* scale = 1 / resp */ + switch (chan) { + case CH_UVA: + /* resp = 0.93 c/uW/cm2: scale = 1.75268817 */ + *val = 1; + *val2 = 75268817; + return IIO_VAL_INT_PLUS_NANO; + case CH_UVB: + /* resp = 2.1 c/uW/cm2: scale = 0.476190476 */ + *val = 0; + *val2 = 476190476; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int veml6075_read_avail(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(veml6075_it_ms); + *vals = veml6075_it_ms; + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + + default: + return -EINVAL; + } +} + +static int veml6075_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct veml6075_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return veml6075_read_uv_direct(data, chan->channel, val); + case IIO_CHAN_INFO_PROCESSED: + return veml6075_read_uvi(data, val, val2); + case IIO_CHAN_INFO_INT_TIME: + return veml6075_read_int_time_ms(data, val); + case IIO_CHAN_INFO_SCALE: + return veml6075_read_responsivity(chan->channel, val, val2); + default: + return -EINVAL; + } +} + +static int veml6075_write_int_time_ms(struct veml6075_data *data, int val) +{ + int i = ARRAY_SIZE(veml6075_it_ms); + + guard(mutex)(&data->lock); + + while (i-- > 0) { + if (val == veml6075_it_ms[i]) + break; + } + if (i < 0) + return -EINVAL; + + return regmap_update_bits(data->regmap, VEML6075_CMD_CONF, + VEML6075_CONF_IT, + FIELD_PREP(VEML6075_CONF_IT, i)); +} + +static int veml6075_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct veml6075_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return veml6075_write_int_time_ms(data, val); + default: + return -EINVAL; + } +} + +static const struct iio_info veml6075_info = { + .read_avail = veml6075_read_avail, + .read_raw = veml6075_read_raw, + .write_raw = veml6075_write_raw, +}; + +static bool veml6075_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case VEML6075_CMD_CONF: + case VEML6075_CMD_UVA: + case VEML6075_CMD_UVB: + case VEML6075_CMD_COMP1: + case VEML6075_CMD_COMP2: + case VEML6075_CMD_ID: + return true; + default: + return false; + } +} + +static bool veml6075_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case VEML6075_CMD_CONF: + return true; + default: + return false; + } +} + +static const struct regmap_config veml6075_regmap_config = { + .name = "veml6075", + .reg_bits = 8, + .val_bits = 16, + .max_register = VEML6075_CMD_ID, + .readable_reg = veml6075_readable_reg, + .writeable_reg = veml6075_writable_reg, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static int veml6075_probe(struct i2c_client *client) +{ + struct veml6075_data *data; + struct iio_dev *indio_dev; + struct regmap *regmap; + int config, ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(client, &veml6075_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + data = iio_priv(indio_dev); + data->client = client; + data->regmap = regmap; + + mutex_init(&data->lock); + + indio_dev->name = "veml6075"; + indio_dev->info = &veml6075_info; + indio_dev->channels = veml6075_channels; + indio_dev->num_channels = ARRAY_SIZE(veml6075_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = devm_regulator_get_enable(&client->dev, "vdd"); + if (ret < 0) + return ret; + + /* default: 100ms integration time, active force enable, shutdown */ + config = FIELD_PREP(VEML6075_CONF_IT, VEML6075_IT_100_MS) | + FIELD_PREP(VEML6075_CONF_AF, VEML6075_AF_ENABLE) | + FIELD_PREP(VEML6075_CONF_SD, VEML6075_SD_ENABLE); + ret = regmap_write(data->regmap, VEML6075_CMD_CONF, config); + if (ret < 0) + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id veml6075_id[] = { + { "veml6075" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, veml6075_id); + +static const struct of_device_id veml6075_of_match[] = { + { .compatible = "vishay,veml6075" }, + {} +}; +MODULE_DEVICE_TABLE(of, veml6075_of_match); + +static struct i2c_driver veml6075_driver = { + .driver = { + .name = "veml6075", + .of_match_table = veml6075_of_match, + }, + .probe = veml6075_probe, + .id_table = veml6075_id, +}; + +module_i2c_driver(veml6075_driver); + +MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>"); +MODULE_DESCRIPTION("Vishay VEML6075 UVA and UVB light sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/magnetometer/tmag5273.c b/drivers/iio/magnetometer/tmag5273.c index e8c4ca142d21..218b1ce076c1 100644 --- a/drivers/iio/magnetometer/tmag5273.c +++ b/drivers/iio/magnetometer/tmag5273.c @@ -497,17 +497,13 @@ static int tmag5273_set_operating_mode(struct tmag5273_data *data, static void tmag5273_read_device_property(struct tmag5273_data *data) { struct device *dev = data->dev; - const char *str; int ret; data->angle_measurement = TMAG5273_ANGLE_EN_X_Y; - ret = device_property_read_string(dev, "ti,angle-measurement", &str); - if (ret) - return; - - ret = match_string(tmag5273_angle_names, - ARRAY_SIZE(tmag5273_angle_names), str); + ret = device_property_match_property_string(dev, "ti,angle-measurement", + tmag5273_angle_names, + ARRAY_SIZE(tmag5273_angle_names)); if (ret >= 0) data->angle_measurement = ret; } diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index 95efa32e4289..79adfd059c3a 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -109,6 +109,28 @@ config HP03 To compile this driver as a module, choose M here: the module will be called hp03. +config HSC030PA + tristate "Honeywell HSC/SSC TruStability pressure sensor series" + depends on (I2C || SPI_MASTER) + select HSC030PA_I2C if I2C + select HSC030PA_SPI if SPI_MASTER + help + Say Y here to build support for the Honeywell TruStability + HSC and SSC pressure and temperature sensor series. + + To compile this driver as a module, choose M here: the module + will be called hsc030pa. + +config HSC030PA_I2C + tristate + depends on HSC030PA + depends on I2C + +config HSC030PA_SPI + tristate + depends on HSC030PA + depends on SPI_MASTER + config ICP10100 tristate "InvenSense ICP-101xx pressure and temperature sensor" depends on I2C diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index 436aec7e65f3..b0f8b94662f2 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -15,6 +15,9 @@ obj-$(CONFIG_DPS310) += dps310.o obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o obj-$(CONFIG_HP03) += hp03.o +obj-$(CONFIG_HSC030PA) += hsc030pa.o +obj-$(CONFIG_HSC030PA_I2C) += hsc030pa_i2c.o +obj-$(CONFIG_HSC030PA_SPI) += hsc030pa_spi.o obj-$(CONFIG_ICP10100) += icp10100.o obj-$(CONFIG_MPL115) += mpl115.o obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index a2ef1373a274..fe8734468ed3 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -13,6 +13,7 @@ * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf + * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp581-ds004.pdf * * Notice: @@ -794,10 +795,12 @@ static int bmp280_chip_config(struct bmp280_data *data) } static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 }; +static const u8 bmp280_chip_ids[] = { BMP280_CHIP_ID }; const struct bmp280_chip_info bmp280_chip_info = { .id_reg = BMP280_REG_ID, - .chip_id = BMP280_CHIP_ID, + .chip_id = bmp280_chip_ids, + .num_chip_id = ARRAY_SIZE(bmp280_chip_ids), .regmap_config = &bmp280_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, @@ -846,9 +849,12 @@ static int bme280_chip_config(struct bmp280_data *data) return bmp280_chip_config(data); } +static const u8 bme280_chip_ids[] = { BME280_CHIP_ID }; + const struct bmp280_chip_info bme280_chip_info = { .id_reg = BMP280_REG_ID, - .chip_id = BME280_CHIP_ID, + .chip_id = bme280_chip_ids, + .num_chip_id = ARRAY_SIZE(bme280_chip_ids), .regmap_config = &bmp280_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, @@ -920,7 +926,7 @@ static int bmp380_cmd(struct bmp280_data *data, u8 cmd) } /* - * Returns temperature in Celsius dregrees, resolution is 0.01º C. Output value of + * Returns temperature in Celsius degrees, resolution is 0.01º C. Output value of * "5123" equals 51.2º C. t_fine carries fine temperature as global value. * * Taken from datasheet, Section Appendix 9, "Compensation formula" and repo @@ -1220,10 +1226,12 @@ static int bmp380_chip_config(struct bmp280_data *data) static const int bmp380_oversampling_avail[] = { 1, 2, 4, 8, 16, 32 }; static const int bmp380_iir_filter_coeffs_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128}; +static const u8 bmp380_chip_ids[] = { BMP380_CHIP_ID, BMP390_CHIP_ID }; const struct bmp280_chip_info bmp380_chip_info = { .id_reg = BMP380_REG_ID, - .chip_id = BMP380_CHIP_ID, + .chip_id = bmp380_chip_ids, + .num_chip_id = ARRAY_SIZE(bmp380_chip_ids), .regmap_config = &bmp380_regmap_config, .start_up_time = 2000, .channels = bmp380_channels, @@ -1385,7 +1393,7 @@ static int bmp580_read_temp(struct bmp280_data *data, int *val, int *val2) /* * Temperature is returned in Celsius degrees in fractional - * form down 2^16. We reescale by x1000 to return milli Celsius + * form down 2^16. We rescale by x1000 to return milli Celsius * to respect IIO ABI. */ *val = raw_temp * 1000; @@ -1412,7 +1420,7 @@ static int bmp580_read_press(struct bmp280_data *data, int *val, int *val2) } /* * Pressure is returned in Pascals in fractional form down 2^16. - * We reescale /1000 to convert to kilopascal to respect IIO ABI. + * We rescale /1000 to convert to kilopascal to respect IIO ABI. */ *val = raw_press; *val2 = 64000; /* 2^6 * 1000 */ @@ -1720,10 +1728,12 @@ static int bmp580_chip_config(struct bmp280_data *data) } static const int bmp580_oversampling_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; +static const u8 bmp580_chip_ids[] = { BMP580_CHIP_ID, BMP580_CHIP_ID_ALT }; const struct bmp280_chip_info bmp580_chip_info = { .id_reg = BMP580_REG_CHIP_ID, - .chip_id = BMP580_CHIP_ID, + .chip_id = bmp580_chip_ids, + .num_chip_id = ARRAY_SIZE(bmp580_chip_ids), .regmap_config = &bmp580_regmap_config, .start_up_time = 2000, .channels = bmp380_channels, @@ -1983,10 +1993,12 @@ static int bmp180_chip_config(struct bmp280_data *data) static const int bmp180_oversampling_temp_avail[] = { 1 }; static const int bmp180_oversampling_press_avail[] = { 1, 2, 4, 8 }; +static const u8 bmp180_chip_ids[] = { BMP180_CHIP_ID }; const struct bmp280_chip_info bmp180_chip_info = { .id_reg = BMP280_REG_ID, - .chip_id = BMP180_CHIP_ID, + .chip_id = bmp180_chip_ids, + .num_chip_id = ARRAY_SIZE(bmp180_chip_ids), .regmap_config = &bmp180_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, @@ -2077,6 +2089,7 @@ int bmp280_common_probe(struct device *dev, struct bmp280_data *data; struct gpio_desc *gpiod; unsigned int chip_id; + unsigned int i; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); @@ -2142,12 +2155,17 @@ int bmp280_common_probe(struct device *dev, ret = regmap_read(regmap, data->chip_info->id_reg, &chip_id); if (ret < 0) return ret; - if (chip_id != data->chip_info->chip_id) { - dev_err(dev, "bad chip id: expected %x got %x\n", - data->chip_info->chip_id, chip_id); - return -EINVAL; + + for (i = 0; i < data->chip_info->num_chip_id; i++) { + if (chip_id == data->chip_info->chip_id[i]) { + dev_info(dev, "0x%x is a known chip id for %s\n", chip_id, name); + break; + } } + if (i == data->chip_info->num_chip_id) + dev_warn(dev, "bad chip id: 0x%x is not a known chip id\n", chip_id); + if (data->chip_info->preinit) { ret = data->chip_info->preinit(data); if (ret) diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index dbe630ad05b5..34e3bc758493 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -7,13 +7,11 @@ static int bmp280_i2c_probe(struct i2c_client *client) { - struct regmap *regmap; - const struct bmp280_chip_info *chip_info; const struct i2c_device_id *id = i2c_client_get_device_id(client); + const struct bmp280_chip_info *chip_info; + struct regmap *regmap; - chip_info = device_get_match_data(&client->dev); - if (!chip_info) - chip_info = (const struct bmp280_chip_info *) id->driver_data; + chip_info = i2c_get_match_data(client); regmap = devm_regmap_init_i2c(client, chip_info->regmap_config); if (IS_ERR(regmap)) { diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 1dff9bb7c4e9..433d6fac83c4 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -14,8 +14,7 @@ static int bmp280_regmap_spi_write(void *context, const void *data, size_t count) { - struct device *dev = context; - struct spi_device *spi = to_spi_device(dev); + struct spi_device *spi = to_spi_device(context); u8 buf[2]; memcpy(buf, data, 2); @@ -31,8 +30,7 @@ static int bmp280_regmap_spi_write(void *context, const void *data, static int bmp280_regmap_spi_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) { - struct device *dev = context; - struct spi_device *spi = to_spi_device(dev); + struct spi_device *spi = to_spi_device(context); return spi_write_then_read(spi, reg, reg_size, val, val_size); } @@ -58,9 +56,7 @@ static int bmp280_spi_probe(struct spi_device *spi) return ret; } - chip_info = device_get_match_data(&spi->dev); - if (!chip_info) - chip_info = (const struct bmp280_chip_info *) id->driver_data; + chip_info = spi_get_device_match_data(spi); regmap = devm_regmap_init(&spi->dev, &bmp280_regmap_bus, diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index 5c0563ce7572..4012387d7956 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -292,6 +292,7 @@ #define BMP580_CHIP_ID_ALT 0x51 #define BMP180_CHIP_ID 0x55 #define BMP280_CHIP_ID 0x58 +#define BMP390_CHIP_ID 0x60 #define BME280_CHIP_ID 0x60 #define BMP280_SOFT_RESET_VAL 0xB6 @@ -410,7 +411,7 @@ struct bmp280_data { __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT]; - /* Miscellaneous, endianess-aware data buffers */ + /* Miscellaneous, endianness-aware data buffers */ __le16 le16; __be16 be16; } __aligned(IIO_DMA_MINALIGN); @@ -418,7 +419,8 @@ struct bmp280_data { struct bmp280_chip_info { unsigned int id_reg; - const unsigned int chip_id; + const u8 *chip_id; + int num_chip_id; const struct regmap_config *regmap_config; diff --git a/drivers/iio/pressure/hsc030pa.c b/drivers/iio/pressure/hsc030pa.c new file mode 100644 index 000000000000..d6a51f0c335f --- /dev/null +++ b/drivers/iio/pressure/hsc030pa.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Honeywell TruStability HSC Series pressure/temperature sensor + * + * Copyright (c) 2023 Petre Rodan <petre.rodan@subdimension.ro> + * + * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/trustability-hsc-series/documents/sps-siot-trustability-hsc-series-high-accuracy-board-mount-pressure-sensors-50099148-a-en-ciid-151133.pdf + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/init.h> +#include <linux/math64.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/printk.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#include <asm/unaligned.h> + +#include "hsc030pa.h" + +/* + * HSC_PRESSURE_TRIPLET_LEN - length for the string that defines the + * pressure range, measurement unit and type as per the part nomenclature. + * Consult honeywell,pressure-triplet in the bindings file for details. + */ +#define HSC_PRESSURE_TRIPLET_LEN 6 +#define HSC_STATUS_MASK GENMASK(7, 6) +#define HSC_TEMPERATURE_MASK GENMASK(15, 5) +#define HSC_PRESSURE_MASK GENMASK(29, 16) + +struct hsc_func_spec { + u32 output_min; + u32 output_max; +}; + +/* + * function A: 10% - 90% of 2^14 + * function B: 5% - 95% of 2^14 + * function C: 5% - 85% of 2^14 + * function F: 4% - 94% of 2^14 + */ +static const struct hsc_func_spec hsc_func_spec[] = { + [HSC_FUNCTION_A] = { .output_min = 1638, .output_max = 14746 }, + [HSC_FUNCTION_B] = { .output_min = 819, .output_max = 15565 }, + [HSC_FUNCTION_C] = { .output_min = 819, .output_max = 13926 }, + [HSC_FUNCTION_F] = { .output_min = 655, .output_max = 15401 }, +}; + +enum hsc_variants { + HSC001BA = 0x00, HSC1_6BA = 0x01, HSC2_5BA = 0x02, HSC004BA = 0x03, + HSC006BA = 0x04, HSC010BA = 0x05, HSC1_6MD = 0x06, HSC2_5MD = 0x07, + HSC004MD = 0x08, HSC006MD = 0x09, HSC010MD = 0x0a, HSC016MD = 0x0b, + HSC025MD = 0x0c, HSC040MD = 0x0d, HSC060MD = 0x0e, HSC100MD = 0x0f, + HSC160MD = 0x10, HSC250MD = 0x11, HSC400MD = 0x12, HSC600MD = 0x13, + HSC001BD = 0x14, HSC1_6BD = 0x15, HSC2_5BD = 0x16, HSC004BD = 0x17, + HSC2_5MG = 0x18, HSC004MG = 0x19, HSC006MG = 0x1a, HSC010MG = 0x1b, + HSC016MG = 0x1c, HSC025MG = 0x1d, HSC040MG = 0x1e, HSC060MG = 0x1f, + HSC100MG = 0x20, HSC160MG = 0x21, HSC250MG = 0x22, HSC400MG = 0x23, + HSC600MG = 0x24, HSC001BG = 0x25, HSC1_6BG = 0x26, HSC2_5BG = 0x27, + HSC004BG = 0x28, HSC006BG = 0x29, HSC010BG = 0x2a, HSC100KA = 0x2b, + HSC160KA = 0x2c, HSC250KA = 0x2d, HSC400KA = 0x2e, HSC600KA = 0x2f, + HSC001GA = 0x30, HSC160LD = 0x31, HSC250LD = 0x32, HSC400LD = 0x33, + HSC600LD = 0x34, HSC001KD = 0x35, HSC1_6KD = 0x36, HSC2_5KD = 0x37, + HSC004KD = 0x38, HSC006KD = 0x39, HSC010KD = 0x3a, HSC016KD = 0x3b, + HSC025KD = 0x3c, HSC040KD = 0x3d, HSC060KD = 0x3e, HSC100KD = 0x3f, + HSC160KD = 0x40, HSC250KD = 0x41, HSC400KD = 0x42, HSC250LG = 0x43, + HSC400LG = 0x44, HSC600LG = 0x45, HSC001KG = 0x46, HSC1_6KG = 0x47, + HSC2_5KG = 0x48, HSC004KG = 0x49, HSC006KG = 0x4a, HSC010KG = 0x4b, + HSC016KG = 0x4c, HSC025KG = 0x4d, HSC040KG = 0x4e, HSC060KG = 0x4f, + HSC100KG = 0x50, HSC160KG = 0x51, HSC250KG = 0x52, HSC400KG = 0x53, + HSC600KG = 0x54, HSC001GG = 0x55, HSC015PA = 0x56, HSC030PA = 0x57, + HSC060PA = 0x58, HSC100PA = 0x59, HSC150PA = 0x5a, HSC0_5ND = 0x5b, + HSC001ND = 0x5c, HSC002ND = 0x5d, HSC004ND = 0x5e, HSC005ND = 0x5f, + HSC010ND = 0x60, HSC020ND = 0x61, HSC030ND = 0x62, HSC001PD = 0x63, + HSC005PD = 0x64, HSC015PD = 0x65, HSC030PD = 0x66, HSC060PD = 0x67, + HSC001NG = 0x68, HSC002NG = 0x69, HSC004NG = 0x6a, HSC005NG = 0x6b, + HSC010NG = 0x6c, HSC020NG = 0x6d, HSC030NG = 0x6e, HSC001PG = 0x6f, + HSC005PG = 0x70, HSC015PG = 0x71, HSC030PG = 0x72, HSC060PG = 0x73, + HSC100PG = 0x74, HSC150PG = 0x75, HSC_VARIANTS_MAX +}; + +static const char * const hsc_triplet_variants[HSC_VARIANTS_MAX] = { + [HSC001BA] = "001BA", [HSC1_6BA] = "1.6BA", [HSC2_5BA] = "2.5BA", + [HSC004BA] = "004BA", [HSC006BA] = "006BA", [HSC010BA] = "010BA", + [HSC1_6MD] = "1.6MD", [HSC2_5MD] = "2.5MD", [HSC004MD] = "004MD", + [HSC006MD] = "006MD", [HSC010MD] = "010MD", [HSC016MD] = "016MD", + [HSC025MD] = "025MD", [HSC040MD] = "040MD", [HSC060MD] = "060MD", + [HSC100MD] = "100MD", [HSC160MD] = "160MD", [HSC250MD] = "250MD", + [HSC400MD] = "400MD", [HSC600MD] = "600MD", [HSC001BD] = "001BD", + [HSC1_6BD] = "1.6BD", [HSC2_5BD] = "2.5BD", [HSC004BD] = "004BD", + [HSC2_5MG] = "2.5MG", [HSC004MG] = "004MG", [HSC006MG] = "006MG", + [HSC010MG] = "010MG", [HSC016MG] = "016MG", [HSC025MG] = "025MG", + [HSC040MG] = "040MG", [HSC060MG] = "060MG", [HSC100MG] = "100MG", + [HSC160MG] = "160MG", [HSC250MG] = "250MG", [HSC400MG] = "400MG", + [HSC600MG] = "600MG", [HSC001BG] = "001BG", [HSC1_6BG] = "1.6BG", + [HSC2_5BG] = "2.5BG", [HSC004BG] = "004BG", [HSC006BG] = "006BG", + [HSC010BG] = "010BG", [HSC100KA] = "100KA", [HSC160KA] = "160KA", + [HSC250KA] = "250KA", [HSC400KA] = "400KA", [HSC600KA] = "600KA", + [HSC001GA] = "001GA", [HSC160LD] = "160LD", [HSC250LD] = "250LD", + [HSC400LD] = "400LD", [HSC600LD] = "600LD", [HSC001KD] = "001KD", + [HSC1_6KD] = "1.6KD", [HSC2_5KD] = "2.5KD", [HSC004KD] = "004KD", + [HSC006KD] = "006KD", [HSC010KD] = "010KD", [HSC016KD] = "016KD", + [HSC025KD] = "025KD", [HSC040KD] = "040KD", [HSC060KD] = "060KD", + [HSC100KD] = "100KD", [HSC160KD] = "160KD", [HSC250KD] = "250KD", + [HSC400KD] = "400KD", [HSC250LG] = "250LG", [HSC400LG] = "400LG", + [HSC600LG] = "600LG", [HSC001KG] = "001KG", [HSC1_6KG] = "1.6KG", + [HSC2_5KG] = "2.5KG", [HSC004KG] = "004KG", [HSC006KG] = "006KG", + [HSC010KG] = "010KG", [HSC016KG] = "016KG", [HSC025KG] = "025KG", + [HSC040KG] = "040KG", [HSC060KG] = "060KG", [HSC100KG] = "100KG", + [HSC160KG] = "160KG", [HSC250KG] = "250KG", [HSC400KG] = "400KG", + [HSC600KG] = "600KG", [HSC001GG] = "001GG", [HSC015PA] = "015PA", + [HSC030PA] = "030PA", [HSC060PA] = "060PA", [HSC100PA] = "100PA", + [HSC150PA] = "150PA", [HSC0_5ND] = "0.5ND", [HSC001ND] = "001ND", + [HSC002ND] = "002ND", [HSC004ND] = "004ND", [HSC005ND] = "005ND", + [HSC010ND] = "010ND", [HSC020ND] = "020ND", [HSC030ND] = "030ND", + [HSC001PD] = "001PD", [HSC005PD] = "005PD", [HSC015PD] = "015PD", + [HSC030PD] = "030PD", [HSC060PD] = "060PD", [HSC001NG] = "001NG", + [HSC002NG] = "002NG", [HSC004NG] = "004NG", [HSC005NG] = "005NG", + [HSC010NG] = "010NG", [HSC020NG] = "020NG", [HSC030NG] = "030NG", + [HSC001PG] = "001PG", [HSC005PG] = "005PG", [HSC015PG] = "015PG", + [HSC030PG] = "030PG", [HSC060PG] = "060PG", [HSC100PG] = "100PG", + [HSC150PG] = "150PG", +}; + +/** + * struct hsc_range_config - list of pressure ranges based on nomenclature + * @pmin: lowest pressure that can be measured + * @pmax: highest pressure that can be measured + */ +struct hsc_range_config { + const s32 pmin; + const s32 pmax; +}; + +/* All min max limits have been converted to pascals */ +static const struct hsc_range_config hsc_range_config[HSC_VARIANTS_MAX] = { + [HSC001BA] = { .pmin = 0, .pmax = 100000 }, + [HSC1_6BA] = { .pmin = 0, .pmax = 160000 }, + [HSC2_5BA] = { .pmin = 0, .pmax = 250000 }, + [HSC004BA] = { .pmin = 0, .pmax = 400000 }, + [HSC006BA] = { .pmin = 0, .pmax = 600000 }, + [HSC010BA] = { .pmin = 0, .pmax = 1000000 }, + [HSC1_6MD] = { .pmin = -160, .pmax = 160 }, + [HSC2_5MD] = { .pmin = -250, .pmax = 250 }, + [HSC004MD] = { .pmin = -400, .pmax = 400 }, + [HSC006MD] = { .pmin = -600, .pmax = 600 }, + [HSC010MD] = { .pmin = -1000, .pmax = 1000 }, + [HSC016MD] = { .pmin = -1600, .pmax = 1600 }, + [HSC025MD] = { .pmin = -2500, .pmax = 2500 }, + [HSC040MD] = { .pmin = -4000, .pmax = 4000 }, + [HSC060MD] = { .pmin = -6000, .pmax = 6000 }, + [HSC100MD] = { .pmin = -10000, .pmax = 10000 }, + [HSC160MD] = { .pmin = -16000, .pmax = 16000 }, + [HSC250MD] = { .pmin = -25000, .pmax = 25000 }, + [HSC400MD] = { .pmin = -40000, .pmax = 40000 }, + [HSC600MD] = { .pmin = -60000, .pmax = 60000 }, + [HSC001BD] = { .pmin = -100000, .pmax = 100000 }, + [HSC1_6BD] = { .pmin = -160000, .pmax = 160000 }, + [HSC2_5BD] = { .pmin = -250000, .pmax = 250000 }, + [HSC004BD] = { .pmin = -400000, .pmax = 400000 }, + [HSC2_5MG] = { .pmin = 0, .pmax = 250 }, + [HSC004MG] = { .pmin = 0, .pmax = 400 }, + [HSC006MG] = { .pmin = 0, .pmax = 600 }, + [HSC010MG] = { .pmin = 0, .pmax = 1000 }, + [HSC016MG] = { .pmin = 0, .pmax = 1600 }, + [HSC025MG] = { .pmin = 0, .pmax = 2500 }, + [HSC040MG] = { .pmin = 0, .pmax = 4000 }, + [HSC060MG] = { .pmin = 0, .pmax = 6000 }, + [HSC100MG] = { .pmin = 0, .pmax = 10000 }, + [HSC160MG] = { .pmin = 0, .pmax = 16000 }, + [HSC250MG] = { .pmin = 0, .pmax = 25000 }, + [HSC400MG] = { .pmin = 0, .pmax = 40000 }, + [HSC600MG] = { .pmin = 0, .pmax = 60000 }, + [HSC001BG] = { .pmin = 0, .pmax = 100000 }, + [HSC1_6BG] = { .pmin = 0, .pmax = 160000 }, + [HSC2_5BG] = { .pmin = 0, .pmax = 250000 }, + [HSC004BG] = { .pmin = 0, .pmax = 400000 }, + [HSC006BG] = { .pmin = 0, .pmax = 600000 }, + [HSC010BG] = { .pmin = 0, .pmax = 1000000 }, + [HSC100KA] = { .pmin = 0, .pmax = 100000 }, + [HSC160KA] = { .pmin = 0, .pmax = 160000 }, + [HSC250KA] = { .pmin = 0, .pmax = 250000 }, + [HSC400KA] = { .pmin = 0, .pmax = 400000 }, + [HSC600KA] = { .pmin = 0, .pmax = 600000 }, + [HSC001GA] = { .pmin = 0, .pmax = 1000000 }, + [HSC160LD] = { .pmin = -160, .pmax = 160 }, + [HSC250LD] = { .pmin = -250, .pmax = 250 }, + [HSC400LD] = { .pmin = -400, .pmax = 400 }, + [HSC600LD] = { .pmin = -600, .pmax = 600 }, + [HSC001KD] = { .pmin = -1000, .pmax = 1000 }, + [HSC1_6KD] = { .pmin = -1600, .pmax = 1600 }, + [HSC2_5KD] = { .pmin = -2500, .pmax = 2500 }, + [HSC004KD] = { .pmin = -4000, .pmax = 4000 }, + [HSC006KD] = { .pmin = -6000, .pmax = 6000 }, + [HSC010KD] = { .pmin = -10000, .pmax = 10000 }, + [HSC016KD] = { .pmin = -16000, .pmax = 16000 }, + [HSC025KD] = { .pmin = -25000, .pmax = 25000 }, + [HSC040KD] = { .pmin = -40000, .pmax = 40000 }, + [HSC060KD] = { .pmin = -60000, .pmax = 60000 }, + [HSC100KD] = { .pmin = -100000, .pmax = 100000 }, + [HSC160KD] = { .pmin = -160000, .pmax = 160000 }, + [HSC250KD] = { .pmin = -250000, .pmax = 250000 }, + [HSC400KD] = { .pmin = -400000, .pmax = 400000 }, + [HSC250LG] = { .pmin = 0, .pmax = 250 }, + [HSC400LG] = { .pmin = 0, .pmax = 400 }, + [HSC600LG] = { .pmin = 0, .pmax = 600 }, + [HSC001KG] = { .pmin = 0, .pmax = 1000 }, + [HSC1_6KG] = { .pmin = 0, .pmax = 1600 }, + [HSC2_5KG] = { .pmin = 0, .pmax = 2500 }, + [HSC004KG] = { .pmin = 0, .pmax = 4000 }, + [HSC006KG] = { .pmin = 0, .pmax = 6000 }, + [HSC010KG] = { .pmin = 0, .pmax = 10000 }, + [HSC016KG] = { .pmin = 0, .pmax = 16000 }, + [HSC025KG] = { .pmin = 0, .pmax = 25000 }, + [HSC040KG] = { .pmin = 0, .pmax = 40000 }, + [HSC060KG] = { .pmin = 0, .pmax = 60000 }, + [HSC100KG] = { .pmin = 0, .pmax = 100000 }, + [HSC160KG] = { .pmin = 0, .pmax = 160000 }, + [HSC250KG] = { .pmin = 0, .pmax = 250000 }, + [HSC400KG] = { .pmin = 0, .pmax = 400000 }, + [HSC600KG] = { .pmin = 0, .pmax = 600000 }, + [HSC001GG] = { .pmin = 0, .pmax = 1000000 }, + [HSC015PA] = { .pmin = 0, .pmax = 103421 }, + [HSC030PA] = { .pmin = 0, .pmax = 206843 }, + [HSC060PA] = { .pmin = 0, .pmax = 413685 }, + [HSC100PA] = { .pmin = 0, .pmax = 689476 }, + [HSC150PA] = { .pmin = 0, .pmax = 1034214 }, + [HSC0_5ND] = { .pmin = -125, .pmax = 125 }, + [HSC001ND] = { .pmin = -249, .pmax = 249 }, + [HSC002ND] = { .pmin = -498, .pmax = 498 }, + [HSC004ND] = { .pmin = -996, .pmax = 996 }, + [HSC005ND] = { .pmin = -1245, .pmax = 1245 }, + [HSC010ND] = { .pmin = -2491, .pmax = 2491 }, + [HSC020ND] = { .pmin = -4982, .pmax = 4982 }, + [HSC030ND] = { .pmin = -7473, .pmax = 7473 }, + [HSC001PD] = { .pmin = -6895, .pmax = 6895 }, + [HSC005PD] = { .pmin = -34474, .pmax = 34474 }, + [HSC015PD] = { .pmin = -103421, .pmax = 103421 }, + [HSC030PD] = { .pmin = -206843, .pmax = 206843 }, + [HSC060PD] = { .pmin = -413685, .pmax = 413685 }, + [HSC001NG] = { .pmin = 0, .pmax = 249 }, + [HSC002NG] = { .pmin = 0, .pmax = 498 }, + [HSC004NG] = { .pmin = 0, .pmax = 996 }, + [HSC005NG] = { .pmin = 0, .pmax = 1245 }, + [HSC010NG] = { .pmin = 0, .pmax = 2491 }, + [HSC020NG] = { .pmin = 0, .pmax = 4982 }, + [HSC030NG] = { .pmin = 0, .pmax = 7473 }, + [HSC001PG] = { .pmin = 0, .pmax = 6895 }, + [HSC005PG] = { .pmin = 0, .pmax = 34474 }, + [HSC015PG] = { .pmin = 0, .pmax = 103421 }, + [HSC030PG] = { .pmin = 0, .pmax = 206843 }, + [HSC060PG] = { .pmin = 0, .pmax = 413685 }, + [HSC100PG] = { .pmin = 0, .pmax = 689476 }, + [HSC150PG] = { .pmin = 0, .pmax = 1034214 }, +}; + +/** + * hsc_measurement_is_valid() - validate last conversion via status bits + * @data: structure containing instantiated sensor data + * Return: true only if both status bits are zero + * + * the two MSB from the first transfered byte contain a status code + * 00 - normal operation, valid data + * 01 - device in factory programming mode + * 10 - stale data + * 11 - diagnostic condition + */ +static bool hsc_measurement_is_valid(struct hsc_data *data) +{ + return !(data->buffer[0] & HSC_STATUS_MASK); +} + +static int hsc_get_measurement(struct hsc_data *data) +{ + const struct hsc_chip_data *chip = data->chip; + int ret; + + ret = data->recv_cb(data); + if (ret < 0) + return ret; + + data->is_valid = chip->valid(data); + if (!data->is_valid) + return -EAGAIN; + + return 0; +} + +/* + * IIO ABI expects + * value = (conv + offset) * scale + * + * datasheet provides the following formula for determining the temperature + * temp[C] = conv * a + b + * where a = 200/2047; b = -50 + * + * temp[C] = (conv + (b/a)) * a * (1000) + * => + * scale = a * 1000 = .097703957 * 1000 = 97.703957 + * offset = b/a = -50 / .097703957 = -50000000 / 97704 + * + * based on the datasheet + * pressure = (conv - Omin) * Q + Pmin = + * ((conv - Omin) + Pmin/Q) * Q + * => + * scale = Q = (Pmax - Pmin) / (Omax - Omin) + * offset = Pmin/Q - Omin = Pmin * (Omax - Omin) / (Pmax - Pmin) - Omin + */ +static int hsc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct hsc_data *data = iio_priv(indio_dev); + int ret; + u32 recvd; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = hsc_get_measurement(data); + if (ret) + return ret; + + recvd = get_unaligned_be32(data->buffer); + switch (channel->type) { + case IIO_PRESSURE: + *val = FIELD_GET(HSC_PRESSURE_MASK, recvd); + return IIO_VAL_INT; + case IIO_TEMP: + *val = FIELD_GET(HSC_TEMPERATURE_MASK, recvd); + return IIO_VAL_INT; + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_SCALE: + switch (channel->type) { + case IIO_TEMP: + *val = 97; + *val2 = 703957; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_PRESSURE: + *val = data->p_scale; + *val2 = data->p_scale_dec; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_OFFSET: + switch (channel->type) { + case IIO_TEMP: + *val = -50000000; + *val2 = 97704; + return IIO_VAL_FRACTIONAL; + case IIO_PRESSURE: + *val = data->p_offset; + *val2 = data->p_offset_dec; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec hsc_channels[] = { + { + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + }, +}; + +static const struct iio_info hsc_info = { + .read_raw = hsc_read_raw, +}; + +static const struct hsc_chip_data hsc_chip = { + .valid = hsc_measurement_is_valid, + .channels = hsc_channels, + .num_channels = ARRAY_SIZE(hsc_channels), +}; + +int hsc_common_probe(struct device *dev, hsc_recv_fn recv) +{ + struct hsc_data *hsc; + struct iio_dev *indio_dev; + const char *triplet; + u64 tmp; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*hsc)); + if (!indio_dev) + return -ENOMEM; + + hsc = iio_priv(indio_dev); + + hsc->chip = &hsc_chip; + hsc->recv_cb = recv; + hsc->dev = dev; + + ret = device_property_read_u32(dev, "honeywell,transfer-function", + &hsc->function); + if (ret) + return dev_err_probe(dev, ret, + "honeywell,transfer-function could not be read\n"); + if (hsc->function > HSC_FUNCTION_F) + return dev_err_probe(dev, -EINVAL, + "honeywell,transfer-function %d invalid\n", + hsc->function); + + ret = device_property_read_string(dev, "honeywell,pressure-triplet", + &triplet); + if (ret) + return dev_err_probe(dev, ret, + "honeywell,pressure-triplet could not be read\n"); + + if (str_has_prefix(triplet, "NA")) { + ret = device_property_read_u32(dev, "honeywell,pmin-pascal", + &hsc->pmin); + if (ret) + return dev_err_probe(dev, ret, + "honeywell,pmin-pascal could not be read\n"); + + ret = device_property_read_u32(dev, "honeywell,pmax-pascal", + &hsc->pmax); + if (ret) + return dev_err_probe(dev, ret, + "honeywell,pmax-pascal could not be read\n"); + } else { + ret = device_property_match_property_string(dev, + "honeywell,pressure-triplet", + hsc_triplet_variants, + HSC_VARIANTS_MAX); + if (ret < 0) + return dev_err_probe(dev, -EINVAL, + "honeywell,pressure-triplet is invalid\n"); + + hsc->pmin = hsc_range_config[ret].pmin; + hsc->pmax = hsc_range_config[ret].pmax; + } + + if (hsc->pmin >= hsc->pmax) + return dev_err_probe(dev, -EINVAL, + "pressure limits are invalid\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "can't get vdd supply\n"); + + hsc->outmin = hsc_func_spec[hsc->function].output_min; + hsc->outmax = hsc_func_spec[hsc->function].output_max; + + tmp = div_s64(((s64)(hsc->pmax - hsc->pmin)) * MICRO, + hsc->outmax - hsc->outmin); + hsc->p_scale = div_s64_rem(tmp, NANO, &hsc->p_scale_dec); + tmp = div_s64(((s64)hsc->pmin * (s64)(hsc->outmax - hsc->outmin)) * MICRO, + hsc->pmax - hsc->pmin); + tmp -= (s64)hsc->outmin * MICRO; + hsc->p_offset = div_s64_rem(tmp, MICRO, &hsc->p_offset_dec); + + indio_dev->name = "hsc030pa"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &hsc_info; + indio_dev->channels = hsc->chip->channels; + indio_dev->num_channels = hsc->chip->num_channels; + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS(hsc_common_probe, IIO_HONEYWELL_HSC030PA); + +MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>"); +MODULE_DESCRIPTION("Honeywell HSC and SSC pressure sensor core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/pressure/hsc030pa.h b/drivers/iio/pressure/hsc030pa.h new file mode 100644 index 000000000000..d20420dba4f6 --- /dev/null +++ b/drivers/iio/pressure/hsc030pa.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Honeywell TruStability HSC Series pressure/temperature sensor + * + * Copyright (c) 2023 Petre Rodan <petre.rodan@subdimension.ro> + */ + +#ifndef _HSC030PA_H +#define _HSC030PA_H + +#include <linux/types.h> + +#define HSC_REG_MEASUREMENT_RD_SIZE 4 + +struct device; + +struct iio_chan_spec; +struct iio_dev; + +struct hsc_data; +struct hsc_chip_data; + +typedef int (*hsc_recv_fn)(struct hsc_data *); + +/** + * struct hsc_data + * @dev: current device structure + * @chip: structure containing chip's channel properties + * @recv_cb: function that implements the chip reads + * @is_valid: true if last transfer has been validated + * @pmin: minimum measurable pressure limit + * @pmax: maximum measurable pressure limit + * @outmin: minimum raw pressure in counts (based on transfer function) + * @outmax: maximum raw pressure in counts (based on transfer function) + * @function: transfer function + * @p_scale: pressure scale + * @p_scale_dec: pressure scale, decimal places + * @p_offset: pressure offset + * @p_offset_dec: pressure offset, decimal places + * @buffer: raw conversion data + */ +struct hsc_data { + struct device *dev; + const struct hsc_chip_data *chip; + hsc_recv_fn recv_cb; + bool is_valid; + s32 pmin; + s32 pmax; + u32 outmin; + u32 outmax; + u32 function; + s64 p_scale; + s32 p_scale_dec; + s64 p_offset; + s32 p_offset_dec; + u8 buffer[HSC_REG_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN); +}; + +struct hsc_chip_data { + bool (*valid)(struct hsc_data *data); + const struct iio_chan_spec *channels; + u8 num_channels; +}; + +enum hsc_func_id { + HSC_FUNCTION_A, + HSC_FUNCTION_B, + HSC_FUNCTION_C, + HSC_FUNCTION_F, +}; + +int hsc_common_probe(struct device *dev, hsc_recv_fn recv); + +#endif diff --git a/drivers/iio/pressure/hsc030pa_i2c.c b/drivers/iio/pressure/hsc030pa_i2c.c new file mode 100644 index 000000000000..e2b524b36417 --- /dev/null +++ b/drivers/iio/pressure/hsc030pa_i2c.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Honeywell TruStability HSC Series pressure/temperature sensor + * + * Copyright (c) 2023 Petre Rodan <petre.rodan@subdimension.ro> + * + * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/trustability-hsc-series/documents/sps-siot-trustability-hsc-series-high-accuracy-board-mount-pressure-sensors-50099148-a-en-ciid-151133.pdf [hsc] + * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/common/documents/sps-siot-i2c-comms-digital-output-pressure-sensors-tn-008201-3-en-ciid-45841.pdf [i2c related] + */ + +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> + +#include "hsc030pa.h" + +static int hsc_i2c_recv(struct hsc_data *data) +{ + struct i2c_client *client = to_i2c_client(data->dev); + struct i2c_msg msg; + int ret; + + msg.addr = client->addr; + msg.flags = client->flags | I2C_M_RD; + msg.len = HSC_REG_MEASUREMENT_RD_SIZE; + msg.buf = data->buffer; + + ret = i2c_transfer(client->adapter, &msg, 1); + + return (ret == 2) ? 0 : ret; +} + +static int hsc_i2c_probe(struct i2c_client *client) +{ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + return hsc_common_probe(&client->dev, hsc_i2c_recv); +} + +static const struct of_device_id hsc_i2c_match[] = { + { .compatible = "honeywell,hsc030pa" }, + {} +}; +MODULE_DEVICE_TABLE(of, hsc_i2c_match); + +static const struct i2c_device_id hsc_i2c_id[] = { + { "hsc030pa" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, hsc_i2c_id); + +static struct i2c_driver hsc_i2c_driver = { + .driver = { + .name = "hsc030pa", + .of_match_table = hsc_i2c_match, + }, + .probe = hsc_i2c_probe, + .id_table = hsc_i2c_id, +}; +module_i2c_driver(hsc_i2c_driver); + +MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>"); +MODULE_DESCRIPTION("Honeywell HSC and SSC pressure sensor i2c driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HONEYWELL_HSC030PA); diff --git a/drivers/iio/pressure/hsc030pa_spi.c b/drivers/iio/pressure/hsc030pa_spi.c new file mode 100644 index 000000000000..a719bade8326 --- /dev/null +++ b/drivers/iio/pressure/hsc030pa_spi.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Honeywell TruStability HSC Series pressure/temperature sensor + * + * Copyright (c) 2023 Petre Rodan <petre.rodan@subdimension.ro> + * + * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/trustability-hsc-series/documents/sps-siot-trustability-hsc-series-high-accuracy-board-mount-pressure-sensors-50099148-a-en-ciid-151133.pdf + */ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/stddef.h> + +#include <linux/iio/iio.h> + +#include "hsc030pa.h" + +static int hsc_spi_recv(struct hsc_data *data) +{ + struct spi_device *spi = to_spi_device(data->dev); + struct spi_transfer xfer = { + .tx_buf = NULL, + .rx_buf = data->buffer, + .len = HSC_REG_MEASUREMENT_RD_SIZE, + }; + + return spi_sync_transfer(spi, &xfer, 1); +} + +static int hsc_spi_probe(struct spi_device *spi) +{ + return hsc_common_probe(&spi->dev, hsc_spi_recv); +} + +static const struct of_device_id hsc_spi_match[] = { + { .compatible = "honeywell,hsc030pa" }, + {} +}; +MODULE_DEVICE_TABLE(of, hsc_spi_match); + +static const struct spi_device_id hsc_spi_id[] = { + { "hsc030pa" }, + {} +}; +MODULE_DEVICE_TABLE(spi, hsc_spi_id); + +static struct spi_driver hsc_spi_driver = { + .driver = { + .name = "hsc030pa", + .of_match_table = hsc_spi_match, + }, + .probe = hsc_spi_probe, + .id_table = hsc_spi_id, +}; +module_spi_driver(hsc_spi_driver); + +MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>"); +MODULE_DESCRIPTION("Honeywell HSC and SSC pressure sensor spi driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HONEYWELL_HSC030PA); diff --git a/drivers/iio/proximity/irsd200.c b/drivers/iio/proximity/irsd200.c index bdff91f6b1a3..323ac6dac90e 100644 --- a/drivers/iio/proximity/irsd200.c +++ b/drivers/iio/proximity/irsd200.c @@ -7,7 +7,6 @@ #include <asm/unaligned.h> #include <linux/bitfield.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/regmap.h> diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index 438f9c9aba6e..ac2ed2da21cc 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -888,7 +888,6 @@ sx9324_get_default_reg(struct device *dev, int idx, char prop[] = SX9324_PROXRAW_DEF; u32 start = 0, raw = 0, pos = 0; int ret, count, ph, pin; - const char *res; memcpy(reg_def, &sx9324_default_regs[idx], sizeof(*reg_def)); @@ -915,24 +914,21 @@ sx9324_get_default_reg(struct device *dev, int idx, reg_def->def = raw; break; case SX9324_REG_AFE_CTRL0: - ret = device_property_read_string(dev, - "semtech,cs-idle-sleep", &res); - if (!ret) - ret = match_string(sx9324_csidle, ARRAY_SIZE(sx9324_csidle), res); + ret = device_property_match_property_string(dev, "semtech,cs-idle-sleep", + sx9324_csidle, + ARRAY_SIZE(sx9324_csidle)); if (ret >= 0) { reg_def->def &= ~SX9324_REG_AFE_CTRL0_CSIDLE_MASK; reg_def->def |= ret << SX9324_REG_AFE_CTRL0_CSIDLE_SHIFT; } - ret = device_property_read_string(dev, - "semtech,int-comp-resistor", &res); - if (ret) - break; - ret = match_string(sx9324_rints, ARRAY_SIZE(sx9324_rints), res); - if (ret < 0) - break; - reg_def->def &= ~SX9324_REG_AFE_CTRL0_RINT_MASK; - reg_def->def |= ret << SX9324_REG_AFE_CTRL0_RINT_SHIFT; + ret = device_property_match_property_string(dev, "semtech,int-comp-resistor", + sx9324_rints, + ARRAY_SIZE(sx9324_rints)); + if (ret >= 0) { + reg_def->def &= ~SX9324_REG_AFE_CTRL0_RINT_MASK; + reg_def->def |= ret << SX9324_REG_AFE_CTRL0_RINT_SHIFT; + } break; case SX9324_REG_AFE_CTRL4: case SX9324_REG_AFE_CTRL7: diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index 1bd1b950e7cc..a414eef12e5e 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -141,7 +141,7 @@ struct ad2s1210_state { struct spi_device *sdev; /** GPIO pin connected to SAMPLE line. */ struct gpio_desc *sample_gpio; - /** GPIO pins connected to A0 and A1 lines. */ + /** GPIO pins connected to A0 and A1 lines (optional). */ struct gpio_descs *mode_gpios; /** Used to access config registers. */ struct regmap *regmap; @@ -149,6 +149,8 @@ struct ad2s1210_state { unsigned long clkin_hz; /** Available raw hysteresis values based on resolution. */ int hysteresis_available[2]; + /* adi,fixed-mode property - only valid when mode_gpios == NULL. */ + enum ad2s1210_mode fixed_mode; /** The selected resolution */ enum ad2s1210_resolution resolution; /** Copy of fault register from the previous read. */ @@ -175,6 +177,9 @@ static int ad2s1210_set_mode(struct ad2s1210_state *st, enum ad2s1210_mode mode) struct gpio_descs *gpios = st->mode_gpios; DECLARE_BITMAP(bitmap, 2); + if (!gpios) + return mode == st->fixed_mode ? 0 : -EOPNOTSUPP; + bitmap[0] = mode; return gpiod_set_array_value(gpios->ndescs, gpios->desc, gpios->info, @@ -276,7 +281,8 @@ static int ad2s1210_regmap_reg_read(void *context, unsigned int reg, * parity error. The fault register is read-only and the D7 bit means * something else there. */ - if (reg != AD2S1210_REG_FAULT && st->rx[1] & AD2S1210_ADDRESS_DATA) + if ((reg > AD2S1210_REG_VELOCITY_LSB && reg != AD2S1210_REG_FAULT) + && st->rx[1] & AD2S1210_ADDRESS_DATA) return -EBADMSG; *val = st->rx[1]; @@ -450,21 +456,53 @@ static int ad2s1210_single_conversion(struct iio_dev *indio_dev, ad2s1210_toggle_sample_line(st); timestamp = iio_get_time_ns(indio_dev); - switch (chan->type) { - case IIO_ANGL: - ret = ad2s1210_set_mode(st, MOD_POS); - break; - case IIO_ANGL_VEL: - ret = ad2s1210_set_mode(st, MOD_VEL); - break; - default: - return -EINVAL; + if (st->fixed_mode == MOD_CONFIG) { + unsigned int reg_val; + + switch (chan->type) { + case IIO_ANGL: + ret = regmap_bulk_read(st->regmap, + AD2S1210_REG_POSITION_MSB, + &st->sample.raw, 2); + if (ret < 0) + return ret; + + break; + case IIO_ANGL_VEL: + ret = regmap_bulk_read(st->regmap, + AD2S1210_REG_VELOCITY_MSB, + &st->sample.raw, 2); + if (ret < 0) + return ret; + + break; + default: + return -EINVAL; + } + + ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, ®_val); + if (ret < 0) + return ret; + + st->sample.fault = reg_val; + } else { + switch (chan->type) { + case IIO_ANGL: + ret = ad2s1210_set_mode(st, MOD_POS); + break; + case IIO_ANGL_VEL: + ret = ad2s1210_set_mode(st, MOD_VEL); + break; + default: + return -EINVAL; + } + if (ret < 0) + return ret; + + ret = spi_read(st->sdev, &st->sample, 3); + if (ret < 0) + return ret; } - if (ret < 0) - return ret; - ret = spi_read(st->sdev, &st->sample, 3); - if (ret < 0) - return ret; switch (chan->type) { case IIO_ANGL: @@ -1252,27 +1290,53 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) ad2s1210_toggle_sample_line(st); if (test_bit(0, indio_dev->active_scan_mask)) { - ret = ad2s1210_set_mode(st, MOD_POS); - if (ret < 0) - goto error_ret; - - ret = spi_read(st->sdev, &st->sample, 3); - if (ret < 0) - goto error_ret; + if (st->fixed_mode == MOD_CONFIG) { + ret = regmap_bulk_read(st->regmap, + AD2S1210_REG_POSITION_MSB, + &st->sample.raw, 2); + if (ret < 0) + goto error_ret; + } else { + ret = ad2s1210_set_mode(st, MOD_POS); + if (ret < 0) + goto error_ret; + + ret = spi_read(st->sdev, &st->sample, 3); + if (ret < 0) + goto error_ret; + } memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); } if (test_bit(1, indio_dev->active_scan_mask)) { - ret = ad2s1210_set_mode(st, MOD_VEL); - if (ret < 0) - goto error_ret; + if (st->fixed_mode == MOD_CONFIG) { + ret = regmap_bulk_read(st->regmap, + AD2S1210_REG_VELOCITY_MSB, + &st->sample.raw, 2); + if (ret < 0) + goto error_ret; + } else { + ret = ad2s1210_set_mode(st, MOD_VEL); + if (ret < 0) + goto error_ret; + + ret = spi_read(st->sdev, &st->sample, 3); + if (ret < 0) + goto error_ret; + } - ret = spi_read(st->sdev, &st->sample, 3); + memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); + } + + if (st->fixed_mode == MOD_CONFIG) { + unsigned int reg_val; + + ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, ®_val); if (ret < 0) - goto error_ret; + return ret; - memcpy(&st->scan.chan[chan++], &st->sample.raw, 2); + st->sample.fault = reg_val; } ad2s1210_push_events(indio_dev, st->sample.fault, pf->timestamp); @@ -1299,9 +1363,24 @@ static const struct iio_info ad2s1210_info = { static int ad2s1210_setup_properties(struct ad2s1210_state *st) { struct device *dev = &st->sdev->dev; + const char *str_val; u32 val; int ret; + ret = device_property_read_string(dev, "adi,fixed-mode", &str_val); + if (ret == -EINVAL) + st->fixed_mode = -1; + else if (ret < 0) + return dev_err_probe(dev, ret, + "failed to read adi,fixed-mode property\n"); + else { + if (strcmp(str_val, "config")) + return dev_err_probe(dev, -EINVAL, + "only adi,fixed-mode=\"config\" is supported\n"); + + st->fixed_mode = MOD_CONFIG; + } + ret = device_property_read_u32(dev, "assigned-resolution-bits", &val); if (ret < 0) return dev_err_probe(dev, ret, @@ -1347,6 +1426,7 @@ static int ad2s1210_setup_gpios(struct ad2s1210_state *st) { struct device *dev = &st->sdev->dev; struct gpio_descs *resolution_gpios; + struct gpio_desc *reset_gpio; DECLARE_BITMAP(bitmap, 2); int ret; @@ -1357,12 +1437,21 @@ static int ad2s1210_setup_gpios(struct ad2s1210_state *st) "failed to request sample GPIO\n"); /* both pins high means that we start in config mode */ - st->mode_gpios = devm_gpiod_get_array(dev, "mode", GPIOD_OUT_HIGH); + st->mode_gpios = devm_gpiod_get_array_optional(dev, "mode", + GPIOD_OUT_HIGH); if (IS_ERR(st->mode_gpios)) return dev_err_probe(dev, PTR_ERR(st->mode_gpios), "failed to request mode GPIOs\n"); - if (st->mode_gpios->ndescs != 2) + if (!st->mode_gpios && st->fixed_mode == -1) + return dev_err_probe(dev, -EINVAL, + "must specify either adi,fixed-mode or mode-gpios\n"); + + if (st->mode_gpios && st->fixed_mode != -1) + return dev_err_probe(dev, -EINVAL, + "must specify only one of adi,fixed-mode or mode-gpios\n"); + + if (st->mode_gpios && st->mode_gpios->ndescs != 2) return dev_err_probe(dev, -EINVAL, "requires exactly 2 mode-gpios\n"); @@ -1393,6 +1482,17 @@ static int ad2s1210_setup_gpios(struct ad2s1210_state *st) "failed to set resolution gpios\n"); } + /* If the optional reset GPIO is present, toggle it to do a hard reset. */ + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return dev_err_probe(dev, PTR_ERR(reset_gpio), + "failed to request reset GPIO\n"); + + if (reset_gpio) { + udelay(10); + gpiod_set_value(reset_gpio, 0); + } + return 0; } diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index ed384f33e0c7..ed0e4963362f 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -76,6 +76,18 @@ config MLX90632 This driver can also be built as a module. If so, the module will be called mlx90632. +config MLX90635 + tristate "MLX90635 contact-less infrared sensor with medical accuracy" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the Melexis + MLX90635 contact-less infrared sensor with medical accuracy + connected with I2C. + + This driver can also be built as a module. If so, the module will + be called mlx90635. + config TMP006 tristate "TMP006 infrared thermopile sensor" depends on I2C @@ -158,4 +170,14 @@ config MAX31865 This driver can also be build as a module. If so, the module will be called max31865. +config MCP9600 + tristate "MCP9600 thermocouple EMF converter" + depends on I2C + help + If you say yes here you get support for MCP9600 + thermocouple EMF converter connected via I2C. + + This driver can also be built as a module. If so, the module + will be called mcp9600. + endmenu diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index dfec8c6d3019..07d6e65709f7 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -10,8 +10,10 @@ obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o obj-$(CONFIG_MAX30208) += max30208.o obj-$(CONFIG_MAX31856) += max31856.o obj-$(CONFIG_MAX31865) += max31865.o +obj-$(CONFIG_MCP9600) += mcp9600.o obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_MLX90632) += mlx90632.o +obj-$(CONFIG_MLX90632) += mlx90635.o obj-$(CONFIG_TMP006) += tmp006.o obj-$(CONFIG_TMP007) += tmp007.o obj-$(CONFIG_TMP117) += tmp117.o diff --git a/drivers/iio/temperature/mcp9600.c b/drivers/iio/temperature/mcp9600.c new file mode 100644 index 000000000000..46845804292b --- /dev/null +++ b/drivers/iio/temperature/mcp9600.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * mcp9600.c - Support for Microchip MCP9600 thermocouple EMF converter + * + * Copyright (c) 2022 Andrew Hepp + * Author: <andrew.hepp@ahepp.dev> + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> + +/* MCP9600 registers */ +#define MCP9600_HOT_JUNCTION 0x0 +#define MCP9600_COLD_JUNCTION 0x2 +#define MCP9600_DEVICE_ID 0x20 + +/* MCP9600 device id value */ +#define MCP9600_DEVICE_ID_MCP9600 0x40 + +static const struct iio_chan_spec mcp9600_channels[] = { + { + .type = IIO_TEMP, + .address = MCP9600_HOT_JUNCTION, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_TEMP, + .address = MCP9600_COLD_JUNCTION, + .channel2 = IIO_MOD_TEMP_AMBIENT, + .modified = 1, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +struct mcp9600_data { + struct i2c_client *client; +}; + +static int mcp9600_read(struct mcp9600_data *data, + struct iio_chan_spec const *chan, int *val) +{ + int ret; + + ret = i2c_smbus_read_word_swapped(data->client, chan->address); + + if (ret < 0) + return ret; + *val = ret; + + return 0; +} + +static int mcp9600_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct mcp9600_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = mcp9600_read(data, chan, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 62; + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info mcp9600_info = { + .read_raw = mcp9600_read_raw, +}; + +static int mcp9600_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct mcp9600_data *data; + int ret; + + ret = i2c_smbus_read_byte_data(client, MCP9600_DEVICE_ID); + if (ret < 0) + return dev_err_probe(&client->dev, ret, "Failed to read device ID\n"); + if (ret != MCP9600_DEVICE_ID_MCP9600) + dev_warn(&client->dev, "Expected ID %x, got %x\n", + MCP9600_DEVICE_ID_MCP9600, ret); + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + + indio_dev->info = &mcp9600_info; + indio_dev->name = "mcp9600"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mcp9600_channels; + indio_dev->num_channels = ARRAY_SIZE(mcp9600_channels); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id mcp9600_id[] = { + { "mcp9600" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, mcp9600_id); + +static const struct of_device_id mcp9600_of_match[] = { + { .compatible = "microchip,mcp9600" }, + {} +}; +MODULE_DEVICE_TABLE(of, mcp9600_of_match); + +static struct i2c_driver mcp9600_driver = { + .driver = { + .name = "mcp9600", + .of_match_table = mcp9600_of_match, + }, + .probe = mcp9600_probe, + .id_table = mcp9600_id +}; +module_i2c_driver(mcp9600_driver); + +MODULE_AUTHOR("Andrew Hepp <andrew.hepp@ahepp.dev>"); +MODULE_DESCRIPTION("Microchip MCP9600 thermocouple EMF converter driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/temperature/mlx90635.c b/drivers/iio/temperature/mlx90635.c new file mode 100644 index 000000000000..1f5c962c1818 --- /dev/null +++ b/drivers/iio/temperature/mlx90635.c @@ -0,0 +1,1097 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mlx90635.c - Melexis MLX90635 contactless IR temperature sensor + * + * Copyright (c) 2023 Melexis <cmo@melexis.com> + * + * Driver for the Melexis MLX90635 I2C 16-bit IR thermopile sensor + */ +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/iopoll.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/limits.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/math64.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#include <linux/iio/iio.h> + +/* Memory sections addresses */ +#define MLX90635_ADDR_RAM 0x0000 /* Start address of ram */ +#define MLX90635_ADDR_EEPROM 0x0018 /* Start address of user eeprom */ + +/* EEPROM addresses - used at startup */ +#define MLX90635_EE_I2C_CFG 0x0018 /* I2C address register initial value */ +#define MLX90635_EE_CTRL1 0x001A /* Control register1 initial value */ +#define MLX90635_EE_CTRL2 0x001C /* Control register2 initial value */ + +#define MLX90635_EE_Ha 0x001E /* Ha customer calib value reg 16bit */ +#define MLX90635_EE_Hb 0x0020 /* Hb customer calib value reg 16bit */ +#define MLX90635_EE_Fa 0x0026 /* Fa calibration register 32bit */ +#define MLX90635_EE_FASCALE 0x002A /* Scaling coefficient for Fa register 16bit */ +#define MLX90635_EE_Ga 0x002C /* Ga calibration register 16bit */ +#define MLX90635_EE_Fb 0x002E /* Fb calibration register 16bit */ +#define MLX90635_EE_Ea 0x0030 /* Ea calibration register 32bit */ +#define MLX90635_EE_Eb 0x0034 /* Eb calibration register 32bit */ +#define MLX90635_EE_P_G 0x0038 /* P_G calibration register 16bit */ +#define MLX90635_EE_P_O 0x003A /* P_O calibration register 16bit */ +#define MLX90635_EE_Aa 0x003C /* Aa calibration register 16bit */ +#define MLX90635_EE_VERSION 0x003E /* Version bits 4:7 and 12:15 */ +#define MLX90635_EE_Gb 0x0040 /* Gb calibration register 16bit */ + +/* Device status register - volatile */ +#define MLX90635_REG_STATUS 0x0000 +#define MLX90635_STAT_BUSY BIT(6) /* Device busy indicator */ +#define MLX90635_STAT_BRST BIT(5) /* Brown out reset indicator */ +#define MLX90635_STAT_CYCLE_POS GENMASK(4, 2) /* Data position */ +#define MLX90635_STAT_END_CONV BIT(1) /* End of conversion indicator */ +#define MLX90635_STAT_DATA_RDY BIT(0) /* Data ready indicator */ + +/* EEPROM control register address - volatile */ +#define MLX90635_REG_EE 0x000C +#define MLX90635_EE_ACTIVE BIT(4) /* Power-on EEPROM */ +#define MLX90635_EE_BUSY_MASK BIT(15) + +#define MLX90635_REG_CMD 0x0010 /* Command register address */ + +/* Control register1 address - volatile */ +#define MLX90635_REG_CTRL1 0x0014 +#define MLX90635_CTRL1_REFRESH_RATE_MASK GENMASK(2, 0) +#define MLX90635_CTRL1_RES_CTRL_MASK GENMASK(4, 3) +#define MLX90635_CTRL1_TABLE_MASK BIT(15) /* Table select */ + +/* Control register2 address - volatile */ +#define MLX90635_REG_CTRL2 0x0016 +#define MLX90635_CTRL2_BURST_CNT_MASK GENMASK(10, 6) /* Burst count */ +#define MLX90635_CTRL2_MODE_MASK GENMASK(12, 11) /* Power mode */ +#define MLX90635_CTRL2_SOB_MASK BIT(15) + +/* PowerModes statuses */ +#define MLX90635_PWR_STATUS_HALT 0 +#define MLX90635_PWR_STATUS_SLEEP_STEP 1 +#define MLX90635_PWR_STATUS_STEP 2 +#define MLX90635_PWR_STATUS_CONTINUOUS 3 + +/* Measurement data addresses */ +#define MLX90635_RESULT_1 0x0002 +#define MLX90635_RESULT_2 0x0004 +#define MLX90635_RESULT_3 0x0006 +#define MLX90635_RESULT_4 0x0008 +#define MLX90635_RESULT_5 0x000A + +/* Timings (ms) */ +#define MLX90635_TIMING_RST_MIN 200 /* Minimum time after addressed reset command */ +#define MLX90635_TIMING_RST_MAX 250 /* Maximum time after addressed reset command */ +#define MLX90635_TIMING_POLLING 10000 /* Time between bit polling*/ +#define MLX90635_TIMING_EE_ACTIVE_MIN 100 /* Minimum time after activating the EEPROM for read */ +#define MLX90635_TIMING_EE_ACTIVE_MAX 150 /* Maximum time after activating the EEPROM for read */ + +/* Magic constants */ +#define MLX90635_ID_DSPv1 0x01 /* EEPROM DSP version */ +#define MLX90635_RESET_CMD 0x0006 /* Reset sensor (address or global) */ +#define MLX90635_MAX_MEAS_NUM 31 /* Maximum number of measurements in list */ +#define MLX90635_PTAT_DIV 12 /* Used to divide the PTAT value in pre-processing */ +#define MLX90635_IR_DIV 24 /* Used to divide the IR value in pre-processing */ +#define MLX90635_SLEEP_DELAY_MS 6000 /* Autosleep delay */ +#define MLX90635_MEAS_MAX_TIME 2000 /* Max measurement time in ms for the lowest refresh rate */ +#define MLX90635_READ_RETRIES 100 /* Number of read retries before quitting with timeout error */ +#define MLX90635_VERSION_MASK (GENMASK(15, 12) | GENMASK(7, 4)) +#define MLX90635_DSP_VERSION(reg) (((reg & GENMASK(14, 12)) >> 9) | ((reg & GENMASK(6, 4)) >> 4)) +#define MLX90635_DSP_FIXED BIT(15) + + +/** + * struct mlx90635_data - private data for the MLX90635 device + * @client: I2C client of the device + * @lock: Internal mutex because multiple reads are needed for single triggered + * measurement to ensure data consistency + * @regmap: Regmap of the device registers + * @regmap_ee: Regmap of the device EEPROM which can be cached + * @emissivity: Object emissivity from 0 to 1000 where 1000 = 1 + * @regulator: Regulator of the device + * @powerstatus: Current POWER status of the device + * @interaction_ts: Timestamp of the last temperature read that is used + * for power management in jiffies + */ +struct mlx90635_data { + struct i2c_client *client; + struct mutex lock; + struct regmap *regmap; + struct regmap *regmap_ee; + u16 emissivity; + struct regulator *regulator; + int powerstatus; + unsigned long interaction_ts; +}; + +static const struct regmap_range mlx90635_volatile_reg_range[] = { + regmap_reg_range(MLX90635_REG_STATUS, MLX90635_REG_STATUS), + regmap_reg_range(MLX90635_RESULT_1, MLX90635_RESULT_5), + regmap_reg_range(MLX90635_REG_EE, MLX90635_REG_EE), + regmap_reg_range(MLX90635_REG_CMD, MLX90635_REG_CMD), + regmap_reg_range(MLX90635_REG_CTRL1, MLX90635_REG_CTRL2), +}; + +static const struct regmap_access_table mlx90635_volatile_regs_tbl = { + .yes_ranges = mlx90635_volatile_reg_range, + .n_yes_ranges = ARRAY_SIZE(mlx90635_volatile_reg_range), +}; + +static const struct regmap_range mlx90635_read_reg_range[] = { + regmap_reg_range(MLX90635_REG_STATUS, MLX90635_REG_STATUS), + regmap_reg_range(MLX90635_RESULT_1, MLX90635_RESULT_5), + regmap_reg_range(MLX90635_REG_EE, MLX90635_REG_EE), + regmap_reg_range(MLX90635_REG_CMD, MLX90635_REG_CMD), + regmap_reg_range(MLX90635_REG_CTRL1, MLX90635_REG_CTRL2), +}; + +static const struct regmap_access_table mlx90635_readable_regs_tbl = { + .yes_ranges = mlx90635_read_reg_range, + .n_yes_ranges = ARRAY_SIZE(mlx90635_read_reg_range), +}; + +static const struct regmap_range mlx90635_no_write_reg_range[] = { + regmap_reg_range(MLX90635_RESULT_1, MLX90635_RESULT_5), +}; + +static const struct regmap_access_table mlx90635_writeable_regs_tbl = { + .no_ranges = mlx90635_no_write_reg_range, + .n_no_ranges = ARRAY_SIZE(mlx90635_no_write_reg_range), +}; + +static const struct regmap_config mlx90635_regmap = { + .name = "mlx90635-registers", + .reg_stride = 1, + .reg_bits = 16, + .val_bits = 16, + + .volatile_table = &mlx90635_volatile_regs_tbl, + .rd_table = &mlx90635_readable_regs_tbl, + .wr_table = &mlx90635_writeable_regs_tbl, + + .use_single_read = true, + .use_single_write = true, + .can_multi_write = false, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct regmap_range mlx90635_read_ee_range[] = { + regmap_reg_range(MLX90635_EE_I2C_CFG, MLX90635_EE_CTRL2), + regmap_reg_range(MLX90635_EE_Ha, MLX90635_EE_Gb), +}; + +static const struct regmap_access_table mlx90635_readable_ees_tbl = { + .yes_ranges = mlx90635_read_ee_range, + .n_yes_ranges = ARRAY_SIZE(mlx90635_read_ee_range), +}; + +static const struct regmap_range mlx90635_no_write_ee_range[] = { + regmap_reg_range(MLX90635_ADDR_EEPROM, MLX90635_EE_Gb), +}; + +static const struct regmap_access_table mlx90635_writeable_ees_tbl = { + .no_ranges = mlx90635_no_write_ee_range, + .n_no_ranges = ARRAY_SIZE(mlx90635_no_write_ee_range), +}; + +static const struct regmap_config mlx90635_regmap_ee = { + .name = "mlx90635-eeprom", + .reg_stride = 1, + .reg_bits = 16, + .val_bits = 16, + + .volatile_table = NULL, + .rd_table = &mlx90635_readable_ees_tbl, + .wr_table = &mlx90635_writeable_ees_tbl, + + .use_single_read = true, + .use_single_write = true, + .can_multi_write = false, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .cache_type = REGCACHE_RBTREE, +}; + +/** + * mlx90635_reset_delay() - Give the mlx90635 some time to reset properly + * If this is not done, the following I2C command(s) will not be accepted. + */ +static void mlx90635_reset_delay(void) +{ + usleep_range(MLX90635_TIMING_RST_MIN, MLX90635_TIMING_RST_MAX); +} + +static int mlx90635_pwr_sleep_step(struct mlx90635_data *data) +{ + int ret; + + if (data->powerstatus == MLX90635_PWR_STATUS_SLEEP_STEP) + return 0; + + ret = regmap_write_bits(data->regmap, MLX90635_REG_CTRL2, MLX90635_CTRL2_MODE_MASK, + FIELD_PREP(MLX90635_CTRL2_MODE_MASK, MLX90635_PWR_STATUS_SLEEP_STEP)); + if (ret < 0) + return ret; + + data->powerstatus = MLX90635_PWR_STATUS_SLEEP_STEP; + return 0; +} + +static int mlx90635_pwr_continuous(struct mlx90635_data *data) +{ + int ret; + + if (data->powerstatus == MLX90635_PWR_STATUS_CONTINUOUS) + return 0; + + ret = regmap_write_bits(data->regmap, MLX90635_REG_CTRL2, MLX90635_CTRL2_MODE_MASK, + FIELD_PREP(MLX90635_CTRL2_MODE_MASK, MLX90635_PWR_STATUS_CONTINUOUS)); + if (ret < 0) + return ret; + + data->powerstatus = MLX90635_PWR_STATUS_CONTINUOUS; + return 0; +} + +static int mlx90635_read_ee_register(struct regmap *regmap, u16 reg_lsb, + s32 *reg_value) +{ + unsigned int read; + u32 value; + int ret; + + ret = regmap_read(regmap, reg_lsb + 2, &read); + if (ret < 0) + return ret; + + value = read; + + ret = regmap_read(regmap, reg_lsb, &read); + if (ret < 0) + return ret; + + *reg_value = (read << 16) | (value & 0xffff); + + return 0; +} + +static int mlx90635_read_ee_ambient(struct regmap *regmap, s16 *PG, s16 *PO, s16 *Gb) +{ + unsigned int read_tmp; + int ret; + + ret = regmap_read(regmap, MLX90635_EE_P_O, &read_tmp); + if (ret < 0) + return ret; + *PO = (s16)read_tmp; + + ret = regmap_read(regmap, MLX90635_EE_P_G, &read_tmp); + if (ret < 0) + return ret; + *PG = (s16)read_tmp; + + ret = regmap_read(regmap, MLX90635_EE_Gb, &read_tmp); + if (ret < 0) + return ret; + *Gb = (u16)read_tmp; + + return 0; +} + +static int mlx90635_read_ee_object(struct regmap *regmap, u32 *Ea, u32 *Eb, u32 *Fa, s16 *Fb, + s16 *Ga, s16 *Gb, s16 *Ha, s16 *Hb, u16 *Fa_scale) +{ + unsigned int read_tmp; + int ret; + + ret = mlx90635_read_ee_register(regmap, MLX90635_EE_Ea, Ea); + if (ret < 0) + return ret; + + ret = mlx90635_read_ee_register(regmap, MLX90635_EE_Eb, Eb); + if (ret < 0) + return ret; + + ret = mlx90635_read_ee_register(regmap, MLX90635_EE_Fa, Fa); + if (ret < 0) + return ret; + + ret = regmap_read(regmap, MLX90635_EE_Ha, &read_tmp); + if (ret < 0) + return ret; + *Ha = (s16)read_tmp; + + ret = regmap_read(regmap, MLX90635_EE_Hb, &read_tmp); + if (ret < 0) + return ret; + *Hb = (s16)read_tmp; + + ret = regmap_read(regmap, MLX90635_EE_Ga, &read_tmp); + if (ret < 0) + return ret; + *Ga = (s16)read_tmp; + + ret = regmap_read(regmap, MLX90635_EE_Gb, &read_tmp); + if (ret < 0) + return ret; + *Gb = (s16)read_tmp; + + ret = regmap_read(regmap, MLX90635_EE_Fb, &read_tmp); + if (ret < 0) + return ret; + *Fb = (s16)read_tmp; + + ret = regmap_read(regmap, MLX90635_EE_FASCALE, &read_tmp); + if (ret < 0) + return ret; + *Fa_scale = (u16)read_tmp; + + return 0; +} + +static int mlx90635_calculate_dataset_ready_time(struct mlx90635_data *data, int *refresh_time) +{ + unsigned int reg; + int ret; + + ret = regmap_read(data->regmap, MLX90635_REG_CTRL1, ®); + if (ret < 0) + return ret; + + *refresh_time = 2 * (MLX90635_MEAS_MAX_TIME >> FIELD_GET(MLX90635_CTRL1_REFRESH_RATE_MASK, reg)) + 80; + + return 0; +} + +static int mlx90635_perform_measurement_burst(struct mlx90635_data *data) +{ + unsigned int reg_status; + int refresh_time; + int ret; + + ret = regmap_write_bits(data->regmap, MLX90635_REG_STATUS, + MLX90635_STAT_END_CONV, MLX90635_STAT_END_CONV); + if (ret < 0) + return ret; + + ret = mlx90635_calculate_dataset_ready_time(data, &refresh_time); + if (ret < 0) + return ret; + + ret = regmap_write_bits(data->regmap, MLX90635_REG_CTRL2, + FIELD_PREP(MLX90635_CTRL2_SOB_MASK, 1), + FIELD_PREP(MLX90635_CTRL2_SOB_MASK, 1)); + if (ret < 0) + return ret; + + msleep(refresh_time); /* Wait minimum time for dataset to be ready */ + + ret = regmap_read_poll_timeout(data->regmap, MLX90635_REG_STATUS, reg_status, + (!(reg_status & MLX90635_STAT_END_CONV)) == 0, + MLX90635_TIMING_POLLING, MLX90635_READ_RETRIES * 10000); + if (ret < 0) { + dev_err(&data->client->dev, "data not ready"); + return -ETIMEDOUT; + } + + return 0; +} + +static int mlx90635_read_ambient_raw(struct regmap *regmap, + s16 *ambient_new_raw, s16 *ambient_old_raw) +{ + unsigned int read_tmp; + int ret; + + ret = regmap_read(regmap, MLX90635_RESULT_2, &read_tmp); + if (ret < 0) + return ret; + *ambient_new_raw = (s16)read_tmp; + + ret = regmap_read(regmap, MLX90635_RESULT_3, &read_tmp); + if (ret < 0) + return ret; + *ambient_old_raw = (s16)read_tmp; + + return 0; +} + +static int mlx90635_read_object_raw(struct regmap *regmap, s16 *object_raw) +{ + unsigned int read_tmp; + s16 read; + int ret; + + ret = regmap_read(regmap, MLX90635_RESULT_1, &read_tmp); + if (ret < 0) + return ret; + + read = (s16)read_tmp; + + ret = regmap_read(regmap, MLX90635_RESULT_4, &read_tmp); + if (ret < 0) + return ret; + *object_raw = (read - (s16)read_tmp) / 2; + + return 0; +} + +static int mlx90635_read_all_channel(struct mlx90635_data *data, + s16 *ambient_new_raw, s16 *ambient_old_raw, + s16 *object_raw) +{ + int ret; + + mutex_lock(&data->lock); + if (data->powerstatus == MLX90635_PWR_STATUS_SLEEP_STEP) { + /* Trigger measurement in Sleep Step mode */ + ret = mlx90635_perform_measurement_burst(data); + if (ret < 0) + goto read_unlock; + } + + ret = mlx90635_read_ambient_raw(data->regmap, ambient_new_raw, + ambient_old_raw); + if (ret < 0) + goto read_unlock; + + ret = mlx90635_read_object_raw(data->regmap, object_raw); +read_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static s64 mlx90635_preprocess_temp_amb(s16 ambient_new_raw, + s16 ambient_old_raw, s16 Gb) +{ + s64 VR_Ta, kGb, tmp; + + kGb = ((s64)Gb * 1000LL) >> 10ULL; + VR_Ta = (s64)ambient_old_raw * 1000000LL + + kGb * div64_s64(((s64)ambient_new_raw * 1000LL), + (MLX90635_PTAT_DIV)); + tmp = div64_s64( + div64_s64(((s64)ambient_new_raw * 1000000000000LL), + (MLX90635_PTAT_DIV)), VR_Ta); + return div64_s64(tmp << 19ULL, 1000LL); +} + +static s64 mlx90635_preprocess_temp_obj(s16 object_raw, + s16 ambient_new_raw, + s16 ambient_old_raw, s16 Gb) +{ + s64 VR_IR, kGb, tmp; + + kGb = ((s64)Gb * 1000LL) >> 10ULL; + VR_IR = (s64)ambient_old_raw * 1000000LL + + kGb * (div64_s64((s64)ambient_new_raw * 1000LL, + MLX90635_PTAT_DIV)); + tmp = div64_s64( + div64_s64((s64)(object_raw * 1000000LL), + MLX90635_IR_DIV) * 1000000LL, + VR_IR); + return div64_s64((tmp << 19ULL), 1000LL); +} + +static s32 mlx90635_calc_temp_ambient(s16 ambient_new_raw, s16 ambient_old_raw, + u16 P_G, u16 P_O, s16 Gb) +{ + s64 kPG, kPO, AMB; + + AMB = mlx90635_preprocess_temp_amb(ambient_new_raw, ambient_old_raw, + Gb); + kPG = ((s64)P_G * 1000000LL) >> 9ULL; + kPO = AMB - (((s64)P_O * 1000LL) >> 1ULL); + + return 30 * 1000LL + div64_s64(kPO * 1000000LL, kPG); +} + +static s32 mlx90635_calc_temp_object_iteration(s32 prev_object_temp, s64 object, + s64 TAdut, s64 TAdut4, s16 Ga, + u32 Fa, u16 Fa_scale, s16 Fb, + s16 Ha, s16 Hb, u16 emissivity) +{ + s64 calcedGa, calcedGb, calcedFa, Alpha_corr; + s64 Ha_customer, Hb_customer; + + Ha_customer = ((s64)Ha * 1000000LL) >> 14ULL; + Hb_customer = ((s64)Hb * 100) >> 10ULL; + + calcedGa = ((s64)((s64)Ga * (prev_object_temp - 35 * 1000LL) + * 1000LL)) >> 24LL; + calcedGb = ((s64)(Fb * (TAdut - 30 * 1000000LL))) >> 24LL; + + Alpha_corr = ((s64)((s64)Fa * Ha_customer * 10000LL) >> Fa_scale); + Alpha_corr *= ((s64)(1 * 1000000LL + calcedGa + calcedGb)); + + Alpha_corr = div64_s64(Alpha_corr, 1000LL); + Alpha_corr *= emissivity; + Alpha_corr = div64_s64(Alpha_corr, 100LL); + calcedFa = div64_s64((s64)object * 100000000000LL, Alpha_corr); + + return (int_sqrt64(int_sqrt64(calcedFa * 100000000LL + TAdut4)) + - 27315 - Hb_customer) * 10; +} + +static s64 mlx90635_calc_ta4(s64 TAdut, s64 scale) +{ + return (div64_s64(TAdut, scale) + 27315) * + (div64_s64(TAdut, scale) + 27315) * + (div64_s64(TAdut, scale) + 27315) * + (div64_s64(TAdut, scale) + 27315); +} + +static s32 mlx90635_calc_temp_object(s64 object, s64 ambient, u32 Ea, u32 Eb, + s16 Ga, u32 Fa, u16 Fa_scale, s16 Fb, s16 Ha, s16 Hb, + u16 tmp_emi) +{ + s64 kTA, kTA0, TAdut, TAdut4; + s64 temp = 35000; + s8 i; + + kTA = (Ea * 1000LL) >> 16LL; + kTA0 = (Eb * 1000LL) >> 8LL; + TAdut = div64_s64(((ambient - kTA0) * 1000000LL), kTA) + 30 * 1000000LL; + TAdut4 = mlx90635_calc_ta4(TAdut, 10000LL); + + /* Iterations of calculation as described in datasheet */ + for (i = 0; i < 5; ++i) { + temp = mlx90635_calc_temp_object_iteration(temp, object, TAdut, TAdut4, + Ga, Fa, Fa_scale, Fb, Ha, Hb, + tmp_emi); + } + return temp; +} + +static int mlx90635_calc_object(struct mlx90635_data *data, int *val) +{ + s16 ambient_new_raw, ambient_old_raw, object_raw; + s16 Fb, Ga, Gb, Ha, Hb; + s64 object, ambient; + u32 Ea, Eb, Fa; + u16 Fa_scale; + int ret; + + ret = mlx90635_read_ee_object(data->regmap_ee, &Ea, &Eb, &Fa, &Fb, &Ga, &Gb, &Ha, &Hb, &Fa_scale); + if (ret < 0) + return ret; + + ret = mlx90635_read_all_channel(data, + &ambient_new_raw, &ambient_old_raw, + &object_raw); + if (ret < 0) + return ret; + + ambient = mlx90635_preprocess_temp_amb(ambient_new_raw, + ambient_old_raw, Gb); + object = mlx90635_preprocess_temp_obj(object_raw, + ambient_new_raw, + ambient_old_raw, Gb); + + *val = mlx90635_calc_temp_object(object, ambient, Ea, Eb, Ga, Fa, Fa_scale, Fb, + Ha, Hb, data->emissivity); + return 0; +} + +static int mlx90635_calc_ambient(struct mlx90635_data *data, int *val) +{ + s16 ambient_new_raw, ambient_old_raw; + s16 PG, PO, Gb; + int ret; + + ret = mlx90635_read_ee_ambient(data->regmap_ee, &PG, &PO, &Gb); + if (ret < 0) + return ret; + + mutex_lock(&data->lock); + if (data->powerstatus == MLX90635_PWR_STATUS_SLEEP_STEP) { + ret = mlx90635_perform_measurement_burst(data); + if (ret < 0) + goto read_ambient_unlock; + } + + ret = mlx90635_read_ambient_raw(data->regmap, &ambient_new_raw, + &ambient_old_raw); +read_ambient_unlock: + mutex_unlock(&data->lock); + if (ret < 0) + return ret; + + *val = mlx90635_calc_temp_ambient(ambient_new_raw, ambient_old_raw, + PG, PO, Gb); + return ret; +} + +static int mlx90635_get_refresh_rate(struct mlx90635_data *data, + unsigned int *refresh_rate) +{ + unsigned int reg; + int ret; + + ret = regmap_read(data->regmap, MLX90635_REG_CTRL1, ®); + if (ret < 0) + return ret; + + *refresh_rate = FIELD_GET(MLX90635_CTRL1_REFRESH_RATE_MASK, reg); + + return 0; +} + +static const struct { + int val; + int val2; +} mlx90635_freqs[] = { + { 0, 200000 }, + { 0, 500000 }, + { 0, 900000 }, + { 1, 700000 }, + { 3, 0 }, + { 4, 800000 }, + { 6, 900000 }, + { 8, 900000 } +}; + +/** + * mlx90635_pm_interaction_wakeup() - Measure time between user interactions to change powermode + * @data: pointer to mlx90635_data object containing interaction_ts information + * + * Switch to continuous mode when interaction is faster than MLX90635_MEAS_MAX_TIME. Update the + * interaction_ts for each function call with the jiffies to enable measurement between function + * calls. Initial value of the interaction_ts needs to be set before this function call. + */ +static int mlx90635_pm_interaction_wakeup(struct mlx90635_data *data) +{ + unsigned long now; + int ret; + + now = jiffies; + if (time_in_range(now, data->interaction_ts, + data->interaction_ts + + msecs_to_jiffies(MLX90635_MEAS_MAX_TIME + 100))) { + ret = mlx90635_pwr_continuous(data); + if (ret < 0) + return ret; + } + + data->interaction_ts = now; + + return 0; +} + +static int mlx90635_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct mlx90635_data *data = iio_priv(indio_dev); + int ret; + int cr; + + pm_runtime_get_sync(&data->client->dev); + ret = mlx90635_pm_interaction_wakeup(data); + if (ret < 0) + goto mlx90635_read_raw_pm; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + switch (channel->channel2) { + case IIO_MOD_TEMP_AMBIENT: + ret = mlx90635_calc_ambient(data, val); + if (ret < 0) + goto mlx90635_read_raw_pm; + + ret = IIO_VAL_INT; + break; + case IIO_MOD_TEMP_OBJECT: + ret = mlx90635_calc_object(data, val); + if (ret < 0) + goto mlx90635_read_raw_pm; + + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + break; + case IIO_CHAN_INFO_CALIBEMISSIVITY: + if (data->emissivity == 1000) { + *val = 1; + *val2 = 0; + } else { + *val = 0; + *val2 = data->emissivity * 1000; + } + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = mlx90635_get_refresh_rate(data, &cr); + if (ret < 0) + goto mlx90635_read_raw_pm; + + *val = mlx90635_freqs[cr].val; + *val2 = mlx90635_freqs[cr].val2; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + break; + } + +mlx90635_read_raw_pm: + pm_runtime_mark_last_busy(&data->client->dev); + pm_runtime_put_autosuspend(&data->client->dev); + return ret; +} + +static int mlx90635_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int val, + int val2, long mask) +{ + struct mlx90635_data *data = iio_priv(indio_dev); + int ret; + int i; + + switch (mask) { + case IIO_CHAN_INFO_CALIBEMISSIVITY: + /* Confirm we are within 0 and 1.0 */ + if (val < 0 || val2 < 0 || val > 1 || + (val == 1 && val2 != 0)) + return -EINVAL; + data->emissivity = val * 1000 + val2 / 1000; + return 0; + case IIO_CHAN_INFO_SAMP_FREQ: + for (i = 0; i < ARRAY_SIZE(mlx90635_freqs); i++) { + if (val == mlx90635_freqs[i].val && + val2 == mlx90635_freqs[i].val2) + break; + } + if (i == ARRAY_SIZE(mlx90635_freqs)) + return -EINVAL; + + ret = regmap_write_bits(data->regmap, MLX90635_REG_CTRL1, + MLX90635_CTRL1_REFRESH_RATE_MASK, i); + + return ret; + default: + return -EINVAL; + } +} + +static int mlx90635_read_avail(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_SAMP_FREQ: + *vals = (int *)mlx90635_freqs; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = 2 * ARRAY_SIZE(mlx90635_freqs); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec mlx90635_channels[] = { + { + .type = IIO_TEMP, + .modified = 1, + .channel2 = IIO_MOD_TEMP_AMBIENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + }, + { + .type = IIO_TEMP, + .modified = 1, + .channel2 = IIO_MOD_TEMP_OBJECT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_CALIBEMISSIVITY), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + }, +}; + +static const struct iio_info mlx90635_info = { + .read_raw = mlx90635_read_raw, + .write_raw = mlx90635_write_raw, + .read_avail = mlx90635_read_avail, +}; + +static void mlx90635_sleep(void *_data) +{ + struct mlx90635_data *data = _data; + + mlx90635_pwr_sleep_step(data); +} + +static int mlx90635_suspend(struct mlx90635_data *data) +{ + return mlx90635_pwr_sleep_step(data); +} + +static int mlx90635_wakeup(struct mlx90635_data *data) +{ + s16 Fb, Ga, Gb, Ha, Hb, PG, PO; + unsigned int dsp_version; + u32 Ea, Eb, Fa; + u16 Fa_scale; + int ret; + + regcache_cache_bypass(data->regmap_ee, false); + regcache_cache_only(data->regmap_ee, false); + regcache_cache_only(data->regmap, false); + + ret = mlx90635_pwr_continuous(data); + if (ret < 0) { + dev_err(&data->client->dev, "Switch to continuous mode failed\n"); + return ret; + } + ret = regmap_write_bits(data->regmap, MLX90635_REG_EE, + MLX90635_EE_ACTIVE, MLX90635_EE_ACTIVE); + if (ret < 0) { + dev_err(&data->client->dev, "Powering EEPROM failed\n"); + return ret; + } + usleep_range(MLX90635_TIMING_EE_ACTIVE_MIN, MLX90635_TIMING_EE_ACTIVE_MAX); + + regcache_mark_dirty(data->regmap_ee); + + ret = regcache_sync(data->regmap_ee); + if (ret < 0) { + dev_err(&data->client->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + + ret = mlx90635_read_ee_ambient(data->regmap_ee, &PG, &PO, &Gb); + if (ret < 0) { + dev_err(&data->client->dev, + "Failed to read to cache Ambient coefficients EEPROM region: %d\n", ret); + return ret; + } + + ret = mlx90635_read_ee_object(data->regmap_ee, &Ea, &Eb, &Fa, &Fb, &Ga, &Gb, &Ha, &Hb, &Fa_scale); + if (ret < 0) { + dev_err(&data->client->dev, + "Failed to read to cache Object coefficients EEPROM region: %d\n", ret); + return ret; + } + + ret = regmap_read(data->regmap_ee, MLX90635_EE_VERSION, &dsp_version); + if (ret < 0) { + dev_err(&data->client->dev, + "Failed to read to cache of EEPROM version: %d\n", ret); + return ret; + } + + regcache_cache_only(data->regmap_ee, true); + + return ret; +} + +static void mlx90635_disable_regulator(void *_data) +{ + struct mlx90635_data *data = _data; + int ret; + + ret = regulator_disable(data->regulator); + if (ret < 0) + dev_err(regmap_get_device(data->regmap), + "Failed to disable power regulator: %d\n", ret); +} + +static int mlx90635_enable_regulator(struct mlx90635_data *data) +{ + int ret; + + ret = regulator_enable(data->regulator); + if (ret < 0) { + dev_err(regmap_get_device(data->regmap), "Failed to enable power regulator!\n"); + return ret; + } + + mlx90635_reset_delay(); + + return ret; +} + +static int mlx90635_probe(struct i2c_client *client) +{ + struct mlx90635_data *mlx90635; + struct iio_dev *indio_dev; + unsigned int dsp_version; + struct regmap *regmap; + struct regmap *regmap_ee; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*mlx90635)); + if (!indio_dev) + return dev_err_probe(&client->dev, -ENOMEM, "failed to allocate device\n"); + + regmap = devm_regmap_init_i2c(client, &mlx90635_regmap); + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), + "failed to allocate regmap\n"); + + regmap_ee = devm_regmap_init_i2c(client, &mlx90635_regmap_ee); + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), + "failed to allocate regmap\n"); + + mlx90635 = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + mlx90635->client = client; + mlx90635->regmap = regmap; + mlx90635->regmap_ee = regmap_ee; + mlx90635->powerstatus = MLX90635_PWR_STATUS_SLEEP_STEP; + + mutex_init(&mlx90635->lock); + indio_dev->name = "mlx90635"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mlx90635_info; + indio_dev->channels = mlx90635_channels; + indio_dev->num_channels = ARRAY_SIZE(mlx90635_channels); + + mlx90635->regulator = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(mlx90635->regulator)) + return dev_err_probe(&client->dev, PTR_ERR(mlx90635->regulator), + "failed to get vdd regulator"); + + ret = mlx90635_enable_regulator(mlx90635); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&client->dev, mlx90635_disable_regulator, + mlx90635); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "failed to setup regulator cleanup action\n"); + + ret = mlx90635_wakeup(mlx90635); + if (ret < 0) + return dev_err_probe(&client->dev, ret, "wakeup failed\n"); + + ret = devm_add_action_or_reset(&client->dev, mlx90635_sleep, mlx90635); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "failed to setup low power cleanup\n"); + + ret = regmap_read(mlx90635->regmap_ee, MLX90635_EE_VERSION, &dsp_version); + if (ret < 0) + return dev_err_probe(&client->dev, ret, "read of version failed\n"); + + dsp_version = dsp_version & MLX90635_VERSION_MASK; + + if (FIELD_GET(MLX90635_DSP_FIXED, dsp_version)) { + if (MLX90635_DSP_VERSION(dsp_version) == MLX90635_ID_DSPv1) { + dev_dbg(&client->dev, + "Detected DSP v1 calibration %x\n", dsp_version); + } else { + dev_dbg(&client->dev, + "Detected Unknown EEPROM calibration %lx\n", + MLX90635_DSP_VERSION(dsp_version)); + } + } else { + return dev_err_probe(&client->dev, -EPROTONOSUPPORT, + "Wrong fixed top bit %x (expected 0x8X0X)\n", + dsp_version); + } + + mlx90635->emissivity = 1000; + mlx90635->interaction_ts = jiffies; /* Set initial value */ + + pm_runtime_get_noresume(&client->dev); + pm_runtime_set_active(&client->dev); + + ret = devm_pm_runtime_enable(&client->dev); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to enable powermanagement\n"); + + pm_runtime_set_autosuspend_delay(&client->dev, MLX90635_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id mlx90635_id[] = { + { "mlx90635" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mlx90635_id); + +static const struct of_device_id mlx90635_of_match[] = { + { .compatible = "melexis,mlx90635" }, + { } +}; +MODULE_DEVICE_TABLE(of, mlx90635_of_match); + +static int mlx90635_pm_suspend(struct device *dev) +{ + struct mlx90635_data *data = iio_priv(dev_get_drvdata(dev)); + int ret; + + ret = mlx90635_suspend(data); + if (ret < 0) + return ret; + + ret = regulator_disable(data->regulator); + if (ret < 0) + dev_err(regmap_get_device(data->regmap), + "Failed to disable power regulator: %d\n", ret); + + return ret; +} + +static int mlx90635_pm_resume(struct device *dev) +{ + struct mlx90635_data *data = iio_priv(dev_get_drvdata(dev)); + int ret; + + ret = mlx90635_enable_regulator(data); + if (ret < 0) + return ret; + + return mlx90635_wakeup(data); +} + +static int mlx90635_pm_runtime_suspend(struct device *dev) +{ + struct mlx90635_data *data = iio_priv(dev_get_drvdata(dev)); + + return mlx90635_pwr_sleep_step(data); +} + +static const struct dev_pm_ops mlx90635_pm_ops = { + SYSTEM_SLEEP_PM_OPS(mlx90635_pm_suspend, mlx90635_pm_resume) + RUNTIME_PM_OPS(mlx90635_pm_runtime_suspend, NULL, NULL) +}; + +static struct i2c_driver mlx90635_driver = { + .driver = { + .name = "mlx90635", + .of_match_table = mlx90635_of_match, + .pm = pm_ptr(&mlx90635_pm_ops), + }, + .probe = mlx90635_probe, + .id_table = mlx90635_id, +}; +module_i2c_driver(mlx90635_driver); + +MODULE_AUTHOR("Crt Mori <cmo@melexis.com>"); +MODULE_DESCRIPTION("Melexis MLX90635 contactless Infra Red temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/input.c b/drivers/input/input.c index 8c5fdb0f858a..f71ea4fb173f 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1365,8 +1365,8 @@ static ssize_t input_dev_show_##name(struct device *dev, \ { \ struct input_dev *input_dev = to_input_dev(dev); \ \ - return scnprintf(buf, PAGE_SIZE, "%s\n", \ - input_dev->name ? input_dev->name : ""); \ + return sysfs_emit(buf, "%s\n", \ + input_dev->name ? input_dev->name : ""); \ } \ static DEVICE_ATTR(name, S_IRUGO, input_dev_show_##name, NULL) @@ -1458,7 +1458,7 @@ static ssize_t inhibited_show(struct device *dev, { struct input_dev *input_dev = to_input_dev(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", input_dev->inhibited); + return sysfs_emit(buf, "%d\n", input_dev->inhibited); } static ssize_t inhibited_store(struct device *dev, @@ -1505,7 +1505,7 @@ static ssize_t input_dev_show_id_##name(struct device *dev, \ char *buf) \ { \ struct input_dev *input_dev = to_input_dev(dev); \ - return scnprintf(buf, PAGE_SIZE, "%04x\n", input_dev->id.name); \ + return sysfs_emit(buf, "%04x\n", input_dev->id.name); \ } \ static DEVICE_ATTR(name, S_IRUGO, input_dev_show_id_##name, NULL) diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index ac6925ce8366..7755e5b454d2 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -412,4 +412,14 @@ config JOYSTICK_SENSEHAT To compile this driver as a module, choose M here: the module will be called sensehat_joystick. +config JOYSTICK_SEESAW + tristate "Adafruit Mini I2C Gamepad with Seesaw" + depends on I2C + select INPUT_SPARSEKMAP + help + Say Y here if you want to use the Adafruit Mini I2C Gamepad. + + To compile this driver as a module, choose M here: the module will be + called adafruit-seesaw. + endif diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile index 3937535f0098..9976f596a920 100644 --- a/drivers/input/joystick/Makefile +++ b/drivers/input/joystick/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_JOYSTICK_N64) += n64joy.o obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o obj-$(CONFIG_JOYSTICK_QWIIC) += qwiic-joystick.o +obj-$(CONFIG_JOYSTICK_SEESAW) += adafruit-seesaw.o obj-$(CONFIG_JOYSTICK_SENSEHAT) += sensehat-joystick.o obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o diff --git a/drivers/input/joystick/adafruit-seesaw.c b/drivers/input/joystick/adafruit-seesaw.c new file mode 100644 index 000000000000..1b9279f024cc --- /dev/null +++ b/drivers/input/joystick/adafruit-seesaw.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com> + * + * Driver for Adafruit Mini I2C Gamepad + * + * Based on the work of: + * Oleh Kravchenko (Sparkfun Qwiic Joystick driver) + * + * Datasheet: https://cdn-learn.adafruit.com/downloads/pdf/gamepad-qt.pdf + * Product page: https://www.adafruit.com/product/5743 + * Firmware and hardware sources: https://github.com/adafruit/Adafruit_Seesaw + * + * TODO: + * - Add interrupt support + */ + +#include <asm/unaligned.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#define SEESAW_DEVICE_NAME "seesaw-gamepad" + +#define SEESAW_ADC_BASE 0x0900 + +#define SEESAW_GPIO_DIRCLR_BULK 0x0103 +#define SEESAW_GPIO_BULK 0x0104 +#define SEESAW_GPIO_BULK_SET 0x0105 +#define SEESAW_GPIO_PULLENSET 0x010b + +#define SEESAW_STATUS_HW_ID 0x0001 +#define SEESAW_STATUS_SWRST 0x007f + +#define SEESAW_ADC_OFFSET 0x07 + +#define SEESAW_BUTTON_A 0x05 +#define SEESAW_BUTTON_B 0x01 +#define SEESAW_BUTTON_X 0x06 +#define SEESAW_BUTTON_Y 0x02 +#define SEESAW_BUTTON_START 0x10 +#define SEESAW_BUTTON_SELECT 0x00 + +#define SEESAW_ANALOG_X 0x0e +#define SEESAW_ANALOG_Y 0x0f + +#define SEESAW_JOYSTICK_MAX_AXIS 1023 +#define SEESAW_JOYSTICK_FUZZ 2 +#define SEESAW_JOYSTICK_FLAT 4 + +#define SEESAW_GAMEPAD_POLL_INTERVAL_MS 16 +#define SEESAW_GAMEPAD_POLL_MIN 8 +#define SEESAW_GAMEPAD_POLL_MAX 32 + +static const unsigned long SEESAW_BUTTON_MASK = + BIT(SEESAW_BUTTON_A) | BIT(SEESAW_BUTTON_B) | BIT(SEESAW_BUTTON_X) | + BIT(SEESAW_BUTTON_Y) | BIT(SEESAW_BUTTON_START) | + BIT(SEESAW_BUTTON_SELECT); + +struct seesaw_gamepad { + struct input_dev *input_dev; + struct i2c_client *i2c_client; +}; + +struct seesaw_data { + u16 x; + u16 y; + u32 button_state; +}; + +static const struct key_entry seesaw_buttons_new[] = { + { KE_KEY, SEESAW_BUTTON_A, .keycode = BTN_SOUTH }, + { KE_KEY, SEESAW_BUTTON_B, .keycode = BTN_EAST }, + { KE_KEY, SEESAW_BUTTON_X, .keycode = BTN_NORTH }, + { KE_KEY, SEESAW_BUTTON_Y, .keycode = BTN_WEST }, + { KE_KEY, SEESAW_BUTTON_START, .keycode = BTN_START }, + { KE_KEY, SEESAW_BUTTON_SELECT, .keycode = BTN_SELECT }, + { KE_END, 0 } +}; + +static int seesaw_register_read(struct i2c_client *client, u16 reg, void *buf, + int count) +{ + __be16 register_buf = cpu_to_be16(reg); + struct i2c_msg message_buf[2] = { + { + .addr = client->addr, + .flags = client->flags, + .len = sizeof(register_buf), + .buf = (u8 *)®ister_buf, + }, + { + .addr = client->addr, + .flags = client->flags | I2C_M_RD, + .len = count, + .buf = (u8 *)buf, + }, + }; + int ret; + + ret = i2c_transfer(client->adapter, message_buf, + ARRAY_SIZE(message_buf)); + if (ret < 0) + return ret; + + return 0; +} + +static int seesaw_register_write_u8(struct i2c_client *client, u16 reg, + u8 value) +{ + u8 write_buf[sizeof(reg) + sizeof(value)]; + int ret; + + put_unaligned_be16(reg, write_buf); + write_buf[sizeof(reg)] = value; + + ret = i2c_master_send(client, write_buf, sizeof(write_buf)); + if (ret < 0) + return ret; + + return 0; +} + +static int seesaw_register_write_u32(struct i2c_client *client, u16 reg, + u32 value) +{ + u8 write_buf[sizeof(reg) + sizeof(value)]; + int ret; + + put_unaligned_be16(reg, write_buf); + put_unaligned_be32(value, write_buf + sizeof(reg)); + ret = i2c_master_send(client, write_buf, sizeof(write_buf)); + if (ret < 0) + return ret; + + return 0; +} + +static int seesaw_read_data(struct i2c_client *client, struct seesaw_data *data) +{ + __be16 adc_data; + __be32 read_buf; + int err; + + err = seesaw_register_read(client, SEESAW_GPIO_BULK, + &read_buf, sizeof(read_buf)); + if (err) + return err; + + data->button_state = ~be32_to_cpu(read_buf); + + err = seesaw_register_read(client, + SEESAW_ADC_BASE | + (SEESAW_ADC_OFFSET + SEESAW_ANALOG_X), + &adc_data, sizeof(adc_data)); + if (err) + return err; + /* + * ADC reads left as max and right as 0, must be reversed since kernel + * expects reports in opposite order. + */ + data->x = SEESAW_JOYSTICK_MAX_AXIS - be16_to_cpu(adc_data); + + err = seesaw_register_read(client, + SEESAW_ADC_BASE | + (SEESAW_ADC_OFFSET + SEESAW_ANALOG_Y), + &adc_data, sizeof(adc_data)); + if (err) + return err; + + data->y = be16_to_cpu(adc_data); + + return 0; +} + +static void seesaw_poll(struct input_dev *input) +{ + struct seesaw_gamepad *private = input_get_drvdata(input); + struct seesaw_data data; + int err, i; + + err = seesaw_read_data(private->i2c_client, &data); + if (err) { + dev_err_ratelimited(&input->dev, + "failed to read joystick state: %d\n", err); + return; + } + + input_report_abs(input, ABS_X, data.x); + input_report_abs(input, ABS_Y, data.y); + + for_each_set_bit(i, &SEESAW_BUTTON_MASK, + BITS_PER_TYPE(SEESAW_BUTTON_MASK)) { + if (!sparse_keymap_report_event(input, i, + data.button_state & BIT(i), + false)) + dev_err_ratelimited(&input->dev, + "failed to report keymap event"); + } + + input_sync(input); +} + +static int seesaw_probe(struct i2c_client *client) +{ + struct seesaw_gamepad *seesaw; + u8 hardware_id; + int err; + + err = seesaw_register_write_u8(client, SEESAW_STATUS_SWRST, 0xFF); + if (err) + return err; + + /* Wait for the registers to reset before proceeding */ + usleep_range(10000, 15000); + + seesaw = devm_kzalloc(&client->dev, sizeof(*seesaw), GFP_KERNEL); + if (!seesaw) + return -ENOMEM; + + err = seesaw_register_read(client, SEESAW_STATUS_HW_ID, + &hardware_id, sizeof(hardware_id)); + if (err) + return err; + + dev_dbg(&client->dev, "Adafruit Seesaw Gamepad, Hardware ID: %02x\n", + hardware_id); + + /* Set Pin Mode to input and enable pull-up resistors */ + err = seesaw_register_write_u32(client, SEESAW_GPIO_DIRCLR_BULK, + SEESAW_BUTTON_MASK); + if (err) + return err; + err = seesaw_register_write_u32(client, SEESAW_GPIO_PULLENSET, + SEESAW_BUTTON_MASK); + if (err) + return err; + err = seesaw_register_write_u32(client, SEESAW_GPIO_BULK_SET, + SEESAW_BUTTON_MASK); + if (err) + return err; + + seesaw->i2c_client = client; + seesaw->input_dev = devm_input_allocate_device(&client->dev); + if (!seesaw->input_dev) + return -ENOMEM; + + seesaw->input_dev->id.bustype = BUS_I2C; + seesaw->input_dev->name = "Adafruit Seesaw Gamepad"; + seesaw->input_dev->phys = "i2c/" SEESAW_DEVICE_NAME; + input_set_drvdata(seesaw->input_dev, seesaw); + input_set_abs_params(seesaw->input_dev, ABS_X, + 0, SEESAW_JOYSTICK_MAX_AXIS, + SEESAW_JOYSTICK_FUZZ, SEESAW_JOYSTICK_FLAT); + input_set_abs_params(seesaw->input_dev, ABS_Y, + 0, SEESAW_JOYSTICK_MAX_AXIS, + SEESAW_JOYSTICK_FUZZ, SEESAW_JOYSTICK_FLAT); + + err = sparse_keymap_setup(seesaw->input_dev, seesaw_buttons_new, NULL); + if (err) { + dev_err(&client->dev, + "failed to set up input device keymap: %d\n", err); + return err; + } + + err = input_setup_polling(seesaw->input_dev, seesaw_poll); + if (err) { + dev_err(&client->dev, "failed to set up polling: %d\n", err); + return err; + } + + input_set_poll_interval(seesaw->input_dev, + SEESAW_GAMEPAD_POLL_INTERVAL_MS); + input_set_max_poll_interval(seesaw->input_dev, SEESAW_GAMEPAD_POLL_MAX); + input_set_min_poll_interval(seesaw->input_dev, SEESAW_GAMEPAD_POLL_MIN); + + err = input_register_device(seesaw->input_dev); + if (err) { + dev_err(&client->dev, "failed to register joystick: %d\n", err); + return err; + } + + return 0; +} + +static const struct i2c_device_id seesaw_id_table[] = { + { SEESAW_DEVICE_NAME }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, seesaw_id_table); + +static const struct of_device_id seesaw_of_table[] = { + { .compatible = "adafruit,seesaw-gamepad"}, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, seesaw_of_table); + +static struct i2c_driver seesaw_driver = { + .driver = { + .name = SEESAW_DEVICE_NAME, + .of_match_table = seesaw_of_table, + }, + .id_table = seesaw_id_table, + .probe = seesaw_probe, +}; +module_i2c_driver(seesaw_driver); + +MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>"); +MODULE_DESCRIPTION("Adafruit Mini I2C Gamepad driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c index bf8b1cc0ea9c..f1822c19a289 100644 --- a/drivers/input/joystick/as5011.c +++ b/drivers/input/joystick/as5011.c @@ -13,7 +13,7 @@ #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/input.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include <linux/input/as5011.h> #include <linux/slab.h> @@ -61,7 +61,7 @@ MODULE_LICENSE("GPL"); struct as5011_device { struct input_dev *input_dev; struct i2c_client *i2c_client; - unsigned int button_gpio; + struct gpio_desc *button_gpiod; unsigned int button_irq; unsigned int axis_irq; }; @@ -114,7 +114,7 @@ static int as5011_i2c_read(struct i2c_client *client, static irqreturn_t as5011_button_interrupt(int irq, void *dev_id) { struct as5011_device *as5011 = dev_id; - int val = gpio_get_value_cansleep(as5011->button_gpio); + int val = gpiod_get_value_cansleep(as5011->button_gpiod); input_report_key(as5011->input_dev, BTN_JOYSTICK, !val); input_sync(as5011->input_dev); @@ -248,7 +248,6 @@ static int as5011_probe(struct i2c_client *client) as5011->i2c_client = client; as5011->input_dev = input_dev; - as5011->button_gpio = plat_data->button_gpio; as5011->axis_irq = plat_data->axis_irq; input_dev->name = "Austria Microsystem as5011 joystick"; @@ -262,18 +261,20 @@ static int as5011_probe(struct i2c_client *client) input_set_abs_params(as5011->input_dev, ABS_Y, AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT); - error = gpio_request(as5011->button_gpio, "AS5011 button"); - if (error < 0) { - dev_err(&client->dev, "Failed to request button gpio\n"); + as5011->button_gpiod = devm_gpiod_get(&client->dev, NULL, GPIOD_IN); + if (IS_ERR(as5011->button_gpiod)) { + error = PTR_ERR(as5011->button_gpiod); + dev_err(&client->dev, "Failed to request button GPIO\n"); goto err_free_mem; } + gpiod_set_consumer_name(as5011->button_gpiod, "AS5011 button"); - irq = gpio_to_irq(as5011->button_gpio); + irq = gpiod_to_irq(as5011->button_gpiod); if (irq < 0) { dev_err(&client->dev, "Failed to get irq number for button gpio\n"); error = irq; - goto err_free_button_gpio; + goto err_free_mem; } as5011->button_irq = irq; @@ -286,7 +287,7 @@ static int as5011_probe(struct i2c_client *client) if (error < 0) { dev_err(&client->dev, "Can't allocate button irq %d\n", as5011->button_irq); - goto err_free_button_gpio; + goto err_free_mem; } error = as5011_configure_chip(as5011, plat_data); @@ -317,8 +318,6 @@ err_free_axis_irq: free_irq(as5011->axis_irq, as5011); err_free_button_irq: free_irq(as5011->button_irq, as5011); -err_free_button_gpio: - gpio_free(as5011->button_gpio); err_free_mem: input_free_device(input_dev); kfree(as5011); @@ -332,7 +331,6 @@ static void as5011_remove(struct i2c_client *client) free_irq(as5011->axis_irq, as5011); free_irq(as5011->button_irq, as5011); - gpio_free(as5011->button_gpio); input_unregister_device(as5011->input_dev); kfree(as5011); diff --git a/drivers/input/joystick/pxrc.c b/drivers/input/joystick/pxrc.c index ea2bf5951d67..52d9eab667b7 100644 --- a/drivers/input/joystick/pxrc.c +++ b/drivers/input/joystick/pxrc.c @@ -5,15 +5,17 @@ * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com> */ -#include <linux/kernel.h> +#include <linux/cleanup.h> #include <linux/errno.h> -#include <linux/slab.h> +#include <linux/input.h> +#include <linux/kernel.h> #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> #include <linux/uaccess.h> + #include <linux/usb.h> #include <linux/usb/input.h> -#include <linux/mutex.h> -#include <linux/input.h> #define PXRC_VENDOR_ID 0x1781 #define PXRC_PRODUCT_ID 0x0898 @@ -81,33 +83,28 @@ exit: static int pxrc_open(struct input_dev *input) { struct pxrc *pxrc = input_get_drvdata(input); - int retval; + int error; - mutex_lock(&pxrc->pm_mutex); - retval = usb_submit_urb(pxrc->urb, GFP_KERNEL); - if (retval) { + guard(mutex)(&pxrc->pm_mutex); + error = usb_submit_urb(pxrc->urb, GFP_KERNEL); + if (error) { dev_err(&pxrc->intf->dev, "%s - usb_submit_urb failed, error: %d\n", - __func__, retval); - retval = -EIO; - goto out; + __func__, error); + return -EIO; } pxrc->is_open = true; - -out: - mutex_unlock(&pxrc->pm_mutex); - return retval; + return 0; } static void pxrc_close(struct input_dev *input) { struct pxrc *pxrc = input_get_drvdata(input); - mutex_lock(&pxrc->pm_mutex); + guard(mutex)(&pxrc->pm_mutex); usb_kill_urb(pxrc->urb); pxrc->is_open = false; - mutex_unlock(&pxrc->pm_mutex); } static void pxrc_free_urb(void *_pxrc) @@ -208,10 +205,9 @@ static int pxrc_suspend(struct usb_interface *intf, pm_message_t message) { struct pxrc *pxrc = usb_get_intfdata(intf); - mutex_lock(&pxrc->pm_mutex); + guard(mutex)(&pxrc->pm_mutex); if (pxrc->is_open) usb_kill_urb(pxrc->urb); - mutex_unlock(&pxrc->pm_mutex); return 0; } @@ -219,14 +215,12 @@ static int pxrc_suspend(struct usb_interface *intf, pm_message_t message) static int pxrc_resume(struct usb_interface *intf) { struct pxrc *pxrc = usb_get_intfdata(intf); - int retval = 0; - mutex_lock(&pxrc->pm_mutex); + guard(mutex)(&pxrc->pm_mutex); if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0) - retval = -EIO; + return -EIO; - mutex_unlock(&pxrc->pm_mutex); - return retval; + return 0; } static int pxrc_pre_reset(struct usb_interface *intf) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index e2c1848182de..7c4b2a5cc1b5 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -294,6 +294,7 @@ static const struct xpad_device { { 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 }, { 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 }, { 0x1689, 0xfe00, "Razer Sabertooth", 0, XTYPE_XBOX360 }, + { 0x17ef, 0x6182, "Lenovo Legion Controller for Windows", 0, XTYPE_XBOX360 }, { 0x1949, 0x041a, "Amazon Game Controller", 0, XTYPE_XBOX360 }, { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 }, { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, @@ -491,6 +492,7 @@ static const struct usb_device_id xpad_table[] = { XPAD_XBOX360_VENDOR(0x15e4), /* Numark Xbox 360 controllers */ XPAD_XBOX360_VENDOR(0x162e), /* Joytech Xbox 360 controllers */ XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */ + XPAD_XBOX360_VENDOR(0x17ef), /* Lenovo */ XPAD_XBOX360_VENDOR(0x1949), /* Amazon controllers */ XPAD_XBOX360_VENDOR(0x1bad), /* Harmonix Rock Band guitar and drums */ XPAD_XBOX360_VENDOR(0x20d6), /* PowerA controllers */ @@ -1670,7 +1672,7 @@ static int xpad_led_probe(struct usb_xpad *xpad) if (!led) return -ENOMEM; - xpad->pad_nr = ida_simple_get(&xpad_pad_seq, 0, 0, GFP_KERNEL); + xpad->pad_nr = ida_alloc(&xpad_pad_seq, GFP_KERNEL); if (xpad->pad_nr < 0) { error = xpad->pad_nr; goto err_free_mem; @@ -1693,7 +1695,7 @@ static int xpad_led_probe(struct usb_xpad *xpad) return 0; err_free_id: - ida_simple_remove(&xpad_pad_seq, xpad->pad_nr); + ida_free(&xpad_pad_seq, xpad->pad_nr); err_free_mem: kfree(led); xpad->led = NULL; @@ -1706,7 +1708,7 @@ static void xpad_led_disconnect(struct usb_xpad *xpad) if (xpad_led) { led_classdev_unregister(&xpad_led->led_cdev); - ida_simple_remove(&xpad_pad_seq, xpad->pad_nr); + ida_free(&xpad_pad_seq, xpad->pad_nr); kfree(xpad_led); } } diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 786f00f6b7fd..7f67f9f2946b 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -791,9 +791,9 @@ static bool atkbd_is_portable_device(void) * not work. So in this case simply assume a keyboard is connected to avoid * confusing some laptop keyboards. * - * Skipping ATKBD_CMD_GETID ends up using a fake keyboard id. Using a fake id is - * ok in translated mode, only atkbd_select_set() checks atkbd->id and in - * translated mode that is a no-op. + * Skipping ATKBD_CMD_GETID ends up using a fake keyboard id. Using the standard + * 0xab83 id is ok in translated mode, only atkbd_select_set() checks atkbd->id + * and in translated mode that is a no-op. */ static bool atkbd_skip_getid(struct atkbd *atkbd) { @@ -824,6 +824,11 @@ static int atkbd_probe(struct atkbd *atkbd) "keyboard reset failed on %s\n", ps2dev->serio->phys); + if (atkbd_skip_getid(atkbd)) { + atkbd->id = 0xab83; + goto deactivate_kbd; + } + /* * Then we check the keyboard ID. We should get 0xab83 under normal conditions. * Some keyboards report different values, but the first byte is always 0xab or @@ -832,10 +837,10 @@ static int atkbd_probe(struct atkbd *atkbd) */ param[0] = param[1] = 0xa5; /* initialize with invalid values */ - if (atkbd_skip_getid(atkbd) || ps2_command(ps2dev, param, ATKBD_CMD_GETID)) { + if (ps2_command(ps2dev, param, ATKBD_CMD_GETID)) { /* - * If the get ID command was skipped or failed, we check if we can at least set + * If the get ID command failed, we check if we can at least set * the LEDs on the keyboard. This should work on every keyboard out there. * It also turns the LEDs off, which we want anyway. */ @@ -858,6 +863,7 @@ static int atkbd_probe(struct atkbd *atkbd) return -1; } +deactivate_kbd: /* * Make sure nothing is coming from the keyboard and disturbs our * internal state. diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index 1b4937dce672..52fba9ee7c1d 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -10,10 +10,11 @@ #include <linux/interrupt.h> #include <linux/input.h> #include <linux/leds.h> -#include <linux/of_irq.h> +#include <linux/of.h> #include <linux/regmap.h> #include <linux/i2c.h> #include <linux/gpio/consumer.h> +#include <linux/bitfield.h> #define CAP11XX_REG_MAIN_CONTROL 0x00 #define CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT (6) @@ -24,6 +25,7 @@ #define CAP11XX_REG_NOISE_FLAG_STATUS 0x0a #define CAP11XX_REG_SENOR_DELTA(X) (0x10 + (X)) #define CAP11XX_REG_SENSITIVITY_CONTROL 0x1f +#define CAP11XX_REG_SENSITIVITY_CONTROL_DELTA_SENSE_MASK 0x70 #define CAP11XX_REG_CONFIG 0x20 #define CAP11XX_REG_SENSOR_ENABLE 0x21 #define CAP11XX_REG_SENSOR_CONFIG 0x22 @@ -32,6 +34,7 @@ #define CAP11XX_REG_CALIBRATION 0x26 #define CAP11XX_REG_INT_ENABLE 0x27 #define CAP11XX_REG_REPEAT_RATE 0x28 +#define CAP11XX_REG_SIGNAL_GUARD_ENABLE 0x29 #define CAP11XX_REG_MT_CONFIG 0x2a #define CAP11XX_REG_MT_PATTERN_CONFIG 0x2b #define CAP11XX_REG_MT_PATTERN 0x2d @@ -47,6 +50,8 @@ #define CAP11XX_REG_SENSOR_BASE_CNT(X) (0x50 + (X)) #define CAP11XX_REG_LED_POLARITY 0x73 #define CAP11XX_REG_LED_OUTPUT_CONTROL 0x74 +#define CAP11XX_REG_CALIB_SENSITIVITY_CONFIG 0x80 +#define CAP11XX_REG_CALIB_SENSITIVITY_CONFIG2 0x81 #define CAP11XX_REG_LED_DUTY_CYCLE_1 0x90 #define CAP11XX_REG_LED_DUTY_CYCLE_2 0x91 @@ -78,12 +83,20 @@ struct cap11xx_led { struct cap11xx_priv { struct regmap *regmap; + struct device *dev; struct input_dev *idev; + const struct cap11xx_hw_model *model; + u8 id; struct cap11xx_led *leds; int num_leds; /* config */ + u8 analog_gain; + u8 sensitivity_delta_sense; + u8 signal_guard_inputs_mask; + u32 thresholds[8]; + u32 calib_sensitivities[8]; u32 keycodes[]; }; @@ -160,9 +173,6 @@ static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg) case CAP11XX_REG_SENOR_DELTA(3): case CAP11XX_REG_SENOR_DELTA(4): case CAP11XX_REG_SENOR_DELTA(5): - case CAP11XX_REG_PRODUCT_ID: - case CAP11XX_REG_MANUFACTURER_ID: - case CAP11XX_REG_REVISION: return true; } @@ -177,10 +187,179 @@ static const struct regmap_config cap11xx_regmap_config = { .reg_defaults = cap11xx_reg_defaults, .num_reg_defaults = ARRAY_SIZE(cap11xx_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = cap11xx_volatile_reg, }; +static int cap11xx_write_calib_sens_config_1(struct cap11xx_priv *priv) +{ + return regmap_write(priv->regmap, + CAP11XX_REG_CALIB_SENSITIVITY_CONFIG, + (priv->calib_sensitivities[3] << 6) | + (priv->calib_sensitivities[2] << 4) | + (priv->calib_sensitivities[1] << 2) | + priv->calib_sensitivities[0]); +} + +static int cap11xx_write_calib_sens_config_2(struct cap11xx_priv *priv) +{ + return regmap_write(priv->regmap, + CAP11XX_REG_CALIB_SENSITIVITY_CONFIG2, + (priv->calib_sensitivities[7] << 6) | + (priv->calib_sensitivities[6] << 4) | + (priv->calib_sensitivities[5] << 2) | + priv->calib_sensitivities[4]); +} + +static int cap11xx_init_keys(struct cap11xx_priv *priv) +{ + struct device_node *node = priv->dev->of_node; + struct device *dev = priv->dev; + int i, error; + u32 u32_val; + + if (!node) { + dev_err(dev, "Corresponding DT entry is not available\n"); + return -ENODEV; + } + + if (!of_property_read_u32(node, "microchip,sensor-gain", &u32_val)) { + if (priv->model->no_gain) { + dev_warn(dev, + "This model doesn't support 'sensor-gain'\n"); + } else if (is_power_of_2(u32_val) && u32_val <= 8) { + priv->analog_gain = (u8)ilog2(u32_val); + + error = regmap_update_bits(priv->regmap, + CAP11XX_REG_MAIN_CONTROL, + CAP11XX_REG_MAIN_CONTROL_GAIN_MASK, + priv->analog_gain << CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT); + if (error) + return error; + } else { + dev_err(dev, "Invalid sensor-gain value %u\n", u32_val); + return -EINVAL; + } + } + + if (of_property_read_bool(node, "microchip,irq-active-high")) { + if (priv->id == CAP1106 || + priv->id == CAP1126 || + priv->id == CAP1188) { + error = regmap_update_bits(priv->regmap, + CAP11XX_REG_CONFIG2, + CAP11XX_REG_CONFIG2_ALT_POL, + 0); + if (error) + return error; + } else { + dev_warn(dev, + "This model doesn't support 'irq-active-high'\n"); + } + } + + if (!of_property_read_u32(node, "microchip,sensitivity-delta-sense", &u32_val)) { + if (!is_power_of_2(u32_val) || u32_val > 128) { + dev_err(dev, "Invalid sensitivity-delta-sense value %u\n", u32_val); + return -EINVAL; + } + + priv->sensitivity_delta_sense = (u8)ilog2(u32_val); + u32_val = ~(FIELD_PREP(CAP11XX_REG_SENSITIVITY_CONTROL_DELTA_SENSE_MASK, + priv->sensitivity_delta_sense)); + + error = regmap_update_bits(priv->regmap, + CAP11XX_REG_SENSITIVITY_CONTROL, + CAP11XX_REG_SENSITIVITY_CONTROL_DELTA_SENSE_MASK, + u32_val); + if (error) + return error; + } + + if (!of_property_read_u32_array(node, "microchip,input-threshold", + priv->thresholds, priv->model->num_channels)) { + for (i = 0; i < priv->model->num_channels; i++) { + if (priv->thresholds[i] > 127) { + dev_err(dev, "Invalid input-threshold value %u\n", + priv->thresholds[i]); + return -EINVAL; + } + + error = regmap_write(priv->regmap, + CAP11XX_REG_SENSOR_THRESH(i), + priv->thresholds[i]); + if (error) + return error; + } + } + + if (!of_property_read_u32_array(node, "microchip,calib-sensitivity", + priv->calib_sensitivities, + priv->model->num_channels)) { + if (priv->id == CAP1293 || priv->id == CAP1298) { + for (i = 0; i < priv->model->num_channels; i++) { + if (!is_power_of_2(priv->calib_sensitivities[i]) || + priv->calib_sensitivities[i] > 4) { + dev_err(dev, "Invalid calib-sensitivity value %u\n", + priv->calib_sensitivities[i]); + return -EINVAL; + } + priv->calib_sensitivities[i] = ilog2(priv->calib_sensitivities[i]); + } + + error = cap11xx_write_calib_sens_config_1(priv); + if (error) + return error; + + if (priv->id == CAP1298) { + error = cap11xx_write_calib_sens_config_2(priv); + if (error) + return error; + } + } else { + dev_warn(dev, + "This model doesn't support 'calib-sensitivity'\n"); + } + } + + for (i = 0; i < priv->model->num_channels; i++) { + if (!of_property_read_u32_index(node, "microchip,signal-guard", + i, &u32_val)) { + if (u32_val > 1) + return -EINVAL; + if (u32_val) + priv->signal_guard_inputs_mask |= 0x01 << i; + } + } + + if (priv->signal_guard_inputs_mask) { + if (priv->id == CAP1293 || priv->id == CAP1298) { + error = regmap_write(priv->regmap, + CAP11XX_REG_SIGNAL_GUARD_ENABLE, + priv->signal_guard_inputs_mask); + if (error) + return error; + } else { + dev_warn(dev, + "This model doesn't support 'signal-guard'\n"); + } + } + + /* Provide some useful defaults */ + for (i = 0; i < priv->model->num_channels; i++) + priv->keycodes[i] = KEY_A + i; + + of_property_read_u32_array(node, "linux,keycodes", + priv->keycodes, priv->model->num_channels); + + /* Disable autorepeat. The Linux input system has its own handling. */ + error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0); + if (error) + return error; + + return 0; +} + static irqreturn_t cap11xx_thread_func(int irq_num, void *data) { struct cap11xx_priv *priv = data; @@ -332,11 +511,9 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) const struct i2c_device_id *id = i2c_client_get_device_id(i2c_client); struct device *dev = &i2c_client->dev; struct cap11xx_priv *priv; - struct device_node *node; const struct cap11xx_hw_model *cap; - int i, error, irq, gain = 0; + int i, error; unsigned int val, rev; - u32 gain32; if (id->driver_data >= ARRAY_SIZE(cap11xx_devices)) { dev_err(dev, "Invalid device ID %lu\n", id->driver_data); @@ -355,6 +532,8 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) if (!priv) return -ENOMEM; + priv->dev = dev; + priv->regmap = devm_regmap_init_i2c(i2c_client, &cap11xx_regmap_config); if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); @@ -384,50 +563,15 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) return error; dev_info(dev, "CAP11XX detected, model %s, revision 0x%02x\n", - id->name, rev); - node = dev->of_node; - - if (!of_property_read_u32(node, "microchip,sensor-gain", &gain32)) { - if (cap->no_gain) - dev_warn(dev, - "This version doesn't support sensor gain\n"); - else if (is_power_of_2(gain32) && gain32 <= 8) - gain = ilog2(gain32); - else - dev_err(dev, "Invalid sensor-gain value %d\n", gain32); - } + id->name, rev); - if (id->driver_data == CAP1106 || - id->driver_data == CAP1126 || - id->driver_data == CAP1188) { - if (of_property_read_bool(node, "microchip,irq-active-high")) { - error = regmap_update_bits(priv->regmap, - CAP11XX_REG_CONFIG2, - CAP11XX_REG_CONFIG2_ALT_POL, - 0); - if (error) - return error; - } - } - - /* Provide some useful defaults */ - for (i = 0; i < cap->num_channels; i++) - priv->keycodes[i] = KEY_A + i; - - of_property_read_u32_array(node, "linux,keycodes", - priv->keycodes, cap->num_channels); + priv->model = cap; + priv->id = id->driver_data; - if (!cap->no_gain) { - error = regmap_update_bits(priv->regmap, - CAP11XX_REG_MAIN_CONTROL, - CAP11XX_REG_MAIN_CONTROL_GAIN_MASK, - gain << CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT); - if (error) - return error; - } + dev_info(dev, "CAP11XX device detected, model %s, revision 0x%02x\n", + id->name, rev); - /* Disable autorepeat. The Linux input system has its own handling. */ - error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0); + error = cap11xx_init_keys(priv); if (error) return error; @@ -439,7 +583,7 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) priv->idev->id.bustype = BUS_I2C; priv->idev->evbit[0] = BIT_MASK(EV_KEY); - if (of_property_read_bool(node, "autorepeat")) + if (of_property_read_bool(dev->of_node, "autorepeat")) __set_bit(EV_REP, priv->idev->evbit); for (i = 0; i < cap->num_channels; i++) @@ -474,13 +618,8 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) if (error) return error; - irq = irq_of_parse_and_map(node, 0); - if (!irq) { - dev_err(dev, "Unable to parse or map IRQ\n"); - return -ENXIO; - } - - error = devm_request_threaded_irq(dev, irq, NULL, cap11xx_thread_func, + error = devm_request_threaded_irq(dev, i2c_client->irq, + NULL, cap11xx_thread_func, IRQF_ONESHOT, dev_name(dev), priv); if (error) return error; diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 2e7c2c046e67..9f3bcd41cf67 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -45,7 +45,9 @@ struct gpio_button_data { unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */ unsigned int irq; + unsigned int wakeirq; unsigned int wakeup_trigger_type; + spinlock_t lock; bool disabled; bool key_pressed; @@ -511,6 +513,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev, struct gpio_button_data *bdata = &ddata->data[idx]; irq_handler_t isr; unsigned long irqflags; + const char *wakedesc; int irq; int error; @@ -575,15 +578,23 @@ static int gpio_keys_setup_key(struct platform_device *pdev, !gpiod_cansleep(bdata->gpiod); } + /* + * If an interrupt was specified, use it instead of the gpio + * interrupt and use the gpio for reading the state. A separate + * interrupt may be used as the main button interrupt for + * runtime PM to detect events also in deeper idle states. If a + * dedicated wakeirq is used for system suspend only, see below + * for bdata->wakeirq setup. + */ if (button->irq) { bdata->irq = button->irq; } else { irq = gpiod_to_irq(bdata->gpiod); if (irq < 0) { error = irq; - dev_err(dev, - "Unable to get irq number for GPIO %d, error %d\n", - button->gpio, error); + dev_err_probe(dev, error, + "Unable to get irq number for GPIO %d\n", + button->gpio); return error; } bdata->irq = irq; @@ -672,6 +683,36 @@ static int gpio_keys_setup_key(struct platform_device *pdev, return error; } + if (!button->wakeirq) + return 0; + + /* Use :wakeup suffix like drivers/base/power/wakeirq.c does */ + wakedesc = devm_kasprintf(dev, GFP_KERNEL, "%s:wakeup", desc); + if (!wakedesc) + return -ENOMEM; + + bdata->wakeirq = button->wakeirq; + irqflags |= IRQF_NO_SUSPEND; + + /* + * Wakeirq shares the handler with the main interrupt, it's only + * active during system suspend. See gpio_keys_button_enable_wakeup() + * and gpio_keys_button_disable_wakeup(). + */ + error = devm_request_any_context_irq(dev, bdata->wakeirq, isr, + irqflags, wakedesc, bdata); + if (error < 0) { + dev_err(dev, "Unable to claim wakeirq %d; error %d\n", + bdata->irq, error); + return error; + } + + /* + * Disable wakeirq until suspend. IRQF_NO_AUTOEN won't work if + * IRQF_SHARED was set based on !button->can_disable. + */ + disable_irq(bdata->wakeirq); + return 0; } @@ -728,7 +769,7 @@ gpio_keys_get_devtree_pdata(struct device *dev) struct gpio_keys_platform_data *pdata; struct gpio_keys_button *button; struct fwnode_handle *child; - int nbuttons; + int nbuttons, irq; nbuttons = device_get_child_node_count(dev); if (nbuttons == 0) @@ -750,9 +791,19 @@ gpio_keys_get_devtree_pdata(struct device *dev) device_property_read_string(dev, "label", &pdata->name); device_for_each_child_node(dev, child) { - if (is_of_node(child)) - button->irq = - irq_of_parse_and_map(to_of_node(child), 0); + if (is_of_node(child)) { + irq = of_irq_get_byname(to_of_node(child), "irq"); + if (irq > 0) + button->irq = irq; + + irq = of_irq_get_byname(to_of_node(child), "wakeup"); + if (irq > 0) + button->wakeirq = irq; + + if (!button->irq && !button->wakeirq) + button->irq = + irq_of_parse_and_map(to_of_node(child), 0); + } if (fwnode_property_read_u32(child, "linux,code", &button->code)) { @@ -921,6 +972,11 @@ gpio_keys_button_enable_wakeup(struct gpio_button_data *bdata) } } + if (bdata->wakeirq) { + enable_irq(bdata->wakeirq); + disable_irq(bdata->irq); + } + return 0; } @@ -929,6 +985,11 @@ gpio_keys_button_disable_wakeup(struct gpio_button_data *bdata) { int error; + if (bdata->wakeirq) { + enable_irq(bdata->irq); + disable_irq(bdata->wakeirq); + } + /* * The trigger type is always both edges for gpio-based keys and we do * not support changing wakeup trigger for interrupt-based keys. diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 454fb8675657..16f936db7305 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -21,7 +21,6 @@ #include <linux/mutex.h> #include <linux/errno.h> #include <linux/slab.h> -#include <linux/gpio.h> #include <linux/platform_data/gpio-omap.h> #include <linux/platform_data/keypad-omap.h> #include <linux/soc/ti/omap1-io.h> @@ -49,9 +48,6 @@ struct omap_kp { static DECLARE_TASKLET_DISABLED_OLD(kp_tasklet, omap_kp_tasklet); -static unsigned int *row_gpios; -static unsigned int *col_gpios; - static irqreturn_t omap_kp_interrupt(int irq, void *dev_id) { /* disable keyboard interrupt and schedule for handling */ @@ -180,7 +176,7 @@ static int omap_kp_probe(struct platform_device *pdev) struct omap_kp *omap_kp; struct input_dev *input_dev; struct omap_kp_platform_data *pdata = dev_get_platdata(&pdev->dev); - int i, col_idx, row_idx, ret; + int ret; unsigned int row_shift, keycodemax; if (!pdata->rows || !pdata->cols || !pdata->keymap_data) { @@ -209,17 +205,9 @@ static int omap_kp_probe(struct platform_device *pdev) if (pdata->delay) omap_kp->delay = pdata->delay; - if (pdata->row_gpios && pdata->col_gpios) { - row_gpios = pdata->row_gpios; - col_gpios = pdata->col_gpios; - } - omap_kp->rows = pdata->rows; omap_kp->cols = pdata->cols; - col_idx = 0; - row_idx = 0; - timer_setup(&omap_kp->timer, omap_kp_timer, 0); /* get the irq and init timer*/ @@ -276,11 +264,6 @@ err4: err3: device_remove_file(&pdev->dev, &dev_attr_enable); err2: - for (i = row_idx - 1; i >= 0; i--) - gpio_free(row_gpios[i]); - for (i = col_idx - 1; i >= 0; i--) - gpio_free(col_gpios[i]); - kfree(omap_kp); input_free_device(input_dev); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index d3f8688fdd9c..040b340995d8 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/platform_device.h> +#include <linux/clk.h> #include <linux/errno.h> #include <linux/io.h> #include <linux/of.h> @@ -83,6 +84,7 @@ struct omap4_keypad { bool no_autorepeat; u64 keys; unsigned short *keymap; + struct clk *fck; }; static int kbd_readl(struct omap4_keypad *keypad_data, u32 offset) @@ -209,6 +211,10 @@ static int omap4_keypad_open(struct input_dev *input) if (error) return error; + error = clk_prepare_enable(keypad_data->fck); + if (error) + goto out; + disable_irq(keypad_data->irq); kbd_writel(keypad_data, OMAP4_KBD_CTRL, @@ -226,10 +232,11 @@ static int omap4_keypad_open(struct input_dev *input) enable_irq(keypad_data->irq); +out: pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); - return 0; + return error; } static void omap4_keypad_stop(struct omap4_keypad *keypad_data) @@ -258,6 +265,7 @@ static void omap4_keypad_close(struct input_dev *input) disable_irq(keypad_data->irq); omap4_keypad_stop(keypad_data); enable_irq(keypad_data->irq); + clk_disable_unprepare(keypad_data->fck); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); @@ -356,6 +364,11 @@ static int omap4_keypad_probe(struct platform_device *pdev) } keypad_data->irq = irq; + keypad_data->fck = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(keypad_data->fck)) + return dev_err_probe(&pdev->dev, PTR_ERR(keypad_data->fck), + "unable to get fck"); + mutex_init(&keypad_data->lock); platform_set_drvdata(pdev, keypad_data); diff --git a/drivers/input/keyboard/qt1050.c b/drivers/input/keyboard/qt1050.c index 6953097db445..b51dfcd76038 100644 --- a/drivers/input/keyboard/qt1050.c +++ b/drivers/input/keyboard/qt1050.c @@ -213,7 +213,7 @@ static struct regmap_config qt1050_regmap_config = { .val_bits = 8, .max_register = QT1050_RES_CAL, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .wr_table = &qt1050_writeable_table, .rd_table = &qt1050_readable_table, diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c index 8af59ced1ec2..677bc4baa5d1 100644 --- a/drivers/input/keyboard/tca6416-keypad.c +++ b/drivers/input/keyboard/tca6416-keypad.c @@ -14,7 +14,6 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/workqueue.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/tca6416_keypad.h> diff --git a/drivers/input/misc/da7280.c b/drivers/input/misc/da7280.c index ce82548916bb..c1fa75c0f970 100644 --- a/drivers/input/misc/da7280.c +++ b/drivers/input/misc/da7280.c @@ -352,7 +352,7 @@ static int da7280_haptic_set_pwm(struct da7280_haptic *haptics, bool enabled) state.duty_cycle = period_mag_multi; } - error = pwm_apply_state(haptics->pwm_dev, &state); + error = pwm_apply_might_sleep(haptics->pwm_dev, &state); if (error) dev_err(haptics->dev, "Failed to apply pwm state: %d\n", error); @@ -1175,7 +1175,7 @@ static int da7280_probe(struct i2c_client *client) /* Sync up PWM state and ensure it is off. */ pwm_init_state(haptics->pwm_dev, &state); state.enabled = false; - error = pwm_apply_state(haptics->pwm_dev, &state); + error = pwm_apply_might_sleep(haptics->pwm_dev, &state); if (error) { dev_err(dev, "Failed to apply PWM state: %d\n", error); return error; diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c index 74808bae326a..c338765e0ecd 100644 --- a/drivers/input/misc/da9063_onkey.c +++ b/drivers/input/misc/da9063_onkey.c @@ -9,11 +9,12 @@ #include <linux/errno.h> #include <linux/input.h> #include <linux/interrupt.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/pm_wakeirq.h> +#include <linux/property.h> #include <linux/workqueue.h> #include <linux/regmap.h> -#include <linux/of.h> #include <linux/mfd/da9063/core.h> #include <linux/mfd/da9063/registers.h> #include <linux/mfd/da9062/core.h> @@ -74,13 +75,6 @@ static const struct da906x_chip_config da9062_regs = { .name = "da9062-onkey", }; -static const struct of_device_id da9063_compatible_reg_id_table[] = { - { .compatible = "dlg,da9063-onkey", .data = &da9063_regs }, - { .compatible = "dlg,da9062-onkey", .data = &da9062_regs }, - { }, -}; -MODULE_DEVICE_TABLE(of, da9063_compatible_reg_id_table); - static void da9063_poll_on(struct work_struct *work) { struct da9063_onkey *onkey = container_of(work, @@ -187,56 +181,43 @@ static irqreturn_t da9063_onkey_irq_handler(int irq, void *data) static int da9063_onkey_probe(struct platform_device *pdev) { struct da9063_onkey *onkey; - const struct of_device_id *match; - int irq; int error; - - match = of_match_node(da9063_compatible_reg_id_table, - pdev->dev.of_node); - if (!match) - return -ENXIO; + int irq; onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9063_onkey), GFP_KERNEL); - if (!onkey) { - dev_err(&pdev->dev, "Failed to allocate memory.\n"); + if (!onkey) return -ENOMEM; - } - onkey->config = match->data; + onkey->config = device_get_match_data(&pdev->dev); + if (!onkey->config) + return -ENXIO; + onkey->dev = &pdev->dev; onkey->regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!onkey->regmap) { - dev_err(&pdev->dev, "Parent regmap unavailable.\n"); - return -ENXIO; - } + if (!onkey->regmap) + return dev_err_probe(&pdev->dev, -ENXIO, + "Parent regmap unavailable.\n"); - onkey->key_power = !of_property_read_bool(pdev->dev.of_node, - "dlg,disable-key-power"); + onkey->key_power = !device_property_read_bool(&pdev->dev, + "dlg,disable-key-power"); onkey->input = devm_input_allocate_device(&pdev->dev); - if (!onkey->input) { - dev_err(&pdev->dev, "Failed to allocated input device.\n"); + if (!onkey->input) return -ENOMEM; - } onkey->input->name = onkey->config->name; snprintf(onkey->phys, sizeof(onkey->phys), "%s/input0", onkey->config->name); onkey->input->phys = onkey->phys; - onkey->input->dev.parent = &pdev->dev; input_set_capability(onkey->input, EV_KEY, KEY_POWER); error = devm_delayed_work_autocancel(&pdev->dev, &onkey->work, da9063_poll_on); - if (error) { - dev_err(&pdev->dev, - "Failed to add cancel poll action: %d\n", - error); + if (error) return error; - } irq = platform_get_irq_byname(pdev, "ONKEY"); if (irq < 0) @@ -246,11 +227,9 @@ static int da9063_onkey_probe(struct platform_device *pdev) NULL, da9063_onkey_irq_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "ONKEY", onkey); - if (error) { - dev_err(&pdev->dev, - "Failed to request IRQ %d: %d\n", irq, error); - return error; - } + if (error) + return dev_err_probe(&pdev->dev, error, + "Failed to allocate onkey IRQ\n"); error = dev_pm_set_wake_irq(&pdev->dev, irq); if (error) @@ -261,15 +240,19 @@ static int da9063_onkey_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, true); error = input_register_device(onkey->input); - if (error) { - dev_err(&pdev->dev, - "Failed to register input device: %d\n", error); + if (error) return error; - } return 0; } +static const struct of_device_id da9063_compatible_reg_id_table[] = { + { .compatible = "dlg,da9063-onkey", .data = &da9063_regs }, + { .compatible = "dlg,da9062-onkey", .data = &da9062_regs }, + { } +}; +MODULE_DEVICE_TABLE(of, da9063_compatible_reg_id_table); + static struct platform_driver da9063_onkey_driver = { .probe = da9063_onkey_probe, .driver = { diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index b2f1292e27ef..6e8cc28debd9 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1050,7 +1050,7 @@ static ssize_t ims_pcu_attribute_show(struct device *dev, container_of(dattr, struct ims_pcu_attribute, dattr); char *field = (char *)pcu + attr->field_offset; - return scnprintf(buf, PAGE_SIZE, "%.*s\n", attr->field_length, field); + return sysfs_emit(buf, "%.*s\n", attr->field_length, field); } static ssize_t ims_pcu_attribute_store(struct device *dev, @@ -1206,7 +1206,7 @@ ims_pcu_update_firmware_status_show(struct device *dev, struct usb_interface *intf = to_usb_interface(dev); struct ims_pcu *pcu = usb_get_intfdata(intf); - return scnprintf(buf, PAGE_SIZE, "%d\n", pcu->update_firmware_status); + return sysfs_emit(buf, "%d\n", pcu->update_firmware_status); } static DEVICE_ATTR(update_firmware_status, S_IRUGO, @@ -1309,7 +1309,7 @@ static ssize_t ims_pcu_ofn_reg_data_show(struct device *dev, if (error) return error; - return scnprintf(buf, PAGE_SIZE, "%x\n", data); + return sysfs_emit(buf, "%x\n", data); } static ssize_t ims_pcu_ofn_reg_data_store(struct device *dev, @@ -1344,7 +1344,7 @@ static ssize_t ims_pcu_ofn_reg_addr_show(struct device *dev, int error; mutex_lock(&pcu->cmd_mutex); - error = scnprintf(buf, PAGE_SIZE, "%x\n", pcu->ofn_reg_addr); + error = sysfs_emit(buf, "%x\n", pcu->ofn_reg_addr); mutex_unlock(&pcu->cmd_mutex); return error; @@ -1397,7 +1397,7 @@ static ssize_t ims_pcu_ofn_bit_show(struct device *dev, if (error) return error; - return scnprintf(buf, PAGE_SIZE, "%d\n", !!(data & (1 << attr->nr))); + return sysfs_emit(buf, "%d\n", !!(data & (1 << attr->nr))); } static ssize_t ims_pcu_ofn_bit_store(struct device *dev, diff --git a/drivers/input/misc/iqs269a.c b/drivers/input/misc/iqs269a.c index 3c636c75e8a1..cd14ff9f57cf 100644 --- a/drivers/input/misc/iqs269a.c +++ b/drivers/input/misc/iqs269a.c @@ -9,6 +9,7 @@ * axial sliders presented by the device. */ +#include <linux/bits.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/device.h> @@ -26,6 +27,8 @@ #define IQS269_VER_INFO 0x00 #define IQS269_VER_INFO_PROD_NUM 0x4F +#define IQS269_VER_INFO_FW_NUM_2 0x03 +#define IQS269_VER_INFO_FW_NUM_3 0x10 #define IQS269_SYS_FLAGS 0x02 #define IQS269_SYS_FLAGS_SHOW_RESET BIT(15) @@ -53,6 +56,7 @@ #define IQS269_SYS_SETTINGS_ULP_UPDATE_MASK GENMASK(10, 8) #define IQS269_SYS_SETTINGS_ULP_UPDATE_SHIFT 8 #define IQS269_SYS_SETTINGS_ULP_UPDATE_MAX 7 +#define IQS269_SYS_SETTINGS_SLIDER_SWIPE BIT(7) #define IQS269_SYS_SETTINGS_RESEED_OFFSET BIT(6) #define IQS269_SYS_SETTINGS_EVENT_MODE BIT(5) #define IQS269_SYS_SETTINGS_EVENT_MODE_LP BIT(4) @@ -69,6 +73,7 @@ #define IQS269_FILT_STR_MAX 3 #define IQS269_EVENT_MASK_SYS BIT(6) +#define IQS269_EVENT_MASK_GESTURE BIT(3) #define IQS269_EVENT_MASK_DEEP BIT(2) #define IQS269_EVENT_MASK_TOUCH BIT(1) #define IQS269_EVENT_MASK_PROX BIT(0) @@ -97,6 +102,15 @@ #define IQS269_MISC_B_TRACKING_UI_ENABLE BIT(4) #define IQS269_MISC_B_FILT_STR_SLIDER GENMASK(1, 0) +#define IQS269_TOUCH_HOLD_SLIDER_SEL 0x89 +#define IQS269_TOUCH_HOLD_DEFAULT 0x14 +#define IQS269_TOUCH_HOLD_MS_MIN 256 +#define IQS269_TOUCH_HOLD_MS_MAX 65280 + +#define IQS269_TIMEOUT_TAP_MS_MAX 4080 +#define IQS269_TIMEOUT_SWIPE_MS_MAX 4080 +#define IQS269_THRESH_SWIPE_MAX 255 + #define IQS269_CHx_ENG_A_MEAS_CAP_SIZE BIT(15) #define IQS269_CHx_ENG_A_RX_GND_INACTIVE BIT(13) #define IQS269_CHx_ENG_A_LOCAL_CAP_SIZE BIT(12) @@ -142,6 +156,10 @@ #define IQS269_MAX_REG 0xFF +#define IQS269_OTP_OPTION_DEFAULT 0x00 +#define IQS269_OTP_OPTION_TWS 0xD0 +#define IQS269_OTP_OPTION_HOLD BIT(7) + #define IQS269_NUM_CH 8 #define IQS269_NUM_SL 2 @@ -175,6 +193,20 @@ enum iqs269_event_id { IQS269_EVENT_DEEP_UP, }; +enum iqs269_slider_id { + IQS269_SLIDER_NONE, + IQS269_SLIDER_KEY, + IQS269_SLIDER_RAW, +}; + +enum iqs269_gesture_id { + IQS269_GESTURE_TAP, + IQS269_GESTURE_HOLD, + IQS269_GESTURE_FLICK_POS, + IQS269_GESTURE_FLICK_NEG, + IQS269_NUM_GESTURES, +}; + struct iqs269_switch_desc { unsigned int code; bool enabled; @@ -234,7 +266,7 @@ struct iqs269_ver_info { u8 prod_num; u8 sw_num; u8 hw_num; - u8 padding; + u8 fw_num; } __packed; struct iqs269_ch_reg { @@ -285,16 +317,42 @@ struct iqs269_private { struct regmap *regmap; struct mutex lock; struct iqs269_switch_desc switches[ARRAY_SIZE(iqs269_events)]; + struct iqs269_ver_info ver_info; struct iqs269_sys_reg sys_reg; struct completion ati_done; struct input_dev *keypad; struct input_dev *slider[IQS269_NUM_SL]; unsigned int keycode[ARRAY_SIZE(iqs269_events) * IQS269_NUM_CH]; + unsigned int sl_code[IQS269_NUM_SL][IQS269_NUM_GESTURES]; + unsigned int otp_option; unsigned int ch_num; bool hall_enable; bool ati_current; }; +static enum iqs269_slider_id iqs269_slider_type(struct iqs269_private *iqs269, + int slider_num) +{ + int i; + + /* + * Slider 1 is unavailable if the touch-and-hold option is enabled via + * OTP. In that case, the channel selection register is repurposed for + * the touch-and-hold timer ceiling. + */ + if (slider_num && (iqs269->otp_option & IQS269_OTP_OPTION_HOLD)) + return IQS269_SLIDER_NONE; + + if (!iqs269->sys_reg.slider_select[slider_num]) + return IQS269_SLIDER_NONE; + + for (i = 0; i < IQS269_NUM_GESTURES; i++) + if (iqs269->sl_code[slider_num][i] != KEY_RESERVED) + return IQS269_SLIDER_KEY; + + return IQS269_SLIDER_RAW; +} + static int iqs269_ati_mode_set(struct iqs269_private *iqs269, unsigned int ch_num, unsigned int mode) { @@ -525,7 +583,8 @@ static int iqs269_parse_chan(struct iqs269_private *iqs269, if (fwnode_property_present(ch_node, "azoteq,slider0-select")) iqs269->sys_reg.slider_select[0] |= BIT(reg); - if (fwnode_property_present(ch_node, "azoteq,slider1-select")) + if (fwnode_property_present(ch_node, "azoteq,slider1-select") && + !(iqs269->otp_option & IQS269_OTP_OPTION_HOLD)) iqs269->sys_reg.slider_select[1] |= BIT(reg); ch_reg = &iqs269->sys_reg.ch_reg[reg]; @@ -950,7 +1009,43 @@ static int iqs269_parse_prop(struct iqs269_private *iqs269) sys_reg->blocking = 0; sys_reg->slider_select[0] = 0; - sys_reg->slider_select[1] = 0; + + /* + * If configured via OTP to do so, the device asserts a pulse on the + * GPIO4 pin for approximately 60 ms once a selected channel is held + * in a state of touch for a configurable length of time. + * + * In that case, the register used for slider 1 channel selection is + * repurposed for the touch-and-hold timer ceiling. + */ + if (iqs269->otp_option & IQS269_OTP_OPTION_HOLD) { + if (!device_property_read_u32(&client->dev, + "azoteq,touch-hold-ms", &val)) { + if (val < IQS269_TOUCH_HOLD_MS_MIN || + val > IQS269_TOUCH_HOLD_MS_MAX) { + dev_err(&client->dev, + "Invalid touch-and-hold ceiling: %u\n", + val); + return -EINVAL; + } + + sys_reg->slider_select[1] = val / 256; + } else if (iqs269->ver_info.fw_num < IQS269_VER_INFO_FW_NUM_3) { + /* + * The default touch-and-hold timer ceiling initially + * read from early revisions of silicon is invalid if + * the device experienced a soft reset between power- + * on and the read operation. + * + * To protect against this case, explicitly cache the + * default value so that it is restored each time the + * device is re-initialized. + */ + sys_reg->slider_select[1] = IQS269_TOUCH_HOLD_DEFAULT; + } + } else { + sys_reg->slider_select[1] = 0; + } sys_reg->event_mask = ~((u8)IQS269_EVENT_MASK_SYS); @@ -1004,6 +1099,76 @@ static int iqs269_parse_prop(struct iqs269_private *iqs269) general |= (val << IQS269_SYS_SETTINGS_ULP_UPDATE_SHIFT); } + if (device_property_present(&client->dev, "linux,keycodes")) { + int scale = 1; + int count = device_property_count_u32(&client->dev, + "linux,keycodes"); + if (count > IQS269_NUM_GESTURES * IQS269_NUM_SL) { + dev_err(&client->dev, "Too many keycodes present\n"); + return -EINVAL; + } else if (count < 0) { + dev_err(&client->dev, "Failed to count keycodes: %d\n", + count); + return count; + } + + error = device_property_read_u32_array(&client->dev, + "linux,keycodes", + *iqs269->sl_code, count); + if (error) { + dev_err(&client->dev, "Failed to read keycodes: %d\n", + error); + return error; + } + + if (device_property_present(&client->dev, + "azoteq,gesture-swipe")) + general |= IQS269_SYS_SETTINGS_SLIDER_SWIPE; + + /* + * Early revisions of silicon use a more granular step size for + * tap and swipe gesture timeouts; scale them appropriately. + */ + if (iqs269->ver_info.fw_num < IQS269_VER_INFO_FW_NUM_3) + scale = 4; + + if (!device_property_read_u32(&client->dev, + "azoteq,timeout-tap-ms", &val)) { + if (val > IQS269_TIMEOUT_TAP_MS_MAX / scale) { + dev_err(&client->dev, "Invalid timeout: %u\n", + val); + return -EINVAL; + } + + sys_reg->timeout_tap = val / (16 / scale); + } + + if (!device_property_read_u32(&client->dev, + "azoteq,timeout-swipe-ms", + &val)) { + if (val > IQS269_TIMEOUT_SWIPE_MS_MAX / scale) { + dev_err(&client->dev, "Invalid timeout: %u\n", + val); + return -EINVAL; + } + + sys_reg->timeout_swipe = val / (16 / scale); + } + + if (!device_property_read_u32(&client->dev, + "azoteq,thresh-swipe", &val)) { + if (val > IQS269_THRESH_SWIPE_MAX) { + dev_err(&client->dev, "Invalid threshold: %u\n", + val); + return -EINVAL; + } + + sys_reg->thresh_swipe = val; + } + + sys_reg->event_mask &= ~IQS269_EVENT_MASK_GESTURE; + } + general &= ~IQS269_SYS_SETTINGS_RESEED_OFFSET; if (device_property_present(&client->dev, "azoteq,reseed-offset")) general |= IQS269_SYS_SETTINGS_RESEED_OFFSET; @@ -1012,10 +1177,11 @@ static int iqs269_parse_prop(struct iqs269_private *iqs269) /* * As per the datasheet, enable streaming during normal-power mode if - * either slider is in use. In that case, the device returns to event - * mode during low-power mode. + * raw coordinates will be read from either slider. In that case, the + * device returns to event mode during low-power mode. */ - if (sys_reg->slider_select[0] || sys_reg->slider_select[1]) + if (iqs269_slider_type(iqs269, 0) == IQS269_SLIDER_RAW || + iqs269_slider_type(iqs269, 1) == IQS269_SLIDER_RAW) general |= IQS269_SYS_SETTINGS_EVENT_MODE_LP; general |= IQS269_SYS_SETTINGS_REDO_ATI; @@ -1026,12 +1192,30 @@ static int iqs269_parse_prop(struct iqs269_private *iqs269) return 0; } +static const struct reg_sequence iqs269_tws_init[] = { + { IQS269_TOUCH_HOLD_SLIDER_SEL, IQS269_TOUCH_HOLD_DEFAULT }, + { 0xF0, 0x580F }, + { 0xF0, 0x59EF }, +}; + static int iqs269_dev_init(struct iqs269_private *iqs269) { int error; mutex_lock(&iqs269->lock); + /* + * Early revisions of silicon require the following workaround in order + * to restore any OTP-enabled functionality after a soft reset. + */ + if (iqs269->otp_option == IQS269_OTP_OPTION_TWS && + iqs269->ver_info.fw_num < IQS269_VER_INFO_FW_NUM_3) { + error = regmap_multi_reg_write(iqs269->regmap, iqs269_tws_init, + ARRAY_SIZE(iqs269_tws_init)); + if (error) + goto err_mutex; + } + error = regmap_update_bits(iqs269->regmap, IQS269_HALL_UI, IQS269_HALL_UI_ENABLE, iqs269->hall_enable ? ~0 : 0); @@ -1106,19 +1290,37 @@ static int iqs269_input_init(struct iqs269_private *iqs269) } for (i = 0; i < IQS269_NUM_SL; i++) { - if (!iqs269->sys_reg.slider_select[i]) + if (iqs269_slider_type(iqs269, i) == IQS269_SLIDER_NONE) continue; iqs269->slider[i] = devm_input_allocate_device(&client->dev); if (!iqs269->slider[i]) return -ENOMEM; + iqs269->slider[i]->keycodemax = ARRAY_SIZE(iqs269->sl_code[i]); + iqs269->slider[i]->keycode = iqs269->sl_code[i]; + iqs269->slider[i]->keycodesize = sizeof(**iqs269->sl_code); + iqs269->slider[i]->name = i ? "iqs269a_slider_1" : "iqs269a_slider_0"; iqs269->slider[i]->id.bustype = BUS_I2C; - input_set_capability(iqs269->slider[i], EV_KEY, BTN_TOUCH); - input_set_abs_params(iqs269->slider[i], ABS_X, 0, 255, 0, 0); + for (j = 0; j < IQS269_NUM_GESTURES; j++) + if (iqs269->sl_code[i][j] != KEY_RESERVED) + input_set_capability(iqs269->slider[i], EV_KEY, + iqs269->sl_code[i][j]); + + /* + * Present the slider as a narrow trackpad if one or more chan- + * nels have been selected to participate, but no gestures have + * been mapped to a keycode. + */ + if (iqs269_slider_type(iqs269, i) == IQS269_SLIDER_RAW) { + input_set_capability(iqs269->slider[i], + EV_KEY, BTN_TOUCH); + input_set_abs_params(iqs269->slider[i], + ABS_X, 0, 255, 0, 0); + } error = input_register_device(iqs269->slider[i]); if (error) { @@ -1167,28 +1369,62 @@ static int iqs269_report(struct iqs269_private *iqs269) if (be16_to_cpu(flags.system) & IQS269_SYS_FLAGS_IN_ATI) return 0; - error = regmap_raw_read(iqs269->regmap, IQS269_SLIDER_X, slider_x, - sizeof(slider_x)); - if (error) { - dev_err(&client->dev, "Failed to read slider position: %d\n", - error); - return error; + if (iqs269_slider_type(iqs269, 0) == IQS269_SLIDER_RAW || + iqs269_slider_type(iqs269, 1) == IQS269_SLIDER_RAW) { + error = regmap_raw_read(iqs269->regmap, IQS269_SLIDER_X, + slider_x, sizeof(slider_x)); + if (error) { + dev_err(&client->dev, + "Failed to read slider position: %d\n", error); + return error; + } } for (i = 0; i < IQS269_NUM_SL; i++) { - if (!iqs269->sys_reg.slider_select[i]) + flags.gesture >>= (i * IQS269_NUM_GESTURES); + + switch (iqs269_slider_type(iqs269, i)) { + case IQS269_SLIDER_NONE: continue; - /* - * Report BTN_TOUCH if any channel that participates in the - * slider is in a state of touch. - */ - if (flags.states[IQS269_ST_OFFS_TOUCH] & - iqs269->sys_reg.slider_select[i]) { - input_report_key(iqs269->slider[i], BTN_TOUCH, 1); - input_report_abs(iqs269->slider[i], ABS_X, slider_x[i]); - } else { - input_report_key(iqs269->slider[i], BTN_TOUCH, 0); + case IQS269_SLIDER_KEY: + for (j = 0; j < IQS269_NUM_GESTURES; j++) + input_report_key(iqs269->slider[i], + iqs269->sl_code[i][j], + flags.gesture & BIT(j)); + + if (!(flags.gesture & (BIT(IQS269_GESTURE_FLICK_NEG) | + BIT(IQS269_GESTURE_FLICK_POS) | + BIT(IQS269_GESTURE_TAP)))) + break; + + input_sync(iqs269->slider[i]); + + /* + * Momentary gestures are followed by a complementary + * release cycle so as to emulate a full keystroke. + */ + for (j = 0; j < IQS269_NUM_GESTURES; j++) + if (j != IQS269_GESTURE_HOLD) + input_report_key(iqs269->slider[i], + iqs269->sl_code[i][j], + 0); + break; + + case IQS269_SLIDER_RAW: + /* + * The slider is considered to be in a state of touch + * if any selected channels are in a state of touch. + */ + state = flags.states[IQS269_ST_OFFS_TOUCH]; + state &= iqs269->sys_reg.slider_select[i]; + + input_report_key(iqs269->slider[i], BTN_TOUCH, state); + + if (state) + input_report_abs(iqs269->slider[i], + ABS_X, slider_x[i]); + break; } input_sync(iqs269->slider[i]); @@ -1286,7 +1522,7 @@ static ssize_t counts_show(struct device *dev, if (error) return error; - return scnprintf(buf, PAGE_SIZE, "%u\n", le16_to_cpu(counts)); + return sysfs_emit(buf, "%u\n", le16_to_cpu(counts)); } static ssize_t hall_bin_show(struct device *dev, @@ -1324,7 +1560,7 @@ static ssize_t hall_bin_show(struct device *dev, return -EINVAL; } - return scnprintf(buf, PAGE_SIZE, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t hall_enable_show(struct device *dev, @@ -1332,7 +1568,7 @@ static ssize_t hall_enable_show(struct device *dev, { struct iqs269_private *iqs269 = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%u\n", iqs269->hall_enable); + return sysfs_emit(buf, "%u\n", iqs269->hall_enable); } static ssize_t hall_enable_store(struct device *dev, @@ -1362,7 +1598,7 @@ static ssize_t ch_number_show(struct device *dev, { struct iqs269_private *iqs269 = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%u\n", iqs269->ch_num); + return sysfs_emit(buf, "%u\n", iqs269->ch_num); } static ssize_t ch_number_store(struct device *dev, @@ -1391,8 +1627,7 @@ static ssize_t rx_enable_show(struct device *dev, struct iqs269_private *iqs269 = dev_get_drvdata(dev); struct iqs269_ch_reg *ch_reg = iqs269->sys_reg.ch_reg; - return scnprintf(buf, PAGE_SIZE, "%u\n", - ch_reg[iqs269->ch_num].rx_enable); + return sysfs_emit(buf, "%u\n", ch_reg[iqs269->ch_num].rx_enable); } static ssize_t rx_enable_store(struct device *dev, @@ -1432,7 +1667,7 @@ static ssize_t ati_mode_show(struct device *dev, if (error) return error; - return scnprintf(buf, PAGE_SIZE, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t ati_mode_store(struct device *dev, @@ -1465,7 +1700,7 @@ static ssize_t ati_base_show(struct device *dev, if (error) return error; - return scnprintf(buf, PAGE_SIZE, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t ati_base_store(struct device *dev, @@ -1498,7 +1733,7 @@ static ssize_t ati_target_show(struct device *dev, if (error) return error; - return scnprintf(buf, PAGE_SIZE, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t ati_target_store(struct device *dev, @@ -1525,9 +1760,9 @@ static ssize_t ati_trigger_show(struct device *dev, { struct iqs269_private *iqs269 = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%u\n", - iqs269->ati_current && - completion_done(&iqs269->ati_done)); + return sysfs_emit(buf, "%u\n", + iqs269->ati_current && + completion_done(&iqs269->ati_done)); } static ssize_t ati_trigger_store(struct device *dev, @@ -1596,7 +1831,6 @@ static const struct regmap_config iqs269_regmap_config = { static int iqs269_probe(struct i2c_client *client) { - struct iqs269_ver_info ver_info; struct iqs269_private *iqs269; int error; @@ -1618,14 +1852,16 @@ static int iqs269_probe(struct i2c_client *client) mutex_init(&iqs269->lock); init_completion(&iqs269->ati_done); - error = regmap_raw_read(iqs269->regmap, IQS269_VER_INFO, &ver_info, - sizeof(ver_info)); + iqs269->otp_option = (uintptr_t)device_get_match_data(&client->dev); + + error = regmap_raw_read(iqs269->regmap, IQS269_VER_INFO, + &iqs269->ver_info, sizeof(iqs269->ver_info)); if (error) return error; - if (ver_info.prod_num != IQS269_VER_INFO_PROD_NUM) { + if (iqs269->ver_info.prod_num != IQS269_VER_INFO_PROD_NUM) { dev_err(&client->dev, "Unrecognized product number: 0x%02X\n", - ver_info.prod_num); + iqs269->ver_info.prod_num); return -EINVAL; } @@ -1728,7 +1964,18 @@ static int iqs269_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(iqs269_pm, iqs269_suspend, iqs269_resume); static const struct of_device_id iqs269_of_match[] = { - { .compatible = "azoteq,iqs269a" }, + { + .compatible = "azoteq,iqs269a", + .data = (void *)IQS269_OTP_OPTION_DEFAULT, + }, + { + .compatible = "azoteq,iqs269a-00", + .data = (void *)IQS269_OTP_OPTION_DEFAULT, + }, + { + .compatible = "azoteq,iqs269a-d0", + .data = (void *)IQS269_OTP_OPTION_TWS, + }, { } }; MODULE_DEVICE_TABLE(of, iqs269_of_match); diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c index 80f4416ffe2f..0e646f1b257b 100644 --- a/drivers/input/misc/max77693-haptic.c +++ b/drivers/input/misc/max77693-haptic.c @@ -307,7 +307,7 @@ static int max77693_haptic_probe(struct platform_device *pdev) haptic->suspend_state = false; /* Variant-specific init */ - haptic->dev_type = platform_get_device_id(pdev)->driver_data; + haptic->dev_type = max77693->type; switch (haptic->dev_type) { case TYPE_MAX77693: haptic->regmap_haptic = max77693->regmap_haptic; @@ -406,16 +406,24 @@ static DEFINE_SIMPLE_DEV_PM_OPS(max77693_haptic_pm_ops, max77693_haptic_resume); static const struct platform_device_id max77693_haptic_id[] = { - { "max77693-haptic", TYPE_MAX77693 }, - { "max77843-haptic", TYPE_MAX77843 }, + { "max77693-haptic", }, + { "max77843-haptic", }, {}, }; MODULE_DEVICE_TABLE(platform, max77693_haptic_id); +static const struct of_device_id of_max77693_haptic_dt_match[] = { + { .compatible = "maxim,max77693-haptic", }, + { .compatible = "maxim,max77843-haptic", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, of_max77693_haptic_dt_match); + static struct platform_driver max77693_haptic_driver = { .driver = { .name = "max77693-haptic", .pm = pm_sleep_ptr(&max77693_haptic_pm_ops), + .of_match_table = of_max77693_haptic_dt_match, }, .probe = max77693_haptic_probe, .id_table = max77693_haptic_id, diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index 1e731d8397c6..5b9aedf4362f 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -39,7 +39,7 @@ static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period) state.period = period; pwm_set_relative_duty_cycle(&state, 50, 100); - error = pwm_apply_state(beeper->pwm, &state); + error = pwm_apply_might_sleep(beeper->pwm, &state); if (error) return error; @@ -138,7 +138,7 @@ static int pwm_beeper_probe(struct platform_device *pdev) /* Sync up PWM state and ensure it is off. */ pwm_init_state(beeper->pwm, &state); state.enabled = false; - error = pwm_apply_state(beeper->pwm, &state); + error = pwm_apply_might_sleep(beeper->pwm, &state); if (error) { dev_err(dev, "failed to apply initial PWM state: %d\n", error); diff --git a/drivers/input/misc/pwm-vibra.c b/drivers/input/misc/pwm-vibra.c index acac79c488aa..3e5ed685ed8f 100644 --- a/drivers/input/misc/pwm-vibra.c +++ b/drivers/input/misc/pwm-vibra.c @@ -56,7 +56,7 @@ static int pwm_vibrator_start(struct pwm_vibrator *vibrator) pwm_set_relative_duty_cycle(&state, vibrator->level, 0xffff); state.enabled = true; - err = pwm_apply_state(vibrator->pwm, &state); + err = pwm_apply_might_sleep(vibrator->pwm, &state); if (err) { dev_err(pdev, "failed to apply pwm state: %d\n", err); return err; @@ -67,7 +67,7 @@ static int pwm_vibrator_start(struct pwm_vibrator *vibrator) state.duty_cycle = vibrator->direction_duty_cycle; state.enabled = true; - err = pwm_apply_state(vibrator->pwm_dir, &state); + err = pwm_apply_might_sleep(vibrator->pwm_dir, &state); if (err) { dev_err(pdev, "failed to apply dir-pwm state: %d\n", err); pwm_disable(vibrator->pwm); @@ -160,7 +160,7 @@ static int pwm_vibrator_probe(struct platform_device *pdev) /* Sync up PWM state and ensure it is off. */ pwm_init_state(vibrator->pwm, &state); state.enabled = false; - err = pwm_apply_state(vibrator->pwm, &state); + err = pwm_apply_might_sleep(vibrator->pwm, &state); if (err) { dev_err(&pdev->dev, "failed to apply initial PWM state: %d\n", err); @@ -174,7 +174,7 @@ static int pwm_vibrator_probe(struct platform_device *pdev) /* Sync up PWM state and ensure it is off. */ pwm_init_state(vibrator->pwm_dir, &state); state.enabled = false; - err = pwm_apply_state(vibrator->pwm_dir, &state); + err = pwm_apply_might_sleep(vibrator->pwm_dir, &state); if (err) { dev_err(&pdev->dev, "failed to apply initial PWM state: %d\n", err); diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index ca150618d32f..953992b458e9 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -19,6 +19,7 @@ * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch) */ +#include "linux/usb.h" #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> @@ -193,6 +194,8 @@ enum tp_type { /* list of device capability bits */ #define HAS_INTEGRATED_BUTTON 1 +/* maximum number of supported endpoints (currently trackpad and button) */ +#define MAX_ENDPOINTS 2 /* trackpad finger data block size */ #define FSIZE_TYPE1 (14 * sizeof(__le16)) @@ -891,6 +894,18 @@ static int bcm5974_resume(struct usb_interface *iface) return error; } +static bool bcm5974_check_endpoints(struct usb_interface *iface, + const struct bcm5974_config *cfg) +{ + u8 ep_addr[MAX_ENDPOINTS + 1] = {0}; + + ep_addr[0] = cfg->tp_ep; + if (cfg->tp_type == TYPE1) + ep_addr[1] = cfg->bt_ep; + + return usb_check_int_endpoints(iface, ep_addr); +} + static int bcm5974_probe(struct usb_interface *iface, const struct usb_device_id *id) { @@ -903,6 +918,11 @@ static int bcm5974_probe(struct usb_interface *iface, /* find the product index */ cfg = bcm5974_get_config(udev); + if (!bcm5974_check_endpoints(iface, cfg)) { + dev_err(&iface->dev, "Unexpected non-int endpoint\n"); + return -ENODEV; + } + /* allocate memory for our device state and initialize it */ dev = kzalloc(sizeof(struct bcm5974), GFP_KERNEL); input_dev = input_allocate_device(); diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index a84098448f5b..5979deabe23d 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -756,16 +756,16 @@ static ssize_t cyapa_show_suspend_scanrate(struct device *dev, switch (pwr_cmd) { case PWR_MODE_BTN_ONLY: - len = scnprintf(buf, PAGE_SIZE, "%s\n", BTN_ONLY_MODE_NAME); + len = sysfs_emit(buf, "%s\n", BTN_ONLY_MODE_NAME); break; case PWR_MODE_OFF: - len = scnprintf(buf, PAGE_SIZE, "%s\n", OFF_MODE_NAME); + len = sysfs_emit(buf, "%s\n", OFF_MODE_NAME); break; default: - len = scnprintf(buf, PAGE_SIZE, "%u\n", - cyapa->gen == CYAPA_GEN3 ? + len = sysfs_emit(buf, "%u\n", + cyapa->gen == CYAPA_GEN3 ? cyapa_pwr_cmd_to_sleep_time(pwr_cmd) : sleep_time); break; @@ -877,8 +877,8 @@ static ssize_t cyapa_show_rt_suspend_scanrate(struct device *dev, mutex_unlock(&cyapa->state_sync_lock); - return scnprintf(buf, PAGE_SIZE, "%u\n", - cyapa->gen == CYAPA_GEN3 ? + return sysfs_emit(buf, "%u\n", + cyapa->gen == CYAPA_GEN3 ? cyapa_pwr_cmd_to_sleep_time(pwr_cmd) : sleep_time); } @@ -988,8 +988,8 @@ static ssize_t cyapa_show_fm_ver(struct device *dev, error = mutex_lock_interruptible(&cyapa->state_sync_lock); if (error) return error; - error = scnprintf(buf, PAGE_SIZE, "%d.%d\n", cyapa->fw_maj_ver, - cyapa->fw_min_ver); + error = sysfs_emit(buf, "%d.%d\n", + cyapa->fw_maj_ver, cyapa->fw_min_ver); mutex_unlock(&cyapa->state_sync_lock); return error; } @@ -1004,7 +1004,7 @@ static ssize_t cyapa_show_product_id(struct device *dev, error = mutex_lock_interruptible(&cyapa->state_sync_lock); if (error) return error; - size = scnprintf(buf, PAGE_SIZE, "%s\n", cyapa->product_id); + size = sysfs_emit(buf, "%s\n", cyapa->product_id); mutex_unlock(&cyapa->state_sync_lock); return size; } @@ -1209,8 +1209,8 @@ static ssize_t cyapa_show_mode(struct device *dev, if (error) return error; - size = scnprintf(buf, PAGE_SIZE, "gen%d %s\n", - cyapa->gen, cyapa_state_to_string(cyapa)); + size = sysfs_emit(buf, "gen%d %s\n", + cyapa->gen, cyapa_state_to_string(cyapa)); mutex_unlock(&cyapa->state_sync_lock); return size; diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c index a97f4acb6452..60c83bc71d84 100644 --- a/drivers/input/mouse/cyapa_gen3.c +++ b/drivers/input/mouse/cyapa_gen3.c @@ -860,7 +860,7 @@ static ssize_t cyapa_gen3_show_baseline(struct device *dev, dev_dbg(dev, "Baseline report successful. Max: %d Min: %d\n", max_baseline, min_baseline); - ret = scnprintf(buf, PAGE_SIZE, "%d %d\n", max_baseline, min_baseline); + ret = sysfs_emit(buf, "%d %d\n", max_baseline, min_baseline); out: return ret; diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c index abf42f77b4c5..2e6bcb07257e 100644 --- a/drivers/input/mouse/cyapa_gen5.c +++ b/drivers/input/mouse/cyapa_gen5.c @@ -2418,12 +2418,12 @@ resume_scanning: return resume_error ? resume_error : error; /* 12. Output data strings */ - size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d %d %d %d %d %d ", + size = sysfs_emit(buf, "%d %d %d %d %d %d %d %d %d %d %d ", gidac_mutual_min, gidac_mutual_max, gidac_mutual_ave, lidac_mutual_min, lidac_mutual_max, lidac_mutual_ave, gidac_self_rx, gidac_self_tx, lidac_self_min, lidac_self_max, lidac_self_ave); - size += scnprintf(buf + size, PAGE_SIZE - size, + size += sysfs_emit_at(buf, size, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", raw_cap_mutual_min, raw_cap_mutual_max, raw_cap_mutual_ave, raw_cap_self_min, raw_cap_self_max, raw_cap_self_ave, diff --git a/drivers/input/mouse/cyapa_gen6.c b/drivers/input/mouse/cyapa_gen6.c index 0caaf3e64215..4ffe08fee10c 100644 --- a/drivers/input/mouse/cyapa_gen6.c +++ b/drivers/input/mouse/cyapa_gen6.c @@ -629,14 +629,14 @@ static ssize_t cyapa_gen6_show_baseline(struct device *dev, if (error) goto resume_scanning; - size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d ", - data[0], /* RX Attenuator Mutual */ - data[1], /* IDAC Mutual */ - data[2], /* RX Attenuator Self RX */ - data[3], /* IDAC Self RX */ - data[4], /* RX Attenuator Self TX */ - data[5] /* IDAC Self TX */ - ); + size = sysfs_emit(buf, "%d %d %d %d %d %d ", + data[0], /* RX Attenuator Mutual */ + data[1], /* IDAC Mutual */ + data[2], /* RX Attenuator Self RX */ + data[3], /* IDAC Self RX */ + data[4], /* RX Attenuator Self TX */ + data[5] /* IDAC Self TX */ + ); /* 3. Read Attenuator Trim. */ data_len = sizeof(data); @@ -648,8 +648,8 @@ static ssize_t cyapa_gen6_show_baseline(struct device *dev, /* set attenuator trim values. */ for (i = 0; i < data_len; i++) - size += scnprintf(buf + size, PAGE_SIZE - size, "%d ", data[i]); - size += scnprintf(buf + size, PAGE_SIZE - size, "\n"); + size += sysfs_emit_at(buf, size, "%d ", data[i]); + size += sysfs_emit_at(buf, size, "\n"); resume_scanning: /* 4. Resume Scanning*/ diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 148a601396f9..8a72c200ccb5 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -572,7 +572,7 @@ static ssize_t elan_sysfs_read_fw_checksum(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct elan_tp_data *data = i2c_get_clientdata(client); - return sprintf(buf, "0x%04x\n", data->fw_checksum); + return sysfs_emit(buf, "0x%04x\n", data->fw_checksum); } static ssize_t elan_sysfs_read_product_id(struct device *dev, @@ -582,8 +582,8 @@ static ssize_t elan_sysfs_read_product_id(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct elan_tp_data *data = i2c_get_clientdata(client); - return sprintf(buf, ETP_PRODUCT_ID_FORMAT_STRING "\n", - data->product_id); + return sysfs_emit(buf, ETP_PRODUCT_ID_FORMAT_STRING "\n", + data->product_id); } static ssize_t elan_sysfs_read_fw_ver(struct device *dev, @@ -593,7 +593,7 @@ static ssize_t elan_sysfs_read_fw_ver(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct elan_tp_data *data = i2c_get_clientdata(client); - return sprintf(buf, "%d.0\n", data->fw_version); + return sysfs_emit(buf, "%d.0\n", data->fw_version); } static ssize_t elan_sysfs_read_sm_ver(struct device *dev, @@ -603,7 +603,7 @@ static ssize_t elan_sysfs_read_sm_ver(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct elan_tp_data *data = i2c_get_clientdata(client); - return sprintf(buf, "%d.0\n", data->sm_version); + return sysfs_emit(buf, "%d.0\n", data->sm_version); } static ssize_t elan_sysfs_read_iap_ver(struct device *dev, @@ -613,7 +613,7 @@ static ssize_t elan_sysfs_read_iap_ver(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct elan_tp_data *data = i2c_get_clientdata(client); - return sprintf(buf, "%d.0\n", data->iap_version); + return sysfs_emit(buf, "%d.0\n", data->iap_version); } static ssize_t elan_sysfs_update_fw(struct device *dev, @@ -754,7 +754,7 @@ static ssize_t elan_sysfs_read_mode(struct device *dev, if (error) return error; - return sprintf(buf, "%d\n", (int)mode); + return sysfs_emit(buf, "%d\n", (int)mode); } static DEVICE_ATTR(product_id, S_IRUGO, elan_sysfs_read_product_id, NULL); @@ -858,7 +858,7 @@ static ssize_t min_show(struct device *dev, goto out; } - retval = snprintf(buf, PAGE_SIZE, "%d", data->min_baseline); + retval = sysfs_emit(buf, "%d", data->min_baseline); out: mutex_unlock(&data->sysfs_mutex); @@ -881,7 +881,7 @@ static ssize_t max_show(struct device *dev, goto out; } - retval = snprintf(buf, PAGE_SIZE, "%d", data->max_baseline); + retval = sysfs_emit(buf, "%d", data->max_baseline); out: mutex_unlock(&data->sysfs_mutex); diff --git a/drivers/input/mouse/navpoint.c b/drivers/input/mouse/navpoint.c index c00dc1275da2..ba757783c258 100644 --- a/drivers/input/mouse/navpoint.c +++ b/drivers/input/mouse/navpoint.c @@ -10,7 +10,7 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/input.h> #include <linux/input/navpoint.h> #include <linux/interrupt.h> @@ -32,7 +32,7 @@ struct navpoint { struct ssp_device *ssp; struct input_dev *input; struct device *dev; - int gpio; + struct gpio_desc *gpiod; int index; u8 data[1 + HEADER_LENGTH(0xff)]; }; @@ -170,16 +170,14 @@ static void navpoint_up(struct navpoint *navpoint) dev_err(navpoint->dev, "timeout waiting for SSSR[CSS] to clear\n"); - if (gpio_is_valid(navpoint->gpio)) - gpio_set_value(navpoint->gpio, 1); + gpiod_set_value(navpoint->gpiod, 1); } static void navpoint_down(struct navpoint *navpoint) { struct ssp_device *ssp = navpoint->ssp; - if (gpio_is_valid(navpoint->gpio)) - gpio_set_value(navpoint->gpio, 0); + gpiod_set_value(navpoint->gpiod, 0); pxa_ssp_write_reg(ssp, SSCR0, 0); @@ -216,18 +214,9 @@ static int navpoint_probe(struct platform_device *pdev) return -EINVAL; } - if (gpio_is_valid(pdata->gpio)) { - error = gpio_request_one(pdata->gpio, GPIOF_OUT_INIT_LOW, - "SYNAPTICS_ON"); - if (error) - return error; - } - ssp = pxa_ssp_request(pdata->port, pdev->name); - if (!ssp) { - error = -ENODEV; - goto err_free_gpio; - } + if (!ssp) + return -ENODEV; /* HaRET does not disable devices before jumping into Linux */ if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { @@ -242,10 +231,18 @@ static int navpoint_probe(struct platform_device *pdev) goto err_free_mem; } + navpoint->gpiod = gpiod_get_optional(&pdev->dev, + NULL, GPIOD_OUT_LOW); + if (IS_ERR(navpoint->gpiod)) { + error = PTR_ERR(navpoint->gpiod); + dev_err(&pdev->dev, "error getting GPIO\n"); + goto err_free_mem; + } + gpiod_set_consumer_name(navpoint->gpiod, "SYNAPTICS_ON"); + navpoint->ssp = ssp; navpoint->input = input; navpoint->dev = &pdev->dev; - navpoint->gpio = pdata->gpio; input->name = pdev->name; input->dev.parent = &pdev->dev; @@ -288,17 +285,12 @@ err_free_mem: input_free_device(input); kfree(navpoint); pxa_ssp_free(ssp); -err_free_gpio: - if (gpio_is_valid(pdata->gpio)) - gpio_free(pdata->gpio); return error; } static void navpoint_remove(struct platform_device *pdev) { - const struct navpoint_platform_data *pdata = - dev_get_platdata(&pdev->dev); struct navpoint *navpoint = platform_get_drvdata(pdev); struct ssp_device *ssp = navpoint->ssp; @@ -308,9 +300,6 @@ static void navpoint_remove(struct platform_device *pdev) kfree(navpoint); pxa_ssp_free(ssp); - - if (gpio_is_valid(pdata->gpio)) - gpio_free(pdata->gpio); } static int navpoint_suspend(struct device *dev) diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c index d7603c50f864..cc1d4b424640 100644 --- a/drivers/input/rmi4/rmi_f01.c +++ b/drivers/input/rmi4/rmi_f01.c @@ -267,8 +267,7 @@ static ssize_t rmi_driver_manufacturer_id_show(struct device *dev, struct rmi_driver_data *data = dev_get_drvdata(dev); struct f01_data *f01 = dev_get_drvdata(&data->f01_container->dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", - f01->properties.manufacturer_id); + return sysfs_emit(buf, "%d\n", f01->properties.manufacturer_id); } static DEVICE_ATTR(manufacturer_id, 0444, @@ -280,7 +279,7 @@ static ssize_t rmi_driver_dom_show(struct device *dev, struct rmi_driver_data *data = dev_get_drvdata(dev); struct f01_data *f01 = dev_get_drvdata(&data->f01_container->dev); - return scnprintf(buf, PAGE_SIZE, "%s\n", f01->properties.dom); + return sysfs_emit(buf, "%s\n", f01->properties.dom); } static DEVICE_ATTR(date_of_manufacture, 0444, rmi_driver_dom_show, NULL); @@ -292,7 +291,7 @@ static ssize_t rmi_driver_product_id_show(struct device *dev, struct rmi_driver_data *data = dev_get_drvdata(dev); struct f01_data *f01 = dev_get_drvdata(&data->f01_container->dev); - return scnprintf(buf, PAGE_SIZE, "%s\n", f01->properties.product_id); + return sysfs_emit(buf, "%s\n", f01->properties.product_id); } static DEVICE_ATTR(product_id, 0444, rmi_driver_product_id_show, NULL); @@ -304,7 +303,7 @@ static ssize_t rmi_driver_firmware_id_show(struct device *dev, struct rmi_driver_data *data = dev_get_drvdata(dev); struct f01_data *f01 = dev_get_drvdata(&data->f01_container->dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", f01->properties.firmware_id); + return sysfs_emit(buf, "%d\n", f01->properties.firmware_id); } static DEVICE_ATTR(firmware_id, 0444, rmi_driver_firmware_id_show, NULL); @@ -318,8 +317,8 @@ static ssize_t rmi_driver_package_id_show(struct device *dev, u32 package_id = f01->properties.package_id; - return scnprintf(buf, PAGE_SIZE, "%04x.%04x\n", - package_id & 0xffff, (package_id >> 16) & 0xffff); + return sysfs_emit(buf, "%04x.%04x\n", + package_id & 0xffff, (package_id >> 16) & 0xffff); } static DEVICE_ATTR(package_id, 0444, rmi_driver_package_id_show, NULL); diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h index b585b1dab870..dfc6c581873b 100644 --- a/drivers/input/serio/i8042-acpipnpio.h +++ b/drivers/input/serio/i8042-acpipnpio.h @@ -635,6 +635,14 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { .driver_data = (void *)(SERIO_QUIRK_NOAUX) }, { + /* Fujitsu Lifebook U728 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U728"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOAUX) + }, + { /* Gigabyte M912 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), @@ -1210,6 +1218,12 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { }, { .matches = { + DMI_MATCH(DMI_BOARD_NAME, "NS5x_7xPU"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOAUX) + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "NJ50_70CU"), }, .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index a2c4b3b87f93..542a31448c8f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2818,8 +2818,8 @@ static ssize_t mxt_fw_version_show(struct device *dev, { struct mxt_data *data = dev_get_drvdata(dev); struct mxt_info *info = data->info; - return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n", - info->version >> 4, info->version & 0xf, info->build); + return sysfs_emit(buf, "%u.%u.%02X\n", + info->version >> 4, info->version & 0xf, info->build); } /* Hardware Version is returned as FamilyID.VariantID */ @@ -2828,8 +2828,7 @@ static ssize_t mxt_hw_version_show(struct device *dev, { struct mxt_data *data = dev_get_drvdata(dev); struct mxt_info *info = data->info; - return scnprintf(buf, PAGE_SIZE, "%u.%u\n", - info->family_id, info->variant_id); + return sysfs_emit(buf, "%u.%u\n", info->family_id, info->variant_id); } static ssize_t mxt_show_instance(char *buf, int count, @@ -2839,19 +2838,18 @@ static ssize_t mxt_show_instance(char *buf, int count, int i; if (mxt_obj_instances(object) > 1) - count += scnprintf(buf + count, PAGE_SIZE - count, - "Instance %u\n", instance); + count += sysfs_emit_at(buf, count, "Instance %u\n", instance); for (i = 0; i < mxt_obj_size(object); i++) - count += scnprintf(buf + count, PAGE_SIZE - count, - "\t[%2u]: %02x (%d)\n", i, val[i], val[i]); - count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); + count += sysfs_emit_at(buf, count, "\t[%2u]: %02x (%d)\n", + i, val[i], val[i]); + count += sysfs_emit_at(buf, count, "\n"); return count; } static ssize_t mxt_object_show(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); struct mxt_object *object; @@ -2872,8 +2870,7 @@ static ssize_t mxt_object_show(struct device *dev, if (!mxt_object_readable(object->type)) continue; - count += scnprintf(buf + count, PAGE_SIZE - count, - "T%u:\n", object->type); + count += sysfs_emit_at(buf, count, "T%u:\n", object->type); for (j = 0; j < mxt_obj_instances(object); j++) { u16 size = mxt_obj_size(object); diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 3e102bcc4a1c..2a1db1134476 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -431,7 +431,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, *field = val; } - count = scnprintf(buf, PAGE_SIZE, "%d\n", val); + count = sysfs_emit(buf, "%d\n", val); out: mutex_unlock(&tsdata->mutex); return error ?: count; diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index af32fbe57b63..b068ff8afbc9 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -884,7 +884,8 @@ static int goodix_add_acpi_gpio_mappings(struct goodix_ts_data *ts) } } - if (ts->gpio_count == 2 && ts->gpio_int_idx == 0) { + /* Some devices with gpio_int_idx 0 list a third unused GPIO */ + if ((ts->gpio_count == 2 || ts->gpio_count == 3) && ts->gpio_int_idx == 0) { ts->irq_pin_access_method = IRQ_PIN_ACCESS_ACPI_GPIO; gpio_mapping = acpi_goodix_int_first_gpios; } else if (ts->gpio_count == 2 && ts->gpio_int_idx == 1) { diff --git a/drivers/input/touchscreen/hideep.c b/drivers/input/touchscreen/hideep.c index 0f58258306bf..eae90676f4e5 100644 --- a/drivers/input/touchscreen/hideep.c +++ b/drivers/input/touchscreen/hideep.c @@ -928,8 +928,7 @@ static ssize_t hideep_fw_version_show(struct device *dev, ssize_t len; mutex_lock(&ts->dev_mutex); - len = scnprintf(buf, PAGE_SIZE, "%04x\n", - be16_to_cpu(ts->dwz_info.release_ver)); + len = sysfs_emit(buf, "%04x\n", be16_to_cpu(ts->dwz_info.release_ver)); mutex_unlock(&ts->dev_mutex); return len; @@ -943,8 +942,7 @@ static ssize_t hideep_product_id_show(struct device *dev, ssize_t len; mutex_lock(&ts->dev_mutex); - len = scnprintf(buf, PAGE_SIZE, "%04x\n", - be16_to_cpu(ts->dwz_info.product_id)); + len = sysfs_emit(buf, "%04x\n", be16_to_cpu(ts->dwz_info.product_id)); mutex_unlock(&ts->dev_mutex); return len; diff --git a/drivers/input/touchscreen/hycon-hy46xx.c b/drivers/input/touchscreen/hycon-hy46xx.c index d0f257989fd6..2e01d87977c1 100644 --- a/drivers/input/touchscreen/hycon-hy46xx.c +++ b/drivers/input/touchscreen/hycon-hy46xx.c @@ -202,7 +202,7 @@ static ssize_t hycon_hy46xx_setting_show(struct device *dev, *field = val; } - count = scnprintf(buf, PAGE_SIZE, "%d\n", val); + count = sysfs_emit(buf, "%d\n", val); out: mutex_unlock(&tsdata->mutex); diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c index 90c4934e750a..fc4e39b6651a 100644 --- a/drivers/input/touchscreen/ilitek_ts_i2c.c +++ b/drivers/input/touchscreen/ilitek_ts_i2c.c @@ -512,12 +512,12 @@ static ssize_t firmware_version_show(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct ilitek_ts_data *ts = i2c_get_clientdata(client); - return scnprintf(buf, PAGE_SIZE, - "fw version: [%02X%02X.%02X%02X.%02X%02X.%02X%02X]\n", - ts->firmware_ver[0], ts->firmware_ver[1], - ts->firmware_ver[2], ts->firmware_ver[3], - ts->firmware_ver[4], ts->firmware_ver[5], - ts->firmware_ver[6], ts->firmware_ver[7]); + return sysfs_emit(buf, + "fw version: [%02X%02X.%02X%02X.%02X%02X.%02X%02X]\n", + ts->firmware_ver[0], ts->firmware_ver[1], + ts->firmware_ver[2], ts->firmware_ver[3], + ts->firmware_ver[4], ts->firmware_ver[5], + ts->firmware_ver[6], ts->firmware_ver[7]); } static DEVICE_ATTR_RO(firmware_version); @@ -527,8 +527,8 @@ static ssize_t product_id_show(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct ilitek_ts_data *ts = i2c_get_clientdata(client); - return scnprintf(buf, PAGE_SIZE, "product id: [%04X], module: [%s]\n", - ts->mcu_ver, ts->product_id); + return sysfs_emit(buf, "product id: [%04X], module: [%s]\n", + ts->mcu_ver, ts->product_id); } static DEVICE_ATTR_RO(product_id); diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index a3f4fb85bee5..4d226118f3cc 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -943,12 +943,12 @@ static ssize_t fw_info_show(struct device *dev, if (!iqs5xx->dev_id_info.bl_status) return -ENODATA; - return scnprintf(buf, PAGE_SIZE, "%u.%u.%u.%u:%u.%u\n", - be16_to_cpu(iqs5xx->dev_id_info.prod_num), - be16_to_cpu(iqs5xx->dev_id_info.proj_num), - iqs5xx->dev_id_info.major_ver, - iqs5xx->dev_id_info.minor_ver, - iqs5xx->exp_file[0], iqs5xx->exp_file[1]); + return sysfs_emit(buf, "%u.%u.%u.%u:%u.%u\n", + be16_to_cpu(iqs5xx->dev_id_info.prod_num), + be16_to_cpu(iqs5xx->dev_id_info.proj_num), + iqs5xx->dev_id_info.major_ver, + iqs5xx->dev_id_info.minor_ver, + iqs5xx->exp_file[0], iqs5xx->exp_file[1]); } static DEVICE_ATTR_WO(fw_file); diff --git a/drivers/input/touchscreen/iqs7211.c b/drivers/input/touchscreen/iqs7211.c index dc084f873762..f0a56cde899e 100644 --- a/drivers/input/touchscreen/iqs7211.c +++ b/drivers/input/touchscreen/iqs7211.c @@ -2401,12 +2401,12 @@ static ssize_t fw_info_show(struct device *dev, { struct iqs7211_private *iqs7211 = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%u.%u.%u.%u:%u.%u\n", - le16_to_cpu(iqs7211->ver_info.prod_num), - le32_to_cpu(iqs7211->ver_info.patch), - le16_to_cpu(iqs7211->ver_info.major), - le16_to_cpu(iqs7211->ver_info.minor), - iqs7211->exp_file[1], iqs7211->exp_file[0]); + return sysfs_emit(buf, "%u.%u.%u.%u:%u.%u\n", + le16_to_cpu(iqs7211->ver_info.prod_num), + le32_to_cpu(iqs7211->ver_info.patch), + le16_to_cpu(iqs7211->ver_info.major), + le16_to_cpu(iqs7211->ver_info.minor), + iqs7211->exp_file[1], iqs7211->exp_file[0]); } static DEVICE_ATTR_RO(fw_info); diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c index aa325486f618..78e1c63e530e 100644 --- a/drivers/input/touchscreen/melfas_mip4.c +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -1336,9 +1336,9 @@ static ssize_t mip4_sysfs_read_fw_version(struct device *dev, /* Take lock to prevent racing with firmware update */ mutex_lock(&ts->input->mutex); - count = snprintf(buf, PAGE_SIZE, "%04X %04X %04X %04X\n", - ts->fw_version.boot, ts->fw_version.core, - ts->fw_version.app, ts->fw_version.param); + count = sysfs_emit(buf, "%04X %04X %04X %04X\n", + ts->fw_version.boot, ts->fw_version.core, + ts->fw_version.app, ts->fw_version.param); mutex_unlock(&ts->input->mutex); @@ -1362,8 +1362,8 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev, * product_name shows the name or version of the hardware * paired with current firmware in the chip. */ - count = snprintf(buf, PAGE_SIZE, "%.*s\n", - (int)sizeof(ts->product_name), ts->product_name); + count = sysfs_emit(buf, "%.*s\n", + (int)sizeof(ts->product_name), ts->product_name); mutex_unlock(&ts->input->mutex); @@ -1382,7 +1382,7 @@ static ssize_t mip4_sysfs_read_product_id(struct device *dev, mutex_lock(&ts->input->mutex); - count = snprintf(buf, PAGE_SIZE, "%04X\n", ts->product_id); + count = sysfs_emit(buf, "%04X\n", ts->product_id); mutex_unlock(&ts->input->mutex); @@ -1401,8 +1401,8 @@ static ssize_t mip4_sysfs_read_ic_name(struct device *dev, mutex_lock(&ts->input->mutex); - count = snprintf(buf, PAGE_SIZE, "%.*s\n", - (int)sizeof(ts->ic_name), ts->ic_name); + count = sysfs_emit(buf, "%.*s\n", + (int)sizeof(ts->ic_name), ts->ic_name); mutex_unlock(&ts->input->mutex); diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index d6d04b9f04fc..60354ebc7242 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -456,8 +456,8 @@ static ssize_t mtouch_firmware_rev_show(struct device *dev, struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); struct mtouch_priv *priv = usbtouch->priv; - return scnprintf(output, PAGE_SIZE, "%1x.%1x\n", - priv->fw_rev_major, priv->fw_rev_minor); + return sysfs_emit(output, "%1x.%1x\n", + priv->fw_rev_major, priv->fw_rev_minor); } static DEVICE_ATTR(firmware_rev, 0444, mtouch_firmware_rev_show, NULL); diff --git a/drivers/input/touchscreen/wdt87xx_i2c.c b/drivers/input/touchscreen/wdt87xx_i2c.c index 128341a6696b..32c7be54434c 100644 --- a/drivers/input/touchscreen/wdt87xx_i2c.c +++ b/drivers/input/touchscreen/wdt87xx_i2c.c @@ -887,7 +887,7 @@ static ssize_t config_csum_show(struct device *dev, cfg_csum = wdt->param.xmls_id1; cfg_csum = (cfg_csum << 16) | wdt->param.xmls_id2; - return scnprintf(buf, PAGE_SIZE, "%x\n", cfg_csum); + return sysfs_emit(buf, "%x\n", cfg_csum); } static ssize_t fw_version_show(struct device *dev, @@ -896,7 +896,7 @@ static ssize_t fw_version_show(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct wdt87xx_data *wdt = i2c_get_clientdata(client); - return scnprintf(buf, PAGE_SIZE, "%x\n", wdt->param.fw_id); + return sysfs_emit(buf, "%x\n", wdt->param.fw_id); } static ssize_t plat_id_show(struct device *dev, @@ -905,7 +905,7 @@ static ssize_t plat_id_show(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct wdt87xx_data *wdt = i2c_get_clientdata(client); - return scnprintf(buf, PAGE_SIZE, "%x\n", wdt->param.plat_id); + return sysfs_emit(buf, "%x\n", wdt->param.plat_id); } static ssize_t update_config_store(struct device *dev, diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index 5be5112845e1..5680075f0bb8 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -20,6 +20,7 @@ #include <linux/device.h> #include <linux/sysfs.h> #include <linux/input/mt.h> +#include <linux/input/touchscreen.h> #include <linux/platform_data/zforce_ts.h> #include <linux/regulator/consumer.h> #include <linux/of.h> @@ -106,6 +107,7 @@ struct zforce_point { struct zforce_ts { struct i2c_client *client; struct input_dev *input; + struct touchscreen_properties prop; const struct zforce_ts_platdata *pdata; char phys[32]; @@ -266,7 +268,6 @@ static int zforce_setconfig(struct zforce_ts *ts, char b1) static int zforce_start(struct zforce_ts *ts) { struct i2c_client *client = ts->client; - const struct zforce_ts_platdata *pdata = ts->pdata; int ret; dev_dbg(&client->dev, "starting device\n"); @@ -277,7 +278,7 @@ static int zforce_start(struct zforce_ts *ts) return ret; } - ret = zforce_resolution(ts, pdata->x_max, pdata->y_max); + ret = zforce_resolution(ts, ts->prop.max_x, ts->prop.max_y); if (ret) { dev_err(&client->dev, "Unable to set resolution, %d\n", ret); goto error; @@ -337,7 +338,6 @@ static int zforce_stop(struct zforce_ts *ts) static int zforce_touch_event(struct zforce_ts *ts, u8 *payload) { struct i2c_client *client = ts->client; - const struct zforce_ts_platdata *pdata = ts->pdata; struct zforce_point point; int count, i, num = 0; @@ -355,8 +355,8 @@ static int zforce_touch_event(struct zforce_ts *ts, u8 *payload) point.coord_y = payload[9 * i + 4] << 8 | payload[9 * i + 3]; - if (point.coord_x > pdata->x_max || - point.coord_y > pdata->y_max) { + if (point.coord_x > ts->prop.max_x || + point.coord_y > ts->prop.max_y) { dev_warn(&client->dev, "coordinates (%d,%d) invalid\n", point.coord_x, point.coord_y); point.coord_x = point.coord_y = 0; @@ -390,10 +390,9 @@ static int zforce_touch_event(struct zforce_ts *ts, u8 *payload) point.state != STATE_UP); if (point.state != STATE_UP) { - input_report_abs(ts->input, ABS_MT_POSITION_X, - point.coord_x); - input_report_abs(ts->input, ABS_MT_POSITION_Y, - point.coord_y); + touchscreen_report_pos(ts->input, &ts->prop, + point.coord_x, point.coord_y, + true); input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, point.area_major); input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, @@ -719,15 +718,8 @@ static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev) return ERR_PTR(-ENOMEM); } - if (of_property_read_u32(np, "x-size", &pdata->x_max)) { - dev_err(dev, "failed to get x-size property\n"); - return ERR_PTR(-EINVAL); - } - - if (of_property_read_u32(np, "y-size", &pdata->y_max)) { - dev_err(dev, "failed to get y-size property\n"); - return ERR_PTR(-EINVAL); - } + of_property_read_u32(np, "x-size", &pdata->x_max); + of_property_read_u32(np, "y-size", &pdata->y_max); return pdata; } @@ -856,6 +848,12 @@ static int zforce_probe(struct i2c_client *client) input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0); + touchscreen_parse_properties(input_dev, true, &ts->prop); + if (ts->prop.max_x == 0 || ts->prop.max_y == 0) { + dev_err(&client->dev, "no size specified\n"); + return -EINVAL; + } + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, ZFORCE_MAX_AREA, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, diff --git a/drivers/input/vivaldi-fmap.c b/drivers/input/vivaldi-fmap.c index 6dae83d96806..0d29ec014e2f 100644 --- a/drivers/input/vivaldi-fmap.c +++ b/drivers/input/vivaldi-fmap.c @@ -27,10 +27,10 @@ ssize_t vivaldi_function_row_physmap_show(const struct vivaldi_data *data, return 0; for (i = 0; i < data->num_function_row_keys; i++) - size += scnprintf(buf + size, PAGE_SIZE - size, - "%s%02X", size ? " " : "", physmap[i]); + size += sysfs_emit_at(buf, size, + "%s%02X", size ? " " : "", physmap[i]); if (size) - size += scnprintf(buf + size, PAGE_SIZE - size, "\n"); + size += sysfs_emit_at(buf, size, "\n"); return size; } diff --git a/drivers/interconnect/imx/imx8mm.c b/drivers/interconnect/imx/imx8mm.c index b43325364aa3..8c40f4182263 100644 --- a/drivers/interconnect/imx/imx8mm.c +++ b/drivers/interconnect/imx/imx8mm.c @@ -86,16 +86,9 @@ static int imx8mm_icc_probe(struct platform_device *pdev) return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes), NULL); } -static int imx8mm_icc_remove(struct platform_device *pdev) -{ - imx_icc_unregister(pdev); - - return 0; -} - static struct platform_driver imx8mm_icc_driver = { .probe = imx8mm_icc_probe, - .remove = imx8mm_icc_remove, + .remove_new = imx_icc_unregister, .driver = { .name = "imx8mm-interconnect", }, diff --git a/drivers/interconnect/imx/imx8mn.c b/drivers/interconnect/imx/imx8mn.c index 8ce6d8e4bf5e..fa3d4f97dfa4 100644 --- a/drivers/interconnect/imx/imx8mn.c +++ b/drivers/interconnect/imx/imx8mn.c @@ -75,16 +75,9 @@ static int imx8mn_icc_probe(struct platform_device *pdev) return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes), NULL); } -static int imx8mn_icc_remove(struct platform_device *pdev) -{ - imx_icc_unregister(pdev); - - return 0; -} - static struct platform_driver imx8mn_icc_driver = { .probe = imx8mn_icc_probe, - .remove = imx8mn_icc_remove, + .remove_new = imx_icc_unregister, .driver = { .name = "imx8mn-interconnect", }, diff --git a/drivers/interconnect/imx/imx8mp.c b/drivers/interconnect/imx/imx8mp.c index a66ae3638b18..d218bb47757a 100644 --- a/drivers/interconnect/imx/imx8mp.c +++ b/drivers/interconnect/imx/imx8mp.c @@ -239,16 +239,9 @@ static int imx8mp_icc_probe(struct platform_device *pdev) return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes), noc_setting_nodes); } -static int imx8mp_icc_remove(struct platform_device *pdev) -{ - imx_icc_unregister(pdev); - - return 0; -} - static struct platform_driver imx8mp_icc_driver = { .probe = imx8mp_icc_probe, - .remove = imx8mp_icc_remove, + .remove_new = imx_icc_unregister, .driver = { .name = "imx8mp-interconnect", }, diff --git a/drivers/interconnect/imx/imx8mq.c b/drivers/interconnect/imx/imx8mq.c index b6fb71305c99..8bbd672b346e 100644 --- a/drivers/interconnect/imx/imx8mq.c +++ b/drivers/interconnect/imx/imx8mq.c @@ -85,16 +85,9 @@ static int imx8mq_icc_probe(struct platform_device *pdev) return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes), NULL); } -static int imx8mq_icc_remove(struct platform_device *pdev) -{ - imx_icc_unregister(pdev); - - return 0; -} - static struct platform_driver imx8mq_icc_driver = { .probe = imx8mq_icc_probe, - .remove = imx8mq_icc_remove, + .remove_new = imx_icc_unregister, .driver = { .name = "imx8mq-interconnect", .sync_state = icc_sync_state, diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 62b516d38d03..697f96c49f6f 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -191,6 +191,15 @@ config INTERCONNECT_QCOM_SDX75 This is a driver for the Qualcomm Network-on-Chip on sdx75-based platforms. +config INTERCONNECT_QCOM_SM6115 + tristate "Qualcomm SM6115 interconnect driver" + depends on INTERCONNECT_QCOM + depends on QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM + help + This is a driver for the Qualcomm Network-on-Chip on sm6115-based + platforms. + config INTERCONNECT_QCOM_SM6350 tristate "Qualcomm SM6350 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE @@ -245,5 +254,23 @@ config INTERCONNECT_QCOM_SM8550 This is a driver for the Qualcomm Network-on-Chip on SM8550-based platforms. +config INTERCONNECT_QCOM_SM8650 + tristate "Qualcomm SM8650 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 SM8650-based + platforms. + +config INTERCONNECT_QCOM_X1E80100 + tristate "Qualcomm X1E80100 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 X1E80100-based + platforms. + config INTERCONNECT_QCOM_SMD_RPM tristate diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index c5320e293960..704846165022 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -24,12 +24,15 @@ qnoc-sdm845-objs := sdm845.o qnoc-sdx55-objs := sdx55.o qnoc-sdx65-objs := sdx65.o qnoc-sdx75-objs := sdx75.o +qnoc-sm6115-objs := sm6115.o qnoc-sm6350-objs := sm6350.o qnoc-sm8150-objs := sm8150.o qnoc-sm8250-objs := sm8250.o qnoc-sm8350-objs := sm8350.o qnoc-sm8450-objs := sm8450.o qnoc-sm8550-objs := sm8550.o +qnoc-sm8650-objs := sm8650.o +qnoc-x1e80100-objs := x1e80100.o icc-smd-rpm-objs := smd-rpm.o icc-rpm.o icc-rpm-clocks.o obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o @@ -53,10 +56,13 @@ obj-$(CONFIG_INTERCONNECT_QCOM_SDM845) += qnoc-sdm845.o obj-$(CONFIG_INTERCONNECT_QCOM_SDX55) += qnoc-sdx55.o obj-$(CONFIG_INTERCONNECT_QCOM_SDX65) += qnoc-sdx65.o obj-$(CONFIG_INTERCONNECT_QCOM_SDX75) += qnoc-sdx75.o +obj-$(CONFIG_INTERCONNECT_QCOM_SM6115) += qnoc-sm6115.o obj-$(CONFIG_INTERCONNECT_QCOM_SM6350) += qnoc-sm6350.o obj-$(CONFIG_INTERCONNECT_QCOM_SM8150) += qnoc-sm8150.o obj-$(CONFIG_INTERCONNECT_QCOM_SM8250) += qnoc-sm8250.o 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_X1E80100) += qnoc-x1e80100.o obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index dbacb2a7af50..a8ed435f696c 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -627,14 +627,12 @@ err_disable_unprepare_clk: } EXPORT_SYMBOL(qnoc_probe); -int qnoc_remove(struct platform_device *pdev) +void qnoc_remove(struct platform_device *pdev) { struct qcom_icc_provider *qp = platform_get_drvdata(pdev); icc_provider_deregister(&qp->provider); icc_nodes_remove(&qp->provider); clk_disable_unprepare(qp->bus_clk); - - return 0; } EXPORT_SYMBOL(qnoc_remove); diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index a13768cfd231..f4883d43eae4 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -161,7 +161,7 @@ extern const struct rpm_clk_resource aggre1_branch_clk; extern const struct rpm_clk_resource aggre2_branch_clk; int qnoc_probe(struct platform_device *pdev); -int qnoc_remove(struct platform_device *pdev); +void qnoc_remove(struct platform_device *pdev); bool qcom_icc_rpm_smd_available(void); int qcom_icc_rpm_smd_send(int ctx, int rsc_type, int id, u32 val); diff --git a/drivers/interconnect/qcom/msm8916.c b/drivers/interconnect/qcom/msm8916.c index 35148880b3e8..499b1a9ac413 100644 --- a/drivers/interconnect/qcom/msm8916.c +++ b/drivers/interconnect/qcom/msm8916.c @@ -1344,7 +1344,7 @@ MODULE_DEVICE_TABLE(of, msm8916_noc_of_match); static struct platform_driver msm8916_noc_driver = { .probe = qnoc_probe, - .remove = qnoc_remove, + .remove_new = qnoc_remove, .driver = { .name = "qnoc-msm8916", .of_match_table = msm8916_noc_of_match, diff --git a/drivers/interconnect/qcom/msm8939.c b/drivers/interconnect/qcom/msm8939.c index b52c5ac1175c..8ff2c23b1ca0 100644 --- a/drivers/interconnect/qcom/msm8939.c +++ b/drivers/interconnect/qcom/msm8939.c @@ -1421,7 +1421,7 @@ MODULE_DEVICE_TABLE(of, msm8939_noc_of_match); static struct platform_driver msm8939_noc_driver = { .probe = qnoc_probe, - .remove = qnoc_remove, + .remove_new = qnoc_remove, .driver = { .name = "qnoc-msm8939", .of_match_table = msm8939_noc_of_match, diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c index 21f6c852141e..241076b5f36b 100644 --- a/drivers/interconnect/qcom/msm8974.c +++ b/drivers/interconnect/qcom/msm8974.c @@ -740,15 +740,13 @@ err_remove_nodes: return ret; } -static int msm8974_icc_remove(struct platform_device *pdev) +static void msm8974_icc_remove(struct platform_device *pdev) { struct msm8974_icc_provider *qp = platform_get_drvdata(pdev); icc_provider_deregister(&qp->provider); icc_nodes_remove(&qp->provider); clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); - - return 0; } static const struct of_device_id msm8974_noc_of_match[] = { @@ -764,7 +762,7 @@ MODULE_DEVICE_TABLE(of, msm8974_noc_of_match); static struct platform_driver msm8974_noc_driver = { .probe = msm8974_icc_probe, - .remove = msm8974_icc_remove, + .remove_new = msm8974_icc_remove, .driver = { .name = "qnoc-msm8974", .of_match_table = msm8974_noc_of_match, diff --git a/drivers/interconnect/qcom/msm8996.c b/drivers/interconnect/qcom/msm8996.c index b73566c9b21f..788131400cd1 100644 --- a/drivers/interconnect/qcom/msm8996.c +++ b/drivers/interconnect/qcom/msm8996.c @@ -2108,7 +2108,7 @@ MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { .probe = qnoc_probe, - .remove = qnoc_remove, + .remove_new = qnoc_remove, .driver = { .name = "qnoc-msm8996", .of_match_table = qnoc_of_match, diff --git a/drivers/interconnect/qcom/osm-l3.c b/drivers/interconnect/qcom/osm-l3.c index e97478bbc282..61a8695a9adc 100644 --- a/drivers/interconnect/qcom/osm-l3.c +++ b/drivers/interconnect/qcom/osm-l3.c @@ -148,14 +148,12 @@ static int qcom_osm_l3_set(struct icc_node *src, struct icc_node *dst) return 0; } -static int qcom_osm_l3_remove(struct platform_device *pdev) +static void qcom_osm_l3_remove(struct platform_device *pdev) { struct qcom_osm_l3_icc_provider *qp = platform_get_drvdata(pdev); icc_provider_deregister(&qp->provider); icc_nodes_remove(&qp->provider); - - return 0; } static int qcom_osm_l3_probe(struct platform_device *pdev) @@ -292,7 +290,7 @@ MODULE_DEVICE_TABLE(of, osm_l3_of_match); static struct platform_driver osm_l3_driver = { .probe = qcom_osm_l3_probe, - .remove = qcom_osm_l3_remove, + .remove_new = qcom_osm_l3_remove, .driver = { .name = "osm-l3", .of_match_table = osm_l3_of_match, diff --git a/drivers/interconnect/qcom/qcm2290.c b/drivers/interconnect/qcom/qcm2290.c index b88cf9a022e0..96735800b13c 100644 --- a/drivers/interconnect/qcom/qcm2290.c +++ b/drivers/interconnect/qcom/qcm2290.c @@ -1367,7 +1367,7 @@ MODULE_DEVICE_TABLE(of, qcm2290_noc_of_match); static struct platform_driver qcm2290_noc_driver = { .probe = qnoc_probe, - .remove = qnoc_remove, + .remove_new = qnoc_remove, .driver = { .name = "qnoc-qcm2290", .of_match_table = qcm2290_noc_of_match, diff --git a/drivers/interconnect/qcom/qcs404.c b/drivers/interconnect/qcom/qcs404.c index 9fa1da70c843..11b49a89c03d 100644 --- a/drivers/interconnect/qcom/qcs404.c +++ b/drivers/interconnect/qcom/qcs404.c @@ -1083,7 +1083,7 @@ MODULE_DEVICE_TABLE(of, qcs404_noc_of_match); static struct platform_driver qcs404_noc_driver = { .probe = qnoc_probe, - .remove = qnoc_remove, + .remove_new = qnoc_remove, .driver = { .name = "qnoc-qcs404", .of_match_table = qcs404_noc_of_match, diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c index 7392bebba334..ab91de446da8 100644 --- a/drivers/interconnect/qcom/sdm660.c +++ b/drivers/interconnect/qcom/sdm660.c @@ -1714,7 +1714,7 @@ MODULE_DEVICE_TABLE(of, sdm660_noc_of_match); static struct platform_driver sdm660_noc_driver = { .probe = qnoc_probe, - .remove = qnoc_remove, + .remove_new = qnoc_remove, .driver = { .name = "qnoc-sdm660", .of_match_table = sdm660_noc_of_match, diff --git a/drivers/interconnect/qcom/sm6115.c b/drivers/interconnect/qcom/sm6115.c new file mode 100644 index 000000000000..88b67634aa2f --- /dev/null +++ b/drivers/interconnect/qcom/sm6115.c @@ -0,0 +1,1423 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, Linaro Limited + */ + +#include <dt-bindings/interconnect/qcom,sm6115.h> +#include <linux/device.h> +#include <linux/interconnect-provider.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include "icc-rpm.h" + +static const char * const snoc_intf_clocks[] = { + "cpu_axi", + "ufs_axi", + "usb_axi", + "ipa", /* Required by qxm_ipa */ +}; + +static const char * const cnoc_intf_clocks[] = { + "usb_axi", +}; + +enum { + SM6115_MASTER_AMPSS_M0, + SM6115_MASTER_ANOC_SNOC, + SM6115_MASTER_BIMC_SNOC, + SM6115_MASTER_CAMNOC_HF, + SM6115_MASTER_CAMNOC_SF, + SM6115_MASTER_CRYPTO_CORE0, + SM6115_MASTER_GRAPHICS_3D, + SM6115_MASTER_IPA, + SM6115_MASTER_MDP_PORT0, + SM6115_MASTER_PIMEM, + SM6115_MASTER_QDSS_BAM, + SM6115_MASTER_QDSS_DAP, + SM6115_MASTER_QDSS_ETR, + SM6115_MASTER_QPIC, + SM6115_MASTER_QUP_0, + SM6115_MASTER_QUP_CORE_0, + SM6115_MASTER_SDCC_1, + SM6115_MASTER_SDCC_2, + SM6115_MASTER_SNOC_BIMC_NRT, + SM6115_MASTER_SNOC_BIMC_RT, + SM6115_MASTER_SNOC_BIMC, + SM6115_MASTER_SNOC_CFG, + SM6115_MASTER_SNOC_CNOC, + SM6115_MASTER_TCU_0, + SM6115_MASTER_TIC, + SM6115_MASTER_USB3, + SM6115_MASTER_VIDEO_P0, + SM6115_MASTER_VIDEO_PROC, + + SM6115_SLAVE_AHB2PHY_USB, + SM6115_SLAVE_ANOC_SNOC, + SM6115_SLAVE_APPSS, + SM6115_SLAVE_APSS_THROTTLE_CFG, + SM6115_SLAVE_BIMC_CFG, + SM6115_SLAVE_BIMC_SNOC, + SM6115_SLAVE_BOOT_ROM, + SM6115_SLAVE_CAMERA_CFG, + SM6115_SLAVE_CAMERA_NRT_THROTTLE_CFG, + SM6115_SLAVE_CAMERA_RT_THROTTLE_CFG, + SM6115_SLAVE_CLK_CTL, + SM6115_SLAVE_CNOC_MSS, + SM6115_SLAVE_CRYPTO_0_CFG, + SM6115_SLAVE_DCC_CFG, + SM6115_SLAVE_DDR_PHY_CFG, + SM6115_SLAVE_DDR_SS_CFG, + SM6115_SLAVE_DISPLAY_CFG, + SM6115_SLAVE_DISPLAY_THROTTLE_CFG, + SM6115_SLAVE_EBI_CH0, + SM6115_SLAVE_GPU_CFG, + SM6115_SLAVE_GPU_THROTTLE_CFG, + SM6115_SLAVE_HWKM_CORE, + SM6115_SLAVE_IMEM_CFG, + SM6115_SLAVE_IPA_CFG, + SM6115_SLAVE_LPASS, + SM6115_SLAVE_MAPSS, + SM6115_SLAVE_MDSP_MPU_CFG, + SM6115_SLAVE_MESSAGE_RAM, + SM6115_SLAVE_OCIMEM, + SM6115_SLAVE_PDM, + SM6115_SLAVE_PIMEM_CFG, + SM6115_SLAVE_PIMEM, + SM6115_SLAVE_PKA_CORE, + SM6115_SLAVE_PMIC_ARB, + SM6115_SLAVE_QDSS_CFG, + SM6115_SLAVE_QDSS_STM, + SM6115_SLAVE_QM_CFG, + SM6115_SLAVE_QM_MPU_CFG, + SM6115_SLAVE_QPIC, + SM6115_SLAVE_QUP_0, + SM6115_SLAVE_QUP_CORE_0, + SM6115_SLAVE_RBCPR_CX_CFG, + SM6115_SLAVE_RBCPR_MX_CFG, + SM6115_SLAVE_RPM, + SM6115_SLAVE_SDCC_1, + SM6115_SLAVE_SDCC_2, + SM6115_SLAVE_SECURITY, + SM6115_SLAVE_SERVICE_CNOC, + SM6115_SLAVE_SERVICE_SNOC, + SM6115_SLAVE_SNOC_BIMC_NRT, + SM6115_SLAVE_SNOC_BIMC_RT, + SM6115_SLAVE_SNOC_BIMC, + SM6115_SLAVE_SNOC_CFG, + SM6115_SLAVE_SNOC_CNOC, + SM6115_SLAVE_TCSR, + SM6115_SLAVE_TCU, + SM6115_SLAVE_TLMM, + SM6115_SLAVE_USB3, + SM6115_SLAVE_VENUS_CFG, + SM6115_SLAVE_VENUS_THROTTLE_CFG, + SM6115_SLAVE_VSENSE_CTRL_CFG, +}; + +static const u16 slv_ebi_slv_bimc_snoc_links[] = { + SM6115_SLAVE_EBI_CH0, + SM6115_SLAVE_BIMC_SNOC, +}; + +static struct qcom_icc_node apps_proc = { + .name = "apps_proc", + .id = SM6115_MASTER_AMPSS_M0, + .channels = 1, + .buswidth = 16, + .qos.qos_port = 0, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.prio_level = 0, + .qos.areq_prio = 0, + .mas_rpm_id = 0, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(slv_ebi_slv_bimc_snoc_links), + .links = slv_ebi_slv_bimc_snoc_links, +}; + +static const u16 link_slv_ebi[] = { + SM6115_SLAVE_EBI_CH0, +}; + +static struct qcom_icc_node mas_snoc_bimc_rt = { + .name = "mas_snoc_bimc_rt", + .id = SM6115_MASTER_SNOC_BIMC_RT, + .channels = 1, + .buswidth = 16, + .qos.qos_port = 2, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_ebi), + .links = link_slv_ebi, +}; + +static struct qcom_icc_node mas_snoc_bimc_nrt = { + .name = "mas_snoc_bimc_nrt", + .id = SM6115_MASTER_SNOC_BIMC_NRT, + .channels = 1, + .buswidth = 16, + .qos.qos_port = 3, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_ebi), + .links = link_slv_ebi, +}; + +static struct qcom_icc_node mas_snoc_bimc = { + .name = "mas_snoc_bimc", + .id = SM6115_MASTER_SNOC_BIMC, + .channels = 1, + .buswidth = 16, + .qos.qos_port = 6, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .mas_rpm_id = 3, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_ebi), + .links = link_slv_ebi, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .id = SM6115_MASTER_GRAPHICS_3D, + .channels = 1, + .buswidth = 32, + .qos.qos_port = 1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.prio_level = 0, + .qos.areq_prio = 0, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(slv_ebi_slv_bimc_snoc_links), + .links = slv_ebi_slv_bimc_snoc_links, +}; + +static struct qcom_icc_node tcu_0 = { + .name = "tcu_0", + .id = SM6115_MASTER_TCU_0, + .channels = 1, + .buswidth = 8, + .qos.qos_port = 4, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.prio_level = 6, + .qos.areq_prio = 6, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(slv_ebi_slv_bimc_snoc_links), + .links = slv_ebi_slv_bimc_snoc_links, +}; + +static const u16 qup_core_0_links[] = { + SM6115_SLAVE_QUP_CORE_0, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .id = SM6115_MASTER_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = 170, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(qup_core_0_links), + .links = qup_core_0_links, +}; + +static const u16 link_slv_anoc_snoc[] = { + SM6115_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node crypto_c0 = { + .name = "crypto_c0", + .id = SM6115_MASTER_CRYPTO_CORE0, + .channels = 1, + .buswidth = 8, + .qos.qos_port = 43, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 23, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_anoc_snoc), + .links = link_slv_anoc_snoc, +}; + +static const u16 mas_snoc_cnoc_links[] = { + SM6115_SLAVE_AHB2PHY_USB, + SM6115_SLAVE_APSS_THROTTLE_CFG, + SM6115_SLAVE_BIMC_CFG, + SM6115_SLAVE_BOOT_ROM, + SM6115_SLAVE_CAMERA_CFG, + SM6115_SLAVE_CAMERA_NRT_THROTTLE_CFG, + SM6115_SLAVE_CAMERA_RT_THROTTLE_CFG, + SM6115_SLAVE_CLK_CTL, + SM6115_SLAVE_CNOC_MSS, + SM6115_SLAVE_CRYPTO_0_CFG, + SM6115_SLAVE_DCC_CFG, + SM6115_SLAVE_DDR_PHY_CFG, + SM6115_SLAVE_DDR_SS_CFG, + SM6115_SLAVE_DISPLAY_CFG, + SM6115_SLAVE_DISPLAY_THROTTLE_CFG, + SM6115_SLAVE_GPU_CFG, + SM6115_SLAVE_GPU_THROTTLE_CFG, + SM6115_SLAVE_HWKM_CORE, + SM6115_SLAVE_IMEM_CFG, + SM6115_SLAVE_IPA_CFG, + SM6115_SLAVE_LPASS, + SM6115_SLAVE_MAPSS, + SM6115_SLAVE_MDSP_MPU_CFG, + SM6115_SLAVE_MESSAGE_RAM, + SM6115_SLAVE_PDM, + SM6115_SLAVE_PIMEM_CFG, + SM6115_SLAVE_PKA_CORE, + SM6115_SLAVE_PMIC_ARB, + SM6115_SLAVE_QDSS_CFG, + SM6115_SLAVE_QM_CFG, + SM6115_SLAVE_QM_MPU_CFG, + SM6115_SLAVE_QPIC, + SM6115_SLAVE_QUP_0, + SM6115_SLAVE_RBCPR_CX_CFG, + SM6115_SLAVE_RBCPR_MX_CFG, + SM6115_SLAVE_RPM, + SM6115_SLAVE_SDCC_1, + SM6115_SLAVE_SDCC_2, + SM6115_SLAVE_SECURITY, + SM6115_SLAVE_SERVICE_CNOC, + SM6115_SLAVE_SNOC_CFG, + SM6115_SLAVE_TCSR, + SM6115_SLAVE_TLMM, + SM6115_SLAVE_USB3, + SM6115_SLAVE_VENUS_CFG, + SM6115_SLAVE_VENUS_THROTTLE_CFG, + SM6115_SLAVE_VSENSE_CTRL_CFG, +}; + +static struct qcom_icc_node mas_snoc_cnoc = { + .name = "mas_snoc_cnoc", + .id = SM6115_MASTER_SNOC_CNOC, + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_cnoc_links), + .links = mas_snoc_cnoc_links, +}; + +static struct qcom_icc_node xm_dap = { + .name = "xm_dap", + .id = SM6115_MASTER_QDSS_DAP, + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_cnoc_links), + .links = mas_snoc_cnoc_links, +}; + +static const u16 link_slv_snoc_bimc_nrt[] = { + SM6115_SLAVE_SNOC_BIMC_NRT, +}; + +static struct qcom_icc_node qnm_camera_nrt = { + .name = "qnm_camera_nrt", + .id = SM6115_MASTER_CAMNOC_SF, + .channels = 1, + .buswidth = 32, + .qos.qos_port = 25, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 3, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_snoc_bimc_nrt), + .links = link_slv_snoc_bimc_nrt, +}; + +static struct qcom_icc_node qxm_venus0 = { + .name = "qxm_venus0", + .id = SM6115_MASTER_VIDEO_P0, + .channels = 1, + .buswidth = 16, + .qos.qos_port = 30, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 3, + .qos.urg_fwd_en = true, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_snoc_bimc_nrt), + .links = link_slv_snoc_bimc_nrt, +}; + +static struct qcom_icc_node qxm_venus_cpu = { + .name = "qxm_venus_cpu", + .id = SM6115_MASTER_VIDEO_PROC, + .channels = 1, + .buswidth = 8, + .qos.qos_port = 34, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_snoc_bimc_nrt), + .links = link_slv_snoc_bimc_nrt, +}; + +static const u16 link_slv_snoc_bimc_rt[] = { + SM6115_SLAVE_SNOC_BIMC_RT, +}; + +static struct qcom_icc_node qnm_camera_rt = { + .name = "qnm_camera_rt", + .id = SM6115_MASTER_CAMNOC_HF, + .channels = 1, + .buswidth = 32, + .qos.qos_port = 31, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 3, + .qos.urg_fwd_en = true, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_snoc_bimc_rt), + .links = link_slv_snoc_bimc_rt, +}; + +static struct qcom_icc_node qxm_mdp0 = { + .name = "qxm_mdp0", + .id = SM6115_MASTER_MDP_PORT0, + .channels = 1, + .buswidth = 16, + .qos.qos_port = 26, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 3, + .qos.urg_fwd_en = true, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_snoc_bimc_rt), + .links = link_slv_snoc_bimc_rt, +}; + +static const u16 slv_service_snoc_links[] = { + SM6115_SLAVE_SERVICE_SNOC, +}; + +static struct qcom_icc_node qhm_snoc_cfg = { + .name = "qhm_snoc_cfg", + .id = SM6115_MASTER_SNOC_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(slv_service_snoc_links), + .links = slv_service_snoc_links, +}; + +static const u16 mas_tic_links[] = { + SM6115_SLAVE_APPSS, + SM6115_SLAVE_OCIMEM, + SM6115_SLAVE_PIMEM, + SM6115_SLAVE_QDSS_STM, + SM6115_SLAVE_TCU, + SM6115_SLAVE_SNOC_BIMC, + SM6115_SLAVE_SNOC_CNOC, +}; + +static struct qcom_icc_node qhm_tic = { + .name = "qhm_tic", + .id = SM6115_MASTER_TIC, + .channels = 1, + .buswidth = 4, + .qos.qos_port = 29, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_tic_links), + .links = mas_tic_links, +}; + +static struct qcom_icc_node mas_anoc_snoc = { + .name = "mas_anoc_snoc", + .id = SM6115_MASTER_ANOC_SNOC, + .channels = 1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_tic_links), + .links = mas_tic_links, +}; + +static const u16 mas_bimc_snoc_links[] = { + SM6115_SLAVE_APPSS, + SM6115_SLAVE_SNOC_CNOC, + SM6115_SLAVE_OCIMEM, + SM6115_SLAVE_PIMEM, + SM6115_SLAVE_QDSS_STM, + SM6115_SLAVE_TCU, +}; + +static struct qcom_icc_node mas_bimc_snoc = { + .name = "mas_bimc_snoc", + .id = SM6115_MASTER_BIMC_SNOC, + .channels = 1, + .buswidth = 8, + .mas_rpm_id = 21, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_bimc_snoc_links), + .links = mas_bimc_snoc_links, +}; + +static const u16 mas_pimem_links[] = { + SM6115_SLAVE_OCIMEM, + SM6115_SLAVE_SNOC_BIMC, +}; + +static struct qcom_icc_node qxm_pimem = { + .name = "qxm_pimem", + .id = SM6115_MASTER_PIMEM, + .channels = 1, + .buswidth = 8, + .qos.qos_port = 41, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pimem_links), + .links = mas_pimem_links, +}; + +static struct qcom_icc_node qhm_qdss_bam = { + .name = "qhm_qdss_bam", + .id = SM6115_MASTER_QDSS_BAM, + .channels = 1, + .buswidth = 4, + .qos.qos_port = 23, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_anoc_snoc), + .links = link_slv_anoc_snoc, +}; + +static struct qcom_icc_node qhm_qpic = { + .name = "qhm_qpic", + .id = SM6115_MASTER_QPIC, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_anoc_snoc), + .links = link_slv_anoc_snoc, +}; + +static struct qcom_icc_node qhm_qup0 = { + .name = "qhm_qup0", + .id = SM6115_MASTER_QUP_0, + .channels = 1, + .buswidth = 4, + .qos.qos_port = 21, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 166, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_anoc_snoc), + .links = link_slv_anoc_snoc, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .id = SM6115_MASTER_IPA, + .channels = 1, + .buswidth = 8, + .qos.qos_port = 24, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 59, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_anoc_snoc), + .links = link_slv_anoc_snoc, +}; + +static struct qcom_icc_node xm_qdss_etr = { + .name = "xm_qdss_etr", + .id = SM6115_MASTER_QDSS_ETR, + .channels = 1, + .buswidth = 8, + .qos.qos_port = 33, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_anoc_snoc), + .links = link_slv_anoc_snoc, +}; + +static struct qcom_icc_node xm_sdc1 = { + .name = "xm_sdc1", + .id = SM6115_MASTER_SDCC_1, + .channels = 1, + .buswidth = 8, + .qos.qos_port = 38, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 33, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_anoc_snoc), + .links = link_slv_anoc_snoc, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .id = SM6115_MASTER_SDCC_2, + .channels = 1, + .buswidth = 8, + .qos.qos_port = 44, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 35, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_anoc_snoc), + .links = link_slv_anoc_snoc, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .id = SM6115_MASTER_USB3, + .channels = 1, + .buswidth = 8, + .qos.qos_port = 45, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(link_slv_anoc_snoc), + .links = link_slv_anoc_snoc, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .id = SM6115_SLAVE_EBI_CH0, + .channels = 2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 0, +}; + +static const u16 slv_bimc_snoc_links[] = { + SM6115_MASTER_BIMC_SNOC, +}; + +static struct qcom_icc_node slv_bimc_snoc = { + .name = "slv_bimc_snoc", + .id = SM6115_SLAVE_BIMC_SNOC, + .channels = 1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 2, + .num_links = ARRAY_SIZE(slv_bimc_snoc_links), + .links = slv_bimc_snoc_links, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .id = SM6115_SLAVE_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_ahb2phy_usb = { + .name = "qhs_ahb2phy_usb", + .id = SM6115_SLAVE_AHB2PHY_USB, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_apss_throttle_cfg = { + .name = "qhs_apss_throttle_cfg", + .id = SM6115_SLAVE_APSS_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_bimc_cfg = { + .name = "qhs_bimc_cfg", + .id = SM6115_SLAVE_BIMC_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_boot_rom = { + .name = "qhs_boot_rom", + .id = SM6115_SLAVE_BOOT_ROM, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_camera_nrt_throttle_cfg = { + .name = "qhs_camera_nrt_throttle_cfg", + .id = SM6115_SLAVE_CAMERA_NRT_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_camera_rt_throttle_cfg = { + .name = "qhs_camera_rt_throttle_cfg", + .id = SM6115_SLAVE_CAMERA_RT_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_camera_ss_cfg = { + .name = "qhs_camera_ss_cfg", + .id = SM6115_SLAVE_CAMERA_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .id = SM6115_SLAVE_CLK_CTL, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_cpr_cx = { + .name = "qhs_cpr_cx", + .id = SM6115_SLAVE_RBCPR_CX_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_cpr_mx = { + .name = "qhs_cpr_mx", + .id = SM6115_SLAVE_RBCPR_MX_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .id = SM6115_SLAVE_CRYPTO_0_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_dcc_cfg = { + .name = "qhs_dcc_cfg", + .id = SM6115_SLAVE_DCC_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_ddr_phy_cfg = { + .name = "qhs_ddr_phy_cfg", + .id = SM6115_SLAVE_DDR_PHY_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_ddr_ss_cfg = { + .name = "qhs_ddr_ss_cfg", + .id = SM6115_SLAVE_DDR_SS_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_disp_ss_cfg = { + .name = "qhs_disp_ss_cfg", + .id = SM6115_SLAVE_DISPLAY_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_display_throttle_cfg = { + .name = "qhs_display_throttle_cfg", + .id = SM6115_SLAVE_DISPLAY_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_gpu_cfg = { + .name = "qhs_gpu_cfg", + .id = SM6115_SLAVE_GPU_CFG, + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_gpu_throttle_cfg = { + .name = "qhs_gpu_throttle_cfg", + .id = SM6115_SLAVE_GPU_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_hwkm = { + .name = "qhs_hwkm", + .id = SM6115_SLAVE_HWKM_CORE, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .id = SM6115_SLAVE_IMEM_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_ipa_cfg = { + .name = "qhs_ipa_cfg", + .id = SM6115_SLAVE_IPA_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_lpass = { + .name = "qhs_lpass", + .id = SM6115_SLAVE_LPASS, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_mapss = { + .name = "qhs_mapss", + .id = SM6115_SLAVE_MAPSS, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_mdsp_mpu_cfg = { + .name = "qhs_mdsp_mpu_cfg", + .id = SM6115_SLAVE_MDSP_MPU_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_mesg_ram = { + .name = "qhs_mesg_ram", + .id = SM6115_SLAVE_MESSAGE_RAM, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_mss = { + .name = "qhs_mss", + .id = SM6115_SLAVE_CNOC_MSS, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_pdm = { + .name = "qhs_pdm", + .id = SM6115_SLAVE_PDM, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_pimem_cfg = { + .name = "qhs_pimem_cfg", + .id = SM6115_SLAVE_PIMEM_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_pka_wrapper = { + .name = "qhs_pka_wrapper", + .id = SM6115_SLAVE_PKA_CORE, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_pmic_arb = { + .name = "qhs_pmic_arb", + .id = SM6115_SLAVE_PMIC_ARB, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .id = SM6115_SLAVE_QDSS_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_qm_cfg = { + .name = "qhs_qm_cfg", + .id = SM6115_SLAVE_QM_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_qm_mpu_cfg = { + .name = "qhs_qm_mpu_cfg", + .id = SM6115_SLAVE_QM_MPU_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_qpic = { + .name = "qhs_qpic", + .id = SM6115_SLAVE_QPIC, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_qup0 = { + .name = "qhs_qup0", + .id = SM6115_SLAVE_QUP_0, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_rpm = { + .name = "qhs_rpm", + .id = SM6115_SLAVE_RPM, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_sdc1 = { + .name = "qhs_sdc1", + .id = SM6115_SLAVE_SDCC_1, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .id = SM6115_SLAVE_SDCC_2, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_security = { + .name = "qhs_security", + .id = SM6115_SLAVE_SECURITY, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static const u16 slv_snoc_cfg_links[] = { + SM6115_MASTER_SNOC_CFG, +}; + +static struct qcom_icc_node qhs_snoc_cfg = { + .name = "qhs_snoc_cfg", + .id = SM6115_SLAVE_SNOC_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(slv_snoc_cfg_links), + .links = slv_snoc_cfg_links, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .id = SM6115_SLAVE_TCSR, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .id = SM6115_SLAVE_TLMM, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_usb3 = { + .name = "qhs_usb3", + .id = SM6115_SLAVE_USB3, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .id = SM6115_SLAVE_VENUS_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_venus_throttle_cfg = { + .name = "qhs_venus_throttle_cfg", + .id = SM6115_SLAVE_VENUS_THROTTLE_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .name = "qhs_vsense_ctrl_cfg", + .id = SM6115_SLAVE_VSENSE_CTRL_CFG, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node srvc_cnoc = { + .name = "srvc_cnoc", + .id = SM6115_SLAVE_SERVICE_CNOC, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static const u16 slv_snoc_bimc_nrt_links[] = { + SM6115_MASTER_SNOC_BIMC_NRT, +}; + +static struct qcom_icc_node slv_snoc_bimc_nrt = { + .name = "slv_snoc_bimc_nrt", + .id = SM6115_SLAVE_SNOC_BIMC_NRT, + .channels = 1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(slv_snoc_bimc_nrt_links), + .links = slv_snoc_bimc_nrt_links, +}; + +static const u16 slv_snoc_bimc_rt_links[] = { + SM6115_MASTER_SNOC_BIMC_RT, +}; + +static struct qcom_icc_node slv_snoc_bimc_rt = { + .name = "slv_snoc_bimc_rt", + .id = SM6115_SLAVE_SNOC_BIMC_RT, + .channels = 1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(slv_snoc_bimc_rt_links), + .links = slv_snoc_bimc_rt_links, +}; + +static struct qcom_icc_node qhs_apss = { + .name = "qhs_apss", + .id = SM6115_SLAVE_APPSS, + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static const u16 slv_snoc_cnoc_links[] = { + SM6115_MASTER_SNOC_CNOC +}; + +static struct qcom_icc_node slv_snoc_cnoc = { + .name = "slv_snoc_cnoc", + .id = SM6115_SLAVE_SNOC_CNOC, + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 25, + .num_links = ARRAY_SIZE(slv_snoc_cnoc_links), + .links = slv_snoc_cnoc_links, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .id = SM6115_SLAVE_OCIMEM, + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 26, +}; + +static struct qcom_icc_node qxs_pimem = { + .name = "qxs_pimem", + .id = SM6115_SLAVE_PIMEM, + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static const u16 slv_snoc_bimc_links[] = { + SM6115_MASTER_SNOC_BIMC, +}; + +static struct qcom_icc_node slv_snoc_bimc = { + .name = "slv_snoc_bimc", + .id = SM6115_SLAVE_SNOC_BIMC, + .channels = 1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 24, + .num_links = ARRAY_SIZE(slv_snoc_bimc_links), + .links = slv_snoc_bimc_links, +}; + +static struct qcom_icc_node srvc_snoc = { + .name = "srvc_snoc", + .id = SM6115_SLAVE_SERVICE_SNOC, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .id = SM6115_SLAVE_QDSS_STM, + .channels = 1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 30, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .id = SM6115_SLAVE_TCU, + .channels = 1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = -1, +}; + +static const u16 slv_anoc_snoc_links[] = { + SM6115_MASTER_ANOC_SNOC, +}; + +static struct qcom_icc_node slv_anoc_snoc = { + .name = "slv_anoc_snoc", + .id = SM6115_SLAVE_ANOC_SNOC, + .channels = 1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(slv_anoc_snoc_links), + .links = slv_anoc_snoc_links, +}; + +static struct qcom_icc_node *bimc_nodes[] = { + [MASTER_AMPSS_M0] = &apps_proc, + [MASTER_SNOC_BIMC_RT] = &mas_snoc_bimc_rt, + [MASTER_SNOC_BIMC_NRT] = &mas_snoc_bimc_nrt, + [SNOC_BIMC_MAS] = &mas_snoc_bimc, + [MASTER_GRAPHICS_3D] = &qnm_gpu, + [MASTER_TCU_0] = &tcu_0, + [SLAVE_EBI_CH0] = &ebi, + [BIMC_SNOC_SLV] = &slv_bimc_snoc, +}; + +static const struct regmap_config bimc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x80000, + .fast_io = true, +}; + +static const struct qcom_icc_desc sm6115_bimc = { + .type = QCOM_ICC_BIMC, + .nodes = bimc_nodes, + .num_nodes = ARRAY_SIZE(bimc_nodes), + .regmap_cfg = &bimc_regmap_config, + .bus_clk_desc = &bimc_clk, + .keep_alive = true, + .qos_offset = 0x8000, + .ab_coeff = 153, +}; + +static struct qcom_icc_node *config_noc_nodes[] = { + [SNOC_CNOC_MAS] = &mas_snoc_cnoc, + [MASTER_QDSS_DAP] = &xm_dap, + [SLAVE_AHB2PHY_USB] = &qhs_ahb2phy_usb, + [SLAVE_APSS_THROTTLE_CFG] = &qhs_apss_throttle_cfg, + [SLAVE_BIMC_CFG] = &qhs_bimc_cfg, + [SLAVE_BOOT_ROM] = &qhs_boot_rom, + [SLAVE_CAMERA_NRT_THROTTLE_CFG] = &qhs_camera_nrt_throttle_cfg, + [SLAVE_CAMERA_RT_THROTTLE_CFG] = &qhs_camera_rt_throttle_cfg, + [SLAVE_CAMERA_CFG] = &qhs_camera_ss_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_RBCPR_CX_CFG] = &qhs_cpr_cx, + [SLAVE_RBCPR_MX_CFG] = &qhs_cpr_mx, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_DCC_CFG] = &qhs_dcc_cfg, + [SLAVE_DDR_PHY_CFG] = &qhs_ddr_phy_cfg, + [SLAVE_DDR_SS_CFG] = &qhs_ddr_ss_cfg, + [SLAVE_DISPLAY_CFG] = &qhs_disp_ss_cfg, + [SLAVE_DISPLAY_THROTTLE_CFG] = &qhs_display_throttle_cfg, + [SLAVE_GPU_CFG] = &qhs_gpu_cfg, + [SLAVE_GPU_THROTTLE_CFG] = &qhs_gpu_throttle_cfg, + [SLAVE_HWKM_CORE] = &qhs_hwkm, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_IPA_CFG] = &qhs_ipa_cfg, + [SLAVE_LPASS] = &qhs_lpass, + [SLAVE_MAPSS] = &qhs_mapss, + [SLAVE_MDSP_MPU_CFG] = &qhs_mdsp_mpu_cfg, + [SLAVE_MESSAGE_RAM] = &qhs_mesg_ram, + [SLAVE_CNOC_MSS] = &qhs_mss, + [SLAVE_PDM] = &qhs_pdm, + [SLAVE_PIMEM_CFG] = &qhs_pimem_cfg, + [SLAVE_PKA_CORE] = &qhs_pka_wrapper, + [SLAVE_PMIC_ARB] = &qhs_pmic_arb, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QM_CFG] = &qhs_qm_cfg, + [SLAVE_QM_MPU_CFG] = &qhs_qm_mpu_cfg, + [SLAVE_QPIC] = &qhs_qpic, + [SLAVE_QUP_0] = &qhs_qup0, + [SLAVE_RPM] = &qhs_rpm, + [SLAVE_SDCC_1] = &qhs_sdc1, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_SECURITY] = &qhs_security, + [SLAVE_SNOC_CFG] = &qhs_snoc_cfg, + [SLAVE_TCSR] = &qhs_tcsr, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_USB3] = &qhs_usb3, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_VENUS_THROTTLE_CFG] = &qhs_venus_throttle_cfg, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_SERVICE_CNOC] = &srvc_cnoc, +}; + +static const struct regmap_config cnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x6200, + .fast_io = true, +}; + +static const struct qcom_icc_desc sm6115_config_noc = { + .type = QCOM_ICC_QNOC, + .nodes = config_noc_nodes, + .num_nodes = ARRAY_SIZE(config_noc_nodes), + .regmap_cfg = &cnoc_regmap_config, + .intf_clocks = cnoc_intf_clocks, + .num_intf_clocks = ARRAY_SIZE(cnoc_intf_clocks), + .bus_clk_desc = &bus_1_clk, + .keep_alive = true, +}; + +static struct qcom_icc_node *sys_noc_nodes[] = { + [MASTER_CRYPTO_CORE0] = &crypto_c0, + [MASTER_SNOC_CFG] = &qhm_snoc_cfg, + [MASTER_TIC] = &qhm_tic, + [MASTER_ANOC_SNOC] = &mas_anoc_snoc, + [BIMC_SNOC_MAS] = &mas_bimc_snoc, + [MASTER_PIMEM] = &qxm_pimem, + [MASTER_QDSS_BAM] = &qhm_qdss_bam, + [MASTER_QPIC] = &qhm_qpic, + [MASTER_QUP_0] = &qhm_qup0, + [MASTER_IPA] = &qxm_ipa, + [MASTER_QDSS_ETR] = &xm_qdss_etr, + [MASTER_SDCC_1] = &xm_sdc1, + [MASTER_SDCC_2] = &xm_sdc2, + [MASTER_USB3] = &xm_usb3_0, + [SLAVE_APPSS] = &qhs_apss, + [SNOC_CNOC_SLV] = &slv_snoc_cnoc, + [SLAVE_OCIMEM] = &qxs_imem, + [SLAVE_PIMEM] = &qxs_pimem, + [SNOC_BIMC_SLV] = &slv_snoc_bimc, + [SLAVE_SERVICE_SNOC] = &srvc_snoc, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, + [SLAVE_ANOC_SNOC] = &slv_anoc_snoc, +}; + +static const struct regmap_config sys_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5f080, + .fast_io = true, +}; + +static const struct qcom_icc_desc sm6115_sys_noc = { + .type = QCOM_ICC_QNOC, + .nodes = sys_noc_nodes, + .num_nodes = ARRAY_SIZE(sys_noc_nodes), + .regmap_cfg = &sys_noc_regmap_config, + .intf_clocks = snoc_intf_clocks, + .num_intf_clocks = ARRAY_SIZE(snoc_intf_clocks), + .bus_clk_desc = &bus_2_clk, + .keep_alive = true, +}; + +static struct qcom_icc_node *clk_virt_nodes[] = { + [MASTER_QUP_CORE_0] = &qup0_core_master, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, +}; + +static const struct qcom_icc_desc sm6115_clk_virt = { + .type = QCOM_ICC_QNOC, + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .regmap_cfg = &sys_noc_regmap_config, + .bus_clk_desc = &qup_clk, + .keep_alive = true, +}; + +static struct qcom_icc_node *mmnrt_virt_nodes[] = { + [MASTER_CAMNOC_SF] = &qnm_camera_nrt, + [MASTER_VIDEO_P0] = &qxm_venus0, + [MASTER_VIDEO_PROC] = &qxm_venus_cpu, + [SLAVE_SNOC_BIMC_NRT] = &slv_snoc_bimc_nrt, +}; + +static const struct qcom_icc_desc sm6115_mmnrt_virt = { + .type = QCOM_ICC_QNOC, + .nodes = mmnrt_virt_nodes, + .num_nodes = ARRAY_SIZE(mmnrt_virt_nodes), + .regmap_cfg = &sys_noc_regmap_config, + .bus_clk_desc = &mmaxi_0_clk, + .keep_alive = true, + .ab_coeff = 142, +}; + +static struct qcom_icc_node *mmrt_virt_nodes[] = { + [MASTER_CAMNOC_HF] = &qnm_camera_rt, + [MASTER_MDP_PORT0] = &qxm_mdp0, + [SLAVE_SNOC_BIMC_RT] = &slv_snoc_bimc_rt, +}; + +static const struct qcom_icc_desc sm6115_mmrt_virt = { + .type = QCOM_ICC_QNOC, + .nodes = mmrt_virt_nodes, + .num_nodes = ARRAY_SIZE(mmrt_virt_nodes), + .regmap_cfg = &sys_noc_regmap_config, + .bus_clk_desc = &mmaxi_1_clk, + .keep_alive = true, + .ab_coeff = 139, +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,sm6115-bimc", .data = &sm6115_bimc }, + { .compatible = "qcom,sm6115-clk-virt", .data = &sm6115_clk_virt }, + { .compatible = "qcom,sm6115-cnoc", .data = &sm6115_config_noc }, + { .compatible = "qcom,sm6115-mmrt-virt", .data = &sm6115_mmrt_virt }, + { .compatible = "qcom,sm6115-mmnrt-virt", .data = &sm6115_mmnrt_virt }, + { .compatible = "qcom,sm6115-snoc", .data = &sm6115_sys_noc }, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qnoc_probe, + .remove_new = qnoc_remove, + .driver = { + .name = "qnoc-sm6115", + .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("SM6115 NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/sm8650.c b/drivers/interconnect/qcom/sm8650.c new file mode 100644 index 000000000000..b83de54577b6 --- /dev/null +++ b/drivers/interconnect/qcom/sm8650.c @@ -0,0 +1,1674 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, Linaro Limited + */ + +#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,sm8650-rpmh.h> + +#include "bcm-voter.h" +#include "icc-common.h" +#include "icc-rpmh.h" +#include "sm8650.h" + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .id = SM8650_MASTER_QSPI_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup1 = { + .name = "qhm_qup1", + .id = SM8650_MASTER_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_qup02 = { + .name = "qxm_qup02", + .id = SM8650_MASTER_QUP_3, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_sdc4 = { + .name = "xm_sdc4", + .id = SM8650_MASTER_SDCC_4, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .id = SM8650_MASTER_UFS_MEM, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .id = SM8650_MASTER_USB3_0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qdss_bam = { + .name = "qhm_qdss_bam", + .id = SM8650_MASTER_QDSS_BAM, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup2 = { + .name = "qhm_qup2", + .id = SM8650_MASTER_QUP_2, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .id = SM8650_MASTER_CRYPTO, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .id = SM8650_MASTER_IPA, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_sp = { + .name = "qxm_sp", + .id = SM8650_MASTER_SP, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .id = SM8650_MASTER_QDSS_ETR, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .id = SM8650_MASTER_QDSS_ETR_1, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .id = SM8650_MASTER_SDCC_2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .id = SM8650_MASTER_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_SLAVE_QUP_CORE_0 }, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .id = SM8650_MASTER_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_SLAVE_QUP_CORE_1 }, +}; + +static struct qcom_icc_node qup2_core_master = { + .name = "qup2_core_master", + .id = SM8650_MASTER_QUP_CORE_2, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_SLAVE_QUP_CORE_2 }, +}; + +static struct qcom_icc_node qsm_cfg = { + .name = "qsm_cfg", + .id = SM8650_MASTER_CNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 46, + .links = { SM8650_SLAVE_AHB2PHY_SOUTH, SM8650_SLAVE_AHB2PHY_NORTH, + SM8650_SLAVE_CAMERA_CFG, SM8650_SLAVE_CLK_CTL, + SM8650_SLAVE_RBCPR_CX_CFG, SM8650_SLAVE_CPR_HMX, + SM8650_SLAVE_RBCPR_MMCX_CFG, SM8650_SLAVE_RBCPR_MXA_CFG, + SM8650_SLAVE_RBCPR_MXC_CFG, SM8650_SLAVE_CPR_NSPCX, + SM8650_SLAVE_CRYPTO_0_CFG, SM8650_SLAVE_CX_RDPM, + SM8650_SLAVE_DISPLAY_CFG, SM8650_SLAVE_GFX3D_CFG, + SM8650_SLAVE_I2C, SM8650_SLAVE_I3C_IBI0_CFG, + SM8650_SLAVE_I3C_IBI1_CFG, SM8650_SLAVE_IMEM_CFG, + SM8650_SLAVE_CNOC_MSS, SM8650_SLAVE_MX_2_RDPM, + SM8650_SLAVE_MX_RDPM, SM8650_SLAVE_PCIE_0_CFG, + SM8650_SLAVE_PCIE_1_CFG, SM8650_SLAVE_PCIE_RSCC, + SM8650_SLAVE_PDM, SM8650_SLAVE_PRNG, + SM8650_SLAVE_QDSS_CFG, SM8650_SLAVE_QSPI_0, + SM8650_SLAVE_QUP_3, SM8650_SLAVE_QUP_1, + SM8650_SLAVE_QUP_2, SM8650_SLAVE_SDCC_2, + SM8650_SLAVE_SDCC_4, SM8650_SLAVE_SPSS_CFG, + SM8650_SLAVE_TCSR, SM8650_SLAVE_TLMM, + SM8650_SLAVE_UFS_MEM_CFG, SM8650_SLAVE_USB3_0, + SM8650_SLAVE_VENUS_CFG, SM8650_SLAVE_VSENSE_CTRL_CFG, + SM8650_SLAVE_CNOC_MNOC_CFG, SM8650_SLAVE_NSP_QTB_CFG, + SM8650_SLAVE_PCIE_ANOC_CFG, SM8650_SLAVE_SERVICE_CNOC_CFG, + SM8650_SLAVE_QDSS_STM, SM8650_SLAVE_TCU }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .id = SM8650_MASTER_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 9, + .links = { SM8650_SLAVE_AOSS, SM8650_SLAVE_IPA_CFG, + SM8650_SLAVE_IPC_ROUTER_CFG, SM8650_SLAVE_TME_CFG, + SM8650_SLAVE_APPSS, SM8650_SLAVE_CNOC_CFG, + SM8650_SLAVE_DDRSS_CFG, SM8650_SLAVE_IMEM, + SM8650_SLAVE_SERVICE_CNOC }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .id = SM8650_MASTER_GEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 2, + .links = { SM8650_SLAVE_PCIE_0, SM8650_SLAVE_PCIE_1 }, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .id = SM8650_MASTER_GPU_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC }, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .id = SM8650_MASTER_SYS_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC }, +}; + +static struct qcom_icc_node alm_ubwc_p_tcu = { + .name = "alm_ubwc_p_tcu", + .id = SM8650_MASTER_UBWC_P_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .id = SM8650_MASTER_APPSS_PROC, + .channels = 3, + .buswidth = 32, + .num_links = 3, + .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC, + SM8650_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .id = SM8650_MASTER_GFX3D, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_lpass_gemnoc = { + .name = "qnm_lpass_gemnoc", + .id = SM8650_MASTER_LPASS_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 3, + .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC, + SM8650_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_mdsp = { + .name = "qnm_mdsp", + .id = SM8650_MASTER_MSS_PROC, + .channels = 1, + .buswidth = 16, + .num_links = 3, + .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC, + SM8650_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .id = SM8650_MASTER_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .id = SM8650_MASTER_MNOC_SF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_nsp_gemnoc = { + .name = "qnm_nsp_gemnoc", + .id = SM8650_MASTER_COMPUTE_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 3, + .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC, + SM8650_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .id = SM8650_MASTER_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 2, + .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .id = SM8650_MASTER_SNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 3, + .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC, + SM8650_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_ubwc_p = { + .name = "qnm_ubwc_p", + .id = SM8650_MASTER_UBWC_P, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SM8650_SLAVE_LLCC }, +}; + +static struct qcom_icc_node xm_gic = { + .name = "xm_gic", + .id = SM8650_MASTER_GIC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc = { + .name = "qnm_lpiaon_noc", + .id = SM8650_MASTER_LPIAON_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_SLAVE_LPASS_GEM_NOC }, +}; + +static struct qcom_icc_node qnm_lpass_lpinoc = { + .name = "qnm_lpass_lpinoc", + .id = SM8650_MASTER_LPASS_LPINOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_SLAVE_LPIAON_NOC_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node qxm_lpinoc_dsp_axim = { + .name = "qxm_lpinoc_dsp_axim", + .id = SM8650_MASTER_LPASS_PROC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_SLAVE_LPICX_NOC_LPIAON_NOC }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .id = SM8650_MASTER_LLCC, + .channels = 4, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_SLAVE_EBI1 }, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .id = SM8650_MASTER_CAMNOC_HF, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8650_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_camnoc_icp = { + .name = "qnm_camnoc_icp", + .id = SM8650_MASTER_CAMNOC_ICP, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .id = SM8650_MASTER_CAMNOC_SF, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_mdp = { + .name = "qnm_mdp", + .id = SM8650_MASTER_MDP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8650_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_vapss_hcp = { + .name = "qnm_vapss_hcp", + .id = SM8650_MASTER_CDSP_HCP, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video = { + .name = "qnm_video", + .id = SM8650_MASTER_VIDEO, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_cv_cpu = { + .name = "qnm_video_cv_cpu", + .id = SM8650_MASTER_VIDEO_CV_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_cvp = { + .name = "qnm_video_cvp", + .id = SM8650_MASTER_VIDEO_PROC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_v_cpu = { + .name = "qnm_video_v_cpu", + .id = SM8650_MASTER_VIDEO_V_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qsm_mnoc_cfg = { + .name = "qsm_mnoc_cfg", + .id = SM8650_MASTER_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_SLAVE_SERVICE_MNOC }, +}; + +static struct qcom_icc_node qnm_nsp = { + .name = "qnm_nsp", + .id = SM8650_MASTER_CDSP_PROC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8650_SLAVE_CDSP_MEM_NOC }, +}; + +static struct qcom_icc_node qsm_pcie_anoc_cfg = { + .name = "qsm_pcie_anoc_cfg", + .id = SM8650_MASTER_PCIE_ANOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_SLAVE_SERVICE_PCIE_ANOC }, +}; + +static struct qcom_icc_node xm_pcie3_0 = { + .name = "xm_pcie3_0", + .id = SM8650_MASTER_PCIE_0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node xm_pcie3_1 = { + .name = "xm_pcie3_1", + .id = SM8650_MASTER_PCIE_1, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node qnm_aggre1_noc = { + .name = "qnm_aggre1_noc", + .id = SM8650_MASTER_A1NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_aggre2_noc = { + .name = "qnm_aggre2_noc", + .id = SM8650_MASTER_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .id = SM8650_SLAVE_A1NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_MASTER_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qns_a2noc_snoc = { + .name = "qns_a2noc_snoc", + .id = SM8650_SLAVE_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_MASTER_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .id = SM8650_SLAVE_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .id = SM8650_SLAVE_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup2_core_slave = { + .name = "qup2_core_slave", + .id = SM8650_SLAVE_QUP_CORE_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .id = SM8650_SLAVE_AHB2PHY_SOUTH, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy1 = { + .name = "qhs_ahb2phy1", + .id = SM8650_SLAVE_AHB2PHY_NORTH, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .id = SM8650_SLAVE_CAMERA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .id = SM8650_SLAVE_CLK_CTL, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_cx = { + .name = "qhs_cpr_cx", + .id = SM8650_SLAVE_RBCPR_CX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_hmx = { + .name = "qhs_cpr_hmx", + .id = SM8650_SLAVE_CPR_HMX, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mmcx = { + .name = "qhs_cpr_mmcx", + .id = SM8650_SLAVE_RBCPR_MMCX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mxa = { + .name = "qhs_cpr_mxa", + .id = SM8650_SLAVE_RBCPR_MXA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mxc = { + .name = "qhs_cpr_mxc", + .id = SM8650_SLAVE_RBCPR_MXC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_nspcx = { + .name = "qhs_cpr_nspcx", + .id = SM8650_SLAVE_CPR_NSPCX, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .id = SM8650_SLAVE_CRYPTO_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cx_rdpm = { + .name = "qhs_cx_rdpm", + .id = SM8650_SLAVE_CX_RDPM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .id = SM8650_SLAVE_DISPLAY_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .id = SM8650_SLAVE_GFX3D_CFG, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_i2c = { + .name = "qhs_i2c", + .id = SM8650_SLAVE_I2C, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_i3c_ibi0_cfg = { + .name = "qhs_i3c_ibi0_cfg", + .id = SM8650_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 = SM8650_SLAVE_I3C_IBI1_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .id = SM8650_SLAVE_IMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mss_cfg = { + .name = "qhs_mss_cfg", + .id = SM8650_SLAVE_CNOC_MSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mx_2_rdpm = { + .name = "qhs_mx_2_rdpm", + .id = SM8650_SLAVE_MX_2_RDPM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mx_rdpm = { + .name = "qhs_mx_rdpm", + .id = SM8650_SLAVE_MX_RDPM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie0_cfg = { + .name = "qhs_pcie0_cfg", + .id = SM8650_SLAVE_PCIE_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie1_cfg = { + .name = "qhs_pcie1_cfg", + .id = SM8650_SLAVE_PCIE_1_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie_rscc = { + .name = "qhs_pcie_rscc", + .id = SM8650_SLAVE_PCIE_RSCC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pdm = { + .name = "qhs_pdm", + .id = SM8650_SLAVE_PDM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .id = SM8650_SLAVE_PRNG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .id = SM8650_SLAVE_QDSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .id = SM8650_SLAVE_QSPI_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup02 = { + .name = "qhs_qup02", + .id = SM8650_SLAVE_QUP_3, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .id = SM8650_SLAVE_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup2 = { + .name = "qhs_qup2", + .id = SM8650_SLAVE_QUP_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .id = SM8650_SLAVE_SDCC_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc4 = { + .name = "qhs_sdc4", + .id = SM8650_SLAVE_SDCC_4, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_spss_cfg = { + .name = "qhs_spss_cfg", + .id = SM8650_SLAVE_SPSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .id = SM8650_SLAVE_TCSR, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .id = SM8650_SLAVE_TLMM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .id = SM8650_SLAVE_UFS_MEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb3_0 = { + .name = "qhs_usb3_0", + .id = SM8650_SLAVE_USB3_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .id = SM8650_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 = SM8650_SLAVE_VSENSE_CTRL_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qss_mnoc_cfg = { + .name = "qss_mnoc_cfg", + .id = SM8650_SLAVE_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_MASTER_CNOC_MNOC_CFG }, +}; + +static struct qcom_icc_node qss_nsp_qtb_cfg = { + .name = "qss_nsp_qtb_cfg", + .id = SM8650_SLAVE_NSP_QTB_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qss_pcie_anoc_cfg = { + .name = "qss_pcie_anoc_cfg", + .id = SM8650_SLAVE_PCIE_ANOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_MASTER_PCIE_ANOC_CFG }, +}; + +static struct qcom_icc_node srvc_cnoc_cfg = { + .name = "srvc_cnoc_cfg", + .id = SM8650_SLAVE_SERVICE_CNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .id = SM8650_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 = SM8650_SLAVE_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .id = SM8650_SLAVE_AOSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .id = SM8650_SLAVE_IPA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .id = SM8650_SLAVE_IPC_ROUTER_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tme_cfg = { + .name = "qhs_tme_cfg", + .id = SM8650_SLAVE_TME_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qss_apss = { + .name = "qss_apss", + .id = SM8650_SLAVE_APPSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qss_cfg = { + .name = "qss_cfg", + .id = SM8650_SLAVE_CNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8650_MASTER_CNOC_CFG }, +}; + +static struct qcom_icc_node qss_ddrss_cfg = { + .name = "qss_ddrss_cfg", + .id = SM8650_SLAVE_DDRSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .id = SM8650_SLAVE_IMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_cnoc_main = { + .name = "srvc_cnoc_main", + .id = SM8650_SLAVE_SERVICE_CNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_0 = { + .name = "xs_pcie_0", + .id = SM8650_SLAVE_PCIE_0, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_1 = { + .name = "xs_pcie_1", + .id = SM8650_SLAVE_PCIE_1, + .channels = 1, + .buswidth = 16, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .id = SM8650_SLAVE_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_MASTER_GEM_NOC_CNOC }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .id = SM8650_SLAVE_LLCC, + .channels = 4, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_MASTER_LLCC }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .id = SM8650_SLAVE_MEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8650_MASTER_GEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qns_lpass_ag_noc_gemnoc = { + .name = "qns_lpass_ag_noc_gemnoc", + .id = SM8650_SLAVE_LPASS_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_MASTER_LPASS_GEM_NOC }, +}; + +static struct qcom_icc_node qns_lpass_aggnoc = { + .name = "qns_lpass_aggnoc", + .id = SM8650_SLAVE_LPIAON_NOC_LPASS_AG_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_MASTER_LPIAON_NOC }, +}; + +static struct qcom_icc_node qns_lpi_aon_noc = { + .name = "qns_lpi_aon_noc", + .id = SM8650_SLAVE_LPICX_NOC_LPIAON_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_MASTER_LPASS_LPINOC }, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .id = SM8650_SLAVE_EBI1, + .channels = 4, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .id = SM8650_SLAVE_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8650_MASTER_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .id = SM8650_SLAVE_MNOC_SF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8650_MASTER_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node srvc_mnoc = { + .name = "srvc_mnoc", + .id = SM8650_SLAVE_SERVICE_MNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .id = SM8650_SLAVE_CDSP_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8650_MASTER_COMPUTE_NOC }, +}; + +static struct qcom_icc_node qns_pcie_mem_noc = { + .name = "qns_pcie_mem_noc", + .id = SM8650_SLAVE_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_MASTER_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node srvc_pcie_aggre_noc = { + .name = "srvc_pcie_aggre_noc", + .id = SM8650_SLAVE_SERVICE_PCIE_ANOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .id = SM8650_SLAVE_SNOC_GEM_NOC_SF, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8650_MASTER_SNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .enable_mask = BIT(3), + .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 = 59, + .nodes = { &qsm_cfg, &qhs_ahb2phy0, + &qhs_ahb2phy1, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_cpr_cx, + &qhs_cpr_hmx, &qhs_cpr_mmcx, + &qhs_cpr_mxa, &qhs_cpr_mxc, + &qhs_cpr_nspcx, &qhs_crypto0_cfg, + &qhs_cx_rdpm, &qhs_display_cfg, + &qhs_gpuss_cfg, &qhs_i2c, + &qhs_i3c_ibi0_cfg, &qhs_i3c_ibi1_cfg, + &qhs_imem_cfg, &qhs_mss_cfg, + &qhs_mx_2_rdpm, &qhs_mx_rdpm, + &qhs_pcie0_cfg, &qhs_pcie1_cfg, + &qhs_pcie_rscc, &qhs_pdm, + &qhs_prng, &qhs_qdss_cfg, + &qhs_qspi, &qhs_qup02, + &qhs_qup1, &qhs_qup2, + &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_nsp_qtb_cfg, &qss_pcie_anoc_cfg, + &srvc_cnoc_cfg, &xs_qdss_stm, + &xs_sys_tcu_cfg, &qnm_gemnoc_cnoc, + &qnm_gemnoc_pcie, &qhs_aoss, + &qhs_ipa, &qhs_ipc_router, + &qhs_tme_cfg, &qss_apss, + &qss_cfg, &qss_ddrss_cfg, + &qxs_imem, &srvc_cnoc_main, + &xs_pcie_0, &xs_pcie_1 }, +}; + +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 = 8, + .nodes = { &qnm_camnoc_hf, &qnm_camnoc_icp, + &qnm_camnoc_sf, &qnm_vapss_hcp, + &qnm_video_cv_cpu, &qnm_video_cvp, + &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 = 15, + .nodes = { &alm_gpu_tcu, &alm_sys_tcu, + &alm_ubwc_p_tcu, &chm_apps, + &qnm_gpu, &qnm_mdsp, + &qnm_mnoc_hf, &qnm_mnoc_sf, + &qnm_nsp_gemnoc, &qnm_pcie, + &qnm_snoc_sf, &qnm_ubwc_p, + &xm_gic, &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_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 sm8650_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_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 sm8650_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 sm8650_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, +}; + +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_RBCPR_CX_CFG] = &qhs_cpr_cx, + [SLAVE_CPR_HMX] = &qhs_cpr_hmx, + [SLAVE_RBCPR_MMCX_CFG] = &qhs_cpr_mmcx, + [SLAVE_RBCPR_MXA_CFG] = &qhs_cpr_mxa, + [SLAVE_RBCPR_MXC_CFG] = &qhs_cpr_mxc, + [SLAVE_CPR_NSPCX] = &qhs_cpr_nspcx, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_CX_RDPM] = &qhs_cx_rdpm, + [SLAVE_DISPLAY_CFG] = &qhs_display_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_MX_2_RDPM] = &qhs_mx_2_rdpm, + [SLAVE_MX_RDPM] = &qhs_mx_rdpm, + [SLAVE_PCIE_0_CFG] = &qhs_pcie0_cfg, + [SLAVE_PCIE_1_CFG] = &qhs_pcie1_cfg, + [SLAVE_PCIE_RSCC] = &qhs_pcie_rscc, + [SLAVE_PDM] = &qhs_pdm, + [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_NSP_QTB_CFG] = &qss_nsp_qtb_cfg, + [SLAVE_PCIE_ANOC_CFG] = &qss_pcie_anoc_cfg, + [SLAVE_SERVICE_CNOC_CFG] = &srvc_cnoc_cfg, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct qcom_icc_desc sm8650_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_TME_CFG] = &qhs_tme_cfg, + [SLAVE_APPSS] = &qss_apss, + [SLAVE_CNOC_CFG] = &qss_cfg, + [SLAVE_DDRSS_CFG] = &qss_ddrss_cfg, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_SERVICE_CNOC] = &srvc_cnoc_main, + [SLAVE_PCIE_0] = &xs_pcie_0, + [SLAVE_PCIE_1] = &xs_pcie_1, +}; + +static const struct qcom_icc_desc sm8650_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, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_UBWC_P_TCU] = &alm_ubwc_p_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_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, +}; + +static const struct qcom_icc_desc sm8650_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 sm8650_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 sm8650_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] = &qxm_lpinoc_dsp_axim, + [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc, +}; + +static const struct qcom_icc_desc sm8650_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 sm8650_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_ICP] = &qnm_camnoc_icp, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_MDP] = &qnm_mdp, + [MASTER_CDSP_HCP] = &qnm_vapss_hcp, + [MASTER_VIDEO] = &qnm_video, + [MASTER_VIDEO_CV_PROC] = &qnm_video_cv_cpu, + [MASTER_VIDEO_PROC] = &qnm_video_cvp, + [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 sm8650_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 sm8650_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_0, + [MASTER_PCIE_1] = &xm_pcie3_1, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, + [SLAVE_SERVICE_PCIE_ANOC] = &srvc_pcie_aggre_noc, +}; + +static const struct qcom_icc_desc sm8650_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 sm8650_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,sm8650-aggre1-noc", .data = &sm8650_aggre1_noc }, + { .compatible = "qcom,sm8650-aggre2-noc", .data = &sm8650_aggre2_noc }, + { .compatible = "qcom,sm8650-clk-virt", .data = &sm8650_clk_virt }, + { .compatible = "qcom,sm8650-config-noc", .data = &sm8650_config_noc }, + { .compatible = "qcom,sm8650-cnoc-main", .data = &sm8650_cnoc_main }, + { .compatible = "qcom,sm8650-gem-noc", .data = &sm8650_gem_noc }, + { .compatible = "qcom,sm8650-lpass-ag-noc", .data = &sm8650_lpass_ag_noc }, + { .compatible = "qcom,sm8650-lpass-lpiaon-noc", .data = &sm8650_lpass_lpiaon_noc }, + { .compatible = "qcom,sm8650-lpass-lpicx-noc", .data = &sm8650_lpass_lpicx_noc }, + { .compatible = "qcom,sm8650-mc-virt", .data = &sm8650_mc_virt }, + { .compatible = "qcom,sm8650-mmss-noc", .data = &sm8650_mmss_noc }, + { .compatible = "qcom,sm8650-nsp-noc", .data = &sm8650_nsp_noc }, + { .compatible = "qcom,sm8650-pcie-anoc", .data = &sm8650_pcie_anoc }, + { .compatible = "qcom,sm8650-system-noc", .data = &sm8650_system_noc }, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove_new = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-sm8650", + .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("sm8650 NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/sm8650.h b/drivers/interconnect/qcom/sm8650.h new file mode 100644 index 000000000000..de35c956fe49 --- /dev/null +++ b/drivers/interconnect/qcom/sm8650.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * SM8650 interconnect IDs + * + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Linaro Limited + */ + +#ifndef __DRIVERS_INTERCONNECT_QCOM_SM8650_H +#define __DRIVERS_INTERCONNECT_QCOM_SM8650_H + +#define SM8650_MASTER_A1NOC_SNOC 0 +#define SM8650_MASTER_A2NOC_SNOC 1 +#define SM8650_MASTER_ANOC_PCIE_GEM_NOC 2 +#define SM8650_MASTER_APPSS_PROC 3 +#define SM8650_MASTER_CAMNOC_HF 4 +#define SM8650_MASTER_CAMNOC_ICP 5 +#define SM8650_MASTER_CAMNOC_SF 6 +#define SM8650_MASTER_CDSP_HCP 7 +#define SM8650_MASTER_CDSP_PROC 8 +#define SM8650_MASTER_CNOC_CFG 9 +#define SM8650_MASTER_CNOC_MNOC_CFG 10 +#define SM8650_MASTER_COMPUTE_NOC 11 +#define SM8650_MASTER_CRYPTO 12 +#define SM8650_MASTER_GEM_NOC_CNOC 13 +#define SM8650_MASTER_GEM_NOC_PCIE_SNOC 14 +#define SM8650_MASTER_GFX3D 15 +#define SM8650_MASTER_GIC 16 +#define SM8650_MASTER_GPU_TCU 17 +#define SM8650_MASTER_IPA 18 +#define SM8650_MASTER_LLCC 19 +#define SM8650_MASTER_LPASS_GEM_NOC 20 +#define SM8650_MASTER_LPASS_LPINOC 21 +#define SM8650_MASTER_LPASS_PROC 22 +#define SM8650_MASTER_LPIAON_NOC 23 +#define SM8650_MASTER_MDP 24 +#define SM8650_MASTER_MNOC_HF_MEM_NOC 25 +#define SM8650_MASTER_MNOC_SF_MEM_NOC 26 +#define SM8650_MASTER_MSS_PROC 27 +#define SM8650_MASTER_PCIE_0 28 +#define SM8650_MASTER_PCIE_1 29 +#define SM8650_MASTER_PCIE_ANOC_CFG 30 +#define SM8650_MASTER_QDSS_BAM 31 +#define SM8650_MASTER_QDSS_ETR 32 +#define SM8650_MASTER_QDSS_ETR_1 33 +#define SM8650_MASTER_QSPI_0 34 +#define SM8650_MASTER_QUP_1 35 +#define SM8650_MASTER_QUP_2 36 +#define SM8650_MASTER_QUP_3 37 +#define SM8650_MASTER_QUP_CORE_0 38 +#define SM8650_MASTER_QUP_CORE_1 39 +#define SM8650_MASTER_QUP_CORE_2 40 +#define SM8650_MASTER_SDCC_2 41 +#define SM8650_MASTER_SDCC_4 42 +#define SM8650_MASTER_SNOC_SF_MEM_NOC 43 +#define SM8650_MASTER_SP 44 +#define SM8650_MASTER_SYS_TCU 45 +#define SM8650_MASTER_UBWC_P 46 +#define SM8650_MASTER_UBWC_P_TCU 47 +#define SM8650_MASTER_UFS_MEM 48 +#define SM8650_MASTER_USB3_0 49 +#define SM8650_MASTER_VIDEO 50 +#define SM8650_MASTER_VIDEO_CV_PROC 51 +#define SM8650_MASTER_VIDEO_PROC 52 +#define SM8650_MASTER_VIDEO_V_PROC 53 +#define SM8650_SLAVE_A1NOC_SNOC 54 +#define SM8650_SLAVE_A2NOC_SNOC 55 +#define SM8650_SLAVE_AHB2PHY_NORTH 56 +#define SM8650_SLAVE_AHB2PHY_SOUTH 57 +#define SM8650_SLAVE_ANOC_PCIE_GEM_NOC 58 +#define SM8650_SLAVE_AOSS 59 +#define SM8650_SLAVE_APPSS 60 +#define SM8650_SLAVE_CAMERA_CFG 61 +#define SM8650_SLAVE_CDSP_MEM_NOC 62 +#define SM8650_SLAVE_CLK_CTL 63 +#define SM8650_SLAVE_CNOC_CFG 64 +#define SM8650_SLAVE_CNOC_MNOC_CFG 65 +#define SM8650_SLAVE_CNOC_MSS 66 +#define SM8650_SLAVE_CPR_HMX 67 +#define SM8650_SLAVE_CPR_NSPCX 68 +#define SM8650_SLAVE_CRYPTO_0_CFG 69 +#define SM8650_SLAVE_CX_RDPM 70 +#define SM8650_SLAVE_DDRSS_CFG 71 +#define SM8650_SLAVE_DISPLAY_CFG 72 +#define SM8650_SLAVE_EBI1 73 +#define SM8650_SLAVE_GEM_NOC_CNOC 74 +#define SM8650_SLAVE_GFX3D_CFG 75 +#define SM8650_SLAVE_I2C 76 +#define SM8650_SLAVE_I3C_IBI0_CFG 77 +#define SM8650_SLAVE_I3C_IBI1_CFG 78 +#define SM8650_SLAVE_IMEM 79 +#define SM8650_SLAVE_IMEM_CFG 80 +#define SM8650_SLAVE_IPA_CFG 81 +#define SM8650_SLAVE_IPC_ROUTER_CFG 82 +#define SM8650_SLAVE_LLCC 83 +#define SM8650_SLAVE_LPASS_GEM_NOC 84 +#define SM8650_SLAVE_LPIAON_NOC_LPASS_AG_NOC 85 +#define SM8650_SLAVE_LPICX_NOC_LPIAON_NOC 86 +#define SM8650_SLAVE_MEM_NOC_PCIE_SNOC 87 +#define SM8650_SLAVE_MNOC_HF_MEM_NOC 88 +#define SM8650_SLAVE_MNOC_SF_MEM_NOC 89 +#define SM8650_SLAVE_MX_2_RDPM 90 +#define SM8650_SLAVE_MX_RDPM 91 +#define SM8650_SLAVE_NSP_QTB_CFG 92 +#define SM8650_SLAVE_PCIE_0 93 +#define SM8650_SLAVE_PCIE_1 94 +#define SM8650_SLAVE_PCIE_0_CFG 95 +#define SM8650_SLAVE_PCIE_1_CFG 96 +#define SM8650_SLAVE_PCIE_ANOC_CFG 97 +#define SM8650_SLAVE_PCIE_RSCC 98 +#define SM8650_SLAVE_PDM 99 +#define SM8650_SLAVE_PRNG 100 +#define SM8650_SLAVE_QDSS_CFG 101 +#define SM8650_SLAVE_QDSS_STM 102 +#define SM8650_SLAVE_QSPI_0 103 +#define SM8650_SLAVE_QUP_1 104 +#define SM8650_SLAVE_QUP_2 105 +#define SM8650_SLAVE_QUP_3 106 +#define SM8650_SLAVE_QUP_CORE_0 107 +#define SM8650_SLAVE_QUP_CORE_1 108 +#define SM8650_SLAVE_QUP_CORE_2 109 +#define SM8650_SLAVE_RBCPR_CX_CFG 110 +#define SM8650_SLAVE_RBCPR_MMCX_CFG 111 +#define SM8650_SLAVE_RBCPR_MXA_CFG 112 +#define SM8650_SLAVE_RBCPR_MXC_CFG 113 +#define SM8650_SLAVE_SDCC_2 114 +#define SM8650_SLAVE_SDCC_4 115 +#define SM8650_SLAVE_SERVICE_CNOC 116 +#define SM8650_SLAVE_SERVICE_CNOC_CFG 117 +#define SM8650_SLAVE_SERVICE_MNOC 118 +#define SM8650_SLAVE_SERVICE_PCIE_ANOC 119 +#define SM8650_SLAVE_SNOC_GEM_NOC_SF 120 +#define SM8650_SLAVE_SPSS_CFG 121 +#define SM8650_SLAVE_TCSR 122 +#define SM8650_SLAVE_TCU 123 +#define SM8650_SLAVE_TLMM 124 +#define SM8650_SLAVE_TME_CFG 125 +#define SM8650_SLAVE_UFS_MEM_CFG 126 +#define SM8650_SLAVE_USB3_0 127 +#define SM8650_SLAVE_VENUS_CFG 128 +#define SM8650_SLAVE_VSENSE_CTRL_CFG 129 + +#endif diff --git a/drivers/interconnect/qcom/smd-rpm.c b/drivers/interconnect/qcom/smd-rpm.c index 16a145a3c914..3816bfb4e2f3 100644 --- a/drivers/interconnect/qcom/smd-rpm.c +++ b/drivers/interconnect/qcom/smd-rpm.c @@ -63,11 +63,9 @@ int qcom_icc_rpm_set_bus_rate(const struct rpm_clk_resource *clk, int ctx, u32 r } EXPORT_SYMBOL_GPL(qcom_icc_rpm_set_bus_rate); -static int qcom_icc_rpm_smd_remove(struct platform_device *pdev) +static void qcom_icc_rpm_smd_remove(struct platform_device *pdev) { icc_smd_rpm = NULL; - - return 0; } static int qcom_icc_rpm_smd_probe(struct platform_device *pdev) @@ -87,7 +85,7 @@ static struct platform_driver qcom_interconnect_rpm_smd_driver = { .name = "icc_smd_rpm", }, .probe = qcom_icc_rpm_smd_probe, - .remove = qcom_icc_rpm_smd_remove, + .remove_new = qcom_icc_rpm_smd_remove, }; module_platform_driver(qcom_interconnect_rpm_smd_driver); MODULE_AUTHOR("Georgi Djakov <georgi.djakov@linaro.org>"); diff --git a/drivers/interconnect/qcom/x1e80100.c b/drivers/interconnect/qcom/x1e80100.c new file mode 100644 index 000000000000..d19501d913b3 --- /dev/null +++ b/drivers/interconnect/qcom/x1e80100.c @@ -0,0 +1,2328 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, Linaro Limited + * + */ + +#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,x1e80100-rpmh.h> + +#include "bcm-voter.h" +#include "icc-common.h" +#include "icc-rpmh.h" +#include "x1e80100.h" + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .id = X1E80100_MASTER_QSPI_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup1 = { + .name = "qhm_qup1", + .id = X1E80100_MASTER_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_sdc4 = { + .name = "xm_sdc4", + .id = X1E80100_MASTER_SDCC_4, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .id = X1E80100_MASTER_UFS_MEM, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup0 = { + .name = "qhm_qup0", + .id = X1E80100_MASTER_QUP_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup2 = { + .name = "qhm_qup2", + .id = X1E80100_MASTER_QUP_2, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .id = X1E80100_MASTER_CRYPTO, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_sp = { + .name = "qxm_sp", + .id = X1E80100_MASTER_SP, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .id = X1E80100_MASTER_QDSS_ETR, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .id = X1E80100_MASTER_QDSS_ETR_1, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .id = X1E80100_MASTER_SDCC_2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node ddr_perf_mode_master = { + .name = "ddr_perf_mode_master", + .id = X1E80100_MASTER_DDR_PERF_MODE, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_SLAVE_DDR_PERF_MODE }, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .id = X1E80100_MASTER_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_SLAVE_QUP_CORE_0 }, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .id = X1E80100_MASTER_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_SLAVE_QUP_CORE_1 }, +}; + +static struct qcom_icc_node qup2_core_master = { + .name = "qup2_core_master", + .id = X1E80100_MASTER_QUP_CORE_2, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_SLAVE_QUP_CORE_2 }, +}; + +static struct qcom_icc_node qsm_cfg = { + .name = "qsm_cfg", + .id = X1E80100_MASTER_CNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 47, + .links = { X1E80100_SLAVE_AHB2PHY_SOUTH, X1E80100_SLAVE_AHB2PHY_NORTH, + X1E80100_SLAVE_AHB2PHY_2, X1E80100_SLAVE_AV1_ENC_CFG, + X1E80100_SLAVE_CAMERA_CFG, X1E80100_SLAVE_CLK_CTL, + X1E80100_SLAVE_CRYPTO_0_CFG, X1E80100_SLAVE_DISPLAY_CFG, + X1E80100_SLAVE_GFX3D_CFG, X1E80100_SLAVE_IMEM_CFG, + X1E80100_SLAVE_IPC_ROUTER_CFG, X1E80100_SLAVE_PCIE_0_CFG, + X1E80100_SLAVE_PCIE_1_CFG, X1E80100_SLAVE_PCIE_2_CFG, + X1E80100_SLAVE_PCIE_3_CFG, X1E80100_SLAVE_PCIE_4_CFG, + X1E80100_SLAVE_PCIE_5_CFG, X1E80100_SLAVE_PCIE_6A_CFG, + X1E80100_SLAVE_PCIE_6B_CFG, X1E80100_SLAVE_PCIE_RSC_CFG, + X1E80100_SLAVE_PDM, X1E80100_SLAVE_PRNG, + X1E80100_SLAVE_QDSS_CFG, X1E80100_SLAVE_QSPI_0, + X1E80100_SLAVE_QUP_0, X1E80100_SLAVE_QUP_1, + X1E80100_SLAVE_QUP_2, X1E80100_SLAVE_SDCC_2, + X1E80100_SLAVE_SDCC_4, X1E80100_SLAVE_SMMUV3_CFG, + X1E80100_SLAVE_TCSR, X1E80100_SLAVE_TLMM, + X1E80100_SLAVE_UFS_MEM_CFG, X1E80100_SLAVE_USB2, + X1E80100_SLAVE_USB3_0, X1E80100_SLAVE_USB3_1, + X1E80100_SLAVE_USB3_2, X1E80100_SLAVE_USB3_MP, + X1E80100_SLAVE_USB4_0, X1E80100_SLAVE_USB4_1, + X1E80100_SLAVE_USB4_2, X1E80100_SLAVE_VENUS_CFG, + X1E80100_SLAVE_LPASS_QTB_CFG, X1E80100_SLAVE_CNOC_MNOC_CFG, + X1E80100_SLAVE_NSP_QTB_CFG, X1E80100_SLAVE_QDSS_STM, + X1E80100_SLAVE_TCU }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .id = X1E80100_MASTER_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 6, + .links = { X1E80100_SLAVE_AOSS, X1E80100_SLAVE_TME_CFG, + X1E80100_SLAVE_APPSS, X1E80100_SLAVE_CNOC_CFG, + X1E80100_SLAVE_BOOT_IMEM, X1E80100_SLAVE_IMEM }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .id = X1E80100_MASTER_GEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 32, + .num_links = 8, + .links = { X1E80100_SLAVE_PCIE_0, X1E80100_SLAVE_PCIE_1, + X1E80100_SLAVE_PCIE_2, X1E80100_SLAVE_PCIE_3, + X1E80100_SLAVE_PCIE_4, X1E80100_SLAVE_PCIE_5, + X1E80100_SLAVE_PCIE_6A, X1E80100_SLAVE_PCIE_6B }, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .id = X1E80100_MASTER_GPU_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { X1E80100_SLAVE_GEM_NOC_CNOC, X1E80100_SLAVE_LLCC }, +}; + +static struct qcom_icc_node alm_pcie_tcu = { + .name = "alm_pcie_tcu", + .id = X1E80100_MASTER_PCIE_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { X1E80100_SLAVE_GEM_NOC_CNOC, X1E80100_SLAVE_LLCC }, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .id = X1E80100_MASTER_SYS_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { X1E80100_SLAVE_GEM_NOC_CNOC, X1E80100_SLAVE_LLCC }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .id = X1E80100_MASTER_APPSS_PROC, + .channels = 6, + .buswidth = 32, + .num_links = 3, + .links = { X1E80100_SLAVE_GEM_NOC_CNOC, X1E80100_SLAVE_LLCC, + X1E80100_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .id = X1E80100_MASTER_GFX3D, + .channels = 4, + .buswidth = 32, + .num_links = 2, + .links = { X1E80100_SLAVE_GEM_NOC_CNOC, X1E80100_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_lpass = { + .name = "qnm_lpass", + .id = X1E80100_MASTER_LPASS_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 3, + .links = { X1E80100_SLAVE_GEM_NOC_CNOC, X1E80100_SLAVE_LLCC, + X1E80100_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .id = X1E80100_MASTER_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { X1E80100_SLAVE_GEM_NOC_CNOC, X1E80100_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .id = X1E80100_MASTER_MNOC_SF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { X1E80100_SLAVE_GEM_NOC_CNOC, X1E80100_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_nsp_noc = { + .name = "qnm_nsp_noc", + .id = X1E80100_MASTER_COMPUTE_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 3, + .links = { X1E80100_SLAVE_GEM_NOC_CNOC, X1E80100_SLAVE_LLCC, + X1E80100_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .id = X1E80100_MASTER_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 64, + .num_links = 2, + .links = { X1E80100_SLAVE_GEM_NOC_CNOC, X1E80100_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .id = X1E80100_MASTER_SNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 64, + .num_links = 3, + .links = { X1E80100_SLAVE_GEM_NOC_CNOC, X1E80100_SLAVE_LLCC, + X1E80100_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node xm_gic = { + .name = "xm_gic", + .id = X1E80100_MASTER_GIC2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc = { + .name = "qnm_lpiaon_noc", + .id = X1E80100_MASTER_LPIAON_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_LPASS_GEM_NOC }, +}; + +static struct qcom_icc_node qnm_lpass_lpinoc = { + .name = "qnm_lpass_lpinoc", + .id = X1E80100_MASTER_LPASS_LPINOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_LPIAON_NOC_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node qxm_lpinoc_dsp_axim = { + .name = "qxm_lpinoc_dsp_axim", + .id = X1E80100_MASTER_LPASS_PROC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_LPICX_NOC_LPIAON_NOC }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .id = X1E80100_MASTER_LLCC, + .channels = 8, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_SLAVE_EBI1 }, +}; + +static struct qcom_icc_node qnm_av1_enc = { + .name = "qnm_av1_enc", + .id = X1E80100_MASTER_AV1_ENC, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .id = X1E80100_MASTER_CAMNOC_HF, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_camnoc_icp = { + .name = "qnm_camnoc_icp", + .id = X1E80100_MASTER_CAMNOC_ICP, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .id = X1E80100_MASTER_CAMNOC_SF, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_eva = { + .name = "qnm_eva", + .id = X1E80100_MASTER_EVA, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_mdp = { + .name = "qnm_mdp", + .id = X1E80100_MASTER_MDP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video = { + .name = "qnm_video", + .id = X1E80100_MASTER_VIDEO, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_cv_cpu = { + .name = "qnm_video_cv_cpu", + .id = X1E80100_MASTER_VIDEO_CV_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_v_cpu = { + .name = "qnm_video_v_cpu", + .id = X1E80100_MASTER_VIDEO_V_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qsm_mnoc_cfg = { + .name = "qsm_mnoc_cfg", + .id = X1E80100_MASTER_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_SLAVE_SERVICE_MNOC }, +}; + +static struct qcom_icc_node qxm_nsp = { + .name = "qxm_nsp", + .id = X1E80100_MASTER_CDSP_PROC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_SLAVE_CDSP_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_pcie_north_gem_noc = { + .name = "qnm_pcie_north_gem_noc", + .id = X1E80100_MASTER_PCIE_NORTH, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node qnm_pcie_south_gem_noc = { + .name = "qnm_pcie_south_gem_noc", + .id = X1E80100_MASTER_PCIE_SOUTH, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node xm_pcie_3 = { + .name = "xm_pcie_3", + .id = X1E80100_MASTER_PCIE_3, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_NORTH }, +}; + +static struct qcom_icc_node xm_pcie_4 = { + .name = "xm_pcie_4", + .id = X1E80100_MASTER_PCIE_4, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_NORTH }, +}; + +static struct qcom_icc_node xm_pcie_5 = { + .name = "xm_pcie_5", + .id = X1E80100_MASTER_PCIE_5, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_NORTH }, +}; + +static struct qcom_icc_node xm_pcie_0 = { + .name = "xm_pcie_0", + .id = X1E80100_MASTER_PCIE_0, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_SOUTH }, +}; + +static struct qcom_icc_node xm_pcie_1 = { + .name = "xm_pcie_1", + .id = X1E80100_MASTER_PCIE_1, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_SOUTH }, +}; + +static struct qcom_icc_node xm_pcie_2 = { + .name = "xm_pcie_2", + .id = X1E80100_MASTER_PCIE_2, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_SOUTH }, +}; + +static struct qcom_icc_node xm_pcie_6a = { + .name = "xm_pcie_6a", + .id = X1E80100_MASTER_PCIE_6A, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_SOUTH }, +}; + +static struct qcom_icc_node xm_pcie_6b = { + .name = "xm_pcie_6b", + .id = X1E80100_MASTER_PCIE_6B, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_SOUTH }, +}; + +static struct qcom_icc_node qnm_aggre1_noc = { + .name = "qnm_aggre1_noc", + .id = X1E80100_MASTER_A1NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_aggre2_noc = { + .name = "qnm_aggre2_noc", + .id = X1E80100_MASTER_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_gic = { + .name = "qnm_gic", + .id = X1E80100_MASTER_GIC1, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_usb_anoc = { + .name = "qnm_usb_anoc", + .id = X1E80100_MASTER_USB_NOC_SNOC, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_aggre_usb_north_snoc = { + .name = "qnm_aggre_usb_north_snoc", + .id = X1E80100_MASTER_AGGRE_USB_NORTH, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_SLAVE_USB_NOC_SNOC }, +}; + +static struct qcom_icc_node qnm_aggre_usb_south_snoc = { + .name = "qnm_aggre_usb_south_snoc", + .id = X1E80100_MASTER_AGGRE_USB_SOUTH, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_SLAVE_USB_NOC_SNOC }, +}; + +static struct qcom_icc_node xm_usb2_0 = { + .name = "xm_usb2_0", + .id = X1E80100_MASTER_USB2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_AGGRE_USB_NORTH }, +}; + +static struct qcom_icc_node xm_usb3_mp = { + .name = "xm_usb3_mp", + .id = X1E80100_MASTER_USB3_MP, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_AGGRE_USB_NORTH }, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .id = X1E80100_MASTER_USB3_0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_AGGRE_USB_SOUTH }, +}; + +static struct qcom_icc_node xm_usb3_1 = { + .name = "xm_usb3_1", + .id = X1E80100_MASTER_USB3_1, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_AGGRE_USB_SOUTH }, +}; + +static struct qcom_icc_node xm_usb3_2 = { + .name = "xm_usb3_2", + .id = X1E80100_MASTER_USB3_2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_AGGRE_USB_SOUTH }, +}; + +static struct qcom_icc_node xm_usb4_0 = { + .name = "xm_usb4_0", + .id = X1E80100_MASTER_USB4_0, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_AGGRE_USB_SOUTH }, +}; + +static struct qcom_icc_node xm_usb4_1 = { + .name = "xm_usb4_1", + .id = X1E80100_MASTER_USB4_1, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_AGGRE_USB_SOUTH }, +}; + +static struct qcom_icc_node xm_usb4_2 = { + .name = "xm_usb4_2", + .id = X1E80100_MASTER_USB4_2, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_AGGRE_USB_SOUTH }, +}; + +static struct qcom_icc_node qnm_mnoc_hf_disp = { + .name = "qnm_mnoc_hf_disp", + .id = X1E80100_MASTER_MNOC_HF_MEM_NOC_DISP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_SLAVE_LLCC_DISP }, +}; + +static struct qcom_icc_node qnm_pcie_disp = { + .name = "qnm_pcie_disp", + .id = X1E80100_MASTER_ANOC_PCIE_GEM_NOC_DISP, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_SLAVE_LLCC_DISP }, +}; + +static struct qcom_icc_node llcc_mc_disp = { + .name = "llcc_mc_disp", + .id = X1E80100_MASTER_LLCC_DISP, + .channels = 8, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_SLAVE_EBI1_DISP }, +}; + +static struct qcom_icc_node qnm_mdp_disp = { + .name = "qnm_mdp_disp", + .id = X1E80100_MASTER_MDP_DISP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_SLAVE_MNOC_HF_MEM_NOC_DISP }, +}; + +static struct qcom_icc_node qnm_pcie_pcie = { + .name = "qnm_pcie_pcie", + .id = X1E80100_MASTER_ANOC_PCIE_GEM_NOC_PCIE, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_SLAVE_LLCC_PCIE }, +}; + +static struct qcom_icc_node llcc_mc_pcie = { + .name = "llcc_mc_pcie", + .id = X1E80100_MASTER_LLCC_PCIE, + .channels = 8, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_SLAVE_EBI1_PCIE }, +}; + +static struct qcom_icc_node qnm_pcie_north_gem_noc_pcie = { + .name = "qnm_pcie_north_gem_noc_pcie", + .id = X1E80100_MASTER_PCIE_NORTH_PCIE, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_SLAVE_ANOC_PCIE_GEM_NOC_PCIE }, +}; + +static struct qcom_icc_node qnm_pcie_south_gem_noc_pcie = { + .name = "qnm_pcie_south_gem_noc_pcie", + .id = X1E80100_MASTER_PCIE_SOUTH_PCIE, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_SLAVE_ANOC_PCIE_GEM_NOC_PCIE }, +}; + +static struct qcom_icc_node xm_pcie_3_pcie = { + .name = "xm_pcie_3_pcie", + .id = X1E80100_MASTER_PCIE_3_PCIE, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_NORTH_PCIE }, +}; + +static struct qcom_icc_node xm_pcie_4_pcie = { + .name = "xm_pcie_4_pcie", + .id = X1E80100_MASTER_PCIE_4_PCIE, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_NORTH_PCIE }, +}; + +static struct qcom_icc_node xm_pcie_5_pcie = { + .name = "xm_pcie_5_pcie", + .id = X1E80100_MASTER_PCIE_5_PCIE, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_NORTH_PCIE }, +}; + +static struct qcom_icc_node xm_pcie_0_pcie = { + .name = "xm_pcie_0_pcie", + .id = X1E80100_MASTER_PCIE_0_PCIE, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_SOUTH_PCIE }, +}; + +static struct qcom_icc_node xm_pcie_1_pcie = { + .name = "xm_pcie_1_pcie", + .id = X1E80100_MASTER_PCIE_1_PCIE, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_SOUTH_PCIE }, +}; + +static struct qcom_icc_node xm_pcie_2_pcie = { + .name = "xm_pcie_2_pcie", + .id = X1E80100_MASTER_PCIE_2_PCIE, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_SOUTH_PCIE }, +}; + +static struct qcom_icc_node xm_pcie_6a_pcie = { + .name = "xm_pcie_6a_pcie", + .id = X1E80100_MASTER_PCIE_6A_PCIE, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_SOUTH_PCIE }, +}; + +static struct qcom_icc_node xm_pcie_6b_pcie = { + .name = "xm_pcie_6b_pcie", + .id = X1E80100_MASTER_PCIE_6B_PCIE, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_SLAVE_PCIE_SOUTH_PCIE }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .id = X1E80100_SLAVE_A1NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_MASTER_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qns_a2noc_snoc = { + .name = "qns_a2noc_snoc", + .id = X1E80100_SLAVE_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_MASTER_A2NOC_SNOC }, +}; + +static struct qcom_icc_node ddr_perf_mode_slave = { + .name = "ddr_perf_mode_slave", + .id = X1E80100_SLAVE_DDR_PERF_MODE, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .id = X1E80100_SLAVE_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .id = X1E80100_SLAVE_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup2_core_slave = { + .name = "qup2_core_slave", + .id = X1E80100_SLAVE_QUP_CORE_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .id = X1E80100_SLAVE_AHB2PHY_SOUTH, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy1 = { + .name = "qhs_ahb2phy1", + .id = X1E80100_SLAVE_AHB2PHY_NORTH, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy2 = { + .name = "qhs_ahb2phy2", + .id = X1E80100_SLAVE_AHB2PHY_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_av1_enc_cfg = { + .name = "qhs_av1_enc_cfg", + .id = X1E80100_SLAVE_AV1_ENC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .id = X1E80100_SLAVE_CAMERA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .id = X1E80100_SLAVE_CLK_CTL, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .id = X1E80100_SLAVE_CRYPTO_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .id = X1E80100_SLAVE_DISPLAY_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .id = X1E80100_SLAVE_GFX3D_CFG, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .id = X1E80100_SLAVE_IMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .id = X1E80100_SLAVE_IPC_ROUTER_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie0_cfg = { + .name = "qhs_pcie0_cfg", + .id = X1E80100_SLAVE_PCIE_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie1_cfg = { + .name = "qhs_pcie1_cfg", + .id = X1E80100_SLAVE_PCIE_1_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie2_cfg = { + .name = "qhs_pcie2_cfg", + .id = X1E80100_SLAVE_PCIE_2_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie3_cfg = { + .name = "qhs_pcie3_cfg", + .id = X1E80100_SLAVE_PCIE_3_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie4_cfg = { + .name = "qhs_pcie4_cfg", + .id = X1E80100_SLAVE_PCIE_4_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie5_cfg = { + .name = "qhs_pcie5_cfg", + .id = X1E80100_SLAVE_PCIE_5_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie6a_cfg = { + .name = "qhs_pcie6a_cfg", + .id = X1E80100_SLAVE_PCIE_6A_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie6b_cfg = { + .name = "qhs_pcie6b_cfg", + .id = X1E80100_SLAVE_PCIE_6B_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie_rsc_cfg = { + .name = "qhs_pcie_rsc_cfg", + .id = X1E80100_SLAVE_PCIE_RSC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pdm = { + .name = "qhs_pdm", + .id = X1E80100_SLAVE_PDM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .id = X1E80100_SLAVE_PRNG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .id = X1E80100_SLAVE_QDSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .id = X1E80100_SLAVE_QSPI_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup0 = { + .name = "qhs_qup0", + .id = X1E80100_SLAVE_QUP_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .id = X1E80100_SLAVE_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup2 = { + .name = "qhs_qup2", + .id = X1E80100_SLAVE_QUP_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .id = X1E80100_SLAVE_SDCC_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc4 = { + .name = "qhs_sdc4", + .id = X1E80100_SLAVE_SDCC_4, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_smmuv3_cfg = { + .name = "qhs_smmuv3_cfg", + .id = X1E80100_SLAVE_SMMUV3_CFG, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .id = X1E80100_SLAVE_TCSR, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .id = X1E80100_SLAVE_TLMM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .id = X1E80100_SLAVE_UFS_MEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb2_0_cfg = { + .name = "qhs_usb2_0_cfg", + .id = X1E80100_SLAVE_USB2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb3_0_cfg = { + .name = "qhs_usb3_0_cfg", + .id = X1E80100_SLAVE_USB3_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb3_1_cfg = { + .name = "qhs_usb3_1_cfg", + .id = X1E80100_SLAVE_USB3_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb3_2_cfg = { + .name = "qhs_usb3_2_cfg", + .id = X1E80100_SLAVE_USB3_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb3_mp_cfg = { + .name = "qhs_usb3_mp_cfg", + .id = X1E80100_SLAVE_USB3_MP, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb4_0_cfg = { + .name = "qhs_usb4_0_cfg", + .id = X1E80100_SLAVE_USB4_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb4_1_cfg = { + .name = "qhs_usb4_1_cfg", + .id = X1E80100_SLAVE_USB4_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb4_2_cfg = { + .name = "qhs_usb4_2_cfg", + .id = X1E80100_SLAVE_USB4_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .id = X1E80100_SLAVE_VENUS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qss_lpass_qtb_cfg = { + .name = "qss_lpass_qtb_cfg", + .id = X1E80100_SLAVE_LPASS_QTB_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qss_mnoc_cfg = { + .name = "qss_mnoc_cfg", + .id = X1E80100_SLAVE_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_MASTER_CNOC_MNOC_CFG }, +}; + +static struct qcom_icc_node qss_nsp_qtb_cfg = { + .name = "qss_nsp_qtb_cfg", + .id = X1E80100_SLAVE_NSP_QTB_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .id = X1E80100_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 = X1E80100_SLAVE_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .id = X1E80100_SLAVE_AOSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tme_cfg = { + .name = "qhs_tme_cfg", + .id = X1E80100_SLAVE_TME_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_apss = { + .name = "qns_apss", + .id = X1E80100_SLAVE_APPSS, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qss_cfg = { + .name = "qss_cfg", + .id = X1E80100_SLAVE_CNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { X1E80100_MASTER_CNOC_CFG }, +}; + +static struct qcom_icc_node qxs_boot_imem = { + .name = "qxs_boot_imem", + .id = X1E80100_SLAVE_BOOT_IMEM, + .channels = 1, + .buswidth = 16, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .id = X1E80100_SLAVE_IMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_0 = { + .name = "xs_pcie_0", + .id = X1E80100_SLAVE_PCIE_0, + .channels = 1, + .buswidth = 16, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_1 = { + .name = "xs_pcie_1", + .id = X1E80100_SLAVE_PCIE_1, + .channels = 1, + .buswidth = 16, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_2 = { + .name = "xs_pcie_2", + .id = X1E80100_SLAVE_PCIE_2, + .channels = 1, + .buswidth = 16, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_3 = { + .name = "xs_pcie_3", + .id = X1E80100_SLAVE_PCIE_3, + .channels = 1, + .buswidth = 64, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_4 = { + .name = "xs_pcie_4", + .id = X1E80100_SLAVE_PCIE_4, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_5 = { + .name = "xs_pcie_5", + .id = X1E80100_SLAVE_PCIE_5, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_6a = { + .name = "xs_pcie_6a", + .id = X1E80100_SLAVE_PCIE_6A, + .channels = 1, + .buswidth = 32, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_6b = { + .name = "xs_pcie_6b", + .id = X1E80100_SLAVE_PCIE_6B, + .channels = 1, + .buswidth = 16, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .id = X1E80100_SLAVE_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_MASTER_GEM_NOC_CNOC }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .id = X1E80100_SLAVE_LLCC, + .channels = 8, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_MASTER_LLCC }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .id = X1E80100_SLAVE_MEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_MASTER_GEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qns_lpass_ag_noc_gemnoc = { + .name = "qns_lpass_ag_noc_gemnoc", + .id = X1E80100_SLAVE_LPASS_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_MASTER_LPASS_GEM_NOC }, +}; + +static struct qcom_icc_node qns_lpass_aggnoc = { + .name = "qns_lpass_aggnoc", + .id = X1E80100_SLAVE_LPIAON_NOC_LPASS_AG_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_MASTER_LPIAON_NOC }, +}; + +static struct qcom_icc_node qns_lpi_aon_noc = { + .name = "qns_lpi_aon_noc", + .id = X1E80100_SLAVE_LPICX_NOC_LPIAON_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_MASTER_LPASS_LPINOC }, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .id = X1E80100_SLAVE_EBI1, + .channels = 8, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .id = X1E80100_SLAVE_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_MASTER_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .id = X1E80100_SLAVE_MNOC_SF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_MASTER_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node srvc_mnoc = { + .name = "srvc_mnoc", + .id = X1E80100_SLAVE_SERVICE_MNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .id = X1E80100_SLAVE_CDSP_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_MASTER_COMPUTE_NOC }, +}; + +static struct qcom_icc_node qns_pcie_mem_noc = { + .name = "qns_pcie_mem_noc", + .id = X1E80100_SLAVE_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_MASTER_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node qns_pcie_north_gem_noc = { + .name = "qns_pcie_north_gem_noc", + .id = X1E80100_SLAVE_PCIE_NORTH, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_MASTER_PCIE_NORTH }, +}; + +static struct qcom_icc_node qns_pcie_south_gem_noc = { + .name = "qns_pcie_south_gem_noc", + .id = X1E80100_SLAVE_PCIE_SOUTH, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_MASTER_PCIE_SOUTH }, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .id = X1E80100_SLAVE_SNOC_GEM_NOC_SF, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_MASTER_SNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qns_aggre_usb_snoc = { + .name = "qns_aggre_usb_snoc", + .id = X1E80100_SLAVE_USB_NOC_SNOC, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_MASTER_USB_NOC_SNOC }, +}; + +static struct qcom_icc_node qns_aggre_usb_north_snoc = { + .name = "qns_aggre_usb_north_snoc", + .id = X1E80100_SLAVE_AGGRE_USB_NORTH, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_MASTER_AGGRE_USB_NORTH }, +}; + +static struct qcom_icc_node qns_aggre_usb_south_snoc = { + .name = "qns_aggre_usb_south_snoc", + .id = X1E80100_SLAVE_AGGRE_USB_SOUTH, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_MASTER_AGGRE_USB_SOUTH }, +}; + +static struct qcom_icc_node qns_llcc_disp = { + .name = "qns_llcc_disp", + .id = X1E80100_SLAVE_LLCC_DISP, + .channels = 8, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_MASTER_LLCC_DISP }, +}; + +static struct qcom_icc_node ebi_disp = { + .name = "ebi_disp", + .id = X1E80100_SLAVE_EBI1_DISP, + .channels = 8, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_mem_noc_hf_disp = { + .name = "qns_mem_noc_hf_disp", + .id = X1E80100_SLAVE_MNOC_HF_MEM_NOC_DISP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { X1E80100_MASTER_MNOC_HF_MEM_NOC_DISP }, +}; + +static struct qcom_icc_node qns_llcc_pcie = { + .name = "qns_llcc_pcie", + .id = X1E80100_SLAVE_LLCC_PCIE, + .channels = 8, + .buswidth = 16, + .num_links = 1, + .links = { X1E80100_MASTER_LLCC_PCIE }, +}; + +static struct qcom_icc_node ebi_pcie = { + .name = "ebi_pcie", + .id = X1E80100_SLAVE_EBI1_PCIE, + .channels = 8, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_pcie_mem_noc_pcie = { + .name = "qns_pcie_mem_noc_pcie", + .id = X1E80100_SLAVE_ANOC_PCIE_GEM_NOC_PCIE, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_MASTER_ANOC_PCIE_GEM_NOC_PCIE }, +}; + +static struct qcom_icc_node qns_pcie_north_gem_noc_pcie = { + .name = "qns_pcie_north_gem_noc_pcie", + .id = X1E80100_SLAVE_PCIE_NORTH_PCIE, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_MASTER_PCIE_NORTH_PCIE }, +}; + +static struct qcom_icc_node qns_pcie_south_gem_noc_pcie = { + .name = "qns_pcie_south_gem_noc_pcie", + .id = X1E80100_SLAVE_PCIE_SOUTH_PCIE, + .channels = 1, + .buswidth = 64, + .num_links = 1, + .links = { X1E80100_MASTER_PCIE_SOUTH_PCIE }, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_acv_perf = { + .name = "ACV_PERF", + .num_nodes = 1, + .nodes = { &ddr_perf_mode_slave }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .keepalive = true, + .num_nodes = 63, + .nodes = { &qsm_cfg, &qhs_ahb2phy0, + &qhs_ahb2phy1, &qhs_ahb2phy2, + &qhs_av1_enc_cfg, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_crypto0_cfg, + &qhs_gpuss_cfg, &qhs_imem_cfg, + &qhs_ipc_router, &qhs_pcie0_cfg, + &qhs_pcie1_cfg, &qhs_pcie2_cfg, + &qhs_pcie3_cfg, &qhs_pcie4_cfg, + &qhs_pcie5_cfg, &qhs_pcie6a_cfg, + &qhs_pcie6b_cfg, &qhs_pcie_rsc_cfg, + &qhs_pdm, &qhs_prng, + &qhs_qdss_cfg, &qhs_qspi, + &qhs_qup0, &qhs_qup1, + &qhs_qup2, &qhs_sdc2, + &qhs_sdc4, &qhs_smmuv3_cfg, + &qhs_tcsr, &qhs_tlmm, + &qhs_ufs_mem_cfg, &qhs_usb2_0_cfg, + &qhs_usb3_0_cfg, &qhs_usb3_1_cfg, + &qhs_usb3_2_cfg, &qhs_usb3_mp_cfg, + &qhs_usb4_0_cfg, &qhs_usb4_1_cfg, + &qhs_usb4_2_cfg, &qhs_venus_cfg, + &qss_lpass_qtb_cfg, &qss_mnoc_cfg, + &qss_nsp_qtb_cfg, &xs_qdss_stm, + &xs_sys_tcu_cfg, &qnm_gemnoc_cnoc, + &qnm_gemnoc_pcie, &qhs_aoss, + &qhs_tme_cfg, &qns_apss, + &qss_cfg, &qxs_boot_imem, + &qxs_imem, &xs_pcie_0, + &xs_pcie_1, &xs_pcie_2, + &xs_pcie_3, &xs_pcie_4, + &xs_pcie_5, &xs_pcie_6a, + &xs_pcie_6b }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 1, + .nodes = { &qhs_display_cfg }, +}; + +static struct qcom_icc_bcm bcm_co0 = { + .name = "CO0", + .num_nodes = 2, + .nodes = { &qxm_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", + .num_nodes = 10, + .nodes = { &qnm_av1_enc, &qnm_camnoc_hf, + &qnm_camnoc_icp, &qnm_camnoc_sf, + &qnm_eva, &qnm_mdp, + &qnm_video, &qnm_video_cv_cpu, + &qnm_video_v_cpu, &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_pc0 = { + .name = "PC0", + .num_nodes = 1, + .nodes = { &qns_pcie_mem_noc }, +}; + +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", + .num_nodes = 13, + .nodes = { &alm_gpu_tcu, &alm_pcie_tcu, + &alm_sys_tcu, &chm_apps, + &qnm_gpu, &qnm_lpass, + &qnm_mnoc_hf, &qnm_mnoc_sf, + &qnm_nsp_noc, &qnm_pcie, + &xm_gic, &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 = { &qnm_usb_anoc }, +}; + +static struct qcom_icc_bcm bcm_acv_disp = { + .name = "ACV", + .num_nodes = 1, + .nodes = { &ebi_disp }, +}; + +static struct qcom_icc_bcm bcm_mc0_disp = { + .name = "MC0", + .num_nodes = 1, + .nodes = { &ebi_disp }, +}; + +static struct qcom_icc_bcm bcm_mm0_disp = { + .name = "MM0", + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf_disp }, +}; + +static struct qcom_icc_bcm bcm_mm1_disp = { + .name = "MM1", + .num_nodes = 1, + .nodes = { &qnm_mdp_disp }, +}; + +static struct qcom_icc_bcm bcm_sh0_disp = { + .name = "SH0", + .num_nodes = 1, + .nodes = { &qns_llcc_disp }, +}; + +static struct qcom_icc_bcm bcm_sh1_disp = { + .name = "SH1", + .num_nodes = 2, + .nodes = { &qnm_mnoc_hf_disp, &qnm_pcie_disp }, +}; + +static struct qcom_icc_bcm bcm_acv_pcie = { + .name = "ACV", + .num_nodes = 1, + .nodes = { &ebi_pcie }, +}; + +static struct qcom_icc_bcm bcm_mc0_pcie = { + .name = "MC0", + .num_nodes = 1, + .nodes = { &ebi_pcie }, +}; + +static struct qcom_icc_bcm bcm_pc0_pcie = { + .name = "PC0", + .num_nodes = 1, + .nodes = { &qns_pcie_mem_noc_pcie }, +}; + +static struct qcom_icc_bcm bcm_sh0_pcie = { + .name = "SH0", + .num_nodes = 1, + .nodes = { &qns_llcc_pcie }, +}; + +static struct qcom_icc_bcm bcm_sh1_pcie = { + .name = "SH1", + .num_nodes = 1, + .nodes = { &qnm_pcie_pcie }, +}; + +static struct qcom_icc_bcm *aggre1_noc_bcms[] = { +}; + +static struct qcom_icc_node * const aggre1_noc_nodes[] = { + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_QUP_1] = &qhm_qup1, + [MASTER_SDCC_4] = &xm_sdc4, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, +}; + +static const struct qcom_icc_desc x1e80100_aggre1_noc = { + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), + .bcms = aggre1_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), +}; + +static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { + &bcm_ce0, +}; + +static struct qcom_icc_node * const aggre2_noc_nodes[] = { + [MASTER_QUP_0] = &qhm_qup0, + [MASTER_QUP_2] = &qhm_qup2, + [MASTER_CRYPTO] = &qxm_crypto, + [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 x1e80100_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_acv_perf, + &bcm_qup0, + &bcm_qup1, + &bcm_qup2, +}; + +static struct qcom_icc_node * const clk_virt_nodes[] = { + [MASTER_DDR_PERF_MODE] = &ddr_perf_mode_master, + [MASTER_QUP_CORE_0] = &qup0_core_master, + [MASTER_QUP_CORE_1] = &qup1_core_master, + [MASTER_QUP_CORE_2] = &qup2_core_master, + [SLAVE_DDR_PERF_MODE] = &ddr_perf_mode_slave, + [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 x1e80100_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 cnoc_cfg_bcms[] = { + &bcm_cn0, + &bcm_cn1, +}; + +static struct qcom_icc_node * const cnoc_cfg_nodes[] = { + [MASTER_CNOC_CFG] = &qsm_cfg, + [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, + [SLAVE_AHB2PHY_NORTH] = &qhs_ahb2phy1, + [SLAVE_AHB2PHY_2] = &qhs_ahb2phy2, + [SLAVE_AV1_ENC_CFG] = &qhs_av1_enc_cfg, + [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_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_PCIE_0_CFG] = &qhs_pcie0_cfg, + [SLAVE_PCIE_1_CFG] = &qhs_pcie1_cfg, + [SLAVE_PCIE_2_CFG] = &qhs_pcie2_cfg, + [SLAVE_PCIE_3_CFG] = &qhs_pcie3_cfg, + [SLAVE_PCIE_4_CFG] = &qhs_pcie4_cfg, + [SLAVE_PCIE_5_CFG] = &qhs_pcie5_cfg, + [SLAVE_PCIE_6A_CFG] = &qhs_pcie6a_cfg, + [SLAVE_PCIE_6B_CFG] = &qhs_pcie6b_cfg, + [SLAVE_PCIE_RSC_CFG] = &qhs_pcie_rsc_cfg, + [SLAVE_PDM] = &qhs_pdm, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_0] = &qhs_qup0, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_QUP_2] = &qhs_qup2, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_SDCC_4] = &qhs_sdc4, + [SLAVE_SMMUV3_CFG] = &qhs_smmuv3_cfg, + [SLAVE_TCSR] = &qhs_tcsr, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB2] = &qhs_usb2_0_cfg, + [SLAVE_USB3_0] = &qhs_usb3_0_cfg, + [SLAVE_USB3_1] = &qhs_usb3_1_cfg, + [SLAVE_USB3_2] = &qhs_usb3_2_cfg, + [SLAVE_USB3_MP] = &qhs_usb3_mp_cfg, + [SLAVE_USB4_0] = &qhs_usb4_0_cfg, + [SLAVE_USB4_1] = &qhs_usb4_1_cfg, + [SLAVE_USB4_2] = &qhs_usb4_2_cfg, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_LPASS_QTB_CFG] = &qss_lpass_qtb_cfg, + [SLAVE_CNOC_MNOC_CFG] = &qss_mnoc_cfg, + [SLAVE_NSP_QTB_CFG] = &qss_nsp_qtb_cfg, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct qcom_icc_desc x1e80100_cnoc_cfg = { + .nodes = cnoc_cfg_nodes, + .num_nodes = ARRAY_SIZE(cnoc_cfg_nodes), + .bcms = cnoc_cfg_bcms, + .num_bcms = ARRAY_SIZE(cnoc_cfg_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_TME_CFG] = &qhs_tme_cfg, + [SLAVE_APPSS] = &qns_apss, + [SLAVE_CNOC_CFG] = &qss_cfg, + [SLAVE_BOOT_IMEM] = &qxs_boot_imem, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_PCIE_0] = &xs_pcie_0, + [SLAVE_PCIE_1] = &xs_pcie_1, + [SLAVE_PCIE_2] = &xs_pcie_2, + [SLAVE_PCIE_3] = &xs_pcie_3, + [SLAVE_PCIE_4] = &xs_pcie_4, + [SLAVE_PCIE_5] = &xs_pcie_5, + [SLAVE_PCIE_6A] = &xs_pcie_6a, + [SLAVE_PCIE_6B] = &xs_pcie_6b, +}; + +static const struct qcom_icc_desc x1e80100_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_sh0_disp, + &bcm_sh1_disp, + &bcm_sh0_pcie, + &bcm_sh1_pcie, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_PCIE_TCU] = &alm_pcie_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_LPASS_GEM_NOC] = &qnm_lpass, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_COMPUTE_NOC] = &qnm_nsp_noc, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_GIC2] = &xm_gic, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, + [MASTER_MNOC_HF_MEM_NOC_DISP] = &qnm_mnoc_hf_disp, + [MASTER_ANOC_PCIE_GEM_NOC_DISP] = &qnm_pcie_disp, + [SLAVE_LLCC_DISP] = &qns_llcc_disp, + [MASTER_ANOC_PCIE_GEM_NOC_PCIE] = &qnm_pcie_pcie, + [SLAVE_LLCC_PCIE] = &qns_llcc_pcie, +}; + +static const struct qcom_icc_desc x1e80100_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_bcm *lpass_ag_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 x1e80100_lpass_ag_noc = { + .nodes = lpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), + .bcms = lpass_ag_noc_bcms, + .num_bcms = ARRAY_SIZE(lpass_ag_noc_bcms), +}; + +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 x1e80100_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_bcm * const lpass_lpicx_noc_bcms[] = { +}; + +static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = { + [MASTER_LPASS_PROC] = &qxm_lpinoc_dsp_axim, + [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc, +}; + +static const struct qcom_icc_desc x1e80100_lpass_lpicx_noc = { + .nodes = lpass_lpicx_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes), + .bcms = lpass_lpicx_noc_bcms, + .num_bcms = ARRAY_SIZE(lpass_lpicx_noc_bcms), +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_acv, + &bcm_mc0, + &bcm_acv_disp, + &bcm_mc0_disp, + &bcm_acv_pcie, + &bcm_mc0_pcie, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI1] = &ebi, + [MASTER_LLCC_DISP] = &llcc_mc_disp, + [SLAVE_EBI1_DISP] = &ebi_disp, + [MASTER_LLCC_PCIE] = &llcc_mc_pcie, + [SLAVE_EBI1_PCIE] = &ebi_pcie, +}; + +static const struct qcom_icc_desc x1e80100_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, + &bcm_mm0_disp, + &bcm_mm1_disp, +}; + +static struct qcom_icc_node * const mmss_noc_nodes[] = { + [MASTER_AV1_ENC] = &qnm_av1_enc, + [MASTER_CAMNOC_HF] = &qnm_camnoc_hf, + [MASTER_CAMNOC_ICP] = &qnm_camnoc_icp, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_EVA] = &qnm_eva, + [MASTER_MDP] = &qnm_mdp, + [MASTER_VIDEO] = &qnm_video, + [MASTER_VIDEO_CV_PROC] = &qnm_video_cv_cpu, + [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, + [MASTER_MDP_DISP] = &qnm_mdp_disp, + [SLAVE_MNOC_HF_MEM_NOC_DISP] = &qns_mem_noc_hf_disp, +}; + +static const struct qcom_icc_desc x1e80100_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] = &qxm_nsp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, +}; + +static const struct qcom_icc_desc x1e80100_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_center_anoc_bcms[] = { + &bcm_pc0, + &bcm_pc0_pcie, +}; + +static struct qcom_icc_node * const pcie_center_anoc_nodes[] = { + [MASTER_PCIE_NORTH] = &qnm_pcie_north_gem_noc, + [MASTER_PCIE_SOUTH] = &qnm_pcie_south_gem_noc, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, + [MASTER_PCIE_NORTH_PCIE] = &qnm_pcie_north_gem_noc_pcie, + [MASTER_PCIE_SOUTH_PCIE] = &qnm_pcie_south_gem_noc_pcie, + [SLAVE_ANOC_PCIE_GEM_NOC_PCIE] = &qns_pcie_mem_noc_pcie, +}; + +static const struct qcom_icc_desc x1e80100_pcie_center_anoc = { + .nodes = pcie_center_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_center_anoc_nodes), + .bcms = pcie_center_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_center_anoc_bcms), +}; + +static struct qcom_icc_bcm * const pcie_north_anoc_bcms[] = { +}; + +static struct qcom_icc_node * const pcie_north_anoc_nodes[] = { + [MASTER_PCIE_3] = &xm_pcie_3, + [MASTER_PCIE_4] = &xm_pcie_4, + [MASTER_PCIE_5] = &xm_pcie_5, + [SLAVE_PCIE_NORTH] = &qns_pcie_north_gem_noc, + [MASTER_PCIE_3_PCIE] = &xm_pcie_3_pcie, + [MASTER_PCIE_4_PCIE] = &xm_pcie_4_pcie, + [MASTER_PCIE_5_PCIE] = &xm_pcie_5_pcie, + [SLAVE_PCIE_NORTH_PCIE] = &qns_pcie_north_gem_noc_pcie, +}; + +static const struct qcom_icc_desc x1e80100_pcie_north_anoc = { + .nodes = pcie_north_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_north_anoc_nodes), + .bcms = pcie_north_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_north_anoc_bcms), +}; + +static struct qcom_icc_bcm *pcie_south_anoc_bcms[] = { +}; + +static struct qcom_icc_node * const pcie_south_anoc_nodes[] = { + [MASTER_PCIE_0] = &xm_pcie_0, + [MASTER_PCIE_1] = &xm_pcie_1, + [MASTER_PCIE_2] = &xm_pcie_2, + [MASTER_PCIE_6A] = &xm_pcie_6a, + [MASTER_PCIE_6B] = &xm_pcie_6b, + [SLAVE_PCIE_SOUTH] = &qns_pcie_south_gem_noc, + [MASTER_PCIE_0_PCIE] = &xm_pcie_0_pcie, + [MASTER_PCIE_1_PCIE] = &xm_pcie_1_pcie, + [MASTER_PCIE_2_PCIE] = &xm_pcie_2_pcie, + [MASTER_PCIE_6A_PCIE] = &xm_pcie_6a_pcie, + [MASTER_PCIE_6B_PCIE] = &xm_pcie_6b_pcie, + [SLAVE_PCIE_SOUTH_PCIE] = &qns_pcie_south_gem_noc_pcie, +}; + +static const struct qcom_icc_desc x1e80100_pcie_south_anoc = { + .nodes = pcie_south_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_south_anoc_nodes), + .bcms = pcie_south_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_south_anoc_bcms), +}; + +static struct qcom_icc_bcm *system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn2, + &bcm_sn3, + &bcm_sn4, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc, + [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc, + [MASTER_GIC1] = &qnm_gic, + [MASTER_USB_NOC_SNOC] = &qnm_usb_anoc, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, +}; + +static const struct qcom_icc_desc x1e80100_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 struct qcom_icc_bcm * const usb_center_anoc_bcms[] = { +}; + +static struct qcom_icc_node * const usb_center_anoc_nodes[] = { + [MASTER_AGGRE_USB_NORTH] = &qnm_aggre_usb_north_snoc, + [MASTER_AGGRE_USB_SOUTH] = &qnm_aggre_usb_south_snoc, + [SLAVE_USB_NOC_SNOC] = &qns_aggre_usb_snoc, +}; + +static const struct qcom_icc_desc x1e80100_usb_center_anoc = { + .nodes = usb_center_anoc_nodes, + .num_nodes = ARRAY_SIZE(usb_center_anoc_nodes), + .bcms = usb_center_anoc_bcms, + .num_bcms = ARRAY_SIZE(usb_center_anoc_bcms), +}; + +static struct qcom_icc_bcm *usb_north_anoc_bcms[] = { +}; + +static struct qcom_icc_node * const usb_north_anoc_nodes[] = { + [MASTER_USB2] = &xm_usb2_0, + [MASTER_USB3_MP] = &xm_usb3_mp, + [SLAVE_AGGRE_USB_NORTH] = &qns_aggre_usb_north_snoc, +}; + +static const struct qcom_icc_desc x1e80100_usb_north_anoc = { + .nodes = usb_north_anoc_nodes, + .num_nodes = ARRAY_SIZE(usb_north_anoc_nodes), + .bcms = usb_north_anoc_bcms, + .num_bcms = ARRAY_SIZE(usb_north_anoc_bcms), +}; + +static struct qcom_icc_bcm *usb_south_anoc_bcms[] = { +}; + +static struct qcom_icc_node * const usb_south_anoc_nodes[] = { + [MASTER_USB3_0] = &xm_usb3_0, + [MASTER_USB3_1] = &xm_usb3_1, + [MASTER_USB3_2] = &xm_usb3_2, + [MASTER_USB4_0] = &xm_usb4_0, + [MASTER_USB4_1] = &xm_usb4_1, + [MASTER_USB4_2] = &xm_usb4_2, + [SLAVE_AGGRE_USB_SOUTH] = &qns_aggre_usb_south_snoc, +}; + +static const struct qcom_icc_desc x1e80100_usb_south_anoc = { + .nodes = usb_south_anoc_nodes, + .num_nodes = ARRAY_SIZE(usb_south_anoc_nodes), + .bcms = usb_south_anoc_bcms, + .num_bcms = ARRAY_SIZE(usb_south_anoc_bcms), +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,x1e80100-aggre1-noc", .data = &x1e80100_aggre1_noc}, + { .compatible = "qcom,x1e80100-aggre2-noc", .data = &x1e80100_aggre2_noc}, + { .compatible = "qcom,x1e80100-clk-virt", .data = &x1e80100_clk_virt}, + { .compatible = "qcom,x1e80100-cnoc-cfg", .data = &x1e80100_cnoc_cfg}, + { .compatible = "qcom,x1e80100-cnoc-main", .data = &x1e80100_cnoc_main}, + { .compatible = "qcom,x1e80100-gem-noc", .data = &x1e80100_gem_noc}, + { .compatible = "qcom,x1e80100-lpass-ag-noc", .data = &x1e80100_lpass_ag_noc}, + { .compatible = "qcom,x1e80100-lpass-lpiaon-noc", .data = &x1e80100_lpass_lpiaon_noc}, + { .compatible = "qcom,x1e80100-lpass-lpicx-noc", .data = &x1e80100_lpass_lpicx_noc}, + { .compatible = "qcom,x1e80100-mc-virt", .data = &x1e80100_mc_virt}, + { .compatible = "qcom,x1e80100-mmss-noc", .data = &x1e80100_mmss_noc}, + { .compatible = "qcom,x1e80100-nsp-noc", .data = &x1e80100_nsp_noc}, + { .compatible = "qcom,x1e80100-pcie-center-anoc", .data = &x1e80100_pcie_center_anoc}, + { .compatible = "qcom,x1e80100-pcie-north-anoc", .data = &x1e80100_pcie_north_anoc}, + { .compatible = "qcom,x1e80100-pcie-south-anoc", .data = &x1e80100_pcie_south_anoc}, + { .compatible = "qcom,x1e80100-system-noc", .data = &x1e80100_system_noc}, + { .compatible = "qcom,x1e80100-usb-center-anoc", .data = &x1e80100_usb_center_anoc}, + { .compatible = "qcom,x1e80100-usb-north-anoc", .data = &x1e80100_usb_north_anoc}, + { .compatible = "qcom,x1e80100-usb-south-anoc", .data = &x1e80100_usb_south_anoc}, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove_new = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-x1e80100", + .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("x1e80100 NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/x1e80100.h b/drivers/interconnect/qcom/x1e80100.h new file mode 100644 index 000000000000..2e14264f4c2b --- /dev/null +++ b/drivers/interconnect/qcom/x1e80100.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * X1E80100 interconnect IDs + * + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, Linaro Limited + */ + +#ifndef __DRIVERS_INTERCONNECT_QCOM_X1E80100_H +#define __DRIVERS_INTERCONNECT_QCOM_X1E80100_H + +#define X1E80100_MASTER_A1NOC_SNOC 0 +#define X1E80100_MASTER_A2NOC_SNOC 1 +#define X1E80100_MASTER_ANOC_PCIE_GEM_NOC 2 +#define X1E80100_MASTER_ANOC_PCIE_GEM_NOC_DISP 3 +#define X1E80100_MASTER_APPSS_PROC 4 +#define X1E80100_MASTER_CAMNOC_HF 5 +#define X1E80100_MASTER_CAMNOC_ICP 6 +#define X1E80100_MASTER_CAMNOC_SF 7 +#define X1E80100_MASTER_CDSP_PROC 8 +#define X1E80100_MASTER_CNOC_CFG 9 +#define X1E80100_MASTER_CNOC_MNOC_CFG 10 +#define X1E80100_MASTER_COMPUTE_NOC 11 +#define X1E80100_MASTER_CRYPTO 12 +#define X1E80100_MASTER_GEM_NOC_CNOC 13 +#define X1E80100_MASTER_GEM_NOC_PCIE_SNOC 14 +#define X1E80100_MASTER_GFX3D 15 +#define X1E80100_MASTER_GPU_TCU 16 +#define X1E80100_MASTER_IPA 17 +#define X1E80100_MASTER_LLCC 18 +#define X1E80100_MASTER_LLCC_DISP 19 +#define X1E80100_MASTER_LPASS_GEM_NOC 20 +#define X1E80100_MASTER_LPASS_LPINOC 21 +#define X1E80100_MASTER_LPASS_PROC 22 +#define X1E80100_MASTER_LPIAON_NOC 23 +#define X1E80100_MASTER_MDP 24 +#define X1E80100_MASTER_MDP_DISP 25 +#define X1E80100_MASTER_MNOC_HF_MEM_NOC 26 +#define X1E80100_MASTER_MNOC_HF_MEM_NOC_DISP 27 +#define X1E80100_MASTER_MNOC_SF_MEM_NOC 28 +#define X1E80100_MASTER_PCIE_0 29 +#define X1E80100_MASTER_PCIE_1 30 +#define X1E80100_MASTER_QDSS_ETR 31 +#define X1E80100_MASTER_QDSS_ETR_1 32 +#define X1E80100_MASTER_QSPI_0 33 +#define X1E80100_MASTER_QUP_0 34 +#define X1E80100_MASTER_QUP_1 35 +#define X1E80100_MASTER_QUP_2 36 +#define X1E80100_MASTER_QUP_CORE_0 37 +#define X1E80100_MASTER_QUP_CORE_1 38 +#define X1E80100_MASTER_SDCC_2 39 +#define X1E80100_MASTER_SDCC_4 40 +#define X1E80100_MASTER_SNOC_SF_MEM_NOC 41 +#define X1E80100_MASTER_SP 42 +#define X1E80100_MASTER_SYS_TCU 43 +#define X1E80100_MASTER_UFS_MEM 44 +#define X1E80100_MASTER_USB3_0 45 +#define X1E80100_MASTER_VIDEO 46 +#define X1E80100_MASTER_VIDEO_CV_PROC 47 +#define X1E80100_MASTER_VIDEO_V_PROC 48 +#define X1E80100_SLAVE_A1NOC_SNOC 49 +#define X1E80100_SLAVE_A2NOC_SNOC 50 +#define X1E80100_SLAVE_AHB2PHY_NORTH 51 +#define X1E80100_SLAVE_AHB2PHY_SOUTH 52 +#define X1E80100_SLAVE_ANOC_PCIE_GEM_NOC 53 +#define X1E80100_SLAVE_AOSS 54 +#define X1E80100_SLAVE_APPSS 55 +#define X1E80100_SLAVE_BOOT_IMEM 56 +#define X1E80100_SLAVE_CAMERA_CFG 57 +#define X1E80100_SLAVE_CDSP_MEM_NOC 58 +#define X1E80100_SLAVE_CLK_CTL 59 +#define X1E80100_SLAVE_CNOC_CFG 60 +#define X1E80100_SLAVE_CNOC_MNOC_CFG 61 +#define X1E80100_SLAVE_CRYPTO_0_CFG 62 +#define X1E80100_SLAVE_DISPLAY_CFG 63 +#define X1E80100_SLAVE_EBI1 64 +#define X1E80100_SLAVE_EBI1_DISP 65 +#define X1E80100_SLAVE_GEM_NOC_CNOC 66 +#define X1E80100_SLAVE_GFX3D_CFG 67 +#define X1E80100_SLAVE_IMEM 68 +#define X1E80100_SLAVE_IMEM_CFG 69 +#define X1E80100_SLAVE_IPC_ROUTER_CFG 70 +#define X1E80100_SLAVE_LLCC 71 +#define X1E80100_SLAVE_LLCC_DISP 72 +#define X1E80100_SLAVE_LPASS_GEM_NOC 73 +#define X1E80100_SLAVE_LPASS_QTB_CFG 74 +#define X1E80100_SLAVE_LPIAON_NOC_LPASS_AG_NOC 75 +#define X1E80100_SLAVE_LPICX_NOC_LPIAON_NOC 76 +#define X1E80100_SLAVE_MEM_NOC_PCIE_SNOC 77 +#define X1E80100_SLAVE_MNOC_HF_MEM_NOC 78 +#define X1E80100_SLAVE_MNOC_HF_MEM_NOC_DISP 79 +#define X1E80100_SLAVE_MNOC_SF_MEM_NOC 80 +#define X1E80100_SLAVE_NSP_QTB_CFG 81 +#define X1E80100_SLAVE_PCIE_0 82 +#define X1E80100_SLAVE_PCIE_0_CFG 83 +#define X1E80100_SLAVE_PCIE_1 84 +#define X1E80100_SLAVE_PCIE_1_CFG 85 +#define X1E80100_SLAVE_PDM 86 +#define X1E80100_SLAVE_PRNG 87 +#define X1E80100_SLAVE_QDSS_CFG 88 +#define X1E80100_SLAVE_QDSS_STM 89 +#define X1E80100_SLAVE_QSPI_0 90 +#define X1E80100_SLAVE_QUP_1 91 +#define X1E80100_SLAVE_QUP_2 92 +#define X1E80100_SLAVE_QUP_CORE_0 93 +#define X1E80100_SLAVE_QUP_CORE_1 94 +#define X1E80100_SLAVE_QUP_CORE_2 95 +#define X1E80100_SLAVE_SDCC_2 96 +#define X1E80100_SLAVE_SDCC_4 97 +#define X1E80100_SLAVE_SERVICE_MNOC 98 +#define X1E80100_SLAVE_SNOC_GEM_NOC_SF 99 +#define X1E80100_SLAVE_TCSR 100 +#define X1E80100_SLAVE_TCU 101 +#define X1E80100_SLAVE_TLMM 102 +#define X1E80100_SLAVE_TME_CFG 103 +#define X1E80100_SLAVE_UFS_MEM_CFG 104 +#define X1E80100_SLAVE_USB3_0 105 +#define X1E80100_SLAVE_VENUS_CFG 106 +#define X1E80100_MASTER_DDR_PERF_MODE 107 +#define X1E80100_MASTER_QUP_CORE_2 108 +#define X1E80100_MASTER_PCIE_TCU 109 +#define X1E80100_MASTER_GIC2 110 +#define X1E80100_MASTER_AV1_ENC 111 +#define X1E80100_MASTER_EVA 112 +#define X1E80100_MASTER_PCIE_NORTH 113 +#define X1E80100_MASTER_PCIE_SOUTH 114 +#define X1E80100_MASTER_PCIE_3 115 +#define X1E80100_MASTER_PCIE_4 116 +#define X1E80100_MASTER_PCIE_5 117 +#define X1E80100_MASTER_PCIE_2 118 +#define X1E80100_MASTER_PCIE_6A 119 +#define X1E80100_MASTER_PCIE_6B 120 +#define X1E80100_MASTER_GIC1 121 +#define X1E80100_MASTER_USB_NOC_SNOC 122 +#define X1E80100_MASTER_AGGRE_USB_NORTH 123 +#define X1E80100_MASTER_AGGRE_USB_SOUTH 124 +#define X1E80100_MASTER_USB2 125 +#define X1E80100_MASTER_USB3_MP 126 +#define X1E80100_MASTER_USB3_1 127 +#define X1E80100_MASTER_USB3_2 128 +#define X1E80100_MASTER_USB4_0 129 +#define X1E80100_MASTER_USB4_1 130 +#define X1E80100_MASTER_USB4_2 131 +#define X1E80100_MASTER_ANOC_PCIE_GEM_NOC_PCIE 132 +#define X1E80100_MASTER_LLCC_PCIE 133 +#define X1E80100_MASTER_PCIE_NORTH_PCIE 134 +#define X1E80100_MASTER_PCIE_SOUTH_PCIE 135 +#define X1E80100_MASTER_PCIE_3_PCIE 136 +#define X1E80100_MASTER_PCIE_4_PCIE 137 +#define X1E80100_MASTER_PCIE_5_PCIE 138 +#define X1E80100_MASTER_PCIE_0_PCIE 139 +#define X1E80100_MASTER_PCIE_1_PCIE 140 +#define X1E80100_MASTER_PCIE_2_PCIE 141 +#define X1E80100_MASTER_PCIE_6A_PCIE 142 +#define X1E80100_MASTER_PCIE_6B_PCIE 143 +#define X1E80100_SLAVE_AHB2PHY_2 144 +#define X1E80100_SLAVE_AV1_ENC_CFG 145 +#define X1E80100_SLAVE_PCIE_2_CFG 146 +#define X1E80100_SLAVE_PCIE_3_CFG 147 +#define X1E80100_SLAVE_PCIE_4_CFG 148 +#define X1E80100_SLAVE_PCIE_5_CFG 149 +#define X1E80100_SLAVE_PCIE_6A_CFG 150 +#define X1E80100_SLAVE_PCIE_6B_CFG 151 +#define X1E80100_SLAVE_PCIE_RSC_CFG 152 +#define X1E80100_SLAVE_QUP_0 153 +#define X1E80100_SLAVE_SMMUV3_CFG 154 +#define X1E80100_SLAVE_USB2 155 +#define X1E80100_SLAVE_USB3_1 156 +#define X1E80100_SLAVE_USB3_2 157 +#define X1E80100_SLAVE_USB3_MP 158 +#define X1E80100_SLAVE_USB4_0 159 +#define X1E80100_SLAVE_USB4_1 160 +#define X1E80100_SLAVE_USB4_2 161 +#define X1E80100_SLAVE_PCIE_2 162 +#define X1E80100_SLAVE_PCIE_3 163 +#define X1E80100_SLAVE_PCIE_4 164 +#define X1E80100_SLAVE_PCIE_5 165 +#define X1E80100_SLAVE_PCIE_6A 166 +#define X1E80100_SLAVE_PCIE_6B 167 +#define X1E80100_SLAVE_DDR_PERF_MODE 168 +#define X1E80100_SLAVE_PCIE_NORTH 169 +#define X1E80100_SLAVE_PCIE_SOUTH 170 +#define X1E80100_SLAVE_USB_NOC_SNOC 171 +#define X1E80100_SLAVE_AGGRE_USB_NORTH 172 +#define X1E80100_SLAVE_AGGRE_USB_SOUTH 173 +#define X1E80100_SLAVE_LLCC_PCIE 174 +#define X1E80100_SLAVE_EBI1_PCIE 175 +#define X1E80100_SLAVE_ANOC_PCIE_GEM_NOC_PCIE 176 +#define X1E80100_SLAVE_PCIE_NORTH_PCIE 177 +#define X1E80100_SLAVE_PCIE_SOUTH_PCIE 178 + +#endif diff --git a/drivers/interconnect/samsung/exynos.c b/drivers/interconnect/samsung/exynos.c index ebf09bbf725b..1ba14cb45d5a 100644 --- a/drivers/interconnect/samsung/exynos.c +++ b/drivers/interconnect/samsung/exynos.c @@ -93,14 +93,12 @@ static struct icc_node *exynos_generic_icc_xlate(struct of_phandle_args *spec, return priv->node; } -static int exynos_generic_icc_remove(struct platform_device *pdev) +static void exynos_generic_icc_remove(struct platform_device *pdev) { struct exynos_icc_priv *priv = platform_get_drvdata(pdev); icc_provider_deregister(&priv->provider); icc_nodes_remove(&priv->provider); - - return 0; } static int exynos_generic_icc_probe(struct platform_device *pdev) @@ -182,7 +180,7 @@ static struct platform_driver exynos_generic_icc_driver = { .sync_state = icc_sync_state, }, .probe = exynos_generic_icc_probe, - .remove = exynos_generic_icc_remove, + .remove_new = exynos_generic_icc_remove, }; module_platform_driver(exynos_generic_icc_driver); diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 7673bb82945b..9a29d742617e 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -160,6 +160,7 @@ config IOMMU_DMA # Shared Virtual Addressing config IOMMU_SVA + select IOMMU_MM_DATA bool config FSL_PAMU diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h index 86be1edd50ee..8b3601f285fd 100644 --- a/drivers/iommu/amd/amd_iommu.h +++ b/drivers/iommu/amd/amd_iommu.h @@ -53,10 +53,16 @@ int amd_iommu_pdev_enable_cap_pri(struct pci_dev *pdev); void amd_iommu_pdev_disable_cap_pri(struct pci_dev *pdev); int amd_iommu_flush_page(struct iommu_domain *dom, u32 pasid, u64 address); +/* + * This function flushes all internal caches of + * the IOMMU used by this driver. + */ +void amd_iommu_flush_all_caches(struct amd_iommu *iommu); void amd_iommu_update_and_flush_device_table(struct protection_domain *domain); void amd_iommu_domain_update(struct protection_domain *domain); void amd_iommu_domain_flush_complete(struct protection_domain *domain); -void amd_iommu_domain_flush_tlb_pde(struct protection_domain *domain); +void amd_iommu_domain_flush_pages(struct protection_domain *domain, + u64 address, size_t size); int amd_iommu_flush_tlb(struct iommu_domain *dom, u32 pasid); int amd_iommu_domain_set_gcr3(struct iommu_domain *dom, u32 pasid, unsigned long cr3); diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h index 90b7d7950a9e..809d74faa1a5 100644 --- a/drivers/iommu/amd/amd_iommu_types.h +++ b/drivers/iommu/amd/amd_iommu_types.h @@ -902,12 +902,6 @@ extern int amd_iommu_max_glx_val; extern u64 amd_iommu_efr; extern u64 amd_iommu_efr2; -/* - * This function flushes all internal caches of - * the IOMMU used by this driver. - */ -void iommu_flush_all_caches(struct amd_iommu *iommu); - static inline int get_ioapic_devid(int id) { struct devid_map *entry; diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index 64bcf3df37ee..c83bd0c2a1c9 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -2223,7 +2223,7 @@ static int __init amd_iommu_init_pci(void) init_device_table_dma(pci_seg); for_each_iommu(iommu) - iommu_flush_all_caches(iommu); + amd_iommu_flush_all_caches(iommu); print_iommu_info(); @@ -2773,7 +2773,7 @@ static void early_enable_iommu(struct amd_iommu *iommu) iommu_enable_xt(iommu); iommu_enable_irtcachedis(iommu); iommu_enable(iommu); - iommu_flush_all_caches(iommu); + amd_iommu_flush_all_caches(iommu); } /* @@ -2829,7 +2829,7 @@ static void early_enable_iommus(void) iommu_enable_xt(iommu); iommu_enable_irtcachedis(iommu); iommu_set_device_table(iommu); - iommu_flush_all_caches(iommu); + amd_iommu_flush_all_caches(iommu); } } } @@ -3293,7 +3293,7 @@ static int __init state_next(void) uninit_device_table_dma(pci_seg); for_each_iommu(iommu) - iommu_flush_all_caches(iommu); + amd_iommu_flush_all_caches(iommu); } } return ret; diff --git a/drivers/iommu/amd/io_pgtable.c b/drivers/iommu/amd/io_pgtable.c index 6c0621f6f572..2a0d1e97e52f 100644 --- a/drivers/iommu/amd/io_pgtable.c +++ b/drivers/iommu/amd/io_pgtable.c @@ -369,6 +369,8 @@ static int iommu_v1_map_pages(struct io_pgtable_ops *ops, unsigned long iova, bool updated = false; u64 __pte, *pte; int ret, i, count; + size_t size = pgcount << __ffs(pgsize); + unsigned long o_iova = iova; BUG_ON(!IS_ALIGNED(iova, pgsize)); BUG_ON(!IS_ALIGNED(paddr, pgsize)); @@ -424,8 +426,7 @@ out: * Updates and flushing already happened in * increase_address_space(). */ - amd_iommu_domain_flush_tlb_pde(dom); - amd_iommu_domain_flush_complete(dom); + amd_iommu_domain_flush_pages(dom, o_iova, size); spin_unlock_irqrestore(&dom->lock, flags); } diff --git a/drivers/iommu/amd/io_pgtable_v2.c b/drivers/iommu/amd/io_pgtable_v2.c index f818a7e254d4..6d69ba60744f 100644 --- a/drivers/iommu/amd/io_pgtable_v2.c +++ b/drivers/iommu/amd/io_pgtable_v2.c @@ -244,7 +244,6 @@ static int iommu_v2_map_pages(struct io_pgtable_ops *ops, unsigned long iova, unsigned long mapped_size = 0; unsigned long o_iova = iova; size_t size = pgcount << __ffs(pgsize); - int count = 0; int ret = 0; bool updated = false; @@ -265,19 +264,14 @@ static int iommu_v2_map_pages(struct io_pgtable_ops *ops, unsigned long iova, *pte = set_pte_attr(paddr, map_size, prot); - count++; iova += map_size; paddr += map_size; mapped_size += map_size; } out: - if (updated) { - if (count > 1) - amd_iommu_flush_tlb(&pdom->domain, 0); - else - amd_iommu_flush_page(&pdom->domain, 0, o_iova); - } + if (updated) + amd_iommu_domain_flush_pages(pdom, o_iova, size); if (mapped) *mapped += mapped_size; diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index b9a0523cbb0a..4283dd8191f0 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -64,7 +64,7 @@ LIST_HEAD(hpet_map); LIST_HEAD(acpihid_map); const struct iommu_ops amd_iommu_ops; -const struct iommu_dirty_ops amd_dirty_ops; +static const struct iommu_dirty_ops amd_dirty_ops; int amd_iommu_max_glx_val = -1; @@ -85,6 +85,11 @@ static void detach_device(struct device *dev); * ****************************************************************************/ +static inline bool pdom_is_v2_pgtbl_mode(struct protection_domain *pdom) +{ + return (pdom && (pdom->flags & PD_IOMMUV2_MASK)); +} + static inline int get_acpihid_device_id(struct device *dev, struct acpihid_map_entry **entry) { @@ -551,8 +556,6 @@ static void amd_iommu_uninit_device(struct device *dev) if (dev_data->domain) detach_device(dev); - dev_iommu_priv_set(dev, NULL); - /* * We keep dev_data around for unplugged devices and reuse it when the * device is re-plugged - not doing so would introduce a ton of races. @@ -1124,68 +1127,44 @@ static inline u64 build_inv_address(u64 address, size_t size) } static void build_inv_iommu_pages(struct iommu_cmd *cmd, u64 address, - size_t size, u16 domid, int pde) + size_t size, u16 domid, + ioasid_t pasid, bool gn) { u64 inv_address = build_inv_address(address, size); memset(cmd, 0, sizeof(*cmd)); + cmd->data[1] |= domid; cmd->data[2] = lower_32_bits(inv_address); cmd->data[3] = upper_32_bits(inv_address); + /* PDE bit - we want to flush everything, not only the PTEs */ + cmd->data[2] |= CMD_INV_IOMMU_PAGES_PDE_MASK; + if (gn) { + cmd->data[0] |= pasid; + cmd->data[2] |= CMD_INV_IOMMU_PAGES_GN_MASK; + } CMD_SET_TYPE(cmd, CMD_INV_IOMMU_PAGES); - if (pde) /* PDE bit - we want to flush everything, not only the PTEs */ - cmd->data[2] |= CMD_INV_IOMMU_PAGES_PDE_MASK; } static void build_inv_iotlb_pages(struct iommu_cmd *cmd, u16 devid, int qdep, - u64 address, size_t size) + u64 address, size_t size, + ioasid_t pasid, bool gn) { u64 inv_address = build_inv_address(address, size); memset(cmd, 0, sizeof(*cmd)); + cmd->data[0] = devid; cmd->data[0] |= (qdep & 0xff) << 24; cmd->data[1] = devid; cmd->data[2] = lower_32_bits(inv_address); cmd->data[3] = upper_32_bits(inv_address); - CMD_SET_TYPE(cmd, CMD_INV_IOTLB_PAGES); -} - -static void build_inv_iommu_pasid(struct iommu_cmd *cmd, u16 domid, u32 pasid, - u64 address, bool size) -{ - memset(cmd, 0, sizeof(*cmd)); - - address &= ~(0xfffULL); - - cmd->data[0] = pasid; - cmd->data[1] = domid; - cmd->data[2] = lower_32_bits(address); - cmd->data[3] = upper_32_bits(address); - cmd->data[2] |= CMD_INV_IOMMU_PAGES_PDE_MASK; - cmd->data[2] |= CMD_INV_IOMMU_PAGES_GN_MASK; - if (size) - cmd->data[2] |= CMD_INV_IOMMU_PAGES_SIZE_MASK; - CMD_SET_TYPE(cmd, CMD_INV_IOMMU_PAGES); -} - -static void build_inv_iotlb_pasid(struct iommu_cmd *cmd, u16 devid, u32 pasid, - int qdep, u64 address, bool size) -{ - memset(cmd, 0, sizeof(*cmd)); - - address &= ~(0xfffULL); + if (gn) { + cmd->data[0] |= ((pasid >> 8) & 0xff) << 16; + cmd->data[1] |= (pasid & 0xff) << 16; + cmd->data[2] |= CMD_INV_IOMMU_PAGES_GN_MASK; + } - cmd->data[0] = devid; - cmd->data[0] |= ((pasid >> 8) & 0xff) << 16; - cmd->data[0] |= (qdep & 0xff) << 24; - cmd->data[1] = devid; - cmd->data[1] |= (pasid & 0xff) << 16; - cmd->data[2] = lower_32_bits(address); - cmd->data[2] |= CMD_INV_IOMMU_PAGES_GN_MASK; - cmd->data[3] = upper_32_bits(address); - if (size) - cmd->data[2] |= CMD_INV_IOMMU_PAGES_SIZE_MASK; CMD_SET_TYPE(cmd, CMD_INV_IOTLB_PAGES); } @@ -1341,7 +1320,7 @@ static void amd_iommu_flush_tlb_all(struct amd_iommu *iommu) for (dom_id = 0; dom_id <= last_bdf; ++dom_id) { struct iommu_cmd cmd; build_inv_iommu_pages(&cmd, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, - dom_id, 1); + dom_id, IOMMU_NO_PASID, false); iommu_queue_command(iommu, &cmd); } @@ -1353,7 +1332,7 @@ static void amd_iommu_flush_tlb_domid(struct amd_iommu *iommu, u32 dom_id) struct iommu_cmd cmd; build_inv_iommu_pages(&cmd, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, - dom_id, 1); + dom_id, IOMMU_NO_PASID, false); iommu_queue_command(iommu, &cmd); iommu_completion_wait(iommu); @@ -1392,7 +1371,7 @@ static void amd_iommu_flush_irt_all(struct amd_iommu *iommu) iommu_completion_wait(iommu); } -void iommu_flush_all_caches(struct amd_iommu *iommu) +void amd_iommu_flush_all_caches(struct amd_iommu *iommu) { if (check_feature(FEATURE_IA)) { amd_iommu_flush_all(iommu); @@ -1406,8 +1385,8 @@ void iommu_flush_all_caches(struct amd_iommu *iommu) /* * Command send function for flushing on-device TLB */ -static int device_flush_iotlb(struct iommu_dev_data *dev_data, - u64 address, size_t size) +static int device_flush_iotlb(struct iommu_dev_data *dev_data, u64 address, + size_t size, ioasid_t pasid, bool gn) { struct amd_iommu *iommu; struct iommu_cmd cmd; @@ -1418,7 +1397,8 @@ static int device_flush_iotlb(struct iommu_dev_data *dev_data, if (!iommu) return -EINVAL; - build_inv_iotlb_pages(&cmd, dev_data->devid, qdep, address, size); + build_inv_iotlb_pages(&cmd, dev_data->devid, qdep, address, + size, pasid, gn); return iommu_queue_command(iommu, &cmd); } @@ -1464,8 +1444,11 @@ static int device_flush_dte(struct iommu_dev_data *dev_data) return ret; } - if (dev_data->ats_enabled) - ret = device_flush_iotlb(dev_data, 0, ~0UL); + if (dev_data->ats_enabled) { + /* Invalidate the entire contents of an IOTLB */ + ret = device_flush_iotlb(dev_data, 0, ~0UL, + IOMMU_NO_PASID, false); + } return ret; } @@ -1476,13 +1459,18 @@ static int device_flush_dte(struct iommu_dev_data *dev_data) * page. Otherwise it flushes the whole TLB of the IOMMU. */ static void __domain_flush_pages(struct protection_domain *domain, - u64 address, size_t size, int pde) + u64 address, size_t size) { struct iommu_dev_data *dev_data; struct iommu_cmd cmd; int ret = 0, i; + ioasid_t pasid = IOMMU_NO_PASID; + bool gn = false; + + if (pdom_is_v2_pgtbl_mode(domain)) + gn = true; - build_inv_iommu_pages(&cmd, address, size, domain->id, pde); + build_inv_iommu_pages(&cmd, address, size, domain->id, pasid, gn); for (i = 0; i < amd_iommu_get_num_iommus(); ++i) { if (!domain->dev_iommu[i]) @@ -1500,17 +1488,21 @@ static void __domain_flush_pages(struct protection_domain *domain, if (!dev_data->ats_enabled) continue; - ret |= device_flush_iotlb(dev_data, address, size); + ret |= device_flush_iotlb(dev_data, address, size, pasid, gn); } WARN_ON(ret); } -static void domain_flush_pages(struct protection_domain *domain, - u64 address, size_t size, int pde) +void amd_iommu_domain_flush_pages(struct protection_domain *domain, + u64 address, size_t size) { if (likely(!amd_iommu_np_cache)) { - __domain_flush_pages(domain, address, size, pde); + __domain_flush_pages(domain, address, size); + + /* Wait until IOMMU TLB and all device IOTLB flushes are complete */ + amd_iommu_domain_flush_complete(domain); + return; } @@ -1543,16 +1535,20 @@ static void domain_flush_pages(struct protection_domain *domain, flush_size = 1ul << min_alignment; - __domain_flush_pages(domain, address, flush_size, pde); + __domain_flush_pages(domain, address, flush_size); address += flush_size; size -= flush_size; } + + /* Wait until IOMMU TLB and all device IOTLB flushes are complete */ + amd_iommu_domain_flush_complete(domain); } /* Flush the whole IO/TLB for a given protection domain - including PDE */ -void amd_iommu_domain_flush_tlb_pde(struct protection_domain *domain) +static void amd_iommu_domain_flush_all(struct protection_domain *domain) { - domain_flush_pages(domain, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, 1); + amd_iommu_domain_flush_pages(domain, 0, + CMD_INV_IOMMU_ALL_PAGES_ADDRESS); } void amd_iommu_domain_flush_complete(struct protection_domain *domain) @@ -1579,8 +1575,7 @@ static void domain_flush_np_cache(struct protection_domain *domain, unsigned long flags; spin_lock_irqsave(&domain->lock, flags); - domain_flush_pages(domain, iova, size, 1); - amd_iommu_domain_flush_complete(domain); + amd_iommu_domain_flush_pages(domain, iova, size); spin_unlock_irqrestore(&domain->lock, flags); } } @@ -1858,11 +1853,8 @@ static void do_detach(struct iommu_dev_data *dev_data) /* Flush the DTE entry */ device_flush_dte(dev_data); - /* Flush IOTLB */ - amd_iommu_domain_flush_tlb_pde(domain); - - /* Wait for the flushes to finish */ - amd_iommu_domain_flush_complete(domain); + /* Flush IOTLB and wait for the flushes to finish */ + amd_iommu_domain_flush_all(domain); /* decrease reference counters - needs to happen after the flushes */ domain->dev_iommu[iommu->index] -= 1; @@ -1896,15 +1888,6 @@ static int attach_device(struct device *dev, do_attach(dev_data, domain); - /* - * We might boot into a crash-kernel here. The crashed kernel - * left the caches in the IOMMU dirty. So we have to flush - * here to evict all dirty stuff. - */ - amd_iommu_domain_flush_tlb_pde(domain); - - amd_iommu_domain_flush_complete(domain); - out: spin_unlock(&dev_data->lock); @@ -2048,8 +2031,7 @@ void amd_iommu_domain_update(struct protection_domain *domain) amd_iommu_update_and_flush_device_table(domain); /* Flush domain TLB(s) and wait for completion */ - amd_iommu_domain_flush_tlb_pde(domain); - amd_iommu_domain_flush_complete(domain); + amd_iommu_domain_flush_all(domain); } /***************************************************************************** @@ -2482,10 +2464,9 @@ static int amd_iommu_set_dirty_tracking(struct iommu_domain *domain, } /* Flush IOTLB to mark IOPTE dirty on the next translation(s) */ - if (domain_flush) { - amd_iommu_domain_flush_tlb_pde(pdomain); - amd_iommu_domain_flush_complete(pdomain); - } + if (domain_flush) + amd_iommu_domain_flush_all(pdomain); + pdomain->dirty_tracking = enable; spin_unlock_irqrestore(&pdomain->lock, flags); @@ -2588,8 +2569,7 @@ static void amd_iommu_flush_iotlb_all(struct iommu_domain *domain) unsigned long flags; spin_lock_irqsave(&dom->lock, flags); - amd_iommu_domain_flush_tlb_pde(dom); - amd_iommu_domain_flush_complete(dom); + amd_iommu_domain_flush_all(dom); spin_unlock_irqrestore(&dom->lock, flags); } @@ -2600,8 +2580,8 @@ static void amd_iommu_iotlb_sync(struct iommu_domain *domain, unsigned long flags; spin_lock_irqsave(&dom->lock, flags); - domain_flush_pages(dom, gather->start, gather->end - gather->start + 1, 1); - amd_iommu_domain_flush_complete(dom); + amd_iommu_domain_flush_pages(dom, gather->start, + gather->end - gather->start + 1); spin_unlock_irqrestore(&dom->lock, flags); } @@ -2635,7 +2615,7 @@ static bool amd_iommu_enforce_cache_coherency(struct iommu_domain *domain) return true; } -const struct iommu_dirty_ops amd_dirty_ops = { +static const struct iommu_dirty_ops amd_dirty_ops = { .set_dirty_tracking = amd_iommu_set_dirty_tracking, .read_and_clear_dirty = amd_iommu_read_and_clear_dirty, }; @@ -2666,7 +2646,7 @@ const struct iommu_ops amd_iommu_ops = { }; static int __flush_pasid(struct protection_domain *domain, u32 pasid, - u64 address, bool size) + u64 address, size_t size) { struct iommu_dev_data *dev_data; struct iommu_cmd cmd; @@ -2675,7 +2655,7 @@ static int __flush_pasid(struct protection_domain *domain, u32 pasid, if (!(domain->flags & PD_IOMMUV2_MASK)) return -EINVAL; - build_inv_iommu_pasid(&cmd, domain->id, pasid, address, size); + build_inv_iommu_pages(&cmd, address, size, domain->id, pasid, true); /* * IOMMU TLB needs to be flushed before Device TLB to @@ -2709,8 +2689,8 @@ static int __flush_pasid(struct protection_domain *domain, u32 pasid, iommu = rlookup_amd_iommu(dev_data->dev); if (!iommu) continue; - build_inv_iotlb_pasid(&cmd, dev_data->devid, pasid, - qdep, address, size); + build_inv_iotlb_pages(&cmd, dev_data->devid, qdep, + address, size, pasid, true); ret = iommu_queue_command(iommu, &cmd); if (ret != 0) @@ -2730,7 +2710,7 @@ out: static int __amd_iommu_flush_page(struct protection_domain *domain, u32 pasid, u64 address) { - return __flush_pasid(domain, pasid, address, false); + return __flush_pasid(domain, pasid, address, PAGE_SIZE); } int amd_iommu_flush_page(struct iommu_domain *dom, u32 pasid, @@ -2749,8 +2729,7 @@ int amd_iommu_flush_page(struct iommu_domain *dom, u32 pasid, static int __amd_iommu_flush_tlb(struct protection_domain *domain, u32 pasid) { - return __flush_pasid(domain, pasid, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, - true); + return __flush_pasid(domain, pasid, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS); } int amd_iommu_flush_tlb(struct iommu_domain *dom, u32 pasid) @@ -3111,8 +3090,8 @@ out: return index; } -static int modify_irte_ga(struct amd_iommu *iommu, u16 devid, int index, - struct irte_ga *irte) +static int __modify_irte_ga(struct amd_iommu *iommu, u16 devid, int index, + struct irte_ga *irte) { struct irq_remap_table *table; struct irte_ga *entry; @@ -3139,6 +3118,18 @@ static int modify_irte_ga(struct amd_iommu *iommu, u16 devid, int index, raw_spin_unlock_irqrestore(&table->lock, flags); + return 0; +} + +static int modify_irte_ga(struct amd_iommu *iommu, u16 devid, int index, + struct irte_ga *irte) +{ + bool ret; + + ret = __modify_irte_ga(iommu, devid, index, irte); + if (ret) + return ret; + iommu_flush_irt_and_complete(iommu, devid); return 0; @@ -3822,8 +3813,8 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data) } entry->lo.fields_vapic.is_run = is_run; - return modify_irte_ga(ir_data->iommu, ir_data->irq_2_irte.devid, - ir_data->irq_2_irte.index, entry); + return __modify_irte_ga(ir_data->iommu, ir_data->irq_2_irte.devid, + ir_data->irq_2_irte.index, entry); } EXPORT_SYMBOL(amd_iommu_update_ga); #endif diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index ee05f4824bfa..ef3ee95706da 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -81,6 +81,7 @@ #define DART_T8020_TCR_BYPASS_DAPF BIT(12) #define DART_T8020_TTBR 0x200 +#define DART_T8020_USB4_TTBR 0x400 #define DART_T8020_TTBR_VALID BIT(31) #define DART_T8020_TTBR_ADDR_FIELD_SHIFT 0 #define DART_T8020_TTBR_SHIFT 12 @@ -368,12 +369,14 @@ apple_dart_t8020_hw_stream_command(struct apple_dart_stream_map *stream_map, u32 command) { unsigned long flags; - int ret; + int ret, i; u32 command_reg; spin_lock_irqsave(&stream_map->dart->lock, flags); - writel(stream_map->sidmap[0], stream_map->dart->regs + DART_T8020_STREAM_SELECT); + for (i = 0; i < BITS_TO_U32(stream_map->dart->num_streams); i++) + writel(stream_map->sidmap[i], + stream_map->dart->regs + DART_T8020_STREAM_SELECT + 4 * i); writel(command, stream_map->dart->regs + DART_T8020_STREAM_COMMAND); ret = readl_poll_timeout_atomic( @@ -740,7 +743,6 @@ static void apple_dart_release_device(struct device *dev) { struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev); - dev_iommu_priv_set(dev, NULL); kfree(cfg); } @@ -908,7 +910,7 @@ static struct iommu_group *apple_dart_device_group(struct device *dev) ret = apple_dart_merge_master_cfg(group_master_cfg, cfg); if (ret) { - dev_err(dev, "Failed to merge DART IOMMU grups.\n"); + dev_err(dev, "Failed to merge DART IOMMU groups.\n"); iommu_group_put(group); res = ERR_PTR(ret); goto out; @@ -1215,6 +1217,33 @@ static const struct apple_dart_hw apple_dart_hw_t8103 = { .ttbr_shift = DART_T8020_TTBR_SHIFT, .ttbr_count = 4, }; + +static const struct apple_dart_hw apple_dart_hw_t8103_usb4 = { + .type = DART_T8020, + .irq_handler = apple_dart_t8020_irq, + .invalidate_tlb = apple_dart_t8020_hw_invalidate_tlb, + .oas = 36, + .fmt = APPLE_DART, + .max_sid_count = 64, + + .enable_streams = DART_T8020_STREAMS_ENABLE, + .lock = DART_T8020_CONFIG, + .lock_bit = DART_T8020_CONFIG_LOCK, + + .error = DART_T8020_ERROR, + + .tcr = DART_T8020_TCR, + .tcr_enabled = DART_T8020_TCR_TRANSLATE_ENABLE, + .tcr_disabled = 0, + .tcr_bypass = 0, + + .ttbr = DART_T8020_USB4_TTBR, + .ttbr_valid = DART_T8020_TTBR_VALID, + .ttbr_addr_field_shift = DART_T8020_TTBR_ADDR_FIELD_SHIFT, + .ttbr_shift = DART_T8020_TTBR_SHIFT, + .ttbr_count = 4, +}; + static const struct apple_dart_hw apple_dart_hw_t6000 = { .type = DART_T6000, .irq_handler = apple_dart_t8020_irq, @@ -1272,7 +1301,7 @@ static __maybe_unused int apple_dart_suspend(struct device *dev) unsigned int sid, idx; for (sid = 0; sid < dart->num_streams; sid++) { - dart->save_tcr[sid] = readl_relaxed(dart->regs + DART_TCR(dart, sid)); + dart->save_tcr[sid] = readl(dart->regs + DART_TCR(dart, sid)); for (idx = 0; idx < dart->hw->ttbr_count; idx++) dart->save_ttbr[sid][idx] = readl(dart->regs + DART_TTBR(dart, sid, idx)); @@ -1307,6 +1336,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(apple_dart_pm_ops, apple_dart_suspend, apple_dar static const struct of_device_id apple_dart_of_match[] = { { .compatible = "apple,t8103-dart", .data = &apple_dart_hw_t8103 }, + { .compatible = "apple,t8103-usb4-dart", .data = &apple_dart_hw_t8103_usb4 }, { .compatible = "apple,t8110-dart", .data = &apple_dart_hw_t8110 }, { .compatible = "apple,t6000-dart", .data = &apple_dart_hw_t6000 }, {}, diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c index 353248ab18e7..05722121f00e 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c @@ -246,7 +246,8 @@ static void arm_smmu_mm_arch_invalidate_secondary_tlbs(struct mmu_notifier *mn, smmu_domain); } - arm_smmu_atc_inv_domain(smmu_domain, mm->pasid, start, size); + arm_smmu_atc_inv_domain(smmu_domain, mm_get_enqcmd_pasid(mm), start, + size); } static void arm_smmu_mm_release(struct mmu_notifier *mn, struct mm_struct *mm) @@ -264,10 +265,11 @@ static void arm_smmu_mm_release(struct mmu_notifier *mn, struct mm_struct *mm) * DMA may still be running. Keep the cd valid to avoid C_BAD_CD events, * but disable translation. */ - arm_smmu_update_ctx_desc_devices(smmu_domain, mm->pasid, &quiet_cd); + arm_smmu_update_ctx_desc_devices(smmu_domain, mm_get_enqcmd_pasid(mm), + &quiet_cd); arm_smmu_tlb_inv_asid(smmu_domain->smmu, smmu_mn->cd->asid); - arm_smmu_atc_inv_domain(smmu_domain, mm->pasid, 0, 0); + arm_smmu_atc_inv_domain(smmu_domain, mm_get_enqcmd_pasid(mm), 0, 0); smmu_mn->cleared = true; mutex_unlock(&sva_lock); @@ -325,10 +327,13 @@ arm_smmu_mmu_notifier_get(struct arm_smmu_domain *smmu_domain, spin_lock_irqsave(&smmu_domain->devices_lock, flags); list_for_each_entry(master, &smmu_domain->devices, domain_head) { - ret = arm_smmu_write_ctx_desc(master, mm->pasid, cd); + ret = arm_smmu_write_ctx_desc(master, mm_get_enqcmd_pasid(mm), + cd); if (ret) { - list_for_each_entry_from_reverse(master, &smmu_domain->devices, domain_head) - arm_smmu_write_ctx_desc(master, mm->pasid, NULL); + list_for_each_entry_from_reverse( + master, &smmu_domain->devices, domain_head) + arm_smmu_write_ctx_desc( + master, mm_get_enqcmd_pasid(mm), NULL); break; } } @@ -358,7 +363,8 @@ static void arm_smmu_mmu_notifier_put(struct arm_smmu_mmu_notifier *smmu_mn) list_del(&smmu_mn->list); - arm_smmu_update_ctx_desc_devices(smmu_domain, mm->pasid, NULL); + arm_smmu_update_ctx_desc_devices(smmu_domain, mm_get_enqcmd_pasid(mm), + NULL); /* * If we went through clear(), we've already invalidated, and no @@ -366,7 +372,8 @@ static void arm_smmu_mmu_notifier_put(struct arm_smmu_mmu_notifier *smmu_mn) */ if (!smmu_mn->cleared) { arm_smmu_tlb_inv_asid(smmu_domain->smmu, cd->asid); - arm_smmu_atc_inv_domain(smmu_domain, mm->pasid, 0, 0); + arm_smmu_atc_inv_domain(smmu_domain, mm_get_enqcmd_pasid(mm), 0, + 0); } /* Frees smmu_mn */ diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 7445454c2af2..0ffb1cf17e0b 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -1063,6 +1063,7 @@ int arm_smmu_write_ctx_desc(struct arm_smmu_master *master, int ssid, bool cd_live; __le64 *cdptr; struct arm_smmu_ctx_desc_cfg *cd_table = &master->cd_table; + struct arm_smmu_device *smmu = master->smmu; if (WARN_ON(ssid >= (1 << cd_table->s1cdmax))) return -E2BIG; @@ -1077,6 +1078,8 @@ int arm_smmu_write_ctx_desc(struct arm_smmu_master *master, int ssid, if (!cd) { /* (5) */ val = 0; } else if (cd == &quiet_cd) { /* (4) */ + if (!(smmu->features & ARM_SMMU_FEAT_STALL_FORCE)) + val &= ~(CTXDESC_CD_0_S | CTXDESC_CD_0_R); val |= CTXDESC_CD_0_TCR_EPD0; } else if (cd_live) { /* (3) */ val &= ~CTXDESC_CD_0_ASID; @@ -1249,7 +1252,7 @@ static void arm_smmu_sync_ste_for_sid(struct arm_smmu_device *smmu, u32 sid) } static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, - __le64 *dst) + struct arm_smmu_ste *dst) { /* * This is hideously complicated, but we only really care about @@ -1267,12 +1270,12 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, * 2. Write everything apart from dword 0, sync, write dword 0, sync * 3. Update Config, sync */ - u64 val = le64_to_cpu(dst[0]); + u64 val = le64_to_cpu(dst->data[0]); bool ste_live = false; - struct arm_smmu_device *smmu = NULL; + struct arm_smmu_device *smmu = master->smmu; struct arm_smmu_ctx_desc_cfg *cd_table = NULL; struct arm_smmu_s2_cfg *s2_cfg = NULL; - struct arm_smmu_domain *smmu_domain = NULL; + struct arm_smmu_domain *smmu_domain = master->domain; struct arm_smmu_cmdq_ent prefetch_cmd = { .opcode = CMDQ_OP_PREFETCH_CFG, .prefetch = { @@ -1280,18 +1283,12 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, }, }; - if (master) { - smmu_domain = master->domain; - smmu = master->smmu; - } - if (smmu_domain) { switch (smmu_domain->stage) { case ARM_SMMU_DOMAIN_S1: cd_table = &master->cd_table; break; case ARM_SMMU_DOMAIN_S2: - case ARM_SMMU_DOMAIN_NESTED: s2_cfg = &smmu_domain->s2_cfg; break; default: @@ -1325,10 +1322,10 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, else val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS); - dst[0] = cpu_to_le64(val); - dst[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG, + dst->data[0] = cpu_to_le64(val); + dst->data[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG, STRTAB_STE_1_SHCFG_INCOMING)); - dst[2] = 0; /* Nuke the VMID */ + dst->data[2] = 0; /* Nuke the VMID */ /* * The SMMU can perform negative caching, so we must sync * the STE regardless of whether the old value was live. @@ -1343,7 +1340,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, STRTAB_STE_1_STRW_EL2 : STRTAB_STE_1_STRW_NSEL1; BUG_ON(ste_live); - dst[1] = cpu_to_le64( + dst->data[1] = cpu_to_le64( FIELD_PREP(STRTAB_STE_1_S1DSS, STRTAB_STE_1_S1DSS_SSID0) | FIELD_PREP(STRTAB_STE_1_S1CIR, STRTAB_STE_1_S1C_CACHE_WBRA) | FIELD_PREP(STRTAB_STE_1_S1COR, STRTAB_STE_1_S1C_CACHE_WBRA) | @@ -1352,7 +1349,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, if (smmu->features & ARM_SMMU_FEAT_STALLS && !master->stall_enabled) - dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD); + dst->data[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD); val |= (cd_table->cdtab_dma & STRTAB_STE_0_S1CTXPTR_MASK) | FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S1_TRANS) | @@ -1362,7 +1359,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, if (s2_cfg) { BUG_ON(ste_live); - dst[2] = cpu_to_le64( + dst->data[2] = cpu_to_le64( FIELD_PREP(STRTAB_STE_2_S2VMID, s2_cfg->vmid) | FIELD_PREP(STRTAB_STE_2_VTCR, s2_cfg->vtcr) | #ifdef __BIG_ENDIAN @@ -1371,18 +1368,18 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 | STRTAB_STE_2_S2R); - dst[3] = cpu_to_le64(s2_cfg->vttbr & STRTAB_STE_3_S2TTB_MASK); + dst->data[3] = cpu_to_le64(s2_cfg->vttbr & STRTAB_STE_3_S2TTB_MASK); val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S2_TRANS); } if (master->ats_enabled) - dst[1] |= cpu_to_le64(FIELD_PREP(STRTAB_STE_1_EATS, + dst->data[1] |= cpu_to_le64(FIELD_PREP(STRTAB_STE_1_EATS, STRTAB_STE_1_EATS_TRANS)); arm_smmu_sync_ste_for_sid(smmu, sid); /* See comment in arm_smmu_write_ctx_desc() */ - WRITE_ONCE(dst[0], cpu_to_le64(val)); + WRITE_ONCE(dst->data[0], cpu_to_le64(val)); arm_smmu_sync_ste_for_sid(smmu, sid); /* It's likely that we'll want to use the new STE soon */ @@ -1390,7 +1387,8 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, arm_smmu_cmdq_issue_cmd(smmu, &prefetch_cmd); } -static void arm_smmu_init_bypass_stes(__le64 *strtab, unsigned int nent, bool force) +static void arm_smmu_init_bypass_stes(struct arm_smmu_ste *strtab, + unsigned int nent, bool force) { unsigned int i; u64 val = STRTAB_STE_0_V; @@ -1401,11 +1399,11 @@ static void arm_smmu_init_bypass_stes(__le64 *strtab, unsigned int nent, bool fo val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS); for (i = 0; i < nent; ++i) { - strtab[0] = cpu_to_le64(val); - strtab[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG, - STRTAB_STE_1_SHCFG_INCOMING)); - strtab[2] = 0; - strtab += STRTAB_STE_DWORDS; + strtab->data[0] = cpu_to_le64(val); + strtab->data[1] = cpu_to_le64(FIELD_PREP( + STRTAB_STE_1_SHCFG, STRTAB_STE_1_SHCFG_INCOMING)); + strtab->data[2] = 0; + strtab++; } } @@ -2171,7 +2169,6 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain) fmt = ARM_64_LPAE_S1; finalise_stage_fn = arm_smmu_domain_finalise_s1; break; - case ARM_SMMU_DOMAIN_NESTED: case ARM_SMMU_DOMAIN_S2: ias = smmu->ias; oas = smmu->oas; @@ -2209,26 +2206,23 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain) return 0; } -static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid) +static struct arm_smmu_ste * +arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid) { - __le64 *step; struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) { - struct arm_smmu_strtab_l1_desc *l1_desc; - int idx; + unsigned int idx1, idx2; /* Two-level walk */ - idx = (sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS; - l1_desc = &cfg->l1_desc[idx]; - idx = (sid & ((1 << STRTAB_SPLIT) - 1)) * STRTAB_STE_DWORDS; - step = &l1_desc->l2ptr[idx]; + idx1 = (sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS; + idx2 = sid & ((1 << STRTAB_SPLIT) - 1); + return &cfg->l1_desc[idx1].l2ptr[idx2]; } else { /* Simple linear lookup */ - step = &cfg->strtab[sid * STRTAB_STE_DWORDS]; + return (struct arm_smmu_ste *)&cfg + ->strtab[sid * STRTAB_STE_DWORDS]; } - - return step; } static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master) @@ -2238,7 +2232,8 @@ static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master) for (i = 0; i < master->num_streams; ++i) { u32 sid = master->streams[i].id; - __le64 *step = arm_smmu_get_step_for_sid(smmu, sid); + struct arm_smmu_ste *step = + arm_smmu_get_step_for_sid(smmu, sid); /* Bridged PCI devices may end up with duplicated IDs */ for (j = 0; j < i; j++) @@ -2649,9 +2644,6 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev) struct arm_smmu_master *master; struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); - if (!fwspec || fwspec->ops != &arm_smmu_ops) - return ERR_PTR(-ENODEV); - if (WARN_ON_ONCE(dev_iommu_priv_get(dev))) return ERR_PTR(-EBUSY); @@ -2698,7 +2690,6 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev) err_free_master: kfree(master); - dev_iommu_priv_set(dev, NULL); return ERR_PTR(ret); } @@ -2742,7 +2733,7 @@ static int arm_smmu_enable_nesting(struct iommu_domain *domain) if (smmu_domain->smmu) ret = -EPERM; else - smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED; + smmu_domain->stage = ARM_SMMU_DOMAIN_S2; mutex_unlock(&smmu_domain->init_mutex); return ret; @@ -3769,7 +3760,7 @@ static void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu) iort_get_rmr_sids(dev_fwnode(smmu->dev), &rmr_list); list_for_each_entry(e, &rmr_list, list) { - __le64 *step; + struct arm_smmu_ste *step; struct iommu_iort_rmr_data *rmr; int ret, i; diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h index 925ac6a47bce..65fb388d5173 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h @@ -206,6 +206,11 @@ #define STRTAB_L1_DESC_L2PTR_MASK GENMASK_ULL(51, 6) #define STRTAB_STE_DWORDS 8 + +struct arm_smmu_ste { + __le64 data[STRTAB_STE_DWORDS]; +}; + #define STRTAB_STE_0_V (1UL << 0) #define STRTAB_STE_0_CFG GENMASK_ULL(3, 1) #define STRTAB_STE_0_CFG_ABORT 0 @@ -571,7 +576,7 @@ struct arm_smmu_priq { struct arm_smmu_strtab_l1_desc { u8 span; - __le64 *l2ptr; + struct arm_smmu_ste *l2ptr; dma_addr_t l2ptr_dma; }; @@ -710,7 +715,6 @@ struct arm_smmu_master { enum arm_smmu_domain_stage { ARM_SMMU_DOMAIN_S1 = 0, ARM_SMMU_DOMAIN_S2, - ARM_SMMU_DOMAIN_NESTED, ARM_SMMU_DOMAIN_BYPASS, }; diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index 549ae4dba3a6..8b04ece00420 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -243,8 +243,10 @@ static int qcom_adreno_smmu_init_context(struct arm_smmu_domain *smmu_domain, static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = { { .compatible = "qcom,adreno" }, + { .compatible = "qcom,adreno-gmu" }, { .compatible = "qcom,mdp4" }, { .compatible = "qcom,mdss" }, + { .compatible = "qcom,qcm2290-mdss" }, { .compatible = "qcom,sc7180-mdss" }, { .compatible = "qcom,sc7180-mss-pil" }, { .compatible = "qcom,sc7280-mdss" }, diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c index d6d1a2a55cc0..68b6bc5e7c71 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c @@ -82,6 +82,23 @@ static inline void arm_smmu_rpm_put(struct arm_smmu_device *smmu) pm_runtime_put_autosuspend(smmu->dev); } +static void arm_smmu_rpm_use_autosuspend(struct arm_smmu_device *smmu) +{ + /* + * Setup an autosuspend delay to avoid bouncing runpm state. + * Otherwise, if a driver for a suspended consumer device + * unmaps buffers, it will runpm resume/suspend for each one. + * + * For example, when used by a GPU device, when an application + * or game exits, it can trigger unmapping 100s or 1000s of + * buffers. With a runpm cycle for each buffer, that adds up + * to 5-10sec worth of reprogramming the context bank, while + * the system appears to be locked up to the user. + */ + pm_runtime_set_autosuspend_delay(smmu->dev, 20); + pm_runtime_use_autosuspend(smmu->dev); +} + static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom) { return container_of(dom, struct arm_smmu_domain, domain); @@ -392,8 +409,7 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev) { u32 fsr, fsynr, cbfrsynra; unsigned long iova; - struct iommu_domain *domain = dev; - struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); + struct arm_smmu_domain *smmu_domain = dev; struct arm_smmu_device *smmu = smmu_domain->smmu; int idx = smmu_domain->cfg.cbndx; int ret; @@ -406,7 +422,7 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev) iova = arm_smmu_cb_readq(smmu, idx, ARM_SMMU_CB_FAR); cbfrsynra = arm_smmu_gr1_read(smmu, ARM_SMMU_GR1_CBFRSYNRA(idx)); - ret = report_iommu_fault(domain, NULL, iova, + ret = report_iommu_fault(&smmu_domain->domain, NULL, iova, fsynr & ARM_SMMU_FSYNR0_WNR ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ); if (ret == -ENOSYS) @@ -607,7 +623,7 @@ static int arm_smmu_alloc_context_bank(struct arm_smmu_domain *smmu_domain, return __arm_smmu_alloc_bitmap(smmu->context_map, start, smmu->num_context_banks); } -static int arm_smmu_init_domain_context(struct iommu_domain *domain, +static int arm_smmu_init_domain_context(struct arm_smmu_domain *smmu_domain, struct arm_smmu_device *smmu, struct device *dev) { @@ -616,7 +632,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, struct io_pgtable_ops *pgtbl_ops; struct io_pgtable_cfg pgtbl_cfg; enum io_pgtable_fmt fmt; - struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); + struct iommu_domain *domain = &smmu_domain->domain; struct arm_smmu_cfg *cfg = &smmu_domain->cfg; irqreturn_t (*context_fault)(int irq, void *dev); @@ -624,12 +640,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, if (smmu_domain->smmu) goto out_unlock; - if (domain->type == IOMMU_DOMAIN_IDENTITY) { - smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS; - smmu_domain->smmu = smmu; - goto out_unlock; - } - /* * Mapping the requested stage onto what we support is surprisingly * complicated, mainly because the spec allows S1+S2 SMMUs without @@ -796,8 +806,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, else context_fault = arm_smmu_context_fault; - ret = devm_request_irq(smmu->dev, irq, context_fault, - IRQF_SHARED, "arm-smmu-context-fault", domain); + ret = devm_request_irq(smmu->dev, irq, context_fault, IRQF_SHARED, + "arm-smmu-context-fault", smmu_domain); if (ret < 0) { dev_err(smmu->dev, "failed to request context IRQ %d (%u)\n", cfg->irptndx, irq); @@ -818,14 +828,13 @@ out_unlock: return ret; } -static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) +static void arm_smmu_destroy_domain_context(struct arm_smmu_domain *smmu_domain) { - struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_device *smmu = smmu_domain->smmu; struct arm_smmu_cfg *cfg = &smmu_domain->cfg; int ret, irq; - if (!smmu || domain->type == IOMMU_DOMAIN_IDENTITY) + if (!smmu) return; ret = arm_smmu_rpm_get(smmu); @@ -841,7 +850,7 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) if (cfg->irptndx != ARM_SMMU_INVALID_IRPTNDX) { irq = smmu->irqs[cfg->irptndx]; - devm_free_irq(smmu->dev, irq, domain); + devm_free_irq(smmu->dev, irq, smmu_domain); } free_io_pgtable_ops(smmu_domain->pgtbl_ops); @@ -850,14 +859,10 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) arm_smmu_rpm_put(smmu); } -static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) +static struct iommu_domain *arm_smmu_domain_alloc_paging(struct device *dev) { struct arm_smmu_domain *smmu_domain; - if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_IDENTITY) { - if (using_legacy_binding || type != IOMMU_DOMAIN_DMA) - return NULL; - } /* * Allocate the domain and initialise some of its data structures. * We can't really do anything meaningful until we've added a @@ -870,6 +875,15 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) mutex_init(&smmu_domain->init_mutex); spin_lock_init(&smmu_domain->cb_lock); + if (dev) { + struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev); + + if (arm_smmu_init_domain_context(smmu_domain, cfg->smmu, dev)) { + kfree(smmu_domain); + return NULL; + } + } + return &smmu_domain->domain; } @@ -881,7 +895,7 @@ static void arm_smmu_domain_free(struct iommu_domain *domain) * Free the domain resources. We assume that all devices have * already been detached. */ - arm_smmu_destroy_domain_context(domain); + arm_smmu_destroy_domain_context(smmu_domain); kfree(smmu_domain); } @@ -1081,21 +1095,14 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg, mutex_unlock(&smmu->stream_map_mutex); } -static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain, - struct arm_smmu_master_cfg *cfg, - struct iommu_fwspec *fwspec) +static void arm_smmu_master_install_s2crs(struct arm_smmu_master_cfg *cfg, + enum arm_smmu_s2cr_type type, + u8 cbndx, struct iommu_fwspec *fwspec) { - struct arm_smmu_device *smmu = smmu_domain->smmu; + struct arm_smmu_device *smmu = cfg->smmu; struct arm_smmu_s2cr *s2cr = smmu->s2crs; - u8 cbndx = smmu_domain->cfg.cbndx; - enum arm_smmu_s2cr_type type; int i, idx; - if (smmu_domain->stage == ARM_SMMU_DOMAIN_BYPASS) - type = S2CR_TYPE_BYPASS; - else - type = S2CR_TYPE_TRANS; - for_each_cfg_sme(cfg, fwspec, i, idx) { if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx) continue; @@ -1105,7 +1112,6 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain, s2cr[idx].cbndx = cbndx; arm_smmu_write_s2cr(smmu, idx); } - return 0; } static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) @@ -1116,11 +1122,6 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) struct arm_smmu_device *smmu; int ret; - if (!fwspec || fwspec->ops != &arm_smmu_ops) { - dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n"); - return -ENXIO; - } - /* * FIXME: The arch/arm DMA API code tries to attach devices to its own * domains between of_xlate() and probe_device() - we have no way to cope @@ -1139,7 +1140,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) return ret; /* Ensure that the domain is finalised */ - ret = arm_smmu_init_domain_context(domain, smmu, dev); + ret = arm_smmu_init_domain_context(smmu_domain, smmu, dev); if (ret < 0) goto rpm_put; @@ -1153,27 +1154,66 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) } /* Looks ok, so add the device to the domain */ - ret = arm_smmu_domain_add_master(smmu_domain, cfg, fwspec); - - /* - * Setup an autosuspend delay to avoid bouncing runpm state. - * Otherwise, if a driver for a suspended consumer device - * unmaps buffers, it will runpm resume/suspend for each one. - * - * For example, when used by a GPU device, when an application - * or game exits, it can trigger unmapping 100s or 1000s of - * buffers. With a runpm cycle for each buffer, that adds up - * to 5-10sec worth of reprogramming the context bank, while - * the system appears to be locked up to the user. - */ - pm_runtime_set_autosuspend_delay(smmu->dev, 20); - pm_runtime_use_autosuspend(smmu->dev); - + arm_smmu_master_install_s2crs(cfg, S2CR_TYPE_TRANS, + smmu_domain->cfg.cbndx, fwspec); + arm_smmu_rpm_use_autosuspend(smmu); rpm_put: arm_smmu_rpm_put(smmu); return ret; } +static int arm_smmu_attach_dev_type(struct device *dev, + enum arm_smmu_s2cr_type type) +{ + struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev); + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + struct arm_smmu_device *smmu; + int ret; + + if (!cfg) + return -ENODEV; + smmu = cfg->smmu; + + ret = arm_smmu_rpm_get(smmu); + if (ret < 0) + return ret; + + arm_smmu_master_install_s2crs(cfg, type, 0, fwspec); + arm_smmu_rpm_use_autosuspend(smmu); + arm_smmu_rpm_put(smmu); + return 0; +} + +static int arm_smmu_attach_dev_identity(struct iommu_domain *domain, + struct device *dev) +{ + return arm_smmu_attach_dev_type(dev, S2CR_TYPE_BYPASS); +} + +static const struct iommu_domain_ops arm_smmu_identity_ops = { + .attach_dev = arm_smmu_attach_dev_identity, +}; + +static struct iommu_domain arm_smmu_identity_domain = { + .type = IOMMU_DOMAIN_IDENTITY, + .ops = &arm_smmu_identity_ops, +}; + +static int arm_smmu_attach_dev_blocked(struct iommu_domain *domain, + struct device *dev) +{ + return arm_smmu_attach_dev_type(dev, S2CR_TYPE_FAULT); +} + +static const struct iommu_domain_ops arm_smmu_blocked_ops = { + .attach_dev = arm_smmu_attach_dev_blocked, +}; + +static struct iommu_domain arm_smmu_blocked_domain = { + .type = IOMMU_DOMAIN_BLOCKED, + .ops = &arm_smmu_blocked_ops, +}; + static int arm_smmu_map_pages(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t pgsize, size_t pgcount, int prot, gfp_t gfp, size_t *mapped) @@ -1357,10 +1397,8 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev) fwspec = dev_iommu_fwspec_get(dev); if (ret) goto out_free; - } else if (fwspec && fwspec->ops == &arm_smmu_ops) { - smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode); } else { - return ERR_PTR(-ENODEV); + smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode); } ret = -EINVAL; @@ -1427,7 +1465,6 @@ static void arm_smmu_release_device(struct device *dev) arm_smmu_rpm_put(cfg->smmu); - dev_iommu_priv_set(dev, NULL); kfree(cfg); } @@ -1560,8 +1597,10 @@ static int arm_smmu_def_domain_type(struct device *dev) } static struct iommu_ops arm_smmu_ops = { + .identity_domain = &arm_smmu_identity_domain, + .blocked_domain = &arm_smmu_blocked_domain, .capable = arm_smmu_capable, - .domain_alloc = arm_smmu_domain_alloc, + .domain_alloc_paging = arm_smmu_domain_alloc_paging, .probe_device = arm_smmu_probe_device, .release_device = arm_smmu_release_device, .probe_finalize = arm_smmu_probe_finalize, @@ -2161,7 +2200,8 @@ static int arm_smmu_device_probe(struct platform_device *pdev) return err; } - err = iommu_device_register(&smmu->iommu, &arm_smmu_ops, dev); + err = iommu_device_register(&smmu->iommu, &arm_smmu_ops, + using_legacy_binding ? NULL : dev); if (err) { dev_err(dev, "Failed to register iommu\n"); iommu_device_sysfs_remove(&smmu->iommu); diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.h b/drivers/iommu/arm/arm-smmu/arm-smmu.h index 703fd5817ec1..836ed6799a80 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu.h +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.h @@ -361,7 +361,6 @@ enum arm_smmu_domain_stage { ARM_SMMU_DOMAIN_S1 = 0, ARM_SMMU_DOMAIN_S2, ARM_SMMU_DOMAIN_NESTED, - ARM_SMMU_DOMAIN_BYPASS, }; struct arm_smmu_domain { diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c index 97b2122032b2..17a1c163fef6 100644 --- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c +++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c @@ -79,16 +79,6 @@ static struct qcom_iommu_domain *to_qcom_iommu_domain(struct iommu_domain *dom) static const struct iommu_ops qcom_iommu_ops; -static struct qcom_iommu_dev * to_iommu(struct device *dev) -{ - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); - - if (!fwspec || fwspec->ops != &qcom_iommu_ops) - return NULL; - - return dev_iommu_priv_get(dev); -} - static struct qcom_iommu_ctx * to_ctx(struct qcom_iommu_domain *d, unsigned asid) { struct qcom_iommu_dev *qcom_iommu = d->iommu; @@ -372,7 +362,7 @@ static void qcom_iommu_domain_free(struct iommu_domain *domain) static int qcom_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) { - struct qcom_iommu_dev *qcom_iommu = to_iommu(dev); + struct qcom_iommu_dev *qcom_iommu = dev_iommu_priv_get(dev); struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); int ret; @@ -404,7 +394,7 @@ static int qcom_iommu_identity_attach(struct iommu_domain *identity_domain, struct iommu_domain *domain = iommu_get_domain_for_dev(dev); struct qcom_iommu_domain *qcom_domain; struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); - struct qcom_iommu_dev *qcom_iommu = to_iommu(dev); + struct qcom_iommu_dev *qcom_iommu = dev_iommu_priv_get(dev); unsigned int i; if (domain == identity_domain || !domain) @@ -535,7 +525,7 @@ static bool qcom_iommu_capable(struct device *dev, enum iommu_cap cap) static struct iommu_device *qcom_iommu_probe_device(struct device *dev) { - struct qcom_iommu_dev *qcom_iommu = to_iommu(dev); + struct qcom_iommu_dev *qcom_iommu = dev_iommu_priv_get(dev); struct device_link *link; if (!qcom_iommu) @@ -900,8 +890,16 @@ static void qcom_iommu_device_remove(struct platform_device *pdev) static int __maybe_unused qcom_iommu_resume(struct device *dev) { struct qcom_iommu_dev *qcom_iommu = dev_get_drvdata(dev); + int ret; + + ret = clk_bulk_prepare_enable(CLK_NUM, qcom_iommu->clks); + if (ret < 0) + return ret; + + if (dev->pm_domain) + return qcom_scm_restore_sec_cfg(qcom_iommu->sec_id, 0); - return clk_bulk_prepare_enable(CLK_NUM, qcom_iommu->clks); + return ret; } static int __maybe_unused qcom_iommu_suspend(struct device *dev) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index e59f50e11ea8..50ccc4f1ef81 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -29,6 +29,7 @@ #include <linux/spinlock.h> #include <linux/swiotlb.h> #include <linux/vmalloc.h> +#include <trace/events/swiotlb.h> #include "dma-iommu.h" @@ -1156,6 +1157,8 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page, return DMA_MAPPING_ERROR; } + trace_swiotlb_bounced(dev, phys, size); + aligned_size = iova_align(iovad, size); phys = swiotlb_tbl_map_single(dev, phys, size, aligned_size, iova_mask(iovad), dir, attrs); diff --git a/drivers/iommu/intel/debugfs.c b/drivers/iommu/intel/debugfs.c index dee61e513be6..86b506af7daa 100644 --- a/drivers/iommu/intel/debugfs.c +++ b/drivers/iommu/intel/debugfs.c @@ -106,9 +106,6 @@ static const struct iommu_regset iommu_regs_64[] = { IOMMU_REGSET_ENTRY(MTRR_PHYSMASK8), IOMMU_REGSET_ENTRY(MTRR_PHYSBASE9), IOMMU_REGSET_ENTRY(MTRR_PHYSMASK9), - IOMMU_REGSET_ENTRY(VCCAP), - IOMMU_REGSET_ENTRY(VCMD), - IOMMU_REGSET_ENTRY(VCRSP), }; static struct dentry *intel_iommu_debug; diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 897159dba47d..6fb5f6fceea1 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -46,9 +46,6 @@ #define DEFAULT_DOMAIN_ADDRESS_WIDTH 57 -#define MAX_AGAW_WIDTH 64 -#define MAX_AGAW_PFN_WIDTH (MAX_AGAW_WIDTH - VTD_PAGE_SHIFT) - #define __DOMAIN_MAX_PFN(gaw) ((((uint64_t)1) << ((gaw) - VTD_PAGE_SHIFT)) - 1) #define __DOMAIN_MAX_ADDR(gaw) ((((uint64_t)1) << (gaw)) - 1) @@ -63,74 +60,6 @@ #define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT) -/* page table handling */ -#define LEVEL_STRIDE (9) -#define LEVEL_MASK (((u64)1 << LEVEL_STRIDE) - 1) - -static inline int agaw_to_level(int agaw) -{ - return agaw + 2; -} - -static inline int agaw_to_width(int agaw) -{ - return min_t(int, 30 + agaw * LEVEL_STRIDE, MAX_AGAW_WIDTH); -} - -static inline int width_to_agaw(int width) -{ - return DIV_ROUND_UP(width - 30, LEVEL_STRIDE); -} - -static inline unsigned int level_to_offset_bits(int level) -{ - return (level - 1) * LEVEL_STRIDE; -} - -static inline int pfn_level_offset(u64 pfn, int level) -{ - return (pfn >> level_to_offset_bits(level)) & LEVEL_MASK; -} - -static inline u64 level_mask(int level) -{ - return -1ULL << level_to_offset_bits(level); -} - -static inline u64 level_size(int level) -{ - return 1ULL << level_to_offset_bits(level); -} - -static inline u64 align_to_level(u64 pfn, int level) -{ - return (pfn + level_size(level) - 1) & level_mask(level); -} - -static inline unsigned long lvl_to_nr_pages(unsigned int lvl) -{ - return 1UL << min_t(int, (lvl - 1) * LEVEL_STRIDE, MAX_AGAW_PFN_WIDTH); -} - -/* VT-d pages must always be _smaller_ than MM pages. Otherwise things - are never going to work. */ -static inline unsigned long mm_to_dma_pfn_start(unsigned long mm_pfn) -{ - return mm_pfn << (PAGE_SHIFT - VTD_PAGE_SHIFT); -} -static inline unsigned long mm_to_dma_pfn_end(unsigned long mm_pfn) -{ - return ((mm_pfn + 1) << (PAGE_SHIFT - VTD_PAGE_SHIFT)) - 1; -} -static inline unsigned long page_to_dma_pfn(struct page *pg) -{ - return mm_to_dma_pfn_start(page_to_pfn(pg)); -} -static inline unsigned long virt_to_dma_pfn(void *p) -{ - return page_to_dma_pfn(virt_to_page(p)); -} - static void __init check_tylersburg_isoch(void); static int rwbf_quirk; @@ -168,78 +97,6 @@ static phys_addr_t root_entry_uctp(struct root_entry *re) return re->hi & VTD_PAGE_MASK; } -static inline void context_set_present(struct context_entry *context) -{ - context->lo |= 1; -} - -static inline void context_set_fault_enable(struct context_entry *context) -{ - context->lo &= (((u64)-1) << 2) | 1; -} - -static inline void context_set_translation_type(struct context_entry *context, - unsigned long value) -{ - context->lo &= (((u64)-1) << 4) | 3; - context->lo |= (value & 3) << 2; -} - -static inline void context_set_address_root(struct context_entry *context, - unsigned long value) -{ - context->lo &= ~VTD_PAGE_MASK; - context->lo |= value & VTD_PAGE_MASK; -} - -static inline void context_set_address_width(struct context_entry *context, - unsigned long value) -{ - context->hi |= value & 7; -} - -static inline void context_set_domain_id(struct context_entry *context, - unsigned long value) -{ - context->hi |= (value & ((1 << 16) - 1)) << 8; -} - -static inline void context_set_pasid(struct context_entry *context) -{ - context->lo |= CONTEXT_PASIDE; -} - -static inline int context_domain_id(struct context_entry *c) -{ - return((c->hi >> 8) & 0xffff); -} - -static inline void context_clear_entry(struct context_entry *context) -{ - context->lo = 0; - context->hi = 0; -} - -static inline bool context_copied(struct intel_iommu *iommu, u8 bus, u8 devfn) -{ - if (!iommu->copied_tables) - return false; - - return test_bit(((long)bus << 8) | devfn, iommu->copied_tables); -} - -static inline void -set_context_copied(struct intel_iommu *iommu, u8 bus, u8 devfn) -{ - set_bit(((long)bus << 8) | devfn, iommu->copied_tables); -} - -static inline void -clear_context_copied(struct intel_iommu *iommu, u8 bus, u8 devfn) -{ - clear_bit(((long)bus << 8) | devfn, iommu->copied_tables); -} - /* * This domain is a statically identity mapping domain. * 1. This domain creats a static 1:1 mapping to all usable memory. @@ -383,13 +240,12 @@ void free_pgtable_page(void *vaddr) free_page((unsigned long)vaddr); } -static inline int domain_type_is_si(struct dmar_domain *domain) +static int domain_type_is_si(struct dmar_domain *domain) { return domain->domain.type == IOMMU_DOMAIN_IDENTITY; } -static inline int domain_pfn_supported(struct dmar_domain *domain, - unsigned long pfn) +static int domain_pfn_supported(struct dmar_domain *domain, unsigned long pfn) { int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; @@ -451,7 +307,7 @@ int iommu_calculate_agaw(struct intel_iommu *iommu) return __iommu_calculate_agaw(iommu, DEFAULT_DOMAIN_ADDRESS_WIDTH); } -static inline bool iommu_paging_structure_coherency(struct intel_iommu *iommu) +static bool iommu_paging_structure_coherency(struct intel_iommu *iommu) { return sm_supported(iommu) ? ecap_smpwc(iommu->ecap) : ecap_coherent(iommu->ecap); @@ -703,7 +559,7 @@ static bool iommu_is_dummy(struct intel_iommu *iommu, struct device *dev) return false; } -struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn) +static struct intel_iommu *device_lookup_iommu(struct device *dev, u8 *bus, u8 *devfn) { struct dmar_drhd_unit *drhd = NULL; struct pci_dev *pdev = NULL; @@ -1574,9 +1430,8 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, } /* Notification for newly created mappings */ -static inline void __mapping_notify_one(struct intel_iommu *iommu, - struct dmar_domain *domain, - unsigned long pfn, unsigned int pages) +static void __mapping_notify_one(struct intel_iommu *iommu, struct dmar_domain *domain, + unsigned long pfn, unsigned int pages) { /* * It's a non-present to present mapping. Only flush if caching mode @@ -1843,7 +1698,7 @@ void domain_detach_iommu(struct dmar_domain *domain, struct intel_iommu *iommu) spin_unlock(&iommu->lock); } -static inline int guestwidth_to_adjustwidth(int gaw) +static int guestwidth_to_adjustwidth(int gaw) { int agaw; int r = (gaw - 12) % 9; @@ -1877,7 +1732,7 @@ static void domain_exit(struct dmar_domain *domain) * Value of X in the PDTS field of a scalable mode context entry * indicates PASID directory with 2^(X + 7) entries. */ -static inline unsigned long context_get_sm_pds(struct pasid_table *table) +static unsigned long context_get_sm_pds(struct pasid_table *table) { unsigned long pds, max_pde; @@ -1889,38 +1744,6 @@ static inline unsigned long context_get_sm_pds(struct pasid_table *table) return pds - 7; } -/* - * Set the RID_PASID field of a scalable mode context entry. The - * IOMMU hardware will use the PASID value set in this field for - * DMA translations of DMA requests without PASID. - */ -static inline void -context_set_sm_rid2pasid(struct context_entry *context, unsigned long pasid) -{ - context->hi |= pasid & ((1 << 20) - 1); -} - -/* - * Set the DTE(Device-TLB Enable) field of a scalable mode context - * entry. - */ -static inline void context_set_sm_dte(struct context_entry *context) -{ - context->lo |= BIT_ULL(2); -} - -/* - * Set the PRE(Page Request Enable) field of a scalable mode context - * entry. - */ -static inline void context_set_sm_pre(struct context_entry *context) -{ - context->lo |= BIT_ULL(4); -} - -/* Convert value to context PASID directory size field coding. */ -#define context_pdts(pds) (((pds) & 0x7) << 9) - static int domain_context_mapping_one(struct dmar_domain *domain, struct intel_iommu *iommu, struct pasid_table *table, @@ -2081,14 +1904,11 @@ static int domain_context_mapping_cb(struct pci_dev *pdev, static int domain_context_mapping(struct dmar_domain *domain, struct device *dev) { + struct device_domain_info *info = dev_iommu_priv_get(dev); struct domain_context_mapping_data data; + struct intel_iommu *iommu = info->iommu; + u8 bus = info->bus, devfn = info->devfn; struct pasid_table *table; - struct intel_iommu *iommu; - u8 bus, devfn; - - iommu = device_to_iommu(dev, &bus, &devfn); - if (!iommu) - return -ENODEV; table = intel_pasid_get_table(dev); @@ -2105,18 +1925,15 @@ domain_context_mapping(struct dmar_domain *domain, struct device *dev) } /* Returns a number of VTD pages, but aligned to MM page size */ -static inline unsigned long aligned_nrpages(unsigned long host_addr, - size_t size) +static unsigned long aligned_nrpages(unsigned long host_addr, size_t size) { host_addr &= ~PAGE_MASK; return PAGE_ALIGN(host_addr + size) >> VTD_PAGE_SHIFT; } /* Return largest possible superpage level for a given mapping */ -static inline int hardware_largepage_caps(struct dmar_domain *domain, - unsigned long iov_pfn, - unsigned long phy_pfn, - unsigned long pages) +static int hardware_largepage_caps(struct dmar_domain *domain, unsigned long iov_pfn, + unsigned long phy_pfn, unsigned long pages) { int support, level = 1; unsigned long pfnmerge; @@ -2449,15 +2266,10 @@ static int dmar_domain_attach_device(struct dmar_domain *domain, struct device *dev) { struct device_domain_info *info = dev_iommu_priv_get(dev); - struct intel_iommu *iommu; + struct intel_iommu *iommu = info->iommu; unsigned long flags; - u8 bus, devfn; int ret; - iommu = device_to_iommu(dev, &bus, &devfn); - if (!iommu) - return -ENODEV; - ret = domain_attach_iommu(domain, iommu); if (ret) return ret; @@ -2470,7 +2282,7 @@ static int dmar_domain_attach_device(struct dmar_domain *domain, if (sm_supported(iommu) && !dev_is_real_dma_subdevice(dev)) { /* Setup the PASID entry for requests without PASID: */ if (hw_pass_through && domain_type_is_si(domain)) - ret = intel_pasid_setup_pass_through(iommu, domain, + ret = intel_pasid_setup_pass_through(iommu, dev, IOMMU_NO_PASID); else if (domain->use_first_level) ret = domain_setup_first_level(iommu, domain, dev, @@ -3615,7 +3427,7 @@ void intel_iommu_shutdown(void) up_write(&dmar_global_lock); } -static inline struct intel_iommu *dev_to_intel_iommu(struct device *dev) +static struct intel_iommu *dev_to_intel_iommu(struct device *dev) { struct iommu_device *iommu_dev = dev_to_iommu_device(dev); @@ -3694,7 +3506,7 @@ const struct attribute_group *intel_iommu_groups[] = { NULL, }; -static inline bool has_external_pci(void) +static bool has_external_pci(void) { struct pci_dev *pdev = NULL; @@ -4119,14 +3931,11 @@ static void intel_iommu_domain_free(struct iommu_domain *domain) int prepare_domain_attach_device(struct iommu_domain *domain, struct device *dev) { + struct device_domain_info *info = dev_iommu_priv_get(dev); struct dmar_domain *dmar_domain = to_dmar_domain(domain); - struct intel_iommu *iommu; + struct intel_iommu *iommu = info->iommu; int addr_width; - iommu = device_to_iommu(dev, NULL, NULL); - if (!iommu) - return -ENODEV; - if (dmar_domain->force_snooping && !ecap_sc_support(iommu->ecap)) return -EINVAL; @@ -4403,7 +4212,7 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev) u8 bus, devfn; int ret; - iommu = device_to_iommu(dev, &bus, &devfn); + iommu = device_lookup_iommu(dev, &bus, &devfn); if (!iommu || !iommu->iommu.ops) return ERR_PTR(-ENODEV); @@ -4461,7 +4270,6 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev) ret = intel_pasid_alloc_table(dev); if (ret) { dev_err(dev, "PASID table allocation failed\n"); - dev_iommu_priv_set(dev, NULL); kfree(info); return ERR_PTR(ret); } @@ -4479,7 +4287,6 @@ static void intel_iommu_release_device(struct device *dev) dmar_remove_one_dev_info(dev); intel_pasid_free_table(dev); intel_iommu_debugfs_remove_dev(info); - dev_iommu_priv_set(dev, NULL); kfree(info); set_dma_ops(dev, NULL); } @@ -4739,8 +4546,9 @@ static int intel_iommu_iotlb_sync_map(struct iommu_domain *domain, static void intel_iommu_remove_dev_pasid(struct device *dev, ioasid_t pasid) { - struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL); + struct device_domain_info *info = dev_iommu_priv_get(dev); struct dev_pasid_info *curr, *dev_pasid = NULL; + struct intel_iommu *iommu = info->iommu; struct dmar_domain *dmar_domain; struct iommu_domain *domain; unsigned long flags; @@ -4811,8 +4619,7 @@ static int intel_iommu_set_dev_pasid(struct iommu_domain *domain, goto out_free; if (domain_type_is_si(dmar_domain)) - ret = intel_pasid_setup_pass_through(iommu, dmar_domain, - dev, pasid); + ret = intel_pasid_setup_pass_through(iommu, dev, pasid); else if (dmar_domain->use_first_level) ret = domain_setup_first_level(iommu, dmar_domain, dev, pasid); diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h index ce030c5b5772..d02f916d8e59 100644 --- a/drivers/iommu/intel/iommu.h +++ b/drivers/iommu/intel/iommu.h @@ -140,9 +140,6 @@ #define DMAR_ECEO_REG 0x408 #define DMAR_ECRSP_REG 0x410 #define DMAR_ECCAP_REG 0x430 -#define DMAR_VCCAP_REG 0xe30 /* Virtual command capability register */ -#define DMAR_VCMD_REG 0xe00 /* Virtual command register */ -#define DMAR_VCRSP_REG 0xe10 /* Virtual command response register */ #define DMAR_IQER_REG_IQEI(reg) FIELD_GET(GENMASK_ULL(3, 0), reg) #define DMAR_IQER_REG_ITESID(reg) FIELD_GET(GENMASK_ULL(47, 32), reg) @@ -854,6 +851,181 @@ static inline bool context_present(struct context_entry *context) return (context->lo & 1); } +#define LEVEL_STRIDE (9) +#define LEVEL_MASK (((u64)1 << LEVEL_STRIDE) - 1) +#define MAX_AGAW_WIDTH (64) +#define MAX_AGAW_PFN_WIDTH (MAX_AGAW_WIDTH - VTD_PAGE_SHIFT) + +static inline int agaw_to_level(int agaw) +{ + return agaw + 2; +} + +static inline int agaw_to_width(int agaw) +{ + return min_t(int, 30 + agaw * LEVEL_STRIDE, MAX_AGAW_WIDTH); +} + +static inline int width_to_agaw(int width) +{ + return DIV_ROUND_UP(width - 30, LEVEL_STRIDE); +} + +static inline unsigned int level_to_offset_bits(int level) +{ + return (level - 1) * LEVEL_STRIDE; +} + +static inline int pfn_level_offset(u64 pfn, int level) +{ + return (pfn >> level_to_offset_bits(level)) & LEVEL_MASK; +} + +static inline u64 level_mask(int level) +{ + return -1ULL << level_to_offset_bits(level); +} + +static inline u64 level_size(int level) +{ + return 1ULL << level_to_offset_bits(level); +} + +static inline u64 align_to_level(u64 pfn, int level) +{ + return (pfn + level_size(level) - 1) & level_mask(level); +} + +static inline unsigned long lvl_to_nr_pages(unsigned int lvl) +{ + return 1UL << min_t(int, (lvl - 1) * LEVEL_STRIDE, MAX_AGAW_PFN_WIDTH); +} + +/* VT-d pages must always be _smaller_ than MM pages. Otherwise things + are never going to work. */ +static inline unsigned long mm_to_dma_pfn_start(unsigned long mm_pfn) +{ + return mm_pfn << (PAGE_SHIFT - VTD_PAGE_SHIFT); +} +static inline unsigned long mm_to_dma_pfn_end(unsigned long mm_pfn) +{ + return ((mm_pfn + 1) << (PAGE_SHIFT - VTD_PAGE_SHIFT)) - 1; +} +static inline unsigned long page_to_dma_pfn(struct page *pg) +{ + return mm_to_dma_pfn_start(page_to_pfn(pg)); +} +static inline unsigned long virt_to_dma_pfn(void *p) +{ + return page_to_dma_pfn(virt_to_page(p)); +} + +static inline void context_set_present(struct context_entry *context) +{ + context->lo |= 1; +} + +static inline void context_set_fault_enable(struct context_entry *context) +{ + context->lo &= (((u64)-1) << 2) | 1; +} + +static inline void context_set_translation_type(struct context_entry *context, + unsigned long value) +{ + context->lo &= (((u64)-1) << 4) | 3; + context->lo |= (value & 3) << 2; +} + +static inline void context_set_address_root(struct context_entry *context, + unsigned long value) +{ + context->lo &= ~VTD_PAGE_MASK; + context->lo |= value & VTD_PAGE_MASK; +} + +static inline void context_set_address_width(struct context_entry *context, + unsigned long value) +{ + context->hi |= value & 7; +} + +static inline void context_set_domain_id(struct context_entry *context, + unsigned long value) +{ + context->hi |= (value & ((1 << 16) - 1)) << 8; +} + +static inline void context_set_pasid(struct context_entry *context) +{ + context->lo |= CONTEXT_PASIDE; +} + +static inline int context_domain_id(struct context_entry *c) +{ + return((c->hi >> 8) & 0xffff); +} + +static inline void context_clear_entry(struct context_entry *context) +{ + context->lo = 0; + context->hi = 0; +} + +#ifdef CONFIG_INTEL_IOMMU +static inline bool context_copied(struct intel_iommu *iommu, u8 bus, u8 devfn) +{ + if (!iommu->copied_tables) + return false; + + return test_bit(((long)bus << 8) | devfn, iommu->copied_tables); +} + +static inline void +set_context_copied(struct intel_iommu *iommu, u8 bus, u8 devfn) +{ + set_bit(((long)bus << 8) | devfn, iommu->copied_tables); +} + +static inline void +clear_context_copied(struct intel_iommu *iommu, u8 bus, u8 devfn) +{ + clear_bit(((long)bus << 8) | devfn, iommu->copied_tables); +} +#endif /* CONFIG_INTEL_IOMMU */ + +/* + * Set the RID_PASID field of a scalable mode context entry. The + * IOMMU hardware will use the PASID value set in this field for + * DMA translations of DMA requests without PASID. + */ +static inline void +context_set_sm_rid2pasid(struct context_entry *context, unsigned long pasid) +{ + context->hi |= pasid & ((1 << 20) - 1); +} + +/* + * Set the DTE(Device-TLB Enable) field of a scalable mode context + * entry. + */ +static inline void context_set_sm_dte(struct context_entry *context) +{ + context->lo |= BIT_ULL(2); +} + +/* + * Set the PRE(Page Request Enable) field of a scalable mode context + * entry. + */ +static inline void context_set_sm_pre(struct context_entry *context) +{ + context->lo |= BIT_ULL(4); +} + +/* Convert value to context PASID directory size field coding. */ +#define context_pdts(pds) (((pds) & 0x7) << 9) + struct dmar_drhd_unit *dmar_find_matched_drhd_unit(struct pci_dev *dev); int dmar_enable_qi(struct intel_iommu *iommu); @@ -900,7 +1072,6 @@ int dmar_ir_support(void); void *alloc_pgtable_page(int node, gfp_t gfp); void free_pgtable_page(void *vaddr); void iommu_flush_write_buffer(struct intel_iommu *iommu); -struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn); struct iommu_domain *intel_nested_domain_alloc(struct iommu_domain *parent, const struct iommu_user_data *user_data); diff --git a/drivers/iommu/intel/nested.c b/drivers/iommu/intel/nested.c index b5a5563ab32c..f26c7f1c46cc 100644 --- a/drivers/iommu/intel/nested.c +++ b/drivers/iommu/intel/nested.c @@ -73,9 +73,97 @@ static void intel_nested_domain_free(struct iommu_domain *domain) kfree(to_dmar_domain(domain)); } +static void nested_flush_dev_iotlb(struct dmar_domain *domain, u64 addr, + unsigned int mask) +{ + struct device_domain_info *info; + unsigned long flags; + u16 sid, qdep; + + spin_lock_irqsave(&domain->lock, flags); + list_for_each_entry(info, &domain->devices, link) { + if (!info->ats_enabled) + continue; + sid = info->bus << 8 | info->devfn; + qdep = info->ats_qdep; + qi_flush_dev_iotlb(info->iommu, sid, info->pfsid, + qdep, addr, mask); + quirk_extra_dev_tlb_flush(info, addr, mask, + IOMMU_NO_PASID, qdep); + } + spin_unlock_irqrestore(&domain->lock, flags); +} + +static void intel_nested_flush_cache(struct dmar_domain *domain, u64 addr, + unsigned long npages, bool ih) +{ + struct iommu_domain_info *info; + unsigned int mask; + unsigned long i; + + xa_for_each(&domain->iommu_array, i, info) + qi_flush_piotlb(info->iommu, + domain_id_iommu(domain, info->iommu), + IOMMU_NO_PASID, addr, npages, ih); + + if (!domain->has_iotlb_device) + return; + + if (npages == U64_MAX) + mask = 64 - VTD_PAGE_SHIFT; + else + mask = ilog2(__roundup_pow_of_two(npages)); + + nested_flush_dev_iotlb(domain, addr, mask); +} + +static int intel_nested_cache_invalidate_user(struct iommu_domain *domain, + struct iommu_user_data_array *array) +{ + struct dmar_domain *dmar_domain = to_dmar_domain(domain); + struct iommu_hwpt_vtd_s1_invalidate inv_entry; + u32 index, processed = 0; + int ret = 0; + + if (array->type != IOMMU_HWPT_INVALIDATE_DATA_VTD_S1) { + ret = -EINVAL; + goto out; + } + + for (index = 0; index < array->entry_num; index++) { + ret = iommu_copy_struct_from_user_array(&inv_entry, array, + IOMMU_HWPT_INVALIDATE_DATA_VTD_S1, + index, __reserved); + if (ret) + break; + + if ((inv_entry.flags & ~IOMMU_VTD_INV_FLAGS_LEAF) || + inv_entry.__reserved) { + ret = -EOPNOTSUPP; + break; + } + + if (!IS_ALIGNED(inv_entry.addr, VTD_PAGE_SIZE) || + ((inv_entry.npages == U64_MAX) && inv_entry.addr)) { + ret = -EINVAL; + break; + } + + intel_nested_flush_cache(dmar_domain, inv_entry.addr, + inv_entry.npages, + inv_entry.flags & IOMMU_VTD_INV_FLAGS_LEAF); + processed++; + } + +out: + array->entry_num = processed; + return ret; +} + static const struct iommu_domain_ops intel_nested_domain_ops = { .attach_dev = intel_nested_attach_dev, .free = intel_nested_domain_free, + .cache_invalidate_user = intel_nested_cache_invalidate_user, }; struct iommu_domain *intel_nested_domain_alloc(struct iommu_domain *parent, diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c index 74e8e4c17e81..3239cefa4c33 100644 --- a/drivers/iommu/intel/pasid.c +++ b/drivers/iommu/intel/pasid.c @@ -26,63 +26,6 @@ */ u32 intel_pasid_max_id = PASID_MAX; -int vcmd_alloc_pasid(struct intel_iommu *iommu, u32 *pasid) -{ - unsigned long flags; - u8 status_code; - int ret = 0; - u64 res; - - raw_spin_lock_irqsave(&iommu->register_lock, flags); - dmar_writeq(iommu->reg + DMAR_VCMD_REG, VCMD_CMD_ALLOC); - IOMMU_WAIT_OP(iommu, DMAR_VCRSP_REG, dmar_readq, - !(res & VCMD_VRSP_IP), res); - raw_spin_unlock_irqrestore(&iommu->register_lock, flags); - - status_code = VCMD_VRSP_SC(res); - switch (status_code) { - case VCMD_VRSP_SC_SUCCESS: - *pasid = VCMD_VRSP_RESULT_PASID(res); - break; - case VCMD_VRSP_SC_NO_PASID_AVAIL: - pr_info("IOMMU: %s: No PASID available\n", iommu->name); - ret = -ENOSPC; - break; - default: - ret = -ENODEV; - pr_warn("IOMMU: %s: Unexpected error code %d\n", - iommu->name, status_code); - } - - return ret; -} - -void vcmd_free_pasid(struct intel_iommu *iommu, u32 pasid) -{ - unsigned long flags; - u8 status_code; - u64 res; - - raw_spin_lock_irqsave(&iommu->register_lock, flags); - dmar_writeq(iommu->reg + DMAR_VCMD_REG, - VCMD_CMD_OPERAND(pasid) | VCMD_CMD_FREE); - IOMMU_WAIT_OP(iommu, DMAR_VCRSP_REG, dmar_readq, - !(res & VCMD_VRSP_IP), res); - raw_spin_unlock_irqrestore(&iommu->register_lock, flags); - - status_code = VCMD_VRSP_SC(res); - switch (status_code) { - case VCMD_VRSP_SC_SUCCESS: - break; - case VCMD_VRSP_SC_INVALID_PASID: - pr_info("IOMMU: %s: Invalid PASID\n", iommu->name); - break; - default: - pr_warn("IOMMU: %s: Unexpected error code %d\n", - iommu->name, status_code); - } -} - /* * Per device pasid table management: */ @@ -230,30 +173,6 @@ retry: /* * Interfaces for PASID table entry manipulation: */ -static inline void pasid_clear_entry(struct pasid_entry *pe) -{ - WRITE_ONCE(pe->val[0], 0); - WRITE_ONCE(pe->val[1], 0); - WRITE_ONCE(pe->val[2], 0); - WRITE_ONCE(pe->val[3], 0); - WRITE_ONCE(pe->val[4], 0); - WRITE_ONCE(pe->val[5], 0); - WRITE_ONCE(pe->val[6], 0); - WRITE_ONCE(pe->val[7], 0); -} - -static inline void pasid_clear_entry_with_fpd(struct pasid_entry *pe) -{ - WRITE_ONCE(pe->val[0], PASID_PTE_FPD); - WRITE_ONCE(pe->val[1], 0); - WRITE_ONCE(pe->val[2], 0); - WRITE_ONCE(pe->val[3], 0); - WRITE_ONCE(pe->val[4], 0); - WRITE_ONCE(pe->val[5], 0); - WRITE_ONCE(pe->val[6], 0); - WRITE_ONCE(pe->val[7], 0); -} - static void intel_pasid_clear_entry(struct device *dev, u32 pasid, bool fault_ignore) { @@ -269,192 +188,6 @@ intel_pasid_clear_entry(struct device *dev, u32 pasid, bool fault_ignore) pasid_clear_entry(pe); } -static inline void pasid_set_bits(u64 *ptr, u64 mask, u64 bits) -{ - u64 old; - - old = READ_ONCE(*ptr); - WRITE_ONCE(*ptr, (old & ~mask) | bits); -} - -static inline u64 pasid_get_bits(u64 *ptr) -{ - return READ_ONCE(*ptr); -} - -/* - * Setup the DID(Domain Identifier) field (Bit 64~79) of scalable mode - * PASID entry. - */ -static inline void -pasid_set_domain_id(struct pasid_entry *pe, u64 value) -{ - pasid_set_bits(&pe->val[1], GENMASK_ULL(15, 0), value); -} - -/* - * Get domain ID value of a scalable mode PASID entry. - */ -static inline u16 -pasid_get_domain_id(struct pasid_entry *pe) -{ - return (u16)(READ_ONCE(pe->val[1]) & GENMASK_ULL(15, 0)); -} - -/* - * Setup the SLPTPTR(Second Level Page Table Pointer) field (Bit 12~63) - * of a scalable mode PASID entry. - */ -static inline void -pasid_set_slptr(struct pasid_entry *pe, u64 value) -{ - pasid_set_bits(&pe->val[0], VTD_PAGE_MASK, value); -} - -/* - * Setup the AW(Address Width) field (Bit 2~4) of a scalable mode PASID - * entry. - */ -static inline void -pasid_set_address_width(struct pasid_entry *pe, u64 value) -{ - pasid_set_bits(&pe->val[0], GENMASK_ULL(4, 2), value << 2); -} - -/* - * Setup the PGTT(PASID Granular Translation Type) field (Bit 6~8) - * of a scalable mode PASID entry. - */ -static inline void -pasid_set_translation_type(struct pasid_entry *pe, u64 value) -{ - pasid_set_bits(&pe->val[0], GENMASK_ULL(8, 6), value << 6); -} - -/* - * Enable fault processing by clearing the FPD(Fault Processing - * Disable) field (Bit 1) of a scalable mode PASID entry. - */ -static inline void pasid_set_fault_enable(struct pasid_entry *pe) -{ - pasid_set_bits(&pe->val[0], 1 << 1, 0); -} - -/* - * Enable second level A/D bits by setting the SLADE (Second Level - * Access Dirty Enable) field (Bit 9) of a scalable mode PASID - * entry. - */ -static inline void pasid_set_ssade(struct pasid_entry *pe) -{ - pasid_set_bits(&pe->val[0], 1 << 9, 1 << 9); -} - -/* - * Disable second level A/D bits by clearing the SLADE (Second Level - * Access Dirty Enable) field (Bit 9) of a scalable mode PASID - * entry. - */ -static inline void pasid_clear_ssade(struct pasid_entry *pe) -{ - pasid_set_bits(&pe->val[0], 1 << 9, 0); -} - -/* - * Checks if second level A/D bits specifically the SLADE (Second Level - * Access Dirty Enable) field (Bit 9) of a scalable mode PASID - * entry is set. - */ -static inline bool pasid_get_ssade(struct pasid_entry *pe) -{ - return pasid_get_bits(&pe->val[0]) & (1 << 9); -} - -/* - * Setup the SRE(Supervisor Request Enable) field (Bit 128) of a - * scalable mode PASID entry. - */ -static inline void pasid_set_sre(struct pasid_entry *pe) -{ - pasid_set_bits(&pe->val[2], 1 << 0, 1); -} - -/* - * Setup the WPE(Write Protect Enable) field (Bit 132) of a - * scalable mode PASID entry. - */ -static inline void pasid_set_wpe(struct pasid_entry *pe) -{ - pasid_set_bits(&pe->val[2], 1 << 4, 1 << 4); -} - -/* - * Setup the P(Present) field (Bit 0) of a scalable mode PASID - * entry. - */ -static inline void pasid_set_present(struct pasid_entry *pe) -{ - pasid_set_bits(&pe->val[0], 1 << 0, 1); -} - -/* - * Setup Page Walk Snoop bit (Bit 87) of a scalable mode PASID - * entry. - */ -static inline void pasid_set_page_snoop(struct pasid_entry *pe, bool value) -{ - pasid_set_bits(&pe->val[1], 1 << 23, value << 23); -} - -/* - * Setup No Execute Enable bit (Bit 133) of a scalable mode PASID - * entry. It is required when XD bit of the first level page table - * entry is about to be set. - */ -static inline void pasid_set_nxe(struct pasid_entry *pe) -{ - pasid_set_bits(&pe->val[2], 1 << 5, 1 << 5); -} - -/* - * Setup the Page Snoop (PGSNP) field (Bit 88) of a scalable mode - * PASID entry. - */ -static inline void -pasid_set_pgsnp(struct pasid_entry *pe) -{ - pasid_set_bits(&pe->val[1], 1ULL << 24, 1ULL << 24); -} - -/* - * Setup the First Level Page table Pointer field (Bit 140~191) - * of a scalable mode PASID entry. - */ -static inline void -pasid_set_flptr(struct pasid_entry *pe, u64 value) -{ - pasid_set_bits(&pe->val[2], VTD_PAGE_MASK, value); -} - -/* - * Setup the First Level Paging Mode field (Bit 130~131) of a - * scalable mode PASID entry. - */ -static inline void -pasid_set_flpm(struct pasid_entry *pe, u64 value) -{ - pasid_set_bits(&pe->val[2], GENMASK_ULL(3, 2), value << 2); -} - -/* - * Setup the Extended Access Flag Enable (EAFE) field (Bit 135) - * of a scalable mode PASID entry. - */ -static inline void pasid_set_eafe(struct pasid_entry *pe) -{ - pasid_set_bits(&pe->val[2], 1 << 7, 1 << 7); -} - static void pasid_cache_invalidation_with_pasid(struct intel_iommu *iommu, u16 did, u32 pasid) @@ -613,9 +346,9 @@ int intel_pasid_setup_first_level(struct intel_iommu *iommu, * Skip top levels of page tables for iommu which has less agaw * than default. Unnecessary for PT mode. */ -static inline int iommu_skip_agaw(struct dmar_domain *domain, - struct intel_iommu *iommu, - struct dma_pte **pgd) +static int iommu_skip_agaw(struct dmar_domain *domain, + struct intel_iommu *iommu, + struct dma_pte **pgd) { int agaw; @@ -767,7 +500,6 @@ int intel_pasid_setup_dirty_tracking(struct intel_iommu *iommu, * Set up the scalable mode pasid entry for passthrough translation type. */ int intel_pasid_setup_pass_through(struct intel_iommu *iommu, - struct dmar_domain *domain, struct device *dev, u32 pasid) { u16 did = FLPT_DEFAULT_DID; diff --git a/drivers/iommu/intel/pasid.h b/drivers/iommu/intel/pasid.h index dd37611175cc..8d40d4c66e31 100644 --- a/drivers/iommu/intel/pasid.h +++ b/drivers/iommu/intel/pasid.h @@ -22,16 +22,6 @@ #define is_pasid_enabled(entry) (((entry)->lo >> 3) & 0x1) #define get_pasid_dir_size(entry) (1 << ((((entry)->lo >> 9) & 0x7) + 7)) -/* Virtual command interface for enlightened pasid management. */ -#define VCMD_CMD_ALLOC 0x1 -#define VCMD_CMD_FREE 0x2 -#define VCMD_VRSP_IP 0x1 -#define VCMD_VRSP_SC(e) (((e) & 0xff) >> 1) -#define VCMD_VRSP_SC_SUCCESS 0 -#define VCMD_VRSP_SC_NO_PASID_AVAIL 16 -#define VCMD_VRSP_SC_INVALID_PASID 16 -#define VCMD_VRSP_RESULT_PASID(e) (((e) >> 16) & 0xfffff) -#define VCMD_CMD_OPERAND(e) ((e) << 16) /* * Domain ID reserved for pasid entries programmed for first-level * only and pass-through transfer modes. @@ -96,6 +86,216 @@ static inline u16 pasid_pte_get_pgtt(struct pasid_entry *pte) return (u16)((READ_ONCE(pte->val[0]) >> 6) & 0x7); } +static inline void pasid_clear_entry(struct pasid_entry *pe) +{ + WRITE_ONCE(pe->val[0], 0); + WRITE_ONCE(pe->val[1], 0); + WRITE_ONCE(pe->val[2], 0); + WRITE_ONCE(pe->val[3], 0); + WRITE_ONCE(pe->val[4], 0); + WRITE_ONCE(pe->val[5], 0); + WRITE_ONCE(pe->val[6], 0); + WRITE_ONCE(pe->val[7], 0); +} + +static inline void pasid_clear_entry_with_fpd(struct pasid_entry *pe) +{ + WRITE_ONCE(pe->val[0], PASID_PTE_FPD); + WRITE_ONCE(pe->val[1], 0); + WRITE_ONCE(pe->val[2], 0); + WRITE_ONCE(pe->val[3], 0); + WRITE_ONCE(pe->val[4], 0); + WRITE_ONCE(pe->val[5], 0); + WRITE_ONCE(pe->val[6], 0); + WRITE_ONCE(pe->val[7], 0); +} + +static inline void pasid_set_bits(u64 *ptr, u64 mask, u64 bits) +{ + u64 old; + + old = READ_ONCE(*ptr); + WRITE_ONCE(*ptr, (old & ~mask) | bits); +} + +static inline u64 pasid_get_bits(u64 *ptr) +{ + return READ_ONCE(*ptr); +} + +/* + * Setup the DID(Domain Identifier) field (Bit 64~79) of scalable mode + * PASID entry. + */ +static inline void +pasid_set_domain_id(struct pasid_entry *pe, u64 value) +{ + pasid_set_bits(&pe->val[1], GENMASK_ULL(15, 0), value); +} + +/* + * Get domain ID value of a scalable mode PASID entry. + */ +static inline u16 +pasid_get_domain_id(struct pasid_entry *pe) +{ + return (u16)(READ_ONCE(pe->val[1]) & GENMASK_ULL(15, 0)); +} + +/* + * Setup the SLPTPTR(Second Level Page Table Pointer) field (Bit 12~63) + * of a scalable mode PASID entry. + */ +static inline void +pasid_set_slptr(struct pasid_entry *pe, u64 value) +{ + pasid_set_bits(&pe->val[0], VTD_PAGE_MASK, value); +} + +/* + * Setup the AW(Address Width) field (Bit 2~4) of a scalable mode PASID + * entry. + */ +static inline void +pasid_set_address_width(struct pasid_entry *pe, u64 value) +{ + pasid_set_bits(&pe->val[0], GENMASK_ULL(4, 2), value << 2); +} + +/* + * Setup the PGTT(PASID Granular Translation Type) field (Bit 6~8) + * of a scalable mode PASID entry. + */ +static inline void +pasid_set_translation_type(struct pasid_entry *pe, u64 value) +{ + pasid_set_bits(&pe->val[0], GENMASK_ULL(8, 6), value << 6); +} + +/* + * Enable fault processing by clearing the FPD(Fault Processing + * Disable) field (Bit 1) of a scalable mode PASID entry. + */ +static inline void pasid_set_fault_enable(struct pasid_entry *pe) +{ + pasid_set_bits(&pe->val[0], 1 << 1, 0); +} + +/* + * Enable second level A/D bits by setting the SLADE (Second Level + * Access Dirty Enable) field (Bit 9) of a scalable mode PASID + * entry. + */ +static inline void pasid_set_ssade(struct pasid_entry *pe) +{ + pasid_set_bits(&pe->val[0], 1 << 9, 1 << 9); +} + +/* + * Disable second level A/D bits by clearing the SLADE (Second Level + * Access Dirty Enable) field (Bit 9) of a scalable mode PASID + * entry. + */ +static inline void pasid_clear_ssade(struct pasid_entry *pe) +{ + pasid_set_bits(&pe->val[0], 1 << 9, 0); +} + +/* + * Checks if second level A/D bits specifically the SLADE (Second Level + * Access Dirty Enable) field (Bit 9) of a scalable mode PASID + * entry is set. + */ +static inline bool pasid_get_ssade(struct pasid_entry *pe) +{ + return pasid_get_bits(&pe->val[0]) & (1 << 9); +} + +/* + * Setup the SRE(Supervisor Request Enable) field (Bit 128) of a + * scalable mode PASID entry. + */ +static inline void pasid_set_sre(struct pasid_entry *pe) +{ + pasid_set_bits(&pe->val[2], 1 << 0, 1); +} + +/* + * Setup the WPE(Write Protect Enable) field (Bit 132) of a + * scalable mode PASID entry. + */ +static inline void pasid_set_wpe(struct pasid_entry *pe) +{ + pasid_set_bits(&pe->val[2], 1 << 4, 1 << 4); +} + +/* + * Setup the P(Present) field (Bit 0) of a scalable mode PASID + * entry. + */ +static inline void pasid_set_present(struct pasid_entry *pe) +{ + pasid_set_bits(&pe->val[0], 1 << 0, 1); +} + +/* + * Setup Page Walk Snoop bit (Bit 87) of a scalable mode PASID + * entry. + */ +static inline void pasid_set_page_snoop(struct pasid_entry *pe, bool value) +{ + pasid_set_bits(&pe->val[1], 1 << 23, value << 23); +} + +/* + * Setup No Execute Enable bit (Bit 133) of a scalable mode PASID + * entry. It is required when XD bit of the first level page table + * entry is about to be set. + */ +static inline void pasid_set_nxe(struct pasid_entry *pe) +{ + pasid_set_bits(&pe->val[2], 1 << 5, 1 << 5); +} + +/* + * Setup the Page Snoop (PGSNP) field (Bit 88) of a scalable mode + * PASID entry. + */ +static inline void +pasid_set_pgsnp(struct pasid_entry *pe) +{ + pasid_set_bits(&pe->val[1], 1ULL << 24, 1ULL << 24); +} + +/* + * Setup the First Level Page table Pointer field (Bit 140~191) + * of a scalable mode PASID entry. + */ +static inline void +pasid_set_flptr(struct pasid_entry *pe, u64 value) +{ + pasid_set_bits(&pe->val[2], VTD_PAGE_MASK, value); +} + +/* + * Setup the First Level Paging Mode field (Bit 130~131) of a + * scalable mode PASID entry. + */ +static inline void +pasid_set_flpm(struct pasid_entry *pe, u64 value) +{ + pasid_set_bits(&pe->val[2], GENMASK_ULL(3, 2), value << 2); +} + +/* + * Setup the Extended Access Flag Enable (EAFE) field (Bit 135) + * of a scalable mode PASID entry. + */ +static inline void pasid_set_eafe(struct pasid_entry *pe) +{ + pasid_set_bits(&pe->val[2], 1 << 7, 1 << 7); +} + extern unsigned int intel_pasid_max_id; int intel_pasid_alloc_table(struct device *dev); void intel_pasid_free_table(struct device *dev); @@ -111,15 +311,12 @@ int intel_pasid_setup_dirty_tracking(struct intel_iommu *iommu, struct device *dev, u32 pasid, bool enabled); int intel_pasid_setup_pass_through(struct intel_iommu *iommu, - struct dmar_domain *domain, struct device *dev, u32 pasid); int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev, u32 pasid, struct dmar_domain *domain); void intel_pasid_tear_down_entry(struct intel_iommu *iommu, struct device *dev, u32 pasid, bool fault_ignore); -int vcmd_alloc_pasid(struct intel_iommu *iommu, u32 *pasid); -void vcmd_free_pasid(struct intel_iommu *iommu, u32 pasid); void intel_pasid_setup_page_snoop_control(struct intel_iommu *iommu, struct device *dev, u32 pasid); #endif /* __INTEL_PASID_H */ diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index ac12f76c1212..40edd282903f 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -316,21 +316,22 @@ out: } static int intel_svm_bind_mm(struct intel_iommu *iommu, struct device *dev, - struct mm_struct *mm) + struct iommu_domain *domain, ioasid_t pasid) { struct device_domain_info *info = dev_iommu_priv_get(dev); + struct mm_struct *mm = domain->mm; struct intel_svm_dev *sdev; struct intel_svm *svm; unsigned long sflags; int ret = 0; - svm = pasid_private_find(mm->pasid); + svm = pasid_private_find(pasid); if (!svm) { svm = kzalloc(sizeof(*svm), GFP_KERNEL); if (!svm) return -ENOMEM; - svm->pasid = mm->pasid; + svm->pasid = pasid; svm->mm = mm; INIT_LIST_HEAD_RCU(&svm->devs); @@ -368,7 +369,7 @@ static int intel_svm_bind_mm(struct intel_iommu *iommu, struct device *dev, /* Setup the pasid table: */ sflags = cpu_feature_enabled(X86_FEATURE_LA57) ? PASID_FLAG_FL5LP : 0; - ret = intel_pasid_setup_first_level(iommu, dev, mm->pgd, mm->pasid, + ret = intel_pasid_setup_first_level(iommu, dev, mm->pgd, pasid, FLPT_DEFAULT_DID, sflags); if (ret) goto free_sdev; @@ -382,7 +383,7 @@ free_sdev: free_svm: if (list_empty(&svm->devs)) { mmu_notifier_unregister(&svm->notifier, mm); - pasid_private_remove(mm->pasid); + pasid_private_remove(pasid); kfree(svm); } @@ -392,14 +393,9 @@ free_svm: void intel_svm_remove_dev_pasid(struct device *dev, u32 pasid) { struct intel_svm_dev *sdev; - struct intel_iommu *iommu; struct intel_svm *svm; struct mm_struct *mm; - iommu = device_to_iommu(dev, NULL, NULL); - if (!iommu) - return; - if (pasid_to_svm_sdev(dev, pasid, &svm, &sdev)) return; mm = svm->mm; @@ -750,25 +746,16 @@ int intel_svm_page_response(struct device *dev, struct iommu_fault_event *evt, struct iommu_page_response *msg) { + struct device_domain_info *info = dev_iommu_priv_get(dev); + struct intel_iommu *iommu = info->iommu; + u8 bus = info->bus, devfn = info->devfn; struct iommu_fault_page_request *prm; - struct intel_iommu *iommu; bool private_present; bool pasid_present; bool last_page; - u8 bus, devfn; int ret = 0; u16 sid; - if (!dev || !dev_is_pci(dev)) - return -ENODEV; - - iommu = device_to_iommu(dev, &bus, &devfn); - if (!iommu) - return -ENODEV; - - if (!msg || !evt) - return -EINVAL; - prm = &evt->fault.prm; sid = PCI_DEVID(bus, devfn); pasid_present = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID; @@ -822,9 +809,8 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain, { struct device_domain_info *info = dev_iommu_priv_get(dev); struct intel_iommu *iommu = info->iommu; - struct mm_struct *mm = domain->mm; - return intel_svm_bind_mm(iommu, dev, mm); + return intel_svm_bind_mm(iommu, dev, domain, pasid); } static void intel_svm_domain_free(struct iommu_domain *domain) diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 72dcdd468cf3..f7828a7aad41 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -188,20 +188,28 @@ static dma_addr_t __arm_lpae_dma_addr(void *pages) } static void *__arm_lpae_alloc_pages(size_t size, gfp_t gfp, - struct io_pgtable_cfg *cfg) + struct io_pgtable_cfg *cfg, + void *cookie) { struct device *dev = cfg->iommu_dev; int order = get_order(size); - struct page *p; dma_addr_t dma; void *pages; VM_BUG_ON((gfp & __GFP_HIGHMEM)); - p = alloc_pages_node(dev_to_node(dev), gfp | __GFP_ZERO, order); - if (!p) + + if (cfg->alloc) { + pages = cfg->alloc(cookie, size, gfp); + } else { + struct page *p; + + p = alloc_pages_node(dev_to_node(dev), gfp | __GFP_ZERO, order); + pages = p ? page_address(p) : NULL; + } + + if (!pages) return NULL; - pages = page_address(p); if (!cfg->coherent_walk) { dma = dma_map_single(dev, pages, size, DMA_TO_DEVICE); if (dma_mapping_error(dev, dma)) @@ -220,18 +228,28 @@ static void *__arm_lpae_alloc_pages(size_t size, gfp_t gfp, out_unmap: dev_err(dev, "Cannot accommodate DMA translation for IOMMU page tables\n"); dma_unmap_single(dev, dma, size, DMA_TO_DEVICE); + out_free: - __free_pages(p, order); + if (cfg->free) + cfg->free(cookie, pages, size); + else + free_pages((unsigned long)pages, order); + return NULL; } static void __arm_lpae_free_pages(void *pages, size_t size, - struct io_pgtable_cfg *cfg) + struct io_pgtable_cfg *cfg, + void *cookie) { if (!cfg->coherent_walk) dma_unmap_single(cfg->iommu_dev, __arm_lpae_dma_addr(pages), size, DMA_TO_DEVICE); - free_pages((unsigned long)pages, get_order(size)); + + if (cfg->free) + cfg->free(cookie, pages, size); + else + free_pages((unsigned long)pages, get_order(size)); } static void __arm_lpae_sync_pte(arm_lpae_iopte *ptep, int num_entries, @@ -373,13 +391,13 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova, /* Grab a pointer to the next level */ pte = READ_ONCE(*ptep); if (!pte) { - cptep = __arm_lpae_alloc_pages(tblsz, gfp, cfg); + cptep = __arm_lpae_alloc_pages(tblsz, gfp, cfg, data->iop.cookie); if (!cptep) return -ENOMEM; pte = arm_lpae_install_table(cptep, ptep, 0, data); if (pte) - __arm_lpae_free_pages(cptep, tblsz, cfg); + __arm_lpae_free_pages(cptep, tblsz, cfg, data->iop.cookie); } else if (!cfg->coherent_walk && !(pte & ARM_LPAE_PTE_SW_SYNC)) { __arm_lpae_sync_pte(ptep, 1, cfg); } @@ -524,7 +542,7 @@ static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl, __arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data)); } - __arm_lpae_free_pages(start, table_size, &data->iop.cfg); + __arm_lpae_free_pages(start, table_size, &data->iop.cfg, data->iop.cookie); } static void arm_lpae_free_pgtable(struct io_pgtable *iop) @@ -552,7 +570,7 @@ static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, if (WARN_ON(lvl == ARM_LPAE_MAX_LEVELS)) return 0; - tablep = __arm_lpae_alloc_pages(tablesz, GFP_ATOMIC, cfg); + tablep = __arm_lpae_alloc_pages(tablesz, GFP_ATOMIC, cfg, data->iop.cookie); if (!tablep) return 0; /* Bytes unmapped */ @@ -575,7 +593,7 @@ static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, pte = arm_lpae_install_table(tablep, ptep, blk_pte, data); if (pte != blk_pte) { - __arm_lpae_free_pages(tablep, tablesz, cfg); + __arm_lpae_free_pages(tablep, tablesz, cfg, data->iop.cookie); /* * We may race against someone unmapping another part of this * block, but anything else is invalid. We can't misinterpret @@ -882,7 +900,7 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) /* Looking good; allocate a pgd */ data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data), - GFP_KERNEL, cfg); + GFP_KERNEL, cfg, cookie); if (!data->pgd) goto out_free_data; @@ -984,7 +1002,7 @@ arm_64_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie) /* Allocate pgd pages */ data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data), - GFP_KERNEL, cfg); + GFP_KERNEL, cfg, cookie); if (!data->pgd) goto out_free_data; @@ -1059,7 +1077,7 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV)); data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data), GFP_KERNEL, - cfg); + cfg, cookie); if (!data->pgd) goto out_free_data; @@ -1080,26 +1098,31 @@ out_free_data: } struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = { + .caps = IO_PGTABLE_CAP_CUSTOM_ALLOCATOR, .alloc = arm_64_lpae_alloc_pgtable_s1, .free = arm_lpae_free_pgtable, }; struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns = { + .caps = IO_PGTABLE_CAP_CUSTOM_ALLOCATOR, .alloc = arm_64_lpae_alloc_pgtable_s2, .free = arm_lpae_free_pgtable, }; struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s1_init_fns = { + .caps = IO_PGTABLE_CAP_CUSTOM_ALLOCATOR, .alloc = arm_32_lpae_alloc_pgtable_s1, .free = arm_lpae_free_pgtable, }; struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns = { + .caps = IO_PGTABLE_CAP_CUSTOM_ALLOCATOR, .alloc = arm_32_lpae_alloc_pgtable_s2, .free = arm_lpae_free_pgtable, }; struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = { + .caps = IO_PGTABLE_CAP_CUSTOM_ALLOCATOR, .alloc = arm_mali_lpae_alloc_pgtable, .free = arm_lpae_free_pgtable, }; diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c index b843fcd365d2..8841c1487f00 100644 --- a/drivers/iommu/io-pgtable.c +++ b/drivers/iommu/io-pgtable.c @@ -34,6 +34,26 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = { #endif }; +static int check_custom_allocator(enum io_pgtable_fmt fmt, + struct io_pgtable_cfg *cfg) +{ + /* No custom allocator, no need to check the format. */ + if (!cfg->alloc && !cfg->free) + return 0; + + /* When passing a custom allocator, both the alloc and free + * functions should be provided. + */ + if (!cfg->alloc || !cfg->free) + return -EINVAL; + + /* Make sure the format supports custom allocators. */ + if (io_pgtable_init_table[fmt]->caps & IO_PGTABLE_CAP_CUSTOM_ALLOCATOR) + return 0; + + return -EINVAL; +} + struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt, struct io_pgtable_cfg *cfg, void *cookie) @@ -44,6 +64,9 @@ struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt, if (fmt >= IO_PGTABLE_NUM_FMTS) return NULL; + if (check_custom_allocator(fmt, cfg)) + return NULL; + fns = io_pgtable_init_table[fmt]; if (!fns) return NULL; diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c index b78671a8a914..c3fc9201d0be 100644 --- a/drivers/iommu/iommu-sva.c +++ b/drivers/iommu/iommu-sva.c @@ -12,32 +12,42 @@ static DEFINE_MUTEX(iommu_sva_lock); /* Allocate a PASID for the mm within range (inclusive) */ -static int iommu_sva_alloc_pasid(struct mm_struct *mm, struct device *dev) +static struct iommu_mm_data *iommu_alloc_mm_data(struct mm_struct *mm, struct device *dev) { + struct iommu_mm_data *iommu_mm; ioasid_t pasid; - int ret = 0; + + lockdep_assert_held(&iommu_sva_lock); if (!arch_pgtable_dma_compat(mm)) - return -EBUSY; + return ERR_PTR(-EBUSY); - mutex_lock(&iommu_sva_lock); + iommu_mm = mm->iommu_mm; /* Is a PASID already associated with this mm? */ - if (mm_valid_pasid(mm)) { - if (mm->pasid >= dev->iommu->max_pasids) - ret = -EOVERFLOW; - goto out; + if (iommu_mm) { + if (iommu_mm->pasid >= dev->iommu->max_pasids) + return ERR_PTR(-EOVERFLOW); + return iommu_mm; } + iommu_mm = kzalloc(sizeof(struct iommu_mm_data), GFP_KERNEL); + if (!iommu_mm) + return ERR_PTR(-ENOMEM); + pasid = iommu_alloc_global_pasid(dev); if (pasid == IOMMU_PASID_INVALID) { - ret = -ENOSPC; - goto out; + kfree(iommu_mm); + return ERR_PTR(-ENOSPC); } - mm->pasid = pasid; - ret = 0; -out: - mutex_unlock(&iommu_sva_lock); - return ret; + iommu_mm->pasid = pasid; + INIT_LIST_HEAD(&iommu_mm->sva_domains); + /* + * Make sure the write to mm->iommu_mm is not reordered in front of + * initialization to iommu_mm fields. If it does, readers may see a + * valid iommu_mm with uninitialized values. + */ + smp_store_release(&mm->iommu_mm, iommu_mm); + return iommu_mm; } /** @@ -58,57 +68,60 @@ out: */ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm) { + struct iommu_mm_data *iommu_mm; struct iommu_domain *domain; struct iommu_sva *handle; int ret; + mutex_lock(&iommu_sva_lock); + /* Allocate mm->pasid if necessary. */ - ret = iommu_sva_alloc_pasid(mm, dev); - if (ret) - return ERR_PTR(ret); + iommu_mm = iommu_alloc_mm_data(mm, dev); + if (IS_ERR(iommu_mm)) { + ret = PTR_ERR(iommu_mm); + goto out_unlock; + } handle = kzalloc(sizeof(*handle), GFP_KERNEL); - if (!handle) - return ERR_PTR(-ENOMEM); - - mutex_lock(&iommu_sva_lock); - /* Search for an existing domain. */ - domain = iommu_get_domain_for_dev_pasid(dev, mm->pasid, - IOMMU_DOMAIN_SVA); - if (IS_ERR(domain)) { - ret = PTR_ERR(domain); + if (!handle) { + ret = -ENOMEM; goto out_unlock; } - if (domain) { - domain->users++; - goto out; + /* Search for an existing domain. */ + list_for_each_entry(domain, &mm->iommu_mm->sva_domains, next) { + ret = iommu_attach_device_pasid(domain, dev, iommu_mm->pasid); + if (!ret) { + domain->users++; + goto out; + } } /* Allocate a new domain and set it on device pasid. */ domain = iommu_sva_domain_alloc(dev, mm); if (!domain) { ret = -ENOMEM; - goto out_unlock; + goto out_free_handle; } - ret = iommu_attach_device_pasid(domain, dev, mm->pasid); + ret = iommu_attach_device_pasid(domain, dev, iommu_mm->pasid); if (ret) goto out_free_domain; domain->users = 1; + list_add(&domain->next, &mm->iommu_mm->sva_domains); + out: mutex_unlock(&iommu_sva_lock); handle->dev = dev; handle->domain = domain; - return handle; out_free_domain: iommu_domain_free(domain); +out_free_handle: + kfree(handle); out_unlock: mutex_unlock(&iommu_sva_lock); - kfree(handle); - return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(iommu_sva_bind_device); @@ -124,12 +137,13 @@ EXPORT_SYMBOL_GPL(iommu_sva_bind_device); void iommu_sva_unbind_device(struct iommu_sva *handle) { struct iommu_domain *domain = handle->domain; - ioasid_t pasid = domain->mm->pasid; + struct iommu_mm_data *iommu_mm = domain->mm->iommu_mm; struct device *dev = handle->dev; mutex_lock(&iommu_sva_lock); + iommu_detach_device_pasid(domain, dev, iommu_mm->pasid); if (--domain->users == 0) { - iommu_detach_device_pasid(domain, dev, pasid); + list_del(&domain->next); iommu_domain_free(domain); } mutex_unlock(&iommu_sva_lock); @@ -141,7 +155,7 @@ u32 iommu_sva_get_pasid(struct iommu_sva *handle) { struct iommu_domain *domain = handle->domain; - return domain->mm->pasid; + return mm_get_enqcmd_pasid(domain->mm); } EXPORT_SYMBOL_GPL(iommu_sva_get_pasid); @@ -205,8 +219,11 @@ out_put_mm: void mm_pasid_drop(struct mm_struct *mm) { - if (likely(!mm_valid_pasid(mm))) + struct iommu_mm_data *iommu_mm = mm->iommu_mm; + + if (!iommu_mm) return; - iommu_free_global_pasid(mm->pasid); + iommu_free_global_pasid(iommu_mm->pasid); + kfree(iommu_mm); } diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 33e2a9b5d339..d14413916f93 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -148,7 +148,7 @@ struct iommu_group_attribute iommu_group_attr_##_name = \ static LIST_HEAD(iommu_device_list); static DEFINE_SPINLOCK(iommu_device_lock); -static struct bus_type * const iommu_buses[] = { +static const struct bus_type * const iommu_buses[] = { &platform_bus_type, #ifdef CONFIG_PCI &pci_bus_type, @@ -257,13 +257,6 @@ int iommu_device_register(struct iommu_device *iommu, /* We need to be able to take module references appropriately */ if (WARN_ON(is_module_address((unsigned long)ops) && !ops->owner)) return -EINVAL; - /* - * Temporarily enforce global restriction to a single driver. This was - * already the de-facto behaviour, since any possible combination of - * existing drivers would compete for at least the PCI or platform bus. - */ - if (iommu_buses[0]->iommu_ops && iommu_buses[0]->iommu_ops != ops) - return -EBUSY; iommu->ops = ops; if (hwdev) @@ -273,10 +266,8 @@ int iommu_device_register(struct iommu_device *iommu, list_add_tail(&iommu->list, &iommu_device_list); spin_unlock(&iommu_device_lock); - for (int i = 0; i < ARRAY_SIZE(iommu_buses) && !err; i++) { - iommu_buses[i]->iommu_ops = ops; + for (int i = 0; i < ARRAY_SIZE(iommu_buses) && !err; i++) err = bus_iommu_probe(iommu_buses[i]); - } if (err) iommu_device_unregister(iommu); return err; @@ -329,7 +320,6 @@ int iommu_device_register_bus(struct iommu_device *iommu, list_add_tail(&iommu->list, &iommu_device_list); spin_unlock(&iommu_device_lock); - bus->iommu_ops = ops; err = bus_iommu_probe(bus); if (err) { iommu_device_unregister_bus(iommu, bus, nb); @@ -344,6 +334,8 @@ static struct dev_iommu *dev_iommu_get(struct device *dev) { struct dev_iommu *param = dev->iommu; + lockdep_assert_held(&iommu_probe_device_lock); + if (param) return param; @@ -368,6 +360,15 @@ static void dev_iommu_free(struct device *dev) kfree(param); } +/* + * Internal equivalent of device_iommu_mapped() for when we care that a device + * actually has API ops, and don't want false positives from VFIO-only groups. + */ +static bool dev_has_iommu(struct device *dev) +{ + return dev->iommu && dev->iommu->iommu_dev; +} + static u32 dev_iommu_get_max_pasids(struct device *dev) { u32 max_pasids = 0, bits = 0; @@ -386,6 +387,15 @@ static u32 dev_iommu_get_max_pasids(struct device *dev) return min_t(u32, max_pasids, dev->iommu->iommu_dev->max_pasids); } +void dev_iommu_priv_set(struct device *dev, void *priv) +{ + /* FSL_PAMU does something weird */ + if (!IS_ENABLED(CONFIG_FSL_PAMU)) + lockdep_assert_held(&iommu_probe_device_lock); + dev->iommu->priv = priv; +} +EXPORT_SYMBOL_GPL(dev_iommu_priv_set); + /* * Init the dev->iommu and dev->iommu_group in the struct device and get the * driver probed @@ -489,11 +499,26 @@ DEFINE_MUTEX(iommu_probe_device_lock); static int __iommu_probe_device(struct device *dev, struct list_head *group_list) { - const struct iommu_ops *ops = dev->bus->iommu_ops; + const struct iommu_ops *ops; + struct iommu_fwspec *fwspec; struct iommu_group *group; struct group_device *gdev; int ret; + /* + * For FDT-based systems and ACPI IORT/VIOT, drivers register IOMMU + * instances with non-NULL fwnodes, and client devices should have been + * identified with a fwspec by this point. Otherwise, we can currently + * assume that only one of Intel, AMD, s390, PAMU or legacy SMMUv2 can + * be present, and that any of their registered instances has suitable + * ops for probing, and thus cheekily co-opt the same mechanism. + */ + fwspec = dev_iommu_fwspec_get(dev); + if (fwspec && fwspec->ops) + ops = fwspec->ops; + else + ops = iommu_ops_from_fwnode(NULL); + if (!ops) return -ENODEV; /* @@ -618,7 +643,7 @@ static void __iommu_group_remove_device(struct device *dev) list_del(&device->list); __iommu_group_free_device(group, device); - if (dev->iommu && dev->iommu->iommu_dev) + if (dev_has_iommu(dev)) iommu_deinit_device(dev); else dev->iommu_group = NULL; @@ -817,7 +842,7 @@ int iommu_get_group_resv_regions(struct iommu_group *group, * Non-API groups still expose reserved_regions in sysfs, * so filter out calls that get here that way. */ - if (!device->dev->iommu) + if (!dev_has_iommu(device->dev)) break; INIT_LIST_HEAD(&dev_resv_regions); @@ -1223,6 +1248,12 @@ void iommu_group_remove_device(struct device *dev) } EXPORT_SYMBOL_GPL(iommu_group_remove_device); +static struct device *iommu_group_first_dev(struct iommu_group *group) +{ + lockdep_assert_held(&group->mutex); + return list_first_entry(&group->devices, struct group_device, list)->dev; +} + /** * iommu_group_for_each_dev - iterate over each device in the group * @group: the group @@ -1751,30 +1782,13 @@ __iommu_group_alloc_default_domain(struct iommu_group *group, int req_type) } /* - * Returns the iommu_ops for the devices in an iommu group. - * - * It is assumed that all devices in an iommu group are managed by a single - * IOMMU unit. Therefore, this returns the dev_iommu_ops of the first device - * in the group. - */ -static const struct iommu_ops *group_iommu_ops(struct iommu_group *group) -{ - struct group_device *device = - list_first_entry(&group->devices, struct group_device, list); - - lockdep_assert_held(&group->mutex); - - return dev_iommu_ops(device->dev); -} - -/* * req_type of 0 means "auto" which means to select a domain based on * iommu_def_domain_type or what the driver actually supports. */ static struct iommu_domain * iommu_group_alloc_default_domain(struct iommu_group *group, int req_type) { - const struct iommu_ops *ops = group_iommu_ops(group); + const struct iommu_ops *ops = dev_iommu_ops(iommu_group_first_dev(group)); struct iommu_domain *dom; lockdep_assert_held(&group->mutex); @@ -1785,7 +1799,7 @@ iommu_group_alloc_default_domain(struct iommu_group *group, int req_type) * domain. Do not use in new drivers. */ if (ops->default_domain) { - if (req_type) + if (req_type != ops->default_domain->type) return ERR_PTR(-EINVAL); return ops->default_domain; } @@ -1854,13 +1868,21 @@ static int iommu_bus_notifier(struct notifier_block *nb, static int iommu_get_def_domain_type(struct iommu_group *group, struct device *dev, int cur_type) { - const struct iommu_ops *ops = group_iommu_ops(group); + const struct iommu_ops *ops = dev_iommu_ops(dev); int type; - if (!ops->def_domain_type) - return cur_type; - - type = ops->def_domain_type(dev); + if (ops->default_domain) { + /* + * Drivers that declare a global static default_domain will + * always choose that. + */ + type = ops->default_domain->type; + } else { + if (ops->def_domain_type) + type = ops->def_domain_type(dev); + else + return cur_type; + } if (!type || cur_type == type) return cur_type; if (!cur_type) @@ -2003,9 +2025,28 @@ int bus_iommu_probe(const struct bus_type *bus) return 0; } +/** + * iommu_present() - make platform-specific assumptions about an IOMMU + * @bus: bus to check + * + * Do not use this function. You want device_iommu_mapped() instead. + * + * Return: true if some IOMMU is present and aware of devices on the given bus; + * in general it may not be the only IOMMU, and it may not have anything to do + * with whatever device you are ultimately interested in. + */ bool iommu_present(const struct bus_type *bus) { - return bus->iommu_ops != NULL; + bool ret = false; + + for (int i = 0; i < ARRAY_SIZE(iommu_buses); i++) { + if (iommu_buses[i] == bus) { + spin_lock(&iommu_device_lock); + ret = !list_empty(&iommu_device_list); + spin_unlock(&iommu_device_lock); + } + } + return ret; } EXPORT_SYMBOL_GPL(iommu_present); @@ -2021,7 +2062,7 @@ bool device_iommu_capable(struct device *dev, enum iommu_cap cap) { const struct iommu_ops *ops; - if (!dev->iommu || !dev->iommu->iommu_dev) + if (!dev_has_iommu(dev)) return false; ops = dev_iommu_ops(dev); @@ -2107,6 +2148,7 @@ static struct iommu_domain *__iommu_domain_alloc(const struct iommu_ops *ops, return ERR_PTR(-ENOMEM); domain->type = type; + domain->owner = ops; /* * If not already set, assume all sizes by default; the driver * may override this later @@ -2132,21 +2174,37 @@ static struct iommu_domain *__iommu_domain_alloc(const struct iommu_ops *ops, static struct iommu_domain * __iommu_group_domain_alloc(struct iommu_group *group, unsigned int type) { - struct device *dev = - list_first_entry(&group->devices, struct group_device, list) - ->dev; + struct device *dev = iommu_group_first_dev(group); - return __iommu_domain_alloc(group_iommu_ops(group), dev, type); + return __iommu_domain_alloc(dev_iommu_ops(dev), dev, type); +} + +static int __iommu_domain_alloc_dev(struct device *dev, void *data) +{ + const struct iommu_ops **ops = data; + + if (!dev_has_iommu(dev)) + return 0; + + if (WARN_ONCE(*ops && *ops != dev_iommu_ops(dev), + "Multiple IOMMU drivers present for bus %s, which the public IOMMU API can't fully support yet. You will still need to disable one or more for this to work, sorry!\n", + dev_bus_name(dev))) + return -EBUSY; + + *ops = dev_iommu_ops(dev); + return 0; } struct iommu_domain *iommu_domain_alloc(const struct bus_type *bus) { + const struct iommu_ops *ops = NULL; + int err = bus_for_each_dev(bus, NULL, &ops, __iommu_domain_alloc_dev); struct iommu_domain *domain; - if (bus == NULL || bus->iommu_ops == NULL) + if (err || !ops) return NULL; - domain = __iommu_domain_alloc(bus->iommu_ops, NULL, - IOMMU_DOMAIN_UNMANAGED); + + domain = __iommu_domain_alloc(ops, NULL, IOMMU_DOMAIN_UNMANAGED); if (IS_ERR(domain)) return NULL; return domain; @@ -2284,10 +2342,16 @@ struct iommu_domain *iommu_get_dma_domain(struct device *dev) static int __iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group) { + struct device *dev; + if (group->domain && group->domain != group->default_domain && group->domain != group->blocking_domain) return -EBUSY; + dev = iommu_group_first_dev(group); + if (!dev_has_iommu(dev) || dev_iommu_ops(dev) != domain->owner) + return -EINVAL; + return __iommu_group_set_domain(group, domain); } @@ -3004,8 +3068,8 @@ EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids); */ int iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features feat) { - if (dev->iommu && dev->iommu->iommu_dev) { - const struct iommu_ops *ops = dev->iommu->iommu_dev->ops; + if (dev_has_iommu(dev)) { + const struct iommu_ops *ops = dev_iommu_ops(dev); if (ops->dev_enable_feat) return ops->dev_enable_feat(dev, feat); @@ -3020,8 +3084,8 @@ EXPORT_SYMBOL_GPL(iommu_dev_enable_feature); */ int iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features feat) { - if (dev->iommu && dev->iommu->iommu_dev) { - const struct iommu_ops *ops = dev->iommu->iommu_dev->ops; + if (dev_has_iommu(dev)) { + const struct iommu_ops *ops = dev_iommu_ops(dev); if (ops->dev_disable_feat) return ops->dev_disable_feat(dev, feat); @@ -3481,6 +3545,9 @@ int iommu_attach_device_pasid(struct iommu_domain *domain, if (!group) return -ENODEV; + if (!dev_has_iommu(dev) || dev_iommu_ops(dev) != domain->owner) + return -EINVAL; + mutex_lock(&group->mutex); curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, domain, GFP_KERNEL); if (curr) { @@ -3569,6 +3636,7 @@ struct iommu_domain *iommu_sva_domain_alloc(struct device *dev, domain->type = IOMMU_DOMAIN_SVA; mmgrab(mm); domain->mm = mm; + domain->owner = ops; domain->iopf_handler = iommu_sva_handle_iopf; domain->fault_data = mm; diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c index cbb5df0a6c32..3f3f1fa1a0a9 100644 --- a/drivers/iommu/iommufd/hw_pagetable.c +++ b/drivers/iommu/iommufd/hw_pagetable.c @@ -135,6 +135,7 @@ iommufd_hwpt_paging_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas, hwpt->domain = NULL; goto out_abort; } + hwpt->domain->owner = ops; } else { hwpt->domain = iommu_domain_alloc(idev->dev->bus); if (!hwpt->domain) { @@ -233,6 +234,7 @@ iommufd_hwpt_nested_alloc(struct iommufd_ctx *ictx, hwpt->domain = NULL; goto out_abort; } + hwpt->domain->owner = ops; if (WARN_ON_ONCE(hwpt->domain->type != IOMMU_DOMAIN_NESTED)) { rc = -EINVAL; @@ -371,3 +373,44 @@ int iommufd_hwpt_get_dirty_bitmap(struct iommufd_ucmd *ucmd) iommufd_put_object(ucmd->ictx, &hwpt_paging->common.obj); return rc; } + +int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd) +{ + struct iommu_hwpt_invalidate *cmd = ucmd->cmd; + struct iommu_user_data_array data_array = { + .type = cmd->data_type, + .uptr = u64_to_user_ptr(cmd->data_uptr), + .entry_len = cmd->entry_len, + .entry_num = cmd->entry_num, + }; + struct iommufd_hw_pagetable *hwpt; + u32 done_num = 0; + int rc; + + if (cmd->__reserved) { + rc = -EOPNOTSUPP; + goto out; + } + + if (cmd->entry_num && (!cmd->data_uptr || !cmd->entry_len)) { + rc = -EINVAL; + goto out; + } + + hwpt = iommufd_get_hwpt_nested(ucmd, cmd->hwpt_id); + if (IS_ERR(hwpt)) { + rc = PTR_ERR(hwpt); + goto out; + } + + rc = hwpt->domain->ops->cache_invalidate_user(hwpt->domain, + &data_array); + done_num = data_array.entry_num; + + iommufd_put_object(ucmd->ictx, &hwpt->obj); +out: + cmd->entry_num = done_num; + if (iommufd_ucmd_respond(ucmd, sizeof(*cmd))) + return -EFAULT; + return rc; +} diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h index abae041e256f..991f864d1f9b 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -328,6 +328,15 @@ iommufd_get_hwpt_paging(struct iommufd_ucmd *ucmd, u32 id) IOMMUFD_OBJ_HWPT_PAGING), struct iommufd_hwpt_paging, common.obj); } + +static inline struct iommufd_hw_pagetable * +iommufd_get_hwpt_nested(struct iommufd_ucmd *ucmd, u32 id) +{ + return container_of(iommufd_get_object(ucmd->ictx, id, + IOMMUFD_OBJ_HWPT_NESTED), + struct iommufd_hw_pagetable, obj); +} + int iommufd_hwpt_set_dirty_tracking(struct iommufd_ucmd *ucmd); int iommufd_hwpt_get_dirty_bitmap(struct iommufd_ucmd *ucmd); @@ -345,6 +354,7 @@ void iommufd_hwpt_paging_abort(struct iommufd_object *obj); void iommufd_hwpt_nested_destroy(struct iommufd_object *obj); void iommufd_hwpt_nested_abort(struct iommufd_object *obj); int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd); +int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd); static inline void iommufd_hw_pagetable_put(struct iommufd_ctx *ictx, struct iommufd_hw_pagetable *hwpt) diff --git a/drivers/iommu/iommufd/iommufd_test.h b/drivers/iommu/iommufd/iommufd_test.h index 7910fbe1962d..482d4059f5db 100644 --- a/drivers/iommu/iommufd/iommufd_test.h +++ b/drivers/iommu/iommufd/iommufd_test.h @@ -21,6 +21,7 @@ enum { IOMMU_TEST_OP_ACCESS_REPLACE_IOAS, IOMMU_TEST_OP_MOCK_DOMAIN_FLAGS, IOMMU_TEST_OP_DIRTY, + IOMMU_TEST_OP_MD_CHECK_IOTLB, }; enum { @@ -121,6 +122,10 @@ struct iommu_test_cmd { __aligned_u64 uptr; __aligned_u64 out_nr_dirty; } dirty; + struct { + __u32 id; + __u32 iotlb; + } check_iotlb; }; __u32 last; }; @@ -148,4 +153,22 @@ struct iommu_hwpt_selftest { __u32 iotlb; }; +/* Should not be equal to any defined value in enum iommu_hwpt_invalidate_data_type */ +#define IOMMU_HWPT_INVALIDATE_DATA_SELFTEST 0xdeadbeef +#define IOMMU_HWPT_INVALIDATE_DATA_SELFTEST_INVALID 0xdadbeef + +/** + * struct iommu_hwpt_invalidate_selftest - Invalidation data for Mock driver + * (IOMMU_HWPT_INVALIDATE_DATA_SELFTEST) + * @flags: Invalidate flags + * @iotlb_id: Invalidate iotlb entry index + * + * If IOMMU_TEST_INVALIDATE_ALL is set in @flags, @iotlb_id will be ignored + */ +struct iommu_hwpt_invalidate_selftest { +#define IOMMU_TEST_INVALIDATE_FLAG_ALL (1 << 0) + __u32 flags; + __u32 iotlb_id; +}; + #endif diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c index c9091e46d208..39b32932c61e 100644 --- a/drivers/iommu/iommufd/main.c +++ b/drivers/iommu/iommufd/main.c @@ -322,6 +322,7 @@ union ucmd_buffer { struct iommu_hw_info info; struct iommu_hwpt_alloc hwpt; struct iommu_hwpt_get_dirty_bitmap get_dirty_bitmap; + struct iommu_hwpt_invalidate cache; struct iommu_hwpt_set_dirty_tracking set_dirty_tracking; struct iommu_ioas_alloc alloc; struct iommu_ioas_allow_iovas allow_iovas; @@ -360,6 +361,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = { __reserved), IOCTL_OP(IOMMU_HWPT_GET_DIRTY_BITMAP, iommufd_hwpt_get_dirty_bitmap, struct iommu_hwpt_get_dirty_bitmap, data), + IOCTL_OP(IOMMU_HWPT_INVALIDATE, iommufd_hwpt_invalidate, + struct iommu_hwpt_invalidate, __reserved), IOCTL_OP(IOMMU_HWPT_SET_DIRTY_TRACKING, iommufd_hwpt_set_dirty_tracking, struct iommu_hwpt_set_dirty_tracking, __reserved), IOCTL_OP(IOMMU_IOAS_ALLOC, iommufd_ioas_alloc_ioctl, diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c index 022ef8f55088..d9e9920c7eba 100644 --- a/drivers/iommu/iommufd/selftest.c +++ b/drivers/iommu/iommufd/selftest.c @@ -25,6 +25,19 @@ static struct iommu_domain_ops domain_nested_ops; size_t iommufd_test_memory_limit = 65536; +struct mock_bus_type { + struct bus_type bus; + struct notifier_block nb; +}; + +static struct mock_bus_type iommufd_mock_bus_type = { + .bus = { + .name = "iommufd_mock", + }, +}; + +static atomic_t mock_dev_num; + enum { MOCK_DIRTY_TRACK = 1, MOCK_IO_PAGE_SIZE = PAGE_SIZE / 2, @@ -437,6 +450,8 @@ static struct iommu_device mock_iommu_device = { static struct iommu_device *mock_probe_device(struct device *dev) { + if (dev->bus != &iommufd_mock_bus_type.bus) + return ERR_PTR(-ENODEV); return &mock_iommu_device; } @@ -473,9 +488,59 @@ static void mock_domain_free_nested(struct iommu_domain *domain) kfree(mock_nested); } +static int +mock_domain_cache_invalidate_user(struct iommu_domain *domain, + struct iommu_user_data_array *array) +{ + struct mock_iommu_domain_nested *mock_nested = + container_of(domain, struct mock_iommu_domain_nested, domain); + struct iommu_hwpt_invalidate_selftest inv; + u32 processed = 0; + int i = 0, j; + int rc = 0; + + if (array->type != IOMMU_HWPT_INVALIDATE_DATA_SELFTEST) { + rc = -EINVAL; + goto out; + } + + for ( ; i < array->entry_num; i++) { + rc = iommu_copy_struct_from_user_array(&inv, array, + IOMMU_HWPT_INVALIDATE_DATA_SELFTEST, + i, iotlb_id); + if (rc) + break; + + if (inv.flags & ~IOMMU_TEST_INVALIDATE_FLAG_ALL) { + rc = -EOPNOTSUPP; + break; + } + + if (inv.iotlb_id > MOCK_NESTED_DOMAIN_IOTLB_ID_MAX) { + rc = -EINVAL; + break; + } + + if (inv.flags & IOMMU_TEST_INVALIDATE_FLAG_ALL) { + /* Invalidate all mock iotlb entries and ignore iotlb_id */ + for (j = 0; j < MOCK_NESTED_DOMAIN_IOTLB_NUM; j++) + mock_nested->iotlb[j] = 0; + } else { + mock_nested->iotlb[inv.iotlb_id] = 0; + } + + processed++; + } + +out: + array->entry_num = processed; + return rc; +} + static struct iommu_domain_ops domain_nested_ops = { .free = mock_domain_free_nested, .attach_dev = mock_domain_nop_attach, + .cache_invalidate_user = mock_domain_cache_invalidate_user, }; static inline struct iommufd_hw_pagetable * @@ -526,19 +591,6 @@ get_md_pagetable_nested(struct iommufd_ucmd *ucmd, u32 mockpt_id, return hwpt; } -struct mock_bus_type { - struct bus_type bus; - struct notifier_block nb; -}; - -static struct mock_bus_type iommufd_mock_bus_type = { - .bus = { - .name = "iommufd_mock", - }, -}; - -static atomic_t mock_dev_num; - static void mock_dev_release(struct device *dev) { struct mock_dev *mdev = container_of(dev, struct mock_dev, dev); @@ -793,6 +845,28 @@ static int iommufd_test_md_check_refs(struct iommufd_ucmd *ucmd, return 0; } +static int iommufd_test_md_check_iotlb(struct iommufd_ucmd *ucmd, + u32 mockpt_id, unsigned int iotlb_id, + u32 iotlb) +{ + struct mock_iommu_domain_nested *mock_nested; + struct iommufd_hw_pagetable *hwpt; + int rc = 0; + + hwpt = get_md_pagetable_nested(ucmd, mockpt_id, &mock_nested); + if (IS_ERR(hwpt)) + return PTR_ERR(hwpt); + + mock_nested = container_of(hwpt->domain, + struct mock_iommu_domain_nested, domain); + + if (iotlb_id > MOCK_NESTED_DOMAIN_IOTLB_ID_MAX || + mock_nested->iotlb[iotlb_id] != iotlb) + rc = -EINVAL; + iommufd_put_object(ucmd->ictx, &hwpt->obj); + return rc; +} + struct selftest_access { struct iommufd_access *access; struct file *file; @@ -1274,6 +1348,10 @@ int iommufd_test(struct iommufd_ucmd *ucmd) return iommufd_test_md_check_refs( ucmd, u64_to_user_ptr(cmd->check_refs.uptr), cmd->check_refs.length, cmd->check_refs.refs); + case IOMMU_TEST_OP_MD_CHECK_IOTLB: + return iommufd_test_md_check_iotlb(ucmd, cmd->id, + cmd->check_iotlb.id, + cmd->check_iotlb.iotlb); case IOMMU_TEST_OP_CREATE_ACCESS: return iommufd_test_create_access(ucmd, cmd->id, cmd->create_access.flags); diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c index 75279500a4a8..7abe9e85a570 100644 --- a/drivers/iommu/mtk_iommu.c +++ b/drivers/iommu/mtk_iommu.c @@ -863,16 +863,11 @@ static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain, static struct iommu_device *mtk_iommu_probe_device(struct device *dev) { struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); - struct mtk_iommu_data *data; + struct mtk_iommu_data *data = dev_iommu_priv_get(dev); struct device_link *link; struct device *larbdev; unsigned int larbid, larbidx, i; - if (!fwspec || fwspec->ops != &mtk_iommu_ops) - return ERR_PTR(-ENODEV); /* Not a iommu client device */ - - data = dev_iommu_priv_get(dev); - if (!MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_MM)) return &data->iommu; diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c index 67e044c1a7d9..25b41222abae 100644 --- a/drivers/iommu/mtk_iommu_v1.c +++ b/drivers/iommu/mtk_iommu_v1.c @@ -481,9 +481,6 @@ static struct iommu_device *mtk_iommu_v1_probe_device(struct device *dev) idx++; } - if (!fwspec || fwspec->ops != &mtk_iommu_v1_ops) - return ERR_PTR(-ENODEV); /* Not a iommu client device */ - data = dev_iommu_priv_get(dev); /* Link the consumer device with the smi-larb device(supplier) */ diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index 35ba090f3b5e..719652b60840 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -17,8 +17,6 @@ #include <linux/slab.h> #include <linux/fsl/mc.h> -#define NO_IOMMU 1 - static int of_iommu_xlate(struct device *dev, struct of_phandle_args *iommu_spec) { @@ -29,7 +27,7 @@ static int of_iommu_xlate(struct device *dev, ops = iommu_ops_from_fwnode(fwnode); if ((ops && !ops->of_xlate) || !of_device_is_available(iommu_spec->np)) - return NO_IOMMU; + return -ENODEV; ret = iommu_fwspec_init(dev, &iommu_spec->np->fwnode, ops); if (ret) @@ -61,7 +59,7 @@ static int of_iommu_configure_dev_id(struct device_node *master_np, "iommu-map-mask", &iommu_spec.np, iommu_spec.args); if (err) - return err == -ENODEV ? NO_IOMMU : err; + return err; err = of_iommu_xlate(dev, &iommu_spec); of_node_put(iommu_spec.np); @@ -72,7 +70,7 @@ static int of_iommu_configure_dev(struct device_node *master_np, struct device *dev) { struct of_phandle_args iommu_spec; - int err = NO_IOMMU, idx = 0; + int err = -ENODEV, idx = 0; while (!of_parse_phandle_with_args(master_np, "iommus", "#iommu-cells", @@ -107,16 +105,21 @@ static int of_iommu_configure_device(struct device_node *master_np, of_iommu_configure_dev(master_np, dev); } -const struct iommu_ops *of_iommu_configure(struct device *dev, - struct device_node *master_np, - const u32 *id) +/* + * Returns: + * 0 on success, an iommu was configured + * -ENODEV if the device does not have any IOMMU + * -EPROBEDEFER if probing should be tried again + * -errno fatal errors + */ +int of_iommu_configure(struct device *dev, struct device_node *master_np, + const u32 *id) { - const struct iommu_ops *ops = NULL; struct iommu_fwspec *fwspec; - int err = NO_IOMMU; + int err; if (!master_np) - return NULL; + return -ENODEV; /* Serialise to make dev->iommu stable under our potential fwspec */ mutex_lock(&iommu_probe_device_lock); @@ -124,7 +127,7 @@ const struct iommu_ops *of_iommu_configure(struct device *dev, if (fwspec) { if (fwspec->ops) { mutex_unlock(&iommu_probe_device_lock); - return fwspec->ops; + return 0; } /* In the deferred case, start again from scratch */ iommu_fwspec_free(dev); @@ -147,36 +150,21 @@ const struct iommu_ops *of_iommu_configure(struct device *dev, } else { err = of_iommu_configure_device(master_np, dev, id); } - - /* - * Two success conditions can be represented by non-negative err here: - * >0 : there is no IOMMU, or one was unavailable for non-fatal reasons - * 0 : we found an IOMMU, and dev->fwspec is initialised appropriately - * <0 : any actual error - */ - if (!err) { - /* The fwspec pointer changed, read it again */ - fwspec = dev_iommu_fwspec_get(dev); - ops = fwspec->ops; - } mutex_unlock(&iommu_probe_device_lock); - /* - * If we have reason to believe the IOMMU driver missed the initial - * probe for dev, replay it to get things in order. - */ - if (!err && dev->bus) - err = iommu_probe_device(dev); - - /* Ignore all other errors apart from EPROBE_DEFER */ - if (err == -EPROBE_DEFER) { - ops = ERR_PTR(err); - } else if (err < 0) { - dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); - ops = NULL; - } + if (err == -ENODEV || err == -EPROBE_DEFER) + return err; + if (err) + goto err_log; - return ops; + err = iommu_probe_device(dev); + if (err) + goto err_log; + return 0; + +err_log: + dev_dbg(dev, "Adding to IOMMU failed: %pe\n", ERR_PTR(err)); + return err; } static enum iommu_resv_type __maybe_unused @@ -260,7 +248,14 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list) phys_addr_t iova; size_t length; + if (of_dma_is_coherent(dev->of_node)) + prot |= IOMMU_CACHE; + maps = of_translate_dma_region(np, maps, &iova, &length); + if (length == 0) { + dev_warn(dev, "Cannot reserve IOVA region of 0 size\n"); + continue; + } type = iommu_resv_region_get_type(dev, &phys, iova, length); region = iommu_alloc_resv_region(iova, length, prot, type, diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index c66b070841dd..c9528065a59a 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -1719,7 +1719,6 @@ static void omap_iommu_release_device(struct device *dev) if (!dev->of_node || !arch_data) return; - dev_iommu_priv_set(dev, NULL); kfree(arch_data); } diff --git a/drivers/iommu/sprd-iommu.c b/drivers/iommu/sprd-iommu.c index 2eb9fb46703b..537359f10997 100644 --- a/drivers/iommu/sprd-iommu.c +++ b/drivers/iommu/sprd-iommu.c @@ -385,13 +385,7 @@ static phys_addr_t sprd_iommu_iova_to_phys(struct iommu_domain *domain, static struct iommu_device *sprd_iommu_probe_device(struct device *dev) { - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); - struct sprd_iommu_device *sdev; - - if (!fwspec || fwspec->ops != &sprd_iommu_ops) - return ERR_PTR(-ENODEV); - - sdev = dev_iommu_priv_get(dev); + struct sprd_iommu_device *sdev = dev_iommu_priv_get(dev); return &sdev->iommu; } diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c index 379ebe03efb6..34db37fd9675 100644 --- a/drivers/iommu/virtio-iommu.c +++ b/drivers/iommu/virtio-iommu.c @@ -843,7 +843,7 @@ static int viommu_map_pages(struct iommu_domain *domain, unsigned long iova, .flags = cpu_to_le32(flags), }; - ret = viommu_send_req_sync(vdomain->viommu, &map, sizeof(map)); + ret = viommu_add_req(vdomain->viommu, &map, sizeof(map)); if (ret) { viommu_del_mappings(vdomain, iova, end); return ret; @@ -912,6 +912,33 @@ static void viommu_iotlb_sync(struct iommu_domain *domain, viommu_sync_req(vdomain->viommu); } +static int viommu_iotlb_sync_map(struct iommu_domain *domain, + unsigned long iova, size_t size) +{ + struct viommu_domain *vdomain = to_viommu_domain(domain); + + /* + * May be called before the viommu is initialized including + * while creating direct mapping + */ + if (!vdomain->nr_endpoints) + return 0; + return viommu_sync_req(vdomain->viommu); +} + +static void viommu_flush_iotlb_all(struct iommu_domain *domain) +{ + struct viommu_domain *vdomain = to_viommu_domain(domain); + + /* + * May be called before the viommu is initialized including + * while creating direct mapping + */ + if (!vdomain->nr_endpoints) + return; + viommu_sync_req(vdomain->viommu); +} + static void viommu_get_resv_regions(struct device *dev, struct list_head *head) { struct iommu_resv_region *entry, *new_entry, *msi = NULL; @@ -969,9 +996,6 @@ static struct iommu_device *viommu_probe_device(struct device *dev) struct viommu_dev *viommu = NULL; struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); - if (!fwspec || fwspec->ops != &viommu_ops) - return ERR_PTR(-ENODEV); - viommu = viommu_get_by_fwnode(fwspec->iommu_fwnode); if (!viommu) return ERR_PTR(-ENODEV); @@ -1037,6 +1061,8 @@ static bool viommu_capable(struct device *dev, enum iommu_cap cap) switch (cap) { case IOMMU_CAP_CACHE_COHERENCY: return true; + case IOMMU_CAP_DEFERRED_FLUSH: + return true; default: return false; } @@ -1057,7 +1083,9 @@ static struct iommu_ops viommu_ops = { .map_pages = viommu_map_pages, .unmap_pages = viommu_unmap_pages, .iova_to_phys = viommu_iova_to_phys, + .flush_iotlb_all = viommu_flush_iotlb_all, .iotlb_sync = viommu_iotlb_sync, + .iotlb_sync_map = viommu_iotlb_sync_map, .free = viommu_domain_free, } }; diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index da308be6c487..ba2e9e52d72b 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c @@ -158,9 +158,7 @@ static int ipoctal_get_icount(struct tty_struct *tty, static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr) { struct tty_port *port = &channel->tty_port; - unsigned char value; - unsigned char flag; - u8 isr; + u8 isr, value, flag; do { value = ioread8(&channel->regs->r.rhr); @@ -202,8 +200,8 @@ static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr) static void ipoctal_irq_tx(struct ipoctal_channel *channel) { - unsigned char value; unsigned int *pointer_write = &channel->pointer_write; + u8 value; if (channel->nb_bytes == 0) return; @@ -436,11 +434,11 @@ err_put_driver: return res; } -static inline int ipoctal_copy_write_buffer(struct ipoctal_channel *channel, - const u8 *buf, int count) +static inline size_t ipoctal_copy_write_buffer(struct ipoctal_channel *channel, + const u8 *buf, size_t count) { unsigned long flags; - int i; + size_t i; unsigned int *pointer_read = &channel->pointer_read; /* Copy the bytes from the user buffer to the internal one */ @@ -462,7 +460,7 @@ static ssize_t ipoctal_write_tty(struct tty_struct *tty, const u8 *buf, size_t count) { struct ipoctal_channel *channel = tty->driver_data; - unsigned int char_copied; + size_t char_copied; char_copied = ipoctal_copy_write_buffer(channel, buf, count); diff --git a/drivers/ipack/ipack.c b/drivers/ipack/ipack.c index cc1ecfd49928..b1471ba016a5 100644 --- a/drivers/ipack/ipack.c +++ b/drivers/ipack/ipack.c @@ -207,7 +207,7 @@ struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots, if (!bus) return NULL; - bus_nr = ida_simple_get(&ipack_ida, 0, 0, GFP_KERNEL); + bus_nr = ida_alloc(&ipack_ida, GFP_KERNEL); if (bus_nr < 0) { kfree(bus); return NULL; @@ -237,7 +237,7 @@ int ipack_bus_unregister(struct ipack_bus_device *bus) { bus_for_each_dev(&ipack_bus_type, NULL, bus, ipack_unregister_bus_member); - ida_simple_remove(&ipack_ida, bus->bus_nr); + ida_free(&ipack_ida, bus->bus_nr); kfree(bus); return 0; } diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 2f3789515445..6e80d7bd3c4d 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -1231,9 +1231,9 @@ static void capinc_tty_hangup(struct tty_struct *tty) tty_port_hangup(&mp->port); } -static void capinc_tty_send_xchar(struct tty_struct *tty, char ch) +static void capinc_tty_send_xchar(struct tty_struct *tty, u8 ch) { - pr_debug("capinc_tty_send_xchar(%d)\n", ch); + pr_debug("capinc_tty_send_xchar(%u)\n", ch); } static const struct tty_operations capinc_ops = { diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 6292fddcc55c..d721b254e1e4 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -95,14 +95,18 @@ config LEDS_ARIEL Say Y to if your machine is a Dell Wyse 3020 thin client. config LEDS_AW200XX - tristate "LED support for Awinic AW20036/AW20054/AW20072" + tristate "LED support for Awinic AW20036/AW20054/AW20072/AW20108" depends on LEDS_CLASS depends on I2C help - This option enables support for the AW20036/AW20054/AW20072 LED driver. - It is a 3x12/6x9/6x12 matrix LED driver programmed via - an I2C interface, up to 36/54/72 LEDs or 12/18/24 RGBs, - 3 pattern controllers for auto breathing or group dimming control. + This option enables support for the Awinic AW200XX LED controllers. + It is a matrix LED driver programmed via an I2C interface. Devices have + a set of individually controlled LEDs and support 3 pattern controllers + for auto breathing or group dimming control. Supported devices: + - AW20036 (3x12) 36 LEDs + - AW20054 (6x9) 54 LEDs + - AW20072 (6x12) 72 LEDs + - AW20108 (9x12) 108 LEDs To compile this driver as a module, choose M here: the module will be called leds-aw200xx. @@ -110,6 +114,7 @@ config LEDS_AW200XX config LEDS_AW2013 tristate "LED support for Awinic AW2013" depends on LEDS_CLASS && I2C && OF + select REGMAP_I2C help This option enables support for the AW2013 3-channel LED driver. @@ -298,6 +303,15 @@ config LEDS_COBALT_RAQ help This option enables support for the Cobalt Raq series LEDs. +config LEDS_SUN50I_A100 + tristate "LED support for Allwinner A100 RGB LED controller" + depends on LEDS_CLASS_MULTICOLOR + depends on ARCH_SUNXI || COMPILE_TEST + help + This option enables support for the RGB LED controller found + in some Allwinner sunxi SoCs, including A100, R329, and D1. + It uses a one-wire interface to control up to 1024 LEDs. + config LEDS_SUNFIRE tristate "LED support for SunFire servers." depends on LEDS_CLASS @@ -638,6 +652,17 @@ config LEDS_ADP5520 To compile this driver as a module, choose M here: the module will be called leds-adp5520. +config LEDS_MAX5970 + tristate "LED Support for Maxim 5970" + depends on LEDS_CLASS + depends on MFD_MAX5970 + help + This option enables support for the Maxim MAX5970 & MAX5978 smart + switch indication LEDs via the I2C bus. + + To compile this driver as a module, choose M here: the module will + be called leds-max5970. + config LEDS_MC13783 tristate "LED Support for MC13XXX PMIC" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index d7348e8bc019..ce07dc295ff0 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o +obj-$(CONFIG_LEDS_MAX5970) += leds-max5970.o obj-$(CONFIG_LEDS_MAX77650) += leds-max77650.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o @@ -78,6 +79,7 @@ obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o obj-$(CONFIG_LEDS_PWM) += leds-pwm.o obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o +obj-$(CONFIG_LEDS_SUN50I_A100) += leds-sun50i-a100.o obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 6a5e1f41f9a4..bd59a14a4a90 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -269,19 +269,6 @@ void led_trigger_set_default(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_trigger_set_default); -void led_trigger_rename_static(const char *name, struct led_trigger *trig) -{ - /* new name must be on a temporary string to prevent races */ - BUG_ON(name == trig->name); - - down_write(&triggers_list_lock); - /* this assumes that trig->name was originaly allocated to - * non constant storage */ - strcpy((char *)trig->name, name); - up_write(&triggers_list_lock); -} -EXPORT_SYMBOL_GPL(led_trigger_rename_static); - /* LED Trigger Interface */ int led_trigger_register(struct led_trigger *trig) diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c index 14ca236ce29e..f584a7f98fc5 100644 --- a/drivers/leds/leds-aw200xx.c +++ b/drivers/leds/leds-aw200xx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Awinic AW20036/AW20054/AW20072 LED driver + * Awinic AW20036/AW20054/AW20072/AW20108 LED driver * * Copyright (c) 2023, SberDevices. All Rights Reserved. * @@ -10,6 +10,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> #include <linux/container_of.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/leds.h> #include <linux/mod_devicetable.h> @@ -74,6 +75,10 @@ #define AW200XX_LED2REG(x, columns) \ ((x) + (((x) / (columns)) * (AW200XX_DSIZE_COLUMNS_MAX - (columns)))) +/* DIM current configuration register on page 1 */ +#define AW200XX_REG_DIM_PAGE1(x, columns) \ + AW200XX_REG(AW200XX_PAGE1, AW200XX_LED2REG(x, columns)) + /* * DIM current configuration register (page 4). * The even address for current DIM configuration. @@ -82,6 +87,8 @@ #define AW200XX_REG_DIM(x, columns) \ AW200XX_REG(AW200XX_PAGE4, AW200XX_LED2REG(x, columns) * 2) #define AW200XX_REG_DIM2FADE(x) ((x) + 1) +#define AW200XX_REG_FADE2DIM(fade) \ + DIV_ROUND_UP((fade) * AW200XX_DIM_MAX, AW200XX_FADE_MAX) /* * Duty ratio of display scan (see p.15 of datasheet for formula): @@ -112,6 +119,7 @@ struct aw200xx { struct mutex mutex; u32 num_leds; u32 display_rows; + struct gpio_desc *hwen; struct aw200xx_led leds[] __counted_by(num_leds); }; @@ -153,7 +161,8 @@ static ssize_t dim_store(struct device *dev, struct device_attribute *devattr, if (dim >= 0) { ret = regmap_write(chip->regmap, - AW200XX_REG_DIM(led->num, columns), dim); + AW200XX_REG_DIM_PAGE1(led->num, columns), + dim); if (ret) goto out_unlock; } @@ -188,9 +197,7 @@ static int aw200xx_brightness_set(struct led_classdev *cdev, dim = led->dim; if (dim < 0) - dim = max_t(int, - brightness / (AW200XX_FADE_MAX / AW200XX_DIM_MAX), - 1); + dim = AW200XX_REG_FADE2DIM(brightness); ret = regmap_write(chip->regmap, reg, dim); if (ret) @@ -314,6 +321,9 @@ static int aw200xx_chip_reset(const struct aw200xx *const chip) if (ret) return ret; + /* According to the datasheet software reset takes at least 1ms */ + fsleep(1000); + regcache_mark_dirty(chip->regmap); return regmap_write(chip->regmap, AW200XX_REG_FCD, AW200XX_FCD_CLEAR); } @@ -353,6 +363,50 @@ static int aw200xx_chip_check(const struct aw200xx *const chip) return 0; } +static void aw200xx_enable(const struct aw200xx *const chip) +{ + gpiod_set_value_cansleep(chip->hwen, 1); + + /* + * After HWEN pin set high the chip begins to load the OTP information, + * which takes 200us to complete. About 200us wait time is needed for + * internal oscillator startup and display SRAM initialization. After + * display SRAM initialization, the registers in page1 to page5 can be + * configured via i2c interface. + */ + fsleep(400); +} + +static void aw200xx_disable(const struct aw200xx *const chip) +{ + return gpiod_set_value_cansleep(chip->hwen, 0); +} + +static int aw200xx_probe_get_display_rows(struct device *dev, + struct aw200xx *chip) +{ + struct fwnode_handle *child; + u32 max_source = 0; + + device_for_each_child_node(dev, child) { + u32 source; + int ret; + + ret = fwnode_property_read_u32(child, "reg", &source); + if (ret || source >= chip->cdef->channels) + continue; + + max_source = max(max_source, source); + } + + if (max_source == 0) + return -EINVAL; + + chip->display_rows = max_source / chip->cdef->display_size_columns + 1; + + return 0; +} + static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip) { struct fwnode_handle *child; @@ -360,18 +414,10 @@ static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip) int ret; int i; - ret = device_property_read_u32(dev, "awinic,display-rows", - &chip->display_rows); + ret = aw200xx_probe_get_display_rows(dev, chip); if (ret) return dev_err_probe(dev, ret, - "Failed to read 'display-rows' property\n"); - - if (!chip->display_rows || - chip->display_rows > chip->cdef->display_size_rows_max) { - return dev_err_probe(dev, -EINVAL, - "Invalid leds display size %u\n", - chip->display_rows); - } + "No valid led definitions found\n"); current_max = aw200xx_imax_from_global(chip, AW200XX_IMAX_MAX_uA); current_min = aw200xx_imax_from_global(chip, AW200XX_IMAX_MIN_uA); @@ -416,6 +462,7 @@ static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip) led->num = source; led->chip = chip; led->cdev.brightness_set_blocking = aw200xx_brightness_set; + led->cdev.max_brightness = AW200XX_FADE_MAX; led->cdev.groups = dim_groups; init_data.fwnode = child; @@ -480,6 +527,7 @@ static const struct regmap_config aw200xx_regmap_config = { .rd_table = &aw200xx_readable_table, .wr_table = &aw200xx_writeable_table, .cache_type = REGCACHE_MAPLE, + .disable_locking = true, }; static int aw200xx_probe(struct i2c_client *client) @@ -512,6 +560,14 @@ static int aw200xx_probe(struct i2c_client *client) if (IS_ERR(chip->regmap)) return PTR_ERR(chip->regmap); + chip->hwen = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(chip->hwen)) + return dev_err_probe(&client->dev, PTR_ERR(chip->hwen), + "Cannot get enable GPIO"); + + aw200xx_enable(chip); + ret = aw200xx_chip_check(chip); if (ret) return ret; @@ -532,6 +588,9 @@ static int aw200xx_probe(struct i2c_client *client) ret = aw200xx_chip_init(chip); out_unlock: + if (ret) + aw200xx_disable(chip); + mutex_unlock(&chip->mutex); return ret; } @@ -541,6 +600,7 @@ static void aw200xx_remove(struct i2c_client *client) struct aw200xx *chip = i2c_get_clientdata(client); aw200xx_chip_reset(chip); + aw200xx_disable(chip); mutex_destroy(&chip->mutex); } @@ -562,10 +622,17 @@ static const struct aw200xx_chipdef aw20072_cdef = { .display_size_columns = 12, }; +static const struct aw200xx_chipdef aw20108_cdef = { + .channels = 108, + .display_size_rows_max = 9, + .display_size_columns = 12, +}; + static const struct i2c_device_id aw200xx_id[] = { { "aw20036" }, { "aw20054" }, { "aw20072" }, + { "aw20108" }, {} }; MODULE_DEVICE_TABLE(i2c, aw200xx_id); @@ -574,6 +641,7 @@ static const struct of_device_id aw200xx_match_table[] = { { .compatible = "awinic,aw20036", .data = &aw20036_cdef, }, { .compatible = "awinic,aw20054", .data = &aw20054_cdef, }, { .compatible = "awinic,aw20072", .data = &aw20072_cdef, }, + { .compatible = "awinic,aw20108", .data = &aw20108_cdef, }, {} }; MODULE_DEVICE_TABLE(of, aw200xx_match_table); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 710c319ad312..83fcd7b6afff 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -172,6 +172,8 @@ static struct gpio_leds_priv *gpio_leds_create(struct device *dev) led.gpiod = devm_fwnode_gpiod_get(dev, child, NULL, GPIOD_ASIS, NULL); if (IS_ERR(led.gpiod)) { + dev_err_probe(dev, PTR_ERR(led.gpiod), "Failed to get GPIO '%pfw'\n", + child); fwnode_handle_put(child); return ERR_CAST(led.gpiod); } diff --git a/drivers/leds/leds-max5970.c b/drivers/leds/leds-max5970.c new file mode 100644 index 000000000000..56a584311581 --- /dev/null +++ b/drivers/leds/leds-max5970.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device driver for leds in MAX5970 and MAX5978 IC + * + * Copyright (c) 2022 9elements GmbH + * + * Author: Patrick Rudolph <patrick.rudolph@9elements.com> + */ + +#include <linux/bits.h> +#include <linux/container_of.h> +#include <linux/device.h> +#include <linux/leds.h> +#include <linux/mfd/max5970.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> + +#define ldev_to_maxled(c) container_of(c, struct max5970_led, cdev) + +struct max5970_led { + struct device *dev; + struct regmap *regmap; + struct led_classdev cdev; + unsigned int index; +}; + +static int max5970_led_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct max5970_led *ddata = ldev_to_maxled(cdev); + int ret, val; + + /* Set/clear corresponding bit for given led index */ + val = !brightness ? BIT(ddata->index) : 0; + + ret = regmap_update_bits(ddata->regmap, MAX5970_REG_LED_FLASH, BIT(ddata->index), val); + if (ret < 0) + dev_err(cdev->dev, "failed to set brightness %d", ret); + + return ret; +} + +static int max5970_led_probe(struct platform_device *pdev) +{ + struct fwnode_handle *led_node, *child; + struct device *dev = &pdev->dev; + struct regmap *regmap; + struct max5970_led *ddata; + int ret = -ENODEV; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + led_node = device_get_named_child_node(dev->parent, "leds"); + if (!led_node) + return -ENODEV; + + fwnode_for_each_available_child_node(led_node, child) { + u32 reg; + + if (fwnode_property_read_u32(child, "reg", ®)) + continue; + + if (reg >= MAX5970_NUM_LEDS) { + dev_err_probe(dev, -EINVAL, "invalid LED (%u >= %d)\n", reg, MAX5970_NUM_LEDS); + continue; + } + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) { + fwnode_handle_put(child); + return -ENOMEM; + } + + ddata->index = reg; + ddata->regmap = regmap; + ddata->dev = dev; + + if (fwnode_property_read_string(child, "label", &ddata->cdev.name)) + ddata->cdev.name = fwnode_get_name(child); + + ddata->cdev.max_brightness = 1; + ddata->cdev.brightness_set_blocking = max5970_led_set_brightness; + ddata->cdev.default_trigger = "none"; + + ret = devm_led_classdev_register(dev, &ddata->cdev); + if (ret < 0) { + fwnode_handle_put(child); + return dev_err_probe(dev, ret, "Failed to initialize LED %u\n", reg); + } + } + + return ret; +} + +static struct platform_driver max5970_led_driver = { + .driver = { + .name = "max5970-led", + }, + .probe = max5970_led_probe, +}; +module_platform_driver(max5970_led_driver); + +MODULE_AUTHOR("Patrick Rudolph <patrick.rudolph@9elements.com>"); +MODULE_AUTHOR("Naresh Solanki <Naresh.Solanki@9elements.com>"); +MODULE_DESCRIPTION("MAX5970_hot-swap controller LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 2b3bf1353b70..4e3936a39d0e 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -54,7 +54,7 @@ static int led_pwm_set(struct led_classdev *led_cdev, led_dat->pwmstate.duty_cycle = duty; led_dat->pwmstate.enabled = true; - return pwm_apply_state(led_dat->pwm, &led_dat->pwmstate); + return pwm_apply_might_sleep(led_dat->pwm, &led_dat->pwmstate); } __attribute__((nonnull)) diff --git a/drivers/leds/leds-sun50i-a100.c b/drivers/leds/leds-sun50i-a100.c new file mode 100644 index 000000000000..62d21c3a3575 --- /dev/null +++ b/drivers/leds/leds-sun50i-a100.c @@ -0,0 +1,584 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021-2023 Samuel Holland <samuel@sholland.org> + * + * Partly based on drivers/leds/leds-turris-omnia.c, which is: + * Copyright (c) 2020 by Marek Behún <kabel@kernel.org> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/led-class-multicolor.h> +#include <linux/leds.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/property.h> +#include <linux/reset.h> +#include <linux/spinlock.h> + +#define LEDC_CTRL_REG 0x0000 +#define LEDC_CTRL_REG_DATA_LENGTH GENMASK(28, 16) +#define LEDC_CTRL_REG_RGB_MODE GENMASK(8, 6) +#define LEDC_CTRL_REG_LEDC_EN BIT(0) +#define LEDC_T01_TIMING_CTRL_REG 0x0004 +#define LEDC_T01_TIMING_CTRL_REG_T1H GENMASK(26, 21) +#define LEDC_T01_TIMING_CTRL_REG_T1L GENMASK(20, 16) +#define LEDC_T01_TIMING_CTRL_REG_T0H GENMASK(10, 6) +#define LEDC_T01_TIMING_CTRL_REG_T0L GENMASK(5, 0) +#define LEDC_RESET_TIMING_CTRL_REG 0x000c +#define LEDC_RESET_TIMING_CTRL_REG_TR GENMASK(28, 16) +#define LEDC_RESET_TIMING_CTRL_REG_LED_NUM GENMASK(9, 0) +#define LEDC_DATA_REG 0x0014 +#define LEDC_DMA_CTRL_REG 0x0018 +#define LEDC_DMA_CTRL_REG_DMA_EN BIT(5) +#define LEDC_DMA_CTRL_REG_FIFO_TRIG_LEVEL GENMASK(4, 0) +#define LEDC_INT_CTRL_REG 0x001c +#define LEDC_INT_CTRL_REG_GLOBAL_INT_EN BIT(5) +#define LEDC_INT_CTRL_REG_FIFO_CPUREQ_INT_EN BIT(1) +#define LEDC_INT_CTRL_REG_TRANS_FINISH_INT_EN BIT(0) +#define LEDC_INT_STS_REG 0x0020 +#define LEDC_INT_STS_REG_FIFO_WLW GENMASK(15, 10) +#define LEDC_INT_STS_REG_FIFO_CPUREQ_INT BIT(1) +#define LEDC_INT_STS_REG_TRANS_FINISH_INT BIT(0) + +#define LEDC_FIFO_DEPTH 32U +#define LEDC_MAX_LEDS 1024 +#define LEDC_CHANNELS_PER_LED 3 /* RGB */ + +#define LEDS_TO_BYTES(n) ((n) * sizeof(u32)) + +struct sun50i_a100_ledc_led { + struct led_classdev_mc mc_cdev; + struct mc_subled subled_info[LEDC_CHANNELS_PER_LED]; + u32 addr; +}; + +#define to_ledc_led(mc) container_of(mc, struct sun50i_a100_ledc_led, mc_cdev) + +struct sun50i_a100_ledc_timing { + u32 t0h_ns; + u32 t0l_ns; + u32 t1h_ns; + u32 t1l_ns; + u32 treset_ns; +}; + +struct sun50i_a100_ledc { + struct device *dev; + void __iomem *base; + struct clk *bus_clk; + struct clk *mod_clk; + struct reset_control *reset; + + u32 *buffer; + struct dma_chan *dma_chan; + dma_addr_t dma_handle; + unsigned int pio_length; + unsigned int pio_offset; + + spinlock_t lock; + unsigned int next_length; + bool xfer_active; + + u32 format; + struct sun50i_a100_ledc_timing timing; + + u32 max_addr; + u32 num_leds; + struct sun50i_a100_ledc_led leds[] __counted_by(num_leds); +}; + +static int sun50i_a100_ledc_dma_xfer(struct sun50i_a100_ledc *priv, unsigned int length) +{ + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + + desc = dmaengine_prep_slave_single(priv->dma_chan, priv->dma_handle, + LEDS_TO_BYTES(length), DMA_MEM_TO_DEV, 0); + if (!desc) + return -ENOMEM; + + cookie = dmaengine_submit(desc); + if (dma_submit_error(cookie)) + return -EIO; + + dma_async_issue_pending(priv->dma_chan); + + return 0; +} + +static void sun50i_a100_ledc_pio_xfer(struct sun50i_a100_ledc *priv, unsigned int fifo_used) +{ + unsigned int burst, length, offset; + u32 control; + + length = priv->pio_length; + offset = priv->pio_offset; + burst = min(length, LEDC_FIFO_DEPTH - fifo_used); + + iowrite32_rep(priv->base + LEDC_DATA_REG, priv->buffer + offset, burst); + + if (burst < length) { + priv->pio_length = length - burst; + priv->pio_offset = offset + burst; + + if (!offset) { + control = readl(priv->base + LEDC_INT_CTRL_REG); + control |= LEDC_INT_CTRL_REG_FIFO_CPUREQ_INT_EN; + writel(control, priv->base + LEDC_INT_CTRL_REG); + } + } else { + /* Disable the request IRQ once all data is written. */ + control = readl(priv->base + LEDC_INT_CTRL_REG); + control &= ~LEDC_INT_CTRL_REG_FIFO_CPUREQ_INT_EN; + writel(control, priv->base + LEDC_INT_CTRL_REG); + } +} + +static void sun50i_a100_ledc_start_xfer(struct sun50i_a100_ledc *priv, unsigned int length) +{ + bool use_dma = false; + u32 control; + + if (priv->dma_chan && length > LEDC_FIFO_DEPTH) { + int ret; + + ret = sun50i_a100_ledc_dma_xfer(priv, length); + if (ret) + dev_warn(priv->dev, "Failed to set up DMA (%d), using PIO\n", ret); + else + use_dma = true; + } + + /* The DMA trigger level must be at least the burst length. */ + control = FIELD_PREP(LEDC_DMA_CTRL_REG_DMA_EN, use_dma) | + FIELD_PREP_CONST(LEDC_DMA_CTRL_REG_FIFO_TRIG_LEVEL, LEDC_FIFO_DEPTH / 2); + writel(control, priv->base + LEDC_DMA_CTRL_REG); + + control = readl(priv->base + LEDC_CTRL_REG); + control &= ~LEDC_CTRL_REG_DATA_LENGTH; + control |= FIELD_PREP(LEDC_CTRL_REG_DATA_LENGTH, length) | LEDC_CTRL_REG_LEDC_EN; + writel(control, priv->base + LEDC_CTRL_REG); + + if (!use_dma) { + /* The FIFO is empty when starting a new transfer. */ + unsigned int fifo_used = 0; + + priv->pio_length = length; + priv->pio_offset = 0; + + sun50i_a100_ledc_pio_xfer(priv, fifo_used); + } +} + +static irqreturn_t sun50i_a100_ledc_irq(int irq, void *data) +{ + struct sun50i_a100_ledc *priv = data; + u32 status; + + status = readl(priv->base + LEDC_INT_STS_REG); + + if (status & LEDC_INT_STS_REG_TRANS_FINISH_INT) { + unsigned int next_length; + + spin_lock(&priv->lock); + + /* If another transfer is queued, dequeue and start it. */ + next_length = priv->next_length; + if (next_length) + priv->next_length = 0; + else + priv->xfer_active = false; + + spin_unlock(&priv->lock); + + if (next_length) + sun50i_a100_ledc_start_xfer(priv, next_length); + } else if (status & LEDC_INT_STS_REG_FIFO_CPUREQ_INT) { + /* Continue the current transfer. */ + sun50i_a100_ledc_pio_xfer(priv, FIELD_GET(LEDC_INT_STS_REG_FIFO_WLW, status)); + } + + /* Clear the W1C status bits. */ + writel(status, priv->base + LEDC_INT_STS_REG); + + return IRQ_HANDLED; +} + +static void sun50i_a100_ledc_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct sun50i_a100_ledc *priv = dev_get_drvdata(cdev->dev->parent); + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); + struct sun50i_a100_ledc_led *led = to_ledc_led(mc_cdev); + unsigned int next_length; + unsigned long flags; + bool xfer_active; + + led_mc_calc_color_components(mc_cdev, brightness); + + priv->buffer[led->addr] = led->subled_info[0].brightness << 16 | + led->subled_info[1].brightness << 8 | + led->subled_info[2].brightness; + + spin_lock_irqsave(&priv->lock, flags); + + /* Start, enqueue, or extend an enqueued transfer, as appropriate. */ + next_length = max(priv->next_length, led->addr + 1); + xfer_active = priv->xfer_active; + if (xfer_active) + priv->next_length = next_length; + else + priv->xfer_active = true; + + spin_unlock_irqrestore(&priv->lock, flags); + + if (!xfer_active) + sun50i_a100_ledc_start_xfer(priv, next_length); +} + +static const char *const sun50i_a100_ledc_formats[] = { + "rgb", "rbg", "grb", "gbr", "brg", "bgr", +}; + +static int sun50i_a100_ledc_parse_format(struct device *dev, + struct sun50i_a100_ledc *priv) +{ + const char *format = "grb"; + u32 i; + + device_property_read_string(dev, "allwinner,pixel-format", &format); + + for (i = 0; i < ARRAY_SIZE(sun50i_a100_ledc_formats); i++) { + if (!strcmp(format, sun50i_a100_ledc_formats[i])) { + priv->format = i; + return 0; + } + } + + return dev_err_probe(dev, -EINVAL, "Bad pixel format '%s'\n", format); +} + +static void sun50i_a100_ledc_set_format(struct sun50i_a100_ledc *priv) +{ + u32 control; + + control = readl(priv->base + LEDC_CTRL_REG); + control &= ~LEDC_CTRL_REG_RGB_MODE; + control |= FIELD_PREP(LEDC_CTRL_REG_RGB_MODE, priv->format); + writel(control, priv->base + LEDC_CTRL_REG); +} + +static const struct sun50i_a100_ledc_timing sun50i_a100_ledc_default_timing = { + .t0h_ns = 336, + .t0l_ns = 840, + .t1h_ns = 882, + .t1l_ns = 294, + .treset_ns = 300000, +}; + +static int sun50i_a100_ledc_parse_timing(struct device *dev, + struct sun50i_a100_ledc *priv) +{ + struct sun50i_a100_ledc_timing *timing = &priv->timing; + + *timing = sun50i_a100_ledc_default_timing; + + device_property_read_u32(dev, "allwinner,t0h-ns", &timing->t0h_ns); + device_property_read_u32(dev, "allwinner,t0l-ns", &timing->t0l_ns); + device_property_read_u32(dev, "allwinner,t1h-ns", &timing->t1h_ns); + device_property_read_u32(dev, "allwinner,t1l-ns", &timing->t1l_ns); + device_property_read_u32(dev, "allwinner,treset-ns", &timing->treset_ns); + + return 0; +} + +static void sun50i_a100_ledc_set_timing(struct sun50i_a100_ledc *priv) +{ + const struct sun50i_a100_ledc_timing *timing = &priv->timing; + unsigned long mod_freq = clk_get_rate(priv->mod_clk); + u32 cycle_ns; + u32 control; + + if (!mod_freq) + return; + + cycle_ns = NSEC_PER_SEC / mod_freq; + control = FIELD_PREP(LEDC_T01_TIMING_CTRL_REG_T1H, timing->t1h_ns / cycle_ns) | + FIELD_PREP(LEDC_T01_TIMING_CTRL_REG_T1L, timing->t1l_ns / cycle_ns) | + FIELD_PREP(LEDC_T01_TIMING_CTRL_REG_T0H, timing->t0h_ns / cycle_ns) | + FIELD_PREP(LEDC_T01_TIMING_CTRL_REG_T0L, timing->t0l_ns / cycle_ns); + writel(control, priv->base + LEDC_T01_TIMING_CTRL_REG); + + control = FIELD_PREP(LEDC_RESET_TIMING_CTRL_REG_TR, timing->treset_ns / cycle_ns) | + FIELD_PREP(LEDC_RESET_TIMING_CTRL_REG_LED_NUM, priv->max_addr); + writel(control, priv->base + LEDC_RESET_TIMING_CTRL_REG); +} + +static int sun50i_a100_ledc_resume(struct device *dev) +{ + struct sun50i_a100_ledc *priv = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(priv->reset); + if (ret) + return ret; + + ret = clk_prepare_enable(priv->bus_clk); + if (ret) + goto err_assert_reset; + + ret = clk_prepare_enable(priv->mod_clk); + if (ret) + goto err_disable_bus_clk; + + sun50i_a100_ledc_set_format(priv); + sun50i_a100_ledc_set_timing(priv); + + writel(LEDC_INT_CTRL_REG_GLOBAL_INT_EN | LEDC_INT_CTRL_REG_TRANS_FINISH_INT_EN, + priv->base + LEDC_INT_CTRL_REG); + + return 0; + +err_disable_bus_clk: + clk_disable_unprepare(priv->bus_clk); +err_assert_reset: + reset_control_assert(priv->reset); + + return ret; +} + +static int sun50i_a100_ledc_suspend(struct device *dev) +{ + struct sun50i_a100_ledc *priv = dev_get_drvdata(dev); + + /* Wait for all transfers to complete. */ + for (;;) { + unsigned long flags; + bool xfer_active; + + spin_lock_irqsave(&priv->lock, flags); + xfer_active = priv->xfer_active; + spin_unlock_irqrestore(&priv->lock, flags); + if (!xfer_active) + break; + + msleep(1); + } + + clk_disable_unprepare(priv->mod_clk); + clk_disable_unprepare(priv->bus_clk); + reset_control_assert(priv->reset); + + return 0; +} + +static void sun50i_a100_ledc_dma_cleanup(void *data) +{ + struct sun50i_a100_ledc *priv = data; + + dma_release_channel(priv->dma_chan); +} + +static int sun50i_a100_ledc_probe(struct platform_device *pdev) +{ + struct dma_slave_config dma_cfg = {}; + struct led_init_data init_data = {}; + struct sun50i_a100_ledc_led *led; + struct device *dev = &pdev->dev; + struct sun50i_a100_ledc *priv; + struct fwnode_handle *child; + struct resource *mem; + u32 max_addr = 0; + u32 num_leds = 0; + int irq, ret; + + /* + * The maximum LED address must be known in sun50i_a100_ledc_resume() before + * class device registration, so parse and validate the subnodes up front. + */ + device_for_each_child_node(dev, child) { + u32 addr, color; + + ret = fwnode_property_read_u32(child, "reg", &addr); + if (ret || addr >= LEDC_MAX_LEDS) { + fwnode_handle_put(child); + return dev_err_probe(dev, -EINVAL, "'reg' must be between 0 and %d\n", + LEDC_MAX_LEDS - 1); + } + + ret = fwnode_property_read_u32(child, "color", &color); + if (ret || color != LED_COLOR_ID_RGB) { + fwnode_handle_put(child); + return dev_err_probe(dev, -EINVAL, "'color' must be LED_COLOR_ID_RGB\n"); + } + + max_addr = max(max_addr, addr); + num_leds++; + } + + if (!num_leds) + return -ENODEV; + + priv = devm_kzalloc(dev, struct_size(priv, leds, num_leds), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->max_addr = max_addr; + priv->num_leds = num_leds; + spin_lock_init(&priv->lock); + dev_set_drvdata(dev, priv); + + ret = sun50i_a100_ledc_parse_format(dev, priv); + if (ret) + return ret; + + ret = sun50i_a100_ledc_parse_timing(dev, priv); + if (ret) + return ret; + + priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(priv->bus_clk)) + return PTR_ERR(priv->bus_clk); + + priv->mod_clk = devm_clk_get(dev, "mod"); + if (IS_ERR(priv->mod_clk)) + return PTR_ERR(priv->mod_clk); + + priv->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(priv->reset)) + return PTR_ERR(priv->reset); + + priv->dma_chan = dma_request_chan(dev, "tx"); + if (IS_ERR(priv->dma_chan)) { + if (PTR_ERR(priv->dma_chan) != -ENODEV) + return PTR_ERR(priv->dma_chan); + + priv->dma_chan = NULL; + + priv->buffer = devm_kzalloc(dev, LEDS_TO_BYTES(LEDC_MAX_LEDS), GFP_KERNEL); + if (!priv->buffer) + return -ENOMEM; + } else { + ret = devm_add_action_or_reset(dev, sun50i_a100_ledc_dma_cleanup, priv); + if (ret) + return ret; + + dma_cfg.dst_addr = mem->start + LEDC_DATA_REG; + dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_cfg.dst_maxburst = LEDC_FIFO_DEPTH / 2; + + ret = dmaengine_slave_config(priv->dma_chan, &dma_cfg); + if (ret) + return ret; + + priv->buffer = dmam_alloc_attrs(dmaengine_get_dma_device(priv->dma_chan), + LEDS_TO_BYTES(LEDC_MAX_LEDS), &priv->dma_handle, + GFP_KERNEL, DMA_ATTR_WRITE_COMBINE); + if (!priv->buffer) + return -ENOMEM; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, sun50i_a100_ledc_irq, 0, dev_name(dev), priv); + if (ret) + return ret; + + ret = sun50i_a100_ledc_resume(dev); + if (ret) + return ret; + + led = priv->leds; + device_for_each_child_node(dev, child) { + struct led_classdev *cdev; + + /* The node was already validated above. */ + fwnode_property_read_u32(child, "reg", &led->addr); + + led->subled_info[0].color_index = LED_COLOR_ID_RED; + led->subled_info[0].channel = 0; + led->subled_info[1].color_index = LED_COLOR_ID_GREEN; + led->subled_info[1].channel = 1; + led->subled_info[2].color_index = LED_COLOR_ID_BLUE; + led->subled_info[2].channel = 2; + + led->mc_cdev.num_colors = ARRAY_SIZE(led->subled_info); + led->mc_cdev.subled_info = led->subled_info; + + cdev = &led->mc_cdev.led_cdev; + cdev->max_brightness = U8_MAX; + cdev->brightness_set = sun50i_a100_ledc_brightness_set; + + init_data.fwnode = child; + + ret = led_classdev_multicolor_register_ext(dev, &led->mc_cdev, &init_data); + if (ret) { + dev_err_probe(dev, ret, "Failed to register multicolor LED %u", led->addr); + goto err_put_child; + } + + led++; + } + + dev_info(dev, "Registered %u LEDs\n", num_leds); + + return 0; + +err_put_child: + fwnode_handle_put(child); + while (led-- > priv->leds) + led_classdev_multicolor_unregister(&led->mc_cdev); + sun50i_a100_ledc_suspend(&pdev->dev); + + return ret; +} + +static void sun50i_a100_ledc_remove(struct platform_device *pdev) +{ + struct sun50i_a100_ledc *priv = platform_get_drvdata(pdev); + + for (u32 i = 0; i < priv->num_leds; i++) + led_classdev_multicolor_unregister(&priv->leds[i].mc_cdev); + sun50i_a100_ledc_suspend(&pdev->dev); +} + +static const struct of_device_id sun50i_a100_ledc_of_match[] = { + { .compatible = "allwinner,sun50i-a100-ledc" }, + {} +}; +MODULE_DEVICE_TABLE(of, sun50i_a100_ledc_of_match); + +static DEFINE_SIMPLE_DEV_PM_OPS(sun50i_a100_ledc_pm, + sun50i_a100_ledc_suspend, + sun50i_a100_ledc_resume); + +static struct platform_driver sun50i_a100_ledc_driver = { + .probe = sun50i_a100_ledc_probe, + .remove_new = sun50i_a100_ledc_remove, + .shutdown = sun50i_a100_ledc_remove, + .driver = { + .name = "sun50i-a100-ledc", + .of_match_table = sun50i_a100_ledc_of_match, + .pm = pm_ptr(&sun50i_a100_ledc_pm), + }, +}; +module_platform_driver(sun50i_a100_ledc_driver); + +MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>"); +MODULE_DESCRIPTION("Allwinner A100 LED controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-syscon.c b/drivers/leds/leds-syscon.c index 360a376fa738..d633ad519d0c 100644 --- a/drivers/leds/leds-syscon.c +++ b/drivers/leds/leds-syscon.c @@ -81,7 +81,8 @@ static int syscon_led_probe(struct platform_device *pdev) sled->map = map; - if (of_property_read_u32(np, "offset", &sled->offset)) + if (of_property_read_u32(np, "reg", &sled->offset) && + of_property_read_u32(np, "offset", &sled->offset)) return -EINVAL; if (of_property_read_u32(np, "mask", &sled->mask)) return -EINVAL; diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index e19074614095..4f22f4224946 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -638,19 +638,13 @@ static int tca6507_probe_gpios(struct device *dev, tca->gpio.direction_output = tca6507_gpio_direction_output; tca->gpio.set = tca6507_gpio_set_value; tca->gpio.parent = dev; - err = gpiochip_add_data(&tca->gpio, tca); + err = devm_gpiochip_add_data(dev, &tca->gpio, tca); if (err) { tca->gpio.ngpio = 0; return err; } return 0; } - -static void tca6507_remove_gpio(struct tca6507_chip *tca) -{ - if (tca->gpio.ngpio) - gpiochip_remove(&tca->gpio); -} #else /* CONFIG_GPIOLIB */ static int tca6507_probe_gpios(struct device *dev, struct tca6507_chip *tca, @@ -658,9 +652,6 @@ static int tca6507_probe_gpios(struct device *dev, { return 0; } -static void tca6507_remove_gpio(struct tca6507_chip *tca) -{ -} #endif /* CONFIG_GPIOLIB */ static struct tca6507_platform_data * @@ -762,38 +753,25 @@ static int tca6507_probe(struct i2c_client *client) l->led_cdev.brightness_set = tca6507_brightness_set; l->led_cdev.blink_set = tca6507_blink_set; l->bank = -1; - err = led_classdev_register(dev, &l->led_cdev); + err = devm_led_classdev_register(dev, &l->led_cdev); if (err < 0) - goto exit; + return err; } } err = tca6507_probe_gpios(dev, tca, pdata); if (err) - goto exit; + return err; /* set all registers to known state - zero */ tca->reg_set = 0x7f; schedule_work(&tca->work); return 0; -exit: - while (i--) { - if (tca->leds[i].led_cdev.name) - led_classdev_unregister(&tca->leds[i].led_cdev); - } - return err; } static void tca6507_remove(struct i2c_client *client) { - int i; struct tca6507_chip *tca = i2c_get_clientdata(client); - struct tca6507_led *tca_leds = tca->leds; - for (i = 0; i < NUM_LEDS; i++) { - if (tca_leds[i].led_cdev.name) - led_classdev_unregister(&tca_leds[i].led_cdev); - } - tca6507_remove_gpio(tca); cancel_work_sync(&tca->work); } diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig index a6a21f564673..e66bd21b9852 100644 --- a/drivers/leds/rgb/Kconfig +++ b/drivers/leds/rgb/Kconfig @@ -4,7 +4,7 @@ if LEDS_CLASS_MULTICOLOR config LEDS_GROUP_MULTICOLOR tristate "LEDs group multi-color support" - depends on OF || COMPILE_TEST + depends on OF help This option enables support for monochrome LEDs that are grouped into multicolor LEDs which is useful in the case where LEDs of diff --git a/drivers/leds/rgb/leds-pwm-multicolor.c b/drivers/leds/rgb/leds-pwm-multicolor.c index 46cd062b8b24..e1a81e0109e8 100644 --- a/drivers/leds/rgb/leds-pwm-multicolor.c +++ b/drivers/leds/rgb/leds-pwm-multicolor.c @@ -51,8 +51,8 @@ static int led_pwm_mc_set(struct led_classdev *cdev, priv->leds[i].state.duty_cycle = duty; priv->leds[i].state.enabled = duty > 0; - ret = pwm_apply_state(priv->leds[i].pwm, - &priv->leds[i].state); + ret = pwm_apply_might_sleep(priv->leds[i].pwm, + &priv->leds[i].state); if (ret) break; } diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index 68d82a682bf6..156b73d1f4a2 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -552,9 +552,9 @@ static int lpg_parse_dtest(struct lpg *lpg) ret = count; goto err_malformed; } else if (count != lpg->data->num_channels * 2) { - dev_err(lpg->dev, "qcom,dtest needs to be %d items\n", - lpg->data->num_channels * 2); - return -EINVAL; + return dev_err_probe(lpg->dev, -EINVAL, + "qcom,dtest needs to be %d items\n", + lpg->data->num_channels * 2); } for (i = 0; i < lpg->data->num_channels; i++) { @@ -574,8 +574,7 @@ static int lpg_parse_dtest(struct lpg *lpg) return 0; err_malformed: - dev_err(lpg->dev, "malformed qcom,dtest\n"); - return ret; + return dev_err_probe(lpg->dev, ret, "malformed qcom,dtest\n"); } static void lpg_apply_dtest(struct lpg_channel *chan) @@ -977,9 +976,14 @@ static int lpg_pattern_mc_clear(struct led_classdev *cdev) return lpg_pattern_clear(led); } +static inline struct lpg *lpg_pwm_from_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct lpg, pwm); +} + static int lpg_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) { - struct lpg *lpg = container_of(chip, struct lpg, pwm); + struct lpg *lpg = lpg_pwm_from_chip(chip); struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; return chan->in_use ? -EBUSY : 0; @@ -995,7 +999,7 @@ static int lpg_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) static int lpg_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - struct lpg *lpg = container_of(chip, struct lpg, pwm); + struct lpg *lpg = lpg_pwm_from_chip(chip); struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; int ret = 0; @@ -1026,7 +1030,7 @@ out_unlock: static int lpg_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { - struct lpg *lpg = container_of(chip, struct lpg, pwm); + struct lpg *lpg = lpg_pwm_from_chip(chip); struct lpg_channel *chan = &lpg->channels[pwm->hwpwm]; unsigned int resolution; unsigned int pre_div; @@ -1095,9 +1099,9 @@ static int lpg_add_pwm(struct lpg *lpg) lpg->pwm.npwm = lpg->num_channels; lpg->pwm.ops = &lpg_pwm_ops; - ret = pwmchip_add(&lpg->pwm); + ret = devm_pwmchip_add(lpg->dev, &lpg->pwm); if (ret) - dev_err(lpg->dev, "failed to add PWM chip: ret %d\n", ret); + dev_err_probe(lpg->dev, ret, "failed to add PWM chip\n"); return ret; } @@ -1111,19 +1115,16 @@ static int lpg_parse_channel(struct lpg *lpg, struct device_node *np, int ret; ret = of_property_read_u32(np, "reg", ®); - if (ret || !reg || reg > lpg->num_channels) { - dev_err(lpg->dev, "invalid \"reg\" of %pOFn\n", np); - return -EINVAL; - } + if (ret || !reg || reg > lpg->num_channels) + return dev_err_probe(lpg->dev, -EINVAL, "invalid \"reg\" of %pOFn\n", np); chan = &lpg->channels[reg - 1]; chan->in_use = true; ret = of_property_read_u32(np, "color", &color); - if (ret < 0 && ret != -EINVAL) { - dev_err(lpg->dev, "failed to parse \"color\" of %pOF\n", np); - return ret; - } + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(lpg->dev, ret, + "failed to parse \"color\" of %pOF\n", np); chan->color = color; @@ -1146,10 +1147,9 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np) int i; ret = of_property_read_u32(np, "color", &color); - if (ret < 0 && ret != -EINVAL) { - dev_err(lpg->dev, "failed to parse \"color\" of %pOF\n", np); - return ret; - } + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(lpg->dev, ret, + "failed to parse \"color\" of %pOF\n", np); if (color == LED_COLOR_ID_RGB) num_channels = of_get_available_child_count(np); @@ -1226,7 +1226,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np) else ret = devm_led_classdev_register_ext(lpg->dev, &led->cdev, &init_data); if (ret) - dev_err(lpg->dev, "unable to register %s\n", cdev->name); + dev_err_probe(lpg->dev, ret, "unable to register %s\n", cdev->name); return ret; } @@ -1272,10 +1272,9 @@ static int lpg_init_triled(struct lpg *lpg) if (lpg->triled_has_src_sel) { ret = of_property_read_u32(np, "qcom,power-source", &lpg->triled_src); - if (ret || lpg->triled_src == 2 || lpg->triled_src > 3) { - dev_err(lpg->dev, "invalid power source\n"); - return -EINVAL; - } + if (ret || lpg->triled_src == 2 || lpg->triled_src > 3) + return dev_err_probe(lpg->dev, -EINVAL, + "invalid power source\n"); } /* Disable automatic trickle charge LED */ @@ -1324,8 +1323,6 @@ static int lpg_probe(struct platform_device *pdev) if (!lpg->data) return -EINVAL; - platform_set_drvdata(pdev, lpg); - lpg->dev = &pdev->dev; mutex_init(&lpg->lock); @@ -1363,13 +1360,6 @@ static int lpg_probe(struct platform_device *pdev) return lpg_add_pwm(lpg); } -static void lpg_remove(struct platform_device *pdev) -{ - struct lpg *lpg = platform_get_drvdata(pdev); - - pwmchip_remove(&lpg->pwm); -} - static const struct lpg_data pm8916_pwm_data = { .num_channels = 1, .channels = (const struct lpg_channel_data[]) { @@ -1529,7 +1519,6 @@ MODULE_DEVICE_TABLE(of, lpg_of_table); static struct platform_driver lpg_driver = { .probe = lpg_probe, - .remove_new = lpg_remove, .driver = { .name = "qcom-spmi-lpg", .of_match_table = lpg_of_table, diff --git a/drivers/leds/trigger/ledtrig-gpio.c b/drivers/leds/trigger/ledtrig-gpio.c index 9b7fe5dd5208..7f6a2352b0ac 100644 --- a/drivers/leds/trigger/ledtrig-gpio.c +++ b/drivers/leds/trigger/ledtrig-gpio.c @@ -41,33 +41,30 @@ static irqreturn_t gpio_trig_irq(int irq, void *_led) return IRQ_HANDLED; } -static ssize_t gpio_trig_brightness_show(struct device *dev, +static ssize_t desired_brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); - return sprintf(buf, "%u\n", gpio_data->desired_brightness); + return sysfs_emit(buf, "%u\n", gpio_data->desired_brightness); } -static ssize_t gpio_trig_brightness_store(struct device *dev, +static ssize_t desired_brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); - unsigned desired_brightness; + u8 desired_brightness; int ret; - ret = sscanf(buf, "%u", &desired_brightness); - if (ret < 1 || desired_brightness > 255) { - dev_err(dev, "invalid value\n"); - return -EINVAL; - } + ret = kstrtou8(buf, 10, &desired_brightness); + if (ret) + return ret; gpio_data->desired_brightness = desired_brightness; return n; } -static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show, - gpio_trig_brightness_store); +static DEVICE_ATTR_RW(desired_brightness); static struct attribute *gpio_trig_attrs[] = { &dev_attr_desired_brightness.attr, @@ -89,10 +86,7 @@ static int gpio_trig_activate(struct led_classdev *led) * The generic property "trigger-sources" is followed, * and we hope that this is a GPIO. */ - gpio_data->gpiod = fwnode_gpiod_get_index(dev->fwnode, - "trigger-sources", - 0, GPIOD_IN, - "led-trigger"); + gpio_data->gpiod = gpiod_get_optional(dev, "trigger-sources", GPIOD_IN); if (IS_ERR(gpio_data->gpiod)) { ret = PTR_ERR(gpio_data->gpiod); kfree(gpio_data); @@ -104,6 +98,8 @@ static int gpio_trig_activate(struct led_classdev *led) return -EINVAL; } + gpiod_set_consumer_name(gpio_data->gpiod, "led-trigger"); + gpio_data->led = led; led_set_trigger_data(led, gpio_data); diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index d76214fa9ad8..8e5475819590 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -38,6 +38,16 @@ * tx - LED blinks on transmitted data * rx - LED blinks on receive data * + * Note: If the user selects a mode that is not supported by hw, default + * behavior is to fall back to software control of the LED. However not every + * hw supports software control. LED callbacks brightness_set() and + * brightness_set_blocking() are NULL in this case. hw_control_is_supported() + * should use available means supported by hw to inform the user that selected + * mode isn't supported by hw. This could be switching off the LED or any + * hw blink mode. If software control fallback isn't possible, we return + * -EOPNOTSUPP to the user, but still store the selected mode. This is needed + * in case an intermediate unsupported mode is necessary to switch from one + * supported mode to another. */ struct led_netdev_data { @@ -99,6 +109,18 @@ static void set_baseline_state(struct led_netdev_data *trigger_data) trigger_data->link_speed == SPEED_1000) blink_on = true; + if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) && + trigger_data->link_speed == SPEED_2500) + blink_on = true; + + if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) && + trigger_data->link_speed == SPEED_5000) + blink_on = true; + + if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) && + trigger_data->link_speed == SPEED_10000) + blink_on = true; + if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) && trigger_data->duplex == DUPLEX_HALF) blink_on = true; @@ -289,6 +311,9 @@ static ssize_t netdev_led_attr_show(struct device *dev, char *buf, case TRIGGER_NETDEV_LINK_10: case TRIGGER_NETDEV_LINK_100: case TRIGGER_NETDEV_LINK_1000: + case TRIGGER_NETDEV_LINK_2500: + case TRIGGER_NETDEV_LINK_5000: + case TRIGGER_NETDEV_LINK_10000: case TRIGGER_NETDEV_HALF_DUPLEX: case TRIGGER_NETDEV_FULL_DUPLEX: case TRIGGER_NETDEV_TX: @@ -306,6 +331,7 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, size_t size, enum led_trigger_netdev_modes attr) { struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); + struct led_classdev *led_cdev = trigger_data->led_cdev; unsigned long state, mode = trigger_data->mode; int ret; int bit; @@ -319,6 +345,9 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, case TRIGGER_NETDEV_LINK_10: case TRIGGER_NETDEV_LINK_100: case TRIGGER_NETDEV_LINK_1000: + case TRIGGER_NETDEV_LINK_2500: + case TRIGGER_NETDEV_LINK_5000: + case TRIGGER_NETDEV_LINK_10000: case TRIGGER_NETDEV_HALF_DUPLEX: case TRIGGER_NETDEV_FULL_DUPLEX: case TRIGGER_NETDEV_TX: @@ -337,7 +366,10 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, if (test_bit(TRIGGER_NETDEV_LINK, &mode) && (test_bit(TRIGGER_NETDEV_LINK_10, &mode) || test_bit(TRIGGER_NETDEV_LINK_100, &mode) || - test_bit(TRIGGER_NETDEV_LINK_1000, &mode))) + test_bit(TRIGGER_NETDEV_LINK_1000, &mode) || + test_bit(TRIGGER_NETDEV_LINK_2500, &mode) || + test_bit(TRIGGER_NETDEV_LINK_5000, &mode) || + test_bit(TRIGGER_NETDEV_LINK_10000, &mode))) return -EINVAL; cancel_delayed_work_sync(&trigger_data->work); @@ -345,6 +377,10 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, trigger_data->mode = mode; trigger_data->hw_control = can_hw_control(trigger_data); + if (!led_cdev->brightness_set && !led_cdev->brightness_set_blocking && + !trigger_data->hw_control) + return -EOPNOTSUPP; + set_baseline_state(trigger_data); return size; @@ -367,6 +403,9 @@ DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK); DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10); DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100); DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000); +DEFINE_NETDEV_TRIGGER(link_2500, TRIGGER_NETDEV_LINK_2500); +DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000); +DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000); DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX); DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX); DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX); @@ -425,6 +464,9 @@ static struct attribute *netdev_trig_attrs[] = { &dev_attr_link_10.attr, &dev_attr_link_100.attr, &dev_attr_link_1000.attr, + &dev_attr_link_2500.attr, + &dev_attr_link_5000.attr, + &dev_attr_link_10000.attr, &dev_attr_full_duplex.attr, &dev_attr_half_duplex.attr, &dev_attr_rx.attr, @@ -522,6 +564,9 @@ static void netdev_trig_work(struct work_struct *work) test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode); interval = jiffies_to_msecs( diff --git a/drivers/leds/trigger/ledtrig-panic.c b/drivers/leds/trigger/ledtrig-panic.c index 64abf2e91608..5a6b21bfeb9a 100644 --- a/drivers/leds/trigger/ledtrig-panic.c +++ b/drivers/leds/trigger/ledtrig-panic.c @@ -64,10 +64,13 @@ static long led_panic_blink(int state) static int __init ledtrig_panic_init(void) { + led_trigger_register_simple("panic", &trigger); + if (!trigger) + return -ENOMEM; + atomic_notifier_chain_register(&panic_notifier_list, &led_trigger_panic_nb); - led_trigger_register_simple("panic", &trigger); panic_blink = led_panic_blink; return 0; } diff --git a/drivers/leds/trigger/ledtrig-tty.c b/drivers/leds/trigger/ledtrig-tty.c index 8ae0d2d284af..8cf1485e8165 100644 --- a/drivers/leds/trigger/ledtrig-tty.c +++ b/drivers/leds/trigger/ledtrig-tty.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 +#include <linux/completion.h> #include <linux/delay.h> #include <linux/leds.h> #include <linux/module.h> @@ -12,15 +13,45 @@ struct ledtrig_tty_data { struct led_classdev *led_cdev; struct delayed_work dwork; - struct mutex mutex; + struct completion sysfs; const char *ttyname; struct tty_struct *tty; int rx, tx; + bool mode_rx; + bool mode_tx; + bool mode_cts; + bool mode_dsr; + bool mode_dcd; + bool mode_rng; }; -static void ledtrig_tty_restart(struct ledtrig_tty_data *trigger_data) +/* Indicates which state the LED should now display */ +enum led_trigger_tty_state { + TTY_LED_BLINK, + TTY_LED_ENABLE, + TTY_LED_DISABLE, +}; + +enum led_trigger_tty_modes { + TRIGGER_TTY_RX = 0, + TRIGGER_TTY_TX, + TRIGGER_TTY_CTS, + TRIGGER_TTY_DSR, + TRIGGER_TTY_DCD, + TRIGGER_TTY_RNG, +}; + +static int ledtrig_tty_wait_for_completion(struct device *dev) { - schedule_delayed_work(&trigger_data->dwork, 0); + struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); + int ret; + + ret = wait_for_completion_timeout(&trigger_data->sysfs, + msecs_to_jiffies(LEDTRIG_TTY_INTERVAL * 20)); + if (ret == 0) + return -ETIMEDOUT; + + return ret; } static ssize_t ttyname_show(struct device *dev, @@ -28,14 +59,16 @@ static ssize_t ttyname_show(struct device *dev, { struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); ssize_t len = 0; + int completion; - mutex_lock(&trigger_data->mutex); + reinit_completion(&trigger_data->sysfs); + completion = ledtrig_tty_wait_for_completion(dev); + if (completion < 0) + return completion; if (trigger_data->ttyname) len = sprintf(buf, "%s\n", trigger_data->ttyname); - mutex_unlock(&trigger_data->mutex); - return len; } @@ -46,7 +79,7 @@ static ssize_t ttyname_store(struct device *dev, struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); char *ttyname; ssize_t ret = size; - bool running; + int completion; if (size > 0 && buf[size - 1] == '\n') size -= 1; @@ -59,9 +92,10 @@ static ssize_t ttyname_store(struct device *dev, ttyname = NULL; } - mutex_lock(&trigger_data->mutex); - - running = trigger_data->ttyname != NULL; + reinit_completion(&trigger_data->sysfs); + completion = ledtrig_tty_wait_for_completion(dev); + if (completion < 0) + return completion; kfree(trigger_data->ttyname); tty_kref_put(trigger_data->tty); @@ -69,29 +103,107 @@ static ssize_t ttyname_store(struct device *dev, trigger_data->ttyname = ttyname; - mutex_unlock(&trigger_data->mutex); - - if (ttyname && !running) - ledtrig_tty_restart(trigger_data); - return ret; } static DEVICE_ATTR_RW(ttyname); +static ssize_t ledtrig_tty_attr_show(struct device *dev, char *buf, + enum led_trigger_tty_modes attr) +{ + struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); + bool state; + + switch (attr) { + case TRIGGER_TTY_RX: + state = trigger_data->mode_rx; + break; + case TRIGGER_TTY_TX: + state = trigger_data->mode_tx; + break; + case TRIGGER_TTY_CTS: + state = trigger_data->mode_cts; + break; + case TRIGGER_TTY_DSR: + state = trigger_data->mode_dsr; + break; + case TRIGGER_TTY_DCD: + state = trigger_data->mode_dcd; + break; + case TRIGGER_TTY_RNG: + state = trigger_data->mode_rng; + break; + } + + return sysfs_emit(buf, "%u\n", state); +} + +static ssize_t ledtrig_tty_attr_store(struct device *dev, const char *buf, + size_t size, enum led_trigger_tty_modes attr) +{ + struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); + bool state; + int ret; + + ret = kstrtobool(buf, &state); + if (ret) + return ret; + + switch (attr) { + case TRIGGER_TTY_RX: + trigger_data->mode_rx = state; + break; + case TRIGGER_TTY_TX: + trigger_data->mode_tx = state; + break; + case TRIGGER_TTY_CTS: + trigger_data->mode_cts = state; + break; + case TRIGGER_TTY_DSR: + trigger_data->mode_dsr = state; + break; + case TRIGGER_TTY_DCD: + trigger_data->mode_dcd = state; + break; + case TRIGGER_TTY_RNG: + trigger_data->mode_rng = state; + break; + } + + return size; +} + +#define DEFINE_TTY_TRIGGER(trigger_name, trigger) \ + static ssize_t trigger_name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ + { \ + return ledtrig_tty_attr_show(dev, buf, trigger); \ + } \ + static ssize_t trigger_name##_store(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t size) \ + { \ + return ledtrig_tty_attr_store(dev, buf, size, trigger); \ + } \ + static DEVICE_ATTR_RW(trigger_name) + +DEFINE_TTY_TRIGGER(rx, TRIGGER_TTY_RX); +DEFINE_TTY_TRIGGER(tx, TRIGGER_TTY_TX); +DEFINE_TTY_TRIGGER(cts, TRIGGER_TTY_CTS); +DEFINE_TTY_TRIGGER(dsr, TRIGGER_TTY_DSR); +DEFINE_TTY_TRIGGER(dcd, TRIGGER_TTY_DCD); +DEFINE_TTY_TRIGGER(rng, TRIGGER_TTY_RNG); + static void ledtrig_tty_work(struct work_struct *work) { struct ledtrig_tty_data *trigger_data = container_of(work, struct ledtrig_tty_data, dwork.work); - struct serial_icounter_struct icount; + enum led_trigger_tty_state state = TTY_LED_DISABLE; + unsigned long interval = LEDTRIG_TTY_INTERVAL; + bool invert = false; + int status; int ret; - mutex_lock(&trigger_data->mutex); - - if (!trigger_data->ttyname) { - /* exit without rescheduling */ - mutex_unlock(&trigger_data->mutex); - return; - } + if (!trigger_data->ttyname) + goto out; /* try to get the tty corresponding to $ttyname */ if (!trigger_data->tty) { @@ -115,32 +227,83 @@ static void ledtrig_tty_work(struct work_struct *work) trigger_data->tty = tty; } - ret = tty_get_icount(trigger_data->tty, &icount); - if (ret) { - dev_info(trigger_data->tty->dev, "Failed to get icount, stopped polling\n"); - mutex_unlock(&trigger_data->mutex); - return; + status = tty_get_tiocm(trigger_data->tty); + if (status > 0) { + if (trigger_data->mode_cts) { + if (status & TIOCM_CTS) + state = TTY_LED_ENABLE; + } + + if (trigger_data->mode_dsr) { + if (status & TIOCM_DSR) + state = TTY_LED_ENABLE; + } + + if (trigger_data->mode_dcd) { + if (status & TIOCM_CAR) + state = TTY_LED_ENABLE; + } + + if (trigger_data->mode_rng) { + if (status & TIOCM_RNG) + state = TTY_LED_ENABLE; + } } - if (icount.rx != trigger_data->rx || - icount.tx != trigger_data->tx) { - unsigned long interval = LEDTRIG_TTY_INTERVAL; + /* + * The evaluation of rx/tx must be done after the evaluation + * of TIOCM_*, because rx/tx has priority. + */ + if (trigger_data->mode_rx || trigger_data->mode_tx) { + struct serial_icounter_struct icount; - led_blink_set_oneshot(trigger_data->led_cdev, &interval, - &interval, 0); + ret = tty_get_icount(trigger_data->tty, &icount); + if (ret) + goto out; - trigger_data->rx = icount.rx; - trigger_data->tx = icount.tx; + if (trigger_data->mode_tx && (icount.tx != trigger_data->tx)) { + trigger_data->tx = icount.tx; + invert = state == TTY_LED_ENABLE; + state = TTY_LED_BLINK; + } + + if (trigger_data->mode_rx && (icount.rx != trigger_data->rx)) { + trigger_data->rx = icount.rx; + invert = state == TTY_LED_ENABLE; + state = TTY_LED_BLINK; + } } out: - mutex_unlock(&trigger_data->mutex); + switch (state) { + case TTY_LED_BLINK: + led_blink_set_oneshot(trigger_data->led_cdev, &interval, + &interval, invert); + break; + case TTY_LED_ENABLE: + led_set_brightness(trigger_data->led_cdev, + trigger_data->led_cdev->blink_brightness); + break; + case TTY_LED_DISABLE: + fallthrough; + default: + led_set_brightness(trigger_data->led_cdev, LED_OFF); + break; + } + + complete_all(&trigger_data->sysfs); schedule_delayed_work(&trigger_data->dwork, msecs_to_jiffies(LEDTRIG_TTY_INTERVAL * 2)); } static struct attribute *ledtrig_tty_attrs[] = { &dev_attr_ttyname.attr, + &dev_attr_rx.attr, + &dev_attr_tx.attr, + &dev_attr_cts.attr, + &dev_attr_dsr.attr, + &dev_attr_dcd.attr, + &dev_attr_rng.attr, NULL }; ATTRIBUTE_GROUPS(ledtrig_tty); @@ -153,11 +316,17 @@ static int ledtrig_tty_activate(struct led_classdev *led_cdev) if (!trigger_data) return -ENOMEM; + /* Enable default rx/tx mode */ + trigger_data->mode_rx = true; + trigger_data->mode_tx = true; + led_set_trigger_data(led_cdev, trigger_data); INIT_DELAYED_WORK(&trigger_data->dwork, ledtrig_tty_work); trigger_data->led_cdev = led_cdev; - mutex_init(&trigger_data->mutex); + init_completion(&trigger_data->sysfs); + + schedule_delayed_work(&trigger_data->dwork, 0); return 0; } @@ -168,6 +337,10 @@ static void ledtrig_tty_deactivate(struct led_classdev *led_cdev) cancel_delayed_work_sync(&trigger_data->dwork); + kfree(trigger_data->ttyname); + tty_kref_put(trigger_data->tty); + trigger_data->tty = NULL; + kfree(trigger_data); } diff --git a/drivers/mailbox/arm_mhuv2.c b/drivers/mailbox/arm_mhuv2.c index c6d4957c4da8..0ec21dcdbde7 100644 --- a/drivers/mailbox/arm_mhuv2.c +++ b/drivers/mailbox/arm_mhuv2.c @@ -553,7 +553,8 @@ static irqreturn_t mhuv2_sender_interrupt(int irq, void *data) priv = chan->con_priv; if (!IS_PROTOCOL_DOORBELL(priv)) { - writel_relaxed(1, &mhu->send->ch_wn[priv->ch_wn_idx + priv->windows - 1].int_clr); + for (i = 0; i < priv->windows; i++) + writel_relaxed(1, &mhu->send->ch_wn[priv->ch_wn_idx + i].int_clr); if (chan->cl) { mbox_chan_txdone(chan, 0); diff --git a/drivers/mailbox/bcm-flexrm-mailbox.c b/drivers/mailbox/bcm-flexrm-mailbox.c index a2b8839d4e7c..e3e28a4f7d01 100644 --- a/drivers/mailbox/bcm-flexrm-mailbox.c +++ b/drivers/mailbox/bcm-flexrm-mailbox.c @@ -1650,7 +1650,7 @@ fail: return ret; } -static int flexrm_mbox_remove(struct platform_device *pdev) +static void flexrm_mbox_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct flexrm_mbox *mbox = platform_get_drvdata(pdev); @@ -1661,8 +1661,6 @@ static int flexrm_mbox_remove(struct platform_device *pdev) dma_pool_destroy(mbox->cmpl_pool); dma_pool_destroy(mbox->bd_pool); - - return 0; } static const struct of_device_id flexrm_mbox_of_match[] = { @@ -1677,7 +1675,7 @@ static struct platform_driver flexrm_mbox_driver = { .of_match_table = flexrm_mbox_of_match, }, .probe = flexrm_mbox_probe, - .remove = flexrm_mbox_remove, + .remove_new = flexrm_mbox_remove, }; module_platform_driver(flexrm_mbox_driver); diff --git a/drivers/mailbox/bcm-pdc-mailbox.c b/drivers/mailbox/bcm-pdc-mailbox.c index 778faeced81e..1768d3d5aaa0 100644 --- a/drivers/mailbox/bcm-pdc-mailbox.c +++ b/drivers/mailbox/bcm-pdc-mailbox.c @@ -1605,7 +1605,7 @@ cleanup: return err; } -static int pdc_remove(struct platform_device *pdev) +static void pdc_remove(struct platform_device *pdev) { struct pdc_state *pdcs = platform_get_drvdata(pdev); @@ -1617,12 +1617,11 @@ static int pdc_remove(struct platform_device *pdev) dma_pool_destroy(pdcs->rx_buf_pool); dma_pool_destroy(pdcs->ring_pool); - return 0; } static struct platform_driver pdc_mbox_driver = { .probe = pdc_probe, - .remove = pdc_remove, + .remove_new = pdc_remove, .driver = { .name = "brcm-iproc-pdc-mbox", .of_match_table = pdc_mbox_of_match, diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 0af739ab571c..656171362fe9 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -903,13 +903,11 @@ disable_runtime_pm: return ret; } -static int imx_mu_remove(struct platform_device *pdev) +static void imx_mu_remove(struct platform_device *pdev) { struct imx_mu_priv *priv = platform_get_drvdata(pdev); pm_runtime_disable(priv->dev); - - return 0; } static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = { @@ -1070,7 +1068,7 @@ static const struct dev_pm_ops imx_mu_pm_ops = { static struct platform_driver imx_mu_driver = { .probe = imx_mu_probe, - .remove = imx_mu_remove, + .remove_new = imx_mu_remove, .driver = { .name = "imx_mu", .of_match_table = imx_mu_dt_ids, diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index 22d6018ceec3..3386b4e72551 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -418,7 +418,7 @@ static int mbox_test_probe(struct platform_device *pdev) return 0; } -static int mbox_test_remove(struct platform_device *pdev) +static void mbox_test_remove(struct platform_device *pdev) { struct mbox_test_device *tdev = platform_get_drvdata(pdev); @@ -428,8 +428,6 @@ static int mbox_test_remove(struct platform_device *pdev) mbox_free_channel(tdev->tx_channel); if (tdev->rx_channel) mbox_free_channel(tdev->rx_channel); - - return 0; } static const struct of_device_id mbox_test_match[] = { @@ -444,7 +442,7 @@ static struct platform_driver mbox_test_driver = { .of_match_table = mbox_test_match, }, .probe = mbox_test_probe, - .remove = mbox_test_remove, + .remove_new = mbox_test_remove, }; module_platform_driver(mbox_test_driver); diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index de862e9137d5..ead2200f39ba 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -367,7 +367,7 @@ static int cmdq_resume(struct device *dev) return 0; } -static int cmdq_remove(struct platform_device *pdev) +static void cmdq_remove(struct platform_device *pdev) { struct cmdq *cmdq = platform_get_drvdata(pdev); @@ -378,7 +378,6 @@ static int cmdq_remove(struct platform_device *pdev) cmdq_runtime_suspend(&pdev->dev); clk_bulk_unprepare(cmdq->pdata->gce_num, cmdq->clocks); - return 0; } static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data) @@ -706,62 +705,70 @@ static const struct dev_pm_ops cmdq_pm_ops = { cmdq_runtime_resume, NULL) }; -static const struct gce_plat gce_plat_v2 = { - .thread_nr = 16, - .shift = 0, +static const struct gce_plat gce_plat_mt6779 = { + .thread_nr = 24, + .shift = 3, .control_by_sw = false, .gce_num = 1 }; -static const struct gce_plat gce_plat_v3 = { - .thread_nr = 24, +static const struct gce_plat gce_plat_mt8173 = { + .thread_nr = 16, .shift = 0, .control_by_sw = false, .gce_num = 1 }; -static const struct gce_plat gce_plat_v4 = { +static const struct gce_plat gce_plat_mt8183 = { .thread_nr = 24, - .shift = 3, + .shift = 0, .control_by_sw = false, .gce_num = 1 }; -static const struct gce_plat gce_plat_v5 = { +static const struct gce_plat gce_plat_mt8186 = { .thread_nr = 24, .shift = 3, .control_by_sw = true, + .sw_ddr_en = true, .gce_num = 1 }; -static const struct gce_plat gce_plat_v6 = { - .thread_nr = 24, +static const struct gce_plat gce_plat_mt8188 = { + .thread_nr = 32, .shift = 3, .control_by_sw = true, .gce_num = 2 }; -static const struct gce_plat gce_plat_v7 = { +static const struct gce_plat gce_plat_mt8192 = { .thread_nr = 24, .shift = 3, .control_by_sw = true, - .sw_ddr_en = true, .gce_num = 1 }; +static const struct gce_plat gce_plat_mt8195 = { + .thread_nr = 24, + .shift = 3, + .control_by_sw = true, + .gce_num = 2 +}; + static const struct of_device_id cmdq_of_ids[] = { - {.compatible = "mediatek,mt8173-gce", .data = (void *)&gce_plat_v2}, - {.compatible = "mediatek,mt8183-gce", .data = (void *)&gce_plat_v3}, - {.compatible = "mediatek,mt8186-gce", .data = (void *)&gce_plat_v7}, - {.compatible = "mediatek,mt6779-gce", .data = (void *)&gce_plat_v4}, - {.compatible = "mediatek,mt8192-gce", .data = (void *)&gce_plat_v5}, - {.compatible = "mediatek,mt8195-gce", .data = (void *)&gce_plat_v6}, + {.compatible = "mediatek,mt6779-gce", .data = (void *)&gce_plat_mt6779}, + {.compatible = "mediatek,mt8173-gce", .data = (void *)&gce_plat_mt8173}, + {.compatible = "mediatek,mt8183-gce", .data = (void *)&gce_plat_mt8183}, + {.compatible = "mediatek,mt8186-gce", .data = (void *)&gce_plat_mt8186}, + {.compatible = "mediatek,mt8188-gce", .data = (void *)&gce_plat_mt8188}, + {.compatible = "mediatek,mt8192-gce", .data = (void *)&gce_plat_mt8192}, + {.compatible = "mediatek,mt8195-gce", .data = (void *)&gce_plat_mt8195}, {} }; static struct platform_driver cmdq_drv = { .probe = cmdq_probe, - .remove = cmdq_remove, + .remove_new = cmdq_remove, .driver = { .name = "mtk_cmdq", .pm = &cmdq_pm_ops, diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index 792bcaebbc9b..c961706fe61d 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -865,19 +865,17 @@ unregister: return ret; } -static int omap_mbox_remove(struct platform_device *pdev) +static void omap_mbox_remove(struct platform_device *pdev) { struct omap_mbox_device *mdev = platform_get_drvdata(pdev); pm_runtime_disable(mdev->dev); omap_mbox_unregister(mdev); - - return 0; } static struct platform_driver omap_mbox_driver = { .probe = omap_mbox_probe, - .remove = omap_mbox_remove, + .remove_new = omap_mbox_remove, .driver = { .name = "omap-mailbox", .pm = &omap_mbox_pm_ops, diff --git a/drivers/mailbox/qcom-apcs-ipc-mailbox.c b/drivers/mailbox/qcom-apcs-ipc-mailbox.c index 002a135ee868..7d91e7c016ba 100644 --- a/drivers/mailbox/qcom-apcs-ipc-mailbox.c +++ b/drivers/mailbox/qcom-apcs-ipc-mailbox.c @@ -129,14 +129,12 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev) return 0; } -static int qcom_apcs_ipc_remove(struct platform_device *pdev) +static void qcom_apcs_ipc_remove(struct platform_device *pdev) { struct qcom_apcs_ipc *apcs = platform_get_drvdata(pdev); struct platform_device *clk = apcs->clk; platform_device_unregister(clk); - - return 0; } /* .data is the offset of the ipc register within the global block */ @@ -145,19 +143,19 @@ static const struct of_device_id qcom_apcs_ipc_of_match[] = { { .compatible = "qcom,msm8916-apcs-kpss-global", .data = &msm8916_apcs_data }, { .compatible = "qcom,msm8939-apcs-kpss-global", .data = &msm8916_apcs_data }, { .compatible = "qcom,msm8953-apcs-kpss-global", .data = &msm8994_apcs_data }, - { .compatible = "qcom,msm8976-apcs-kpss-global", .data = &msm8994_apcs_data }, { .compatible = "qcom,msm8994-apcs-kpss-global", .data = &msm8994_apcs_data }, { .compatible = "qcom,msm8996-apcs-hmss-global", .data = &msm8996_apcs_data }, - { .compatible = "qcom,msm8998-apcs-hmss-global", .data = &msm8994_apcs_data }, { .compatible = "qcom,qcm2290-apcs-hmss-global", .data = &msm8994_apcs_data }, + { .compatible = "qcom,sdm845-apss-shared", .data = &apps_shared_apcs_data }, + { .compatible = "qcom,sdx55-apcs-gcc", .data = &sdx55_apcs_data }, + /* Do not add any more entries using existing driver data */ + { .compatible = "qcom,msm8976-apcs-kpss-global", .data = &msm8994_apcs_data }, + { .compatible = "qcom,msm8998-apcs-hmss-global", .data = &msm8994_apcs_data }, { .compatible = "qcom,qcs404-apcs-apps-global", .data = &msm8916_apcs_data }, { .compatible = "qcom,sdm660-apcs-hmss-global", .data = &msm8994_apcs_data }, - { .compatible = "qcom,sdm845-apss-shared", .data = &apps_shared_apcs_data }, { .compatible = "qcom,sm4250-apcs-hmss-global", .data = &msm8994_apcs_data }, { .compatible = "qcom,sm6125-apcs-hmss-global", .data = &msm8994_apcs_data }, { .compatible = "qcom,sm6115-apcs-hmss-global", .data = &msm8994_apcs_data }, - { .compatible = "qcom,sdx55-apcs-gcc", .data = &sdx55_apcs_data }, - /* Do not add any more entries using existing driver data */ { .compatible = "qcom,ipq5332-apcs-apps-global", .data = &ipq6018_apcs_data }, { .compatible = "qcom,ipq8074-apcs-apps-global", .data = &ipq6018_apcs_data }, { .compatible = "qcom,sc7180-apss-shared", .data = &apps_shared_apcs_data }, @@ -169,7 +167,7 @@ MODULE_DEVICE_TABLE(of, qcom_apcs_ipc_of_match); static struct platform_driver qcom_apcs_ipc_driver = { .probe = qcom_apcs_ipc_probe, - .remove = qcom_apcs_ipc_remove, + .remove_new = qcom_apcs_ipc_remove, .driver = { .name = "qcom_apcs_ipc", .of_match_table = qcom_apcs_ipc_of_match, diff --git a/drivers/mailbox/qcom-ipcc.c b/drivers/mailbox/qcom-ipcc.c index f597a1bd5684..d537cc9c4d4b 100644 --- a/drivers/mailbox/qcom-ipcc.c +++ b/drivers/mailbox/qcom-ipcc.c @@ -326,14 +326,12 @@ err_mbox: return ret; } -static int qcom_ipcc_remove(struct platform_device *pdev) +static void qcom_ipcc_remove(struct platform_device *pdev) { struct qcom_ipcc *ipcc = platform_get_drvdata(pdev); disable_irq_wake(ipcc->irq); irq_domain_remove(ipcc->irq_domain); - - return 0; } static const struct of_device_id qcom_ipcc_of_match[] = { @@ -348,7 +346,7 @@ static const struct dev_pm_ops qcom_ipcc_dev_pm_ops = { static struct platform_driver qcom_ipcc_driver = { .probe = qcom_ipcc_probe, - .remove = qcom_ipcc_remove, + .remove_new = qcom_ipcc_remove, .driver = { .name = "qcom-ipcc", .of_match_table = qcom_ipcc_of_match, diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c index 4ad3653f3866..1442f275782b 100644 --- a/drivers/mailbox/stm32-ipcc.c +++ b/drivers/mailbox/stm32-ipcc.c @@ -331,7 +331,7 @@ err_clk: return ret; } -static int stm32_ipcc_remove(struct platform_device *pdev) +static void stm32_ipcc_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -339,8 +339,6 @@ static int stm32_ipcc_remove(struct platform_device *pdev) dev_pm_clear_wake_irq(&pdev->dev); device_set_wakeup_capable(dev, false); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -381,7 +379,7 @@ static struct platform_driver stm32_ipcc_driver = { .of_match_table = stm32_ipcc_of_match, }, .probe = stm32_ipcc_probe, - .remove = stm32_ipcc_remove, + .remove_new = stm32_ipcc_remove, }; module_platform_driver(stm32_ipcc_driver); diff --git a/drivers/mailbox/sun6i-msgbox.c b/drivers/mailbox/sun6i-msgbox.c index 7f8d931042d3..3dcc54dc83b2 100644 --- a/drivers/mailbox/sun6i-msgbox.c +++ b/drivers/mailbox/sun6i-msgbox.c @@ -287,15 +287,13 @@ err_disable_unprepare: return ret; } -static int sun6i_msgbox_remove(struct platform_device *pdev) +static void sun6i_msgbox_remove(struct platform_device *pdev) { struct sun6i_msgbox *mbox = platform_get_drvdata(pdev); mbox_controller_unregister(&mbox->controller); /* See the comment in sun6i_msgbox_probe about the reset line. */ clk_disable_unprepare(mbox->clk); - - return 0; } static const struct of_device_id sun6i_msgbox_of_match[] = { @@ -310,7 +308,7 @@ static struct platform_driver sun6i_msgbox_driver = { .of_match_table = sun6i_msgbox_of_match, }, .probe = sun6i_msgbox_probe, - .remove = sun6i_msgbox_remove, + .remove_new = sun6i_msgbox_remove, }; module_platform_driver(sun6i_msgbox_driver); diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c index fe29fc2ca526..19ef56cbcfd3 100644 --- a/drivers/mailbox/tegra-hsp.c +++ b/drivers/mailbox/tegra-hsp.c @@ -868,13 +868,11 @@ static int tegra_hsp_probe(struct platform_device *pdev) return 0; } -static int tegra_hsp_remove(struct platform_device *pdev) +static void tegra_hsp_remove(struct platform_device *pdev) { struct tegra_hsp *hsp = platform_get_drvdata(pdev); lockdep_unregister_key(&hsp->lock_key); - - return 0; } static int __maybe_unused tegra_hsp_resume(struct device *dev) @@ -953,7 +951,7 @@ static struct platform_driver tegra_hsp_driver = { .pm = &tegra_hsp_pm_ops, }, .probe = tegra_hsp_probe, - .remove = tegra_hsp_remove, + .remove_new = tegra_hsp_remove, }; static int __init tegra_hsp_init(void) diff --git a/drivers/mailbox/zynqmp-ipi-mailbox.c b/drivers/mailbox/zynqmp-ipi-mailbox.c index 7fa533e80dd9..25c65afc030a 100644 --- a/drivers/mailbox/zynqmp-ipi-mailbox.c +++ b/drivers/mailbox/zynqmp-ipi-mailbox.c @@ -81,7 +81,6 @@ struct zynqmp_ipi_mchan { * @remote_id: remote IPI agent ID * @mbox: mailbox Controller * @mchans: array for channels, tx channel and rx channel. - * @irq: IPI agent interrupt ID */ struct zynqmp_ipi_mbox { struct zynqmp_ipi_pdata *pdata; @@ -688,19 +687,17 @@ free_mbox_dev: return ret; } -static int zynqmp_ipi_remove(struct platform_device *pdev) +static void zynqmp_ipi_remove(struct platform_device *pdev) { struct zynqmp_ipi_pdata *pdata; pdata = platform_get_drvdata(pdev); zynqmp_ipi_free_mboxes(pdata); - - return 0; } static struct platform_driver zynqmp_ipi_driver = { .probe = zynqmp_ipi_probe, - .remove = zynqmp_ipi_remove, + .remove_new = zynqmp_ipi_remove, .driver = { .name = "zynqmp-ipi", .of_match_table = of_match_ptr(zynqmp_ipi_of_match), diff --git a/drivers/mcb/mcb-core.c b/drivers/mcb/mcb-core.c index ba4530459de8..61994da7bad0 100644 --- a/drivers/mcb/mcb-core.c +++ b/drivers/mcb/mcb-core.c @@ -263,6 +263,7 @@ static void mcb_free_bus(struct device *dev) /** * mcb_alloc_bus() - Allocate a new @mcb_bus + * @carrier: generic &struct device for the carrier device * * Allocate a new @mcb_bus. */ @@ -327,7 +328,7 @@ void mcb_release_bus(struct mcb_bus *bus) EXPORT_SYMBOL_NS_GPL(mcb_release_bus, MCB); /** - * mcb_bus_put() - Increment refcnt + * mcb_bus_get() - Increment refcnt * @bus: The @mcb_bus * * Get a @mcb_bus' ref @@ -455,7 +456,7 @@ EXPORT_SYMBOL_NS_GPL(mcb_request_mem, MCB); /** * mcb_release_mem() - Release memory requested by device - * @dev: The @mcb_device that requested the memory + * @mem: The memory resource to be released * * Release memory that was prior requested via @mcb_request_mem(). */ diff --git a/drivers/md/dm-core.h b/drivers/md/dm-core.h index 095b9b49aa82..e6757a30dcca 100644 --- a/drivers/md/dm-core.h +++ b/drivers/md/dm-core.h @@ -22,6 +22,8 @@ #include "dm-ima.h" #define DM_RESERVED_MAX_IOS 1024 +#define DM_MAX_TARGETS 1048576 +#define DM_MAX_TARGET_PARAMS 1024 struct dm_io; diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 855b482cbff1..f745f8508243 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -73,10 +73,8 @@ struct dm_crypt_io { struct bio *base_bio; u8 *integrity_metadata; bool integrity_metadata_from_pool:1; - bool in_tasklet:1; struct work_struct work; - struct tasklet_struct tasklet; struct convert_context ctx; @@ -1762,7 +1760,6 @@ static void crypt_io_init(struct dm_crypt_io *io, struct crypt_config *cc, io->ctx.r.req = NULL; io->integrity_metadata = NULL; io->integrity_metadata_from_pool = false; - io->in_tasklet = false; atomic_set(&io->io_pending, 0); } @@ -1771,13 +1768,6 @@ static void crypt_inc_pending(struct dm_crypt_io *io) atomic_inc(&io->io_pending); } -static void kcryptd_io_bio_endio(struct work_struct *work) -{ - struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work); - - bio_endio(io->base_bio); -} - /* * One of the bios was finished. Check for completion of * the whole request and correctly clean up the buffer. @@ -1801,20 +1791,6 @@ static void crypt_dec_pending(struct dm_crypt_io *io) base_bio->bi_status = error; - /* - * If we are running this function from our tasklet, - * we can't call bio_endio() here, because it will call - * clone_endio() from dm.c, which in turn will - * free the current struct dm_crypt_io structure with - * our tasklet. In this case we need to delay bio_endio() - * execution to after the tasklet is done and dequeued. - */ - if (io->in_tasklet) { - INIT_WORK(&io->work, kcryptd_io_bio_endio); - queue_work(cc->io_queue, &io->work); - return; - } - bio_endio(base_bio); } @@ -2246,11 +2222,6 @@ static void kcryptd_crypt(struct work_struct *work) kcryptd_crypt_write_convert(io); } -static void kcryptd_crypt_tasklet(unsigned long work) -{ - kcryptd_crypt((struct work_struct *)work); -} - static void kcryptd_queue_crypt(struct dm_crypt_io *io) { struct crypt_config *cc = io->cc; @@ -2262,15 +2233,10 @@ static void kcryptd_queue_crypt(struct dm_crypt_io *io) * irqs_disabled(): the kernel may run some IO completion from the idle thread, but * it is being executed with irqs disabled. */ - if (in_hardirq() || irqs_disabled()) { - io->in_tasklet = true; - tasklet_init(&io->tasklet, kcryptd_crypt_tasklet, (unsigned long)&io->work); - tasklet_schedule(&io->tasklet); + if (!(in_hardirq() || irqs_disabled())) { + kcryptd_crypt(&io->work); return; } - - kcryptd_crypt(&io->work); - return; } INIT_WORK(&io->work, kcryptd_crypt); diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index e65058e0ed06..3b1ad7127cb8 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1941,7 +1941,8 @@ static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kern minimum_data_size - sizeof(param_kernel->version))) return -EFAULT; - if (param_kernel->data_size < minimum_data_size) { + if (unlikely(param_kernel->data_size < minimum_data_size) || + unlikely(param_kernel->data_size > DM_MAX_TARGETS * DM_MAX_TARGET_PARAMS)) { DMERR("Invalid data size in the ioctl structure: %u", param_kernel->data_size); return -EINVAL; diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index bdc14ec99814..1e5d988f44da 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -66,6 +66,9 @@ struct dm_stats_last_position { unsigned int last_rw; }; +#define DM_STAT_MAX_ENTRIES 8388608 +#define DM_STAT_MAX_HISTOGRAM_ENTRIES 134217728 + /* * A typo on the command line could possibly make the kernel run out of memory * and crash. To prevent the crash we account all used memory. We fail if we @@ -285,6 +288,9 @@ static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t end, if (n_entries != (size_t)n_entries || !(size_t)(n_entries + 1)) return -EOVERFLOW; + if (n_entries > DM_STAT_MAX_ENTRIES) + return -EOVERFLOW; + shared_alloc_size = struct_size(s, stat_shared, n_entries); if ((shared_alloc_size - sizeof(struct dm_stat)) / sizeof(struct dm_stat_shared) != n_entries) return -EOVERFLOW; @@ -297,6 +303,9 @@ static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t end, if (histogram_alloc_size / (n_histogram_entries + 1) != (size_t)n_entries * sizeof(unsigned long long)) return -EOVERFLOW; + if ((n_histogram_entries + 1) * (size_t)n_entries > DM_STAT_MAX_HISTOGRAM_ENTRIES) + return -EOVERFLOW; + if (!check_shared_memory(shared_alloc_size + histogram_alloc_size + num_possible_cpus() * (percpu_alloc_size + histogram_alloc_size))) return -ENOMEM; diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 260b5b8f2b0d..41f1d731ae5a 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -129,7 +129,12 @@ static int alloc_targets(struct dm_table *t, unsigned int num) int dm_table_create(struct dm_table **result, blk_mode_t mode, unsigned int num_targets, struct mapped_device *md) { - struct dm_table *t = kzalloc(sizeof(*t), GFP_KERNEL); + struct dm_table *t; + + if (num_targets > DM_MAX_TARGETS) + return -EOVERFLOW; + + t = kzalloc(sizeof(*t), GFP_KERNEL); if (!t) return -ENOMEM; @@ -144,7 +149,7 @@ int dm_table_create(struct dm_table **result, blk_mode_t mode, if (!num_targets) { kfree(t); - return -ENOMEM; + return -EOVERFLOW; } if (alloc_targets(t, num_targets)) { diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 14e58ae70521..82662f5769c4 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -645,23 +645,6 @@ static void verity_work(struct work_struct *w) verity_finish_io(io, errno_to_blk_status(verity_verify_io(io))); } -static void verity_tasklet(unsigned long data) -{ - struct dm_verity_io *io = (struct dm_verity_io *)data; - int err; - - io->in_tasklet = true; - err = verity_verify_io(io); - if (err == -EAGAIN || err == -ENOMEM) { - /* fallback to retrying with work-queue */ - INIT_WORK(&io->work, verity_work); - queue_work(io->v->verify_wq, &io->work); - return; - } - - verity_finish_io(io, errno_to_blk_status(err)); -} - static void verity_end_io(struct bio *bio) { struct dm_verity_io *io = bio->bi_private; @@ -674,13 +657,8 @@ static void verity_end_io(struct bio *bio) return; } - if (static_branch_unlikely(&use_tasklet_enabled) && io->v->use_tasklet) { - tasklet_init(&io->tasklet, verity_tasklet, (unsigned long)io); - tasklet_schedule(&io->tasklet); - } else { - INIT_WORK(&io->work, verity_work); - queue_work(io->v->verify_wq, &io->work); - } + INIT_WORK(&io->work, verity_work); + queue_work(io->v->verify_wq, &io->work); } /* diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index f9d522c870e6..f3f607008419 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -83,7 +83,6 @@ struct dm_verity_io { struct bvec_iter iter; struct work_struct work; - struct tasklet_struct tasklet; /* * Three variably-size fields follow this struct: diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c index 074cb785eafc..b463c28c39ad 100644 --- a/drivers/md/dm-writecache.c +++ b/drivers/md/dm-writecache.c @@ -299,7 +299,7 @@ static int persistent_memory_claim(struct dm_writecache *wc) long i; wc->memory_map = NULL; - pages = kvmalloc_array(p, sizeof(struct page *), GFP_KERNEL); + pages = vmalloc_array(p, sizeof(struct page *)); if (!pages) { r = -ENOMEM; goto err2; @@ -330,7 +330,7 @@ static int persistent_memory_claim(struct dm_writecache *wc) r = -ENOMEM; goto err3; } - kvfree(pages); + vfree(pages); wc->memory_vmapped = true; } @@ -341,7 +341,7 @@ static int persistent_memory_claim(struct dm_writecache *wc) return 0; err3: - kvfree(pages); + vfree(pages); err2: dax_read_unlock(id); err1: @@ -962,7 +962,7 @@ static int writecache_alloc_entries(struct dm_writecache *wc) if (wc->entries) return 0; - wc->entries = vmalloc(array_size(sizeof(struct wc_entry), wc->n_blocks)); + wc->entries = vmalloc_array(wc->n_blocks, sizeof(struct wc_entry)); if (!wc->entries) return -ENOMEM; for (b = 0; b < wc->n_blocks; b++) { diff --git a/drivers/md/md.c b/drivers/md/md.c index 0a2bd72a6d76..2266358d8074 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -8132,6 +8132,19 @@ static void status_unused(struct seq_file *seq) seq_printf(seq, "\n"); } +static void status_personalities(struct seq_file *seq) +{ + struct md_personality *pers; + + seq_puts(seq, "Personalities : "); + spin_lock(&pers_lock); + list_for_each_entry(pers, &pers_list, list) + seq_printf(seq, "[%s] ", pers->name); + + spin_unlock(&pers_lock); + seq_puts(seq, "\n"); +} + static int status_resync(struct seq_file *seq, struct mddev *mddev) { sector_t max_sectors, resync, res; @@ -8273,20 +8286,10 @@ static int status_resync(struct seq_file *seq, struct mddev *mddev) static void *md_seq_start(struct seq_file *seq, loff_t *pos) __acquires(&all_mddevs_lock) { - struct md_personality *pers; - - seq_puts(seq, "Personalities : "); - spin_lock(&pers_lock); - list_for_each_entry(pers, &pers_list, list) - seq_printf(seq, "[%s] ", pers->name); - - spin_unlock(&pers_lock); - seq_puts(seq, "\n"); seq->poll_event = atomic_read(&md_event_count); - spin_lock(&all_mddevs_lock); - return seq_list_start(&all_mddevs, *pos); + return seq_list_start_head(&all_mddevs, *pos); } static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos) @@ -8297,16 +8300,23 @@ static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void md_seq_stop(struct seq_file *seq, void *v) __releases(&all_mddevs_lock) { - status_unused(seq); spin_unlock(&all_mddevs_lock); } static int md_seq_show(struct seq_file *seq, void *v) { - struct mddev *mddev = list_entry(v, struct mddev, all_mddevs); + struct mddev *mddev; sector_t sectors; struct md_rdev *rdev; + if (v == &all_mddevs) { + status_personalities(seq); + if (list_empty(&all_mddevs)) + status_unused(seq); + return 0; + } + + mddev = list_entry(v, struct mddev, all_mddevs); if (!mddev_get(mddev)) return 0; @@ -8382,6 +8392,10 @@ static int md_seq_show(struct seq_file *seq, void *v) } spin_unlock(&mddev->lock); spin_lock(&all_mddevs_lock); + + if (mddev == list_last_entry(&all_mddevs, struct mddev, all_mddevs)) + status_unused(seq); + if (atomic_dec_and_test(&mddev->active)) __mddev_put(mddev); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index aaa434f0c175..286f8b16c7bd 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1968,12 +1968,12 @@ static void end_sync_write(struct bio *bio) } static int r1_sync_page_io(struct md_rdev *rdev, sector_t sector, - int sectors, struct page *page, int rw) + int sectors, struct page *page, blk_opf_t rw) { if (sync_page_io(rdev, sector, sectors << 9, page, rw, false)) /* success */ return 1; - if (rw == WRITE) { + if (rw == REQ_OP_WRITE) { set_bit(WriteErrorSeen, &rdev->flags); if (!test_and_set_bit(WantReplacement, &rdev->flags)) @@ -2090,7 +2090,7 @@ static int fix_sync_read_error(struct r1bio *r1_bio) rdev = conf->mirrors[d].rdev; if (r1_sync_page_io(rdev, sect, s, pages[idx], - WRITE) == 0) { + REQ_OP_WRITE) == 0) { r1_bio->bios[d]->bi_end_io = NULL; rdev_dec_pending(rdev, mddev); } @@ -2105,7 +2105,7 @@ static int fix_sync_read_error(struct r1bio *r1_bio) rdev = conf->mirrors[d].rdev; if (r1_sync_page_io(rdev, sect, s, pages[idx], - READ) != 0) + REQ_OP_READ) != 0) atomic_add(s, &rdev->corrected_errors); } sectors -= s; @@ -2262,7 +2262,7 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio) int sectors = r1_bio->sectors; int read_disk = r1_bio->read_disk; struct mddev *mddev = conf->mddev; - struct md_rdev *rdev = rcu_dereference(conf->mirrors[read_disk].rdev); + struct md_rdev *rdev = conf->mirrors[read_disk].rdev; if (exceed_read_errors(mddev, rdev)) { r1_bio->bios[r1_bio->read_disk] = IO_BLOCKED; @@ -2321,7 +2321,7 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio) !test_bit(Faulty, &rdev->flags)) { atomic_inc(&rdev->nr_pending); r1_sync_page_io(rdev, sect, s, - conf->tmppage, WRITE); + conf->tmppage, REQ_OP_WRITE); rdev_dec_pending(rdev, mddev); } } @@ -2335,7 +2335,7 @@ static void fix_read_error(struct r1conf *conf, struct r1bio *r1_bio) !test_bit(Faulty, &rdev->flags)) { atomic_inc(&rdev->nr_pending); if (r1_sync_page_io(rdev, sect, s, - conf->tmppage, READ)) { + conf->tmppage, REQ_OP_READ)) { atomic_add(s, &rdev->corrected_errors); pr_info("md/raid1:%s: read error corrected (%d sectors at %llu on %pg)\n", mdname(mddev), s, diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 41a832dd1426..b6bf8f232f48 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -989,7 +989,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, bool no_previous_buffers = !q_num_bufs; int ret = 0; - if (q->num_buffers == q->max_num_buffers) { + if (q_num_bufs == q->max_num_buffers) { dprintk(q, 1, "maximum number of buffers already allocated\n"); return -ENOBUFS; } diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 54d572c3b515..c575198e8354 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -671,8 +671,20 @@ int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) } EXPORT_SYMBOL(vb2_querybuf); -static void fill_buf_caps(struct vb2_queue *q, u32 *caps) +static void vb2_set_flags_and_caps(struct vb2_queue *q, u32 memory, + u32 *flags, u32 *caps, u32 *max_num_bufs) { + if (!q->allow_cache_hints || memory != V4L2_MEMORY_MMAP) { + /* + * This needs to clear V4L2_MEMORY_FLAG_NON_COHERENT only, + * but in order to avoid bugs we zero out all bits. + */ + *flags = 0; + } else { + /* Clear all unknown flags. */ + *flags &= V4L2_MEMORY_FLAG_NON_COHERENT; + } + *caps = V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS; if (q->io_modes & VB2_MMAP) *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP; @@ -686,21 +698,9 @@ static void fill_buf_caps(struct vb2_queue *q, u32 *caps) *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS; if (q->supports_requests) *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS; -} - -static void validate_memory_flags(struct vb2_queue *q, - int memory, - u32 *flags) -{ - if (!q->allow_cache_hints || memory != V4L2_MEMORY_MMAP) { - /* - * This needs to clear V4L2_MEMORY_FLAG_NON_COHERENT only, - * but in order to avoid bugs we zero out all bits. - */ - *flags = 0; - } else { - /* Clear all unknown flags. */ - *flags &= V4L2_MEMORY_FLAG_NON_COHERENT; + if (max_num_bufs) { + *max_num_bufs = q->max_num_buffers; + *caps |= V4L2_BUF_CAP_SUPPORTS_MAX_NUM_BUFFERS; } } @@ -709,8 +709,8 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) int ret = vb2_verify_memory_type(q, req->memory, req->type); u32 flags = req->flags; - fill_buf_caps(q, &req->capabilities); - validate_memory_flags(q, req->memory, &flags); + vb2_set_flags_and_caps(q, req->memory, &flags, + &req->capabilities, NULL); req->flags = flags; return ret ? ret : vb2_core_reqbufs(q, req->memory, req->flags, &req->count); @@ -751,11 +751,9 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) int ret = vb2_verify_memory_type(q, create->memory, f->type); unsigned i; - fill_buf_caps(q, &create->capabilities); - validate_memory_flags(q, create->memory, &create->flags); create->index = vb2_get_num_buffers(q); - create->max_num_buffers = q->max_num_buffers; - create->capabilities |= V4L2_BUF_CAP_SUPPORTS_MAX_NUM_BUFFERS; + vb2_set_flags_and_caps(q, create->memory, &create->flags, + &create->capabilities, &create->max_num_buffers); if (create->count == 0) return ret != -EBUSY ? ret : 0; @@ -1006,8 +1004,8 @@ int vb2_ioctl_reqbufs(struct file *file, void *priv, int res = vb2_verify_memory_type(vdev->queue, p->memory, p->type); u32 flags = p->flags; - fill_buf_caps(vdev->queue, &p->capabilities); - validate_memory_flags(vdev->queue, p->memory, &flags); + vb2_set_flags_and_caps(vdev->queue, p->memory, &flags, + &p->capabilities, NULL); p->flags = flags; if (res) return res; @@ -1026,12 +1024,11 @@ int vb2_ioctl_create_bufs(struct file *file, void *priv, struct v4l2_create_buffers *p) { struct video_device *vdev = video_devdata(file); - int res = vb2_verify_memory_type(vdev->queue, p->memory, - p->format.type); + int res = vb2_verify_memory_type(vdev->queue, p->memory, p->format.type); - p->index = vdev->queue->num_buffers; - fill_buf_caps(vdev->queue, &p->capabilities); - validate_memory_flags(vdev->queue, p->memory, &p->flags); + p->index = vb2_get_num_buffers(vdev->queue); + vb2_set_flags_and_caps(vdev->queue, p->memory, &p->flags, + &p->capabilities, &p->max_num_buffers); /* * If count == 0, then just check if memory and type are valid. * Any -EBUSY result from vb2_verify_memory_type can be mapped to 0. diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c index bd38ce444232..46676f2c89c7 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c @@ -289,7 +289,7 @@ static const struct i2c_algorithm netup_i2c_algorithm = { static const struct i2c_adapter netup_i2c_adapter = { .owner = THIS_MODULE, .name = NETUP_UNIDVB_NAME, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .class = I2C_CLASS_HWMON, .algo = &netup_i2c_algorithm, }; diff --git a/drivers/media/pci/solo6x10/solo6x10-offsets.h b/drivers/media/pci/solo6x10/solo6x10-offsets.h index f414ee1316f2..fdbb817e6360 100644 --- a/drivers/media/pci/solo6x10/solo6x10-offsets.h +++ b/drivers/media/pci/solo6x10/solo6x10-offsets.h @@ -57,16 +57,16 @@ #define SOLO_MP4E_EXT_ADDR(__solo) \ (SOLO_EREF_EXT_ADDR(__solo) + SOLO_EREF_EXT_AREA(__solo)) #define SOLO_MP4E_EXT_SIZE(__solo) \ - max((__solo->nr_chans * 0x00080000), \ - min(((__solo->sdram_size - SOLO_MP4E_EXT_ADDR(__solo)) - \ - __SOLO_JPEG_MIN_SIZE(__solo)), 0x00ff0000)) + clamp(__solo->sdram_size - SOLO_MP4E_EXT_ADDR(__solo) - \ + __SOLO_JPEG_MIN_SIZE(__solo), \ + __solo->nr_chans * 0x00080000, 0x00ff0000) #define __SOLO_JPEG_MIN_SIZE(__solo) (__solo->nr_chans * 0x00080000) #define SOLO_JPEG_EXT_ADDR(__solo) \ (SOLO_MP4E_EXT_ADDR(__solo) + SOLO_MP4E_EXT_SIZE(__solo)) #define SOLO_JPEG_EXT_SIZE(__solo) \ - max(__SOLO_JPEG_MIN_SIZE(__solo), \ - min((__solo->sdram_size - SOLO_JPEG_EXT_ADDR(__solo)), 0x00ff0000)) + clamp(__solo->sdram_size - SOLO_JPEG_EXT_ADDR(__solo), \ + __SOLO_JPEG_MIN_SIZE(__solo), 0x00ff0000) #define SOLO_SDRAM_END(__solo) \ (SOLO_JPEG_EXT_ADDR(__solo) + SOLO_JPEG_EXT_SIZE(__solo)) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c index bfe4caa79cc9..0d90b5820bef 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c @@ -272,7 +272,7 @@ static const struct wave5_match_data ti_wave521c_data = { }; static const struct of_device_id wave5_dt_ids[] = { - { .compatible = "ti,k3-j721s2-wave521c", .data = &ti_wave521c_data }, + { .compatible = "ti,j721s2-wave521c", .data = &ti_wave521c_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, wave5_dt_ids); diff --git a/drivers/media/rc/pwm-ir-tx.c b/drivers/media/rc/pwm-ir-tx.c index c5f37c03af9c..fe368aebbc13 100644 --- a/drivers/media/rc/pwm-ir-tx.c +++ b/drivers/media/rc/pwm-ir-tx.c @@ -10,6 +10,8 @@ #include <linux/slab.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/hrtimer.h> +#include <linux/completion.h> #include <media/rc-core.h> #define DRIVER_NAME "pwm-ir-tx" @@ -17,8 +19,14 @@ struct pwm_ir { struct pwm_device *pwm; - unsigned int carrier; - unsigned int duty_cycle; + struct hrtimer timer; + struct completion tx_done; + struct pwm_state *state; + u32 carrier; + u32 duty_cycle; + const unsigned int *txbuf; + unsigned int txbuf_len; + unsigned int txbuf_index; }; static const struct of_device_id pwm_ir_of_match[] = { @@ -49,8 +57,8 @@ static int pwm_ir_set_carrier(struct rc_dev *dev, u32 carrier) return 0; } -static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf, - unsigned int count) +static int pwm_ir_tx_sleep(struct rc_dev *dev, unsigned int *txbuf, + unsigned int count) { struct pwm_ir *pwm_ir = dev->priv; struct pwm_device *pwm = pwm_ir->pwm; @@ -68,7 +76,7 @@ static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf, for (i = 0; i < count; i++) { state.enabled = !(i % 2); - pwm_apply_state(pwm, &state); + pwm_apply_might_sleep(pwm, &state); edge = ktime_add_us(edge, txbuf[i]); delta = ktime_us_delta(edge, ktime_get()); @@ -77,11 +85,67 @@ static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf, } state.enabled = false; - pwm_apply_state(pwm, &state); + pwm_apply_might_sleep(pwm, &state); return count; } +static int pwm_ir_tx_atomic(struct rc_dev *dev, unsigned int *txbuf, + unsigned int count) +{ + struct pwm_ir *pwm_ir = dev->priv; + struct pwm_device *pwm = pwm_ir->pwm; + struct pwm_state state; + + pwm_init_state(pwm, &state); + + state.period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier); + pwm_set_relative_duty_cycle(&state, pwm_ir->duty_cycle, 100); + + pwm_ir->txbuf = txbuf; + pwm_ir->txbuf_len = count; + pwm_ir->txbuf_index = 0; + pwm_ir->state = &state; + + hrtimer_start(&pwm_ir->timer, 0, HRTIMER_MODE_REL); + + wait_for_completion(&pwm_ir->tx_done); + + return count; +} + +static enum hrtimer_restart pwm_ir_timer(struct hrtimer *timer) +{ + struct pwm_ir *pwm_ir = container_of(timer, struct pwm_ir, timer); + ktime_t now; + + /* + * If we happen to hit an odd latency spike, loop through the + * pulses until we catch up. + */ + do { + u64 ns; + + pwm_ir->state->enabled = !(pwm_ir->txbuf_index % 2); + pwm_apply_atomic(pwm_ir->pwm, pwm_ir->state); + + if (pwm_ir->txbuf_index >= pwm_ir->txbuf_len) { + complete(&pwm_ir->tx_done); + + return HRTIMER_NORESTART; + } + + ns = US_TO_NS(pwm_ir->txbuf[pwm_ir->txbuf_index]); + hrtimer_add_expires_ns(timer, ns); + + pwm_ir->txbuf_index++; + + now = timer->base->get_time(); + } while (hrtimer_get_expires_tv64(timer) < now); + + return HRTIMER_RESTART; +} + static int pwm_ir_probe(struct platform_device *pdev) { struct pwm_ir *pwm_ir; @@ -103,10 +167,19 @@ static int pwm_ir_probe(struct platform_device *pdev) if (!rcdev) return -ENOMEM; + if (pwm_might_sleep(pwm_ir->pwm)) { + dev_info(&pdev->dev, "TX will not be accurate as PWM device might sleep\n"); + rcdev->tx_ir = pwm_ir_tx_sleep; + } else { + init_completion(&pwm_ir->tx_done); + hrtimer_init(&pwm_ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pwm_ir->timer.function = pwm_ir_timer; + rcdev->tx_ir = pwm_ir_tx_atomic; + } + rcdev->priv = pwm_ir; rcdev->driver_name = DRIVER_NAME; rcdev->device_name = DEVICE_NAME; - rcdev->tx_ir = pwm_ir_tx; rcdev->s_tx_duty_cycle = pwm_ir_set_duty_cycle; rcdev->s_tx_carrier = pwm_ir_set_carrier; diff --git a/drivers/memory/tegra/tegra186.c b/drivers/memory/tegra/tegra186.c index 0ff014a9d3cd..1b3183951bfe 100644 --- a/drivers/memory/tegra/tegra186.c +++ b/drivers/memory/tegra/tegra186.c @@ -114,9 +114,12 @@ static void tegra186_mc_client_sid_override(struct tegra_mc *mc, static int tegra186_mc_probe_device(struct tegra_mc *mc, struct device *dev) { #if IS_ENABLED(CONFIG_IOMMU_API) - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); struct of_phandle_args args; unsigned int i, index = 0; + u32 sid; + + if (!tegra_dev_iommu_get_stream_id(dev, &sid)) + return 0; while (!of_parse_phandle_with_args(dev->of_node, "interconnects", "#interconnect-cells", index, &args)) { @@ -124,11 +127,10 @@ static int tegra186_mc_probe_device(struct tegra_mc *mc, struct device *dev) for (i = 0; i < mc->soc->num_clients; i++) { const struct tegra_mc_client *client = &mc->soc->clients[i]; - if (client->id == args.args[0]) { - u32 sid = fwspec->ids[0] & MC_SID_STREAMID_OVERRIDE_MASK; - - tegra186_mc_client_sid_override(mc, client, sid); - } + if (client->id == args.args[0]) + tegra186_mc_client_sid_override( + mc, client, + sid & MC_SID_STREAMID_OVERRIDE_MASK); } } diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 925c19ee513b..e7a6e45b9fac 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1483,6 +1483,7 @@ config MFD_SYSCON config MFD_TI_AM335X_TSCADC tristate "TI ADC / Touch Screen chip support" + depends on ARCH_OMAP2PLUS || ARCH_K3 || COMPILE_TEST select MFD_CORE select REGMAP select REGMAP_MMIO diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index eeeb62415f53..8f3ebe651eea 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -30,7 +30,7 @@ static void ab8500_power_off(void) { sigset_t old; sigset_t all; - static const char * const pss[] = {"ab8500_ac", "pm2301", "ab8500_usb"}; + static const char * const pss[] = {"ab8500_ac", "ab8500_usb"}; int i; bool charger_present = false; union power_supply_propval val; @@ -140,14 +140,12 @@ static int ab8500_sysctrl_probe(struct platform_device *pdev) return 0; } -static int ab8500_sysctrl_remove(struct platform_device *pdev) +static void ab8500_sysctrl_remove(struct platform_device *pdev) { sysctrl_dev = NULL; if (pm_power_off == ab8500_power_off) pm_power_off = NULL; - - return 0; } static const struct of_device_id ab8500_sysctrl_match[] = { @@ -161,7 +159,7 @@ static struct platform_driver ab8500_sysctrl_driver = { .of_match_table = ab8500_sysctrl_match, }, .probe = ab8500_sysctrl_probe, - .remove = ab8500_sysctrl_remove, + .remove_new = ab8500_sysctrl_remove, }; static int __init ab8500_sysctrl_init(void) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 79d393b602bf..603b1cd52785 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -288,13 +288,12 @@ failed: return retval; } -static int ec_device_remove(struct platform_device *pdev) +static void ec_device_remove(struct platform_device *pdev) { struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); mfd_remove_devices(ec->dev); device_unregister(&ec->class_dev); - return 0; } static const struct platform_device_id cros_ec_id[] = { @@ -309,7 +308,7 @@ static struct platform_driver cros_ec_dev_driver = { }, .id_table = cros_ec_id, .probe = ec_device_probe, - .remove = ec_device_remove, + .remove_new = ec_device_remove, }; static int __init cros_ec_dev_init(void) diff --git a/drivers/mfd/cs42l43-sdw.c b/drivers/mfd/cs42l43-sdw.c index 7392b3d2e6b9..1d85bbf8cdd5 100644 --- a/drivers/mfd/cs42l43-sdw.c +++ b/drivers/mfd/cs42l43-sdw.c @@ -6,24 +6,23 @@ * Cirrus Logic International Semiconductor Ltd. */ +#include <linux/device.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/mfd/cs42l43-regs.h> #include <linux/module.h> -#include <linux/device.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_registers.h> #include <linux/soundwire/sdw_type.h> #include "cs42l43.h" -enum cs42l43_sdw_ports { - CS42L43_DMIC_DEC_ASP_PORT = 1, - CS42L43_SPK_TX_PORT, - CS42L43_SPDIF_HP_PORT, - CS42L43_SPK_RX_PORT, - CS42L43_ASP_PORT, -}; +#define CS42L43_SDW_PORT(port, chans) { \ + .num = port, \ + .max_ch = chans, \ + .type = SDW_DPN_FULL, \ + .max_word = 24, \ +} static const struct regmap_config cs42l43_sdw_regmap = { .reg_bits = 32, @@ -42,65 +41,48 @@ static const struct regmap_config cs42l43_sdw_regmap = { .num_reg_defaults = ARRAY_SIZE(cs42l43_reg_default), }; +static const struct sdw_dpn_prop cs42l43_src_port_props[] = { + CS42L43_SDW_PORT(1, 4), + CS42L43_SDW_PORT(2, 2), + CS42L43_SDW_PORT(3, 2), + CS42L43_SDW_PORT(4, 2), +}; + +static const struct sdw_dpn_prop cs42l43_sink_port_props[] = { + CS42L43_SDW_PORT(5, 2), + CS42L43_SDW_PORT(6, 2), + CS42L43_SDW_PORT(7, 2), +}; + static int cs42l43_read_prop(struct sdw_slave *sdw) { struct sdw_slave_prop *prop = &sdw->prop; struct device *dev = &sdw->dev; - struct sdw_dpn_prop *dpn; - unsigned long addr; - int nval; int i; - u32 bit; prop->use_domain_irq = true; prop->paging_support = true; prop->wake_capable = true; - prop->source_ports = BIT(CS42L43_DMIC_DEC_ASP_PORT) | BIT(CS42L43_SPK_TX_PORT); - prop->sink_ports = BIT(CS42L43_SPDIF_HP_PORT) | - BIT(CS42L43_SPK_RX_PORT) | BIT(CS42L43_ASP_PORT); prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF; - nval = hweight32(prop->source_ports); - prop->src_dpn_prop = devm_kcalloc(dev, nval, sizeof(*prop->src_dpn_prop), - GFP_KERNEL); + for (i = 0; i < ARRAY_SIZE(cs42l43_src_port_props); i++) + prop->source_ports |= BIT(cs42l43_src_port_props[i].num); + + prop->src_dpn_prop = devm_kmemdup(dev, cs42l43_src_port_props, + sizeof(cs42l43_src_port_props), GFP_KERNEL); if (!prop->src_dpn_prop) return -ENOMEM; - i = 0; - dpn = prop->src_dpn_prop; - addr = prop->source_ports; - for_each_set_bit(bit, &addr, 32) { - dpn[i].num = bit; - dpn[i].max_ch = 2; - dpn[i].type = SDW_DPN_FULL; - dpn[i].max_word = 24; - i++; - } - /* - * All ports are 2 channels max, except the first one, - * CS42L43_DMIC_DEC_ASP_PORT. - */ - dpn[CS42L43_DMIC_DEC_ASP_PORT].max_ch = 4; + for (i = 0; i < ARRAY_SIZE(cs42l43_sink_port_props); i++) + prop->sink_ports |= BIT(cs42l43_sink_port_props[i].num); - nval = hweight32(prop->sink_ports); - prop->sink_dpn_prop = devm_kcalloc(dev, nval, sizeof(*prop->sink_dpn_prop), - GFP_KERNEL); + prop->sink_dpn_prop = devm_kmemdup(dev, cs42l43_sink_port_props, + sizeof(cs42l43_sink_port_props), GFP_KERNEL); if (!prop->sink_dpn_prop) return -ENOMEM; - i = 0; - dpn = prop->sink_dpn_prop; - addr = prop->sink_ports; - for_each_set_bit(bit, &addr, 32) { - dpn[i].num = bit; - dpn[i].max_ch = 2; - dpn[i].type = SDW_DPN_FULL; - dpn[i].max_word = 24; - i++; - } - return 0; } diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c index 45da007d3e70..73a22107900c 100644 --- a/drivers/mfd/da9062-core.c +++ b/drivers/mfd/da9062-core.c @@ -588,16 +588,8 @@ static struct regmap_config da9062_regmap_config = { .volatile_table = &da9062_aa_volatile_table, }; -static const struct of_device_id da9062_dt_ids[] = { - { .compatible = "dlg,da9061", .data = (void *)COMPAT_TYPE_DA9061, }, - { .compatible = "dlg,da9062", .data = (void *)COMPAT_TYPE_DA9062, }, - { } -}; -MODULE_DEVICE_TABLE(of, da9062_dt_ids); - static int da9062_i2c_probe(struct i2c_client *i2c) { - const struct i2c_device_id *id = i2c_client_get_device_id(i2c); struct da9062 *chip; unsigned int irq_base = 0; const struct mfd_cell *cell; @@ -611,10 +603,7 @@ static int da9062_i2c_probe(struct i2c_client *i2c) if (!chip) return -ENOMEM; - if (i2c->dev.of_node) - chip->chip_type = (uintptr_t)of_device_get_match_data(&i2c->dev); - else - chip->chip_type = id->driver_data; + chip->chip_type = (uintptr_t)i2c_get_match_data(i2c); i2c_set_clientdata(i2c, chip); chip->dev = &i2c->dev; @@ -714,10 +703,17 @@ static void da9062_i2c_remove(struct i2c_client *i2c) regmap_del_irq_chip(i2c->irq, chip->regmap_irq); } +static const struct of_device_id da9062_dt_ids[] = { + { .compatible = "dlg,da9061", .data = (void *)COMPAT_TYPE_DA9061 }, + { .compatible = "dlg,da9062", .data = (void *)COMPAT_TYPE_DA9062 }, + { } +}; +MODULE_DEVICE_TABLE(of, da9062_dt_ids); + static const struct i2c_device_id da9062_i2c_id[] = { { "da9061", COMPAT_TYPE_DA9061 }, { "da9062", COMPAT_TYPE_DA9062 }, - { }, + { } }; MODULE_DEVICE_TABLE(i2c, da9062_i2c_id); diff --git a/drivers/mfd/exynos-lpass.c b/drivers/mfd/exynos-lpass.c index 1506d8d352b1..e58990c85ed8 100644 --- a/drivers/mfd/exynos-lpass.c +++ b/drivers/mfd/exynos-lpass.c @@ -137,7 +137,7 @@ static int exynos_lpass_probe(struct platform_device *pdev) return devm_of_platform_populate(dev); } -static int exynos_lpass_remove(struct platform_device *pdev) +static void exynos_lpass_remove(struct platform_device *pdev) { struct exynos_lpass *lpass = platform_get_drvdata(pdev); @@ -146,8 +146,6 @@ static int exynos_lpass_remove(struct platform_device *pdev) if (!pm_runtime_status_suspended(&pdev->dev)) exynos_lpass_disable(lpass); regmap_exit(lpass->top); - - return 0; } static int __maybe_unused exynos_lpass_suspend(struct device *dev) @@ -187,7 +185,7 @@ static struct platform_driver exynos_lpass_driver = { .of_match_table = exynos_lpass_of_match, }, .probe = exynos_lpass_probe, - .remove = exynos_lpass_remove, + .remove_new = exynos_lpass_remove, }; module_platform_driver(exynos_lpass_driver); diff --git a/drivers/mfd/fsl-imx25-tsadc.c b/drivers/mfd/fsl-imx25-tsadc.c index 089c2ce615b6..74f38bf3778f 100644 --- a/drivers/mfd/fsl-imx25-tsadc.c +++ b/drivers/mfd/fsl-imx25-tsadc.c @@ -194,11 +194,9 @@ err_irq: return ret; } -static int mx25_tsadc_remove(struct platform_device *pdev) +static void mx25_tsadc_remove(struct platform_device *pdev) { mx25_tsadc_unset_irq(pdev); - - return 0; } static const struct of_device_id mx25_tsadc_ids[] = { @@ -213,7 +211,7 @@ static struct platform_driver mx25_tsadc_driver = { .of_match_table = mx25_tsadc_ids, }, .probe = mx25_tsadc_probe, - .remove = mx25_tsadc_remove, + .remove_new = mx25_tsadc_remove, }; module_platform_driver(mx25_tsadc_driver); diff --git a/drivers/mfd/hi655x-pmic.c b/drivers/mfd/hi655x-pmic.c index 8feae8d8fd9d..042109304db4 100644 --- a/drivers/mfd/hi655x-pmic.c +++ b/drivers/mfd/hi655x-pmic.c @@ -144,13 +144,12 @@ static int hi655x_pmic_probe(struct platform_device *pdev) return 0; } -static int hi655x_pmic_remove(struct platform_device *pdev) +static void hi655x_pmic_remove(struct platform_device *pdev) { struct hi655x_pmic *pmic = platform_get_drvdata(pdev); regmap_del_irq_chip(gpiod_to_irq(pmic->gpio), pmic->irq_data); mfd_remove_devices(&pdev->dev); - return 0; } static const struct of_device_id hi655x_pmic_match[] = { @@ -165,7 +164,7 @@ static struct platform_driver hi655x_pmic_driver = { .of_match_table = hi655x_pmic_match, }, .probe = hi655x_pmic_probe, - .remove = hi655x_pmic_remove, + .remove_new = hi655x_pmic_remove, }; module_platform_driver(hi655x_pmic_driver); diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c index 212818aef93e..2a83f8678f1d 100644 --- a/drivers/mfd/intel-lpss-acpi.c +++ b/drivers/mfd/intel-lpss-acpi.c @@ -8,15 +8,20 @@ * Mika Westerberg <mika.westerberg@linux.intel.com> */ -#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/gfp_types.h> #include <linux/ioport.h> -#include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/property.h> + #include <linux/pxa2xx_ssp.h> +#include <asm/errno.h> + #include "intel-lpss.h" static const struct property_entry spt_spi_properties[] = { @@ -169,23 +174,20 @@ MODULE_DEVICE_TABLE(acpi, intel_lpss_acpi_ids); static int intel_lpss_acpi_probe(struct platform_device *pdev) { + const struct intel_lpss_platform_info *data; struct intel_lpss_platform_info *info; - const struct acpi_device_id *id; int ret; - id = acpi_match_device(intel_lpss_acpi_ids, &pdev->dev); - if (!id) + data = device_get_match_data(&pdev->dev); + if (!data) return -ENODEV; - info = devm_kmemdup(&pdev->dev, (void *)id->driver_data, sizeof(*info), - GFP_KERNEL); + info = devm_kmemdup(&pdev->dev, data, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; + /* No need to check mem and irq here as intel_lpss_probe() does it for us */ info->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!info->mem) - return -ENODEV; - info->irq = platform_get_irq(pdev, 0); ret = intel_lpss_probe(&pdev->dev, info); @@ -198,23 +200,19 @@ static int intel_lpss_acpi_probe(struct platform_device *pdev) return 0; } -static int intel_lpss_acpi_remove(struct platform_device *pdev) +static void intel_lpss_acpi_remove(struct platform_device *pdev) { intel_lpss_remove(&pdev->dev); pm_runtime_disable(&pdev->dev); - - return 0; } -static INTEL_LPSS_PM_OPS(intel_lpss_acpi_pm_ops); - static struct platform_driver intel_lpss_acpi_driver = { .probe = intel_lpss_acpi_probe, - .remove = intel_lpss_acpi_remove, + .remove_new = intel_lpss_acpi_remove, .driver = { .name = "intel-lpss", .acpi_match_table = intel_lpss_acpi_ids, - .pm = &intel_lpss_acpi_pm_ops, + .pm = pm_ptr(&intel_lpss_pm_ops), }, }; @@ -224,3 +222,4 @@ MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); MODULE_DESCRIPTION("Intel LPSS ACPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(INTEL_LPSS); diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index ae5759200622..4621d3950b8f 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -8,14 +8,19 @@ * Mika Westerberg <mika.westerberg@linux.intel.com> */ -#include <linux/ioport.h> -#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/gfp_types.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/property.h> + #include <linux/pxa2xx_ssp.h> +#include <asm/errno.h> + #include "intel-lpss.h" /* Some DSDTs have an unused GEXP ACPI device conflicting with I2C4 resources */ @@ -30,6 +35,7 @@ static const struct pci_device_id ignore_resource_conflicts_ids[] = { static int intel_lpss_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + const struct intel_lpss_platform_info *data = (void *)id->driver_data; struct intel_lpss_platform_info *info; int ret; @@ -37,13 +43,17 @@ static int intel_lpss_pci_probe(struct pci_dev *pdev, if (ret) return ret; - info = devm_kmemdup(&pdev->dev, (void *)id->driver_data, sizeof(*info), - GFP_KERNEL); + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY); + if (ret < 0) + return ret; + + info = devm_kmemdup(&pdev->dev, data, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; - info->mem = &pdev->resource[0]; - info->irq = pdev->irq; + /* No need to check mem and irq here as intel_lpss_probe() does it for us */ + info->mem = pci_resource_n(pdev, 0); + info->irq = pci_irq_vector(pdev, 0); if (pci_match_id(ignore_resource_conflicts_ids, pdev)) info->ignore_resource_conflicts = true; @@ -72,8 +82,6 @@ static void intel_lpss_pci_remove(struct pci_dev *pdev) intel_lpss_remove(&pdev->dev); } -static INTEL_LPSS_PM_OPS(intel_lpss_pci_pm_ops); - static const struct property_entry spt_spi_properties[] = { PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_SPT_SSP), { } @@ -584,7 +592,7 @@ static struct pci_driver intel_lpss_pci_driver = { .probe = intel_lpss_pci_probe, .remove = intel_lpss_pci_remove, .driver = { - .pm = &intel_lpss_pci_pm_ops, + .pm = pm_ptr(&intel_lpss_pm_ops), }, }; @@ -594,3 +602,4 @@ MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); MODULE_DESCRIPTION("Intel LPSS PCI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(INTEL_LPSS); diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index 9591b354072a..eff423f7dd28 100644 --- a/drivers/mfd/intel-lpss.c +++ b/drivers/mfd/intel-lpss.c @@ -10,26 +10,34 @@ * Jarkko Nikula <jarkko.nikula@linux.intel.com> */ -#include <linux/clk.h> +#include <linux/array_size.h> +#include <linux/bits.h> #include <linux/clkdev.h> +#include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gfp_types.h> #include <linux/idr.h> #include <linux/io.h> #include <linux/ioport.h> -#include <linux/kernel.h> -#include <linux/module.h> #include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/pm.h> #include <linux/pm_qos.h> #include <linux/pm_runtime.h> -#include <linux/property.h> -#include <linux/seq_file.h> +#include <linux/sprintf.h> +#include <linux/types.h> + #include <linux/io-64-nonatomic-lo-hi.h> #include <linux/dma/idma64.h> #include "intel-lpss.h" +struct dentry; + #define LPSS_DEV_OFFSET 0x000 #define LPSS_DEV_SIZE 0x200 #define LPSS_PRIV_OFFSET 0x200 @@ -301,8 +309,8 @@ static int intel_lpss_register_clock_divider(struct intel_lpss *lpss, snprintf(name, sizeof(name), "%s-div", devname); tmp = clk_register_fractional_divider(NULL, name, __clk_get_name(tmp), + 0, lpss->priv, 1, 15, 16, 15, CLK_FRAC_DIVIDER_POWER_OF_TWO_PS, - lpss->priv, 1, 15, 16, 15, 0, NULL); if (IS_ERR(tmp)) return PTR_ERR(tmp); @@ -378,9 +386,12 @@ int intel_lpss_probe(struct device *dev, struct intel_lpss *lpss; int ret; - if (!info || !info->mem || info->irq <= 0) + if (!info || !info->mem) return -EINVAL; + if (info->irq < 0) + return info->irq; + lpss = devm_kzalloc(dev, sizeof(*lpss), GFP_KERNEL); if (!lpss) return -ENOMEM; @@ -405,7 +416,7 @@ int intel_lpss_probe(struct device *dev, intel_lpss_init_dev(lpss); - lpss->devid = ida_simple_get(&intel_lpss_devid_ida, 0, 0, GFP_KERNEL); + lpss->devid = ida_alloc(&intel_lpss_devid_ida, GFP_KERNEL); if (lpss->devid < 0) return lpss->devid; @@ -442,11 +453,11 @@ err_remove_ltr: intel_lpss_unregister_clock(lpss); err_clk_register: - ida_simple_remove(&intel_lpss_devid_ida, lpss->devid); + ida_free(&intel_lpss_devid_ida, lpss->devid); return ret; } -EXPORT_SYMBOL_GPL(intel_lpss_probe); +EXPORT_SYMBOL_NS_GPL(intel_lpss_probe, INTEL_LPSS); void intel_lpss_remove(struct device *dev) { @@ -456,11 +467,10 @@ void intel_lpss_remove(struct device *dev) intel_lpss_debugfs_remove(lpss); intel_lpss_ltr_hide(lpss); intel_lpss_unregister_clock(lpss); - ida_simple_remove(&intel_lpss_devid_ida, lpss->devid); + ida_free(&intel_lpss_devid_ida, lpss->devid); } -EXPORT_SYMBOL_GPL(intel_lpss_remove); +EXPORT_SYMBOL_NS_GPL(intel_lpss_remove, INTEL_LPSS); -#ifdef CONFIG_PM static int resume_lpss_device(struct device *dev, void *data) { if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND)) @@ -469,7 +479,7 @@ static int resume_lpss_device(struct device *dev, void *data) return 0; } -int intel_lpss_prepare(struct device *dev) +static int intel_lpss_prepare(struct device *dev) { /* * Resume both child devices before entering system sleep. This @@ -478,9 +488,8 @@ int intel_lpss_prepare(struct device *dev) device_for_each_child_reverse(dev, NULL, resume_lpss_device); return 0; } -EXPORT_SYMBOL_GPL(intel_lpss_prepare); -int intel_lpss_suspend(struct device *dev) +static int intel_lpss_suspend(struct device *dev) { struct intel_lpss *lpss = dev_get_drvdata(dev); unsigned int i; @@ -499,9 +508,8 @@ int intel_lpss_suspend(struct device *dev) return 0; } -EXPORT_SYMBOL_GPL(intel_lpss_suspend); -int intel_lpss_resume(struct device *dev) +static int intel_lpss_resume(struct device *dev) { struct intel_lpss *lpss = dev_get_drvdata(dev); unsigned int i; @@ -514,8 +522,12 @@ int intel_lpss_resume(struct device *dev) return 0; } -EXPORT_SYMBOL_GPL(intel_lpss_resume); -#endif + +EXPORT_NS_GPL_DEV_PM_OPS(intel_lpss_pm_ops, INTEL_LPSS) = { + .prepare = pm_sleep_ptr(&intel_lpss_prepare), + LATE_SYSTEM_SLEEP_PM_OPS(intel_lpss_suspend, intel_lpss_resume) + RUNTIME_PM_OPS(intel_lpss_suspend, intel_lpss_resume, NULL) +}; static int __init intel_lpss_init(void) { diff --git a/drivers/mfd/intel-lpss.h b/drivers/mfd/intel-lpss.h index 062ce95b68b9..c1d72b117ed5 100644 --- a/drivers/mfd/intel-lpss.h +++ b/drivers/mfd/intel-lpss.h @@ -30,32 +30,6 @@ int intel_lpss_probe(struct device *dev, const struct intel_lpss_platform_info *info); void intel_lpss_remove(struct device *dev); -#ifdef CONFIG_PM -int intel_lpss_prepare(struct device *dev); -int intel_lpss_suspend(struct device *dev); -int intel_lpss_resume(struct device *dev); - -#ifdef CONFIG_PM_SLEEP -#define INTEL_LPSS_SLEEP_PM_OPS \ - .prepare = intel_lpss_prepare, \ - SET_LATE_SYSTEM_SLEEP_PM_OPS(intel_lpss_suspend, intel_lpss_resume) -#else -#define INTEL_LPSS_SLEEP_PM_OPS -#endif - -#define INTEL_LPSS_RUNTIME_PM_OPS \ - .runtime_suspend = intel_lpss_suspend, \ - .runtime_resume = intel_lpss_resume, - -#else /* !CONFIG_PM */ -#define INTEL_LPSS_SLEEP_PM_OPS -#define INTEL_LPSS_RUNTIME_PM_OPS -#endif /* CONFIG_PM */ - -#define INTEL_LPSS_PM_OPS(name) \ -const struct dev_pm_ops name = { \ - INTEL_LPSS_SLEEP_PM_OPS \ - INTEL_LPSS_RUNTIME_PM_OPS \ -} +extern const struct dev_pm_ops intel_lpss_pm_ops; #endif /* __MFD_INTEL_LPSS_H */ diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index 33c6cfe9fe42..67af36a38913 100644 --- a/drivers/mfd/kempld-core.c +++ b/drivers/mfd/kempld-core.c @@ -535,7 +535,7 @@ static int kempld_probe(struct platform_device *pdev) return kempld_detect_device(pld); } -static int kempld_remove(struct platform_device *pdev) +static void kempld_remove(struct platform_device *pdev) { struct kempld_device_data *pld = platform_get_drvdata(pdev); const struct kempld_platform_data *pdata = dev_get_platdata(pld->dev); @@ -544,8 +544,6 @@ static int kempld_remove(struct platform_device *pdev) mfd_remove_devices(&pdev->dev); pdata->release_hardware_mutex(pld); - - return 0; } #ifdef CONFIG_ACPI @@ -563,7 +561,7 @@ static struct platform_driver kempld_driver = { .acpi_match_table = ACPI_PTR(kempld_acpi_table), }, .probe = kempld_probe, - .remove = kempld_remove, + .remove_new = kempld_remove, }; static const struct dmi_system_id kempld_dmi_table[] __initconst = { diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 1c9831b78cf9..3883e472b739 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -232,7 +232,7 @@ static int mcp_sa11x0_probe(struct platform_device *dev) return ret; } -static int mcp_sa11x0_remove(struct platform_device *dev) +static void mcp_sa11x0_remove(struct platform_device *dev) { struct mcp *mcp = platform_get_drvdata(dev); struct mcp_sa11x0 *m = priv(mcp); @@ -251,8 +251,6 @@ static int mcp_sa11x0_remove(struct platform_device *dev) mcp_host_free(mcp); release_mem_region(mem1->start, resource_size(mem1)); release_mem_region(mem0->start, resource_size(mem0)); - - return 0; } static int mcp_sa11x0_suspend(struct device *dev) @@ -288,7 +286,7 @@ static const struct dev_pm_ops mcp_sa11x0_pm_ops = { static struct platform_driver mcp_sa11x0_driver = { .probe = mcp_sa11x0_probe, - .remove = mcp_sa11x0_remove, + .remove_new = mcp_sa11x0_remove, .driver = { .name = DRIVER_NAME, .pm = pm_sleep_ptr(&mcp_sa11x0_pm_ops), diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c index ec1b356562b9..73893890b50a 100644 --- a/drivers/mfd/mxs-lradc.c +++ b/drivers/mfd/mxs-lradc.c @@ -230,13 +230,11 @@ err_clk: return ret; } -static int mxs_lradc_remove(struct platform_device *pdev) +static void mxs_lradc_remove(struct platform_device *pdev) { struct mxs_lradc *lradc = platform_get_drvdata(pdev); clk_disable_unprepare(lradc->clk); - - return 0; } static struct platform_driver mxs_lradc_driver = { @@ -245,7 +243,7 @@ static struct platform_driver mxs_lradc_driver = { .of_match_table = mxs_lradc_dt_ids, }, .probe = mxs_lradc_probe, - .remove = mxs_lradc_remove, + .remove_new = mxs_lradc_remove, }; module_platform_driver(mxs_lradc_driver); diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 78f1bb55dbc0..ebc62033db16 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -816,13 +816,12 @@ static int usbhs_omap_remove_child(struct device *dev, void *data) * * Reverses the effect of usbhs_omap_probe(). */ -static int usbhs_omap_remove(struct platform_device *pdev) +static void usbhs_omap_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); /* remove children */ device_for_each_child(&pdev->dev, NULL, usbhs_omap_remove_child); - return 0; } static const struct dev_pm_ops usbhsomap_dev_pm_ops = { @@ -845,7 +844,7 @@ static struct platform_driver usbhs_omap_driver = { .of_match_table = usbhs_omap_dt_ids, }, .probe = usbhs_omap_probe, - .remove = usbhs_omap_remove, + .remove_new = usbhs_omap_remove, }; MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>"); diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 906353735c78..b6303ddb013b 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -270,7 +270,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) * * Reverses the effect of usbtll_omap_probe(). */ -static int usbtll_omap_remove(struct platform_device *pdev) +static void usbtll_omap_remove(struct platform_device *pdev) { struct usbtll_omap *tll = platform_get_drvdata(pdev); int i; @@ -287,7 +287,6 @@ static int usbtll_omap_remove(struct platform_device *pdev) } pm_runtime_disable(&pdev->dev); - return 0; } static const struct of_device_id usbtll_omap_dt_ids[] = { @@ -303,7 +302,7 @@ static struct platform_driver usbtll_omap_driver = { .of_match_table = usbtll_omap_dt_ids, }, .probe = usbtll_omap_probe, - .remove = usbtll_omap_remove, + .remove_new = usbtll_omap_remove, }; int omap_tll_init(struct usbhs_omap_platform_data *pdata) diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c index 191b1bc6141c..ab55906f91f9 100644 --- a/drivers/mfd/pcf50633-adc.c +++ b/drivers/mfd/pcf50633-adc.c @@ -218,7 +218,7 @@ static int pcf50633_adc_probe(struct platform_device *pdev) return 0; } -static int pcf50633_adc_remove(struct platform_device *pdev) +static void pcf50633_adc_remove(struct platform_device *pdev) { struct pcf50633_adc *adc = platform_get_drvdata(pdev); int i, head; @@ -236,8 +236,6 @@ static int pcf50633_adc_remove(struct platform_device *pdev) kfree(adc->queue[i]); mutex_unlock(&adc->queue_mutex); - - return 0; } static struct platform_driver pcf50633_adc_driver = { @@ -245,7 +243,7 @@ static struct platform_driver pcf50633_adc_driver = { .name = "pcf50633-adc", }, .probe = pcf50633_adc_probe, - .remove = pcf50633_adc_remove, + .remove_new = pcf50633_adc_remove, }; module_platform_driver(pcf50633_adc_driver); diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c index 07c531bd1236..8b6285f687da 100644 --- a/drivers/mfd/qcom-pm8xxx.c +++ b/drivers/mfd/qcom-pm8xxx.c @@ -585,19 +585,17 @@ static int pm8xxx_remove_child(struct device *dev, void *unused) return 0; } -static int pm8xxx_remove(struct platform_device *pdev) +static void pm8xxx_remove(struct platform_device *pdev) { struct pm_irq_chip *chip = platform_get_drvdata(pdev); device_for_each_child(&pdev->dev, NULL, pm8xxx_remove_child); irq_domain_remove(chip->irqdomain); - - return 0; } static struct platform_driver pm8xxx_driver = { .probe = pm8xxx_probe, - .remove = pm8xxx_remove, + .remove_new = pm8xxx_remove, .driver = { .name = "pm8xxx-core", .of_match_table = pm8xxx_id_table, diff --git a/drivers/mfd/qcom-spmi-pmic.c b/drivers/mfd/qcom-spmi-pmic.c index 4549fa9f7d4b..eab5bf6cff10 100644 --- a/drivers/mfd/qcom-spmi-pmic.c +++ b/drivers/mfd/qcom-spmi-pmic.c @@ -53,6 +53,7 @@ static const struct of_device_id pmic_spmi_id_table[] = { { .compatible = "qcom,pm8901", .data = N_USIDS(2) }, { .compatible = "qcom,pm8909", .data = N_USIDS(2) }, { .compatible = "qcom,pm8916", .data = N_USIDS(2) }, + { .compatible = "qcom,pm8937", .data = N_USIDS(2) }, { .compatible = "qcom,pm8941", .data = N_USIDS(2) }, { .compatible = "qcom,pm8950", .data = N_USIDS(2) }, { .compatible = "qcom,pm8994", .data = N_USIDS(2) }, diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c index f62422740de2..6ff84b2600c5 100644 --- a/drivers/mfd/rave-sp.c +++ b/drivers/mfd/rave-sp.c @@ -471,17 +471,17 @@ static void rave_sp_receive_frame(struct rave_sp *sp, rave_sp_receive_reply(sp, data, length); } -static int rave_sp_receive_buf(struct serdev_device *serdev, - const unsigned char *buf, size_t size) +static ssize_t rave_sp_receive_buf(struct serdev_device *serdev, + const u8 *buf, size_t size) { struct device *dev = &serdev->dev; struct rave_sp *sp = dev_get_drvdata(dev); struct rave_sp_deframer *deframer = &sp->deframer; - const unsigned char *src = buf; - const unsigned char *end = buf + size; + const u8 *src = buf; + const u8 *end = buf + size; while (src < end) { - const unsigned char byte = *src++; + const u8 byte = *src++; switch (deframer->state) { case RAVE_SP_EXPECT_SOF: diff --git a/drivers/mfd/rk8xx-core.c b/drivers/mfd/rk8xx-core.c index c47164a3ec1d..b1ffc3b9e2be 100644 --- a/drivers/mfd/rk8xx-core.c +++ b/drivers/mfd/rk8xx-core.c @@ -53,76 +53,68 @@ static const struct resource rk817_charger_resources[] = { }; static const struct mfd_cell rk805s[] = { - { .name = "rk808-clkout", .id = PLATFORM_DEVID_NONE, }, - { .name = "rk808-regulator", .id = PLATFORM_DEVID_NONE, }, - { .name = "rk805-pinctrl", .id = PLATFORM_DEVID_NONE, }, + { .name = "rk808-clkout", }, + { .name = "rk808-regulator", }, + { .name = "rk805-pinctrl", }, { .name = "rk808-rtc", .num_resources = ARRAY_SIZE(rtc_resources), .resources = &rtc_resources[0], - .id = PLATFORM_DEVID_NONE, }, { .name = "rk805-pwrkey", .num_resources = ARRAY_SIZE(rk805_key_resources), .resources = &rk805_key_resources[0], - .id = PLATFORM_DEVID_NONE, }, }; static const struct mfd_cell rk806s[] = { - { .name = "rk805-pinctrl", .id = PLATFORM_DEVID_AUTO, }, - { .name = "rk808-regulator", .id = PLATFORM_DEVID_AUTO, }, + { .name = "rk805-pinctrl", }, + { .name = "rk808-regulator", }, { .name = "rk805-pwrkey", .resources = rk806_pwrkey_resources, .num_resources = ARRAY_SIZE(rk806_pwrkey_resources), - .id = PLATFORM_DEVID_AUTO, }, }; static const struct mfd_cell rk808s[] = { - { .name = "rk808-clkout", .id = PLATFORM_DEVID_NONE, }, - { .name = "rk808-regulator", .id = PLATFORM_DEVID_NONE, }, + { .name = "rk808-clkout", }, + { .name = "rk808-regulator", }, { .name = "rk808-rtc", .num_resources = ARRAY_SIZE(rtc_resources), .resources = rtc_resources, - .id = PLATFORM_DEVID_NONE, }, }; static const struct mfd_cell rk817s[] = { - { .name = "rk808-clkout", .id = PLATFORM_DEVID_NONE, }, - { .name = "rk808-regulator", .id = PLATFORM_DEVID_NONE, }, + { .name = "rk808-clkout", }, + { .name = "rk808-regulator", }, { .name = "rk805-pwrkey", .num_resources = ARRAY_SIZE(rk817_pwrkey_resources), .resources = &rk817_pwrkey_resources[0], - .id = PLATFORM_DEVID_NONE, }, { .name = "rk808-rtc", .num_resources = ARRAY_SIZE(rk817_rtc_resources), .resources = &rk817_rtc_resources[0], - .id = PLATFORM_DEVID_NONE, }, - { .name = "rk817-codec", .id = PLATFORM_DEVID_NONE, }, + { .name = "rk817-codec", }, { .name = "rk817-charger", .num_resources = ARRAY_SIZE(rk817_charger_resources), .resources = &rk817_charger_resources[0], - .id = PLATFORM_DEVID_NONE, }, }; static const struct mfd_cell rk818s[] = { - { .name = "rk808-clkout", .id = PLATFORM_DEVID_NONE, }, - { .name = "rk808-regulator", .id = PLATFORM_DEVID_NONE, }, + { .name = "rk808-clkout", }, + { .name = "rk808-regulator", }, { .name = "rk808-rtc", .num_resources = ARRAY_SIZE(rtc_resources), .resources = rtc_resources, - .id = PLATFORM_DEVID_NONE, }, }; @@ -684,7 +676,7 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regmap pre_init_reg[i].addr); } - ret = devm_mfd_add_devices(dev, 0, cells, nr_cells, NULL, 0, + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, nr_cells, NULL, 0, regmap_irq_get_domain(rk808->irq_data)); if (ret) return dev_err_probe(dev, ret, "failed to add MFD devices\n"); diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 28027982cf69..b3592982a83b 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1667,7 +1667,7 @@ static void sm501_pci_remove(struct pci_dev *dev) pci_disable_device(dev); } -static int sm501_plat_remove(struct platform_device *dev) +static void sm501_plat_remove(struct platform_device *dev) { struct sm501_devdata *sm = platform_get_drvdata(dev); @@ -1675,8 +1675,6 @@ static int sm501_plat_remove(struct platform_device *dev) iounmap(sm->regs); release_mem_region(sm->io_res->start, 0x100); - - return 0; } static const struct pci_device_id sm501_pci_tbl[] = { @@ -1707,7 +1705,7 @@ static struct platform_driver sm501_plat_driver = { .of_match_table = of_sm501_match_tbl, }, .probe = sm501_plat_probe, - .remove = sm501_plat_remove, + .remove_new = sm501_plat_remove, .suspend = pm_sleep_ptr(sm501_plat_suspend), .resume = pm_sleep_ptr(sm501_plat_resume), }; diff --git a/drivers/mfd/stm32-timers.c b/drivers/mfd/stm32-timers.c index a656a1c186a8..9fd13d88950c 100644 --- a/drivers/mfd/stm32-timers.c +++ b/drivers/mfd/stm32-timers.c @@ -306,7 +306,7 @@ static int stm32_timers_probe(struct platform_device *pdev) return ret; } -static int stm32_timers_remove(struct platform_device *pdev) +static void stm32_timers_remove(struct platform_device *pdev) { struct stm32_timers *ddata = platform_get_drvdata(pdev); @@ -316,8 +316,6 @@ static int stm32_timers_remove(struct platform_device *pdev) */ of_platform_depopulate(&pdev->dev); stm32_timers_dma_remove(&pdev->dev, ddata); - - return 0; } static const struct of_device_id stm32_timers_of_match[] = { @@ -328,7 +326,7 @@ MODULE_DEVICE_TABLE(of, stm32_timers_of_match); static struct platform_driver stm32_timers_driver = { .probe = stm32_timers_probe, - .remove = stm32_timers_remove, + .remove_new = stm32_timers_remove, .driver = { .name = "stm32-timers", .of_match_table = stm32_timers_of_match, diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 57b29c325131..c9550368d9ea 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -105,6 +105,10 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_res) } syscon_config.name = kasprintf(GFP_KERNEL, "%pOFn@%pa", np, &res.start); + if (!syscon_config.name) { + ret = -ENOMEM; + goto err_regmap; + } syscon_config.reg_stride = reg_io_width; syscon_config.val_bits = reg_io_width * 8; syscon_config.max_register = resource_size(&res) - reg_io_width; diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index b88eb70c17b3..4bbd542d753e 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -298,7 +298,7 @@ err_disable_clk: return err; } -static int ti_tscadc_remove(struct platform_device *pdev) +static void ti_tscadc_remove(struct platform_device *pdev) { struct ti_tscadc_dev *tscadc = platform_get_drvdata(pdev); @@ -308,8 +308,6 @@ static int ti_tscadc_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); mfd_remove_devices(tscadc->dev); - - return 0; } static int __maybe_unused ti_tscadc_can_wakeup(struct device *dev, void *data) @@ -381,7 +379,7 @@ static struct platform_driver ti_tscadc_driver = { .of_match_table = ti_tscadc_dt_ids, }, .probe = ti_tscadc_probe, - .remove = ti_tscadc_remove, + .remove_new = ti_tscadc_remove, }; diff --git a/drivers/mfd/tps65086.c b/drivers/mfd/tps65086.c index 152179ee11ca..fdce81b33f60 100644 --- a/drivers/mfd/tps65086.c +++ b/drivers/mfd/tps65086.c @@ -36,6 +36,7 @@ static const struct regmap_config tps65086_regmap_config = { .val_bits = 8, .cache_type = REGCACHE_MAPLE, .volatile_table = &tps65086_volatile_table, + .max_register = TPS65086_OC_STATUS, }; static const struct regmap_irq tps65086_irqs[] = { diff --git a/drivers/mfd/tps65911-comparator.c b/drivers/mfd/tps65911-comparator.c index 8f4210075913..f206a9c50e9d 100644 --- a/drivers/mfd/tps65911-comparator.c +++ b/drivers/mfd/tps65911-comparator.c @@ -140,15 +140,13 @@ static int tps65911_comparator_probe(struct platform_device *pdev) return ret; } -static int tps65911_comparator_remove(struct platform_device *pdev) +static void tps65911_comparator_remove(struct platform_device *pdev) { struct tps65910 *tps65910; tps65910 = dev_get_drvdata(pdev->dev.parent); device_remove_file(&pdev->dev, &dev_attr_comp2_threshold); device_remove_file(&pdev->dev, &dev_attr_comp1_threshold); - - return 0; } static struct platform_driver tps65911_comparator_driver = { @@ -156,7 +154,7 @@ static struct platform_driver tps65911_comparator_driver = { .name = "tps65911-comparator", }, .probe = tps65911_comparator_probe, - .remove = tps65911_comparator_remove, + .remove_new = tps65911_comparator_remove, }; static int __init tps65911_comparator_init(void) diff --git a/drivers/mfd/tps6594-core.c b/drivers/mfd/tps6594-core.c index 0fb9c5cf213a..783ee59901e8 100644 --- a/drivers/mfd/tps6594-core.c +++ b/drivers/mfd/tps6594-core.c @@ -433,6 +433,9 @@ int tps6594_device_init(struct tps6594 *tps, bool enable_crc) tps6594_irq_chip.name = devm_kasprintf(dev, GFP_KERNEL, "%s-%ld-0x%02x", dev->driver->name, tps->chip_id, tps->reg); + if (!tps6594_irq_chip.name) + return -ENOMEM; + ret = devm_regmap_add_irq_chip(dev, tps->regmap, tps->irq, IRQF_SHARED | IRQF_ONESHOT, 0, &tps6594_irq_chip, &tps->irq_data); if (ret) diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c index 88002f8941e5..d436ddf661da 100644 --- a/drivers/mfd/twl4030-audio.c +++ b/drivers/mfd/twl4030-audio.c @@ -258,12 +258,10 @@ static int twl4030_audio_probe(struct platform_device *pdev) return ret; } -static int twl4030_audio_remove(struct platform_device *pdev) +static void twl4030_audio_remove(struct platform_device *pdev) { mfd_remove_devices(&pdev->dev); twl4030_audio_dev = NULL; - - return 0; } static const struct of_device_id twl4030_audio_of_match[] = { @@ -278,7 +276,7 @@ static struct platform_driver twl4030_audio_driver = { .of_match_table = twl4030_audio_of_match, }, .probe = twl4030_audio_probe, - .remove = twl4030_audio_remove, + .remove_new = twl4030_audio_remove, }; module_platform_driver(twl4030_audio_driver); diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index f9fce8408c2c..3c03681c124c 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -24,10 +24,10 @@ #include <linux/kthread.h> #include <linux/mfd/twl.h> #include <linux/platform_device.h> -#include <linux/property.h> #include <linux/suspend.h> #include <linux/of.h> #include <linux/irqdomain.h> +#include <linux/of_device.h> #include "twl-core.h" @@ -368,10 +368,10 @@ int twl6030_init_irq(struct device *dev, int irq_num) int nr_irqs; int status; u8 mask[3]; - const int *irq_tbl; + const struct of_device_id *of_id; - irq_tbl = device_get_match_data(dev); - if (!irq_tbl) { + of_id = of_match_device(twl6030_of_match, dev); + if (!of_id || !of_id->data) { dev_err(dev, "Unknown TWL device model\n"); return -EINVAL; } @@ -409,7 +409,7 @@ int twl6030_init_irq(struct device *dev, int irq_num) twl6030_irq->pm_nb.notifier_call = twl6030_irq_pm_notifier; atomic_set(&twl6030_irq->wakeirqs, 0); - twl6030_irq->irq_mapping_tbl = irq_tbl; + twl6030_irq->irq_mapping_tbl = of_id->data; twl6030_irq->irq_domain = irq_domain_add_linear(node, nr_irqs, diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index f37c4b8380ae..4fb291f0bf7c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -562,6 +562,18 @@ config TPS6594_PFSM This driver can also be built as a module. If so, the module will be called tps6594-pfsm. +config NSM + tristate "Nitro (Enclaves) Security Module support" + depends on VIRTIO + select HW_RANDOM + help + This driver provides support for the Nitro Security Module + in AWS EC2 Nitro based Enclaves. The driver exposes a /dev/nsm + device user space can use to communicate with the hypervisor. + + To compile this driver as a module, choose M here. + The module will be called nsm. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index f2a4d1ff65d4..ea6ea5bbbc9c 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -67,3 +67,4 @@ obj-$(CONFIG_TMR_MANAGER) += xilinx_tmr_manager.o obj-$(CONFIG_TMR_INJECT) += xilinx_tmr_inject.o obj-$(CONFIG_TPS6594_ESM) += tps6594-esm.o obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o +obj-$(CONFIG_NSM) += nsm.o diff --git a/drivers/misc/bcm-vk/bcm_vk_tty.c b/drivers/misc/bcm-vk/bcm_vk_tty.c index 2bce835ca43e..59bab76ff0a9 100644 --- a/drivers/misc/bcm-vk/bcm_vk_tty.c +++ b/drivers/misc/bcm-vk/bcm_vk_tty.c @@ -64,9 +64,9 @@ static void bcm_vk_tty_wq_handler(struct work_struct *work) struct bcm_vk_tty *vktty; int card_status; int count; - unsigned char c; int i; int wr; + u8 c; card_status = vkread32(vk, BAR_0, BAR_CARD_STATUS); if (BCM_VK_INTF_IS_DOWN(card_status)) @@ -192,7 +192,7 @@ static ssize_t bcm_vk_tty_write(struct tty_struct *tty, const u8 *buffer, int index; struct bcm_vk *vk; struct bcm_vk_tty *vktty; - int i; + size_t i; index = tty->index; vk = dev_get_drvdata(tty->dev); diff --git a/drivers/misc/cardreader/Makefile b/drivers/misc/cardreader/Makefile index 895128475d83..1e1bca6b0b22 100644 --- a/drivers/misc/cardreader/Makefile +++ b/drivers/misc/cardreader/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_MISC_ALCOR_PCI) += alcor_pci.o obj-$(CONFIG_MISC_RTSX_PCI) += rtsx_pci.o -rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o rts5261.o rts5228.o +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o rts5261.o rts5228.o rts5264.o obj-$(CONFIG_MISC_RTSX_USB) += rtsx_usb.o diff --git a/drivers/misc/cardreader/rts5264.c b/drivers/misc/cardreader/rts5264.c new file mode 100644 index 000000000000..8be4ed7d9d47 --- /dev/null +++ b/drivers/misc/cardreader/rts5264.c @@ -0,0 +1,886 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. + * + * Author: + * Ricky Wu <ricky_wu@realtek.com> + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/rtsx_pci.h> + +#include "rts5264.h" +#include "rtsx_pcr.h" + +static u8 rts5264_get_ic_version(struct rtsx_pcr *pcr) +{ + u8 val; + + rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val); + return val & 0x0F; +} + +static void rts5264_fill_driving(struct rtsx_pcr *pcr, u8 voltage) +{ + u8 driving_3v3[4][3] = { + {0x88, 0x88, 0x88}, + {0x77, 0x77, 0x77}, + {0x99, 0x99, 0x99}, + {0x66, 0x66, 0x66}, + }; + u8 driving_1v8[4][3] = { + {0x99, 0x99, 0x99}, + {0x77, 0x77, 0x77}, + {0xBB, 0xBB, 0xBB}, + {0x65, 0x65, 0x65}, + }; + u8 (*driving)[3], drive_sel; + + if (voltage == OUTPUT_3V3) { + driving = driving_3v3; + drive_sel = pcr->sd30_drive_sel_3v3; + } else { + driving = driving_1v8; + drive_sel = pcr->sd30_drive_sel_1v8; + } + + rtsx_pci_write_register(pcr, SD30_CLK_DRIVE_SEL, + 0xFF, driving[drive_sel][0]); + rtsx_pci_write_register(pcr, SD30_CMD_DRIVE_SEL, + 0xFF, driving[drive_sel][1]); + rtsx_pci_write_register(pcr, SD30_DAT_DRIVE_SEL, + 0xFF, driving[drive_sel][2]); +} + +static void rts5264_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime) +{ + /* Set relink_time to 0 */ + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, MASK_8_BIT_DEF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, + RELINK_TIME_MASK, 0); + + if (pm_state == HOST_ENTER_S3) + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, + D3_DELINK_MODE_EN, D3_DELINK_MODE_EN); + + if (!runtime) { + rtsx_pci_write_register(pcr, RTS5264_AUTOLOAD_CFG1, + CD_RESUME_EN_MASK, 0); + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x00); + rtsx_pci_write_register(pcr, RTS5264_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL); + } else { + rtsx_pci_write_register(pcr, RTS5264_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, 0); + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x01); + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, + D3_DELINK_MODE_EN, 0); + rtsx_pci_write_register(pcr, RTS5264_FW_CTL, + RTS5264_INFORM_RTD3_COLD, RTS5264_INFORM_RTD3_COLD); + rtsx_pci_write_register(pcr, RTS5264_AUTOLOAD_CFG4, + RTS5264_FORCE_PRSNT_LOW, RTS5264_FORCE_PRSNT_LOW); + } + + rtsx_pci_write_register(pcr, RTS5264_REG_FPDCTL, + SSC_POWER_DOWN, SSC_POWER_DOWN); +} + +static int rts5264_enable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, + LED_SHINE_MASK, LED_SHINE_EN); +} + +static int rts5264_disable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, + LED_SHINE_MASK, LED_SHINE_DISABLE); +} + +static int rts5264_turn_on_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, + 0x02, 0x02); +} + +static int rts5264_turn_off_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, + 0x02, 0x00); +} + +/* SD Pull Control Enable: + * SD_DAT[3:0] ==> pull up + * SD_CD ==> pull up + * SD_WP ==> pull up + * SD_CMD ==> pull up + * SD_CLK ==> pull down + */ +static const u32 rts5264_sd_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), + 0, +}; + +/* SD Pull Control Disable: + * SD_DAT[3:0] ==> pull down + * SD_CD ==> pull up + * SD_WP ==> pull down + * SD_CMD ==> pull down + * SD_CLK ==> pull down + */ +static const u32 rts5264_sd_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), + 0, +}; + +static int rts5264_sd_set_sample_push_timing_sd30(struct rtsx_pcr *pcr) +{ + rtsx_pci_write_register(pcr, SD_CFG1, SD_MODE_SELECT_MASK + | SD_ASYNC_FIFO_NOT_RST, SD_30_MODE | SD_ASYNC_FIFO_NOT_RST); + rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_write_register(pcr, CARD_CLK_SOURCE, 0xFF, + CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1); + rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0); + + return 0; +} + +static int rts5264_card_power_on(struct rtsx_pcr *pcr, int card) +{ + struct rtsx_cr_option *option = &pcr->option; + + if (option->ocp_en) + rtsx_pci_enable_ocp(pcr); + + rtsx_pci_write_register(pcr, REG_CRC_DUMMY_0, + CFG_SD_POW_AUTO_PD, CFG_SD_POW_AUTO_PD); + + rtsx_pci_write_register(pcr, RTS5264_LDO1_CFG1, + RTS5264_LDO1_TUNE_MASK, RTS5264_LDO1_33); + rtsx_pci_write_register(pcr, RTS5264_LDO1233318_POW_CTL, + RTS5264_LDO1_POWERON, RTS5264_LDO1_POWERON); + rtsx_pci_write_register(pcr, RTS5264_LDO1233318_POW_CTL, + RTS5264_LDO3318_POWERON, RTS5264_LDO3318_POWERON); + + msleep(20); + + rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, SD_OUTPUT_EN); + + /* Initialize SD_CFG1 register */ + rtsx_pci_write_register(pcr, SD_CFG1, 0xFF, + SD_CLK_DIVIDE_128 | SD_20_MODE | SD_BUS_WIDTH_1BIT); + rtsx_pci_write_register(pcr, SD_SAMPLE_POINT_CTL, + 0xFF, SD20_RX_POS_EDGE); + rtsx_pci_write_register(pcr, SD_PUSH_POINT_CTL, 0xFF, 0); + rtsx_pci_write_register(pcr, CARD_STOP, SD_STOP | SD_CLR_ERR, + SD_STOP | SD_CLR_ERR); + + /* Reset SD_CFG3 register */ + rtsx_pci_write_register(pcr, SD_CFG3, SD30_CLK_END_EN, 0); + rtsx_pci_write_register(pcr, REG_SD_STOP_SDCLK_CFG, + SD30_CLK_STOP_CFG_EN | SD30_CLK_STOP_CFG1 | + SD30_CLK_STOP_CFG0, 0); + + if (pcr->extra_caps & EXTRA_CAPS_SD_SDR50 || + pcr->extra_caps & EXTRA_CAPS_SD_SDR104) + rts5264_sd_set_sample_push_timing_sd30(pcr); + + return 0; +} + +static int rts5264_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + rtsx_pci_write_register(pcr, RTS5264_CARD_PWR_CTL, + RTS5264_PUPDC, RTS5264_PUPDC); + + switch (voltage) { + case OUTPUT_3V3: + rtsx_pci_write_register(pcr, RTS5264_LDO1233318_POW_CTL, + RTS5264_TUNE_REF_LDO3318, RTS5264_TUNE_REF_LDO3318); + rtsx_pci_write_register(pcr, RTS5264_DV3318_CFG, + RTS5264_DV3318_TUNE_MASK, RTS5264_DV3318_33); + rtsx_pci_write_register(pcr, SD_PAD_CTL, + SD_IO_USING_1V8, 0); + break; + case OUTPUT_1V8: + rtsx_pci_write_register(pcr, RTS5264_LDO1233318_POW_CTL, + RTS5264_TUNE_REF_LDO3318, RTS5264_TUNE_REF_LDO3318_DFT); + rtsx_pci_write_register(pcr, RTS5264_DV3318_CFG, + RTS5264_DV3318_TUNE_MASK, RTS5264_DV3318_18); + rtsx_pci_write_register(pcr, SD_PAD_CTL, + SD_IO_USING_1V8, SD_IO_USING_1V8); + break; + default: + return -EINVAL; + } + + /* set pad drive */ + rts5264_fill_driving(pcr, voltage); + + return 0; +} + +static void rts5264_stop_cmd(struct rtsx_pcr *pcr) +{ + rtsx_pci_writel(pcr, RTSX_HCBCTLR, STOP_CMD); + rtsx_pci_writel(pcr, RTSX_HDBCTLR, STOP_DMA); + rtsx_pci_write_register(pcr, DMACTL, DMA_RST, DMA_RST); + rtsx_pci_write_register(pcr, RBCTL, RB_FLUSH, RB_FLUSH); +} + +static void rts5264_card_before_power_off(struct rtsx_pcr *pcr) +{ + rts5264_stop_cmd(pcr); + rts5264_switch_output_voltage(pcr, OUTPUT_3V3); +} + +static int rts5264_card_power_off(struct rtsx_pcr *pcr, int card) +{ + int err = 0; + + rts5264_card_before_power_off(pcr); + err = rtsx_pci_write_register(pcr, RTS5264_LDO1233318_POW_CTL, + RTS5264_LDO_POWERON_MASK, 0); + + rtsx_pci_write_register(pcr, REG_CRC_DUMMY_0, + CFG_SD_POW_AUTO_PD, 0); + if (pcr->option.ocp_en) + rtsx_pci_disable_ocp(pcr); + + return err; +} + +static void rts5264_enable_ocp(struct rtsx_pcr *pcr) +{ + u8 mask = 0; + u8 val = 0; + + rtsx_pci_write_register(pcr, RTS5264_LDO1_CFG0, + RTS5264_LDO1_OCP_EN | RTS5264_LDO1_OCP_LMT_EN, + RTS5264_LDO1_OCP_EN | RTS5264_LDO1_OCP_LMT_EN); + rtsx_pci_write_register(pcr, RTS5264_LDO2_CFG0, + RTS5264_LDO2_OCP_EN | RTS5264_LDO2_OCP_LMT_EN, + RTS5264_LDO2_OCP_EN | RTS5264_LDO2_OCP_LMT_EN); + rtsx_pci_write_register(pcr, RTS5264_LDO3_CFG0, + RTS5264_LDO3_OCP_EN | RTS5264_LDO3_OCP_LMT_EN, + RTS5264_LDO3_OCP_EN | RTS5264_LDO3_OCP_LMT_EN); + rtsx_pci_write_register(pcr, RTS5264_OVP_DET, + RTS5264_POW_VDET, RTS5264_POW_VDET); + + mask = SD_OCP_INT_EN | SD_DETECT_EN; + mask |= SDVIO_OCP_INT_EN | SDVIO_DETECT_EN; + val = mask; + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, val); + + mask = SD_VDD3_OCP_INT_EN | SD_VDD3_DETECT_EN; + val = mask; + rtsx_pci_write_register(pcr, RTS5264_OCP_VDD3_CTL, mask, val); + + mask = RTS5264_OVP_INT_EN | RTS5264_OVP_DETECT_EN; + val = mask; + rtsx_pci_write_register(pcr, RTS5264_OVP_CTL, mask, val); +} + +static void rts5264_disable_ocp(struct rtsx_pcr *pcr) +{ + u8 mask = 0; + + mask = SD_OCP_INT_EN | SD_DETECT_EN; + mask |= SDVIO_OCP_INT_EN | SDVIO_DETECT_EN; + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, 0); + + mask = SD_VDD3_OCP_INT_EN | SD_VDD3_DETECT_EN; + rtsx_pci_write_register(pcr, RTS5264_OCP_VDD3_CTL, mask, 0); + + mask = RTS5264_OVP_INT_EN | RTS5264_OVP_DETECT_EN; + rtsx_pci_write_register(pcr, RTS5264_OVP_CTL, mask, 0); + + rtsx_pci_write_register(pcr, RTS5264_LDO1_CFG0, + RTS5264_LDO1_OCP_EN | RTS5264_LDO1_OCP_LMT_EN, 0); + rtsx_pci_write_register(pcr, RTS5264_LDO2_CFG0, + RTS5264_LDO2_OCP_EN | RTS5264_LDO2_OCP_LMT_EN, 0); + rtsx_pci_write_register(pcr, RTS5264_LDO3_CFG0, + RTS5264_LDO3_OCP_EN | RTS5264_LDO3_OCP_LMT_EN, 0); + rtsx_pci_write_register(pcr, RTS5264_OVP_DET, RTS5264_POW_VDET, 0); +} + +static void rts5264_init_ocp(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + if (option->ocp_en) { + u8 mask, val; + + rtsx_pci_write_register(pcr, RTS5264_LDO1_CFG0, + RTS5264_LDO1_OCP_THD_MASK, option->sd_800mA_ocp_thd); + rtsx_pci_write_register(pcr, RTS5264_LDO1_CFG0, + RTS5264_LDO1_OCP_LMT_THD_MASK, + RTS5264_LDO1_LMT_THD_2000); + + rtsx_pci_write_register(pcr, RTS5264_LDO2_CFG0, + RTS5264_LDO2_OCP_THD_MASK, RTS5264_LDO2_OCP_THD_950); + rtsx_pci_write_register(pcr, RTS5264_LDO2_CFG0, + RTS5264_LDO2_OCP_LMT_THD_MASK, + RTS5264_LDO2_LMT_THD_2000); + + rtsx_pci_write_register(pcr, RTS5264_LDO3_CFG0, + RTS5264_LDO3_OCP_THD_MASK, RTS5264_LDO3_OCP_THD_710); + rtsx_pci_write_register(pcr, RTS5264_LDO3_CFG0, + RTS5264_LDO3_OCP_LMT_THD_MASK, + RTS5264_LDO3_LMT_THD_1500); + + rtsx_pci_write_register(pcr, RTS5264_OVP_DET, + RTS5264_TUNE_VROV_MASK, RTS5264_TUNE_VROV_1V6); + + mask = SD_OCP_GLITCH_MASK | SDVIO_OCP_GLITCH_MASK; + val = pcr->hw_param.ocp_glitch; + rtsx_pci_write_register(pcr, REG_OCPGLITCH, mask, val); + + } else { + rtsx_pci_write_register(pcr, RTS5264_LDO1_CFG0, + RTS5264_LDO1_OCP_EN | RTS5264_LDO1_OCP_LMT_EN, 0); + rtsx_pci_write_register(pcr, RTS5264_LDO2_CFG0, + RTS5264_LDO2_OCP_EN | RTS5264_LDO2_OCP_LMT_EN, 0); + rtsx_pci_write_register(pcr, RTS5264_LDO3_CFG0, + RTS5264_LDO3_OCP_EN | RTS5264_LDO3_OCP_LMT_EN, 0); + rtsx_pci_write_register(pcr, RTS5264_OVP_DET, + RTS5264_POW_VDET, 0); + } +} + +static int rts5264_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val) +{ + return rtsx_pci_read_register(pcr, RTS5264_OCP_VDD3_STS, val); +} + +static int rts5264_get_ovpstat(struct rtsx_pcr *pcr, u8 *val) +{ + return rtsx_pci_read_register(pcr, RTS5264_OVP_STS, val); +} + +static void rts5264_clear_ocpstat(struct rtsx_pcr *pcr) +{ + u8 mask = 0; + u8 val = 0; + + mask = SD_OCP_INT_CLR | SD_OC_CLR; + mask |= SDVIO_OCP_INT_CLR | SDVIO_OC_CLR; + val = mask; + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, val); + rtsx_pci_write_register(pcr, RTS5264_OCP_VDD3_CTL, + SD_VDD3_OCP_INT_CLR | SD_VDD3_OC_CLR, + SD_VDD3_OCP_INT_CLR | SD_VDD3_OC_CLR); + rtsx_pci_write_register(pcr, RTS5264_OVP_CTL, + RTS5264_OVP_INT_CLR | RTS5264_OVP_CLR, + RTS5264_OVP_INT_CLR | RTS5264_OVP_CLR); + + udelay(1000); + + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, 0); + rtsx_pci_write_register(pcr, RTS5264_OCP_VDD3_CTL, + SD_VDD3_OCP_INT_CLR | SD_VDD3_OC_CLR, 0); + rtsx_pci_write_register(pcr, RTS5264_OVP_CTL, + RTS5264_OVP_INT_CLR | RTS5264_OVP_CLR, 0); +} + +static void rts5264_process_ocp(struct rtsx_pcr *pcr) +{ + if (!pcr->option.ocp_en) + return; + + rtsx_pci_get_ocpstat(pcr, &pcr->ocp_stat); + rts5264_get_ocpstat2(pcr, &pcr->ocp_stat2); + rts5264_get_ovpstat(pcr, &pcr->ovp_stat); + + if ((pcr->ocp_stat & (SD_OC_NOW | SD_OC_EVER | SDVIO_OC_NOW | SDVIO_OC_EVER)) || + (pcr->ocp_stat2 & (SD_VDD3_OC_NOW | SD_VDD3_OC_EVER)) || + (pcr->ovp_stat & (RTS5264_OVP_NOW | RTS5264_OVP_EVER))) { + rts5264_clear_ocpstat(pcr); + rts5264_card_power_off(pcr, RTSX_SD_CARD); + rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, 0); + pcr->ocp_stat = 0; + pcr->ocp_stat2 = 0; + pcr->ovp_stat = 0; + } +} + +static void rts5264_init_from_hw(struct rtsx_pcr *pcr) +{ + struct pci_dev *pdev = pcr->pci; + u32 lval1, lval2, i; + u16 setting_reg1, setting_reg2; + u8 valid, efuse_valid, tmp; + + rtsx_pci_write_register(pcr, RTS5264_REG_PME_FORCE_CTL, + REG_EFUSE_POR | REG_EFUSE_POWER_MASK, + REG_EFUSE_POR | REG_EFUSE_POWERON); + udelay(1); + rtsx_pci_write_register(pcr, RTS5264_EFUSE_ADDR, + RTS5264_EFUSE_ADDR_MASK, 0x00); + rtsx_pci_write_register(pcr, RTS5264_EFUSE_CTL, + RTS5264_EFUSE_ENABLE | RTS5264_EFUSE_MODE_MASK, + RTS5264_EFUSE_ENABLE); + + /* Wait transfer end */ + for (i = 0; i < MAX_RW_REG_CNT; i++) { + rtsx_pci_read_register(pcr, RTS5264_EFUSE_CTL, &tmp); + if ((tmp & 0x80) == 0) + break; + } + rtsx_pci_read_register(pcr, RTS5264_EFUSE_READ_DATA, &tmp); + efuse_valid = ((tmp & 0x0C) >> 2); + pcr_dbg(pcr, "Load efuse valid: 0x%x\n", efuse_valid); + + pci_read_config_dword(pdev, PCR_SETTING_REG2, &lval2); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, lval2); + /* 0x816 */ + valid = (u8)((lval2 >> 16) & 0x03); + + rtsx_pci_write_register(pcr, RTS5264_REG_PME_FORCE_CTL, + REG_EFUSE_POR, 0); + pcr_dbg(pcr, "Disable efuse por!\n"); + + if (efuse_valid == 2 || efuse_valid == 3) { + if (valid == 3) { + /* Bypass efuse */ + setting_reg1 = PCR_SETTING_REG1; + setting_reg2 = PCR_SETTING_REG2; + } else { + /* Use efuse data */ + setting_reg1 = PCR_SETTING_REG4; + setting_reg2 = PCR_SETTING_REG5; + } + } else if (efuse_valid == 0) { + // default + setting_reg1 = PCR_SETTING_REG1; + setting_reg2 = PCR_SETTING_REG2; + } else { + return; + } + + pci_read_config_dword(pdev, setting_reg2, &lval2); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", setting_reg2, lval2); + + if (!rts5264_vendor_setting_valid(lval2)) { + pcr_dbg(pcr, "skip fetch vendor setting\n"); + return; + } + + pcr->rtd3_en = rts5264_reg_to_rtd3(lval2); + + if (rts5264_reg_check_reverse_socket(lval2)) + pcr->flags |= PCR_REVERSE_SOCKET; + + pci_read_config_dword(pdev, setting_reg1, &lval1); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", setting_reg1, lval1); + + pcr->aspm_en = rts5264_reg_to_aspm(lval1); + pcr->sd30_drive_sel_1v8 = rts5264_reg_to_sd30_drive_sel_1v8(lval1); + pcr->sd30_drive_sel_3v3 = rts5264_reg_to_sd30_drive_sel_3v3(lval1); + + if (setting_reg1 == PCR_SETTING_REG1) { + /* store setting */ + rtsx_pci_write_register(pcr, 0xFF0C, 0xFF, (u8)(lval1 & 0xFF)); + rtsx_pci_write_register(pcr, 0xFF0D, 0xFF, (u8)((lval1 >> 8) & 0xFF)); + rtsx_pci_write_register(pcr, 0xFF0E, 0xFF, (u8)((lval1 >> 16) & 0xFF)); + rtsx_pci_write_register(pcr, 0xFF0F, 0xFF, (u8)((lval1 >> 24) & 0xFF)); + rtsx_pci_write_register(pcr, 0xFF10, 0xFF, (u8)(lval2 & 0xFF)); + rtsx_pci_write_register(pcr, 0xFF11, 0xFF, (u8)((lval2 >> 8) & 0xFF)); + rtsx_pci_write_register(pcr, 0xFF12, 0xFF, (u8)((lval2 >> 16) & 0xFF)); + + pci_write_config_dword(pdev, PCR_SETTING_REG4, lval1); + lval2 = lval2 & 0x00FFFFFF; + pci_write_config_dword(pdev, PCR_SETTING_REG5, lval2); + } +} + +static void rts5264_init_from_cfg(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN + | PM_L1_1_EN | PM_L1_2_EN)) + rtsx_pci_disable_oobs_polling(pcr); + else + rtsx_pci_enable_oobs_polling(pcr); + + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0xFF, 0); + + if (option->ltr_en) { + if (option->ltr_enabled) + rtsx_set_ltr_latency(pcr, option->ltr_active_latency); + } +} + +static int rts5264_extra_init_hw(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + rtsx_pci_write_register(pcr, RTS5264_AUTOLOAD_CFG1, + CD_RESUME_EN_MASK, CD_RESUME_EN_MASK); + rtsx_pci_write_register(pcr, REG_VREF, PWD_SUSPND_EN, PWD_SUSPND_EN); + + rts5264_init_from_cfg(pcr); + rts5264_init_from_hw(pcr); + + /* power off efuse */ + rtsx_pci_write_register(pcr, RTS5264_REG_PME_FORCE_CTL, + REG_EFUSE_POWER_MASK, REG_EFUSE_POWEROFF); + rtsx_pci_write_register(pcr, RTS5264_AUTOLOAD_CFG2, + RTS5264_CHIP_RST_N_SEL, 0); + rtsx_pci_write_register(pcr, RTS5264_REG_LDO12_CFG, + RTS5264_LDO12_SR_MASK, RTS5264_LDO12_SR_0_0_MS); + rtsx_pci_write_register(pcr, CDGW, 0xFF, 0x01); + rtsx_pci_write_register(pcr, RTS5264_CKMUX_MBIAS_PWR, + RTS5264_POW_CKMUX, RTS5264_POW_CKMUX); + rtsx_pci_write_register(pcr, RTS5264_CMD_OE_START_EARLY, + RTS5264_CMD_OE_EARLY_EN | RTS5264_CMD_OE_EARLY_CYCLE_MASK, + RTS5264_CMD_OE_EARLY_EN); + rtsx_pci_write_register(pcr, RTS5264_DAT_OE_START_EARLY, + RTS5264_DAT_OE_EARLY_EN | RTS5264_DAT_OE_EARLY_CYCLE_MASK, + RTS5264_DAT_OE_EARLY_EN); + rtsx_pci_write_register(pcr, SSC_DIV_N_0, 0xFF, 0x5D); + + rtsx_pci_write_register(pcr, RTS5264_PWR_CUT, + RTS5264_CFG_MEM_PD, RTS5264_CFG_MEM_PD); + rtsx_pci_write_register(pcr, L1SUB_CONFIG1, + AUX_CLK_ACTIVE_SEL_MASK, MAC_CKSW_DONE); + rtsx_pci_write_register(pcr, L1SUB_CONFIG3, 0xFF, 0); + rtsx_pci_write_register(pcr, RTS5264_AUTOLOAD_CFG4, + RTS5264_AUX_CLK_16M_EN, 0); + + /* Release PRSNT# */ + rtsx_pci_write_register(pcr, RTS5264_AUTOLOAD_CFG4, + RTS5264_FORCE_PRSNT_LOW, 0); + rtsx_pci_write_register(pcr, PCLK_CTL, + PCLK_MODE_SEL, PCLK_MODE_SEL); + + /* LED shine disabled, set initial shine cycle period */ + rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x0F, 0x02); + + /* Configure driving */ + rts5264_fill_driving(pcr, OUTPUT_3V3); + + if (pcr->flags & PCR_REVERSE_SOCKET) + rtsx_pci_write_register(pcr, PETXCFG, 0x30, 0x30); + else + rtsx_pci_write_register(pcr, PETXCFG, 0x30, 0x00); + + /* + * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced + * to drive low, and we forcibly request clock. + */ + if (option->force_clkreq_0) + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); + else + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); + + rtsx_pci_write_register(pcr, PWD_SUSPEND_EN, 0xFF, 0xFF); + rtsx_pci_write_register(pcr, RBCTL, U_AUTO_DMA_EN_MASK, 0); + rtsx_pci_write_register(pcr, RTS5264_AUTOLOAD_CFG4, + RTS5264_F_HIGH_RC_MASK, RTS5264_F_HIGH_RC_400K); + + if (pcr->rtd3_en) { + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x00); + rtsx_pci_write_register(pcr, RTS5264_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, 0); + } else { + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x01, 0x00); + rtsx_pci_write_register(pcr, RTS5264_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL); + } + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, D3_DELINK_MODE_EN, 0x00); + + /* Clear Enter RTD3_cold Information*/ + rtsx_pci_write_register(pcr, RTS5264_FW_CTL, + RTS5264_INFORM_RTD3_COLD, 0); + + return 0; +} + +static void rts5264_enable_aspm(struct rtsx_pcr *pcr, bool enable) +{ + u8 val = FORCE_ASPM_CTL0 | FORCE_ASPM_CTL1; + u8 mask = FORCE_ASPM_VAL_MASK | FORCE_ASPM_CTL0 | FORCE_ASPM_CTL1; + + if (pcr->aspm_enabled == enable) + return; + + val |= (pcr->aspm_en & 0x02); + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val); + pcie_capability_clear_and_set_word(pcr->pci, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, pcr->aspm_en); + pcr->aspm_enabled = enable; +} + +static void rts5264_disable_aspm(struct rtsx_pcr *pcr, bool enable) +{ + u8 val = FORCE_ASPM_CTL0 | FORCE_ASPM_CTL1; + u8 mask = FORCE_ASPM_VAL_MASK | FORCE_ASPM_CTL0 | FORCE_ASPM_CTL1; + + if (pcr->aspm_enabled == enable) + return; + + pcie_capability_clear_and_set_word(pcr->pci, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, 0); + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val); + rtsx_pci_write_register(pcr, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0); + udelay(10); + pcr->aspm_enabled = enable; +} + +static void rts5264_set_aspm(struct rtsx_pcr *pcr, bool enable) +{ + if (enable) + rts5264_enable_aspm(pcr, true); + else + rts5264_disable_aspm(pcr, false); +} + +static void rts5264_set_l1off_cfg_sub_d0(struct rtsx_pcr *pcr, int active) +{ + struct rtsx_cr_option *option = &(pcr->option); + + u32 interrupt = rtsx_pci_readl(pcr, RTSX_BIPR); + int card_exist = (interrupt & SD_EXIST); + int aspm_L1_1, aspm_L1_2; + u8 val = 0; + + aspm_L1_1 = rtsx_check_dev_flag(pcr, ASPM_L1_1_EN); + aspm_L1_2 = rtsx_check_dev_flag(pcr, ASPM_L1_2_EN); + + if (active) { + /* Run, latency: 60us */ + if (aspm_L1_1) + val = option->ltr_l1off_snooze_sspwrgate; + } else { + /* L1off, latency: 300us */ + if (aspm_L1_2) + val = option->ltr_l1off_sspwrgate; + } + + if (aspm_L1_1 || aspm_L1_2) { + if (rtsx_check_dev_flag(pcr, + LTR_L1SS_PWR_GATE_CHECK_CARD_EN)) { + if (card_exist) + val &= ~L1OFF_MBIAS2_EN_5250; + else + val |= L1OFF_MBIAS2_EN_5250; + } + } + rtsx_set_l1off_sub(pcr, val); +} + +static const struct pcr_ops rts5264_pcr_ops = { + .turn_on_led = rts5264_turn_on_led, + .turn_off_led = rts5264_turn_off_led, + .extra_init_hw = rts5264_extra_init_hw, + .enable_auto_blink = rts5264_enable_auto_blink, + .disable_auto_blink = rts5264_disable_auto_blink, + .card_power_on = rts5264_card_power_on, + .card_power_off = rts5264_card_power_off, + .switch_output_voltage = rts5264_switch_output_voltage, + .force_power_down = rts5264_force_power_down, + .stop_cmd = rts5264_stop_cmd, + .set_aspm = rts5264_set_aspm, + .set_l1off_cfg_sub_d0 = rts5264_set_l1off_cfg_sub_d0, + .enable_ocp = rts5264_enable_ocp, + .disable_ocp = rts5264_disable_ocp, + .init_ocp = rts5264_init_ocp, + .process_ocp = rts5264_process_ocp, + .clear_ocpstat = rts5264_clear_ocpstat, +}; + +static inline u8 double_ssc_depth(u8 depth) +{ + return ((depth > 1) ? (depth - 1) : depth); +} + +int rts5264_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, + u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) +{ + int err, clk; + u16 n; + u8 clk_divider, mcu_cnt, div; + static const u8 depth[] = { + [RTSX_SSC_DEPTH_4M] = RTS5264_SSC_DEPTH_4M, + [RTSX_SSC_DEPTH_2M] = RTS5264_SSC_DEPTH_2M, + [RTSX_SSC_DEPTH_1M] = RTS5264_SSC_DEPTH_1M, + [RTSX_SSC_DEPTH_500K] = RTS5264_SSC_DEPTH_512K, + }; + + if (initial_mode) { + /* We use 250k(around) here, in initial stage */ + clk_divider = SD_CLK_DIVIDE_128; + card_clock = 30000000; + } else { + clk_divider = SD_CLK_DIVIDE_0; + } + err = rtsx_pci_write_register(pcr, SD_CFG1, + SD_CLK_DIVIDE_MASK, clk_divider); + if (err < 0) + return err; + + card_clock /= 1000000; + pcr_dbg(pcr, "Switch card clock to %dMHz\n", card_clock); + + clk = card_clock; + if (!initial_mode && double_clk) + clk = card_clock * 2; + pcr_dbg(pcr, "Internal SSC clock: %dMHz (cur_clock = %d)\n", + clk, pcr->cur_clock); + + if (clk == pcr->cur_clock) + return 0; + + if (pcr->ops->conv_clk_and_div_n) + n = pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N); + else + n = clk - 4; + if ((clk <= 4) || (n > 396)) + return -EINVAL; + + mcu_cnt = 125/clk + 3; + if (mcu_cnt > 15) + mcu_cnt = 15; + + div = CLK_DIV_1; + while ((n < MIN_DIV_N_PCR - 4) && (div < CLK_DIV_8)) { + if (pcr->ops->conv_clk_and_div_n) { + int dbl_clk = pcr->ops->conv_clk_and_div_n(n, + DIV_N_TO_CLK) * 2; + n = pcr->ops->conv_clk_and_div_n(dbl_clk, + CLK_TO_DIV_N); + } else { + n = (n + 4) * 2 - 4; + } + div++; + } + + n = (n / 2) - 1; + pcr_dbg(pcr, "n = %d, div = %d\n", n, div); + + ssc_depth = depth[ssc_depth]; + if (double_clk) + ssc_depth = double_ssc_depth(ssc_depth); + + if (ssc_depth) { + if (div == CLK_DIV_2) { + if (ssc_depth > 1) + ssc_depth -= 1; + else + ssc_depth = RTS5264_SSC_DEPTH_8M; + } else if (div == CLK_DIV_4) { + if (ssc_depth > 2) + ssc_depth -= 2; + else + ssc_depth = RTS5264_SSC_DEPTH_8M; + } else if (div == CLK_DIV_8) { + if (ssc_depth > 3) + ssc_depth -= 3; + else + ssc_depth = RTS5264_SSC_DEPTH_8M; + } + } else { + ssc_depth = 0; + } + pcr_dbg(pcr, "ssc_depth = %d\n", ssc_depth); + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, + CHANGE_CLK, CHANGE_CLK); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_DIV, + 0xFF, (div << 4) | mcu_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, + SSC_DEPTH_MASK, ssc_depth); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); + + if (is_version(pcr, 0x5264, IC_VER_A)) { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS5264_CARD_CLK_SRC2, + RTS5264_REG_BIG_KVCO_A, 0); + } else { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS5264_SYS_DUMMY_1, + RTS5264_REG_BIG_KVCO, 0); + } + + if (vpclk) { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, + PHASE_NOT_RESET, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK1_CTL, + PHASE_NOT_RESET, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, + PHASE_NOT_RESET, PHASE_NOT_RESET); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK1_CTL, + PHASE_NOT_RESET, PHASE_NOT_RESET); + } + + err = rtsx_pci_send_cmd(pcr, 2000); + if (err < 0) + return err; + + /* Wait SSC clock stable */ + udelay(SSC_CLOCK_STABLE_WAIT); + err = rtsx_pci_write_register(pcr, CLK_CTL, CHANGE_CLK, 0); + if (err < 0) + return err; + + pcr->cur_clock = clk; + return 0; +} + +void rts5264_init_params(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + struct rtsx_hw_param *hw_param = &pcr->hw_param; + u8 val; + + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; + pcr->extra_caps |= EXTRA_CAPS_NO_MMC; + rtsx_pci_read_register(pcr, RTS5264_FW_STATUS, &val); + if (!(val & RTS5264_EXPRESS_LINK_FAIL_MASK)) + pcr->extra_caps |= EXTRA_CAPS_SD_EXPRESS; + pcr->num_slots = 1; + pcr->ops = &rts5264_pcr_ops; + + pcr->flags = 0; + pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT; + pcr->sd30_drive_sel_1v8 = 0x00; + pcr->sd30_drive_sel_3v3 = 0x00; + pcr->aspm_en = ASPM_L1_EN; + pcr->aspm_mode = ASPM_MODE_REG; + pcr->tx_initial_phase = SET_CLOCK_PHASE(24, 24, 11); + pcr->rx_initial_phase = SET_CLOCK_PHASE(24, 6, 5); + + pcr->ic_version = rts5264_get_ic_version(pcr); + pcr->sd_pull_ctl_enable_tbl = rts5264_sd_pull_ctl_enable_tbl; + pcr->sd_pull_ctl_disable_tbl = rts5264_sd_pull_ctl_disable_tbl; + + pcr->reg_pm_ctrl3 = RTS5264_AUTOLOAD_CFG3; + + option->dev_flags = (LTR_L1SS_PWR_GATE_CHECK_CARD_EN + | LTR_L1SS_PWR_GATE_EN); + option->ltr_en = true; + + /* init latency of active, idle, L1OFF to 60us, 300us, 3ms */ + option->ltr_active_latency = LTR_ACTIVE_LATENCY_DEF; + option->ltr_idle_latency = LTR_IDLE_LATENCY_DEF; + option->ltr_l1off_latency = LTR_L1OFF_LATENCY_DEF; + option->l1_snooze_delay = L1_SNOOZE_DELAY_DEF; + option->ltr_l1off_sspwrgate = 0x7F; + option->ltr_l1off_snooze_sspwrgate = 0x78; + + option->ocp_en = 1; + hw_param->interrupt_en |= (SD_OC_INT_EN | SD_OVP_INT_EN); + hw_param->ocp_glitch = SD_OCP_GLITCH_800U | SDVIO_OCP_GLITCH_800U; + option->sd_800mA_ocp_thd = RTS5264_LDO1_OCP_THD_1150; +} diff --git a/drivers/misc/cardreader/rts5264.h b/drivers/misc/cardreader/rts5264.h new file mode 100644 index 000000000000..e3cbbf2fe1a4 --- /dev/null +++ b/drivers/misc/cardreader/rts5264.h @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2018-2019 Realtek Semiconductor Corp. All rights reserved. + * + * Author: + * Ricky Wu <ricky_wu@realtek.com> + */ +#ifndef RTS5264_H +#define RTS5264_H + +/*New add*/ +#define rts5264_vendor_setting_valid(reg) ((reg) & 0x010000) +#define rts5264_reg_to_aspm(reg) \ + (((~(reg) >> 28) & 0x02) | (((reg) >> 28) & 0x01)) +#define rts5264_reg_check_reverse_socket(reg) ((reg) & 0x04) +#define rts5264_reg_to_sd30_drive_sel_1v8(reg) (((reg) >> 22) & 0x03) +#define rts5264_reg_to_sd30_drive_sel_3v3(reg) (((reg) >> 16) & 0x03) +#define rts5264_reg_to_rtd3(reg) ((reg) & 0x08) + +#define RTS5264_AUTOLOAD_CFG0 0xFF7B +#define RTS5264_AUTOLOAD_CFG1 0xFF7C +#define RTS5264_AUTOLOAD_CFG3 0xFF7E +#define RTS5264_AUTOLOAD_CFG4 0xFF7F +#define RTS5264_FORCE_PRSNT_LOW (1 << 6) +#define RTS5264_AUX_CLK_16M_EN (1 << 5) +#define RTS5264_F_HIGH_RC_MASK (1 << 4) +#define RTS5264_F_HIGH_RC_1_6M (1 << 4) +#define RTS5264_F_HIGH_RC_400K (0 << 4) + +/* SSC_CTL2 0xFC12 */ +#define RTS5264_SSC_DEPTH_MASK 0x07 +#define RTS5264_SSC_DEPTH_DISALBE 0x00 +#define RTS5264_SSC_DEPTH_8M 0x01 +#define RTS5264_SSC_DEPTH_4M 0x02 +#define RTS5264_SSC_DEPTH_2M 0x03 +#define RTS5264_SSC_DEPTH_1M 0x04 +#define RTS5264_SSC_DEPTH_512K 0x05 +#define RTS5264_SSC_DEPTH_256K 0x06 +#define RTS5264_SSC_DEPTH_128K 0x07 + +#define RTS5264_CARD_CLK_SRC2 0xFC2F +#define RTS5264_REG_BIG_KVCO_A 0x20 + +/* efuse control register*/ +#define RTS5264_EFUSE_CTL 0xFC30 +#define RTS5264_EFUSE_ENABLE 0x80 +/* EFUSE_MODE: 0=READ 1=PROGRAM */ +#define RTS5264_EFUSE_MODE_MASK 0x40 +#define RTS5264_EFUSE_PROGRAM 0x40 + +#define RTS5264_EFUSE_ADDR 0xFC31 +#define RTS5264_EFUSE_ADDR_MASK 0x3F + +#define RTS5264_EFUSE_WRITE_DATA 0xFC32 +#define RTS5264_EFUSE_READ_DATA 0xFC34 + +#define RTS5264_SYS_DUMMY_1 0xFC35 +#define RTS5264_REG_BIG_KVCO 0x04 + +/* DMACTL 0xFE2C */ +#define RTS5264_DMA_PACK_SIZE_MASK 0x70 + +#define RTS5264_FW_CFG1 0xFF55 +#define RTS5264_SYS_CLK_SEL_MCU_CLK (0x01<<7) +#define RTS5264_CRC_CLK_SEL_MCU_CLK (0x01<<6) +#define RTS5264_FAKE_MCU_CLOCK_GATING (0x01<<5) +#define RTS5264_MCU_BUS_SEL_MASK (0x01<<4) + +/* FW status register */ +#define RTS5264_FW_STATUS 0xFF56 +#define RTS5264_EXPRESS_LINK_FAIL_MASK (0x01<<7) + +/* FW control register */ +#define RTS5264_FW_CTL 0xFF5F +#define RTS5264_INFORM_RTD3_COLD (0x01<<5) + +#define RTS5264_REG_FPDCTL 0xFF60 + +#define RTS5264_REG_LDO12_CFG 0xFF6E +#define RTS5264_LDO12_SR_MASK (0x03<<6) +#define RTS5264_LDO12_SR_1_0_MS (0x03<<6) +#define RTS5264_LDO12_SR_0_5_MS (0x02<<6) +#define RTS5264_LDO12_SR_0_2_5_MS (0x01<<6) +#define RTS5264_LDO12_SR_0_0_MS (0x00<<6) +#define RTS5264_LDO12_VO_TUNE_MASK (0x07<<1) +#define RTS5264_LDO12_115 (0x03<<1) +#define RTS5264_LDO12_120 (0x04<<1) +#define RTS5264_LDO12_125 (0x05<<1) +#define RTS5264_LDO12_130 (0x06<<1) +#define RTS5264_LDO12_135 (0x07<<1) + +/* LDO control register */ +#define RTS5264_CARD_PWR_CTL 0xFD50 +#define RTS5264_SD_CLK_ISO (0x01<<7) +#define RTS5264_PAD_SD_DAT_FW_CTRL (0x01<<6) +#define RTS5264_PUPDC (0x01<<5) +#define RTS5264_SD_CMD_ISO (0x01<<4) + +#define RTS5264_OCP_VDD3_CTL 0xFD89 +#define SD_VDD3_DETECT_EN 0x08 +#define SD_VDD3_OCP_INT_EN 0x04 +#define SD_VDD3_OCP_INT_CLR 0x02 +#define SD_VDD3_OC_CLR 0x01 + +#define RTS5264_OCP_VDD3_STS 0xFD8A +#define SD_VDD3_OCP_DETECT 0x08 +#define SD_VDD3_OC_NOW 0x04 +#define SD_VDD3_OC_EVER 0x02 + +#define RTS5264_OVP_CTL 0xFD8D +#define RTS5264_OVP_TIME_MASK 0xF0 +#define RTS5264_OVP_TIME_DFT 0x50 +#define RTS5264_OVP_DETECT_EN 0x08 +#define RTS5264_OVP_INT_EN 0x04 +#define RTS5264_OVP_INT_CLR 0x02 +#define RTS5264_OVP_CLR 0x01 + +#define RTS5264_OVP_STS 0xFD8E +#define RTS5264_OVP_GLTCH_TIME_MASK 0xF0 +#define RTS5264_OVP_GLTCH_TIME_DFT 0x50 +#define RTS5264_VOVER_DET 0x08 +#define RTS5264_OVP_NOW 0x04 +#define RTS5264_OVP_EVER 0x02 + +#define RTS5264_CMD_OE_START_EARLY 0xFDCB +#define RTS5264_CMD_OE_EARLY_LEAVE 0x08 +#define RTS5264_CMD_OE_EARLY_CYCLE_MASK 0x06 +#define RTS5264_CMD_OE_EARLY_4CYCLE 0x06 +#define RTS5264_CMD_OE_EARLY_3CYCLE 0x04 +#define RTS5264_CMD_OE_EARLY_2CYCLE 0x02 +#define RTS5264_CMD_OE_EARLY_1CYCLE 0x00 +#define RTS5264_CMD_OE_EARLY_EN 0x01 + +#define RTS5264_DAT_OE_START_EARLY 0xFDCC +#define RTS5264_DAT_OE_EARLY_LEAVE 0x08 +#define RTS5264_DAT_OE_EARLY_CYCLE_MASK 0x06 +#define RTS5264_DAT_OE_EARLY_4CYCLE 0x06 +#define RTS5264_DAT_OE_EARLY_3CYCLE 0x04 +#define RTS5264_DAT_OE_EARLY_2CYCLE 0x02 +#define RTS5264_DAT_OE_EARLY_1CYCLE 0x00 +#define RTS5264_DAT_OE_EARLY_EN 0x01 + +#define RTS5264_LDO1233318_POW_CTL 0xFF70 +#define RTS5264_TUNE_REF_LDO3318 (0x03<<6) +#define RTS5264_TUNE_REF_LDO3318_DFT (0x02<<6) +#define RTS5264_LDO3318_POWERON (0x01<<3) +#define RTS5264_LDO3_POWERON (0x01<<2) +#define RTS5264_LDO2_POWERON (0x01<<1) +#define RTS5264_LDO1_POWERON (0x01<<0) +#define RTS5264_LDO_POWERON_MASK (0x0F<<0) + +#define RTS5264_DV3318_CFG 0xFF71 +#define RTS5264_DV3318_TUNE_MASK (0x07<<4) +#define RTS5264_DV3318_18 (0x02<<4) +#define RTS5264_DV3318_19 (0x04<<4) +#define RTS5264_DV3318_33 (0x07<<4) + +#define RTS5264_LDO1_CFG0 0xFF72 +#define RTS5264_LDO1_OCP_THD_MASK (0x07 << 5) +#define RTS5264_LDO1_OCP_EN (0x01 << 4) +#define RTS5264_LDO1_OCP_LMT_THD_MASK (0x03 << 2) +#define RTS5264_LDO1_OCP_LMT_EN (0x01 << 1) + +#define RTS5264_LDO1_OCP_THD_850 (0x00<<5) +#define RTS5264_LDO1_OCP_THD_950 (0x01<<5) +#define RTS5264_LDO1_OCP_THD_1050 (0x02<<5) +#define RTS5264_LDO1_OCP_THD_1100 (0x03<<5) +#define RTS5264_LDO1_OCP_THD_1150 (0x04<<5) +#define RTS5264_LDO1_OCP_THD_1200 (0x05<<5) +#define RTS5264_LDO1_OCP_THD_1300 (0x06<<5) +#define RTS5264_LDO1_OCP_THD_1350 (0x07<<5) + +#define RTS5264_LDO1_LMT_THD_1700 (0x00<<2) +#define RTS5264_LDO1_LMT_THD_1800 (0x01<<2) +#define RTS5264_LDO1_LMT_THD_1900 (0x02<<2) +#define RTS5264_LDO1_LMT_THD_2000 (0x03<<2) + +#define RTS5264_LDO1_CFG1 0xFF73 +#define RTS5264_LDO1_TUNE_MASK (0x07<<1) +#define RTS5264_LDO1_18 (0x05<<1) +#define RTS5264_LDO1_33 (0x07<<1) +#define RTS5264_LDO1_PWD_MASK (0x01<<0) + +#define RTS5264_LDO2_CFG0 0xFF74 +#define RTS5264_LDO2_OCP_THD_MASK (0x07<<5) +#define RTS5264_LDO2_OCP_EN (0x01<<4) +#define RTS5264_LDO2_OCP_LMT_THD_MASK (0x03<<2) +#define RTS5264_LDO2_OCP_LMT_EN (0x01<<1) + +#define RTS5264_LDO2_OCP_THD_750 (0x00<<5) +#define RTS5264_LDO2_OCP_THD_850 (0x01<<5) +#define RTS5264_LDO2_OCP_THD_900 (0x02<<5) +#define RTS5264_LDO2_OCP_THD_950 (0x03<<5) +#define RTS5264_LDO2_OCP_THD_1050 (0x04<<5) +#define RTS5264_LDO2_OCP_THD_1100 (0x05<<5) +#define RTS5264_LDO2_OCP_THD_1150 (0x06<<5) +#define RTS5264_LDO2_OCP_THD_1200 (0x07<<5) + +#define RTS5264_LDO2_LMT_THD_1700 (0x00<<2) +#define RTS5264_LDO2_LMT_THD_1800 (0x01<<2) +#define RTS5264_LDO2_LMT_THD_1900 (0x02<<2) +#define RTS5264_LDO2_LMT_THD_2000 (0x03<<2) + +#define RTS5264_LDO2_CFG1 0xFF75 +#define RTS5264_LDO2_TUNE_MASK (0x07<<1) +#define RTS5264_LDO2_18 (0x02<<1) +#define RTS5264_LDO2_185 (0x03<<1) +#define RTS5264_LDO2_19 (0x04<<1) +#define RTS5264_LDO2_195 (0x05<<1) +#define RTS5264_LDO2_33 (0x07<<1) +#define RTS5264_LDO2_PWD_MASK (0x01<<0) + +#define RTS5264_LDO3_CFG0 0xFF76 +#define RTS5264_LDO3_OCP_THD_MASK (0x07<<5) +#define RTS5264_LDO3_OCP_EN (0x01<<4) +#define RTS5264_LDO3_OCP_LMT_THD_MASK (0x03<<2) +#define RTS5264_LDO3_OCP_LMT_EN (0x01<<1) + +#define RTS5264_LDO3_OCP_THD_610 (0x00<<5) +#define RTS5264_LDO3_OCP_THD_630 (0x01<<5) +#define RTS5264_LDO3_OCP_THD_670 (0x02<<5) +#define RTS5264_LDO3_OCP_THD_710 (0x03<<5) +#define RTS5264_LDO3_OCP_THD_750 (0x04<<5) +#define RTS5264_LDO3_OCP_THD_770 (0x05<<5) +#define RTS5264_LDO3_OCP_THD_810 (0x06<<5) +#define RTS5264_LDO3_OCP_THD_850 (0x07<<5) + +#define RTS5264_LDO3_LMT_THD_1200 (0x00<<2) +#define RTS5264_LDO3_LMT_THD_1300 (0x01<<2) +#define RTS5264_LDO3_LMT_THD_1400 (0x02<<2) +#define RTS5264_LDO3_LMT_THD_1500 (0x03<<2) + +#define RTS5264_LDO3_CFG1 0xFF77 +#define RTS5264_LDO3_TUNE_MASK (0x07<<1) +#define RTS5264_LDO3_12 (0x02<<1) +#define RTS5264_LDO3_125 (0x03<<1) +#define RTS5264_LDO3_13 (0x04<<1) +#define RTS5264_LDO3_135 (0x05<<1) +#define RTS5264_LDO3_33 (0x07<<1) +#define RTS5264_LDO3_PWD_MASK (0x01<<0) + +#define RTS5264_REG_PME_FORCE_CTL 0xFF78 +#define FORCE_PM_CONTROL 0x20 +#define FORCE_PM_VALUE 0x10 +#define REG_EFUSE_BYPASS 0x08 +#define REG_EFUSE_POR 0x04 +#define REG_EFUSE_POWER_MASK 0x03 +#define REG_EFUSE_POWERON 0x03 +#define REG_EFUSE_POWEROFF 0x00 + +#define RTS5264_PWR_CUT 0xFF81 +#define RTS5264_CFG_MEM_PD 0xF0 + +#define RTS5264_OVP_DET 0xFF8A +#define RTS5264_POW_VDET 0x04 +#define RTS5264_TUNE_VROV_MASK 0x03 +#define RTS5264_TUNE_VROV_2V 0x03 +#define RTS5264_TUNE_VROV_1V8 0x02 +#define RTS5264_TUNE_VROV_1V6 0x01 +#define RTS5264_TUNE_VROV_1V4 0x00 + +#define RTS5264_CKMUX_MBIAS_PWR 0xFF8B +#define RTS5264_NON_XTAL_SEL 0x80 +#define RTS5264_POW_CKMUX 0x40 +#define RTS5264_LVD_MASK 0x04 +#define RTS5264_POW_PSW_MASK 0x03 +#define RTS5264_POW_PSW_DFT 0x03 + +/* Single LUN, support SD/SD EXPRESS */ +#define DEFAULT_SINGLE 0 +#define SD_LUN 1 +#define SD_EXPRESS_LUN 2 + +int rts5264_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, + u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk); + +#endif /* RTS5264_H */ diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index a30751ad3733..1a64364700eb 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -26,6 +26,7 @@ #include "rtsx_pcr.h" #include "rts5261.h" #include "rts5228.h" +#include "rts5264.h" static bool msi_en = true; module_param(msi_en, bool, S_IRUGO | S_IWUSR); @@ -54,6 +55,7 @@ static const struct pci_device_id rtsx_pci_ids[] = { { PCI_DEVICE(0x10EC, 0x5260), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5261), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5228), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5264), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; @@ -714,6 +716,9 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, if (PCI_PID(pcr) == PID_5228) return rts5228_pci_switch_clock(pcr, card_clock, ssc_depth, initial_mode, double_clk, vpclk); + if (PCI_PID(pcr) == PID_5264) + return rts5264_pci_switch_clock(pcr, card_clock, + ssc_depth, initial_mode, double_clk, vpclk); if (initial_mode) { /* We use 250k(around) here, in initial stage */ @@ -987,7 +992,8 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id) int_reg &= (pcr->bier | 0x7FFFFF); - if (int_reg & SD_OC_INT) + if ((int_reg & SD_OC_INT) || + ((int_reg & SD_OVP_INT) && (PCI_PID(pcr) == PID_5264))) rtsx_pci_process_ocp_interrupt(pcr); if (int_reg & SD_INT) { @@ -1159,7 +1165,9 @@ void rtsx_pci_enable_oobs_polling(struct rtsx_pcr *pcr) { u16 val; - if ((PCI_PID(pcr) != PID_525A) && (PCI_PID(pcr) != PID_5260)) { + if ((PCI_PID(pcr) != PID_525A) && + (PCI_PID(pcr) != PID_5260) && + (PCI_PID(pcr) != PID_5264)) { rtsx_pci_read_phy_register(pcr, 0x01, &val); val |= 1<<9; rtsx_pci_write_phy_register(pcr, 0x01, val); @@ -1175,7 +1183,9 @@ void rtsx_pci_disable_oobs_polling(struct rtsx_pcr *pcr) { u16 val; - if ((PCI_PID(pcr) != PID_525A) && (PCI_PID(pcr) != PID_5260)) { + if ((PCI_PID(pcr) != PID_525A) && + (PCI_PID(pcr) != PID_5260) && + (PCI_PID(pcr) != PID_5264)) { rtsx_pci_read_phy_register(pcr, 0x01, &val); val &= ~(1<<9); rtsx_pci_write_phy_register(pcr, 0x01, val); @@ -1226,7 +1236,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) rtsx_pci_enable_bus_int(pcr); /* Power on SSC */ - if (PCI_PID(pcr) == PID_5261) { + if ((PCI_PID(pcr) == PID_5261) || (PCI_PID(pcr) == PID_5264)) { /* Gating real mcu clock */ err = rtsx_pci_write_register(pcr, RTS5261_FW_CFG1, RTS5261_MCU_CLOCK_GATING, 0); @@ -1270,6 +1280,11 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) else if (PCI_PID(pcr) == PID_5228) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, RTS5228_SSC_DEPTH_2M); + else if (is_version(pcr, 0x5264, IC_VER_A)) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); + else if (PCI_PID(pcr) == PID_5264) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, + RTS5264_SSC_DEPTH_2M); else rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, 0x12); @@ -1305,6 +1320,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) case PID_5260: case PID_5261: case PID_5228: + case PID_5264: rtsx_pci_write_register(pcr, PM_CLK_FORCE_CTL, 1, 1); break; default: @@ -1404,6 +1420,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) case 0x5228: rts5228_init_params(pcr); break; + + case 0x5264: + rts5264_init_params(pcr); + break; } pcr_dbg(pcr, "PID: 0x%04x, IC version: 0x%02x\n", @@ -1544,7 +1564,7 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, pcr->pci = pcidev; dev_set_drvdata(&pcidev->dev, handle); - if (CHK_PCI_PID(pcr, 0x525A)) + if ((CHK_PCI_PID(pcr, 0x525A)) || (CHK_PCI_PID(pcr, 0x5264))) bar = 1; len = pci_resource_len(pcidev, bar); base = pci_resource_start(pcidev, bar); diff --git a/drivers/misc/cardreader/rtsx_pcr.h b/drivers/misc/cardreader/rtsx_pcr.h index 37d1f316ae17..9215d66de00c 100644 --- a/drivers/misc/cardreader/rtsx_pcr.h +++ b/drivers/misc/cardreader/rtsx_pcr.h @@ -74,6 +74,7 @@ void rtl8411b_init_params(struct rtsx_pcr *pcr); void rts5260_init_params(struct rtsx_pcr *pcr); void rts5261_init_params(struct rtsx_pcr *pcr); void rts5228_init_params(struct rtsx_pcr *pcr); +void rts5264_init_params(struct rtsx_pcr *pcr); static inline u8 map_sd_drive(int idx) { diff --git a/drivers/misc/dw-xdata-pcie.c b/drivers/misc/dw-xdata-pcie.c index 257c25da5199..efd0ca8cc925 100644 --- a/drivers/misc/dw-xdata-pcie.c +++ b/drivers/misc/dw-xdata-pcie.c @@ -333,7 +333,7 @@ static int dw_xdata_pcie_probe(struct pci_dev *pdev, dw->pdev = pdev; - id = ida_simple_get(&xdata_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&xdata_ida, GFP_KERNEL); if (id < 0) { dev_err(dev, "xData: unable to get id\n"); return id; @@ -377,7 +377,7 @@ err_kfree_name: kfree(dw->misc_dev.name); err_ida_remove: - ida_simple_remove(&xdata_ida, id); + ida_free(&xdata_ida, id); return err; } @@ -396,7 +396,7 @@ static void dw_xdata_pcie_remove(struct pci_dev *pdev) dw_xdata_stop(dw); misc_deregister(&dw->misc_dev); kfree(dw->misc_dev.name); - ida_simple_remove(&xdata_ida, id); + ida_free(&xdata_ida, id); } static const struct pci_device_id dw_xdata_pcie_id_table[] = { diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index f61a80597a22..572333ead5fb 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/nvmem-provider.h> +#include <linux/of.h> #include <linux/of_device.h> #include <linux/pm_runtime.h> #include <linux/property.h> @@ -242,7 +243,7 @@ static const struct i2c_device_id at24_ids[] = { }; MODULE_DEVICE_TABLE(i2c, at24_ids); -static const struct of_device_id at24_of_match[] = { +static const struct of_device_id __maybe_unused at24_of_match[] = { { .compatible = "atmel,24c00", .data = &at24_data_24c00 }, { .compatible = "atmel,24c01", .data = &at24_data_24c01 }, { .compatible = "atmel,24cs01", .data = &at24_data_24cs01 }, @@ -439,12 +440,9 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count) if (off + count > at24->byte_len) return -EINVAL; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) return ret; - } - /* * Read data from chip, protecting against concurrent updates * from this host, but not from other I2C masters. @@ -486,12 +484,9 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) if (off + count > at24->byte_len) return -EINVAL; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) return ret; - } - /* * Write data to chip, protecting against concurrent updates * from this host, but not from other I2C masters. @@ -563,6 +558,31 @@ static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len) } } +static void at24_probe_temp_sensor(struct i2c_client *client) +{ + struct at24_data *at24 = i2c_get_clientdata(client); + struct i2c_board_info info = { .type = "jc42" }; + int ret; + u8 val; + + /* + * Byte 2 has value 11 for DDR3, earlier versions don't + * support the thermal sensor present flag + */ + ret = at24_read(at24, 2, &val, 1); + if (ret || val != 11) + return; + + /* Byte 32, bit 7 is set if temp sensor is present */ + ret = at24_read(at24, 32, &val, 1); + if (ret || !(val & BIT(7))) + return; + + info.addr = 0x18 | (client->addr & 7); + + i2c_new_client_device(client->adapter, &info); +} + static int at24_probe(struct i2c_client *client) { struct regmap_config regmap_config = { }; @@ -762,6 +782,10 @@ static int at24_probe(struct i2c_client *client) } } + /* If this a SPD EEPROM, probe for DDR3 thermal sensor */ + if (cdata == &at24_data_spd) + at24_probe_temp_sensor(client); + pm_runtime_idle(dev); if (writable) @@ -812,7 +836,7 @@ static struct i2c_driver at24_driver = { .driver = { .name = "at24", .pm = &at24_pm_ops, - .of_match_table = at24_of_match, + .of_match_table = of_match_ptr(at24_of_match), .acpi_match_table = ACPI_PTR(at24_acpi_ids), }, .probe = at24_probe, diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/misc/eeprom/ee1004.c index a1acd77130f2..21feebc3044c 100644 --- a/drivers/misc/eeprom/ee1004.c +++ b/drivers/misc/eeprom/ee1004.c @@ -31,6 +31,7 @@ * over performance. */ +#define EE1004_MAX_BUSSES 8 #define EE1004_ADDR_SET_PAGE 0x36 #define EE1004_NUM_PAGES 2 #define EE1004_PAGE_SIZE 256 @@ -42,9 +43,13 @@ * from page selection to end of read. */ static DEFINE_MUTEX(ee1004_bus_lock); -static struct i2c_client *ee1004_set_page[EE1004_NUM_PAGES]; -static unsigned int ee1004_dev_count; -static int ee1004_current_page; + +static struct ee1004_bus_data { + struct i2c_adapter *adap; + struct i2c_client *set_page[EE1004_NUM_PAGES]; + unsigned int dev_count; + int current_page; +} ee1004_bus_data[EE1004_MAX_BUSSES]; static const struct i2c_device_id ee1004_ids[] = { { "ee1004", 0 }, @@ -54,11 +59,29 @@ MODULE_DEVICE_TABLE(i2c, ee1004_ids); /*-------------------------------------------------------------------------*/ -static int ee1004_get_current_page(void) +static struct ee1004_bus_data *ee1004_get_bus_data(struct i2c_adapter *adap) +{ + int i; + + for (i = 0; i < EE1004_MAX_BUSSES; i++) + if (ee1004_bus_data[i].adap == adap) + return ee1004_bus_data + i; + + /* If not existent yet, create new entry */ + for (i = 0; i < EE1004_MAX_BUSSES; i++) + if (!ee1004_bus_data[i].adap) { + ee1004_bus_data[i].adap = adap; + return ee1004_bus_data + i; + } + + return NULL; +} + +static int ee1004_get_current_page(struct ee1004_bus_data *bd) { int err; - err = i2c_smbus_read_byte(ee1004_set_page[0]); + err = i2c_smbus_read_byte(bd->set_page[0]); if (err == -ENXIO) { /* Nack means page 1 is selected */ return 1; @@ -72,28 +95,29 @@ static int ee1004_get_current_page(void) return 0; } -static int ee1004_set_current_page(struct device *dev, int page) +static int ee1004_set_current_page(struct i2c_client *client, int page) { + struct ee1004_bus_data *bd = i2c_get_clientdata(client); int ret; - if (page == ee1004_current_page) + if (page == bd->current_page) return 0; /* Data is ignored */ - ret = i2c_smbus_write_byte(ee1004_set_page[page], 0x00); + ret = i2c_smbus_write_byte(bd->set_page[page], 0x00); /* * Don't give up just yet. Some memory modules will select the page * but not ack the command. Check which page is selected now. */ - if (ret == -ENXIO && ee1004_get_current_page() == page) + if (ret == -ENXIO && ee1004_get_current_page(bd) == page) ret = 0; if (ret < 0) { - dev_err(dev, "Failed to select page %d (%d)\n", page, ret); + dev_err(&client->dev, "Failed to select page %d (%d)\n", page, ret); return ret; } - dev_dbg(dev, "Selected page %d\n", page); - ee1004_current_page = page; + dev_dbg(&client->dev, "Selected page %d\n", page); + bd->current_page = page; return 0; } @@ -106,7 +130,7 @@ static ssize_t ee1004_eeprom_read(struct i2c_client *client, char *buf, page = offset >> EE1004_PAGE_SHIFT; offset &= (1 << EE1004_PAGE_SHIFT) - 1; - status = ee1004_set_current_page(&client->dev, page); + status = ee1004_set_current_page(client, page); if (status) return status; @@ -158,17 +182,34 @@ static struct bin_attribute *ee1004_attrs[] = { BIN_ATTRIBUTE_GROUPS(ee1004); -static void ee1004_cleanup(int idx) +static void ee1004_probe_temp_sensor(struct i2c_client *client) { - if (--ee1004_dev_count == 0) - while (--idx >= 0) { - i2c_unregister_device(ee1004_set_page[idx]); - ee1004_set_page[idx] = NULL; - } + struct i2c_board_info info = { .type = "jc42" }; + u8 byte14; + int ret; + + /* byte 14, bit 7 is set if temp sensor is present */ + ret = ee1004_eeprom_read(client, &byte14, 14, 1); + if (ret != 1 || !(byte14 & BIT(7))) + return; + + info.addr = 0x18 | (client->addr & 7); + + i2c_new_client_device(client->adapter, &info); +} + +static void ee1004_cleanup(int idx, struct ee1004_bus_data *bd) +{ + if (--bd->dev_count == 0) { + while (--idx >= 0) + i2c_unregister_device(bd->set_page[idx]); + memset(bd, 0, sizeof(struct ee1004_bus_data)); + } } static int ee1004_probe(struct i2c_client *client) { + struct ee1004_bus_data *bd; int err, cnr = 0; /* Make sure we can operate on this adapter */ @@ -178,9 +219,19 @@ static int ee1004_probe(struct i2c_client *client) I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA)) return -EPFNOSUPPORT; - /* Use 2 dummy devices for page select command */ mutex_lock(&ee1004_bus_lock); - if (++ee1004_dev_count == 1) { + + bd = ee1004_get_bus_data(client->adapter); + if (!bd) { + mutex_unlock(&ee1004_bus_lock); + return dev_err_probe(&client->dev, -ENOSPC, + "Only %d busses supported", EE1004_MAX_BUSSES); + } + + i2c_set_clientdata(client, bd); + + if (++bd->dev_count == 1) { + /* Use 2 dummy devices for page select command */ for (cnr = 0; cnr < EE1004_NUM_PAGES; cnr++) { struct i2c_client *cl; @@ -189,21 +240,19 @@ static int ee1004_probe(struct i2c_client *client) err = PTR_ERR(cl); goto err_clients; } - ee1004_set_page[cnr] = cl; + bd->set_page[cnr] = cl; } /* Remember current page to avoid unneeded page select */ - err = ee1004_get_current_page(); + err = ee1004_get_current_page(bd); if (err < 0) goto err_clients; dev_dbg(&client->dev, "Currently selected page: %d\n", err); - ee1004_current_page = err; - } else if (client->adapter != ee1004_set_page[0]->adapter) { - dev_err(&client->dev, - "Driver only supports devices on a single I2C bus\n"); - err = -EOPNOTSUPP; - goto err_clients; + bd->current_page = err; } + + ee1004_probe_temp_sensor(client); + mutex_unlock(&ee1004_bus_lock); dev_info(&client->dev, @@ -213,7 +262,7 @@ static int ee1004_probe(struct i2c_client *client) return 0; err_clients: - ee1004_cleanup(cnr); + ee1004_cleanup(cnr, bd); mutex_unlock(&ee1004_bus_lock); return err; @@ -221,9 +270,11 @@ static int ee1004_probe(struct i2c_client *client) static void ee1004_remove(struct i2c_client *client) { + struct ee1004_bus_data *bd = i2c_get_clientdata(client); + /* Remove page select clients if this is the last device */ mutex_lock(&ee1004_bus_lock); - ee1004_cleanup(EE1004_NUM_PAGES); + ee1004_cleanup(EE1004_NUM_PAGES, bd); mutex_unlock(&ee1004_bus_lock); } diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 1c6c62a7f7f5..03319a1fa97f 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -2191,7 +2191,7 @@ static int fastrpc_cb_remove(struct platform_device *pdev) int i; spin_lock_irqsave(&cctx->lock, flags); - for (i = 1; i < FASTRPC_MAX_SESSIONS; i++) { + for (i = 0; i < FASTRPC_MAX_SESSIONS; i++) { if (cctx->session[i].sid == sess->sid) { cctx->session[i].valid = false; cctx->sesscount--; diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c index 3882e97e96a7..c6eb27d46cb0 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c @@ -150,6 +150,7 @@ static int lis3lv02d_i2c_probe(struct i2c_client *client) lis3_dev.init = lis3_i2c_init; lis3_dev.read = lis3_i2c_read; lis3_dev.write = lis3_i2c_write; + lis3_dev.reg_ctrl = lis3_reg_ctrl; lis3_dev.irq = client->irq; lis3_dev.ac = lis3lv02d_axis_map; lis3_dev.pm_dev = &client->dev; diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index 37db142de413..67d9391f1855 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -3,6 +3,7 @@ config INTEL_MEI tristate "Intel Management Engine Interface" depends on X86 && PCI + default GENERIC_CPU || MCORE2 || MATOM || X86_GENERIC help The Intel Management Engine (Intel ME) provides Manageability, Security and Media services for system containing Intel chipsets. @@ -11,10 +12,11 @@ config INTEL_MEI For more information see <https://software.intel.com/en-us/manageability/> +if INTEL_MEI + config INTEL_MEI_ME tristate "ME Enabled Intel Chipsets" - select INTEL_MEI - depends on X86 && PCI + default y help MEI support for ME Enabled Intel chipsets. @@ -38,8 +40,6 @@ config INTEL_MEI_ME config INTEL_MEI_TXE tristate "Intel Trusted Execution Environment with ME Interface" - select INTEL_MEI - depends on X86 && PCI help MEI Support for Trusted Execution Environment device on Intel SoCs @@ -48,9 +48,7 @@ config INTEL_MEI_TXE config INTEL_MEI_GSC tristate "Intel MEI GSC embedded device" - depends on INTEL_MEI depends on INTEL_MEI_ME - depends on X86 && PCI depends on DRM_I915 help Intel auxiliary driver for GSC devices embedded in Intel graphics devices. @@ -60,6 +58,31 @@ config INTEL_MEI_GSC tasks such as graphics card firmware update and security tasks. +config INTEL_MEI_VSC_HW + tristate "Intel visual sensing controller device transport driver" + depends on ACPI && SPI + depends on GPIOLIB || COMPILE_TEST + help + Intel SPI transport driver between host and Intel visual sensing + controller (IVSC) device. + + This driver can also be built as a module. If so, the module + will be called mei-vsc-hw. + +config INTEL_MEI_VSC + tristate "Intel visual sensing controller device with ME interface" + depends on INTEL_MEI_VSC_HW + help + Intel MEI over SPI driver for Intel visual sensing controller + (IVSC) device embedded in IA platform. It supports camera sharing + between IVSC for context sensing and IPU for typical media usage. + Select this config should enable transport layer for IVSC device. + + This driver can also be built as a module. If so, the module + will be called mei-vsc. + source "drivers/misc/mei/hdcp/Kconfig" source "drivers/misc/mei/pxp/Kconfig" source "drivers/misc/mei/gsc_proxy/Kconfig" + +endif diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 14aee253ae48..6f9fdbf1a495 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -31,3 +31,10 @@ CFLAGS_mei-trace.o = -I$(src) obj-$(CONFIG_INTEL_MEI_HDCP) += hdcp/ obj-$(CONFIG_INTEL_MEI_PXP) += pxp/ obj-$(CONFIG_INTEL_MEI_GSC_PROXY) += gsc_proxy/ + +obj-$(CONFIG_INTEL_MEI_VSC_HW) += mei-vsc-hw.o +mei-vsc-hw-y := vsc-tp.o +mei-vsc-hw-y += vsc-fw-loader.o + +obj-$(CONFIG_INTEL_MEI_VSC) += mei-vsc.o +mei-vsc-y := platform-vsc.o diff --git a/drivers/misc/mei/gsc_proxy/Kconfig b/drivers/misc/mei/gsc_proxy/Kconfig index 5f68d9f3d691..ac78b9d1eccd 100644 --- a/drivers/misc/mei/gsc_proxy/Kconfig +++ b/drivers/misc/mei/gsc_proxy/Kconfig @@ -3,7 +3,7 @@ # config INTEL_MEI_GSC_PROXY tristate "Intel GSC Proxy services of ME Interface" - select INTEL_MEI_ME + depends on INTEL_MEI_ME depends on DRM_I915 help MEI Support for GSC Proxy Services on Intel platforms. diff --git a/drivers/misc/mei/hdcp/Kconfig b/drivers/misc/mei/hdcp/Kconfig index 54e1c9526909..9be312ec798d 100644 --- a/drivers/misc/mei/hdcp/Kconfig +++ b/drivers/misc/mei/hdcp/Kconfig @@ -3,7 +3,7 @@ # config INTEL_MEI_HDCP tristate "Intel HDCP2.2 services of ME Interface" - select INTEL_MEI_ME + depends on INTEL_MEI_ME depends on DRM_I915 help MEI Support for HDCP2.2 Services on Intel platforms. diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c new file mode 100644 index 000000000000..8d303c6c0000 --- /dev/null +++ b/drivers/misc/mei/platform-vsc.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, Intel Corporation. + * Intel Visual Sensing Controller Interface Linux driver + */ + +#include <linux/align.h> +#include <linux/cache.h> +#include <linux/cleanup.h> +#include <linux/iopoll.h> +#include <linux/list.h> +#include <linux/mei.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/overflow.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/timekeeping.h> +#include <linux/types.h> + +#include <asm-generic/bug.h> +#include <asm-generic/unaligned.h> + +#include "mei_dev.h" +#include "vsc-tp.h" + +#define MEI_VSC_DRV_NAME "intel_vsc" + +#define MEI_VSC_MAX_MSG_SIZE 512 + +#define MEI_VSC_POLL_DELAY_US (50 * USEC_PER_MSEC) +#define MEI_VSC_POLL_TIMEOUT_US (200 * USEC_PER_MSEC) + +#define mei_dev_to_vsc_hw(dev) ((struct mei_vsc_hw *)((dev)->hw)) + +struct mei_vsc_host_timestamp { + u64 realtime; + u64 boottime; +}; + +struct mei_vsc_hw { + struct vsc_tp *tp; + + bool fw_ready; + bool host_ready; + + atomic_t write_lock_cnt; + + u32 rx_len; + u32 rx_hdr; + + /* buffer for tx */ + char tx_buf[MEI_VSC_MAX_MSG_SIZE + sizeof(struct mei_msg_hdr)] ____cacheline_aligned; + /* buffer for rx */ + char rx_buf[MEI_VSC_MAX_MSG_SIZE + sizeof(struct mei_msg_hdr)] ____cacheline_aligned; +}; + +static int mei_vsc_read_helper(struct mei_vsc_hw *hw, u8 *buf, + u32 max_len) +{ + struct mei_vsc_host_timestamp ts = { + .realtime = ktime_to_ns(ktime_get_real()), + .boottime = ktime_to_ns(ktime_get_boottime()), + }; + + return vsc_tp_xfer(hw->tp, VSC_TP_CMD_READ, &ts, sizeof(ts), + buf, max_len); +} + +static int mei_vsc_write_helper(struct mei_vsc_hw *hw, u8 *buf, u32 len) +{ + u8 status; + + return vsc_tp_xfer(hw->tp, VSC_TP_CMD_WRITE, buf, len, &status, + sizeof(status)); +} + +static int mei_vsc_fw_status(struct mei_device *mei_dev, + struct mei_fw_status *fw_status) +{ + if (!fw_status) + return -EINVAL; + + fw_status->count = 0; + + return 0; +} + +static inline enum mei_pg_state mei_vsc_pg_state(struct mei_device *mei_dev) +{ + return MEI_PG_OFF; +} + +static void mei_vsc_intr_enable(struct mei_device *mei_dev) +{ + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); + + vsc_tp_intr_enable(hw->tp); +} + +static void mei_vsc_intr_disable(struct mei_device *mei_dev) +{ + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); + + vsc_tp_intr_disable(hw->tp); +} + +/* mei framework requires this ops */ +static void mei_vsc_intr_clear(struct mei_device *mei_dev) +{ +} + +/* wait for pending irq handler */ +static void mei_vsc_synchronize_irq(struct mei_device *mei_dev) +{ + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); + + vsc_tp_intr_synchronize(hw->tp); +} + +static int mei_vsc_hw_config(struct mei_device *mei_dev) +{ + return 0; +} + +static bool mei_vsc_host_is_ready(struct mei_device *mei_dev) +{ + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); + + return hw->host_ready; +} + +static bool mei_vsc_hw_is_ready(struct mei_device *mei_dev) +{ + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); + + return hw->fw_ready; +} + +static int mei_vsc_hw_start(struct mei_device *mei_dev) +{ + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); + int ret, rlen; + u8 buf; + + hw->host_ready = true; + + vsc_tp_intr_enable(hw->tp); + + ret = read_poll_timeout(mei_vsc_read_helper, rlen, + rlen >= 0, MEI_VSC_POLL_DELAY_US, + MEI_VSC_POLL_TIMEOUT_US, true, + hw, &buf, sizeof(buf)); + if (ret) { + dev_err(mei_dev->dev, "wait fw ready failed: %d\n", ret); + return ret; + } + + hw->fw_ready = true; + + return 0; +} + +static bool mei_vsc_hbuf_is_ready(struct mei_device *mei_dev) +{ + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); + + return atomic_read(&hw->write_lock_cnt) == 0; +} + +static int mei_vsc_hbuf_empty_slots(struct mei_device *mei_dev) +{ + return MEI_VSC_MAX_MSG_SIZE / MEI_SLOT_SIZE; +} + +static u32 mei_vsc_hbuf_depth(const struct mei_device *mei_dev) +{ + return MEI_VSC_MAX_MSG_SIZE / MEI_SLOT_SIZE; +} + +static int mei_vsc_write(struct mei_device *mei_dev, + const void *hdr, size_t hdr_len, + const void *data, size_t data_len) +{ + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); + char *buf = hw->tx_buf; + int ret; + + if (WARN_ON(!hdr || !IS_ALIGNED(hdr_len, 4))) + return -EINVAL; + + if (!data || data_len > MEI_VSC_MAX_MSG_SIZE) + return -EINVAL; + + atomic_inc(&hw->write_lock_cnt); + + memcpy(buf, hdr, hdr_len); + memcpy(buf + hdr_len, data, data_len); + + ret = mei_vsc_write_helper(hw, buf, hdr_len + data_len); + + atomic_dec_if_positive(&hw->write_lock_cnt); + + return ret < 0 ? ret : 0; +} + +static inline u32 mei_vsc_read(const struct mei_device *mei_dev) +{ + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); + int ret; + + ret = mei_vsc_read_helper(hw, hw->rx_buf, sizeof(hw->rx_buf)); + if (ret < 0 || ret < sizeof(u32)) + return 0; + hw->rx_len = ret; + + hw->rx_hdr = get_unaligned_le32(hw->rx_buf); + + return hw->rx_hdr; +} + +static int mei_vsc_count_full_read_slots(struct mei_device *mei_dev) +{ + return MEI_VSC_MAX_MSG_SIZE / MEI_SLOT_SIZE; +} + +static int mei_vsc_read_slots(struct mei_device *mei_dev, unsigned char *buf, + unsigned long len) +{ + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); + struct mei_msg_hdr *hdr; + + hdr = (struct mei_msg_hdr *)&hw->rx_hdr; + if (len != hdr->length || hdr->length + sizeof(*hdr) != hw->rx_len) + return -EINVAL; + + memcpy(buf, hw->rx_buf + sizeof(*hdr), len); + + return 0; +} + +static bool mei_vsc_pg_in_transition(struct mei_device *mei_dev) +{ + return mei_dev->pg_event >= MEI_PG_EVENT_WAIT && + mei_dev->pg_event <= MEI_PG_EVENT_INTR_WAIT; +} + +static bool mei_vsc_pg_is_enabled(struct mei_device *mei_dev) +{ + return false; +} + +static int mei_vsc_hw_reset(struct mei_device *mei_dev, bool intr_enable) +{ + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); + + vsc_tp_reset(hw->tp); + + vsc_tp_intr_disable(hw->tp); + + return vsc_tp_init(hw->tp, mei_dev->dev); +} + +static const struct mei_hw_ops mei_vsc_hw_ops = { + .fw_status = mei_vsc_fw_status, + .pg_state = mei_vsc_pg_state, + + .host_is_ready = mei_vsc_host_is_ready, + .hw_is_ready = mei_vsc_hw_is_ready, + .hw_reset = mei_vsc_hw_reset, + .hw_config = mei_vsc_hw_config, + .hw_start = mei_vsc_hw_start, + + .pg_in_transition = mei_vsc_pg_in_transition, + .pg_is_enabled = mei_vsc_pg_is_enabled, + + .intr_clear = mei_vsc_intr_clear, + .intr_enable = mei_vsc_intr_enable, + .intr_disable = mei_vsc_intr_disable, + .synchronize_irq = mei_vsc_synchronize_irq, + + .hbuf_free_slots = mei_vsc_hbuf_empty_slots, + .hbuf_is_ready = mei_vsc_hbuf_is_ready, + .hbuf_depth = mei_vsc_hbuf_depth, + .write = mei_vsc_write, + + .rdbuf_full_slots = mei_vsc_count_full_read_slots, + .read_hdr = mei_vsc_read, + .read = mei_vsc_read_slots, +}; + +static void mei_vsc_event_cb(void *context) +{ + struct mei_device *mei_dev = context; + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); + struct list_head cmpl_list; + s32 slots; + int ret; + + if (mei_dev->dev_state == MEI_DEV_RESETTING || + mei_dev->dev_state == MEI_DEV_INITIALIZING) + return; + + INIT_LIST_HEAD(&cmpl_list); + + guard(mutex)(&mei_dev->device_lock); + + while (vsc_tp_need_read(hw->tp)) { + /* check slots available for reading */ + slots = mei_count_full_read_slots(mei_dev); + + ret = mei_irq_read_handler(mei_dev, &cmpl_list, &slots); + if (ret) { + if (ret != -ENODATA) { + if (mei_dev->dev_state != MEI_DEV_RESETTING && + mei_dev->dev_state != MEI_DEV_POWER_DOWN) + schedule_work(&mei_dev->reset_work); + } + + return; + } + } + + mei_dev->hbuf_is_ready = mei_hbuf_is_ready(mei_dev); + ret = mei_irq_write_handler(mei_dev, &cmpl_list); + if (ret) + dev_err(mei_dev->dev, "dispatch write request failed: %d\n", ret); + + mei_dev->hbuf_is_ready = mei_hbuf_is_ready(mei_dev); + mei_irq_compl_handler(mei_dev, &cmpl_list); +} + +static int mei_vsc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mei_device *mei_dev; + struct mei_vsc_hw *hw; + struct vsc_tp *tp; + int ret; + + tp = *(struct vsc_tp **)dev_get_platdata(dev); + if (!tp) + return dev_err_probe(dev, -ENODEV, "no platform data\n"); + + mei_dev = devm_kzalloc(dev, size_add(sizeof(*mei_dev), sizeof(*hw)), + GFP_KERNEL); + if (!mei_dev) + return -ENOMEM; + + mei_device_init(mei_dev, dev, false, &mei_vsc_hw_ops); + mei_dev->fw_f_fw_ver_supported = 0; + mei_dev->kind = "ivsc"; + + hw = mei_dev_to_vsc_hw(mei_dev); + atomic_set(&hw->write_lock_cnt, 0); + hw->tp = tp; + + platform_set_drvdata(pdev, mei_dev); + + vsc_tp_register_event_cb(tp, mei_vsc_event_cb, mei_dev); + + ret = mei_start(mei_dev); + if (ret) { + dev_err_probe(dev, ret, "init hw failed\n"); + goto err_cancel; + } + + ret = mei_register(mei_dev, dev); + if (ret) + goto err_stop; + + pm_runtime_enable(mei_dev->dev); + + return 0; + +err_stop: + mei_stop(mei_dev); + +err_cancel: + mei_cancel_work(mei_dev); + + mei_disable_interrupts(mei_dev); + + return ret; +} + +static int mei_vsc_remove(struct platform_device *pdev) +{ + struct mei_device *mei_dev = platform_get_drvdata(pdev); + + pm_runtime_disable(mei_dev->dev); + + mei_stop(mei_dev); + + mei_disable_interrupts(mei_dev); + + mei_deregister(mei_dev); + + return 0; +} + +static int mei_vsc_suspend(struct device *dev) +{ + struct mei_device *mei_dev = dev_get_drvdata(dev); + + mei_stop(mei_dev); + + return 0; +} + +static int mei_vsc_resume(struct device *dev) +{ + struct mei_device *mei_dev = dev_get_drvdata(dev); + int ret; + + ret = mei_restart(mei_dev); + if (ret) + return ret; + + /* start timer if stopped in suspend */ + schedule_delayed_work(&mei_dev->timer_work, HZ); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(mei_vsc_pm_ops, mei_vsc_suspend, mei_vsc_resume); + +static const struct platform_device_id mei_vsc_id_table[] = { + { MEI_VSC_DRV_NAME }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, mei_vsc_id_table); + +static struct platform_driver mei_vsc_drv = { + .probe = mei_vsc_probe, + .remove = mei_vsc_remove, + .id_table = mei_vsc_id_table, + .driver = { + .name = MEI_VSC_DRV_NAME, + .pm = &mei_vsc_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; +module_platform_driver(mei_vsc_drv); + +MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>"); +MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>"); +MODULE_DESCRIPTION("Intel Visual Sensing Controller Interface"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(VSC_TP); diff --git a/drivers/misc/mei/pxp/Kconfig b/drivers/misc/mei/pxp/Kconfig index 4029b96afc04..e9219b61cd92 100644 --- a/drivers/misc/mei/pxp/Kconfig +++ b/drivers/misc/mei/pxp/Kconfig @@ -1,10 +1,9 @@ - # SPDX-License-Identifier: GPL-2.0 # Copyright (c) 2020, Intel Corporation. All rights reserved. # config INTEL_MEI_PXP tristate "Intel PXP services of ME Interface" - select INTEL_MEI_ME + depends on INTEL_MEI_ME depends on DRM_I915 help MEI Support for PXP Services on Intel platforms. diff --git a/drivers/misc/mei/vsc-fw-loader.c b/drivers/misc/mei/vsc-fw-loader.c new file mode 100644 index 000000000000..ffa4ccd96a10 --- /dev/null +++ b/drivers/misc/mei/vsc-fw-loader.c @@ -0,0 +1,770 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, Intel Corporation. + * Intel Visual Sensing Controller Transport Layer Linux driver + */ + +#include <linux/acpi.h> +#include <linux/align.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/firmware.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/string_helpers.h> +#include <linux/types.h> + +#include <asm-generic/unaligned.h> + +#include "vsc-tp.h" + +#define VSC_MAGIC_NUM 0x49505343 /* IPSC */ +#define VSC_MAGIC_FW 0x49574653 /* IWFS */ +#define VSC_MAGIC_FILE 0x46564353 /* FVCS */ + +#define VSC_ADDR_BASE 0xE0030000 +#define VSC_EFUSE_ADDR (VSC_ADDR_BASE + 0x038) +#define VSC_STRAP_ADDR (VSC_ADDR_BASE + 0x100) + +#define VSC_MAINSTEPPING_VERSION_MASK GENMASK(7, 4) +#define VSC_MAINSTEPPING_VERSION_A 0 + +#define VSC_SUBSTEPPING_VERSION_MASK GENMASK(3, 0) +#define VSC_SUBSTEPPING_VERSION_0 0 +#define VSC_SUBSTEPPING_VERSION_1 2 + +#define VSC_BOOT_IMG_OPTION_MASK GENMASK(15, 0) + +#define VSC_SKU_CFG_LOCATION 0x5001A000 +#define VSC_SKU_MAX_SIZE 4100u + +#define VSC_ACE_IMG_CNT 2 +#define VSC_CSI_IMG_CNT 4 +#define VSC_IMG_CNT_MAX 6 + +#define VSC_ROM_PKG_SIZE 256u +#define VSC_FW_PKG_SIZE 512u + +#define VSC_IMAGE_DIR "intel/vsc/" + +#define VSC_CSI_IMAGE_NAME VSC_IMAGE_DIR "ivsc_fw.bin" +#define VSC_ACE_IMAGE_NAME_FMT VSC_IMAGE_DIR "ivsc_pkg_%s_0.bin" +#define VSC_CFG_IMAGE_NAME_FMT VSC_IMAGE_DIR "ivsc_skucfg_%s_0_1.bin" + +#define VSC_IMAGE_PATH_MAX_LEN 64 + +#define VSC_SENSOR_NAME_MAX_LEN 16 + +/* command id */ +enum { + VSC_CMD_QUERY = 0, + VSC_CMD_DL_SET = 1, + VSC_CMD_DL_START = 2, + VSC_CMD_DL_CONT = 3, + VSC_CMD_DUMP_MEM = 4, + VSC_CMD_GET_CONT = 8, + VSC_CMD_CAM_BOOT = 10, +}; + +/* command ack token */ +enum { + VSC_TOKEN_BOOTLOADER_REQ = 1, + VSC_TOKEN_DUMP_RESP = 4, + VSC_TOKEN_ERROR = 7, +}; + +/* image type */ +enum { + VSC_IMG_BOOTLOADER_TYPE = 1, + VSC_IMG_CSI_EM7D_TYPE, + VSC_IMG_CSI_SEM_TYPE, + VSC_IMG_CSI_RUNTIME_TYPE, + VSC_IMG_ACE_VISION_TYPE, + VSC_IMG_ACE_CFG_TYPE, + VSC_IMG_SKU_CFG_TYPE, +}; + +/* image fragments */ +enum { + VSC_IMG_BOOTLOADER_FRAG, + VSC_IMG_CSI_SEM_FRAG, + VSC_IMG_CSI_RUNTIME_FRAG, + VSC_IMG_ACE_VISION_FRAG, + VSC_IMG_ACE_CFG_FRAG, + VSC_IMG_CSI_EM7D_FRAG, + VSC_IMG_SKU_CFG_FRAG, + VSC_IMG_FRAG_MAX +}; + +struct vsc_rom_cmd { + __le32 magic; + __u8 cmd_id; + union { + /* download start */ + struct { + __u8 img_type; + __le16 option; + __le32 img_len; + __le32 img_loc; + __le32 crc; + DECLARE_FLEX_ARRAY(__u8, res); + } __packed dl_start; + /* download set */ + struct { + __u8 option; + __le16 img_cnt; + DECLARE_FLEX_ARRAY(__le32, payload); + } __packed dl_set; + /* download continue */ + struct { + __u8 end_flag; + __le16 len; + /* 8 is the offset of payload */ + __u8 payload[VSC_ROM_PKG_SIZE - 8]; + } __packed dl_cont; + /* dump memory */ + struct { + __u8 res; + __le16 len; + __le32 addr; + DECLARE_FLEX_ARRAY(__u8, payload); + } __packed dump_mem; + /* 5 is the offset of padding */ + __u8 padding[VSC_ROM_PKG_SIZE - 5]; + } data; +}; + +struct vsc_rom_cmd_ack { + __le32 magic; + __u8 token; + __u8 type; + __u8 res[2]; + __u8 payload[]; +}; + +struct vsc_fw_cmd { + __le32 magic; + __u8 cmd_id; + union { + struct { + __le16 option; + __u8 img_type; + __le32 img_len; + __le32 img_loc; + __le32 crc; + DECLARE_FLEX_ARRAY(__u8, res); + } __packed dl_start; + struct { + __le16 option; + __u8 img_cnt; + DECLARE_FLEX_ARRAY(__le32, payload); + } __packed dl_set; + struct { + __le32 addr; + __u8 len; + DECLARE_FLEX_ARRAY(__u8, payload); + } __packed dump_mem; + struct { + __u8 resv[3]; + __le32 crc; + DECLARE_FLEX_ARRAY(__u8, payload); + } __packed boot; + /* 5 is the offset of padding */ + __u8 padding[VSC_FW_PKG_SIZE - 5]; + } data; +}; + +struct vsc_img { + __le32 magic; + __le32 option; + __le32 image_count; + __le32 image_location[VSC_IMG_CNT_MAX]; +}; + +struct vsc_fw_sign { + __le32 magic; + __le32 image_size; + __u8 image[]; +}; + +struct vsc_image_code_data { + /* fragment index */ + u8 frag_index; + /* image type */ + u8 image_type; +}; + +struct vsc_img_frag { + u8 type; + u32 location; + const u8 *data; + u32 size; +}; + +/** + * struct vsc_fw_loader - represent vsc firmware loader + * @dev: device used to request fimware + * @tp: transport layer used with the firmware loader + * @csi: CSI image + * @ace: ACE image + * @cfg: config image + * @tx_buf: tx buffer + * @rx_buf: rx buffer + * @option: command option + * @count: total image count + * @sensor_name: camera sensor name + * @frags: image fragments + */ +struct vsc_fw_loader { + struct device *dev; + struct vsc_tp *tp; + + const struct firmware *csi; + const struct firmware *ace; + const struct firmware *cfg; + + void *tx_buf; + void *rx_buf; + + u16 option; + u16 count; + + char sensor_name[VSC_SENSOR_NAME_MAX_LEN]; + + struct vsc_img_frag frags[VSC_IMG_FRAG_MAX]; +}; + +static inline u32 vsc_sum_crc(void *data, size_t size) +{ + u32 crc = 0; + size_t i; + + for (i = 0; i < size; i++) + crc += *((u8 *)data + i); + + return crc; +} + +/* get sensor name to construct image name */ +static int vsc_get_sensor_name(struct vsc_fw_loader *fw_loader, + struct device *dev) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER }; + union acpi_object obj = { + .type = ACPI_TYPE_INTEGER, + .integer.value = 1, + }; + struct acpi_object_list arg_list = { + .count = 1, + .pointer = &obj, + }; + union acpi_object *ret_obj; + acpi_handle handle; + acpi_status status; + int ret = 0; + + handle = ACPI_HANDLE(dev); + if (!handle) + return -EINVAL; + + status = acpi_evaluate_object(handle, "SID", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "can't evaluate SID method: %d\n", status); + return -ENODEV; + } + + ret_obj = buffer.pointer; + if (!ret_obj) { + dev_err(dev, "can't locate ACPI buffer\n"); + return -ENODEV; + } + + if (ret_obj->type != ACPI_TYPE_STRING) { + dev_err(dev, "found non-string entry\n"); + ret = -ENODEV; + goto out_free_buff; + } + + /* string length excludes trailing NUL */ + if (ret_obj->string.length >= sizeof(fw_loader->sensor_name)) { + dev_err(dev, "sensor name buffer too small\n"); + ret = -EINVAL; + goto out_free_buff; + } + + memcpy(fw_loader->sensor_name, ret_obj->string.pointer, + ret_obj->string.length); + + string_lower(fw_loader->sensor_name, fw_loader->sensor_name); + +out_free_buff: + ACPI_FREE(buffer.pointer); + + return ret; +} + +static int vsc_identify_silicon(struct vsc_fw_loader *fw_loader) +{ + struct vsc_rom_cmd_ack *ack = fw_loader->rx_buf; + struct vsc_rom_cmd *cmd = fw_loader->tx_buf; + u8 version, sub_version; + int ret; + + /* identify stepping information */ + cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); + cmd->cmd_id = VSC_CMD_DUMP_MEM; + cmd->data.dump_mem.addr = cpu_to_le32(VSC_EFUSE_ADDR); + cmd->data.dump_mem.len = cpu_to_le16(sizeof(__le32)); + ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, ack, VSC_ROM_PKG_SIZE); + if (ret) + return ret; + if (ack->token == VSC_TOKEN_ERROR) + return -EINVAL; + + cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); + cmd->cmd_id = VSC_CMD_GET_CONT; + ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, ack, VSC_ROM_PKG_SIZE); + if (ret) + return ret; + if (ack->token != VSC_TOKEN_DUMP_RESP) + return -EINVAL; + + version = FIELD_GET(VSC_MAINSTEPPING_VERSION_MASK, ack->payload[0]); + sub_version = FIELD_GET(VSC_SUBSTEPPING_VERSION_MASK, ack->payload[0]); + + if (version != VSC_MAINSTEPPING_VERSION_A) + return -EINVAL; + + if (sub_version != VSC_SUBSTEPPING_VERSION_0 && + sub_version != VSC_SUBSTEPPING_VERSION_1) + return -EINVAL; + + dev_info(fw_loader->dev, "silicon stepping version is %u:%u\n", + version, sub_version); + + /* identify strap information */ + cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); + cmd->cmd_id = VSC_CMD_DUMP_MEM; + cmd->data.dump_mem.addr = cpu_to_le32(VSC_STRAP_ADDR); + cmd->data.dump_mem.len = cpu_to_le16(sizeof(__le32)); + ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, ack, VSC_ROM_PKG_SIZE); + if (ret) + return ret; + if (ack->token == VSC_TOKEN_ERROR) + return -EINVAL; + + cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); + cmd->cmd_id = VSC_CMD_GET_CONT; + ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, ack, VSC_ROM_PKG_SIZE); + if (ret) + return ret; + if (ack->token != VSC_TOKEN_DUMP_RESP) + return -EINVAL; + + return 0; +} + +static int vsc_identify_csi_image(struct vsc_fw_loader *fw_loader) +{ + const struct firmware *image; + struct vsc_fw_sign *sign; + struct vsc_img *img; + unsigned int i; + int ret; + + ret = request_firmware(&image, VSC_CSI_IMAGE_NAME, fw_loader->dev); + if (ret) + return ret; + + img = (struct vsc_img *)image->data; + if (!img) { + ret = -ENOENT; + goto err_release_image; + } + + if (le32_to_cpu(img->magic) != VSC_MAGIC_FILE) { + ret = -EINVAL; + goto err_release_image; + } + + if (le32_to_cpu(img->image_count) != VSC_CSI_IMG_CNT) { + ret = -EINVAL; + goto err_release_image; + } + fw_loader->count += le32_to_cpu(img->image_count) - 1; + + fw_loader->option = + FIELD_GET(VSC_BOOT_IMG_OPTION_MASK, le32_to_cpu(img->option)); + + sign = (struct vsc_fw_sign *) + (img->image_location + le32_to_cpu(img->image_count)); + + for (i = 0; i < VSC_CSI_IMG_CNT; i++) { + /* mapping from CSI image index to image code data */ + static const struct vsc_image_code_data csi_image_map[] = { + { VSC_IMG_BOOTLOADER_FRAG, VSC_IMG_BOOTLOADER_TYPE }, + { VSC_IMG_CSI_SEM_FRAG, VSC_IMG_CSI_SEM_TYPE }, + { VSC_IMG_CSI_RUNTIME_FRAG, VSC_IMG_CSI_RUNTIME_TYPE }, + { VSC_IMG_CSI_EM7D_FRAG, VSC_IMG_CSI_EM7D_TYPE }, + }; + struct vsc_img_frag *frag; + + if ((u8 *)sign + sizeof(*sign) > image->data + image->size) { + ret = -EINVAL; + goto err_release_image; + } + + if (le32_to_cpu(sign->magic) != VSC_MAGIC_FW) { + ret = -EINVAL; + goto err_release_image; + } + + if (!le32_to_cpu(img->image_location[i])) { + ret = -EINVAL; + goto err_release_image; + } + + frag = &fw_loader->frags[csi_image_map[i].frag_index]; + + frag->data = sign->image; + frag->size = le32_to_cpu(sign->image_size); + frag->location = le32_to_cpu(img->image_location[i]); + frag->type = csi_image_map[i].image_type; + + sign = (struct vsc_fw_sign *) + (sign->image + le32_to_cpu(sign->image_size)); + } + + fw_loader->csi = image; + + return 0; + +err_release_image: + release_firmware(image); + + return ret; +} + +static int vsc_identify_ace_image(struct vsc_fw_loader *fw_loader) +{ + char path[VSC_IMAGE_PATH_MAX_LEN]; + const struct firmware *image; + struct vsc_fw_sign *sign; + struct vsc_img *img; + unsigned int i; + int ret; + + snprintf(path, sizeof(path), VSC_ACE_IMAGE_NAME_FMT, + fw_loader->sensor_name); + + ret = request_firmware(&image, path, fw_loader->dev); + if (ret) + return ret; + + img = (struct vsc_img *)image->data; + if (!img) { + ret = -ENOENT; + goto err_release_image; + } + + if (le32_to_cpu(img->magic) != VSC_MAGIC_FILE) { + ret = -EINVAL; + goto err_release_image; + } + + if (le32_to_cpu(img->image_count) != VSC_ACE_IMG_CNT) { + ret = -EINVAL; + goto err_release_image; + } + fw_loader->count += le32_to_cpu(img->image_count); + + sign = (struct vsc_fw_sign *) + (img->image_location + le32_to_cpu(img->image_count)); + + for (i = 0; i < VSC_ACE_IMG_CNT; i++) { + /* mapping from ACE image index to image code data */ + static const struct vsc_image_code_data ace_image_map[] = { + { VSC_IMG_ACE_VISION_FRAG, VSC_IMG_ACE_VISION_TYPE }, + { VSC_IMG_ACE_CFG_FRAG, VSC_IMG_ACE_CFG_TYPE }, + }; + struct vsc_img_frag *frag, *last_frag; + u8 frag_index; + + if ((u8 *)sign + sizeof(*sign) > image->data + image->size) { + ret = -EINVAL; + goto err_release_image; + } + + if (le32_to_cpu(sign->magic) != VSC_MAGIC_FW) { + ret = -EINVAL; + goto err_release_image; + } + + frag_index = ace_image_map[i].frag_index; + frag = &fw_loader->frags[frag_index]; + + frag->data = sign->image; + frag->size = le32_to_cpu(sign->image_size); + frag->location = le32_to_cpu(img->image_location[i]); + frag->type = ace_image_map[i].image_type; + + if (!frag->location) { + last_frag = &fw_loader->frags[frag_index - 1]; + frag->location = + ALIGN(last_frag->location + last_frag->size, SZ_4K); + } + + sign = (struct vsc_fw_sign *) + (sign->image + le32_to_cpu(sign->image_size)); + } + + fw_loader->ace = image; + + return 0; + +err_release_image: + release_firmware(image); + + return ret; +} + +static int vsc_identify_cfg_image(struct vsc_fw_loader *fw_loader) +{ + struct vsc_img_frag *frag = &fw_loader->frags[VSC_IMG_SKU_CFG_FRAG]; + char path[VSC_IMAGE_PATH_MAX_LEN]; + const struct firmware *image; + u32 size; + int ret; + + snprintf(path, sizeof(path), VSC_CFG_IMAGE_NAME_FMT, + fw_loader->sensor_name); + + ret = request_firmware(&image, path, fw_loader->dev); + if (ret) + return ret; + + /* identify image size */ + if (image->size <= sizeof(u32) || image->size > VSC_SKU_MAX_SIZE) { + ret = -EINVAL; + goto err_release_image; + } + + size = le32_to_cpu(*((__le32 *)image->data)) + sizeof(u32); + if (image->size != size) { + ret = -EINVAL; + goto err_release_image; + } + + frag->data = image->data; + frag->size = image->size; + frag->type = VSC_IMG_SKU_CFG_TYPE; + frag->location = VSC_SKU_CFG_LOCATION; + + fw_loader->cfg = image; + + return 0; + +err_release_image: + release_firmware(image); + + return ret; +} + +static int vsc_download_bootloader(struct vsc_fw_loader *fw_loader) +{ + struct vsc_img_frag *frag = &fw_loader->frags[VSC_IMG_BOOTLOADER_FRAG]; + struct vsc_rom_cmd_ack *ack = fw_loader->rx_buf; + struct vsc_rom_cmd *cmd = fw_loader->tx_buf; + u32 len, c_len; + size_t remain; + const u8 *p; + int ret; + + cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); + cmd->cmd_id = VSC_CMD_QUERY; + ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, ack, VSC_ROM_PKG_SIZE); + if (ret) + return ret; + if (ack->token != VSC_TOKEN_DUMP_RESP && + ack->token != VSC_TOKEN_BOOTLOADER_REQ) + return -EINVAL; + + cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); + cmd->cmd_id = VSC_CMD_DL_START; + cmd->data.dl_start.option = cpu_to_le16(fw_loader->option); + cmd->data.dl_start.img_type = frag->type; + cmd->data.dl_start.img_len = cpu_to_le32(frag->size); + cmd->data.dl_start.img_loc = cpu_to_le32(frag->location); + + c_len = offsetof(struct vsc_rom_cmd, data.dl_start.crc); + cmd->data.dl_start.crc = cpu_to_le32(vsc_sum_crc(cmd, c_len)); + + ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, NULL, VSC_ROM_PKG_SIZE); + if (ret) + return ret; + + p = frag->data; + remain = frag->size; + + /* download image data */ + while (remain > 0) { + len = min(remain, sizeof(cmd->data.dl_cont.payload)); + + cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); + cmd->cmd_id = VSC_CMD_DL_CONT; + cmd->data.dl_cont.len = cpu_to_le16(len); + cmd->data.dl_cont.end_flag = remain == len; + memcpy(cmd->data.dl_cont.payload, p, len); + + ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, NULL, VSC_ROM_PKG_SIZE); + if (ret) + return ret; + + p += len; + remain -= len; + } + + return 0; +} + +static int vsc_download_firmware(struct vsc_fw_loader *fw_loader) +{ + struct vsc_fw_cmd *cmd = fw_loader->tx_buf; + unsigned int i, index = 0; + u32 c_len; + int ret; + + cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); + cmd->cmd_id = VSC_CMD_DL_SET; + cmd->data.dl_set.img_cnt = cpu_to_le16(fw_loader->count); + put_unaligned_le16(fw_loader->option, &cmd->data.dl_set.option); + + for (i = VSC_IMG_CSI_SEM_FRAG; i <= VSC_IMG_CSI_EM7D_FRAG; i++) { + struct vsc_img_frag *frag = &fw_loader->frags[i]; + + cmd->data.dl_set.payload[index++] = cpu_to_le32(frag->location); + cmd->data.dl_set.payload[index++] = cpu_to_le32(frag->size); + } + + c_len = offsetof(struct vsc_fw_cmd, data.dl_set.payload[index]); + cmd->data.dl_set.payload[index] = cpu_to_le32(vsc_sum_crc(cmd, c_len)); + + ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, NULL, VSC_FW_PKG_SIZE); + if (ret) + return ret; + + for (i = VSC_IMG_CSI_SEM_FRAG; i < VSC_IMG_FRAG_MAX; i++) { + struct vsc_img_frag *frag = &fw_loader->frags[i]; + const u8 *p; + u32 remain; + + cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); + cmd->cmd_id = VSC_CMD_DL_START; + cmd->data.dl_start.img_type = frag->type; + cmd->data.dl_start.img_len = cpu_to_le32(frag->size); + cmd->data.dl_start.img_loc = cpu_to_le32(frag->location); + put_unaligned_le16(fw_loader->option, &cmd->data.dl_start.option); + + c_len = offsetof(struct vsc_fw_cmd, data.dl_start.crc); + cmd->data.dl_start.crc = cpu_to_le32(vsc_sum_crc(cmd, c_len)); + + ret = vsc_tp_rom_xfer(fw_loader->tp, cmd, NULL, VSC_FW_PKG_SIZE); + if (ret) + return ret; + + p = frag->data; + remain = frag->size; + + /* download image data */ + while (remain > 0) { + u32 len = min(remain, VSC_FW_PKG_SIZE); + + memcpy(fw_loader->tx_buf, p, len); + memset(fw_loader->tx_buf + len, 0, VSC_FW_PKG_SIZE - len); + + ret = vsc_tp_rom_xfer(fw_loader->tp, fw_loader->tx_buf, + NULL, VSC_FW_PKG_SIZE); + if (ret) + break; + + p += len; + remain -= len; + } + } + + cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); + cmd->cmd_id = VSC_CMD_CAM_BOOT; + + c_len = offsetof(struct vsc_fw_cmd, data.dl_start.crc); + cmd->data.boot.crc = cpu_to_le32(vsc_sum_crc(cmd, c_len)); + + return vsc_tp_rom_xfer(fw_loader->tp, cmd, NULL, VSC_FW_PKG_SIZE); +} + +/** + * vsc_tp_init - init vsc_tp + * @tp: vsc_tp device handle + * @dev: device node for mei vsc device + * Return: 0 in case of success, negative value in case of error + */ +int vsc_tp_init(struct vsc_tp *tp, struct device *dev) +{ + struct vsc_fw_loader *fw_loader __free(kfree) = NULL; + void *tx_buf __free(kfree) = NULL; + void *rx_buf __free(kfree) = NULL; + int ret; + + fw_loader = kzalloc(sizeof(*fw_loader), GFP_KERNEL); + if (!fw_loader) + return -ENOMEM; + + tx_buf = kzalloc(VSC_FW_PKG_SIZE, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + + rx_buf = kzalloc(VSC_FW_PKG_SIZE, GFP_KERNEL); + if (!rx_buf) + return -ENOMEM; + + fw_loader->tx_buf = tx_buf; + fw_loader->rx_buf = rx_buf; + + fw_loader->tp = tp; + fw_loader->dev = dev; + + ret = vsc_get_sensor_name(fw_loader, dev); + if (ret) + return ret; + + ret = vsc_identify_silicon(fw_loader); + if (ret) + return ret; + + ret = vsc_identify_csi_image(fw_loader); + if (ret) + return ret; + + ret = vsc_identify_ace_image(fw_loader); + if (ret) + goto err_release_csi; + + ret = vsc_identify_cfg_image(fw_loader); + if (ret) + goto err_release_ace; + + ret = vsc_download_bootloader(fw_loader); + if (!ret) + ret = vsc_download_firmware(fw_loader); + + release_firmware(fw_loader->cfg); + +err_release_ace: + release_firmware(fw_loader->ace); + +err_release_csi: + release_firmware(fw_loader->csi); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(vsc_tp_init, VSC_TP); diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c new file mode 100644 index 000000000000..6f4a4be6ccb5 --- /dev/null +++ b/drivers/misc/mei/vsc-tp.c @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, Intel Corporation. + * Intel Visual Sensing Controller Transport Layer Linux driver + */ + +#include <linux/acpi.h> +#include <linux/cleanup.h> +#include <linux/crc32.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/irq.h> +#include <linux/irqreturn.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/types.h> + +#include "vsc-tp.h" + +#define VSC_TP_RESET_PIN_TOGGLE_INTERVAL_MS 20 +#define VSC_TP_ROM_BOOTUP_DELAY_MS 10 +#define VSC_TP_ROM_XFER_POLL_TIMEOUT_US (500 * USEC_PER_MSEC) +#define VSC_TP_ROM_XFER_POLL_DELAY_US (20 * USEC_PER_MSEC) +#define VSC_TP_WAIT_FW_ASSERTED_TIMEOUT (2 * HZ) +#define VSC_TP_MAX_XFER_COUNT 5 + +#define VSC_TP_PACKET_SYNC 0x31 +#define VSC_TP_CRC_SIZE sizeof(u32) +#define VSC_TP_MAX_MSG_SIZE 2048 +/* SPI xfer timeout size */ +#define VSC_TP_XFER_TIMEOUT_BYTES 700 +#define VSC_TP_PACKET_PADDING_SIZE 1 +#define VSC_TP_PACKET_SIZE(pkt) \ + (sizeof(struct vsc_tp_packet) + le16_to_cpu((pkt)->len) + VSC_TP_CRC_SIZE) +#define VSC_TP_MAX_PACKET_SIZE \ + (sizeof(struct vsc_tp_packet) + VSC_TP_MAX_MSG_SIZE + VSC_TP_CRC_SIZE) +#define VSC_TP_MAX_XFER_SIZE \ + (VSC_TP_MAX_PACKET_SIZE + VSC_TP_XFER_TIMEOUT_BYTES) +#define VSC_TP_NEXT_XFER_LEN(len, offset) \ + (len + sizeof(struct vsc_tp_packet) + VSC_TP_CRC_SIZE - offset + VSC_TP_PACKET_PADDING_SIZE) + +struct vsc_tp_packet { + __u8 sync; + __u8 cmd; + __le16 len; + __le32 seq; + __u8 buf[] __counted_by(len); +}; + +struct vsc_tp { + /* do the actual data transfer */ + struct spi_device *spi; + + /* bind with mei framework */ + struct platform_device *pdev; + + struct gpio_desc *wakeuphost; + struct gpio_desc *resetfw; + struct gpio_desc *wakeupfw; + + /* command sequence number */ + u32 seq; + + /* command buffer */ + void *tx_buf; + void *rx_buf; + + atomic_t assert_cnt; + wait_queue_head_t xfer_wait; + + vsc_tp_event_cb_t event_notify; + void *event_notify_context; + + /* used to protect command download */ + struct mutex mutex; +}; + +/* GPIO resources */ +static const struct acpi_gpio_params wakeuphost_gpio = { 0, 0, false }; +static const struct acpi_gpio_params wakeuphostint_gpio = { 1, 0, false }; +static const struct acpi_gpio_params resetfw_gpio = { 2, 0, false }; +static const struct acpi_gpio_params wakeupfw = { 3, 0, false }; + +static const struct acpi_gpio_mapping vsc_tp_acpi_gpios[] = { + { "wakeuphost-gpios", &wakeuphost_gpio, 1 }, + { "wakeuphostint-gpios", &wakeuphostint_gpio, 1 }, + { "resetfw-gpios", &resetfw_gpio, 1 }, + { "wakeupfw-gpios", &wakeupfw, 1 }, + {} +}; + +/* wakeup firmware and wait for response */ +static int vsc_tp_wakeup_request(struct vsc_tp *tp) +{ + int ret; + + gpiod_set_value_cansleep(tp->wakeupfw, 0); + + ret = wait_event_timeout(tp->xfer_wait, + atomic_read(&tp->assert_cnt) && + gpiod_get_value_cansleep(tp->wakeuphost), + VSC_TP_WAIT_FW_ASSERTED_TIMEOUT); + if (!ret) + return -ETIMEDOUT; + + return 0; +} + +static void vsc_tp_wakeup_release(struct vsc_tp *tp) +{ + atomic_dec_if_positive(&tp->assert_cnt); + + gpiod_set_value_cansleep(tp->wakeupfw, 1); +} + +static int vsc_tp_dev_xfer(struct vsc_tp *tp, void *obuf, void *ibuf, size_t len) +{ + struct spi_message msg = { 0 }; + struct spi_transfer xfer = { + .tx_buf = obuf, + .rx_buf = ibuf, + .len = len, + }; + + spi_message_init_with_transfers(&msg, &xfer, 1); + + return spi_sync_locked(tp->spi, &msg); +} + +static int vsc_tp_xfer_helper(struct vsc_tp *tp, struct vsc_tp_packet *pkt, + void *ibuf, u16 ilen) +{ + int ret, offset = 0, cpy_len, src_len, dst_len = sizeof(struct vsc_tp_packet); + int next_xfer_len = VSC_TP_PACKET_SIZE(pkt) + VSC_TP_XFER_TIMEOUT_BYTES; + u8 *src, *crc_src, *rx_buf = tp->rx_buf; + int count_down = VSC_TP_MAX_XFER_COUNT; + u32 recv_crc = 0, crc = ~0; + struct vsc_tp_packet ack; + u8 *dst = (u8 *)&ack; + bool synced = false; + + do { + ret = vsc_tp_dev_xfer(tp, pkt, rx_buf, next_xfer_len); + if (ret) + return ret; + memset(pkt, 0, VSC_TP_MAX_XFER_SIZE); + + if (synced) { + src = rx_buf; + src_len = next_xfer_len; + } else { + src = memchr(rx_buf, VSC_TP_PACKET_SYNC, next_xfer_len); + if (!src) + continue; + synced = true; + src_len = next_xfer_len - (src - rx_buf); + } + + /* traverse received data */ + while (src_len > 0) { + cpy_len = min(src_len, dst_len); + memcpy(dst, src, cpy_len); + crc_src = src; + src += cpy_len; + src_len -= cpy_len; + dst += cpy_len; + dst_len -= cpy_len; + + if (offset < sizeof(ack)) { + offset += cpy_len; + crc = crc32(crc, crc_src, cpy_len); + + if (!src_len) + continue; + + if (le16_to_cpu(ack.len)) { + dst = ibuf; + dst_len = min(ilen, le16_to_cpu(ack.len)); + } else { + dst = (u8 *)&recv_crc; + dst_len = sizeof(recv_crc); + } + } else if (offset < sizeof(ack) + le16_to_cpu(ack.len)) { + offset += cpy_len; + crc = crc32(crc, crc_src, cpy_len); + + if (src_len) { + int remain = sizeof(ack) + le16_to_cpu(ack.len) - offset; + + cpy_len = min(src_len, remain); + offset += cpy_len; + crc = crc32(crc, src, cpy_len); + src += cpy_len; + src_len -= cpy_len; + if (src_len) { + dst = (u8 *)&recv_crc; + dst_len = sizeof(recv_crc); + continue; + } + } + next_xfer_len = VSC_TP_NEXT_XFER_LEN(le16_to_cpu(ack.len), offset); + } else if (offset < sizeof(ack) + le16_to_cpu(ack.len) + VSC_TP_CRC_SIZE) { + offset += cpy_len; + + if (src_len) { + /* terminate the traverse */ + next_xfer_len = 0; + break; + } + next_xfer_len = VSC_TP_NEXT_XFER_LEN(le16_to_cpu(ack.len), offset); + } + } + } while (next_xfer_len > 0 && --count_down); + + if (next_xfer_len > 0) + return -EAGAIN; + + if (~recv_crc != crc || le32_to_cpu(ack.seq) != tp->seq) { + dev_err(&tp->spi->dev, "recv crc or seq error\n"); + return -EINVAL; + } + + if (ack.cmd == VSC_TP_CMD_ACK || ack.cmd == VSC_TP_CMD_NACK || + ack.cmd == VSC_TP_CMD_BUSY) { + dev_err(&tp->spi->dev, "recv cmd ack error\n"); + return -EAGAIN; + } + + return min(le16_to_cpu(ack.len), ilen); +} + +/** + * vsc_tp_xfer - transfer data to firmware + * @tp: vsc_tp device handle + * @cmd: the command to be sent to the device + * @obuf: the tx buffer to be sent to the device + * @olen: the length of tx buffer + * @ibuf: the rx buffer to receive from the device + * @ilen: the length of rx buffer + * Return: the length of received data in case of success, + * otherwise negative value + */ +int vsc_tp_xfer(struct vsc_tp *tp, u8 cmd, const void *obuf, size_t olen, + void *ibuf, size_t ilen) +{ + struct vsc_tp_packet *pkt = tp->tx_buf; + u32 crc; + int ret; + + if (!obuf || !ibuf || olen > VSC_TP_MAX_MSG_SIZE) + return -EINVAL; + + guard(mutex)(&tp->mutex); + + pkt->sync = VSC_TP_PACKET_SYNC; + pkt->cmd = cmd; + pkt->len = cpu_to_le16(olen); + pkt->seq = cpu_to_le32(++tp->seq); + memcpy(pkt->buf, obuf, olen); + + crc = ~crc32(~0, (u8 *)pkt, sizeof(pkt) + olen); + memcpy(pkt->buf + olen, &crc, sizeof(crc)); + + ret = vsc_tp_wakeup_request(tp); + if (unlikely(ret)) + dev_err(&tp->spi->dev, "wakeup firmware failed ret: %d\n", ret); + else + ret = vsc_tp_xfer_helper(tp, pkt, ibuf, ilen); + + vsc_tp_wakeup_release(tp); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(vsc_tp_xfer, VSC_TP); + +/** + * vsc_tp_rom_xfer - transfer data to rom code + * @tp: vsc_tp device handle + * @obuf: the data buffer to be sent to the device + * @ibuf: the buffer to receive data from the device + * @len: the length of tx buffer and rx buffer + * Return: 0 in case of success, negative value in case of error + */ +int vsc_tp_rom_xfer(struct vsc_tp *tp, const void *obuf, void *ibuf, size_t len) +{ + size_t words = len / sizeof(__be32); + int ret; + + if (len % sizeof(__be32) || len > VSC_TP_MAX_MSG_SIZE) + return -EINVAL; + + guard(mutex)(&tp->mutex); + + /* rom xfer is big endian */ + cpu_to_be32_array(tp->tx_buf, obuf, words); + + ret = read_poll_timeout(gpiod_get_value_cansleep, ret, + !ret, VSC_TP_ROM_XFER_POLL_DELAY_US, + VSC_TP_ROM_XFER_POLL_TIMEOUT_US, false, + tp->wakeuphost); + if (ret) { + dev_err(&tp->spi->dev, "wait rom failed ret: %d\n", ret); + return ret; + } + + ret = vsc_tp_dev_xfer(tp, tp->tx_buf, tp->rx_buf, len); + if (ret) + return ret; + + if (ibuf) + cpu_to_be32_array(ibuf, tp->rx_buf, words); + + return ret; +} + +/** + * vsc_tp_reset - reset vsc transport layer + * @tp: vsc_tp device handle + */ +void vsc_tp_reset(struct vsc_tp *tp) +{ + disable_irq(tp->spi->irq); + + /* toggle reset pin */ + gpiod_set_value_cansleep(tp->resetfw, 0); + msleep(VSC_TP_RESET_PIN_TOGGLE_INTERVAL_MS); + gpiod_set_value_cansleep(tp->resetfw, 1); + + /* wait for ROM */ + msleep(VSC_TP_ROM_BOOTUP_DELAY_MS); + + /* + * Set default host wakeup pin to non-active + * to avoid unexpected host irq interrupt. + */ + gpiod_set_value_cansleep(tp->wakeupfw, 1); + + atomic_set(&tp->assert_cnt, 0); + + enable_irq(tp->spi->irq); +} +EXPORT_SYMBOL_NS_GPL(vsc_tp_reset, VSC_TP); + +/** + * vsc_tp_need_read - check if device has data to sent + * @tp: vsc_tp device handle + * Return: true if device has data to sent, otherwise false + */ +bool vsc_tp_need_read(struct vsc_tp *tp) +{ + if (!atomic_read(&tp->assert_cnt)) + return false; + if (!gpiod_get_value_cansleep(tp->wakeuphost)) + return false; + if (!gpiod_get_value_cansleep(tp->wakeupfw)) + return false; + + return true; +} +EXPORT_SYMBOL_NS_GPL(vsc_tp_need_read, VSC_TP); + +/** + * vsc_tp_register_event_cb - register a callback function to receive event + * @tp: vsc_tp device handle + * @event_cb: callback function + * @context: execution context of event callback + * Return: 0 in case of success, negative value in case of error + */ +int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb, + void *context) +{ + tp->event_notify = event_cb; + tp->event_notify_context = context; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(vsc_tp_register_event_cb, VSC_TP); + +/** + * vsc_tp_intr_synchronize - synchronize vsc_tp interrupt + * @tp: vsc_tp device handle + */ +void vsc_tp_intr_synchronize(struct vsc_tp *tp) +{ + synchronize_irq(tp->spi->irq); +} +EXPORT_SYMBOL_NS_GPL(vsc_tp_intr_synchronize, VSC_TP); + +/** + * vsc_tp_intr_enable - enable vsc_tp interrupt + * @tp: vsc_tp device handle + */ +void vsc_tp_intr_enable(struct vsc_tp *tp) +{ + enable_irq(tp->spi->irq); +} +EXPORT_SYMBOL_NS_GPL(vsc_tp_intr_enable, VSC_TP); + +/** + * vsc_tp_intr_disable - disable vsc_tp interrupt + * @tp: vsc_tp device handle + */ +void vsc_tp_intr_disable(struct vsc_tp *tp) +{ + disable_irq(tp->spi->irq); +} +EXPORT_SYMBOL_NS_GPL(vsc_tp_intr_disable, VSC_TP); + +static irqreturn_t vsc_tp_isr(int irq, void *data) +{ + struct vsc_tp *tp = data; + + atomic_inc(&tp->assert_cnt); + + wake_up(&tp->xfer_wait); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t vsc_tp_thread_isr(int irq, void *data) +{ + struct vsc_tp *tp = data; + + if (tp->event_notify) + tp->event_notify(tp->event_notify_context); + + return IRQ_HANDLED; +} + +static int vsc_tp_match_any(struct acpi_device *adev, void *data) +{ + struct acpi_device **__adev = data; + + *__adev = adev; + + return 1; +} + +static int vsc_tp_probe(struct spi_device *spi) +{ + struct platform_device_info pinfo = { 0 }; + struct device *dev = &spi->dev; + struct platform_device *pdev; + struct acpi_device *adev; + struct vsc_tp *tp; + int ret; + + tp = devm_kzalloc(dev, sizeof(*tp), GFP_KERNEL); + if (!tp) + return -ENOMEM; + + tp->tx_buf = devm_kzalloc(dev, VSC_TP_MAX_XFER_SIZE, GFP_KERNEL); + if (!tp->tx_buf) + return -ENOMEM; + + tp->rx_buf = devm_kzalloc(dev, VSC_TP_MAX_XFER_SIZE, GFP_KERNEL); + if (!tp->rx_buf) + return -ENOMEM; + + ret = devm_acpi_dev_add_driver_gpios(dev, vsc_tp_acpi_gpios); + if (ret) + return ret; + + tp->wakeuphost = devm_gpiod_get(dev, "wakeuphost", GPIOD_IN); + if (IS_ERR(tp->wakeuphost)) + return PTR_ERR(tp->wakeuphost); + + tp->resetfw = devm_gpiod_get(dev, "resetfw", GPIOD_OUT_HIGH); + if (IS_ERR(tp->resetfw)) + return PTR_ERR(tp->resetfw); + + tp->wakeupfw = devm_gpiod_get(dev, "wakeupfw", GPIOD_OUT_HIGH); + if (IS_ERR(tp->wakeupfw)) + return PTR_ERR(tp->wakeupfw); + + atomic_set(&tp->assert_cnt, 0); + init_waitqueue_head(&tp->xfer_wait); + tp->spi = spi; + + irq_set_status_flags(spi->irq, IRQ_DISABLE_UNLAZY); + ret = devm_request_threaded_irq(dev, spi->irq, vsc_tp_isr, + vsc_tp_thread_isr, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(dev), tp); + if (ret) + return ret; + + mutex_init(&tp->mutex); + + /* only one child acpi device */ + ret = acpi_dev_for_each_child(ACPI_COMPANION(dev), + vsc_tp_match_any, &adev); + if (!ret) { + ret = -ENODEV; + goto err_destroy_lock; + } + pinfo.fwnode = acpi_fwnode_handle(adev); + + pinfo.name = "intel_vsc"; + pinfo.data = &tp; + pinfo.size_data = sizeof(tp); + pinfo.id = PLATFORM_DEVID_NONE; + + pdev = platform_device_register_full(&pinfo); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); + goto err_destroy_lock; + } + + tp->pdev = pdev; + spi_set_drvdata(spi, tp); + + return 0; + +err_destroy_lock: + mutex_destroy(&tp->mutex); + + return ret; +} + +static void vsc_tp_remove(struct spi_device *spi) +{ + struct vsc_tp *tp = spi_get_drvdata(spi); + + platform_device_unregister(tp->pdev); + + mutex_destroy(&tp->mutex); +} + +static const struct acpi_device_id vsc_tp_acpi_ids[] = { + { "INTC1009" }, /* Raptor Lake */ + { "INTC1058" }, /* Tiger Lake */ + { "INTC1094" }, /* Alder Lake */ + {} +}; +MODULE_DEVICE_TABLE(acpi, vsc_tp_acpi_ids); + +static struct spi_driver vsc_tp_driver = { + .probe = vsc_tp_probe, + .remove = vsc_tp_remove, + .driver = { + .name = "vsc-tp", + .acpi_match_table = vsc_tp_acpi_ids, + }, +}; +module_spi_driver(vsc_tp_driver); + +MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>"); +MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>"); +MODULE_DESCRIPTION("Intel Visual Sensing Controller Transport Layer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/mei/vsc-tp.h b/drivers/misc/mei/vsc-tp.h new file mode 100644 index 000000000000..f9513ddc3e40 --- /dev/null +++ b/drivers/misc/mei/vsc-tp.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2023, Intel Corporation. + * Intel Visual Sensing Controller Transport Layer Linux driver + */ + +#ifndef _VSC_TP_H_ +#define _VSC_TP_H_ + +#include <linux/types.h> + +#define VSC_TP_CMD_WRITE 0x01 +#define VSC_TP_CMD_READ 0x02 + +#define VSC_TP_CMD_ACK 0x10 +#define VSC_TP_CMD_NACK 0x11 +#define VSC_TP_CMD_BUSY 0x12 + +struct vsc_tp; + +/** + * typedef vsc_event_cb_t - event callback function signature + * @context: the execution context of who registered this callback + * + * The callback function is called in interrupt context and the data + * payload is only valid during the call. If the user needs access + * the data payload later, it must copy the payload. + */ +typedef void (*vsc_tp_event_cb_t)(void *context); + +int vsc_tp_rom_xfer(struct vsc_tp *tp, const void *obuf, void *ibuf, + size_t len); + +int vsc_tp_xfer(struct vsc_tp *tp, u8 cmd, const void *obuf, size_t olen, + void *ibuf, size_t ilen); + +int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb, + void *context); + +void vsc_tp_intr_enable(struct vsc_tp *tp); +void vsc_tp_intr_disable(struct vsc_tp *tp); +void vsc_tp_intr_synchronize(struct vsc_tp *tp); + +void vsc_tp_reset(struct vsc_tp *tp); + +bool vsc_tp_need_read(struct vsc_tp *tp); + +int vsc_tp_init(struct vsc_tp *tp, struct device *dev); + +#endif diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c new file mode 100644 index 000000000000..0eaa3b4484bd --- /dev/null +++ b/drivers/misc/nsm.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Amazon Nitro Secure Module driver. + * + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * The Nitro Secure Module implements commands via CBOR over virtio. + * This driver exposes a raw message ioctls on /dev/nsm that user + * space can use to issue these commands. + */ + +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/hw_random.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/uaccess.h> +#include <linux/uio.h> +#include <linux/virtio_config.h> +#include <linux/virtio_ids.h> +#include <linux/virtio.h> +#include <linux/wait.h> +#include <uapi/linux/nsm.h> + +/* Timeout for NSM virtqueue respose in milliseconds. */ +#define NSM_DEFAULT_TIMEOUT_MSECS (120000) /* 2 minutes */ + +/* Maximum length input data */ +struct nsm_data_req { + u32 len; + u8 data[NSM_REQUEST_MAX_SIZE]; +}; + +/* Maximum length output data */ +struct nsm_data_resp { + u32 len; + u8 data[NSM_RESPONSE_MAX_SIZE]; +}; + +/* Full NSM request/response message */ +struct nsm_msg { + struct nsm_data_req req; + struct nsm_data_resp resp; +}; + +struct nsm { + struct virtio_device *vdev; + struct virtqueue *vq; + struct mutex lock; + struct completion cmd_done; + struct miscdevice misc; + struct hwrng hwrng; + struct work_struct misc_init; + struct nsm_msg msg; +}; + +/* NSM device ID */ +static const struct virtio_device_id id_table[] = { + { VIRTIO_ID_NITRO_SEC_MOD, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct nsm *file_to_nsm(struct file *file) +{ + return container_of(file->private_data, struct nsm, misc); +} + +static struct nsm *hwrng_to_nsm(struct hwrng *rng) +{ + return container_of(rng, struct nsm, hwrng); +} + +#define CBOR_TYPE_MASK 0xE0 +#define CBOR_TYPE_MAP 0xA0 +#define CBOR_TYPE_TEXT 0x60 +#define CBOR_TYPE_ARRAY 0x40 +#define CBOR_HEADER_SIZE_SHORT 1 + +#define CBOR_SHORT_SIZE_MAX_VALUE 23 +#define CBOR_LONG_SIZE_U8 24 +#define CBOR_LONG_SIZE_U16 25 +#define CBOR_LONG_SIZE_U32 26 +#define CBOR_LONG_SIZE_U64 27 + +static bool cbor_object_is_array(const u8 *cbor_object, size_t cbor_object_size) +{ + if (cbor_object_size == 0 || cbor_object == NULL) + return false; + + return (cbor_object[0] & CBOR_TYPE_MASK) == CBOR_TYPE_ARRAY; +} + +static int cbor_object_get_array(u8 *cbor_object, size_t cbor_object_size, u8 **cbor_array) +{ + u8 cbor_short_size; + void *array_len_p; + u64 array_len; + u64 array_offset; + + if (!cbor_object_is_array(cbor_object, cbor_object_size)) + return -EFAULT; + + cbor_short_size = (cbor_object[0] & 0x1F); + + /* Decoding byte array length */ + array_offset = CBOR_HEADER_SIZE_SHORT; + if (cbor_short_size >= CBOR_LONG_SIZE_U8) + array_offset += BIT(cbor_short_size - CBOR_LONG_SIZE_U8); + + if (cbor_object_size < array_offset) + return -EFAULT; + + array_len_p = &cbor_object[1]; + + switch (cbor_short_size) { + case CBOR_SHORT_SIZE_MAX_VALUE: /* short encoding */ + array_len = cbor_short_size; + break; + case CBOR_LONG_SIZE_U8: + array_len = *(u8 *)array_len_p; + break; + case CBOR_LONG_SIZE_U16: + array_len = be16_to_cpup((__be16 *)array_len_p); + break; + case CBOR_LONG_SIZE_U32: + array_len = be32_to_cpup((__be32 *)array_len_p); + break; + case CBOR_LONG_SIZE_U64: + array_len = be64_to_cpup((__be64 *)array_len_p); + break; + } + + if (cbor_object_size < array_offset) + return -EFAULT; + + if (cbor_object_size - array_offset < array_len) + return -EFAULT; + + if (array_len > INT_MAX) + return -EFAULT; + + *cbor_array = cbor_object + array_offset; + return array_len; +} + +/* Copy the request of a raw message to kernel space */ +static int fill_req_raw(struct nsm *nsm, struct nsm_data_req *req, + struct nsm_raw *raw) +{ + /* Verify the user input size. */ + if (raw->request.len > sizeof(req->data)) + return -EMSGSIZE; + + /* Copy the request payload */ + if (copy_from_user(req->data, u64_to_user_ptr(raw->request.addr), + raw->request.len)) + return -EFAULT; + + req->len = raw->request.len; + + return 0; +} + +/* Copy the response of a raw message back to user-space */ +static int parse_resp_raw(struct nsm *nsm, struct nsm_data_resp *resp, + struct nsm_raw *raw) +{ + /* Truncate any message that does not fit. */ + raw->response.len = min_t(u64, raw->response.len, resp->len); + + /* Copy the response content to user space */ + if (copy_to_user(u64_to_user_ptr(raw->response.addr), + resp->data, raw->response.len)) + return -EFAULT; + + return 0; +} + +/* Virtqueue interrupt handler */ +static void nsm_vq_callback(struct virtqueue *vq) +{ + struct nsm *nsm = vq->vdev->priv; + + complete(&nsm->cmd_done); +} + +/* Forward a message to the NSM device and wait for the response from it */ +static int nsm_sendrecv_msg_locked(struct nsm *nsm) +{ + struct device *dev = &nsm->vdev->dev; + struct scatterlist sg_in, sg_out; + struct nsm_msg *msg = &nsm->msg; + struct virtqueue *vq = nsm->vq; + unsigned int len; + void *queue_buf; + bool kicked; + int rc; + + /* Initialize scatter-gather lists with request and response buffers. */ + sg_init_one(&sg_out, msg->req.data, msg->req.len); + sg_init_one(&sg_in, msg->resp.data, sizeof(msg->resp.data)); + + init_completion(&nsm->cmd_done); + /* Add the request buffer (read by the device). */ + rc = virtqueue_add_outbuf(vq, &sg_out, 1, msg->req.data, GFP_KERNEL); + if (rc) + return rc; + + /* Add the response buffer (written by the device). */ + rc = virtqueue_add_inbuf(vq, &sg_in, 1, msg->resp.data, GFP_KERNEL); + if (rc) + goto cleanup; + + kicked = virtqueue_kick(vq); + if (!kicked) { + /* Cannot kick the virtqueue. */ + rc = -EIO; + goto cleanup; + } + + /* If the kick succeeded, wait for the device's response. */ + if (!wait_for_completion_io_timeout(&nsm->cmd_done, + msecs_to_jiffies(NSM_DEFAULT_TIMEOUT_MSECS))) { + rc = -ETIMEDOUT; + goto cleanup; + } + + queue_buf = virtqueue_get_buf(vq, &len); + if (!queue_buf || (queue_buf != msg->req.data)) { + dev_err(dev, "wrong request buffer."); + rc = -ENODATA; + goto cleanup; + } + + queue_buf = virtqueue_get_buf(vq, &len); + if (!queue_buf || (queue_buf != msg->resp.data)) { + dev_err(dev, "wrong response buffer."); + rc = -ENODATA; + goto cleanup; + } + + msg->resp.len = len; + + rc = 0; + +cleanup: + if (rc) { + /* Clean the virtqueue. */ + while (virtqueue_get_buf(vq, &len) != NULL) + ; + } + + return rc; +} + +static int fill_req_get_random(struct nsm *nsm, struct nsm_data_req *req) +{ + /* + * 69 # text(9) + * 47657452616E646F6D # "GetRandom" + */ + const u8 request[] = { CBOR_TYPE_TEXT + strlen("GetRandom"), + 'G', 'e', 't', 'R', 'a', 'n', 'd', 'o', 'm' }; + + memcpy(req->data, request, sizeof(request)); + req->len = sizeof(request); + + return 0; +} + +static int parse_resp_get_random(struct nsm *nsm, struct nsm_data_resp *resp, + void *out, size_t max) +{ + /* + * A1 # map(1) + * 69 # text(9) - Name of field + * 47657452616E646F6D # "GetRandom" + * A1 # map(1) - The field itself + * 66 # text(6) + * 72616E646F6D # "random" + * # The rest of the response is random data + */ + const u8 response[] = { CBOR_TYPE_MAP + 1, + CBOR_TYPE_TEXT + strlen("GetRandom"), + 'G', 'e', 't', 'R', 'a', 'n', 'd', 'o', 'm', + CBOR_TYPE_MAP + 1, + CBOR_TYPE_TEXT + strlen("random"), + 'r', 'a', 'n', 'd', 'o', 'm' }; + struct device *dev = &nsm->vdev->dev; + u8 *rand_data = NULL; + u8 *resp_ptr = resp->data; + u64 resp_len = resp->len; + int rc; + + if ((resp->len < sizeof(response) + 1) || + (memcmp(resp_ptr, response, sizeof(response)) != 0)) { + dev_err(dev, "Invalid response for GetRandom"); + return -EFAULT; + } + + resp_ptr += sizeof(response); + resp_len -= sizeof(response); + + rc = cbor_object_get_array(resp_ptr, resp_len, &rand_data); + if (rc < 0) { + dev_err(dev, "GetRandom: Invalid CBOR encoding\n"); + return rc; + } + + rc = min_t(size_t, rc, max); + memcpy(out, rand_data, rc); + + return rc; +} + +/* + * HwRNG implementation + */ +static int nsm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + struct nsm *nsm = hwrng_to_nsm(rng); + struct device *dev = &nsm->vdev->dev; + int rc = 0; + + /* NSM always needs to wait for a response */ + if (!wait) + return 0; + + mutex_lock(&nsm->lock); + + rc = fill_req_get_random(nsm, &nsm->msg.req); + if (rc != 0) + goto out; + + rc = nsm_sendrecv_msg_locked(nsm); + if (rc != 0) + goto out; + + rc = parse_resp_get_random(nsm, &nsm->msg.resp, data, max); + if (rc < 0) + goto out; + + dev_dbg(dev, "RNG: returning rand bytes = %d", rc); +out: + mutex_unlock(&nsm->lock); + return rc; +} + +static long nsm_dev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = u64_to_user_ptr((u64)arg); + struct nsm *nsm = file_to_nsm(file); + struct nsm_raw raw; + int r = 0; + + if (cmd != NSM_IOCTL_RAW) + return -EINVAL; + + if (_IOC_SIZE(cmd) != sizeof(raw)) + return -EINVAL; + + /* Copy user argument struct to kernel argument struct */ + r = -EFAULT; + if (copy_from_user(&raw, argp, _IOC_SIZE(cmd))) + goto out; + + mutex_lock(&nsm->lock); + + /* Convert kernel argument struct to device request */ + r = fill_req_raw(nsm, &nsm->msg.req, &raw); + if (r) + goto out; + + /* Send message to NSM and read reply */ + r = nsm_sendrecv_msg_locked(nsm); + if (r) + goto out; + + /* Parse device response into kernel argument struct */ + r = parse_resp_raw(nsm, &nsm->msg.resp, &raw); + if (r) + goto out; + + /* Copy kernel argument struct back to user argument struct */ + r = -EFAULT; + if (copy_to_user(argp, &raw, sizeof(raw))) + goto out; + + r = 0; + +out: + mutex_unlock(&nsm->lock); + return r; +} + +static int nsm_device_init_vq(struct virtio_device *vdev) +{ + struct virtqueue *vq = virtio_find_single_vq(vdev, + nsm_vq_callback, "nsm.vq.0"); + struct nsm *nsm = vdev->priv; + + if (IS_ERR(vq)) + return PTR_ERR(vq); + + nsm->vq = vq; + + return 0; +} + +static const struct file_operations nsm_dev_fops = { + .unlocked_ioctl = nsm_dev_ioctl, + .compat_ioctl = compat_ptr_ioctl, +}; + +/* Handler for probing the NSM device */ +static int nsm_device_probe(struct virtio_device *vdev) +{ + struct device *dev = &vdev->dev; + struct nsm *nsm; + int rc; + + nsm = devm_kzalloc(&vdev->dev, sizeof(*nsm), GFP_KERNEL); + if (!nsm) + return -ENOMEM; + + vdev->priv = nsm; + nsm->vdev = vdev; + + rc = nsm_device_init_vq(vdev); + if (rc) { + dev_err(dev, "queue failed to initialize: %d.\n", rc); + goto err_init_vq; + } + + mutex_init(&nsm->lock); + + /* Register as hwrng provider */ + nsm->hwrng = (struct hwrng) { + .read = nsm_rng_read, + .name = "nsm-hwrng", + .quality = 1000, + }; + + rc = hwrng_register(&nsm->hwrng); + if (rc) { + dev_err(dev, "RNG initialization error: %d.\n", rc); + goto err_hwrng; + } + + /* Register /dev/nsm device node */ + nsm->misc = (struct miscdevice) { + .minor = MISC_DYNAMIC_MINOR, + .name = "nsm", + .fops = &nsm_dev_fops, + .mode = 0666, + }; + + rc = misc_register(&nsm->misc); + if (rc) { + dev_err(dev, "misc device registration error: %d.\n", rc); + goto err_misc; + } + + return 0; + +err_misc: + hwrng_unregister(&nsm->hwrng); +err_hwrng: + vdev->config->del_vqs(vdev); +err_init_vq: + return rc; +} + +/* Handler for removing the NSM device */ +static void nsm_device_remove(struct virtio_device *vdev) +{ + struct nsm *nsm = vdev->priv; + + hwrng_unregister(&nsm->hwrng); + + vdev->config->del_vqs(vdev); + misc_deregister(&nsm->misc); +} + +/* NSM device configuration structure */ +static struct virtio_driver virtio_nsm_driver = { + .feature_table = 0, + .feature_table_size = 0, + .feature_table_legacy = 0, + .feature_table_size_legacy = 0, + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = nsm_device_probe, + .remove = nsm_device_remove, +}; + +module_virtio_driver(virtio_nsm_driver); +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio NSM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/open-dice.c b/drivers/misc/open-dice.c index 8aea2d070a40..d279a4f195e2 100644 --- a/drivers/misc/open-dice.c +++ b/drivers/misc/open-dice.c @@ -140,7 +140,6 @@ static int __init open_dice_probe(struct platform_device *pdev) return -ENOMEM; *drvdata = (struct open_dice_drvdata){ - .lock = __MUTEX_INITIALIZER(drvdata->lock), .rmem = rmem, .misc = (struct miscdevice){ .parent = dev, @@ -150,6 +149,7 @@ static int __init open_dice_probe(struct platform_device *pdev) .mode = 0600, }, }; + mutex_init(&drvdata->lock); /* Index overflow check not needed, misc_register() will fail. */ snprintf(drvdata->name, sizeof(drvdata->name), DRIVER_NAME"%u", dev_idx++); diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index af519088732d..c38a6083f0a7 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -28,14 +28,14 @@ #define DRV_MODULE_NAME "pci-endpoint-test" #define IRQ_TYPE_UNDEFINED -1 -#define IRQ_TYPE_LEGACY 0 +#define IRQ_TYPE_INTX 0 #define IRQ_TYPE_MSI 1 #define IRQ_TYPE_MSIX 2 #define PCI_ENDPOINT_TEST_MAGIC 0x0 #define PCI_ENDPOINT_TEST_COMMAND 0x4 -#define COMMAND_RAISE_LEGACY_IRQ BIT(0) +#define COMMAND_RAISE_INTX_IRQ BIT(0) #define COMMAND_RAISE_MSI_IRQ BIT(1) #define COMMAND_RAISE_MSIX_IRQ BIT(2) #define COMMAND_READ BIT(3) @@ -183,8 +183,8 @@ static bool pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test, bool res = true; switch (type) { - case IRQ_TYPE_LEGACY: - irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY); + case IRQ_TYPE_INTX: + irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX); if (irq < 0) dev_err(dev, "Failed to get Legacy interrupt\n"); break; @@ -244,7 +244,7 @@ static bool pci_endpoint_test_request_irq(struct pci_endpoint_test *test) fail: switch (irq_type) { - case IRQ_TYPE_LEGACY: + case IRQ_TYPE_INTX: dev_err(dev, "Failed to request IRQ %d for Legacy\n", pci_irq_vector(pdev, i)); break; @@ -263,6 +263,15 @@ fail: return false; } +static const u32 bar_test_pattern[] = { + 0xA0A0A0A0, + 0xA1A1A1A1, + 0xA2A2A2A2, + 0xA3A3A3A3, + 0xA4A4A4A4, + 0xA5A5A5A5, +}; + static bool pci_endpoint_test_bar(struct pci_endpoint_test *test, enum pci_barno barno) { @@ -280,26 +289,27 @@ static bool pci_endpoint_test_bar(struct pci_endpoint_test *test, size = 0x4; for (j = 0; j < size; j += 4) - pci_endpoint_test_bar_writel(test, barno, j, 0xA0A0A0A0); + pci_endpoint_test_bar_writel(test, barno, j, + bar_test_pattern[barno]); for (j = 0; j < size; j += 4) { val = pci_endpoint_test_bar_readl(test, barno, j); - if (val != 0xA0A0A0A0) + if (val != bar_test_pattern[barno]) return false; } return true; } -static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test) +static bool pci_endpoint_test_intx_irq(struct pci_endpoint_test *test) { u32 val; pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, - IRQ_TYPE_LEGACY); + IRQ_TYPE_INTX); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 0); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, - COMMAND_RAISE_LEGACY_IRQ); + COMMAND_RAISE_INTX_IRQ); val = wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000)); if (!val) @@ -385,7 +395,7 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, if (use_dma) flags |= FLAG_USE_DMA; - if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) { + if (irq_type < IRQ_TYPE_INTX || irq_type > IRQ_TYPE_MSIX) { dev_err(dev, "Invalid IRQ type option\n"); goto err; } @@ -521,7 +531,7 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, if (use_dma) flags |= FLAG_USE_DMA; - if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) { + if (irq_type < IRQ_TYPE_INTX || irq_type > IRQ_TYPE_MSIX) { dev_err(dev, "Invalid IRQ type option\n"); goto err; } @@ -621,7 +631,7 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, if (use_dma) flags |= FLAG_USE_DMA; - if (irq_type < IRQ_TYPE_LEGACY || irq_type > IRQ_TYPE_MSIX) { + if (irq_type < IRQ_TYPE_INTX || irq_type > IRQ_TYPE_MSIX) { dev_err(dev, "Invalid IRQ type option\n"); goto err; } @@ -691,7 +701,7 @@ static bool pci_endpoint_test_set_irq(struct pci_endpoint_test *test, struct pci_dev *pdev = test->pdev; struct device *dev = &pdev->dev; - if (req_irq_type < IRQ_TYPE_LEGACY || req_irq_type > IRQ_TYPE_MSIX) { + if (req_irq_type < IRQ_TYPE_INTX || req_irq_type > IRQ_TYPE_MSIX) { dev_err(dev, "Invalid IRQ type option\n"); return false; } @@ -737,8 +747,8 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, goto ret; ret = pci_endpoint_test_bar(test, bar); break; - case PCITEST_LEGACY_IRQ: - ret = pci_endpoint_test_legacy_irq(test); + case PCITEST_INTX_IRQ: + ret = pci_endpoint_test_intx_irq(test); break; case PCITEST_MSI: case PCITEST_MSIX: @@ -801,7 +811,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, test->irq_type = IRQ_TYPE_UNDEFINED; if (no_msi) - irq_type = IRQ_TYPE_LEGACY; + irq_type = IRQ_TYPE_INTX; data = (struct pci_endpoint_test_data *)ent->driver_data; if (data) { @@ -860,7 +870,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, test); - id = ida_simple_get(&pci_endpoint_test_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&pci_endpoint_test_ida, GFP_KERNEL); if (id < 0) { err = id; dev_err(dev, "Unable to get id\n"); @@ -907,7 +917,7 @@ err_kfree_test_name: kfree(test->name); err_ida_remove: - ida_simple_remove(&pci_endpoint_test_ida, id); + ida_free(&pci_endpoint_test_ida, id); err_iounmap: for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { @@ -943,7 +953,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) misc_deregister(&test->miscdev); kfree(misc_device->name); kfree(test->name); - ida_simple_remove(&pci_endpoint_test_ida, id); + ida_free(&pci_endpoint_test_ida, id); for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { if (test->bar[bar]) pci_iounmap(pdev, test->bar[bar]); diff --git a/drivers/misc/pvpanic/pvpanic-mmio.c b/drivers/misc/pvpanic/pvpanic-mmio.c index 9715798acce3..f3f2113a54a7 100644 --- a/drivers/misc/pvpanic/pvpanic-mmio.c +++ b/drivers/misc/pvpanic/pvpanic-mmio.c @@ -7,16 +7,15 @@ * Copyright (C) 2021 Oracle. */ +#include <linux/device.h> +#include <linux/err.h> #include <linux/io.h> -#include <linux/kernel.h> +#include <linux/ioport.h> #include <linux/kexec.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/types.h> -#include <linux/slab.h> - -#include <uapi/misc/pvpanic.h> #include "pvpanic.h" diff --git a/drivers/misc/pvpanic/pvpanic-pci.c b/drivers/misc/pvpanic/pvpanic-pci.c index 689af4c28c2a..9ad20e82785b 100644 --- a/drivers/misc/pvpanic/pvpanic-pci.c +++ b/drivers/misc/pvpanic/pvpanic-pci.c @@ -5,17 +5,13 @@ * Copyright (C) 2021 Oracle. */ -#include <linux/kernel.h> +#include <linux/errno.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/types.h> -#include <linux/slab.h> - -#include <uapi/misc/pvpanic.h> #include "pvpanic.h" -#define PCI_VENDOR_ID_REDHAT 0x1b36 #define PCI_DEVICE_ID_REDHAT_PVPANIC 0x0011 MODULE_AUTHOR("Mihai Carabas <mihai.carabas@oracle.com>"); diff --git a/drivers/misc/pvpanic/pvpanic.c b/drivers/misc/pvpanic/pvpanic.c index 305b367e0ce3..df3457ce1cb1 100644 --- a/drivers/misc/pvpanic/pvpanic.c +++ b/drivers/misc/pvpanic/pvpanic.c @@ -8,16 +8,20 @@ */ #include <linux/device.h> +#include <linux/errno.h> +#include <linux/gfp_types.h> #include <linux/io.h> -#include <linux/kernel.h> #include <linux/kexec.h> +#include <linux/kstrtox.h> +#include <linux/limits.h> +#include <linux/list.h> #include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/platform_device.h> #include <linux/panic_notifier.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/sysfs.h> #include <linux/types.h> -#include <linux/cdev.h> -#include <linux/list.h> #include <uapi/misc/pvpanic.h> diff --git a/drivers/misc/pvpanic/pvpanic.h b/drivers/misc/pvpanic/pvpanic.h index 46ffb10438ad..a42fa760eed5 100644 --- a/drivers/misc/pvpanic/pvpanic.h +++ b/drivers/misc/pvpanic/pvpanic.h @@ -8,6 +8,11 @@ #ifndef PVPANIC_H_ #define PVPANIC_H_ +#include <linux/compiler_types.h> + +struct attribute_group; +struct device; + int devm_pvpanic_probe(struct device *dev, void __iomem *base); extern const struct attribute_group *pvpanic_dev_groups[]; diff --git a/drivers/misc/vmw_vmci/vmci_handle_array.c b/drivers/misc/vmw_vmci/vmci_handle_array.c index de7fee7ead1b..681b3500125a 100644 --- a/drivers/misc/vmw_vmci/vmci_handle_array.c +++ b/drivers/misc/vmw_vmci/vmci_handle_array.c @@ -8,12 +8,6 @@ #include <linux/slab.h> #include "vmci_handle_array.h" -static size_t handle_arr_calc_size(u32 capacity) -{ - return VMCI_HANDLE_ARRAY_HEADER_SIZE + - capacity * sizeof(struct vmci_handle); -} - struct vmci_handle_arr *vmci_handle_arr_create(u32 capacity, u32 max_capacity) { struct vmci_handle_arr *array; @@ -25,7 +19,7 @@ struct vmci_handle_arr *vmci_handle_arr_create(u32 capacity, u32 max_capacity) capacity = min((u32)VMCI_HANDLE_ARRAY_DEFAULT_CAPACITY, max_capacity); - array = kmalloc(handle_arr_calc_size(capacity), GFP_ATOMIC); + array = kmalloc(struct_size(array, entries, capacity), GFP_ATOMIC); if (!array) return NULL; @@ -51,8 +45,8 @@ int vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr, struct vmci_handle_arr *new_array; u32 capacity_bump = min(array->max_capacity - array->capacity, array->capacity); - size_t new_size = handle_arr_calc_size(array->capacity + - capacity_bump); + size_t new_size = struct_size(array, entries, + size_add(array->capacity, capacity_bump)); if (array->size >= array->max_capacity) return VMCI_ERROR_NO_MEM; diff --git a/drivers/misc/vmw_vmci/vmci_handle_array.h b/drivers/misc/vmw_vmci/vmci_handle_array.h index b0e6b1956014..27a38b97e8a8 100644 --- a/drivers/misc/vmw_vmci/vmci_handle_array.h +++ b/drivers/misc/vmw_vmci/vmci_handle_array.h @@ -20,14 +20,8 @@ struct vmci_handle_arr { struct vmci_handle entries[] __counted_by(capacity); }; -#define VMCI_HANDLE_ARRAY_HEADER_SIZE \ - offsetof(struct vmci_handle_arr, entries) /* Select a default capacity that results in a 64 byte sized array */ #define VMCI_HANDLE_ARRAY_DEFAULT_CAPACITY 6 -/* Make sure that the max array size can be expressed by a u32 */ -#define VMCI_HANDLE_ARRAY_MAX_CAPACITY \ - ((U32_MAX - VMCI_HANDLE_ARRAY_HEADER_SIZE - 1) / \ - sizeof(struct vmci_handle)) struct vmci_handle_arr *vmci_handle_arr_create(u32 capacity, u32 max_capacity); void vmci_handle_arr_destroy(struct vmci_handle_arr *array); diff --git a/drivers/mmc/core/sdio_uart.c b/drivers/mmc/core/sdio_uart.c index ef38dcd3a887..575ebbce378e 100644 --- a/drivers/mmc/core/sdio_uart.c +++ b/drivers/mmc/core/sdio_uart.c @@ -178,11 +178,9 @@ static inline void sdio_uart_release_func(struct sdio_uart_port *port) sdio_release_host(port->func); } -static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset) +static inline u8 sdio_in(struct sdio_uart_port *port, int offset) { - unsigned char c; - c = sdio_readb(port->func, port->regs_offset + offset, NULL); - return c; + return sdio_readb(port->func, port->regs_offset + offset, NULL); } static inline void sdio_out(struct sdio_uart_port *port, int offset, int value) @@ -192,8 +190,8 @@ static inline void sdio_out(struct sdio_uart_port *port, int offset, int value) static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port) { - unsigned char status; unsigned int ret; + u8 status; /* FIXME: What stops this losing the delta bits and breaking sdio_uart_check_modem_status ? */ @@ -354,15 +352,13 @@ static void sdio_uart_stop_rx(struct sdio_uart_port *port) sdio_out(port, UART_IER, port->ier); } -static void sdio_uart_receive_chars(struct sdio_uart_port *port, - unsigned int *status) +static void sdio_uart_receive_chars(struct sdio_uart_port *port, u8 *status) { - unsigned int ch, flag; int max_count = 256; do { - ch = sdio_in(port, UART_RX); - flag = TTY_NORMAL; + u8 ch = sdio_in(port, UART_RX); + u8 flag = TTY_NORMAL; port->icount.rx++; if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | @@ -449,8 +445,8 @@ static void sdio_uart_transmit_chars(struct sdio_uart_port *port) static void sdio_uart_check_modem_status(struct sdio_uart_port *port) { - int status; struct tty_struct *tty; + u8 status; status = sdio_in(port, UART_MSR); @@ -499,7 +495,7 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port) static void sdio_uart_irq(struct sdio_func *func) { struct sdio_uart_port *port = sdio_get_drvdata(func); - unsigned int iir, lsr; + u8 iir, lsr; /* * In a few places sdio_uart_irq() is called directly instead of @@ -795,7 +791,7 @@ static unsigned int sdio_uart_chars_in_buffer(struct tty_struct *tty) return kfifo_len(&port->xmit_fifo); } -static void sdio_uart_send_xchar(struct tty_struct *tty, char ch) +static void sdio_uart_send_xchar(struct tty_struct *tty, u8 ch) { struct sdio_uart_port *port = tty->driver_data; diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 2a2d949a9344..39f45c2b6de8 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -75,11 +75,15 @@ EXPORT_SYMBOL(mmc_gpio_set_cd_irq); int mmc_gpio_get_ro(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; + int cansleep; if (!ctx || !ctx->ro_gpio) return -ENOSYS; - return gpiod_get_value_cansleep(ctx->ro_gpio); + cansleep = gpiod_cansleep(ctx->ro_gpio); + return cansleep ? + gpiod_get_value_cansleep(ctx->ro_gpio) : + gpiod_get_value(ctx->ro_gpio); } EXPORT_SYMBOL(mmc_gpio_get_ro); diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 87d78432a1e0..7dfe7c4e0077 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -7,6 +7,7 @@ * Wei WANG <wei_wang@realsil.com.cn> */ +#include <linux/pci.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/highmem.h> @@ -947,7 +948,7 @@ static int sd_power_on(struct realtek_pci_sdmmc *host, unsigned char power_mode) /* send at least 74 clocks */ rtsx_pci_write_register(pcr, SD_BUS_STAT, SD_CLK_TOGGLE_EN, SD_CLK_TOGGLE_EN); - if (PCI_PID(pcr) == PID_5261) { + if ((PCI_PID(pcr) == PID_5261) || (PCI_PID(pcr) == PID_5264)) { /* * If test mode is set switch to SD Express mandatorily, * this is only for factory testing. @@ -1364,6 +1365,14 @@ static int sdmmc_init_sd_express(struct mmc_host *mmc, struct mmc_ios *ios) struct realtek_pci_sdmmc *host = mmc_priv(mmc); struct rtsx_pcr *pcr = host->pcr; + if (PCI_PID(pcr) == PID_5264) { + pcie_capability_clear_and_set_word(pcr->pci, PCI_EXP_LNKCTL2, + PCI_EXP_LNKCTL2_TLS, PCI_EXP_LNKCTL2_TLS_2_5GT); + pci_write_config_byte(pcr->pci, 0x80e, 0x02); + pcie_capability_clear_and_set_word(pcr->pci, PCI_EXP_LNKCTL2, + PCI_EXP_LNKCTL2_TLS, PCI_EXP_LNKCTL2_TLS_5_0GT); + } + /* Set relink_time for changing to PCIe card */ relink_time = 0x8FFF; @@ -1379,6 +1388,12 @@ static int sdmmc_init_sd_express(struct mmc_host *mmc, struct mmc_ios *ios) if (pcr->ops->disable_auto_blink) pcr->ops->disable_auto_blink(pcr); + if (PCI_PID(pcr) == PID_5264) { + rtsx_pci_write_register(pcr, RTS5264_AUTOLOAD_CFG2, + RTS5264_CHIP_RST_N_SEL, RTS5264_CHIP_RST_N_SEL); + rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); + } + /* For PCIe/NVMe mode can't enter delink issue */ pcr->hw_param.interrupt_en &= ~(SD_INT_EN); rtsx_pci_writel(pcr, RTSX_BIER, pcr->hw_param.interrupt_en); diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index 7bfee28116af..d4a02184784a 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -693,6 +693,35 @@ static int sdhci_pci_o2_init_sd_express(struct mmc_host *mmc, struct mmc_ios *io return 0; } +static void sdhci_pci_o2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd) +{ + struct sdhci_pci_chip *chip; + struct sdhci_pci_slot *slot = sdhci_priv(host); + u32 scratch_32 = 0; + u8 scratch_8 = 0; + + chip = slot->chip; + + if (mode == MMC_POWER_OFF) { + /* UnLock WP */ + pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch_8); + scratch_8 &= 0x7f; + pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); + + /* Set PCR 0x354[16] to switch Clock Source back to OPE Clock */ + pci_read_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, &scratch_32); + scratch_32 &= ~(O2_SD_SEL_DLL); + pci_write_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, scratch_32); + + /* Lock WP */ + pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch_8); + scratch_8 |= 0x80; + pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8); + } + + sdhci_set_power(host, mode, vdd); +} + static int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) { struct sdhci_pci_chip *chip; @@ -1051,6 +1080,7 @@ static const struct sdhci_ops sdhci_pci_o2_ops = { .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, + .set_power = sdhci_pci_o2_set_power, }; const struct sdhci_pci_fixes sdhci_o2 = { diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index bb0759ca12f1..e451b28840d5 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -30,6 +30,7 @@ #include <linux/debugfs.h> #include <linux/nvmem-provider.h> #include <linux/root_dev.h> +#include <linux/error-injection.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> @@ -1412,6 +1413,7 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) return ret; } EXPORT_SYMBOL_GPL(mtd_erase); +ALLOW_ERROR_INJECTION(mtd_erase, ERRNO); /* * This stuff for eXecute-In-Place. phys is optional and may be set to NULL. @@ -1511,6 +1513,7 @@ int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, return ret; } EXPORT_SYMBOL_GPL(mtd_read); +ALLOW_ERROR_INJECTION(mtd_read, ERRNO); int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) @@ -1527,6 +1530,7 @@ int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, return ret; } EXPORT_SYMBOL_GPL(mtd_write); +ALLOW_ERROR_INJECTION(mtd_write, ERRNO); /* * In blackbox flight recorder like scenarios we want to make successful writes @@ -2347,6 +2351,7 @@ int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs) return 0; } EXPORT_SYMBOL_GPL(mtd_block_markbad); +ALLOW_ERROR_INJECTION(mtd_block_markbad, ERRNO); /* * default_mtd_writev - the default writev method diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index 2ed77b7b3fcb..7499a540121e 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -104,4 +104,13 @@ config MTD_UBI_BLOCK If in doubt, say "N". +config MTD_UBI_FAULT_INJECTION + bool "Fault injection capability of UBI device" + default n + depends on FAULT_INJECTION_DEBUG_FS + help + This option enables fault-injection support for UBI devices for + testing purposes. + + If in doubt, say "N". endif # MTD_UBI diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c index 309a42aeaa4c..654bd7372cd8 100644 --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c @@ -434,7 +434,7 @@ out_remove_minor: list_del(&dev->list); idr_remove(&ubiblock_minor_idr, gd->first_minor); out_cleanup_disk: - put_disk(dev->gd); + put_disk(gd); out_free_tags: blk_mq_free_tag_set(&dev->tag_set); out_free_dev: diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index 27168f511d6d..d57f52bd2ff3 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c @@ -10,7 +10,37 @@ #include <linux/uaccess.h> #include <linux/module.h> #include <linux/seq_file.h> +#include <linux/fault-inject.h> + +#ifdef CONFIG_MTD_UBI_FAULT_INJECTION +static DECLARE_FAULT_ATTR(fault_eccerr_attr); +static DECLARE_FAULT_ATTR(fault_bitflips_attr); +static DECLARE_FAULT_ATTR(fault_read_failure_attr); +static DECLARE_FAULT_ATTR(fault_write_failure_attr); +static DECLARE_FAULT_ATTR(fault_erase_failure_attr); +static DECLARE_FAULT_ATTR(fault_power_cut_attr); +static DECLARE_FAULT_ATTR(fault_io_ff_attr); +static DECLARE_FAULT_ATTR(fault_io_ff_bitflips_attr); +static DECLARE_FAULT_ATTR(fault_bad_hdr_attr); +static DECLARE_FAULT_ATTR(fault_bad_hdr_ebadmsg_attr); + +#define FAIL_ACTION(name, fault_attr) \ +bool should_fail_##name(void) \ +{ \ + return should_fail(&fault_attr, 1); \ +} +FAIL_ACTION(eccerr, fault_eccerr_attr) +FAIL_ACTION(bitflips, fault_bitflips_attr) +FAIL_ACTION(read_failure, fault_read_failure_attr) +FAIL_ACTION(write_failure, fault_write_failure_attr) +FAIL_ACTION(erase_failure, fault_erase_failure_attr) +FAIL_ACTION(power_cut, fault_power_cut_attr) +FAIL_ACTION(io_ff, fault_io_ff_attr) +FAIL_ACTION(io_ff_bitflips, fault_io_ff_bitflips_attr) +FAIL_ACTION(bad_hdr, fault_bad_hdr_attr) +FAIL_ACTION(bad_hdr_ebadmsg, fault_bad_hdr_ebadmsg_attr) +#endif /** * ubi_dump_flash - dump a region of flash. @@ -212,6 +242,52 @@ void ubi_dump_mkvol_req(const struct ubi_mkvol_req *req) */ static struct dentry *dfs_rootdir; +#ifdef CONFIG_MTD_UBI_FAULT_INJECTION +static void dfs_create_fault_entry(struct dentry *parent) +{ + struct dentry *dir; + + dir = debugfs_create_dir("fault_inject", parent); + if (IS_ERR_OR_NULL(dir)) { + int err = dir ? PTR_ERR(dir) : -ENODEV; + + pr_warn("UBI error: cannot create \"fault_inject\" debugfs directory, error %d\n", + err); + return; + } + + fault_create_debugfs_attr("emulate_eccerr", dir, + &fault_eccerr_attr); + + fault_create_debugfs_attr("emulate_read_failure", dir, + &fault_read_failure_attr); + + fault_create_debugfs_attr("emulate_bitflips", dir, + &fault_bitflips_attr); + + fault_create_debugfs_attr("emulate_write_failure", dir, + &fault_write_failure_attr); + + fault_create_debugfs_attr("emulate_erase_failure", dir, + &fault_erase_failure_attr); + + fault_create_debugfs_attr("emulate_power_cut", dir, + &fault_power_cut_attr); + + fault_create_debugfs_attr("emulate_io_ff", dir, + &fault_io_ff_attr); + + fault_create_debugfs_attr("emulate_io_ff_bitflips", dir, + &fault_io_ff_bitflips_attr); + + fault_create_debugfs_attr("emulate_bad_hdr", dir, + &fault_bad_hdr_attr); + + fault_create_debugfs_attr("emulate_bad_hdr_ebadmsg", dir, + &fault_bad_hdr_ebadmsg_attr); +} +#endif + /** * ubi_debugfs_init - create UBI debugfs directory. * @@ -232,6 +308,10 @@ int ubi_debugfs_init(void) return err; } +#ifdef CONFIG_MTD_UBI_FAULT_INJECTION + dfs_create_fault_entry(dfs_rootdir); +#endif + return 0; } @@ -252,7 +332,7 @@ static ssize_t dfs_file_read(struct file *file, char __user *user_buf, struct dentry *dent = file->f_path.dentry; struct ubi_device *ubi; struct ubi_debug_info *d; - char buf[8]; + char buf[16]; int val; ubi = ubi_get_device(ubi_num); @@ -272,7 +352,12 @@ static ssize_t dfs_file_read(struct file *file, char __user *user_buf, val = d->emulate_bitflips; else if (dent == d->dfs_emulate_io_failures) val = d->emulate_io_failures; - else if (dent == d->dfs_emulate_power_cut) { + else if (dent == d->dfs_emulate_failures) { + snprintf(buf, sizeof(buf), "0x%04x\n", d->emulate_failures); + count = simple_read_from_buffer(user_buf, count, ppos, + buf, strlen(buf)); + goto out; + } else if (dent == d->dfs_emulate_power_cut) { snprintf(buf, sizeof(buf), "%u\n", d->emulate_power_cut); count = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); @@ -287,8 +372,7 @@ static ssize_t dfs_file_read(struct file *file, char __user *user_buf, count = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); goto out; - } - else { + } else { count = -EINVAL; goto out; } @@ -316,7 +400,7 @@ static ssize_t dfs_file_write(struct file *file, const char __user *user_buf, struct ubi_device *ubi; struct ubi_debug_info *d; size_t buf_size; - char buf[8] = {0}; + char buf[16] = {0}; int val; ubi = ubi_get_device(ubi_num); @@ -330,7 +414,11 @@ static ssize_t dfs_file_write(struct file *file, const char __user *user_buf, goto out; } - if (dent == d->dfs_power_cut_min) { + if (dent == d->dfs_emulate_failures) { + if (kstrtouint(buf, 0, &d->emulate_failures) != 0) + count = -EINVAL; + goto out; + } else if (dent == d->dfs_power_cut_min) { if (kstrtouint(buf, 0, &d->power_cut_min) != 0) count = -EINVAL; goto out; @@ -559,6 +647,12 @@ int ubi_debugfs_init_dev(struct ubi_device *ubi) debugfs_create_file("detailed_erase_block_info", S_IRUSR, d->dfs_dir, (void *)ubi_num, &eraseblk_count_fops); +#ifdef CONFIG_MTD_UBI_FAULT_INJECTION + d->dfs_emulate_failures = debugfs_create_file("emulate_failures", + mode, d->dfs_dir, + (void *)ubi_num, + &dfs_fops); +#endif return 0; } @@ -600,7 +694,5 @@ int ubi_dbg_power_cut(struct ubi_device *ubi, int caller) if (ubi->dbg.power_cut_counter) return 0; - ubi_msg(ubi, "XXXXXXXXXXXXXXX emulating a power cut XXXXXXXXXXXXXXXX"); - ubi_ro_mode(ubi); return 1; } diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index 23676f32b681..b2fd97548808 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -53,56 +53,315 @@ int ubi_debugfs_init_dev(struct ubi_device *ubi); void ubi_debugfs_exit_dev(struct ubi_device *ubi); /** - * ubi_dbg_is_bgt_disabled - if the background thread is disabled. + * The following function is a legacy implementation of UBI fault-injection + * hook. When using more powerful fault injection capabilities, the legacy + * fault injection interface should be retained. + */ +int ubi_dbg_power_cut(struct ubi_device *ubi, int caller); + +static inline int ubi_dbg_bitflip(const struct ubi_device *ubi) +{ + if (ubi->dbg.emulate_bitflips) + return !get_random_u32_below(200); + return 0; +} + +static inline int ubi_dbg_write_failure(const struct ubi_device *ubi) +{ + if (ubi->dbg.emulate_io_failures) + return !get_random_u32_below(500); + return 0; +} + +static inline int ubi_dbg_erase_failure(const struct ubi_device *ubi) +{ + if (ubi->dbg.emulate_io_failures) + return !get_random_u32_below(400); + return 0; +} + +/** + * MASK_XXX: Mask for emulate_failures in ubi_debug_info.The mask is used to + * precisely control the type and process of fault injection. + */ +/* Emulate a power cut when writing EC/VID header */ +#define MASK_POWER_CUT_EC (1 << 0) +#define MASK_POWER_CUT_VID (1 << 1) +/* Emulate a power cut when writing data*/ +#define MASK_POWER_CUT_DATA (1 << 2) +/* Emulate bit-flips */ +#define MASK_BITFLIPS (1 << 3) +/* Emulate ecc error */ +#define MASK_ECCERR (1 << 4) +/* Emulates -EIO during data read */ +#define MASK_READ_FAILURE (1 << 5) +#define MASK_READ_FAILURE_EC (1 << 6) +#define MASK_READ_FAILURE_VID (1 << 7) +/* Emulates -EIO during data write */ +#define MASK_WRITE_FAILURE (1 << 8) +/* Emulates -EIO during erase a PEB*/ +#define MASK_ERASE_FAILURE (1 << 9) +/* Return UBI_IO_FF when reading EC/VID header */ +#define MASK_IO_FF_EC (1 << 10) +#define MASK_IO_FF_VID (1 << 11) +/* Return UBI_IO_FF_BITFLIPS when reading EC/VID header */ +#define MASK_IO_FF_BITFLIPS_EC (1 << 12) +#define MASK_IO_FF_BITFLIPS_VID (1 << 13) +/* Return UBI_IO_BAD_HDR when reading EC/VID header */ +#define MASK_BAD_HDR_EC (1 << 14) +#define MASK_BAD_HDR_VID (1 << 15) +/* Return UBI_IO_BAD_HDR_EBADMSG when reading EC/VID header */ +#define MASK_BAD_HDR_EBADMSG_EC (1 << 16) +#define MASK_BAD_HDR_EBADMSG_VID (1 << 17) + +#ifdef CONFIG_MTD_UBI_FAULT_INJECTION + +extern bool should_fail_eccerr(void); +extern bool should_fail_bitflips(void); +extern bool should_fail_read_failure(void); +extern bool should_fail_write_failure(void); +extern bool should_fail_erase_failure(void); +extern bool should_fail_power_cut(void); +extern bool should_fail_io_ff(void); +extern bool should_fail_io_ff_bitflips(void); +extern bool should_fail_bad_hdr(void); +extern bool should_fail_bad_hdr_ebadmsg(void); + +static inline bool ubi_dbg_fail_bitflip(const struct ubi_device *ubi) +{ + if (ubi->dbg.emulate_failures & MASK_BITFLIPS) + return should_fail_bitflips(); + return false; +} + +static inline bool ubi_dbg_fail_write(const struct ubi_device *ubi) +{ + if (ubi->dbg.emulate_failures & MASK_WRITE_FAILURE) + return should_fail_write_failure(); + return false; +} + +static inline bool ubi_dbg_fail_erase(const struct ubi_device *ubi) +{ + if (ubi->dbg.emulate_failures & MASK_ERASE_FAILURE) + return should_fail_erase_failure(); + return false; +} + +static inline bool ubi_dbg_fail_power_cut(const struct ubi_device *ubi, + unsigned int caller) +{ + if (ubi->dbg.emulate_failures & caller) + return should_fail_power_cut(); + return false; +} + +static inline bool ubi_dbg_fail_read(const struct ubi_device *ubi, + unsigned int caller) +{ + if (ubi->dbg.emulate_failures & caller) + return should_fail_read_failure(); + return false; +} + +static inline bool ubi_dbg_fail_eccerr(const struct ubi_device *ubi) +{ + if (ubi->dbg.emulate_failures & MASK_ECCERR) + return should_fail_eccerr(); + return false; +} + +static inline bool ubi_dbg_fail_ff(const struct ubi_device *ubi, + unsigned int caller) +{ + if (ubi->dbg.emulate_failures & caller) + return should_fail_io_ff(); + return false; +} + +static inline bool ubi_dbg_fail_ff_bitflips(const struct ubi_device *ubi, + unsigned int caller) +{ + if (ubi->dbg.emulate_failures & caller) + return should_fail_io_ff_bitflips(); + return false; +} + +static inline bool ubi_dbg_fail_bad_hdr(const struct ubi_device *ubi, + unsigned int caller) +{ + if (ubi->dbg.emulate_failures & caller) + return should_fail_bad_hdr(); + return false; +} + +static inline bool ubi_dbg_fail_bad_hdr_ebadmsg(const struct ubi_device *ubi, + unsigned int caller) +{ + if (ubi->dbg.emulate_failures & caller) + return should_fail_bad_hdr_ebadmsg(); + return false; +} +#else /* CONFIG_MTD_UBI_FAULT_INJECTION */ + +#define ubi_dbg_fail_bitflip(u) false +#define ubi_dbg_fail_write(u) false +#define ubi_dbg_fail_erase(u) false +#define ubi_dbg_fail_power_cut(u, c) false +#define ubi_dbg_fail_read(u, c) false +#define ubi_dbg_fail_eccerr(u) false +#define ubi_dbg_fail_ff(u, c) false +#define ubi_dbg_fail_ff_bitflips(u, v) false +#define ubi_dbg_fail_bad_hdr(u, c) false +#define ubi_dbg_fail_bad_hdr_ebadmsg(u, c) false + +#endif + +/** + * ubi_dbg_is_power_cut - if it is time to emulate power cut. * @ubi: UBI device description object * - * Returns non-zero if the UBI background thread is disabled for testing - * purposes. + * Returns true if power cut should be emulated, otherwise returns false. */ -static inline int ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi) +static inline bool ubi_dbg_is_power_cut(struct ubi_device *ubi, + unsigned int caller) { - return ubi->dbg.disable_bgt; + if (ubi_dbg_power_cut(ubi, caller)) + return true; + return ubi_dbg_fail_power_cut(ubi, caller); } /** * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip. * @ubi: UBI device description object * - * Returns non-zero if a bit-flip should be emulated, otherwise returns zero. + * Returns true if a bit-flip should be emulated, otherwise returns false. */ -static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi) +static inline bool ubi_dbg_is_bitflip(const struct ubi_device *ubi) { - if (ubi->dbg.emulate_bitflips) - return !get_random_u32_below(200); - return 0; + if (ubi_dbg_bitflip(ubi)) + return true; + return ubi_dbg_fail_bitflip(ubi); } /** * ubi_dbg_is_write_failure - if it is time to emulate a write failure. * @ubi: UBI device description object * - * Returns non-zero if a write failure should be emulated, otherwise returns - * zero. + * Returns true if a write failure should be emulated, otherwise returns + * false. */ -static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi) +static inline bool ubi_dbg_is_write_failure(const struct ubi_device *ubi) { - if (ubi->dbg.emulate_io_failures) - return !get_random_u32_below(500); - return 0; + if (ubi_dbg_write_failure(ubi)) + return true; + return ubi_dbg_fail_write(ubi); } /** * ubi_dbg_is_erase_failure - if its time to emulate an erase failure. * @ubi: UBI device description object * - * Returns non-zero if an erase failure should be emulated, otherwise returns - * zero. + * Returns true if an erase failure should be emulated, otherwise returns + * false. */ -static inline int ubi_dbg_is_erase_failure(const struct ubi_device *ubi) +static inline bool ubi_dbg_is_erase_failure(const struct ubi_device *ubi) { - if (ubi->dbg.emulate_io_failures) - return !get_random_u32_below(400); - return 0; + if (ubi_dbg_erase_failure(ubi)) + return true; + return ubi_dbg_fail_erase(ubi); +} + +/** + * ubi_dbg_is_eccerr - if it is time to emulate ECC error. + * @ubi: UBI device description object + * + * Returns true if a ECC error should be emulated, otherwise returns false. + */ +static inline bool ubi_dbg_is_eccerr(const struct ubi_device *ubi) +{ + return ubi_dbg_fail_eccerr(ubi); +} + +/** + * ubi_dbg_is_read_failure - if it is time to emulate a read failure. + * @ubi: UBI device description object + * + * Returns true if a read failure should be emulated, otherwise returns + * false. + */ +static inline bool ubi_dbg_is_read_failure(const struct ubi_device *ubi, + unsigned int caller) +{ + return ubi_dbg_fail_read(ubi, caller); +} + +/** + * ubi_dbg_is_ff - if it is time to emulate that read region is only 0xFF. + * @ubi: UBI device description object + * + * Returns true if read region should be emulated 0xFF, otherwise + * returns false. + */ +static inline bool ubi_dbg_is_ff(const struct ubi_device *ubi, + unsigned int caller) +{ + return ubi_dbg_fail_ff(ubi, caller); +} + +/** + * ubi_dbg_is_ff_bitflips - if it is time to emulate that read region is only 0xFF + * with error reported by the MTD driver + * + * @ubi: UBI device description object + * + * Returns true if read region should be emulated 0xFF and error + * reported by the MTD driver, otherwise returns false. + */ +static inline bool ubi_dbg_is_ff_bitflips(const struct ubi_device *ubi, + unsigned int caller) +{ + return ubi_dbg_fail_ff_bitflips(ubi, caller); +} + +/** + * ubi_dbg_is_bad_hdr - if it is time to emulate a bad header + * @ubi: UBI device description object + * + * Returns true if a bad header error should be emulated, otherwise + * returns false. + */ +static inline bool ubi_dbg_is_bad_hdr(const struct ubi_device *ubi, + unsigned int caller) +{ + return ubi_dbg_fail_bad_hdr(ubi, caller); +} + +/** + * ubi_dbg_is_bad_hdr_ebadmsg - if it is time to emulate a bad header with + * ECC error. + * + * @ubi: UBI device description object + * + * Returns true if a bad header with ECC error should be emulated, otherwise + * returns false. + */ +static inline bool ubi_dbg_is_bad_hdr_ebadmsg(const struct ubi_device *ubi, + unsigned int caller) +{ + return ubi_dbg_fail_bad_hdr_ebadmsg(ubi, caller); +} + +/** + * ubi_dbg_is_bgt_disabled - if the background thread is disabled. + * @ubi: UBI device description object + * + * Returns non-zero if the UBI background thread is disabled for testing + * purposes. + */ +static inline int ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi) +{ + return ubi->dbg.disable_bgt; } static inline int ubi_dbg_chk_io(const struct ubi_device *ubi) @@ -125,5 +384,4 @@ static inline void ubi_enable_dbg_chk_fastmap(struct ubi_device *ubi) ubi->dbg.chk_fastmap = 1; } -int ubi_dbg_power_cut(struct ubi_device *ubi, int caller); #endif /* !__UBI_DEBUG_H__ */ diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 01b644861253..a4999bce435f 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -195,7 +195,19 @@ retry: if (ubi_dbg_is_bitflip(ubi)) { dbg_gen("bit-flip (emulated)"); - err = UBI_IO_BITFLIPS; + return UBI_IO_BITFLIPS; + } + + if (ubi_dbg_is_read_failure(ubi, MASK_READ_FAILURE)) { + ubi_warn(ubi, "cannot read %d bytes from PEB %d:%d (emulated)", + len, pnum, offset); + return -EIO; + } + + if (ubi_dbg_is_eccerr(ubi)) { + ubi_warn(ubi, "ECC error (emulated) while reading %d bytes from PEB %d:%d, read %zd bytes", + len, pnum, offset, read); + return -EBADMSG; } } @@ -782,7 +794,36 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, * If there was %-EBADMSG, but the header CRC is still OK, report about * a bit-flip to force scrubbing on this PEB. */ - return read_err ? UBI_IO_BITFLIPS : 0; + if (read_err) + return UBI_IO_BITFLIPS; + + if (ubi_dbg_is_read_failure(ubi, MASK_READ_FAILURE_EC)) { + ubi_warn(ubi, "cannot read EC header from PEB %d (emulated)", + pnum); + return -EIO; + } + + if (ubi_dbg_is_ff(ubi, MASK_IO_FF_EC)) { + ubi_warn(ubi, "bit-all-ff (emulated)"); + return UBI_IO_FF; + } + + if (ubi_dbg_is_ff_bitflips(ubi, MASK_IO_FF_BITFLIPS_EC)) { + ubi_warn(ubi, "bit-all-ff with error reported by MTD driver (emulated)"); + return UBI_IO_FF_BITFLIPS; + } + + if (ubi_dbg_is_bad_hdr(ubi, MASK_BAD_HDR_EC)) { + ubi_warn(ubi, "bad_hdr (emulated)"); + return UBI_IO_BAD_HDR; + } + + if (ubi_dbg_is_bad_hdr_ebadmsg(ubi, MASK_BAD_HDR_EBADMSG_EC)) { + ubi_warn(ubi, "bad_hdr with ECC error (emulated)"); + return UBI_IO_BAD_HDR_EBADMSG; + } + + return 0; } /** @@ -821,8 +862,11 @@ int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, if (err) return err; - if (ubi_dbg_power_cut(ubi, POWER_CUT_EC_WRITE)) + if (ubi_dbg_is_power_cut(ubi, MASK_POWER_CUT_EC)) { + ubi_warn(ubi, "emulating a power cut when writing EC header"); + ubi_ro_mode(ubi); return -EROFS; + } err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize); return err; @@ -1029,7 +1073,36 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, return -EINVAL; } - return read_err ? UBI_IO_BITFLIPS : 0; + if (read_err) + return UBI_IO_BITFLIPS; + + if (ubi_dbg_is_read_failure(ubi, MASK_READ_FAILURE_VID)) { + ubi_warn(ubi, "cannot read VID header from PEB %d (emulated)", + pnum); + return -EIO; + } + + if (ubi_dbg_is_ff(ubi, MASK_IO_FF_VID)) { + ubi_warn(ubi, "bit-all-ff (emulated)"); + return UBI_IO_FF; + } + + if (ubi_dbg_is_ff_bitflips(ubi, MASK_IO_FF_BITFLIPS_VID)) { + ubi_warn(ubi, "bit-all-ff with error reported by MTD driver (emulated)"); + return UBI_IO_FF_BITFLIPS; + } + + if (ubi_dbg_is_bad_hdr(ubi, MASK_BAD_HDR_VID)) { + ubi_warn(ubi, "bad_hdr (emulated)"); + return UBI_IO_BAD_HDR; + } + + if (ubi_dbg_is_bad_hdr_ebadmsg(ubi, MASK_BAD_HDR_EBADMSG_VID)) { + ubi_warn(ubi, "bad_hdr with ECC error (emulated)"); + return UBI_IO_BAD_HDR_EBADMSG; + } + + return 0; } /** @@ -1071,8 +1144,11 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, if (err) return err; - if (ubi_dbg_power_cut(ubi, POWER_CUT_VID_WRITE)) + if (ubi_dbg_is_power_cut(ubi, MASK_POWER_CUT_VID)) { + ubi_warn(ubi, "emulating a power cut when writing VID header"); + ubi_ro_mode(ubi); return -EROFS; + } err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, ubi->vid_hdr_alsize); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index a5ec566df0d7..0b42bb45dd84 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -145,17 +145,6 @@ enum { UBI_BAD_FASTMAP, }; -/* - * Flags for emulate_power_cut in ubi_debug_info - * - * POWER_CUT_EC_WRITE: Emulate a power cut when writing an EC header - * POWER_CUT_VID_WRITE: Emulate a power cut when writing a VID header - */ -enum { - POWER_CUT_EC_WRITE = 0x01, - POWER_CUT_VID_WRITE = 0x02, -}; - /** * struct ubi_vid_io_buf - VID buffer used to read/write VID info to/from the * flash. @@ -404,6 +393,7 @@ struct ubi_volume_desc { * @power_cut_counter: count down for writes left until emulated power cut * @power_cut_min: minimum number of writes before emulating a power cut * @power_cut_max: maximum number of writes until emulating a power cut + * @emulate_failures: emulate failures for testing purposes * @dfs_dir_name: name of debugfs directory containing files of this UBI device * @dfs_dir: direntry object of the UBI device debugfs directory * @dfs_chk_gen: debugfs knob to enable UBI general extra checks @@ -415,6 +405,7 @@ struct ubi_volume_desc { * @dfs_emulate_power_cut: debugfs knob to emulate power cuts * @dfs_power_cut_min: debugfs knob for minimum writes before power cut * @dfs_power_cut_max: debugfs knob for maximum writes until power cut + * @dfs_emulate_failures: debugfs entry to control the fault injection type */ struct ubi_debug_info { unsigned int chk_gen:1; @@ -427,6 +418,7 @@ struct ubi_debug_info { unsigned int power_cut_counter; unsigned int power_cut_min; unsigned int power_cut_max; + unsigned int emulate_failures; char dfs_dir_name[UBI_DFS_DIR_LEN + 1]; struct dentry *dfs_dir; struct dentry *dfs_chk_gen; @@ -438,6 +430,7 @@ struct ubi_debug_info { struct dentry *dfs_emulate_power_cut; struct dentry *dfs_power_cut_min; struct dentry *dfs_power_cut_max; + struct dentry *dfs_emulate_failures; }; /** @@ -1130,6 +1123,19 @@ static inline struct ubi_vid_hdr *ubi_get_vid_hdr(struct ubi_vid_io_buf *vidb) return vidb->hdr; } +/** + * ubi_ro_mode - switch to read-only mode. + * @ubi: UBI device description object + */ +static inline void ubi_ro_mode(struct ubi_device *ubi) +{ + if (!ubi->ro_mode) { + ubi->ro_mode = 1; + ubi_warn(ubi, "switch to read-only mode"); + dump_stack(); + } +} + /* * This function is equivalent to 'ubi_io_read()', but @offset is relative to * the beginning of the logical eraseblock, not to the beginning of the @@ -1151,20 +1157,13 @@ static inline int ubi_io_write_data(struct ubi_device *ubi, const void *buf, int pnum, int offset, int len) { ubi_assert(offset >= 0); - return ubi_io_write(ubi, buf, pnum, offset + ubi->leb_start, len); -} -/** - * ubi_ro_mode - switch to read-only mode. - * @ubi: UBI device description object - */ -static inline void ubi_ro_mode(struct ubi_device *ubi) -{ - if (!ubi->ro_mode) { - ubi->ro_mode = 1; - ubi_warn(ubi, "switch to read-only mode"); - dump_stack(); + if (ubi_dbg_power_cut(ubi, MASK_POWER_CUT_DATA)) { + ubi_warn(ubi, "XXXXX emulating a power cut when writing data XXXXX"); + ubi_ro_mode(ubi); + return -EROFS; } + return ubi_io_write(ubi, buf, pnum, offset + ubi->leb_start, len); } /** diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c index fd1d121a584b..30a952c34365 100644 --- a/drivers/mux/mmio.c +++ b/drivers/mux/mmio.c @@ -44,15 +44,20 @@ static int mux_mmio_probe(struct platform_device *pdev) int ret; int i; - if (of_device_is_compatible(np, "mmio-mux")) + if (of_device_is_compatible(np, "mmio-mux")) { regmap = syscon_node_to_regmap(np->parent); - else - regmap = dev_get_regmap(dev->parent, NULL) ?: ERR_PTR(-ENODEV); - if (IS_ERR(regmap)) { - ret = PTR_ERR(regmap); - dev_err(dev, "failed to get regmap: %d\n", ret); - return ret; + } else { + regmap = device_node_to_regmap(np); + /* Fallback to checking the parent node on "real" errors. */ + if (IS_ERR(regmap) && regmap != ERR_PTR(-EPROBE_DEFER)) { + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + regmap = ERR_PTR(-ENODEV); + } } + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "failed to get regmap\n"); ret = of_property_count_u32_elems(np, "mux-reg-masks"); if (ret == 0 || ret % 2) diff --git a/drivers/net/amt.c b/drivers/net/amt.c index 53415e83821c..68e79b1272f6 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -11,7 +11,7 @@ #include <linux/net.h> #include <linux/igmp.h> #include <linux/workqueue.h> -#include <net/sch_generic.h> +#include <net/pkt_sched.h> #include <net/net_namespace.h> #include <net/ip.h> #include <net/udp.h> @@ -80,11 +80,11 @@ static struct mld2_grec mldv2_zero_grec; static struct amt_skb_cb *amt_skb_cb(struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(struct amt_skb_cb) + sizeof(struct qdisc_skb_cb) > + BUILD_BUG_ON(sizeof(struct amt_skb_cb) + sizeof(struct tc_skb_cb) > sizeof_field(struct sk_buff, cb)); return (struct amt_skb_cb *)((void *)skb->cb + - sizeof(struct qdisc_skb_cb)); + sizeof(struct tc_skb_cb)); } static void __amt_source_gc_work(void) diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index f44ba2600415..e2ec69aa46e5 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -30,9 +30,9 @@ #include <linux/io.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/clk.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> @@ -259,22 +259,13 @@ static int c_can_plat_probe(struct platform_device *pdev) void __iomem *addr; struct net_device *dev; struct c_can_priv *priv; - const struct of_device_id *match; struct resource *mem; int irq; struct clk *clk; const struct c_can_driver_data *drvdata; struct device_node *np = pdev->dev.of_node; - match = of_match_device(c_can_of_table, &pdev->dev); - if (match) { - drvdata = match->data; - } else if (pdev->id_entry->driver_data) { - drvdata = (struct c_can_driver_data *) - platform_get_device_id(pdev)->driver_data; - } else { - return -ENODEV; - } + drvdata = device_get_match_data(&pdev->dev); /* get the appropriate clk */ clk = devm_clk_get(&pdev->dev, NULL); diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c index d15f85a40c1e..8ea7f2795551 100644 --- a/drivers/net/can/flexcan/flexcan-core.c +++ b/drivers/net/can/flexcan/flexcan-core.c @@ -23,11 +23,11 @@ #include <linux/module.h> #include <linux/netdevice.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/can/platform/flexcan.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -2034,7 +2034,6 @@ MODULE_DEVICE_TABLE(platform, flexcan_id_table); static int flexcan_probe(struct platform_device *pdev) { - const struct of_device_id *of_id; const struct flexcan_devtype_data *devtype_data; struct net_device *dev; struct flexcan_priv *priv; @@ -2090,14 +2089,7 @@ static int flexcan_probe(struct platform_device *pdev) if (IS_ERR(regs)) return PTR_ERR(regs); - of_id = of_match_device(flexcan_of_match, &pdev->dev); - if (of_id) - devtype_data = of_id->data; - else if (platform_get_device_id(pdev)->driver_data) - devtype_data = (struct flexcan_devtype_data *) - platform_get_device_id(pdev)->driver_data; - else - return -ENODEV; + devtype_data = device_get_match_data(&pdev->dev); if ((devtype_data->quirks & FLEXCAN_QUIRK_SUPPORT_FD) && !((devtype_data->quirks & diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c index 4837df6efa92..5b3d69c3b6b6 100644 --- a/drivers/net/can/mscan/mpc5xxx_can.c +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -12,8 +12,10 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/netdevice.h> #include <linux/can/dev.h> +#include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> @@ -290,7 +292,7 @@ static int mpc5xxx_can_probe(struct platform_device *ofdev) int irq, mscan_clksrc = 0; int err = -ENOMEM; - data = of_device_get_match_data(&ofdev->dev); + data = device_get_match_data(&ofdev->dev); if (!data) return -EINVAL; @@ -351,13 +353,11 @@ exit_unmap_mem: static void mpc5xxx_can_remove(struct platform_device *ofdev) { - const struct of_device_id *match; const struct mpc5xxx_can_data *data; struct net_device *dev = platform_get_drvdata(ofdev); struct mscan_priv *priv = netdev_priv(dev); - match = of_match_device(mpc5xxx_can_table, &ofdev->dev); - data = match ? match->data : NULL; + data = device_get_match_data(&ofdev->dev); unregister_mscandev(dev); if (data && data->put_clock) diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index 24ad9f593a77..1efa39e134f4 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -1143,7 +1143,7 @@ static void __exit peak_usb_exit(void) int err; /* last chance do send any synchronous commands here */ - err = driver_for_each_device(&peak_usb_driver.drvwrap.driver, NULL, + err = driver_for_each_device(&peak_usb_driver.driver, NULL, NULL, peak_usb_do_device_exit); if (err) pr_err("%s: failed to stop all can devices (err %d)\n", diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c index abe58f103043..3722eaa84234 100644 --- a/drivers/net/can/xilinx_can.c +++ b/drivers/net/can/xilinx_can.c @@ -20,8 +20,8 @@ #include <linux/module.h> #include <linux/netdevice.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/skbuff.h> #include <linux/spinlock.h> #include <linux/string.h> @@ -1726,8 +1726,7 @@ static int xcan_probe(struct platform_device *pdev) struct net_device *ndev; struct xcan_priv *priv; struct phy *transceiver; - const struct of_device_id *of_id; - const struct xcan_devtype_data *devtype = &xcan_axi_data; + const struct xcan_devtype_data *devtype; void __iomem *addr; int ret; int rx_max, tx_max; @@ -1741,9 +1740,7 @@ static int xcan_probe(struct platform_device *pdev) goto err; } - of_id = of_match_device(xcan_of_match, &pdev->dev); - if (of_id && of_id->data) - devtype = of_id->data; + devtype = device_get_match_data(&pdev->dev); hw_tx_max_property = devtype->flags & XCAN_FLAG_TX_MAILBOXES ? "tx-mailbox-count" : "tx-fifo-depth"; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 391c4dbdff42..3c1f657593a8 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -2838,8 +2838,7 @@ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port, /* MT753x MAC works in 1G full duplex mode for all up-clocked * variants. */ - if (interface == PHY_INTERFACE_MODE_INTERNAL || - interface == PHY_INTERFACE_MODE_TRGMII || + if (interface == PHY_INTERFACE_MODE_TRGMII || (phy_interface_mode_is_8023z(interface))) { speed = SPEED_1000; duplex = DUPLEX_FULL; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 383b3c4d6f59..614cabb5c1b0 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3659,7 +3659,7 @@ static int mv88e6xxx_mdio_read_c45(struct mii_bus *bus, int phy, int devad, int err; if (!chip->info->ops->phy_read_c45) - return -EOPNOTSUPP; + return 0xffff; mv88e6xxx_reg_lock(chip); err = chip->info->ops->phy_read_c45(chip, bus, phy, devad, reg, &val); diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c index c51f40960961..7a864329cb72 100644 --- a/drivers/net/dsa/qca/qca8k-8xxx.c +++ b/drivers/net/dsa/qca/qca8k-8xxx.c @@ -2051,12 +2051,11 @@ qca8k_sw_probe(struct mdio_device *mdiodev) priv->info = of_device_get_match_data(priv->dev); priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", - GPIOD_ASIS); + GPIOD_OUT_HIGH); if (IS_ERR(priv->reset_gpio)) return PTR_ERR(priv->reset_gpio); if (priv->reset_gpio) { - gpiod_set_value_cansleep(priv->reset_gpio, 1); /* The active low duration must be greater than 10 ms * and checkpatch.pl wants 20 ms. */ diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index dd50502e2122..ae70eac3be28 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-core.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -1135,6 +1135,8 @@ static int vsc73xx_gpio_probe(struct vsc73xx *vsc) vsc->gc.label = devm_kasprintf(vsc->dev, GFP_KERNEL, "VSC%04x", vsc->chipid); + if (!vsc->gc.label) + return -ENOMEM; vsc->gc.ngpio = 4; vsc->gc.owner = THIS_MODULE; vsc->gc.parent = vsc->dev; diff --git a/drivers/net/ethernet/8390/8390.c b/drivers/net/ethernet/8390/8390.c index 0e0aa4016858..c5636245f1ca 100644 --- a/drivers/net/ethernet/8390/8390.c +++ b/drivers/net/ethernet/8390/8390.c @@ -100,4 +100,5 @@ static void __exit ns8390_module_exit(void) module_init(ns8390_module_init); module_exit(ns8390_module_exit); #endif /* MODULE */ +MODULE_DESCRIPTION("National Semiconductor 8390 core driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/8390/8390p.c b/drivers/net/ethernet/8390/8390p.c index 6834742057b3..6d429b11e9c6 100644 --- a/drivers/net/ethernet/8390/8390p.c +++ b/drivers/net/ethernet/8390/8390p.c @@ -102,4 +102,5 @@ static void __exit NS8390p_cleanup_module(void) module_init(NS8390p_init_module); module_exit(NS8390p_cleanup_module); +MODULE_DESCRIPTION("National Semiconductor 8390 core for ISA driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/8390/apne.c b/drivers/net/ethernet/8390/apne.c index a09f383dd249..828edca8d30c 100644 --- a/drivers/net/ethernet/8390/apne.c +++ b/drivers/net/ethernet/8390/apne.c @@ -610,4 +610,5 @@ static int init_pcmcia(void) return 1; } +MODULE_DESCRIPTION("National Semiconductor 8390 Amiga PCMCIA ethernet driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/8390/hydra.c b/drivers/net/ethernet/8390/hydra.c index 24f49a8ff903..fd9dcdc356e6 100644 --- a/drivers/net/ethernet/8390/hydra.c +++ b/drivers/net/ethernet/8390/hydra.c @@ -270,4 +270,5 @@ static void __exit hydra_cleanup_module(void) module_init(hydra_init_module); module_exit(hydra_cleanup_module); +MODULE_DESCRIPTION("Zorro-II Hydra 8390 ethernet driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/8390/stnic.c b/drivers/net/ethernet/8390/stnic.c index 265976e3b64a..6cc0e190aa79 100644 --- a/drivers/net/ethernet/8390/stnic.c +++ b/drivers/net/ethernet/8390/stnic.c @@ -296,4 +296,5 @@ static void __exit stnic_cleanup(void) module_init(stnic_probe); module_exit(stnic_cleanup); +MODULE_DESCRIPTION("National Semiconductor DP83902AV ethernet driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/8390/zorro8390.c b/drivers/net/ethernet/8390/zorro8390.c index d70390e9d03d..c24dd4fe7a10 100644 --- a/drivers/net/ethernet/8390/zorro8390.c +++ b/drivers/net/ethernet/8390/zorro8390.c @@ -443,4 +443,5 @@ static void __exit zorro8390_cleanup_module(void) module_init(zorro8390_init_module); module_exit(zorro8390_cleanup_module); +MODULE_DESCRIPTION("Zorro NS8390-based ethernet driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/amd/pds_core/adminq.c b/drivers/net/ethernet/amd/pds_core/adminq.c index 5beadabc2136..ea773cfa0af6 100644 --- a/drivers/net/ethernet/amd/pds_core/adminq.c +++ b/drivers/net/ethernet/amd/pds_core/adminq.c @@ -63,6 +63,15 @@ static int pdsc_process_notifyq(struct pdsc_qcq *qcq) return nq_work; } +static bool pdsc_adminq_inc_if_up(struct pdsc *pdsc) +{ + if (pdsc->state & BIT_ULL(PDSC_S_STOPPING_DRIVER) || + pdsc->state & BIT_ULL(PDSC_S_FW_DEAD)) + return false; + + return refcount_inc_not_zero(&pdsc->adminq_refcnt); +} + void pdsc_process_adminq(struct pdsc_qcq *qcq) { union pds_core_adminq_comp *comp; @@ -75,9 +84,9 @@ void pdsc_process_adminq(struct pdsc_qcq *qcq) int aq_work = 0; int credits; - /* Don't process AdminQ when shutting down */ - if (pdsc->state & BIT_ULL(PDSC_S_STOPPING_DRIVER)) { - dev_err(pdsc->dev, "%s: called while PDSC_S_STOPPING_DRIVER\n", + /* Don't process AdminQ when it's not up */ + if (!pdsc_adminq_inc_if_up(pdsc)) { + dev_err(pdsc->dev, "%s: called while adminq is unavailable\n", __func__); return; } @@ -124,6 +133,7 @@ credits: pds_core_intr_credits(&pdsc->intr_ctrl[qcq->intx], credits, PDS_CORE_INTR_CRED_REARM); + refcount_dec(&pdsc->adminq_refcnt); } void pdsc_work_thread(struct work_struct *work) @@ -135,18 +145,20 @@ void pdsc_work_thread(struct work_struct *work) irqreturn_t pdsc_adminq_isr(int irq, void *data) { - struct pdsc_qcq *qcq = data; - struct pdsc *pdsc = qcq->pdsc; + struct pdsc *pdsc = data; + struct pdsc_qcq *qcq; - /* Don't process AdminQ when shutting down */ - if (pdsc->state & BIT_ULL(PDSC_S_STOPPING_DRIVER)) { - dev_err(pdsc->dev, "%s: called while PDSC_S_STOPPING_DRIVER\n", + /* Don't process AdminQ when it's not up */ + if (!pdsc_adminq_inc_if_up(pdsc)) { + dev_err(pdsc->dev, "%s: called while adminq is unavailable\n", __func__); return IRQ_HANDLED; } + qcq = &pdsc->adminqcq; queue_work(pdsc->wq, &qcq->work); pds_core_intr_mask(&pdsc->intr_ctrl[qcq->intx], PDS_CORE_INTR_MASK_CLEAR); + refcount_dec(&pdsc->adminq_refcnt); return IRQ_HANDLED; } @@ -179,10 +191,16 @@ static int __pdsc_adminq_post(struct pdsc *pdsc, /* Check that the FW is running */ if (!pdsc_is_fw_running(pdsc)) { - u8 fw_status = ioread8(&pdsc->info_regs->fw_status); - - dev_info(pdsc->dev, "%s: post failed - fw not running %#02x:\n", - __func__, fw_status); + if (pdsc->info_regs) { + u8 fw_status = + ioread8(&pdsc->info_regs->fw_status); + + dev_info(pdsc->dev, "%s: post failed - fw not running %#02x:\n", + __func__, fw_status); + } else { + dev_info(pdsc->dev, "%s: post failed - BARs not setup\n", + __func__); + } ret = -ENXIO; goto err_out_unlock; @@ -230,6 +248,12 @@ int pdsc_adminq_post(struct pdsc *pdsc, int err = 0; int index; + if (!pdsc_adminq_inc_if_up(pdsc)) { + dev_dbg(pdsc->dev, "%s: preventing adminq cmd %u\n", + __func__, cmd->opcode); + return -ENXIO; + } + wc.qcq = &pdsc->adminqcq; index = __pdsc_adminq_post(pdsc, &pdsc->adminqcq, cmd, comp, &wc); if (index < 0) { @@ -248,10 +272,16 @@ int pdsc_adminq_post(struct pdsc *pdsc, break; if (!pdsc_is_fw_running(pdsc)) { - u8 fw_status = ioread8(&pdsc->info_regs->fw_status); - - dev_dbg(pdsc->dev, "%s: post wait failed - fw not running %#02x:\n", - __func__, fw_status); + if (pdsc->info_regs) { + u8 fw_status = + ioread8(&pdsc->info_regs->fw_status); + + dev_dbg(pdsc->dev, "%s: post wait failed - fw not running %#02x:\n", + __func__, fw_status); + } else { + dev_dbg(pdsc->dev, "%s: post wait failed - BARs not setup\n", + __func__); + } err = -ENXIO; break; } @@ -285,6 +315,8 @@ err_out: queue_work(pdsc->wq, &pdsc->health_work); } + refcount_dec(&pdsc->adminq_refcnt); + return err; } EXPORT_SYMBOL_GPL(pdsc_adminq_post); diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index 0d2091e9eb28..7658a7286767 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -125,7 +125,7 @@ static int pdsc_qcq_intr_alloc(struct pdsc *pdsc, struct pdsc_qcq *qcq) snprintf(name, sizeof(name), "%s-%d-%s", PDS_CORE_DRV_NAME, pdsc->pdev->bus->number, qcq->q.name); - index = pdsc_intr_alloc(pdsc, name, pdsc_adminq_isr, qcq); + index = pdsc_intr_alloc(pdsc, name, pdsc_adminq_isr, pdsc); if (index < 0) return index; qcq->intx = index; @@ -404,10 +404,7 @@ int pdsc_setup(struct pdsc *pdsc, bool init) int numdescs; int err; - if (init) - err = pdsc_dev_init(pdsc); - else - err = pdsc_dev_reinit(pdsc); + err = pdsc_dev_init(pdsc); if (err) return err; @@ -450,6 +447,7 @@ int pdsc_setup(struct pdsc *pdsc, bool init) pdsc_debugfs_add_viftype(pdsc); } + refcount_set(&pdsc->adminq_refcnt, 1); clear_bit(PDSC_S_FW_DEAD, &pdsc->state); return 0; @@ -464,6 +462,8 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing) if (!pdsc->pdev->is_virtfn) pdsc_devcmd_reset(pdsc); + if (pdsc->adminqcq.work.func) + cancel_work_sync(&pdsc->adminqcq.work); pdsc_qcq_free(pdsc, &pdsc->notifyqcq); pdsc_qcq_free(pdsc, &pdsc->adminqcq); @@ -476,10 +476,9 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing) for (i = 0; i < pdsc->nintrs; i++) pdsc_intr_free(pdsc, i); - if (removing) { - kfree(pdsc->intr_info); - pdsc->intr_info = NULL; - } + kfree(pdsc->intr_info); + pdsc->intr_info = NULL; + pdsc->nintrs = 0; } if (pdsc->kern_dbpage) { @@ -487,6 +486,7 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing) pdsc->kern_dbpage = NULL; } + pci_free_irq_vectors(pdsc->pdev); set_bit(PDSC_S_FW_DEAD, &pdsc->state); } @@ -512,6 +512,24 @@ void pdsc_stop(struct pdsc *pdsc) PDS_CORE_INTR_MASK_SET); } +static void pdsc_adminq_wait_and_dec_once_unused(struct pdsc *pdsc) +{ + /* The driver initializes the adminq_refcnt to 1 when the adminq is + * allocated and ready for use. Other users/requesters will increment + * the refcnt while in use. If the refcnt is down to 1 then the adminq + * is not in use and the refcnt can be cleared and adminq freed. Before + * calling this function the driver will set PDSC_S_FW_DEAD, which + * prevent subsequent attempts to use the adminq and increment the + * refcnt to fail. This guarantees that this function will eventually + * exit. + */ + while (!refcount_dec_if_one(&pdsc->adminq_refcnt)) { + dev_dbg_ratelimited(pdsc->dev, "%s: adminq in use\n", + __func__); + cpu_relax(); + } +} + void pdsc_fw_down(struct pdsc *pdsc) { union pds_core_notifyq_comp reset_event = { @@ -527,6 +545,8 @@ void pdsc_fw_down(struct pdsc *pdsc) if (pdsc->pdev->is_virtfn) return; + pdsc_adminq_wait_and_dec_once_unused(pdsc); + /* Notify clients of fw_down */ if (pdsc->fw_reporter) devlink_health_report(pdsc->fw_reporter, "FW down reported", pdsc); @@ -577,7 +597,13 @@ err_out: static void pdsc_check_pci_health(struct pdsc *pdsc) { - u8 fw_status = ioread8(&pdsc->info_regs->fw_status); + u8 fw_status; + + /* some sort of teardown already in progress */ + if (!pdsc->info_regs) + return; + + fw_status = ioread8(&pdsc->info_regs->fw_status); /* is PCI broken? */ if (fw_status != PDS_RC_BAD_PCI) diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h index e35d3e7006bf..110c4b826b22 100644 --- a/drivers/net/ethernet/amd/pds_core/core.h +++ b/drivers/net/ethernet/amd/pds_core/core.h @@ -184,6 +184,7 @@ struct pdsc { struct mutex devcmd_lock; /* lock for dev_cmd operations */ struct mutex config_lock; /* lock for configuration operations */ spinlock_t adminq_lock; /* lock for adminq operations */ + refcount_t adminq_refcnt; struct pds_core_dev_info_regs __iomem *info_regs; struct pds_core_dev_cmd_regs __iomem *cmd_regs; struct pds_core_intr __iomem *intr_ctrl; @@ -280,7 +281,6 @@ int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd, union pds_core_dev_comp *comp, int max_seconds); int pdsc_devcmd_init(struct pdsc *pdsc); int pdsc_devcmd_reset(struct pdsc *pdsc); -int pdsc_dev_reinit(struct pdsc *pdsc); int pdsc_dev_init(struct pdsc *pdsc); void pdsc_reset_prepare(struct pci_dev *pdev); diff --git a/drivers/net/ethernet/amd/pds_core/debugfs.c b/drivers/net/ethernet/amd/pds_core/debugfs.c index 8ec392299b7d..4e8579ca1c8c 100644 --- a/drivers/net/ethernet/amd/pds_core/debugfs.c +++ b/drivers/net/ethernet/amd/pds_core/debugfs.c @@ -64,6 +64,10 @@ DEFINE_SHOW_ATTRIBUTE(identity); void pdsc_debugfs_add_ident(struct pdsc *pdsc) { + /* This file will already exist in the reset flow */ + if (debugfs_lookup("identity", pdsc->dentry)) + return; + debugfs_create_file("identity", 0400, pdsc->dentry, pdsc, &identity_fops); } diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c index 31940b857e0e..e65a1632df50 100644 --- a/drivers/net/ethernet/amd/pds_core/dev.c +++ b/drivers/net/ethernet/amd/pds_core/dev.c @@ -57,6 +57,9 @@ int pdsc_err_to_errno(enum pds_core_status_code code) bool pdsc_is_fw_running(struct pdsc *pdsc) { + if (!pdsc->info_regs) + return false; + pdsc->fw_status = ioread8(&pdsc->info_regs->fw_status); pdsc->last_fw_time = jiffies; pdsc->last_hb = ioread32(&pdsc->info_regs->fw_heartbeat); @@ -182,13 +185,17 @@ int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd, { int err; + if (!pdsc->cmd_regs) + return -ENXIO; + memcpy_toio(&pdsc->cmd_regs->cmd, cmd, sizeof(*cmd)); pdsc_devcmd_dbell(pdsc); err = pdsc_devcmd_wait(pdsc, cmd->opcode, max_seconds); - memcpy_fromio(comp, &pdsc->cmd_regs->comp, sizeof(*comp)); if ((err == -ENXIO || err == -ETIMEDOUT) && pdsc->wq) queue_work(pdsc->wq, &pdsc->health_work); + else + memcpy_fromio(comp, &pdsc->cmd_regs->comp, sizeof(*comp)); return err; } @@ -309,13 +316,6 @@ static int pdsc_identify(struct pdsc *pdsc) return 0; } -int pdsc_dev_reinit(struct pdsc *pdsc) -{ - pdsc_init_devinfo(pdsc); - - return pdsc_identify(pdsc); -} - int pdsc_dev_init(struct pdsc *pdsc) { unsigned int nintrs; diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c index e9948ea5bbcd..54864f27c87a 100644 --- a/drivers/net/ethernet/amd/pds_core/devlink.c +++ b/drivers/net/ethernet/amd/pds_core/devlink.c @@ -111,7 +111,8 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, mutex_lock(&pdsc->devcmd_lock); err = pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout * 2); - memcpy_fromio(&fw_list, pdsc->cmd_regs->data, sizeof(fw_list)); + if (!err) + memcpy_fromio(&fw_list, pdsc->cmd_regs->data, sizeof(fw_list)); mutex_unlock(&pdsc->devcmd_lock); if (err && err != -EIO) return err; diff --git a/drivers/net/ethernet/amd/pds_core/fw.c b/drivers/net/ethernet/amd/pds_core/fw.c index 90a811f3878a..fa626719e68d 100644 --- a/drivers/net/ethernet/amd/pds_core/fw.c +++ b/drivers/net/ethernet/amd/pds_core/fw.c @@ -107,6 +107,9 @@ int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw, dev_info(pdsc->dev, "Installing firmware\n"); + if (!pdsc->cmd_regs) + return -ENXIO; + dl = priv_to_devlink(pdsc); devlink_flash_update_status_notify(dl, "Preparing to flash", NULL, 0, 0); diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c index 3080898d7b95..cdbf053b5376 100644 --- a/drivers/net/ethernet/amd/pds_core/main.c +++ b/drivers/net/ethernet/amd/pds_core/main.c @@ -37,6 +37,11 @@ static void pdsc_unmap_bars(struct pdsc *pdsc) struct pdsc_dev_bar *bars = pdsc->bars; unsigned int i; + pdsc->info_regs = NULL; + pdsc->cmd_regs = NULL; + pdsc->intr_status = NULL; + pdsc->intr_ctrl = NULL; + for (i = 0; i < PDS_CORE_BARS_MAX; i++) { if (bars[i].vaddr) pci_iounmap(pdsc->pdev, bars[i].vaddr); @@ -293,7 +298,7 @@ err_out_stop: err_out_teardown: pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING); err_out_unmap_bars: - del_timer_sync(&pdsc->wdtimer); + timer_shutdown_sync(&pdsc->wdtimer); if (pdsc->wq) destroy_workqueue(pdsc->wq); mutex_destroy(&pdsc->config_lock); @@ -420,7 +425,7 @@ static void pdsc_remove(struct pci_dev *pdev) */ pdsc_sriov_configure(pdev, 0); - del_timer_sync(&pdsc->wdtimer); + timer_shutdown_sync(&pdsc->wdtimer); if (pdsc->wq) destroy_workqueue(pdsc->wq); @@ -433,7 +438,6 @@ static void pdsc_remove(struct pci_dev *pdev) mutex_destroy(&pdsc->config_lock); mutex_destroy(&pdsc->devcmd_lock); - pci_free_irq_vectors(pdev); pdsc_unmap_bars(pdsc); pci_release_regions(pdev); } @@ -445,13 +449,26 @@ static void pdsc_remove(struct pci_dev *pdev) devlink_free(dl); } +static void pdsc_stop_health_thread(struct pdsc *pdsc) +{ + timer_shutdown_sync(&pdsc->wdtimer); + if (pdsc->health_work.func) + cancel_work_sync(&pdsc->health_work); +} + +static void pdsc_restart_health_thread(struct pdsc *pdsc) +{ + timer_setup(&pdsc->wdtimer, pdsc_wdtimer_cb, 0); + mod_timer(&pdsc->wdtimer, jiffies + 1); +} + void pdsc_reset_prepare(struct pci_dev *pdev) { struct pdsc *pdsc = pci_get_drvdata(pdev); + pdsc_stop_health_thread(pdsc); pdsc_fw_down(pdsc); - pci_free_irq_vectors(pdev); pdsc_unmap_bars(pdsc); pci_release_regions(pdev); pci_disable_device(pdev); @@ -486,6 +503,7 @@ void pdsc_reset_done(struct pci_dev *pdev) } pdsc_fw_up(pdsc); + pdsc_restart_health_thread(pdsc); } static const struct pci_error_handlers pdsc_err_handler = { diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c index abd4832e4ed2..5acb3e16b567 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c @@ -993,7 +993,7 @@ int aq_ptp_ring_alloc(struct aq_nic_s *aq_nic) return 0; err_exit_hwts_rx: - aq_ring_free(&aq_ptp->hwts_rx); + aq_ring_hwts_rx_free(&aq_ptp->hwts_rx); err_exit_ptp_rx: aq_ring_free(&aq_ptp->ptp_rx); err_exit_ptp_tx: @@ -1011,7 +1011,7 @@ void aq_ptp_ring_free(struct aq_nic_s *aq_nic) aq_ring_free(&aq_ptp->ptp_tx); aq_ring_free(&aq_ptp->ptp_rx); - aq_ring_free(&aq_ptp->hwts_rx); + aq_ring_hwts_rx_free(&aq_ptp->hwts_rx); aq_ptp_skb_ring_release(&aq_ptp->skb_ring); } diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index cda8597b4e14..f7433abd6591 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -919,6 +919,19 @@ void aq_ring_free(struct aq_ring_s *self) } } +void aq_ring_hwts_rx_free(struct aq_ring_s *self) +{ + if (!self) + return; + + if (self->dx_ring) { + dma_free_coherent(aq_nic_get_dev(self->aq_nic), + self->size * self->dx_size + AQ_CFG_RXDS_DEF, + self->dx_ring, self->dx_ring_pa); + self->dx_ring = NULL; + } +} + unsigned int aq_ring_fill_stats_data(struct aq_ring_s *self, u64 *data) { unsigned int count; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h index 52847310740a..d627ace850ff 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h @@ -210,6 +210,7 @@ int aq_ring_rx_fill(struct aq_ring_s *self); int aq_ring_hwts_rx_alloc(struct aq_ring_s *self, struct aq_nic_s *aq_nic, unsigned int idx, unsigned int size, unsigned int dx_size); +void aq_ring_hwts_rx_free(struct aq_ring_s *self); void aq_ring_hwts_rx_clean(struct aq_ring_s *self, struct aq_nic_s *aq_nic); unsigned int aq_ring_fill_stats_data(struct aq_ring_s *self, u64 *data); diff --git a/drivers/net/ethernet/broadcom/bcm4908_enet.c b/drivers/net/ethernet/broadcom/bcm4908_enet.c index 3e7c8671cd11..72df1bb10172 100644 --- a/drivers/net/ethernet/broadcom/bcm4908_enet.c +++ b/drivers/net/ethernet/broadcom/bcm4908_enet.c @@ -793,5 +793,6 @@ static struct platform_driver bcm4908_enet_driver = { }; module_platform_driver(bcm4908_enet_driver); +MODULE_DESCRIPTION("Broadcom BCM4908 Gigabit Ethernet driver"); MODULE_LICENSE("GPL v2"); MODULE_DEVICE_TABLE(of, bcm4908_enet_of_match); diff --git a/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c b/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c index 9b83d5361699..50b8e97a811d 100644 --- a/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c +++ b/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c @@ -260,4 +260,5 @@ void bcma_mdio_mii_unregister(struct mii_bus *mii_bus) EXPORT_SYMBOL_GPL(bcma_mdio_mii_unregister); MODULE_AUTHOR("RafaÅ‚ MiÅ‚ecki"); +MODULE_DESCRIPTION("Broadcom iProc GBit BCMA MDIO helpers"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/broadcom/bgmac-bcma.c b/drivers/net/ethernet/broadcom/bgmac-bcma.c index 6e4f36aaf5db..36f9bad28e6a 100644 --- a/drivers/net/ethernet/broadcom/bgmac-bcma.c +++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c @@ -362,4 +362,5 @@ module_init(bgmac_init) module_exit(bgmac_exit) MODULE_AUTHOR("RafaÅ‚ MiÅ‚ecki"); +MODULE_DESCRIPTION("Broadcom iProc GBit BCMA interface driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/broadcom/bgmac-platform.c b/drivers/net/ethernet/broadcom/bgmac-platform.c index 0b21fd5bd457..77425c7a32db 100644 --- a/drivers/net/ethernet/broadcom/bgmac-platform.c +++ b/drivers/net/ethernet/broadcom/bgmac-platform.c @@ -298,4 +298,5 @@ static struct platform_driver bgmac_enet_driver = { }; module_platform_driver(bgmac_enet_driver); +MODULE_DESCRIPTION("Broadcom iProc GBit platform interface driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 448a1b90de5e..6ffdc4229407 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -1626,4 +1626,5 @@ int bgmac_enet_resume(struct bgmac *bgmac) EXPORT_SYMBOL_GPL(bgmac_enet_resume); MODULE_AUTHOR("RafaÅ‚ MiÅ‚ecki"); +MODULE_DESCRIPTION("Broadcom iProc GBit driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 0aacd3c6ed5c..39845d556baf 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -3817,7 +3817,7 @@ static int bnxt_alloc_cp_rings(struct bnxt *bp) { bool sh = !!(bp->flags & BNXT_FLAG_SHARED_RINGS); int i, j, rc, ulp_base_vec, ulp_msix; - int tcs = netdev_get_num_tc(bp->dev); + int tcs = bp->num_tc; if (!tcs) tcs = 1; @@ -5935,8 +5935,12 @@ static u16 bnxt_get_max_rss_ring(struct bnxt *bp) int bnxt_get_nr_rss_ctxs(struct bnxt *bp, int rx_rings) { - if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) - return DIV_ROUND_UP(rx_rings, BNXT_RSS_TABLE_ENTRIES_P5); + if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) { + if (!rx_rings) + return 0; + return bnxt_calc_nr_ring_pages(rx_rings - 1, + BNXT_RSS_TABLE_ENTRIES_P5); + } if (BNXT_CHIP_TYPE_NITRO_A0(bp)) return 2; return 1; @@ -6926,7 +6930,7 @@ static int bnxt_hwrm_get_rings(struct bnxt *bp) if (cp < (rx + tx)) { rc = __bnxt_trim_rings(bp, &rx, &tx, cp, false); if (rc) - return rc; + goto get_rings_exit; if (bp->flags & BNXT_FLAG_AGG_RINGS) rx <<= 1; hw_resc->resv_rx_rings = rx; @@ -6938,8 +6942,9 @@ static int bnxt_hwrm_get_rings(struct bnxt *bp) hw_resc->resv_cp_rings = cp; hw_resc->resv_stat_ctxs = stats; } +get_rings_exit: hwrm_req_drop(bp, req); - return 0; + return rc; } int __bnxt_hwrm_get_tx_rings(struct bnxt *bp, u16 fid, int *tx_rings) @@ -7000,10 +7005,11 @@ __bnxt_hwrm_reserve_pf_rings(struct bnxt *bp, int tx_rings, int rx_rings, req->num_rx_rings = cpu_to_le16(rx_rings); if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) { + u16 rss_ctx = bnxt_get_nr_rss_ctxs(bp, ring_grps); + req->num_cmpl_rings = cpu_to_le16(tx_rings + ring_grps); req->num_msix = cpu_to_le16(cp_rings); - req->num_rsscos_ctxs = - cpu_to_le16(DIV_ROUND_UP(ring_grps, 64)); + req->num_rsscos_ctxs = cpu_to_le16(rss_ctx); } else { req->num_cmpl_rings = cpu_to_le16(cp_rings); req->num_hw_ring_grps = cpu_to_le16(ring_grps); @@ -7050,8 +7056,10 @@ __bnxt_hwrm_reserve_vf_rings(struct bnxt *bp, int tx_rings, int rx_rings, req->num_tx_rings = cpu_to_le16(tx_rings); req->num_rx_rings = cpu_to_le16(rx_rings); if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) { + u16 rss_ctx = bnxt_get_nr_rss_ctxs(bp, ring_grps); + req->num_cmpl_rings = cpu_to_le16(tx_rings + ring_grps); - req->num_rsscos_ctxs = cpu_to_le16(DIV_ROUND_UP(ring_grps, 64)); + req->num_rsscos_ctxs = cpu_to_le16(rss_ctx); } else { req->num_cmpl_rings = cpu_to_le16(cp_rings); req->num_hw_ring_grps = cpu_to_le16(ring_grps); @@ -9938,7 +9946,7 @@ static int __bnxt_num_tx_to_cp(struct bnxt *bp, int tx, int tx_sets, int tx_xdp) int bnxt_num_tx_to_cp(struct bnxt *bp, int tx) { - int tcs = netdev_get_num_tc(bp->dev); + int tcs = bp->num_tc; if (!tcs) tcs = 1; @@ -9947,7 +9955,7 @@ int bnxt_num_tx_to_cp(struct bnxt *bp, int tx) static int bnxt_num_cp_to_tx(struct bnxt *bp, int tx_cp) { - int tcs = netdev_get_num_tc(bp->dev); + int tcs = bp->num_tc; return (tx_cp - bp->tx_nr_rings_xdp) * tcs + bp->tx_nr_rings_xdp; @@ -9977,7 +9985,7 @@ static void bnxt_setup_msix(struct bnxt *bp) struct net_device *dev = bp->dev; int tcs, i; - tcs = netdev_get_num_tc(dev); + tcs = bp->num_tc; if (tcs) { int i, off, count; @@ -10009,8 +10017,10 @@ static void bnxt_setup_inta(struct bnxt *bp) { const int len = sizeof(bp->irq_tbl[0].name); - if (netdev_get_num_tc(bp->dev)) + if (bp->num_tc) { netdev_reset_tc(bp->dev); + bp->num_tc = 0; + } snprintf(bp->irq_tbl[0].name, len, "%s-%s-%d", bp->dev->name, "TxRx", 0); @@ -10236,8 +10246,8 @@ static void bnxt_clear_int_mode(struct bnxt *bp) int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init) { - int tcs = netdev_get_num_tc(bp->dev); bool irq_cleared = false; + int tcs = bp->num_tc; int rc; if (!bnxt_need_reserve_rings(bp)) @@ -10263,6 +10273,7 @@ int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init) bp->tx_nr_rings - bp->tx_nr_rings_xdp)) { netdev_err(bp->dev, "tx ring reservation failure\n"); netdev_reset_tc(bp->dev); + bp->num_tc = 0; if (bp->tx_nr_rings_xdp) bp->tx_nr_rings_per_tc = bp->tx_nr_rings_xdp; else @@ -11564,10 +11575,12 @@ int bnxt_half_open_nic(struct bnxt *bp) netdev_err(bp->dev, "bnxt_alloc_mem err: %x\n", rc); goto half_open_err; } + bnxt_init_napi(bp); set_bit(BNXT_STATE_HALF_OPEN, &bp->state); rc = bnxt_init_nic(bp, true); if (rc) { clear_bit(BNXT_STATE_HALF_OPEN, &bp->state); + bnxt_del_napi(bp); netdev_err(bp->dev, "bnxt_init_nic err: %x\n", rc); goto half_open_err; } @@ -11586,6 +11599,7 @@ half_open_err: void bnxt_half_close_nic(struct bnxt *bp) { bnxt_hwrm_resource_free(bp, false, true); + bnxt_del_napi(bp); bnxt_free_skbs(bp); bnxt_free_mem(bp, true); clear_bit(BNXT_STATE_HALF_OPEN, &bp->state); @@ -13232,6 +13246,11 @@ static int bnxt_fw_init_one_p1(struct bnxt *bp) bp->fw_cap = 0; rc = bnxt_hwrm_ver_get(bp); + /* FW may be unresponsive after FLR. FLR must complete within 100 msec + * so wait before continuing with recovery. + */ + if (rc) + msleep(100); bnxt_try_map_fw_health_reg(bp); if (rc) { rc = bnxt_try_recover_fw(bp); @@ -13784,7 +13803,7 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) return -EINVAL; } - if (netdev_get_num_tc(dev) == tc) + if (bp->num_tc == tc) return 0; if (bp->flags & BNXT_FLAG_SHARED_RINGS) @@ -13802,9 +13821,11 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) if (tc) { bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tc; netdev_set_num_tc(dev, tc); + bp->num_tc = tc; } else { bp->tx_nr_rings = bp->tx_nr_rings_per_tc; netdev_reset_tc(dev); + bp->num_tc = 0; } bp->tx_nr_rings += bp->tx_nr_rings_xdp; tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index b8ef1717cb65..47338b48ca20 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -2225,6 +2225,7 @@ struct bnxt { u8 tc_to_qidx[BNXT_MAX_QUEUE]; u8 q_ids[BNXT_MAX_QUEUE]; u8 max_q; + u8 num_tc; unsigned int current_interval; #define BNXT_TIMER_INTERVAL HZ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c index 63e067038385..0dbb880a7aa0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c @@ -228,7 +228,7 @@ static int bnxt_queue_remap(struct bnxt *bp, unsigned int lltc_mask) } } if (bp->ieee_ets) { - int tc = netdev_get_num_tc(bp->dev); + int tc = bp->num_tc; if (!tc) tc = 1; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 27b983c0a8a9..dc4ca706b0e2 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -884,7 +884,7 @@ static void bnxt_get_channels(struct net_device *dev, if (max_tx_sch_inputs) max_tx_rings = min_t(int, max_tx_rings, max_tx_sch_inputs); - tcs = netdev_get_num_tc(dev); + tcs = bp->num_tc; tx_grps = max(tcs, 1); if (bp->tx_nr_rings_xdp) tx_grps++; @@ -944,7 +944,7 @@ static int bnxt_set_channels(struct net_device *dev, if (channel->combined_count) sh = true; - tcs = netdev_get_num_tc(dev); + tcs = bp->num_tc; req_tx_rings = sh ? channel->combined_count : channel->tx_count; req_rx_rings = sh ? channel->combined_count : channel->rx_count; @@ -1574,7 +1574,8 @@ u32 bnxt_get_rxfh_indir_size(struct net_device *dev) struct bnxt *bp = netdev_priv(dev); if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) - return ALIGN(bp->rx_nr_rings, BNXT_RSS_TABLE_ENTRIES_P5); + return bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings) * + BNXT_RSS_TABLE_ENTRIES_P5; return HW_HASH_INDEX_SIZE; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c index adad188e38b8..cc07660330f5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c @@ -684,7 +684,7 @@ static void bnxt_stamp_tx_skb(struct bnxt *bp, struct sk_buff *skb) timestamp.hwtstamp = ns_to_ktime(ns); skb_tstamp_tx(ptp->tx_skb, ×tamp); } else { - netdev_WARN_ONCE(bp->dev, + netdev_warn_once(bp->dev, "TS query for TX timer failed rc = %x\n", rc); } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index c2b25fc623ec..4079538bc310 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -407,7 +407,7 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog) if (prog) tx_xdp = bp->rx_nr_rings; - tc = netdev_get_num_tc(dev); + tc = bp->num_tc; if (!tc) tc = 1; rc = bnxt_check_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings, diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index 068ed52b66c9..b3c81a2e9d46 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -1490,7 +1490,7 @@ int cn23xx_get_vf_stats(struct octeon_device *oct, int vfidx, mbox_cmd.q_no = vfidx * oct->sriov_info.rings_per_vf; mbox_cmd.recv_len = 0; mbox_cmd.recv_status = 0; - mbox_cmd.fn = (octeon_mbox_callback_t)cn23xx_get_vf_stats_callback; + mbox_cmd.fn = cn23xx_get_vf_stats_callback; ctx.stats = stats; atomic_set(&ctx.status, 0); mbox_cmd.fn_arg = (void *)&ctx; diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c index dd5d80fee24f..d2fcb3da484e 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.c @@ -429,7 +429,7 @@ int cn23xx_octeon_pfvf_handshake(struct octeon_device *oct) mbox_cmd.q_no = 0; mbox_cmd.recv_len = 0; mbox_cmd.recv_status = 0; - mbox_cmd.fn = (octeon_mbox_callback_t)octeon_pfvf_hs_callback; + mbox_cmd.fn = octeon_pfvf_hs_callback; mbox_cmd.fn_arg = &status; octeon_mbox_write(oct, &mbox_cmd); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 9cc6303c82ff..f38d31bfab1b 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -27,6 +27,7 @@ #include "octeon_network.h" MODULE_AUTHOR("Cavium Networks, <support@cavium.com>"); +MODULE_DESCRIPTION("Cavium LiquidIO Intelligent Server Adapter Core"); MODULE_LICENSE("GPL"); /* OOM task polling interval */ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.h b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.h index d92bd7e16477..9ac85d22c615 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.h @@ -57,7 +57,10 @@ union octeon_mbox_message { } s; }; -typedef void (*octeon_mbox_callback_t)(void *, void *, void *); +struct octeon_mbox_cmd; + +typedef void (*octeon_mbox_callback_t)(struct octeon_device *, + struct octeon_mbox_cmd *, void *); struct octeon_mbox_cmd { union octeon_mbox_message msg; diff --git a/drivers/net/ethernet/cirrus/ep93xx_eth.c b/drivers/net/ethernet/cirrus/ep93xx_eth.c index 1c2a540db13d..1f495cfd7959 100644 --- a/drivers/net/ethernet/cirrus/ep93xx_eth.c +++ b/drivers/net/ethernet/cirrus/ep93xx_eth.c @@ -868,5 +868,6 @@ static struct platform_driver ep93xx_eth_driver = { module_platform_driver(ep93xx_eth_driver); +MODULE_DESCRIPTION("Cirrus EP93xx Ethernet driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:ep93xx-eth"); diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c index df40c720e7b2..64eadd320798 100644 --- a/drivers/net/ethernet/engleder/tsnep_main.c +++ b/drivers/net/ethernet/engleder/tsnep_main.c @@ -719,17 +719,25 @@ static void tsnep_xdp_xmit_flush(struct tsnep_tx *tx) static bool tsnep_xdp_xmit_back(struct tsnep_adapter *adapter, struct xdp_buff *xdp, - struct netdev_queue *tx_nq, struct tsnep_tx *tx) + struct netdev_queue *tx_nq, struct tsnep_tx *tx, + bool zc) { struct xdp_frame *xdpf = xdp_convert_buff_to_frame(xdp); bool xmit; + u32 type; if (unlikely(!xdpf)) return false; + /* no page pool for zero copy */ + if (zc) + type = TSNEP_TX_TYPE_XDP_NDO; + else + type = TSNEP_TX_TYPE_XDP_TX; + __netif_tx_lock(tx_nq, smp_processor_id()); - xmit = tsnep_xdp_xmit_frame_ring(xdpf, tx, TSNEP_TX_TYPE_XDP_TX); + xmit = tsnep_xdp_xmit_frame_ring(xdpf, tx, type); /* Avoid transmit queue timeout since we share it with the slow path */ if (xmit) @@ -1273,7 +1281,7 @@ static bool tsnep_xdp_run_prog(struct tsnep_rx *rx, struct bpf_prog *prog, case XDP_PASS: return false; case XDP_TX: - if (!tsnep_xdp_xmit_back(rx->adapter, xdp, tx_nq, tx)) + if (!tsnep_xdp_xmit_back(rx->adapter, xdp, tx_nq, tx, false)) goto out_failure; *status |= TSNEP_XDP_TX; return true; @@ -1323,7 +1331,7 @@ static bool tsnep_xdp_run_prog_zc(struct tsnep_rx *rx, struct bpf_prog *prog, case XDP_PASS: return false; case XDP_TX: - if (!tsnep_xdp_xmit_back(rx->adapter, xdp, tx_nq, tx)) + if (!tsnep_xdp_xmit_back(rx->adapter, xdp, tx_nq, tx, true)) goto out_failure; *status |= TSNEP_XDP_TX; return true; @@ -1485,7 +1493,7 @@ static int tsnep_rx_poll(struct tsnep_rx *rx, struct napi_struct *napi, xdp_prepare_buff(&xdp, page_address(entry->page), XDP_PACKET_HEADROOM + TSNEP_RX_INLINE_METADATA_SIZE, - length, false); + length - ETH_FCS_LEN, false); consume = tsnep_xdp_run_prog(rx, prog, &xdp, &xdp_status, tx_nq, tx); @@ -1568,7 +1576,7 @@ static int tsnep_rx_poll_zc(struct tsnep_rx *rx, struct napi_struct *napi, prefetch(entry->xdp->data); length = __le32_to_cpu(entry->desc_wb->properties) & TSNEP_DESC_LENGTH_MASK; - xsk_buff_set_size(entry->xdp, length); + xsk_buff_set_size(entry->xdp, length - ETH_FCS_LEN); xsk_buff_dma_sync_for_cpu(entry->xdp, rx->xsk_pool); /* RX metadata with timestamps is in front of actual data, @@ -1762,6 +1770,19 @@ static void tsnep_rx_reopen_xsk(struct tsnep_rx *rx) allocated--; } } + + /* set need wakeup flag immediately if ring is not filled completely, + * first polling would be too late as need wakeup signalisation would + * be delayed for an indefinite time + */ + if (xsk_uses_need_wakeup(rx->xsk_pool)) { + int desc_available = tsnep_rx_desc_available(rx); + + if (desc_available) + xsk_set_rx_need_wakeup(rx->xsk_pool); + else + xsk_clear_rx_need_wakeup(rx->xsk_pool); + } } static bool tsnep_pending(struct tsnep_queue *queue) diff --git a/drivers/net/ethernet/ezchip/nps_enet.c b/drivers/net/ethernet/ezchip/nps_enet.c index 07c2b701b5fa..9ebe751c1df0 100644 --- a/drivers/net/ethernet/ezchip/nps_enet.c +++ b/drivers/net/ethernet/ezchip/nps_enet.c @@ -661,4 +661,5 @@ static struct platform_driver nps_enet_driver = { module_platform_driver(nps_enet_driver); MODULE_AUTHOR("EZchip Semiconductor"); +MODULE_DESCRIPTION("EZchip NPS Ethernet driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index cffbf27c4656..bfdbdab443ae 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -3216,4 +3216,5 @@ void enetc_pci_remove(struct pci_dev *pdev) } EXPORT_SYMBOL_GPL(enetc_pci_remove); +MODULE_DESCRIPTION("NXP ENETC Ethernet driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index d42594f32275..432523b2c789 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2036,6 +2036,7 @@ static void fec_enet_adjust_link(struct net_device *ndev) /* if any of the above changed restart the FEC */ if (status_change) { + netif_stop_queue(ndev); napi_disable(&fep->napi); netif_tx_lock_bh(ndev); fec_restart(ndev); @@ -2045,6 +2046,7 @@ static void fec_enet_adjust_link(struct net_device *ndev) } } else { if (fep->link) { + netif_stop_queue(ndev); napi_disable(&fep->napi); netif_tx_lock_bh(ndev); fec_stop(ndev); @@ -4769,4 +4771,5 @@ static struct platform_driver fec_driver = { module_platform_driver(fec_driver); +MODULE_DESCRIPTION("NXP Fast Ethernet Controller (FEC) driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c index 70dd982a5edc..026f7270a54d 100644 --- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c +++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c @@ -531,4 +531,5 @@ static struct platform_driver fsl_pq_mdio_driver = { module_platform_driver(fsl_pq_mdio_driver); +MODULE_DESCRIPTION("Freescale PQ MDIO helpers"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index 7a8dc5386fff..76615d47e055 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -356,7 +356,7 @@ static enum pkt_hash_types gve_rss_type(__be16 pkt_flags) static struct sk_buff *gve_rx_add_frags(struct napi_struct *napi, struct gve_rx_slot_page_info *page_info, - u16 packet_buffer_size, u16 len, + unsigned int truesize, u16 len, struct gve_rx_ctx *ctx) { u32 offset = page_info->page_offset + page_info->pad; @@ -389,10 +389,10 @@ static struct sk_buff *gve_rx_add_frags(struct napi_struct *napi, if (skb != ctx->skb_head) { ctx->skb_head->len += len; ctx->skb_head->data_len += len; - ctx->skb_head->truesize += packet_buffer_size; + ctx->skb_head->truesize += truesize; } skb_add_rx_frag(skb, num_frags, page_info->page, - offset, len, packet_buffer_size); + offset, len, truesize); return ctx->skb_head; } @@ -486,7 +486,7 @@ static struct sk_buff *gve_rx_copy_to_pool(struct gve_rx_ring *rx, memcpy(alloc_page_info.page_address, src, page_info->pad + len); skb = gve_rx_add_frags(napi, &alloc_page_info, - rx->packet_buffer_size, + PAGE_SIZE, len, ctx); u64_stats_update_begin(&rx->statss); diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index a187582d2299..ba9c19e6994c 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -360,23 +360,43 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca); * As a result, a shift of INCVALUE_SHIFT_n is used to fit a value of * INCVALUE_n into the TIMINCA register allowing 32+8+(24-INCVALUE_SHIFT_n) * bits to count nanoseconds leaving the rest for fractional nonseconds. + * + * Any given INCVALUE also has an associated maximum adjustment value. This + * maximum adjustment value is the largest increase (or decrease) which can be + * safely applied without overflowing the INCVALUE. Since INCVALUE has + * a maximum range of 24 bits, its largest value is 0xFFFFFF. + * + * To understand where the maximum value comes from, consider the following + * equation: + * + * new_incval = base_incval + (base_incval * adjustment) / 1billion + * + * To avoid overflow that means: + * max_incval = base_incval + (base_incval * max_adj) / billion + * + * Re-arranging: + * max_adj = floor(((max_incval - base_incval) * 1billion) / 1billion) */ #define INCVALUE_96MHZ 125 #define INCVALUE_SHIFT_96MHZ 17 #define INCPERIOD_SHIFT_96MHZ 2 #define INCPERIOD_96MHZ (12 >> INCPERIOD_SHIFT_96MHZ) +#define MAX_PPB_96MHZ 23999900 /* 23,999,900 ppb */ #define INCVALUE_25MHZ 40 #define INCVALUE_SHIFT_25MHZ 18 #define INCPERIOD_25MHZ 1 +#define MAX_PPB_25MHZ 599999900 /* 599,999,900 ppb */ #define INCVALUE_24MHZ 125 #define INCVALUE_SHIFT_24MHZ 14 #define INCPERIOD_24MHZ 3 +#define MAX_PPB_24MHZ 999999999 /* 999,999,999 ppb */ #define INCVALUE_38400KHZ 26 #define INCVALUE_SHIFT_38400KHZ 19 #define INCPERIOD_38400KHZ 1 +#define MAX_PPB_38400KHZ 230769100 /* 230,769,100 ppb */ /* Another drawback of scaling the incvalue by a large factor is the * 64-bit SYSTIM register overflows more quickly. This is dealt with diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c index 02d871bc112a..bbcfd529399b 100644 --- a/drivers/net/ethernet/intel/e1000e/ptp.c +++ b/drivers/net/ethernet/intel/e1000e/ptp.c @@ -280,8 +280,17 @@ void e1000e_ptp_init(struct e1000_adapter *adapter) switch (hw->mac.type) { case e1000_pch2lan: + adapter->ptp_clock_info.max_adj = MAX_PPB_96MHZ; + break; case e1000_pch_lpt: + if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) + adapter->ptp_clock_info.max_adj = MAX_PPB_96MHZ; + else + adapter->ptp_clock_info.max_adj = MAX_PPB_25MHZ; + break; case e1000_pch_spt: + adapter->ptp_clock_info.max_adj = MAX_PPB_24MHZ; + break; case e1000_pch_cnp: case e1000_pch_tgp: case e1000_pch_adp: @@ -289,15 +298,14 @@ void e1000e_ptp_init(struct e1000_adapter *adapter) case e1000_pch_lnp: case e1000_pch_ptp: case e1000_pch_nvp: - if ((hw->mac.type < e1000_pch_lpt) || - (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI)) { - adapter->ptp_clock_info.max_adj = 24000000 - 1; - break; - } - fallthrough; + if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) + adapter->ptp_clock_info.max_adj = MAX_PPB_24MHZ; + else + adapter->ptp_clock_info.max_adj = MAX_PPB_38400KHZ; + break; case e1000_82574: case e1000_82583: - adapter->ptp_clock_info.max_adj = 600000000 - 1; + adapter->ptp_clock_info.max_adj = MAX_PPB_25MHZ; break; default: break; diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 18a1c3b6d72c..c8f35d4de271 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -5,6 +5,7 @@ #define _I40E_ADMINQ_CMD_H_ #include <linux/bits.h> +#include <linux/types.h> /* This header file defines the i40e Admin Queue commands and is shared between * i40e Firmware and Software. diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.h b/drivers/net/ethernet/intel/i40e/i40e_dcb.h index 6b60dc9b7736..d76497566e40 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.h +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.h @@ -43,7 +43,7 @@ #define I40E_LLDP_TLV_SUBTYPE_SHIFT 0 #define I40E_LLDP_TLV_SUBTYPE_MASK (0xFF << I40E_LLDP_TLV_SUBTYPE_SHIFT) #define I40E_LLDP_TLV_OUI_SHIFT 8 -#define I40E_LLDP_TLV_OUI_MASK (0xFFFFFF << I40E_LLDP_TLV_OUI_SHIFT) +#define I40E_LLDP_TLV_OUI_MASK (0xFFFFFFU << I40E_LLDP_TLV_OUI_SHIFT) /* Defines for IEEE ETS TLV */ #define I40E_IEEE_ETS_MAXTC_SHIFT 0 diff --git a/drivers/net/ethernet/intel/i40e/i40e_diag.h b/drivers/net/ethernet/intel/i40e/i40e_diag.h index ece3a6b9a5c6..ab20202a3da3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_diag.h +++ b/drivers/net/ethernet/intel/i40e/i40e_diag.h @@ -4,6 +4,7 @@ #ifndef _I40E_DIAG_H_ #define _I40E_DIAG_H_ +#include <linux/types.h> #include "i40e_adminq_cmd.h" /* forward-declare the HW struct for the compiler */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index ae8f9f135725..6e7fd473abfd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3588,40 +3588,55 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) struct i40e_hmc_obj_rxq rx_ctx; int err = 0; bool ok; - int ret; bitmap_zero(ring->state, __I40E_RING_STATE_NBITS); /* clear the context structure first */ memset(&rx_ctx, 0, sizeof(rx_ctx)); - if (ring->vsi->type == I40E_VSI_MAIN) - xdp_rxq_info_unreg_mem_model(&ring->xdp_rxq); + ring->rx_buf_len = vsi->rx_buf_len; + + /* XDP RX-queue info only needed for RX rings exposed to XDP */ + if (ring->vsi->type != I40E_VSI_MAIN) + goto skip; + + if (!xdp_rxq_info_is_reg(&ring->xdp_rxq)) { + err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev, + ring->queue_index, + ring->q_vector->napi.napi_id, + ring->rx_buf_len); + if (err) + return err; + } ring->xsk_pool = i40e_xsk_pool(ring); if (ring->xsk_pool) { - ring->rx_buf_len = - xsk_pool_get_rx_frame_size(ring->xsk_pool); - ret = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq, + xdp_rxq_info_unreg(&ring->xdp_rxq); + ring->rx_buf_len = xsk_pool_get_rx_frame_size(ring->xsk_pool); + err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev, + ring->queue_index, + ring->q_vector->napi.napi_id, + ring->rx_buf_len); + if (err) + return err; + err = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq, MEM_TYPE_XSK_BUFF_POOL, NULL); - if (ret) - return ret; + if (err) + return err; dev_info(&vsi->back->pdev->dev, "Registered XDP mem model MEM_TYPE_XSK_BUFF_POOL on Rx ring %d\n", ring->queue_index); } else { - ring->rx_buf_len = vsi->rx_buf_len; - if (ring->vsi->type == I40E_VSI_MAIN) { - ret = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq, - MEM_TYPE_PAGE_SHARED, - NULL); - if (ret) - return ret; - } + err = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq, + MEM_TYPE_PAGE_SHARED, + NULL); + if (err) + return err; } +skip: xdp_init_buff(&ring->xdp, i40e_rx_pg_size(ring) / 2, &ring->xdp_rxq); rx_ctx.dbuff = DIV_ROUND_UP(ring->rx_buf_len, diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 971ba3322038..0d7177083708 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1548,7 +1548,6 @@ void i40e_free_rx_resources(struct i40e_ring *rx_ring) int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring) { struct device *dev = rx_ring->dev; - int err; u64_stats_init(&rx_ring->syncp); @@ -1569,14 +1568,6 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring) rx_ring->next_to_process = 0; rx_ring->next_to_use = 0; - /* XDP RX-queue info only needed for RX rings exposed to XDP */ - if (rx_ring->vsi->type == I40E_VSI_MAIN) { - err = xdp_rxq_info_reg(&rx_ring->xdp_rxq, rx_ring->netdev, - rx_ring->queue_index, rx_ring->q_vector->napi.napi_id); - if (err < 0) - return err; - } - rx_ring->xdp_prog = rx_ring->vsi->xdp_prog; rx_ring->rx_bi = @@ -2087,7 +2078,8 @@ static void i40e_put_rx_buffer(struct i40e_ring *rx_ring, static void i40e_process_rx_buffs(struct i40e_ring *rx_ring, int xdp_res, struct xdp_buff *xdp) { - u32 next = rx_ring->next_to_clean; + u32 nr_frags = xdp_get_shared_info_from_buff(xdp)->nr_frags; + u32 next = rx_ring->next_to_clean, i = 0; struct i40e_rx_buffer *rx_buffer; xdp->flags = 0; @@ -2100,10 +2092,10 @@ static void i40e_process_rx_buffs(struct i40e_ring *rx_ring, int xdp_res, if (!rx_buffer->page) continue; - if (xdp_res == I40E_XDP_CONSUMED) - rx_buffer->pagecnt_bias++; - else + if (xdp_res != I40E_XDP_CONSUMED) i40e_rx_buffer_flip(rx_buffer, xdp->frame_sz); + else if (i++ <= nr_frags) + rx_buffer->pagecnt_bias++; /* EOP buffer will be put in i40e_clean_rx_irq() */ if (next == rx_ring->next_to_process) @@ -2117,20 +2109,20 @@ static void i40e_process_rx_buffs(struct i40e_ring *rx_ring, int xdp_res, * i40e_construct_skb - Allocate skb and populate it * @rx_ring: rx descriptor ring to transact packets on * @xdp: xdp_buff pointing to the data - * @nr_frags: number of buffers for the packet * * This function allocates an skb. It then populates it with the page * data from the current receive descriptor, taking care to set up the * skb correctly. */ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, - struct xdp_buff *xdp, - u32 nr_frags) + struct xdp_buff *xdp) { unsigned int size = xdp->data_end - xdp->data; struct i40e_rx_buffer *rx_buffer; + struct skb_shared_info *sinfo; unsigned int headlen; struct sk_buff *skb; + u32 nr_frags = 0; /* prefetch first cache line of first page */ net_prefetch(xdp->data); @@ -2168,6 +2160,10 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, memcpy(__skb_put(skb, headlen), xdp->data, ALIGN(headlen, sizeof(long))); + if (unlikely(xdp_buff_has_frags(xdp))) { + sinfo = xdp_get_shared_info_from_buff(xdp); + nr_frags = sinfo->nr_frags; + } rx_buffer = i40e_rx_bi(rx_ring, rx_ring->next_to_clean); /* update all of the pointers */ size -= headlen; @@ -2187,9 +2183,8 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, } if (unlikely(xdp_buff_has_frags(xdp))) { - struct skb_shared_info *sinfo, *skinfo = skb_shinfo(skb); + struct skb_shared_info *skinfo = skb_shinfo(skb); - sinfo = xdp_get_shared_info_from_buff(xdp); memcpy(&skinfo->frags[skinfo->nr_frags], &sinfo->frags[0], sizeof(skb_frag_t) * nr_frags); @@ -2212,17 +2207,17 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, * i40e_build_skb - Build skb around an existing buffer * @rx_ring: Rx descriptor ring to transact packets on * @xdp: xdp_buff pointing to the data - * @nr_frags: number of buffers for the packet * * This function builds an skb around an existing Rx buffer, taking care * to set up the skb correctly and avoid any memcpy overhead. */ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, - struct xdp_buff *xdp, - u32 nr_frags) + struct xdp_buff *xdp) { unsigned int metasize = xdp->data - xdp->data_meta; + struct skb_shared_info *sinfo; struct sk_buff *skb; + u32 nr_frags; /* Prefetch first cache line of first page. If xdp->data_meta * is unused, this points exactly as xdp->data, otherwise we @@ -2231,6 +2226,11 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, */ net_prefetch(xdp->data_meta); + if (unlikely(xdp_buff_has_frags(xdp))) { + sinfo = xdp_get_shared_info_from_buff(xdp); + nr_frags = sinfo->nr_frags; + } + /* build an skb around the page buffer */ skb = napi_build_skb(xdp->data_hard_start, xdp->frame_sz); if (unlikely(!skb)) @@ -2243,9 +2243,6 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, skb_metadata_set(skb, metasize); if (unlikely(xdp_buff_has_frags(xdp))) { - struct skb_shared_info *sinfo; - - sinfo = xdp_get_shared_info_from_buff(xdp); xdp_update_skb_shared_info(skb, nr_frags, sinfo->xdp_frags_size, nr_frags * xdp->frame_sz, @@ -2589,9 +2586,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, total_rx_bytes += size; } else { if (ring_uses_build_skb(rx_ring)) - skb = i40e_build_skb(rx_ring, xdp, nfrags); + skb = i40e_build_skb(rx_ring, xdp); else - skb = i40e_construct_skb(rx_ring, xdp, nfrags); + skb = i40e_construct_skb(rx_ring, xdp); /* drop if we failed to retrieve a buffer */ if (!skb) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c b/drivers/net/ethernet/intel/i40e/i40e_xsk.c index af7d5fa6cdc1..11500003af0d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c +++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c @@ -414,7 +414,8 @@ i40e_add_xsk_frag(struct i40e_ring *rx_ring, struct xdp_buff *first, } __skb_fill_page_desc_noacc(sinfo, sinfo->nr_frags++, - virt_to_page(xdp->data_hard_start), 0, size); + virt_to_page(xdp->data_hard_start), + XDP_PACKET_HEADROOM, size); sinfo->xdp_frags_size += size; xsk_buff_add_frag(xdp); @@ -498,7 +499,6 @@ int i40e_clean_rx_irq_zc(struct i40e_ring *rx_ring, int budget) xdp_res = i40e_run_xdp_zc(rx_ring, first, xdp_prog); i40e_handle_xdp_result_zc(rx_ring, first, rx_desc, &rx_packets, &rx_bytes, xdp_res, &failure); - first->flags = 0; next_to_clean = next_to_process; if (failure) break; diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c index 533b923cae2d..7ac847718882 100644 --- a/drivers/net/ethernet/intel/ice/ice_base.c +++ b/drivers/net/ethernet/intel/ice/ice_base.c @@ -547,19 +547,27 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) ring->rx_buf_len = ring->vsi->rx_buf_len; if (ring->vsi->type == ICE_VSI_PF) { - if (!xdp_rxq_info_is_reg(&ring->xdp_rxq)) - /* coverity[check_return] */ - __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev, - ring->q_index, - ring->q_vector->napi.napi_id, - ring->vsi->rx_buf_len); + if (!xdp_rxq_info_is_reg(&ring->xdp_rxq)) { + err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev, + ring->q_index, + ring->q_vector->napi.napi_id, + ring->rx_buf_len); + if (err) + return err; + } ring->xsk_pool = ice_xsk_pool(ring); if (ring->xsk_pool) { - xdp_rxq_info_unreg_mem_model(&ring->xdp_rxq); + xdp_rxq_info_unreg(&ring->xdp_rxq); ring->rx_buf_len = xsk_pool_get_rx_frame_size(ring->xsk_pool); + err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev, + ring->q_index, + ring->q_vector->napi.napi_id, + ring->rx_buf_len); + if (err) + return err; err = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq, MEM_TYPE_XSK_BUFF_POOL, NULL); @@ -571,13 +579,14 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) dev_info(dev, "Registered XDP mem model MEM_TYPE_XSK_BUFF_POOL on Rx ring %d\n", ring->q_index); } else { - if (!xdp_rxq_info_is_reg(&ring->xdp_rxq)) - /* coverity[check_return] */ - __xdp_rxq_info_reg(&ring->xdp_rxq, - ring->netdev, - ring->q_index, - ring->q_vector->napi.napi_id, - ring->vsi->rx_buf_len); + if (!xdp_rxq_info_is_reg(&ring->xdp_rxq)) { + err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev, + ring->q_index, + ring->q_vector->napi.napi_id, + ring->rx_buf_len); + if (err) + return err; + } err = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq, MEM_TYPE_PAGE_SHARED, diff --git a/drivers/net/ethernet/intel/ice/ice_osdep.h b/drivers/net/ethernet/intel/ice/ice_osdep.h index 82bc54fec7f3..a2562f04267f 100644 --- a/drivers/net/ethernet/intel/ice/ice_osdep.h +++ b/drivers/net/ethernet/intel/ice/ice_osdep.h @@ -24,7 +24,7 @@ #define rd64(a, reg) readq((a)->hw_addr + (reg)) #define ice_flush(a) rd32((a), GLGEN_STAT) -#define ICE_M(m, s) ((m) << (s)) +#define ICE_M(m, s) ((m ## U) << (s)) struct ice_dma_mem { void *va; diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index 74d13cc5a3a7..97d41d6ebf1f 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -513,11 +513,6 @@ int ice_setup_rx_ring(struct ice_rx_ring *rx_ring) if (ice_is_xdp_ena_vsi(rx_ring->vsi)) WRITE_ONCE(rx_ring->xdp_prog, rx_ring->vsi->xdp_prog); - if (rx_ring->vsi->type == ICE_VSI_PF && - !xdp_rxq_info_is_reg(&rx_ring->xdp_rxq)) - if (xdp_rxq_info_reg(&rx_ring->xdp_rxq, rx_ring->netdev, - rx_ring->q_index, rx_ring->q_vector->napi.napi_id)) - goto err; return 0; err: @@ -603,9 +598,7 @@ out_failure: ret = ICE_XDP_CONSUMED; } exit: - rx_buf->act = ret; - if (unlikely(xdp_buff_has_frags(xdp))) - ice_set_rx_bufs_act(xdp, rx_ring, ret); + ice_set_rx_bufs_act(xdp, rx_ring, ret); } /** @@ -893,14 +886,17 @@ ice_add_xdp_frag(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp, } if (unlikely(sinfo->nr_frags == MAX_SKB_FRAGS)) { - if (unlikely(xdp_buff_has_frags(xdp))) - ice_set_rx_bufs_act(xdp, rx_ring, ICE_XDP_CONSUMED); + ice_set_rx_bufs_act(xdp, rx_ring, ICE_XDP_CONSUMED); return -ENOMEM; } __skb_fill_page_desc_noacc(sinfo, sinfo->nr_frags++, rx_buf->page, rx_buf->page_offset, size); sinfo->xdp_frags_size += size; + /* remember frag count before XDP prog execution; bpf_xdp_adjust_tail() + * can pop off frags but driver has to handle it on its own + */ + rx_ring->nr_frags = sinfo->nr_frags; if (page_is_pfmemalloc(rx_buf->page)) xdp_buff_set_frag_pfmemalloc(xdp); @@ -1251,6 +1247,7 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) xdp->data = NULL; rx_ring->first_desc = ntc; + rx_ring->nr_frags = 0; continue; construct_skb: if (likely(ice_ring_uses_build_skb(rx_ring))) @@ -1266,10 +1263,12 @@ construct_skb: ICE_XDP_CONSUMED); xdp->data = NULL; rx_ring->first_desc = ntc; + rx_ring->nr_frags = 0; break; } xdp->data = NULL; rx_ring->first_desc = ntc; + rx_ring->nr_frags = 0; stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_RXE_S); if (unlikely(ice_test_staterr(rx_desc->wb.status_error0, diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.h b/drivers/net/ethernet/intel/ice/ice_txrx.h index b3379ff73674..af955b0e5dc5 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.h +++ b/drivers/net/ethernet/intel/ice/ice_txrx.h @@ -358,6 +358,7 @@ struct ice_rx_ring { struct ice_tx_ring *xdp_ring; struct ice_rx_ring *next; /* pointer to next ring in q_vector */ struct xsk_buff_pool *xsk_pool; + u32 nr_frags; dma_addr_t dma; /* physical address of ring */ u16 rx_buf_len; u8 dcb_tc; /* Traffic class of ring */ diff --git a/drivers/net/ethernet/intel/ice/ice_txrx_lib.h b/drivers/net/ethernet/intel/ice/ice_txrx_lib.h index 762047508619..afcead4baef4 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_txrx_lib.h @@ -12,26 +12,39 @@ * act: action to store onto Rx buffers related to XDP buffer parts * * Set action that should be taken before putting Rx buffer from first frag - * to one before last. Last one is handled by caller of this function as it - * is the EOP frag that is currently being processed. This function is - * supposed to be called only when XDP buffer contains frags. + * to the last. */ static inline void ice_set_rx_bufs_act(struct xdp_buff *xdp, const struct ice_rx_ring *rx_ring, const unsigned int act) { - const struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); - u32 first = rx_ring->first_desc; - u32 nr_frags = sinfo->nr_frags; + u32 sinfo_frags = xdp_get_shared_info_from_buff(xdp)->nr_frags; + u32 nr_frags = rx_ring->nr_frags + 1; + u32 idx = rx_ring->first_desc; u32 cnt = rx_ring->count; struct ice_rx_buf *buf; for (int i = 0; i < nr_frags; i++) { - buf = &rx_ring->rx_buf[first]; + buf = &rx_ring->rx_buf[idx]; buf->act = act; - if (++first == cnt) - first = 0; + if (++idx == cnt) + idx = 0; + } + + /* adjust pagecnt_bias on frags freed by XDP prog */ + if (sinfo_frags < rx_ring->nr_frags && act == ICE_XDP_CONSUMED) { + u32 delta = rx_ring->nr_frags - sinfo_frags; + + while (delta) { + if (idx == 0) + idx = cnt - 1; + else + idx--; + buf = &rx_ring->rx_buf[idx]; + buf->pagecnt_bias--; + delta--; + } } } diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index 41ab6d7bbd9e..a508e917ce5f 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -1072,7 +1072,7 @@ struct ice_aq_get_set_rss_lut_params { #define ICE_OROM_VER_BUILD_SHIFT 8 #define ICE_OROM_VER_BUILD_MASK (0xffff << ICE_OROM_VER_BUILD_SHIFT) #define ICE_OROM_VER_SHIFT 24 -#define ICE_OROM_VER_MASK (0xff << ICE_OROM_VER_SHIFT) +#define ICE_OROM_VER_MASK (0xffU << ICE_OROM_VER_SHIFT) #define ICE_SR_PFA_PTR 0x40 #define ICE_SR_1ST_NVM_BANK_PTR 0x42 #define ICE_SR_NVM_BANK_SIZE 0x43 diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.c b/drivers/net/ethernet/intel/ice/ice_xsk.c index 5d1ae8e4058a..8b81a1677045 100644 --- a/drivers/net/ethernet/intel/ice/ice_xsk.c +++ b/drivers/net/ethernet/intel/ice/ice_xsk.c @@ -825,7 +825,8 @@ ice_add_xsk_frag(struct ice_rx_ring *rx_ring, struct xdp_buff *first, } __skb_fill_page_desc_noacc(sinfo, sinfo->nr_frags++, - virt_to_page(xdp->data_hard_start), 0, size); + virt_to_page(xdp->data_hard_start), + XDP_PACKET_HEADROOM, size); sinfo->xdp_frags_size += size; xsk_buff_add_frag(xdp); @@ -895,7 +896,6 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget) if (!first) { first = xdp; - xdp_buff_clear_frags_flag(first); } else if (ice_add_xsk_frag(rx_ring, first, xdp, size)) { break; } diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c index 5fea2fd957eb..58179bd733ff 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_lib.c +++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c @@ -783,6 +783,8 @@ static int idpf_cfg_netdev(struct idpf_vport *vport) /* setup watchdog timeout value to be 5 second */ netdev->watchdog_timeo = 5 * HZ; + netdev->dev_port = idx; + /* configure default MTU size */ netdev->min_mtu = ETH_MIN_MTU; netdev->max_mtu = vport->max_mtu; diff --git a/drivers/net/ethernet/intel/idpf/virtchnl2.h b/drivers/net/ethernet/intel/idpf/virtchnl2.h index 8dc837889723..4a3c4454d25a 100644 --- a/drivers/net/ethernet/intel/idpf/virtchnl2.h +++ b/drivers/net/ethernet/intel/idpf/virtchnl2.h @@ -978,7 +978,7 @@ struct virtchnl2_ptype { u8 proto_id_count; __le16 pad; __le16 proto_id[]; -}; +} __packed __aligned(2); VIRTCHNL2_CHECK_STRUCT_LEN(6, virtchnl2_ptype); /** diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 6208923e29a2..c1adc94a5a65 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -716,7 +716,8 @@ static s32 ixgbe_read_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr, if ((command & IXGBE_SB_IOSF_CTRL_RESP_STAT_MASK) != 0) { error = FIELD_GET(IXGBE_SB_IOSF_CTRL_CMPL_ERR_MASK, command); hw_dbg(hw, "Failed to read, error %x\n", error); - return -EIO; + ret = -EIO; + goto out; } if (!ret) diff --git a/drivers/net/ethernet/litex/litex_liteeth.c b/drivers/net/ethernet/litex/litex_liteeth.c index 5182fe737c37..ff54fbe41bcc 100644 --- a/drivers/net/ethernet/litex/litex_liteeth.c +++ b/drivers/net/ethernet/litex/litex_liteeth.c @@ -318,4 +318,5 @@ static struct platform_driver liteeth_driver = { module_platform_driver(liteeth_driver); MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>"); +MODULE_DESCRIPTION("LiteX Liteeth Ethernet driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 820b1fabe297..23adf53c2aa1 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -614,12 +614,38 @@ static void mvpp23_bm_set_8pool_mode(struct mvpp2 *priv) mvpp2_write(priv, MVPP22_BM_POOL_BASE_ADDR_HIGH_REG, val); } +/* Cleanup pool before actual initialization in the OS */ +static void mvpp2_bm_pool_cleanup(struct mvpp2 *priv, int pool_id) +{ + unsigned int thread = mvpp2_cpu_to_thread(priv, get_cpu()); + u32 val; + int i; + + /* Drain the BM from all possible residues left by firmware */ + for (i = 0; i < MVPP2_BM_POOL_SIZE_MAX; i++) + mvpp2_thread_read(priv, thread, MVPP2_BM_PHY_ALLOC_REG(pool_id)); + + put_cpu(); + + /* Stop the BM pool */ + val = mvpp2_read(priv, MVPP2_BM_POOL_CTRL_REG(pool_id)); + val |= MVPP2_BM_STOP_MASK; + mvpp2_write(priv, MVPP2_BM_POOL_CTRL_REG(pool_id), val); +} + static int mvpp2_bm_init(struct device *dev, struct mvpp2 *priv) { enum dma_data_direction dma_dir = DMA_FROM_DEVICE; int i, err, poolnum = MVPP2_BM_POOLS_NUM; struct mvpp2_port *port; + if (priv->percpu_pools) + poolnum = mvpp2_get_nrxqs(priv) * 2; + + /* Clean up the pool state in case it contains stale state */ + for (i = 0; i < poolnum; i++) + mvpp2_bm_pool_cleanup(priv, i); + if (priv->percpu_pools) { for (i = 0; i < priv->port_count; i++) { port = priv->port_list[i]; @@ -629,7 +655,6 @@ static int mvpp2_bm_init(struct device *dev, struct mvpp2 *priv) } } - poolnum = mvpp2_get_nrxqs(priv) * 2; for (i = 0; i < poolnum; i++) { /* the pool in use */ int pn = i / (poolnum / 2); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.c b/drivers/net/ethernet/marvell/octeontx2/af/mbox.c index 9690ac01f02c..b92264d0a77e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.c @@ -413,4 +413,5 @@ const char *otx2_mbox_id2name(u16 id) EXPORT_SYMBOL(otx2_mbox_id2name); MODULE_AUTHOR("Marvell."); +MODULE_DESCRIPTION("Marvell RVU NIC Mbox helpers"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c index 4728ba34b0e3..76218f1cb459 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c @@ -506,6 +506,7 @@ u32 rpm2_get_lmac_fifo_len(void *rpmd, int lmac_id) rpm_t *rpm = rpmd; u8 num_lmacs; u32 fifo_len; + u16 max_lmac; lmac_info = rpm_read(rpm, 0, RPM2_CMRX_RX_LMACS); /* LMACs are divided into two groups and each group @@ -513,7 +514,11 @@ u32 rpm2_get_lmac_fifo_len(void *rpmd, int lmac_id) * Group0 lmac_id range {0..3} * Group1 lmac_id range {4..7} */ - fifo_len = rpm->mac_ops->fifo_len / 2; + max_lmac = (rpm_read(rpm, 0, CGX_CONST) >> 24) & 0xFF; + if (max_lmac > 4) + fifo_len = rpm->mac_ops->fifo_len / 2; + else + fifo_len = rpm->mac_ops->fifo_len; if (lmac_id < 4) { num_lmacs = hweight8(lmac_info & 0xF); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 167145bdcb75..8cfd74ad991c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -1850,8 +1850,8 @@ void npc_mcam_rsrcs_deinit(struct rvu *rvu) { struct npc_mcam *mcam = &rvu->hw->mcam; - kfree(mcam->bmap); - kfree(mcam->bmap_reverse); + bitmap_free(mcam->bmap); + bitmap_free(mcam->bmap_reverse); kfree(mcam->entry2pfvf_map); kfree(mcam->cntr2pfvf_map); kfree(mcam->entry2cntr_map); @@ -1904,21 +1904,20 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) mcam->pf_offset = mcam->nixlf_offset + nixlf_count; /* Allocate bitmaps for managing MCAM entries */ - mcam->bmap = kmalloc_array(BITS_TO_LONGS(mcam->bmap_entries), - sizeof(long), GFP_KERNEL); + mcam->bmap = bitmap_zalloc(mcam->bmap_entries, GFP_KERNEL); if (!mcam->bmap) return -ENOMEM; - mcam->bmap_reverse = kmalloc_array(BITS_TO_LONGS(mcam->bmap_entries), - sizeof(long), GFP_KERNEL); + mcam->bmap_reverse = bitmap_zalloc(mcam->bmap_entries, GFP_KERNEL); if (!mcam->bmap_reverse) goto free_bmap; mcam->bmap_fcnt = mcam->bmap_entries; /* Alloc memory for saving entry to RVU PFFUNC allocation mapping */ - mcam->entry2pfvf_map = kmalloc_array(mcam->bmap_entries, - sizeof(u16), GFP_KERNEL); + mcam->entry2pfvf_map = kcalloc(mcam->bmap_entries, sizeof(u16), + GFP_KERNEL); + if (!mcam->entry2pfvf_map) goto free_bmap_reverse; @@ -1941,21 +1940,21 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) if (err) goto free_entry_map; - mcam->cntr2pfvf_map = kmalloc_array(mcam->counters.max, - sizeof(u16), GFP_KERNEL); + mcam->cntr2pfvf_map = kcalloc(mcam->counters.max, sizeof(u16), + GFP_KERNEL); if (!mcam->cntr2pfvf_map) goto free_cntr_bmap; /* Alloc memory for MCAM entry to counter mapping and for tracking * counter's reference count. */ - mcam->entry2cntr_map = kmalloc_array(mcam->bmap_entries, - sizeof(u16), GFP_KERNEL); + mcam->entry2cntr_map = kcalloc(mcam->bmap_entries, sizeof(u16), + GFP_KERNEL); if (!mcam->entry2cntr_map) goto free_cntr_map; - mcam->cntr_refcnt = kmalloc_array(mcam->counters.max, - sizeof(u16), GFP_KERNEL); + mcam->cntr_refcnt = kcalloc(mcam->counters.max, sizeof(u16), + GFP_KERNEL); if (!mcam->cntr_refcnt) goto free_entry_cntr_map; @@ -1988,9 +1987,9 @@ free_cntr_bmap: free_entry_map: kfree(mcam->entry2pfvf_map); free_bmap_reverse: - kfree(mcam->bmap_reverse); + bitmap_free(mcam->bmap_reverse); free_bmap: - kfree(mcam->bmap); + bitmap_free(mcam->bmap); return -ENOMEM; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 7ca6941ea0b9..02d0b707aea5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -951,8 +951,11 @@ int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura) if (pfvf->ptp && qidx < pfvf->hw.tx_queues) { err = qmem_alloc(pfvf->dev, &sq->timestamps, qset->sqe_cnt, sizeof(*sq->timestamps)); - if (err) + if (err) { + kfree(sq->sg); + sq->sg = NULL; return err; + } } sq->head = 0; @@ -968,7 +971,14 @@ int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura) sq->stats.bytes = 0; sq->stats.pkts = 0; - return pfvf->hw_ops->sq_aq_init(pfvf, qidx, sqb_aura); + err = pfvf->hw_ops->sq_aq_init(pfvf, qidx, sqb_aura); + if (err) { + kfree(sq->sg); + sq->sg = NULL; + return err; + } + + return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c index 2928898c7f8d..7f786de61014 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c @@ -314,7 +314,6 @@ static int otx2_set_channels(struct net_device *dev, pfvf->hw.tx_queues = channel->tx_count; if (pfvf->xdp_prog) pfvf->hw.xdp_queues = channel->rx_count; - pfvf->hw.non_qos_queues = pfvf->hw.tx_queues + pfvf->hw.xdp_queues; if (if_up) err = dev->netdev_ops->ndo_open(dev); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index a57455aebff6..e5fe67e73865 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1744,6 +1744,7 @@ int otx2_open(struct net_device *netdev) /* RQ and SQs are mapped to different CQs, * so find out max CQ IRQs (i.e CINTs) needed. */ + pf->hw.non_qos_queues = pf->hw.tx_queues + pf->hw.xdp_queues; pf->hw.cint_cnt = max3(pf->hw.rx_queues, pf->hw.tx_queues, pf->hw.tc_tx_queues); @@ -2643,8 +2644,6 @@ static int otx2_xdp_setup(struct otx2_nic *pf, struct bpf_prog *prog) xdp_features_clear_redirect_target(dev); } - pf->hw.non_qos_queues += pf->hw.xdp_queues; - if (if_up) otx2_open(pf->netdev); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index 4d519ea833b2..f828d32737af 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -1403,7 +1403,7 @@ static bool otx2_xdp_rcv_pkt_handler(struct otx2_nic *pfvf, struct otx2_cq_queue *cq, bool *need_xdp_flush) { - unsigned char *hard_start, *data; + unsigned char *hard_start; int qidx = cq->cq_idx; struct xdp_buff xdp; struct page *page; @@ -1417,9 +1417,8 @@ static bool otx2_xdp_rcv_pkt_handler(struct otx2_nic *pfvf, xdp_init_buff(&xdp, pfvf->rbsize, &cq->xdp_rxq); - data = (unsigned char *)phys_to_virt(pa); - hard_start = page_address(page); - xdp_prepare_buff(&xdp, hard_start, data - hard_start, + hard_start = (unsigned char *)phys_to_virt(pa); + xdp_prepare_buff(&xdp, hard_start, OTX2_HEAD_ROOM, cqe->sg.seg_size, false); act = bpf_prog_run_xdp(prog, &xdp); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index a6e91573f8da..de123350bd46 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -4761,7 +4761,10 @@ static int mtk_probe(struct platform_device *pdev) } if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA)) { - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36)); + err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(36)); + if (!err) + err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (err) { dev_err(&pdev->dev, "Wrong DMA config\n"); return -EINVAL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index a7b1f9686c09..4957412ff1f6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -1923,6 +1923,7 @@ static void cmd_status_log(struct mlx5_core_dev *dev, u16 opcode, u8 status, { const char *namep = mlx5_command_str(opcode); struct mlx5_cmd_stats *stats; + unsigned long flags; if (!err || !(strcmp(namep, "unknown command opcode"))) return; @@ -1930,7 +1931,7 @@ static void cmd_status_log(struct mlx5_core_dev *dev, u16 opcode, u8 status, stats = xa_load(&dev->cmd.stats, opcode); if (!stats) return; - spin_lock_irq(&stats->lock); + spin_lock_irqsave(&stats->lock, flags); stats->failed++; if (err < 0) stats->last_failed_errno = -err; @@ -1939,7 +1940,7 @@ static void cmd_status_log(struct mlx5_core_dev *dev, u16 opcode, u8 status, stats->last_failed_mbox_status = status; stats->last_failed_syndrome = syndrome; } - spin_unlock_irq(&stats->lock); + spin_unlock_irqrestore(&stats->lock, flags); } /* preserve -EREMOTEIO for outbox.status != OK, otherwise return err as is */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 0bfe1ca8a364..55c6ace0acd5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -1124,7 +1124,7 @@ static inline bool mlx5_tx_swp_supported(struct mlx5_core_dev *mdev) extern const struct ethtool_ops mlx5e_ethtool_ops; int mlx5e_create_mkey(struct mlx5_core_dev *mdev, u32 pdn, u32 *mkey); -int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev); +int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev, bool create_tises); void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev); int mlx5e_refresh_tirs(struct mlx5e_priv *priv, bool enable_uc_lb, bool enable_mc_lb); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c b/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c index e1283531e0b8..671adbad0a40 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c @@ -436,6 +436,7 @@ static int fs_any_create_groups(struct mlx5e_flow_table *ft) in = kvzalloc(inlen, GFP_KERNEL); if (!in || !ft->g) { kfree(ft->g); + ft->g = NULL; kvfree(in); return -ENOMEM; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c index 284253b79266..5d213a9886f1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c @@ -1064,8 +1064,8 @@ void mlx5e_build_sq_param(struct mlx5_core_dev *mdev, void *wq = MLX5_ADDR_OF(sqc, sqc, wq); bool allow_swp; - allow_swp = - mlx5_geneve_tx_allowed(mdev) || !!mlx5_ipsec_device_caps(mdev); + allow_swp = mlx5_geneve_tx_allowed(mdev) || + (mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_CRYPTO); mlx5e_build_sq_param_common(mdev, param); MLX5_SET(wq, wq, log_wq_sz, params->log_sq_size); MLX5_SET(sqc, sqc, allow_swp, allow_swp); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c index c206cc0a8483..078f56a3cbb2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c @@ -213,7 +213,7 @@ static void mlx5e_ptp_handle_ts_cqe(struct mlx5e_ptpsq *ptpsq, mlx5e_ptpsq_mark_ts_cqes_undelivered(ptpsq, hwtstamp); out: napi_consume_skb(skb, budget); - md_buff[*md_buff_sz++] = metadata_id; + md_buff[(*md_buff_sz)++] = metadata_id; if (unlikely(mlx5e_ptp_metadata_map_unhealthy(&ptpsq->metadata_map)) && !test_and_set_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state)) queue_work(ptpsq->txqsq.priv->wq, &ptpsq->report_unhealthy_work); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 161c5190c236..05612d9c6080 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -336,12 +336,17 @@ void mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, /* iv len */ aes_gcm->icv_len = x->aead->alg_icv_len; + attrs->dir = x->xso.dir; + /* esn */ if (x->props.flags & XFRM_STATE_ESN) { attrs->replay_esn.trigger = true; attrs->replay_esn.esn = sa_entry->esn_state.esn; attrs->replay_esn.esn_msb = sa_entry->esn_state.esn_msb; attrs->replay_esn.overlap = sa_entry->esn_state.overlap; + if (attrs->dir == XFRM_DEV_OFFLOAD_OUT) + goto skip_replay_window; + switch (x->replay_esn->replay_window) { case 32: attrs->replay_esn.replay_window = @@ -365,7 +370,7 @@ void mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry, } } - attrs->dir = x->xso.dir; +skip_replay_window: /* spi */ attrs->spi = be32_to_cpu(x->id.spi); @@ -501,7 +506,8 @@ static int mlx5e_xfrm_validate_state(struct mlx5_core_dev *mdev, return -EINVAL; } - if (x->replay_esn && x->replay_esn->replay_window != 32 && + if (x->replay_esn && x->xso.dir == XFRM_DEV_OFFLOAD_IN && + x->replay_esn->replay_window != 32 && x->replay_esn->replay_window != 64 && x->replay_esn->replay_window != 128 && x->replay_esn->replay_window != 256) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c index bb7f86c993e5..e66f486faafe 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c @@ -254,11 +254,13 @@ static int arfs_create_groups(struct mlx5e_flow_table *ft, ft->g = kcalloc(MLX5E_ARFS_NUM_GROUPS, sizeof(*ft->g), GFP_KERNEL); - in = kvzalloc(inlen, GFP_KERNEL); - if (!in || !ft->g) { - kfree(ft->g); - kvfree(in); + if (!ft->g) return -ENOMEM; + + in = kvzalloc(inlen, GFP_KERNEL); + if (!in) { + err = -ENOMEM; + goto err_free_g; } mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); @@ -278,7 +280,7 @@ static int arfs_create_groups(struct mlx5e_flow_table *ft, break; default: err = -EINVAL; - goto out; + goto err_free_in; } switch (type) { @@ -300,7 +302,7 @@ static int arfs_create_groups(struct mlx5e_flow_table *ft, break; default: err = -EINVAL; - goto out; + goto err_free_in; } MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); @@ -309,7 +311,7 @@ static int arfs_create_groups(struct mlx5e_flow_table *ft, MLX5_SET_CFG(in, end_flow_index, ix - 1); ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); if (IS_ERR(ft->g[ft->num_groups])) - goto err; + goto err_clean_group; ft->num_groups++; memset(in, 0, inlen); @@ -318,18 +320,20 @@ static int arfs_create_groups(struct mlx5e_flow_table *ft, MLX5_SET_CFG(in, end_flow_index, ix - 1); ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); if (IS_ERR(ft->g[ft->num_groups])) - goto err; + goto err_clean_group; ft->num_groups++; kvfree(in); return 0; -err: +err_clean_group: err = PTR_ERR(ft->g[ft->num_groups]); ft->g[ft->num_groups] = NULL; -out: +err_free_in: kvfree(in); - +err_free_g: + kfree(ft->g); + ft->g = NULL; return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c index 67f546683e85..6ed3a32b7e22 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c @@ -95,7 +95,7 @@ static void mlx5e_destroy_tises(struct mlx5_core_dev *mdev, u32 tisn[MLX5_MAX_PO { int tc, i; - for (i = 0; i < MLX5_MAX_PORTS; i++) + for (i = 0; i < mlx5e_get_num_lag_ports(mdev); i++) for (tc = 0; tc < MLX5_MAX_NUM_TC; tc++) mlx5e_destroy_tis(mdev, tisn[i][tc]); } @@ -110,7 +110,7 @@ static int mlx5e_create_tises(struct mlx5_core_dev *mdev, u32 tisn[MLX5_MAX_PORT int tc, i; int err; - for (i = 0; i < MLX5_MAX_PORTS; i++) { + for (i = 0; i < mlx5e_get_num_lag_ports(mdev); i++) { for (tc = 0; tc < MLX5_MAX_NUM_TC; tc++) { u32 in[MLX5_ST_SZ_DW(create_tis_in)] = {}; void *tisc; @@ -140,7 +140,7 @@ err_close_tises: return err; } -int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev) +int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev, bool create_tises) { struct mlx5e_hw_objs *res = &mdev->mlx5e_res.hw_objs; int err; @@ -169,11 +169,15 @@ int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev) goto err_destroy_mkey; } - err = mlx5e_create_tises(mdev, res->tisn); - if (err) { - mlx5_core_err(mdev, "alloc tises failed, %d\n", err); - goto err_destroy_bfreg; + if (create_tises) { + err = mlx5e_create_tises(mdev, res->tisn); + if (err) { + mlx5_core_err(mdev, "alloc tises failed, %d\n", err); + goto err_destroy_bfreg; + } + res->tisn_valid = true; } + INIT_LIST_HEAD(&res->td.tirs_list); mutex_init(&res->td.list_lock); @@ -203,7 +207,8 @@ void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev) mlx5_crypto_dek_cleanup(mdev->mlx5e_res.dek_priv); mdev->mlx5e_res.dek_priv = NULL; - mlx5e_destroy_tises(mdev, res->tisn); + if (res->tisn_valid) + mlx5e_destroy_tises(mdev, res->tisn); mlx5_free_bfreg(mdev, &res->bfreg); mlx5_core_destroy_mkey(mdev, res->mkey); mlx5_core_dealloc_transport_domain(mdev, res->td.tdn); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index b5f1c4ca38ba..c8e8f512803e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -5992,7 +5992,7 @@ static int mlx5e_resume(struct auxiliary_device *adev) if (netif_device_present(netdev)) return 0; - err = mlx5e_create_mdev_resources(mdev); + err = mlx5e_create_mdev_resources(mdev, true); if (err) return err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 30932c9c9a8f..9fb2c057bd78 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -761,7 +761,7 @@ static int mlx5e_hairpin_create_indirect_rqt(struct mlx5e_hairpin *hp) err = mlx5e_rss_params_indir_init(&indir, mdev, mlx5e_rqt_size(mdev, hp->num_channels), - mlx5e_rqt_size(mdev, priv->max_nch)); + mlx5e_rqt_size(mdev, hp->num_channels)); if (err) return err; @@ -2014,9 +2014,10 @@ static void mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow, list_for_each_entry_safe(peer_flow, tmp, &flow->peer_flows, peer_flows) { if (peer_index != mlx5_get_dev_index(peer_flow->priv->mdev)) continue; + + list_del(&peer_flow->peer_flows); if (refcount_dec_and_test(&peer_flow->refcnt)) { mlx5e_tc_del_fdb_flow(peer_flow->priv, peer_flow); - list_del(&peer_flow->peer_flows); kfree(peer_flow); } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge_mcast.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge_mcast.c index a7ed87e9d842..22dd30cf8033 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge_mcast.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge_mcast.c @@ -83,6 +83,7 @@ mlx5_esw_bridge_mdb_flow_create(u16 esw_owner_vhca_id, struct mlx5_esw_bridge_md i++; } + rule_spec->flow_context.flags |= FLOW_CONTEXT_UPLINK_HAIRPIN_EN; rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; dmac_v = MLX5_ADDR_OF(fte_match_param, rule_spec->match_value, outer_headers.dmac_47_16); ether_addr_copy(dmac_v, entry->key.addr); @@ -587,6 +588,7 @@ mlx5_esw_bridge_mcast_vlan_flow_create(u16 vlan_proto, struct mlx5_esw_bridge_po if (!rule_spec) return ERR_PTR(-ENOMEM); + rule_spec->flow_context.flags |= FLOW_CONTEXT_UPLINK_HAIRPIN_EN; rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT; @@ -662,6 +664,7 @@ mlx5_esw_bridge_mcast_fwd_flow_create(struct mlx5_esw_bridge_port *port) dest.vport.flags = MLX5_FLOW_DEST_VPORT_VHCA_ID; dest.vport.vhca_id = port->esw_owner_vhca_id; } + rule_spec->flow_context.flags |= FLOW_CONTEXT_UPLINK_HAIRPIN_EN; handle = mlx5_add_flow_rules(port->mcast.ft, rule_spec, &flow_act, &dest, 1); kvfree(rule_spec); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index 1616a6144f7b..9b8599c200e2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -566,6 +566,8 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev, fte->flow_context.flow_tag); MLX5_SET(flow_context, in_flow_context, flow_source, fte->flow_context.flow_source); + MLX5_SET(flow_context, in_flow_context, uplink_hairpin_en, + !!(fte->flow_context.flags & FLOW_CONTEXT_UPLINK_HAIRPIN_EN)); MLX5_SET(flow_context, in_flow_context, extended_destination, extended_dest); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c index 58845121954c..d77be1b4dd9c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c @@ -783,7 +783,7 @@ static int mlx5_rdma_setup_rn(struct ib_device *ibdev, u32 port_num, } /* This should only be called once per mdev */ - err = mlx5e_create_mdev_resources(mdev); + err = mlx5e_create_mdev_resources(mdev, false); if (err) goto destroy_ht; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c index 40c7be124041..58bd749b5e4d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c @@ -98,7 +98,7 @@ static int create_aso_cq(struct mlx5_aso_cq *cq, void *cqc_data) mlx5_fill_page_frag_array(&cq->wq_ctrl.buf, (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas)); - MLX5_SET(cqc, cqc, cq_period_mode, DIM_CQ_PERIOD_MODE_START_FROM_EQE); + MLX5_SET(cqc, cqc, cq_period_mode, MLX5_CQ_PERIOD_MODE_START_FROM_EQE); MLX5_SET(cqc, cqc, c_eqn_or_apu_element, eqn); MLX5_SET(cqc, cqc, uar_page, mdev->priv.uar->index); MLX5_SET(cqc, cqc, log_page_size, cq->wq_ctrl.buf.page_shift - diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c index 6f9790e97fed..2ebb61ef3ea9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c @@ -788,6 +788,7 @@ int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher, switch (action_type) { case DR_ACTION_TYP_DROP: attr.final_icm_addr = nic_dmn->drop_icm_addr; + attr.hit_gvmi = nic_dmn->drop_icm_addr >> 48; break; case DR_ACTION_TYP_FT: dest_action = action; @@ -873,11 +874,17 @@ int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher, action->sampler->tx_icm_addr; break; case DR_ACTION_TYP_VPORT: - attr.hit_gvmi = action->vport->caps->vhca_gvmi; - dest_action = action; - attr.final_icm_addr = rx_rule ? - action->vport->caps->icm_address_rx : - action->vport->caps->icm_address_tx; + if (unlikely(rx_rule && action->vport->caps->num == MLX5_VPORT_UPLINK)) { + /* can't go to uplink on RX rule - dropping instead */ + attr.final_icm_addr = nic_dmn->drop_icm_addr; + attr.hit_gvmi = nic_dmn->drop_icm_addr >> 48; + } else { + attr.hit_gvmi = action->vport->caps->vhca_gvmi; + dest_action = action; + attr.final_icm_addr = rx_rule ? + action->vport->caps->icm_address_rx : + action->vport->caps->icm_address_tx; + } break; case DR_ACTION_TYP_POP_VLAN: if (!rx_rule && !(dmn->ste_ctx->actions_caps & diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index 21753f327868..1005bb6935b6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -440,6 +440,27 @@ out: } EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_system_image_guid); +int mlx5_query_nic_vport_sd_group(struct mlx5_core_dev *mdev, u8 *sd_group) +{ + int outlen = MLX5_ST_SZ_BYTES(query_nic_vport_context_out); + u32 *out; + int err; + + out = kvzalloc(outlen, GFP_KERNEL); + if (!out) + return -ENOMEM; + + err = mlx5_query_nic_vport_context(mdev, 0, out); + if (err) + goto out; + + *sd_group = MLX5_GET(query_nic_vport_context_out, out, + nic_vport_context.sd_group); +out: + kvfree(out); + return err; +} + int mlx5_query_nic_vport_node_guid(struct mlx5_core_dev *mdev, u64 *node_guid) { u32 *out; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c index 4c98950380d5..d231f4d2888b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c @@ -301,6 +301,7 @@ mlxsw_sp_acl_erp_table_alloc(struct mlxsw_sp_acl_erp_core *erp_core, unsigned long *p_index) { unsigned int num_rows, entry_size; + unsigned long index; /* We only allow allocations of entire rows */ if (num_erps % erp_core->num_erp_banks != 0) @@ -309,10 +310,11 @@ mlxsw_sp_acl_erp_table_alloc(struct mlxsw_sp_acl_erp_core *erp_core, entry_size = erp_core->erpt_entries_size[region_type]; num_rows = num_erps / erp_core->num_erp_banks; - *p_index = gen_pool_alloc(erp_core->erp_tables, num_rows * entry_size); - if (*p_index == 0) + index = gen_pool_alloc(erp_core->erp_tables, num_rows * entry_size); + if (!index) return -ENOBUFS; - *p_index -= MLXSW_SP_ACL_ERP_GENALLOC_OFFSET; + + *p_index = index - MLXSW_SP_ACL_ERP_GENALLOC_OFFSET; return 0; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c index d50786b0a6ce..50ea1eff02b2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c @@ -681,13 +681,13 @@ static void mlxsw_sp_acl_tcam_region_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_tcam_region *region) { + struct mlxsw_sp_acl_tcam *tcam = mlxsw_sp_acl_to_tcam(mlxsw_sp->acl); const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops; ops->region_fini(mlxsw_sp, region->priv); mlxsw_sp_acl_tcam_region_disable(mlxsw_sp, region); mlxsw_sp_acl_tcam_region_free(mlxsw_sp, region); - mlxsw_sp_acl_tcam_region_id_put(region->group->tcam, - region->id); + mlxsw_sp_acl_tcam_region_id_put(tcam, region->id); kfree(region); } @@ -1564,6 +1564,8 @@ int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp, tcam->max_groups = max_groups; tcam->max_group_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_GROUP_SIZE); + tcam->max_group_size = min_t(unsigned int, tcam->max_group_size, + MLXSW_REG_PAGT_ACL_MAX_NUM); err = ops->init(mlxsw_sp, tcam->priv, tcam); if (err) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 2c255ed9b8a9..7164f9e6370f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -11472,6 +11472,13 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp, if (err) goto err_register_netevent_notifier; + mlxsw_sp->router->netdevice_nb.notifier_call = + mlxsw_sp_router_netdevice_event; + err = register_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp), + &mlxsw_sp->router->netdevice_nb); + if (err) + goto err_register_netdev_notifier; + mlxsw_sp->router->nexthop_nb.notifier_call = mlxsw_sp_nexthop_obj_event; err = register_nexthop_notifier(mlxsw_sp_net(mlxsw_sp), @@ -11487,22 +11494,15 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp, if (err) goto err_register_fib_notifier; - mlxsw_sp->router->netdevice_nb.notifier_call = - mlxsw_sp_router_netdevice_event; - err = register_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp), - &mlxsw_sp->router->netdevice_nb); - if (err) - goto err_register_netdev_notifier; - return 0; -err_register_netdev_notifier: - unregister_fib_notifier(mlxsw_sp_net(mlxsw_sp), - &mlxsw_sp->router->fib_nb); err_register_fib_notifier: unregister_nexthop_notifier(mlxsw_sp_net(mlxsw_sp), &mlxsw_sp->router->nexthop_nb); err_register_nexthop_notifier: + unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp), + &router->netdevice_nb); +err_register_netdev_notifier: unregister_netevent_notifier(&mlxsw_sp->router->netevent_nb); err_register_netevent_notifier: unregister_inet6addr_validator_notifier(&router->inet6addr_valid_nb); @@ -11550,11 +11550,11 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp) { struct mlxsw_sp_router *router = mlxsw_sp->router; - unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp), - &router->netdevice_nb); unregister_fib_notifier(mlxsw_sp_net(mlxsw_sp), &router->fib_nb); unregister_nexthop_notifier(mlxsw_sp_net(mlxsw_sp), &router->nexthop_nb); + unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp), + &router->netdevice_nb); unregister_netevent_notifier(&router->netevent_nb); unregister_inet6addr_validator_notifier(&router->inet6addr_valid_nb); unregister_inetaddr_validator_notifier(&router->inetaddr_valid_nb); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c index 92108d354051..2e83bbb9477e 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c @@ -168,9 +168,10 @@ static void lan966x_port_link_up(struct lan966x_port *port) lan966x_taprio_speed_set(port, config->speed); /* Also the GIGA_MODE_ENA(1) needs to be set regardless of the - * port speed for QSGMII ports. + * port speed for QSGMII or SGMII ports. */ - if (phy_interface_num_ports(config->portmode) == 4) + if (phy_interface_num_ports(config->portmode) == 4 || + config->portmode == PHY_INTERFACE_MODE_SGMII) mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA_SET(1); lan_wr(config->duplex | mode, diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index 61d8bfd12d5f..55408f16fbbc 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -414,6 +414,7 @@ static const u64 fix_mac[] = { END_SIGN }; +MODULE_DESCRIPTION("Neterion 10GbE driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c index 2967bab72505..15180538b80a 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c +++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c @@ -1424,10 +1424,30 @@ static void nfp_nft_ct_translate_mangle_action(struct flow_action_entry *mangle_ mangle_action->mangle.mask = (__force u32)cpu_to_be32(mangle_action->mangle.mask); return; + /* Both struct tcphdr and struct udphdr start with + * __be16 source; + * __be16 dest; + * so we can use the same code for both. + */ case FLOW_ACT_MANGLE_HDR_TYPE_TCP: case FLOW_ACT_MANGLE_HDR_TYPE_UDP: - mangle_action->mangle.val = (__force u16)cpu_to_be16(mangle_action->mangle.val); - mangle_action->mangle.mask = (__force u16)cpu_to_be16(mangle_action->mangle.mask); + if (mangle_action->mangle.offset == offsetof(struct tcphdr, source)) { + mangle_action->mangle.val = + (__force u32)cpu_to_be32(mangle_action->mangle.val << 16); + /* The mask of mangle action is inverse mask, + * so clear the dest tp port with 0xFFFF to + * instead of rotate-left operation. + */ + mangle_action->mangle.mask = + (__force u32)cpu_to_be32(mangle_action->mangle.mask << 16 | 0xFFFF); + } + if (mangle_action->mangle.offset == offsetof(struct tcphdr, dest)) { + mangle_action->mangle.offset = 0; + mangle_action->mangle.val = + (__force u32)cpu_to_be32(mangle_action->mangle.val); + mangle_action->mangle.mask = + (__force u32)cpu_to_be32(mangle_action->mangle.mask); + } return; default: @@ -1864,10 +1884,30 @@ int nfp_fl_ct_handle_post_ct(struct nfp_flower_priv *priv, { struct flow_rule *rule = flow_cls_offload_flow_rule(flow); struct nfp_fl_ct_flow_entry *ct_entry; + struct flow_action_entry *ct_goto; struct nfp_fl_ct_zone_entry *zt; + struct flow_action_entry *act; bool wildcarded = false; struct flow_match_ct ct; - struct flow_action_entry *ct_goto; + int i; + + flow_action_for_each(i, act, &rule->action) { + switch (act->id) { + case FLOW_ACTION_REDIRECT: + case FLOW_ACTION_REDIRECT_INGRESS: + case FLOW_ACTION_MIRRED: + case FLOW_ACTION_MIRRED_INGRESS: + if (act->dev->rtnl_link_ops && + !strcmp(act->dev->rtnl_link_ops->kind, "openvswitch")) { + NL_SET_ERR_MSG_MOD(extack, + "unsupported offload: out port is openvswitch internal port"); + return -EOPNOTSUPP; + } + break; + default: + break; + } + } flow_rule_match_ct(rule, &ct); if (!ct.mask->ct_zone) { diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c index e522845c7c21..0d7d138d6e0d 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c @@ -1084,7 +1084,7 @@ nfp_tunnel_add_shared_mac(struct nfp_app *app, struct net_device *netdev, u16 nfp_mac_idx = 0; entry = nfp_tunnel_lookup_offloaded_macs(app, netdev->dev_addr); - if (entry && nfp_tunnel_is_mac_idx_global(entry->index)) { + if (entry && (nfp_tunnel_is_mac_idx_global(entry->index) || netif_is_lag_port(netdev))) { if (entry->bridge_count || !nfp_flower_is_supported_bridge(netdev)) { nfp_tunnel_offloaded_macs_inc_ref_and_link(entry, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 3b3210d823e8..f28e769e6fda 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2776,6 +2776,7 @@ static void nfp_net_netdev_init(struct nfp_net *nn) case NFP_NFD_VER_NFD3: netdev->netdev_ops = &nfp_nfd3_netdev_ops; netdev->xdp_features |= NETDEV_XDP_ACT_XSK_ZEROCOPY; + netdev->xdp_features |= NETDEV_XDP_ACT_REDIRECT; break; case NFP_NFD_VER_NFDK: netdev->netdev_ops = &nfp_nfdk_netdev_ops; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c index 33b4c2856316..3f10c5365c80 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c @@ -537,11 +537,13 @@ static int enable_bars(struct nfp6000_pcie *nfp, u16 interface) const u32 barcfg_msix_general = NFP_PCIE_BAR_PCIE2CPP_MapType( NFP_PCIE_BAR_PCIE2CPP_MapType_GENERAL) | - NFP_PCIE_BAR_PCIE2CPP_LengthSelect_32BIT; + NFP_PCIE_BAR_PCIE2CPP_LengthSelect( + NFP_PCIE_BAR_PCIE2CPP_LengthSelect_32BIT); const u32 barcfg_msix_xpb = NFP_PCIE_BAR_PCIE2CPP_MapType( NFP_PCIE_BAR_PCIE2CPP_MapType_BULK) | - NFP_PCIE_BAR_PCIE2CPP_LengthSelect_32BIT | + NFP_PCIE_BAR_PCIE2CPP_LengthSelect( + NFP_PCIE_BAR_PCIE2CPP_LengthSelect_32BIT) | NFP_PCIE_BAR_PCIE2CPP_Target_BaseAddress( NFP_CPP_TARGET_ISLAND_XPB); const u32 barcfg_explicit[4] = { diff --git a/drivers/net/ethernet/qualcomm/qca_uart.c b/drivers/net/ethernet/qualcomm/qca_uart.c index 9adec91f35e9..223321897b96 100644 --- a/drivers/net/ethernet/qualcomm/qca_uart.c +++ b/drivers/net/ethernet/qualcomm/qca_uart.c @@ -58,9 +58,8 @@ struct qcauart { unsigned char *tx_buffer; }; -static int -qca_tty_receive(struct serdev_device *serdev, const unsigned char *data, - size_t count) +static ssize_t +qca_tty_receive(struct serdev_device *serdev, const u8 *data, size_t count) { struct qcauart *qca = serdev_device_get_drvdata(serdev); struct net_device *netdev = qca->net_dev; diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c index 39d24e07f306..5b69b9268c75 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c @@ -396,7 +396,7 @@ nla_put_failure: struct rtnl_link_ops rmnet_link_ops __read_mostly = { .kind = "rmnet", - .maxtype = __IFLA_RMNET_MAX, + .maxtype = IFLA_RMNET_MAX, .priv_size = sizeof(struct rmnet_priv), .setup = rmnet_vnd_setup, .validate = rmnet_rtnl_validate, diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 8649b3e90edb..0e3731f50fc2 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -1949,7 +1949,7 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) struct ravb_tstamp_skb *ts_skb; struct ravb_tx_desc *desc; unsigned long flags; - u32 dma_addr; + dma_addr_t dma_addr; void *buffer; u32 entry; u32 len; diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 721c1f8e892f..5ba606a596e7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -59,28 +59,51 @@ #undef FRAME_FILTER_DEBUG /* #define FRAME_FILTER_DEBUG */ +struct stmmac_q_tx_stats { + u64_stats_t tx_bytes; + u64_stats_t tx_set_ic_bit; + u64_stats_t tx_tso_frames; + u64_stats_t tx_tso_nfrags; +}; + +struct stmmac_napi_tx_stats { + u64_stats_t tx_packets; + u64_stats_t tx_pkt_n; + u64_stats_t poll; + u64_stats_t tx_clean; + u64_stats_t tx_set_ic_bit; +}; + struct stmmac_txq_stats { - u64 tx_bytes; - u64 tx_packets; - u64 tx_pkt_n; - u64 tx_normal_irq_n; - u64 napi_poll; - u64 tx_clean; - u64 tx_set_ic_bit; - u64 tx_tso_frames; - u64 tx_tso_nfrags; - struct u64_stats_sync syncp; + /* Updates protected by tx queue lock. */ + struct u64_stats_sync q_syncp; + struct stmmac_q_tx_stats q; + + /* Updates protected by NAPI poll logic. */ + struct u64_stats_sync napi_syncp; + struct stmmac_napi_tx_stats napi; } ____cacheline_aligned_in_smp; +struct stmmac_napi_rx_stats { + u64_stats_t rx_bytes; + u64_stats_t rx_packets; + u64_stats_t rx_pkt_n; + u64_stats_t poll; +}; + struct stmmac_rxq_stats { - u64 rx_bytes; - u64 rx_packets; - u64 rx_pkt_n; - u64 rx_normal_irq_n; - u64 napi_poll; - struct u64_stats_sync syncp; + /* Updates protected by NAPI poll logic. */ + struct u64_stats_sync napi_syncp; + struct stmmac_napi_rx_stats napi; } ____cacheline_aligned_in_smp; +/* Updates on each CPU protected by not allowing nested irqs. */ +struct stmmac_pcpu_stats { + struct u64_stats_sync syncp; + u64_stats_t rx_normal_irq_n[MTL_MAX_TX_QUEUES]; + u64_stats_t tx_normal_irq_n[MTL_MAX_RX_QUEUES]; +}; + /* Extra statistic and debug information exposed by ethtool */ struct stmmac_extra_stats { /* Transmit errors */ @@ -205,6 +228,7 @@ struct stmmac_extra_stats { /* per queue statistics */ struct stmmac_txq_stats txq_stats[MTL_MAX_TX_QUEUES]; struct stmmac_rxq_stats rxq_stats[MTL_MAX_RX_QUEUES]; + struct stmmac_pcpu_stats __percpu *pcpu_stats; unsigned long rx_dropped; unsigned long rx_errors; unsigned long tx_dropped; @@ -216,6 +240,7 @@ struct stmmac_safety_stats { unsigned long mac_errors[32]; unsigned long mtl_errors[32]; unsigned long dma_errors[32]; + unsigned long dma_dpp_errors[32]; }; /* Number of fields in Safety Stats */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c index 8f730ada71f9..6b65420e11b5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c @@ -353,6 +353,10 @@ static int imx_dwmac_probe(struct platform_device *pdev) if (data->flags & STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY) plat_dat->flags |= STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY; + /* Default TX Q0 to use TSO and rest TXQ for TBS */ + for (int i = 1; i < plat_dat->tx_queues_to_use; i++) + plat_dat->tx_queues_cfg[i].tbs_en = 1; + plat_dat->host_dma_width = dwmac->ops->addr_width; plat_dat->init = imx_dwmac_init; plat_dat->exit = imx_dwmac_exit; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index 137741b94122..b21d99faa2d0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -441,8 +441,7 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, struct stmmac_extra_stats *x, u32 chan, u32 dir) { - struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; - struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; + struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats); int ret = 0; u32 v; @@ -455,9 +454,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, if (v & EMAC_TX_INT) { ret |= handle_tx; - u64_stats_update_begin(&txq_stats->syncp); - txq_stats->tx_normal_irq_n++; - u64_stats_update_end(&txq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->tx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); } if (v & EMAC_TX_DMA_STOP_INT) @@ -479,9 +478,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, if (v & EMAC_RX_INT) { ret |= handle_rx; - u64_stats_update_begin(&rxq_stats->syncp); - rxq_stats->rx_normal_irq_n++; - u64_stats_update_end(&rxq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->rx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); } if (v & EMAC_RX_BUF_UA_INT) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c index 9470d3fd2ded..0d185e54eb7e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c @@ -171,8 +171,7 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs; u32 intr_status = readl(ioaddr + DMA_CHAN_STATUS(dwmac4_addrs, chan)); u32 intr_en = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); - struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; - struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; + struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats); int ret = 0; if (dir == DMA_DIR_RX) @@ -201,15 +200,15 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, } /* TX/RX NORMAL interrupts */ if (likely(intr_status & DMA_CHAN_STATUS_RI)) { - u64_stats_update_begin(&rxq_stats->syncp); - rxq_stats->rx_normal_irq_n++; - u64_stats_update_end(&rxq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->rx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); ret |= handle_rx; } if (likely(intr_status & DMA_CHAN_STATUS_TI)) { - u64_stats_update_begin(&txq_stats->syncp); - txq_stats->tx_normal_irq_n++; - u64_stats_update_end(&txq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->tx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); ret |= handle_tx; } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index 7907d62d3437..85e18f9a22f9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -162,8 +162,7 @@ static void show_rx_process_state(unsigned int status) int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_extra_stats *x, u32 chan, u32 dir) { - struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; - struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; + struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats); int ret = 0; /* read the status register (CSR5) */ u32 intr_status = readl(ioaddr + DMA_STATUS); @@ -215,16 +214,16 @@ int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, u32 value = readl(ioaddr + DMA_INTR_ENA); /* to schedule NAPI on real RIE event. */ if (likely(value & DMA_INTR_ENA_RIE)) { - u64_stats_update_begin(&rxq_stats->syncp); - rxq_stats->rx_normal_irq_n++; - u64_stats_update_end(&rxq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->rx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); ret |= handle_rx; } } if (likely(intr_status & DMA_STATUS_TI)) { - u64_stats_update_begin(&txq_stats->syncp); - txq_stats->tx_normal_irq_n++; - u64_stats_update_end(&txq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->tx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); ret |= handle_tx; } if (unlikely(intr_status & DMA_STATUS_ERI)) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h index 207ff1799f2c..6a2c7d22df1e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h @@ -303,6 +303,8 @@ #define XGMAC_RXCEIE BIT(4) #define XGMAC_TXCEIE BIT(0) #define XGMAC_MTL_ECC_INT_STATUS 0x000010cc +#define XGMAC_MTL_DPP_CONTROL 0x000010e0 +#define XGMAC_DPP_DISABLE BIT(0) #define XGMAC_MTL_TXQ_OPMODE(x) (0x00001100 + (0x80 * (x))) #define XGMAC_TQS GENMASK(25, 16) #define XGMAC_TQS_SHIFT 16 @@ -385,6 +387,7 @@ #define XGMAC_DCEIE BIT(1) #define XGMAC_TCEIE BIT(0) #define XGMAC_DMA_ECC_INT_STATUS 0x0000306c +#define XGMAC_DMA_DPP_INT_STATUS 0x00003074 #define XGMAC_DMA_CH_CONTROL(x) (0x00003100 + (0x80 * (x))) #define XGMAC_SPH BIT(24) #define XGMAC_PBLx8 BIT(16) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c index eb48211d9b0e..323c57f03c93 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c @@ -830,6 +830,43 @@ static const struct dwxgmac3_error_desc dwxgmac3_dma_errors[32]= { { false, "UNKNOWN", "Unknown Error" }, /* 31 */ }; +static const char * const dpp_rx_err = "Read Rx Descriptor Parity checker Error"; +static const char * const dpp_tx_err = "Read Tx Descriptor Parity checker Error"; +static const struct dwxgmac3_error_desc dwxgmac3_dma_dpp_errors[32] = { + { true, "TDPES0", dpp_tx_err }, + { true, "TDPES1", dpp_tx_err }, + { true, "TDPES2", dpp_tx_err }, + { true, "TDPES3", dpp_tx_err }, + { true, "TDPES4", dpp_tx_err }, + { true, "TDPES5", dpp_tx_err }, + { true, "TDPES6", dpp_tx_err }, + { true, "TDPES7", dpp_tx_err }, + { true, "TDPES8", dpp_tx_err }, + { true, "TDPES9", dpp_tx_err }, + { true, "TDPES10", dpp_tx_err }, + { true, "TDPES11", dpp_tx_err }, + { true, "TDPES12", dpp_tx_err }, + { true, "TDPES13", dpp_tx_err }, + { true, "TDPES14", dpp_tx_err }, + { true, "TDPES15", dpp_tx_err }, + { true, "RDPES0", dpp_rx_err }, + { true, "RDPES1", dpp_rx_err }, + { true, "RDPES2", dpp_rx_err }, + { true, "RDPES3", dpp_rx_err }, + { true, "RDPES4", dpp_rx_err }, + { true, "RDPES5", dpp_rx_err }, + { true, "RDPES6", dpp_rx_err }, + { true, "RDPES7", dpp_rx_err }, + { true, "RDPES8", dpp_rx_err }, + { true, "RDPES9", dpp_rx_err }, + { true, "RDPES10", dpp_rx_err }, + { true, "RDPES11", dpp_rx_err }, + { true, "RDPES12", dpp_rx_err }, + { true, "RDPES13", dpp_rx_err }, + { true, "RDPES14", dpp_rx_err }, + { true, "RDPES15", dpp_rx_err }, +}; + static void dwxgmac3_handle_dma_err(struct net_device *ndev, void __iomem *ioaddr, bool correctable, struct stmmac_safety_stats *stats) @@ -841,6 +878,13 @@ static void dwxgmac3_handle_dma_err(struct net_device *ndev, dwxgmac3_log_error(ndev, value, correctable, "DMA", dwxgmac3_dma_errors, STAT_OFF(dma_errors), stats); + + value = readl(ioaddr + XGMAC_DMA_DPP_INT_STATUS); + writel(value, ioaddr + XGMAC_DMA_DPP_INT_STATUS); + + dwxgmac3_log_error(ndev, value, false, "DMA_DPP", + dwxgmac3_dma_dpp_errors, + STAT_OFF(dma_dpp_errors), stats); } static int @@ -881,6 +925,12 @@ dwxgmac3_safety_feat_config(void __iomem *ioaddr, unsigned int asp, value |= XGMAC_TMOUTEN; /* FSM Timeout Feature */ writel(value, ioaddr + XGMAC_MAC_FSM_CONTROL); + /* 5. Enable Data Path Parity Protection */ + value = readl(ioaddr + XGMAC_MTL_DPP_CONTROL); + /* already enabled by default, explicit enable it again */ + value &= ~XGMAC_DPP_DISABLE; + writel(value, ioaddr + XGMAC_MTL_DPP_CONTROL); + return 0; } @@ -914,7 +964,11 @@ static int dwxgmac3_safety_feat_irq_status(struct net_device *ndev, ret |= !corr; } - err = dma & (XGMAC_DEUIS | XGMAC_DECIS); + /* DMA_DPP_Interrupt_Status is indicated by MCSIS bit in + * DMA_Safety_Interrupt_Status, so we handle DMA Data Path + * Parity Errors here + */ + err = dma & (XGMAC_DEUIS | XGMAC_DECIS | XGMAC_MCSIS); corr = dma & XGMAC_DECIS; if (err) { dwxgmac3_handle_dma_err(ndev, ioaddr, corr, stats); @@ -930,6 +984,7 @@ static const struct dwxgmac3_error { { dwxgmac3_mac_errors }, { dwxgmac3_mtl_errors }, { dwxgmac3_dma_errors }, + { dwxgmac3_dma_dpp_errors }, }; static int dwxgmac3_safety_feat_dump(struct stmmac_safety_stats *stats, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c index 3cde695fec91..dd2ab6185c40 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c @@ -337,8 +337,7 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv, struct stmmac_extra_stats *x, u32 chan, u32 dir) { - struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; - struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; + struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats); u32 intr_status = readl(ioaddr + XGMAC_DMA_CH_STATUS(chan)); u32 intr_en = readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan)); int ret = 0; @@ -367,15 +366,15 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv, /* TX/RX NORMAL interrupts */ if (likely(intr_status & XGMAC_NIS)) { if (likely(intr_status & XGMAC_RI)) { - u64_stats_update_begin(&rxq_stats->syncp); - rxq_stats->rx_normal_irq_n++; - u64_stats_update_end(&rxq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->rx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); ret |= handle_rx; } if (likely(intr_status & (XGMAC_TI | XGMAC_TBU))) { - u64_stats_update_begin(&txq_stats->syncp); - txq_stats->tx_normal_irq_n++; - u64_stats_update_end(&txq_stats->syncp); + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->tx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); ret |= handle_tx; } } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 9f89acf31050..f155e4841c62 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -267,6 +267,7 @@ struct stmmac_priv { u32 msg_enable; int wolopts; int wol_irq; + bool wol_irq_disabled; int clk_csr; struct timer_list eee_ctrl_timer; int lpi_irq; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index dd05437b51f9..ec44becf0e2d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -321,8 +321,9 @@ static int stmmac_ethtool_get_link_ksettings(struct net_device *dev, { struct stmmac_priv *priv = netdev_priv(dev); - if (priv->hw->pcs & STMMAC_PCS_RGMII || - priv->hw->pcs & STMMAC_PCS_SGMII) { + if (!(priv->plat->flags & STMMAC_FLAG_HAS_INTEGRATED_PCS) && + (priv->hw->pcs & STMMAC_PCS_RGMII || + priv->hw->pcs & STMMAC_PCS_SGMII)) { struct rgmii_adv adv; u32 supported, advertising, lp_advertising; @@ -407,8 +408,9 @@ stmmac_ethtool_set_link_ksettings(struct net_device *dev, { struct stmmac_priv *priv = netdev_priv(dev); - if (priv->hw->pcs & STMMAC_PCS_RGMII || - priv->hw->pcs & STMMAC_PCS_SGMII) { + if (!(priv->plat->flags & STMMAC_FLAG_HAS_INTEGRATED_PCS) && + (priv->hw->pcs & STMMAC_PCS_RGMII || + priv->hw->pcs & STMMAC_PCS_SGMII)) { /* Only support ANE */ if (cmd->base.autoneg != AUTONEG_ENABLE) return -EINVAL; @@ -547,44 +549,79 @@ stmmac_set_pauseparam(struct net_device *netdev, } } +static u64 stmmac_get_rx_normal_irq_n(struct stmmac_priv *priv, int q) +{ + u64 total; + int cpu; + + total = 0; + for_each_possible_cpu(cpu) { + struct stmmac_pcpu_stats *pcpu; + unsigned int start; + u64 irq_n; + + pcpu = per_cpu_ptr(priv->xstats.pcpu_stats, cpu); + do { + start = u64_stats_fetch_begin(&pcpu->syncp); + irq_n = u64_stats_read(&pcpu->rx_normal_irq_n[q]); + } while (u64_stats_fetch_retry(&pcpu->syncp, start)); + total += irq_n; + } + return total; +} + +static u64 stmmac_get_tx_normal_irq_n(struct stmmac_priv *priv, int q) +{ + u64 total; + int cpu; + + total = 0; + for_each_possible_cpu(cpu) { + struct stmmac_pcpu_stats *pcpu; + unsigned int start; + u64 irq_n; + + pcpu = per_cpu_ptr(priv->xstats.pcpu_stats, cpu); + do { + start = u64_stats_fetch_begin(&pcpu->syncp); + irq_n = u64_stats_read(&pcpu->tx_normal_irq_n[q]); + } while (u64_stats_fetch_retry(&pcpu->syncp, start)); + total += irq_n; + } + return total; +} + static void stmmac_get_per_qstats(struct stmmac_priv *priv, u64 *data) { u32 tx_cnt = priv->plat->tx_queues_to_use; u32 rx_cnt = priv->plat->rx_queues_to_use; unsigned int start; - int q, stat; - char *p; + int q; for (q = 0; q < tx_cnt; q++) { struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[q]; - struct stmmac_txq_stats snapshot; + u64 pkt_n; do { - start = u64_stats_fetch_begin(&txq_stats->syncp); - snapshot = *txq_stats; - } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); + start = u64_stats_fetch_begin(&txq_stats->napi_syncp); + pkt_n = u64_stats_read(&txq_stats->napi.tx_pkt_n); + } while (u64_stats_fetch_retry(&txq_stats->napi_syncp, start)); - p = (char *)&snapshot + offsetof(struct stmmac_txq_stats, tx_pkt_n); - for (stat = 0; stat < STMMAC_TXQ_STATS; stat++) { - *data++ = (*(u64 *)p); - p += sizeof(u64); - } + *data++ = pkt_n; + *data++ = stmmac_get_tx_normal_irq_n(priv, q); } for (q = 0; q < rx_cnt; q++) { struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[q]; - struct stmmac_rxq_stats snapshot; + u64 pkt_n; do { - start = u64_stats_fetch_begin(&rxq_stats->syncp); - snapshot = *rxq_stats; - } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); + start = u64_stats_fetch_begin(&rxq_stats->napi_syncp); + pkt_n = u64_stats_read(&rxq_stats->napi.rx_pkt_n); + } while (u64_stats_fetch_retry(&rxq_stats->napi_syncp, start)); - p = (char *)&snapshot + offsetof(struct stmmac_rxq_stats, rx_pkt_n); - for (stat = 0; stat < STMMAC_RXQ_STATS; stat++) { - *data++ = (*(u64 *)p); - p += sizeof(u64); - } + *data++ = pkt_n; + *data++ = stmmac_get_rx_normal_irq_n(priv, q); } } @@ -643,39 +680,49 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, pos = j; for (i = 0; i < rx_queues_count; i++) { struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[i]; - struct stmmac_rxq_stats snapshot; + struct stmmac_napi_rx_stats snapshot; + u64 n_irq; j = pos; do { - start = u64_stats_fetch_begin(&rxq_stats->syncp); - snapshot = *rxq_stats; - } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); - - data[j++] += snapshot.rx_pkt_n; - data[j++] += snapshot.rx_normal_irq_n; - normal_irq_n += snapshot.rx_normal_irq_n; - napi_poll += snapshot.napi_poll; + start = u64_stats_fetch_begin(&rxq_stats->napi_syncp); + snapshot = rxq_stats->napi; + } while (u64_stats_fetch_retry(&rxq_stats->napi_syncp, start)); + + data[j++] += u64_stats_read(&snapshot.rx_pkt_n); + n_irq = stmmac_get_rx_normal_irq_n(priv, i); + data[j++] += n_irq; + normal_irq_n += n_irq; + napi_poll += u64_stats_read(&snapshot.poll); } pos = j; for (i = 0; i < tx_queues_count; i++) { struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[i]; - struct stmmac_txq_stats snapshot; + struct stmmac_napi_tx_stats napi_snapshot; + struct stmmac_q_tx_stats q_snapshot; + u64 n_irq; j = pos; do { - start = u64_stats_fetch_begin(&txq_stats->syncp); - snapshot = *txq_stats; - } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); - - data[j++] += snapshot.tx_pkt_n; - data[j++] += snapshot.tx_normal_irq_n; - normal_irq_n += snapshot.tx_normal_irq_n; - data[j++] += snapshot.tx_clean; - data[j++] += snapshot.tx_set_ic_bit; - data[j++] += snapshot.tx_tso_frames; - data[j++] += snapshot.tx_tso_nfrags; - napi_poll += snapshot.napi_poll; + start = u64_stats_fetch_begin(&txq_stats->q_syncp); + q_snapshot = txq_stats->q; + } while (u64_stats_fetch_retry(&txq_stats->q_syncp, start)); + do { + start = u64_stats_fetch_begin(&txq_stats->napi_syncp); + napi_snapshot = txq_stats->napi; + } while (u64_stats_fetch_retry(&txq_stats->napi_syncp, start)); + + data[j++] += u64_stats_read(&napi_snapshot.tx_pkt_n); + n_irq = stmmac_get_tx_normal_irq_n(priv, i); + data[j++] += n_irq; + normal_irq_n += n_irq; + data[j++] += u64_stats_read(&napi_snapshot.tx_clean); + data[j++] += u64_stats_read(&q_snapshot.tx_set_ic_bit) + + u64_stats_read(&napi_snapshot.tx_set_ic_bit); + data[j++] += u64_stats_read(&q_snapshot.tx_tso_frames); + data[j++] += u64_stats_read(&q_snapshot.tx_tso_nfrags); + napi_poll += u64_stats_read(&napi_snapshot.poll); } normal_irq_n += priv->xstats.rx_early_irq; data[j++] = normal_irq_n; @@ -830,10 +877,16 @@ static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) if (wol->wolopts) { pr_info("stmmac: wakeup enable\n"); device_set_wakeup_enable(priv->device, 1); - enable_irq_wake(priv->wol_irq); + /* Avoid unbalanced enable_irq_wake calls */ + if (priv->wol_irq_disabled) + enable_irq_wake(priv->wol_irq); + priv->wol_irq_disabled = false; } else { device_set_wakeup_enable(priv->device, 0); - disable_irq_wake(priv->wol_irq); + /* Avoid unbalanced disable_irq_wake calls */ + if (!priv->wol_irq_disabled) + disable_irq_wake(priv->wol_irq); + priv->wol_irq_disabled = true; } mutex_lock(&priv->lock); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 47de466e432c..75d029704503 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2482,7 +2482,6 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) struct xdp_desc xdp_desc; bool work_done = true; u32 tx_set_ic_bit = 0; - unsigned long flags; /* Avoids TX time-out as we are sharing with slow path */ txq_trans_cond_update(nq); @@ -2566,9 +2565,9 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size); entry = tx_q->cur_tx; } - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->tx_set_ic_bit += tx_set_ic_bit; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_update_begin(&txq_stats->napi_syncp); + u64_stats_add(&txq_stats->napi.tx_set_ic_bit, tx_set_ic_bit); + u64_stats_update_end(&txq_stats->napi_syncp); if (tx_desc) { stmmac_flush_tx_descriptors(priv, queue); @@ -2616,7 +2615,6 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, unsigned int bytes_compl = 0, pkts_compl = 0; unsigned int entry, xmits = 0, count = 0; u32 tx_packets = 0, tx_errors = 0; - unsigned long flags; __netif_tx_lock_bh(netdev_get_tx_queue(priv->dev, queue)); @@ -2782,11 +2780,11 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, if (tx_q->dirty_tx != tx_q->cur_tx) *pending_packets = true; - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->tx_packets += tx_packets; - txq_stats->tx_pkt_n += tx_packets; - txq_stats->tx_clean++; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_update_begin(&txq_stats->napi_syncp); + u64_stats_add(&txq_stats->napi.tx_packets, tx_packets); + u64_stats_add(&txq_stats->napi.tx_pkt_n, tx_packets); + u64_stats_inc(&txq_stats->napi.tx_clean); + u64_stats_update_end(&txq_stats->napi_syncp); priv->xstats.tx_errors += tx_errors; @@ -3628,6 +3626,7 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev) /* Request the Wake IRQ in case of another line * is used for WoL */ + priv->wol_irq_disabled = true; if (priv->wol_irq > 0 && priv->wol_irq != dev->irq) { int_name = priv->int_name_wol; sprintf(int_name, "%s:%s", dev->name, "wol"); @@ -3931,6 +3930,9 @@ static int __stmmac_open(struct net_device *dev, priv->rx_copybreak = STMMAC_RX_COPYBREAK; buf_sz = dma_conf->dma_buf_sz; + for (int i = 0; i < MTL_MAX_TX_QUEUES; i++) + if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_EN) + dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs; memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf)); stmmac_reset_queues_param(priv); @@ -4209,7 +4211,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) struct stmmac_tx_queue *tx_q; bool has_vlan, set_ic; u8 proto_hdr_len, hdr; - unsigned long flags; u32 pay_len, mss; dma_addr_t des; int i; @@ -4374,13 +4375,13 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); } - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->tx_bytes += skb->len; - txq_stats->tx_tso_frames++; - txq_stats->tx_tso_nfrags += nfrags; + u64_stats_update_begin(&txq_stats->q_syncp); + u64_stats_add(&txq_stats->q.tx_bytes, skb->len); + u64_stats_inc(&txq_stats->q.tx_tso_frames); + u64_stats_add(&txq_stats->q.tx_tso_nfrags, nfrags); if (set_ic) - txq_stats->tx_set_ic_bit++; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_inc(&txq_stats->q.tx_set_ic_bit); + u64_stats_update_end(&txq_stats->q_syncp); if (priv->sarc_type) stmmac_set_desc_sarc(priv, first, priv->sarc_type); @@ -4435,6 +4436,28 @@ dma_map_err: } /** + * stmmac_has_ip_ethertype() - Check if packet has IP ethertype + * @skb: socket buffer to check + * + * Check if a packet has an ethertype that will trigger the IP header checks + * and IP/TCP checksum engine of the stmmac core. + * + * Return: true if the ethertype can trigger the checksum engine, false + * otherwise + */ +static bool stmmac_has_ip_ethertype(struct sk_buff *skb) +{ + int depth = 0; + __be16 proto; + + proto = __vlan_get_protocol(skb, eth_header_parse_protocol(skb), + &depth); + + return (depth <= ETH_HLEN) && + (proto == htons(ETH_P_IP) || proto == htons(ETH_P_IPV6)); +} + +/** * stmmac_xmit - Tx entry point of the driver * @skb : the socket buffer * @dev : device pointer @@ -4457,7 +4480,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) struct stmmac_tx_queue *tx_q; bool has_vlan, set_ic; int entry, first_tx; - unsigned long flags; dma_addr_t des; tx_q = &priv->dma_conf.tx_queue[queue]; @@ -4498,9 +4520,13 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) /* DWMAC IPs can be synthesized to support tx coe only for a few tx * queues. In that case, checksum offloading for those queues that don't * support tx coe needs to fallback to software checksum calculation. + * + * Packets that won't trigger the COE e.g. most DSA-tagged packets will + * also have to be checksummed in software. */ if (csum_insertion && - priv->plat->tx_queues_cfg[queue].coe_unsupported) { + (priv->plat->tx_queues_cfg[queue].coe_unsupported || + !stmmac_has_ip_ethertype(skb))) { if (unlikely(skb_checksum_help(skb))) goto dma_map_err; csum_insertion = !csum_insertion; @@ -4623,11 +4649,11 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); } - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->tx_bytes += skb->len; + u64_stats_update_begin(&txq_stats->q_syncp); + u64_stats_add(&txq_stats->q.tx_bytes, skb->len); if (set_ic) - txq_stats->tx_set_ic_bit++; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_inc(&txq_stats->q.tx_set_ic_bit); + u64_stats_update_end(&txq_stats->q_syncp); if (priv->sarc_type) stmmac_set_desc_sarc(priv, first, priv->sarc_type); @@ -4891,12 +4917,11 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue, set_ic = false; if (set_ic) { - unsigned long flags; tx_q->tx_count_frames = 0; stmmac_set_tx_ic(priv, tx_desc); - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->tx_set_ic_bit++; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_update_begin(&txq_stats->q_syncp); + u64_stats_inc(&txq_stats->q.tx_set_ic_bit); + u64_stats_update_end(&txq_stats->q_syncp); } stmmac_enable_dma_transmission(priv, priv->ioaddr); @@ -5046,7 +5071,6 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue, unsigned int len = xdp->data_end - xdp->data; enum pkt_hash_types hash_type; int coe = priv->hw->rx_csum; - unsigned long flags; struct sk_buff *skb; u32 hash; @@ -5065,7 +5089,7 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue, stmmac_rx_vlan(priv->dev, skb); skb->protocol = eth_type_trans(skb, priv->dev); - if (unlikely(!coe)) + if (unlikely(!coe) || !stmmac_has_ip_ethertype(skb)) skb_checksum_none_assert(skb); else skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -5076,10 +5100,10 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue, skb_record_rx_queue(skb, queue); napi_gro_receive(&ch->rxtx_napi, skb); - flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); - rxq_stats->rx_pkt_n++; - rxq_stats->rx_bytes += len; - u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); + u64_stats_update_begin(&rxq_stats->napi_syncp); + u64_stats_inc(&rxq_stats->napi.rx_pkt_n); + u64_stats_add(&rxq_stats->napi.rx_bytes, len); + u64_stats_update_end(&rxq_stats->napi_syncp); } static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget) @@ -5161,7 +5185,6 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) unsigned int desc_size; struct bpf_prog *prog; bool failure = false; - unsigned long flags; int xdp_status = 0; int status = 0; @@ -5316,9 +5339,9 @@ read_again: stmmac_finalize_xdp_rx(priv, xdp_status); - flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); - rxq_stats->rx_pkt_n += count; - u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); + u64_stats_update_begin(&rxq_stats->napi_syncp); + u64_stats_add(&rxq_stats->napi.rx_pkt_n, count); + u64_stats_update_end(&rxq_stats->napi_syncp); priv->xstats.rx_dropped += rx_dropped; priv->xstats.rx_errors += rx_errors; @@ -5356,7 +5379,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) unsigned int desc_size; struct sk_buff *skb = NULL; struct stmmac_xdp_buff ctx; - unsigned long flags; int xdp_status = 0; int buf_sz; @@ -5588,7 +5610,7 @@ drain_data: skb->protocol = eth_type_trans(skb, priv->dev); - if (unlikely(!coe)) + if (unlikely(!coe) || !stmmac_has_ip_ethertype(skb)) skb_checksum_none_assert(skb); else skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -5616,11 +5638,11 @@ drain_data: stmmac_rx_refill(priv, queue); - flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); - rxq_stats->rx_packets += rx_packets; - rxq_stats->rx_bytes += rx_bytes; - rxq_stats->rx_pkt_n += count; - u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); + u64_stats_update_begin(&rxq_stats->napi_syncp); + u64_stats_add(&rxq_stats->napi.rx_packets, rx_packets); + u64_stats_add(&rxq_stats->napi.rx_bytes, rx_bytes); + u64_stats_add(&rxq_stats->napi.rx_pkt_n, count); + u64_stats_update_end(&rxq_stats->napi_syncp); priv->xstats.rx_dropped += rx_dropped; priv->xstats.rx_errors += rx_errors; @@ -5635,13 +5657,12 @@ static int stmmac_napi_poll_rx(struct napi_struct *napi, int budget) struct stmmac_priv *priv = ch->priv_data; struct stmmac_rxq_stats *rxq_stats; u32 chan = ch->index; - unsigned long flags; int work_done; rxq_stats = &priv->xstats.rxq_stats[chan]; - flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); - rxq_stats->napi_poll++; - u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); + u64_stats_update_begin(&rxq_stats->napi_syncp); + u64_stats_inc(&rxq_stats->napi.poll); + u64_stats_update_end(&rxq_stats->napi_syncp); work_done = stmmac_rx(priv, budget, chan); if (work_done < budget && napi_complete_done(napi, work_done)) { @@ -5663,13 +5684,12 @@ static int stmmac_napi_poll_tx(struct napi_struct *napi, int budget) struct stmmac_txq_stats *txq_stats; bool pending_packets = false; u32 chan = ch->index; - unsigned long flags; int work_done; txq_stats = &priv->xstats.txq_stats[chan]; - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->napi_poll++; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_update_begin(&txq_stats->napi_syncp); + u64_stats_inc(&txq_stats->napi.poll); + u64_stats_update_end(&txq_stats->napi_syncp); work_done = stmmac_tx_clean(priv, budget, chan, &pending_packets); work_done = min(work_done, budget); @@ -5699,17 +5719,16 @@ static int stmmac_napi_poll_rxtx(struct napi_struct *napi, int budget) struct stmmac_rxq_stats *rxq_stats; struct stmmac_txq_stats *txq_stats; u32 chan = ch->index; - unsigned long flags; rxq_stats = &priv->xstats.rxq_stats[chan]; - flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); - rxq_stats->napi_poll++; - u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); + u64_stats_update_begin(&rxq_stats->napi_syncp); + u64_stats_inc(&rxq_stats->napi.poll); + u64_stats_update_end(&rxq_stats->napi_syncp); txq_stats = &priv->xstats.txq_stats[chan]; - flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); - txq_stats->napi_poll++; - u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); + u64_stats_update_begin(&txq_stats->napi_syncp); + u64_stats_inc(&txq_stats->napi.poll); + u64_stats_update_end(&txq_stats->napi_syncp); tx_done = stmmac_tx_clean(priv, budget, chan, &tx_pending_packets); tx_done = min(tx_done, budget); @@ -7035,10 +7054,13 @@ static void stmmac_get_stats64(struct net_device *dev, struct rtnl_link_stats64 u64 tx_bytes; do { - start = u64_stats_fetch_begin(&txq_stats->syncp); - tx_packets = txq_stats->tx_packets; - tx_bytes = txq_stats->tx_bytes; - } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); + start = u64_stats_fetch_begin(&txq_stats->q_syncp); + tx_bytes = u64_stats_read(&txq_stats->q.tx_bytes); + } while (u64_stats_fetch_retry(&txq_stats->q_syncp, start)); + do { + start = u64_stats_fetch_begin(&txq_stats->napi_syncp); + tx_packets = u64_stats_read(&txq_stats->napi.tx_packets); + } while (u64_stats_fetch_retry(&txq_stats->napi_syncp, start)); stats->tx_packets += tx_packets; stats->tx_bytes += tx_bytes; @@ -7050,10 +7072,10 @@ static void stmmac_get_stats64(struct net_device *dev, struct rtnl_link_stats64 u64 rx_bytes; do { - start = u64_stats_fetch_begin(&rxq_stats->syncp); - rx_packets = rxq_stats->rx_packets; - rx_bytes = rxq_stats->rx_bytes; - } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); + start = u64_stats_fetch_begin(&rxq_stats->napi_syncp); + rx_packets = u64_stats_read(&rxq_stats->napi.rx_packets); + rx_bytes = u64_stats_read(&rxq_stats->napi.rx_bytes); + } while (u64_stats_fetch_retry(&rxq_stats->napi_syncp, start)); stats->rx_packets += rx_packets; stats->rx_bytes += rx_bytes; @@ -7447,9 +7469,16 @@ int stmmac_dvr_probe(struct device *device, priv->dev = ndev; for (i = 0; i < MTL_MAX_RX_QUEUES; i++) - u64_stats_init(&priv->xstats.rxq_stats[i].syncp); - for (i = 0; i < MTL_MAX_TX_QUEUES; i++) - u64_stats_init(&priv->xstats.txq_stats[i].syncp); + u64_stats_init(&priv->xstats.rxq_stats[i].napi_syncp); + for (i = 0; i < MTL_MAX_TX_QUEUES; i++) { + u64_stats_init(&priv->xstats.txq_stats[i].q_syncp); + u64_stats_init(&priv->xstats.txq_stats[i].napi_syncp); + } + + priv->xstats.pcpu_stats = + devm_netdev_alloc_pcpu_stats(device, struct stmmac_pcpu_stats); + if (!priv->xstats.pcpu_stats) + return -ENOMEM; stmmac_set_ethtool_ops(ndev); priv->pause = pause; @@ -7515,6 +7544,9 @@ int stmmac_dvr_probe(struct device *device, dev_err(priv->device, "unable to bring out of ahb reset: %pe\n", ERR_PTR(ret)); + /* Wait a bit for the reset to take effect */ + udelay(10); + /* Init MAC and get the capabilities */ ret = stmmac_hw_init(priv); if (ret) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index faa0561e988e..9d2f4ac783e4 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -56,7 +56,7 @@ #define AM65_CPSW_MAX_PORTS 8 #define AM65_CPSW_MIN_PACKET_SIZE VLAN_ETH_ZLEN -#define AM65_CPSW_MAX_PACKET_SIZE (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN) +#define AM65_CPSW_MAX_PACKET_SIZE 2024 #define AM65_CPSW_REG_CTL 0x004 #define AM65_CPSW_REG_STAT_PORT_EN 0x014 @@ -2244,7 +2244,8 @@ am65_cpsw_nuss_init_port_ndev(struct am65_cpsw_common *common, u32 port_idx) eth_hw_addr_set(port->ndev, port->slave.mac_addr); port->ndev->min_mtu = AM65_CPSW_MIN_PACKET_SIZE; - port->ndev->max_mtu = AM65_CPSW_MAX_PACKET_SIZE; + port->ndev->max_mtu = AM65_CPSW_MAX_PACKET_SIZE - + (VLAN_ETH_HLEN + ETH_FCS_LEN); port->ndev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM | NETIF_F_HW_CSUM | diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index ea85c6dd5484..c0a5abd8d9a8 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -631,6 +631,8 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) } } + phy->mac_managed_pm = true; + slave->phy = phy; phy_attached_info(slave->phy); diff --git a/drivers/net/ethernet/ti/cpsw_new.c b/drivers/net/ethernet/ti/cpsw_new.c index 498c50c6d1a7..087dcb67505a 100644 --- a/drivers/net/ethernet/ti/cpsw_new.c +++ b/drivers/net/ethernet/ti/cpsw_new.c @@ -773,6 +773,9 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) slave->slave_num); return; } + + phy->mac_managed_pm = true; + slave->phy = phy; phy_attached_info(slave->phy); diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig index 23cd610bd376..85cdbdd44fec 100644 --- a/drivers/net/ethernet/wangxun/Kconfig +++ b/drivers/net/ethernet/wangxun/Kconfig @@ -26,7 +26,7 @@ config NGBE tristate "Wangxun(R) GbE PCI Express adapters support" depends on PCI select LIBWX - select PHYLIB + select PHYLINK help This driver supports Wangxun(R) GbE PCI Express family of adapters. diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 23355cc408fd..8706223a6e5a 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -2769,4 +2769,5 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count, } EXPORT_SYMBOL(wx_set_ring); +MODULE_DESCRIPTION("Common library for Wangxun(R) Ethernet drivers."); MODULE_LICENSE("GPL"); diff --git a/drivers/net/fjes/fjes_hw.c b/drivers/net/fjes/fjes_hw.c index 704e949484d0..b9b5554ea862 100644 --- a/drivers/net/fjes/fjes_hw.c +++ b/drivers/net/fjes/fjes_hw.c @@ -221,21 +221,25 @@ static int fjes_hw_setup(struct fjes_hw *hw) mem_size = FJES_DEV_REQ_BUF_SIZE(hw->max_epid); hw->hw_info.req_buf = kzalloc(mem_size, GFP_KERNEL); - if (!(hw->hw_info.req_buf)) - return -ENOMEM; + if (!(hw->hw_info.req_buf)) { + result = -ENOMEM; + goto free_ep_info; + } hw->hw_info.req_buf_size = mem_size; mem_size = FJES_DEV_RES_BUF_SIZE(hw->max_epid); hw->hw_info.res_buf = kzalloc(mem_size, GFP_KERNEL); - if (!(hw->hw_info.res_buf)) - return -ENOMEM; + if (!(hw->hw_info.res_buf)) { + result = -ENOMEM; + goto free_req_buf; + } hw->hw_info.res_buf_size = mem_size; result = fjes_hw_alloc_shared_status_region(hw); if (result) - return result; + goto free_res_buf; hw->hw_info.buffer_share_bit = 0; hw->hw_info.buffer_unshare_reserve_bit = 0; @@ -246,11 +250,11 @@ static int fjes_hw_setup(struct fjes_hw *hw) result = fjes_hw_alloc_epbuf(&buf_pair->tx); if (result) - return result; + goto free_epbuf; result = fjes_hw_alloc_epbuf(&buf_pair->rx); if (result) - return result; + goto free_epbuf; spin_lock_irqsave(&hw->rx_status_lock, flags); fjes_hw_setup_epbuf(&buf_pair->tx, mac, @@ -273,6 +277,25 @@ static int fjes_hw_setup(struct fjes_hw *hw) fjes_hw_init_command_registers(hw, ¶m); return 0; + +free_epbuf: + for (epidx = 0; epidx < hw->max_epid ; epidx++) { + if (epidx == hw->my_epid) + continue; + fjes_hw_free_epbuf(&hw->ep_shm_info[epidx].tx); + fjes_hw_free_epbuf(&hw->ep_shm_info[epidx].rx); + } + fjes_hw_free_shared_status_region(hw); +free_res_buf: + kfree(hw->hw_info.res_buf); + hw->hw_info.res_buf = NULL; +free_req_buf: + kfree(hw->hw_info.req_buf); + hw->hw_info.req_buf = NULL; +free_ep_info: + kfree(hw->ep_shm_info); + hw->ep_shm_info = NULL; + return result; } static void fjes_hw_cleanup(struct fjes_hw *hw) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 1dafa44155d0..a6fcbda64ecc 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -708,7 +708,10 @@ void netvsc_device_remove(struct hv_device *device) /* Disable NAPI and disassociate its context from the device. */ for (i = 0; i < net_device->num_chn; i++) { /* See also vmbus_reset_channel_cb(). */ - napi_disable(&net_device->chan_table[i].napi); + /* only disable enabled NAPI channel */ + if (i < ndev->real_num_rx_queues) + napi_disable(&net_device->chan_table[i].napi); + netif_napi_del(&net_device->chan_table[i].napi); } diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 4406427d4617..11831a1c9762 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -42,9 +42,13 @@ #define LINKCHANGE_INT (2 * HZ) #define VF_TAKEOVER_INT (HZ / 10) +/* Macros to define the context of vf registration */ +#define VF_REG_IN_PROBE 1 +#define VF_REG_IN_NOTIFIER 2 + static unsigned int ring_size __ro_after_init = 128; module_param(ring_size, uint, 0444); -MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); +MODULE_PARM_DESC(ring_size, "Ring buffer size (# of 4K pages)"); unsigned int netvsc_ring_bytes __ro_after_init; static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | @@ -2185,7 +2189,7 @@ static rx_handler_result_t netvsc_vf_handle_frame(struct sk_buff **pskb) } static int netvsc_vf_join(struct net_device *vf_netdev, - struct net_device *ndev) + struct net_device *ndev, int context) { struct net_device_context *ndev_ctx = netdev_priv(ndev); int ret; @@ -2208,7 +2212,11 @@ static int netvsc_vf_join(struct net_device *vf_netdev, goto upper_link_failed; } - schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT); + /* If this registration is called from probe context vf_takeover + * is taken care of later in probe itself. + */ + if (context == VF_REG_IN_NOTIFIER) + schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT); call_netdevice_notifiers(NETDEV_JOIN, vf_netdev); @@ -2346,7 +2354,7 @@ static int netvsc_prepare_bonding(struct net_device *vf_netdev) return NOTIFY_DONE; } -static int netvsc_register_vf(struct net_device *vf_netdev) +static int netvsc_register_vf(struct net_device *vf_netdev, int context) { struct net_device_context *net_device_ctx; struct netvsc_device *netvsc_dev; @@ -2386,7 +2394,7 @@ static int netvsc_register_vf(struct net_device *vf_netdev) netdev_info(ndev, "VF registering: %s\n", vf_netdev->name); - if (netvsc_vf_join(vf_netdev, ndev) != 0) + if (netvsc_vf_join(vf_netdev, ndev, context) != 0) return NOTIFY_DONE; dev_hold(vf_netdev); @@ -2484,10 +2492,31 @@ static int netvsc_unregister_vf(struct net_device *vf_netdev) return NOTIFY_OK; } +static int check_dev_is_matching_vf(struct net_device *event_ndev) +{ + /* Skip NetVSC interfaces */ + if (event_ndev->netdev_ops == &device_ops) + return -ENODEV; + + /* Avoid non-Ethernet type devices */ + if (event_ndev->type != ARPHRD_ETHER) + return -ENODEV; + + /* Avoid Vlan dev with same MAC registering as VF */ + if (is_vlan_dev(event_ndev)) + return -ENODEV; + + /* Avoid Bonding master dev with same MAC registering as VF */ + if (netif_is_bond_master(event_ndev)) + return -ENODEV; + + return 0; +} + static int netvsc_probe(struct hv_device *dev, const struct hv_vmbus_device_id *dev_id) { - struct net_device *net = NULL; + struct net_device *net = NULL, *vf_netdev; struct net_device_context *net_device_ctx; struct netvsc_device_info *device_info = NULL; struct netvsc_device *nvdev; @@ -2599,6 +2628,30 @@ static int netvsc_probe(struct hv_device *dev, } list_add(&net_device_ctx->list, &netvsc_dev_list); + + /* When the hv_netvsc driver is unloaded and reloaded, the + * NET_DEVICE_REGISTER for the vf device is replayed before probe + * is complete. This is because register_netdevice_notifier() gets + * registered before vmbus_driver_register() so that callback func + * is set before probe and we don't miss events like NETDEV_POST_INIT + * So, in this section we try to register the matching vf device that + * is present as a netdevice, knowing that its register call is not + * processed in the netvsc_netdev_notifier(as probing is progress and + * get_netvsc_byslot fails). + */ + for_each_netdev(dev_net(net), vf_netdev) { + ret = check_dev_is_matching_vf(vf_netdev); + if (ret != 0) + continue; + + if (net != get_netvsc_byslot(vf_netdev)) + continue; + + netvsc_prepare_bonding(vf_netdev); + netvsc_register_vf(vf_netdev, VF_REG_IN_PROBE); + __netvsc_vf_setup(net, vf_netdev); + break; + } rtnl_unlock(); netvsc_devinfo_put(device_info); @@ -2754,28 +2807,17 @@ static int netvsc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); + int ret = 0; - /* Skip our own events */ - if (event_dev->netdev_ops == &device_ops) - return NOTIFY_DONE; - - /* Avoid non-Ethernet type devices */ - if (event_dev->type != ARPHRD_ETHER) - return NOTIFY_DONE; - - /* Avoid Vlan dev with same MAC registering as VF */ - if (is_vlan_dev(event_dev)) - return NOTIFY_DONE; - - /* Avoid Bonding master dev with same MAC registering as VF */ - if (netif_is_bond_master(event_dev)) + ret = check_dev_is_matching_vf(event_dev); + if (ret != 0) return NOTIFY_DONE; switch (event) { case NETDEV_POST_INIT: return netvsc_prepare_bonding(event_dev); case NETDEV_REGISTER: - return netvsc_register_vf(event_dev); + return netvsc_register_vf(event_dev, VF_REG_IN_NOTIFIER); case NETDEV_UNREGISTER: return netvsc_unregister_vf(event_dev); case NETDEV_UP: @@ -2807,7 +2849,7 @@ static int __init netvsc_drv_init(void) pr_info("Increased ring_size to %u (min allowed)\n", ring_size); } - netvsc_ring_bytes = ring_size * PAGE_SIZE; + netvsc_ring_bytes = VMBUS_RING_SIZE(ring_size * 4096); register_netdevice_notifier(&netvsc_netdev_notifier); diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index e34816638569..7f5426285c61 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -607,11 +607,26 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, return ERR_PTR(-EINVAL); } - ret = skb_ensure_writable_head_tail(skb, dev); - if (unlikely(ret < 0)) { - macsec_txsa_put(tx_sa); - kfree_skb(skb); - return ERR_PTR(ret); + if (unlikely(skb_headroom(skb) < MACSEC_NEEDED_HEADROOM || + skb_tailroom(skb) < MACSEC_NEEDED_TAILROOM)) { + struct sk_buff *nskb = skb_copy_expand(skb, + MACSEC_NEEDED_HEADROOM, + MACSEC_NEEDED_TAILROOM, + GFP_ATOMIC); + if (likely(nskb)) { + consume_skb(skb); + skb = nskb; + } else { + macsec_txsa_put(tx_sa); + kfree_skb(skb); + return ERR_PTR(-ENOMEM); + } + } else { + skb = skb_unshare(skb, GFP_ATOMIC); + if (!skb) { + macsec_txsa_put(tx_sa); + return ERR_PTR(-ENOMEM); + } } unprotected_len = skb->len; diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index b4d3b9cde8bd..92a7a36b93ac 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -835,14 +835,14 @@ static void nsim_dev_trap_report_work(struct work_struct *work) trap_report_dw.work); nsim_dev = nsim_trap_data->nsim_dev; - /* For each running port and enabled packet trap, generate a UDP - * packet with a random 5-tuple and report it. - */ if (!devl_trylock(priv_to_devlink(nsim_dev))) { - schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 0); + schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 1); return; } + /* For each running port and enabled packet trap, generate a UDP + * packet with a random 5-tuple and report it. + */ list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) { if (!netif_running(nsim_dev_port->ns->netdev)) continue; diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index aecaf5f44374..77e8250282a5 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -369,6 +369,12 @@ static int nsim_init_netdevsim_vf(struct netdevsim *ns) return err; } +static void nsim_exit_netdevsim(struct netdevsim *ns) +{ + nsim_udp_tunnels_info_destroy(ns->netdev); + mock_phc_destroy(ns->phc); +} + struct netdevsim * nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port) { @@ -417,8 +423,7 @@ void nsim_destroy(struct netdevsim *ns) } rtnl_unlock(); if (nsim_dev_port_is_pf(ns->nsim_dev_port)) - nsim_udp_tunnels_info_destroy(dev); - mock_phc_destroy(ns->phc); + nsim_exit_netdevsim(ns); free_netdev(dev); } diff --git a/drivers/net/phy/mediatek-ge-soc.c b/drivers/net/phy/mediatek-ge-soc.c index 8a20d9889f10..0f3a1538a8b8 100644 --- a/drivers/net/phy/mediatek-ge-soc.c +++ b/drivers/net/phy/mediatek-ge-soc.c @@ -489,7 +489,7 @@ static int tx_r50_fill_result(struct phy_device *phydev, u16 tx_r50_cal_val, u16 reg, val; if (phydev->drv->phy_id == MTK_GPHY_ID_MT7988) - bias = -2; + bias = -1; val = clamp_val(bias + tx_r50_cal_val, 0, 63); @@ -705,6 +705,11 @@ restore: static void mt798x_phy_common_finetune(struct phy_device *phydev) { phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + /* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */ + __phy_write(phydev, 0x11, 0xc71); + __phy_write(phydev, 0x12, 0xc); + __phy_write(phydev, 0x10, 0x8fae); + /* EnabRandUpdTrig = 1 */ __phy_write(phydev, 0x11, 0x2f00); __phy_write(phydev, 0x12, 0xe); @@ -715,15 +720,56 @@ static void mt798x_phy_common_finetune(struct phy_device *phydev) __phy_write(phydev, 0x12, 0x0); __phy_write(phydev, 0x10, 0x83aa); - /* TrFreeze = 0 */ + /* FfeUpdGainForce = 1(Enable), FfeUpdGainForceVal = 4 */ + __phy_write(phydev, 0x11, 0x240); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x9680); + + /* TrFreeze = 0 (mt7988 default) */ __phy_write(phydev, 0x11, 0x0); __phy_write(phydev, 0x12, 0x0); __phy_write(phydev, 0x10, 0x9686); + /* SSTrKp100 = 5 */ + /* SSTrKf100 = 6 */ + /* SSTrKp1000Mas = 5 */ + /* SSTrKf1000Mas = 6 */ /* SSTrKp1000Slv = 5 */ + /* SSTrKf1000Slv = 6 */ __phy_write(phydev, 0x11, 0xbaef); __phy_write(phydev, 0x12, 0x2e); __phy_write(phydev, 0x10, 0x968c); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +} + +static void mt7981_phy_finetune(struct phy_device *phydev) +{ + u16 val[8] = { 0x01ce, 0x01c1, + 0x020f, 0x0202, + 0x03d0, 0x03c0, + 0x0013, 0x0005 }; + int i, k; + + /* 100M eye finetune: + * Keep middle level of TX MLT3 shapper as default. + * Only change TX MLT3 overshoot level here. + */ + for (k = 0, i = 1; i < 12; i++) { + if (i % 3 == 0) + continue; + phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[k++]); + } + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + /* ResetSyncOffset = 6 */ + __phy_write(phydev, 0x11, 0x600); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x8fc0); + + /* VgaDecRate = 1 */ + __phy_write(phydev, 0x11, 0x4c2a); + __phy_write(phydev, 0x12, 0x3e); + __phy_write(phydev, 0x10, 0x8fa4); /* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2, * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2 @@ -738,7 +784,7 @@ static void mt798x_phy_common_finetune(struct phy_device *phydev) __phy_write(phydev, 0x10, 0x8ec0); phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); - /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9*/ + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9 */ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); @@ -771,48 +817,6 @@ static void mt798x_phy_common_finetune(struct phy_device *phydev) phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_OUTPUT_V, 0x2222); } -static void mt7981_phy_finetune(struct phy_device *phydev) -{ - u16 val[8] = { 0x01ce, 0x01c1, - 0x020f, 0x0202, - 0x03d0, 0x03c0, - 0x0013, 0x0005 }; - int i, k; - - /* 100M eye finetune: - * Keep middle level of TX MLT3 shapper as default. - * Only change TX MLT3 overshoot level here. - */ - for (k = 0, i = 1; i < 12; i++) { - if (i % 3 == 0) - continue; - phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[k++]); - } - - phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); - /* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */ - __phy_write(phydev, 0x11, 0xc71); - __phy_write(phydev, 0x12, 0xc); - __phy_write(phydev, 0x10, 0x8fae); - - /* ResetSyncOffset = 6 */ - __phy_write(phydev, 0x11, 0x600); - __phy_write(phydev, 0x12, 0x0); - __phy_write(phydev, 0x10, 0x8fc0); - - /* VgaDecRate = 1 */ - __phy_write(phydev, 0x11, 0x4c2a); - __phy_write(phydev, 0x12, 0x3e); - __phy_write(phydev, 0x10, 0x8fa4); - - /* FfeUpdGainForce = 4 */ - __phy_write(phydev, 0x11, 0x240); - __phy_write(phydev, 0x12, 0x0); - __phy_write(phydev, 0x10, 0x9680); - - phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); -} - static void mt7988_phy_finetune(struct phy_device *phydev) { u16 val[12] = { 0x0187, 0x01cd, 0x01c8, 0x0182, @@ -827,17 +831,7 @@ static void mt7988_phy_finetune(struct phy_device *phydev) /* TCT finetune */ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5); - /* Disable TX power saving */ - phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, - MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8); - phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); - - /* SlvDSPreadyTime = 24, MasDSPreadyTime = 12 */ - __phy_write(phydev, 0x11, 0x671); - __phy_write(phydev, 0x12, 0xc); - __phy_write(phydev, 0x10, 0x8fae); - /* ResetSyncOffset = 5 */ __phy_write(phydev, 0x11, 0x500); __phy_write(phydev, 0x12, 0x0); @@ -845,13 +839,27 @@ static void mt7988_phy_finetune(struct phy_device *phydev) /* VgaDecRate is 1 at default on mt7988 */ - phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + /* MrvlTrFix100Kp = 6, MrvlTrFix100Kf = 7, + * MrvlTrFix1000Kp = 6, MrvlTrFix1000Kf = 7 + */ + __phy_write(phydev, 0x11, 0xb90a); + __phy_write(phydev, 0x12, 0x6f); + __phy_write(phydev, 0x10, 0x8f82); + + /* RemAckCntLimitCtrl = 1 */ + __phy_write(phydev, 0x11, 0xfbba); + __phy_write(phydev, 0x12, 0xc3); + __phy_write(phydev, 0x10, 0x87f8); - phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_2A30); - /* TxClkOffset = 2 */ - __phy_modify(phydev, MTK_PHY_ANARG_RG, MTK_PHY_TCLKOFFSET_MASK, - FIELD_PREP(MTK_PHY_TCLKOFFSET_MASK, 0x2)); phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 10 */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, + MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, + BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0xa)); + + /* rg_tr_lpf_cnt_val = 1023 */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x3ff); } static void mt798x_phy_eee(struct phy_device *phydev) @@ -884,11 +892,11 @@ static void mt798x_phy_eee(struct phy_device *phydev) MTK_PHY_LPI_SLV_SEND_TX_EN, FIELD_PREP(MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK, 0x120)); - phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG239, - MTK_PHY_LPI_SEND_LOC_TIMER_MASK | - MTK_PHY_LPI_TXPCS_LOC_RCV, - FIELD_PREP(MTK_PHY_LPI_SEND_LOC_TIMER_MASK, 0x117)); + /* Keep MTK_PHY_LPI_SEND_LOC_TIMER as 375 */ + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG239, + MTK_PHY_LPI_TXPCS_LOC_RCV); + /* This also fixes some IoT issues, such as CH340 */ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2C7, MTK_PHY_MAX_GAIN_MASK | MTK_PHY_MIN_GAIN_MASK, FIELD_PREP(MTK_PHY_MAX_GAIN_MASK, 0x8) | @@ -922,7 +930,7 @@ static void mt798x_phy_eee(struct phy_device *phydev) __phy_write(phydev, 0x12, 0x0); __phy_write(phydev, 0x10, 0x9690); - /* REG_EEE_st2TrKf1000 = 3 */ + /* REG_EEE_st2TrKf1000 = 2 */ __phy_write(phydev, 0x11, 0x114f); __phy_write(phydev, 0x12, 0x2); __phy_write(phydev, 0x10, 0x969a); @@ -947,7 +955,7 @@ static void mt798x_phy_eee(struct phy_device *phydev) __phy_write(phydev, 0x12, 0x0); __phy_write(phydev, 0x10, 0x96b8); - /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 1 */ + /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 0 */ __phy_write(phydev, 0x11, 0x1463); __phy_write(phydev, 0x12, 0x0); __phy_write(phydev, 0x10, 0x96ca); @@ -1459,6 +1467,13 @@ static int mt7988_phy_probe(struct phy_device *phydev) if (err) return err; + /* Disable TX power saving at probing to: + * 1. Meet common mode compliance test criteria + * 2. Make sure that TX-VCM calibration works fine + */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, + MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8); + return mt798x_phy_calibration(phydev); } diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index d2aa3d0695e3..dad720138baa 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -120,6 +120,11 @@ */ #define LAN8814_1PPM_FORMAT 17179 +#define PTP_RX_VERSION 0x0248 +#define PTP_TX_VERSION 0x0288 +#define PTP_MAX_VERSION(x) (((x) & GENMASK(7, 0)) << 8) +#define PTP_MIN_VERSION(x) ((x) & GENMASK(7, 0)) + #define PTP_RX_MOD 0x024F #define PTP_RX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_ BIT(3) #define PTP_RX_TIMESTAMP_EN 0x024D @@ -3150,6 +3155,12 @@ static void lan8814_ptp_init(struct phy_device *phydev) lanphy_write_page_reg(phydev, 5, PTP_TX_PARSE_IP_ADDR_EN, 0); lanphy_write_page_reg(phydev, 5, PTP_RX_PARSE_IP_ADDR_EN, 0); + /* Disable checking for minorVersionPTP field */ + lanphy_write_page_reg(phydev, 5, PTP_RX_VERSION, + PTP_MAX_VERSION(0xff) | PTP_MIN_VERSION(0x0)); + lanphy_write_page_reg(phydev, 5, PTP_TX_VERSION, + PTP_MAX_VERSION(0xff) | PTP_MIN_VERSION(0x0)); + skb_queue_head_init(&ptp_priv->tx_queue); skb_queue_head_init(&ptp_priv->rx_queue); INIT_LIST_HEAD(&ptp_priv->rx_ts_list); @@ -3338,8 +3349,10 @@ static int lan8814_probe(struct phy_device *phydev) #define LAN8841_ADC_CHANNEL_MASK 198 #define LAN8841_PTP_RX_PARSE_L2_ADDR_EN 370 #define LAN8841_PTP_RX_PARSE_IP_ADDR_EN 371 +#define LAN8841_PTP_RX_VERSION 374 #define LAN8841_PTP_TX_PARSE_L2_ADDR_EN 434 #define LAN8841_PTP_TX_PARSE_IP_ADDR_EN 435 +#define LAN8841_PTP_TX_VERSION 438 #define LAN8841_PTP_CMD_CTL 256 #define LAN8841_PTP_CMD_CTL_PTP_ENABLE BIT(2) #define LAN8841_PTP_CMD_CTL_PTP_DISABLE BIT(1) @@ -3383,6 +3396,12 @@ static int lan8841_config_init(struct phy_device *phydev) phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, LAN8841_PTP_RX_PARSE_IP_ADDR_EN, 0); + /* Disable checking for minorVersionPTP field */ + phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + LAN8841_PTP_RX_VERSION, 0xff00); + phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + LAN8841_PTP_TX_VERSION, 0xff00); + /* 100BT Clause 40 improvenent errata */ phy_write_mmd(phydev, LAN8841_MMD_ANALOG_REG, LAN8841_ANALOG_CONTROL_1, @@ -4839,6 +4858,7 @@ static struct phy_driver ksphy_driver[] = { .flags = PHY_POLL_CABLE_TEST, .driver_data = &ksz9131_type, .probe = kszphy_probe, + .soft_reset = genphy_soft_reset, .config_init = ksz9131_config_init, .config_intr = kszphy_config_intr, .config_aneg = ksz9131_config_aneg, diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 6fa679b36290..db39dec7f247 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -151,10 +151,6 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, unsigned int br_min, br_nom, br_max; __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, }; - phylink_set(modes, Autoneg); - phylink_set(modes, Pause); - phylink_set(modes, Asym_Pause); - /* Decode the bitrate information to MBd */ br_min = br_nom = br_max = 0; if (id->base.br_nominal) { @@ -339,6 +335,10 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, } } + phylink_set(modes, Autoneg); + phylink_set(modes, Pause); + phylink_set(modes, Asym_Pause); + if (bus->sfp_quirk && bus->sfp_quirk->modes) bus->sfp_quirk->modes(id, modes, interfaces); diff --git a/drivers/net/ppp/ppp_async.c b/drivers/net/ppp/ppp_async.c index 840da924708b..125793d8aefa 100644 --- a/drivers/net/ppp/ppp_async.c +++ b/drivers/net/ppp/ppp_async.c @@ -460,6 +460,10 @@ ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg) case PPPIOCSMRU: if (get_user(val, p)) break; + if (val > U16_MAX) { + err = -EINVAL; + break; + } if (val < PPP_MRU) val = PPP_MRU; ap->mru = val; diff --git a/drivers/net/slip/slhc.c b/drivers/net/slip/slhc.c index ba93bab948e0..18df7ca66198 100644 --- a/drivers/net/slip/slhc.c +++ b/drivers/net/slip/slhc.c @@ -752,4 +752,5 @@ EXPORT_SYMBOL(slhc_compress); EXPORT_SYMBOL(slhc_uncompress); EXPORT_SYMBOL(slhc_toss); +MODULE_DESCRIPTION("Compression helpers for SLIP (serial line)"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index e4280e37fec9..0aba3569ccc0 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -1437,5 +1437,6 @@ out: } #endif +MODULE_DESCRIPTION("SLIP (serial line) protocol module"); MODULE_LICENSE("GPL"); MODULE_ALIAS_LDISC(N_SLIP); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index afa5497f7c35..4a4f8c8e79fa 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1630,13 +1630,19 @@ static int tun_xdp_act(struct tun_struct *tun, struct bpf_prog *xdp_prog, switch (act) { case XDP_REDIRECT: err = xdp_do_redirect(tun->dev, xdp, xdp_prog); - if (err) + if (err) { + dev_core_stats_rx_dropped_inc(tun->dev); return err; + } + dev_sw_netstats_rx_add(tun->dev, xdp->data_end - xdp->data); break; case XDP_TX: err = tun_xdp_tx(tun->dev, xdp); - if (err < 0) + if (err < 0) { + dev_core_stats_rx_dropped_inc(tun->dev); return err; + } + dev_sw_netstats_rx_add(tun->dev, xdp->data_end - xdp->data); break; case XDP_PASS: break; diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 83b8452220ec..f088ea2ba6f3 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -363,7 +363,6 @@ static int disable_net; /* driver info */ static const char driver_name[] = "hso"; static const char tty_filename[] = "ttyHS"; -static const char *version = __FILE__ ": " MOD_AUTHOR; /* the usb driver itself (registered in hso_init) */ static struct usb_driver hso_driver; /* serial structures */ @@ -3228,16 +3227,8 @@ static struct usb_driver hso_driver = { static int __init hso_init(void) { - int i; int result; - /* put it in the log */ - pr_info("%s\n", version); - - /* Initialise the serial table semaphore and table */ - for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) - serial_table[i] = NULL; - /* allocate our driver using the proper amount of supported minors */ tty_drv = tty_alloc_driver(HSO_SERIAL_TTY_MINORS, TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV); @@ -3285,8 +3276,6 @@ err_free_tty: static void __exit hso_exit(void) { - pr_info("unloaded\n"); - tty_unregister_driver(tty_drv); /* deregister the usb driver */ usb_deregister(&hso_driver); diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 9bf2140fd0a1..0d0672d2a654 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -10069,7 +10069,7 @@ static struct usb_driver rtl8152_driver = { .disable_hub_initiated_lpm = 1, }; -static int rtl8152_cfgselector_probe(struct usb_device *udev) +static int rtl8152_cfgselector_choose_configuration(struct usb_device *udev) { struct usb_host_config *c; int i, num_configs; @@ -10096,19 +10096,13 @@ static int rtl8152_cfgselector_probe(struct usb_device *udev) if (i == num_configs) return -ENODEV; - if (usb_set_configuration(udev, c->desc.bConfigurationValue)) { - dev_err(&udev->dev, "Failed to set configuration %d\n", - c->desc.bConfigurationValue); - return -ENODEV; - } - - return 0; + return c->desc.bConfigurationValue; } static struct usb_device_driver rtl8152_cfgselector_driver = { - .name = MODULENAME "-cfgselector", - .probe = rtl8152_cfgselector_probe, - .id_table = rtl8152_table, + .name = MODULENAME "-cfgselector", + .choose_configuration = rtl8152_cfgselector_choose_configuration, + .id_table = rtl8152_table, .generic_subclass = 1, .supports_autosuspend = 1, }; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 3cb8aa193884..d7ce4a1011ea 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -4295,10 +4295,11 @@ static int virtnet_find_vqs(struct virtnet_info *vi) { vq_callback_t **callbacks; struct virtqueue **vqs; - int ret = -ENOMEM; - int i, total_vqs; const char **names; + int ret = -ENOMEM; + int total_vqs; bool *ctx; + u16 i; /* We expect 1 RX virtqueue followed by 1 TX virtqueue, followed by * possible N-1 RX/TX queue pairs used in multiqueue mode, followed by @@ -4335,8 +4336,8 @@ static int virtnet_find_vqs(struct virtnet_info *vi) for (i = 0; i < vi->max_queue_pairs; i++) { callbacks[rxq2vq(i)] = skb_recv_done; callbacks[txq2vq(i)] = skb_xmit_done; - sprintf(vi->rq[i].name, "input.%d", i); - sprintf(vi->sq[i].name, "output.%d", i); + sprintf(vi->rq[i].name, "input.%u", i); + sprintf(vi->sq[i].name, "output.%u", i); names[rxq2vq(i)] = vi->rq[i].name; names[txq2vq(i)] = vi->sq[i].name; if (ctx) diff --git a/drivers/net/wan/slic_ds26522.c b/drivers/net/wan/slic_ds26522.c index 8a51cfcff99e..cbb99fc5ea9f 100644 --- a/drivers/net/wan/slic_ds26522.c +++ b/drivers/net/wan/slic_ds26522.c @@ -28,6 +28,7 @@ static struct spi_device *g_spi; +MODULE_DESCRIPTION("Slic Maxim DS26522 driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Zhao Qiang<B45475@freescale.com>"); diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 43e0db78d42b..a742cec44e3d 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -1803,5 +1803,6 @@ static struct usb_driver ar5523_driver = { module_usb_driver(ar5523_driver); +MODULE_DESCRIPTION("Atheros AR5523 wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_FIRMWARE(AR5523_FIRMWARE_FILE); diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 7e3b6779f4e9..02e160d831be 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -368,10 +368,6 @@ struct ath11k_vif { struct ieee80211_chanctx_conf chanctx; struct ath11k_arp_ns_offload arp_ns_offload; struct ath11k_rekey_data rekey_data; - -#ifdef CONFIG_ATH11K_DEBUGFS - struct dentry *debugfs_twt; -#endif /* CONFIG_ATH11K_DEBUGFS */ }; struct ath11k_vif_iter { diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index a847bc0d50c0..a48e737ef35d 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -1894,35 +1894,30 @@ static const struct file_operations ath11k_fops_twt_resume_dialog = { .open = simple_open }; -void ath11k_debugfs_add_interface(struct ath11k_vif *arvif) +void ath11k_debugfs_op_vif_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ath11k_base *ab = arvif->ar->ab; + struct dentry *debugfs_twt; if (arvif->vif->type != NL80211_IFTYPE_AP && !(arvif->vif->type == NL80211_IFTYPE_STATION && test_bit(WMI_TLV_SERVICE_STA_TWT, ab->wmi_ab.svc_map))) return; - arvif->debugfs_twt = debugfs_create_dir("twt", - arvif->vif->debugfs_dir); - debugfs_create_file("add_dialog", 0200, arvif->debugfs_twt, + debugfs_twt = debugfs_create_dir("twt", + arvif->vif->debugfs_dir); + debugfs_create_file("add_dialog", 0200, debugfs_twt, arvif, &ath11k_fops_twt_add_dialog); - debugfs_create_file("del_dialog", 0200, arvif->debugfs_twt, + debugfs_create_file("del_dialog", 0200, debugfs_twt, arvif, &ath11k_fops_twt_del_dialog); - debugfs_create_file("pause_dialog", 0200, arvif->debugfs_twt, + debugfs_create_file("pause_dialog", 0200, debugfs_twt, arvif, &ath11k_fops_twt_pause_dialog); - debugfs_create_file("resume_dialog", 0200, arvif->debugfs_twt, + debugfs_create_file("resume_dialog", 0200, debugfs_twt, arvif, &ath11k_fops_twt_resume_dialog); } -void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif) -{ - if (!arvif->debugfs_twt) - return; - - debugfs_remove_recursive(arvif->debugfs_twt); - arvif->debugfs_twt = NULL; -} diff --git a/drivers/net/wireless/ath/ath11k/debugfs.h b/drivers/net/wireless/ath/ath11k/debugfs.h index 44d15845f39a..a39e458637b0 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.h +++ b/drivers/net/wireless/ath/ath11k/debugfs.h @@ -307,8 +307,8 @@ static inline int ath11k_debugfs_rx_filter(struct ath11k *ar) return ar->debug.rx_filter; } -void ath11k_debugfs_add_interface(struct ath11k_vif *arvif); -void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif); +void ath11k_debugfs_op_vif_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); void ath11k_debugfs_add_dbring_entry(struct ath11k *ar, enum wmi_direct_buffer_module id, enum ath11k_dbg_dbr_event event, @@ -387,14 +387,6 @@ static inline int ath11k_debugfs_get_fw_stats(struct ath11k *ar, return 0; } -static inline void ath11k_debugfs_add_interface(struct ath11k_vif *arvif) -{ -} - -static inline void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif) -{ -} - static inline void ath11k_debugfs_add_dbring_entry(struct ath11k *ar, enum wmi_direct_buffer_module id, diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index db241589424d..b13525bbbb80 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -6756,13 +6756,6 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, goto err; } - /* In the case of hardware recovery, debugfs files are - * not deleted since ieee80211_ops.remove_interface() is - * not invoked. In such cases, try to delete the files. - * These will be re-created later. - */ - ath11k_debugfs_remove_interface(arvif); - memset(arvif, 0, sizeof(*arvif)); arvif->ar = ar; @@ -6939,8 +6932,6 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, ath11k_dp_vdev_tx_attach(ar, arvif); - ath11k_debugfs_add_interface(arvif); - if (vif->type != NL80211_IFTYPE_MONITOR && test_bit(ATH11K_FLAG_MONITOR_CONF_ENABLED, &ar->monitor_flags)) { ret = ath11k_mac_monitor_vdev_create(ar); @@ -7056,8 +7047,6 @@ err_vdev_del: /* Recalc txpower for remaining vdev */ ath11k_mac_txpower_recalc(ar); - ath11k_debugfs_remove_interface(arvif); - /* TODO: recal traffic pause state based on the available vdevs */ mutex_unlock(&ar->conf_mutex); @@ -9153,6 +9142,7 @@ static const struct ieee80211_ops ath11k_ops = { #endif #ifdef CONFIG_ATH11K_DEBUGFS + .vif_add_debugfs = ath11k_debugfs_op_vif_add, .sta_add_debugfs = ath11k_debugfs_sta_op_add, #endif diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 41119fb177e3..4e6b4df8562f 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1685,6 +1685,7 @@ static struct platform_driver wcn36xx_driver = { module_platform_driver(wcn36xx_driver); +MODULE_DESCRIPTION("Qualcomm Atheros WCN3660/3680 wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com"); MODULE_FIRMWARE(WLAN_NV_FILE); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c index d55f3271d619..4f0c1e1a8e60 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/module.c @@ -20,6 +20,7 @@ static void __exit brcmf_bca_exit(void) brcmf_fwvid_unregister_vendor(BRCMF_FWVENDOR_BCA, THIS_MODULE); } +MODULE_DESCRIPTION("Broadcom FullMAC WLAN driver plugin for Broadcom AP chipsets"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(BRCMFMAC); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 133c5ea6429c..28d6a30cc010 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3779,8 +3779,10 @@ static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req, if (req->channels[i] == chan) break; } - if (i == req->n_channels) - req->channels[req->n_channels++] = chan; + if (i == req->n_channels) { + req->n_channels++; + req->channels[i] = chan; + } for (i = 0; i < req->n_ssids; i++) { if (req->ssids[i].ssid_len == ssid_len && diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c index f82fbbe3ecef..90d06cda03a2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/module.c @@ -20,6 +20,7 @@ static void __exit brcmf_cyw_exit(void) brcmf_fwvid_unregister_vendor(BRCMF_FWVENDOR_CYW, THIS_MODULE); } +MODULE_DESCRIPTION("Broadcom FullMAC WLAN driver plugin for Cypress/Infineon chipsets"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(BRCMFMAC); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 80220685f5e4..d7fb88bb6ae1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -2707,7 +2707,6 @@ MODULE_DEVICE_TABLE(pci, brcmf_pcie_devid_table); static struct pci_driver brcmf_pciedrvr = { - .node = {}, .name = KBUILD_MODNAME, .id_table = brcmf_pcie_devid_table, .probe = brcmf_pcie_probe, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 2178675ae1a4..0ccf735316c2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1581,7 +1581,7 @@ static int brcmf_usb_reset_device(struct device *dev, void *notused) void brcmf_usb_exit(void) { - struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver; + struct device_driver *drv = &brcmf_usbdrvr.driver; int ret; brcmf_dbg(USB, "Enter\n"); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c index 02918d434556..b66135e3cff4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/module.c @@ -20,6 +20,7 @@ static void __exit brcmf_wcc_exit(void) brcmf_fwvid_unregister_vendor(BRCMF_FWVENDOR_WCC, THIS_MODULE); } +MODULE_DESCRIPTION("Broadcom FullMAC WLAN driver plugin for Broadcom mobility chipsets"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(BRCMFMAC); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h index 798731ecbefd..b740c65a7dca 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h @@ -537,7 +537,7 @@ enum iwl_fw_dbg_config_cmd_type { }; /* LDBG_CFG_CMD_TYPE_API_E_VER_1 */ /* this token disables debug asserts in the firmware */ -#define IWL_FW_DBG_CONFIG_TOKEN 0x00011301 +#define IWL_FW_DBG_CONFIG_TOKEN 0x00010001 /** * struct iwl_fw_dbg_config_cmd - configure FW debug diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index e27774e7ed74..80fda056e46a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2023 Intel Corporation + * Copyright (C) 2005-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -19,7 +19,6 @@ * @fwrt_ptr: pointer to the buffer coming from fwrt * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the * transport's data. - * @trans_len: length of the valid data in trans_ptr * @fwrt_len: length of the valid data in fwrt_ptr */ struct iwl_fw_dump_ptrs { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index 3b14f6476743..72075720969c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #include <linux/firmware.h> #include "iwl-drv.h" @@ -1096,7 +1096,7 @@ static int iwl_dbg_tlv_override_trig_node(struct iwl_fw_runtime *fwrt, node_trig = (void *)node_tlv->data; } - memcpy(node_trig->data + offset, trig->data, trig_data_len); + memcpy((u8 *)node_trig->data + offset, trig->data, trig_data_len); node_tlv->length = cpu_to_le32(size); if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG) { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index ffe2670720c9..abf8001bdac1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -128,6 +128,7 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv) kfree(drv->fw.ucode_capa.cmd_versions); kfree(drv->fw.phy_integration_ver); kfree(drv->trans->dbg.pc_data); + drv->trans->dbg.pc_data = NULL; for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) iwl_free_fw_img(drv, drv->fw.img + i); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 402896988686..2f6774ec37b2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -668,7 +668,6 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { .has_eht = true, .eht_cap_elem = { .mac_cap_info[0] = - IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | IEEE80211_EHT_MAC_CAP0_OM_CONTROL | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2 | @@ -793,7 +792,6 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { .has_eht = true, .eht_cap_elem = { .mac_cap_info[0] = - IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | IEEE80211_EHT_MAC_CAP0_OM_CONTROL | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2, @@ -1020,8 +1018,7 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, if (CSR_HW_REV_TYPE(trans->hw_rev) == IWL_CFG_MAC_TYPE_GL && iftype_data->eht_cap.has_eht) { iftype_data->eht_cap.eht_cap_elem.mac_cap_info[0] &= - ~(IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | - IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | + ~(IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2); iftype_data->eht_cap.eht_cap_elem.phy_cap_info[3] &= ~(IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 7f13dff04b26..3447d67a8b31 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1600,7 +1600,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, */ if (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_ADHOC) { - iwl_mvm_vif_dbgfs_add_link(mvm, vif); + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + iwl_mvm_vif_dbgfs_add_link(mvm, vif); ret = 0; goto out; } @@ -1640,7 +1641,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, iwl_mvm_chandef_get_primary_80(&vif->bss_conf.chandef); } - iwl_mvm_vif_dbgfs_add_link(mvm, vif); + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + iwl_mvm_vif_dbgfs_add_link(mvm, vif); if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && vif->type == NL80211_IFTYPE_STATION && !vif->p2p && diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 61170173f917..893b69fc841b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -81,7 +81,8 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); } - iwl_mvm_vif_dbgfs_add_link(mvm, vif); + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + iwl_mvm_vif_dbgfs_add_link(mvm, vif); if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && vif->type == NL80211_IFTYPE_STATION && !vif->p2p && @@ -437,6 +438,9 @@ __iwl_mvm_mld_unassign_vif_chanctx(struct iwl_mvm *mvm, mvmvif->ap_ibss_active = false; } + iwl_mvm_link_changed(mvm, vif, link_conf, + LINK_CONTEXT_MODIFY_ACTIVE, false); + if (iwl_mvm_is_esr_supported(mvm->fwrt.trans) && n_active > 1) { int ret = iwl_mvm_esr_mode_inactive(mvm, vif); @@ -448,9 +452,6 @@ __iwl_mvm_mld_unassign_vif_chanctx(struct iwl_mvm *mvm, if (vif->type == NL80211_IFTYPE_MONITOR) iwl_mvm_mld_rm_snif_sta(mvm, vif); - iwl_mvm_link_changed(mvm, vif, link_conf, - LINK_CONTEXT_MODIFY_ACTIVE, false); - if (switching_chanctx) return; mvmvif->link[link_id]->phy_ctxt = NULL; diff --git a/drivers/net/wireless/intersil/p54/fwio.c b/drivers/net/wireless/intersil/p54/fwio.c index b52cce38115d..c4fe70e05b9b 100644 --- a/drivers/net/wireless/intersil/p54/fwio.c +++ b/drivers/net/wireless/intersil/p54/fwio.c @@ -125,7 +125,7 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) "FW rev %s - Softmac protocol %x.%x\n", fw_version, priv->fw_var >> 8, priv->fw_var & 0xff); snprintf(dev->wiphy->fw_version, sizeof(dev->wiphy->fw_version), - "%s - %x.%x", fw_version, + "%.19s - %x.%x", fw_version, priv->fw_var >> 8, priv->fw_var & 0xff); } diff --git a/drivers/net/wireless/intersil/p54/p54spi.c b/drivers/net/wireless/intersil/p54/p54spi.c index ce0179b8ab36..0073b5e0f9c9 100644 --- a/drivers/net/wireless/intersil/p54/p54spi.c +++ b/drivers/net/wireless/intersil/p54/p54spi.c @@ -700,6 +700,7 @@ static struct spi_driver p54spi_driver = { module_spi_driver(p54spi_driver); +MODULE_DESCRIPTION("Prism54 SPI wireless driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>"); MODULE_ALIAS("spi:cx3110x"); diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index d3ab9572e711..515e6db410f2 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -687,7 +687,7 @@ static struct usb_driver mwifiex_usb_driver = { .suspend = mwifiex_usb_suspend, .resume = mwifiex_usb_resume, .soft_unbind = 1, - .drvwrap.driver = { + .driver = { .coredump = mwifiex_usb_coredump, }, }; diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c index 89d738deea62..e2146d30e553 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c @@ -728,6 +728,7 @@ const struct ieee80211_ops mt7603_ops = { .set_sar_specs = mt7603_set_sar_specs, }; +MODULE_DESCRIPTION("MediaTek MT7603E and MT76x8 wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); static int __init mt7603_init(void) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index dab16b5fc386..0971c164b57e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -1375,4 +1375,5 @@ const struct ieee80211_ops mt7615_ops = { }; EXPORT_SYMBOL_GPL(mt7615_ops); +MODULE_DESCRIPTION("MediaTek MT7615E and MT7663E wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c index ac036a072439..87a956ea3ad7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c @@ -270,4 +270,5 @@ static void __exit mt7615_exit(void) module_init(mt7615_init); module_exit(mt7615_exit); +MODULE_DESCRIPTION("MediaTek MT7615E MMIO helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c index 67cedd2555f9..9692890ba51b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c @@ -253,4 +253,5 @@ module_sdio_driver(mt7663s_driver); MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); +MODULE_DESCRIPTION("MediaTek MT7663S (SDIO) wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c index 04963b9f7498..df737e1ff27b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c @@ -281,4 +281,5 @@ module_usb_driver(mt7663u_driver); MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); +MODULE_DESCRIPTION("MediaTek MT7663U (USB) wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c index 0052d103e276..820b39590027 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c @@ -349,4 +349,5 @@ EXPORT_SYMBOL_GPL(mt7663_usb_sdio_register_device); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); +MODULE_DESCRIPTION("MediaTek MT7663 SDIO/USB helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 96494ba2fdf7..3a20ba0d2492 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -3160,4 +3160,5 @@ exit: EXPORT_SYMBOL_GPL(mt76_connac2_mcu_fill_message); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); +MODULE_DESCRIPTION("MediaTek MT76x connac layer helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c index c3a392a1a659..bcd24c9072ec 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c @@ -342,4 +342,5 @@ int mt76x0_eeprom_init(struct mt76x02_dev *dev) return 0; } +MODULE_DESCRIPTION("MediaTek MT76x EEPROM helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c index 9277ff38b7a2..293e66fa83d5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c @@ -302,6 +302,7 @@ static const struct pci_device_id mt76x0e_device_table[] = { MODULE_DEVICE_TABLE(pci, mt76x0e_device_table); MODULE_FIRMWARE(MT7610E_FIRMWARE); MODULE_FIRMWARE(MT7650E_FIRMWARE); +MODULE_DESCRIPTION("MediaTek MT76x0E (PCIe) wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); static struct pci_driver mt76x0e_driver = { diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c index 0422c332354a..dd042949cf82 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c @@ -336,6 +336,7 @@ err: MODULE_DEVICE_TABLE(usb, mt76x0_device_table); MODULE_FIRMWARE(MT7610E_FIRMWARE); MODULE_FIRMWARE(MT7610U_FIRMWARE); +MODULE_DESCRIPTION("MediaTek MT76x0U (USB) wireless driver"); MODULE_LICENSE("GPL"); static struct usb_driver mt76x0_driver = { diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c index 02da543dfc5c..b2cc44914294 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c @@ -293,4 +293,5 @@ void mt76x02u_init_mcu(struct mt76_dev *dev) EXPORT_SYMBOL_GPL(mt76x02u_init_mcu); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>"); +MODULE_DESCRIPTION("MediaTek MT76x02 MCU helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c index 8a0e8124b894..8020446be37b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c @@ -696,4 +696,5 @@ void mt76x02_config_mac_addr_list(struct mt76x02_dev *dev) } EXPORT_SYMBOL_GPL(mt76x02_config_mac_addr_list); +MODULE_DESCRIPTION("MediaTek MT76x02 helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c index 8c01855885ce..1fe5f5a02f93 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c @@ -506,4 +506,5 @@ int mt76x2_eeprom_init(struct mt76x02_dev *dev) } EXPORT_SYMBOL_GPL(mt76x2_eeprom_init); +MODULE_DESCRIPTION("MediaTek MT76x2 EEPROM helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c index df85ebc6e1df..30959746e924 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c @@ -165,6 +165,7 @@ mt76x2e_resume(struct pci_dev *pdev) MODULE_DEVICE_TABLE(pci, mt76x2e_device_table); MODULE_FIRMWARE(MT7662_FIRMWARE); MODULE_FIRMWARE(MT7662_ROM_PATCH); +MODULE_DESCRIPTION("MediaTek MT76x2E (PCIe) wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); static struct pci_driver mt76pci_driver = { diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c index 55068f3252ef..ca78e14251c2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c @@ -147,4 +147,5 @@ static struct usb_driver mt76x2u_driver = { module_usb_driver(mt76x2u_driver); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>"); +MODULE_DESCRIPTION("MediaTek MT76x2U (USB) wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index aff4f21e843d..3039f53e2245 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -958,4 +958,5 @@ static void __exit mt7915_exit(void) module_init(mt7915_init); module_exit(mt7915_exit); +MODULE_DESCRIPTION("MediaTek MT7915E MMIO helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 0645417e0582..0d5adc5ddae3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -1418,5 +1418,6 @@ const struct ieee80211_ops mt7921_ops = { }; EXPORT_SYMBOL_GPL(mt7921_ops); +MODULE_DESCRIPTION("MediaTek MT7921 core driver"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 57903c6e4f11..dde26f327478 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -544,4 +544,5 @@ MODULE_FIRMWARE(MT7922_FIRMWARE_WM); MODULE_FIRMWARE(MT7922_ROM_PATCH); MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); +MODULE_DESCRIPTION("MediaTek MT7921E (PCIe) wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c index 7591e54d2897..a9ce1e746b95 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c @@ -323,5 +323,6 @@ static struct sdio_driver mt7921s_driver = { .drv.pm = pm_sleep_ptr(&mt7921s_pm_ops), }; module_sdio_driver(mt7921s_driver); +MODULE_DESCRIPTION("MediaTek MT7921S (SDIO) wireless driver"); MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c index e5258c74fc07..8b7c03c47598 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -336,5 +336,6 @@ static struct usb_driver mt7921u_driver = { }; module_usb_driver(mt7921u_driver); +MODULE_DESCRIPTION("MediaTek MT7921U (USB) wireless driver"); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 8f1075da4903..125a1be3cb64 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1450,4 +1450,5 @@ const struct ieee80211_ops mt7925_ops = { EXPORT_SYMBOL_GPL(mt7925_ops); MODULE_AUTHOR("Deren Wu <deren.wu@mediatek.com>"); +MODULE_DESCRIPTION("MediaTek MT7925 core driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 734f31ee40d3..1fd99a856541 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -583,4 +583,5 @@ MODULE_FIRMWARE(MT7925_FIRMWARE_WM); MODULE_FIRMWARE(MT7925_ROM_PATCH); MODULE_AUTHOR("Deren Wu <deren.wu@mediatek.com>"); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); +MODULE_DESCRIPTION("MediaTek MT7925E (PCIe) wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c index 9b885c5b3ed5..1e0f094fc905 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c @@ -329,4 +329,5 @@ static struct usb_driver mt7925u_driver = { module_usb_driver(mt7925u_driver); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); +MODULE_DESCRIPTION("MediaTek MT7925U (USB) wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 502be22dbe36..c42101aa9e45 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -862,5 +862,6 @@ int mt792x_load_firmware(struct mt792x_dev *dev) } EXPORT_SYMBOL_GPL(mt792x_load_firmware); +MODULE_DESCRIPTION("MediaTek MT792x core driver"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c index 2dd283caed36..589a3efb9f8c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c @@ -314,5 +314,6 @@ void mt792xu_disconnect(struct usb_interface *usb_intf) } EXPORT_SYMBOL_GPL(mt792xu_disconnect); +MODULE_DESCRIPTION("MediaTek MT792x USB helpers"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 3c729b563edc..699be57309c2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -4477,7 +4477,8 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy) skb_put_data(skb, &req, sizeof(req)); /* cck and ofdm */ - skb_put_data(skb, &la.cck, sizeof(la.cck) + sizeof(la.ofdm)); + skb_put_data(skb, &la.cck, sizeof(la.cck)); + skb_put_data(skb, &la.ofdm, sizeof(la.ofdm)); /* ht20 */ skb_put_data(skb, &la.mcs[0], 8); /* ht40 */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c index c50d89a445e9..9f2abfa273c9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -650,4 +650,5 @@ static void __exit mt7996_exit(void) module_init(mt7996_init); module_exit(mt7996_exit); +MODULE_DESCRIPTION("MediaTek MT7996 MMIO helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/sdio.c b/drivers/net/wireless/mediatek/mt76/sdio.c index c52d550f0c32..3e88798df017 100644 --- a/drivers/net/wireless/mediatek/mt76/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/sdio.c @@ -672,4 +672,5 @@ EXPORT_SYMBOL_GPL(mt76s_init); MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); +MODULE_DESCRIPTION("MediaTek MT76x SDIO helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 1584665fe3cb..5a0bcb5071bd 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -1128,4 +1128,5 @@ int mt76u_init(struct mt76_dev *dev, struct usb_interface *intf) EXPORT_SYMBOL_GPL(mt76u_init); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>"); +MODULE_DESCRIPTION("MediaTek MT76x USB helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/util.c b/drivers/net/wireless/mediatek/mt76/util.c index fc76c66ff1a5..d6c01a2dd198 100644 --- a/drivers/net/wireless/mediatek/mt76/util.c +++ b/drivers/net/wireless/mediatek/mt76/util.c @@ -138,4 +138,5 @@ int __mt76_worker_fn(void *ptr) } EXPORT_SYMBOL_GPL(__mt76_worker_fn); +MODULE_DESCRIPTION("MediaTek MT76x helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.c b/drivers/net/wireless/microchip/wilc1000/netdev.c index 91d71e0f7ef2..81e8f25863f5 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.c +++ b/drivers/net/wireless/microchip/wilc1000/netdev.c @@ -1018,5 +1018,6 @@ unregister_netdev: return ERR_PTR(ret); } +MODULE_DESCRIPTION("Atmel WILC1000 core wireless driver"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(WILC1000_FW(WILC1000_API_VER)); diff --git a/drivers/net/wireless/microchip/wilc1000/sdio.c b/drivers/net/wireless/microchip/wilc1000/sdio.c index 0d13e3e46e98..d6d394693090 100644 --- a/drivers/net/wireless/microchip/wilc1000/sdio.c +++ b/drivers/net/wireless/microchip/wilc1000/sdio.c @@ -984,4 +984,5 @@ static struct sdio_driver wilc_sdio_driver = { module_driver(wilc_sdio_driver, sdio_register_driver, sdio_unregister_driver); +MODULE_DESCRIPTION("Atmel WILC1000 SDIO wireless driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/microchip/wilc1000/spi.c b/drivers/net/wireless/microchip/wilc1000/spi.c index 77b4cdff73c3..1d8b241ce43c 100644 --- a/drivers/net/wireless/microchip/wilc1000/spi.c +++ b/drivers/net/wireless/microchip/wilc1000/spi.c @@ -273,6 +273,7 @@ static struct spi_driver wilc_spi_driver = { .remove = wilc_bus_remove, }; module_spi_driver(wilc_spi_driver); +MODULE_DESCRIPTION("Atmel WILC1000 SPI wireless driver"); MODULE_LICENSE("GPL"); static int wilc_spi_tx(struct wilc *wilc, u8 *b, u32 len) diff --git a/drivers/net/wireless/ti/wl1251/sdio.c b/drivers/net/wireless/ti/wl1251/sdio.c index 301bd0043a43..4e5b351f80f0 100644 --- a/drivers/net/wireless/ti/wl1251/sdio.c +++ b/drivers/net/wireless/ti/wl1251/sdio.c @@ -343,5 +343,6 @@ static void __exit wl1251_sdio_exit(void) module_init(wl1251_sdio_init); module_exit(wl1251_sdio_exit); +MODULE_DESCRIPTION("TI WL1251 SDIO helpers"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>"); diff --git a/drivers/net/wireless/ti/wl1251/spi.c b/drivers/net/wireless/ti/wl1251/spi.c index 29292f06bd3d..1936bb3af54a 100644 --- a/drivers/net/wireless/ti/wl1251/spi.c +++ b/drivers/net/wireless/ti/wl1251/spi.c @@ -342,6 +342,7 @@ static struct spi_driver wl1251_spi_driver = { module_spi_driver(wl1251_spi_driver); +MODULE_DESCRIPTION("TI WL1251 SPI helpers"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>"); MODULE_ALIAS("spi:wl1251"); diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index de045fe4ca1e..b26d42b4e3cc 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1955,6 +1955,7 @@ module_param_named(tcxo, tcxo_param, charp, 0); MODULE_PARM_DESC(tcxo, "TCXO clock: 19.2, 26, 38.4, 52, 16.368, 32.736, 16.8, 33.6"); +MODULE_DESCRIPTION("TI WL12xx wireless driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE); diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 20d9181b3410..2ccac1cdec01 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -2086,6 +2086,7 @@ module_param_named(num_rx_desc, num_rx_desc_param, int, 0400); MODULE_PARM_DESC(num_rx_desc_param, "Number of Rx descriptors: u8 (default is 32)"); +MODULE_DESCRIPTION("TI WiLink 8 wireless driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_FIRMWARE(WL18XX_FW_NAME); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index fb9ed97774c7..5736acb4d206 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -6793,6 +6793,7 @@ MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery"); module_param(no_recovery, int, 0600); MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck."); +MODULE_DESCRIPTION("TI WLAN core driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index f0686635db46..eb5482ed76ae 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -447,6 +447,7 @@ module_sdio_driver(wl1271_sdio_driver); module_param(dump, bool, 0600); MODULE_PARM_DESC(dump, "Enable sdio read/write dumps."); +MODULE_DESCRIPTION("TI WLAN SDIO helpers"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 7d9a139db59e..0aa2b2f3c5c9 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -562,6 +562,7 @@ static struct spi_driver wl1271_spi_driver = { }; module_spi_driver(wl1271_spi_driver); +MODULE_DESCRIPTION("TI WLAN SPI helpers"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 88f760a7cbc3..fab361a250d6 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -104,13 +104,12 @@ bool provides_xdp_headroom = true; module_param(provides_xdp_headroom, bool, 0644); static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, - u8 status); + s8 status); static void make_tx_response(struct xenvif_queue *queue, - struct xen_netif_tx_request *txp, + const struct xen_netif_tx_request *txp, unsigned int extra_count, - s8 st); -static void push_tx_responses(struct xenvif_queue *queue); + s8 status); static void xenvif_idx_unmap(struct xenvif_queue *queue, u16 pending_idx); @@ -208,13 +207,9 @@ static void xenvif_tx_err(struct xenvif_queue *queue, unsigned int extra_count, RING_IDX end) { RING_IDX cons = queue->tx.req_cons; - unsigned long flags; do { - spin_lock_irqsave(&queue->response_lock, flags); make_tx_response(queue, txp, extra_count, XEN_NETIF_RSP_ERROR); - push_tx_responses(queue); - spin_unlock_irqrestore(&queue->response_lock, flags); if (cons == end) break; RING_COPY_REQUEST(&queue->tx, cons++, txp); @@ -463,12 +458,20 @@ static void xenvif_get_requests(struct xenvif_queue *queue, } for (shinfo->nr_frags = 0; nr_slots > 0 && shinfo->nr_frags < MAX_SKB_FRAGS; - shinfo->nr_frags++, gop++, nr_slots--) { + nr_slots--) { + if (unlikely(!txp->size)) { + make_tx_response(queue, txp, 0, XEN_NETIF_RSP_OKAY); + ++txp; + continue; + } + index = pending_index(queue->pending_cons++); pending_idx = queue->pending_ring[index]; xenvif_tx_create_map_op(queue, pending_idx, txp, txp == first ? extra_count : 0, gop); frag_set_pending_idx(&frags[shinfo->nr_frags], pending_idx); + ++shinfo->nr_frags; + ++gop; if (txp == first) txp = txfrags; @@ -481,20 +484,33 @@ static void xenvif_get_requests(struct xenvif_queue *queue, shinfo = skb_shinfo(nskb); frags = shinfo->frags; - for (shinfo->nr_frags = 0; shinfo->nr_frags < nr_slots; - shinfo->nr_frags++, txp++, gop++) { + for (shinfo->nr_frags = 0; shinfo->nr_frags < nr_slots; ++txp) { + if (unlikely(!txp->size)) { + make_tx_response(queue, txp, 0, + XEN_NETIF_RSP_OKAY); + continue; + } + index = pending_index(queue->pending_cons++); pending_idx = queue->pending_ring[index]; xenvif_tx_create_map_op(queue, pending_idx, txp, 0, gop); frag_set_pending_idx(&frags[shinfo->nr_frags], pending_idx); + ++shinfo->nr_frags; + ++gop; + } + + if (shinfo->nr_frags) { + skb_shinfo(skb)->frag_list = nskb; + nskb = NULL; } + } - skb_shinfo(skb)->frag_list = nskb; - } else if (nskb) { + if (nskb) { /* A frag_list skb was allocated but it is no longer needed - * because enough slots were converted to copy ops above. + * because enough slots were converted to copy ops above or some + * were empty. */ kfree_skb(nskb); } @@ -963,7 +979,6 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue, (ret == 0) ? XEN_NETIF_RSP_OKAY : XEN_NETIF_RSP_ERROR); - push_tx_responses(queue); continue; } @@ -975,7 +990,6 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue, make_tx_response(queue, &txreq, extra_count, XEN_NETIF_RSP_OKAY); - push_tx_responses(queue); continue; } @@ -1401,8 +1415,35 @@ int xenvif_tx_action(struct xenvif_queue *queue, int budget) return work_done; } +static void _make_tx_response(struct xenvif_queue *queue, + const struct xen_netif_tx_request *txp, + unsigned int extra_count, + s8 status) +{ + RING_IDX i = queue->tx.rsp_prod_pvt; + struct xen_netif_tx_response *resp; + + resp = RING_GET_RESPONSE(&queue->tx, i); + resp->id = txp->id; + resp->status = status; + + while (extra_count-- != 0) + RING_GET_RESPONSE(&queue->tx, ++i)->status = XEN_NETIF_RSP_NULL; + + queue->tx.rsp_prod_pvt = ++i; +} + +static void push_tx_responses(struct xenvif_queue *queue) +{ + int notify; + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->tx, notify); + if (notify) + notify_remote_via_irq(queue->tx_irq); +} + static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, - u8 status) + s8 status) { struct pending_tx_info *pending_tx_info; pending_ring_idx_t index; @@ -1412,8 +1453,8 @@ static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, spin_lock_irqsave(&queue->response_lock, flags); - make_tx_response(queue, &pending_tx_info->req, - pending_tx_info->extra_count, status); + _make_tx_response(queue, &pending_tx_info->req, + pending_tx_info->extra_count, status); /* Release the pending index before pusing the Tx response so * its available before a new Tx request is pushed by the @@ -1427,32 +1468,19 @@ static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, spin_unlock_irqrestore(&queue->response_lock, flags); } - static void make_tx_response(struct xenvif_queue *queue, - struct xen_netif_tx_request *txp, + const struct xen_netif_tx_request *txp, unsigned int extra_count, - s8 st) + s8 status) { - RING_IDX i = queue->tx.rsp_prod_pvt; - struct xen_netif_tx_response *resp; - - resp = RING_GET_RESPONSE(&queue->tx, i); - resp->id = txp->id; - resp->status = st; - - while (extra_count-- != 0) - RING_GET_RESPONSE(&queue->tx, ++i)->status = XEN_NETIF_RSP_NULL; + unsigned long flags; - queue->tx.rsp_prod_pvt = ++i; -} + spin_lock_irqsave(&queue->response_lock, flags); -static void push_tx_responses(struct xenvif_queue *queue) -{ - int notify; + _make_tx_response(queue, txp, extra_count, status); + push_tx_responses(queue); - RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->tx, notify); - if (notify) - notify_remote_via_irq(queue->tx_irq); + spin_unlock_irqrestore(&queue->response_lock, flags); } static void xenvif_idx_unmap(struct xenvif_queue *queue, u16 pending_idx) diff --git a/drivers/nfc/pn533/uart.c b/drivers/nfc/pn533/uart.c index a556acdb947b..2eb5978bd79e 100644 --- a/drivers/nfc/pn533/uart.c +++ b/drivers/nfc/pn533/uart.c @@ -203,8 +203,8 @@ static int pn532_uart_rx_is_frame(struct sk_buff *skb) return 0; } -static int pn532_receive_buf(struct serdev_device *serdev, - const unsigned char *data, size_t count) +static ssize_t pn532_receive_buf(struct serdev_device *serdev, + const u8 *data, size_t count) { struct pn532_uart_phy *dev = serdev_device_get_drvdata(serdev); size_t i; diff --git a/drivers/nfc/s3fwrn5/uart.c b/drivers/nfc/s3fwrn5/uart.c index 82ea35d748a5..456d3947116c 100644 --- a/drivers/nfc/s3fwrn5/uart.c +++ b/drivers/nfc/s3fwrn5/uart.c @@ -51,9 +51,8 @@ static const struct s3fwrn5_phy_ops uart_phy_ops = { .write = s3fwrn82_uart_write, }; -static int s3fwrn82_uart_read(struct serdev_device *serdev, - const unsigned char *data, - size_t count) +static ssize_t s3fwrn82_uart_read(struct serdev_device *serdev, + const u8 *data, size_t count) { struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev); size_t i; diff --git a/drivers/nvdimm/virtio_pmem.c b/drivers/nvdimm/virtio_pmem.c index a92eb172f0e7..4ceced5cefcf 100644 --- a/drivers/nvdimm/virtio_pmem.c +++ b/drivers/nvdimm/virtio_pmem.c @@ -29,12 +29,27 @@ static int init_vq(struct virtio_pmem *vpmem) return 0; }; +static int virtio_pmem_validate(struct virtio_device *vdev) +{ + struct virtio_shm_region shm_reg; + + if (virtio_has_feature(vdev, VIRTIO_PMEM_F_SHMEM_REGION) && + !virtio_get_shm_region(vdev, &shm_reg, (u8)VIRTIO_PMEM_SHMEM_REGION_ID) + ) { + dev_notice(&vdev->dev, "failed to get shared memory region %d\n", + VIRTIO_PMEM_SHMEM_REGION_ID); + __virtio_clear_bit(vdev, VIRTIO_PMEM_F_SHMEM_REGION); + } + return 0; +} + static int virtio_pmem_probe(struct virtio_device *vdev) { struct nd_region_desc ndr_desc = {}; struct nd_region *nd_region; struct virtio_pmem *vpmem; struct resource res; + struct virtio_shm_region shm_reg; int err = 0; if (!vdev->config->get) { @@ -57,10 +72,16 @@ static int virtio_pmem_probe(struct virtio_device *vdev) goto out_err; } - virtio_cread_le(vpmem->vdev, struct virtio_pmem_config, - start, &vpmem->start); - virtio_cread_le(vpmem->vdev, struct virtio_pmem_config, - size, &vpmem->size); + if (virtio_has_feature(vdev, VIRTIO_PMEM_F_SHMEM_REGION)) { + virtio_get_shm_region(vdev, &shm_reg, (u8)VIRTIO_PMEM_SHMEM_REGION_ID); + vpmem->start = shm_reg.addr; + vpmem->size = shm_reg.len; + } else { + virtio_cread_le(vpmem->vdev, struct virtio_pmem_config, + start, &vpmem->start); + virtio_cread_le(vpmem->vdev, struct virtio_pmem_config, + size, &vpmem->size); + } res.start = vpmem->start; res.end = vpmem->start + vpmem->size - 1; @@ -122,10 +143,17 @@ static void virtio_pmem_remove(struct virtio_device *vdev) virtio_reset_device(vdev); } +static unsigned int features[] = { + VIRTIO_PMEM_F_SHMEM_REGION, +}; + static struct virtio_driver virtio_pmem_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, .driver.owner = THIS_MODULE, .id_table = id_table, + .validate = virtio_pmem_validate, .probe = virtio_pmem_probe, .remove = virtio_pmem_remove, }; diff --git a/drivers/nvme/common/auth.c b/drivers/nvme/common/auth.c index a23ab5c968b9..a3455f1d67fa 100644 --- a/drivers/nvme/common/auth.c +++ b/drivers/nvme/common/auth.c @@ -471,4 +471,5 @@ int nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key) } EXPORT_SYMBOL_GPL(nvme_auth_generate_key); +MODULE_DESCRIPTION("NVMe Authentication framework"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvme/common/keyring.c b/drivers/nvme/common/keyring.c index ee341b83eeba..6f7e7a8fa5ae 100644 --- a/drivers/nvme/common/keyring.c +++ b/drivers/nvme/common/keyring.c @@ -111,7 +111,7 @@ static struct key *nvme_tls_psk_lookup(struct key *keyring, * should be preferred to 'generated' PSKs, * and SHA-384 should be preferred to SHA-256. */ -struct nvme_tls_psk_priority_list { +static struct nvme_tls_psk_priority_list { bool generated; enum nvme_tcp_tls_cipher cipher; } nvme_tls_psk_prio[] = { @@ -181,5 +181,6 @@ static void __exit nvme_keyring_exit(void) MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Hannes Reinecke <hare@suse.de>"); +MODULE_DESCRIPTION("NVMe Keyring implementation"); module_init(nvme_keyring_init); module_exit(nvme_keyring_exit); diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index 596bb11eeba5..c727cd1f264b 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -797,6 +797,7 @@ static int apple_nvme_init_request(struct blk_mq_tag_set *set, static void apple_nvme_disable(struct apple_nvme *anv, bool shutdown) { + enum nvme_ctrl_state state = nvme_ctrl_state(&anv->ctrl); u32 csts = readl(anv->mmio_nvme + NVME_REG_CSTS); bool dead = false, freeze = false; unsigned long flags; @@ -808,8 +809,8 @@ static void apple_nvme_disable(struct apple_nvme *anv, bool shutdown) if (csts & NVME_CSTS_CFS) dead = true; - if (anv->ctrl.state == NVME_CTRL_LIVE || - anv->ctrl.state == NVME_CTRL_RESETTING) { + if (state == NVME_CTRL_LIVE || + state == NVME_CTRL_RESETTING) { freeze = true; nvme_start_freeze(&anv->ctrl); } @@ -881,7 +882,7 @@ static enum blk_eh_timer_return apple_nvme_timeout(struct request *req) unsigned long flags; u32 csts = readl(anv->mmio_nvme + NVME_REG_CSTS); - if (anv->ctrl.state != NVME_CTRL_LIVE) { + if (nvme_ctrl_state(&anv->ctrl) != NVME_CTRL_LIVE) { /* * From rdma.c: * If we are resetting, connecting or deleting we should @@ -985,10 +986,10 @@ static void apple_nvme_reset_work(struct work_struct *work) u32 boot_status, aqa; struct apple_nvme *anv = container_of(work, struct apple_nvme, ctrl.reset_work); + enum nvme_ctrl_state state = nvme_ctrl_state(&anv->ctrl); - if (anv->ctrl.state != NVME_CTRL_RESETTING) { - dev_warn(anv->dev, "ctrl state %d is not RESETTING\n", - anv->ctrl.state); + if (state != NVME_CTRL_RESETTING) { + dev_warn(anv->dev, "ctrl state %d is not RESETTING\n", state); ret = -ENODEV; goto out; } diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c index 72c0525c75f5..a264b3ae078b 100644 --- a/drivers/nvme/host/auth.c +++ b/drivers/nvme/host/auth.c @@ -48,11 +48,6 @@ struct nvme_dhchap_queue_context { static struct workqueue_struct *nvme_auth_wq; -#define nvme_auth_flags_from_qid(qid) \ - (qid == 0) ? 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED -#define nvme_auth_queue_from_qid(ctrl, qid) \ - (qid == 0) ? (ctrl)->fabrics_q : (ctrl)->connect_q - static inline int ctrl_max_dhchaps(struct nvme_ctrl *ctrl) { return ctrl->opts->nr_io_queues + ctrl->opts->nr_write_queues + @@ -63,10 +58,15 @@ static int nvme_auth_submit(struct nvme_ctrl *ctrl, int qid, void *data, size_t data_len, bool auth_send) { struct nvme_command cmd = {}; - blk_mq_req_flags_t flags = nvme_auth_flags_from_qid(qid); - struct request_queue *q = nvme_auth_queue_from_qid(ctrl, qid); + nvme_submit_flags_t flags = NVME_SUBMIT_RETRY; + struct request_queue *q = ctrl->fabrics_q; int ret; + if (qid != 0) { + flags |= NVME_SUBMIT_NOWAIT | NVME_SUBMIT_RESERVED; + q = ctrl->connect_q; + } + cmd.auth_common.opcode = nvme_fabrics_command; cmd.auth_common.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER; cmd.auth_common.spsp0 = 0x01; @@ -80,8 +80,7 @@ static int nvme_auth_submit(struct nvme_ctrl *ctrl, int qid, } ret = __nvme_submit_sync_cmd(q, &cmd, NULL, data, data_len, - qid == 0 ? NVME_QID_ANY : qid, - 0, flags); + qid == 0 ? NVME_QID_ANY : qid, flags); if (ret > 0) dev_warn(ctrl->device, "qid %d auth_send failed with status %d\n", qid, ret); @@ -897,7 +896,7 @@ static void nvme_ctrl_auth_work(struct work_struct *work) * If the ctrl is no connected, bail as reconnect will handle * authentication. */ - if (ctrl->state != NVME_CTRL_LIVE) + if (nvme_ctrl_state(ctrl) != NVME_CTRL_LIVE) return; /* Authenticate admin queue first */ diff --git a/drivers/nvme/host/constants.c b/drivers/nvme/host/constants.c index 20f46c230885..6f2ebb5fcdb0 100644 --- a/drivers/nvme/host/constants.c +++ b/drivers/nvme/host/constants.c @@ -171,15 +171,15 @@ static const char * const nvme_statuses[] = { [NVME_SC_HOST_ABORTED_CMD] = "Host Aborted Command", }; -const unsigned char *nvme_get_error_status_str(u16 status) +const char *nvme_get_error_status_str(u16 status) { status &= 0x7ff; if (status < ARRAY_SIZE(nvme_statuses) && nvme_statuses[status]) - return nvme_statuses[status & 0x7ff]; + return nvme_statuses[status]; return "Unknown"; } -const unsigned char *nvme_get_opcode_str(u8 opcode) +const char *nvme_get_opcode_str(u8 opcode) { if (opcode < ARRAY_SIZE(nvme_ops) && nvme_ops[opcode]) return nvme_ops[opcode]; @@ -187,7 +187,7 @@ const unsigned char *nvme_get_opcode_str(u8 opcode) } EXPORT_SYMBOL_GPL(nvme_get_opcode_str); -const unsigned char *nvme_get_admin_opcode_str(u8 opcode) +const char *nvme_get_admin_opcode_str(u8 opcode) { if (opcode < ARRAY_SIZE(nvme_admin_ops) && nvme_admin_ops[opcode]) return nvme_admin_ops[opcode]; @@ -195,7 +195,7 @@ const unsigned char *nvme_get_admin_opcode_str(u8 opcode) } EXPORT_SYMBOL_GPL(nvme_get_admin_opcode_str); -const unsigned char *nvme_get_fabrics_opcode_str(u8 opcode) { +const char *nvme_get_fabrics_opcode_str(u8 opcode) { if (opcode < ARRAY_SIZE(nvme_fabrics_ops) && nvme_fabrics_ops[opcode]) return nvme_fabrics_ops[opcode]; return "Unknown"; diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 0af612387083..60537c9224bf 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -338,6 +338,30 @@ static void nvme_log_error(struct request *req) nr->status & NVME_SC_DNR ? "DNR " : ""); } +static void nvme_log_err_passthru(struct request *req) +{ + struct nvme_ns *ns = req->q->queuedata; + struct nvme_request *nr = nvme_req(req); + + pr_err_ratelimited("%s: %s(0x%x), %s (sct 0x%x / sc 0x%x) %s%s" + "cdw10=0x%x cdw11=0x%x cdw12=0x%x cdw13=0x%x cdw14=0x%x cdw15=0x%x\n", + ns ? ns->disk->disk_name : dev_name(nr->ctrl->device), + ns ? nvme_get_opcode_str(nr->cmd->common.opcode) : + nvme_get_admin_opcode_str(nr->cmd->common.opcode), + nr->cmd->common.opcode, + nvme_get_error_status_str(nr->status), + nr->status >> 8 & 7, /* Status Code Type */ + nr->status & 0xff, /* Status Code */ + nr->status & NVME_SC_MORE ? "MORE " : "", + nr->status & NVME_SC_DNR ? "DNR " : "", + nr->cmd->common.cdw10, + nr->cmd->common.cdw11, + nr->cmd->common.cdw12, + nr->cmd->common.cdw13, + nr->cmd->common.cdw14, + nr->cmd->common.cdw14); +} + enum nvme_disposition { COMPLETE, RETRY, @@ -385,8 +409,12 @@ static inline void nvme_end_req(struct request *req) { blk_status_t status = nvme_error_status(nvme_req(req)->status); - if (unlikely(nvme_req(req)->status && !(req->rq_flags & RQF_QUIET))) - nvme_log_error(req); + if (unlikely(nvme_req(req)->status && !(req->rq_flags & RQF_QUIET))) { + if (blk_rq_is_passthrough(req)) + nvme_log_err_passthru(req); + else + nvme_log_error(req); + } nvme_end_req_zoned(req); nvme_trace_bio_complete(req); if (req->cmd_flags & REQ_NVME_MPATH) @@ -679,10 +707,21 @@ static inline void nvme_clear_nvme_request(struct request *req) /* initialize a passthrough request */ void nvme_init_request(struct request *req, struct nvme_command *cmd) { - if (req->q->queuedata) + struct nvme_request *nr = nvme_req(req); + bool logging_enabled; + + if (req->q->queuedata) { + struct nvme_ns *ns = req->q->disk->private_data; + + logging_enabled = ns->head->passthru_err_log_enabled; req->timeout = NVME_IO_TIMEOUT; - else /* no queuedata implies admin queue */ + } else { /* no queuedata implies admin queue */ + logging_enabled = nr->ctrl->passthru_err_log_enabled; req->timeout = NVME_ADMIN_TIMEOUT; + } + + if (!logging_enabled) + req->rq_flags |= RQF_QUIET; /* passthru commands should let the driver set the SGL flags */ cmd->common.flags &= ~NVME_CMD_SGL_ALL; @@ -691,8 +730,7 @@ void nvme_init_request(struct request *req, struct nvme_command *cmd) if (req->mq_hctx->type == HCTX_TYPE_POLL) req->cmd_flags |= REQ_POLLED; nvme_clear_nvme_request(req); - req->rq_flags |= RQF_QUIET; - memcpy(nvme_req(req)->cmd, cmd, sizeof(*cmd)); + memcpy(nr->cmd, cmd, sizeof(*cmd)); } EXPORT_SYMBOL_GPL(nvme_init_request); @@ -721,7 +759,7 @@ blk_status_t nvme_fail_nonready_command(struct nvme_ctrl *ctrl, EXPORT_SYMBOL_GPL(nvme_fail_nonready_command); bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq, - bool queue_live) + bool queue_live, enum nvme_ctrl_state state) { struct nvme_request *req = nvme_req(rq); @@ -742,7 +780,7 @@ bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq, * command, which is require to set the queue live in the * appropinquate states. */ - switch (nvme_ctrl_state(ctrl)) { + switch (state) { case NVME_CTRL_CONNECTING: if (blk_rq_is_passthrough(rq) && nvme_is_fabrics(req->cmd) && (req->cmd->fabrics.fctype == nvme_fabrics_type_connect || @@ -1051,20 +1089,27 @@ EXPORT_SYMBOL_NS_GPL(nvme_execute_rq, NVME_TARGET_PASSTHRU); */ int __nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, union nvme_result *result, void *buffer, unsigned bufflen, - int qid, int at_head, blk_mq_req_flags_t flags) + int qid, nvme_submit_flags_t flags) { struct request *req; int ret; + blk_mq_req_flags_t blk_flags = 0; + if (flags & NVME_SUBMIT_NOWAIT) + blk_flags |= BLK_MQ_REQ_NOWAIT; + if (flags & NVME_SUBMIT_RESERVED) + blk_flags |= BLK_MQ_REQ_RESERVED; if (qid == NVME_QID_ANY) - req = blk_mq_alloc_request(q, nvme_req_op(cmd), flags); + req = blk_mq_alloc_request(q, nvme_req_op(cmd), blk_flags); else - req = blk_mq_alloc_request_hctx(q, nvme_req_op(cmd), flags, + req = blk_mq_alloc_request_hctx(q, nvme_req_op(cmd), blk_flags, qid - 1); if (IS_ERR(req)) return PTR_ERR(req); nvme_init_request(req, cmd); + if (flags & NVME_SUBMIT_RETRY) + req->cmd_flags &= ~REQ_FAILFAST_DRIVER; if (buffer && bufflen) { ret = blk_rq_map_kern(q, req, buffer, bufflen, GFP_KERNEL); @@ -1072,7 +1117,7 @@ int __nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, goto out; } - ret = nvme_execute_rq(req, at_head); + ret = nvme_execute_rq(req, flags & NVME_SUBMIT_AT_HEAD); if (result && ret >= 0) *result = nvme_req(req)->result; out: @@ -1085,7 +1130,7 @@ int nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, void *buffer, unsigned bufflen) { return __nvme_submit_sync_cmd(q, cmd, NULL, buffer, bufflen, - NVME_QID_ANY, 0, 0); + NVME_QID_ANY, 0); } EXPORT_SYMBOL_GPL(nvme_submit_sync_cmd); @@ -1560,7 +1605,7 @@ static int nvme_features(struct nvme_ctrl *dev, u8 op, unsigned int fid, c.features.dword11 = cpu_to_le32(dword11); ret = __nvme_submit_sync_cmd(dev->admin_q, &c, &res, - buffer, buflen, NVME_QID_ANY, 0, 0); + buffer, buflen, NVME_QID_ANY, 0); if (ret >= 0 && result) *result = le32_to_cpu(res.u32); return ret; @@ -1740,13 +1785,13 @@ static void nvme_config_discard(struct nvme_ctrl *ctrl, struct gendisk *disk, struct nvme_ns_head *head) { struct request_queue *queue = disk->queue; - u32 size = queue_logical_block_size(queue); + u32 max_discard_sectors; - if (ctrl->dmrsl && ctrl->dmrsl <= nvme_sect_to_lba(head, UINT_MAX)) - ctrl->max_discard_sectors = - nvme_lba_to_sect(head, ctrl->dmrsl); - - if (ctrl->max_discard_sectors == 0) { + if (ctrl->dmrsl && ctrl->dmrsl <= nvme_sect_to_lba(head, UINT_MAX)) { + max_discard_sectors = nvme_lba_to_sect(head, ctrl->dmrsl); + } else if (ctrl->oncs & NVME_CTRL_ONCS_DSM) { + max_discard_sectors = UINT_MAX; + } else { blk_queue_max_discard_sectors(queue, 0); return; } @@ -1754,14 +1799,22 @@ static void nvme_config_discard(struct nvme_ctrl *ctrl, struct gendisk *disk, BUILD_BUG_ON(PAGE_SIZE / sizeof(struct nvme_dsm_range) < NVME_DSM_MAX_RANGES); - queue->limits.discard_granularity = size; - - /* If discard is already enabled, don't reset queue limits */ + /* + * If discard is already enabled, don't reset queue limits. + * + * This works around the fact that the block layer can't cope well with + * updating the hardware limits when overridden through sysfs. This is + * harmless because discard limits in NVMe are purely advisory. + */ if (queue->limits.max_discard_sectors) return; - blk_queue_max_discard_sectors(queue, ctrl->max_discard_sectors); - blk_queue_max_discard_segments(queue, ctrl->max_discard_segments); + blk_queue_max_discard_sectors(queue, max_discard_sectors); + if (ctrl->dmrl) + blk_queue_max_discard_segments(queue, ctrl->dmrl); + else + blk_queue_max_discard_segments(queue, NVME_DSM_MAX_RANGES); + queue->limits.discard_granularity = queue_logical_block_size(queue); if (ctrl->quirks & NVME_QUIRK_DEALLOCATE_ZEROES) blk_queue_max_write_zeroes_sectors(queue, UINT_MAX); @@ -2164,7 +2217,7 @@ static int nvme_sec_submit(void *data, u16 spsp, u8 secp, void *buffer, size_t l cmd.common.cdw11 = cpu_to_le32(len); return __nvme_submit_sync_cmd(ctrl->admin_q, &cmd, NULL, buffer, len, - NVME_QID_ANY, 1, 0); + NVME_QID_ANY, NVME_SUBMIT_AT_HEAD); } static void nvme_configure_opal(struct nvme_ctrl *ctrl, bool was_suspended) @@ -2930,14 +2983,6 @@ static int nvme_init_non_mdts_limits(struct nvme_ctrl *ctrl) struct nvme_id_ctrl_nvm *id; int ret; - if (ctrl->oncs & NVME_CTRL_ONCS_DSM) { - ctrl->max_discard_sectors = UINT_MAX; - ctrl->max_discard_segments = NVME_DSM_MAX_RANGES; - } else { - ctrl->max_discard_sectors = 0; - ctrl->max_discard_segments = 0; - } - /* * Even though NVMe spec explicitly states that MDTS is not applicable * to the write-zeroes, we are cautious and limit the size to the @@ -2967,8 +3012,7 @@ static int nvme_init_non_mdts_limits(struct nvme_ctrl *ctrl) if (ret) goto free_data; - if (id->dmrl) - ctrl->max_discard_segments = id->dmrl; + ctrl->dmrl = id->dmrl; ctrl->dmrsl = le32_to_cpu(id->dmrsl); if (id->wzsl) ctrl->max_zeroes_sectors = nvme_mps_to_sectors(ctrl, id->wzsl); @@ -3715,6 +3759,13 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, struct nvme_ns_info *info) nvme_mpath_add_disk(ns, info->anagrpid); nvme_fault_inject_init(&ns->fault_inject, ns->disk->disk_name); + /* + * Set ns->disk->device->driver_data to ns so we can access + * ns->head->passthru_err_log_enabled in + * nvme_io_passthru_err_log_enabled_[store | show](). + */ + dev_set_drvdata(disk_to_dev(ns->disk), ns); + return; out_cleanup_ns_from_list: @@ -4139,6 +4190,7 @@ static bool nvme_ctrl_pp_status(struct nvme_ctrl *ctrl) static void nvme_get_fw_slot_info(struct nvme_ctrl *ctrl) { struct nvme_fw_slot_info_log *log; + u8 next_fw_slot, cur_fw_slot; log = kmalloc(sizeof(*log), GFP_KERNEL); if (!log) @@ -4150,13 +4202,15 @@ static void nvme_get_fw_slot_info(struct nvme_ctrl *ctrl) goto out_free_log; } - if (log->afi & 0x70 || !(log->afi & 0x7)) { + cur_fw_slot = log->afi & 0x7; + next_fw_slot = (log->afi & 0x70) >> 4; + if (!cur_fw_slot || (next_fw_slot && (cur_fw_slot != next_fw_slot))) { dev_info(ctrl->device, "Firmware is activated after next Controller Level Reset\n"); goto out_free_log; } - memcpy(ctrl->subsys->firmware_rev, &log->frs[(log->afi & 0x7) - 1], + memcpy(ctrl->subsys->firmware_rev, &log->frs[cur_fw_slot - 1], sizeof(ctrl->subsys->firmware_rev)); out_free_log: @@ -4515,6 +4569,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, int ret; WRITE_ONCE(ctrl->state, NVME_CTRL_NEW); + ctrl->passthru_err_log_enabled = false; clear_bit(NVME_CTRL_FAILFAST_EXPIRED, &ctrl->flags); spin_lock_init(&ctrl->lock); mutex_init(&ctrl->scan_lock); @@ -4852,5 +4907,6 @@ static void __exit nvme_core_exit(void) MODULE_LICENSE("GPL"); MODULE_VERSION("1.0"); +MODULE_DESCRIPTION("NVMe host core framework"); module_init(nvme_core_init); module_exit(nvme_core_exit); diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c index b5752a77ad98..3499acbf6a82 100644 --- a/drivers/nvme/host/fabrics.c +++ b/drivers/nvme/host/fabrics.c @@ -180,7 +180,7 @@ int nvmf_reg_read32(struct nvme_ctrl *ctrl, u32 off, u32 *val) cmd.prop_get.offset = cpu_to_le32(off); ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &res, NULL, 0, - NVME_QID_ANY, 0, 0); + NVME_QID_ANY, 0); if (ret >= 0) *val = le64_to_cpu(res.u64); @@ -226,7 +226,7 @@ int nvmf_reg_read64(struct nvme_ctrl *ctrl, u32 off, u64 *val) cmd.prop_get.offset = cpu_to_le32(off); ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &res, NULL, 0, - NVME_QID_ANY, 0, 0); + NVME_QID_ANY, 0); if (ret >= 0) *val = le64_to_cpu(res.u64); @@ -271,7 +271,7 @@ int nvmf_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val) cmd.prop_set.value = cpu_to_le64(val); ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, NULL, NULL, 0, - NVME_QID_ANY, 0, 0); + NVME_QID_ANY, 0); if (unlikely(ret)) dev_err(ctrl->device, "Property Set error: %d, offset %#x\n", @@ -450,8 +450,10 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) return -ENOMEM; ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &res, - data, sizeof(*data), NVME_QID_ANY, 1, - BLK_MQ_REQ_RESERVED | BLK_MQ_REQ_NOWAIT); + data, sizeof(*data), NVME_QID_ANY, + NVME_SUBMIT_AT_HEAD | + NVME_SUBMIT_NOWAIT | + NVME_SUBMIT_RESERVED); if (ret) { nvmf_log_connect_error(ctrl, ret, le32_to_cpu(res.u32), &cmd, data); @@ -525,8 +527,10 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid) return -ENOMEM; ret = __nvme_submit_sync_cmd(ctrl->connect_q, &cmd, &res, - data, sizeof(*data), qid, 1, - BLK_MQ_REQ_RESERVED | BLK_MQ_REQ_NOWAIT); + data, sizeof(*data), qid, + NVME_SUBMIT_AT_HEAD | + NVME_SUBMIT_RESERVED | + NVME_SUBMIT_NOWAIT); if (ret) { nvmf_log_connect_error(ctrl, ret, le32_to_cpu(res.u32), &cmd, data); @@ -1488,6 +1492,7 @@ static void __exit nvmf_exit(void) } MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("NVMe host fabrics library"); module_init(nvmf_init); module_exit(nvmf_exit); diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h index fbaee5a7be19..06cc54851b1b 100644 --- a/drivers/nvme/host/fabrics.h +++ b/drivers/nvme/host/fabrics.h @@ -185,9 +185,11 @@ static inline bool nvmf_ctlr_matches_baseopts(struct nvme_ctrl *ctrl, struct nvmf_ctrl_options *opts) { - if (ctrl->state == NVME_CTRL_DELETING || - ctrl->state == NVME_CTRL_DELETING_NOIO || - ctrl->state == NVME_CTRL_DEAD || + enum nvme_ctrl_state state = nvme_ctrl_state(ctrl); + + if (state == NVME_CTRL_DELETING || + state == NVME_CTRL_DELETING_NOIO || + state == NVME_CTRL_DEAD || strcmp(opts->subsysnqn, ctrl->opts->subsysnqn) || strcmp(opts->host->nqn, ctrl->opts->host->nqn) || !uuid_equal(&opts->host->id, &ctrl->opts->host->id)) diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 16847a316421..68a5d971657b 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -221,11 +221,6 @@ static LIST_HEAD(nvme_fc_lport_list); static DEFINE_IDA(nvme_fc_local_port_cnt); static DEFINE_IDA(nvme_fc_ctrl_cnt); -static struct workqueue_struct *nvme_fc_wq; - -static bool nvme_fc_waiting_to_unload; -static DECLARE_COMPLETION(nvme_fc_unload_proceed); - /* * These items are short-term. They will eventually be moved into * a generic FC class. See comments in module init. @@ -255,8 +250,6 @@ nvme_fc_free_lport(struct kref *ref) /* remove from transport list */ spin_lock_irqsave(&nvme_fc_lock, flags); list_del(&lport->port_list); - if (nvme_fc_waiting_to_unload && list_empty(&nvme_fc_lport_list)) - complete(&nvme_fc_unload_proceed); spin_unlock_irqrestore(&nvme_fc_lock, flags); ida_free(&nvme_fc_local_port_cnt, lport->localport.port_num); @@ -2574,6 +2567,7 @@ static enum blk_eh_timer_return nvme_fc_timeout(struct request *rq) { struct nvme_fc_fcp_op *op = blk_mq_rq_to_pdu(rq); struct nvme_fc_ctrl *ctrl = op->ctrl; + u16 qnum = op->queue->qnum; struct nvme_fc_cmd_iu *cmdiu = &op->cmd_iu; struct nvme_command *sqe = &cmdiu->sqe; @@ -2582,10 +2576,11 @@ static enum blk_eh_timer_return nvme_fc_timeout(struct request *rq) * will detect the aborted io and will fail the connection. */ dev_info(ctrl->ctrl.device, - "NVME-FC{%d.%d}: io timeout: opcode %d fctype %d w10/11: " + "NVME-FC{%d.%d}: io timeout: opcode %d fctype %d (%s) w10/11: " "x%08x/x%08x\n", - ctrl->cnum, op->queue->qnum, sqe->common.opcode, - sqe->connect.fctype, sqe->common.cdw10, sqe->common.cdw11); + ctrl->cnum, qnum, sqe->common.opcode, sqe->fabrics.fctype, + nvme_fabrics_opcode_str(qnum, sqe), + sqe->common.cdw10, sqe->common.cdw11); if (__nvme_fc_abort_op(ctrl, op)) nvme_fc_error_recovery(ctrl, "io timeout abort failed"); @@ -3575,8 +3570,8 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, flush_delayed_work(&ctrl->connect_work); dev_info(ctrl->ctrl.device, - "NVME-FC{%d}: new ctrl: NQN \"%s\"\n", - ctrl->cnum, nvmf_ctrl_subsysnqn(&ctrl->ctrl)); + "NVME-FC{%d}: new ctrl: NQN \"%s\", hostnqn: %s\n", + ctrl->cnum, nvmf_ctrl_subsysnqn(&ctrl->ctrl), opts->host->nqn); return &ctrl->ctrl; @@ -3894,10 +3889,6 @@ static int __init nvme_fc_init_module(void) { int ret; - nvme_fc_wq = alloc_workqueue("nvme_fc_wq", WQ_MEM_RECLAIM, 0); - if (!nvme_fc_wq) - return -ENOMEM; - /* * NOTE: * It is expected that in the future the kernel will combine @@ -3915,7 +3906,7 @@ static int __init nvme_fc_init_module(void) ret = class_register(&fc_class); if (ret) { pr_err("couldn't register class fc\n"); - goto out_destroy_wq; + return ret; } /* @@ -3939,8 +3930,6 @@ out_destroy_device: device_destroy(&fc_class, MKDEV(0, 0)); out_destroy_class: class_unregister(&fc_class); -out_destroy_wq: - destroy_workqueue(nvme_fc_wq); return ret; } @@ -3960,48 +3949,27 @@ nvme_fc_delete_controllers(struct nvme_fc_rport *rport) spin_unlock(&rport->lock); } -static void -nvme_fc_cleanup_for_unload(void) +static void __exit nvme_fc_exit_module(void) { struct nvme_fc_lport *lport; struct nvme_fc_rport *rport; - - list_for_each_entry(lport, &nvme_fc_lport_list, port_list) { - list_for_each_entry(rport, &lport->endp_list, endp_list) { - nvme_fc_delete_controllers(rport); - } - } -} - -static void __exit nvme_fc_exit_module(void) -{ unsigned long flags; - bool need_cleanup = false; spin_lock_irqsave(&nvme_fc_lock, flags); - nvme_fc_waiting_to_unload = true; - if (!list_empty(&nvme_fc_lport_list)) { - need_cleanup = true; - nvme_fc_cleanup_for_unload(); - } + list_for_each_entry(lport, &nvme_fc_lport_list, port_list) + list_for_each_entry(rport, &lport->endp_list, endp_list) + nvme_fc_delete_controllers(rport); spin_unlock_irqrestore(&nvme_fc_lock, flags); - if (need_cleanup) { - pr_info("%s: waiting for ctlr deletes\n", __func__); - wait_for_completion(&nvme_fc_unload_proceed); - pr_info("%s: ctrl deletes complete\n", __func__); - } + flush_workqueue(nvme_delete_wq); nvmf_unregister_transport(&nvme_fc_transport); - ida_destroy(&nvme_fc_local_port_cnt); - ida_destroy(&nvme_fc_ctrl_cnt); - device_destroy(&fc_class, MKDEV(0, 0)); class_unregister(&fc_class); - destroy_workqueue(nvme_fc_wq); } module_init(nvme_fc_init_module); module_exit(nvme_fc_exit_module); +MODULE_DESCRIPTION("NVMe host FC transport driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c index 18f5c1be5d67..3dfd5ae99ae0 100644 --- a/drivers/nvme/host/ioctl.c +++ b/drivers/nvme/host/ioctl.c @@ -228,7 +228,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) length = (io.nblocks + 1) << ns->head->lba_shift; if ((io.control & NVME_RW_PRINFO_PRACT) && - ns->head->ms == sizeof(struct t10_pi_tuple)) { + (ns->head->ms == ns->head->pi_size)) { /* * Protection information is stripped/inserted by the * controller. diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index 2dd4137a08b2..74de1e64aeea 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -156,7 +156,7 @@ void nvme_kick_requeue_lists(struct nvme_ctrl *ctrl) if (!ns->head->disk) continue; kblockd_schedule_work(&ns->head->requeue_work); - if (ctrl->state == NVME_CTRL_LIVE) + if (nvme_ctrl_state(ns->ctrl) == NVME_CTRL_LIVE) disk_uevent(ns->head->disk, KOBJ_CHANGE); } up_read(&ctrl->namespaces_rwsem); @@ -223,13 +223,14 @@ void nvme_mpath_revalidate_paths(struct nvme_ns *ns) static bool nvme_path_is_disabled(struct nvme_ns *ns) { + enum nvme_ctrl_state state = nvme_ctrl_state(ns->ctrl); + /* * We don't treat NVME_CTRL_DELETING as a disabled path as I/O should * still be able to complete assuming that the controller is connected. * Otherwise it will fail immediately and return to the requeue list. */ - if (ns->ctrl->state != NVME_CTRL_LIVE && - ns->ctrl->state != NVME_CTRL_DELETING) + if (state != NVME_CTRL_LIVE && state != NVME_CTRL_DELETING) return true; if (test_bit(NVME_NS_ANA_PENDING, &ns->flags) || !test_bit(NVME_NS_READY, &ns->flags)) @@ -331,7 +332,7 @@ out: static inline bool nvme_path_is_optimized(struct nvme_ns *ns) { - return ns->ctrl->state == NVME_CTRL_LIVE && + return nvme_ctrl_state(ns->ctrl) == NVME_CTRL_LIVE && ns->ana_state == NVME_ANA_OPTIMIZED; } @@ -358,7 +359,7 @@ static bool nvme_available_path(struct nvme_ns_head *head) list_for_each_entry_rcu(ns, &head->list, siblings) { if (test_bit(NVME_CTRL_FAILFAST_EXPIRED, &ns->ctrl->flags)) continue; - switch (ns->ctrl->state) { + switch (nvme_ctrl_state(ns->ctrl)) { case NVME_CTRL_LIVE: case NVME_CTRL_RESETTING: case NVME_CTRL_CONNECTING: @@ -667,7 +668,7 @@ static void nvme_update_ns_ana_state(struct nvme_ana_group_desc *desc, * controller is ready. */ if (nvme_state_is_live(ns->ana_state) && - ns->ctrl->state == NVME_CTRL_LIVE) + nvme_ctrl_state(ns->ctrl) == NVME_CTRL_LIVE) nvme_mpath_set_live(ns); } @@ -748,7 +749,7 @@ static void nvme_ana_work(struct work_struct *work) { struct nvme_ctrl *ctrl = container_of(work, struct nvme_ctrl, ana_work); - if (ctrl->state != NVME_CTRL_LIVE) + if (nvme_ctrl_state(ctrl) != NVME_CTRL_LIVE) return; nvme_read_ana_log(ctrl); diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 4be7f6822966..7b87763e2f8a 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -263,6 +263,7 @@ enum nvme_ctrl_flags { struct nvme_ctrl { bool comp_seen; bool identified; + bool passthru_err_log_enabled; enum nvme_ctrl_state state; spinlock_t lock; struct mutex scan_lock; @@ -303,14 +304,13 @@ struct nvme_ctrl { u32 max_hw_sectors; u32 max_segments; u32 max_integrity_segments; - u32 max_discard_sectors; - u32 max_discard_segments; u32 max_zeroes_sectors; #ifdef CONFIG_BLK_DEV_ZONED u32 max_zone_append; #endif u16 crdt[3]; u16 oncs; + u8 dmrl; u32 dmrsl; u16 oacs; u16 sqsize; @@ -455,6 +455,7 @@ struct nvme_ns_head { struct list_head entry; struct kref ref; bool shared; + bool passthru_err_log_enabled; int instance; struct nvme_effects_log *effects; u64 nuse; @@ -523,7 +524,6 @@ struct nvme_ns { struct device cdev_device; struct nvme_fault_inject fault_inject; - }; /* NVMe ns supports metadata actions by the controller (generate/strip) */ @@ -806,17 +806,18 @@ blk_status_t nvme_setup_cmd(struct nvme_ns *ns, struct request *req); blk_status_t nvme_fail_nonready_command(struct nvme_ctrl *ctrl, struct request *req); bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq, - bool queue_live); + bool queue_live, enum nvme_ctrl_state state); static inline bool nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq, bool queue_live) { - if (likely(ctrl->state == NVME_CTRL_LIVE)) + enum nvme_ctrl_state state = nvme_ctrl_state(ctrl); + + if (likely(state == NVME_CTRL_LIVE)) return true; - if (ctrl->ops->flags & NVME_F_FABRICS && - ctrl->state == NVME_CTRL_DELETING) + if (ctrl->ops->flags & NVME_F_FABRICS && state == NVME_CTRL_DELETING) return queue_live; - return __nvme_check_ready(ctrl, rq, queue_live); + return __nvme_check_ready(ctrl, rq, queue_live, state); } /* @@ -837,12 +838,27 @@ static inline bool nvme_is_unique_nsid(struct nvme_ctrl *ctrl, (ctrl->ctratt & NVME_CTRL_CTRATT_NVM_SETS); } +/* + * Flags for __nvme_submit_sync_cmd() + */ +typedef __u32 __bitwise nvme_submit_flags_t; + +enum { + /* Insert request at the head of the queue */ + NVME_SUBMIT_AT_HEAD = (__force nvme_submit_flags_t)(1 << 0), + /* Set BLK_MQ_REQ_NOWAIT when allocating request */ + NVME_SUBMIT_NOWAIT = (__force nvme_submit_flags_t)(1 << 1), + /* Set BLK_MQ_REQ_RESERVED when allocating request */ + NVME_SUBMIT_RESERVED = (__force nvme_submit_flags_t)(1 << 2), + /* Retry command when NVME_SC_DNR is not set in the result */ + NVME_SUBMIT_RETRY = (__force nvme_submit_flags_t)(1 << 3), +}; + int nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, void *buf, unsigned bufflen); int __nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, union nvme_result *result, void *buffer, unsigned bufflen, - int qid, int at_head, - blk_mq_req_flags_t flags); + int qid, nvme_submit_flags_t flags); int nvme_set_features(struct nvme_ctrl *dev, unsigned int fid, unsigned int dword11, void *buffer, size_t buflen, u32 *result); @@ -932,6 +948,10 @@ extern struct device_attribute dev_attr_ana_grpid; extern struct device_attribute dev_attr_ana_state; extern struct device_attribute subsys_attr_iopolicy; +static inline bool nvme_disk_is_ns_head(struct gendisk *disk) +{ + return disk->fops == &nvme_ns_head_ops; +} #else #define multipath false static inline bool nvme_ctrl_use_ana(struct nvme_ctrl *ctrl) @@ -1009,6 +1029,10 @@ static inline void nvme_mpath_start_request(struct request *rq) static inline void nvme_mpath_end_request(struct request *rq) { } +static inline bool nvme_disk_is_ns_head(struct gendisk *disk) +{ + return false; +} #endif /* CONFIG_NVME_MULTIPATH */ int nvme_revalidate_zones(struct nvme_ns *ns); @@ -1037,7 +1061,10 @@ static inline int nvme_update_zone_info(struct nvme_ns *ns, unsigned lbaf) static inline struct nvme_ns *nvme_get_ns_from_dev(struct device *dev) { - return dev_to_disk(dev)->private_data; + struct gendisk *disk = dev_to_disk(dev); + + WARN_ON(nvme_disk_is_ns_head(disk)); + return disk->private_data; } #ifdef CONFIG_NVME_HWMON @@ -1114,35 +1141,42 @@ static inline bool nvme_multi_css(struct nvme_ctrl *ctrl) } #ifdef CONFIG_NVME_VERBOSE_ERRORS -const unsigned char *nvme_get_error_status_str(u16 status); -const unsigned char *nvme_get_opcode_str(u8 opcode); -const unsigned char *nvme_get_admin_opcode_str(u8 opcode); -const unsigned char *nvme_get_fabrics_opcode_str(u8 opcode); +const char *nvme_get_error_status_str(u16 status); +const char *nvme_get_opcode_str(u8 opcode); +const char *nvme_get_admin_opcode_str(u8 opcode); +const char *nvme_get_fabrics_opcode_str(u8 opcode); #else /* CONFIG_NVME_VERBOSE_ERRORS */ -static inline const unsigned char *nvme_get_error_status_str(u16 status) +static inline const char *nvme_get_error_status_str(u16 status) { return "I/O Error"; } -static inline const unsigned char *nvme_get_opcode_str(u8 opcode) +static inline const char *nvme_get_opcode_str(u8 opcode) { return "I/O Cmd"; } -static inline const unsigned char *nvme_get_admin_opcode_str(u8 opcode) +static inline const char *nvme_get_admin_opcode_str(u8 opcode) { return "Admin Cmd"; } -static inline const unsigned char *nvme_get_fabrics_opcode_str(u8 opcode) +static inline const char *nvme_get_fabrics_opcode_str(u8 opcode) { return "Fabrics Cmd"; } #endif /* CONFIG_NVME_VERBOSE_ERRORS */ -static inline const unsigned char *nvme_opcode_str(int qid, u8 opcode, u8 fctype) +static inline const char *nvme_opcode_str(int qid, u8 opcode) { - if (opcode == nvme_fabrics_command) - return nvme_get_fabrics_opcode_str(fctype); return qid ? nvme_get_opcode_str(opcode) : nvme_get_admin_opcode_str(opcode); } + +static inline const char *nvme_fabrics_opcode_str( + int qid, const struct nvme_command *cmd) +{ + if (nvme_is_fabrics(cmd)) + return nvme_get_fabrics_opcode_str(cmd->fabrics.fctype); + + return nvme_opcode_str(qid, cmd->common.opcode); +} #endif /* _NVME_H */ diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 61af7ff1a9d6..e6267a6aa380 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -1284,6 +1284,7 @@ static enum blk_eh_timer_return nvme_timeout(struct request *req) struct request *abort_req; struct nvme_command cmd = { }; u32 csts = readl(dev->bar + NVME_REG_CSTS); + u8 opcode; /* If PCI error recovery process is happening, we cannot reset or * the recovery mechanism will surely fail. @@ -1310,8 +1311,8 @@ static enum blk_eh_timer_return nvme_timeout(struct request *req) if (blk_mq_rq_state(req) != MQ_RQ_IN_FLIGHT) { dev_warn(dev->ctrl.device, - "I/O %d QID %d timeout, completion polled\n", - req->tag, nvmeq->qid); + "I/O tag %d (%04x) QID %d timeout, completion polled\n", + req->tag, nvme_cid(req), nvmeq->qid); return BLK_EH_DONE; } @@ -1327,8 +1328,8 @@ static enum blk_eh_timer_return nvme_timeout(struct request *req) fallthrough; case NVME_CTRL_DELETING: dev_warn_ratelimited(dev->ctrl.device, - "I/O %d QID %d timeout, disable controller\n", - req->tag, nvmeq->qid); + "I/O tag %d (%04x) QID %d timeout, disable controller\n", + req->tag, nvme_cid(req), nvmeq->qid); nvme_req(req)->flags |= NVME_REQ_CANCELLED; nvme_dev_disable(dev, true); return BLK_EH_DONE; @@ -1343,10 +1344,12 @@ static enum blk_eh_timer_return nvme_timeout(struct request *req) * command was already aborted once before and still hasn't been * returned to the driver, or if this is the admin queue. */ + opcode = nvme_req(req)->cmd->common.opcode; if (!nvmeq->qid || iod->aborted) { dev_warn(dev->ctrl.device, - "I/O %d QID %d timeout, reset controller\n", - req->tag, nvmeq->qid); + "I/O tag %d (%04x) opcode %#x (%s) QID %d timeout, reset controller\n", + req->tag, nvme_cid(req), opcode, + nvme_opcode_str(nvmeq->qid, opcode), nvmeq->qid); nvme_req(req)->flags |= NVME_REQ_CANCELLED; goto disable; } @@ -1362,10 +1365,10 @@ static enum blk_eh_timer_return nvme_timeout(struct request *req) cmd.abort.sqid = cpu_to_le16(nvmeq->qid); dev_warn(nvmeq->dev->ctrl.device, - "I/O %d (%s) QID %d timeout, aborting\n", - req->tag, - nvme_get_opcode_str(nvme_req(req)->cmd->common.opcode), - nvmeq->qid); + "I/O tag %d (%04x) opcode %#x (%s) QID %d timeout, aborting req_op:%s(%u) size:%u\n", + req->tag, nvme_cid(req), opcode, nvme_get_opcode_str(opcode), + nvmeq->qid, blk_op_str(req_op(req)), req_op(req), + blk_rq_bytes(req)); abort_req = blk_mq_alloc_request(dev->ctrl.admin_q, nvme_req_op(&cmd), BLK_MQ_REQ_NOWAIT); @@ -2743,10 +2746,10 @@ static void nvme_reset_work(struct work_struct *work) * controller around but remove all namespaces. */ if (dev->online_queues > 1) { + nvme_dbbuf_set(dev); nvme_unquiesce_io_queues(&dev->ctrl); nvme_wait_freeze(&dev->ctrl); nvme_pci_update_nr_queues(dev); - nvme_dbbuf_set(dev); nvme_unfreeze(&dev->ctrl); } else { dev_warn(dev->ctrl.device, "IO queues lost\n"); @@ -3408,6 +3411,8 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, { PCI_DEVICE(0x1c5c, 0x174a), /* SK Hynix P31 SSD */ .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1c5c, 0x1D59), /* SK Hynix BC901 */ + .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, { PCI_DEVICE(0x15b7, 0x2001), /* Sandisk Skyhawk */ .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, { PCI_DEVICE(0x1d97, 0x2263), /* SPCC */ @@ -3538,5 +3543,6 @@ static void __exit nvme_exit(void) MODULE_AUTHOR("Matthew Wilcox <willy@linux.intel.com>"); MODULE_LICENSE("GPL"); MODULE_VERSION("1.0"); +MODULE_DESCRIPTION("NVMe host PCIe transport driver"); module_init(nvme_init); module_exit(nvme_exit); diff --git a/drivers/nvme/host/pr.c b/drivers/nvme/host/pr.c index 391b1465ebfd..fc3eed00f9ff 100644 --- a/drivers/nvme/host/pr.c +++ b/drivers/nvme/host/pr.c @@ -98,7 +98,7 @@ static int nvme_send_pr_command(struct block_device *bdev, struct nvme_command *c, void *data, unsigned int data_len) { if (IS_ENABLED(CONFIG_NVME_MULTIPATH) && - bdev->bd_disk->fops == &nvme_ns_head_ops) + nvme_disk_is_ns_head(bdev->bd_disk)) return nvme_send_ns_head_pr_command(bdev, c, data, data_len); return nvme_send_ns_pr_command(bdev->bd_disk->private_data, c, data, diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index c89503da24d7..20fdd40b1879 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -1410,6 +1410,8 @@ static int nvme_rdma_map_sg_pi(struct nvme_rdma_queue *queue, struct nvme_ns *ns = rq->q->queuedata; struct bio *bio = rq->bio; struct nvme_keyed_sgl_desc *sg = &c->common.dptr.ksgl; + struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk); + u32 xfer_len; int nr; req->mr = ib_mr_pool_get(queue->qp, &queue->qp->sig_mrs); @@ -1422,8 +1424,7 @@ static int nvme_rdma_map_sg_pi(struct nvme_rdma_queue *queue, if (unlikely(nr)) goto mr_put; - nvme_rdma_set_sig_attrs(blk_get_integrity(bio->bi_bdev->bd_disk), c, - req->mr->sig_attrs, ns->head->pi_type); + nvme_rdma_set_sig_attrs(bi, c, req->mr->sig_attrs, ns->head->pi_type); nvme_rdma_set_prot_checks(c, &req->mr->sig_attrs->check_mask); ib_update_fast_reg_key(req->mr, ib_inc_rkey(req->mr->rkey)); @@ -1441,7 +1442,11 @@ static int nvme_rdma_map_sg_pi(struct nvme_rdma_queue *queue, IB_ACCESS_REMOTE_WRITE; sg->addr = cpu_to_le64(req->mr->iova); - put_unaligned_le24(req->mr->length, sg->length); + xfer_len = req->mr->length; + /* Check if PI is added by the HW */ + if (!pi_count) + xfer_len += (xfer_len >> bi->interval_exp) * ns->head->pi_size; + put_unaligned_le24(xfer_len, sg->length); put_unaligned_le32(req->mr->rkey, sg->key); sg->type = NVME_KEY_SGL_FMT_DATA_DESC << 4; @@ -1946,9 +1951,13 @@ static enum blk_eh_timer_return nvme_rdma_timeout(struct request *rq) struct nvme_rdma_request *req = blk_mq_rq_to_pdu(rq); struct nvme_rdma_queue *queue = req->queue; struct nvme_rdma_ctrl *ctrl = queue->ctrl; + struct nvme_command *cmd = req->req.cmd; + int qid = nvme_rdma_queue_idx(queue); - dev_warn(ctrl->ctrl.device, "I/O %d QID %d timeout\n", - rq->tag, nvme_rdma_queue_idx(queue)); + dev_warn(ctrl->ctrl.device, + "I/O tag %d (%04x) opcode %#x (%s) QID %d timeout\n", + rq->tag, nvme_cid(rq), cmd->common.opcode, + nvme_fabrics_opcode_str(qid, cmd), qid); if (nvme_ctrl_state(&ctrl->ctrl) != NVME_CTRL_LIVE) { /* @@ -2291,8 +2300,8 @@ static struct nvme_ctrl *nvme_rdma_create_ctrl(struct device *dev, if (ret) goto out_uninit_ctrl; - dev_info(ctrl->ctrl.device, "new ctrl: NQN \"%s\", addr %pISpcs\n", - nvmf_ctrl_subsysnqn(&ctrl->ctrl), &ctrl->addr); + dev_info(ctrl->ctrl.device, "new ctrl: NQN \"%s\", addr %pISpcs, hostnqn: %s\n", + nvmf_ctrl_subsysnqn(&ctrl->ctrl), &ctrl->addr, opts->host->nqn); mutex_lock(&nvme_rdma_ctrl_mutex); list_add_tail(&ctrl->list, &nvme_rdma_ctrl_list); @@ -2395,4 +2404,5 @@ static void __exit nvme_rdma_cleanup_module(void) module_init(nvme_rdma_init_module); module_exit(nvme_rdma_cleanup_module); +MODULE_DESCRIPTION("NVMe host RDMA transport driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c index ac24ad102380..f2832f70e7e0 100644 --- a/drivers/nvme/host/sysfs.c +++ b/drivers/nvme/host/sysfs.c @@ -35,16 +35,71 @@ static ssize_t nvme_sysfs_rescan(struct device *dev, } static DEVICE_ATTR(rescan_controller, S_IWUSR, NULL, nvme_sysfs_rescan); +static ssize_t nvme_adm_passthru_err_log_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + + return sysfs_emit(buf, + ctrl->passthru_err_log_enabled ? "on\n" : "off\n"); +} + +static ssize_t nvme_adm_passthru_err_log_enabled_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + bool passthru_err_log_enabled; + int err; + + err = kstrtobool(buf, &passthru_err_log_enabled); + if (err) + return -EINVAL; + + ctrl->passthru_err_log_enabled = passthru_err_log_enabled; + + return count; +} + static inline struct nvme_ns_head *dev_to_ns_head(struct device *dev) { struct gendisk *disk = dev_to_disk(dev); - if (disk->fops == &nvme_bdev_ops) - return nvme_get_ns_from_dev(dev)->head; - else + if (nvme_disk_is_ns_head(disk)) return disk->private_data; + return nvme_get_ns_from_dev(dev)->head; } +static ssize_t nvme_io_passthru_err_log_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ns_head *head = dev_to_ns_head(dev); + + return sysfs_emit(buf, head->passthru_err_log_enabled ? "on\n" : "off\n"); +} + +static ssize_t nvme_io_passthru_err_log_enabled_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct nvme_ns_head *head = dev_to_ns_head(dev); + bool passthru_err_log_enabled; + int err; + + err = kstrtobool(buf, &passthru_err_log_enabled); + if (err) + return -EINVAL; + head->passthru_err_log_enabled = passthru_err_log_enabled; + + return count; +} + +static struct device_attribute dev_attr_adm_passthru_err_log_enabled = \ + __ATTR(passthru_err_log_enabled, S_IRUGO | S_IWUSR, \ + nvme_adm_passthru_err_log_enabled_show, nvme_adm_passthru_err_log_enabled_store); + +static struct device_attribute dev_attr_io_passthru_err_log_enabled = \ + __ATTR(passthru_err_log_enabled, S_IRUGO | S_IWUSR, \ + nvme_io_passthru_err_log_enabled_show, nvme_io_passthru_err_log_enabled_store); + static ssize_t wwid_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -209,6 +264,7 @@ static struct attribute *nvme_ns_attrs[] = { &dev_attr_ana_grpid.attr, &dev_attr_ana_state.attr, #endif + &dev_attr_io_passthru_err_log_enabled.attr, NULL, }; @@ -233,7 +289,8 @@ static umode_t nvme_ns_attrs_are_visible(struct kobject *kobj, } #ifdef CONFIG_NVME_MULTIPATH if (a == &dev_attr_ana_grpid.attr || a == &dev_attr_ana_state.attr) { - if (dev_to_disk(dev)->fops != &nvme_bdev_ops) /* per-path attr */ + /* per-path attr */ + if (nvme_disk_is_ns_head(dev_to_disk(dev))) return 0; if (!nvme_ctrl_use_ana(nvme_get_ns_from_dev(dev)->ctrl)) return 0; @@ -311,6 +368,7 @@ static ssize_t nvme_sysfs_show_state(struct device *dev, char *buf) { struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + unsigned state = (unsigned)nvme_ctrl_state(ctrl); static const char *const state_name[] = { [NVME_CTRL_NEW] = "new", [NVME_CTRL_LIVE] = "live", @@ -321,9 +379,8 @@ static ssize_t nvme_sysfs_show_state(struct device *dev, [NVME_CTRL_DEAD] = "dead", }; - if ((unsigned)ctrl->state < ARRAY_SIZE(state_name) && - state_name[ctrl->state]) - return sysfs_emit(buf, "%s\n", state_name[ctrl->state]); + if (state < ARRAY_SIZE(state_name) && state_name[state]) + return sysfs_emit(buf, "%s\n", state_name[state]); return sysfs_emit(buf, "unknown state\n"); } @@ -655,6 +712,7 @@ static struct attribute *nvme_dev_attrs[] = { #ifdef CONFIG_NVME_TCP_TLS &dev_attr_tls_key.attr, #endif + &dev_attr_adm_passthru_err_log_enabled.attr, NULL }; diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 08805f027810..a6d596e05602 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -1922,14 +1922,13 @@ static int nvme_tcp_alloc_admin_queue(struct nvme_ctrl *ctrl) ctrl->opts->subsysnqn); if (!pskid) { dev_err(ctrl->device, "no valid PSK found\n"); - ret = -ENOKEY; - goto out_free_queue; + return -ENOKEY; } } ret = nvme_tcp_alloc_queue(ctrl, 0, pskid); if (ret) - goto out_free_queue; + return ret; ret = nvme_tcp_alloc_async_req(to_tcp_ctrl(ctrl)); if (ret) @@ -2429,13 +2428,13 @@ static enum blk_eh_timer_return nvme_tcp_timeout(struct request *rq) struct nvme_tcp_request *req = blk_mq_rq_to_pdu(rq); struct nvme_ctrl *ctrl = &req->queue->ctrl->ctrl; struct nvme_tcp_cmd_pdu *pdu = nvme_tcp_req_cmd_pdu(req); - u8 opc = pdu->cmd.common.opcode, fctype = pdu->cmd.fabrics.fctype; + struct nvme_command *cmd = &pdu->cmd; int qid = nvme_tcp_queue_id(req->queue); dev_warn(ctrl->device, - "queue %d: timeout cid %#x type %d opcode %#x (%s)\n", - nvme_tcp_queue_id(req->queue), nvme_cid(rq), pdu->hdr.type, - opc, nvme_opcode_str(qid, opc, fctype)); + "I/O tag %d (%04x) type %d opcode %#x (%s) QID %d timeout\n", + rq->tag, nvme_cid(rq), pdu->hdr.type, cmd->common.opcode, + nvme_fabrics_opcode_str(qid, cmd), qid); if (nvme_ctrl_state(ctrl) != NVME_CTRL_LIVE) { /* @@ -2754,8 +2753,8 @@ static struct nvme_ctrl *nvme_tcp_create_ctrl(struct device *dev, if (ret) goto out_uninit_ctrl; - dev_info(ctrl->ctrl.device, "new ctrl: NQN \"%s\", addr %pISp\n", - nvmf_ctrl_subsysnqn(&ctrl->ctrl), &ctrl->addr); + dev_info(ctrl->ctrl.device, "new ctrl: NQN \"%s\", addr %pISp, hostnqn: %s\n", + nvmf_ctrl_subsysnqn(&ctrl->ctrl), &ctrl->addr, opts->host->nqn); mutex_lock(&nvme_tcp_ctrl_mutex); list_add_tail(&ctrl->list, &nvme_tcp_ctrl_list); @@ -2827,4 +2826,5 @@ static void __exit nvme_tcp_cleanup_module(void) module_init(nvme_tcp_init_module); module_exit(nvme_tcp_cleanup_module); +MODULE_DESCRIPTION("NVMe host TCP transport driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index d26aa30f8702..8658e9c08534 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -248,7 +248,7 @@ void nvmet_ns_changed(struct nvmet_subsys *subsys, u32 nsid) nvmet_add_to_changed_ns_log(ctrl, cpu_to_le32(nsid)); if (nvmet_aen_bit_disabled(ctrl, NVME_AEN_BIT_NS_ATTR)) continue; - nvmet_add_async_event(ctrl, NVME_AER_TYPE_NOTICE, + nvmet_add_async_event(ctrl, NVME_AER_NOTICE, NVME_AER_NOTICE_NS_CHANGED, NVME_LOG_CHANGED_NS); } @@ -265,7 +265,7 @@ void nvmet_send_ana_event(struct nvmet_subsys *subsys, continue; if (nvmet_aen_bit_disabled(ctrl, NVME_AEN_BIT_ANA_CHANGE)) continue; - nvmet_add_async_event(ctrl, NVME_AER_TYPE_NOTICE, + nvmet_add_async_event(ctrl, NVME_AER_NOTICE, NVME_AER_NOTICE_ANA, NVME_LOG_ANA); } mutex_unlock(&subsys->lock); @@ -1705,4 +1705,5 @@ static void __exit nvmet_exit(void) module_init(nvmet_init); module_exit(nvmet_exit); +MODULE_DESCRIPTION("NVMe target core framework"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c index 668d257fa986..68e82ccc0e4e 100644 --- a/drivers/nvme/target/discovery.c +++ b/drivers/nvme/target/discovery.c @@ -21,7 +21,7 @@ static void __nvmet_disc_changed(struct nvmet_port *port, if (nvmet_aen_bit_disabled(ctrl, NVME_AEN_BIT_DISC_CHANGE)) return; - nvmet_add_async_event(ctrl, NVME_AER_TYPE_NOTICE, + nvmet_add_async_event(ctrl, NVME_AER_NOTICE, NVME_AER_NOTICE_DISC_CHANGED, NVME_LOG_DISC); } diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c index bd59990b5250..fd229f310c93 100644 --- a/drivers/nvme/target/fc.c +++ b/drivers/nvme/target/fc.c @@ -111,6 +111,8 @@ struct nvmet_fc_tgtport { struct nvmet_fc_port_entry *pe; struct kref ref; u32 max_sg_cnt; + + struct work_struct put_work; }; struct nvmet_fc_port_entry { @@ -145,7 +147,6 @@ struct nvmet_fc_tgt_queue { struct list_head avail_defer_list; struct workqueue_struct *work_q; struct kref ref; - struct rcu_head rcu; /* array of fcp_iods */ struct nvmet_fc_fcp_iod fod[] __counted_by(sqsize); } __aligned(sizeof(unsigned long long)); @@ -166,10 +167,9 @@ struct nvmet_fc_tgt_assoc { struct nvmet_fc_hostport *hostport; struct nvmet_fc_ls_iod *rcv_disconn; struct list_head a_list; - struct nvmet_fc_tgt_queue __rcu *queues[NVMET_NR_QUEUES + 1]; + struct nvmet_fc_tgt_queue *queues[NVMET_NR_QUEUES + 1]; struct kref ref; struct work_struct del_work; - struct rcu_head rcu; }; @@ -249,6 +249,13 @@ static int nvmet_fc_tgt_a_get(struct nvmet_fc_tgt_assoc *assoc); static void nvmet_fc_tgt_q_put(struct nvmet_fc_tgt_queue *queue); static int nvmet_fc_tgt_q_get(struct nvmet_fc_tgt_queue *queue); static void nvmet_fc_tgtport_put(struct nvmet_fc_tgtport *tgtport); +static void nvmet_fc_put_tgtport_work(struct work_struct *work) +{ + struct nvmet_fc_tgtport *tgtport = + container_of(work, struct nvmet_fc_tgtport, put_work); + + nvmet_fc_tgtport_put(tgtport); +} static int nvmet_fc_tgtport_get(struct nvmet_fc_tgtport *tgtport); static void nvmet_fc_handle_fcp_rqst(struct nvmet_fc_tgtport *tgtport, struct nvmet_fc_fcp_iod *fod); @@ -360,7 +367,7 @@ __nvmet_fc_finish_ls_req(struct nvmet_fc_ls_req_op *lsop) if (!lsop->req_queued) { spin_unlock_irqrestore(&tgtport->lock, flags); - return; + goto out_putwork; } list_del(&lsop->lsreq_list); @@ -373,7 +380,8 @@ __nvmet_fc_finish_ls_req(struct nvmet_fc_ls_req_op *lsop) (lsreq->rqstlen + lsreq->rsplen), DMA_BIDIRECTIONAL); - nvmet_fc_tgtport_put(tgtport); +out_putwork: + queue_work(nvmet_wq, &tgtport->put_work); } static int @@ -489,8 +497,7 @@ nvmet_fc_xmt_disconnect_assoc(struct nvmet_fc_tgt_assoc *assoc) * message is normal. Otherwise, send unless the hostport has * already been invalidated by the lldd. */ - if (!tgtport->ops->ls_req || !assoc->hostport || - assoc->hostport->invalid) + if (!tgtport->ops->ls_req || assoc->hostport->invalid) return; lsop = kzalloc((sizeof(*lsop) + @@ -802,14 +809,11 @@ nvmet_fc_alloc_target_queue(struct nvmet_fc_tgt_assoc *assoc, if (!queue) return NULL; - if (!nvmet_fc_tgt_a_get(assoc)) - goto out_free_queue; - queue->work_q = alloc_workqueue("ntfc%d.%d.%d", 0, 0, assoc->tgtport->fc_target_port.port_num, assoc->a_id, qid); if (!queue->work_q) - goto out_a_put; + goto out_free_queue; queue->qid = qid; queue->sqsize = sqsize; @@ -831,15 +835,13 @@ nvmet_fc_alloc_target_queue(struct nvmet_fc_tgt_assoc *assoc, goto out_fail_iodlist; WARN_ON(assoc->queues[qid]); - rcu_assign_pointer(assoc->queues[qid], queue); + assoc->queues[qid] = queue; return queue; out_fail_iodlist: nvmet_fc_destroy_fcp_iodlist(assoc->tgtport, queue); destroy_workqueue(queue->work_q); -out_a_put: - nvmet_fc_tgt_a_put(assoc); out_free_queue: kfree(queue); return NULL; @@ -852,15 +854,11 @@ nvmet_fc_tgt_queue_free(struct kref *ref) struct nvmet_fc_tgt_queue *queue = container_of(ref, struct nvmet_fc_tgt_queue, ref); - rcu_assign_pointer(queue->assoc->queues[queue->qid], NULL); - nvmet_fc_destroy_fcp_iodlist(queue->assoc->tgtport, queue); - nvmet_fc_tgt_a_put(queue->assoc); - destroy_workqueue(queue->work_q); - kfree_rcu(queue, rcu); + kfree(queue); } static void @@ -969,7 +967,7 @@ nvmet_fc_find_target_queue(struct nvmet_fc_tgtport *tgtport, rcu_read_lock(); list_for_each_entry_rcu(assoc, &tgtport->assoc_list, a_list) { if (association_id == assoc->association_id) { - queue = rcu_dereference(assoc->queues[qid]); + queue = assoc->queues[qid]; if (queue && (!atomic_read(&queue->connected) || !nvmet_fc_tgt_q_get(queue))) @@ -1031,7 +1029,7 @@ nvmet_fc_match_hostport(struct nvmet_fc_tgtport *tgtport, void *hosthandle) list_for_each_entry(host, &tgtport->host_list, host_list) { if (host->hosthandle == hosthandle && !host->invalid) { if (nvmet_fc_hostport_get(host)) - return (host); + return host; } } @@ -1078,8 +1076,6 @@ nvmet_fc_alloc_hostport(struct nvmet_fc_tgtport *tgtport, void *hosthandle) /* new allocation not needed */ kfree(newhost); newhost = match; - /* no new allocation - release reference */ - nvmet_fc_tgtport_put(tgtport); } else { newhost->tgtport = tgtport; newhost->hosthandle = hosthandle; @@ -1094,23 +1090,54 @@ nvmet_fc_alloc_hostport(struct nvmet_fc_tgtport *tgtport, void *hosthandle) } static void -nvmet_fc_delete_assoc(struct work_struct *work) +nvmet_fc_delete_assoc(struct nvmet_fc_tgt_assoc *assoc) +{ + nvmet_fc_delete_target_assoc(assoc); + nvmet_fc_tgt_a_put(assoc); +} + +static void +nvmet_fc_delete_assoc_work(struct work_struct *work) { struct nvmet_fc_tgt_assoc *assoc = container_of(work, struct nvmet_fc_tgt_assoc, del_work); + struct nvmet_fc_tgtport *tgtport = assoc->tgtport; - nvmet_fc_delete_target_assoc(assoc); - nvmet_fc_tgt_a_put(assoc); + nvmet_fc_delete_assoc(assoc); + nvmet_fc_tgtport_put(tgtport); +} + +static void +nvmet_fc_schedule_delete_assoc(struct nvmet_fc_tgt_assoc *assoc) +{ + nvmet_fc_tgtport_get(assoc->tgtport); + queue_work(nvmet_wq, &assoc->del_work); +} + +static bool +nvmet_fc_assoc_exits(struct nvmet_fc_tgtport *tgtport, u64 association_id) +{ + struct nvmet_fc_tgt_assoc *a; + + list_for_each_entry_rcu(a, &tgtport->assoc_list, a_list) { + if (association_id == a->association_id) + return true; + } + + return false; } static struct nvmet_fc_tgt_assoc * nvmet_fc_alloc_target_assoc(struct nvmet_fc_tgtport *tgtport, void *hosthandle) { - struct nvmet_fc_tgt_assoc *assoc, *tmpassoc; + struct nvmet_fc_tgt_assoc *assoc; unsigned long flags; + bool done; u64 ran; int idx; - bool needrandom = true; + + if (!tgtport->pe) + return NULL; assoc = kzalloc(sizeof(*assoc), GFP_KERNEL); if (!assoc) @@ -1120,43 +1147,35 @@ nvmet_fc_alloc_target_assoc(struct nvmet_fc_tgtport *tgtport, void *hosthandle) if (idx < 0) goto out_free_assoc; - if (!nvmet_fc_tgtport_get(tgtport)) - goto out_ida; - assoc->hostport = nvmet_fc_alloc_hostport(tgtport, hosthandle); if (IS_ERR(assoc->hostport)) - goto out_put; + goto out_ida; assoc->tgtport = tgtport; assoc->a_id = idx; INIT_LIST_HEAD(&assoc->a_list); kref_init(&assoc->ref); - INIT_WORK(&assoc->del_work, nvmet_fc_delete_assoc); + INIT_WORK(&assoc->del_work, nvmet_fc_delete_assoc_work); atomic_set(&assoc->terminating, 0); - while (needrandom) { + done = false; + do { get_random_bytes(&ran, sizeof(ran) - BYTES_FOR_QID); ran = ran << BYTES_FOR_QID_SHIFT; spin_lock_irqsave(&tgtport->lock, flags); - needrandom = false; - list_for_each_entry(tmpassoc, &tgtport->assoc_list, a_list) { - if (ran == tmpassoc->association_id) { - needrandom = true; - break; - } - } - if (!needrandom) { + rcu_read_lock(); + if (!nvmet_fc_assoc_exits(tgtport, ran)) { assoc->association_id = ran; list_add_tail_rcu(&assoc->a_list, &tgtport->assoc_list); + done = true; } + rcu_read_unlock(); spin_unlock_irqrestore(&tgtport->lock, flags); - } + } while (!done); return assoc; -out_put: - nvmet_fc_tgtport_put(tgtport); out_ida: ida_free(&tgtport->assoc_cnt, idx); out_free_assoc: @@ -1172,13 +1191,18 @@ nvmet_fc_target_assoc_free(struct kref *ref) struct nvmet_fc_tgtport *tgtport = assoc->tgtport; struct nvmet_fc_ls_iod *oldls; unsigned long flags; + int i; + + for (i = NVMET_NR_QUEUES; i >= 0; i--) { + if (assoc->queues[i]) + nvmet_fc_delete_target_queue(assoc->queues[i]); + } /* Send Disconnect now that all i/o has completed */ nvmet_fc_xmt_disconnect_assoc(assoc); nvmet_fc_free_hostport(assoc->hostport); spin_lock_irqsave(&tgtport->lock, flags); - list_del_rcu(&assoc->a_list); oldls = assoc->rcv_disconn; spin_unlock_irqrestore(&tgtport->lock, flags); /* if pending Rcv Disconnect Association LS, send rsp now */ @@ -1188,8 +1212,7 @@ nvmet_fc_target_assoc_free(struct kref *ref) dev_info(tgtport->dev, "{%d:%d} Association freed\n", tgtport->fc_target_port.port_num, assoc->a_id); - kfree_rcu(assoc, rcu); - nvmet_fc_tgtport_put(tgtport); + kfree(assoc); } static void @@ -1208,7 +1231,7 @@ static void nvmet_fc_delete_target_assoc(struct nvmet_fc_tgt_assoc *assoc) { struct nvmet_fc_tgtport *tgtport = assoc->tgtport; - struct nvmet_fc_tgt_queue *queue; + unsigned long flags; int i, terminating; terminating = atomic_xchg(&assoc->terminating, 1); @@ -1217,29 +1240,21 @@ nvmet_fc_delete_target_assoc(struct nvmet_fc_tgt_assoc *assoc) if (terminating) return; + spin_lock_irqsave(&tgtport->lock, flags); + list_del_rcu(&assoc->a_list); + spin_unlock_irqrestore(&tgtport->lock, flags); - for (i = NVMET_NR_QUEUES; i >= 0; i--) { - rcu_read_lock(); - queue = rcu_dereference(assoc->queues[i]); - if (!queue) { - rcu_read_unlock(); - continue; - } + synchronize_rcu(); - if (!nvmet_fc_tgt_q_get(queue)) { - rcu_read_unlock(); - continue; - } - rcu_read_unlock(); - nvmet_fc_delete_target_queue(queue); - nvmet_fc_tgt_q_put(queue); + /* ensure all in-flight I/Os have been processed */ + for (i = NVMET_NR_QUEUES; i >= 0; i--) { + if (assoc->queues[i]) + flush_workqueue(assoc->queues[i]->work_q); } dev_info(tgtport->dev, "{%d:%d} Association deleted\n", tgtport->fc_target_port.port_num, assoc->a_id); - - nvmet_fc_tgt_a_put(assoc); } static struct nvmet_fc_tgt_assoc * @@ -1415,6 +1430,7 @@ nvmet_fc_register_targetport(struct nvmet_fc_port_info *pinfo, kref_init(&newrec->ref); ida_init(&newrec->assoc_cnt); newrec->max_sg_cnt = template->max_sgl_segments; + INIT_WORK(&newrec->put_work, nvmet_fc_put_tgtport_work); ret = nvmet_fc_alloc_ls_iodlist(newrec); if (ret) { @@ -1492,9 +1508,8 @@ __nvmet_fc_free_assocs(struct nvmet_fc_tgtport *tgtport) list_for_each_entry_rcu(assoc, &tgtport->assoc_list, a_list) { if (!nvmet_fc_tgt_a_get(assoc)) continue; - if (!queue_work(nvmet_wq, &assoc->del_work)) - /* already deleting - release local reference */ - nvmet_fc_tgt_a_put(assoc); + nvmet_fc_schedule_delete_assoc(assoc); + nvmet_fc_tgt_a_put(assoc); } rcu_read_unlock(); } @@ -1540,16 +1555,14 @@ nvmet_fc_invalidate_host(struct nvmet_fc_target_port *target_port, spin_lock_irqsave(&tgtport->lock, flags); list_for_each_entry_safe(assoc, next, &tgtport->assoc_list, a_list) { - if (!assoc->hostport || - assoc->hostport->hosthandle != hosthandle) + if (assoc->hostport->hosthandle != hosthandle) continue; if (!nvmet_fc_tgt_a_get(assoc)) continue; assoc->hostport->invalid = 1; noassoc = false; - if (!queue_work(nvmet_wq, &assoc->del_work)) - /* already deleting - release local reference */ - nvmet_fc_tgt_a_put(assoc); + nvmet_fc_schedule_delete_assoc(assoc); + nvmet_fc_tgt_a_put(assoc); } spin_unlock_irqrestore(&tgtport->lock, flags); @@ -1581,7 +1594,7 @@ nvmet_fc_delete_ctrl(struct nvmet_ctrl *ctrl) rcu_read_lock(); list_for_each_entry_rcu(assoc, &tgtport->assoc_list, a_list) { - queue = rcu_dereference(assoc->queues[0]); + queue = assoc->queues[0]; if (queue && queue->nvme_sq.ctrl == ctrl) { if (nvmet_fc_tgt_a_get(assoc)) found_ctrl = true; @@ -1593,9 +1606,8 @@ nvmet_fc_delete_ctrl(struct nvmet_ctrl *ctrl) nvmet_fc_tgtport_put(tgtport); if (found_ctrl) { - if (!queue_work(nvmet_wq, &assoc->del_work)) - /* already deleting - release local reference */ - nvmet_fc_tgt_a_put(assoc); + nvmet_fc_schedule_delete_assoc(assoc); + nvmet_fc_tgt_a_put(assoc); return; } @@ -1625,6 +1637,8 @@ nvmet_fc_unregister_targetport(struct nvmet_fc_target_port *target_port) /* terminate any outstanding associations */ __nvmet_fc_free_assocs(tgtport); + flush_workqueue(nvmet_wq); + /* * should terminate LS's as well. However, LS's will be generated * at the tail end of association termination, so they likely don't @@ -1870,9 +1884,6 @@ nvmet_fc_ls_disconnect(struct nvmet_fc_tgtport *tgtport, sizeof(struct fcnvme_ls_disconnect_assoc_acc)), FCNVME_LS_DISCONNECT_ASSOC); - /* release get taken in nvmet_fc_find_target_assoc */ - nvmet_fc_tgt_a_put(assoc); - /* * The rules for LS response says the response cannot * go back until ABTS's have been sent for all outstanding @@ -1887,8 +1898,6 @@ nvmet_fc_ls_disconnect(struct nvmet_fc_tgtport *tgtport, assoc->rcv_disconn = iod; spin_unlock_irqrestore(&tgtport->lock, flags); - nvmet_fc_delete_target_assoc(assoc); - if (oldls) { dev_info(tgtport->dev, "{%d:%d} Multiple Disconnect Association LS's " @@ -1904,6 +1913,9 @@ nvmet_fc_ls_disconnect(struct nvmet_fc_tgtport *tgtport, nvmet_fc_xmt_ls_rsp(tgtport, oldls); } + nvmet_fc_schedule_delete_assoc(assoc); + nvmet_fc_tgt_a_put(assoc); + return false; } @@ -2540,8 +2552,9 @@ nvmet_fc_handle_fcp_rqst(struct nvmet_fc_tgtport *tgtport, fod->req.cmd = &fod->cmdiubuf.sqe; fod->req.cqe = &fod->rspiubuf.cqe; - if (tgtport->pe) - fod->req.port = tgtport->pe->port; + if (!tgtport->pe) + goto transport_error; + fod->req.port = tgtport->pe->port; /* clear any response payload */ memset(&fod->rspiubuf, 0, sizeof(fod->rspiubuf)); @@ -2902,6 +2915,9 @@ nvmet_fc_remove_port(struct nvmet_port *port) nvmet_fc_portentry_unbind(pe); + /* terminate any outstanding associations */ + __nvmet_fc_free_assocs(pe->tgtport); + kfree(pe); } @@ -2933,6 +2949,9 @@ static int __init nvmet_fc_init_module(void) static void __exit nvmet_fc_exit_module(void) { + /* ensure any shutdown operation, e.g. delete ctrls have finished */ + flush_workqueue(nvmet_wq); + /* sanity check - all lports should be removed */ if (!list_empty(&nvmet_fc_target_list)) pr_warn("%s: targetport list not empty\n", __func__); @@ -2945,4 +2964,5 @@ static void __exit nvmet_fc_exit_module(void) module_init(nvmet_fc_init_module); module_exit(nvmet_fc_exit_module); +MODULE_DESCRIPTION("NVMe target FC transport driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c index c65a73433c05..1471af250ea6 100644 --- a/drivers/nvme/target/fcloop.c +++ b/drivers/nvme/target/fcloop.c @@ -358,7 +358,7 @@ fcloop_h2t_ls_req(struct nvme_fc_local_port *localport, if (!rport->targetport) { tls_req->status = -ECONNREFUSED; spin_lock(&rport->lock); - list_add_tail(&rport->ls_list, &tls_req->ls_list); + list_add_tail(&tls_req->ls_list, &rport->ls_list); spin_unlock(&rport->lock); queue_work(nvmet_wq, &rport->ls_work); return ret; @@ -391,7 +391,7 @@ fcloop_h2t_xmt_ls_rsp(struct nvmet_fc_target_port *targetport, if (remoteport) { rport = remoteport->private; spin_lock(&rport->lock); - list_add_tail(&rport->ls_list, &tls_req->ls_list); + list_add_tail(&tls_req->ls_list, &rport->ls_list); spin_unlock(&rport->lock); queue_work(nvmet_wq, &rport->ls_work); } @@ -446,7 +446,7 @@ fcloop_t2h_ls_req(struct nvmet_fc_target_port *targetport, void *hosthandle, if (!tport->remoteport) { tls_req->status = -ECONNREFUSED; spin_lock(&tport->lock); - list_add_tail(&tport->ls_list, &tls_req->ls_list); + list_add_tail(&tls_req->ls_list, &tport->ls_list); spin_unlock(&tport->lock); queue_work(nvmet_wq, &tport->ls_work); return ret; @@ -995,11 +995,6 @@ fcloop_nport_free(struct kref *ref) { struct fcloop_nport *nport = container_of(ref, struct fcloop_nport, ref); - unsigned long flags; - - spin_lock_irqsave(&fcloop_lock, flags); - list_del(&nport->nport_list); - spin_unlock_irqrestore(&fcloop_lock, flags); kfree(nport); } @@ -1357,6 +1352,8 @@ __unlink_remote_port(struct fcloop_nport *nport) nport->tport->remoteport = NULL; nport->rport = NULL; + list_del(&nport->nport_list); + return rport; } @@ -1653,4 +1650,5 @@ static void __exit fcloop_exit(void) module_init(fcloop_init); module_exit(fcloop_exit); +MODULE_DESCRIPTION("NVMe target FC loop transport driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c index 9cb434c58075..e589915ddef8 100644 --- a/drivers/nvme/target/loop.c +++ b/drivers/nvme/target/loop.c @@ -400,7 +400,7 @@ static void nvme_loop_shutdown_ctrl(struct nvme_loop_ctrl *ctrl) } nvme_quiesce_admin_queue(&ctrl->ctrl); - if (ctrl->ctrl.state == NVME_CTRL_LIVE) + if (nvme_ctrl_state(&ctrl->ctrl) == NVME_CTRL_LIVE) nvme_disable_ctrl(&ctrl->ctrl, true); nvme_cancel_admin_tagset(&ctrl->ctrl); @@ -434,8 +434,10 @@ static void nvme_loop_reset_ctrl_work(struct work_struct *work) nvme_loop_shutdown_ctrl(ctrl); if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) { - if (ctrl->ctrl.state != NVME_CTRL_DELETING && - ctrl->ctrl.state != NVME_CTRL_DELETING_NOIO) + enum nvme_ctrl_state state = nvme_ctrl_state(&ctrl->ctrl); + + if (state != NVME_CTRL_DELETING && + state != NVME_CTRL_DELETING_NOIO) /* state change failure for non-deleted ctrl? */ WARN_ON_ONCE(1); return; @@ -688,5 +690,6 @@ static void __exit nvme_loop_cleanup_module(void) module_init(nvme_loop_init_module); module_exit(nvme_loop_cleanup_module); +MODULE_DESCRIPTION("NVMe target loop transport driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("nvmet-transport-254"); /* 254 == NVMF_TRTYPE_LOOP */ diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c index 4597bca43a6d..3a0f2c170f4c 100644 --- a/drivers/nvme/target/rdma.c +++ b/drivers/nvme/target/rdma.c @@ -37,6 +37,8 @@ #define NVMET_RDMA_MAX_MDTS 8 #define NVMET_RDMA_MAX_METADATA_MDTS 5 +#define NVMET_RDMA_BACKLOG 128 + struct nvmet_rdma_srq; struct nvmet_rdma_cmd { @@ -1583,8 +1585,19 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id, } if (queue->host_qid == 0) { - /* Let inflight controller teardown complete */ - flush_workqueue(nvmet_wq); + struct nvmet_rdma_queue *q; + int pending = 0; + + /* Check for pending controller teardown */ + mutex_lock(&nvmet_rdma_queue_mutex); + list_for_each_entry(q, &nvmet_rdma_queue_list, queue_list) { + if (q->nvme_sq.ctrl == queue->nvme_sq.ctrl && + q->state == NVMET_RDMA_Q_DISCONNECTING) + pending++; + } + mutex_unlock(&nvmet_rdma_queue_mutex); + if (pending > NVMET_RDMA_BACKLOG) + return NVME_SC_CONNECT_CTRL_BUSY; } ret = nvmet_rdma_cm_accept(cm_id, queue, &event->param.conn); @@ -1880,7 +1893,7 @@ static int nvmet_rdma_enable_port(struct nvmet_rdma_port *port) goto out_destroy_id; } - ret = rdma_listen(cm_id, 128); + ret = rdma_listen(cm_id, NVMET_RDMA_BACKLOG); if (ret) { pr_err("listening to %pISpcs failed (%d)\n", addr, ret); goto out_destroy_id; @@ -2091,5 +2104,6 @@ static void __exit nvmet_rdma_exit(void) module_init(nvmet_rdma_init); module_exit(nvmet_rdma_exit); +MODULE_DESCRIPTION("NVMe target RDMA transport driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("nvmet-transport-1"); /* 1 == NVMF_TRTYPE_RDMA */ diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 4cc27856aa8f..c8655fc5aa5b 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -24,6 +24,8 @@ #include "nvmet.h" #define NVMET_TCP_DEF_INLINE_DATA_SIZE (4 * PAGE_SIZE) +#define NVMET_TCP_MAXH2CDATA 0x400000 /* 16M arbitrary limit */ +#define NVMET_TCP_BACKLOG 128 static int param_store_val(const char *str, int *val, int min, int max) { @@ -923,7 +925,7 @@ static int nvmet_tcp_handle_icreq(struct nvmet_tcp_queue *queue) icresp->hdr.pdo = 0; icresp->hdr.plen = cpu_to_le32(icresp->hdr.hlen); icresp->pfv = cpu_to_le16(NVME_TCP_PFV_1_0); - icresp->maxdata = cpu_to_le32(0x400000); /* 16M arbitrary limit */ + icresp->maxdata = cpu_to_le32(NVMET_TCP_MAXH2CDATA); icresp->cpda = 0; if (queue->hdr_digest) icresp->digest |= NVME_TCP_HDR_DIGEST_ENABLE; @@ -978,13 +980,13 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue) { struct nvme_tcp_data_pdu *data = &queue->pdu.data; struct nvmet_tcp_cmd *cmd; + unsigned int exp_data_len; if (likely(queue->nr_cmds)) { if (unlikely(data->ttag >= queue->nr_cmds)) { pr_err("queue %d: received out of bound ttag %u, nr_cmds %u\n", queue->idx, data->ttag, queue->nr_cmds); - nvmet_tcp_fatal_error(queue); - return -EPROTO; + goto err_proto; } cmd = &queue->cmds[data->ttag]; } else { @@ -995,19 +997,32 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue) pr_err("ttag %u unexpected data offset %u (expected %u)\n", data->ttag, le32_to_cpu(data->data_offset), cmd->rbytes_done); - /* FIXME: use path and transport errors */ - nvmet_req_complete(&cmd->req, - NVME_SC_INVALID_FIELD | NVME_SC_DNR); - return -EPROTO; + goto err_proto; } + exp_data_len = le32_to_cpu(data->hdr.plen) - + nvmet_tcp_hdgst_len(queue) - + nvmet_tcp_ddgst_len(queue) - + sizeof(*data); + cmd->pdu_len = le32_to_cpu(data->data_length); + if (unlikely(cmd->pdu_len != exp_data_len || + cmd->pdu_len == 0 || + cmd->pdu_len > NVMET_TCP_MAXH2CDATA)) { + pr_err("H2CData PDU len %u is invalid\n", cmd->pdu_len); + goto err_proto; + } cmd->pdu_recv = 0; nvmet_tcp_build_pdu_iovec(cmd); queue->cmd = cmd; queue->rcv_state = NVMET_TCP_RECV_DATA; return 0; + +err_proto: + /* FIXME: use proper transport errors */ + nvmet_tcp_fatal_error(queue); + return -EPROTO; } static int nvmet_tcp_done_recv_pdu(struct nvmet_tcp_queue *queue) @@ -1768,7 +1783,7 @@ static int nvmet_tcp_try_peek_pdu(struct nvmet_tcp_queue *queue) (int)sizeof(struct nvme_tcp_icreq_pdu)); if (hdr->type == nvme_tcp_icreq && hdr->hlen == sizeof(struct nvme_tcp_icreq_pdu) && - hdr->plen == (__le32)sizeof(struct nvme_tcp_icreq_pdu)) { + hdr->plen == cpu_to_le32(sizeof(struct nvme_tcp_icreq_pdu))) { pr_debug("queue %d: icreq detected\n", queue->idx); return len; @@ -2053,7 +2068,7 @@ static int nvmet_tcp_add_port(struct nvmet_port *nport) goto err_sock; } - ret = kernel_listen(port->sock, 128); + ret = kernel_listen(port->sock, NVMET_TCP_BACKLOG); if (ret) { pr_err("failed to listen %d on port sock\n", ret); goto err_sock; @@ -2119,8 +2134,19 @@ static u16 nvmet_tcp_install_queue(struct nvmet_sq *sq) container_of(sq, struct nvmet_tcp_queue, nvme_sq); if (sq->qid == 0) { - /* Let inflight controller teardown complete */ - flush_workqueue(nvmet_wq); + struct nvmet_tcp_queue *q; + int pending = 0; + + /* Check for pending controller teardown */ + mutex_lock(&nvmet_tcp_queue_mutex); + list_for_each_entry(q, &nvmet_tcp_queue_list, queue_list) { + if (q->nvme_sq.ctrl == sq->ctrl && + q->state == NVMET_TCP_Q_DISCONNECTING) + pending++; + } + mutex_unlock(&nvmet_tcp_queue_mutex); + if (pending > NVMET_TCP_BACKLOG) + return NVME_SC_CONNECT_CTRL_BUSY; } queue->nr_cmds = sq->size * 2; @@ -2190,10 +2216,12 @@ static void __exit nvmet_tcp_exit(void) flush_workqueue(nvmet_wq); destroy_workqueue(nvmet_tcp_wq); + ida_destroy(&nvmet_tcp_queue_ida); } module_init(nvmet_tcp_init); module_exit(nvmet_tcp_exit); +MODULE_DESCRIPTION("NVMe target TCP transport driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("nvmet-transport-3"); /* 3 == NVMF_TRTYPE_TCP */ diff --git a/drivers/nvme/target/trace.c b/drivers/nvme/target/trace.c index bff454d46255..6ee1f3db81d0 100644 --- a/drivers/nvme/target/trace.c +++ b/drivers/nvme/target/trace.c @@ -211,7 +211,7 @@ const char *nvmet_trace_disk_name(struct trace_seq *p, char *name) return ret; } -const char *nvmet_trace_ctrl_name(struct trace_seq *p, struct nvmet_ctrl *ctrl) +const char *nvmet_trace_ctrl_id(struct trace_seq *p, u16 ctrl_id) { const char *ret = trace_seq_buffer_ptr(p); @@ -224,8 +224,8 @@ const char *nvmet_trace_ctrl_name(struct trace_seq *p, struct nvmet_ctrl *ctrl) * If we can know the extra data of the connect command in this stage, * we can update this print statement later. */ - if (ctrl) - trace_seq_printf(p, "%d", ctrl->cntlid); + if (ctrl_id) + trace_seq_printf(p, "%d", ctrl_id); else trace_seq_printf(p, "_"); trace_seq_putc(p, 0); diff --git a/drivers/nvme/target/trace.h b/drivers/nvme/target/trace.h index 6109b3806b12..7f7ebf9558e5 100644 --- a/drivers/nvme/target/trace.h +++ b/drivers/nvme/target/trace.h @@ -32,18 +32,24 @@ const char *nvmet_trace_parse_fabrics_cmd(struct trace_seq *p, u8 fctype, nvmet_trace_parse_nvm_cmd(p, opcode, cdw10) : \ nvmet_trace_parse_admin_cmd(p, opcode, cdw10))) -const char *nvmet_trace_ctrl_name(struct trace_seq *p, struct nvmet_ctrl *ctrl); -#define __print_ctrl_name(ctrl) \ - nvmet_trace_ctrl_name(p, ctrl) +const char *nvmet_trace_ctrl_id(struct trace_seq *p, u16 ctrl_id); +#define __print_ctrl_id(ctrl_id) \ + nvmet_trace_ctrl_id(p, ctrl_id) const char *nvmet_trace_disk_name(struct trace_seq *p, char *name); #define __print_disk_name(name) \ nvmet_trace_disk_name(p, name) #ifndef TRACE_HEADER_MULTI_READ -static inline struct nvmet_ctrl *nvmet_req_to_ctrl(struct nvmet_req *req) +static inline u16 nvmet_req_to_ctrl_id(struct nvmet_req *req) { - return req->sq->ctrl; + /* + * The queue and controller pointers are not valid until an association + * has been established. + */ + if (!req->sq || !req->sq->ctrl) + return 0; + return req->sq->ctrl->cntlid; } static inline void __assign_req_name(char *name, struct nvmet_req *req) @@ -53,8 +59,7 @@ static inline void __assign_req_name(char *name, struct nvmet_req *req) return; } - strncpy(name, req->ns->device_path, - min_t(size_t, DISK_NAME_LEN, strlen(req->ns->device_path))); + strscpy_pad(name, req->ns->device_path, DISK_NAME_LEN); } #endif @@ -63,7 +68,7 @@ TRACE_EVENT(nvmet_req_init, TP_ARGS(req, cmd), TP_STRUCT__entry( __field(struct nvme_command *, cmd) - __field(struct nvmet_ctrl *, ctrl) + __field(u16, ctrl_id) __array(char, disk, DISK_NAME_LEN) __field(int, qid) __field(u16, cid) @@ -76,7 +81,7 @@ TRACE_EVENT(nvmet_req_init, ), TP_fast_assign( __entry->cmd = cmd; - __entry->ctrl = nvmet_req_to_ctrl(req); + __entry->ctrl_id = nvmet_req_to_ctrl_id(req); __assign_req_name(__entry->disk, req); __entry->qid = req->sq->qid; __entry->cid = cmd->common.command_id; @@ -85,12 +90,12 @@ TRACE_EVENT(nvmet_req_init, __entry->flags = cmd->common.flags; __entry->nsid = le32_to_cpu(cmd->common.nsid); __entry->metadata = le64_to_cpu(cmd->common.metadata); - memcpy(__entry->cdw10, &cmd->common.cdw10, + memcpy(__entry->cdw10, &cmd->common.cdws, sizeof(__entry->cdw10)); ), TP_printk("nvmet%s: %sqid=%d, cmdid=%u, nsid=%u, flags=%#x, " "meta=%#llx, cmd=(%s, %s)", - __print_ctrl_name(__entry->ctrl), + __print_ctrl_id(__entry->ctrl_id), __print_disk_name(__entry->disk), __entry->qid, __entry->cid, __entry->nsid, __entry->flags, __entry->metadata, @@ -104,7 +109,7 @@ TRACE_EVENT(nvmet_req_complete, TP_PROTO(struct nvmet_req *req), TP_ARGS(req), TP_STRUCT__entry( - __field(struct nvmet_ctrl *, ctrl) + __field(u16, ctrl_id) __array(char, disk, DISK_NAME_LEN) __field(int, qid) __field(int, cid) @@ -112,7 +117,7 @@ TRACE_EVENT(nvmet_req_complete, __field(u16, status) ), TP_fast_assign( - __entry->ctrl = nvmet_req_to_ctrl(req); + __entry->ctrl_id = nvmet_req_to_ctrl_id(req); __entry->qid = req->cq->qid; __entry->cid = req->cqe->command_id; __entry->result = le64_to_cpu(req->cqe->result.u64); @@ -120,7 +125,7 @@ TRACE_EVENT(nvmet_req_complete, __assign_req_name(__entry->disk, req); ), TP_printk("nvmet%s: %sqid=%d, cmdid=%u, res=%#llx, status=%#x", - __print_ctrl_name(__entry->ctrl), + __print_ctrl_id(__entry->ctrl_id), __print_disk_name(__entry->disk), __entry->qid, __entry->cid, __entry->result, __entry->status) diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 5bc9c4874fe3..283134498fbc 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig NVMEM bool "NVMEM Support" + imply NVMEM_LAYOUTS help Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES... diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 423baf089515..cdd01fbf1313 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -5,6 +5,8 @@ obj-$(CONFIG_NVMEM) += nvmem_core.o nvmem_core-y := core.o +obj-$(CONFIG_NVMEM_LAYOUTS) += nvmem_layouts.o +nvmem_layouts-y := layouts.o obj-y += layouts/ # Devices diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 608b352a7d91..980123fb4dde 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -19,29 +19,7 @@ #include <linux/of.h> #include <linux/slab.h> -struct nvmem_device { - struct module *owner; - struct device dev; - int stride; - int word_size; - int id; - struct kref refcnt; - size_t size; - bool read_only; - bool root_only; - int flags; - enum nvmem_type type; - struct bin_attribute eeprom; - struct device *base_dev; - struct list_head cells; - const struct nvmem_keepout *keepout; - unsigned int nkeepout; - nvmem_reg_read_t reg_read; - nvmem_reg_write_t reg_write; - struct gpio_desc *wp_gpio; - struct nvmem_layout *layout; - void *priv; -}; +#include "internals.h" #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev) @@ -77,9 +55,6 @@ static LIST_HEAD(nvmem_lookup_list); static BLOCKING_NOTIFIER_HEAD(nvmem_notifier); -static DEFINE_SPINLOCK(nvmem_layout_lock); -static LIST_HEAD(nvmem_layouts); - static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, void *val, size_t bytes) { @@ -324,6 +299,43 @@ static umode_t nvmem_bin_attr_is_visible(struct kobject *kobj, return nvmem_bin_attr_get_umode(nvmem); } +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, + loff_t pos, size_t count) +{ + struct nvmem_cell_entry *entry; + struct nvmem_cell *cell = NULL; + size_t cell_sz, read_len; + void *content; + + entry = attr->private; + cell = nvmem_create_cell(entry, entry->name, 0); + if (IS_ERR(cell)) + return PTR_ERR(cell); + + if (!cell) + return -EINVAL; + + content = nvmem_cell_read(cell, &cell_sz); + if (IS_ERR(content)) { + read_len = PTR_ERR(content); + goto destroy_cell; + } + + read_len = min_t(unsigned int, cell_sz - pos, count); + memcpy(buf, content + pos, read_len); + kfree(content); + +destroy_cell: + kfree_const(cell->id); + kfree(cell); + + return read_len; +} + /* default read/write permissions */ static struct bin_attribute bin_attr_rw_nvmem = { .attr = { @@ -345,11 +357,21 @@ static const struct attribute_group nvmem_bin_group = { .is_bin_visible = nvmem_bin_attr_is_visible, }; +/* Cell attributes will be dynamically allocated */ +static struct attribute_group nvmem_cells_group = { + .name = "cells", +}; + static const struct attribute_group *nvmem_dev_groups[] = { &nvmem_bin_group, NULL, }; +static const struct attribute_group *nvmem_cells_groups[] = { + &nvmem_cells_group, + NULL, +}; + static struct bin_attribute bin_attr_nvmem_eeprom_compat = { .attr = { .name = "eeprom", @@ -405,6 +427,68 @@ static void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem, device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); } +static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) +{ + struct bin_attribute **cells_attrs, *attrs; + struct nvmem_cell_entry *entry; + unsigned int ncells = 0, i = 0; + int ret = 0; + + mutex_lock(&nvmem_mutex); + + if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) { + nvmem_cells_group.bin_attrs = NULL; + goto unlock_mutex; + } + + /* Allocate an array of attributes with a sentinel */ + ncells = list_count_nodes(&nvmem->cells); + cells_attrs = devm_kcalloc(&nvmem->dev, ncells + 1, + sizeof(struct bin_attribute *), GFP_KERNEL); + if (!cells_attrs) { + ret = -ENOMEM; + goto unlock_mutex; + } + + attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL); + if (!attrs) { + ret = -ENOMEM; + goto unlock_mutex; + } + + /* Initialize each attribute to take the name and size of the cell */ + list_for_each_entry(entry, &nvmem->cells, node) { + sysfs_bin_attr_init(&attrs[i]); + attrs[i].attr.name = devm_kasprintf(&nvmem->dev, GFP_KERNEL, + "%s@%x", entry->name, + entry->offset); + attrs[i].attr.mode = 0444; + attrs[i].size = entry->bytes; + attrs[i].read = &nvmem_cell_attr_read; + attrs[i].private = entry; + if (!attrs[i].attr.name) { + ret = -ENOMEM; + goto unlock_mutex; + } + + cells_attrs[i] = &attrs[i]; + i++; + } + + nvmem_cells_group.bin_attrs = cells_attrs; + + ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups); + if (ret) + goto unlock_mutex; + + nvmem->sysfs_cells_populated = true; + +unlock_mutex: + mutex_unlock(&nvmem_mutex); + + return ret; +} + #else /* CONFIG_NVMEM_SYSFS */ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, @@ -697,7 +781,6 @@ static int nvmem_validate_keepouts(struct nvmem_device *nvmem) static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np) { - struct nvmem_layout *layout = nvmem->layout; struct device *dev = &nvmem->dev; struct device_node *child; const __be32 *addr; @@ -727,8 +810,8 @@ static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_nod info.np = of_node_get(child); - if (layout && layout->fixup_cell_info) - layout->fixup_cell_info(nvmem, layout, &info); + if (nvmem->fixup_dt_cell_info) + nvmem->fixup_dt_cell_info(nvmem, &info); ret = nvmem_add_one_cell(nvmem, &info); kfree(info.name); @@ -763,117 +846,35 @@ static int nvmem_add_cells_from_fixed_layout(struct nvmem_device *nvmem) return err; } -int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner) -{ - layout->owner = owner; - - spin_lock(&nvmem_layout_lock); - list_add(&layout->node, &nvmem_layouts); - spin_unlock(&nvmem_layout_lock); - - blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_ADD, layout); - - return 0; -} -EXPORT_SYMBOL_GPL(__nvmem_layout_register); - -void nvmem_layout_unregister(struct nvmem_layout *layout) -{ - blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_REMOVE, layout); - - spin_lock(&nvmem_layout_lock); - list_del(&layout->node); - spin_unlock(&nvmem_layout_lock); -} -EXPORT_SYMBOL_GPL(nvmem_layout_unregister); - -static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem) +int nvmem_layout_register(struct nvmem_layout *layout) { - struct device_node *layout_np; - struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER); - - layout_np = of_nvmem_layout_get_container(nvmem); - if (!layout_np) - return NULL; - - /* Fixed layouts don't have a matching driver */ - if (of_device_is_compatible(layout_np, "fixed-layout")) { - of_node_put(layout_np); - return NULL; - } - - /* - * In case the nvmem device was built-in while the layout was built as a - * module, we shall manually request the layout driver loading otherwise - * we'll never have any match. - */ - of_request_module(layout_np); - - spin_lock(&nvmem_layout_lock); - - list_for_each_entry(l, &nvmem_layouts, node) { - if (of_match_node(l->of_match_table, layout_np)) { - if (try_module_get(l->owner)) - layout = l; - - break; - } - } - - spin_unlock(&nvmem_layout_lock); - of_node_put(layout_np); - - return layout; -} + int ret; -static void nvmem_layout_put(struct nvmem_layout *layout) -{ - if (layout) - module_put(layout->owner); -} + if (!layout->add_cells) + return -EINVAL; -static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem) -{ - struct nvmem_layout *layout = nvmem->layout; - int ret; + /* Populate the cells */ + ret = layout->add_cells(layout); + if (ret) + return ret; - if (layout && layout->add_cells) { - ret = layout->add_cells(&nvmem->dev, nvmem, layout); - if (ret) - return ret; +#ifdef CONFIG_NVMEM_SYSFS + ret = nvmem_populate_sysfs_cells(layout->nvmem); + if (ret) { + nvmem_device_remove_all_cells(layout->nvmem); + return ret; } +#endif return 0; } +EXPORT_SYMBOL_GPL(nvmem_layout_register); -#if IS_ENABLED(CONFIG_OF) -/** - * of_nvmem_layout_get_container() - Get OF node to layout container. - * - * @nvmem: nvmem device. - * - * Return: a node pointer with refcount incremented or NULL if no - * container exists. Use of_node_put() on it when done. - */ -struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) -{ - return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout"); -} -EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container); -#endif - -const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem, - struct nvmem_layout *layout) +void nvmem_layout_unregister(struct nvmem_layout *layout) { - struct device_node __maybe_unused *layout_np; - const struct of_device_id *match; - - layout_np = of_nvmem_layout_get_container(nvmem); - match = of_match_node(layout->of_match_table, layout_np); - - return match ? match->data : NULL; + /* Keep the API even with an empty stub in case we need it later */ } -EXPORT_SYMBOL_GPL(nvmem_layout_get_match_data); +EXPORT_SYMBOL_GPL(nvmem_layout_unregister); /** * nvmem_register() - Register a nvmem device for given nvmem_config. @@ -925,6 +926,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) kref_init(&nvmem->refcnt); INIT_LIST_HEAD(&nvmem->cells); + nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info; nvmem->owner = config->owner; if (!nvmem->owner && config->dev->driver) @@ -980,19 +982,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_put_device; } - /* - * If the driver supplied a layout by config->layout, the module - * pointer will be NULL and nvmem_layout_put() will be a noop. - */ - nvmem->layout = config->layout ?: nvmem_layout_get(nvmem); - if (IS_ERR(nvmem->layout)) { - rval = PTR_ERR(nvmem->layout); - nvmem->layout = NULL; - - if (rval == -EPROBE_DEFER) - goto err_teardown_compat; - } - if (config->cells) { rval = nvmem_add_cells(nvmem, config->cells, config->ncells); if (rval) @@ -1013,24 +1002,34 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (rval) goto err_remove_cells; - rval = nvmem_add_cells_from_layout(nvmem); - if (rval) - goto err_remove_cells; - dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); rval = device_add(&nvmem->dev); if (rval) goto err_remove_cells; + rval = nvmem_populate_layout(nvmem); + if (rval) + goto err_remove_dev; + +#ifdef CONFIG_NVMEM_SYSFS + rval = nvmem_populate_sysfs_cells(nvmem); + if (rval) + goto err_destroy_layout; +#endif + blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem); return nvmem; +#ifdef CONFIG_NVMEM_SYSFS +err_destroy_layout: + nvmem_destroy_layout(nvmem); +#endif +err_remove_dev: + device_del(&nvmem->dev); err_remove_cells: nvmem_device_remove_all_cells(nvmem); - nvmem_layout_put(nvmem->layout); -err_teardown_compat: if (config->compat) nvmem_sysfs_remove_compat(nvmem, config); err_put_device: @@ -1052,7 +1051,7 @@ static void nvmem_device_release(struct kref *kref) device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); nvmem_device_remove_all_cells(nvmem); - nvmem_layout_put(nvmem->layout); + nvmem_destroy_layout(nvmem); device_unregister(&nvmem->dev); } @@ -1354,6 +1353,12 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) return cell; } +static void nvmem_layout_module_put(struct nvmem_device *nvmem) +{ + if (nvmem->layout && nvmem->layout->dev.driver) + module_put(nvmem->layout->dev.driver->owner); +} + #if IS_ENABLED(CONFIG_OF) static struct nvmem_cell_entry * nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np) @@ -1372,6 +1377,18 @@ nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np return cell; } +static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem) +{ + if (!nvmem->layout) + return 0; + + if (!nvmem->layout->dev.driver || + !try_module_get(nvmem->layout->dev.driver->owner)) + return -EPROBE_DEFER; + + return 0; +} + /** * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id * @@ -1434,16 +1451,29 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) return ERR_CAST(nvmem); } + ret = nvmem_layout_module_get_optional(nvmem); + if (ret) { + of_node_put(cell_np); + __nvmem_device_put(nvmem); + return ERR_PTR(ret); + } + cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np); of_node_put(cell_np); if (!cell_entry) { __nvmem_device_put(nvmem); - return ERR_PTR(-ENOENT); + nvmem_layout_module_put(nvmem); + if (nvmem->layout) + return ERR_PTR(-EPROBE_DEFER); + else + return ERR_PTR(-ENOENT); } cell = nvmem_create_cell(cell_entry, id, cell_index); - if (IS_ERR(cell)) + if (IS_ERR(cell)) { __nvmem_device_put(nvmem); + nvmem_layout_module_put(nvmem); + } return cell; } @@ -1557,6 +1587,7 @@ void nvmem_cell_put(struct nvmem_cell *cell) kfree(cell); __nvmem_device_put(nvmem); + nvmem_layout_module_put(nvmem); } EXPORT_SYMBOL_GPL(nvmem_cell_put); @@ -2132,13 +2163,37 @@ const char *nvmem_dev_name(struct nvmem_device *nvmem) } EXPORT_SYMBOL_GPL(nvmem_dev_name); +/** + * nvmem_dev_size() - Get the size of a given nvmem device. + * + * @nvmem: nvmem device. + * + * Return: size of the nvmem device. + */ +size_t nvmem_dev_size(struct nvmem_device *nvmem) +{ + return nvmem->size; +} +EXPORT_SYMBOL_GPL(nvmem_dev_size); + static int __init nvmem_init(void) { - return bus_register(&nvmem_bus_type); + int ret; + + ret = bus_register(&nvmem_bus_type); + if (ret) + return ret; + + ret = nvmem_layout_bus_register(); + if (ret) + bus_unregister(&nvmem_bus_type); + + return ret; } static void __exit nvmem_exit(void) { + nvmem_layout_bus_unregister(); bus_unregister(&nvmem_bus_type); } diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index f1e202efaa49..79dd4fda0329 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -583,17 +583,12 @@ static const struct of_device_id imx_ocotp_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); -static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem, - struct nvmem_layout *layout, - struct nvmem_cell_info *cell) +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 struct nvmem_layout imx_ocotp_layout = { - .fixup_cell_info = imx_ocotp_fixup_cell_info, -}; - static int imx_ocotp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -619,7 +614,7 @@ static int imx_ocotp_probe(struct platform_device *pdev) imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; imx_ocotp_nvmem_config.dev = dev; imx_ocotp_nvmem_config.priv = priv; - imx_ocotp_nvmem_config.layout = &imx_ocotp_layout; + imx_ocotp_nvmem_config.fixup_dt_cell_info = &imx_ocotp_fixup_dt_cell_info; priv->config = &imx_ocotp_nvmem_config; diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h new file mode 100644 index 000000000000..18fed57270e5 --- /dev/null +++ b/drivers/nvmem/internals.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _LINUX_NVMEM_INTERNALS_H +#define _LINUX_NVMEM_INTERNALS_H + +#include <linux/device.h> +#include <linux/nvmem-consumer.h> +#include <linux/nvmem-provider.h> + +struct nvmem_device { + struct module *owner; + struct device dev; + struct list_head node; + int stride; + int word_size; + int id; + struct kref refcnt; + size_t size; + bool read_only; + bool root_only; + int flags; + enum nvmem_type type; + struct bin_attribute eeprom; + struct device *base_dev; + struct list_head cells; + void (*fixup_dt_cell_info)(struct nvmem_device *nvmem, + struct nvmem_cell_info *cell); + const struct nvmem_keepout *keepout; + unsigned int nkeepout; + nvmem_reg_read_t reg_read; + nvmem_reg_write_t reg_write; + struct gpio_desc *wp_gpio; + struct nvmem_layout *layout; + void *priv; + bool sysfs_cells_populated; +}; + +#if IS_ENABLED(CONFIG_OF) +int nvmem_layout_bus_register(void); +void nvmem_layout_bus_unregister(void); +int nvmem_populate_layout(struct nvmem_device *nvmem); +void nvmem_destroy_layout(struct nvmem_device *nvmem); +#else /* CONFIG_OF */ +static inline int nvmem_layout_bus_register(void) +{ + return 0; +} + +static inline void nvmem_layout_bus_unregister(void) {} + +static inline int nvmem_populate_layout(struct nvmem_device *nvmem) +{ + return 0; +} + +static inline void nvmem_destroy_layout(struct nvmem_device *nvmem) { } +#endif /* CONFIG_OF */ + +#endif /* ifndef _LINUX_NVMEM_INTERNALS_H */ diff --git a/drivers/nvmem/layouts.c b/drivers/nvmem/layouts.c new file mode 100644 index 000000000000..6a6aa58369ff --- /dev/null +++ b/drivers/nvmem/layouts.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NVMEM layout bus handling + * + * Copyright (C) 2023 Bootlin + * Author: Miquel Raynal <miquel.raynal@bootlin.com + */ + +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/nvmem-consumer.h> +#include <linux/nvmem-provider.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> + +#include "internals.h" + +#define to_nvmem_layout_driver(drv) \ + (container_of((drv), struct nvmem_layout_driver, driver)) +#define to_nvmem_layout_device(_dev) \ + container_of((_dev), struct nvmem_layout, dev) + +static int nvmem_layout_bus_match(struct device *dev, struct device_driver *drv) +{ + return of_driver_match_device(dev, drv); +} + +static int nvmem_layout_bus_probe(struct device *dev) +{ + struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver); + struct nvmem_layout *layout = to_nvmem_layout_device(dev); + + if (!drv->probe || !drv->remove) + return -EINVAL; + + return drv->probe(layout); +} + +static void nvmem_layout_bus_remove(struct device *dev) +{ + struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver); + struct nvmem_layout *layout = to_nvmem_layout_device(dev); + + return drv->remove(layout); +} + +static struct bus_type nvmem_layout_bus_type = { + .name = "nvmem-layout", + .match = nvmem_layout_bus_match, + .probe = nvmem_layout_bus_probe, + .remove = nvmem_layout_bus_remove, +}; + +int nvmem_layout_driver_register(struct nvmem_layout_driver *drv) +{ + drv->driver.bus = &nvmem_layout_bus_type; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(nvmem_layout_driver_register); + +void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(nvmem_layout_driver_unregister); + +static void nvmem_layout_release_device(struct device *dev) +{ + struct nvmem_layout *layout = to_nvmem_layout_device(dev); + + of_node_put(layout->dev.of_node); + kfree(layout); +} + +static int nvmem_layout_create_device(struct nvmem_device *nvmem, + struct device_node *np) +{ + struct nvmem_layout *layout; + struct device *dev; + int ret; + + layout = kzalloc(sizeof(*layout), GFP_KERNEL); + if (!layout) + return -ENOMEM; + + /* Create a bidirectional link */ + layout->nvmem = nvmem; + nvmem->layout = layout; + + /* Device model registration */ + dev = &layout->dev; + device_initialize(dev); + dev->parent = &nvmem->dev; + dev->bus = &nvmem_layout_bus_type; + dev->release = nvmem_layout_release_device; + dev->coherent_dma_mask = DMA_BIT_MASK(32); + dev->dma_mask = &dev->coherent_dma_mask; + device_set_node(dev, of_fwnode_handle(of_node_get(np))); + of_device_make_bus_id(dev); + of_msi_configure(dev, dev->of_node); + + ret = device_add(dev); + if (ret) { + put_device(dev); + return ret; + } + + return 0; +} + +static const struct of_device_id of_nvmem_layout_skip_table[] = { + { .compatible = "fixed-layout", }, + {} +}; + +static int nvmem_layout_bus_populate(struct nvmem_device *nvmem, + struct device_node *layout_dn) +{ + int ret; + + /* Make sure it has a compatible property */ + if (!of_get_property(layout_dn, "compatible", NULL)) { + pr_debug("%s() - skipping %pOF, no compatible prop\n", + __func__, layout_dn); + return 0; + } + + /* Fixed layouts are parsed manually somewhere else for now */ + if (of_match_node(of_nvmem_layout_skip_table, layout_dn)) { + pr_debug("%s() - skipping %pOF node\n", __func__, layout_dn); + return 0; + } + + if (of_node_check_flag(layout_dn, OF_POPULATED_BUS)) { + pr_debug("%s() - skipping %pOF, already populated\n", + __func__, layout_dn); + + return 0; + } + + /* NVMEM layout buses expect only a single device representing the layout */ + ret = nvmem_layout_create_device(nvmem, layout_dn); + if (ret) + return ret; + + of_node_set_flag(layout_dn, OF_POPULATED_BUS); + + return 0; +} + +struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) +{ + return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout"); +} +EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container); + +/* + * Returns the number of devices populated, 0 if the operation was not relevant + * for this nvmem device, an error code otherwise. + */ +int nvmem_populate_layout(struct nvmem_device *nvmem) +{ + struct device_node *layout_dn; + int ret; + + layout_dn = of_nvmem_layout_get_container(nvmem); + if (!layout_dn) + return 0; + + /* Populate the layout device */ + device_links_supplier_sync_state_pause(); + ret = nvmem_layout_bus_populate(nvmem, layout_dn); + device_links_supplier_sync_state_resume(); + + of_node_put(layout_dn); + return ret; +} + +void nvmem_destroy_layout(struct nvmem_device *nvmem) +{ + struct device *dev; + + if (!nvmem->layout) + return; + + dev = &nvmem->layout->dev; + of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); + device_unregister(dev); +} + +int nvmem_layout_bus_register(void) +{ + return bus_register(&nvmem_layout_bus_type); +} + +void nvmem_layout_bus_unregister(void) +{ + bus_unregister(&nvmem_layout_bus_type); +} diff --git a/drivers/nvmem/layouts/Kconfig b/drivers/nvmem/layouts/Kconfig index 7ff1ee1c1f05..9c6e672fc350 100644 --- a/drivers/nvmem/layouts/Kconfig +++ b/drivers/nvmem/layouts/Kconfig @@ -1,5 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 +config NVMEM_LAYOUTS + bool + depends on OF + +if NVMEM_LAYOUTS + menu "Layout Types" config NVMEM_LAYOUT_SL28_VPD @@ -21,3 +27,5 @@ config NVMEM_LAYOUT_ONIE_TLV If unsure, say N. endmenu + +endif diff --git a/drivers/nvmem/layouts/onie-tlv.c b/drivers/nvmem/layouts/onie-tlv.c index 59fc87ccfcff..9d2ad5f2dc10 100644 --- a/drivers/nvmem/layouts/onie-tlv.c +++ b/drivers/nvmem/layouts/onie-tlv.c @@ -182,9 +182,10 @@ static bool onie_tlv_crc_is_valid(struct device *dev, size_t table_len, u8 *tabl return true; } -static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem, - struct nvmem_layout *layout) +static int onie_tlv_parse_table(struct nvmem_layout *layout) { + struct nvmem_device *nvmem = layout->nvmem; + struct device *dev = &layout->dev; struct onie_tlv_hdr hdr; size_t table_len, data_len, hdr_len; u8 *table, *data; @@ -226,16 +227,32 @@ static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem, return 0; } +static int onie_tlv_probe(struct nvmem_layout *layout) +{ + layout->add_cells = onie_tlv_parse_table; + + return nvmem_layout_register(layout); +} + +static void onie_tlv_remove(struct nvmem_layout *layout) +{ + nvmem_layout_unregister(layout); +} + static const struct of_device_id onie_tlv_of_match_table[] = { { .compatible = "onie,tlv-layout", }, {}, }; MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table); -static struct nvmem_layout onie_tlv_layout = { - .name = "ONIE tlv layout", - .of_match_table = onie_tlv_of_match_table, - .add_cells = onie_tlv_parse_table, +static struct nvmem_layout_driver onie_tlv_layout = { + .driver = { + .owner = THIS_MODULE, + .name = "onie-tlv-layout", + .of_match_table = onie_tlv_of_match_table, + }, + .probe = onie_tlv_probe, + .remove = onie_tlv_remove, }; module_nvmem_layout_driver(onie_tlv_layout); diff --git a/drivers/nvmem/layouts/sl28vpd.c b/drivers/nvmem/layouts/sl28vpd.c index 05671371f631..53fa50f17dca 100644 --- a/drivers/nvmem/layouts/sl28vpd.c +++ b/drivers/nvmem/layouts/sl28vpd.c @@ -80,9 +80,10 @@ static int sl28vpd_v1_check_crc(struct device *dev, struct nvmem_device *nvmem) return 0; } -static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem, - struct nvmem_layout *layout) +static int sl28vpd_add_cells(struct nvmem_layout *layout) { + struct nvmem_device *nvmem = layout->nvmem; + struct device *dev = &layout->dev; const struct nvmem_cell_info *pinfo; struct nvmem_cell_info info = {0}; struct device_node *layout_np; @@ -135,16 +136,32 @@ static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem, return 0; } +static int sl28vpd_probe(struct nvmem_layout *layout) +{ + layout->add_cells = sl28vpd_add_cells; + + return nvmem_layout_register(layout); +} + +static void sl28vpd_remove(struct nvmem_layout *layout) +{ + nvmem_layout_unregister(layout); +} + static const struct of_device_id sl28vpd_of_match_table[] = { { .compatible = "kontron,sl28-vpd" }, {}, }; MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table); -static struct nvmem_layout sl28vpd_layout = { - .name = "sl28-vpd", - .of_match_table = sl28vpd_of_match_table, - .add_cells = sl28vpd_add_cells, +static struct nvmem_layout_driver sl28vpd_layout = { + .driver = { + .owner = THIS_MODULE, + .name = "kontron-sl28vpd-layout", + .of_match_table = sl28vpd_of_match_table, + }, + .probe = sl28vpd_probe, + .remove = sl28vpd_remove, }; module_nvmem_layout_driver(sl28vpd_layout); diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c index 87c94686cfd2..84f05b40a411 100644 --- a/drivers/nvmem/mtk-efuse.c +++ b/drivers/nvmem/mtk-efuse.c @@ -45,9 +45,8 @@ static int mtk_efuse_gpu_speedbin_pp(void *context, const char *id, int index, return 0; } -static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem, - struct nvmem_layout *layout, - struct nvmem_cell_info *cell) +static void mtk_efuse_fixup_dt_cell_info(struct nvmem_device *nvmem, + struct nvmem_cell_info *cell) { size_t sz = strlen(cell->name); @@ -61,10 +60,6 @@ static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem, cell->read_post_process = mtk_efuse_gpu_speedbin_pp; } -static struct nvmem_layout mtk_efuse_layout = { - .fixup_cell_info = mtk_efuse_fixup_cell_info, -}; - static int mtk_efuse_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -91,7 +86,7 @@ static int mtk_efuse_probe(struct platform_device *pdev) econfig.priv = priv; econfig.dev = dev; if (pdata->uses_post_processing) - econfig.layout = &mtk_efuse_layout; + econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info; nvmem = devm_nvmem_register(dev, &econfig); return PTR_ERR_OR_ZERO(nvmem); diff --git a/drivers/nvmem/stm32-romem.c b/drivers/nvmem/stm32-romem.c index 8a553b1799a8..82879b1c9eb9 100644 --- a/drivers/nvmem/stm32-romem.c +++ b/drivers/nvmem/stm32-romem.c @@ -269,6 +269,19 @@ static const struct stm32_romem_cfg stm32mp13_bsec_cfg = { .ta = true, }; +/* + * STM32MP25 BSEC OTP: 3 regions of 32-bits data words + * lower OTP (OTP0 to OTP127), bitwise (1-bit) programmable + * mid OTP (OTP128 to OTP255), bulk (32-bit) programmable + * upper OTP (OTP256 to OTP383), bulk (32-bit) programmable + * but no access to HWKEY and ECIES key: limited at OTP367 + */ +static const struct stm32_romem_cfg stm32mp25_bsec_cfg = { + .size = 368 * 4, + .lower = 127, + .ta = true, +}; + static const struct of_device_id stm32_romem_of_match[] __maybe_unused = { { .compatible = "st,stm32f4-otp", }, { .compatible = "st,stm32mp15-bsec", @@ -276,6 +289,9 @@ static const struct of_device_id stm32_romem_of_match[] __maybe_unused = { }, { .compatible = "st,stm32mp13-bsec", .data = (void *)&stm32mp13_bsec_cfg, + }, { + .compatible = "st,stm32mp25-bsec", + .data = (void *)&stm32mp25_bsec_cfg, }, { /* sentinel */ }, }; diff --git a/drivers/nvmem/u-boot-env.c b/drivers/nvmem/u-boot-env.c index c4ae94af4af7..befbab156cda 100644 --- a/drivers/nvmem/u-boot-env.c +++ b/drivers/nvmem/u-boot-env.c @@ -23,13 +23,10 @@ enum u_boot_env_format { struct u_boot_env { struct device *dev; + struct nvmem_device *nvmem; enum u_boot_env_format format; struct mtd_info *mtd; - - /* Cells */ - struct nvmem_cell_info *cells; - int ncells; }; struct u_boot_env_image_single { @@ -94,70 +91,71 @@ static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, i static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf, size_t data_offset, size_t data_len) { + struct nvmem_device *nvmem = priv->nvmem; struct device *dev = priv->dev; char *data = buf + data_offset; char *var, *value, *eq; - int idx; - - priv->ncells = 0; - for (var = data; var < data + data_len && *var; var += strlen(var) + 1) - priv->ncells++; - - priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL); - if (!priv->cells) - return -ENOMEM; - for (var = data, idx = 0; + for (var = data; var < data + data_len && *var; - var = value + strlen(value) + 1, idx++) { + var = value + strlen(value) + 1) { + struct nvmem_cell_info info = {}; + eq = strchr(var, '='); if (!eq) break; *eq = '\0'; value = eq + 1; - priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL); - if (!priv->cells[idx].name) + info.name = devm_kstrdup(dev, var, GFP_KERNEL); + if (!info.name) return -ENOMEM; - priv->cells[idx].offset = data_offset + value - data; - priv->cells[idx].bytes = strlen(value); - priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name); + info.offset = data_offset + value - data; + info.bytes = strlen(value); + info.np = of_get_child_by_name(dev->of_node, info.name); if (!strcmp(var, "ethaddr")) { - priv->cells[idx].raw_len = strlen(value); - priv->cells[idx].bytes = ETH_ALEN; - priv->cells[idx].read_post_process = u_boot_env_read_post_process_ethaddr; + info.raw_len = strlen(value); + info.bytes = ETH_ALEN; + info.read_post_process = u_boot_env_read_post_process_ethaddr; } - } - if (WARN_ON(idx != priv->ncells)) - priv->ncells = idx; + nvmem_add_one_cell(nvmem, &info); + } return 0; } static int u_boot_env_parse(struct u_boot_env *priv) { + struct nvmem_device *nvmem = priv->nvmem; struct device *dev = priv->dev; size_t crc32_data_offset; size_t crc32_data_len; size_t crc32_offset; + __le32 *crc32_addr; size_t data_offset; size_t data_len; + size_t dev_size; uint32_t crc32; uint32_t calc; - size_t bytes; uint8_t *buf; + int bytes; int err; - buf = kcalloc(1, priv->mtd->size, GFP_KERNEL); + dev_size = nvmem_dev_size(nvmem); + + buf = kzalloc(dev_size, GFP_KERNEL); if (!buf) { err = -ENOMEM; goto err_out; } - err = mtd_read(priv->mtd, 0, priv->mtd->size, &bytes, buf); - if ((err && !mtd_is_bitflip(err)) || bytes != priv->mtd->size) { - dev_err(dev, "Failed to read from mtd: %d\n", err); + bytes = nvmem_device_read(nvmem, 0, dev_size, buf); + if (bytes < 0) { + err = bytes; + goto err_kfree; + } else if (bytes != dev_size) { + err = -EIO; goto err_kfree; } @@ -178,9 +176,10 @@ static int u_boot_env_parse(struct u_boot_env *priv) data_offset = offsetof(struct u_boot_env_image_broadcom, data); break; } - crc32 = le32_to_cpu(*(__le32 *)(buf + crc32_offset)); - crc32_data_len = priv->mtd->size - crc32_data_offset; - data_len = priv->mtd->size - data_offset; + crc32_addr = (__le32 *)(buf + crc32_offset); + crc32 = le32_to_cpu(*crc32_addr); + crc32_data_len = dev_size - crc32_data_offset; + data_len = dev_size - data_offset; calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L; if (calc != crc32) { @@ -189,10 +188,8 @@ static int u_boot_env_parse(struct u_boot_env *priv) goto err_kfree; } - buf[priv->mtd->size - 1] = '\0'; + buf[dev_size - 1] = '\0'; err = u_boot_env_add_cells(priv, buf, data_offset, data_len); - if (err) - dev_err(dev, "Failed to add cells: %d\n", err); err_kfree: kfree(buf); @@ -209,7 +206,6 @@ static int u_boot_env_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct u_boot_env *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -224,17 +220,15 @@ static int u_boot_env_probe(struct platform_device *pdev) return PTR_ERR(priv->mtd); } - err = u_boot_env_parse(priv); - if (err) - return err; - config.dev = dev; - config.cells = priv->cells; - config.ncells = priv->ncells; config.priv = priv; config.size = priv->mtd->size; - return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config)); + priv->nvmem = devm_nvmem_register(dev, &config); + if (IS_ERR(priv->nvmem)) + return PTR_ERR(priv->nvmem); + + return u_boot_env_parse(priv); } static const struct of_device_id u_boot_env_of_match_table[] = { diff --git a/drivers/of/base.c b/drivers/of/base.c index 8d93cb6ea9cd..b0ad8fc06e80 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1464,6 +1464,7 @@ int of_parse_phandle_with_args_map(const struct device_node *np, out_args->np = new; of_node_put(cur); cur = new; + new = NULL; } put: of_node_put(cur); diff --git a/drivers/of/device.c b/drivers/of/device.c index 1ca42ad9dd15..de89f9906375 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -93,12 +93,12 @@ of_dma_set_restricted_buffer(struct device *dev, struct device_node *np) int of_dma_configure_id(struct device *dev, struct device_node *np, bool force_dma, const u32 *id) { - const struct iommu_ops *iommu; const struct bus_dma_region *map = NULL; struct device_node *bus_np; u64 dma_start = 0; u64 mask, end, size = 0; bool coherent; + int iommu_ret; int ret; if (np == dev->of_node) @@ -181,21 +181,29 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, dev_dbg(dev, "device is%sdma coherent\n", coherent ? " " : " not "); - iommu = of_iommu_configure(dev, np, id); - if (PTR_ERR(iommu) == -EPROBE_DEFER) { + iommu_ret = of_iommu_configure(dev, np, id); + if (iommu_ret == -EPROBE_DEFER) { /* Don't touch range map if it wasn't set from a valid dma-ranges */ if (!ret) dev->dma_range_map = NULL; kfree(map); return -EPROBE_DEFER; - } + } else if (iommu_ret == -ENODEV) { + dev_dbg(dev, "device is not behind an iommu\n"); + } else if (iommu_ret) { + dev_err(dev, "iommu configuration for device failed with %pe\n", + ERR_PTR(iommu_ret)); - dev_dbg(dev, "device is%sbehind an iommu\n", - iommu ? " " : " not "); + /* + * Historically this routine doesn't fail driver probing + * due to errors in of_iommu_configure() + */ + } else + dev_dbg(dev, "device is behind an iommu\n"); - arch_setup_dma_ops(dev, dma_start, size, iommu, coherent); + arch_setup_dma_ops(dev, dma_start, size, coherent); - if (!iommu) + if (iommu_ret) of_dma_set_restricted_buffer(dev, np); return 0; @@ -304,3 +312,44 @@ int of_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env * return 0; } EXPORT_SYMBOL_GPL(of_device_uevent_modalias); + +/** + * of_device_make_bus_id - Use the device node data to assign a unique name + * @dev: pointer to device structure that is linked to a device tree node + * + * This routine will first try using the translated bus address to + * derive a unique name. If it cannot, then it will prepend names from + * parent nodes until a unique name can be derived. + */ +void of_device_make_bus_id(struct device *dev) +{ + struct device_node *node = dev->of_node; + const __be32 *reg; + u64 addr; + u32 mask; + + /* Construct the name, using parent nodes if necessary to ensure uniqueness */ + while (node->parent) { + /* + * If the address can be translated, then that is as much + * uniqueness as we need. Make it the first component and return + */ + reg = of_get_property(node, "reg", NULL); + if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { + if (!of_property_read_u32(node, "mask", &mask)) + dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn", + addr, ffs(mask) - 1, node, dev_name(dev)); + + else + dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn", + addr, node, dev_name(dev)); + return; + } + + /* format arguments only used if dev_name() resolves to NULL */ + dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", + kbasename(node->full_name), dev_name(dev)); + node = node->parent; + } +} +EXPORT_SYMBOL_GPL(of_device_make_bus_id); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index a9a292d6d59b..2ae7e9d24a64 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -964,7 +964,7 @@ out: return ret; } -/* +/** * of_overlay_fdt_apply() - Create and apply an overlay changeset * @overlay_fdt: pointer to overlay FDT * @overlay_fdt_size: number of bytes in @overlay_fdt diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 126d265aa7d8..b7708a06dc78 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -20,6 +20,7 @@ #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/sysfb.h> #include "of_private.h" @@ -98,46 +99,6 @@ static const struct of_device_id of_skipped_node_table[] = { */ /** - * of_device_make_bus_id - Use the device node data to assign a unique name - * @dev: pointer to device structure that is linked to a device tree node - * - * This routine will first try using the translated bus address to - * derive a unique name. If it cannot, then it will prepend names from - * parent nodes until a unique name can be derived. - */ -static void of_device_make_bus_id(struct device *dev) -{ - struct device_node *node = dev->of_node; - const __be32 *reg; - u64 addr; - u32 mask; - - /* Construct the name, using parent nodes if necessary to ensure uniqueness */ - while (node->parent) { - /* - * If the address can be translated, then that is as much - * uniqueness as we need. Make it the first component and return - */ - reg = of_get_property(node, "reg", NULL); - if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { - if (!of_property_read_u32(node, "mask", &mask)) - dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn", - addr, ffs(mask) - 1, node, dev_name(dev)); - - else - dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn", - addr, node, dev_name(dev)); - return; - } - - /* format arguments only used if dev_name() resolves to NULL */ - dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", - kbasename(node->full_name), dev_name(dev)); - node = node->parent; - } -} - -/** * of_device_alloc - Allocate and initialize an of_device * @np: device node to assign to device * @bus_id: Name to assign to the device. May be null to use default name. @@ -621,8 +582,21 @@ static int __init of_platform_default_populate_init(void) } node = of_get_compatible_child(of_chosen, "simple-framebuffer"); - of_platform_device_create(node, NULL, NULL); - of_node_put(node); + if (node) { + /* + * Since a "simple-framebuffer" device is already added + * here, disable the Generic System Framebuffers (sysfb) + * to prevent it from registering another device for the + * system framebuffer later (e.g: using the screen_info + * data that may had been filled as well). + * + * This can happen for example on DT systems that do EFI + * booting and may provide a GOP handle to the EFI stub. + */ + sysfb_disable(); + of_platform_device_create(node, NULL, NULL); + of_node_put(node); + } /* Populate everything else. */ of_platform_default_populate(NULL, NULL, NULL); @@ -668,7 +642,7 @@ EXPORT_SYMBOL_GPL(of_platform_device_destroy); * @parent: device which children will be removed * * Complementary to of_platform_populate(), this function removes children - * of the given device (and, recurrently, their children) that have been + * of the given device (and, recursively, their children) that have been * created from their respective device tree nodes (and only those, * leaving others - eg. manually created - unharmed). */ @@ -737,7 +711,7 @@ static int devm_of_platform_match(struct device *dev, void *res, void *data) * @dev: device that requested to depopulate from device tree data * * Complementary to devm_of_platform_populate(), this function removes children - * of the given device (and, recurrently, their children) that have been + * of the given device (and, recursively, their children) that have been * created from their respective device tree nodes (and only those, * leaving others - eg. manually created - unharmed). */ diff --git a/drivers/of/property.c b/drivers/of/property.c index afdaefbd03f6..641a40cf5cf3 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -441,6 +441,7 @@ int of_property_read_string(const struct device_node *np, const char *propname, const char **out_string) { const struct property *prop = of_find_property(np, propname, NULL); + if (!prop) return -EINVAL; if (!prop->length) @@ -1217,9 +1218,9 @@ static struct device_node *parse_##fname(struct device_node *np, \ * * @parse_prop: function name * parse_prop() finds the node corresponding to a supplier phandle - * @parse_prop.np: Pointer to device node holding supplier phandle property - * @parse_prop.prop_name: Name of property holding a phandle value - * @parse_prop.index: For properties holding a list of phandles, this is the + * parse_prop.np: Pointer to device node holding supplier phandle property + * parse_prop.prop_name: Name of property holding a phandle value + * parse_prop.index: For properties holding a list of phandles, this is the * index into the list * @optional: Describes whether a supplier is mandatory or not * @node_not_dev: The consumer node containing the property is never converted diff --git a/drivers/of/unittest-data/tests-phandle.dtsi b/drivers/of/unittest-data/tests-phandle.dtsi index d01f92f0f0db..554a996b2ef1 100644 --- a/drivers/of/unittest-data/tests-phandle.dtsi +++ b/drivers/of/unittest-data/tests-phandle.dtsi @@ -40,6 +40,13 @@ phandle-map-pass-thru = <0x0 0xf0>; }; + provider5: provider5 { + #phandle-cells = <2>; + phandle-map = <2 7 &provider4 2 3>; + phandle-map-mask = <0xff 0xf>; + phandle-map-pass-thru = <0x0 0xf0>; + }; + consumer-a { phandle-list = <&provider1 1>, <&provider2 2 0>, @@ -66,7 +73,8 @@ <&provider4 4 0x100>, <&provider4 0 0x61>, <&provider0>, - <&provider4 19 0x20>; + <&provider4 19 0x20>, + <&provider5 2 7>; phandle-list-bad-phandle = <12345678 0 0>; phandle-list-bad-args = <&provider2 1 0>, <&provider4 0>; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index e9e90e96600e..cfd60e35a899 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -456,6 +456,9 @@ static void __init of_unittest_parse_phandle_with_args(void) unittest(passed, "index %i - data error on node %pOF rc=%i\n", i, args.np, rc); + + if (rc == 0) + of_node_put(args.np); } /* Check for missing list property */ @@ -545,8 +548,9 @@ static void __init of_unittest_parse_phandle_with_args(void) static void __init of_unittest_parse_phandle_with_args_map(void) { - struct device_node *np, *p0, *p1, *p2, *p3; + struct device_node *np, *p[6] = {}; struct of_phandle_args args; + unsigned int prefs[6]; int i, rc; np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-b"); @@ -555,34 +559,24 @@ static void __init of_unittest_parse_phandle_with_args_map(void) return; } - p0 = of_find_node_by_path("/testcase-data/phandle-tests/provider0"); - if (!p0) { - pr_err("missing testcase data\n"); - return; - } - - p1 = of_find_node_by_path("/testcase-data/phandle-tests/provider1"); - if (!p1) { - pr_err("missing testcase data\n"); - return; - } - - p2 = of_find_node_by_path("/testcase-data/phandle-tests/provider2"); - if (!p2) { - pr_err("missing testcase data\n"); - return; - } - - p3 = of_find_node_by_path("/testcase-data/phandle-tests/provider3"); - if (!p3) { - pr_err("missing testcase data\n"); - return; + p[0] = of_find_node_by_path("/testcase-data/phandle-tests/provider0"); + p[1] = of_find_node_by_path("/testcase-data/phandle-tests/provider1"); + p[2] = of_find_node_by_path("/testcase-data/phandle-tests/provider2"); + p[3] = of_find_node_by_path("/testcase-data/phandle-tests/provider3"); + p[4] = of_find_node_by_path("/testcase-data/phandle-tests/provider4"); + p[5] = of_find_node_by_path("/testcase-data/phandle-tests/provider5"); + for (i = 0; i < ARRAY_SIZE(p); ++i) { + if (!p[i]) { + pr_err("missing testcase data\n"); + return; + } + prefs[i] = kref_read(&p[i]->kobj.kref); } rc = of_count_phandle_with_args(np, "phandle-list", "#phandle-cells"); - unittest(rc == 7, "of_count_phandle_with_args() returned %i, expected 7\n", rc); + unittest(rc == 8, "of_count_phandle_with_args() returned %i, expected 8\n", rc); - for (i = 0; i < 8; i++) { + for (i = 0; i < 9; i++) { bool passed = true; memset(&args, 0, sizeof(args)); @@ -593,13 +587,13 @@ static void __init of_unittest_parse_phandle_with_args_map(void) switch (i) { case 0: passed &= !rc; - passed &= (args.np == p1); + passed &= (args.np == p[1]); passed &= (args.args_count == 1); passed &= (args.args[0] == 1); break; case 1: passed &= !rc; - passed &= (args.np == p3); + passed &= (args.np == p[3]); passed &= (args.args_count == 3); passed &= (args.args[0] == 2); passed &= (args.args[1] == 5); @@ -610,28 +604,36 @@ static void __init of_unittest_parse_phandle_with_args_map(void) break; case 3: passed &= !rc; - passed &= (args.np == p0); + passed &= (args.np == p[0]); passed &= (args.args_count == 0); break; case 4: passed &= !rc; - passed &= (args.np == p1); + passed &= (args.np == p[1]); passed &= (args.args_count == 1); passed &= (args.args[0] == 3); break; case 5: passed &= !rc; - passed &= (args.np == p0); + passed &= (args.np == p[0]); passed &= (args.args_count == 0); break; case 6: passed &= !rc; - passed &= (args.np == p2); + passed &= (args.np == p[2]); passed &= (args.args_count == 2); passed &= (args.args[0] == 15); passed &= (args.args[1] == 0x20); break; case 7: + passed &= !rc; + passed &= (args.np == p[3]); + passed &= (args.args_count == 3); + passed &= (args.args[0] == 2); + passed &= (args.args[1] == 5); + passed &= (args.args[2] == 3); + break; + case 8: passed &= (rc == -ENOENT); break; default: @@ -640,6 +642,9 @@ static void __init of_unittest_parse_phandle_with_args_map(void) unittest(passed, "index %i - data error on node %s rc=%i\n", i, args.np->full_name, rc); + + if (rc == 0) + of_node_put(args.np); } /* Check for missing list property */ @@ -686,6 +691,13 @@ static void __init of_unittest_parse_phandle_with_args_map(void) "OF: /testcase-data/phandle-tests/consumer-b: #phandle-cells = 2 found 1"); unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); + + for (i = 0; i < ARRAY_SIZE(p); ++i) { + unittest(prefs[i] == kref_read(&p[i]->kobj.kref), + "provider%d: expected:%d got:%d\n", + i, prefs[i], kref_read(&p[i]->kobj.kref)); + of_node_put(p[i]); + } } static void __init of_unittest_property_string(void) diff --git a/drivers/parisc/power.c b/drivers/parisc/power.c index bb0d92461b08..7a6a3e7f2825 100644 --- a/drivers/parisc/power.c +++ b/drivers/parisc/power.c @@ -213,7 +213,7 @@ static int __init power_init(void) if (running_on_qemu && soft_power_reg) register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_DEFAULT, qemu_power_off, (void *)soft_power_reg); - else + if (!running_on_qemu || soft_power_reg) power_task = kthread_run(kpowerswd, (void*)soft_power_reg, KTHREAD_NAME); if (IS_ERR(power_task)) { diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index 9f5d784cd95d..3644997a8342 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -65,6 +65,10 @@ enum parport_pc_pci_cards { sunix_5069a, sunix_5079a, sunix_5099a, + brainboxes_uc257, + brainboxes_is300, + brainboxes_uc414, + brainboxes_px263, }; /* each element directly indexed from enum list, above */ @@ -158,6 +162,10 @@ static struct parport_pc_pci cards[] = { /* sunix_5069a */ { 1, { { 1, 2 }, } }, /* sunix_5079a */ { 1, { { 1, 2 }, } }, /* sunix_5099a */ { 1, { { 1, 2 }, } }, + /* brainboxes_uc257 */ { 1, { { 3, -1 }, } }, + /* brainboxes_is300 */ { 1, { { 3, -1 }, } }, + /* brainboxes_uc414 */ { 1, { { 3, -1 }, } }, + /* brainboxes_px263 */ { 1, { { 3, -1 }, } }, }; static struct pci_device_id parport_serial_pci_tbl[] = { @@ -277,6 +285,38 @@ static struct pci_device_id parport_serial_pci_tbl[] = { { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, 0x0104, 0, 0, sunix_5099a }, + /* Brainboxes UC-203 */ + { PCI_VENDOR_ID_INTASHIELD, 0x0bc1, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VENDOR_ID_INTASHIELD, 0x0bc2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + + /* Brainboxes UC-257 */ + { PCI_VENDOR_ID_INTASHIELD, 0x0861, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VENDOR_ID_INTASHIELD, 0x0862, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VENDOR_ID_INTASHIELD, 0x0863, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + + /* Brainboxes UC-414 */ + { PCI_VENDOR_ID_INTASHIELD, 0x0e61, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc414 }, + + /* Brainboxes UC-475 */ + { PCI_VENDOR_ID_INTASHIELD, 0x0981, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + { PCI_VENDOR_ID_INTASHIELD, 0x0982, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_uc257 }, + + /* Brainboxes IS-300/IS-500 */ + { PCI_VENDOR_ID_INTASHIELD, 0x0da0, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_is300 }, + + /* Brainboxes PX-263/PX-295 */ + { PCI_VENDOR_ID_INTASHIELD, 0x402c, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, brainboxes_px263 }, + { 0, } /* terminate list */ }; MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl); @@ -542,6 +582,30 @@ static struct pciserial_board pci_parport_serial_boards[] = { .base_baud = 921600, .uart_offset = 0x8, }, + [brainboxes_uc257] = { + .flags = FL_BASE2, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [brainboxes_is300] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [brainboxes_uc414] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [brainboxes_px263] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, }; struct parport_serial_private { diff --git a/drivers/parport/share.c b/drivers/parport/share.c index e21831d93305..49c74ded8a53 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -611,7 +611,7 @@ static void free_pardevice(struct device *dev) { struct pardevice *par_dev = to_pardevice(dev); - kfree(par_dev->name); + kfree_const(par_dev->name); kfree(par_dev); } @@ -682,8 +682,8 @@ parport_register_dev_model(struct parport *port, const char *name, const struct pardev_cb *par_dev_cb, int id) { struct pardevice *par_dev; + const char *devname; int ret; - char *devname; if (port->physport->flags & PARPORT_FLAG_EXCL) { /* An exclusive device is registered. */ @@ -726,7 +726,7 @@ parport_register_dev_model(struct parport *port, const char *name, if (!par_dev->state) goto err_put_par_dev; - devname = kstrdup(name, GFP_KERNEL); + devname = kstrdup_const(name, GFP_KERNEL); if (!devname) goto err_free_par_dev; @@ -804,7 +804,7 @@ parport_register_dev_model(struct parport *port, const char *name, return par_dev; err_free_devname: - kfree(devname); + kfree_const(devname); err_free_par_dev: kfree(par_dev->state); err_put_par_dev: diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 9c2137dae429..826b5016a101 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -386,21 +386,8 @@ void pci_bus_add_devices(const struct pci_bus *bus) } EXPORT_SYMBOL(pci_bus_add_devices); -/** pci_walk_bus - walk devices on/under bus, calling callback. - * @top bus whose devices should be walked - * @cb callback to be called for each device found - * @userdata arbitrary pointer to be passed to callback. - * - * Walk the given bus, including any bridged devices - * on buses under this bus. Call the provided callback - * on each device found. - * - * We check the return of @cb each time. If it returns anything - * other than 0, we break out. - * - */ -void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), - void *userdata) +static void __pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), + void *userdata, bool locked) { struct pci_dev *dev; struct pci_bus *bus; @@ -408,7 +395,8 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), int retval; bus = top; - down_read(&pci_bus_sem); + if (!locked) + down_read(&pci_bus_sem); next = top->devices.next; for (;;) { if (next == &bus->devices) { @@ -431,10 +419,37 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), if (retval) break; } - up_read(&pci_bus_sem); + if (!locked) + up_read(&pci_bus_sem); +} + +/** + * pci_walk_bus - walk devices on/under bus, calling callback. + * @top: bus whose devices should be walked + * @cb: callback to be called for each device found + * @userdata: arbitrary pointer to be passed to callback + * + * Walk the given bus, including any bridged devices + * on buses under this bus. Call the provided callback + * on each device found. + * + * We check the return of @cb each time. If it returns anything + * other than 0, we break out. + */ +void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata) +{ + __pci_walk_bus(top, cb, userdata, false); } EXPORT_SYMBOL_GPL(pci_walk_bus); +void pci_walk_bus_locked(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata) +{ + lockdep_assert_held(&pci_bus_sem); + + __pci_walk_bus(top, cb, userdata, true); +} +EXPORT_SYMBOL_GPL(pci_walk_bus_locked); + struct pci_bus *pci_bus_get(struct pci_bus *bus) { if (bus) diff --git a/drivers/pci/controller/cadence/Kconfig b/drivers/pci/controller/cadence/Kconfig index 291d12711363..1d5a70c9055e 100644 --- a/drivers/pci/controller/cadence/Kconfig +++ b/drivers/pci/controller/cadence/Kconfig @@ -47,6 +47,7 @@ config PCI_J721E config PCI_J721E_HOST bool "TI J721E PCIe controller (host mode)" + depends on ARCH_K3 || COMPILE_TEST depends on OF select PCIE_CADENCE_HOST select PCI_J721E @@ -57,6 +58,7 @@ config PCI_J721E_HOST config PCI_J721E_EP bool "TI J721E PCIe controller (endpoint mode)" + depends on ARCH_K3 || COMPILE_TEST depends on OF depends on PCI_ENDPOINT select PCIE_CADENCE_EP diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index 2c87e7728a65..85718246016b 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -42,18 +42,16 @@ enum link_status { }; #define J721E_MODE_RC BIT(7) -#define LANE_COUNT_MASK BIT(8) #define LANE_COUNT(n) ((n) << 8) #define GENERATION_SEL_MASK GENMASK(1, 0) -#define MAX_LANES 2 - struct j721e_pcie { struct cdns_pcie *cdns_pcie; struct clk *refclk; u32 mode; u32 num_lanes; + u32 max_lanes; void __iomem *user_cfg_base; void __iomem *intd_cfg_base; u32 linkdown_irq_regfield; @@ -71,6 +69,7 @@ struct j721e_pcie_data { unsigned int quirk_disable_flr:1; u32 linkdown_irq_regfield; unsigned int byte_access_allowed:1; + unsigned int max_lanes; }; static inline u32 j721e_pcie_user_readl(struct j721e_pcie *pcie, u32 offset) @@ -206,11 +205,15 @@ static int j721e_pcie_set_lane_count(struct j721e_pcie *pcie, { struct device *dev = pcie->cdns_pcie->dev; u32 lanes = pcie->num_lanes; + u32 mask = BIT(8); u32 val = 0; int ret; + if (pcie->max_lanes == 4) + mask = GENMASK(9, 8); + val = LANE_COUNT(lanes - 1); - ret = regmap_update_bits(syscon, offset, LANE_COUNT_MASK, val); + ret = regmap_update_bits(syscon, offset, mask, val); if (ret) dev_err(dev, "failed to set link count\n"); @@ -290,11 +293,13 @@ static const struct j721e_pcie_data j721e_pcie_rc_data = { .quirk_retrain_flag = true, .byte_access_allowed = false, .linkdown_irq_regfield = LINK_DOWN, + .max_lanes = 2, }; static const struct j721e_pcie_data j721e_pcie_ep_data = { .mode = PCI_MODE_EP, .linkdown_irq_regfield = LINK_DOWN, + .max_lanes = 2, }; static const struct j721e_pcie_data j7200_pcie_rc_data = { @@ -302,23 +307,41 @@ static const struct j721e_pcie_data j7200_pcie_rc_data = { .quirk_detect_quiet_flag = true, .linkdown_irq_regfield = J7200_LINK_DOWN, .byte_access_allowed = true, + .max_lanes = 2, }; static const struct j721e_pcie_data j7200_pcie_ep_data = { .mode = PCI_MODE_EP, .quirk_detect_quiet_flag = true, .quirk_disable_flr = true, + .max_lanes = 2, }; static const struct j721e_pcie_data am64_pcie_rc_data = { .mode = PCI_MODE_RC, .linkdown_irq_regfield = J7200_LINK_DOWN, .byte_access_allowed = true, + .max_lanes = 1, }; static const struct j721e_pcie_data am64_pcie_ep_data = { .mode = PCI_MODE_EP, .linkdown_irq_regfield = J7200_LINK_DOWN, + .max_lanes = 1, +}; + +static const struct j721e_pcie_data j784s4_pcie_rc_data = { + .mode = PCI_MODE_RC, + .quirk_retrain_flag = true, + .byte_access_allowed = false, + .linkdown_irq_regfield = LINK_DOWN, + .max_lanes = 4, +}; + +static const struct j721e_pcie_data j784s4_pcie_ep_data = { + .mode = PCI_MODE_EP, + .linkdown_irq_regfield = LINK_DOWN, + .max_lanes = 4, }; static const struct of_device_id of_j721e_pcie_match[] = { @@ -346,6 +369,14 @@ static const struct of_device_id of_j721e_pcie_match[] = { .compatible = "ti,am64-pcie-ep", .data = &am64_pcie_ep_data, }, + { + .compatible = "ti,j784s4-pcie-host", + .data = &j784s4_pcie_rc_data, + }, + { + .compatible = "ti,j784s4-pcie-ep", + .data = &j784s4_pcie_ep_data, + }, {}, }; @@ -432,9 +463,13 @@ static int j721e_pcie_probe(struct platform_device *pdev) pcie->user_cfg_base = base; ret = of_property_read_u32(node, "num-lanes", &num_lanes); - if (ret || num_lanes > MAX_LANES) + if (ret || num_lanes > data->max_lanes) { + dev_warn(dev, "num-lanes property not provided or invalid, setting num-lanes to 1\n"); num_lanes = 1; + } + pcie->num_lanes = num_lanes; + pcie->max_lanes = data->max_lanes; if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48))) return -EINVAL; diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index 3142feb8ac19..2d0a8d78bffb 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -360,8 +360,8 @@ static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, u8 intx, writel(0, ep->irq_cpu_addr + offset); } -static int cdns_pcie_ep_send_legacy_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn, - u8 intx) +static int cdns_pcie_ep_send_intx_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn, + u8 intx) { u16 cmd; @@ -371,7 +371,7 @@ static int cdns_pcie_ep_send_legacy_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn, cdns_pcie_ep_assert_intx(ep, fn, intx, true); /* - * The mdelay() value was taken from dra7xx_pcie_raise_legacy_irq() + * The mdelay() value was taken from dra7xx_pcie_raise_intx_irq() */ mdelay(1); cdns_pcie_ep_assert_intx(ep, fn, intx, false); @@ -532,25 +532,24 @@ static int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn, } static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; struct device *dev = pcie->dev; switch (type) { - case PCI_EPC_IRQ_LEGACY: + case PCI_IRQ_INTX: if (vfn > 0) { - dev_err(dev, "Cannot raise legacy interrupts for VF\n"); + dev_err(dev, "Cannot raise INTX interrupts for VF\n"); return -EINVAL; } - return cdns_pcie_ep_send_legacy_irq(ep, fn, vfn, 0); + return cdns_pcie_ep_send_intx_irq(ep, fn, vfn, 0); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_MSI: return cdns_pcie_ep_send_msi_irq(ep, fn, vfn, interrupt_num); - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: return cdns_pcie_ep_send_msix_irq(ep, fn, vfn, interrupt_num); default: diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 373cb50fcd15..03b96798f858 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -347,16 +347,16 @@ struct cdns_pcie_epf { * @max_regions: maximum number of regions supported by hardware * @ob_region_map: bitmask of mapped outbound regions * @ob_addr: base addresses in the AXI bus where the outbound regions start - * @irq_phys_addr: base address on the AXI bus where the MSI/legacy IRQ + * @irq_phys_addr: base address on the AXI bus where the MSI/INTX IRQ * dedicated outbound regions is mapped. * @irq_cpu_addr: base address in the CPU space where a write access triggers - * the sending of a memory write (MSI) / normal message (legacy + * the sending of a memory write (MSI) / normal message (INTX * IRQ) TLP through the PCIe bus. - * @irq_pci_addr: used to save the current mapping of the MSI/legacy IRQ + * @irq_pci_addr: used to save the current mapping of the MSI/INTX IRQ * dedicated outbound region. * @irq_pci_fn: the latest PCI function that has updated the mapping of - * the MSI/legacy IRQ dedicated outbound region. - * @irq_pending: bitmask of asserted legacy IRQs. + * the MSI/INTX IRQ dedicated outbound region. + * @irq_pending: bitmask of asserted INTX IRQs. * @lock: spin lock to disable interrupts while modifying PCIe controller * registers fields (RMW) accessible by both remote RC and EP to * minimize time between read and write @@ -374,7 +374,7 @@ struct cdns_pcie_ep { u64 irq_pci_addr; u8 irq_pci_fn; u8 irq_pending; - /* protect writing to PCI_STATUS while raising legacy interrupts */ + /* protect writing to PCI_STATUS while raising INTX interrupts */ spinlock_t lock; struct cdns_pcie_epf *epf; unsigned int quirk_detect_quiet_flag:1; diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 5ac021dbd46a..8afacc90c63b 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -336,7 +336,7 @@ config PCI_EXYNOS config PCIE_FU740 bool "SiFive FU740 PCIe controller" depends on PCI_MSI - depends on SOC_SIFIVE || COMPILE_TEST + depends on ARCH_SIFIVE || COMPILE_TEST select PCIE_DW_HOST help Say Y here if you want PCIe controller support for the SiFive diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index b445ffe95e3f..0e406677060d 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -371,7 +371,7 @@ static int dra7xx_pcie_init_irq_domain(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops dra7xx_pcie_host_ops = { - .host_init = dra7xx_pcie_host_init, + .init = dra7xx_pcie_host_init, }; static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) @@ -386,7 +386,7 @@ static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) dra7xx_pcie_enable_wrapper_interrupts(dra7xx); } -static void dra7xx_pcie_raise_legacy_irq(struct dra7xx_pcie *dra7xx) +static void dra7xx_pcie_raise_intx_irq(struct dra7xx_pcie *dra7xx) { dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_ASSERT, 0x1); mdelay(1); @@ -404,16 +404,16 @@ static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx, } static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); switch (type) { - case PCI_EPC_IRQ_LEGACY: - dra7xx_pcie_raise_legacy_irq(dra7xx); + case PCI_IRQ_INTX: + dra7xx_pcie_raise_intx_irq(dra7xx); break; - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_MSI: dra7xx_pcie_raise_msi_irq(dra7xx, interrupt_num); break; default: @@ -436,7 +436,7 @@ dra7xx_pcie_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops pcie_ep_ops = { - .ep_init = dra7xx_pcie_ep_init, + .init = dra7xx_pcie_ep_init, .raise_irq = dra7xx_pcie_raise_irq, .get_features = dra7xx_pcie_get_features, }; diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index c6bede346932..a33fa98a252e 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -268,7 +268,7 @@ static int exynos_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops exynos_pcie_host_ops = { - .host_init = exynos_pcie_host_init, + .init = exynos_pcie_host_init, }; static int exynos_add_pcie_port(struct exynos_pcie *ep, @@ -375,7 +375,7 @@ fail_probe: return ret; } -static int exynos_pcie_remove(struct platform_device *pdev) +static void exynos_pcie_remove(struct platform_device *pdev) { struct exynos_pcie *ep = platform_get_drvdata(pdev); @@ -385,8 +385,6 @@ static int exynos_pcie_remove(struct platform_device *pdev) phy_exit(ep->phy); exynos_pcie_deinit_clk_resources(ep); regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies); - - return 0; } static int exynos_pcie_suspend_noirq(struct device *dev) @@ -431,7 +429,7 @@ static const struct of_device_id exynos_pcie_of_match[] = { static struct platform_driver exynos_pcie_driver = { .probe = exynos_pcie_probe, - .remove = exynos_pcie_remove, + .remove_new = exynos_pcie_remove, .driver = { .name = "exynos-pcie", .of_match_table = exynos_pcie_of_match, diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 74703362aeec..dc2c036ab28c 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1039,8 +1039,8 @@ static void imx6_pcie_host_exit(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops imx6_pcie_host_ops = { - .host_init = imx6_pcie_host_init, - .host_deinit = imx6_pcie_host_exit, + .init = imx6_pcie_host_init, + .deinit = imx6_pcie_host_exit, }; static const struct dw_pcie_ops dw_pcie_ops = { @@ -1058,17 +1058,16 @@ static void imx6_pcie_ep_init(struct dw_pcie_ep *ep) } static int imx6_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return dw_pcie_ep_raise_legacy_irq(ep, func_no); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_INTX: + return dw_pcie_ep_raise_intx_irq(ep, func_no); + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); default: dev_err(pci->dev, "UNKNOWN IRQ type\n"); @@ -1093,7 +1092,7 @@ imx6_pcie_ep_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops pcie_ep_ops = { - .ep_init = imx6_pcie_ep_init, + .init = imx6_pcie_ep_init, .raise_irq = imx6_pcie_ep_raise_irq, .get_features = imx6_pcie_ep_get_features, }; diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 0def919f89fa..c0c62533a3f1 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -115,8 +115,7 @@ struct keystone_pcie { struct dw_pcie *pci; /* PCI Device ID */ u32 device_id; - int legacy_host_irqs[PCI_NUM_INTX]; - struct device_node *legacy_intc_np; + int intx_host_irqs[PCI_NUM_INTX]; int msi_host_irq; int num_lanes; @@ -124,7 +123,7 @@ struct keystone_pcie { struct phy **phy; struct device_link **link; struct device_node *msi_intc_np; - struct irq_domain *legacy_irq_domain; + struct irq_domain *intx_irq_domain; struct device_node *np; /* Application register space */ @@ -252,8 +251,8 @@ static int ks_pcie_msi_host_init(struct dw_pcie_rp *pp) return dw_pcie_allocate_domains(pp); } -static void ks_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, - int offset) +static void ks_pcie_handle_intx_irq(struct keystone_pcie *ks_pcie, + int offset) { struct dw_pcie *pci = ks_pcie->pci; struct device *dev = pci->dev; @@ -263,7 +262,7 @@ static void ks_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, if (BIT(0) & pending) { dev_dbg(dev, ": irq: irq_offset %d", offset); - generic_handle_domain_irq(ks_pcie->legacy_irq_domain, offset); + generic_handle_domain_irq(ks_pcie->intx_irq_domain, offset); } /* EOI the INTx interrupt */ @@ -307,38 +306,37 @@ static irqreturn_t ks_pcie_handle_error_irq(struct keystone_pcie *ks_pcie) return IRQ_HANDLED; } -static void ks_pcie_ack_legacy_irq(struct irq_data *d) +static void ks_pcie_ack_intx_irq(struct irq_data *d) { } -static void ks_pcie_mask_legacy_irq(struct irq_data *d) +static void ks_pcie_mask_intx_irq(struct irq_data *d) { } -static void ks_pcie_unmask_legacy_irq(struct irq_data *d) +static void ks_pcie_unmask_intx_irq(struct irq_data *d) { } -static struct irq_chip ks_pcie_legacy_irq_chip = { - .name = "Keystone-PCI-Legacy-IRQ", - .irq_ack = ks_pcie_ack_legacy_irq, - .irq_mask = ks_pcie_mask_legacy_irq, - .irq_unmask = ks_pcie_unmask_legacy_irq, +static struct irq_chip ks_pcie_intx_irq_chip = { + .name = "Keystone-PCI-INTX-IRQ", + .irq_ack = ks_pcie_ack_intx_irq, + .irq_mask = ks_pcie_mask_intx_irq, + .irq_unmask = ks_pcie_unmask_intx_irq, }; -static int ks_pcie_init_legacy_irq_map(struct irq_domain *d, - unsigned int irq, - irq_hw_number_t hw_irq) +static int ks_pcie_init_intx_irq_map(struct irq_domain *d, + unsigned int irq, irq_hw_number_t hw_irq) { - irq_set_chip_and_handler(irq, &ks_pcie_legacy_irq_chip, + irq_set_chip_and_handler(irq, &ks_pcie_intx_irq_chip, handle_level_irq); irq_set_chip_data(irq, d->host_data); return 0; } -static const struct irq_domain_ops ks_pcie_legacy_irq_domain_ops = { - .map = ks_pcie_init_legacy_irq_map, +static const struct irq_domain_ops ks_pcie_intx_irq_domain_ops = { + .map = ks_pcie_init_intx_irq_map, .xlate = irq_domain_xlate_onetwocell, }; @@ -605,22 +603,22 @@ static void ks_pcie_msi_irq_handler(struct irq_desc *desc) } /** - * ks_pcie_legacy_irq_handler() - Handle legacy interrupt + * ks_pcie_intx_irq_handler() - Handle INTX interrupt * @desc: Pointer to irq descriptor * - * Traverse through pending legacy interrupts and invoke handler for each. Also + * Traverse through pending INTX interrupts and invoke handler for each. Also * takes care of interrupt controller level mask/ack operation. */ -static void ks_pcie_legacy_irq_handler(struct irq_desc *desc) +static void ks_pcie_intx_irq_handler(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); struct dw_pcie *pci = ks_pcie->pci; struct device *dev = pci->dev; - u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0]; + u32 irq_offset = irq - ks_pcie->intx_host_irqs[0]; struct irq_chip *chip = irq_desc_get_chip(desc); - dev_dbg(dev, ": Handling legacy irq %d\n", irq); + dev_dbg(dev, ": Handling INTX irq %d\n", irq); /* * The chained irq handler installation would have replaced normal @@ -628,7 +626,7 @@ static void ks_pcie_legacy_irq_handler(struct irq_desc *desc) * ack operation. */ chained_irq_enter(chip, desc); - ks_pcie_handle_legacy_irq(ks_pcie, irq_offset); + ks_pcie_handle_intx_irq(ks_pcie, irq_offset); chained_irq_exit(chip, desc); } @@ -686,10 +684,10 @@ err: return ret; } -static int ks_pcie_config_legacy_irq(struct keystone_pcie *ks_pcie) +static int ks_pcie_config_intx_irq(struct keystone_pcie *ks_pcie) { struct device *dev = ks_pcie->pci->dev; - struct irq_domain *legacy_irq_domain; + struct irq_domain *intx_irq_domain; struct device_node *np = ks_pcie->np; struct device_node *intc_np; int irq_count, irq, ret = 0, i; @@ -697,7 +695,7 @@ static int ks_pcie_config_legacy_irq(struct keystone_pcie *ks_pcie) intc_np = of_get_child_by_name(np, "legacy-interrupt-controller"); if (!intc_np) { /* - * Since legacy interrupts are modeled as edge-interrupts in + * Since INTX interrupts are modeled as edge-interrupts in * AM6, keep it disabled for now. */ if (ks_pcie->is_am6) @@ -719,22 +717,21 @@ static int ks_pcie_config_legacy_irq(struct keystone_pcie *ks_pcie) ret = -EINVAL; goto err; } - ks_pcie->legacy_host_irqs[i] = irq; + ks_pcie->intx_host_irqs[i] = irq; irq_set_chained_handler_and_data(irq, - ks_pcie_legacy_irq_handler, + ks_pcie_intx_irq_handler, ks_pcie); } - legacy_irq_domain = - irq_domain_add_linear(intc_np, PCI_NUM_INTX, - &ks_pcie_legacy_irq_domain_ops, NULL); - if (!legacy_irq_domain) { - dev_err(dev, "Failed to add irq domain for legacy irqs\n"); + intx_irq_domain = irq_domain_add_linear(intc_np, PCI_NUM_INTX, + &ks_pcie_intx_irq_domain_ops, NULL); + if (!intx_irq_domain) { + dev_err(dev, "Failed to add irq domain for INTX irqs\n"); ret = -EINVAL; goto err; } - ks_pcie->legacy_irq_domain = legacy_irq_domain; + ks_pcie->intx_irq_domain = intx_irq_domain; for (i = 0; i < PCI_NUM_INTX; i++) ks_pcie_app_writel(ks_pcie, IRQ_ENABLE_SET(i), INTx_EN); @@ -808,7 +805,7 @@ static int __init ks_pcie_host_init(struct dw_pcie_rp *pp) if (!ks_pcie->is_am6) pp->bridge->child_ops = &ks_child_pcie_ops; - ret = ks_pcie_config_legacy_irq(ks_pcie); + ret = ks_pcie_config_intx_irq(ks_pcie); if (ret) return ret; @@ -838,12 +835,12 @@ static int __init ks_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops ks_pcie_host_ops = { - .host_init = ks_pcie_host_init, - .msi_host_init = ks_pcie_msi_host_init, + .init = ks_pcie_host_init, + .msi_init = ks_pcie_msi_host_init, }; static const struct dw_pcie_host_ops ks_pcie_am654_host_ops = { - .host_init = ks_pcie_host_init, + .init = ks_pcie_host_init, }; static irqreturn_t ks_pcie_err_irq_handler(int irq, void *priv) @@ -881,7 +878,7 @@ static void ks_pcie_am654_ep_init(struct dw_pcie_ep *ep) dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, flags); } -static void ks_pcie_am654_raise_legacy_irq(struct keystone_pcie *ks_pcie) +static void ks_pcie_am654_raise_intx_irq(struct keystone_pcie *ks_pcie) { struct dw_pcie *pci = ks_pcie->pci; u8 int_pin; @@ -900,20 +897,19 @@ static void ks_pcie_am654_raise_legacy_irq(struct keystone_pcie *ks_pcie) } static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); switch (type) { - case PCI_EPC_IRQ_LEGACY: - ks_pcie_am654_raise_legacy_irq(ks_pcie); + case PCI_IRQ_INTX: + ks_pcie_am654_raise_intx_irq(ks_pcie); break; - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_MSI: dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); break; - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); break; default: @@ -944,7 +940,7 @@ ks_pcie_am654_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops ks_pcie_am654_ep_ops = { - .ep_init = ks_pcie_am654_ep_init, + .init = ks_pcie_am654_ep_init, .raise_irq = ks_pcie_am654_raise_irq, .get_features = &ks_pcie_am654_get_features, }; @@ -1218,7 +1214,16 @@ static int ks_pcie_probe(struct platform_device *pdev) goto err_link; } + /* Obtain references to the PHYs */ + for (i = 0; i < num_lanes; i++) + phy_pm_runtime_get_sync(ks_pcie->phy[i]); + ret = ks_pcie_enable_phy(ks_pcie); + + /* Release references to the PHYs */ + for (i = 0; i < num_lanes; i++) + phy_pm_runtime_put_sync(ks_pcie->phy[i]); + if (ret) { dev_err(dev, "failed to enable phy\n"); goto err_link; @@ -1302,7 +1307,7 @@ err_link: return ret; } -static int ks_pcie_remove(struct platform_device *pdev) +static void ks_pcie_remove(struct platform_device *pdev) { struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev); struct device_link **link = ks_pcie->link; @@ -1314,13 +1319,11 @@ static int ks_pcie_remove(struct platform_device *pdev) ks_pcie_disable_phy(ks_pcie); while (num_lanes--) device_link_del(link[num_lanes]); - - return 0; } static struct platform_driver ks_pcie_driver = { .probe = ks_pcie_probe, - .remove = ks_pcie_remove, + .remove_new = ks_pcie_remove, .driver = { .name = "keystone-pcie", .of_match_table = ks_pcie_of_match, diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c index 3d3c50ef4b6f..2e398494e7c0 100644 --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c @@ -49,7 +49,7 @@ struct ls_pcie_ep { bool big_endian; }; -static u32 ls_lut_readl(struct ls_pcie_ep *pcie, u32 offset) +static u32 ls_pcie_pf_lut_readl(struct ls_pcie_ep *pcie, u32 offset) { struct dw_pcie *pci = pcie->pci; @@ -59,7 +59,7 @@ static u32 ls_lut_readl(struct ls_pcie_ep *pcie, u32 offset) return ioread32(pci->dbi_base + offset); } -static void ls_lut_writel(struct ls_pcie_ep *pcie, u32 offset, u32 value) +static void ls_pcie_pf_lut_writel(struct ls_pcie_ep *pcie, u32 offset, u32 value) { struct dw_pcie *pci = pcie->pci; @@ -76,8 +76,8 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id) u32 val, cfg; u8 offset; - val = ls_lut_readl(pcie, PEX_PF0_PME_MES_DR); - ls_lut_writel(pcie, PEX_PF0_PME_MES_DR, val); + val = ls_pcie_pf_lut_readl(pcie, PEX_PF0_PME_MES_DR); + ls_pcie_pf_lut_writel(pcie, PEX_PF0_PME_MES_DR, val); if (!val) return IRQ_NONE; @@ -96,9 +96,9 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id) dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, pcie->lnkcap); dw_pcie_dbi_ro_wr_dis(pci); - cfg = ls_lut_readl(pcie, PEX_PF0_CONFIG); + cfg = ls_pcie_pf_lut_readl(pcie, PEX_PF0_CONFIG); cfg |= PEX_PF0_CFG_READY; - ls_lut_writel(pcie, PEX_PF0_CONFIG, cfg); + ls_pcie_pf_lut_writel(pcie, PEX_PF0_CONFIG, cfg); dw_pcie_ep_linkup(&pci->ep); dev_dbg(pci->dev, "Link up\n"); @@ -130,10 +130,10 @@ static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie, } /* Enable interrupts */ - val = ls_lut_readl(pcie, PEX_PF0_PME_MES_IER); + val = ls_pcie_pf_lut_readl(pcie, PEX_PF0_PME_MES_IER); val |= PEX_PF0_PME_MES_IER_LDDIE | PEX_PF0_PME_MES_IER_HRDIE | PEX_PF0_PME_MES_IER_LUDIE; - ls_lut_writel(pcie, PEX_PF0_PME_MES_IER, val); + ls_pcie_pf_lut_writel(pcie, PEX_PF0_PME_MES_IER, val); return 0; } @@ -166,16 +166,16 @@ static void ls_pcie_ep_init(struct dw_pcie_ep *ep) } static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return dw_pcie_ep_raise_legacy_irq(ep, func_no); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_INTX: + return dw_pcie_ep_raise_intx_irq(ep, func_no); + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: return dw_pcie_ep_raise_msix_irq_doorbell(ep, func_no, interrupt_num); default: @@ -184,8 +184,7 @@ static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } } -static unsigned int ls_pcie_ep_func_conf_select(struct dw_pcie_ep *ep, - u8 func_no) +static unsigned int ls_pcie_ep_get_dbi_offset(struct dw_pcie_ep *ep, u8 func_no) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct ls_pcie_ep *pcie = to_ls_pcie_ep(pci); @@ -195,10 +194,10 @@ static unsigned int ls_pcie_ep_func_conf_select(struct dw_pcie_ep *ep, } static const struct dw_pcie_ep_ops ls_pcie_ep_ops = { - .ep_init = ls_pcie_ep_init, + .init = ls_pcie_ep_init, .raise_irq = ls_pcie_ep_raise_irq, .get_features = ls_pcie_ep_get_features, - .func_conf_select = ls_pcie_ep_func_conf_select, + .get_dbi_offset = ls_pcie_ep_get_dbi_offset, }; static const struct ls_pcie_ep_drvdata ls1_ep_drvdata = { diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c index 37956e09c65b..ee6f52568133 100644 --- a/drivers/pci/controller/dwc/pci-layerscape.c +++ b/drivers/pci/controller/dwc/pci-layerscape.c @@ -35,21 +35,41 @@ #define PF_MCR_PTOMR BIT(0) #define PF_MCR_EXL2S BIT(1) +/* LS1021A PEXn PM Write Control Register */ +#define SCFG_PEXPMWRCR(idx) (0x5c + (idx) * 0x64) +#define PMXMTTURNOFF BIT(31) +#define SCFG_PEXSFTRSTCR 0x190 +#define PEXSR(idx) BIT(idx) + +/* LS1043A PEX PME control register */ +#define SCFG_PEXPMECR 0x144 +#define PEXPME(idx) BIT(31 - (idx) * 4) + +/* LS1043A PEX LUT debug register */ +#define LS_PCIE_LDBG 0x7fc +#define LDBG_SR BIT(30) +#define LDBG_WE BIT(31) + #define PCIE_IATU_NUM 6 struct ls_pcie_drvdata { - const u32 pf_off; + const u32 pf_lut_off; + const struct dw_pcie_host_ops *ops; + int (*exit_from_l2)(struct dw_pcie_rp *pp); + bool scfg_support; bool pm_support; }; struct ls_pcie { struct dw_pcie *pci; const struct ls_pcie_drvdata *drvdata; - void __iomem *pf_base; + void __iomem *pf_lut_base; + struct regmap *scfg; + int index; bool big_endian; }; -#define ls_pcie_pf_readl_addr(addr) ls_pcie_pf_readl(pcie, addr) +#define ls_pcie_pf_lut_readl_addr(addr) ls_pcie_pf_lut_readl(pcie, addr) #define to_ls_pcie(x) dev_get_drvdata((x)->dev) static bool ls_pcie_is_bridge(struct ls_pcie *pcie) @@ -90,20 +110,20 @@ static void ls_pcie_fix_error_response(struct ls_pcie *pcie) iowrite32(PCIE_ABSERR_SETTING, pci->dbi_base + PCIE_ABSERR); } -static u32 ls_pcie_pf_readl(struct ls_pcie *pcie, u32 off) +static u32 ls_pcie_pf_lut_readl(struct ls_pcie *pcie, u32 off) { if (pcie->big_endian) - return ioread32be(pcie->pf_base + off); + return ioread32be(pcie->pf_lut_base + off); - return ioread32(pcie->pf_base + off); + return ioread32(pcie->pf_lut_base + off); } -static void ls_pcie_pf_writel(struct ls_pcie *pcie, u32 off, u32 val) +static void ls_pcie_pf_lut_writel(struct ls_pcie *pcie, u32 off, u32 val) { if (pcie->big_endian) - iowrite32be(val, pcie->pf_base + off); + iowrite32be(val, pcie->pf_lut_base + off); else - iowrite32(val, pcie->pf_base + off); + iowrite32(val, pcie->pf_lut_base + off); } static void ls_pcie_send_turnoff_msg(struct dw_pcie_rp *pp) @@ -113,11 +133,11 @@ static void ls_pcie_send_turnoff_msg(struct dw_pcie_rp *pp) u32 val; int ret; - val = ls_pcie_pf_readl(pcie, LS_PCIE_PF_MCR); + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_PF_MCR); val |= PF_MCR_PTOMR; - ls_pcie_pf_writel(pcie, LS_PCIE_PF_MCR, val); + ls_pcie_pf_lut_writel(pcie, LS_PCIE_PF_MCR, val); - ret = readx_poll_timeout(ls_pcie_pf_readl_addr, LS_PCIE_PF_MCR, + ret = readx_poll_timeout(ls_pcie_pf_lut_readl_addr, LS_PCIE_PF_MCR, val, !(val & PF_MCR_PTOMR), PCIE_PME_TO_L2_TIMEOUT_US/10, PCIE_PME_TO_L2_TIMEOUT_US); @@ -125,7 +145,7 @@ static void ls_pcie_send_turnoff_msg(struct dw_pcie_rp *pp) dev_err(pcie->pci->dev, "PME_Turn_off timeout\n"); } -static void ls_pcie_exit_from_l2(struct dw_pcie_rp *pp) +static int ls_pcie_exit_from_l2(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct ls_pcie *pcie = to_ls_pcie(pci); @@ -136,20 +156,22 @@ static void ls_pcie_exit_from_l2(struct dw_pcie_rp *pp) * Set PF_MCR_EXL2S bit in LS_PCIE_PF_MCR register for the link * to exit L2 state. */ - val = ls_pcie_pf_readl(pcie, LS_PCIE_PF_MCR); + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_PF_MCR); val |= PF_MCR_EXL2S; - ls_pcie_pf_writel(pcie, LS_PCIE_PF_MCR, val); + ls_pcie_pf_lut_writel(pcie, LS_PCIE_PF_MCR, val); /* * L2 exit timeout of 10ms is not defined in the specifications, * it was chosen based on empirical observations. */ - ret = readx_poll_timeout(ls_pcie_pf_readl_addr, LS_PCIE_PF_MCR, + ret = readx_poll_timeout(ls_pcie_pf_lut_readl_addr, LS_PCIE_PF_MCR, val, !(val & PF_MCR_EXL2S), 1000, 10000); if (ret) dev_err(pcie->pci->dev, "L2 exit timeout\n"); + + return ret; } static int ls_pcie_host_init(struct dw_pcie_rp *pp) @@ -168,25 +190,130 @@ static int ls_pcie_host_init(struct dw_pcie_rp *pp) return 0; } +static void scfg_pcie_send_turnoff_msg(struct regmap *scfg, u32 reg, u32 mask) +{ + /* Send PME_Turn_Off message */ + regmap_write_bits(scfg, reg, mask, mask); + + /* + * There is no specific register to check for PME_To_Ack from endpoint. + * So on the safe side, wait for PCIE_PME_TO_L2_TIMEOUT_US. + */ + mdelay(PCIE_PME_TO_L2_TIMEOUT_US/1000); + + /* + * Layerscape hardware reference manual recommends clearing the PMXMTTURNOFF bit + * to complete the PME_Turn_Off handshake. + */ + regmap_write_bits(scfg, reg, mask, 0); +} + +static void ls1021a_pcie_send_turnoff_msg(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + + scfg_pcie_send_turnoff_msg(pcie->scfg, SCFG_PEXPMWRCR(pcie->index), PMXMTTURNOFF); +} + +static int scfg_pcie_exit_from_l2(struct regmap *scfg, u32 reg, u32 mask) +{ + /* Reset the PEX wrapper to bring the link out of L2 */ + regmap_write_bits(scfg, reg, mask, mask); + regmap_write_bits(scfg, reg, mask, 0); + + return 0; +} + +static int ls1021a_pcie_exit_from_l2(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + + return scfg_pcie_exit_from_l2(pcie->scfg, SCFG_PEXSFTRSTCR, PEXSR(pcie->index)); +} + +static void ls1043a_pcie_send_turnoff_msg(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + + scfg_pcie_send_turnoff_msg(pcie->scfg, SCFG_PEXPMECR, PEXPME(pcie->index)); +} + +static int ls1043a_pcie_exit_from_l2(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + u32 val; + + /* + * Reset the PEX wrapper to bring the link out of L2. + * LDBG_WE: allows the user to have write access to the PEXDBG[SR] for both setting and + * clearing the soft reset on the PEX module. + * LDBG_SR: When SR is set to 1, the PEX module enters soft reset. + */ + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG); + val |= LDBG_WE; + ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val); + + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG); + val |= LDBG_SR; + ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val); + + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG); + val &= ~LDBG_SR; + ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val); + + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG); + val &= ~LDBG_WE; + ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val); + + return 0; +} + static const struct dw_pcie_host_ops ls_pcie_host_ops = { - .host_init = ls_pcie_host_init, + .init = ls_pcie_host_init, .pme_turn_off = ls_pcie_send_turnoff_msg, }; +static const struct dw_pcie_host_ops ls1021a_pcie_host_ops = { + .init = ls_pcie_host_init, + .pme_turn_off = ls1021a_pcie_send_turnoff_msg, +}; + static const struct ls_pcie_drvdata ls1021a_drvdata = { - .pm_support = false, + .pm_support = true, + .scfg_support = true, + .ops = &ls1021a_pcie_host_ops, + .exit_from_l2 = ls1021a_pcie_exit_from_l2, +}; + +static const struct dw_pcie_host_ops ls1043a_pcie_host_ops = { + .init = ls_pcie_host_init, + .pme_turn_off = ls1043a_pcie_send_turnoff_msg, +}; + +static const struct ls_pcie_drvdata ls1043a_drvdata = { + .pf_lut_off = 0x10000, + .pm_support = true, + .scfg_support = true, + .ops = &ls1043a_pcie_host_ops, + .exit_from_l2 = ls1043a_pcie_exit_from_l2, }; static const struct ls_pcie_drvdata layerscape_drvdata = { - .pf_off = 0xc0000, + .pf_lut_off = 0xc0000, .pm_support = true, + .ops = &ls_pcie_host_ops, + .exit_from_l2 = ls_pcie_exit_from_l2, }; static const struct of_device_id ls_pcie_of_match[] = { { .compatible = "fsl,ls1012a-pcie", .data = &layerscape_drvdata }, { .compatible = "fsl,ls1021a-pcie", .data = &ls1021a_drvdata }, { .compatible = "fsl,ls1028a-pcie", .data = &layerscape_drvdata }, - { .compatible = "fsl,ls1043a-pcie", .data = &ls1021a_drvdata }, + { .compatible = "fsl,ls1043a-pcie", .data = &ls1043a_drvdata }, { .compatible = "fsl,ls1046a-pcie", .data = &layerscape_drvdata }, { .compatible = "fsl,ls2080a-pcie", .data = &layerscape_drvdata }, { .compatible = "fsl,ls2085a-pcie", .data = &layerscape_drvdata }, @@ -201,6 +328,8 @@ static int ls_pcie_probe(struct platform_device *pdev) struct dw_pcie *pci; struct ls_pcie *pcie; struct resource *dbi_base; + u32 index[2]; + int ret; pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) @@ -213,9 +342,8 @@ static int ls_pcie_probe(struct platform_device *pdev) pcie->drvdata = of_device_get_match_data(dev); pci->dev = dev; - pci->pp.ops = &ls_pcie_host_ops; - pcie->pci = pci; + pci->pp.ops = pcie->drvdata->ops; dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base); @@ -224,7 +352,21 @@ static int ls_pcie_probe(struct platform_device *pdev) pcie->big_endian = of_property_read_bool(dev->of_node, "big-endian"); - pcie->pf_base = pci->dbi_base + pcie->drvdata->pf_off; + pcie->pf_lut_base = pci->dbi_base + pcie->drvdata->pf_lut_off; + + if (pcie->drvdata->scfg_support) { + pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,pcie-scfg"); + if (IS_ERR(pcie->scfg)) { + dev_err(dev, "No syscfg phandle specified\n"); + return PTR_ERR(pcie->scfg); + } + + ret = of_property_read_u32_array(dev->of_node, "fsl,pcie-scfg", index, 2); + if (ret) + return ret; + + pcie->index = index[1]; + } if (!ls_pcie_is_bridge(pcie)) return -ENODEV; @@ -247,11 +389,14 @@ static int ls_pcie_suspend_noirq(struct device *dev) static int ls_pcie_resume_noirq(struct device *dev) { struct ls_pcie *pcie = dev_get_drvdata(dev); + int ret; if (!pcie->drvdata->pm_support) return 0; - ls_pcie_exit_from_l2(&pcie->pci->pp); + ret = pcie->drvdata->exit_from_l2(&pcie->pci->pp); + if (ret) + return ret; return dw_pcie_resume_noirq(pcie->pci); } diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index 407558f5d74a..6477c83262c2 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -389,7 +389,7 @@ static int meson_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops meson_pcie_host_ops = { - .host_init = meson_pcie_host_init, + .init = meson_pcie_host_init, }; static const struct dw_pcie_ops dw_pcie_ops = { diff --git a/drivers/pci/controller/dwc/pcie-al.c b/drivers/pci/controller/dwc/pcie-al.c index b8cb77c9c4bd..6dfdda59f328 100644 --- a/drivers/pci/controller/dwc/pcie-al.c +++ b/drivers/pci/controller/dwc/pcie-al.c @@ -311,7 +311,7 @@ static int al_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops al_pcie_host_ops = { - .host_init = al_pcie_host_init, + .init = al_pcie_host_init, }; static int al_pcie_probe(struct platform_device *pdev) diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c index 5c999e15c357..b5c599ccaacf 100644 --- a/drivers/pci/controller/dwc/pcie-armada8k.c +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -225,7 +225,7 @@ static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg) } static const struct dw_pcie_host_ops armada8k_pcie_host_ops = { - .host_init = armada8k_pcie_host_init, + .init = armada8k_pcie_host_init, }; static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 9b572a2b2c9a..9ed0a9ba7619 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -333,7 +333,7 @@ static int artpec6_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops artpec6_pcie_host_ops = { - .host_init = artpec6_pcie_host_init, + .init = artpec6_pcie_host_init, }; static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) @@ -352,15 +352,15 @@ static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) } static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - dev_err(pci->dev, "EP cannot trigger legacy IRQs\n"); + case PCI_IRQ_INTX: + dev_err(pci->dev, "EP cannot trigger INTx IRQs\n"); return -EINVAL; - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); default: dev_err(pci->dev, "UNKNOWN IRQ type\n"); @@ -370,7 +370,7 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct dw_pcie_ep_ops pcie_ep_ops = { - .ep_init = artpec6_pcie_ep_init, + .init = artpec6_pcie_ep_init, .raise_irq = artpec6_pcie_raise_irq, }; diff --git a/drivers/pci/controller/dwc/pcie-bt1.c b/drivers/pci/controller/dwc/pcie-bt1.c index 17e696797ff5..76d0ddea8007 100644 --- a/drivers/pci/controller/dwc/pcie-bt1.c +++ b/drivers/pci/controller/dwc/pcie-bt1.c @@ -559,8 +559,8 @@ static void bt1_pcie_host_deinit(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops bt1_pcie_host_ops = { - .host_init = bt1_pcie_host_init, - .host_deinit = bt1_pcie_host_deinit, + .init = bt1_pcie_host_init, + .deinit = bt1_pcie_host_deinit, }; static struct bt1_pcie *bt1_pcie_create_data(struct platform_device *pdev) diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index f6207989fc6a..9a437cfce073 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -6,6 +6,7 @@ * Author: Kishon Vijay Abraham I <kishon@ti.com> */ +#include <linux/align.h> #include <linux/bitfield.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -43,46 +44,19 @@ dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no) return NULL; } -static unsigned int dw_pcie_ep_func_select(struct dw_pcie_ep *ep, u8 func_no) -{ - unsigned int func_offset = 0; - - if (ep->ops->func_conf_select) - func_offset = ep->ops->func_conf_select(ep, func_no); - - return func_offset; -} - -static unsigned int dw_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep, u8 func_no) -{ - unsigned int dbi2_offset = 0; - - if (ep->ops->get_dbi2_offset) - dbi2_offset = ep->ops->get_dbi2_offset(ep, func_no); - else if (ep->ops->func_conf_select) /* for backward compatibility */ - dbi2_offset = ep->ops->func_conf_select(ep, func_no); - - return dbi2_offset; -} - static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, u8 func_no, enum pci_barno bar, int flags) { - unsigned int func_offset, dbi2_offset; struct dw_pcie_ep *ep = &pci->ep; - u32 reg, reg_dbi2; - - func_offset = dw_pcie_ep_func_select(ep, func_no); - dbi2_offset = dw_pcie_ep_get_dbi2_offset(ep, func_no); + u32 reg; - reg = func_offset + PCI_BASE_ADDRESS_0 + (4 * bar); - reg_dbi2 = dbi2_offset + PCI_BASE_ADDRESS_0 + (4 * bar); + reg = PCI_BASE_ADDRESS_0 + (4 * bar); dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writel_dbi2(pci, reg_dbi2, 0x0); - dw_pcie_writel_dbi(pci, reg, 0x0); + dw_pcie_ep_writel_dbi2(ep, func_no, reg, 0x0); + dw_pcie_ep_writel_dbi(ep, func_no, reg, 0x0); if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { - dw_pcie_writel_dbi2(pci, reg_dbi2 + 4, 0x0); - dw_pcie_writel_dbi(pci, reg + 4, 0x0); + dw_pcie_ep_writel_dbi2(ep, func_no, reg + 4, 0x0); + dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0x0); } dw_pcie_dbi_ro_wr_dis(pci); } @@ -99,19 +73,15 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) EXPORT_SYMBOL_GPL(dw_pcie_ep_reset_bar); static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie_ep *ep, u8 func_no, - u8 cap_ptr, u8 cap) + u8 cap_ptr, u8 cap) { - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - unsigned int func_offset = 0; u8 cap_id, next_cap_ptr; u16 reg; if (!cap_ptr) return 0; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = dw_pcie_readw_dbi(pci, func_offset + cap_ptr); + reg = dw_pcie_ep_readw_dbi(ep, func_no, cap_ptr); cap_id = (reg & 0x00ff); if (cap_id > PCI_CAP_ID_MAX) @@ -126,14 +96,10 @@ static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie_ep *ep, u8 func_no, static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap) { - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - unsigned int func_offset = 0; u8 next_cap_ptr; u16 reg; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = dw_pcie_readw_dbi(pci, func_offset + PCI_CAPABILITY_LIST); + reg = dw_pcie_ep_readw_dbi(ep, func_no, PCI_CAPABILITY_LIST); next_cap_ptr = (reg & 0x00ff); return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap); @@ -144,24 +110,21 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - unsigned int func_offset = 0; - - func_offset = dw_pcie_ep_func_select(ep, func_no); dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writew_dbi(pci, func_offset + PCI_VENDOR_ID, hdr->vendorid); - dw_pcie_writew_dbi(pci, func_offset + PCI_DEVICE_ID, hdr->deviceid); - dw_pcie_writeb_dbi(pci, func_offset + PCI_REVISION_ID, hdr->revid); - dw_pcie_writeb_dbi(pci, func_offset + PCI_CLASS_PROG, hdr->progif_code); - dw_pcie_writew_dbi(pci, func_offset + PCI_CLASS_DEVICE, - hdr->subclass_code | hdr->baseclass_code << 8); - dw_pcie_writeb_dbi(pci, func_offset + PCI_CACHE_LINE_SIZE, - hdr->cache_line_size); - dw_pcie_writew_dbi(pci, func_offset + PCI_SUBSYSTEM_VENDOR_ID, - hdr->subsys_vendor_id); - dw_pcie_writew_dbi(pci, func_offset + PCI_SUBSYSTEM_ID, hdr->subsys_id); - dw_pcie_writeb_dbi(pci, func_offset + PCI_INTERRUPT_PIN, - hdr->interrupt_pin); + dw_pcie_ep_writew_dbi(ep, func_no, PCI_VENDOR_ID, hdr->vendorid); + dw_pcie_ep_writew_dbi(ep, func_no, PCI_DEVICE_ID, hdr->deviceid); + dw_pcie_ep_writeb_dbi(ep, func_no, PCI_REVISION_ID, hdr->revid); + dw_pcie_ep_writeb_dbi(ep, func_no, PCI_CLASS_PROG, hdr->progif_code); + dw_pcie_ep_writew_dbi(ep, func_no, PCI_CLASS_DEVICE, + hdr->subclass_code | hdr->baseclass_code << 8); + dw_pcie_ep_writeb_dbi(ep, func_no, PCI_CACHE_LINE_SIZE, + hdr->cache_line_size); + dw_pcie_ep_writew_dbi(ep, func_no, PCI_SUBSYSTEM_VENDOR_ID, + hdr->subsys_vendor_id); + dw_pcie_ep_writew_dbi(ep, func_no, PCI_SUBSYSTEM_ID, hdr->subsys_id); + dw_pcie_ep_writeb_dbi(ep, func_no, PCI_INTERRUPT_PIN, + hdr->interrupt_pin); dw_pcie_dbi_ro_wr_dis(pci); return 0; @@ -243,18 +206,13 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - unsigned int func_offset, dbi2_offset; enum pci_barno bar = epf_bar->barno; size_t size = epf_bar->size; int flags = epf_bar->flags; - u32 reg, reg_dbi2; int ret, type; + u32 reg; - func_offset = dw_pcie_ep_func_select(ep, func_no); - dbi2_offset = dw_pcie_ep_get_dbi2_offset(ep, func_no); - - reg = PCI_BASE_ADDRESS_0 + (4 * bar) + func_offset; - reg_dbi2 = PCI_BASE_ADDRESS_0 + (4 * bar) + dbi2_offset; + reg = PCI_BASE_ADDRESS_0 + (4 * bar); if (!(flags & PCI_BASE_ADDRESS_SPACE)) type = PCIE_ATU_TYPE_MEM; @@ -270,12 +228,12 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writel_dbi2(pci, reg_dbi2, lower_32_bits(size - 1)); - dw_pcie_writel_dbi(pci, reg, flags); + dw_pcie_ep_writel_dbi2(ep, func_no, reg, lower_32_bits(size - 1)); + dw_pcie_ep_writel_dbi(ep, func_no, reg, flags); if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { - dw_pcie_writel_dbi2(pci, reg_dbi2 + 4, upper_32_bits(size - 1)); - dw_pcie_writel_dbi(pci, reg + 4, 0); + dw_pcie_ep_writel_dbi2(ep, func_no, reg + 4, upper_32_bits(size - 1)); + dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0); } ep->epf_bar[bar] = epf_bar; @@ -335,19 +293,15 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - u32 val, reg; - unsigned int func_offset = 0; struct dw_pcie_ep_func *ep_func; + u32 val, reg; ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); if (!ep_func || !ep_func->msi_cap) return -EINVAL; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS; - val = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msi_cap + PCI_MSI_FLAGS; + val = dw_pcie_ep_readw_dbi(ep, func_no, reg); if (!(val & PCI_MSI_FLAGS_ENABLE)) return -EINVAL; @@ -361,22 +315,19 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - u32 val, reg; - unsigned int func_offset = 0; struct dw_pcie_ep_func *ep_func; + u32 val, reg; ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); if (!ep_func || !ep_func->msi_cap) return -EINVAL; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS; - val = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msi_cap + PCI_MSI_FLAGS; + val = dw_pcie_ep_readw_dbi(ep, func_no, reg); val &= ~PCI_MSI_FLAGS_QMASK; val |= FIELD_PREP(PCI_MSI_FLAGS_QMASK, interrupts); dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writew_dbi(pci, reg, val); + dw_pcie_ep_writew_dbi(ep, func_no, reg, val); dw_pcie_dbi_ro_wr_dis(pci); return 0; @@ -385,19 +336,15 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - u32 val, reg; - unsigned int func_offset = 0; struct dw_pcie_ep_func *ep_func; + u32 val, reg; ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); if (!ep_func || !ep_func->msix_cap) return -EINVAL; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = ep_func->msix_cap + func_offset + PCI_MSIX_FLAGS; - val = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msix_cap + PCI_MSIX_FLAGS; + val = dw_pcie_ep_readw_dbi(ep, func_no, reg); if (!(val & PCI_MSIX_FLAGS_ENABLE)) return -EINVAL; @@ -411,9 +358,8 @@ static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - u32 val, reg; - unsigned int func_offset = 0; struct dw_pcie_ep_func *ep_func; + u32 val, reg; ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); if (!ep_func || !ep_func->msix_cap) @@ -421,21 +367,19 @@ static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, dw_pcie_dbi_ro_wr_en(pci); - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = ep_func->msix_cap + func_offset + PCI_MSIX_FLAGS; - val = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msix_cap + PCI_MSIX_FLAGS; + val = dw_pcie_ep_readw_dbi(ep, func_no, reg); val &= ~PCI_MSIX_FLAGS_QSIZE; val |= interrupts; dw_pcie_writew_dbi(pci, reg, val); - reg = ep_func->msix_cap + func_offset + PCI_MSIX_TABLE; + reg = ep_func->msix_cap + PCI_MSIX_TABLE; val = offset | bir; - dw_pcie_writel_dbi(pci, reg, val); + dw_pcie_ep_writel_dbi(ep, func_no, reg, val); - reg = ep_func->msix_cap + func_offset + PCI_MSIX_PBA; + reg = ep_func->msix_cap + PCI_MSIX_PBA; val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir; - dw_pcie_writel_dbi(pci, reg, val); + dw_pcie_ep_writel_dbi(ep, func_no, reg, val); dw_pcie_dbi_ro_wr_dis(pci); @@ -443,7 +387,7 @@ static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, } static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no, - enum pci_epc_irq_type type, u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); @@ -496,56 +440,53 @@ static const struct pci_epc_ops epc_ops = { .get_features = dw_pcie_ep_get_features, }; -int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no) +int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct device *dev = pci->dev; - dev_err(dev, "EP cannot trigger legacy IRQs\n"); + dev_err(dev, "EP cannot raise INTX IRQs\n"); return -EINVAL; } -EXPORT_SYMBOL_GPL(dw_pcie_ep_raise_legacy_irq); +EXPORT_SYMBOL_GPL(dw_pcie_ep_raise_intx_irq); int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, u8 interrupt_num) { - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + u32 msg_addr_lower, msg_addr_upper, reg; struct dw_pcie_ep_func *ep_func; struct pci_epc *epc = ep->epc; unsigned int aligned_offset; - unsigned int func_offset = 0; u16 msg_ctrl, msg_data; - u32 msg_addr_lower, msg_addr_upper, reg; - u64 msg_addr; bool has_upper; + u64 msg_addr; int ret; ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); if (!ep_func || !ep_func->msi_cap) return -EINVAL; - func_offset = dw_pcie_ep_func_select(ep, func_no); - /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */ - reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS; - msg_ctrl = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msi_cap + PCI_MSI_FLAGS; + msg_ctrl = dw_pcie_ep_readw_dbi(ep, func_no, reg); has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT); - reg = ep_func->msi_cap + func_offset + PCI_MSI_ADDRESS_LO; - msg_addr_lower = dw_pcie_readl_dbi(pci, reg); + reg = ep_func->msi_cap + PCI_MSI_ADDRESS_LO; + msg_addr_lower = dw_pcie_ep_readl_dbi(ep, func_no, reg); if (has_upper) { - reg = ep_func->msi_cap + func_offset + PCI_MSI_ADDRESS_HI; - msg_addr_upper = dw_pcie_readl_dbi(pci, reg); - reg = ep_func->msi_cap + func_offset + PCI_MSI_DATA_64; - msg_data = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msi_cap + PCI_MSI_ADDRESS_HI; + msg_addr_upper = dw_pcie_ep_readl_dbi(ep, func_no, reg); + reg = ep_func->msi_cap + PCI_MSI_DATA_64; + msg_data = dw_pcie_ep_readw_dbi(ep, func_no, reg); } else { msg_addr_upper = 0; - reg = ep_func->msi_cap + func_offset + PCI_MSI_DATA_32; - msg_data = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msi_cap + PCI_MSI_DATA_32; + msg_data = dw_pcie_ep_readw_dbi(ep, func_no, reg); } - aligned_offset = msg_addr_lower & (epc->mem->window.page_size - 1); - msg_addr = ((u64)msg_addr_upper) << 32 | - (msg_addr_lower & ~aligned_offset); + msg_addr = ((u64)msg_addr_upper) << 32 | msg_addr_lower; + + aligned_offset = msg_addr & (epc->mem->window.page_size - 1); + msg_addr = ALIGN_DOWN(msg_addr, epc->mem->window.page_size); ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr, epc->mem->window.page_size); if (ret) @@ -582,10 +523,9 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - struct dw_pcie_ep_func *ep_func; struct pci_epf_msix_tbl *msix_tbl; + struct dw_pcie_ep_func *ep_func; struct pci_epc *epc = ep->epc; - unsigned int func_offset = 0; u32 reg, msg_data, vec_ctrl; unsigned int aligned_offset; u32 tbl_offset; @@ -597,10 +537,8 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, if (!ep_func || !ep_func->msix_cap) return -EINVAL; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = ep_func->msix_cap + func_offset + PCI_MSIX_TABLE; - tbl_offset = dw_pcie_readl_dbi(pci, reg); + reg = ep_func->msix_cap + PCI_MSIX_TABLE; + tbl_offset = dw_pcie_ep_readl_dbi(ep, func_no, reg); bir = FIELD_GET(PCI_MSIX_TABLE_BIR, tbl_offset); tbl_offset &= PCI_MSIX_TABLE_OFFSET; @@ -615,6 +553,7 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, } aligned_offset = msg_addr & (epc->mem->window.page_size - 1); + msg_addr = ALIGN_DOWN(msg_addr, epc->mem->window.page_size); ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr, epc->mem->window.page_size); if (ret) @@ -794,8 +733,8 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) list_add_tail(&ep_func->list, &ep->func_list); } - if (ep->ops->ep_init) - ep->ops->ep_init(ep); + if (ep->ops->init) + ep->ops->init(ep); ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size, ep->page_size); diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 7991f0e179b2..d5fc31f8345f 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -441,14 +441,14 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) bridge->ops = &dw_pcie_ops; bridge->child_ops = &dw_child_pcie_ops; - if (pp->ops->host_init) { - ret = pp->ops->host_init(pp); + if (pp->ops->init) { + ret = pp->ops->init(pp); if (ret) return ret; } if (pci_msi_enabled()) { - pp->has_msi_ctrl = !(pp->ops->msi_host_init || + pp->has_msi_ctrl = !(pp->ops->msi_init || of_property_read_bool(np, "msi-parent") || of_property_read_bool(np, "msi-map")); @@ -464,8 +464,8 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) goto err_deinit_host; } - if (pp->ops->msi_host_init) { - ret = pp->ops->msi_host_init(pp); + if (pp->ops->msi_init) { + ret = pp->ops->msi_init(pp); if (ret < 0) goto err_deinit_host; } else if (pp->has_msi_ctrl) { @@ -502,8 +502,8 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) if (ret) goto err_stop_link; - if (pp->ops->host_post_init) - pp->ops->host_post_init(pp); + if (pp->ops->post_init) + pp->ops->post_init(pp); return 0; @@ -518,8 +518,8 @@ err_free_msi: dw_pcie_free_msi(pp); err_deinit_host: - if (pp->ops->host_deinit) - pp->ops->host_deinit(pp); + if (pp->ops->deinit) + pp->ops->deinit(pp); return ret; } @@ -539,8 +539,8 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp) if (pp->has_msi_ctrl) dw_pcie_free_msi(pp); - if (pp->ops->host_deinit) - pp->ops->host_deinit(pp); + if (pp->ops->deinit) + pp->ops->deinit(pp); } EXPORT_SYMBOL_GPL(dw_pcie_host_deinit); @@ -842,8 +842,8 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci) return ret; } - if (pci->pp.ops->host_deinit) - pci->pp.ops->host_deinit(&pci->pp); + if (pci->pp.ops->deinit) + pci->pp.ops->deinit(&pci->pp); pci->suspended = true; @@ -860,8 +860,8 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci) pci->suspended = false; - if (pci->pp.ops->host_init) { - ret = pci->pp.ops->host_init(&pci->pp); + if (pci->pp.ops->init) { + ret = pci->pp.ops->init(&pci->pp); if (ret) { dev_err(pci->dev, "Host init failed: %d\n", ret); return ret; diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index b625841e98aa..778588b4be70 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -42,17 +42,16 @@ static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep) } static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return dw_pcie_ep_raise_legacy_irq(ep, func_no); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_INTX: + return dw_pcie_ep_raise_intx_irq(ep, func_no); + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); default: dev_err(pci->dev, "UNKNOWN IRQ type\n"); @@ -74,7 +73,7 @@ dw_plat_pcie_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops pcie_ep_ops = { - .ep_init = dw_plat_pcie_ep_init, + .init = dw_plat_pcie_ep_init, .raise_irq = dw_plat_pcie_ep_raise_irq, .get_features = dw_plat_pcie_get_features, }; diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 55ff76e3d384..26dae4837462 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -300,10 +300,10 @@ enum dw_pcie_ltssm { }; struct dw_pcie_host_ops { - int (*host_init)(struct dw_pcie_rp *pp); - void (*host_deinit)(struct dw_pcie_rp *pp); - void (*host_post_init)(struct dw_pcie_rp *pp); - int (*msi_host_init)(struct dw_pcie_rp *pp); + int (*init)(struct dw_pcie_rp *pp); + void (*deinit)(struct dw_pcie_rp *pp); + void (*post_init)(struct dw_pcie_rp *pp); + int (*msi_init)(struct dw_pcie_rp *pp); void (*pme_turn_off)(struct dw_pcie_rp *pp); }; @@ -332,10 +332,10 @@ struct dw_pcie_rp { struct dw_pcie_ep_ops { void (*pre_init)(struct dw_pcie_ep *ep); - void (*ep_init)(struct dw_pcie_ep *ep); + void (*init)(struct dw_pcie_ep *ep); void (*deinit)(struct dw_pcie_ep *ep); int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u16 interrupt_num); + unsigned int type, u16 interrupt_num); const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep); /* * Provide a method to implement the different func config space @@ -344,7 +344,7 @@ struct dw_pcie_ep_ops { * return a 0, and implement code in callback function of platform * driver. */ - unsigned int (*func_conf_select)(struct dw_pcie_ep *ep, u8 func_no); + unsigned int (*get_dbi_offset)(struct dw_pcie_ep *ep, u8 func_no); unsigned int (*get_dbi2_offset)(struct dw_pcie_ep *ep, u8 func_no); }; @@ -486,6 +486,99 @@ static inline void dw_pcie_writel_dbi2(struct dw_pcie *pci, u32 reg, u32 val) dw_pcie_write_dbi2(pci, reg, 0x4, val); } +static inline unsigned int dw_pcie_ep_get_dbi_offset(struct dw_pcie_ep *ep, + u8 func_no) +{ + unsigned int dbi_offset = 0; + + if (ep->ops->get_dbi_offset) + dbi_offset = ep->ops->get_dbi_offset(ep, func_no); + + return dbi_offset; +} + +static inline u32 dw_pcie_ep_read_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, size_t size) +{ + unsigned int offset = dw_pcie_ep_get_dbi_offset(ep, func_no); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + return dw_pcie_read_dbi(pci, offset + reg, size); +} + +static inline void dw_pcie_ep_write_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, size_t size, u32 val) +{ + unsigned int offset = dw_pcie_ep_get_dbi_offset(ep, func_no); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + dw_pcie_write_dbi(pci, offset + reg, size, val); +} + +static inline void dw_pcie_ep_writel_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, u32 val) +{ + dw_pcie_ep_write_dbi(ep, func_no, reg, 0x4, val); +} + +static inline u32 dw_pcie_ep_readl_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg) +{ + return dw_pcie_ep_read_dbi(ep, func_no, reg, 0x4); +} + +static inline void dw_pcie_ep_writew_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, u16 val) +{ + dw_pcie_ep_write_dbi(ep, func_no, reg, 0x2, val); +} + +static inline u16 dw_pcie_ep_readw_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg) +{ + return dw_pcie_ep_read_dbi(ep, func_no, reg, 0x2); +} + +static inline void dw_pcie_ep_writeb_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, u8 val) +{ + dw_pcie_ep_write_dbi(ep, func_no, reg, 0x1, val); +} + +static inline u8 dw_pcie_ep_readb_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg) +{ + return dw_pcie_ep_read_dbi(ep, func_no, reg, 0x1); +} + +static inline unsigned int dw_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep, + u8 func_no) +{ + unsigned int dbi2_offset = 0; + + if (ep->ops->get_dbi2_offset) + dbi2_offset = ep->ops->get_dbi2_offset(ep, func_no); + else if (ep->ops->get_dbi_offset) /* for backward compatibility */ + dbi2_offset = ep->ops->get_dbi_offset(ep, func_no); + + return dbi2_offset; +} + +static inline void dw_pcie_ep_write_dbi2(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, size_t size, u32 val) +{ + unsigned int offset = dw_pcie_ep_get_dbi2_offset(ep, func_no); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + dw_pcie_write_dbi2(pci, offset + reg, size, val); +} + +static inline void dw_pcie_ep_writel_dbi2(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, u32 val) +{ + dw_pcie_ep_write_dbi2(ep, func_no, reg, 0x4, val); +} + static inline void dw_pcie_dbi_ro_wr_en(struct dw_pcie *pci) { u32 reg; @@ -580,7 +673,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep); int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep); void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep); void dw_pcie_ep_exit(struct dw_pcie_ep *ep); -int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no); +int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no); int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, u8 interrupt_num); int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, @@ -613,7 +706,7 @@ static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep) { } -static inline int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no) +static inline int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no) { return 0; } diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 2fe42c70097f..d6842141d384 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -72,7 +72,7 @@ static void rockchip_pcie_writel_apb(struct rockchip_pcie *rockchip, writel_relaxed(val, rockchip->apb_base + reg); } -static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc) +static void rockchip_pcie_intx_handler(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct rockchip_pcie *rockchip = irq_desc_get_handler_data(desc); @@ -202,7 +202,7 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) if (ret < 0) dev_err(dev, "failed to init irq domain\n"); - irq_set_chained_handler_and_data(irq, rockchip_pcie_legacy_int_handler, + irq_set_chained_handler_and_data(irq, rockchip_pcie_intx_handler, rockchip); /* LTSSM enable control mode */ @@ -215,7 +215,7 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops rockchip_pcie_host_ops = { - .host_init = rockchip_pcie_host_init, + .init = rockchip_pcie_host_init, }; static int rockchip_pcie_clk_init(struct rockchip_pcie *rockchip) diff --git a/drivers/pci/controller/dwc/pcie-fu740.c b/drivers/pci/controller/dwc/pcie-fu740.c index 1e9b44b8bba4..66367252032b 100644 --- a/drivers/pci/controller/dwc/pcie-fu740.c +++ b/drivers/pci/controller/dwc/pcie-fu740.c @@ -279,7 +279,7 @@ static int fu740_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops fu740_pcie_host_ops = { - .host_init = fu740_pcie_host_init, + .init = fu740_pcie_host_init, }; static const struct dw_pcie_ops dw_pcie_ops = { diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index fd484cc7c481..7a11c618b9d9 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -198,7 +198,7 @@ static int histb_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops histb_pcie_host_ops = { - .host_init = histb_pcie_host_init, + .init = histb_pcie_host_init, }; static void histb_pcie_host_disable(struct histb_pcie *hipcie) diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index c9c93524e01d..acbe4f6d3291 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -391,7 +391,7 @@ static const struct dw_pcie_ops intel_pcie_ops = { }; static const struct dw_pcie_host_ops intel_pcie_dw_ops = { - .host_init = intel_pcie_rc_init, + .init = intel_pcie_rc_init, }; static int intel_pcie_probe(struct platform_device *pdev) diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index 289bff99d762..208d3b0ba196 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -289,19 +289,18 @@ static void keembay_pcie_ep_init(struct dw_pcie_ep *ep) } static int keembay_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - /* Legacy interrupts are not supported in Keem Bay */ - dev_err(pci->dev, "Legacy IRQ is not supported\n"); + case PCI_IRQ_INTX: + /* INTx interrupts are not supported in Keem Bay */ + dev_err(pci->dev, "INTx IRQ is not supported\n"); return -EINVAL; - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); default: dev_err(pci->dev, "Unknown IRQ type %d\n", type); @@ -325,7 +324,7 @@ keembay_pcie_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops keembay_pcie_ep_ops = { - .ep_init = keembay_pcie_ep_init, + .init = keembay_pcie_ep_init, .raise_irq = keembay_pcie_ep_raise_irq, .get_features = keembay_pcie_get_features, }; diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index 2ee146767971..d5523f302102 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -366,7 +366,6 @@ static int kirin_pcie_get_gpio_enable(struct kirin_pcie *pcie, struct platform_device *pdev) { struct device *dev = &pdev->dev; - char name[32]; int ret, i; /* This is an optional property */ @@ -387,9 +386,8 @@ static int kirin_pcie_get_gpio_enable(struct kirin_pcie *pcie, if (pcie->gpio_id_clkreq[i] < 0) return pcie->gpio_id_clkreq[i]; - sprintf(name, "pcie_clkreq_%d", i); - pcie->clkreq_names[i] = devm_kstrdup_const(dev, name, - GFP_KERNEL); + pcie->clkreq_names[i] = devm_kasprintf(dev, GFP_KERNEL, + "pcie_clkreq_%d", i); if (!pcie->clkreq_names[i]) return -ENOMEM; } @@ -404,7 +402,6 @@ static int kirin_pcie_parse_port(struct kirin_pcie *pcie, struct device *dev = &pdev->dev; struct device_node *parent, *child; int ret, slot, i; - char name[32]; for_each_available_child_of_node(node, parent) { for_each_available_child_of_node(parent, child) { @@ -430,9 +427,9 @@ static int kirin_pcie_parse_port(struct kirin_pcie *pcie, slot = PCI_SLOT(ret); - sprintf(name, "pcie_perst_%d", slot); - pcie->reset_names[i] = devm_kstrdup_const(dev, name, - GFP_KERNEL); + pcie->reset_names[i] = devm_kasprintf(dev, GFP_KERNEL, + "pcie_perst_%d", + slot); if (!pcie->reset_names[i]) { ret = -ENOMEM; goto put_node; @@ -672,7 +669,7 @@ static const struct dw_pcie_ops kirin_dw_pcie_ops = { }; static const struct dw_pcie_host_ops kirin_pcie_host_ops = { - .host_init = kirin_pcie_host_init, + .init = kirin_pcie_host_init, }; static int kirin_pcie_power_off(struct kirin_pcie *kirin_pcie) @@ -741,15 +738,13 @@ err: return ret; } -static int kirin_pcie_remove(struct platform_device *pdev) +static void kirin_pcie_remove(struct platform_device *pdev) { struct kirin_pcie *kirin_pcie = platform_get_drvdata(pdev); dw_pcie_host_deinit(&kirin_pcie->pci->pp); kirin_pcie_power_off(kirin_pcie); - - return 0; } struct kirin_pcie_data { @@ -818,7 +813,7 @@ static int kirin_pcie_probe(struct platform_device *pdev) static struct platform_driver kirin_pcie_driver = { .probe = kirin_pcie_probe, - .remove = kirin_pcie_remove, + .remove_new = kirin_pcie_remove, .driver = { .name = "kirin-pcie", .of_match_table = kirin_pcie_match, diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 9e58f055199a..36e5e80cd22f 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -726,14 +726,14 @@ static int qcom_pcie_ep_enable_irq_resources(struct platform_device *pdev, } static int qcom_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return dw_pcie_ep_raise_legacy_irq(ep, func_no); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_INTX: + return dw_pcie_ep_raise_intx_irq(ep, func_no); + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); default: dev_err(pci->dev, "Unknown IRQ type\n"); @@ -796,7 +796,7 @@ static void qcom_pcie_ep_init(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops pci_ep_ops = { - .ep_init = qcom_pcie_ep_init, + .init = qcom_pcie_ep_init, .raise_irq = qcom_pcie_ep_raise_irq, .get_features = qcom_pcie_epc_get_features, }; diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 11c80555d975..2ce2a3bd932b 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -972,7 +972,7 @@ static int qcom_pcie_enable_aspm(struct pci_dev *pdev, void *userdata) * Downstream devices need to be in D0 state before enabling PCI PM * substates. */ - pci_set_power_state(pdev, PCI_D0); + pci_set_power_state_locked(pdev, PCI_D0); pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL); return 0; @@ -1247,9 +1247,9 @@ static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops qcom_pcie_dw_ops = { - .host_init = qcom_pcie_host_init, - .host_deinit = qcom_pcie_host_deinit, - .host_post_init = qcom_pcie_host_post_init, + .init = qcom_pcie_host_init, + .deinit = qcom_pcie_host_deinit, + .post_init = qcom_pcie_host_post_init, }; /* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */ diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 3bc45e513b3d..e9166619b1f9 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -8,7 +8,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> @@ -307,8 +307,8 @@ static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops rcar_gen4_pcie_host_ops = { - .host_init = rcar_gen4_pcie_host_init, - .host_deinit = rcar_gen4_pcie_host_deinit, + .init = rcar_gen4_pcie_host_init, + .deinit = rcar_gen4_pcie_host_deinit, }; static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar) @@ -362,15 +362,14 @@ static void rcar_gen4_pcie_ep_deinit(struct dw_pcie_ep *ep) } static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *dw = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return dw_pcie_ep_raise_legacy_irq(ep, func_no); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_INTX: + return dw_pcie_ep_raise_intx_irq(ep, func_no); + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); default: dev_err(dw->dev, "Unknown IRQ type\n"); @@ -394,7 +393,7 @@ rcar_gen4_pcie_ep_get_features(struct dw_pcie_ep *ep) return &rcar_gen4_pcie_epc_features; } -static unsigned int rcar_gen4_pcie_ep_func_conf_select(struct dw_pcie_ep *ep, +static unsigned int rcar_gen4_pcie_ep_get_dbi_offset(struct dw_pcie_ep *ep, u8 func_no) { return func_no * RCAR_GEN4_PCIE_EP_FUNC_DBI_OFFSET; @@ -408,11 +407,11 @@ static unsigned int rcar_gen4_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep, static const struct dw_pcie_ep_ops pcie_ep_ops = { .pre_init = rcar_gen4_pcie_ep_pre_init, - .ep_init = rcar_gen4_pcie_ep_init, + .init = rcar_gen4_pcie_ep_init, .deinit = rcar_gen4_pcie_ep_deinit, .raise_irq = rcar_gen4_pcie_ep_raise_irq, .get_features = rcar_gen4_pcie_ep_get_features, - .func_conf_select = rcar_gen4_pcie_ep_func_conf_select, + .get_dbi_offset = rcar_gen4_pcie_ep_get_dbi_offset, .get_dbi2_offset = rcar_gen4_pcie_ep_get_dbi2_offset, }; @@ -436,7 +435,7 @@ static void rcar_gen4_remove_dw_pcie_ep(struct rcar_gen4_pcie *rcar) /* Common */ static int rcar_gen4_add_dw_pcie(struct rcar_gen4_pcie *rcar) { - rcar->mode = (enum dw_pcie_device_mode)of_device_get_match_data(&rcar->pdev->dev); + rcar->mode = (uintptr_t)of_device_get_match_data(&rcar->pdev->dev); switch (rcar->mode) { case DW_PCIE_RC_TYPE: diff --git a/drivers/pci/controller/dwc/pcie-spear13xx.c b/drivers/pci/controller/dwc/pcie-spear13xx.c index 99d47ae80331..201dced209f0 100644 --- a/drivers/pci/controller/dwc/pcie-spear13xx.c +++ b/drivers/pci/controller/dwc/pcie-spear13xx.c @@ -148,7 +148,7 @@ static int spear13xx_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops spear13xx_pcie_host_ops = { - .host_init = spear13xx_pcie_host_init, + .init = spear13xx_pcie_host_init, }; static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie, diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 0fe113598ebb..7afa9e9aabe2 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -773,13 +773,13 @@ static void tegra_pcie_enable_system_interrupts(struct dw_pcie_rp *pp) val_w); } -static void tegra_pcie_enable_legacy_interrupts(struct dw_pcie_rp *pp) +static void tegra_pcie_enable_intx_interrupts(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); u32 val; - /* Enable legacy interrupt generation */ + /* Enable INTX interrupt generation */ val = appl_readl(pcie, APPL_INTR_EN_L0_0); val |= APPL_INTR_EN_L0_0_SYS_INTR_EN; val |= APPL_INTR_EN_L0_0_INT_INT_EN; @@ -830,7 +830,7 @@ static void tegra_pcie_enable_interrupts(struct dw_pcie_rp *pp) appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_17); tegra_pcie_enable_system_interrupts(pp); - tegra_pcie_enable_legacy_interrupts(pp); + tegra_pcie_enable_intx_interrupts(pp); if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_enable_msi_interrupts(pp); } @@ -1060,7 +1060,7 @@ static const struct dw_pcie_ops tegra_dw_pcie_ops = { }; static const struct dw_pcie_host_ops tegra_pcie_dw_host_ops = { - .host_init = tegra_pcie_dw_host_init, + .init = tegra_pcie_dw_host_init, }; static void tegra_pcie_disable_phy(struct tegra_pcie_dw *pcie) @@ -1947,7 +1947,7 @@ static irqreturn_t tegra_pcie_ep_pex_rst_irq(int irq, void *arg) return IRQ_HANDLED; } -static int tegra_pcie_ep_raise_legacy_irq(struct tegra_pcie_dw *pcie, u16 irq) +static int tegra_pcie_ep_raise_intx_irq(struct tegra_pcie_dw *pcie, u16 irq) { /* Tegra194 supports only INTA */ if (irq > 1) @@ -1979,20 +1979,19 @@ static int tegra_pcie_ep_raise_msix_irq(struct tegra_pcie_dw *pcie, u16 irq) } static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return tegra_pcie_ep_raise_legacy_irq(pcie, interrupt_num); + case PCI_IRQ_INTX: + return tegra_pcie_ep_raise_intx_irq(pcie, interrupt_num); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_MSI: return tegra_pcie_ep_raise_msi_irq(pcie, interrupt_num); - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: return tegra_pcie_ep_raise_msix_irq(pcie, interrupt_num); default: diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c index cba3c88fcf39..3fced0d3e851 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c @@ -212,7 +212,7 @@ static void uniphier_pcie_ep_init(struct dw_pcie_ep *ep) dw_pcie_ep_reset_bar(pci, bar); } -static int uniphier_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep) +static int uniphier_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct uniphier_pcie_ep_priv *priv = to_uniphier_pcie(pci); @@ -256,15 +256,14 @@ static int uniphier_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, } static int uniphier_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return uniphier_pcie_ep_raise_legacy_irq(ep); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_INTX: + return uniphier_pcie_ep_raise_intx_irq(ep); + case PCI_IRQ_MSI: return uniphier_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); default: @@ -284,7 +283,7 @@ uniphier_pcie_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops uniphier_pcie_ep_ops = { - .ep_init = uniphier_pcie_ep_init, + .init = uniphier_pcie_ep_init, .raise_irq = uniphier_pcie_ep_raise_irq, .get_features = uniphier_pcie_get_features, }; diff --git a/drivers/pci/controller/dwc/pcie-uniphier.c b/drivers/pci/controller/dwc/pcie-uniphier.c index 48c3eba817b4..5757ca3803c9 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier.c +++ b/drivers/pci/controller/dwc/pcie-uniphier.c @@ -67,7 +67,7 @@ struct uniphier_pcie { struct clk *clk; struct reset_control *rst; struct phy *phy; - struct irq_domain *legacy_irq_domain; + struct irq_domain *intx_irq_domain; }; #define to_uniphier_pcie(x) dev_get_drvdata((x)->dev) @@ -253,12 +253,12 @@ static void uniphier_pcie_irq_handler(struct irq_desc *desc) reg = FIELD_GET(PCL_RCV_INTX_ALL_STATUS, val); for_each_set_bit(bit, ®, PCI_NUM_INTX) - generic_handle_domain_irq(pcie->legacy_irq_domain, bit); + generic_handle_domain_irq(pcie->intx_irq_domain, bit); chained_irq_exit(chip, desc); } -static int uniphier_pcie_config_legacy_irq(struct dw_pcie_rp *pp) +static int uniphier_pcie_config_intx_irq(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct uniphier_pcie *pcie = to_uniphier_pcie(pci); @@ -279,9 +279,9 @@ static int uniphier_pcie_config_legacy_irq(struct dw_pcie_rp *pp) goto out_put_node; } - pcie->legacy_irq_domain = irq_domain_add_linear(np_intc, PCI_NUM_INTX, + pcie->intx_irq_domain = irq_domain_add_linear(np_intc, PCI_NUM_INTX, &uniphier_intx_domain_ops, pp); - if (!pcie->legacy_irq_domain) { + if (!pcie->intx_irq_domain) { dev_err(pci->dev, "Failed to get INTx domain\n"); ret = -ENODEV; goto out_put_node; @@ -301,7 +301,7 @@ static int uniphier_pcie_host_init(struct dw_pcie_rp *pp) struct uniphier_pcie *pcie = to_uniphier_pcie(pci); int ret; - ret = uniphier_pcie_config_legacy_irq(pp); + ret = uniphier_pcie_config_intx_irq(pp); if (ret) return ret; @@ -311,7 +311,7 @@ static int uniphier_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops uniphier_pcie_host_ops = { - .host_init = uniphier_pcie_host_init, + .init = uniphier_pcie_host_init, }; static int uniphier_pcie_host_enable(struct uniphier_pcie *pcie) diff --git a/drivers/pci/controller/dwc/pcie-visconti.c b/drivers/pci/controller/dwc/pcie-visconti.c index 71026fefa366..318c278e65c8 100644 --- a/drivers/pci/controller/dwc/pcie-visconti.c +++ b/drivers/pci/controller/dwc/pcie-visconti.c @@ -236,7 +236,7 @@ static int visconti_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops visconti_pcie_host_ops = { - .host_init = visconti_pcie_host_init, + .init = visconti_pcie_host_init, }; static int visconti_get_resources(struct platform_device *pdev, diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index 6be3266cd7b5..45b71806182d 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -85,7 +85,7 @@ int pci_host_common_probe(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(pci_host_common_probe); -int pci_host_common_remove(struct platform_device *pdev) +void pci_host_common_remove(struct platform_device *pdev) { struct pci_host_bridge *bridge = platform_get_drvdata(pdev); @@ -93,8 +93,6 @@ int pci_host_common_remove(struct platform_device *pdev) pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); pci_unlock_rescan_remove(); - - return 0; } EXPORT_SYMBOL_GPL(pci_host_common_remove); diff --git a/drivers/pci/controller/pci-host-generic.c b/drivers/pci/controller/pci-host-generic.c index 63865aeb636b..41cb6a057f6e 100644 --- a/drivers/pci/controller/pci-host-generic.c +++ b/drivers/pci/controller/pci-host-generic.c @@ -82,7 +82,7 @@ static struct platform_driver gen_pci_driver = { .of_match_table = gen_pci_of_match, }, .probe = pci_host_common_probe, - .remove = pci_host_common_remove, + .remove_new = pci_host_common_remove, }; module_platform_driver(gen_pci_driver); diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index f9dd6622fe10..5b0730c3891b 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -48,6 +48,9 @@ #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY 0x04dc #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK 0xc00 +#define PCIE_RC_CFG_PRIV1_ROOT_CAP 0x4f8 +#define PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK 0xf8 + #define PCIE_RC_DL_MDIO_ADDR 0x1100 #define PCIE_RC_DL_MDIO_WR_DATA 0x1104 #define PCIE_RC_DL_MDIO_RD_DATA 0x1108 @@ -121,9 +124,12 @@ #define PCIE_MISC_HARD_PCIE_HARD_DEBUG 0x4204 #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2 +#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK 0x200000 #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000 #define PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x00800000 - +#define PCIE_CLKREQ_MASK \ + (PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK | \ + PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK) #define PCIE_INTR2_CPU_BASE 0x4300 #define PCIE_MSI_INTR2_BASE 0x4500 @@ -1028,13 +1034,89 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) return 0; } +/* + * This extends the timeout period for an access to an internal bus. This + * access timeout may occur during L1SS sleep periods, even without the + * presence of a PCIe access. + */ +static void brcm_extend_rbus_timeout(struct brcm_pcie *pcie) +{ + /* TIMEOUT register is two registers before RGR1_SW_INIT_1 */ + const unsigned int REG_OFFSET = PCIE_RGR1_SW_INIT_1(pcie) - 8; + u32 timeout_us = 4000000; /* 4 seconds, our setting for L1SS */ + + /* Each unit in timeout register is 1/216,000,000 seconds */ + writel(216 * timeout_us, pcie->base + REG_OFFSET); +} + +static void brcm_config_clkreq(struct brcm_pcie *pcie) +{ + static const char err_msg[] = "invalid 'brcm,clkreq-mode' DT string\n"; + const char *mode = "default"; + u32 clkreq_cntl; + int ret, tmp; + + ret = of_property_read_string(pcie->np, "brcm,clkreq-mode", &mode); + if (ret && ret != -EINVAL) { + dev_err(pcie->dev, err_msg); + mode = "safe"; + } + + /* Start out assuming safe mode (both mode bits cleared) */ + clkreq_cntl = readl(pcie->base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + clkreq_cntl &= ~PCIE_CLKREQ_MASK; + + if (strcmp(mode, "no-l1ss") == 0) { + /* + * "no-l1ss" -- Provides Clock Power Management, L0s, and + * L1, but cannot provide L1 substate (L1SS) power + * savings. If the downstream device connected to the RC is + * L1SS capable AND the OS enables L1SS, all PCIe traffic + * may abruptly halt, potentially hanging the system. + */ + clkreq_cntl |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK; + /* + * We want to un-advertise L1 substates because if the OS + * tries to configure the controller into using L1 substate + * power savings it may fail or hang when the RC HW is in + * "no-l1ss" mode. + */ + tmp = readl(pcie->base + PCIE_RC_CFG_PRIV1_ROOT_CAP); + u32p_replace_bits(&tmp, 2, PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK); + writel(tmp, pcie->base + PCIE_RC_CFG_PRIV1_ROOT_CAP); + + } else if (strcmp(mode, "default") == 0) { + /* + * "default" -- Provides L0s, L1, and L1SS, but not + * compliant to provide Clock Power Management; + * specifically, may not be able to meet the Tclron max + * timing of 400ns as specified in "Dynamic Clock Control", + * section 3.2.5.2.2 of the PCIe spec. This situation is + * atypical and should happen only with older devices. + */ + clkreq_cntl |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK; + brcm_extend_rbus_timeout(pcie); + + } else { + /* + * "safe" -- No power savings; refclk is driven by RC + * unconditionally. + */ + if (strcmp(mode, "safe") != 0) + dev_err(pcie->dev, err_msg); + mode = "safe"; + } + writel(clkreq_cntl, pcie->base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + + dev_info(pcie->dev, "clkreq-mode set to %s\n", mode); +} + static int brcm_pcie_start_link(struct brcm_pcie *pcie) { struct device *dev = pcie->dev; void __iomem *base = pcie->base; u16 nlw, cls, lnksta; bool ssc_good = false; - u32 tmp; int ret, i; /* Unassert the fundamental reset */ @@ -1059,6 +1141,8 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) return -ENODEV; } + brcm_config_clkreq(pcie); + if (pcie->gen) brcm_pcie_set_gen(pcie, pcie->gen); @@ -1077,14 +1161,6 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) pci_speed_string(pcie_link_speed[cls]), nlw, ssc_good ? "(SSC)" : "(!SSC)"); - /* - * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1 - * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1. - */ - tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); - tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK; - writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); - return 0; } diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c index acdc583d2980..4e6aa882a567 100644 --- a/drivers/pci/controller/pcie-iproc-platform.c +++ b/drivers/pci/controller/pcie-iproc-platform.c @@ -52,7 +52,7 @@ static int iproc_pltfm_pcie_probe(struct platform_device *pdev) pcie = pci_host_bridge_priv(bridge); pcie->dev = dev; - pcie->type = (enum iproc_pcie_type) of_device_get_match_data(dev); + pcie->type = (uintptr_t)of_device_get_match_data(dev); ret = of_address_to_resource(np, 0, ®); if (ret < 0) { diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index e0e27645fdf4..975b3024fb08 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -245,35 +245,60 @@ static int mtk_pcie_set_trans_table(struct mtk_gen3_pcie *pcie, resource_size_t cpu_addr, resource_size_t pci_addr, resource_size_t size, - unsigned long type, int num) + unsigned long type, int *num) { + resource_size_t remaining = size; + resource_size_t table_size; + resource_size_t addr_align; + const char *range_type; void __iomem *table; u32 val; - if (num >= PCIE_MAX_TRANS_TABLES) { - dev_err(pcie->dev, "not enough translate table for addr: %#llx, limited to [%d]\n", - (unsigned long long)cpu_addr, PCIE_MAX_TRANS_TABLES); - return -ENODEV; - } + while (remaining && (*num < PCIE_MAX_TRANS_TABLES)) { + /* Table size needs to be a power of 2 */ + table_size = BIT(fls(remaining) - 1); + + if (cpu_addr > 0) { + addr_align = BIT(ffs(cpu_addr) - 1); + table_size = min(table_size, addr_align); + } + + /* Minimum size of translate table is 4KiB */ + if (table_size < 0x1000) { + dev_err(pcie->dev, "illegal table size %#llx\n", + (unsigned long long)table_size); + return -EINVAL; + } - table = pcie->base + PCIE_TRANS_TABLE_BASE_REG + - num * PCIE_ATR_TLB_SET_OFFSET; + table = pcie->base + PCIE_TRANS_TABLE_BASE_REG + *num * PCIE_ATR_TLB_SET_OFFSET; + writel_relaxed(lower_32_bits(cpu_addr) | PCIE_ATR_SIZE(fls(table_size) - 1), table); + writel_relaxed(upper_32_bits(cpu_addr), table + PCIE_ATR_SRC_ADDR_MSB_OFFSET); + writel_relaxed(lower_32_bits(pci_addr), table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET); + writel_relaxed(upper_32_bits(pci_addr), table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET); - writel_relaxed(lower_32_bits(cpu_addr) | PCIE_ATR_SIZE(fls(size) - 1), - table); - writel_relaxed(upper_32_bits(cpu_addr), - table + PCIE_ATR_SRC_ADDR_MSB_OFFSET); - writel_relaxed(lower_32_bits(pci_addr), - table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET); - writel_relaxed(upper_32_bits(pci_addr), - table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET); + if (type == IORESOURCE_IO) { + val = PCIE_ATR_TYPE_IO | PCIE_ATR_TLP_TYPE_IO; + range_type = "IO"; + } else { + val = PCIE_ATR_TYPE_MEM | PCIE_ATR_TLP_TYPE_MEM; + range_type = "MEM"; + } - if (type == IORESOURCE_IO) - val = PCIE_ATR_TYPE_IO | PCIE_ATR_TLP_TYPE_IO; - else - val = PCIE_ATR_TYPE_MEM | PCIE_ATR_TLP_TYPE_MEM; + writel_relaxed(val, table + PCIE_ATR_TRSL_PARAM_OFFSET); - writel_relaxed(val, table + PCIE_ATR_TRSL_PARAM_OFFSET); + dev_dbg(pcie->dev, "set %s trans window[%d]: cpu_addr = %#llx, pci_addr = %#llx, size = %#llx\n", + range_type, *num, (unsigned long long)cpu_addr, + (unsigned long long)pci_addr, (unsigned long long)table_size); + + cpu_addr += table_size; + pci_addr += table_size; + remaining -= table_size; + (*num)++; + } + + if (remaining) + dev_warn(pcie->dev, "not enough translate table for addr: %#llx, limited to [%d]\n", + (unsigned long long)cpu_addr, PCIE_MAX_TRANS_TABLES); return 0; } @@ -380,30 +405,20 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) resource_size_t cpu_addr; resource_size_t pci_addr; resource_size_t size; - const char *range_type; - if (type == IORESOURCE_IO) { + if (type == IORESOURCE_IO) cpu_addr = pci_pio_to_address(res->start); - range_type = "IO"; - } else if (type == IORESOURCE_MEM) { + else if (type == IORESOURCE_MEM) cpu_addr = res->start; - range_type = "MEM"; - } else { + else continue; - } pci_addr = res->start - entry->offset; size = resource_size(res); err = mtk_pcie_set_trans_table(pcie, cpu_addr, pci_addr, size, - type, table_index); + type, &table_index); if (err) return err; - - dev_dbg(pcie->dev, "set %s trans window[%d]: cpu_addr = %#llx, pci_addr = %#llx, size = %#llx\n", - range_type, table_index, (unsigned long long)cpu_addr, - (unsigned long long)pci_addr, (unsigned long long)size); - - table_index++; } return 0; diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 66a8f73296fc..48372013f26d 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -617,12 +617,18 @@ static void mtk_pcie_intr_handler(struct irq_desc *desc) if (status & MSI_STATUS){ unsigned long imsi_status; + /* + * The interrupt status can be cleared even if the + * MSI status remains pending. As such, given the + * edge-triggered interrupt type, its status should + * be cleared before being dispatched to the + * handler of the underlying device. + */ + writel(MSI_STATUS, port->base + PCIE_INT_STATUS); while ((imsi_status = readl(port->base + PCIE_IMSI_STATUS))) { for_each_set_bit(bit, &imsi_status, MTK_MSI_IRQS_NUM) generic_handle_domain_irq(port->inner_domain, bit); } - /* Clear MSI interrupt status */ - writel(MSI_STATUS, port->base + PCIE_INT_STATUS); } } diff --git a/drivers/pci/controller/pcie-rcar-ep.c b/drivers/pci/controller/pcie-rcar-ep.c index 7034c0ff23d0..e6909271def7 100644 --- a/drivers/pci/controller/pcie-rcar-ep.c +++ b/drivers/pci/controller/pcie-rcar-ep.c @@ -402,16 +402,15 @@ static int rcar_pcie_ep_assert_msi(struct rcar_pcie *pcie, } static int rcar_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); switch (type) { - case PCI_EPC_IRQ_LEGACY: + case PCI_IRQ_INTX: return rcar_pcie_ep_assert_intx(ep, fn, 0); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_MSI: return rcar_pcie_ep_assert_msi(&ep->pcie, fn, interrupt_num); default: diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index bf7cc0b6a695..996077ab7cfd 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -29,6 +29,7 @@ #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> #include "pcie-rcar.h" @@ -953,14 +954,22 @@ static const struct of_device_id rcar_pcie_of_match[] = { {}, }; +/* Design note 346 from Linear Technology says order is not important. */ +static const char * const rcar_pcie_supplies[] = { + "vpcie1v5", + "vpcie3v3", + "vpcie12v", +}; + static int rcar_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct pci_host_bridge *bridge; struct rcar_pcie_host *host; struct rcar_pcie *pcie; + unsigned int i; u32 data; int err; - struct pci_host_bridge *bridge; bridge = devm_pci_alloc_host_bridge(dev, sizeof(*host)); if (!bridge) @@ -971,6 +980,13 @@ static int rcar_pcie_probe(struct platform_device *pdev) pcie->dev = dev; platform_set_drvdata(pdev, host); + for (i = 0; i < ARRAY_SIZE(rcar_pcie_supplies); i++) { + err = devm_regulator_get_enable_optional(dev, rcar_pcie_supplies[i]); + if (err < 0 && err != -ENODEV) + return dev_err_probe(dev, err, "failed to enable regulator: %s\n", + rcar_pcie_supplies[i]); + } + pm_runtime_enable(pcie->dev); err = pm_runtime_get_sync(pcie->dev); if (err < 0) { diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index 0af0e965fb57..c9046e97a1d2 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -26,16 +26,16 @@ * @max_regions: maximum number of regions supported by hardware * @ob_region_map: bitmask of mapped outbound regions * @ob_addr: base addresses in the AXI bus where the outbound regions start - * @irq_phys_addr: base address on the AXI bus where the MSI/legacy IRQ + * @irq_phys_addr: base address on the AXI bus where the MSI/INTX IRQ * dedicated outbound regions is mapped. * @irq_cpu_addr: base address in the CPU space where a write access triggers - * the sending of a memory write (MSI) / normal message (legacy + * the sending of a memory write (MSI) / normal message (INTX * IRQ) TLP through the PCIe bus. - * @irq_pci_addr: used to save the current mapping of the MSI/legacy IRQ + * @irq_pci_addr: used to save the current mapping of the MSI/INTX IRQ * dedicated outbound region. * @irq_pci_fn: the latest PCI function that has updated the mapping of - * the MSI/legacy IRQ dedicated outbound region. - * @irq_pending: bitmask of asserted legacy IRQs. + * the MSI/INTX IRQ dedicated outbound region. + * @irq_pending: bitmask of asserted INTX IRQs. */ struct rockchip_pcie_ep { struct rockchip_pcie rockchip; @@ -325,8 +325,8 @@ static void rockchip_pcie_ep_assert_intx(struct rockchip_pcie_ep *ep, u8 fn, } } -static int rockchip_pcie_ep_send_legacy_irq(struct rockchip_pcie_ep *ep, u8 fn, - u8 intx) +static int rockchip_pcie_ep_send_intx_irq(struct rockchip_pcie_ep *ep, u8 fn, + u8 intx) { u16 cmd; @@ -407,15 +407,14 @@ static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn, } static int rockchip_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return rockchip_pcie_ep_send_legacy_irq(ep, fn, 0); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_INTX: + return rockchip_pcie_ep_send_intx_irq(ep, fn, 0); + case PCI_IRQ_MSI: return rockchip_pcie_ep_send_msi_irq(ep, fn, interrupt_num); default: return -EINVAL; diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index afbbdccd195d..300b9dc85ecc 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -505,7 +505,7 @@ static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg) return IRQ_HANDLED; } -static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc) +static void rockchip_pcie_intx_handler(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct rockchip_pcie *rockchip = irq_desc_get_handler_data(desc); @@ -553,7 +553,7 @@ static int rockchip_pcie_setup_irq(struct rockchip_pcie *rockchip) return irq; irq_set_chained_handler_and_data(irq, - rockchip_pcie_legacy_int_handler, + rockchip_pcie_intx_handler, rockchip); irq = platform_get_irq_byname(pdev, "client"); diff --git a/drivers/pci/controller/pcie-xilinx-dma-pl.c b/drivers/pci/controller/pcie-xilinx-dma-pl.c index 2f7d676c683c..5be5dfd8398f 100644 --- a/drivers/pci/controller/pcie-xilinx-dma-pl.c +++ b/drivers/pci/controller/pcie-xilinx-dma-pl.c @@ -576,7 +576,7 @@ static int xilinx_pl_dma_pcie_init_irq_domain(struct pl_dma_pcie *port) &intx_domain_ops, port); if (!port->intx_domain) { dev_err(dev, "Failed to get a INTx IRQ domain\n"); - return PTR_ERR(port->intx_domain); + return -ENOMEM; } irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); @@ -635,14 +635,14 @@ static int xilinx_pl_dma_pcie_setup_irq(struct pl_dma_pcie *port) err = devm_request_irq(dev, port->intx_irq, xilinx_pl_dma_pcie_intx_flow, IRQF_SHARED | IRQF_NO_THREAD, NULL, port); if (err) { - dev_err(dev, "Failed to request INTx IRQ %d\n", irq); + dev_err(dev, "Failed to request INTx IRQ %d\n", port->intx_irq); return err; } err = devm_request_irq(dev, port->irq, xilinx_pl_dma_pcie_event_flow, IRQF_SHARED | IRQF_NO_THREAD, NULL, port); if (err) { - dev_err(dev, "Failed to request event IRQ %d\n", irq); + dev_err(dev, "Failed to request event IRQ %d\n", port->irq); return err; } @@ -684,10 +684,8 @@ static int xilinx_request_msi_irq(struct pl_dma_pcie *port) int ret; port->msi.irq_msi0 = platform_get_irq_byname(pdev, "msi0"); - if (port->msi.irq_msi0 <= 0) { - dev_err(dev, "Unable to find msi0 IRQ line\n"); + if (port->msi.irq_msi0 <= 0) return port->msi.irq_msi0; - } ret = devm_request_irq(dev, port->msi.irq_msi0, xilinx_pl_dma_pcie_msi_handler_low, IRQF_SHARED | IRQF_NO_THREAD, "xlnx-pcie-dma-pl", @@ -698,10 +696,8 @@ static int xilinx_request_msi_irq(struct pl_dma_pcie *port) } port->msi.irq_msi1 = platform_get_irq_byname(pdev, "msi1"); - if (port->msi.irq_msi1 <= 0) { - dev_err(dev, "Unable to find msi1 IRQ line\n"); + if (port->msi.irq_msi1 <= 0) return port->msi.irq_msi1; - } ret = devm_request_irq(dev, port->msi.irq_msi1, xilinx_pl_dma_pcie_msi_handler_high, IRQF_SHARED | IRQF_NO_THREAD, "xlnx-pcie-dma-pl", diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index e307aceba5c9..0408f4d612b5 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -166,7 +166,7 @@ struct nwl_pcie { int irq_intx; int irq_misc; struct nwl_msi msi; - struct irq_domain *legacy_irq_domain; + struct irq_domain *intx_irq_domain; struct clk *clk; raw_spinlock_t leg_mask_lock; }; @@ -324,7 +324,7 @@ static void nwl_pcie_leg_handler(struct irq_desc *desc) while ((status = nwl_bridge_readl(pcie, MSGF_LEG_STATUS) & MSGF_LEG_SR_MASKALL) != 0) { for_each_set_bit(bit, &status, PCI_NUM_INTX) - generic_handle_domain_irq(pcie->legacy_irq_domain, bit); + generic_handle_domain_irq(pcie->intx_irq_domain, bit); } chained_irq_exit(chip, desc); @@ -364,7 +364,7 @@ static void nwl_pcie_msi_handler_low(struct irq_desc *desc) chained_irq_exit(chip, desc); } -static void nwl_mask_leg_irq(struct irq_data *data) +static void nwl_mask_intx_irq(struct irq_data *data) { struct nwl_pcie *pcie = irq_data_get_irq_chip_data(data); unsigned long flags; @@ -378,7 +378,7 @@ static void nwl_mask_leg_irq(struct irq_data *data) raw_spin_unlock_irqrestore(&pcie->leg_mask_lock, flags); } -static void nwl_unmask_leg_irq(struct irq_data *data) +static void nwl_unmask_intx_irq(struct irq_data *data) { struct nwl_pcie *pcie = irq_data_get_irq_chip_data(data); unsigned long flags; @@ -392,26 +392,26 @@ static void nwl_unmask_leg_irq(struct irq_data *data) raw_spin_unlock_irqrestore(&pcie->leg_mask_lock, flags); } -static struct irq_chip nwl_leg_irq_chip = { +static struct irq_chip nwl_intx_irq_chip = { .name = "nwl_pcie:legacy", - .irq_enable = nwl_unmask_leg_irq, - .irq_disable = nwl_mask_leg_irq, - .irq_mask = nwl_mask_leg_irq, - .irq_unmask = nwl_unmask_leg_irq, + .irq_enable = nwl_unmask_intx_irq, + .irq_disable = nwl_mask_intx_irq, + .irq_mask = nwl_mask_intx_irq, + .irq_unmask = nwl_unmask_intx_irq, }; -static int nwl_legacy_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) +static int nwl_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) { - irq_set_chip_and_handler(irq, &nwl_leg_irq_chip, handle_level_irq); + irq_set_chip_and_handler(irq, &nwl_intx_irq_chip, handle_level_irq); irq_set_chip_data(irq, domain->host_data); irq_set_status_flags(irq, IRQ_LEVEL); return 0; } -static const struct irq_domain_ops legacy_domain_ops = { - .map = nwl_legacy_map, +static const struct irq_domain_ops intx_domain_ops = { + .map = nwl_intx_map, .xlate = pci_irqd_intx_xlate, }; @@ -525,20 +525,20 @@ static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie) { struct device *dev = pcie->dev; struct device_node *node = dev->of_node; - struct device_node *legacy_intc_node; + struct device_node *intc_node; - legacy_intc_node = of_get_next_child(node, NULL); - if (!legacy_intc_node) { + intc_node = of_get_next_child(node, NULL); + if (!intc_node) { dev_err(dev, "No legacy intc node found\n"); return -EINVAL; } - pcie->legacy_irq_domain = irq_domain_add_linear(legacy_intc_node, - PCI_NUM_INTX, - &legacy_domain_ops, - pcie); - of_node_put(legacy_intc_node); - if (!pcie->legacy_irq_domain) { + pcie->intx_irq_domain = irq_domain_add_linear(intc_node, + PCI_NUM_INTX, + &intx_domain_ops, + pcie); + of_node_put(intc_node); + if (!pcie->intx_irq_domain) { dev_err(dev, "failed to create IRQ domain\n"); return -ENOMEM; } @@ -710,14 +710,14 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie) /* Enable all misc interrupts */ nwl_bridge_writel(pcie, MSGF_MISC_SR_MASKALL, MSGF_MISC_MASK); - /* Disable all legacy interrupts */ + /* Disable all INTX interrupts */ nwl_bridge_writel(pcie, (u32)~MSGF_LEG_SR_MASKALL, MSGF_LEG_MASK); - /* Clear pending legacy interrupts */ + /* Clear pending INTX interrupts */ nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_LEG_STATUS) & MSGF_LEG_SR_MASKALL, MSGF_LEG_STATUS); - /* Enable all legacy interrupts */ + /* Enable all INTX interrupts */ nwl_bridge_writel(pcie, MSGF_LEG_SR_MASKALL, MSGF_LEG_MASK); /* Enable the bridge config interrupt */ diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 0452cbc362ee..87b7856f375a 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -984,7 +984,7 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id) return -ENOMEM; vmd->dev = dev; - vmd->instance = ida_simple_get(&vmd_instance_ida, 0, 0, GFP_KERNEL); + vmd->instance = ida_alloc(&vmd_instance_ida, GFP_KERNEL); if (vmd->instance < 0) return vmd->instance; @@ -1026,7 +1026,7 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id) return 0; out_release_instance: - ida_simple_remove(&vmd_instance_ida, vmd->instance); + ida_free(&vmd_instance_ida, vmd->instance); return err; } @@ -1048,7 +1048,7 @@ static void vmd_remove(struct pci_dev *dev) vmd_cleanup_srcu(vmd); vmd_detach_resources(vmd); vmd_remove_irq_domain(vmd); - ida_simple_remove(&vmd_instance_ida, vmd->instance); + ida_free(&vmd_instance_ida, vmd->instance); } static void vmd_shutdown(struct pci_dev *dev) diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c index b7b9d3e21f97..1c3e4ea76bd2 100644 --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -21,6 +21,15 @@ /* Platform specific flags */ #define MHI_EPF_USE_DMA BIT(0) +struct pci_epf_mhi_dma_transfer { + struct pci_epf_mhi *epf_mhi; + struct mhi_ep_buf_info buf_info; + struct list_head node; + dma_addr_t paddr; + enum dma_data_direction dir; + size_t size; +}; + struct pci_epf_mhi_ep_info { const struct mhi_ep_cntrl_config *config; struct pci_epf_header *epf_header; @@ -124,6 +133,10 @@ struct pci_epf_mhi { resource_size_t mmio_phys; struct dma_chan *dma_chan_tx; struct dma_chan *dma_chan_rx; + struct workqueue_struct *dma_wq; + struct work_struct dma_work; + struct list_head dma_list; + spinlock_t list_lock; u32 mmio_size; int irq; }; @@ -205,63 +218,69 @@ static void pci_epf_mhi_raise_irq(struct mhi_ep_cntrl *mhi_cntrl, u32 vector) * MHI supplies 0 based MSI vectors but the API expects the vector * number to start from 1, so we need to increment the vector by 1. */ - pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, PCI_EPC_IRQ_MSI, + pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, PCI_IRQ_MSI, vector + 1); } -static int pci_epf_mhi_iatu_read(struct mhi_ep_cntrl *mhi_cntrl, u64 from, - void *to, size_t size) +static int pci_epf_mhi_iatu_read(struct mhi_ep_cntrl *mhi_cntrl, + struct mhi_ep_buf_info *buf_info) { struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); - size_t offset = get_align_offset(epf_mhi, from); + size_t offset = get_align_offset(epf_mhi, buf_info->host_addr); void __iomem *tre_buf; phys_addr_t tre_phys; int ret; mutex_lock(&epf_mhi->lock); - ret = __pci_epf_mhi_alloc_map(mhi_cntrl, from, &tre_phys, &tre_buf, - offset, size); + ret = __pci_epf_mhi_alloc_map(mhi_cntrl, buf_info->host_addr, &tre_phys, + &tre_buf, offset, buf_info->size); if (ret) { mutex_unlock(&epf_mhi->lock); return ret; } - memcpy_fromio(to, tre_buf, size); + memcpy_fromio(buf_info->dev_addr, tre_buf, buf_info->size); - __pci_epf_mhi_unmap_free(mhi_cntrl, from, tre_phys, tre_buf, offset, - size); + __pci_epf_mhi_unmap_free(mhi_cntrl, buf_info->host_addr, tre_phys, + tre_buf, offset, buf_info->size); mutex_unlock(&epf_mhi->lock); + if (buf_info->cb) + buf_info->cb(buf_info); + return 0; } static int pci_epf_mhi_iatu_write(struct mhi_ep_cntrl *mhi_cntrl, - void *from, u64 to, size_t size) + struct mhi_ep_buf_info *buf_info) { struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); - size_t offset = get_align_offset(epf_mhi, to); + size_t offset = get_align_offset(epf_mhi, buf_info->host_addr); void __iomem *tre_buf; phys_addr_t tre_phys; int ret; mutex_lock(&epf_mhi->lock); - ret = __pci_epf_mhi_alloc_map(mhi_cntrl, to, &tre_phys, &tre_buf, - offset, size); + ret = __pci_epf_mhi_alloc_map(mhi_cntrl, buf_info->host_addr, &tre_phys, + &tre_buf, offset, buf_info->size); if (ret) { mutex_unlock(&epf_mhi->lock); return ret; } - memcpy_toio(tre_buf, from, size); + memcpy_toio(tre_buf, buf_info->dev_addr, buf_info->size); - __pci_epf_mhi_unmap_free(mhi_cntrl, to, tre_phys, tre_buf, offset, - size); + __pci_epf_mhi_unmap_free(mhi_cntrl, buf_info->host_addr, tre_phys, + tre_buf, offset, buf_info->size); mutex_unlock(&epf_mhi->lock); + if (buf_info->cb) + buf_info->cb(buf_info); + return 0; } @@ -270,8 +289,8 @@ static void pci_epf_mhi_dma_callback(void *param) complete(param); } -static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl, u64 from, - void *to, size_t size) +static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl, + struct mhi_ep_buf_info *buf_info) { struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); struct device *dma_dev = epf_mhi->epf->epc->dev.parent; @@ -284,13 +303,13 @@ static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl, u64 from, dma_addr_t dst_addr; int ret; - if (size < SZ_4K) - return pci_epf_mhi_iatu_read(mhi_cntrl, from, to, size); + if (buf_info->size < SZ_4K) + return pci_epf_mhi_iatu_read(mhi_cntrl, buf_info); mutex_lock(&epf_mhi->lock); config.direction = DMA_DEV_TO_MEM; - config.src_addr = from; + config.src_addr = buf_info->host_addr; ret = dmaengine_slave_config(chan, &config); if (ret) { @@ -298,14 +317,16 @@ static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl, u64 from, goto err_unlock; } - dst_addr = dma_map_single(dma_dev, to, size, DMA_FROM_DEVICE); + dst_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size, + DMA_FROM_DEVICE); ret = dma_mapping_error(dma_dev, dst_addr); if (ret) { dev_err(dev, "Failed to map remote memory\n"); goto err_unlock; } - desc = dmaengine_prep_slave_single(chan, dst_addr, size, DMA_DEV_TO_MEM, + desc = dmaengine_prep_slave_single(chan, dst_addr, buf_info->size, + DMA_DEV_TO_MEM, DMA_CTRL_ACK | DMA_PREP_INTERRUPT); if (!desc) { dev_err(dev, "Failed to prepare DMA\n"); @@ -332,15 +353,15 @@ static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl, u64 from, } err_unmap: - dma_unmap_single(dma_dev, dst_addr, size, DMA_FROM_DEVICE); + dma_unmap_single(dma_dev, dst_addr, buf_info->size, DMA_FROM_DEVICE); err_unlock: mutex_unlock(&epf_mhi->lock); return ret; } -static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl, void *from, - u64 to, size_t size) +static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl, + struct mhi_ep_buf_info *buf_info) { struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); struct device *dma_dev = epf_mhi->epf->epc->dev.parent; @@ -353,13 +374,13 @@ static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl, void *from, dma_addr_t src_addr; int ret; - if (size < SZ_4K) - return pci_epf_mhi_iatu_write(mhi_cntrl, from, to, size); + if (buf_info->size < SZ_4K) + return pci_epf_mhi_iatu_write(mhi_cntrl, buf_info); mutex_lock(&epf_mhi->lock); config.direction = DMA_MEM_TO_DEV; - config.dst_addr = to; + config.dst_addr = buf_info->host_addr; ret = dmaengine_slave_config(chan, &config); if (ret) { @@ -367,14 +388,16 @@ static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl, void *from, goto err_unlock; } - src_addr = dma_map_single(dma_dev, from, size, DMA_TO_DEVICE); + src_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size, + DMA_TO_DEVICE); ret = dma_mapping_error(dma_dev, src_addr); if (ret) { dev_err(dev, "Failed to map remote memory\n"); goto err_unlock; } - desc = dmaengine_prep_slave_single(chan, src_addr, size, DMA_MEM_TO_DEV, + desc = dmaengine_prep_slave_single(chan, src_addr, buf_info->size, + DMA_MEM_TO_DEV, DMA_CTRL_ACK | DMA_PREP_INTERRUPT); if (!desc) { dev_err(dev, "Failed to prepare DMA\n"); @@ -401,7 +424,199 @@ static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl, void *from, } err_unmap: - dma_unmap_single(dma_dev, src_addr, size, DMA_FROM_DEVICE); + dma_unmap_single(dma_dev, src_addr, buf_info->size, DMA_TO_DEVICE); +err_unlock: + mutex_unlock(&epf_mhi->lock); + + return ret; +} + +static void pci_epf_mhi_dma_worker(struct work_struct *work) +{ + struct pci_epf_mhi *epf_mhi = container_of(work, struct pci_epf_mhi, dma_work); + struct device *dma_dev = epf_mhi->epf->epc->dev.parent; + struct pci_epf_mhi_dma_transfer *itr, *tmp; + struct mhi_ep_buf_info *buf_info; + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&epf_mhi->list_lock, flags); + list_splice_tail_init(&epf_mhi->dma_list, &head); + spin_unlock_irqrestore(&epf_mhi->list_lock, flags); + + list_for_each_entry_safe(itr, tmp, &head, node) { + list_del(&itr->node); + dma_unmap_single(dma_dev, itr->paddr, itr->size, itr->dir); + buf_info = &itr->buf_info; + buf_info->cb(buf_info); + kfree(itr); + } +} + +static void pci_epf_mhi_dma_async_callback(void *param) +{ + struct pci_epf_mhi_dma_transfer *transfer = param; + struct pci_epf_mhi *epf_mhi = transfer->epf_mhi; + + spin_lock(&epf_mhi->list_lock); + list_add_tail(&transfer->node, &epf_mhi->dma_list); + spin_unlock(&epf_mhi->list_lock); + + queue_work(epf_mhi->dma_wq, &epf_mhi->dma_work); +} + +static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl, + struct mhi_ep_buf_info *buf_info) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + struct device *dma_dev = epf_mhi->epf->epc->dev.parent; + struct pci_epf_mhi_dma_transfer *transfer = NULL; + struct dma_chan *chan = epf_mhi->dma_chan_rx; + struct device *dev = &epf_mhi->epf->dev; + DECLARE_COMPLETION_ONSTACK(complete); + struct dma_async_tx_descriptor *desc; + struct dma_slave_config config = {}; + dma_cookie_t cookie; + dma_addr_t dst_addr; + int ret; + + mutex_lock(&epf_mhi->lock); + + config.direction = DMA_DEV_TO_MEM; + config.src_addr = buf_info->host_addr; + + ret = dmaengine_slave_config(chan, &config); + if (ret) { + dev_err(dev, "Failed to configure DMA channel\n"); + goto err_unlock; + } + + dst_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size, + DMA_FROM_DEVICE); + ret = dma_mapping_error(dma_dev, dst_addr); + if (ret) { + dev_err(dev, "Failed to map remote memory\n"); + goto err_unlock; + } + + desc = dmaengine_prep_slave_single(chan, dst_addr, buf_info->size, + DMA_DEV_TO_MEM, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(dev, "Failed to prepare DMA\n"); + ret = -EIO; + goto err_unmap; + } + + transfer = kzalloc(sizeof(*transfer), GFP_KERNEL); + if (!transfer) { + ret = -ENOMEM; + goto err_unmap; + } + + transfer->epf_mhi = epf_mhi; + transfer->paddr = dst_addr; + transfer->size = buf_info->size; + transfer->dir = DMA_FROM_DEVICE; + memcpy(&transfer->buf_info, buf_info, sizeof(*buf_info)); + + desc->callback = pci_epf_mhi_dma_async_callback; + desc->callback_param = transfer; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) { + dev_err(dev, "Failed to do DMA submit\n"); + goto err_free_transfer; + } + + dma_async_issue_pending(chan); + + goto err_unlock; + +err_free_transfer: + kfree(transfer); +err_unmap: + dma_unmap_single(dma_dev, dst_addr, buf_info->size, DMA_FROM_DEVICE); +err_unlock: + mutex_unlock(&epf_mhi->lock); + + return ret; +} + +static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl, + struct mhi_ep_buf_info *buf_info) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + struct device *dma_dev = epf_mhi->epf->epc->dev.parent; + struct pci_epf_mhi_dma_transfer *transfer = NULL; + struct dma_chan *chan = epf_mhi->dma_chan_tx; + struct device *dev = &epf_mhi->epf->dev; + DECLARE_COMPLETION_ONSTACK(complete); + struct dma_async_tx_descriptor *desc; + struct dma_slave_config config = {}; + dma_cookie_t cookie; + dma_addr_t src_addr; + int ret; + + mutex_lock(&epf_mhi->lock); + + config.direction = DMA_MEM_TO_DEV; + config.dst_addr = buf_info->host_addr; + + ret = dmaengine_slave_config(chan, &config); + if (ret) { + dev_err(dev, "Failed to configure DMA channel\n"); + goto err_unlock; + } + + src_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size, + DMA_TO_DEVICE); + ret = dma_mapping_error(dma_dev, src_addr); + if (ret) { + dev_err(dev, "Failed to map remote memory\n"); + goto err_unlock; + } + + desc = dmaengine_prep_slave_single(chan, src_addr, buf_info->size, + DMA_MEM_TO_DEV, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(dev, "Failed to prepare DMA\n"); + ret = -EIO; + goto err_unmap; + } + + transfer = kzalloc(sizeof(*transfer), GFP_KERNEL); + if (!transfer) { + ret = -ENOMEM; + goto err_unmap; + } + + transfer->epf_mhi = epf_mhi; + transfer->paddr = src_addr; + transfer->size = buf_info->size; + transfer->dir = DMA_TO_DEVICE; + memcpy(&transfer->buf_info, buf_info, sizeof(*buf_info)); + + desc->callback = pci_epf_mhi_dma_async_callback; + desc->callback_param = transfer; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) { + dev_err(dev, "Failed to do DMA submit\n"); + goto err_free_transfer; + } + + dma_async_issue_pending(chan); + + goto err_unlock; + +err_free_transfer: + kfree(transfer); +err_unmap: + dma_unmap_single(dma_dev, src_addr, buf_info->size, DMA_TO_DEVICE); err_unlock: mutex_unlock(&epf_mhi->lock); @@ -431,6 +646,7 @@ static int pci_epf_mhi_dma_init(struct pci_epf_mhi *epf_mhi) struct device *dev = &epf_mhi->epf->dev; struct epf_dma_filter filter; dma_cap_mask_t mask; + int ret; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -449,16 +665,35 @@ static int pci_epf_mhi_dma_init(struct pci_epf_mhi *epf_mhi) &filter); if (IS_ERR_OR_NULL(epf_mhi->dma_chan_rx)) { dev_err(dev, "Failed to request rx channel\n"); - dma_release_channel(epf_mhi->dma_chan_tx); - epf_mhi->dma_chan_tx = NULL; - return -ENODEV; + ret = -ENODEV; + goto err_release_tx; + } + + epf_mhi->dma_wq = alloc_workqueue("pci_epf_mhi_dma_wq", 0, 0); + if (!epf_mhi->dma_wq) { + ret = -ENOMEM; + goto err_release_rx; } + INIT_LIST_HEAD(&epf_mhi->dma_list); + INIT_WORK(&epf_mhi->dma_work, pci_epf_mhi_dma_worker); + spin_lock_init(&epf_mhi->list_lock); + return 0; + +err_release_rx: + dma_release_channel(epf_mhi->dma_chan_rx); + epf_mhi->dma_chan_rx = NULL; +err_release_tx: + dma_release_channel(epf_mhi->dma_chan_tx); + epf_mhi->dma_chan_tx = NULL; + + return ret; } static void pci_epf_mhi_dma_deinit(struct pci_epf_mhi *epf_mhi) { + destroy_workqueue(epf_mhi->dma_wq); dma_release_channel(epf_mhi->dma_chan_tx); dma_release_channel(epf_mhi->dma_chan_rx); epf_mhi->dma_chan_tx = NULL; @@ -531,12 +766,13 @@ static int pci_epf_mhi_link_up(struct pci_epf *epf) mhi_cntrl->raise_irq = pci_epf_mhi_raise_irq; mhi_cntrl->alloc_map = pci_epf_mhi_alloc_map; mhi_cntrl->unmap_free = pci_epf_mhi_unmap_free; + mhi_cntrl->read_sync = mhi_cntrl->read_async = pci_epf_mhi_iatu_read; + mhi_cntrl->write_sync = mhi_cntrl->write_async = pci_epf_mhi_iatu_write; if (info->flags & MHI_EPF_USE_DMA) { - mhi_cntrl->read_from_host = pci_epf_mhi_edma_read; - mhi_cntrl->write_to_host = pci_epf_mhi_edma_write; - } else { - mhi_cntrl->read_from_host = pci_epf_mhi_iatu_read; - mhi_cntrl->write_to_host = pci_epf_mhi_iatu_write; + mhi_cntrl->read_sync = pci_epf_mhi_edma_read; + mhi_cntrl->write_sync = pci_epf_mhi_edma_write; + mhi_cntrl->read_async = pci_epf_mhi_edma_read_async; + mhi_cntrl->write_async = pci_epf_mhi_edma_write_async; } /* Register the MHI EP controller */ @@ -644,7 +880,7 @@ static void pci_epf_mhi_unbind(struct pci_epf *epf) pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, epf_bar); } -static struct pci_epc_event_ops pci_epf_mhi_event_ops = { +static const struct pci_epc_event_ops pci_epf_mhi_event_ops = { .core_init = pci_epf_mhi_core_init, .link_up = pci_epf_mhi_link_up, .link_down = pci_epf_mhi_link_down, @@ -682,7 +918,7 @@ static const struct pci_epf_device_id pci_epf_mhi_ids[] = { {}, }; -static struct pci_epf_ops pci_epf_mhi_ops = { +static const struct pci_epf_ops pci_epf_mhi_ops = { .unbind = pci_epf_mhi_unbind, .bind = pci_epf_mhi_bind, }; diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c index 9aac2c6f3bb9..0553946005c4 100644 --- a/drivers/pci/endpoint/functions/pci-epf-ntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c @@ -140,9 +140,9 @@ static struct pci_epf_header epf_ntb_header = { static int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up) { enum pci_epc_interface_type type; - enum pci_epc_irq_type irq_type; struct epf_ntb_epc *ntb_epc; struct epf_ntb_ctrl *ctrl; + unsigned int irq_type; struct pci_epc *epc; u8 func_no, vfunc_no; bool is_msix; @@ -159,7 +159,7 @@ static int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up) ctrl->link_status |= LINK_STATUS_UP; else ctrl->link_status &= ~LINK_STATUS_UP; - irq_type = is_msix ? PCI_EPC_IRQ_MSIX : PCI_EPC_IRQ_MSI; + irq_type = is_msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI; ret = pci_epc_raise_irq(epc, func_no, vfunc_no, irq_type, 1); if (ret) { dev_err(&epc->dev, @@ -2099,7 +2099,7 @@ static int epf_ntb_probe(struct pci_epf *epf, return 0; } -static struct pci_epf_ops epf_ntb_ops = { +static const struct pci_epf_ops epf_ntb_ops = { .bind = epf_ntb_bind, .unbind = epf_ntb_unbind, .add_cfs = epf_ntb_add_cfs, diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 1f0d2b84296a..18c80002d3bd 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -19,11 +19,11 @@ #include <linux/pci-epf.h> #include <linux/pci_regs.h> -#define IRQ_TYPE_LEGACY 0 +#define IRQ_TYPE_INTX 0 #define IRQ_TYPE_MSI 1 #define IRQ_TYPE_MSIX 2 -#define COMMAND_RAISE_LEGACY_IRQ BIT(0) +#define COMMAND_RAISE_INTX_IRQ BIT(0) #define COMMAND_RAISE_MSI_IRQ BIT(1) #define COMMAND_RAISE_MSIX_IRQ BIT(2) #define COMMAND_READ BIT(3) @@ -600,9 +600,9 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, WRITE_ONCE(reg->status, status); switch (reg->irq_type) { - case IRQ_TYPE_LEGACY: + case IRQ_TYPE_INTX: pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, - PCI_EPC_IRQ_LEGACY, 0); + PCI_IRQ_INTX, 0); break; case IRQ_TYPE_MSI: count = pci_epc_get_msi(epc, epf->func_no, epf->vfunc_no); @@ -612,7 +612,7 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, return; } pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, - PCI_EPC_IRQ_MSI, reg->irq_number); + PCI_IRQ_MSI, reg->irq_number); break; case IRQ_TYPE_MSIX: count = pci_epc_get_msix(epc, epf->func_no, epf->vfunc_no); @@ -622,7 +622,7 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, return; } pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, - PCI_EPC_IRQ_MSIX, reg->irq_number); + PCI_IRQ_MSIX, reg->irq_number); break; default: dev_err(dev, "Failed to raise IRQ, unknown type\n"); @@ -659,7 +659,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) } switch (command) { - case COMMAND_RAISE_LEGACY_IRQ: + case COMMAND_RAISE_INTX_IRQ: case COMMAND_RAISE_MSI_IRQ: case COMMAND_RAISE_MSIX_IRQ: pci_epf_test_raise_irq(epf_test, reg); @@ -973,7 +973,7 @@ static int pci_epf_test_probe(struct pci_epf *epf, return 0; } -static struct pci_epf_ops ops = { +static const struct pci_epf_ops ops = { .unbind = pci_epf_test_unbind, .bind = pci_epf_test_bind, }; diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 3f60128560ed..e75a2af77328 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -1172,11 +1172,8 @@ static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) func_no = ntb->epf->func_no; vfunc_no = ntb->epf->vfunc_no; - ret = pci_epc_raise_irq(ntb->epf->epc, - func_no, - vfunc_no, - PCI_EPC_IRQ_MSI, - interrupt_num + 1); + ret = pci_epc_raise_irq(ntb->epf->epc, func_no, vfunc_no, + PCI_IRQ_MSI, interrupt_num + 1); if (ret) dev_err(&ntb->ntb.dev, "Failed to raise IRQ\n"); @@ -1387,7 +1384,7 @@ static void epf_ntb_unbind(struct pci_epf *epf) } // EPF driver probe -static struct pci_epf_ops epf_ntb_ops = { +static const struct pci_epf_ops epf_ntb_ops = { .bind = epf_ntb_bind, .unbind = epf_ntb_unbind, .add_cfs = epf_ntb_add_cfs, diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 56e1184bc6c2..dcd4e66430c1 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -211,13 +211,13 @@ EXPORT_SYMBOL_GPL(pci_epc_start); * @epc: the EPC device which has to interrupt the host * @func_no: the physical endpoint function number in the EPC device * @vfunc_no: the virtual endpoint function number in the physical function - * @type: specify the type of interrupt; legacy, MSI or MSI-X + * @type: specify the type of interrupt; INTX, MSI or MSI-X * @interrupt_num: the MSI or MSI-X interrupt number with range (1-N) * - * Invoke to raise an legacy, MSI or MSI-X interrupt + * Invoke to raise an INTX, MSI or MSI-X interrupt */ int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no, - enum pci_epc_irq_type type, u16 interrupt_num) + unsigned int type, u16 interrupt_num) { int ret; diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 25dbe85c4217..aaa33e8dc4c9 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -745,6 +745,7 @@ static int sriov_init(struct pci_dev *dev, int pos) u16 ctrl, total; struct pci_sriov *iov; struct resource *res; + const char *res_name; struct pci_dev *pdev; pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl); @@ -785,6 +786,8 @@ found: nres = 0; for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { res = &dev->resource[i + PCI_IOV_RESOURCES]; + res_name = pci_resource_name(dev, i + PCI_IOV_RESOURCES); + /* * If it is already FIXED, don't change it, something * (perhaps EA or header fixups) wants it this way. @@ -802,8 +805,8 @@ found: } iov->barsz[i] = resource_size(res); res->end = res->start + resource_size(res) * total - 1; - pci_info(dev, "VF(n) BAR%d space: %pR (contains BAR%d for %d VFs)\n", - i, res, i, total); + pci_info(dev, "%s %pR: contains BAR %d for %d VFs\n", + res_name, res, i, total); i += bar64; nres++; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ae550d71b815..9ab9b1008d8b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -851,6 +851,66 @@ struct resource *pci_find_resource(struct pci_dev *dev, struct resource *res) EXPORT_SYMBOL(pci_find_resource); /** + * pci_resource_name - Return the name of the PCI resource + * @dev: PCI device to query + * @i: index of the resource + * + * Return the standard PCI resource (BAR) name according to their index. + */ +const char *pci_resource_name(struct pci_dev *dev, unsigned int i) +{ + static const char * const bar_name[] = { + "BAR 0", + "BAR 1", + "BAR 2", + "BAR 3", + "BAR 4", + "BAR 5", + "ROM", +#ifdef CONFIG_PCI_IOV + "VF BAR 0", + "VF BAR 1", + "VF BAR 2", + "VF BAR 3", + "VF BAR 4", + "VF BAR 5", +#endif + "bridge window", /* "io" included in %pR */ + "bridge window", /* "mem" included in %pR */ + "bridge window", /* "mem pref" included in %pR */ + }; + static const char * const cardbus_name[] = { + "BAR 1", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", +#ifdef CONFIG_PCI_IOV + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", + "unknown", +#endif + "CardBus bridge window 0", /* I/O */ + "CardBus bridge window 1", /* I/O */ + "CardBus bridge window 0", /* mem */ + "CardBus bridge window 1", /* mem */ + }; + + if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS && + i < ARRAY_SIZE(cardbus_name)) + return cardbus_name[i]; + + if (i < ARRAY_SIZE(bar_name)) + return bar_name[i]; + + return "unknown"; +} + +/** * pci_wait_for_pending - wait for @mask bit(s) to clear in status word @pos * @dev: the PCI device to operate on * @pos: config space offset of status word @@ -1294,6 +1354,7 @@ end: /** * pci_set_full_power_state - Put a PCI device into D0 and update its state * @dev: PCI device to power up + * @locked: whether pci_bus_sem is held * * Call pci_power_up() to put @dev into D0, read from its PCI_PM_CTRL register * to confirm the state change, restore its BARs if they might be lost and @@ -1303,7 +1364,7 @@ end: * to D0, it is more efficient to use pci_power_up() directly instead of this * function. */ -static int pci_set_full_power_state(struct pci_dev *dev) +static int pci_set_full_power_state(struct pci_dev *dev, bool locked) { u16 pmcsr; int ret; @@ -1339,7 +1400,7 @@ static int pci_set_full_power_state(struct pci_dev *dev) } if (dev->bus->self) - pcie_aspm_pm_state_change(dev->bus->self); + pcie_aspm_pm_state_change(dev->bus->self, locked); return 0; } @@ -1368,10 +1429,22 @@ void pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state) pci_walk_bus(bus, __pci_dev_set_current_state, &state); } +static void __pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state, bool locked) +{ + if (!bus) + return; + + if (locked) + pci_walk_bus_locked(bus, __pci_dev_set_current_state, &state); + else + pci_walk_bus(bus, __pci_dev_set_current_state, &state); +} + /** * pci_set_low_power_state - Put a PCI device into a low-power state. * @dev: PCI device to handle. * @state: PCI power state (D1, D2, D3hot) to put the device into. + * @locked: whether pci_bus_sem is held * * Use the device's PCI_PM_CTRL register to put it into a low-power state. * @@ -1382,7 +1455,7 @@ void pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state) * 0 if device already is in the requested state. * 0 if device's power state has been successfully changed. */ -static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state) +static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state, bool locked) { u16 pmcsr; @@ -1436,29 +1509,12 @@ static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state) pci_power_name(state)); if (dev->bus->self) - pcie_aspm_pm_state_change(dev->bus->self); + pcie_aspm_pm_state_change(dev->bus->self, locked); return 0; } -/** - * pci_set_power_state - Set the power state of a PCI device - * @dev: PCI device to handle. - * @state: PCI power state (D0, D1, D2, D3hot) to put the device into. - * - * Transition a device to a new power state, using the platform firmware and/or - * the device's PCI PM registers. - * - * RETURN VALUE: - * -EINVAL if the requested state is invalid. - * -EIO if device does not support PCI PM or its PM capabilities register has a - * wrong version, or device doesn't support the requested state. - * 0 if the transition is to D1 or D2 but D1 and D2 are not supported. - * 0 if device already is in the requested state. - * 0 if the transition is to D3 but D3 is not supported. - * 0 if device's power state has been successfully changed. - */ -int pci_set_power_state(struct pci_dev *dev, pci_power_t state) +static int __pci_set_power_state(struct pci_dev *dev, pci_power_t state, bool locked) { int error; @@ -1482,7 +1538,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) return 0; if (state == PCI_D0) - return pci_set_full_power_state(dev); + return pci_set_full_power_state(dev, locked); /* * This device is quirked not to be put into D3, so don't put it in @@ -1496,16 +1552,16 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) * To put the device in D3cold, put it into D3hot in the native * way, then put it into D3cold using platform ops. */ - error = pci_set_low_power_state(dev, PCI_D3hot); + error = pci_set_low_power_state(dev, PCI_D3hot, locked); if (pci_platform_power_transition(dev, PCI_D3cold)) return error; /* Powering off a bridge may power off the whole hierarchy */ if (dev->current_state == PCI_D3cold) - pci_bus_set_current_state(dev->subordinate, PCI_D3cold); + __pci_bus_set_current_state(dev->subordinate, PCI_D3cold, locked); } else { - error = pci_set_low_power_state(dev, state); + error = pci_set_low_power_state(dev, state, locked); if (pci_platform_power_transition(dev, state)) return error; @@ -1513,8 +1569,38 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) return 0; } + +/** + * pci_set_power_state - Set the power state of a PCI device + * @dev: PCI device to handle. + * @state: PCI power state (D0, D1, D2, D3hot) to put the device into. + * + * Transition a device to a new power state, using the platform firmware and/or + * the device's PCI PM registers. + * + * RETURN VALUE: + * -EINVAL if the requested state is invalid. + * -EIO if device does not support PCI PM or its PM capabilities register has a + * wrong version, or device doesn't support the requested state. + * 0 if the transition is to D1 or D2 but D1 and D2 are not supported. + * 0 if device already is in the requested state. + * 0 if the transition is to D3 but D3 is not supported. + * 0 if device's power state has been successfully changed. + */ +int pci_set_power_state(struct pci_dev *dev, pci_power_t state) +{ + return __pci_set_power_state(dev, state, false); +} EXPORT_SYMBOL(pci_set_power_state); +int pci_set_power_state_locked(struct pci_dev *dev, pci_power_t state) +{ + lockdep_assert_held(&pci_bus_sem); + + return __pci_set_power_state(dev, state, true); +} +EXPORT_SYMBOL(pci_set_power_state_locked); + #define PCI_EXP_SAVE_REGS 7 static struct pci_cap_saved_state *_pci_find_saved_cap(struct pci_dev *pci_dev, @@ -3299,6 +3385,7 @@ static struct resource *pci_ea_get_resource(struct pci_dev *dev, u8 bei, static int pci_ea_read(struct pci_dev *dev, int offset) { struct resource *res; + const char *res_name; int ent_size, ent_offset = offset; resource_size_t start, end; unsigned long flags; @@ -3328,6 +3415,7 @@ static int pci_ea_read(struct pci_dev *dev, int offset) goto out; res = pci_ea_get_resource(dev, bei, prop); + res_name = pci_resource_name(dev, bei); if (!res) { pci_err(dev, "Unsupported EA entry BEI: %u\n", bei); goto out; @@ -3401,16 +3489,16 @@ static int pci_ea_read(struct pci_dev *dev, int offset) res->flags = flags; if (bei <= PCI_EA_BEI_BAR5) - pci_info(dev, "BAR %d: %pR (from Enhanced Allocation, properties %#02x)\n", - bei, res, prop); + pci_info(dev, "%s %pR: from Enhanced Allocation, properties %#02x\n", + res_name, res, prop); else if (bei == PCI_EA_BEI_ROM) - pci_info(dev, "ROM: %pR (from Enhanced Allocation, properties %#02x)\n", - res, prop); + pci_info(dev, "%s %pR: from Enhanced Allocation, properties %#02x\n", + res_name, res, prop); else if (bei >= PCI_EA_BEI_VF_BAR0 && bei <= PCI_EA_BEI_VF_BAR5) - pci_info(dev, "VF BAR %d: %pR (from Enhanced Allocation, properties %#02x)\n", - bei - PCI_EA_BEI_VF_BAR0, res, prop); + pci_info(dev, "%s %pR: from Enhanced Allocation, properties %#02x\n", + res_name, res, prop); else - pci_info(dev, "BEI %d res: %pR (from Enhanced Allocation, properties %#02x)\n", + pci_info(dev, "BEI %d %pR: from Enhanced Allocation, properties %#02x\n", bei, res, prop); out: @@ -6233,6 +6321,41 @@ int pcie_set_mps(struct pci_dev *dev, int mps) } EXPORT_SYMBOL(pcie_set_mps); +static enum pci_bus_speed to_pcie_link_speed(u16 lnksta) +{ + return pcie_link_speed[FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta)]; +} + +int pcie_link_speed_mbps(struct pci_dev *pdev) +{ + u16 lnksta; + int err; + + err = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta); + if (err) + return err; + + switch (to_pcie_link_speed(lnksta)) { + case PCIE_SPEED_2_5GT: + return 2500; + case PCIE_SPEED_5_0GT: + return 5000; + case PCIE_SPEED_8_0GT: + return 8000; + case PCIE_SPEED_16_0GT: + return 16000; + case PCIE_SPEED_32_0GT: + return 32000; + case PCIE_SPEED_64_0GT: + return 64000; + default: + break; + } + + return -EINVAL; +} +EXPORT_SYMBOL(pcie_link_speed_mbps); + /** * pcie_bandwidth_available - determine minimum link settings of a PCIe * device and its bandwidth limitation @@ -6266,8 +6389,7 @@ u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev, while (dev) { pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); - next_speed = pcie_link_speed[FIELD_GET(PCI_EXP_LNKSTA_CLS, - lnksta)]; + next_speed = to_pcie_link_speed(lnksta); next_width = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta); next_bw = next_width * PCIE_SPEED2MBS_ENC(next_speed); @@ -6698,14 +6820,15 @@ static void pci_request_resource_alignment(struct pci_dev *dev, int bar, resource_size_t align, bool resize) { struct resource *r = &dev->resource[bar]; + const char *r_name = pci_resource_name(dev, bar); resource_size_t size; if (!(r->flags & IORESOURCE_MEM)) return; if (r->flags & IORESOURCE_PCI_FIXED) { - pci_info(dev, "BAR%d %pR: ignoring requested alignment %#llx\n", - bar, r, (unsigned long long)align); + pci_info(dev, "%s %pR: ignoring requested alignment %#llx\n", + r_name, r, (unsigned long long)align); return; } @@ -6741,8 +6864,8 @@ static void pci_request_resource_alignment(struct pci_dev *dev, int bar, * devices and we use the second. */ - pci_info(dev, "BAR%d %pR: requesting alignment to %#llx\n", - bar, r, (unsigned long long)align); + pci_info(dev, "%s %pR: requesting alignment to %#llx\n", + r_name, r, (unsigned long long)align); if (resize) { r->start = 0; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f43873049d52..e9750b1b19ba 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -255,6 +255,8 @@ void __pci_bus_assign_resources(const struct pci_bus *bus, struct list_head *fail_head); bool pci_bus_clip_resource(struct pci_dev *dev, int idx); +const char *pci_resource_name(struct pci_dev *dev, unsigned int i); + void pci_reassigndev_resource_alignment(struct pci_dev *dev); void pci_disable_bridge_window(struct pci_dev *dev); struct pci_bus *pci_bus_get(struct pci_bus *bus); @@ -272,7 +274,7 @@ void pci_bus_put(struct pci_bus *bus); /* PCIe speed to Mb/s reduced by encoding overhead */ #define PCIE_SPEED2MBS_ENC(speed) \ - ((speed) == PCIE_SPEED_64_0GT ? 64000*128/130 : \ + ((speed) == PCIE_SPEED_64_0GT ? 64000*1/1 : \ (speed) == PCIE_SPEED_32_0GT ? 32000*128/130 : \ (speed) == PCIE_SPEED_16_0GT ? 16000*128/130 : \ (speed) == PCIE_SPEED_8_0GT ? 8000*128/130 : \ @@ -569,12 +571,12 @@ int pcie_retrain_link(struct pci_dev *pdev, bool use_lt); #ifdef CONFIG_PCIEASPM void pcie_aspm_init_link_state(struct pci_dev *pdev); void pcie_aspm_exit_link_state(struct pci_dev *pdev); -void pcie_aspm_pm_state_change(struct pci_dev *pdev); +void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked); void pcie_aspm_powersave_config_link(struct pci_dev *pdev); #else static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { } static inline void pcie_aspm_exit_link_state(struct pci_dev *pdev) { } -static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev) { } +static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked) { } static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) { } #endif diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 42a3bd35a3e1..05fc30bb5134 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -41,8 +41,8 @@ #define AER_MAX_TYPEOF_UNCOR_ERRS 27 /* as per PCI_ERR_UNCOR_STATUS*/ struct aer_err_source { - unsigned int status; - unsigned int id; + u32 status; /* PCI_ERR_ROOT_STATUS */ + u32 id; /* PCI_ERR_ROOT_ERR_SRC */ }; struct aer_rpc { @@ -435,10 +435,10 @@ void pci_aer_exit(struct pci_dev *dev) /* * AER error strings */ -static const char *aer_error_severity_string[] = { - "Uncorrected (Non-Fatal)", - "Uncorrected (Fatal)", - "Corrected" +static const char * const aer_error_severity_string[] = { + "Uncorrectable (Non-Fatal)", + "Uncorrectable (Fatal)", + "Correctable" }; static const char *aer_error_layer[] = { @@ -740,7 +740,7 @@ static void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info) u8 bus = info->id >> 8; u8 devfn = info->id & 0xff; - pci_info(dev, "%s%s error received: %04x:%02x:%02x.%d\n", + pci_info(dev, "%s%s error message received from %04x:%02x:%02x.%d\n", info->multi_error_valid ? "Multiple " : "", aer_error_severity_string[info->severity], pci_domain_nr(dev->bus), bus, PCI_SLOT(devfn), @@ -929,7 +929,12 @@ static bool find_source_device(struct pci_dev *parent, pci_walk_bus(parent->subordinate, find_device_iter, e_info); if (!e_info->error_dev_num) { - pci_info(parent, "can't find device of ID%04x\n", e_info->id); + u8 bus = e_info->id >> 8; + u8 devfn = e_info->id & 0xff; + + pci_info(parent, "found no error details for %04x:%02x:%02x.%d\n", + pci_domain_nr(parent->bus), bus, PCI_SLOT(devfn), + PCI_FUNC(devfn)); return false; } return true; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 5a0066ecc3c5..bc0bd86695ec 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1003,8 +1003,11 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) up_read(&pci_bus_sem); } -/* @pdev: the root port or switch downstream port */ -void pcie_aspm_pm_state_change(struct pci_dev *pdev) +/* + * @pdev: the root port or switch downstream port + * @locked: whether pci_bus_sem is held + */ +void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked) { struct pcie_link_state *link = pdev->link_state; @@ -1014,12 +1017,14 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev) * Devices changed PM state, we should recheck if latency * meets all functions' requirement */ - down_read(&pci_bus_sem); + if (!locked) + down_read(&pci_bus_sem); mutex_lock(&aspm_lock); pcie_update_aspm_capable(link->root); pcie_config_aspm_path(link); mutex_unlock(&aspm_lock); - up_read(&pci_bus_sem); + if (!locked) + up_read(&pci_bus_sem); } void pcie_aspm_powersave_config_link(struct pci_dev *pdev) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ed6b7f48736a..b7335be56008 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -180,6 +180,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, u64 l64, sz64, mask64; u16 orig_cmd; struct pci_bus_region region, inverted_region; + const char *res_name = pci_resource_name(dev, res - dev->resource); mask = type ? PCI_ROM_ADDRESS_MASK : ~0; @@ -254,8 +255,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, sz64 = pci_size(l64, sz64, mask64); if (!sz64) { - pci_info(dev, FW_BUG "reg 0x%x: invalid BAR (can't size)\n", - pos); + pci_info(dev, FW_BUG "%s: invalid; can't size\n", res_name); goto fail; } @@ -265,8 +265,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED; res->start = 0; res->end = 0; - pci_err(dev, "reg 0x%x: can't handle BAR larger than 4GB (size %#010llx)\n", - pos, (unsigned long long)sz64); + pci_err(dev, "%s: can't handle BAR larger than 4GB (size %#010llx)\n", + res_name, (unsigned long long)sz64); goto out; } @@ -275,8 +275,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, res->flags |= IORESOURCE_UNSET; res->start = 0; res->end = sz64 - 1; - pci_info(dev, "reg 0x%x: can't handle BAR above 4GB (bus address %#010llx)\n", - pos, (unsigned long long)l64); + pci_info(dev, "%s: can't handle BAR above 4GB (bus address %#010llx)\n", + res_name, (unsigned long long)l64); goto out; } } @@ -302,8 +302,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, res->flags |= IORESOURCE_UNSET; res->start = 0; res->end = region.end - region.start; - pci_info(dev, "reg 0x%x: initial BAR value %#010llx invalid\n", - pos, (unsigned long long)region.start); + pci_info(dev, "%s: initial BAR value %#010llx invalid\n", + res_name, (unsigned long long)region.start); } goto out; @@ -313,7 +313,7 @@ fail: res->flags = 0; out: if (res->flags) - pci_info(dev, "reg 0x%x: %pR\n", pos, res); + pci_info(dev, "%s %pR\n", res_name, res); return (res->flags & IORESOURCE_MEM_64) ? 1 : 0; } @@ -344,64 +344,12 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) } } -static void pci_read_bridge_windows(struct pci_dev *bridge) +static void pci_read_bridge_io(struct pci_dev *dev, struct resource *res, + bool log) { - u16 io; - u32 pmem, tmp; - - pci_read_config_word(bridge, PCI_IO_BASE, &io); - if (!io) { - pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0); - pci_read_config_word(bridge, PCI_IO_BASE, &io); - pci_write_config_word(bridge, PCI_IO_BASE, 0x0); - } - if (io) - bridge->io_window = 1; - - /* - * DECchip 21050 pass 2 errata: the bridge may miss an address - * disconnect boundary by one PCI data phase. Workaround: do not - * use prefetching on this device. - */ - if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001) - return; - - pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); - if (!pmem) { - pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, - 0xffe0fff0); - pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); - pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); - } - if (!pmem) - return; - - bridge->pref_window = 1; - - if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { - - /* - * Bridge claims to have a 64-bit prefetchable memory - * window; verify that the upper bits are actually - * writable. - */ - pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &pmem); - pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, - 0xffffffff); - pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp); - pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, pmem); - if (tmp) - bridge->pref_64_window = 1; - } -} - -static void pci_read_bridge_io(struct pci_bus *child) -{ - struct pci_dev *dev = child->self; u8 io_base_lo, io_limit_lo; unsigned long io_mask, io_granularity, base, limit; struct pci_bus_region region; - struct resource *res; io_mask = PCI_IO_RANGE_MASK; io_granularity = 0x1000; @@ -411,7 +359,6 @@ static void pci_read_bridge_io(struct pci_bus *child) io_granularity = 0x400; } - res = child->resource[0]; pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); base = (io_base_lo & io_mask) << 8; @@ -431,19 +378,18 @@ static void pci_read_bridge_io(struct pci_bus *child) region.start = base; region.end = limit + io_granularity - 1; pcibios_bus_to_resource(dev->bus, res, ®ion); - pci_info(dev, " bridge window %pR\n", res); + if (log) + pci_info(dev, " bridge window %pR\n", res); } } -static void pci_read_bridge_mmio(struct pci_bus *child) +static void pci_read_bridge_mmio(struct pci_dev *dev, struct resource *res, + bool log) { - struct pci_dev *dev = child->self; u16 mem_base_lo, mem_limit_lo; unsigned long base, limit; struct pci_bus_region region; - struct resource *res; - res = child->resource[1]; pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); base = ((unsigned long) mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; @@ -453,20 +399,19 @@ static void pci_read_bridge_mmio(struct pci_bus *child) region.start = base; region.end = limit + 0xfffff; pcibios_bus_to_resource(dev->bus, res, ®ion); - pci_info(dev, " bridge window %pR\n", res); + if (log) + pci_info(dev, " bridge window %pR\n", res); } } -static void pci_read_bridge_mmio_pref(struct pci_bus *child) +static void pci_read_bridge_mmio_pref(struct pci_dev *dev, struct resource *res, + bool log) { - struct pci_dev *dev = child->self; u16 mem_base_lo, mem_limit_lo; u64 base64, limit64; pci_bus_addr_t base, limit; struct pci_bus_region region; - struct resource *res; - res = child->resource[2]; pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); base64 = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16; @@ -506,10 +451,77 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child) region.start = base; region.end = limit + 0xfffff; pcibios_bus_to_resource(dev->bus, res, ®ion); - pci_info(dev, " bridge window %pR\n", res); + if (log) + pci_info(dev, " bridge window %pR\n", res); } } +static void pci_read_bridge_windows(struct pci_dev *bridge) +{ + u32 buses; + u16 io; + u32 pmem, tmp; + struct resource res; + + pci_read_config_dword(bridge, PCI_PRIMARY_BUS, &buses); + res.flags = IORESOURCE_BUS; + res.start = (buses >> 8) & 0xff; + res.end = (buses >> 16) & 0xff; + pci_info(bridge, "PCI bridge to %pR%s\n", &res, + bridge->transparent ? " (subtractive decode)" : ""); + + pci_read_config_word(bridge, PCI_IO_BASE, &io); + if (!io) { + pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0); + pci_read_config_word(bridge, PCI_IO_BASE, &io); + pci_write_config_word(bridge, PCI_IO_BASE, 0x0); + } + if (io) { + bridge->io_window = 1; + pci_read_bridge_io(bridge, &res, true); + } + + pci_read_bridge_mmio(bridge, &res, true); + + /* + * DECchip 21050 pass 2 errata: the bridge may miss an address + * disconnect boundary by one PCI data phase. Workaround: do not + * use prefetching on this device. + */ + if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001) + return; + + pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); + if (!pmem) { + pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, + 0xffe0fff0); + pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); + pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); + } + if (!pmem) + return; + + bridge->pref_window = 1; + + if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { + + /* + * Bridge claims to have a 64-bit prefetchable memory + * window; verify that the upper bits are actually + * writable. + */ + pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &pmem); + pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, + 0xffffffff); + pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp); + pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, pmem); + if (tmp) + bridge->pref_64_window = 1; + } + + pci_read_bridge_mmio_pref(bridge, &res, true); +} + void pci_read_bridge_bases(struct pci_bus *child) { struct pci_dev *dev = child->self; @@ -527,9 +539,9 @@ void pci_read_bridge_bases(struct pci_bus *child) for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; - pci_read_bridge_io(child); - pci_read_bridge_mmio(child); - pci_read_bridge_mmio_pref(child); + pci_read_bridge_io(child->self, child->resource[0], false); + pci_read_bridge_mmio(child->self, child->resource[1], false); + pci_read_bridge_mmio_pref(child->self, child->resource[2], false); if (dev->transparent) { pci_bus_for_each_resource(child->parent, res) { @@ -1817,6 +1829,43 @@ static void early_dump_pci_device(struct pci_dev *pdev) value, 256, false); } +static const char *pci_type_str(struct pci_dev *dev) +{ + static const char * const str[] = { + "PCIe Endpoint", + "PCIe Legacy Endpoint", + "PCIe unknown", + "PCIe unknown", + "PCIe Root Port", + "PCIe Switch Upstream Port", + "PCIe Switch Downstream Port", + "PCIe to PCI/PCI-X bridge", + "PCI/PCI-X to PCIe bridge", + "PCIe Root Complex Integrated Endpoint", + "PCIe Root Complex Event Collector", + }; + int type; + + if (pci_is_pcie(dev)) { + type = pci_pcie_type(dev); + if (type < ARRAY_SIZE(str)) + return str[type]; + + return "PCIe unknown"; + } + + switch (dev->hdr_type) { + case PCI_HEADER_TYPE_NORMAL: + return "conventional PCI endpoint"; + case PCI_HEADER_TYPE_BRIDGE: + return "conventional PCI bridge"; + case PCI_HEADER_TYPE_CARDBUS: + return "CardBus bridge"; + default: + return "conventional PCI"; + } +} + /** * pci_setup_device - Fill in class and map information of a device * @dev: the device structure to fill @@ -1887,8 +1936,9 @@ int pci_setup_device(struct pci_dev *dev) pci_set_removable(dev); - pci_info(dev, "[%04x:%04x] type %02x class %#08x\n", - dev->vendor, dev->device, dev->hdr_type, dev->class); + pci_info(dev, "[%04x:%04x] type %02x class %#08x %s\n", + dev->vendor, dev->device, dev->hdr_type, dev->class, + pci_type_str(dev)); /* Device class may be changed after fixup */ class = dev->class >> 8; @@ -1929,14 +1979,14 @@ int pci_setup_device(struct pci_dev *dev) res = &dev->resource[0]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); - pci_info(dev, "legacy IDE quirk: reg 0x10: %pR\n", + pci_info(dev, "BAR 0 %pR: legacy IDE quirk\n", res); region.start = 0x3F6; region.end = 0x3F6; res = &dev->resource[1]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); - pci_info(dev, "legacy IDE quirk: reg 0x14: %pR\n", + pci_info(dev, "BAR 1 %pR: legacy IDE quirk\n", res); } if ((progif & 4) == 0) { @@ -1945,14 +1995,14 @@ int pci_setup_device(struct pci_dev *dev) res = &dev->resource[2]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); - pci_info(dev, "legacy IDE quirk: reg 0x18: %pR\n", + pci_info(dev, "BAR 2 %pR: legacy IDE quirk\n", res); region.start = 0x376; region.end = 0x376; res = &dev->resource[3]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); - pci_info(dev, "legacy IDE quirk: reg 0x1c: %pR\n", + pci_info(dev, "BAR 3 %pR: legacy IDE quirk\n", res); } } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index d208047d1b8f..d797df6e5f3e 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -570,13 +570,14 @@ static void quirk_extend_bar_to_page(struct pci_dev *dev) for (i = 0; i < PCI_STD_NUM_BARS; i++) { struct resource *r = &dev->resource[i]; + const char *r_name = pci_resource_name(dev, i); if (r->flags & IORESOURCE_MEM && resource_size(r) < PAGE_SIZE) { r->end = PAGE_SIZE - 1; r->start = 0; r->flags |= IORESOURCE_UNSET; - pci_info(dev, "expanded BAR %d to page size: %pR\n", - i, r); + pci_info(dev, "%s %pR: expanded to page size\n", + r_name, r); } } } @@ -605,6 +606,7 @@ static void quirk_io(struct pci_dev *dev, int pos, unsigned int size, u32 region; struct pci_bus_region bus_region; struct resource *res = dev->resource + pos; + const char *res_name = pci_resource_name(dev, pos); pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (pos << 2), ®ion); @@ -622,8 +624,7 @@ static void quirk_io(struct pci_dev *dev, int pos, unsigned int size, bus_region.end = region + size - 1; pcibios_bus_to_resource(dev->bus, res, &bus_region); - pci_info(dev, FW_BUG "%s quirk: reg 0x%x: %pR\n", - name, PCI_BASE_ADDRESS_0 + (pos << 2), res); + pci_info(dev, FW_BUG "%s %pR: %s quirk\n", res_name, res, name); } /* @@ -670,6 +671,12 @@ static void quirk_io_region(struct pci_dev *dev, int port, bus_region.end = region + size - 1; pcibios_bus_to_resource(dev->bus, res, &bus_region); + /* + * "res" is typically a bridge window resource that's not being + * used for a bridge window, so it's just a place to stash this + * non-standard resource. Printing "nr" or pci_resource_name() of + * it doesn't really make sense. + */ if (!pci_claim_resource(dev, nr)) pci_info(dev, "quirk: %pR claimed by %s\n", res, name); } @@ -702,10 +709,13 @@ static void quirk_amd_dwc_class(struct pci_dev *pdev) { u32 class = pdev->class; - /* Use "USB Device (not host controller)" class */ - pdev->class = PCI_CLASS_SERIAL_USB_DEVICE; - pci_info(pdev, "PCI class overridden (%#08x -> %#08x) so dwc3 driver can claim this instead of xhci\n", - class, pdev->class); + if (class != PCI_CLASS_SERIAL_USB_DEVICE) { + /* Use "USB Device (not host controller)" class */ + pdev->class = PCI_CLASS_SERIAL_USB_DEVICE; + pci_info(pdev, + "PCI class overridden (%#08x -> %#08x) so dwc3 driver can claim this instead of xhci\n", + class, pdev->class); + } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB, quirk_amd_dwc_class); @@ -4712,17 +4722,21 @@ static int pci_quirk_xgene_acs(struct pci_dev *dev, u16 acs_flags) * But the implementation could block peer-to-peer transactions between them * and provide ACS-like functionality. */ -static int pci_quirk_zhaoxin_pcie_ports_acs(struct pci_dev *dev, u16 acs_flags) +static int pci_quirk_zhaoxin_pcie_ports_acs(struct pci_dev *dev, u16 acs_flags) { if (!pci_is_pcie(dev) || ((pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT) && (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM))) return -ENOTTY; + /* + * Future Zhaoxin Root Ports and Switch Downstream Ports will + * implement ACS capability in accordance with the PCIe Spec. + */ switch (dev->device) { case 0x0710 ... 0x071e: case 0x0721: - case 0x0723 ... 0x0732: + case 0x0723 ... 0x0752: return pci_acs_ctrl_enabled(acs_flags, PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF); } diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index fd74f1c99dba..909e6a7c3cc3 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -213,6 +213,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head, struct list_head *head) { struct resource *res; + const char *res_name; struct pci_dev_resource *add_res, *tmp; struct pci_dev_resource *dev_res; resource_size_t add_size, align; @@ -222,6 +223,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head, bool found_match = false; res = add_res->res; + /* Skip resource that has been reset */ if (!res->flags) goto out; @@ -237,6 +239,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head, continue; idx = res - &add_res->dev->resource[0]; + res_name = pci_resource_name(add_res->dev, idx); add_size = add_res->add_size; align = add_res->min_align; if (!resource_size(res)) { @@ -249,9 +252,9 @@ static void reassign_resources_sorted(struct list_head *realloc_head, (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); if (pci_reassign_resource(add_res->dev, idx, add_size, align)) - pci_info(add_res->dev, "failed to add %llx res[%d]=%pR\n", - (unsigned long long) add_size, idx, - res); + pci_info(add_res->dev, "%s %pR: failed to add %llx\n", + res_name, res, + (unsigned long long) add_size); } out: list_del(&add_res->list); @@ -571,6 +574,7 @@ EXPORT_SYMBOL(pci_setup_cardbus); static void pci_setup_bridge_io(struct pci_dev *bridge) { struct resource *res; + const char *res_name; struct pci_bus_region region; unsigned long io_mask; u8 io_base_lo, io_limit_lo; @@ -583,6 +587,7 @@ static void pci_setup_bridge_io(struct pci_dev *bridge) /* Set up the top and bottom of the PCI I/O segment for this bus */ res = &bridge->resource[PCI_BRIDGE_IO_WINDOW]; + res_name = pci_resource_name(bridge, PCI_BRIDGE_IO_WINDOW); pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_IO) { pci_read_config_word(bridge, PCI_IO_BASE, &l); @@ -591,7 +596,7 @@ static void pci_setup_bridge_io(struct pci_dev *bridge) l = ((u16) io_limit_lo << 8) | io_base_lo; /* Set up upper 16 bits of I/O base/limit */ io_upper16 = (region.end & 0xffff0000) | (region.start >> 16); - pci_info(bridge, " bridge window %pR\n", res); + pci_info(bridge, " %s %pR\n", res_name, res); } else { /* Clear upper 16 bits of I/O base/limit */ io_upper16 = 0; @@ -608,16 +613,18 @@ static void pci_setup_bridge_io(struct pci_dev *bridge) static void pci_setup_bridge_mmio(struct pci_dev *bridge) { struct resource *res; + const char *res_name; struct pci_bus_region region; u32 l; /* Set up the top and bottom of the PCI Memory segment for this bus */ res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW]; + res_name = pci_resource_name(bridge, PCI_BRIDGE_MEM_WINDOW); pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_MEM) { l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; - pci_info(bridge, " bridge window %pR\n", res); + pci_info(bridge, " %s %pR\n", res_name, res); } else { l = 0x0000fff0; } @@ -627,6 +634,7 @@ static void pci_setup_bridge_mmio(struct pci_dev *bridge) static void pci_setup_bridge_mmio_pref(struct pci_dev *bridge) { struct resource *res; + const char *res_name; struct pci_bus_region region; u32 l, bu, lu; @@ -640,6 +648,7 @@ static void pci_setup_bridge_mmio_pref(struct pci_dev *bridge) /* Set up PREF base/limit */ bu = lu = 0; res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; + res_name = pci_resource_name(bridge, PCI_BRIDGE_PREF_MEM_WINDOW); pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_PREFETCH) { l = (region.start >> 16) & 0xfff0; @@ -648,7 +657,7 @@ static void pci_setup_bridge_mmio_pref(struct pci_dev *bridge) bu = upper_32_bits(region.start); lu = upper_32_bits(region.end); } - pci_info(bridge, " bridge window %pR\n", res); + pci_info(bridge, " %s %pR\n", res_name, res); } else { l = 0x0000fff0; } @@ -1013,6 +1022,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, int i; pci_dev_for_each_resource(dev, r, i) { + const char *r_name = pci_resource_name(dev, i); resource_size_t r_size; if (r->parent || (r->flags & IORESOURCE_PCI_FIXED) || @@ -1043,8 +1053,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, if (order < 0) order = 0; if (order >= ARRAY_SIZE(aligns)) { - pci_warn(dev, "disabling BAR %d: %pR (bad alignment %#llx)\n", - i, r, (unsigned long long) align); + pci_warn(dev, "%s %pR: disabling; bad alignment %#llx\n", + r_name, r, (unsigned long long) align); r->flags = 0; continue; } @@ -2235,6 +2245,7 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCE_END; i++) { struct resource *res = &bridge->resource[i]; + const char *res_name = pci_resource_name(bridge, i); if ((res->flags ^ type) & PCI_RES_TYPE_MASK) continue; @@ -2247,8 +2258,7 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) if (ret) goto cleanup; - pci_info(bridge, "BAR %d: releasing %pR\n", - i, res); + pci_info(bridge, "%s %pR: releasing\n", res_name, res); if (res->parent) release_resource(res); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index ceaa69491f5e..c6d933ddfd46 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -30,6 +30,7 @@ static void pci_std_update_resource(struct pci_dev *dev, int resno) u32 new, check, mask; int reg; struct resource *res = dev->resource + resno; + const char *res_name = pci_resource_name(dev, resno); /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */ if (dev->is_virtfn) @@ -104,8 +105,8 @@ static void pci_std_update_resource(struct pci_dev *dev, int resno) pci_read_config_dword(dev, reg, &check); if ((new ^ check) & mask) { - pci_err(dev, "BAR %d: error updating (%#010x != %#010x)\n", - resno, new, check); + pci_err(dev, "%s: error updating (%#010x != %#010x)\n", + res_name, new, check); } if (res->flags & IORESOURCE_MEM_64) { @@ -113,8 +114,8 @@ static void pci_std_update_resource(struct pci_dev *dev, int resno) pci_write_config_dword(dev, reg + 4, new); pci_read_config_dword(dev, reg + 4, &check); if (check != new) { - pci_err(dev, "BAR %d: error updating (high %#010x != %#010x)\n", - resno, new, check); + pci_err(dev, "%s: error updating (high %#010x != %#010x)\n", + res_name, new, check); } } @@ -135,11 +136,12 @@ void pci_update_resource(struct pci_dev *dev, int resno) int pci_claim_resource(struct pci_dev *dev, int resource) { struct resource *res = &dev->resource[resource]; + const char *res_name = pci_resource_name(dev, resource); struct resource *root, *conflict; if (res->flags & IORESOURCE_UNSET) { - pci_info(dev, "can't claim BAR %d %pR: no address assigned\n", - resource, res); + pci_info(dev, "%s %pR: can't claim; no address assigned\n", + res_name, res); return -EINVAL; } @@ -153,16 +155,16 @@ int pci_claim_resource(struct pci_dev *dev, int resource) root = pci_find_parent_resource(dev, res); if (!root) { - pci_info(dev, "can't claim BAR %d %pR: no compatible bridge window\n", - resource, res); + pci_info(dev, "%s %pR: can't claim; no compatible bridge window\n", + res_name, res); res->flags |= IORESOURCE_UNSET; return -EINVAL; } conflict = request_resource_conflict(root, res); if (conflict) { - pci_info(dev, "can't claim BAR %d %pR: address conflict with %s %pR\n", - resource, res, conflict->name, conflict); + pci_info(dev, "%s %pR: can't claim; address conflict with %s %pR\n", + res_name, res, conflict->name, conflict); res->flags |= IORESOURCE_UNSET; return -EBUSY; } @@ -201,6 +203,7 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, { struct resource *root, *conflict; resource_size_t fw_addr, start, end; + const char *res_name = pci_resource_name(dev, resno); fw_addr = pcibios_retrieve_fw_addr(dev, resno); if (!fw_addr) @@ -231,12 +234,11 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, root = &iomem_resource; } - pci_info(dev, "BAR %d: trying firmware assignment %pR\n", - resno, res); + pci_info(dev, "%s: trying firmware assignment %pR\n", res_name, res); conflict = request_resource_conflict(root, res); if (conflict) { - pci_info(dev, "BAR %d: %pR conflicts with %s %pR\n", - resno, res, conflict->name, conflict); + pci_info(dev, "%s %pR: conflicts with %s %pR\n", res_name, res, + conflict->name, conflict); res->start = start; res->end = end; res->flags |= IORESOURCE_UNSET; @@ -325,6 +327,7 @@ static int _pci_assign_resource(struct pci_dev *dev, int resno, int pci_assign_resource(struct pci_dev *dev, int resno) { struct resource *res = dev->resource + resno; + const char *res_name = pci_resource_name(dev, resno); resource_size_t align, size; int ret; @@ -334,8 +337,8 @@ int pci_assign_resource(struct pci_dev *dev, int resno) res->flags |= IORESOURCE_UNSET; align = pci_resource_alignment(dev, res); if (!align) { - pci_info(dev, "BAR %d: can't assign %pR (bogus alignment)\n", - resno, res); + pci_info(dev, "%s %pR: can't assign; bogus alignment\n", + res_name, res); return -EINVAL; } @@ -348,18 +351,18 @@ int pci_assign_resource(struct pci_dev *dev, int resno) * working, which is better than just leaving it disabled. */ if (ret < 0) { - pci_info(dev, "BAR %d: no space for %pR\n", resno, res); + pci_info(dev, "%s %pR: can't assign; no space\n", res_name, res); ret = pci_revert_fw_address(res, dev, resno, size); } if (ret < 0) { - pci_info(dev, "BAR %d: failed to assign %pR\n", resno, res); + pci_info(dev, "%s %pR: failed to assign\n", res_name, res); return ret; } res->flags &= ~IORESOURCE_UNSET; res->flags &= ~IORESOURCE_STARTALIGN; - pci_info(dev, "BAR %d: assigned %pR\n", resno, res); + pci_info(dev, "%s %pR: assigned\n", res_name, res); if (resno < PCI_BRIDGE_RESOURCES) pci_update_resource(dev, resno); @@ -367,10 +370,11 @@ int pci_assign_resource(struct pci_dev *dev, int resno) } EXPORT_SYMBOL(pci_assign_resource); -int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize, - resource_size_t min_align) +int pci_reassign_resource(struct pci_dev *dev, int resno, + resource_size_t addsize, resource_size_t min_align) { struct resource *res = dev->resource + resno; + const char *res_name = pci_resource_name(dev, resno); unsigned long flags; resource_size_t new_size; int ret; @@ -381,8 +385,8 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz flags = res->flags; res->flags |= IORESOURCE_UNSET; if (!res->parent) { - pci_info(dev, "BAR %d: can't reassign an unassigned resource %pR\n", - resno, res); + pci_info(dev, "%s %pR: can't reassign; unassigned resource\n", + res_name, res); return -EINVAL; } @@ -391,15 +395,15 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz ret = _pci_assign_resource(dev, resno, new_size, min_align); if (ret) { res->flags = flags; - pci_info(dev, "BAR %d: %pR (failed to expand by %#llx)\n", - resno, res, (unsigned long long) addsize); + pci_info(dev, "%s %pR: failed to expand by %#llx\n", + res_name, res, (unsigned long long) addsize); return ret; } res->flags &= ~IORESOURCE_UNSET; res->flags &= ~IORESOURCE_STARTALIGN; - pci_info(dev, "BAR %d: reassigned %pR (expanded by %#llx)\n", - resno, res, (unsigned long long) addsize); + pci_info(dev, "%s %pR: reassigned; expanded by %#llx\n", + res_name, res, (unsigned long long) addsize); if (resno < PCI_BRIDGE_RESOURCES) pci_update_resource(dev, resno); @@ -409,8 +413,9 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz void pci_release_resource(struct pci_dev *dev, int resno) { struct resource *res = dev->resource + resno; + const char *res_name = pci_resource_name(dev, resno); - pci_info(dev, "BAR %d: releasing %pR\n", resno, res); + pci_info(dev, "%s %pR: releasing\n", res_name, res); if (!res->parent) return; @@ -480,6 +485,7 @@ int pci_enable_resources(struct pci_dev *dev, int mask) u16 cmd, old_cmd; int i; struct resource *r; + const char *r_name; pci_read_config_word(dev, PCI_COMMAND, &cmd); old_cmd = cmd; @@ -488,6 +494,8 @@ int pci_enable_resources(struct pci_dev *dev, int mask) if (!(mask & (1 << i))) continue; + r_name = pci_resource_name(dev, i); + if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) continue; if ((i == PCI_ROM_RESOURCE) && @@ -495,14 +503,14 @@ int pci_enable_resources(struct pci_dev *dev, int mask) continue; if (r->flags & IORESOURCE_UNSET) { - pci_err(dev, "can't enable device: BAR %d %pR not assigned\n", - i, r); + pci_err(dev, "%s %pR: not assigned; can't enable device\n", + r_name, r); return -EINVAL; } if (!r->parent) { - pci_err(dev, "can't enable device: BAR %d %pR not claimed\n", - i, r); + pci_err(dev, "%s %pR: not claimed; can't enable device\n", + r_name, r); return -EINVAL; } diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 5b921387eca6..1804794d0e68 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -1308,13 +1308,6 @@ static void stdev_release(struct device *dev) { struct switchtec_dev *stdev = to_stdev(dev); - if (stdev->dma_mrpc) { - iowrite32(0, &stdev->mmio_mrpc->dma_en); - flush_wc_buf(stdev); - writeq(0, &stdev->mmio_mrpc->dma_addr); - dma_free_coherent(&stdev->pdev->dev, sizeof(*stdev->dma_mrpc), - stdev->dma_mrpc, stdev->dma_mrpc_dma_addr); - } kfree(stdev); } @@ -1358,7 +1351,7 @@ static struct switchtec_dev *stdev_create(struct pci_dev *pdev) return ERR_PTR(-ENOMEM); stdev->alive = true; - stdev->pdev = pdev; + stdev->pdev = pci_dev_get(pdev); INIT_LIST_HEAD(&stdev->mrpc_queue); mutex_init(&stdev->mrpc_mutex); stdev->mrpc_busy = 0; @@ -1391,6 +1384,7 @@ static struct switchtec_dev *stdev_create(struct pci_dev *pdev) return stdev; err_put: + pci_dev_put(stdev->pdev); put_device(&stdev->dev); return ERR_PTR(rc); } @@ -1644,6 +1638,18 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, return 0; } +static void switchtec_exit_pci(struct switchtec_dev *stdev) +{ + if (stdev->dma_mrpc) { + iowrite32(0, &stdev->mmio_mrpc->dma_en); + flush_wc_buf(stdev); + writeq(0, &stdev->mmio_mrpc->dma_addr); + dma_free_coherent(&stdev->pdev->dev, sizeof(*stdev->dma_mrpc), + stdev->dma_mrpc, stdev->dma_mrpc_dma_addr); + stdev->dma_mrpc = NULL; + } +} + static int switchtec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -1703,6 +1709,9 @@ static void switchtec_pci_remove(struct pci_dev *pdev) ida_free(&switchtec_minor_ida, MINOR(stdev->dev.devt)); dev_info(&stdev->dev, "unregistered.\n"); stdev_kill(stdev); + switchtec_exit_pci(stdev); + pci_dev_put(stdev->pdev); + stdev->pdev = NULL; put_device(&stdev->dev); } diff --git a/drivers/pcmcia/bcm63xx_pcmcia.c b/drivers/pcmcia/bcm63xx_pcmcia.c index dd3c26099048..a5414441834a 100644 --- a/drivers/pcmcia/bcm63xx_pcmcia.c +++ b/drivers/pcmcia/bcm63xx_pcmcia.c @@ -437,7 +437,7 @@ err: return ret; } -static int bcm63xx_drv_pcmcia_remove(struct platform_device *pdev) +static void bcm63xx_drv_pcmcia_remove(struct platform_device *pdev) { struct bcm63xx_pcmcia_socket *skt; struct resource *res; @@ -449,12 +449,11 @@ static int bcm63xx_drv_pcmcia_remove(struct platform_device *pdev) res = skt->reg_res; release_mem_region(res->start, resource_size(res)); kfree(skt); - return 0; } struct platform_driver bcm63xx_pcmcia_driver = { .probe = bcm63xx_drv_pcmcia_probe, - .remove = bcm63xx_drv_pcmcia_remove, + .remove_new = bcm63xx_drv_pcmcia_remove, .driver = { .name = "bcm63xx_pcmcia", .owner = THIS_MODULE, diff --git a/drivers/pcmcia/db1xxx_ss.c b/drivers/pcmcia/db1xxx_ss.c index 87a33ecc2cf1..509713b9a502 100644 --- a/drivers/pcmcia/db1xxx_ss.c +++ b/drivers/pcmcia/db1xxx_ss.c @@ -577,7 +577,7 @@ out0: return ret; } -static int db1x_pcmcia_socket_remove(struct platform_device *pdev) +static void db1x_pcmcia_socket_remove(struct platform_device *pdev) { struct db1x_pcmcia_sock *sock = platform_get_drvdata(pdev); @@ -585,8 +585,6 @@ static int db1x_pcmcia_socket_remove(struct platform_device *pdev) pcmcia_unregister_socket(&sock->socket); iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); kfree(sock); - - return 0; } static struct platform_driver db1x_pcmcia_socket_driver = { @@ -594,7 +592,7 @@ static struct platform_driver db1x_pcmcia_socket_driver = { .name = "db1xxx_pcmcia", }, .probe = db1x_pcmcia_socket_probe, - .remove = db1x_pcmcia_socket_remove, + .remove_new = db1x_pcmcia_socket_remove, }; module_platform_driver(db1x_pcmcia_socket_driver); diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c index efc27bc15152..5ae826e54811 100644 --- a/drivers/pcmcia/electra_cf.c +++ b/drivers/pcmcia/electra_cf.c @@ -307,7 +307,7 @@ out_free_cf: } -static int electra_cf_remove(struct platform_device *ofdev) +static void electra_cf_remove(struct platform_device *ofdev) { struct device *device = &ofdev->dev; struct electra_cf_socket *cf; @@ -326,8 +326,6 @@ static int electra_cf_remove(struct platform_device *ofdev) release_region(cf->io_base, cf->io_size); kfree(cf); - - return 0; } static const struct of_device_id electra_cf_match[] = { @@ -344,7 +342,7 @@ static struct platform_driver electra_cf_driver = { .of_match_table = electra_cf_match, }, .probe = electra_cf_probe, - .remove = electra_cf_remove, + .remove_new = electra_cf_remove, }; module_platform_driver(electra_cf_driver); diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c index e613818dc0bc..80137c7afe0d 100644 --- a/drivers/pcmcia/omap_cf.c +++ b/drivers/pcmcia/omap_cf.c @@ -290,7 +290,7 @@ fail0: return status; } -static int __exit omap_cf_remove(struct platform_device *pdev) +static void __exit omap_cf_remove(struct platform_device *pdev) { struct omap_cf_socket *cf = platform_get_drvdata(pdev); @@ -300,14 +300,13 @@ static int __exit omap_cf_remove(struct platform_device *pdev) release_mem_region(cf->phys_cf, SZ_8K); free_irq(cf->irq, cf); kfree(cf); - return 0; } static struct platform_driver omap_cf_driver = { .driver = { .name = driver_name, }, - .remove = __exit_p(omap_cf_remove), + .remove_new = __exit_p(omap_cf_remove), }; static int __init omap_cf_init(void) diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index 5254028354f4..457fb81b497a 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -313,15 +313,13 @@ err0: return ret; } -static int pxa2xx_drv_pcmcia_remove(struct platform_device *dev) +static void pxa2xx_drv_pcmcia_remove(struct platform_device *dev) { struct skt_dev_info *sinfo = platform_get_drvdata(dev); int i; for (i = 0; i < sinfo->nskt; i++) soc_pcmcia_remove_one(&sinfo->skt[i]); - - return 0; } static int pxa2xx_drv_pcmcia_resume(struct device *dev) @@ -338,7 +336,7 @@ static const struct dev_pm_ops pxa2xx_drv_pcmcia_pm_ops = { static struct platform_driver pxa2xx_pcmcia_driver = { .probe = pxa2xx_drv_pcmcia_probe, - .remove = pxa2xx_drv_pcmcia_remove, + .remove_new = pxa2xx_drv_pcmcia_remove, .driver = { .name = "pxa2xx-pcmcia", .pm = &pxa2xx_drv_pcmcia_pm_ops, diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c index 89d4ba58c891..ccb219c38761 100644 --- a/drivers/pcmcia/sa1100_generic.c +++ b/drivers/pcmcia/sa1100_generic.c @@ -158,20 +158,18 @@ static int sa11x0_drv_pcmcia_probe(struct platform_device *pdev) return sa11xx_drv_pcmcia_add_one(skt); } -static int sa11x0_drv_pcmcia_remove(struct platform_device *dev) +static void sa11x0_drv_pcmcia_remove(struct platform_device *dev) { struct soc_pcmcia_socket *skt; if (dev->id == -1) { sa11x0_drv_pcmcia_legacy_remove(dev); - return 0; + return; } skt = platform_get_drvdata(dev); soc_pcmcia_remove_one(skt); - - return 0; } static struct platform_driver sa11x0_pcmcia_driver = { @@ -179,7 +177,7 @@ static struct platform_driver sa11x0_pcmcia_driver = { .name = "sa11x0-pcmcia", }, .probe = sa11x0_drv_pcmcia_probe, - .remove = sa11x0_drv_pcmcia_remove, + .remove_new = sa11x0_drv_pcmcia_remove, }; /* sa11x0_pcmcia_init() diff --git a/drivers/pcmcia/xxs1500_ss.c b/drivers/pcmcia/xxs1500_ss.c index b11c7abb1dc0..2a93fbbd128d 100644 --- a/drivers/pcmcia/xxs1500_ss.c +++ b/drivers/pcmcia/xxs1500_ss.c @@ -301,7 +301,7 @@ out0: return ret; } -static int xxs1500_pcmcia_remove(struct platform_device *pdev) +static void xxs1500_pcmcia_remove(struct platform_device *pdev) { struct xxs1500_pcmcia_sock *sock = platform_get_drvdata(pdev); @@ -309,8 +309,6 @@ static int xxs1500_pcmcia_remove(struct platform_device *pdev) free_irq(gpio_to_irq(GPIO_CDA), sock); iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); kfree(sock); - - return 0; } static struct platform_driver xxs1500_pcmcia_socket_driver = { @@ -318,7 +316,7 @@ static struct platform_driver xxs1500_pcmcia_socket_driver = { .name = "xxs1500_pcmcia", }, .probe = xxs1500_pcmcia_probe, - .remove = xxs1500_pcmcia_remove, + .remove_new = xxs1500_pcmcia_remove, }; module_platform_driver(xxs1500_pcmcia_socket_driver); diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c index 05eab9014132..a4746f6cb8a1 100644 --- a/drivers/phy/mediatek/phy-mtk-tphy.c +++ b/drivers/phy/mediatek/phy-mtk-tphy.c @@ -185,6 +185,10 @@ #define P3D_RG_CDR_BIR_LTD1 GENMASK(28, 24) #define P3D_RG_CDR_BIR_LTD0 GENMASK(12, 8) +#define U3P_U3_PHYD_TOP1 0x100 +#define P3D_RG_PHY_MODE GENMASK(2, 1) +#define P3D_RG_FORCE_PHY_MODE BIT(0) + #define U3P_U3_PHYD_RXDET1 0x128 #define P3D_RG_RXDET_STB2_SET GENMASK(17, 9) @@ -327,6 +331,7 @@ struct mtk_phy_instance { int discth; int pre_emphasis; bool bc12_en; + bool type_force_mode; }; struct mtk_tphy { @@ -768,6 +773,23 @@ static void u3_phy_instance_init(struct mtk_tphy *tphy, void __iomem *phya = u3_banks->phya; void __iomem *phyd = u3_banks->phyd; + if (instance->type_force_mode) { + /* force phy as usb mode, default is pcie rc mode */ + mtk_phy_update_field(phyd + U3P_U3_PHYD_TOP1, P3D_RG_PHY_MODE, 1); + mtk_phy_set_bits(phyd + U3P_U3_PHYD_TOP1, P3D_RG_FORCE_PHY_MODE); + /* power down phy by ip and pipe reset */ + mtk_phy_set_bits(u3_banks->chip + U3P_U3_CHIP_GPIO_CTLD, + P3C_FORCE_IP_SW_RST | P3C_MCU_BUS_CK_GATE_EN); + mtk_phy_set_bits(u3_banks->chip + U3P_U3_CHIP_GPIO_CTLE, + P3C_RG_SWRST_U3_PHYD | P3C_RG_SWRST_U3_PHYD_FORCE_EN); + udelay(10); + /* power on phy again */ + mtk_phy_clear_bits(u3_banks->chip + U3P_U3_CHIP_GPIO_CTLD, + P3C_FORCE_IP_SW_RST | P3C_MCU_BUS_CK_GATE_EN); + mtk_phy_clear_bits(u3_banks->chip + U3P_U3_CHIP_GPIO_CTLE, + P3C_RG_SWRST_U3_PHYD | P3C_RG_SWRST_U3_PHYD_FORCE_EN); + } + /* gating PCIe Analog XTAL clock */ mtk_phy_set_bits(u3_banks->spllc + U3P_SPLLC_XTALCTL3, XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD); @@ -1120,6 +1142,9 @@ static void phy_parse_property(struct mtk_tphy *tphy, { struct device *dev = &instance->phy->dev; + if (instance->type == PHY_TYPE_USB3) + instance->type_force_mode = device_property_read_bool(dev, "mediatek,force-mode"); + if (instance->type != PHY_TYPE_USB2) return; diff --git a/drivers/phy/microchip/lan966x_serdes.c b/drivers/phy/microchip/lan966x_serdes.c index c1a41b6cd29b..b5ac2b7995e7 100644 --- a/drivers/phy/microchip/lan966x_serdes.c +++ b/drivers/phy/microchip/lan966x_serdes.c @@ -96,6 +96,8 @@ static const struct serdes_mux lan966x_serdes_muxes[] = { SERDES_MUX_SGMII(SERDES6G(1), 3, HSIO_HW_CFG_SD6G_1_CFG, HSIO_HW_CFG_SD6G_1_CFG_SET(1)), + SERDES_MUX_SGMII(SERDES6G(2), 4, 0, 0), + SERDES_MUX_RGMII(RGMII(0), 2, HSIO_HW_CFG_RGMII_0_CFG | HSIO_HW_CFG_RGMII_ENA | HSIO_HW_CFG_GMII_ENA, diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 840b7f8a31c5..ee4ce4249698 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -6,11 +6,11 @@ * */ #include <linux/of.h> -#include<linux/phy/phy.h> -#include<linux/platform_device.h> -#include<linux/module.h> -#include<linux/gpio.h> -#include<linux/gpio/consumer.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/mux/consumer.h> struct can_transceiver_data { diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 96a0b1e111f3..d9be6a4d5383 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -959,7 +959,7 @@ struct phy *phy_create(struct device *dev, struct device_node *node, if (!phy) return ERR_PTR(-ENOMEM); - id = ida_simple_get(&phy_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&phy_ida, GFP_KERNEL); if (id < 0) { dev_err(dev, "unable to get id\n"); ret = id; @@ -1232,7 +1232,7 @@ static void phy_release(struct device *dev) dev_vdbg(dev, "releasing '%s'\n", dev_name(dev)); debugfs_remove_recursive(phy->debugfs); regulator_put(phy->pwr); - ida_simple_remove(&phy_ida, phy->id); + ida_free(&phy_ida, phy->id); kfree(phy); } diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index f6c727249104..1ad10110dd25 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -1203,6 +1203,127 @@ static const struct qmp_phy_init_tbl sc8280xp_usb43dp_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), }; +static const struct qmp_phy_init_tbl x1e80100_usb43dp_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER1, 0x62), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE0, 0xc2), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE1, 0xc2), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_CFG, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MSB_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MSB_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE1_MODE0, 0xba), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE2_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE1_MODE1, 0xba), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE2_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CORE_CLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORE_CLK_EN, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x76), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO_MODE1, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_INTEGLOOP_GAIN0_MODE0, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_INTEGLOOP_GAIN0_MODE1, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_INITVAL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAXVAL2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SVS_MODE_CLK_SEL, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BG_TIMER, 0x0a), +}; + +static const struct qmp_phy_init_tbl x1e80100_usb43dp_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_LANE_MODE_2, 0x50), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_LANE_MODE_3, 0x50), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_RES_CODE_LANE_OFFSET_TX, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_TX_RES_CODE_LANE_OFFSET_RX, 0x0a), +}; + +static const struct qmp_phy_init_tbl x1e80100_usb43dp_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_SIGDET_ENABLES, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE_0_1_B0, 0xc3), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE_0_1_B1, 0xc3), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE_0_1_B2, 0xd8), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE_0_1_B3, 0x9e), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE_0_1_B4, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE_0_1_B5, 0xb6), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE_0_1_B6, 0x64), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE2_B0, 0xd6), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE2_B1, 0xee), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE2_B2, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE2_B3, 0x9a), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE2_B4, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE2_B5, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_MODE_RATE2_B6, 0xe3), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_IVCM_CAL_CODE_OVERRIDE, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_RX_IVCM_CAL_CTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_RX_SUMMER_CAL_SPD_MODE, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_PI_CONTROLS, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_PI_CTRL1, 0xd0), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_PI_CTRL2, 0x48), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_SB2_GAIN2_RATE2, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_RX_IVCM_POSTCAL_OFFSET, 0x7c), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_VGA_CAL_CNTRL1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_VGA_CAL_MAN_VAL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_DAC_ENABLE1, 0x88), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_DFE_3, 0x45), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_GM_CAL, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_FO_GAIN_RATE2, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_UCDR_SO_GAIN_RATE2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_Q_PI_INTRINSIC_BIAS_RATE32, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V6_N4_RX_RX_BKUP_CTRL1, 0x14), +}; + +static const struct qmp_phy_init_tbl x1e80100_usb43dp_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V6_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_LOCK_DETECT_CONFIG1, 0xc4), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_LOCK_DETECT_CONFIG2, 0x89), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_RX_SIGDET_LVL, 0x55), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_ALIGN_DETECT_CONFIG1, 0xd4), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_ALIGN_DETECT_CONFIG2, 0x30), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl x1e80100_usb43dp_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + /* list of regulators */ struct qmp_regulator_data { const char *name; @@ -1682,6 +1803,51 @@ static const struct qmp_phy_cfg sc8280xp_usb43dpphy_cfg = { .regs = qmp_v5_5nm_usb3phy_regs_layout, }; +static const struct qmp_phy_cfg x1e80100_usb3dpphy_cfg = { + .offsets = &qmp_combo_offsets_v5, + + .serdes_tbl = x1e80100_usb43dp_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(x1e80100_usb43dp_serdes_tbl), + .tx_tbl = x1e80100_usb43dp_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(x1e80100_usb43dp_tx_tbl), + .rx_tbl = x1e80100_usb43dp_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(x1e80100_usb43dp_rx_tbl), + .pcs_tbl = x1e80100_usb43dp_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(x1e80100_usb43dp_pcs_tbl), + .pcs_usb_tbl = x1e80100_usb43dp_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(x1e80100_usb43dp_pcs_usb_tbl), + + .dp_serdes_tbl = qmp_v6_dp_serdes_tbl, + .dp_serdes_tbl_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl), + .dp_tx_tbl = qmp_v6_dp_tx_tbl, + .dp_tx_tbl_num = ARRAY_SIZE(qmp_v6_dp_tx_tbl), + + .serdes_tbl_rbr = qmp_v6_dp_serdes_tbl_rbr, + .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_rbr), + .serdes_tbl_hbr = qmp_v6_dp_serdes_tbl_hbr, + .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr), + .serdes_tbl_hbr2 = qmp_v6_dp_serdes_tbl_hbr2, + .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr2), + .serdes_tbl_hbr3 = qmp_v6_dp_serdes_tbl_hbr3, + .serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr3), + + .swing_hbr_rbr = &qmp_dp_v5_voltage_swing_hbr_rbr, + .pre_emphasis_hbr_rbr = &qmp_dp_v5_pre_emphasis_hbr_rbr, + .swing_hbr3_hbr2 = &qmp_dp_v5_voltage_swing_hbr3_hbr2, + .pre_emphasis_hbr3_hbr2 = &qmp_dp_v5_pre_emphasis_hbr3_hbr2, + + .dp_aux_init = qmp_v4_dp_aux_init, + .configure_dp_tx = qmp_v4_configure_dp_tx, + .configure_dp_phy = qmp_v4_configure_dp_phy, + .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, + + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v45_usb3phy_regs_layout, +}; + static const struct qmp_phy_cfg sm6350_usb3dpphy_cfg = { .offsets = &qmp_combo_offsets_v3, @@ -3518,6 +3684,14 @@ static const struct of_device_id qmp_combo_of_match_table[] = { .compatible = "qcom,sm8550-qmp-usb3-dp-phy", .data = &sm8550_usb3dpphy_cfg, }, + { + .compatible = "qcom,sm8650-qmp-usb3-dp-phy", + .data = &sm8550_usb3dpphy_cfg, + }, + { + .compatible = "qcom,x1e80100-qmp-usb3-dp-phy", + .data = &x1e80100_usb3dpphy_cfg, + }, { } }; MODULE_DEVICE_TABLE(of, qmp_combo_of_match_table); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index b64598ac59f4..2af7115ef968 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -1909,6 +1909,35 @@ static const struct qmp_phy_init_tbl sm8550_qmp_gen4x2_pcie_pcs_misc_tbl[] = { QMP_PHY_INIT_CFG(QPHY_PCIE_V6_20_PCS_G4_FOM_EQ_CONFIG5, 0xf2), }; +static const struct qmp_phy_init_tbl sm8650_qmp_gen4x2_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_UCDR_FO_GAIN_RATE_2, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_UCDR_FO_GAIN_RATE_3, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_UCDR_PI_CONTROLS, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_UCDR_SO_ACC_DEFAULT_VAL_RATE3, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_IVCM_CAL_CTRL2, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_DFE_3, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_VGA_CAL_MAN_VAL, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_GM_CAL, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_EQU_ADAPTOR_CNTRL4, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_PHPRE_CTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B0, 0xd3), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B1, 0xd3), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B3, 0x9a), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B4, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B5, 0xb6), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE2_B6, 0xee), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B0, 0x23), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B1, 0x9b), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B2, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B3, 0xdf), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B4, 0x43), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B5, 0x76), + QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B6, 0xff), +}; + static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x2_pcie_serdes_alt_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN, 0x14), QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f), @@ -3047,6 +3076,36 @@ static const struct qmp_phy_cfg sm8550_qmp_gen4x2_pciephy_cfg = { .has_nocsr_reset = true, }; +static const struct qmp_phy_cfg sm8650_qmp_gen4x2_pciephy_cfg = { + .lanes = 2, + + .offsets = &qmp_pcie_offsets_v6_20, + + .tbls = { + .serdes = sm8550_qmp_gen4x2_pcie_serdes_tbl, + .serdes_num = ARRAY_SIZE(sm8550_qmp_gen4x2_pcie_serdes_tbl), + .tx = sm8550_qmp_gen4x2_pcie_tx_tbl, + .tx_num = ARRAY_SIZE(sm8550_qmp_gen4x2_pcie_tx_tbl), + .rx = sm8650_qmp_gen4x2_pcie_rx_tbl, + .rx_num = ARRAY_SIZE(sm8650_qmp_gen4x2_pcie_rx_tbl), + .pcs = sm8550_qmp_gen4x2_pcie_pcs_tbl, + .pcs_num = ARRAY_SIZE(sm8550_qmp_gen4x2_pcie_pcs_tbl), + .pcs_misc = sm8550_qmp_gen4x2_pcie_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(sm8550_qmp_gen4x2_pcie_pcs_misc_tbl), + .ln_shrd = sm8550_qmp_gen4x2_pcie_ln_shrd_tbl, + .ln_shrd_num = ARRAY_SIZE(sm8550_qmp_gen4x2_pcie_ln_shrd_tbl), + }, + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = sm8550_qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(sm8550_qmp_phy_vreg_l), + .regs = pciephy_v5_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS_4_20, + .has_nocsr_reset = true, +}; + static const struct qmp_phy_cfg sa8775p_qmp_gen4x2_pciephy_cfg = { .lanes = 2, .offsets = &qmp_pcie_offsets_v5_20, @@ -3820,6 +3879,12 @@ static const struct of_device_id qmp_pcie_of_match_table[] = { }, { .compatible = "qcom,sm8550-qmp-gen4x2-pcie-phy", .data = &sm8550_qmp_gen4x2_pciephy_cfg, + }, { + .compatible = "qcom,sm8650-qmp-gen3x2-pcie-phy", + .data = &sm8550_qmp_gen3x2_pciephy_cfg, + }, { + .compatible = "qcom,sm8650-qmp-gen4x2-pcie-phy", + .data = &sm8650_qmp_gen4x2_pciephy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v6.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v6.h index c23d5e41e25b..fe6c450f6123 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v6.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v6.h @@ -12,6 +12,7 @@ #define QPHY_V6_PCS_UFS_SW_RESET 0x008 #define QPHY_V6_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB 0x00c #define QPHY_V6_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB 0x010 +#define QPHY_V6_PCS_UFS_PCS_CTRL1 0x020 #define QPHY_V6_PCS_UFS_PLL_CNTL 0x02c #define QPHY_V6_PCS_UFS_TX_LARGE_AMP_DRV_LVL 0x030 #define QPHY_V6_PCS_UFS_TX_SMALL_AMP_DRV_LVL 0x038 diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v7.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v7.h new file mode 100644 index 000000000000..24368d45ae76 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v7.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023, Linaro Limited + */ + +#ifndef QCOM_PHY_QMP_PCS_USB_V7_H_ +#define QCOM_PHY_QMP_PCS_USB_V7_H_ + +#define QPHY_V7_PCS_USB3_POWER_STATE_CONFIG1 0x00 +#define QPHY_V7_PCS_USB3_AUTONOMOUS_MODE_CTRL 0x08 +#define QPHY_V7_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR 0x14 +#define QPHY_V7_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL 0x18 +#define QPHY_V7_PCS_USB3_RXEQTRAINING_DFE_TIME_S2 0x3c +#define QPHY_V7_PCS_USB3_RCVR_DTCT_DLY_U3_L 0x40 +#define QPHY_V7_PCS_USB3_RCVR_DTCT_DLY_U3_H 0x44 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v7.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v7.h new file mode 100644 index 000000000000..c7759892ed2e --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v7.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023, Linaro Limited + */ + +#ifndef QCOM_PHY_QMP_PCS_V7_H_ +#define QCOM_PHY_QMP_PCS_V7_H_ + +/* Only for QMP V7 PHY - USB/PCIe PCS registers */ +#define QPHY_V7_PCS_SW_RESET 0x000 +#define QPHY_V7_PCS_PCS_STATUS1 0x014 +#define QPHY_V7_PCS_POWER_DOWN_CONTROL 0x040 +#define QPHY_V7_PCS_START_CONTROL 0x044 +#define QPHY_V7_PCS_POWER_STATE_CONFIG1 0x090 +#define QPHY_V7_PCS_LOCK_DETECT_CONFIG1 0x0c4 +#define QPHY_V7_PCS_LOCK_DETECT_CONFIG2 0x0c8 +#define QPHY_V7_PCS_LOCK_DETECT_CONFIG3 0x0cc +#define QPHY_V7_PCS_LOCK_DETECT_CONFIG6 0x0d8 +#define QPHY_V7_PCS_REFGEN_REQ_CONFIG1 0x0dc +#define QPHY_V7_PCS_RX_SIGDET_LVL 0x188 +#define QPHY_V7_PCS_RCVR_DTCT_DLY_P1U2_L 0x190 +#define QPHY_V7_PCS_RCVR_DTCT_DLY_P1U2_H 0x194 +#define QPHY_V7_PCS_RATE_SLEW_CNTRL1 0x198 +#define QPHY_V7_PCS_CDR_RESET_TIME 0x1b0 +#define QPHY_V7_PCS_ALIGN_DETECT_CONFIG1 0x1c0 +#define QPHY_V7_PCS_ALIGN_DETECT_CONFIG2 0x1c4 +#define QPHY_V7_PCS_PCS_TX_RX_CONFIG 0x1d0 +#define QPHY_V7_PCS_EQ_CONFIG1 0x1dc +#define QPHY_V7_PCS_EQ_CONFIG2 0x1e0 +#define QPHY_V7_PCS_EQ_CONFIG5 0x1ec + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v6.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v6.h index f420f8faf16a..ec7291424dd1 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v6.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v6.h @@ -22,6 +22,8 @@ #define QSERDES_V6_COM_DIV_FRAC_START2_MODE1 0x34 #define QSERDES_V6_COM_DIV_FRAC_START3_MODE1 0x38 #define QSERDES_V6_COM_HSCLK_SEL_1 0x3c +#define QSERDES_V6_COM_INTEGLOOP_GAIN0_MODE1 0x40 +#define QSERDES_V6_COM_INTEGLOOP_GAIN1_MODE1 0x44 #define QSERDES_V6_COM_VCO_TUNE1_MODE1 0x48 #define QSERDES_V6_COM_VCO_TUNE2_MODE1 0x4c #define QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x50 @@ -48,6 +50,7 @@ #define QSERDES_V6_COM_VCO_TUNE2_MODE0 0xac #define QSERDES_V6_COM_BG_TIMER 0xbc #define QSERDES_V6_COM_SSC_EN_CENTER 0xc0 +#define QSERDES_V6_COM_SSC_ADJ_PER1 0xc4 #define QSERDES_V6_COM_SSC_PER1 0xcc #define QSERDES_V6_COM_SSC_PER2 0xd0 #define QSERDES_V6_COM_PLL_POST_DIV_MUX 0xd8 @@ -56,6 +59,7 @@ #define QSERDES_V6_COM_SYS_CLK_CTRL 0xe4 #define QSERDES_V6_COM_SYSCLK_BUF_ENABLE 0xe8 #define QSERDES_V6_COM_PLL_IVCO 0xf4 +#define QSERDES_V6_COM_PLL_IVCO_MODE1 0xf8 #define QSERDES_V6_COM_SYSCLK_EN_SEL 0x110 #define QSERDES_V6_COM_RESETSM_CNTRL 0x118 #define QSERDES_V6_COM_LOCK_CMP_EN 0x120 @@ -63,6 +67,7 @@ #define QSERDES_V6_COM_VCO_TUNE_CTRL 0x13c #define QSERDES_V6_COM_VCO_TUNE_MAP 0x140 #define QSERDES_V6_COM_VCO_TUNE_INITVAL2 0x148 +#define QSERDES_V6_COM_VCO_TUNE_MAXVAL2 0x158 #define QSERDES_V6_COM_CLK_SELECT 0x164 #define QSERDES_V6_COM_CORE_CLK_EN 0x170 #define QSERDES_V6_COM_CMN_CONFIG_1 0x174 diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v7.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v7.h new file mode 100644 index 000000000000..7430f4921477 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v7.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023, Linaro Limited + */ + +#ifndef QCOM_PHY_QMP_QSERDES_COM_V7_H_ +#define QCOM_PHY_QMP_QSERDES_COM_V7_H_ + +/* Only for QMP V7 PHY - QSERDES COM registers */ + +#define QSERDES_V7_COM_SSC_STEP_SIZE1_MODE1 0x00 +#define QSERDES_V7_COM_SSC_STEP_SIZE2_MODE1 0x04 +#define QSERDES_V7_COM_CP_CTRL_MODE1 0x10 +#define QSERDES_V7_COM_PLL_RCTRL_MODE1 0x14 +#define QSERDES_V7_COM_PLL_CCTRL_MODE1 0x18 +#define QSERDES_V7_COM_CORECLK_DIV_MODE1 0x1c +#define QSERDES_V7_COM_LOCK_CMP1_MODE1 0x20 +#define QSERDES_V7_COM_LOCK_CMP2_MODE1 0x24 +#define QSERDES_V7_COM_DEC_START_MODE1 0x28 +#define QSERDES_V7_COM_DEC_START_MSB_MODE1 0x2c +#define QSERDES_V7_COM_DIV_FRAC_START1_MODE1 0x30 +#define QSERDES_V7_COM_DIV_FRAC_START2_MODE1 0x34 +#define QSERDES_V7_COM_DIV_FRAC_START3_MODE1 0x38 +#define QSERDES_V7_COM_HSCLK_SEL_1 0x3c +#define QSERDES_V7_COM_INTEGLOOP_GAIN0_MODE1 0x40 +#define QSERDES_V7_COM_INTEGLOOP_GAIN1_MODE1 0x44 +#define QSERDES_V7_COM_VCO_TUNE1_MODE1 0x48 +#define QSERDES_V7_COM_VCO_TUNE2_MODE1 0x4c +#define QSERDES_V7_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x50 +#define QSERDES_V7_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x54 +#define QSERDES_V7_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x58 +#define QSERDES_V7_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x5c +#define QSERDES_V7_COM_SSC_STEP_SIZE1_MODE0 0x60 +#define QSERDES_V7_COM_SSC_STEP_SIZE2_MODE0 0x64 +#define QSERDES_V7_COM_CP_CTRL_MODE0 0x70 +#define QSERDES_V7_COM_PLL_RCTRL_MODE0 0x74 +#define QSERDES_V7_COM_PLL_CCTRL_MODE0 0x78 +#define QSERDES_V7_COM_PLL_CORE_CLK_DIV_MODE0 0x7c +#define QSERDES_V7_COM_LOCK_CMP1_MODE0 0x80 +#define QSERDES_V7_COM_LOCK_CMP2_MODE0 0x84 +#define QSERDES_V7_COM_DEC_START_MODE0 0x88 +#define QSERDES_V7_COM_DEC_START_MSB_MODE0 0x8c +#define QSERDES_V7_COM_DIV_FRAC_START1_MODE0 0x90 +#define QSERDES_V7_COM_DIV_FRAC_START2_MODE0 0x94 +#define QSERDES_V7_COM_DIV_FRAC_START3_MODE0 0x98 +#define QSERDES_V7_COM_HSCLK_HS_SWITCH_SEL_1 0x9c +#define QSERDES_V7_COM_INTEGLOOP_GAIN0_MODE0 0xa0 +#define QSERDES_V7_COM_INTEGLOOP_GAIN1_MODE0 0xa4 +#define QSERDES_V7_COM_VCO_TUNE1_MODE0 0xa8 +#define QSERDES_V7_COM_VCO_TUNE2_MODE0 0xac +#define QSERDES_V7_COM_BG_TIMER 0xbc +#define QSERDES_V7_COM_SSC_EN_CENTER 0xc0 +#define QSERDES_V7_COM_SSC_ADJ_PER1 0xc4 +#define QSERDES_V7_COM_SSC_PER1 0xcc +#define QSERDES_V7_COM_SSC_PER2 0xd0 +#define QSERDES_V7_COM_PLL_POST_DIV_MUX 0xd8 +#define QSERDES_V7_COM_PLL_BIAS_EN_CLK_BUFLR_EN 0xdc +#define QSERDES_V7_COM_CLK_ENABLE1 0xe0 +#define QSERDES_V7_COM_SYS_CLK_CTRL 0xe4 +#define QSERDES_V7_COM_SYSCLK_BUF_ENABLE 0xe8 +#define QSERDES_V7_COM_PLL_IVCO 0xf4 +#define QSERDES_V7_COM_PLL_IVCO_MODE1 0xf8 +#define QSERDES_V7_COM_SYSCLK_EN_SEL 0x110 +#define QSERDES_V7_COM_RESETSM_CNTRL 0x118 +#define QSERDES_V7_COM_LOCK_CMP_EN 0x120 +#define QSERDES_V7_COM_LOCK_CMP_CFG 0x124 +#define QSERDES_V7_COM_VCO_TUNE_CTRL 0x13c +#define QSERDES_V7_COM_VCO_TUNE_MAP 0x140 +#define QSERDES_V7_COM_VCO_TUNE_INITVAL2 0x148 +#define QSERDES_V7_COM_VCO_TUNE_MAXVAL2 0x158 +#define QSERDES_V7_COM_CLK_SELECT 0x164 +#define QSERDES_V7_COM_CORE_CLK_EN 0x170 +#define QSERDES_V7_COM_CMN_CONFIG_1 0x174 +#define QSERDES_V7_COM_SVS_MODE_CLK_SEL 0x17c +#define QSERDES_V7_COM_CMN_MISC_1 0x184 +#define QSERDES_V7_COM_CMN_MODE 0x188 +#define QSERDES_V7_COM_PLL_VCO_DC_LEVEL_CTRL 0x198 +#define QSERDES_V7_COM_AUTO_GAIN_ADJ_CTRL_1 0x1a4 +#define QSERDES_V7_COM_AUTO_GAIN_ADJ_CTRL_2 0x1a8 +#define QSERDES_V7_COM_AUTO_GAIN_ADJ_CTRL_3 0x1ac +#define QSERDES_V7_COM_ADDITIONAL_MISC 0x1b4 +#define QSERDES_V7_COM_ADDITIONAL_MISC_2 0x1b8 +#define QSERDES_V7_COM_ADDITIONAL_MISC_3 0x1bc +#define QSERDES_V7_COM_CMN_STATUS 0x1d0 +#define QSERDES_V7_COM_C_READY_STATUS 0x1f8 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v6.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v6.h index 15bcb4ba9139..35d497fd9f9a 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v6.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v6.h @@ -10,10 +10,18 @@ #define QSERDES_UFS_V6_TX_RES_CODE_LANE_RX 0x2c #define QSERDES_UFS_V6_TX_RES_CODE_LANE_OFFSET_TX 0x30 #define QSERDES_UFS_V6_TX_RES_CODE_LANE_OFFSET_RX 0x34 +#define QSERDES_UFS_V6_TX_LANE_MODE_1 0x7c +#define QSERDES_UFS_V6_TX_FR_DCC_CTRL 0x108 #define QSERDES_UFS_V6_RX_UCDR_FASTLOCK_FO_GAIN_RATE2 0x08 #define QSERDES_UFS_V6_RX_UCDR_FASTLOCK_FO_GAIN_RATE4 0x10 +#define QSERDES_UFS_V6_RX_UCDR_SO_SATURATION 0x28 +#define QSERDES_UFS_V6_RX_UCDR_PI_CTRL1 0x58 +#define QSERDES_UFS_V6_RX_RX_TERM_BW_CTRL0 0xc4 +#define QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE2 0xd4 +#define QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE4 0xdc #define QSERDES_UFS_V6_RX_VGA_CAL_MAN_VAL 0x178 +#define QSERDES_UFS_V6_RX_INTERFACE_MODE 0x1e0 #define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B0 0x208 #define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B1 0x20c #define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B3 0x214 diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6.h index 8883e1de730e..23ffcfae9efa 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6.h @@ -23,6 +23,7 @@ #define QSERDES_V6_TX_PARRATE_REC_DETECT_IDLE_EN 0x60 #define QSERDES_V6_TX_BIST_PATTERN7 0x7c #define QSERDES_V6_TX_LANE_MODE_1 0x84 +#define QSERDES_V6_TX_LANE_MODE_2 0x88 #define QSERDES_V6_TX_LANE_MODE_3 0x8c #define QSERDES_V6_TX_LANE_MODE_4 0x90 #define QSERDES_V6_TX_LANE_MODE_5 0x94 diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_20.h index 5385a8b60970..6ed5339fd2ea 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_20.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_20.h @@ -15,10 +15,13 @@ #define QSERDES_V6_20_RX_UCDR_FO_GAIN_RATE_2 0x08 #define QSERDES_V6_20_RX_UCDR_FO_GAIN_RATE_3 0x0c +#define QSERDES_V6_20_RX_UCDR_SO_GAIN_RATE_2 0x18 #define QSERDES_V6_20_RX_UCDR_PI_CONTROLS 0x20 #define QSERDES_V6_20_RX_UCDR_SO_ACC_DEFAULT_VAL_RATE3 0x34 #define QSERDES_V6_20_RX_IVCM_CAL_CTRL2 0x9c #define QSERDES_V6_20_RX_IVCM_POSTCAL_OFFSET 0xa0 +#define QSERDES_V6_20_RX_DFE_1 0xac +#define QSERDES_V6_20_RX_DFE_2 0xb0 #define QSERDES_V6_20_RX_DFE_3 0xb4 #define QSERDES_V6_20_RX_VGA_CAL_MAN_VAL 0xe8 #define QSERDES_V6_20_RX_GM_CAL 0x10c @@ -41,5 +44,6 @@ #define QSERDES_V6_20_RX_MODE_RATE3_B4 0x220 #define QSERDES_V6_20_RX_MODE_RATE3_B5 0x224 #define QSERDES_V6_20_RX_MODE_RATE3_B6 0x228 +#define QSERDES_V6_20_RX_BKUP_CTRL1 0x22c #endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h new file mode 100644 index 000000000000..a814ad11af07 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6_n4.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023, Linaro Limited + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V6_N4_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_V6_N4_H_ + +#define QSERDES_V6_N4_TX_RES_CODE_LANE_OFFSET_TX 0x30 +#define QSERDES_V6_N4_TX_RES_CODE_LANE_OFFSET_RX 0x34 +#define QSERDES_V6_N4_TX_LANE_MODE_1 0x78 +#define QSERDES_V6_N4_TX_LANE_MODE_2 0x7c +#define QSERDES_V6_N4_TX_LANE_MODE_3 0x80 + +#define QSERDES_V6_N4_RX_UCDR_FO_GAIN_RATE2 0x8 +#define QSERDES_V6_N4_RX_UCDR_SO_GAIN_RATE2 0x18 +#define QSERDES_V6_N4_RX_UCDR_PI_CONTROLS 0x20 +#define QSERDES_V6_N4_RX_IVCM_CAL_CODE_OVERRIDE 0x94 +#define QSERDES_V6_N4_RX_RX_IVCM_CAL_CTRL2 0x9c +#define QSERDES_V6_N4_RX_RX_IVCM_POSTCAL_OFFSET 0xa0 +#define QSERDES_V6_N4_RX_DFE_3 0xb4 +#define QSERDES_V6_N4_RX_VGA_CAL_CNTRL1 0xe0 +#define QSERDES_V6_N4_RX_VGA_CAL_MAN_VAL 0xe8 +#define QSERDES_V6_N4_RX_GM_CAL 0x10c +#define QSERDES_V6_N4_RX_SIGDET_ENABLES 0x148 +#define QSERDES_V6_N4_RX_SIGDET_CNTRL 0x14c +#define QSERDES_V6_N4_RX_SIGDET_DEGLITCH_CNTRL 0x154 +#define QSERDES_V6_N4_RX_DFE_CTLE_POST_CAL_OFFSET 0x194 +#define QSERDES_V6_N4_RX_Q_PI_INTRINSIC_BIAS_RATE32 0x1dc +#define QSERDES_V6_N4_RX_UCDR_PI_CTRL1 0x23c +#define QSERDES_V6_N4_RX_UCDR_PI_CTRL2 0x240 +#define QSERDES_V6_N4_RX_UCDR_SB2_GAIN2_RATE2 0x27c +#define QSERDES_V6_N4_RX_DFE_DAC_ENABLE1 0x298 +#define QSERDES_V6_N4_RX_MODE_RATE_0_1_B0 0x2b8 +#define QSERDES_V6_N4_RX_MODE_RATE_0_1_B1 0x2bc +#define QSERDES_V6_N4_RX_MODE_RATE_0_1_B2 0x2c0 +#define QSERDES_V6_N4_RX_MODE_RATE_0_1_B3 0x2c4 +#define QSERDES_V6_N4_RX_MODE_RATE_0_1_B4 0x2c8 +#define QSERDES_V6_N4_RX_MODE_RATE_0_1_B5 0x2cc +#define QSERDES_V6_N4_RX_MODE_RATE_0_1_B6 0x2d0 +#define QSERDES_V6_N4_RX_MODE_RATE2_B0 0x2d4 +#define QSERDES_V6_N4_RX_MODE_RATE2_B1 0x2d8 +#define QSERDES_V6_N4_RX_MODE_RATE2_B2 0x2dc +#define QSERDES_V6_N4_RX_MODE_RATE2_B3 0x2e0 +#define QSERDES_V6_N4_RX_MODE_RATE2_B4 0x2e4 +#define QSERDES_V6_N4_RX_MODE_RATE2_B5 0x2e8 +#define QSERDES_V6_N4_RX_MODE_RATE2_B6 0x2ec +#define QSERDES_V6_N4_RX_RX_SUMMER_CAL_SPD_MODE 0x30c +#define QSERDES_V6_N4_RX_RX_BKUP_CTRL1 0x310 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v7.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v7.h new file mode 100644 index 000000000000..91f865b11347 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v7.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023, Linaro Limited + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V7_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_V7_H_ + +#define QSERDES_V7_TX_CLKBUF_ENABLE 0x08 +#define QSERDES_V7_TX_RESET_TSYNC_EN 0x1c +#define QSERDES_V7_TX_PRE_STALL_LDO_BOOST_EN 0x20 +#define QSERDES_V7_TX_TX_BAND 0x24 +#define QSERDES_V7_TX_INTERFACE_SELECT 0x2c +#define QSERDES_V7_TX_RES_CODE_LANE_TX 0x34 +#define QSERDES_V7_TX_RES_CODE_LANE_RX 0x38 +#define QSERDES_V7_TX_RES_CODE_LANE_OFFSET_TX 0x3c +#define QSERDES_V7_TX_RES_CODE_LANE_OFFSET_RX 0x40 +#define QSERDES_V7_TX_PARRATE_REC_DETECT_IDLE_EN 0x60 +#define QSERDES_V7_TX_BIST_PATTERN7 0x7c +#define QSERDES_V7_TX_LANE_MODE_1 0x84 +#define QSERDES_V7_TX_LANE_MODE_2 0x88 +#define QSERDES_V7_TX_LANE_MODE_3 0x8c +#define QSERDES_V7_TX_LANE_MODE_4 0x90 +#define QSERDES_V7_TX_LANE_MODE_5 0x94 +#define QSERDES_V7_TX_RCV_DETECT_LVL_2 0xa4 +#define QSERDES_V7_TX_TRAN_DRVR_EMP_EN 0xc0 +#define QSERDES_V7_TX_TX_INTERFACE_MODE 0xc4 +#define QSERDES_V7_TX_VMODE_CTRL1 0xc8 +#define QSERDES_V7_TX_PI_QEC_CTRL 0xe4 + +#define QSERDES_V7_RX_UCDR_FO_GAIN 0x08 +#define QSERDES_V7_RX_UCDR_SO_GAIN 0x14 +#define QSERDES_V7_RX_UCDR_FASTLOCK_FO_GAIN 0x30 +#define QSERDES_V7_RX_UCDR_SO_SATURATION_AND_ENABLE 0x34 +#define QSERDES_V7_RX_UCDR_FASTLOCK_COUNT_LOW 0x3c +#define QSERDES_V7_RX_UCDR_FASTLOCK_COUNT_HIGH 0x40 +#define QSERDES_V7_RX_UCDR_PI_CONTROLS 0x44 +#define QSERDES_V7_RX_UCDR_SB2_THRESH1 0x4c +#define QSERDES_V7_RX_UCDR_SB2_THRESH2 0x50 +#define QSERDES_V7_RX_UCDR_SB2_GAIN1 0x54 +#define QSERDES_V7_RX_UCDR_SB2_GAIN2 0x58 +#define QSERDES_V7_RX_AUX_DATA_TCOARSE_TFINE 0x60 +#define QSERDES_V7_RX_TX_ADAPT_POST_THRESH 0xcc +#define QSERDES_V7_RX_VGA_CAL_CNTRL1 0xd4 +#define QSERDES_V7_RX_VGA_CAL_CNTRL2 0xd8 +#define QSERDES_V7_RX_GM_CAL 0xdc +#define QSERDES_V7_RX_RX_EQU_ADAPTOR_CNTRL2 0xec +#define QSERDES_V7_RX_RX_EQU_ADAPTOR_CNTRL3 0xf0 +#define QSERDES_V7_RX_RX_EQU_ADAPTOR_CNTRL4 0xf4 +#define QSERDES_V7_RX_RX_IDAC_TSETTLE_LOW 0xf8 +#define QSERDES_V7_RX_RX_IDAC_TSETTLE_HIGH 0xfc +#define QSERDES_V7_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x110 +#define QSERDES_V7_RX_SIDGET_ENABLES 0x118 +#define QSERDES_V7_RX_SIGDET_CNTRL 0x11c +#define QSERDES_V7_RX_SIGDET_DEGLITCH_CNTRL 0x124 +#define QSERDES_V7_RX_RX_MODE_00_LOW 0x15c +#define QSERDES_V7_RX_RX_MODE_00_HIGH 0x160 +#define QSERDES_V7_RX_RX_MODE_00_HIGH2 0x164 +#define QSERDES_V7_RX_RX_MODE_00_HIGH3 0x168 +#define QSERDES_V7_RX_RX_MODE_00_HIGH4 0x16c +#define QSERDES_V7_RX_RX_MODE_01_LOW 0x170 +#define QSERDES_V7_RX_RX_MODE_01_HIGH 0x174 +#define QSERDES_V7_RX_RX_MODE_01_HIGH2 0x178 +#define QSERDES_V7_RX_RX_MODE_01_HIGH3 0x17c +#define QSERDES_V7_RX_RX_MODE_01_HIGH4 0x180 +#define QSERDES_V7_RX_RX_MODE_10_LOW 0x184 +#define QSERDES_V7_RX_RX_MODE_10_HIGH 0x188 +#define QSERDES_V7_RX_RX_MODE_10_HIGH2 0x18c +#define QSERDES_V7_RX_RX_MODE_10_HIGH3 0x190 +#define QSERDES_V7_RX_RX_MODE_10_HIGH4 0x194 +#define QSERDES_V7_RX_DFE_EN_TIMER 0x1a0 +#define QSERDES_V7_RX_DFE_CTLE_POST_CAL_OFFSET 0x1a4 +#define QSERDES_V7_RX_DCC_CTRL1 0x1a8 +#define QSERDES_V7_RX_VTH_CODE 0x1b0 +#define QSERDES_V7_RX_SIGDET_CAL_CTRL1 0x1e4 +#define QSERDES_V7_RX_SIGDET_CAL_TRIM 0x1f8 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c index 514fa14df634..3c2e6255e26f 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c @@ -763,22 +763,26 @@ static const struct qmp_phy_init_tbl sm8550_ufsphy_serdes[] = { QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x14), QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x7f), QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x4c), - QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18), - QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x14), - QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x99), - QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8550_ufsphy_hs_b_serdes[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x44), }; static const struct qmp_phy_init_tbl sm8550_ufsphy_tx[] = { - QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_LANE_MODE_1, 0x05), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_RES_CODE_LANE_OFFSET_TX, 0x07), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_FR_DCC_CTRL, 0x4c), }; static const struct qmp_phy_init_tbl sm8550_ufsphy_rx[] = { - QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FASTLOCK_FO_GAIN_RATE2, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FASTLOCK_FO_GAIN_RATE4, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE2, 0x0c), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_VGA_CAL_MAN_VAL, 0x0e), QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B0, 0xc2), @@ -801,6 +805,69 @@ static const struct qmp_phy_init_tbl sm8550_ufsphy_pcs[] = { QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_MID_TERM_CTRL1, 0x43), QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x2b), QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x04), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x04), +}; + +static const struct qmp_phy_init_tbl sm8650_ufsphy_serdes[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x44), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_INITVAL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8650_ufsphy_tx[] = { + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_RES_CODE_LANE_OFFSET_TX, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8650_ufsphy_rx[] = { + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE4, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_VGA_CAL_MAN_VAL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B0, 0xc2), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B1, 0xc2), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B3, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B6, 0x60), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE2_B3, 0x9e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE2_B6, 0x60), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B3, 0x9e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B4, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B5, 0x36), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B8, 0x02), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B3, 0xb9), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B6, 0xff), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_SO_SATURATION, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_PI_CTRL1, 0x94), + QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_RX_TERM_BW_CTRL0, 0xfa), +}; + +static const struct qmp_phy_init_tbl sm8650_ufsphy_pcs[] = { + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x00), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_MID_TERM_CTRL1, 0x43), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PCS_CTRL1, 0xc1), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x33), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x04), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x04), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_SIGDET_CTRL2, 0x69), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x02), }; struct qmp_ufs_offsets { @@ -1296,6 +1363,32 @@ static const struct qmp_phy_cfg sm8550_ufsphy_cfg = { .pcs = sm8550_ufsphy_pcs, .pcs_num = ARRAY_SIZE(sm8550_ufsphy_pcs), }, + .tbls_hs_b = { + .serdes = sm8550_ufsphy_hs_b_serdes, + .serdes_num = ARRAY_SIZE(sm8550_ufsphy_hs_b_serdes), + }, + .clk_list = sdm845_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = ufsphy_v6_regs_layout, +}; + +static const struct qmp_phy_cfg sm8650_ufsphy_cfg = { + .lanes = 2, + + .offsets = &qmp_ufs_offsets_v6, + + .tbls = { + .serdes = sm8650_ufsphy_serdes, + .serdes_num = ARRAY_SIZE(sm8650_ufsphy_serdes), + .tx = sm8650_ufsphy_tx, + .tx_num = ARRAY_SIZE(sm8650_ufsphy_tx), + .rx = sm8650_ufsphy_rx, + .rx_num = ARRAY_SIZE(sm8650_ufsphy_rx), + .pcs = sm8650_ufsphy_pcs, + .pcs_num = ARRAY_SIZE(sm8650_ufsphy_pcs), + }, .clk_list = sdm845_ufs_phy_clk_l, .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), .vreg_list = qmp_phy_vreg_l, @@ -1826,6 +1919,9 @@ static const struct of_device_id qmp_ufs_of_match_table[] = { }, { .compatible = "qcom,sm8550-qmp-ufs-phy", .data = &sm8550_ufsphy_cfg, + }, { + .compatible = "qcom,sm8650-qmp-ufs-phy", + .data = &sm8650_ufsphy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c index 02f156298e77..6621246e4ddf 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c @@ -24,6 +24,8 @@ #include "phy-qcom-qmp-pcs-misc-v4.h" #include "phy-qcom-qmp-pcs-usb-v4.h" #include "phy-qcom-qmp-pcs-usb-v5.h" +#include "phy-qcom-qmp-pcs-usb-v6.h" +#include "phy-qcom-qmp-pcs-usb-v7.h" /* QPHY_SW_RESET bit */ #define SW_RESET BIT(0) @@ -151,6 +153,28 @@ static const unsigned int qmp_v5_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V5_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR, }; +static const unsigned int qmp_v6_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = QPHY_V6_PCS_SW_RESET, + [QPHY_START_CTRL] = QPHY_V6_PCS_START_CONTROL, + [QPHY_PCS_STATUS] = QPHY_V6_PCS_PCS_STATUS1, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_PCS_POWER_DOWN_CONTROL, + + /* In PCS_USB */ + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = QPHY_V6_PCS_USB3_AUTONOMOUS_MODE_CTRL, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V6_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR, +}; + +static const unsigned int qmp_v7_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = QPHY_V7_PCS_SW_RESET, + [QPHY_START_CTRL] = QPHY_V7_PCS_START_CONTROL, + [QPHY_PCS_STATUS] = QPHY_V7_PCS_PCS_STATUS1, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V7_PCS_POWER_DOWN_CONTROL, + + /* In PCS_USB */ + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = QPHY_V7_PCS_USB3_AUTONOMOUS_MODE_CTRL, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V7_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR, +}; + static const struct qmp_phy_init_tbl ipq9574_usb3_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x1a), QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), @@ -871,6 +895,134 @@ static const struct qmp_phy_init_tbl sdx65_usb3_uniphy_rx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_ENABLES, 0x00), }; +static const struct qmp_phy_init_tbl sdx75_usb3_uniphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE1, 0x9e), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x2e), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE1, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE1_MODE1, 0x25), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xb7), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xb7), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE0, 0x9e), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE1_MODE0, 0x25), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BG_TIMER, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_CFG, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORE_CLK_EN, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_AUTO_GAIN_ADJ_CTRL_1, 0xb6), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_AUTO_GAIN_ADJ_CTRL_2, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_AUTO_GAIN_ADJ_CTRL_3, 0x37), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_ADDITIONAL_MISC, 0x0c), +}; + +static const struct qmp_phy_init_tbl sdx75_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_RX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_OFFSET_TX, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_RES_CODE_LANE_OFFSET_RX, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_1, 0xf5), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_3, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_4, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_LANE_MODE_5, 0x5f), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V6_TX_PI_QEC_CTRL, 0x21), +}; + +static const struct qmp_phy_init_tbl sdx75_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SB2_GAIN1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SB2_GAIN2, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_VGA_CAL_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_GM_CAL, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_IDAC_TSETTLE_LOW, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_LOW, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH2, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH3, 0xdf), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH4, 0xed), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH2, 0x9c), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH3, 0x1d), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH4, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_VTH_CODE, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_SIGDET_CAL_CTRL1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_SIGDET_CAL_TRIM, 0x08), +}; + +static const struct qmp_phy_init_tbl sdx75_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V6_PCS_LOCK_DETECT_CONFIG1, 0xc4), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_LOCK_DETECT_CONFIG2, 0x89), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl sdx75_usb3_uniphy_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB3_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB3_RCVR_DTCT_DLY_U3_H, 0x00), +}; + static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_tx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0xa5), QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_2, 0x82), @@ -1161,6 +1313,134 @@ static const struct qmp_phy_init_tbl sa8775p_usb3_uniphy_pcs_usb_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_POWER_STATE_CONFIG1, 0x6f), }; +static const struct qmp_phy_init_tbl x1e80100_usb3_uniphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_STEP_SIZE1_MODE1, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_STEP_SIZE2_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_CP_CTRL_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_LOCK_CMP1_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_LOCK_CMP2_MODE1, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DEC_START_MODE1, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DIV_FRAC_START2_MODE1, 0x75), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DIV_FRAC_START3_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_HSCLK_SEL_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_VCO_TUNE1_MODE1, 0x25), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_STEP_SIZE1_MODE0, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_STEP_SIZE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_CP_CTRL_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_LOCK_CMP1_MODE0, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DEC_START_MODE0, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DIV_FRAC_START2_MODE0, 0x75), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DIV_FRAC_START3_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_VCO_TUNE1_MODE0, 0x25), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_PER1, 0x62), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_PER2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_LOCK_CMP_CFG, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_VCO_TUNE_MAP, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_CORE_CLK_EN, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_CMN_CONFIG_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_AUTO_GAIN_ADJ_CTRL_1, 0xb6), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_AUTO_GAIN_ADJ_CTRL_2, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_AUTO_GAIN_ADJ_CTRL_3, 0x37), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_ADDITIONAL_MISC, 0x0c), +}; + +static const struct qmp_phy_init_tbl x1e80100_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V7_TX_RES_CODE_LANE_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_RES_CODE_LANE_RX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_RES_CODE_LANE_OFFSET_TX, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_RES_CODE_LANE_OFFSET_RX, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_LANE_MODE_1, 0xf5), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_LANE_MODE_3, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_LANE_MODE_4, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_LANE_MODE_5, 0x5f), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_PI_QEC_CTRL, 0x21), +}; + +static const struct qmp_phy_init_tbl x1e80100_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_SB2_GAIN1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_SB2_GAIN2, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_VGA_CAL_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_GM_CAL, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_IDAC_TSETTLE_LOW, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_00_LOW, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_00_HIGH, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_00_HIGH2, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_00_HIGH3, 0xdf), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_00_HIGH4, 0xed), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_01_HIGH, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_01_HIGH2, 0x9c), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_01_HIGH3, 0x1d), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_01_HIGH4, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_VTH_CODE, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_SIGDET_CAL_CTRL1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_SIGDET_CAL_TRIM, 0x08), +}; + +static const struct qmp_phy_init_tbl x1e80100_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V7_PCS_LOCK_DETECT_CONFIG1, 0xc4), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_LOCK_DETECT_CONFIG2, 0x89), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl x1e80100_usb3_uniphy_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V7_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_USB3_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_USB3_RCVR_DTCT_DLY_U3_H, 0x00), +}; + struct qmp_usb_offsets { u16 serdes; u16 pcs; @@ -1276,6 +1556,14 @@ static const char * const qmp_phy_vreg_l[] = { "vdda-phy", "vdda-pll", }; +static const struct qmp_usb_offsets qmp_usb_offsets_ipq8074 = { + .serdes = 0, + .pcs = 0x800, + .pcs_misc = 0x600, + .tx = 0x200, + .rx = 0x400, +}; + static const struct qmp_usb_offsets qmp_usb_offsets_ipq9574 = { .serdes = 0, .pcs = 0x800, @@ -1317,10 +1605,44 @@ static const struct qmp_usb_offsets qmp_usb_offsets_v5 = { .rx = 0x1000, }; +static const struct qmp_usb_offsets qmp_usb_offsets_v6 = { + .serdes = 0, + .pcs = 0x0200, + .pcs_usb = 0x1200, + .tx = 0x0e00, + .rx = 0x1000, +}; + +static const struct qmp_usb_offsets qmp_usb_offsets_v7 = { + .serdes = 0, + .pcs = 0x0200, + .pcs_usb = 0x1200, + .tx = 0x0e00, + .rx = 0x1000, +}; + +static const struct qmp_phy_cfg ipq6018_usb3phy_cfg = { + .lanes = 1, + + .offsets = &qmp_usb_offsets_ipq8074, + + .serdes_tbl = ipq9574_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(ipq9574_usb3_serdes_tbl), + .tx_tbl = msm8996_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl), + .rx_tbl = ipq8074_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(ipq8074_usb3_rx_tbl), + .pcs_tbl = ipq8074_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(ipq8074_usb3_pcs_tbl), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, +}; + static const struct qmp_phy_cfg ipq8074_usb3phy_cfg = { .lanes = 1, - .offsets = &qmp_usb_offsets_v3, + .offsets = &qmp_usb_offsets_ipq8074, .serdes_tbl = ipq8074_usb3_serdes_tbl, .serdes_tbl_num = ARRAY_SIZE(ipq8074_usb3_serdes_tbl), @@ -1541,6 +1863,28 @@ static const struct qmp_phy_cfg sdx65_usb3_uniphy_cfg = { .has_pwrdn_delay = true, }; +static const struct qmp_phy_cfg sdx75_usb3_uniphy_cfg = { + .lanes = 1, + .offsets = &qmp_usb_offsets_v6, + + .serdes_tbl = sdx75_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sdx75_usb3_uniphy_serdes_tbl), + .tx_tbl = sdx75_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sdx75_usb3_uniphy_tx_tbl), + .rx_tbl = sdx75_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sdx75_usb3_uniphy_rx_tbl), + .pcs_tbl = sdx75_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sdx75_usb3_uniphy_pcs_tbl), + .pcs_usb_tbl = sdx75_usb3_uniphy_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sdx75_usb3_uniphy_pcs_usb_tbl), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v6_usb3phy_regs_layout, + .pcs_usb_offset = 0x1000, + + .has_pwrdn_delay = true, +}; + static const struct qmp_phy_cfg sm8350_usb3_uniphy_cfg = { .lanes = 1, @@ -1582,6 +1926,26 @@ static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = { .regs = qmp_v3_usb3phy_regs_layout_qcm2290, }; +static const struct qmp_phy_cfg x1e80100_usb3_uniphy_cfg = { + .lanes = 1, + + .offsets = &qmp_usb_offsets_v7, + + .serdes_tbl = x1e80100_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(x1e80100_usb3_uniphy_serdes_tbl), + .tx_tbl = x1e80100_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(x1e80100_usb3_uniphy_tx_tbl), + .rx_tbl = x1e80100_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(x1e80100_usb3_uniphy_rx_tbl), + .pcs_tbl = x1e80100_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(x1e80100_usb3_uniphy_pcs_tbl), + .pcs_usb_tbl = x1e80100_usb3_uniphy_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(x1e80100_usb3_uniphy_pcs_usb_tbl), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v7_usb3phy_regs_layout, +}; + static void qmp_usb_configure_lane(void __iomem *base, const struct qmp_phy_init_tbl tbl[], int num, @@ -2225,7 +2589,7 @@ err_node_put: static const struct of_device_id qmp_usb_of_match_table[] = { { .compatible = "qcom,ipq6018-qmp-usb3-phy", - .data = &ipq8074_usb3phy_cfg, + .data = &ipq6018_usb3phy_cfg, }, { .compatible = "qcom,ipq8074-qmp-usb3-phy", .data = &ipq8074_usb3phy_cfg, @@ -2257,6 +2621,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = { .compatible = "qcom,sdx65-qmp-usb3-uni-phy", .data = &sdx65_usb3_uniphy_cfg, }, { + .compatible = "qcom,sdx75-qmp-usb3-uni-phy", + .data = &sdx75_usb3_uniphy_cfg, + }, { .compatible = "qcom,sm6115-qmp-usb3-phy", .data = &qcm2290_usb3phy_cfg, }, { @@ -2268,6 +2635,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = { }, { .compatible = "qcom,sm8350-qmp-usb3-uni-phy", .data = &sm8350_usb3_uniphy_cfg, + }, { + .compatible = "qcom,x1e80100-qmp-usb3-uni-phy", + .data = &x1e80100_usb3_uniphy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 71f063f4a56e..6923496cbfee 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -24,8 +24,12 @@ #include "phy-qcom-qmp-qserdes-com-v6.h" #include "phy-qcom-qmp-qserdes-txrx-v6.h" #include "phy-qcom-qmp-qserdes-txrx-v6_20.h" +#include "phy-qcom-qmp-qserdes-txrx-v6_n4.h" #include "phy-qcom-qmp-qserdes-ln-shrd-v6.h" +#include "phy-qcom-qmp-qserdes-com-v7.h" +#include "phy-qcom-qmp-qserdes-txrx-v7.h" + #include "phy-qcom-qmp-qserdes-pll.h" #include "phy-qcom-qmp-pcs-v2.h" @@ -44,6 +48,8 @@ #include "phy-qcom-qmp-pcs-v6_20.h" +#include "phy-qcom-qmp-pcs-v7.h" + /* Only for QMP V3 & V4 PHY - DP COM registers */ #define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00 #define QPHY_V3_DP_COM_SW_RESET 0x04 diff --git a/drivers/phy/renesas/Kconfig b/drivers/phy/renesas/Kconfig index 36505fc5f386..e342eef0640b 100644 --- a/drivers/phy/renesas/Kconfig +++ b/drivers/phy/renesas/Kconfig @@ -13,7 +13,7 @@ config PHY_R8A779F0_ETHERNET_SERDES config PHY_RCAR_GEN2 tristate "Renesas R-Car generation 2 USB PHY driver" depends on ARCH_RENESAS - depends on GENERIC_PHY + select GENERIC_PHY help Support for USB PHY found on Renesas R-Car generation 2 SoCs. diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index e53eace7c91e..6387c0d34c55 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -673,8 +673,6 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) channel->irq = platform_get_irq_optional(pdev, 0); channel->dr_mode = rcar_gen3_get_dr_mode(dev->of_node); if (channel->dr_mode != USB_DR_MODE_UNKNOWN) { - int ret; - channel->is_otg_channel = true; channel->uses_otg_pins = !of_property_read_bool(dev->of_node, "renesas,no-otg-pins"); @@ -738,8 +736,6 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) ret = PTR_ERR(provider); goto error; } else if (channel->is_otg_channel) { - int ret; - ret = device_create_file(dev, &dev_attr_role); if (ret < 0) goto error; diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index a24d2af154df..4f71373ae6e1 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -123,9 +123,12 @@ struct rockchip_chg_det_reg { * @disrise_en: host disconnect rise edge detection enable. * @disrise_st: host disconnect rise edge detection state. * @disrise_clr: host disconnect rise edge detection clear. - * @id_det_en: id detection enable register. - * @id_det_st: id detection state register. - * @id_det_clr: id detection clear register. + * @idfall_det_en: id detection enable register, falling edge + * @idfall_det_st: id detection state register, falling edge + * @idfall_det_clr: id detection clear register, falling edge + * @idrise_det_en: id detection enable register, rising edge + * @idrise_det_st: id detection state register, rising edge + * @idrise_det_clr: id detection clear register, rising edge * @ls_det_en: linestate detection enable register. * @ls_det_st: linestate detection state register. * @ls_det_clr: linestate detection clear register. @@ -146,9 +149,12 @@ struct rockchip_usb2phy_port_cfg { struct usb2phy_reg disrise_en; struct usb2phy_reg disrise_st; struct usb2phy_reg disrise_clr; - struct usb2phy_reg id_det_en; - struct usb2phy_reg id_det_st; - struct usb2phy_reg id_det_clr; + struct usb2phy_reg idfall_det_en; + struct usb2phy_reg idfall_det_st; + struct usb2phy_reg idfall_det_clr; + struct usb2phy_reg idrise_det_en; + struct usb2phy_reg idrise_det_st; + struct usb2phy_reg idrise_det_clr; struct usb2phy_reg ls_det_en; struct usb2phy_reg ls_det_st; struct usb2phy_reg ls_det_clr; @@ -488,15 +494,27 @@ static int rockchip_usb2phy_init(struct phy *phy) if (ret) goto out; - /* clear id status and enable id detect irq */ + /* clear id status and enable id detect irqs */ ret = property_enable(rphy->grf, - &rport->port_cfg->id_det_clr, + &rport->port_cfg->idfall_det_clr, true); if (ret) goto out; ret = property_enable(rphy->grf, - &rport->port_cfg->id_det_en, + &rport->port_cfg->idrise_det_clr, + true); + if (ret) + goto out; + + ret = property_enable(rphy->grf, + &rport->port_cfg->idfall_det_en, + true); + if (ret) + goto out; + + ret = property_enable(rphy->grf, + &rport->port_cfg->idrise_det_en, true); if (ret) goto out; @@ -1030,11 +1048,16 @@ static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data) struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); bool id; - if (!property_enabled(rphy->grf, &rport->port_cfg->id_det_st)) + if (!property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st) && + !property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st)) return IRQ_NONE; /* clear id detect irq pending status */ - property_enable(rphy->grf, &rport->port_cfg->id_det_clr, true); + if (property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st)) + property_enable(rphy->grf, &rport->port_cfg->idfall_det_clr, true); + + if (property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st)) + property_enable(rphy->grf, &rport->port_cfg->idrise_det_clr, true); id = property_enabled(rphy->grf, &rport->port_cfg->utmi_id); extcon_set_state_sync(rphy->edev, EXTCON_USB_HOST, !id); @@ -1464,6 +1487,14 @@ put_child: return ret; } +static int rk3128_usb2phy_tuning(struct rockchip_usb2phy *rphy) +{ + /* Turn off differential receiver in suspend mode */ + return regmap_write_bits(rphy->grf, 0x298, + BIT(2) << BIT_WRITEABLE_SHIFT | BIT(2), + BIT(2) << BIT_WRITEABLE_SHIFT | 0); +} + static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy) { int ret; @@ -1513,6 +1544,54 @@ static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy) return ret; } +static const struct rockchip_usb2phy_cfg rk3128_phy_cfgs[] = { + { + .reg = 0x17c, + .num_ports = 2, + .phy_tuning = rk3128_usb2phy_tuning, + .clkout_ctl = { 0x0190, 15, 15, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x017c, 8, 0, 0, 0x1d1 }, + .bvalid_det_en = { 0x017c, 14, 14, 0, 1 }, + .bvalid_det_st = { 0x017c, 15, 15, 0, 1 }, + .bvalid_det_clr = { 0x017c, 15, 15, 0, 1 }, + .idfall_det_en = { 0x01a0, 2, 2, 0, 1 }, + .idfall_det_st = { 0x01a0, 3, 3, 0, 1 }, + .idfall_det_clr = { 0x01a0, 3, 3, 0, 1 }, + .idrise_det_en = { 0x01a0, 0, 0, 0, 1 }, + .idrise_det_st = { 0x01a0, 1, 1, 0, 1 }, + .idrise_det_clr = { 0x01a0, 1, 1, 0, 1 }, + .ls_det_en = { 0x017c, 12, 12, 0, 1 }, + .ls_det_st = { 0x017c, 13, 13, 0, 1 }, + .ls_det_clr = { 0x017c, 13, 13, 0, 1 }, + .utmi_bvalid = { 0x014c, 5, 5, 0, 1 }, + .utmi_id = { 0x014c, 8, 8, 0, 1 }, + .utmi_ls = { 0x014c, 7, 6, 0, 1 }, + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0194, 8, 0, 0, 0x1d1 }, + .ls_det_en = { 0x0194, 14, 14, 0, 1 }, + .ls_det_st = { 0x0194, 15, 15, 0, 1 }, + .ls_det_clr = { 0x0194, 15, 15, 0, 1 } + } + }, + .chg_det = { + .opmode = { 0x017c, 3, 0, 5, 1 }, + .cp_det = { 0x02c0, 6, 6, 0, 1 }, + .dcp_det = { 0x02c0, 5, 5, 0, 1 }, + .dp_det = { 0x02c0, 7, 7, 0, 1 }, + .idm_sink_en = { 0x0184, 8, 8, 0, 1 }, + .idp_sink_en = { 0x0184, 7, 7, 0, 1 }, + .idp_src_en = { 0x0184, 9, 9, 0, 1 }, + .rdm_pdwn_en = { 0x0184, 10, 10, 0, 1 }, + .vdm_src_en = { 0x0184, 12, 12, 0, 1 }, + .vdp_src_en = { 0x0184, 11, 11, 0, 1 }, + }, + }, + { /* sentinel */ } +}; + static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = { { .reg = 0x760, @@ -1524,9 +1603,12 @@ static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = { .bvalid_det_en = { 0x0680, 3, 3, 0, 1 }, .bvalid_det_st = { 0x0690, 3, 3, 0, 1 }, .bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 }, - .id_det_en = { 0x0680, 6, 5, 0, 3 }, - .id_det_st = { 0x0690, 6, 5, 0, 3 }, - .id_det_clr = { 0x06a0, 6, 5, 0, 3 }, + .idfall_det_en = { 0x0680, 6, 6, 0, 1 }, + .idfall_det_st = { 0x0690, 6, 6, 0, 1 }, + .idfall_det_clr = { 0x06a0, 6, 6, 0, 1 }, + .idrise_det_en = { 0x0680, 5, 5, 0, 1 }, + .idrise_det_st = { 0x0690, 5, 5, 0, 1 }, + .idrise_det_clr = { 0x06a0, 5, 5, 0, 1 }, .ls_det_en = { 0x0680, 2, 2, 0, 1 }, .ls_det_st = { 0x0690, 2, 2, 0, 1 }, .ls_det_clr = { 0x06a0, 2, 2, 0, 1 }, @@ -1587,9 +1669,12 @@ static const struct rockchip_usb2phy_cfg rk3308_phy_cfgs[] = { .bvalid_det_en = { 0x3020, 3, 2, 0, 3 }, .bvalid_det_st = { 0x3024, 3, 2, 0, 3 }, .bvalid_det_clr = { 0x3028, 3, 2, 0, 3 }, - .id_det_en = { 0x3020, 5, 4, 0, 3 }, - .id_det_st = { 0x3024, 5, 4, 0, 3 }, - .id_det_clr = { 0x3028, 5, 4, 0, 3 }, + .idfall_det_en = { 0x3020, 5, 5, 0, 1 }, + .idfall_det_st = { 0x3024, 5, 5, 0, 1 }, + .idfall_det_clr = { 0x3028, 5, 5, 0, 1 }, + .idrise_det_en = { 0x3020, 4, 4, 0, 1 }, + .idrise_det_st = { 0x3024, 4, 4, 0, 1 }, + .idrise_det_clr = { 0x3028, 4, 4, 0, 1 }, .ls_det_en = { 0x3020, 0, 0, 0, 1 }, .ls_det_st = { 0x3024, 0, 0, 0, 1 }, .ls_det_clr = { 0x3028, 0, 0, 0, 1 }, @@ -1634,9 +1719,12 @@ static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { .bvalid_det_en = { 0x0110, 3, 2, 0, 3 }, .bvalid_det_st = { 0x0114, 3, 2, 0, 3 }, .bvalid_det_clr = { 0x0118, 3, 2, 0, 3 }, - .id_det_en = { 0x0110, 5, 4, 0, 3 }, - .id_det_st = { 0x0114, 5, 4, 0, 3 }, - .id_det_clr = { 0x0118, 5, 4, 0, 3 }, + .idfall_det_en = { 0x0110, 5, 5, 0, 1 }, + .idfall_det_st = { 0x0114, 5, 5, 0, 1 }, + .idfall_det_clr = { 0x0118, 5, 5, 0, 1 }, + .idrise_det_en = { 0x0110, 4, 4, 0, 1 }, + .idrise_det_st = { 0x0114, 4, 4, 0, 1 }, + .idrise_det_clr = { 0x0118, 4, 4, 0, 1 }, .ls_det_en = { 0x0110, 0, 0, 0, 1 }, .ls_det_st = { 0x0114, 0, 0, 0, 1 }, .ls_det_clr = { 0x0118, 0, 0, 0, 1 }, @@ -1700,9 +1788,12 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 }, .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 }, .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 }, - .id_det_en = { 0xe3c0, 5, 4, 0, 3 }, - .id_det_st = { 0xe3e0, 5, 4, 0, 3 }, - .id_det_clr = { 0xe3d0, 5, 4, 0, 3 }, + .idfall_det_en = { 0xe3c0, 5, 5, 0, 1 }, + .idfall_det_st = { 0xe3e0, 5, 5, 0, 1 }, + .idfall_det_clr = { 0xe3d0, 5, 5, 0, 1 }, + .idrise_det_en = { 0xe3c0, 4, 4, 0, 1 }, + .idrise_det_st = { 0xe3e0, 4, 4, 0, 1 }, + .idrise_det_clr = { 0xe3d0, 4, 4, 0, 1 }, .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 }, .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 }, .utmi_id = { 0xe2ac, 8, 8, 0, 1 }, @@ -1739,9 +1830,12 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 }, .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 }, .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 }, - .id_det_en = { 0xe3c0, 10, 9, 0, 3 }, - .id_det_st = { 0xe3e0, 10, 9, 0, 3 }, - .id_det_clr = { 0xe3d0, 10, 9, 0, 3 }, + .idfall_det_en = { 0xe3c0, 10, 10, 0, 1 }, + .idfall_det_st = { 0xe3e0, 10, 10, 0, 1 }, + .idfall_det_clr = { 0xe3d0, 10, 10, 0, 1 }, + .idrise_det_en = { 0xe3c0, 9, 9, 0, 1 }, + .idrise_det_st = { 0xe3e0, 9, 9, 0, 1 }, + .idrise_det_clr = { 0xe3d0, 9, 9, 0, 1 }, .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 }, .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 }, .utmi_id = { 0xe2ac, 11, 11, 0, 1 }, @@ -1770,9 +1864,12 @@ static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = { .bvalid_det_en = { 0x0080, 3, 2, 0, 3 }, .bvalid_det_st = { 0x0084, 3, 2, 0, 3 }, .bvalid_det_clr = { 0x0088, 3, 2, 0, 3 }, - .id_det_en = { 0x0080, 5, 4, 0, 3 }, - .id_det_st = { 0x0084, 5, 4, 0, 3 }, - .id_det_clr = { 0x0088, 5, 4, 0, 3 }, + .idfall_det_en = { 0x0080, 5, 5, 0, 1 }, + .idfall_det_st = { 0x0084, 5, 5, 0, 1 }, + .idfall_det_clr = { 0x0088, 5, 5, 0, 1 }, + .idrise_det_en = { 0x0080, 4, 4, 0, 1 }, + .idrise_det_st = { 0x0084, 4, 4, 0, 1 }, + .idrise_det_clr = { 0x0088, 4, 4, 0, 1 }, .utmi_avalid = { 0x00c0, 10, 10, 0, 1 }, .utmi_bvalid = { 0x00c0, 9, 9, 0, 1 }, .utmi_id = { 0x00c0, 6, 6, 0, 1 }, @@ -1990,6 +2087,7 @@ static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = { static const struct of_device_id rockchip_usb2phy_dt_match[] = { { .compatible = "rockchip,px30-usb2phy", .data = &rk3328_phy_cfgs }, + { .compatible = "rockchip,rk3128-usb2phy", .data = &rk3128_phy_cfgs }, { .compatible = "rockchip,rk3228-usb2phy", .data = &rk3228_phy_cfgs }, { .compatible = "rockchip,rk3308-usb2phy", .data = &rk3308_phy_cfgs }, { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c index bc847d3879f7..0f4818adb440 100644 --- a/drivers/phy/ti/phy-gmii-sel.c +++ b/drivers/phy/ti/phy-gmii-sel.c @@ -248,7 +248,7 @@ static const struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw9g_soc_j784s4 = { .use_of_data = true, .regfields = phy_gmii_sel_fields_am654, - .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII) | + .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII) | BIT(PHY_INTERFACE_MODE_SGMII) | BIT(PHY_INTERFACE_MODE_USXGMII), .num_ports = 8, .num_qsgmii_main_ports = 2, diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c index fc3cd98c60ff..00d7e6a6de03 100644 --- a/drivers/phy/ti/phy-j721e-wiz.c +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -1240,6 +1240,7 @@ static int wiz_phy_fullrt_div(struct wiz *wiz, int lane) case J721E_WIZ_10G: case J7200_WIZ_10G: case J721S2_WIZ_10G: + case J784S4_WIZ_10G: if (wiz->lane_phy_type[lane] == PHY_TYPE_SGMII) return regmap_field_write(wiz->p0_fullrt_div[lane], 0x2); break; diff --git a/drivers/phy/ti/phy-omap-usb2.c b/drivers/phy/ti/phy-omap-usb2.c index dd2913ac0fa2..78e19b128962 100644 --- a/drivers/phy/ti/phy-omap-usb2.c +++ b/drivers/phy/ti/phy-omap-usb2.c @@ -117,7 +117,7 @@ static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled) { struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); - if (!phy->comparator) + if (!phy->comparator || !phy->comparator->set_vbus) return -ENODEV; return phy->comparator->set_vbus(phy->comparator, enabled); @@ -127,7 +127,7 @@ static int omap_usb_start_srp(struct usb_otg *otg) { struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); - if (!phy->comparator) + if (!phy->comparator || !phy->comparator->start_srp) return -ENODEV; return phy->comparator->start_srp(phy->comparator); diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index b11144bb448c..8163a5983166 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -484,6 +484,22 @@ config PINCTRL_TB10X depends on OF && ARC_PLAT_TB10X select GPIOLIB +config PINCTRL_TPS6594 + tristate "Pinctrl and GPIO driver for TI TPS6594 PMIC" + depends on OF && MFD_TPS6594 + default MFD_TPS6594 + select PINMUX + select GPIOLIB + select REGMAP + select GPIO_REGMAP + select GENERIC_PINCONF + help + Say Y to select the pinmuxing and GPIOs driver for the TPS6594 + PMICs chip family. + + This driver can also be built as a module + called tps6594-pinctrl. + config PINCTRL_ZYNQ bool "Pinctrl driver for Xilinx Zynq" depends on ARCH_ZYNQ diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 7ac5d59c83e7..1071f301cc70 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o obj-$(CONFIG_PINCTRL_SX150X) += pinctrl-sx150x.o obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o +obj-$(CONFIG_PINCTRL_TPS6594) += pinctrl-tps6594.o obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o diff --git a/drivers/pinctrl/bcm/pinctrl-ns.c b/drivers/pinctrl/bcm/pinctrl-ns.c index d099a7f25f64..6bb2b461950b 100644 --- a/drivers/pinctrl/bcm/pinctrl-ns.c +++ b/drivers/pinctrl/bcm/pinctrl-ns.c @@ -171,8 +171,8 @@ static int ns_pinctrl_set_mux(struct pinctrl_dev *pctrl_dev, if (!group) return -EINVAL; - for (i = 0; i < group->num_pins; i++) - unset |= BIT(group->pins[i]); + for (i = 0; i < group->grp.npins; i++) + unset |= BIT(group->grp.pins[i]); tmp = readl(ns_pinctrl->base); tmp &= ~unset; diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index f2977eb65522..ee56856cb80c 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -13,6 +13,7 @@ #define pr_fmt(fmt) "pinctrl core: " fmt #include <linux/array_size.h> +#include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/device.h> #include <linux/err.h> @@ -23,6 +24,7 @@ #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/gpio.h> #include <linux/gpio/driver.h> #include <linux/pinctrl/consumer.h> @@ -30,10 +32,6 @@ #include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinctrl.h> -#ifdef CONFIG_GPIOLIB -#include "../gpio/gpiolib.h" -#endif - #include "core.h" #include "devicetree.h" #include "pinconf.h" @@ -145,7 +143,7 @@ struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np) */ int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name) { - unsigned i, pin; + unsigned int i, pin; /* The pin number can be retrived from the pin controller descriptor */ for (i = 0; i < pctldev->desc->npins; i++) { @@ -166,7 +164,7 @@ int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name) * @pctldev: the pin control device to lookup the pin on * @pin: pin number/id to look up */ -const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin) +const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned int pin) { const struct pin_desc *desc; @@ -184,7 +182,7 @@ EXPORT_SYMBOL_GPL(pin_get_name); /* Deletes a range of pin descriptors */ static void pinctrl_free_pindescs(struct pinctrl_dev *pctldev, const struct pinctrl_pin_desc *pins, - unsigned num_pins) + unsigned int num_pins) { int i; @@ -252,9 +250,9 @@ failed: static int pinctrl_register_pins(struct pinctrl_dev *pctldev, const struct pinctrl_pin_desc *pins, - unsigned num_descs) + unsigned int num_descs) { - unsigned i; + unsigned int i; int ret = 0; for (i = 0; i < num_descs; i++) { @@ -428,7 +426,7 @@ EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range); void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *ranges, - unsigned nranges) + unsigned int nranges) { int i; @@ -459,7 +457,7 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname, EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range); int pinctrl_get_group_pins(struct pinctrl_dev *pctldev, const char *pin_group, - const unsigned **pins, unsigned *num_pins) + const unsigned int **pins, unsigned int *num_pins) { const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; int gs; @@ -559,7 +557,7 @@ const char *pinctrl_generic_get_group_name(struct pinctrl_dev *pctldev, if (!group) return NULL; - return group->name; + return group->grp.name; } EXPORT_SYMBOL_GPL(pinctrl_generic_get_group_name); @@ -585,8 +583,8 @@ int pinctrl_generic_get_group_pins(struct pinctrl_dev *pctldev, return -EINVAL; } - *pins = group->pins; - *num_pins = group->num_pins; + *pins = group->grp.pins; + *num_pins = group->grp.npins; return 0; } @@ -642,7 +640,7 @@ static int pinctrl_generic_group_name_to_selector(struct pinctrl_dev *pctldev, * Note that the caller must take care of locking. */ int pinctrl_generic_add_group(struct pinctrl_dev *pctldev, const char *name, - int *pins, int num_pins, void *data) + const unsigned int *pins, int num_pins, void *data) { struct group_desc *group; int selector, error; @@ -660,10 +658,7 @@ int pinctrl_generic_add_group(struct pinctrl_dev *pctldev, const char *name, if (!group) return -ENOMEM; - group->name = name; - group->pins = pins; - group->num_pins = num_pins; - group->data = data; + *group = PINCTRL_GROUP_DESC(name, pins, num_pins, data); error = radix_tree_insert(&pctldev->pin_group_tree, selector, group); if (error) @@ -734,8 +729,8 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, const char *pin_group) { const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; - unsigned ngroups = pctlops->get_groups_count(pctldev); - unsigned group_selector = 0; + unsigned int ngroups = pctlops->get_groups_count(pctldev); + unsigned int group_selector = 0; while (group_selector < ngroups) { const char *gname = pctlops->get_group_name(pctldev, @@ -1432,7 +1427,7 @@ EXPORT_SYMBOL_GPL(devm_pinctrl_put); * @num_maps: the number of maps in the mapping table */ int pinctrl_register_mappings(const struct pinctrl_map *maps, - unsigned num_maps) + unsigned int num_maps) { int i, ret; struct pinctrl_maps *maps_node; @@ -1647,10 +1642,10 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev = s->private; const struct pinctrl_ops *ops = pctldev->desc->pctlops; - unsigned i, pin; + unsigned int i, pin; #ifdef CONFIG_GPIOLIB + struct gpio_device *gdev __free(gpio_device_put) = NULL; struct pinctrl_gpio_range *range; - struct gpio_chip *chip; int gpio_num; #endif @@ -1685,11 +1680,11 @@ static int pinctrl_pins_show(struct seq_file *s, void *what) * we need to get rid of the range->base eventually and * get the descriptor directly from the gpio_chip. */ - chip = gpiod_to_chip(gpio_to_desc(gpio_num)); - else - chip = NULL; - if (chip) - seq_printf(s, "%u:%s ", gpio_num - chip->gpiodev->base, chip->label); + gdev = gpiod_to_gpio_device(gpio_to_desc(gpio_num)); + if (gdev) + seq_printf(s, "%u:%s ", + gpio_num - gpio_device_get_base(gdev), + gpio_device_get_label(gdev)); else seq_puts(s, "0:? "); #endif @@ -1711,7 +1706,7 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev = s->private; const struct pinctrl_ops *ops = pctldev->desc->pctlops; - unsigned ngroups, selector = 0; + unsigned int ngroups, selector = 0; mutex_lock(&pctldev->mutex); @@ -1719,8 +1714,8 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) seq_puts(s, "registered pin groups:\n"); while (selector < ngroups) { - const unsigned *pins = NULL; - unsigned num_pins = 0; + const unsigned int *pins = NULL; + unsigned int num_pins = 0; const char *gname = ops->get_group_name(pctldev, selector); const char *pname; int ret = 0; diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 530370443c19..837fd5bd903d 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -111,8 +111,8 @@ struct pinctrl_state { * @func: the function selector to program */ struct pinctrl_setting_mux { - unsigned group; - unsigned func; + unsigned int group; + unsigned int func; }; /** @@ -124,9 +124,9 @@ struct pinctrl_setting_mux { * @num_configs: the number of entries in array @configs */ struct pinctrl_setting_configs { - unsigned group_or_pin; + unsigned int group_or_pin; unsigned long *configs; - unsigned num_configs; + unsigned int num_configs; }; /** @@ -173,7 +173,7 @@ struct pin_desc { void *drv_data; /* These fields only added when supporting pinmux drivers */ #ifdef CONFIG_PINMUX - unsigned mux_usecount; + unsigned int mux_usecount; const char *mux_owner; const struct pinctrl_setting_mux *mux_setting; const char *gpio_owner; @@ -189,25 +189,30 @@ struct pin_desc { struct pinctrl_maps { struct list_head node; const struct pinctrl_map *maps; - unsigned num_maps; + unsigned int num_maps; }; #ifdef CONFIG_GENERIC_PINCTRL_GROUPS +#include <linux/pinctrl/pinctrl.h> + /** * struct group_desc - generic pin group descriptor - * @name: name of the pin group - * @pins: array of pins that belong to the group - * @num_pins: number of pins in the group + * @grp: generic data of the pin group (name and pins) * @data: pin controller driver specific data */ struct group_desc { - const char *name; - int *pins; - int num_pins; + struct pingroup grp; void *data; }; +/* Convenience macro to define a generic pin group descriptor */ +#define PINCTRL_GROUP_DESC(_name, _pins, _num_pins, _data) \ +(struct group_desc) { \ + .grp = PINCTRL_PINGROUP(_name, _pins, _num_pins), \ + .data = _data, \ +} + int pinctrl_generic_get_group_count(struct pinctrl_dev *pctldev); const char *pinctrl_generic_get_group_name(struct pinctrl_dev *pctldev, @@ -222,7 +227,7 @@ struct group_desc *pinctrl_generic_get_group(struct pinctrl_dev *pctldev, unsigned int group_selector); int pinctrl_generic_add_group(struct pinctrl_dev *pctldev, const char *name, - int *gpins, int ngpins, void *data); + const unsigned int *pins, int num_pins, void *data); int pinctrl_generic_remove_group(struct pinctrl_dev *pctldev, unsigned int group_selector); @@ -232,7 +237,7 @@ int pinctrl_generic_remove_group(struct pinctrl_dev *pctldev, struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name); struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np); int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name); -const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin); +const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned int pin); int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, const char *pin_group); diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index 6e0a40962f38..df1efc2e5202 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -24,11 +24,11 @@ struct pinctrl_dt_map { struct list_head node; struct pinctrl_dev *pctldev; struct pinctrl_map *map; - unsigned num_maps; + unsigned int num_maps; }; static void dt_free_map(struct pinctrl_dev *pctldev, - struct pinctrl_map *map, unsigned num_maps) + struct pinctrl_map *map, unsigned int num_maps) { int i; @@ -64,7 +64,7 @@ void pinctrl_dt_free_maps(struct pinctrl *p) static int dt_remember_or_free_map(struct pinctrl *p, const char *statename, struct pinctrl_dev *pctldev, - struct pinctrl_map *map, unsigned num_maps) + struct pinctrl_map *map, unsigned int num_maps) { int i; struct pinctrl_dt_map *dt_map; @@ -116,7 +116,7 @@ static int dt_to_map_one_config(struct pinctrl *p, const struct pinctrl_ops *ops; int ret; struct pinctrl_map *map; - unsigned num_maps; + unsigned int num_maps; bool allow_default = false; /* Find the pin controller containing np_config */ diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c index 9bc16943014f..2d3d80921c0d 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx.c +++ b/drivers/pinctrl/freescale/pinctrl-imx.c @@ -42,7 +42,7 @@ static inline const struct group_desc *imx_pinctrl_find_group_by_name( for (i = 0; i < pctldev->num_groups; i++) { grp = pinctrl_generic_get_group(pctldev, i); - if (grp && !strcmp(grp->name, name)) + if (grp && !strcmp(grp->grp.name, name)) break; } @@ -79,9 +79,9 @@ static int imx_dt_node_to_map(struct pinctrl_dev *pctldev, } if (info->flags & IMX_USE_SCU) { - map_num += grp->num_pins; + map_num += grp->grp.npins; } else { - for (i = 0; i < grp->num_pins; i++) { + for (i = 0; i < grp->grp.npins; i++) { pin = &((struct imx_pin *)(grp->data))[i]; if (!(pin->conf.mmio.config & IMX_NO_PAD_CTL)) map_num++; @@ -109,7 +109,7 @@ static int imx_dt_node_to_map(struct pinctrl_dev *pctldev, /* create config map */ new_map++; - for (i = j = 0; i < grp->num_pins; i++) { + for (i = j = 0; i < grp->grp.npins; i++) { pin = &((struct imx_pin *)(grp->data))[i]; /* @@ -263,10 +263,10 @@ static int imx_pmx_set(struct pinctrl_dev *pctldev, unsigned selector, if (!func) return -EINVAL; - npins = grp->num_pins; + npins = grp->grp.npins; dev_dbg(ipctl->dev, "enable function %s group %s\n", - func->name, grp->name); + func->name, grp->grp.name); for (i = 0; i < npins; i++) { /* @@ -423,7 +423,7 @@ static void imx_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, if (!grp) return; - for (i = 0; i < grp->num_pins; i++) { + for (i = 0; i < grp->grp.npins; i++) { struct imx_pin *pin = &((struct imx_pin *)(grp->data))[i]; name = pin_get_name(pctldev, pin->pin); @@ -511,6 +511,7 @@ static int imx_pinctrl_parse_groups(struct device_node *np, { const struct imx_pinctrl_soc_info *info = ipctl->info; struct imx_pin *pin; + unsigned int *pins; int size, pin_size; const __be32 *list; int i; @@ -525,7 +526,7 @@ static int imx_pinctrl_parse_groups(struct device_node *np, pin_size = FSL_PIN_SIZE; /* Initialise group */ - grp->name = np->name; + grp->grp.name = np->name; /* * the binding format is fsl,pins = <PIN_FUNC_ID CONFIG ...>, @@ -553,24 +554,22 @@ static int imx_pinctrl_parse_groups(struct device_node *np, return -EINVAL; } - grp->num_pins = size / pin_size; - grp->data = devm_kcalloc(ipctl->dev, - grp->num_pins, sizeof(struct imx_pin), - GFP_KERNEL); - grp->pins = devm_kcalloc(ipctl->dev, - grp->num_pins, sizeof(unsigned int), - GFP_KERNEL); - if (!grp->pins || !grp->data) + grp->grp.npins = size / pin_size; + grp->data = devm_kcalloc(ipctl->dev, grp->grp.npins, sizeof(*pin), GFP_KERNEL); + if (!grp->data) return -ENOMEM; - for (i = 0; i < grp->num_pins; i++) { + pins = devm_kcalloc(ipctl->dev, grp->grp.npins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + grp->grp.pins = pins; + + for (i = 0; i < grp->grp.npins; i++) { pin = &((struct imx_pin *)(grp->data))[i]; if (info->flags & IMX_USE_SCU) - info->imx_pinctrl_parse_pin(ipctl, &grp->pins[i], - pin, &list); + info->imx_pinctrl_parse_pin(ipctl, &pins[i], pin, &list); else - imx_pinctrl_parse_pin_mmio(ipctl, &grp->pins[i], - pin, &list, np); + imx_pinctrl_parse_pin_mmio(ipctl, &pins[i], pin, &list, np); } return 0; @@ -612,8 +611,7 @@ static int imx_pinctrl_parse_functions(struct device_node *np, i = 0; for_each_child_of_node(np, child) { - grp = devm_kzalloc(ipctl->dev, sizeof(struct group_desc), - GFP_KERNEL); + grp = devm_kzalloc(ipctl->dev, sizeof(*grp), GFP_KERNEL); if (!grp) { of_node_put(child); return -ENOMEM; diff --git a/drivers/pinctrl/intel/Kconfig b/drivers/pinctrl/intel/Kconfig index d66f4f6932d8..2101d30bd66c 100644 --- a/drivers/pinctrl/intel/Kconfig +++ b/drivers/pinctrl/intel/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Intel pin control drivers menu "Intel pinctrl drivers" - depends on ACPI && (X86 || COMPILE_TEST) + depends on (ACPI && X86) || COMPILE_TEST config PINCTRL_BAYTRAIL bool "Intel Baytrail GPIO pin control" @@ -37,6 +37,16 @@ config PINCTRL_INTEL select GPIOLIB select GPIOLIB_IRQCHIP +config PINCTRL_INTEL_PLATFORM + tristate "Intel pinctrl and GPIO platform driver" + depends on ACPI + select PINCTRL_INTEL + help + This pinctrl driver provides an interface that allows configuring + of Intel PCH pins and using them as GPIOs. Currently the following + Intel SoCs / platforms require this to be functional: + - Lunar Lake + config PINCTRL_ALDERLAKE tristate "Intel Alder Lake pinctrl and GPIO driver" select PINCTRL_INTEL @@ -128,6 +138,15 @@ config PINCTRL_METEORLAKE This pinctrl driver provides an interface that allows configuring of Intel Meteor Lake pins and using them as GPIOs. +config PINCTRL_METEORPOINT + tristate "Intel Meteor Point pinctrl and GPIO driver" + depends on ACPI + select PINCTRL_INTEL + help + Meteor Point is the PCH of Intel Meteor Lake. This pinctrl driver + provides an interface that allows configuring of PCH pins and + using them as GPIOs. + config PINCTRL_SUNRISEPOINT tristate "Intel Sunrisepoint pinctrl and GPIO driver" select PINCTRL_INTEL diff --git a/drivers/pinctrl/intel/Makefile b/drivers/pinctrl/intel/Makefile index f6d30f2d973a..d0d868c9a622 100644 --- a/drivers/pinctrl/intel/Makefile +++ b/drivers/pinctrl/intel/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_PINCTRL_TANGIER) += pinctrl-tangier.o obj-$(CONFIG_PINCTRL_MERRIFIELD) += pinctrl-merrifield.o obj-$(CONFIG_PINCTRL_MOOREFIELD) += pinctrl-moorefield.o obj-$(CONFIG_PINCTRL_INTEL) += pinctrl-intel.o +obj-$(CONFIG_PINCTRL_INTEL_PLATFORM) += pinctrl-intel-platform.o obj-$(CONFIG_PINCTRL_ALDERLAKE) += pinctrl-alderlake.o obj-$(CONFIG_PINCTRL_BROXTON) += pinctrl-broxton.o obj-$(CONFIG_PINCTRL_CANNONLAKE) += pinctrl-cannonlake.o @@ -21,5 +22,6 @@ obj-$(CONFIG_PINCTRL_JASPERLAKE) += pinctrl-jasperlake.o obj-$(CONFIG_PINCTRL_LAKEFIELD) += pinctrl-lakefield.o obj-$(CONFIG_PINCTRL_LEWISBURG) += pinctrl-lewisburg.o obj-$(CONFIG_PINCTRL_METEORLAKE) += pinctrl-meteorlake.o +obj-$(CONFIG_PINCTRL_METEORPOINT) += pinctrl-meteorpoint.o obj-$(CONFIG_PINCTRL_SUNRISEPOINT) += pinctrl-sunrisepoint.o obj-$(CONFIG_PINCTRL_TIGERLAKE) += pinctrl-tigerlake.o diff --git a/drivers/pinctrl/intel/pinctrl-alderlake.c b/drivers/pinctrl/intel/pinctrl-alderlake.c index 4a37dc273d63..7d9948e5f422 100644 --- a/drivers/pinctrl/intel/pinctrl-alderlake.c +++ b/drivers/pinctrl/intel/pinctrl-alderlake.c @@ -9,6 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -733,14 +734,12 @@ static const struct acpi_device_id adl_pinctrl_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, adl_pinctrl_acpi_match); -static INTEL_PINCTRL_PM_OPS(adl_pinctrl_pm_ops); - static struct platform_driver adl_pinctrl_driver = { .probe = intel_pinctrl_probe_by_hid, .driver = { .name = "alderlake-pinctrl", .acpi_match_table = adl_pinctrl_acpi_match, - .pm = &adl_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; module_platform_driver(adl_pinctrl_driver); diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index 3c8c02043481..ac97724c59ba 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -588,10 +588,9 @@ static void byt_set_group_simple_mux(struct intel_pinctrl *vg, const struct intel_pingroup group, unsigned int func) { - unsigned long flags; int i; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); for (i = 0; i < group.grp.npins; i++) { void __iomem *padcfg0; @@ -609,18 +608,15 @@ static void byt_set_group_simple_mux(struct intel_pinctrl *vg, value |= func; writel(value, padcfg0); } - - raw_spin_unlock_irqrestore(&byt_lock, flags); } static void byt_set_group_mixed_mux(struct intel_pinctrl *vg, const struct intel_pingroup group, const unsigned int *func) { - unsigned long flags; int i; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); for (i = 0; i < group.grp.npins; i++) { void __iomem *padcfg0; @@ -638,8 +634,6 @@ static void byt_set_group_mixed_mux(struct intel_pinctrl *vg, value |= func[i]; writel(value, padcfg0); } - - raw_spin_unlock_irqrestore(&byt_lock, flags); } static int byt_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, @@ -677,10 +671,10 @@ static u32 byt_get_gpio_mux(struct intel_pinctrl *vg, unsigned int offset) static void byt_gpio_clear_triggering(struct intel_pinctrl *vg, unsigned int offset) { void __iomem *reg = byt_gpio_reg(vg, offset, BYT_CONF0_REG); - unsigned long flags; u32 value; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); + value = readl(reg); /* Do not clear direct-irq enabled IRQs (from gpio_disable_free) */ @@ -688,7 +682,6 @@ static void byt_gpio_clear_triggering(struct intel_pinctrl *vg, unsigned int off value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); writel(value, reg); - raw_spin_unlock_irqrestore(&byt_lock, flags); } static int byt_gpio_request_enable(struct pinctrl_dev *pctl_dev, @@ -698,9 +691,8 @@ static int byt_gpio_request_enable(struct pinctrl_dev *pctl_dev, struct intel_pinctrl *vg = pinctrl_dev_get_drvdata(pctl_dev); void __iomem *reg = byt_gpio_reg(vg, offset, BYT_CONF0_REG); u32 value, gpio_mux; - unsigned long flags; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); /* * In most cases, func pin mux 000 means GPIO function. @@ -713,15 +705,14 @@ static int byt_gpio_request_enable(struct pinctrl_dev *pctl_dev, */ value = readl(reg) & BYT_PIN_MUX; gpio_mux = byt_get_gpio_mux(vg, offset); - if (gpio_mux != value) { - value = readl(reg) & ~BYT_PIN_MUX; - value |= gpio_mux; - writel(value, reg); + if (gpio_mux == value) + return 0; - dev_warn(vg->dev, FW_BUG "Pin %i: forcibly re-configured as GPIO\n", offset); - } + value = readl(reg) & ~BYT_PIN_MUX; + value |= gpio_mux; + writel(value, reg); - raw_spin_unlock_irqrestore(&byt_lock, flags); + dev_warn(vg->dev, FW_BUG "Pin %i: forcibly re-configured as GPIO\n", offset); return 0; } @@ -759,10 +750,9 @@ static int byt_gpio_set_direction(struct pinctrl_dev *pctl_dev, { struct intel_pinctrl *vg = pinctrl_dev_get_drvdata(pctl_dev); void __iomem *val_reg = byt_gpio_reg(vg, offset, BYT_VAL_REG); - unsigned long flags; u32 value; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); value = readl(val_reg); value &= ~BYT_DIR_MASK; @@ -773,8 +763,6 @@ static int byt_gpio_set_direction(struct pinctrl_dev *pctl_dev, writel(value, val_reg); - raw_spin_unlock_irqrestore(&byt_lock, flags); - return 0; } @@ -811,6 +799,7 @@ static int byt_set_pull_strength(u32 *reg, u16 strength) *reg &= ~BYT_PULL_STR_MASK; switch (strength) { + case 1: /* Set default strength value in case none is given */ case 2000: *reg |= BYT_PULL_STR_2K; break; @@ -830,6 +819,24 @@ static int byt_set_pull_strength(u32 *reg, u16 strength) return 0; } +static void byt_gpio_force_input_mode(struct intel_pinctrl *vg, unsigned int offset) +{ + void __iomem *reg = byt_gpio_reg(vg, offset, BYT_VAL_REG); + u32 value; + + value = readl(reg); + if (!(value & BYT_INPUT_EN)) + return; + + /* + * Pull assignment is only applicable in input mode. If + * chip is not in input mode, set it and warn about it. + */ + value &= ~BYT_INPUT_EN; + writel(value, reg); + dev_warn(vg->dev, "Pin %i: forcibly set to input mode\n", offset); +} + static int byt_pin_config_get(struct pinctrl_dev *pctl_dev, unsigned int offset, unsigned long *config) { @@ -838,15 +845,15 @@ static int byt_pin_config_get(struct pinctrl_dev *pctl_dev, unsigned int offset, void __iomem *conf_reg = byt_gpio_reg(vg, offset, BYT_CONF0_REG); void __iomem *val_reg = byt_gpio_reg(vg, offset, BYT_VAL_REG); void __iomem *db_reg = byt_gpio_reg(vg, offset, BYT_DEBOUNCE_REG); - unsigned long flags; u32 conf, pull, val, debounce; u16 arg = 0; - raw_spin_lock_irqsave(&byt_lock, flags); - conf = readl(conf_reg); + scoped_guard(raw_spinlock_irqsave, &byt_lock) { + conf = readl(conf_reg); + val = readl(val_reg); + } + pull = conf & BYT_PULL_ASSIGN_MASK; - val = readl(val_reg); - raw_spin_unlock_irqrestore(&byt_lock, flags); switch (param) { case PIN_CONFIG_BIAS_DISABLE: @@ -873,9 +880,8 @@ static int byt_pin_config_get(struct pinctrl_dev *pctl_dev, unsigned int offset, if (!(conf & BYT_DEBOUNCE_EN)) return -EINVAL; - raw_spin_lock_irqsave(&byt_lock, flags); - debounce = readl(db_reg); - raw_spin_unlock_irqrestore(&byt_lock, flags); + scoped_guard(raw_spinlock_irqsave, &byt_lock) + debounce = readl(db_reg); switch (debounce & BYT_DEBOUNCE_PULSE_MASK) { case BYT_DEBOUNCE_PULSE_375US: @@ -919,18 +925,16 @@ static int byt_pin_config_set(struct pinctrl_dev *pctl_dev, unsigned int num_configs) { struct intel_pinctrl *vg = pinctrl_dev_get_drvdata(pctl_dev); - unsigned int param, arg; void __iomem *conf_reg = byt_gpio_reg(vg, offset, BYT_CONF0_REG); - void __iomem *val_reg = byt_gpio_reg(vg, offset, BYT_VAL_REG); void __iomem *db_reg = byt_gpio_reg(vg, offset, BYT_DEBOUNCE_REG); - u32 conf, val, db_pulse, debounce; - unsigned long flags; - int i, ret = 0; + u32 conf, db_pulse, debounce; + enum pin_config_param param; + int i, ret; + u32 arg; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); conf = readl(conf_reg); - val = readl(val_reg); for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); @@ -941,59 +945,30 @@ static int byt_pin_config_set(struct pinctrl_dev *pctl_dev, conf &= ~BYT_PULL_ASSIGN_MASK; break; case PIN_CONFIG_BIAS_PULL_DOWN: - /* Set default strength value in case none is given */ - if (arg == 1) - arg = 2000; - - /* - * Pull assignment is only applicable in input mode. If - * chip is not in input mode, set it and warn about it. - */ - if (val & BYT_INPUT_EN) { - val &= ~BYT_INPUT_EN; - writel(val, val_reg); - dev_warn(vg->dev, "Pin %i: forcibly set to input mode\n", offset); - } + byt_gpio_force_input_mode(vg, offset); conf &= ~BYT_PULL_ASSIGN_MASK; conf |= BYT_PULL_ASSIGN_DOWN; ret = byt_set_pull_strength(&conf, arg); + if (ret) + return ret; break; case PIN_CONFIG_BIAS_PULL_UP: - /* Set default strength value in case none is given */ - if (arg == 1) - arg = 2000; - - /* - * Pull assignment is only applicable in input mode. If - * chip is not in input mode, set it and warn about it. - */ - if (val & BYT_INPUT_EN) { - val &= ~BYT_INPUT_EN; - writel(val, val_reg); - dev_warn(vg->dev, "Pin %i: forcibly set to input mode\n", offset); - } + byt_gpio_force_input_mode(vg, offset); conf &= ~BYT_PULL_ASSIGN_MASK; conf |= BYT_PULL_ASSIGN_UP; ret = byt_set_pull_strength(&conf, arg); + if (ret) + return ret; break; case PIN_CONFIG_INPUT_DEBOUNCE: - if (arg) { - conf |= BYT_DEBOUNCE_EN; - } else { - conf &= ~BYT_DEBOUNCE_EN; - - /* - * No need to update the pulse value. - * Debounce is going to be disabled. - */ - break; - } - switch (arg) { + case 0: + db_pulse = 0; + break; case 375: db_pulse = BYT_DEBOUNCE_PULSE_375US; break; @@ -1016,33 +991,28 @@ static int byt_pin_config_set(struct pinctrl_dev *pctl_dev, db_pulse = BYT_DEBOUNCE_PULSE_24MS; break; default: - if (arg) - ret = -EINVAL; - break; + return -EINVAL; } - if (ret) - break; + if (db_pulse) { + debounce = readl(db_reg); + debounce = (debounce & ~BYT_DEBOUNCE_PULSE_MASK) | db_pulse; + writel(debounce, db_reg); - debounce = readl(db_reg); - debounce = (debounce & ~BYT_DEBOUNCE_PULSE_MASK) | db_pulse; - writel(debounce, db_reg); + conf |= BYT_DEBOUNCE_EN; + } else { + conf &= ~BYT_DEBOUNCE_EN; + } break; default: - ret = -ENOTSUPP; + return -ENOTSUPP; } - - if (ret) - break; } - if (!ret) - writel(conf, conf_reg); - - raw_spin_unlock_irqrestore(&byt_lock, flags); + writel(conf, conf_reg); - return ret; + return 0; } static const struct pinconf_ops byt_pinconf_ops = { @@ -1062,12 +1032,10 @@ static int byt_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct intel_pinctrl *vg = gpiochip_get_data(chip); void __iomem *reg = byt_gpio_reg(vg, offset, BYT_VAL_REG); - unsigned long flags; u32 val; - raw_spin_lock_irqsave(&byt_lock, flags); - val = readl(reg); - raw_spin_unlock_irqrestore(&byt_lock, flags); + scoped_guard(raw_spinlock_irqsave, &byt_lock) + val = readl(reg); return !!(val & BYT_LEVEL); } @@ -1075,35 +1043,34 @@ static int byt_gpio_get(struct gpio_chip *chip, unsigned int offset) static void byt_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct intel_pinctrl *vg = gpiochip_get_data(chip); - void __iomem *reg = byt_gpio_reg(vg, offset, BYT_VAL_REG); - unsigned long flags; + void __iomem *reg; u32 old_val; + reg = byt_gpio_reg(vg, offset, BYT_VAL_REG); if (!reg) return; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); + old_val = readl(reg); if (value) writel(old_val | BYT_LEVEL, reg); else writel(old_val & ~BYT_LEVEL, reg); - raw_spin_unlock_irqrestore(&byt_lock, flags); } static int byt_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { struct intel_pinctrl *vg = gpiochip_get_data(chip); - void __iomem *reg = byt_gpio_reg(vg, offset, BYT_VAL_REG); - unsigned long flags; + void __iomem *reg; u32 value; + reg = byt_gpio_reg(vg, offset, BYT_VAL_REG); if (!reg) return -EINVAL; - raw_spin_lock_irqsave(&byt_lock, flags); - value = readl(reg); - raw_spin_unlock_irqrestore(&byt_lock, flags); + scoped_guard(raw_spinlock_irqsave, &byt_lock) + value = readl(reg); if (!(value & BYT_OUTPUT_EN)) return GPIO_LINE_DIRECTION_OUT; @@ -1117,17 +1084,15 @@ static int byt_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct intel_pinctrl *vg = gpiochip_get_data(chip); void __iomem *val_reg = byt_gpio_reg(vg, offset, BYT_VAL_REG); - unsigned long flags; u32 reg; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); reg = readl(val_reg); reg &= ~BYT_DIR_MASK; reg |= BYT_OUTPUT_EN; writel(reg, val_reg); - raw_spin_unlock_irqrestore(&byt_lock, flags); return 0; } @@ -1142,10 +1107,9 @@ static int byt_gpio_direction_output(struct gpio_chip *chip, { struct intel_pinctrl *vg = gpiochip_get_data(chip); void __iomem *val_reg = byt_gpio_reg(vg, offset, BYT_VAL_REG); - unsigned long flags; u32 reg; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); byt_gpio_direct_irq_check(vg, offset); @@ -1158,7 +1122,6 @@ static int byt_gpio_direction_output(struct gpio_chip *chip, writel(reg, val_reg); - raw_spin_unlock_irqrestore(&byt_lock, flags); return 0; } @@ -1173,7 +1136,6 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) void __iomem *conf_reg, *val_reg; const char *pull_str = NULL; const char *pull = NULL; - unsigned long flags; unsigned int pin; pin = vg->soc->pins[i].number; @@ -1190,10 +1152,10 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) continue; } - raw_spin_lock_irqsave(&byt_lock, flags); - conf0 = readl(conf_reg); - val = readl(val_reg); - raw_spin_unlock_irqrestore(&byt_lock, flags); + scoped_guard(raw_spinlock_irqsave, &byt_lock) { + conf0 = readl(conf_reg); + val = readl(val_reg); + } comm = intel_get_community(vg, pin); if (!comm) { @@ -1278,9 +1240,9 @@ static void byt_irq_ack(struct irq_data *d) if (!reg) return; - raw_spin_lock(&byt_lock); + guard(raw_spinlock)(&byt_lock); + writel(BIT(hwirq % 32), reg); - raw_spin_unlock(&byt_lock); } static void byt_irq_mask(struct irq_data *d) @@ -1298,7 +1260,6 @@ static void byt_irq_unmask(struct irq_data *d) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct intel_pinctrl *vg = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); - unsigned long flags; void __iomem *reg; u32 value; @@ -1308,7 +1269,8 @@ static void byt_irq_unmask(struct irq_data *d) if (!reg) return; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); + value = readl(reg); switch (irqd_get_trigger_type(d)) { @@ -1330,23 +1292,21 @@ static void byt_irq_unmask(struct irq_data *d) } writel(value, reg); - - raw_spin_unlock_irqrestore(&byt_lock, flags); } static int byt_irq_type(struct irq_data *d, unsigned int type) { struct intel_pinctrl *vg = gpiochip_get_data(irq_data_get_irq_chip_data(d)); irq_hw_number_t hwirq = irqd_to_hwirq(d); - u32 value; - unsigned long flags; void __iomem *reg; + u32 value; reg = byt_gpio_reg(vg, hwirq, BYT_CONF0_REG); if (!reg) return -EINVAL; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); + value = readl(reg); WARN(value & BYT_DIRECT_IRQ_EN, @@ -1368,8 +1328,6 @@ static int byt_irq_type(struct irq_data *d, unsigned int type) else if (type & IRQ_TYPE_LEVEL_MASK) irq_set_handler_locked(d, handle_level_irq); - raw_spin_unlock_irqrestore(&byt_lock, flags); - return 0; } @@ -1401,9 +1359,8 @@ static void byt_gpio_irq_handler(struct irq_desc *desc) continue; } - raw_spin_lock(&byt_lock); - pending = readl(reg); - raw_spin_unlock(&byt_lock); + scoped_guard(raw_spinlock, &byt_lock) + pending = readl(reg); for_each_set_bit(pin, &pending, 32) generic_handle_domain_irq(vg->chip.irq.domain, base + pin); } @@ -1666,10 +1623,9 @@ static int byt_pinctrl_probe(struct platform_device *pdev) static int byt_gpio_suspend(struct device *dev) { struct intel_pinctrl *vg = dev_get_drvdata(dev); - unsigned long flags; int i; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); for (i = 0; i < vg->soc->npins; i++) { void __iomem *reg; @@ -1693,17 +1649,15 @@ static int byt_gpio_suspend(struct device *dev) vg->context.pads[i].val = value; } - raw_spin_unlock_irqrestore(&byt_lock, flags); return 0; } static int byt_gpio_resume(struct device *dev) { struct intel_pinctrl *vg = dev_get_drvdata(dev); - unsigned long flags; int i; - raw_spin_lock_irqsave(&byt_lock, flags); + guard(raw_spinlock_irqsave)(&byt_lock); for (i = 0; i < vg->soc->npins; i++) { void __iomem *reg; @@ -1743,7 +1697,6 @@ static int byt_gpio_resume(struct device *dev) } } - raw_spin_unlock_irqrestore(&byt_lock, flags); return 0; } diff --git a/drivers/pinctrl/intel/pinctrl-broxton.c b/drivers/pinctrl/intel/pinctrl-broxton.c index 3118c7c8842f..d99541676630 100644 --- a/drivers/pinctrl/intel/pinctrl-broxton.c +++ b/drivers/pinctrl/intel/pinctrl-broxton.c @@ -9,6 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -1000,14 +1001,12 @@ static const struct platform_device_id bxt_pinctrl_platform_ids[] = { }; MODULE_DEVICE_TABLE(platform, bxt_pinctrl_platform_ids); -static INTEL_PINCTRL_PM_OPS(bxt_pinctrl_pm_ops); - static struct platform_driver bxt_pinctrl_driver = { .probe = intel_pinctrl_probe_by_uid, .driver = { .name = "broxton-pinctrl", .acpi_match_table = bxt_pinctrl_acpi_match, - .pm = &bxt_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, .id_table = bxt_pinctrl_platform_ids, }; diff --git a/drivers/pinctrl/intel/pinctrl-cannonlake.c b/drivers/pinctrl/intel/pinctrl-cannonlake.c index 95976abfb785..1aa09f950be1 100644 --- a/drivers/pinctrl/intel/pinctrl-cannonlake.c +++ b/drivers/pinctrl/intel/pinctrl-cannonlake.c @@ -10,6 +10,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -824,14 +825,12 @@ static const struct acpi_device_id cnl_pinctrl_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, cnl_pinctrl_acpi_match); -static INTEL_PINCTRL_PM_OPS(cnl_pinctrl_pm_ops); - static struct platform_driver cnl_pinctrl_driver = { .probe = intel_pinctrl_probe_by_hid, .driver = { .name = "cannonlake-pinctrl", .acpi_match_table = cnl_pinctrl_acpi_match, - .pm = &cnl_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; module_platform_driver(cnl_pinctrl_driver); diff --git a/drivers/pinctrl/intel/pinctrl-cedarfork.c b/drivers/pinctrl/intel/pinctrl-cedarfork.c index a20465eb2dc6..48af8930dd1f 100644 --- a/drivers/pinctrl/intel/pinctrl-cedarfork.c +++ b/drivers/pinctrl/intel/pinctrl-cedarfork.c @@ -9,6 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -319,8 +320,6 @@ static const struct intel_pinctrl_soc_data cdf_soc_data = { .ncommunities = ARRAY_SIZE(cdf_communities), }; -static INTEL_PINCTRL_PM_OPS(cdf_pinctrl_pm_ops); - static const struct acpi_device_id cdf_pinctrl_acpi_match[] = { { "INTC3001", (kernel_ulong_t)&cdf_soc_data }, { } @@ -332,7 +331,7 @@ static struct platform_driver cdf_pinctrl_driver = { .driver = { .name = "cedarfork-pinctrl", .acpi_match_table = cdf_pinctrl_acpi_match, - .pm = &cdf_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; diff --git a/drivers/pinctrl/intel/pinctrl-denverton.c b/drivers/pinctrl/intel/pinctrl-denverton.c index 562a4f9188e4..666507f54f27 100644 --- a/drivers/pinctrl/intel/pinctrl-denverton.c +++ b/drivers/pinctrl/intel/pinctrl-denverton.c @@ -9,6 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -249,8 +250,6 @@ static const struct intel_pinctrl_soc_data dnv_soc_data = { .ncommunities = ARRAY_SIZE(dnv_communities), }; -static INTEL_PINCTRL_PM_OPS(dnv_pinctrl_pm_ops); - static const struct acpi_device_id dnv_pinctrl_acpi_match[] = { { "INTC3000", (kernel_ulong_t)&dnv_soc_data }, { } @@ -268,7 +267,7 @@ static struct platform_driver dnv_pinctrl_driver = { .driver = { .name = "denverton-pinctrl", .acpi_match_table = dnv_pinctrl_acpi_match, - .pm = &dnv_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, .id_table = dnv_pinctrl_platform_ids, }; diff --git a/drivers/pinctrl/intel/pinctrl-elkhartlake.c b/drivers/pinctrl/intel/pinctrl-elkhartlake.c index 81581ab85316..1678634ebc06 100644 --- a/drivers/pinctrl/intel/pinctrl-elkhartlake.c +++ b/drivers/pinctrl/intel/pinctrl-elkhartlake.c @@ -9,6 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -485,14 +486,12 @@ static const struct acpi_device_id ehl_pinctrl_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, ehl_pinctrl_acpi_match); -static INTEL_PINCTRL_PM_OPS(ehl_pinctrl_pm_ops); - static struct platform_driver ehl_pinctrl_driver = { .probe = intel_pinctrl_probe_by_uid, .driver = { .name = "elkhartlake-pinctrl", .acpi_match_table = ehl_pinctrl_acpi_match, - .pm = &ehl_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; module_platform_driver(ehl_pinctrl_driver); diff --git a/drivers/pinctrl/intel/pinctrl-emmitsburg.c b/drivers/pinctrl/intel/pinctrl-emmitsburg.c index 099ec8351d5f..e4798d32492c 100644 --- a/drivers/pinctrl/intel/pinctrl-emmitsburg.c +++ b/drivers/pinctrl/intel/pinctrl-emmitsburg.c @@ -9,6 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -358,14 +359,12 @@ static const struct acpi_device_id ebg_pinctrl_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, ebg_pinctrl_acpi_match); -static INTEL_PINCTRL_PM_OPS(ebg_pinctrl_pm_ops); - static struct platform_driver ebg_pinctrl_driver = { .probe = intel_pinctrl_probe_by_hid, .driver = { .name = "emmitsburg-pinctrl", .acpi_match_table = ebg_pinctrl_acpi_match, - .pm = &ebg_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; module_platform_driver(ebg_pinctrl_driver); diff --git a/drivers/pinctrl/intel/pinctrl-geminilake.c b/drivers/pinctrl/intel/pinctrl-geminilake.c index 9effa06b61e9..6dcf0ac2059f 100644 --- a/drivers/pinctrl/intel/pinctrl-geminilake.c +++ b/drivers/pinctrl/intel/pinctrl-geminilake.c @@ -9,6 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -447,14 +448,12 @@ static const struct acpi_device_id glk_pinctrl_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, glk_pinctrl_acpi_match); -static INTEL_PINCTRL_PM_OPS(glk_pinctrl_pm_ops); - static struct platform_driver glk_pinctrl_driver = { .probe = intel_pinctrl_probe_by_uid, .driver = { .name = "geminilake-pinctrl", .acpi_match_table = glk_pinctrl_acpi_match, - .pm = &glk_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; diff --git a/drivers/pinctrl/intel/pinctrl-icelake.c b/drivers/pinctrl/intel/pinctrl-icelake.c index 300e1538c8d0..fe3042de891a 100644 --- a/drivers/pinctrl/intel/pinctrl-icelake.c +++ b/drivers/pinctrl/intel/pinctrl-icelake.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -668,8 +669,6 @@ static const struct intel_pinctrl_soc_data icln_soc_data = { .ncommunities = ARRAY_SIZE(icln_communities), }; -static INTEL_PINCTRL_PM_OPS(icl_pinctrl_pm_ops); - static const struct acpi_device_id icl_pinctrl_acpi_match[] = { { "INT3455", (kernel_ulong_t)&icllp_soc_data }, { "INT34C3", (kernel_ulong_t)&icln_soc_data }, @@ -682,7 +681,7 @@ static struct platform_driver icl_pinctrl_driver = { .driver = { .name = "icelake-pinctrl", .acpi_match_table = icl_pinctrl_acpi_match, - .pm = &icl_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; module_platform_driver(icl_pinctrl_driver); diff --git a/drivers/pinctrl/intel/pinctrl-intel-platform.c b/drivers/pinctrl/intel/pinctrl-intel-platform.c new file mode 100644 index 000000000000..4a19ab3b4ba7 --- /dev/null +++ b/drivers/pinctrl/intel/pinctrl-intel-platform.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel PCH pinctrl/GPIO driver + * + * Copyright (C) 2021-2023, Intel Corporation + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/property.h> +#include <linux/string_helpers.h> + +#include <linux/pinctrl/pinctrl.h> + +#include "pinctrl-intel.h" + +struct intel_platform_pins { + struct pinctrl_pin_desc *pins; + size_t npins; +}; + +static int intel_platform_pinctrl_prepare_pins(struct device *dev, size_t base, + const char *name, u32 size, + struct intel_platform_pins *pins) +{ + struct pinctrl_pin_desc *descs; + char **pin_names; + unsigned int i; + + pin_names = devm_kasprintf_strarray(dev, name, size); + if (IS_ERR(pin_names)) + return PTR_ERR(pin_names); + + descs = devm_krealloc_array(dev, pins->pins, base + size, sizeof(*descs), GFP_KERNEL); + if (!descs) + return -ENOMEM; + + for (i = 0; i < size; i++) { + unsigned int pin_number = base + i; + char *pin_name = pin_names[i]; + struct pinctrl_pin_desc *desc; + + /* Unify delimiter for pin name */ + strreplace(pin_name, '-', '_'); + + desc = &descs[pin_number]; + desc->number = pin_number; + desc->name = pin_name; + } + + pins->pins = descs; + pins->npins = base + size; + + return 0; +} + +static int intel_platform_pinctrl_prepare_group(struct device *dev, + struct fwnode_handle *child, + struct intel_padgroup *gpp, + struct intel_platform_pins *pins) +{ + size_t base = pins->npins; + const char *name; + u32 size; + int ret; + + ret = fwnode_property_read_string(child, "intc-gpio-group-name", &name); + if (ret) + return ret; + + ret = fwnode_property_read_u32(child, "intc-gpio-pad-count", &size); + if (ret) + return ret; + + ret = intel_platform_pinctrl_prepare_pins(dev, base, name, size, pins); + if (ret) + return ret; + + gpp->base = base; + gpp->size = size; + gpp->gpio_base = INTEL_GPIO_BASE_MATCH; + + return 0; +} + +static int intel_platform_pinctrl_prepare_community(struct device *dev, + struct intel_community *community, + struct intel_platform_pins *pins) +{ + struct fwnode_handle *child; + struct intel_padgroup *gpps; + unsigned int group; + size_t ngpps; + u32 offset; + int ret; + + ret = device_property_read_u32(dev, "intc-gpio-pad-ownership-offset", &offset); + if (ret) + return ret; + community->padown_offset = offset; + + ret = device_property_read_u32(dev, "intc-gpio-pad-configuration-lock-offset", &offset); + if (ret) + return ret; + community->padcfglock_offset = offset; + + ret = device_property_read_u32(dev, "intc-gpio-host-software-pad-ownership-offset", &offset); + if (ret) + return ret; + community->hostown_offset = offset; + + ret = device_property_read_u32(dev, "intc-gpio-gpi-interrupt-status-offset", &offset); + if (ret) + return ret; + community->is_offset = offset; + + ret = device_property_read_u32(dev, "intc-gpio-gpi-interrupt-enable-offset", &offset); + if (ret) + return ret; + community->ie_offset = offset; + + ngpps = device_get_child_node_count(dev); + if (!ngpps) + return -ENODEV; + + gpps = devm_kcalloc(dev, ngpps, sizeof(*gpps), GFP_KERNEL); + if (!gpps) + return -ENOMEM; + + group = 0; + device_for_each_child_node(dev, child) { + struct intel_padgroup *gpp = &gpps[group]; + + gpp->reg_num = group; + + ret = intel_platform_pinctrl_prepare_group(dev, child, gpp, pins); + if (ret) + return ret; + + group++; + } + + community->ngpps = ngpps; + community->gpps = gpps; + + return 0; +} + +static int intel_platform_pinctrl_prepare_soc_data(struct device *dev, + struct intel_pinctrl_soc_data *data) +{ + struct intel_platform_pins pins = {}; + struct intel_community *communities; + size_t ncommunities; + unsigned int i; + int ret; + + /* Version 1.0 of the specification assumes only a single community per device node */ + ncommunities = 1, + communities = devm_kcalloc(dev, ncommunities, sizeof(*communities), GFP_KERNEL); + if (!communities) + return -ENOMEM; + + for (i = 0; i < ncommunities; i++) { + struct intel_community *community = &communities[i]; + + community->barno = i; + community->pin_base = pins.npins; + + ret = intel_platform_pinctrl_prepare_community(dev, community, &pins); + if (ret) + return ret; + + community->npins = pins.npins - community->pin_base; + } + + data->ncommunities = ncommunities; + data->communities = communities; + + data->npins = pins.npins; + data->pins = pins.pins; + + return 0; +} + +static int intel_platform_pinctrl_probe(struct platform_device *pdev) +{ + struct intel_pinctrl_soc_data *data; + struct device *dev = &pdev->dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = intel_platform_pinctrl_prepare_soc_data(dev, data); + if (ret) + return ret; + + return intel_pinctrl_probe(pdev, data); +} + +static const struct acpi_device_id intel_platform_pinctrl_acpi_match[] = { + { "INTC105F" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, intel_platform_pinctrl_acpi_match); + +static struct platform_driver intel_platform_pinctrl_driver = { + .probe = intel_platform_pinctrl_probe, + .driver = { + .name = "intel-pinctrl", + .acpi_match_table = intel_platform_pinctrl_acpi_match, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), + }, +}; +module_platform_driver(intel_platform_pinctrl_driver); + +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("Intel PCH pinctrl/GPIO driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(PINCTRL_INTEL); diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index 652ba451f885..d6f29e6faab7 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -677,10 +677,6 @@ static int intel_config_set_pull(struct intel_pinctrl *pctrl, unsigned int pin, u32 term = 0, up = 0, value; void __iomem *padcfg1; - /* Set default strength value in case none is given */ - if (arg == 1) - arg = 5000; - switch (param) { case PIN_CONFIG_BIAS_DISABLE: break; @@ -690,6 +686,7 @@ static int intel_config_set_pull(struct intel_pinctrl *pctrl, unsigned int pin, case 20000: term = PADCFG1_TERM_20K; break; + case 1: /* Set default strength value in case none is given */ case 5000: term = PADCFG1_TERM_5K; break; @@ -716,6 +713,7 @@ static int intel_config_set_pull(struct intel_pinctrl *pctrl, unsigned int pin, case 20000: term = PADCFG1_TERM_20K; break; + case 1: /* Set default strength value in case none is given */ case 5000: term = PADCFG1_TERM_5K; break; @@ -899,7 +897,7 @@ static int intel_gpio_to_pin(struct intel_pinctrl *pctrl, unsigned int offset, * * Return: a GPIO offset, or negative error code if translation can't be done. */ -static __maybe_unused int intel_pin_to_gpio(struct intel_pinctrl *pctrl, int pin) +static int intel_pin_to_gpio(struct intel_pinctrl *pctrl, int pin) { const struct intel_community *community; const struct intel_padgroup *padgrp; @@ -1506,8 +1504,8 @@ static int intel_pinctrl_probe_pwm(struct intel_pinctrl *pctrl, return PTR_ERR_OR_ZERO(pwm); } -static int intel_pinctrl_probe(struct platform_device *pdev, - const struct intel_pinctrl_soc_data *soc_data) +int intel_pinctrl_probe(struct platform_device *pdev, + const struct intel_pinctrl_soc_data *soc_data) { struct device *dev = &pdev->dev; struct intel_pinctrl *pctrl; @@ -1625,6 +1623,7 @@ static int intel_pinctrl_probe(struct platform_device *pdev, return 0; } +EXPORT_SYMBOL_NS_GPL(intel_pinctrl_probe, PINCTRL_INTEL); int intel_pinctrl_probe_by_hid(struct platform_device *pdev) { @@ -1653,7 +1652,7 @@ EXPORT_SYMBOL_NS_GPL(intel_pinctrl_probe_by_uid, PINCTRL_INTEL); const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_device *pdev) { const struct intel_pinctrl_soc_data * const *table; - const struct intel_pinctrl_soc_data *data = NULL; + const struct intel_pinctrl_soc_data *data; struct device *dev = &pdev->dev; table = device_get_match_data(dev); @@ -1662,11 +1661,10 @@ const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_ unsigned int i; for (i = 0; table[i]; i++) { - if (acpi_dev_uid_match(adev, table[i]->uid)) { - data = table[i]; + if (acpi_dev_uid_match(adev, table[i]->uid)) break; - } } + data = table[i]; } else { const struct platform_device_id *id; @@ -1682,7 +1680,6 @@ const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_ } EXPORT_SYMBOL_NS_GPL(intel_pinctrl_get_soc_data, PINCTRL_INTEL); -#ifdef CONFIG_PM_SLEEP static bool __intel_gpio_is_direct_irq(u32 value) { return (value & PADCFG0_GPIROUTIOXAPIC) && (value & PADCFG0_GPIOTXDIS) && @@ -1728,7 +1725,7 @@ static bool intel_pinctrl_should_save(struct intel_pinctrl *pctrl, unsigned int return false; } -int intel_pinctrl_suspend_noirq(struct device *dev) +static int intel_pinctrl_suspend_noirq(struct device *dev) { struct intel_pinctrl *pctrl = dev_get_drvdata(dev); struct intel_community_context *communities; @@ -1771,7 +1768,6 @@ int intel_pinctrl_suspend_noirq(struct device *dev) return 0; } -EXPORT_SYMBOL_GPL(intel_pinctrl_suspend_noirq); static bool intel_gpio_update_reg(void __iomem *reg, u32 mask, u32 value) { @@ -1838,7 +1834,7 @@ static void intel_restore_padcfg(struct intel_pinctrl *pctrl, unsigned int pin, dev_dbg(dev, "restored pin %u padcfg%u %#08x\n", pin, n, readl(padcfg)); } -int intel_pinctrl_resume_noirq(struct device *dev) +static int intel_pinctrl_resume_noirq(struct device *dev) { struct intel_pinctrl *pctrl = dev_get_drvdata(dev); const struct intel_community_context *communities; @@ -1882,8 +1878,10 @@ int intel_pinctrl_resume_noirq(struct device *dev) return 0; } -EXPORT_SYMBOL_GPL(intel_pinctrl_resume_noirq); -#endif + +EXPORT_NS_GPL_DEV_SLEEP_PM_OPS(intel_pinctrl_pm_ops, PINCTRL_INTEL) = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(intel_pinctrl_suspend_noirq, intel_pinctrl_resume_noirq) +}; MODULE_AUTHOR("Mathias Nyman <mathias.nyman@linux.intel.com>"); MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); diff --git a/drivers/pinctrl/intel/pinctrl-intel.h b/drivers/pinctrl/intel/pinctrl-intel.h index 2bb553598e8b..fde65e18cd14 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.h +++ b/drivers/pinctrl/intel/pinctrl-intel.h @@ -252,19 +252,13 @@ struct intel_pinctrl { int irq; }; +int intel_pinctrl_probe(struct platform_device *pdev, + const struct intel_pinctrl_soc_data *soc_data); + int intel_pinctrl_probe_by_hid(struct platform_device *pdev); int intel_pinctrl_probe_by_uid(struct platform_device *pdev); -#ifdef CONFIG_PM_SLEEP -int intel_pinctrl_suspend_noirq(struct device *dev); -int intel_pinctrl_resume_noirq(struct device *dev); -#endif - -#define INTEL_PINCTRL_PM_OPS(_name) \ -const struct dev_pm_ops _name = { \ - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(intel_pinctrl_suspend_noirq, \ - intel_pinctrl_resume_noirq) \ -} +extern const struct dev_pm_ops intel_pinctrl_pm_ops; struct intel_community *intel_get_community(struct intel_pinctrl *pctrl, unsigned int pin); diff --git a/drivers/pinctrl/intel/pinctrl-jasperlake.c b/drivers/pinctrl/intel/pinctrl-jasperlake.c index 50f137deed9c..3525480428ea 100644 --- a/drivers/pinctrl/intel/pinctrl-jasperlake.c +++ b/drivers/pinctrl/intel/pinctrl-jasperlake.c @@ -9,6 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -326,14 +327,12 @@ static const struct acpi_device_id jsl_pinctrl_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, jsl_pinctrl_acpi_match); -static INTEL_PINCTRL_PM_OPS(jsl_pinctrl_pm_ops); - static struct platform_driver jsl_pinctrl_driver = { .probe = intel_pinctrl_probe_by_hid, .driver = { .name = "jasperlake-pinctrl", .acpi_match_table = jsl_pinctrl_acpi_match, - .pm = &jsl_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; module_platform_driver(jsl_pinctrl_driver); diff --git a/drivers/pinctrl/intel/pinctrl-lakefield.c b/drivers/pinctrl/intel/pinctrl-lakefield.c index 0b94e11b78ac..adef85db82ca 100644 --- a/drivers/pinctrl/intel/pinctrl-lakefield.c +++ b/drivers/pinctrl/intel/pinctrl-lakefield.c @@ -9,6 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -347,14 +348,12 @@ static const struct acpi_device_id lkf_pinctrl_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, lkf_pinctrl_acpi_match); -static INTEL_PINCTRL_PM_OPS(lkf_pinctrl_pm_ops); - static struct platform_driver lkf_pinctrl_driver = { .probe = intel_pinctrl_probe_by_hid, .driver = { .name = "lakefield-pinctrl", .acpi_match_table = lkf_pinctrl_acpi_match, - .pm = &lkf_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; module_platform_driver(lkf_pinctrl_driver); diff --git a/drivers/pinctrl/intel/pinctrl-lewisburg.c b/drivers/pinctrl/intel/pinctrl-lewisburg.c index aa725a5d62b9..a304d30ea9ed 100644 --- a/drivers/pinctrl/intel/pinctrl-lewisburg.c +++ b/drivers/pinctrl/intel/pinctrl-lewisburg.c @@ -9,6 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -301,8 +302,6 @@ static const struct intel_pinctrl_soc_data lbg_soc_data = { .ncommunities = ARRAY_SIZE(lbg_communities), }; -static INTEL_PINCTRL_PM_OPS(lbg_pinctrl_pm_ops); - static const struct acpi_device_id lbg_pinctrl_acpi_match[] = { { "INT3536", (kernel_ulong_t)&lbg_soc_data }, { } @@ -314,7 +313,7 @@ static struct platform_driver lbg_pinctrl_driver = { .driver = { .name = "lewisburg-pinctrl", .acpi_match_table = lbg_pinctrl_acpi_match, - .pm = &lbg_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; module_platform_driver(lbg_pinctrl_driver); diff --git a/drivers/pinctrl/intel/pinctrl-lynxpoint.c b/drivers/pinctrl/intel/pinctrl-lynxpoint.c index e6878e4cf20c..1fb0bba8b386 100644 --- a/drivers/pinctrl/intel/pinctrl-lynxpoint.c +++ b/drivers/pinctrl/intel/pinctrl-lynxpoint.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/array_size.h> #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/gpio/driver.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -291,10 +292,9 @@ static int lp_pinmux_set_mux(struct pinctrl_dev *pctldev, { struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); const struct intel_pingroup *grp = &lg->soc->groups[group]; - unsigned long flags; int i; - raw_spin_lock_irqsave(&lg->lock, flags); + guard(raw_spinlock_irqsave)(&lg->lock); /* Now enable the mux setting for each pin in the group */ for (i = 0; i < grp->grp.npins; i++) { @@ -312,8 +312,6 @@ static int lp_pinmux_set_mux(struct pinctrl_dev *pctldev, iowrite32(value, reg); } - raw_spin_unlock_irqrestore(&lg->lock, flags); - return 0; } @@ -334,10 +332,9 @@ static int lp_gpio_request_enable(struct pinctrl_dev *pctldev, struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); void __iomem *reg = lp_gpio_reg(&lg->chip, pin, LP_CONFIG1); void __iomem *conf2 = lp_gpio_reg(&lg->chip, pin, LP_CONFIG2); - unsigned long flags; u32 value; - raw_spin_lock_irqsave(&lg->lock, flags); + guard(raw_spinlock_irqsave)(&lg->lock); /* * Reconfigure pin to GPIO mode if needed and issue a warning, @@ -352,8 +349,6 @@ static int lp_gpio_request_enable(struct pinctrl_dev *pctldev, /* Enable input sensing */ lp_gpio_enable_input(conf2); - raw_spin_unlock_irqrestore(&lg->lock, flags); - return 0; } @@ -363,14 +358,11 @@ static void lp_gpio_disable_free(struct pinctrl_dev *pctldev, { struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); void __iomem *conf2 = lp_gpio_reg(&lg->chip, pin, LP_CONFIG2); - unsigned long flags; - raw_spin_lock_irqsave(&lg->lock, flags); + guard(raw_spinlock_irqsave)(&lg->lock); /* Disable input sensing */ lp_gpio_disable_input(conf2); - - raw_spin_unlock_irqrestore(&lg->lock, flags); } static int lp_gpio_set_direction(struct pinctrl_dev *pctldev, @@ -379,10 +371,9 @@ static int lp_gpio_set_direction(struct pinctrl_dev *pctldev, { struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); void __iomem *reg = lp_gpio_reg(&lg->chip, pin, LP_CONFIG1); - unsigned long flags; u32 value; - raw_spin_lock_irqsave(&lg->lock, flags); + guard(raw_spinlock_irqsave)(&lg->lock); value = ioread32(reg); value &= ~DIR_BIT; @@ -400,8 +391,6 @@ static int lp_gpio_set_direction(struct pinctrl_dev *pctldev, } iowrite32(value, reg); - raw_spin_unlock_irqrestore(&lg->lock, flags); - return 0; } @@ -421,13 +410,11 @@ static int lp_pin_config_get(struct pinctrl_dev *pctldev, unsigned int pin, struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); void __iomem *conf2 = lp_gpio_reg(&lg->chip, pin, LP_CONFIG2); enum pin_config_param param = pinconf_to_config_param(*config); - unsigned long flags; u32 value, pull; u16 arg; - raw_spin_lock_irqsave(&lg->lock, flags); - value = ioread32(conf2); - raw_spin_unlock_irqrestore(&lg->lock, flags); + scoped_guard(raw_spinlock_irqsave, &lg->lock) + value = ioread32(conf2); pull = value & GPIWP_MASK; @@ -464,11 +451,10 @@ static int lp_pin_config_set(struct pinctrl_dev *pctldev, unsigned int pin, struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev); void __iomem *conf2 = lp_gpio_reg(&lg->chip, pin, LP_CONFIG2); enum pin_config_param param; - unsigned long flags; - int i, ret = 0; + unsigned int i; u32 value; - raw_spin_lock_irqsave(&lg->lock, flags); + guard(raw_spinlock_irqsave)(&lg->lock); value = ioread32(conf2); @@ -489,19 +475,13 @@ static int lp_pin_config_set(struct pinctrl_dev *pctldev, unsigned int pin, value |= GPIWP_UP; break; default: - ret = -ENOTSUPP; + return -ENOTSUPP; } - - if (ret) - break; } - if (!ret) - iowrite32(value, conf2); + iowrite32(value, conf2); - raw_spin_unlock_irqrestore(&lg->lock, flags); - - return ret; + return 0; } static const struct pinconf_ops lptlp_pinconf_ops = { @@ -527,16 +507,13 @@ static void lp_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct intel_pinctrl *lg = gpiochip_get_data(chip); void __iomem *reg = lp_gpio_reg(chip, offset, LP_CONFIG1); - unsigned long flags; - raw_spin_lock_irqsave(&lg->lock, flags); + guard(raw_spinlock_irqsave)(&lg->lock); if (value) iowrite32(ioread32(reg) | OUT_LVL_BIT, reg); else iowrite32(ioread32(reg) & ~OUT_LVL_BIT, reg); - - raw_spin_unlock_irqrestore(&lg->lock, flags); } static int lp_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) @@ -592,11 +569,10 @@ static void lp_irq_ack(struct irq_data *d) struct intel_pinctrl *lg = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); void __iomem *reg = lp_gpio_reg(&lg->chip, hwirq, LP_INT_STAT); - unsigned long flags; - raw_spin_lock_irqsave(&lg->lock, flags); + guard(raw_spinlock_irqsave)(&lg->lock); + iowrite32(BIT(hwirq % 32), reg); - raw_spin_unlock_irqrestore(&lg->lock, flags); } static void lp_irq_unmask(struct irq_data *d) @@ -613,13 +589,11 @@ static void lp_irq_enable(struct irq_data *d) struct intel_pinctrl *lg = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); void __iomem *reg = lp_gpio_reg(&lg->chip, hwirq, LP_INT_ENABLE); - unsigned long flags; gpiochip_enable_irq(gc, hwirq); - raw_spin_lock_irqsave(&lg->lock, flags); - iowrite32(ioread32(reg) | BIT(hwirq % 32), reg); - raw_spin_unlock_irqrestore(&lg->lock, flags); + scoped_guard(raw_spinlock_irqsave, &lg->lock) + iowrite32(ioread32(reg) | BIT(hwirq % 32), reg); } static void lp_irq_disable(struct irq_data *d) @@ -628,11 +602,9 @@ static void lp_irq_disable(struct irq_data *d) struct intel_pinctrl *lg = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); void __iomem *reg = lp_gpio_reg(&lg->chip, hwirq, LP_INT_ENABLE); - unsigned long flags; - raw_spin_lock_irqsave(&lg->lock, flags); - iowrite32(ioread32(reg) & ~BIT(hwirq % 32), reg); - raw_spin_unlock_irqrestore(&lg->lock, flags); + scoped_guard(raw_spinlock_irqsave, &lg->lock) + iowrite32(ioread32(reg) & ~BIT(hwirq % 32), reg); gpiochip_disable_irq(gc, hwirq); } @@ -642,7 +614,6 @@ static int lp_irq_set_type(struct irq_data *d, unsigned int type) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct intel_pinctrl *lg = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); - unsigned long flags; void __iomem *reg; u32 value; @@ -656,7 +627,8 @@ static int lp_irq_set_type(struct irq_data *d, unsigned int type) return -EBUSY; } - raw_spin_lock_irqsave(&lg->lock, flags); + guard(raw_spinlock_irqsave)(&lg->lock); + value = ioread32(reg); /* set both TRIG_SEL and INV bits to 0 for rising edge */ @@ -682,8 +654,6 @@ static int lp_irq_set_type(struct irq_data *d, unsigned int type) else if (type & IRQ_TYPE_LEVEL_MASK) irq_set_handler_locked(d, handle_level_irq); - raw_spin_unlock_irqrestore(&lg->lock, flags); - return 0; } diff --git a/drivers/pinctrl/intel/pinctrl-meteorlake.c b/drivers/pinctrl/intel/pinctrl-meteorlake.c index 7ced2b402dce..cc44890c6699 100644 --- a/drivers/pinctrl/intel/pinctrl-meteorlake.c +++ b/drivers/pinctrl/intel/pinctrl-meteorlake.c @@ -9,6 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -589,14 +590,12 @@ static const struct acpi_device_id mtl_pinctrl_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, mtl_pinctrl_acpi_match); -static INTEL_PINCTRL_PM_OPS(mtl_pinctrl_pm_ops); - static struct platform_driver mtl_pinctrl_driver = { .probe = intel_pinctrl_probe_by_hid, .driver = { .name = "meteorlake-pinctrl", .acpi_match_table = mtl_pinctrl_acpi_match, - .pm = &mtl_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; module_platform_driver(mtl_pinctrl_driver); diff --git a/drivers/pinctrl/intel/pinctrl-meteorpoint.c b/drivers/pinctrl/intel/pinctrl-meteorpoint.c new file mode 100644 index 000000000000..77e97775a60b --- /dev/null +++ b/drivers/pinctrl/intel/pinctrl-meteorpoint.c @@ -0,0 +1,465 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Meteor Point PCH pinctrl/GPIO driver + * + * Copyright (C) 2022-2023, Intel Corporation + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm.h> + +#include <linux/pinctrl/pinctrl.h> + +#include "pinctrl-intel.h" + +#define MTP_PAD_OWN 0x0b0 +#define MTP_PADCFGLOCK 0x110 +#define MTP_HOSTSW_OWN 0x150 +#define MTP_GPI_IS 0x200 +#define MTP_GPI_IE 0x220 + +#define MTP_GPP(r, s, e, g) \ + { \ + .reg_num = (r), \ + .base = (s), \ + .size = ((e) - (s) + 1), \ + .gpio_base = (g), \ + } + +#define MTP_COMMUNITY(b, s, e, g) \ + INTEL_COMMUNITY_GPPS(b, s, e, g, MTP) + +/* Meteor Point-S */ +static const struct pinctrl_pin_desc mtps_pins[] = { + /* GPP_D */ + PINCTRL_PIN(0, "GPP_D_0"), + PINCTRL_PIN(1, "GPP_D_1"), + PINCTRL_PIN(2, "GPP_D_2"), + PINCTRL_PIN(3, "GPP_D_3"), + PINCTRL_PIN(4, "GPP_D_4"), + PINCTRL_PIN(5, "CNV_RF_RESET_B"), + PINCTRL_PIN(6, "CRF_CLKREQ"), + PINCTRL_PIN(7, "GPP_D_7"), + PINCTRL_PIN(8, "GPP_D_8"), + PINCTRL_PIN(9, "SML0CLK"), + PINCTRL_PIN(10, "SML0DATA"), + PINCTRL_PIN(11, "GPP_D_11"), + PINCTRL_PIN(12, "GPP_D_12"), + PINCTRL_PIN(13, "GPP_D_13"), + PINCTRL_PIN(14, "GPP_D_14"), + PINCTRL_PIN(15, "GPP_D_15"), + PINCTRL_PIN(16, "GPP_D_16"), + PINCTRL_PIN(17, "GPP_D_17"), + PINCTRL_PIN(18, "GPP_D_18"), + PINCTRL_PIN(19, "GPP_D_19"), + PINCTRL_PIN(20, "GPP_D_20"), + PINCTRL_PIN(21, "GPP_D_21"), + PINCTRL_PIN(22, "GPP_D_22"), + PINCTRL_PIN(23, "GPP_D_23"), + PINCTRL_PIN(24, "GSPI3_CLK_LOOPBK"), + /* GPP_R */ + PINCTRL_PIN(25, "HDA_BCLK"), + PINCTRL_PIN(26, "HDA_SYNC"), + PINCTRL_PIN(27, "HDA_SDO"), + PINCTRL_PIN(28, "HDA_SDI_0"), + PINCTRL_PIN(29, "HDA_RSTB"), + PINCTRL_PIN(30, "GPP_R_5"), + PINCTRL_PIN(31, "GPP_R_6"), + PINCTRL_PIN(32, "GPP_R_7"), + PINCTRL_PIN(33, "GPP_R_8"), + PINCTRL_PIN(34, "GPP_R_9"), + PINCTRL_PIN(35, "GPP_R_10"), + PINCTRL_PIN(36, "GPP_R_11"), + PINCTRL_PIN(37, "GPP_R_12"), + PINCTRL_PIN(38, "GSPI2_CLK_LOOPBK"), + /* GPP_J */ + PINCTRL_PIN(39, "GPP_J_0"), + PINCTRL_PIN(40, "CNV_BRI_DT"), + PINCTRL_PIN(41, "CNV_BRI_RSP"), + PINCTRL_PIN(42, "CNV_RGI_DT"), + PINCTRL_PIN(43, "CNV_RGI_RSP"), + PINCTRL_PIN(44, "GPP_J_5"), + PINCTRL_PIN(45, "GPP_J_6"), + PINCTRL_PIN(46, "BOOTHALT_B"), + PINCTRL_PIN(47, "RTCCLKOUT"), + PINCTRL_PIN(48, "BPKI3C_SDA"), + PINCTRL_PIN(49, "BPKI3C_SCL"), + PINCTRL_PIN(50, "DAM"), + PINCTRL_PIN(51, "HDACPU_SDI"), + PINCTRL_PIN(52, "HDACPU_SDO"), + PINCTRL_PIN(53, "HDACPU_BCLK"), + PINCTRL_PIN(54, "AUX_PWRGD"), + PINCTRL_PIN(55, "GLB_RST_WARN_B"), + PINCTRL_PIN(56, "RESET_SYNCB"), + /* vGPIO */ + PINCTRL_PIN(57, "CNV_BTEN"), + PINCTRL_PIN(58, "CNV_BT_HOST_WAKEB"), + PINCTRL_PIN(59, "CNV_BT_IF_SELECT"), + PINCTRL_PIN(60, "vCNV_BT_UART_TXD"), + PINCTRL_PIN(61, "vCNV_BT_UART_RXD"), + PINCTRL_PIN(62, "vCNV_BT_UART_CTS_B"), + PINCTRL_PIN(63, "vCNV_BT_UART_RTS_B"), + PINCTRL_PIN(64, "vCNV_MFUART1_TXD"), + PINCTRL_PIN(65, "vCNV_MFUART1_RXD"), + PINCTRL_PIN(66, "vCNV_MFUART1_CTS_B"), + PINCTRL_PIN(67, "vCNV_MFUART1_RTS_B"), + PINCTRL_PIN(68, "vUART0_TXD"), + PINCTRL_PIN(69, "vUART0_RXD"), + PINCTRL_PIN(70, "vUART0_CTS_B"), + PINCTRL_PIN(71, "vUART0_RTS_B"), + PINCTRL_PIN(72, "vISH_UART0_TXD"), + PINCTRL_PIN(73, "vISH_UART0_RXD"), + PINCTRL_PIN(74, "vISH_UART0_CTS_B"), + PINCTRL_PIN(75, "vISH_UART0_RTS_B"), + PINCTRL_PIN(76, "vCNV_BT_I2S_BCLK"), + PINCTRL_PIN(77, "vCNV_BT_I2S_WS_SYNC"), + PINCTRL_PIN(78, "vCNV_BT_I2S_SDO"), + PINCTRL_PIN(79, "vCNV_BT_I2S_SDI"), + PINCTRL_PIN(80, "vI2S2_SCLK"), + PINCTRL_PIN(81, "vI2S2_SFRM"), + PINCTRL_PIN(82, "vI2S2_TXD"), + PINCTRL_PIN(83, "vI2S2_RXD"), + PINCTRL_PIN(84, "THC0_WOT_INT"), + PINCTRL_PIN(85, "THC1_WOT_INT"), + PINCTRL_PIN(86, "THC0_WHC_INT"), + PINCTRL_PIN(87, "THC1_WHC_INT"), + /* GPP_A */ + PINCTRL_PIN(88, "ESPI_IO_0"), + PINCTRL_PIN(89, "ESPI_IO_1"), + PINCTRL_PIN(90, "ESPI_IO_2"), + PINCTRL_PIN(91, "ESPI_IO_3"), + PINCTRL_PIN(92, "ESPI_CS0B"), + PINCTRL_PIN(93, "ESPI_CLK"), + PINCTRL_PIN(94, "ESPI_RESETB"), + PINCTRL_PIN(95, "ESPI_CS1B"), + PINCTRL_PIN(96, "ESPI_CS2B"), + PINCTRL_PIN(97, "ESPI_CS3B"), + PINCTRL_PIN(98, "ESPI_ALERT0B"), + PINCTRL_PIN(99, "ESPI_ALERT1B"), + PINCTRL_PIN(100, "ESPI_ALERT2B"), + PINCTRL_PIN(101, "ESPI_ALERT3B"), + PINCTRL_PIN(102, "ESPI_CLK_LOOPBK"), + /* DIR_ESPI */ + PINCTRL_PIN(103, "PWRBTNB_OUT"), + PINCTRL_PIN(104, "DMI_PERSTB"), + PINCTRL_PIN(105, "DMI_CLKREQB"), + PINCTRL_PIN(106, "DIR_ESPI_IO_0"), + PINCTRL_PIN(107, "DIR_ESPI_IO_1"), + PINCTRL_PIN(108, "DIR_ESPI_IO_2"), + PINCTRL_PIN(109, "DIR_ESPI_IO_3"), + PINCTRL_PIN(110, "DIR_ESPI_CSB"), + PINCTRL_PIN(111, "DIR_ESPI_RESETB"), + PINCTRL_PIN(112, "DIR_ESPI_CLK"), + PINCTRL_PIN(113, "DIR_ESPI_RCLK"), + PINCTRL_PIN(114, "DIR_ESPI_ALERTB"), + /* GPP_B */ + PINCTRL_PIN(115, "GPP_B_0"), + PINCTRL_PIN(116, "GPP_B_1"), + PINCTRL_PIN(117, "GPP_B_2"), + PINCTRL_PIN(118, "GPP_B_3"), + PINCTRL_PIN(119, "GPP_B_4"), + PINCTRL_PIN(120, "GPP_B_5"), + PINCTRL_PIN(121, "CLKOUT_48"), + PINCTRL_PIN(122, "GPP_B_7"), + PINCTRL_PIN(123, "GPP_B_8"), + PINCTRL_PIN(124, "GPP_B_9"), + PINCTRL_PIN(125, "GPP_B_10"), + PINCTRL_PIN(126, "GPP_B_11"), + PINCTRL_PIN(127, "SLP_S0B"), + PINCTRL_PIN(128, "PLTRSTB"), + PINCTRL_PIN(129, "GPP_B_14"), + PINCTRL_PIN(130, "GPP_B_15"), + PINCTRL_PIN(131, "GPP_B_16"), + PINCTRL_PIN(132, "GPP_B_17"), + PINCTRL_PIN(133, "GPP_B_18"), + PINCTRL_PIN(134, "FUSA_DIAGTEST_EN"), + PINCTRL_PIN(135, "FUSA_DIAGTEST_MODE"), + PINCTRL_PIN(136, "GPP_B_21"), + /* SPI0 */ + PINCTRL_PIN(137, "SPI0_IO_2"), + PINCTRL_PIN(138, "SPI0_IO_3"), + PINCTRL_PIN(139, "SPI0_MOSI_IO_0"), + PINCTRL_PIN(140, "SPI0_MISO_IO_1"), + PINCTRL_PIN(141, "SPI0_TPM_CSB"), + PINCTRL_PIN(142, "SPI0_FLASH_0_CSB"), + PINCTRL_PIN(143, "SPI0_FLASH_1_CSB"), + PINCTRL_PIN(144, "SPI0_CLK"), + PINCTRL_PIN(145, "SPI0_CLK_LOOPBK"), + /* GPP_C */ + PINCTRL_PIN(146, "SMBCLK"), + PINCTRL_PIN(147, "SMBDATA"), + PINCTRL_PIN(148, "SMBALERTB"), + PINCTRL_PIN(149, "GPP_C_3"), + PINCTRL_PIN(150, "GPP_C_4"), + PINCTRL_PIN(151, "GPP_C_5"), + PINCTRL_PIN(152, "GPP_C_6"), + PINCTRL_PIN(153, "GPP_C_7"), + PINCTRL_PIN(154, "GPP_C_8"), + PINCTRL_PIN(155, "GPP_C_9"), + PINCTRL_PIN(156, "GPP_C_10"), + PINCTRL_PIN(157, "GPP_C_11"), + PINCTRL_PIN(158, "GPP_C_12"), + PINCTRL_PIN(159, "GPP_C_13"), + PINCTRL_PIN(160, "GPP_C_14"), + PINCTRL_PIN(161, "GPP_C_15"), + PINCTRL_PIN(162, "GPP_C_16"), + PINCTRL_PIN(163, "GPP_C_17"), + PINCTRL_PIN(164, "GPP_C_18"), + PINCTRL_PIN(165, "GPP_C_19"), + PINCTRL_PIN(166, "GPP_C_20"), + PINCTRL_PIN(167, "GPP_C_21"), + PINCTRL_PIN(168, "GPP_C_22"), + PINCTRL_PIN(169, "GPP_C_23"), + /* GPP_H */ + PINCTRL_PIN(170, "GPP_H_0"), + PINCTRL_PIN(171, "GPP_H_1"), + PINCTRL_PIN(172, "GPP_H_2"), + PINCTRL_PIN(173, "GPP_H_3"), + PINCTRL_PIN(174, "GPP_H_4"), + PINCTRL_PIN(175, "GPP_H_5"), + PINCTRL_PIN(176, "GPP_H_6"), + PINCTRL_PIN(177, "GPP_H_7"), + PINCTRL_PIN(178, "GPP_H_8"), + PINCTRL_PIN(179, "GPP_H_9"), + PINCTRL_PIN(180, "GPP_H_10"), + PINCTRL_PIN(181, "GPP_H_11"), + PINCTRL_PIN(182, "GPP_H_12"), + PINCTRL_PIN(183, "GPP_H_13"), + PINCTRL_PIN(184, "GPP_H_14"), + PINCTRL_PIN(185, "GPP_H_15"), + PINCTRL_PIN(186, "GPP_H_16"), + PINCTRL_PIN(187, "GPP_H_17"), + PINCTRL_PIN(188, "GPP_H_18"), + PINCTRL_PIN(189, "GPP_H_19"), + /* vGPIO_3 */ + PINCTRL_PIN(190, "CPU_PCIE_LNK_DN_0"), + PINCTRL_PIN(191, "CPU_PCIE_LNK_DN_1"), + PINCTRL_PIN(192, "CPU_PCIE_LNK_DN_2"), + PINCTRL_PIN(193, "CPU_PCIE_LNK_DN_3"), + /* vGPIO_0 */ + PINCTRL_PIN(194, "ESPI_USB_OCB_0"), + PINCTRL_PIN(195, "ESPI_USB_OCB_1"), + PINCTRL_PIN(196, "ESPI_USB_OCB_2"), + PINCTRL_PIN(197, "ESPI_USB_OCB_3"), + PINCTRL_PIN(198, "USB_CPU_OCB_0"), + PINCTRL_PIN(199, "USB_CPU_OCB_1"), + PINCTRL_PIN(200, "USB_CPU_OCB_2"), + PINCTRL_PIN(201, "USB_CPU_OCB_3"), + /* vGPIO_4 */ + PINCTRL_PIN(202, "ESPI_ISCLK_XTAL_CLKREQ"), + PINCTRL_PIN(203, "ISCLK_ESPI_XTAL_CLKACK"), + PINCTRL_PIN(204, "ME_SLPC_FTPM_ENABLE"), + PINCTRL_PIN(205, "GP_SLPC_DTFUS_CORE_SPITPM_DIS"), + PINCTRL_PIN(206, "GP_SLPC_SPI_STRAP_TOS"), + PINCTRL_PIN(207, "GP_SLPC_DTFUS_CORE_SPITPM_DIS_L01"), + PINCTRL_PIN(208, "GP_SLPC_SPI_STRAP_TOS_L01"), + PINCTRL_PIN(209, "LPC_PRR_TS_OVR"), + PINCTRL_PIN(210, "ITSS_KU1_SHTDWN"), + PINCTRL_PIN(211, "vGPIO_SPARE_0"), + PINCTRL_PIN(212, "vGPIO_SPARE_1"), + PINCTRL_PIN(213, "vGPIO_SPARE_2"), + PINCTRL_PIN(214, "vGPIO_SPARE_3"), + PINCTRL_PIN(215, "vGPIO_SPARE_4"), + PINCTRL_PIN(216, "vGPIO_SPARE_5"), + PINCTRL_PIN(217, "vGPIO_SPARE_6"), + PINCTRL_PIN(218, "vGPIO_SPARE_7"), + PINCTRL_PIN(219, "vGPIO_SPARE_8"), + PINCTRL_PIN(220, "vGPIO_SPARE_9"), + PINCTRL_PIN(221, "vGPIO_SPARE_10"), + PINCTRL_PIN(222, "vGPIO_SPARE_11"), + PINCTRL_PIN(223, "vGPIO_SPARE_12"), + PINCTRL_PIN(224, "vGPIO_SPARE_13"), + PINCTRL_PIN(225, "vGPIO_SPARE_14"), + PINCTRL_PIN(226, "vGPIO_SPARE_15"), + PINCTRL_PIN(227, "vGPIO_SPARE_16"), + PINCTRL_PIN(228, "vGPIO_SPARE_17"), + PINCTRL_PIN(229, "vGPIO_SPARE_18"), + PINCTRL_PIN(230, "vGPIO_SPARE_19"), + PINCTRL_PIN(231, "vGPIO_SPARE_20"), + PINCTRL_PIN(232, "vGPIO_SPARE_21"), + /* GPP_S */ + PINCTRL_PIN(233, "GPP_S_0"), + PINCTRL_PIN(234, "GPP_S_1"), + PINCTRL_PIN(235, "GPP_S_2"), + PINCTRL_PIN(236, "GPP_S_3"), + PINCTRL_PIN(237, "GPP_S_4"), + PINCTRL_PIN(238, "GPP_S_5"), + PINCTRL_PIN(239, "GPP_S_6"), + PINCTRL_PIN(240, "GPP_S_7"), + /* GPP_E */ + PINCTRL_PIN(241, "GPP_E_0"), + PINCTRL_PIN(242, "GPP_E_1"), + PINCTRL_PIN(243, "GPP_E_2"), + PINCTRL_PIN(244, "GPP_E_3"), + PINCTRL_PIN(245, "GPP_E_4"), + PINCTRL_PIN(246, "GPP_E_5"), + PINCTRL_PIN(247, "GPP_E_6"), + PINCTRL_PIN(248, "GPP_E_7"), + PINCTRL_PIN(249, "GPP_E_8"), + PINCTRL_PIN(250, "GPP_E_9"), + PINCTRL_PIN(251, "GPP_E_10"), + PINCTRL_PIN(252, "GPP_E_11"), + PINCTRL_PIN(253, "GPP_E_12"), + PINCTRL_PIN(254, "GPP_E_13"), + PINCTRL_PIN(255, "GPP_E_14"), + PINCTRL_PIN(256, "GPP_E_15"), + PINCTRL_PIN(257, "GPP_E_16"), + PINCTRL_PIN(258, "GPP_E_17"), + PINCTRL_PIN(259, "GPP_E_18"), + PINCTRL_PIN(260, "GPP_E_19"), + PINCTRL_PIN(261, "GPP_E_20"), + PINCTRL_PIN(262, "GPP_E_21"), + PINCTRL_PIN(263, "SPI1_CLK_LOOPBK"), + /* GPP_K */ + PINCTRL_PIN(264, "GPP_K_0"), + PINCTRL_PIN(265, "GPP_K_1"), + PINCTRL_PIN(266, "GPP_K_2"), + PINCTRL_PIN(267, "GPP_K_3"), + PINCTRL_PIN(268, "GPP_K_4"), + PINCTRL_PIN(269, "GPP_K_5"), + PINCTRL_PIN(270, "FUSE_SORT_BUMP_0"), + PINCTRL_PIN(271, "FUSE_SORT_BUMP_1"), + PINCTRL_PIN(272, "CORE_VID_0"), + PINCTRL_PIN(273, "CORE_VID_1"), + PINCTRL_PIN(274, "FUSE_SORT_BUMP_2"), + PINCTRL_PIN(275, "MISC_SPARE"), + PINCTRL_PIN(276, "SYS_RESETB"), + PINCTRL_PIN(277, "MLK_RSTB"), + /* GPP_F */ + PINCTRL_PIN(278, "SATAXPCIE_3"), + PINCTRL_PIN(279, "SATAXPCIE_4"), + PINCTRL_PIN(280, "SATAXPCIE_5"), + PINCTRL_PIN(281, "SATAXPCIE_6"), + PINCTRL_PIN(282, "SATAXPCIE_7"), + PINCTRL_PIN(283, "SATA_DEVSLP_3"), + PINCTRL_PIN(284, "SATA_DEVSLP_4"), + PINCTRL_PIN(285, "SATA_DEVSLP_5"), + PINCTRL_PIN(286, "SATA_DEVSLP_6"), + PINCTRL_PIN(287, "GPP_F_9"), + PINCTRL_PIN(288, "GPP_F_10"), + PINCTRL_PIN(289, "GPP_F_11"), + PINCTRL_PIN(290, "GPP_F_12"), + PINCTRL_PIN(291, "GPP_F_13"), + PINCTRL_PIN(292, "GPP_F_14"), + PINCTRL_PIN(293, "GPP_F_15"), + PINCTRL_PIN(294, "GPP_F_16"), + PINCTRL_PIN(295, "GPP_F_17"), + PINCTRL_PIN(296, "GPP_F_18"), + PINCTRL_PIN(297, "DNX_FORCE_RELOAD"), + PINCTRL_PIN(298, "GPP_F_20"), + PINCTRL_PIN(299, "GPP_F_21"), + PINCTRL_PIN(300, "GPP_F_22"), + PINCTRL_PIN(301, "GPP_F_23"), + /* GPP_I */ + PINCTRL_PIN(302, "GPP_I_0"), + PINCTRL_PIN(303, "GPP_I_1"), + PINCTRL_PIN(304, "GPP_I_2"), + PINCTRL_PIN(305, "GPP_I_3"), + PINCTRL_PIN(306, "GPP_I_4"), + PINCTRL_PIN(307, "GPP_I_5"), + PINCTRL_PIN(308, "GPP_I_6"), + PINCTRL_PIN(309, "GPP_I_7"), + PINCTRL_PIN(310, "GPP_I_8"), + PINCTRL_PIN(311, "GPP_I_9"), + PINCTRL_PIN(312, "GPP_I_10"), + PINCTRL_PIN(313, "GPP_I_11"), + PINCTRL_PIN(314, "GPP_I_12"), + PINCTRL_PIN(315, "GPP_I_13"), + PINCTRL_PIN(316, "GPP_I_14"), + PINCTRL_PIN(317, "GPP_I_15"), + PINCTRL_PIN(318, "GPP_I_16"), + PINCTRL_PIN(319, "GSPI0_CLK_LOOPBK"), + PINCTRL_PIN(320, "GSPI1_CLK_LOOPBK"), + PINCTRL_PIN(321, "ISH_I3C0_CLK_LOOPBK"), + PINCTRL_PIN(322, "I3C0_CLK_LOOPBK"), + /* JTAG_CPU */ + PINCTRL_PIN(323, "JTAG_TDO"), + PINCTRL_PIN(324, "JTAGX"), + PINCTRL_PIN(325, "PRDYB"), + PINCTRL_PIN(326, "PREQB"), + PINCTRL_PIN(327, "JTAG_TDI"), + PINCTRL_PIN(328, "JTAG_TMS"), + PINCTRL_PIN(329, "JTAG_TCK"), + PINCTRL_PIN(330, "DBG_PMODE"), + PINCTRL_PIN(331, "CPU_TRSTB"), + PINCTRL_PIN(332, "CPUPWRGD"), + PINCTRL_PIN(333, "PM_SPARE0"), + PINCTRL_PIN(334, "PM_SPARE1"), + PINCTRL_PIN(335, "CRASHLOG_TRIG_N"), + PINCTRL_PIN(336, "TRIGGER_IN"), + PINCTRL_PIN(337, "TRIGGER_OUT"), + PINCTRL_PIN(338, "FBRK_OUT_N"), +}; + +static const struct intel_padgroup mtps_community0_gpps[] = { + MTP_GPP(0, 0, 24, 0), /* GPP_D */ + MTP_GPP(1, 25, 38, 32), /* GPP_R */ + MTP_GPP(2, 39, 56, 64), /* GPP_J */ + MTP_GPP(3, 57, 87, 96), /* vGPIO */ +}; + +static const struct intel_padgroup mtps_community1_gpps[] = { + MTP_GPP(0, 88, 102, 128), /* GPP_A */ + MTP_GPP(1, 103, 114, 160), /* DIR_ESPI */ + MTP_GPP(2, 115, 136, 192), /* GPP_B */ +}; + +static const struct intel_padgroup mtps_community3_gpps[] = { + MTP_GPP(0, 137, 145, 224), /* SPI0 */ + MTP_GPP(1, 146, 169, 256), /* GPP_C */ + MTP_GPP(2, 170, 189, 288), /* GPP_H */ + MTP_GPP(3, 190, 193, 320), /* vGPIO_3 */ + MTP_GPP(4, 194, 201, 352), /* vGPIO_0 */ + MTP_GPP(5, 202, 232, 384), /* vGPIO_4 */ +}; + +static const struct intel_padgroup mtps_community4_gpps[] = { + MTP_GPP(0, 233, 240, 416), /* GPP_S */ + MTP_GPP(1, 241, 263, 448), /* GPP_E */ + MTP_GPP(2, 264, 277, 480), /* GPP_K */ + MTP_GPP(3, 278, 301, 512), /* GPP_F */ +}; + +static const struct intel_padgroup mtps_community5_gpps[] = { + MTP_GPP(0, 302, 322, 544), /* GPP_I */ + MTP_GPP(1, 323, 338, 576), /* JTAG_CPU */ +}; + +static const struct intel_community mtps_communities[] = { + MTP_COMMUNITY(0, 0, 87, mtps_community0_gpps), + MTP_COMMUNITY(1, 88, 136, mtps_community1_gpps), + MTP_COMMUNITY(2, 137, 232, mtps_community3_gpps), + MTP_COMMUNITY(3, 233, 301, mtps_community4_gpps), + MTP_COMMUNITY(4, 302, 338, mtps_community5_gpps), +}; + +static const struct intel_pinctrl_soc_data mtps_soc_data = { + .pins = mtps_pins, + .npins = ARRAY_SIZE(mtps_pins), + .communities = mtps_communities, + .ncommunities = ARRAY_SIZE(mtps_communities), +}; + +static const struct acpi_device_id mtp_pinctrl_acpi_match[] = { + { "INTC1084", (kernel_ulong_t)&mtps_soc_data }, + { } +}; +MODULE_DEVICE_TABLE(acpi, mtp_pinctrl_acpi_match); + +static struct platform_driver mtp_pinctrl_driver = { + .probe = intel_pinctrl_probe_by_hid, + .driver = { + .name = "meteorpoint-pinctrl", + .acpi_match_table = mtp_pinctrl_acpi_match, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), + }, +}; +module_platform_driver(mtp_pinctrl_driver); + +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("Intel Meteor Point PCH pinctrl/GPIO driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(PINCTRL_INTEL); diff --git a/drivers/pinctrl/intel/pinctrl-sunrisepoint.c b/drivers/pinctrl/intel/pinctrl-sunrisepoint.c index b7a40ab0bca8..55df9d2cfb1b 100644 --- a/drivers/pinctrl/intel/pinctrl-sunrisepoint.c +++ b/drivers/pinctrl/intel/pinctrl-sunrisepoint.c @@ -10,6 +10,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -579,14 +580,12 @@ static const struct acpi_device_id spt_pinctrl_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, spt_pinctrl_acpi_match); -static INTEL_PINCTRL_PM_OPS(spt_pinctrl_pm_ops); - static struct platform_driver spt_pinctrl_driver = { .probe = intel_pinctrl_probe_by_hid, .driver = { .name = "sunrisepoint-pinctrl", .acpi_match_table = spt_pinctrl_acpi_match, - .pm = &spt_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; diff --git a/drivers/pinctrl/intel/pinctrl-tangier.c b/drivers/pinctrl/intel/pinctrl-tangier.c index 40dd60c9e526..2cb0b4758269 100644 --- a/drivers/pinctrl/intel/pinctrl-tangier.c +++ b/drivers/pinctrl/intel/pinctrl-tangier.c @@ -9,6 +9,7 @@ */ #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/err.h> #include <linux/errno.h> @@ -220,7 +221,6 @@ static int tng_pinmux_set_mux(struct pinctrl_dev *pctldev, const struct intel_pingroup *grp = &tp->groups[group]; u32 bits = grp->mode << BUFCFG_PINMODE_SHIFT; u32 mask = BUFCFG_PINMODE_MASK; - unsigned long flags; unsigned int i; /* @@ -232,11 +232,11 @@ static int tng_pinmux_set_mux(struct pinctrl_dev *pctldev, return -EBUSY; } + guard(raw_spinlock_irqsave)(&tp->lock); + /* Now enable the mux setting for each pin in the group */ - raw_spin_lock_irqsave(&tp->lock, flags); for (i = 0; i < grp->grp.npins; i++) tng_update_bufcfg(tp, grp->grp.pins[i], bits, mask); - raw_spin_unlock_irqrestore(&tp->lock, flags); return 0; } @@ -248,14 +248,13 @@ static int tng_gpio_request_enable(struct pinctrl_dev *pctldev, struct tng_pinctrl *tp = pinctrl_dev_get_drvdata(pctldev); u32 bits = BUFCFG_PINMODE_GPIO << BUFCFG_PINMODE_SHIFT; u32 mask = BUFCFG_PINMODE_MASK; - unsigned long flags; if (!tng_buf_available(tp, pin)) return -EBUSY; - raw_spin_lock_irqsave(&tp->lock, flags); + guard(raw_spinlock_irqsave)(&tp->lock); + tng_update_bufcfg(tp, pin, bits, mask); - raw_spin_unlock_irqrestore(&tp->lock, flags); return 0; } @@ -360,7 +359,6 @@ static int tng_config_set_pin(struct tng_pinctrl *tp, unsigned int pin, unsigned int param = pinconf_to_config_param(config); unsigned int arg = pinconf_to_config_argument(config); u32 mask, term, value = 0; - unsigned long flags; switch (param) { case PIN_CONFIG_BIAS_DISABLE: @@ -368,20 +366,20 @@ static int tng_config_set_pin(struct tng_pinctrl *tp, unsigned int pin, break; case PIN_CONFIG_BIAS_PULL_UP: - /* Set default strength value in case none is given */ - if (arg == 1) - arg = 20000; - switch (arg) { case 50000: term = BUFCFG_PUPD_VAL_50K; break; + case 1: /* Set default strength value in case none is given */ case 20000: term = BUFCFG_PUPD_VAL_20K; break; case 2000: term = BUFCFG_PUPD_VAL_2K; break; + case 910: + term = BUFCFG_PUPD_VAL_910; + break; default: return -EINVAL; } @@ -391,20 +389,20 @@ static int tng_config_set_pin(struct tng_pinctrl *tp, unsigned int pin, break; case PIN_CONFIG_BIAS_PULL_DOWN: - /* Set default strength value in case none is given */ - if (arg == 1) - arg = 20000; - switch (arg) { case 50000: term = BUFCFG_PUPD_VAL_50K; break; + case 1: /* Set default strength value in case none is given */ case 20000: term = BUFCFG_PUPD_VAL_20K; break; case 2000: term = BUFCFG_PUPD_VAL_2K; break; + case 910: + term = BUFCFG_PUPD_VAL_910; + break; default: return -EINVAL; } @@ -432,9 +430,9 @@ static int tng_config_set_pin(struct tng_pinctrl *tp, unsigned int pin, return -EINVAL; } - raw_spin_lock_irqsave(&tp->lock, flags); + guard(raw_spinlock_irqsave)(&tp->lock); + tng_update_bufcfg(tp, pin, value, mask); - raw_spin_unlock_irqrestore(&tp->lock, flags); return 0; } diff --git a/drivers/pinctrl/intel/pinctrl-tigerlake.c b/drivers/pinctrl/intel/pinctrl-tigerlake.c index 4768a69a9258..80cd7a06fe5a 100644 --- a/drivers/pinctrl/intel/pinctrl-tigerlake.c +++ b/drivers/pinctrl/intel/pinctrl-tigerlake.c @@ -10,6 +10,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/pinctrl/pinctrl.h> @@ -743,14 +744,12 @@ static const struct acpi_device_id tgl_pinctrl_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, tgl_pinctrl_acpi_match); -static INTEL_PINCTRL_PM_OPS(tgl_pinctrl_pm_ops); - static struct platform_driver tgl_pinctrl_driver = { .probe = intel_pinctrl_probe_by_hid, .driver = { .name = "tigerlake-pinctrl", .acpi_match_table = tgl_pinctrl_acpi_match, - .pm = &tgl_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&intel_pinctrl_pm_ops), }, }; module_platform_driver(tgl_pinctrl_driver); diff --git a/drivers/pinctrl/mediatek/pinctrl-moore.c b/drivers/pinctrl/mediatek/pinctrl-moore.c index c3d59eddd994..d972584c0519 100644 --- a/drivers/pinctrl/mediatek/pinctrl-moore.c +++ b/drivers/pinctrl/mediatek/pinctrl-moore.c @@ -56,12 +56,12 @@ static int mtk_pinmux_set_mux(struct pinctrl_dev *pctldev, return -EINVAL; dev_dbg(pctldev->dev, "enable function %s group %s\n", - func->name, grp->name); + func->name, grp->grp.name); - for (i = 0; i < grp->num_pins; i++) { + for (i = 0; i < grp->grp.npins; i++) { const struct mtk_pin_desc *desc; int *pin_modes = grp->data; - int pin = grp->pins[i]; + int pin = grp->grp.pins[i]; desc = (const struct mtk_pin_desc *)&hw->soc->pins[pin]; if (!desc->name) @@ -602,13 +602,12 @@ static int mtk_build_groups(struct mtk_pinctrl *hw) for (i = 0; i < hw->soc->ngrps; i++) { const struct group_desc *group = hw->soc->grps + i; + const struct pingroup *grp = &group->grp; - err = pinctrl_generic_add_group(hw->pctrl, group->name, - group->pins, group->num_pins, + err = pinctrl_generic_add_group(hw->pctrl, grp->name, grp->pins, grp->npins, group->data); if (err < 0) { - dev_err(hw->dev, "Failed to register group %s\n", - group->name); + dev_err(hw->dev, "Failed to register group %s\n", grp->name); return err; } } diff --git a/drivers/pinctrl/mediatek/pinctrl-moore.h b/drivers/pinctrl/mediatek/pinctrl-moore.h index e1b4b82b9d3d..e0313e7a1fe0 100644 --- a/drivers/pinctrl/mediatek/pinctrl-moore.h +++ b/drivers/pinctrl/mediatek/pinctrl-moore.h @@ -37,12 +37,10 @@ .funcs = NULL, \ } -#define PINCTRL_PIN_GROUP(name, id) \ - { \ - name, \ - id##_pins, \ - ARRAY_SIZE(id##_pins), \ - id##_funcs, \ +#define PINCTRL_PIN_GROUP(_name_, id) \ + { \ + .grp = PINCTRL_PINGROUP(_name_, id##_pins, ARRAY_SIZE(id##_pins)), \ + .data = id##_funcs, \ } int mtk_moore_pinctrl_probe(struct platform_device *pdev, diff --git a/drivers/pinctrl/mediatek/pinctrl-mt2701.c b/drivers/pinctrl/mediatek/pinctrl-mt2701.c index 5fb377c1668b..6b1c7122b0fb 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt2701.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt2701.c @@ -533,7 +533,7 @@ static struct platform_driver mtk_pinctrl_driver = { .driver = { .name = "mediatek-mt2701-pinctrl", .of_match_table = mt2701_pctrl_match, - .pm = &mtk_eint_pm_ops, + .pm = pm_sleep_ptr(&mtk_eint_pm_ops), }, }; diff --git a/drivers/pinctrl/mediatek/pinctrl-mt2712.c b/drivers/pinctrl/mediatek/pinctrl-mt2712.c index 8a6daa0db54b..bb7394ae252b 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt2712.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt2712.c @@ -581,7 +581,7 @@ static struct platform_driver mtk_pinctrl_driver = { .driver = { .name = "mediatek-mt2712-pinctrl", .of_match_table = mt2712_pctrl_match, - .pm = &mtk_eint_pm_ops, + .pm = pm_sleep_ptr(&mtk_eint_pm_ops), }, }; diff --git a/drivers/pinctrl/mediatek/pinctrl-mt6795.c b/drivers/pinctrl/mediatek/pinctrl-mt6795.c index 01e855ccd4dd..ee3ae3d2fa7e 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt6795.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt6795.c @@ -612,7 +612,7 @@ static struct platform_driver mt6795_pinctrl_driver = { .driver = { .name = "mt6795-pinctrl", .of_match_table = mt6795_pctrl_match, - .pm = &mtk_paris_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&mtk_paris_pinctrl_pm_ops), }, .probe = mtk_paris_pinctrl_probe, }; diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8167.c b/drivers/pinctrl/mediatek/pinctrl-mt8167.c index ba7f30c3296f..143c26622272 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8167.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8167.c @@ -334,7 +334,7 @@ static struct platform_driver mtk_pinctrl_driver = { .driver = { .name = "mediatek-mt8167-pinctrl", .of_match_table = mt8167_pctrl_match, - .pm = &mtk_eint_pm_ops, + .pm = pm_sleep_ptr(&mtk_eint_pm_ops), }, }; diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8173.c b/drivers/pinctrl/mediatek/pinctrl-mt8173.c index 455eec018f93..b214deeafbf1 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8173.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8173.c @@ -347,7 +347,7 @@ static struct platform_driver mtk_pinctrl_driver = { .driver = { .name = "mediatek-mt8173-pinctrl", .of_match_table = mt8173_pctrl_match, - .pm = &mtk_eint_pm_ops, + .pm = pm_sleep_ptr(&mtk_eint_pm_ops), }, }; diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8183.c b/drivers/pinctrl/mediatek/pinctrl-mt8183.c index ddc48b725c22..93e482c6b5fd 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8183.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8183.c @@ -576,7 +576,7 @@ static struct platform_driver mt8183_pinctrl_driver = { .driver = { .name = "mt8183-pinctrl", .of_match_table = mt8183_pinctrl_of_match, - .pm = &mtk_paris_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&mtk_paris_pinctrl_pm_ops), }, .probe = mtk_paris_pinctrl_probe, }; diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8186.c b/drivers/pinctrl/mediatek/pinctrl-mt8186.c index a02f7c326970..7be591591cce 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8186.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8186.c @@ -1255,7 +1255,7 @@ static struct platform_driver mt8186_pinctrl_driver = { .driver = { .name = "mt8186-pinctrl", .of_match_table = mt8186_pinctrl_of_match, - .pm = &mtk_paris_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&mtk_paris_pinctrl_pm_ops), }, .probe = mtk_paris_pinctrl_probe, }; diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8188.c b/drivers/pinctrl/mediatek/pinctrl-mt8188.c index c067e043e619..3975e99d9cf4 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8188.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8188.c @@ -1658,7 +1658,7 @@ static struct platform_driver mt8188_pinctrl_driver = { .driver = { .name = "mt8188-pinctrl", .of_match_table = mt8188_pinctrl_of_match, - .pm = &mtk_paris_pinctrl_pm_ops + .pm = pm_sleep_ptr(&mtk_paris_pinctrl_pm_ops) }, .probe = mtk_paris_pinctrl_probe, }; diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8192.c b/drivers/pinctrl/mediatek/pinctrl-mt8192.c index dee1b3aefd36..e3a76381f7f4 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8192.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8192.c @@ -1420,7 +1420,7 @@ static struct platform_driver mt8192_pinctrl_driver = { .driver = { .name = "mt8192-pinctrl", .of_match_table = mt8192_pinctrl_of_match, - .pm = &mtk_paris_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&mtk_paris_pinctrl_pm_ops), }, .probe = mtk_paris_pinctrl_probe, }; diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8195.c b/drivers/pinctrl/mediatek/pinctrl-mt8195.c index 09c4dcef9338..83345c52b2fa 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8195.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8195.c @@ -968,7 +968,7 @@ static struct platform_driver mt8195_pinctrl_driver = { .driver = { .name = "mt8195-pinctrl", .of_match_table = mt8195_pinctrl_of_match, - .pm = &mtk_paris_pinctrl_pm_ops, + .pm = pm_sleep_ptr(&mtk_paris_pinctrl_pm_ops), }, .probe = mtk_paris_pinctrl_probe, }; diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8365.c b/drivers/pinctrl/mediatek/pinctrl-mt8365.c index 1db04bbdb423..e3e0d66cfbbf 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8365.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8365.c @@ -484,7 +484,7 @@ static struct platform_driver mtk_pinctrl_driver = { .driver = { .name = "mediatek-mt8365-pinctrl", .of_match_table = mt8365_pctrl_match, - .pm = &mtk_eint_pm_ops, + .pm = pm_sleep_ptr(&mtk_eint_pm_ops), }, }; diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8516.c b/drivers/pinctrl/mediatek/pinctrl-mt8516.c index 950275c47122..abda75d4354e 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt8516.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt8516.c @@ -334,7 +334,7 @@ static struct platform_driver mtk_pinctrl_driver = { .driver = { .name = "mediatek-mt8516-pinctrl", .of_match_table = mt8516_pctrl_match, - .pm = &mtk_eint_pm_ops, + .pm = pm_sleep_ptr(&mtk_eint_pm_ops), }, }; diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c index e79d66a04194..d39afc122516 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c @@ -914,9 +914,8 @@ static int mtk_eint_resume(struct device *device) return mtk_eint_do_resume(pctl->eint); } -const struct dev_pm_ops mtk_eint_pm_ops = { - .suspend_noirq = mtk_eint_suspend, - .resume_noirq = mtk_eint_resume, +EXPORT_GPL_DEV_SLEEP_PM_OPS(mtk_eint_pm_ops) = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_eint_suspend, mtk_eint_resume) }; static int mtk_pctrl_build_state(struct platform_device *pdev) diff --git a/drivers/pinctrl/mediatek/pinctrl-paris.c b/drivers/pinctrl/mediatek/pinctrl-paris.c index 6392f1e05d02..b6bc31abd2b0 100644 --- a/drivers/pinctrl/mediatek/pinctrl-paris.c +++ b/drivers/pinctrl/mediatek/pinctrl-paris.c @@ -1131,9 +1131,8 @@ static int mtk_paris_pinctrl_resume(struct device *device) return mtk_eint_do_resume(pctl->eint); } -const struct dev_pm_ops mtk_paris_pinctrl_pm_ops = { - .suspend_noirq = mtk_paris_pinctrl_suspend, - .resume_noirq = mtk_paris_pinctrl_resume, +EXPORT_GPL_DEV_SLEEP_PM_OPS(mtk_paris_pinctrl_pm_ops) = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_paris_pinctrl_suspend, mtk_paris_pinctrl_resume) }; MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/mediatek/pinctrl-paris.h b/drivers/pinctrl/mediatek/pinctrl-paris.h index 8762ac599329..948ce126aa0c 100644 --- a/drivers/pinctrl/mediatek/pinctrl-paris.h +++ b/drivers/pinctrl/mediatek/pinctrl-paris.h @@ -49,12 +49,10 @@ __VA_ARGS__, { } }, \ } -#define PINCTRL_PIN_GROUP(name, id) \ - { \ - name, \ - id##_pins, \ - ARRAY_SIZE(id##_pins), \ - id##_funcs, \ +#define PINCTRL_PIN_GROUP(_name_, id) \ + { \ + .grp = PINCTRL_PINGROUP(_name_,id##_pins, ARRAY_SIZE(id##_pins)), \ + .data = id##_funcs, \ } int mtk_paris_pinctrl_probe(struct platform_device *pdev); diff --git a/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c b/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c index 1e658721aaba..62a46d824b46 100644 --- a/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c +++ b/drivers/pinctrl/nuvoton/pinctrl-npcm7xx.c @@ -1790,8 +1790,8 @@ static int npcm7xx_config_set_one(struct npcm7xx_pinctrl *npcm, bank->direction_input(&bank->gc, pin % bank->gc.ngpio); break; case PIN_CONFIG_OUTPUT: - iowrite32(gpio, bank->base + NPCM7XX_GP_N_OES); bank->direction_output(&bank->gc, pin % bank->gc.ngpio, arg); + iowrite32(gpio, bank->base + NPCM7XX_GP_N_OES); break; case PIN_CONFIG_DRIVE_PUSH_PULL: npcm_gpio_clr(&bank->gc, bank->base + NPCM7XX_GP_N_OTYP, gpio); diff --git a/drivers/pinctrl/nuvoton/pinctrl-wpcm450.c b/drivers/pinctrl/nuvoton/pinctrl-wpcm450.c index 0cff44b07b29..4589900244c7 100644 --- a/drivers/pinctrl/nuvoton/pinctrl-wpcm450.c +++ b/drivers/pinctrl/nuvoton/pinctrl-wpcm450.c @@ -474,9 +474,8 @@ enum { #undef WPCM450_GRP }; -static struct group_desc wpcm450_groups[] = { -#define WPCM450_GRP(x) { .name = #x, .pins = x ## _pins, \ - .num_pins = ARRAY_SIZE(x ## _pins) } +static struct pingroup wpcm450_groups[] = { +#define WPCM450_GRP(x) PINCTRL_PINGROUP(#x, x ## _pins, ARRAY_SIZE(x ## _pins)) WPCM450_GRPS #undef WPCM450_GRP }; @@ -852,7 +851,7 @@ static int wpcm450_get_group_pins(struct pinctrl_dev *pctldev, const unsigned int **pins, unsigned int *npins) { - *npins = wpcm450_groups[selector].num_pins; + *npins = wpcm450_groups[selector].npins; *pins = wpcm450_groups[selector].pins; return 0; @@ -901,7 +900,7 @@ static int wpcm450_pinmux_set_mux(struct pinctrl_dev *pctldev, struct wpcm450_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); wpcm450_setfunc(pctrl->gcr_regmap, wpcm450_groups[group].pins, - wpcm450_groups[group].num_pins, function); + wpcm450_groups[group].npins, function); return 0; } diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index 8313cb5f3b3c..cada5d18ffae 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -57,7 +57,7 @@ static const struct pin_config_item conf_items[] = { static void pinconf_generic_dump_one(struct pinctrl_dev *pctldev, struct seq_file *s, const char *gname, - unsigned pin, + unsigned int pin, const struct pin_config_item *items, int nitems, int *print_sep) { @@ -110,7 +110,7 @@ static void pinconf_generic_dump_one(struct pinctrl_dev *pctldev, * to be specified the other can be NULL/0. */ void pinconf_generic_dump_pins(struct pinctrl_dev *pctldev, struct seq_file *s, - const char *gname, unsigned pin) + const char *gname, unsigned int pin) { const struct pinconf_ops *ops = pctldev->desc->confops; int print_sep = 0; @@ -295,15 +295,15 @@ EXPORT_SYMBOL_GPL(pinconf_generic_parse_dt_config); int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, - unsigned *reserved_maps, unsigned *num_maps, + unsigned int *reserved_maps, unsigned int *num_maps, enum pinctrl_map_type type) { int ret; const char *function; struct device *dev = pctldev->dev; unsigned long *configs = NULL; - unsigned num_configs = 0; - unsigned reserve, strings_count; + unsigned int num_configs = 0; + unsigned int reserve, strings_count; struct property *prop; const char *group; const char *subnode_target_type = "pins"; @@ -379,9 +379,9 @@ EXPORT_SYMBOL_GPL(pinconf_generic_dt_subnode_to_map); int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, - unsigned *num_maps, enum pinctrl_map_type type) + unsigned int *num_maps, enum pinctrl_map_type type) { - unsigned reserved_maps; + unsigned int reserved_maps; struct device_node *np; int ret; @@ -412,7 +412,7 @@ EXPORT_SYMBOL_GPL(pinconf_generic_dt_node_to_map); void pinconf_generic_dt_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map, - unsigned num_maps) + unsigned int num_maps) { pinctrl_utils_free_map(pctldev, map, num_maps); } diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 96d853a8f339..dca963633b5d 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -55,7 +55,7 @@ int pinconf_validate_map(const struct pinctrl_map *map, int i) return 0; } -int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, +int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *config) { const struct pinconf_ops *ops = pctldev->desc->confops; @@ -199,7 +199,7 @@ int pinconf_apply_setting(const struct pinctrl_setting *setting) return 0; } -int pinconf_set_config(struct pinctrl_dev *pctldev, unsigned pin, +int pinconf_set_config(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *configs, size_t nconfigs) { const struct pinconf_ops *ops; @@ -214,7 +214,7 @@ int pinconf_set_config(struct pinctrl_dev *pctldev, unsigned pin, #ifdef CONFIG_DEBUG_FS static void pinconf_show_config(struct seq_file *s, struct pinctrl_dev *pctldev, - unsigned long *configs, unsigned num_configs) + unsigned long *configs, unsigned int num_configs) { const struct pinconf_ops *confops; int i; @@ -304,7 +304,7 @@ static void pinconf_dump_pin(struct pinctrl_dev *pctldev, static int pinconf_pins_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev = s->private; - unsigned i, pin; + unsigned int i, pin; seq_puts(s, "Pin config settings per pin\n"); seq_puts(s, "Format: pin (name): configs\n"); @@ -333,7 +333,7 @@ static int pinconf_pins_show(struct seq_file *s, void *what) } static void pinconf_dump_group(struct pinctrl_dev *pctldev, - struct seq_file *s, unsigned selector, + struct seq_file *s, unsigned int selector, const char *gname) { const struct pinconf_ops *ops = pctldev->desc->confops; @@ -348,8 +348,8 @@ static int pinconf_groups_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev = s->private; const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; - unsigned ngroups = pctlops->get_groups_count(pctldev); - unsigned selector = 0; + unsigned int ngroups = pctlops->get_groups_count(pctldev); + unsigned int selector = 0; seq_puts(s, "Pin config settings per pin group\n"); seq_puts(s, "Format: group (name): configs\n"); diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index 694bfc9961fa..a14c950bc700 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -29,14 +29,14 @@ int pinconf_map_to_setting(const struct pinctrl_map *map, void pinconf_free_setting(const struct pinctrl_setting *setting); int pinconf_apply_setting(const struct pinctrl_setting *setting); -int pinconf_set_config(struct pinctrl_dev *pctldev, unsigned pin, +int pinconf_set_config(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *configs, size_t nconfigs); /* * You will only be interested in these if you're using PINCONF * so don't supply any stubs for these. */ -int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, +int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *config); int pin_config_group_get(const char *dev_name, const char *pin_group, unsigned long *config); @@ -68,7 +68,7 @@ static inline int pinconf_apply_setting(const struct pinctrl_setting *setting) return 0; } -static inline int pinconf_set_config(struct pinctrl_dev *pctldev, unsigned pin, +static inline int pinconf_set_config(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *configs, size_t nconfigs) { return -ENOTSUPP; @@ -112,7 +112,7 @@ static inline void pinconf_init_device_debugfs(struct dentry *devroot, void pinconf_generic_dump_pins(struct pinctrl_dev *pctldev, struct seq_file *s, const char *gname, - unsigned pin); + unsigned int pin); void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned long config); @@ -120,7 +120,7 @@ void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, static inline void pinconf_generic_dump_pins(struct pinctrl_dev *pctldev, struct seq_file *s, - const char *gname, unsigned pin) + const char *gname, unsigned int pin) { return; } diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index 03ecb3d1aaf6..49f89b70dcec 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -1159,7 +1159,7 @@ static int amd_gpio_probe(struct platform_device *pdev) } ret = devm_request_irq(&pdev->dev, gpio_dev->irq, amd_gpio_irq_handler, - IRQF_SHARED, KBUILD_MODNAME, gpio_dev); + IRQF_SHARED | IRQF_ONESHOT, KBUILD_MODNAME, gpio_dev); if (ret) goto out2; diff --git a/drivers/pinctrl/pinctrl-as3722.c b/drivers/pinctrl/pinctrl-as3722.c index 6a5f23cf7a2a..0d8c75ce20ed 100644 --- a/drivers/pinctrl/pinctrl-as3722.c +++ b/drivers/pinctrl/pinctrl-as3722.c @@ -542,7 +542,6 @@ static int as3722_pinctrl_probe(struct platform_device *pdev) as_pci->dev = &pdev->dev; as_pci->as3722 = dev_get_drvdata(pdev->dev.parent); - platform_set_drvdata(pdev, as_pci); as_pci->pins = as3722_pins_desc; as_pci->num_pins = ARRAY_SIZE(as3722_pins_desc); @@ -562,7 +561,7 @@ static int as3722_pinctrl_probe(struct platform_device *pdev) as_pci->gpio_chip = as3722_gpio_chip; as_pci->gpio_chip.parent = &pdev->dev; - ret = gpiochip_add_data(&as_pci->gpio_chip, as_pci); + ret = devm_gpiochip_add_data(&pdev->dev, &as_pci->gpio_chip, as_pci); if (ret < 0) { dev_err(&pdev->dev, "Couldn't register gpiochip, %d\n", ret); return ret; @@ -572,21 +571,10 @@ static int as3722_pinctrl_probe(struct platform_device *pdev) 0, 0, AS3722_PIN_NUM); if (ret < 0) { dev_err(&pdev->dev, "Couldn't add pin range, %d\n", ret); - goto fail_range_add; + return ret; } return 0; - -fail_range_add: - gpiochip_remove(&as_pci->gpio_chip); - return ret; -} - -static void as3722_pinctrl_remove(struct platform_device *pdev) -{ - struct as3722_pctrl_info *as_pci = platform_get_drvdata(pdev); - - gpiochip_remove(&as_pci->gpio_chip); } static const struct of_device_id as3722_pinctrl_of_match[] = { @@ -601,7 +589,6 @@ static struct platform_driver as3722_pinctrl_driver = { .of_match_table = as3722_pinctrl_of_match, }, .probe = as3722_pinctrl_probe, - .remove_new = as3722_pinctrl_remove, }; module_platform_driver(as3722_pinctrl_driver); diff --git a/drivers/pinctrl/pinctrl-cy8c95x0.c b/drivers/pinctrl/pinctrl-cy8c95x0.c index fe9545c630a2..67b5d160c027 100644 --- a/drivers/pinctrl/pinctrl-cy8c95x0.c +++ b/drivers/pinctrl/pinctrl-cy8c95x0.c @@ -58,6 +58,10 @@ #define CY8C95X0_PIN_TO_OFFSET(x) (((x) >= 20) ? ((x) + 4) : (x)) +#define CY8C95X0_MUX_REGMAP_TO_PORT(x) ((x) / MUXED_STRIDE) +#define CY8C95X0_MUX_REGMAP_TO_REG(x) (((x) % MUXED_STRIDE) + CY8C95X0_INTMASK) +#define CY8C95X0_MUX_REGMAP_TO_OFFSET(x, p) ((x) - CY8C95X0_INTMASK + (p) * MUXED_STRIDE) + static const struct i2c_device_id cy8c95x0_id[] = { { "cy8c9520", 20, }, { "cy8c9540", 40, }, @@ -119,12 +123,13 @@ static const struct dmi_system_id cy8c95x0_dmi_acpi_irq_info[] = { #define MAX_BANK 8 #define BANK_SZ 8 #define MAX_LINE (MAX_BANK * BANK_SZ) - +#define MUXED_STRIDE 16 #define CY8C95X0_GPIO_MASK GENMASK(7, 0) /** * struct cy8c95x0_pinctrl - driver data - * @regmap: Device's regmap + * @regmap: Device's regmap. Only direct access registers. + * @muxed_regmap: Regmap for all muxed registers. * @irq_lock: IRQ bus lock * @i2c_lock: Mutex for the device internal mux register * @irq_mask: I/O bits affected by interrupts @@ -147,6 +152,7 @@ static const struct dmi_system_id cy8c95x0_dmi_acpi_irq_info[] = { */ struct cy8c95x0_pinctrl { struct regmap *regmap; + struct regmap *muxed_regmap; struct mutex irq_lock; struct mutex i2c_lock; DECLARE_BITMAP(irq_mask, MAX_LINE); @@ -379,6 +385,54 @@ static bool cy8c95x0_precious_register(struct device *dev, unsigned int reg) } } +static bool cy8c95x0_muxed_register(unsigned int reg) +{ + switch (reg) { + case CY8C95X0_INTMASK: + case CY8C95X0_PWMSEL: + case CY8C95X0_INVERT: + case CY8C95X0_DIRECTION: + case CY8C95X0_DRV_PU: + case CY8C95X0_DRV_PD: + case CY8C95X0_DRV_ODH: + case CY8C95X0_DRV_ODL: + case CY8C95X0_DRV_PP_FAST: + case CY8C95X0_DRV_PP_SLOW: + case CY8C95X0_DRV_HIZ: + return true; + default: + return false; + } +} + +static bool cy8c95x0_wc_register(unsigned int reg) +{ + switch (reg) { + case CY8C95X0_DRV_PU: + case CY8C95X0_DRV_PD: + case CY8C95X0_DRV_ODH: + case CY8C95X0_DRV_ODL: + case CY8C95X0_DRV_PP_FAST: + case CY8C95X0_DRV_PP_SLOW: + case CY8C95X0_DRV_HIZ: + return true; + default: + return false; + } +} + +static bool cy8c95x0_quick_path_register(unsigned int reg) +{ + switch (reg) { + case CY8C95X0_INPUT_(0) ... CY8C95X0_INPUT_(7): + case CY8C95X0_INTSTATUS_(0) ... CY8C95X0_INTSTATUS_(7): + case CY8C95X0_OUTPUT_(0) ... CY8C95X0_OUTPUT_(7): + return true; + default: + return false; + } +} + static const struct reg_default cy8c95x0_reg_defaults[] = { { CY8C95X0_OUTPUT_(0), GENMASK(7, 0) }, { CY8C95X0_OUTPUT_(1), GENMASK(7, 0) }, @@ -392,7 +446,89 @@ static const struct reg_default cy8c95x0_reg_defaults[] = { { CY8C95X0_PWMSEL, 0 }, }; +static int +cy8c95x0_mux_reg_read(void *context, unsigned int off, unsigned int *val) +{ + struct cy8c95x0_pinctrl *chip = context; + u8 port = CY8C95X0_MUX_REGMAP_TO_PORT(off); + int ret, reg = CY8C95X0_MUX_REGMAP_TO_REG(off); + + mutex_lock(&chip->i2c_lock); + /* Select the correct bank */ + ret = regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); + if (ret < 0) + goto out; + + /* + * Read the register through direct access regmap. The target range + * is marked volatile. + */ + ret = regmap_read(chip->regmap, reg, val); +out: + mutex_unlock(&chip->i2c_lock); + + return ret; +} + +static int +cy8c95x0_mux_reg_write(void *context, unsigned int off, unsigned int val) +{ + struct cy8c95x0_pinctrl *chip = context; + u8 port = CY8C95X0_MUX_REGMAP_TO_PORT(off); + int ret, reg = CY8C95X0_MUX_REGMAP_TO_REG(off); + + mutex_lock(&chip->i2c_lock); + /* Select the correct bank */ + ret = regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); + if (ret < 0) + goto out; + + /* + * Write the register through direct access regmap. The target range + * is marked volatile. + */ + ret = regmap_write(chip->regmap, reg, val); +out: + mutex_unlock(&chip->i2c_lock); + + return ret; +} + +static bool cy8c95x0_mux_accessible_register(struct device *dev, unsigned int off) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct cy8c95x0_pinctrl *chip = i2c_get_clientdata(i2c); + u8 port = CY8C95X0_MUX_REGMAP_TO_PORT(off); + u8 reg = CY8C95X0_MUX_REGMAP_TO_REG(off); + + if (port >= chip->nport) + return false; + + return cy8c95x0_muxed_register(reg); +} + +static struct regmap_bus cy8c95x0_regmap_bus = { + .reg_read = cy8c95x0_mux_reg_read, + .reg_write = cy8c95x0_mux_reg_write, +}; + +/* Regmap for muxed registers CY8C95X0_INTMASK - CY8C95X0_DRV_HIZ */ +static const struct regmap_config cy8c95x0_muxed_regmap = { + .name = "muxed", + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_FLAT, + .use_single_read = true, + .use_single_write = true, + .max_register = MUXED_STRIDE * BANK_SZ, + .num_reg_defaults_raw = MUXED_STRIDE * BANK_SZ, + .readable_reg = cy8c95x0_mux_accessible_register, + .writeable_reg = cy8c95x0_mux_accessible_register, +}; + +/* Direct access regmap */ static const struct regmap_config cy8c95x0_i2c_regmap = { + .name = "direct", .reg_bits = 8, .val_bits = 8, @@ -408,6 +544,147 @@ static const struct regmap_config cy8c95x0_i2c_regmap = { .max_register = CY8C95X0_COMMAND, }; +static inline int cy8c95x0_regmap_update_bits_base(struct cy8c95x0_pinctrl *chip, + unsigned int reg, + unsigned int port, + unsigned int mask, + unsigned int val, + bool *change, bool async, + bool force) +{ + struct regmap *regmap; + int ret, off, i, read_val; + + /* Caller should never modify PORTSEL directly */ + if (reg == CY8C95X0_PORTSEL) + return -EINVAL; + + /* Registers behind the PORTSEL mux have their own regmap */ + if (cy8c95x0_muxed_register(reg)) { + regmap = chip->muxed_regmap; + off = CY8C95X0_MUX_REGMAP_TO_OFFSET(reg, port); + } else { + regmap = chip->regmap; + /* Quick path direct access registers honor the port argument */ + if (cy8c95x0_quick_path_register(reg)) + off = reg + port; + else + off = reg; + } + + ret = regmap_update_bits_base(regmap, off, mask, val, change, async, force); + if (ret < 0) + return ret; + + /* Update the cache when a WC bit is written */ + if (cy8c95x0_wc_register(reg) && (mask & val)) { + for (i = CY8C95X0_DRV_PU; i <= CY8C95X0_DRV_HIZ; i++) { + if (i == reg) + continue; + off = CY8C95X0_MUX_REGMAP_TO_OFFSET(i, port); + + ret = regmap_read(regmap, off, &read_val); + if (ret < 0) + continue; + + if (!(read_val & mask & val)) + continue; + + regcache_cache_only(regmap, true); + regmap_update_bits(regmap, off, mask & val, 0); + regcache_cache_only(regmap, false); + } + } + + return ret; +} + +/** + * cy8c95x0_regmap_write_bits() - writes a register using the regmap cache + * @chip: The pinctrl to work on + * @reg: The register to write to. Can be direct access or muxed register. + * MUST NOT be the PORTSEL register. + * @port: The port to be used for muxed registers or quick path direct access + * registers. Otherwise unused. + * @mask: Bitmask to change + * @val: New value for bitmask + * + * This function handles the register writes to the direct access registers and + * the muxed registers while caching all register accesses, internally handling + * the correct state of the PORTSEL register and protecting the access to muxed + * registers. + * The caller must only use this function to change registers behind the PORTSEL mux. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int cy8c95x0_regmap_write_bits(struct cy8c95x0_pinctrl *chip, unsigned int reg, + unsigned int port, unsigned int mask, unsigned int val) +{ + return cy8c95x0_regmap_update_bits_base(chip, reg, port, mask, val, NULL, false, true); +} + +/** + * cy8c95x0_regmap_update_bits() - updates a register using the regmap cache + * @chip: The pinctrl to work on + * @reg: The register to write to. Can be direct access or muxed register. + * MUST NOT be the PORTSEL register. + * @port: The port to be used for muxed registers or quick path direct access + * registers. Otherwise unused. + * @mask: Bitmask to change + * @val: New value for bitmask + * + * This function handles the register updates to the direct access registers and + * the muxed registers while caching all register accesses, internally handling + * the correct state of the PORTSEL register and protecting the access to muxed + * registers. + * The caller must only use this function to change registers behind the PORTSEL mux. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int cy8c95x0_regmap_update_bits(struct cy8c95x0_pinctrl *chip, unsigned int reg, + unsigned int port, unsigned int mask, unsigned int val) +{ + return cy8c95x0_regmap_update_bits_base(chip, reg, port, mask, val, NULL, false, false); +} + +/** + * cy8c95x0_regmap_read() - reads a register using the regmap cache + * @chip: The pinctrl to work on + * @reg: The register to read from. Can be direct access or muxed register. + * @port: The port to be used for muxed registers or quick path direct access + * registers. Otherwise unused. + * @read_val: Value read from hardware or cache + * + * This function handles the register reads from the direct access registers and + * the muxed registers while caching all register accesses, internally handling + * the correct state of the PORTSEL register and protecting the access to muxed + * registers. + * The caller must only use this function to read registers behind the PORTSEL mux. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int cy8c95x0_regmap_read(struct cy8c95x0_pinctrl *chip, unsigned int reg, + unsigned int port, unsigned int *read_val) +{ + struct regmap *regmap; + int off; + + /* Registers behind the PORTSEL mux have their own regmap */ + if (cy8c95x0_muxed_register(reg)) { + regmap = chip->muxed_regmap; + off = CY8C95X0_MUX_REGMAP_TO_OFFSET(reg, port); + } else { + regmap = chip->regmap; + /* Quick path direct access registers honor the port argument */ + if (cy8c95x0_quick_path_register(reg)) + off = reg + port; + else + off = reg; + } + + return regmap_read(regmap, off, read_val); +} + static int cy8c95x0_write_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, unsigned long *val, unsigned long *mask) { @@ -415,7 +692,7 @@ static int cy8c95x0_write_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, DECLARE_BITMAP(tval, MAX_LINE); int write_val; int ret = 0; - int i, off = 0; + int i; u8 bits; /* Add the 4 bit gap of Gport2 */ @@ -427,53 +704,22 @@ static int cy8c95x0_write_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, bitmap_shift_left(tval, tval, 4, MAX_LINE); bitmap_replace(tval, tval, val, chip->shiftmask, BANK_SZ * 3); - mutex_lock(&chip->i2c_lock); for (i = 0; i < chip->nport; i++) { /* Skip over unused banks */ bits = bitmap_get_value8(tmask, i * BANK_SZ); if (!bits) continue; - switch (reg) { - /* Muxed registers */ - case CY8C95X0_INTMASK: - case CY8C95X0_PWMSEL: - case CY8C95X0_INVERT: - case CY8C95X0_DIRECTION: - case CY8C95X0_DRV_PU: - case CY8C95X0_DRV_PD: - case CY8C95X0_DRV_ODH: - case CY8C95X0_DRV_ODL: - case CY8C95X0_DRV_PP_FAST: - case CY8C95X0_DRV_PP_SLOW: - case CY8C95X0_DRV_HIZ: - ret = regmap_write(chip->regmap, CY8C95X0_PORTSEL, i); - if (ret < 0) - goto out; - off = reg; - break; - /* Direct access registers */ - case CY8C95X0_INPUT: - case CY8C95X0_OUTPUT: - case CY8C95X0_INTSTATUS: - off = reg + i; - break; - default: - ret = -EINVAL; - goto out; - } - write_val = bitmap_get_value8(tval, i * BANK_SZ); - ret = regmap_update_bits(chip->regmap, off, bits, write_val); + ret = cy8c95x0_regmap_update_bits(chip, reg, i, bits, write_val); if (ret < 0) goto out; } out: - mutex_unlock(&chip->i2c_lock); if (ret < 0) - dev_err(chip->dev, "failed writing register %d: err %d\n", off, ret); + dev_err(chip->dev, "failed writing register %d, port %d: err %d\n", reg, i, ret); return ret; } @@ -486,7 +732,7 @@ static int cy8c95x0_read_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, DECLARE_BITMAP(tmp, MAX_LINE); int read_val; int ret = 0; - int i, off = 0; + int i; u8 bits; /* Add the 4 bit gap of Gport2 */ @@ -498,43 +744,13 @@ static int cy8c95x0_read_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, bitmap_shift_left(tval, tval, 4, MAX_LINE); bitmap_replace(tval, tval, val, chip->shiftmask, BANK_SZ * 3); - mutex_lock(&chip->i2c_lock); for (i = 0; i < chip->nport; i++) { /* Skip over unused banks */ bits = bitmap_get_value8(tmask, i * BANK_SZ); if (!bits) continue; - switch (reg) { - /* Muxed registers */ - case CY8C95X0_INTMASK: - case CY8C95X0_PWMSEL: - case CY8C95X0_INVERT: - case CY8C95X0_DIRECTION: - case CY8C95X0_DRV_PU: - case CY8C95X0_DRV_PD: - case CY8C95X0_DRV_ODH: - case CY8C95X0_DRV_ODL: - case CY8C95X0_DRV_PP_FAST: - case CY8C95X0_DRV_PP_SLOW: - case CY8C95X0_DRV_HIZ: - ret = regmap_write(chip->regmap, CY8C95X0_PORTSEL, i); - if (ret < 0) - goto out; - off = reg; - break; - /* Direct access registers */ - case CY8C95X0_INPUT: - case CY8C95X0_OUTPUT: - case CY8C95X0_INTSTATUS: - off = reg + i; - break; - default: - ret = -EINVAL; - goto out; - } - - ret = regmap_read(chip->regmap, off, &read_val); + ret = cy8c95x0_regmap_read(chip, reg, i, &read_val); if (ret < 0) goto out; @@ -548,10 +764,8 @@ static int cy8c95x0_read_regs_mask(struct cy8c95x0_pinctrl *chip, int reg, bitmap_replace(val, tmp, tval, chip->shiftmask, MAX_LINE); out: - mutex_unlock(&chip->i2c_lock); - if (ret < 0) - dev_err(chip->dev, "failed reading register %d: err %d\n", off, ret); + dev_err(chip->dev, "failed reading register %d, port %d: err %d\n", reg, i, ret); return ret; } @@ -566,12 +780,11 @@ static int cy8c95x0_gpio_direction_output(struct gpio_chip *gc, { struct cy8c95x0_pinctrl *chip = gpiochip_get_data(gc); u8 port = cypress_get_port(chip, off); - u8 outreg = CY8C95X0_OUTPUT_(port); u8 bit = cypress_get_pin_mask(chip, off); int ret; /* Set output level */ - ret = regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); + ret = cy8c95x0_regmap_write_bits(chip, CY8C95X0_OUTPUT, port, bit, val ? bit : 0); if (ret) return ret; @@ -581,12 +794,12 @@ static int cy8c95x0_gpio_direction_output(struct gpio_chip *gc, static int cy8c95x0_gpio_get_value(struct gpio_chip *gc, unsigned int off) { struct cy8c95x0_pinctrl *chip = gpiochip_get_data(gc); - u8 inreg = CY8C95X0_INPUT_(cypress_get_port(chip, off)); + u8 port = cypress_get_port(chip, off); u8 bit = cypress_get_pin_mask(chip, off); u32 reg_val; int ret; - ret = regmap_read(chip->regmap, inreg, ®_val); + ret = cy8c95x0_regmap_read(chip, CY8C95X0_INPUT, port, ®_val); if (ret < 0) { /* * NOTE: @@ -604,10 +817,10 @@ static void cy8c95x0_gpio_set_value(struct gpio_chip *gc, unsigned int off, int val) { struct cy8c95x0_pinctrl *chip = gpiochip_get_data(gc); - u8 outreg = CY8C95X0_OUTPUT_(cypress_get_port(chip, off)); + u8 port = cypress_get_port(chip, off); u8 bit = cypress_get_pin_mask(chip, off); - regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); + cy8c95x0_regmap_write_bits(chip, CY8C95X0_OUTPUT, port, bit, val ? bit : 0); } static int cy8c95x0_gpio_get_direction(struct gpio_chip *gc, unsigned int off) @@ -618,24 +831,15 @@ static int cy8c95x0_gpio_get_direction(struct gpio_chip *gc, unsigned int off) u32 reg_val; int ret; - mutex_lock(&chip->i2c_lock); - - ret = regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); - if (ret < 0) - goto out; - - ret = regmap_read(chip->regmap, CY8C95X0_DIRECTION, ®_val); + ret = cy8c95x0_regmap_read(chip, CY8C95X0_DIRECTION, port, ®_val); if (ret < 0) goto out; - mutex_unlock(&chip->i2c_lock); - if (reg_val & bit) return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; out: - mutex_unlock(&chip->i2c_lock); return ret; } @@ -651,13 +855,6 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pinctrl *chip, u16 arg = 0; int ret; - mutex_lock(&chip->i2c_lock); - - /* Select port */ - ret = regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); - if (ret < 0) - goto out; - switch (param) { case PIN_CONFIG_BIAS_PULL_UP: reg = CY8C95X0_DRV_PU; @@ -684,7 +881,7 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pinctrl *chip, reg = CY8C95X0_PWMSEL; break; case PIN_CONFIG_OUTPUT: - reg = CY8C95X0_OUTPUT_(port); + reg = CY8C95X0_OUTPUT; break; case PIN_CONFIG_OUTPUT_ENABLE: reg = CY8C95X0_DIRECTION; @@ -712,7 +909,10 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pinctrl *chip, * Writing 1 to one of the drive mode registers will automatically * clear conflicting set bits in the other drive mode registers. */ - ret = regmap_read(chip->regmap, reg, ®_val); + ret = cy8c95x0_regmap_read(chip, reg, port, ®_val); + if (ret < 0) + goto out; + if (reg_val & bit) arg = 1; if (param == PIN_CONFIG_OUTPUT_ENABLE) @@ -720,8 +920,6 @@ static int cy8c95x0_gpio_get_pincfg(struct cy8c95x0_pinctrl *chip, *config = pinconf_to_config_packed(param, (u16)arg); out: - mutex_unlock(&chip->i2c_lock); - return ret; } @@ -736,13 +934,6 @@ static int cy8c95x0_gpio_set_pincfg(struct cy8c95x0_pinctrl *chip, unsigned int reg; int ret; - mutex_lock(&chip->i2c_lock); - - /* Select port */ - ret = regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); - if (ret < 0) - goto out; - switch (param) { case PIN_CONFIG_BIAS_PULL_UP: __clear_bit(off, chip->push_pull); @@ -785,10 +976,8 @@ static int cy8c95x0_gpio_set_pincfg(struct cy8c95x0_pinctrl *chip, * Writing 1 to one of the drive mode registers will automatically * clear conflicting set bits in the other drive mode registers. */ - ret = regmap_write_bits(chip->regmap, reg, bit, bit); - + ret = cy8c95x0_regmap_write_bits(chip, reg, port, bit, bit); out: - mutex_unlock(&chip->i2c_lock); return ret; } @@ -1105,14 +1294,8 @@ static int cy8c95x0_set_mode(struct cy8c95x0_pinctrl *chip, unsigned int off, bo { u8 port = cypress_get_port(chip, off); u8 bit = cypress_get_pin_mask(chip, off); - int ret; - /* Select port */ - ret = regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); - if (ret < 0) - return ret; - - return regmap_write_bits(chip->regmap, CY8C95X0_PWMSEL, bit, mode ? bit : 0); + return cy8c95x0_regmap_write_bits(chip, CY8C95X0_PWMSEL, port, bit, mode ? bit : 0); } static int cy8c95x0_pinmux_mode(struct cy8c95x0_pinctrl *chip, @@ -1130,24 +1313,19 @@ static int cy8c95x0_pinmux_mode(struct cy8c95x0_pinctrl *chip, return 0; /* Set direction to output & set output to 1 so that PWM can work */ - ret = regmap_write_bits(chip->regmap, CY8C95X0_DIRECTION, bit, bit); + ret = cy8c95x0_regmap_write_bits(chip, CY8C95X0_DIRECTION, port, bit, bit); if (ret < 0) return ret; - return regmap_write_bits(chip->regmap, CY8C95X0_OUTPUT_(port), bit, bit); + return cy8c95x0_regmap_write_bits(chip, CY8C95X0_OUTPUT, port, bit, bit); } static int cy8c95x0_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, unsigned int group) { struct cy8c95x0_pinctrl *chip = pinctrl_dev_get_drvdata(pctldev); - int ret; - mutex_lock(&chip->i2c_lock); - ret = cy8c95x0_pinmux_mode(chip, selector, group); - mutex_unlock(&chip->i2c_lock); - - return ret; + return cy8c95x0_pinmux_mode(chip, selector, group); } static int cy8c95x0_gpio_request_enable(struct pinctrl_dev *pctldev, @@ -1155,13 +1333,8 @@ static int cy8c95x0_gpio_request_enable(struct pinctrl_dev *pctldev, unsigned int pin) { struct cy8c95x0_pinctrl *chip = pinctrl_dev_get_drvdata(pctldev); - int ret; - - mutex_lock(&chip->i2c_lock); - ret = cy8c95x0_set_mode(chip, pin, false); - mutex_unlock(&chip->i2c_lock); - return ret; + return cy8c95x0_set_mode(chip, pin, false); } static int cy8c95x0_pinmux_direction(struct cy8c95x0_pinctrl *chip, @@ -1171,13 +1344,7 @@ static int cy8c95x0_pinmux_direction(struct cy8c95x0_pinctrl *chip, u8 bit = cypress_get_pin_mask(chip, pin); int ret; - /* Select port... */ - ret = regmap_write(chip->regmap, CY8C95X0_PORTSEL, port); - if (ret) - return ret; - - /* ...then direction */ - ret = regmap_write_bits(chip->regmap, CY8C95X0_DIRECTION, bit, input ? bit : 0); + ret = cy8c95x0_regmap_write_bits(chip, CY8C95X0_DIRECTION, port, bit, input ? bit : 0); if (ret) return ret; @@ -1186,7 +1353,7 @@ static int cy8c95x0_pinmux_direction(struct cy8c95x0_pinctrl *chip, * the direction register isn't sufficient in Push-Pull mode. */ if (input && test_bit(pin, chip->push_pull)) { - ret = regmap_write_bits(chip->regmap, CY8C95X0_DRV_HIZ, bit, bit); + ret = cy8c95x0_regmap_write_bits(chip, CY8C95X0_DRV_HIZ, port, bit, bit); if (ret) return ret; @@ -1201,13 +1368,8 @@ static int cy8c95x0_gpio_set_direction(struct pinctrl_dev *pctldev, unsigned int pin, bool input) { struct cy8c95x0_pinctrl *chip = pinctrl_dev_get_drvdata(pctldev); - int ret; - mutex_lock(&chip->i2c_lock); - ret = cy8c95x0_pinmux_direction(chip, pin, input); - mutex_unlock(&chip->i2c_lock); - - return ret; + return cy8c95x0_pinmux_direction(chip, pin, input); } static const struct pinmux_ops cy8c95x0_pmxops = { @@ -1409,12 +1571,22 @@ static int cy8c95x0_probe(struct i2c_client *client) gpiod_set_consumer_name(chip->gpio_reset, "CY8C95X0 RESET"); } + /* Generic regmap for direct access registers */ chip->regmap = devm_regmap_init_i2c(client, &cy8c95x0_i2c_regmap); if (IS_ERR(chip->regmap)) { ret = PTR_ERR(chip->regmap); goto err_exit; } + /* Port specific regmap behind PORTSEL mux */ + chip->muxed_regmap = devm_regmap_init(&client->dev, &cy8c95x0_regmap_bus, + chip, &cy8c95x0_muxed_regmap); + if (IS_ERR(chip->muxed_regmap)) { + ret = dev_err_probe(&client->dev, PTR_ERR(chip->muxed_regmap), + "Failed to register muxed regmap\n"); + goto err_exit; + } + bitmap_zero(chip->push_pull, MAX_LINE); bitmap_zero(chip->shiftmask, MAX_LINE); bitmap_set(chip->shiftmask, 0, 20); diff --git a/drivers/pinctrl/pinctrl-equilibrium.c b/drivers/pinctrl/pinctrl-equilibrium.c index 5b5ddf7e5d0e..6e1be38865c3 100644 --- a/drivers/pinctrl/pinctrl-equilibrium.c +++ b/drivers/pinctrl/pinctrl-equilibrium.c @@ -331,8 +331,8 @@ static int eqbr_pinmux_set_mux(struct pinctrl_dev *pctldev, return -EINVAL; pinmux = grp->data; - for (i = 0; i < grp->num_pins; i++) - eqbr_set_pin_mux(pctl, pinmux[i], grp->pins[i]); + for (i = 0; i < grp->grp.npins; i++) + eqbr_set_pin_mux(pctl, pinmux[i], grp->grp.pins[i]); return 0; } @@ -704,8 +704,8 @@ static int eqbr_build_groups(struct eqbr_pinctrl_drv_data *drvdata) { struct device *dev = drvdata->dev; struct device_node *node = dev->of_node; - unsigned int *pinmux, pin_id, pinmux_id; - struct group_desc group; + unsigned int *pins, *pinmux, pin_id, pinmux_id; + struct pingroup group, *grp = &group; struct device_node *np; struct property *prop; int j, err; @@ -715,55 +715,55 @@ static int eqbr_build_groups(struct eqbr_pinctrl_drv_data *drvdata) if (!prop) continue; - group.num_pins = of_property_count_u32_elems(np, "pins"); - if (group.num_pins < 0) { + err = of_property_count_u32_elems(np, "pins"); + if (err < 0) { dev_err(dev, "No pins in the group: %s\n", prop->name); of_node_put(np); - return -EINVAL; + return err; } - group.name = prop->value; - group.pins = devm_kcalloc(dev, group.num_pins, - sizeof(*(group.pins)), GFP_KERNEL); - if (!group.pins) { + grp->npins = err; + grp->name = prop->value; + pins = devm_kcalloc(dev, grp->npins, sizeof(*pins), GFP_KERNEL); + if (!pins) { of_node_put(np); return -ENOMEM; } + grp->pins = pins; - pinmux = devm_kcalloc(dev, group.num_pins, sizeof(*pinmux), - GFP_KERNEL); + pinmux = devm_kcalloc(dev, grp->npins, sizeof(*pinmux), GFP_KERNEL); if (!pinmux) { of_node_put(np); return -ENOMEM; } - for (j = 0; j < group.num_pins; j++) { + for (j = 0; j < grp->npins; j++) { if (of_property_read_u32_index(np, "pins", j, &pin_id)) { dev_err(dev, "Group %s: Read intel pins id failed\n", - group.name); + grp->name); of_node_put(np); return -EINVAL; } if (pin_id >= drvdata->pctl_desc.npins) { dev_err(dev, "Group %s: Invalid pin ID, idx: %d, pin %u\n", - group.name, j, pin_id); + grp->name, j, pin_id); of_node_put(np); return -EINVAL; } - group.pins[j] = pin_id; + pins[j] = pin_id; if (of_property_read_u32_index(np, "pinmux", j, &pinmux_id)) { dev_err(dev, "Group %s: Read intel pinmux id failed\n", - group.name); + grp->name); of_node_put(np); return -EINVAL; } pinmux[j] = pinmux_id; } - err = pinctrl_generic_add_group(drvdata->pctl_dev, group.name, - group.pins, group.num_pins, + err = pinctrl_generic_add_group(drvdata->pctl_dev, + grp->name, grp->pins, grp->npins, pinmux); if (err < 0) { - dev_err(dev, "Failed to register group %s\n", group.name); + dev_err(dev, "Failed to register group %s\n", grp->name); of_node_put(np); return err; } diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c index ee718f6e2556..bc6358a686fc 100644 --- a/drivers/pinctrl/pinctrl-ingenic.c +++ b/drivers/pinctrl/pinctrl-ingenic.c @@ -82,16 +82,17 @@ #define PINS_PER_GPIO_CHIP 32 #define JZ4730_PINS_PER_PAIRED_REG 16 -#define INGENIC_PIN_GROUP_FUNCS(name, id, funcs) \ - { \ - name, \ - id##_pins, \ - ARRAY_SIZE(id##_pins), \ - funcs, \ +#define INGENIC_PIN_GROUP_FUNCS(_name_, id, funcs) \ + { \ + .grp = PINCTRL_PINGROUP(_name_, id##_pins, ARRAY_SIZE(id##_pins)), \ + .data = funcs, \ } -#define INGENIC_PIN_GROUP(name, id, func) \ - INGENIC_PIN_GROUP_FUNCS(name, id, (void *)(func)) +#define INGENIC_PIN_GROUP(_name_, id, func) \ + { \ + .grp = PINCTRL_PINGROUP(_name_, id##_pins, ARRAY_SIZE(id##_pins)), \ + .data = (void *)func, \ + } enum jz_version { ID_JZ4730, @@ -3761,17 +3762,17 @@ static int ingenic_pinmux_set_mux(struct pinctrl_dev *pctldev, return -EINVAL; dev_dbg(pctldev->dev, "enable function %s group %s\n", - func->name, grp->name); + func->name, grp->grp.name); mode = (uintptr_t)grp->data; if (mode <= 3) { - for (i = 0; i < grp->num_pins; i++) - ingenic_pinmux_set_pin_fn(jzpc, grp->pins[i], mode); + for (i = 0; i < grp->grp.npins; i++) + ingenic_pinmux_set_pin_fn(jzpc, grp->grp.pins[i], mode); } else { pin_modes = grp->data; - for (i = 0; i < grp->num_pins; i++) - ingenic_pinmux_set_pin_fn(jzpc, grp->pins[i], pin_modes[i]); + for (i = 0; i < grp->grp.npins; i++) + ingenic_pinmux_set_pin_fn(jzpc, grp->grp.pins[i], pin_modes[i]); } return 0; @@ -4298,12 +4299,12 @@ static int __init ingenic_pinctrl_probe(struct platform_device *pdev) for (i = 0; i < chip_info->num_groups; i++) { const struct group_desc *group = &chip_info->groups[i]; + const struct pingroup *grp = &group->grp; - err = pinctrl_generic_add_group(jzpc->pctl, group->name, - group->pins, group->num_pins, group->data); + err = pinctrl_generic_add_group(jzpc->pctl, grp->name, grp->pins, grp->npins, + group->data); if (err < 0) { - dev_err(dev, "Failed to register group %s\n", - group->name); + dev_err(dev, "Failed to register group %s\n", grp->name); return err; } } diff --git a/drivers/pinctrl/pinctrl-keembay.c b/drivers/pinctrl/pinctrl-keembay.c index 152c35bce8ec..b1349ee22799 100644 --- a/drivers/pinctrl/pinctrl-keembay.c +++ b/drivers/pinctrl/pinctrl-keembay.c @@ -945,7 +945,7 @@ static int keembay_set_mux(struct pinctrl_dev *pctldev, unsigned int fun_sel, return -EINVAL; /* Change modes for pins in the selected group */ - pin = *grp->pins; + pin = *grp->grp.pins; pin_mode = *(u8 *)(func->data); val = keembay_read_reg(kpc->base1 + KEEMBAY_GPIO_MODE, pin); @@ -1517,7 +1517,7 @@ static int keembay_gpiochip_probe(struct keembay_pinctrl *kpc, static int keembay_build_groups(struct keembay_pinctrl *kpc) { - struct group_desc *grp; + struct pingroup *grp; unsigned int i; kpc->ngroups = kpc->npins; @@ -1528,7 +1528,7 @@ static int keembay_build_groups(struct keembay_pinctrl *kpc) /* Each pin is categorised as one group */ for (i = 0; i < kpc->ngroups; i++) { const struct pinctrl_pin_desc *pdesc = keembay_pins + i; - struct group_desc *kmb_grp = grp + i; + struct pingroup *kmb_grp = grp + i; kmb_grp->name = pdesc->name; kmb_grp->pins = (int *)&pdesc->number; diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 8267be769635..19cc0db771a5 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -1955,6 +1955,10 @@ static const struct pcs_soc_data pinctrl_single_am654 = { .irq_status_mask = (1 << 30), /* WKUP_EVT */ }; +static const struct pcs_soc_data pinctrl_single_j7200 = { + .flags = PCS_CONTEXT_LOSS_OFF, +}; + static const struct pcs_soc_data pinctrl_single = { }; @@ -1969,6 +1973,7 @@ static const struct of_device_id pcs_of_match[] = { { .compatible = "ti,omap3-padconf", .data = &pinctrl_single_omap_wkup }, { .compatible = "ti,omap4-padconf", .data = &pinctrl_single_omap_wkup }, { .compatible = "ti,omap5-padconf", .data = &pinctrl_single_omap_wkup }, + { .compatible = "ti,j7200-padconf", .data = &pinctrl_single_j7200 }, { .compatible = "pinctrl-single", .data = &pinctrl_single }, { .compatible = "pinconf-single", .data = &pinconf_single }, { }, diff --git a/drivers/pinctrl/pinctrl-tps6594.c b/drivers/pinctrl/pinctrl-tps6594.c new file mode 100644 index 000000000000..66985e54b74a --- /dev/null +++ b/drivers/pinctrl/pinctrl-tps6594.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Pinmux and GPIO driver for tps6594 PMIC + * + * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ + */ + +#include <linux/gpio/driver.h> +#include <linux/gpio/regmap.h> +#include <linux/module.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/platform_device.h> +#include <linux/mod_devicetable.h> + +#include <linux/mfd/tps6594.h> + +#define TPS6594_PINCTRL_PINS_NB 11 + +#define TPS6594_PINCTRL_GPIO_FUNCTION 0 +#define TPS6594_PINCTRL_SCL_I2C2_CS_SPI_FUNCTION 1 +#define TPS6594_PINCTRL_TRIG_WDOG_FUNCTION 1 +#define TPS6594_PINCTRL_CLK32KOUT_FUNCTION 1 +#define TPS6594_PINCTRL_SCLK_SPMI_FUNCTION 1 +#define TPS6594_PINCTRL_SDATA_SPMI_FUNCTION 1 +#define TPS6594_PINCTRL_NERR_MCU_FUNCTION 1 +#define TPS6594_PINCTRL_PDOG_FUNCTION 1 +#define TPS6594_PINCTRL_SYNCCLKIN_FUNCTION 1 +#define TPS6594_PINCTRL_NRSTOUT_SOC_FUNCTION 2 +#define TPS6594_PINCTRL_SYNCCLKOUT_FUNCTION 2 +#define TPS6594_PINCTRL_SDA_I2C2_SDO_SPI_FUNCTION 2 +#define TPS6594_PINCTRL_NERR_SOC_FUNCTION 2 +#define TPS6594_PINCTRL_DISABLE_WDOG_FUNCTION 3 +#define TPS6594_PINCTRL_NSLEEP1_FUNCTION 4 +#define TPS6594_PINCTRL_NSLEEP2_FUNCTION 5 +#define TPS6594_PINCTRL_WKUP1_FUNCTION 6 +#define TPS6594_PINCTRL_WKUP2_FUNCTION 7 + +/* Special muxval for recalcitrant pins */ +#define TPS6594_PINCTRL_DISABLE_WDOG_FUNCTION_GPIO8 2 +#define TPS6594_PINCTRL_SYNCCLKOUT_FUNCTION_GPIO8 3 +#define TPS6594_PINCTRL_CLK32KOUT_FUNCTION_GPIO9 3 + +#define TPS6594_OFFSET_GPIO_SEL 5 + +#define FUNCTION(fname, v) \ +{ \ + .pinfunction = PINCTRL_PINFUNCTION(#fname, \ + tps6594_##fname##_func_group_names, \ + ARRAY_SIZE(tps6594_##fname##_func_group_names)),\ + .muxval = v, \ +} + +static const struct pinctrl_pin_desc tps6594_pins[TPS6594_PINCTRL_PINS_NB] = { + PINCTRL_PIN(0, "GPIO0"), PINCTRL_PIN(1, "GPIO1"), + PINCTRL_PIN(2, "GPIO2"), PINCTRL_PIN(3, "GPIO3"), + PINCTRL_PIN(4, "GPIO4"), PINCTRL_PIN(5, "GPIO5"), + PINCTRL_PIN(6, "GPIO6"), PINCTRL_PIN(7, "GPIO7"), + PINCTRL_PIN(8, "GPIO8"), PINCTRL_PIN(9, "GPIO9"), + PINCTRL_PIN(10, "GPIO10"), +}; + +static const char *const tps6594_gpio_func_group_names[] = { + "GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", + "GPIO6", "GPIO7", "GPIO8", "GPIO9", "GPIO10", +}; + +static const char *const tps6594_nsleep1_func_group_names[] = { + "GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", + "GPIO6", "GPIO7", "GPIO8", "GPIO9", "GPIO10", +}; + +static const char *const tps6594_nsleep2_func_group_names[] = { + "GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", + "GPIO6", "GPIO7", "GPIO8", "GPIO9", "GPIO10", +}; + +static const char *const tps6594_wkup1_func_group_names[] = { + "GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", + "GPIO6", "GPIO7", "GPIO8", "GPIO9", "GPIO10", +}; + +static const char *const tps6594_wkup2_func_group_names[] = { + "GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", + "GPIO6", "GPIO7", "GPIO8", "GPIO9", "GPIO10", +}; + +static const char *const tps6594_scl_i2c2_cs_spi_func_group_names[] = { + "GPIO0", + "GPIO1", +}; + +static const char *const tps6594_nrstout_soc_func_group_names[] = { + "GPIO0", + "GPIO10", +}; + +static const char *const tps6594_trig_wdog_func_group_names[] = { + "GPIO1", + "GPIO10", +}; + +static const char *const tps6594_sda_i2c2_sdo_spi_func_group_names[] = { + "GPIO1", +}; + +static const char *const tps6594_clk32kout_func_group_names[] = { + "GPIO2", + "GPIO3", + "GPIO7", +}; + +static const char *const tps6594_nerr_soc_func_group_names[] = { + "GPIO2", +}; + +static const char *const tps6594_sclk_spmi_func_group_names[] = { + "GPIO4", +}; + +static const char *const tps6594_sdata_spmi_func_group_names[] = { + "GPIO5", +}; + +static const char *const tps6594_nerr_mcu_func_group_names[] = { + "GPIO6", +}; + +static const char *const tps6594_syncclkout_func_group_names[] = { + "GPIO7", + "GPIO9", +}; + +static const char *const tps6594_disable_wdog_func_group_names[] = { + "GPIO7", + "GPIO8", +}; + +static const char *const tps6594_pdog_func_group_names[] = { + "GPIO8", +}; + +static const char *const tps6594_syncclkin_func_group_names[] = { + "GPIO9", +}; + +struct tps6594_pinctrl_function { + struct pinfunction pinfunction; + u8 muxval; +}; + +static const struct tps6594_pinctrl_function pinctrl_functions[] = { + FUNCTION(gpio, TPS6594_PINCTRL_GPIO_FUNCTION), + FUNCTION(nsleep1, TPS6594_PINCTRL_NSLEEP1_FUNCTION), + FUNCTION(nsleep2, TPS6594_PINCTRL_NSLEEP2_FUNCTION), + FUNCTION(wkup1, TPS6594_PINCTRL_WKUP1_FUNCTION), + FUNCTION(wkup2, TPS6594_PINCTRL_WKUP2_FUNCTION), + FUNCTION(scl_i2c2_cs_spi, TPS6594_PINCTRL_SCL_I2C2_CS_SPI_FUNCTION), + FUNCTION(nrstout_soc, TPS6594_PINCTRL_NRSTOUT_SOC_FUNCTION), + FUNCTION(trig_wdog, TPS6594_PINCTRL_TRIG_WDOG_FUNCTION), + FUNCTION(sda_i2c2_sdo_spi, TPS6594_PINCTRL_SDA_I2C2_SDO_SPI_FUNCTION), + FUNCTION(clk32kout, TPS6594_PINCTRL_CLK32KOUT_FUNCTION), + FUNCTION(nerr_soc, TPS6594_PINCTRL_NERR_SOC_FUNCTION), + FUNCTION(sclk_spmi, TPS6594_PINCTRL_SCLK_SPMI_FUNCTION), + FUNCTION(sdata_spmi, TPS6594_PINCTRL_SDATA_SPMI_FUNCTION), + FUNCTION(nerr_mcu, TPS6594_PINCTRL_NERR_MCU_FUNCTION), + FUNCTION(syncclkout, TPS6594_PINCTRL_SYNCCLKOUT_FUNCTION), + FUNCTION(disable_wdog, TPS6594_PINCTRL_DISABLE_WDOG_FUNCTION), + FUNCTION(pdog, TPS6594_PINCTRL_PDOG_FUNCTION), + FUNCTION(syncclkin, TPS6594_PINCTRL_SYNCCLKIN_FUNCTION), +}; + +struct tps6594_pinctrl { + struct tps6594 *tps; + struct gpio_regmap *gpio_regmap; + struct pinctrl_dev *pctl_dev; + const struct tps6594_pinctrl_function *funcs; + const struct pinctrl_pin_desc *pins; +}; + +static int tps6594_gpio_regmap_xlate(struct gpio_regmap *gpio, + unsigned int base, unsigned int offset, + unsigned int *reg, unsigned int *mask) +{ + unsigned int line = offset % 8; + unsigned int stride = offset / 8; + + switch (base) { + case TPS6594_REG_GPIOX_CONF(0): + *reg = TPS6594_REG_GPIOX_CONF(offset); + *mask = TPS6594_BIT_GPIO_DIR; + return 0; + case TPS6594_REG_GPIO_IN_1: + case TPS6594_REG_GPIO_OUT_1: + *reg = base + stride; + *mask = BIT(line); + return 0; + default: + return -EINVAL; + } +} + +static int tps6594_pmx_func_cnt(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(pinctrl_functions); +} + +static const char *tps6594_pmx_func_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct tps6594_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + + return pinctrl->funcs[selector].pinfunction.name; +} + +static int tps6594_pmx_func_groups(struct pinctrl_dev *pctldev, + unsigned int selector, + const char *const **groups, + unsigned int *num_groups) +{ + struct tps6594_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + + *groups = pinctrl->funcs[selector].pinfunction.groups; + *num_groups = pinctrl->funcs[selector].pinfunction.ngroups; + + return 0; +} + +static int tps6594_pmx_set(struct tps6594_pinctrl *pinctrl, unsigned int pin, + u8 muxval) +{ + u8 mux_sel_val = muxval << TPS6594_OFFSET_GPIO_SEL; + + return regmap_update_bits(pinctrl->tps->regmap, + TPS6594_REG_GPIOX_CONF(pin), + TPS6594_MASK_GPIO_SEL, mux_sel_val); +} + +static int tps6594_pmx_set_mux(struct pinctrl_dev *pctldev, + unsigned int function, unsigned int group) +{ + struct tps6594_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + u8 muxval = pinctrl->funcs[function].muxval; + + /* Some pins don't have the same muxval for the same function... */ + if (group == 8) { + if (muxval == TPS6594_PINCTRL_DISABLE_WDOG_FUNCTION) + muxval = TPS6594_PINCTRL_DISABLE_WDOG_FUNCTION_GPIO8; + else if (muxval == TPS6594_PINCTRL_SYNCCLKOUT_FUNCTION) + muxval = TPS6594_PINCTRL_SYNCCLKOUT_FUNCTION_GPIO8; + } else if (group == 9) { + if (muxval == TPS6594_PINCTRL_CLK32KOUT_FUNCTION) + muxval = TPS6594_PINCTRL_CLK32KOUT_FUNCTION_GPIO9; + } + + return tps6594_pmx_set(pinctrl, group, muxval); +} + +static int tps6594_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset, bool input) +{ + struct tps6594_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + u8 muxval = pinctrl->funcs[TPS6594_PINCTRL_GPIO_FUNCTION].muxval; + + return tps6594_pmx_set(pinctrl, offset, muxval); +} + +static const struct pinmux_ops tps6594_pmx_ops = { + .get_functions_count = tps6594_pmx_func_cnt, + .get_function_name = tps6594_pmx_func_name, + .get_function_groups = tps6594_pmx_func_groups, + .set_mux = tps6594_pmx_set_mux, + .gpio_set_direction = tps6594_pmx_gpio_set_direction, + .strict = true, +}; + +static int tps6594_groups_cnt(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(tps6594_pins); +} + +static int tps6594_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, const unsigned int **pins, + unsigned int *num_pins) +{ + struct tps6594_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + + *pins = &pinctrl->pins[selector].number; + *num_pins = 1; + + return 0; +} + +static const char *tps6594_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct tps6594_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); + + return pinctrl->pins[selector].name; +} + +static const struct pinctrl_ops tps6594_pctrl_ops = { + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinconf_generic_dt_free_map, + .get_groups_count = tps6594_groups_cnt, + .get_group_name = tps6594_group_name, + .get_group_pins = tps6594_group_pins, +}; + +static int tps6594_pinctrl_probe(struct platform_device *pdev) +{ + struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct tps6594_pinctrl *pinctrl; + struct pinctrl_desc *pctrl_desc; + struct gpio_regmap_config config = {}; + + pctrl_desc = devm_kzalloc(dev, sizeof(*pctrl_desc), GFP_KERNEL); + if (!pctrl_desc) + return -ENOMEM; + pctrl_desc->name = dev_name(dev); + pctrl_desc->owner = THIS_MODULE; + pctrl_desc->pins = tps6594_pins; + pctrl_desc->npins = ARRAY_SIZE(tps6594_pins); + pctrl_desc->pctlops = &tps6594_pctrl_ops; + pctrl_desc->pmxops = &tps6594_pmx_ops; + + pinctrl = devm_kzalloc(dev, sizeof(*pinctrl), GFP_KERNEL); + if (!pinctrl) + return -ENOMEM; + pinctrl->tps = dev_get_drvdata(dev->parent); + pinctrl->funcs = pinctrl_functions; + pinctrl->pins = tps6594_pins; + pinctrl->pctl_dev = devm_pinctrl_register(dev, pctrl_desc, pinctrl); + if (IS_ERR(pinctrl->pctl_dev)) + return dev_err_probe(dev, PTR_ERR(pinctrl->pctl_dev), + "Couldn't register pinctrl driver\n"); + + config.parent = tps->dev; + config.regmap = tps->regmap; + config.ngpio = TPS6594_PINCTRL_PINS_NB; + config.ngpio_per_reg = 8; + config.reg_dat_base = TPS6594_REG_GPIO_IN_1; + config.reg_set_base = TPS6594_REG_GPIO_OUT_1; + config.reg_dir_out_base = TPS6594_REG_GPIOX_CONF(0); + config.reg_mask_xlate = tps6594_gpio_regmap_xlate; + + pinctrl->gpio_regmap = devm_gpio_regmap_register(dev, &config); + if (IS_ERR(pinctrl->gpio_regmap)) + return dev_err_probe(dev, PTR_ERR(pinctrl->gpio_regmap), + "Couldn't register gpio_regmap driver\n"); + + return 0; +} + +static const struct platform_device_id tps6594_pinctrl_id_table[] = { + { "tps6594-pinctrl", }, + {} +}; +MODULE_DEVICE_TABLE(platform, tps6594_pinctrl_id_table); + +static struct platform_driver tps6594_pinctrl_driver = { + .probe = tps6594_pinctrl_probe, + .driver = { + .name = "tps6594-pinctrl", + }, + .id_table = tps6594_pinctrl_id_table, +}; +module_platform_driver(tps6594_pinctrl_driver); + +MODULE_AUTHOR("Esteban Blanc <eblanc@baylibre.com>"); +MODULE_DESCRIPTION("TPS6594 pinctrl and GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/pinctrl-utils.c b/drivers/pinctrl/pinctrl-utils.c index 40862f7bd6ca..d81d7b46116c 100644 --- a/drivers/pinctrl/pinctrl-utils.c +++ b/drivers/pinctrl/pinctrl-utils.c @@ -18,11 +18,11 @@ #include "pinctrl-utils.h" int pinctrl_utils_reserve_map(struct pinctrl_dev *pctldev, - struct pinctrl_map **map, unsigned *reserved_maps, - unsigned *num_maps, unsigned reserve) + struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, unsigned int reserve) { - unsigned old_num = *reserved_maps; - unsigned new_num = *num_maps + reserve; + unsigned int old_num = *reserved_maps; + unsigned int new_num = *num_maps + reserve; struct pinctrl_map *new_map; if (old_num >= new_num) @@ -43,8 +43,8 @@ int pinctrl_utils_reserve_map(struct pinctrl_dev *pctldev, EXPORT_SYMBOL_GPL(pinctrl_utils_reserve_map); int pinctrl_utils_add_map_mux(struct pinctrl_dev *pctldev, - struct pinctrl_map **map, unsigned *reserved_maps, - unsigned *num_maps, const char *group, + struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, const char *group, const char *function) { if (WARN_ON(*num_maps == *reserved_maps)) @@ -60,9 +60,9 @@ int pinctrl_utils_add_map_mux(struct pinctrl_dev *pctldev, EXPORT_SYMBOL_GPL(pinctrl_utils_add_map_mux); int pinctrl_utils_add_map_configs(struct pinctrl_dev *pctldev, - struct pinctrl_map **map, unsigned *reserved_maps, - unsigned *num_maps, const char *group, - unsigned long *configs, unsigned num_configs, + struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, const char *group, + unsigned long *configs, unsigned int num_configs, enum pinctrl_map_type type) { unsigned long *dup_configs; @@ -86,11 +86,11 @@ int pinctrl_utils_add_map_configs(struct pinctrl_dev *pctldev, EXPORT_SYMBOL_GPL(pinctrl_utils_add_map_configs); int pinctrl_utils_add_config(struct pinctrl_dev *pctldev, - unsigned long **configs, unsigned *num_configs, + unsigned long **configs, unsigned int *num_configs, unsigned long config) { - unsigned old_num = *num_configs; - unsigned new_num = old_num + 1; + unsigned int old_num = *num_configs; + unsigned int new_num = old_num + 1; unsigned long *new_configs; new_configs = krealloc(*configs, sizeof(*new_configs) * new_num, @@ -110,7 +110,7 @@ int pinctrl_utils_add_config(struct pinctrl_dev *pctldev, EXPORT_SYMBOL_GPL(pinctrl_utils_add_config); void pinctrl_utils_free_map(struct pinctrl_dev *pctldev, - struct pinctrl_map *map, unsigned num_maps) + struct pinctrl_map *map, unsigned int num_maps) { int i; diff --git a/drivers/pinctrl/pinctrl-utils.h b/drivers/pinctrl/pinctrl-utils.h index 4108ee2dd6d0..203fba257d71 100644 --- a/drivers/pinctrl/pinctrl-utils.h +++ b/drivers/pinctrl/pinctrl-utils.h @@ -15,21 +15,21 @@ struct pinctrl_dev; struct pinctrl_map; int pinctrl_utils_reserve_map(struct pinctrl_dev *pctldev, - struct pinctrl_map **map, unsigned *reserved_maps, - unsigned *num_maps, unsigned reserve); + struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, unsigned int reserve); int pinctrl_utils_add_map_mux(struct pinctrl_dev *pctldev, - struct pinctrl_map **map, unsigned *reserved_maps, - unsigned *num_maps, const char *group, + struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, const char *group, const char *function); int pinctrl_utils_add_map_configs(struct pinctrl_dev *pctldev, - struct pinctrl_map **map, unsigned *reserved_maps, - unsigned *num_maps, const char *group, - unsigned long *configs, unsigned num_configs, + struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, const char *group, + unsigned long *configs, unsigned int num_configs, enum pinctrl_map_type type); int pinctrl_utils_add_config(struct pinctrl_dev *pctldev, - unsigned long **configs, unsigned *num_configs, + unsigned long **configs, unsigned int *num_configs, unsigned long config); void pinctrl_utils_free_map(struct pinctrl_dev *pctldev, - struct pinctrl_map *map, unsigned num_maps); + struct pinctrl_map *map, unsigned int num_maps); #endif /* __PINCTRL_UTILS_H__ */ diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 23d2da0b99b9..abbb044d6ace 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -35,8 +35,8 @@ int pinmux_check_ops(struct pinctrl_dev *pctldev) { const struct pinmux_ops *ops = pctldev->desc->pmxops; - unsigned nfuncs; - unsigned selector = 0; + unsigned int nfuncs; + unsigned int selector = 0; /* Check that we implement required operations */ if (!ops || @@ -84,7 +84,7 @@ int pinmux_validate_map(const struct pinctrl_map *map, int i) * Controllers not defined as strict will always return true, * menaning that the gpio can be used. */ -bool pinmux_can_be_used_for_gpio(struct pinctrl_dev *pctldev, unsigned pin) +bool pinmux_can_be_used_for_gpio(struct pinctrl_dev *pctldev, unsigned int pin) { struct pin_desc *desc = pin_desc_get(pctldev, pin); const struct pinmux_ops *ops = pctldev->desc->pmxops; @@ -262,7 +262,7 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin, */ int pinmux_request_gpio(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, - unsigned pin, unsigned gpio) + unsigned int pin, unsigned int gpio) { const char *owner; int ret; @@ -285,7 +285,7 @@ int pinmux_request_gpio(struct pinctrl_dev *pctldev, * @pin: the affected currently GPIO-muxed in pin * @range: applicable GPIO range */ -void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned pin, +void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned int pin, struct pinctrl_gpio_range *range) { const char *owner; @@ -303,7 +303,7 @@ void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned pin, */ int pinmux_gpio_direction(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, - unsigned pin, bool input) + unsigned int pin, bool input) { const struct pinmux_ops *ops; int ret; @@ -322,8 +322,8 @@ static int pinmux_func_name_to_selector(struct pinctrl_dev *pctldev, const char *function) { const struct pinmux_ops *ops = pctldev->desc->pmxops; - unsigned nfuncs = ops->get_functions_count(pctldev); - unsigned selector = 0; + unsigned int nfuncs = ops->get_functions_count(pctldev); + unsigned int selector = 0; /* See if this pctldev has this function */ while (selector < nfuncs) { @@ -344,7 +344,7 @@ int pinmux_map_to_setting(const struct pinctrl_map *map, struct pinctrl_dev *pctldev = setting->pctldev; const struct pinmux_ops *pmxops = pctldev->desc->pmxops; char const * const *groups; - unsigned num_groups; + unsigned int num_groups; int ret; const char *group; @@ -409,8 +409,8 @@ int pinmux_enable_setting(const struct pinctrl_setting *setting) const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; const struct pinmux_ops *ops = pctldev->desc->pmxops; int ret = 0; - const unsigned *pins = NULL; - unsigned num_pins = 0; + const unsigned int *pins = NULL; + unsigned int num_pins = 0; int i; struct pin_desc *desc; @@ -489,8 +489,8 @@ void pinmux_disable_setting(const struct pinctrl_setting *setting) struct pinctrl_dev *pctldev = setting->pctldev; const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; int ret = 0; - const unsigned *pins = NULL; - unsigned num_pins = 0; + const unsigned int *pins = NULL; + unsigned int num_pins = 0; int i; struct pin_desc *desc; @@ -541,8 +541,8 @@ static int pinmux_functions_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev = s->private; const struct pinmux_ops *pmxops = pctldev->desc->pmxops; - unsigned nfuncs; - unsigned func_selector = 0; + unsigned int nfuncs; + unsigned int func_selector = 0; if (!pmxops) return 0; @@ -553,7 +553,7 @@ static int pinmux_functions_show(struct seq_file *s, void *what) const char *func = pmxops->get_function_name(pctldev, func_selector); const char * const *groups; - unsigned num_groups; + unsigned int num_groups; int ret; int i; @@ -584,7 +584,7 @@ static int pinmux_pins_show(struct seq_file *s, void *what) struct pinctrl_dev *pctldev = s->private; const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; const struct pinmux_ops *pmxops = pctldev->desc->pmxops; - unsigned i, pin; + unsigned int i, pin; if (!pmxops) return 0; @@ -818,7 +818,7 @@ EXPORT_SYMBOL_GPL(pinmux_generic_get_function_name); int pinmux_generic_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector, const char * const **groups, - unsigned * const num_groups) + unsigned int * const num_groups) { struct function_desc *function; diff --git a/drivers/pinctrl/pinmux.h b/drivers/pinctrl/pinmux.h index ea6f99c24aa5..7c8aa25ccc80 100644 --- a/drivers/pinctrl/pinmux.h +++ b/drivers/pinctrl/pinmux.h @@ -26,16 +26,16 @@ int pinmux_check_ops(struct pinctrl_dev *pctldev); int pinmux_validate_map(const struct pinctrl_map *map, int i); -bool pinmux_can_be_used_for_gpio(struct pinctrl_dev *pctldev, unsigned pin); +bool pinmux_can_be_used_for_gpio(struct pinctrl_dev *pctldev, unsigned int pin); int pinmux_request_gpio(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, - unsigned pin, unsigned gpio); -void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned pin, + unsigned int pin, unsigned int gpio); +void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned int pin, struct pinctrl_gpio_range *range); int pinmux_gpio_direction(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, - unsigned pin, bool input); + unsigned int pin, bool input); int pinmux_map_to_setting(const struct pinctrl_map *map, struct pinctrl_setting *setting); @@ -56,27 +56,27 @@ static inline int pinmux_validate_map(const struct pinctrl_map *map, int i) } static inline bool pinmux_can_be_used_for_gpio(struct pinctrl_dev *pctldev, - unsigned pin) + unsigned int pin) { return true; } static inline int pinmux_request_gpio(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, - unsigned pin, unsigned gpio) + unsigned int pin, unsigned int gpio) { return 0; } static inline void pinmux_free_gpio(struct pinctrl_dev *pctldev, - unsigned pin, + unsigned int pin, struct pinctrl_gpio_range *range) { } static inline int pinmux_gpio_direction(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, - unsigned pin, bool input) + unsigned int pin, bool input) { return 0; } @@ -154,7 +154,7 @@ pinmux_generic_get_function_name(struct pinctrl_dev *pctldev, int pinmux_generic_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector, const char * const **groups, - unsigned * const num_groups); + unsigned int * const num_groups); struct function_desc *pinmux_generic_get_function(struct pinctrl_dev *pctldev, unsigned int selector); @@ -162,7 +162,7 @@ struct function_desc *pinmux_generic_get_function(struct pinctrl_dev *pctldev, int pinmux_generic_add_function(struct pinctrl_dev *pctldev, const char *name, const char * const *groups, - unsigned const num_groups, + unsigned int const num_groups, void *data); int pinmux_generic_remove_function(struct pinctrl_dev *pctldev, diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index f84c0d3b7951..e0f2829c15d6 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -124,4 +124,14 @@ config PINCTRL_SM8550_LPASS_LPI (Low Power Island) found on the Qualcomm Technologies Inc SM8550 platform. +config PINCTRL_SM8650_LPASS_LPI + tristate "Qualcomm Technologies Inc SM8550 LPASS LPI pin controller driver" + depends on ARM64 || COMPILE_TEST + depends on PINCTRL_LPASS_LPI + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc LPASS (Low Power Audio SubSystem) LPI + (Low Power Island) found on the Qualcomm Technologies Inc SM8650 + platform. + endif diff --git a/drivers/pinctrl/qcom/Kconfig.msm b/drivers/pinctrl/qcom/Kconfig.msm index 01dd7b134354..8fe459d082ed 100644 --- a/drivers/pinctrl/qcom/Kconfig.msm +++ b/drivers/pinctrl/qcom/Kconfig.msm @@ -286,6 +286,14 @@ config PINCTRL_SDX75 Qualcomm Technologies Inc TLMM block found on the Qualcomm Technologies Inc SDX75 platform. +config PINCTRL_SM4450 + tristate "Qualcomm Technologies Inc SM4450 pin controller driver" + depends on ARM64 || COMPILE_TEST + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc TLMM block found on the Qualcomm + Technologies Inc SM4450 platform. + config PINCTRL_SM6115 tristate "Qualcomm Technologies Inc SM6115,SM4250 pin controller driver" depends on ARM64 || COMPILE_TEST @@ -366,4 +374,22 @@ config PINCTRL_SM8550 Qualcomm Technologies Inc TLMM block found on the Qualcomm Technologies Inc SM8550 platform. +config PINCTRL_SM8650 + tristate "Qualcomm Technologies Inc SM8650 pin controller driver" + depends on ARM64 || COMPILE_TEST + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc TLMM block found on the Qualcomm + Technologies Inc SM8650 platform. + +config PINCTRL_X1E80100 + tristate "Qualcomm Technologies Inc X1E80100 pin controller driver" + depends on ARM64 || COMPILE_TEST + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc Top Level Mode Multiplexer block (TLMM) + block found on the Qualcomm Technologies Inc X1E80100 platform. + Say Y here to compile statically, or M here to compile it as a module. + If unsure, say N. + endif diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index 5910e08c84ce..e2e76071d268 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o obj-$(CONFIG_PINCTRL_SDX55) += pinctrl-sdx55.o obj-$(CONFIG_PINCTRL_SDX65) += pinctrl-sdx65.o obj-$(CONFIG_PINCTRL_SDX75) += pinctrl-sdx75.o +obj-$(CONFIG_PINCTRL_SM4450) += pinctrl-sm4450.o obj-$(CONFIG_PINCTRL_SM6115) += pinctrl-sm6115.o obj-$(CONFIG_PINCTRL_SM6115_LPASS_LPI) += pinctrl-sm6115-lpass-lpi.o obj-$(CONFIG_PINCTRL_SM6125) += pinctrl-sm6125.o @@ -58,5 +59,8 @@ obj-$(CONFIG_PINCTRL_SM8450) += pinctrl-sm8450.o obj-$(CONFIG_PINCTRL_SM8450_LPASS_LPI) += pinctrl-sm8450-lpass-lpi.o obj-$(CONFIG_PINCTRL_SM8550) += pinctrl-sm8550.o obj-$(CONFIG_PINCTRL_SM8550_LPASS_LPI) += pinctrl-sm8550-lpass-lpi.o +obj-$(CONFIG_PINCTRL_SM8650) += pinctrl-sm8650.o +obj-$(CONFIG_PINCTRL_SM8650_LPASS_LPI) += pinctrl-sm8650-lpass-lpi.o obj-$(CONFIG_PINCTRL_SC8280XP_LPASS_LPI) += pinctrl-sc8280xp-lpass-lpi.o obj-$(CONFIG_PINCTRL_LPASS_LPI) += pinctrl-lpass-lpi.o +obj-$(CONFIG_PINCTRL_X1E80100) += pinctrl-x1e80100.o diff --git a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c index 9651aed048cf..0d98008e33ee 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c @@ -186,6 +186,41 @@ static int lpi_config_get(struct pinctrl_dev *pctldev, return 0; } +static int lpi_config_set_slew_rate(struct lpi_pinctrl *pctrl, + const struct lpi_pingroup *g, + unsigned int group, unsigned int slew) +{ + unsigned long sval; + void __iomem *reg; + int slew_offset; + + if (slew > LPI_SLEW_RATE_MAX) { + dev_err(pctrl->dev, "invalid slew rate %u for pin: %d\n", + slew, group); + return -EINVAL; + } + + slew_offset = g->slew_offset; + if (slew_offset == LPI_NO_SLEW) + return 0; + + if (pctrl->data->flags & LPI_FLAG_SLEW_RATE_SAME_REG) + reg = pctrl->tlmm_base + LPI_TLMM_REG_OFFSET * group + LPI_GPIO_CFG_REG; + else + reg = pctrl->slew_base + LPI_SLEW_RATE_CTL_REG; + + mutex_lock(&pctrl->lock); + + sval = ioread32(reg); + sval &= ~(LPI_SLEW_RATE_MASK << slew_offset); + sval |= slew << slew_offset; + iowrite32(sval, reg); + + mutex_unlock(&pctrl->lock); + + return 0; +} + static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group, unsigned long *configs, unsigned int nconfs) { @@ -193,8 +228,7 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group, unsigned int param, arg, pullup = LPI_GPIO_BIAS_DISABLE, strength = 2; bool value, output_enabled = false; const struct lpi_pingroup *g; - unsigned long sval; - int i, slew_offset; + int i, ret; u32 val; g = &pctrl->data->groups[group]; @@ -226,24 +260,9 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group, strength = arg; break; case PIN_CONFIG_SLEW_RATE: - if (arg > LPI_SLEW_RATE_MAX) { - dev_err(pctldev->dev, "invalid slew rate %u for pin: %d\n", - arg, group); - return -EINVAL; - } - - slew_offset = g->slew_offset; - if (slew_offset == LPI_NO_SLEW) - break; - - mutex_lock(&pctrl->lock); - - sval = ioread32(pctrl->slew_base + LPI_SLEW_RATE_CTL_REG); - sval &= ~(LPI_SLEW_RATE_MASK << slew_offset); - sval |= arg << slew_offset; - iowrite32(sval, pctrl->slew_base + LPI_SLEW_RATE_CTL_REG); - - mutex_unlock(&pctrl->lock); + ret = lpi_config_set_slew_rate(pctrl, g, group, arg); + if (ret) + return ret; break; default: return -EINVAL; @@ -319,7 +338,6 @@ static void lpi_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) } #ifdef CONFIG_DEBUG_FS -#include <linux/seq_file.h> static unsigned int lpi_regval_to_drive(u32 val) { @@ -439,10 +457,12 @@ int lpi_pinctrl_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(pctrl->tlmm_base), "TLMM resource not provided\n"); - pctrl->slew_base = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(pctrl->slew_base)) - return dev_err_probe(dev, PTR_ERR(pctrl->slew_base), - "Slew resource not provided\n"); + if (!(data->flags & LPI_FLAG_SLEW_RATE_SAME_REG)) { + pctrl->slew_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(pctrl->slew_base)) + return dev_err_probe(dev, PTR_ERR(pctrl->slew_base), + "Slew resource not provided\n"); + } ret = devm_clk_bulk_get_optional(dev, MAX_LPI_NUM_CLKS, pctrl->clks); if (ret) diff --git a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.h b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.h index 387d83ee95b5..a9b2f65c1ebe 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.h +++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.h @@ -6,8 +6,8 @@ #ifndef __PINCTRL_LPASS_LPI_H__ #define __PINCTRL_LPASS_LPI_H__ +#include <linux/array_size.h> #include <linux/bits.h> -#include <linux/kernel.h> #include "../core.h" @@ -45,11 +45,8 @@ struct pinctrl_pin_desc; #define LPI_PINGROUP(id, soff, f1, f2, f3, f4) \ { \ - .group.name = "gpio" #id, \ - .group.pins = gpio##id##_pins, \ .pin = id, \ .slew_offset = soff, \ - .group.num_pins = ARRAY_SIZE(gpio##id##_pins), \ .funcs = (int[]){ \ LPI_MUX_gpio, \ LPI_MUX_##f1, \ @@ -60,8 +57,13 @@ struct pinctrl_pin_desc; .nfuncs = 5, \ } +/* + * Slew rate control is done in the same register as rest of the + * pin configuration. + */ +#define LPI_FLAG_SLEW_RATE_SAME_REG BIT(0) + struct lpi_pingroup { - struct group_desc group; unsigned int pin; /* Bit offset in slew register for SoundWire pins only */ int slew_offset; @@ -82,6 +84,7 @@ struct lpi_pinctrl_variant_data { int ngroups; const struct lpi_function *functions; int nfunctions; + unsigned int flags; }; int lpi_pinctrl_probe(struct platform_device *pdev); diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 395040346d0f..aeaf0d1958f5 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -358,6 +358,10 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev, int ret; u32 val; + /* Pin information can only be requested from valid pin groups */ + if (!gpiochip_line_is_valid(&pctrl->chip, group)) + return -EINVAL; + g = &pctrl->soc->groups[group]; ret = msm_config_reg(pctrl, g, param, &mask, &bit); @@ -1196,6 +1200,8 @@ static int msm_gpio_irq_reqres(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + const struct msm_pingroup *g = &pctrl->soc->groups[d->hwirq]; + unsigned long flags; int ret; if (!try_module_get(gc->owner)) @@ -1221,6 +1227,28 @@ static int msm_gpio_irq_reqres(struct irq_data *d) */ irq_set_status_flags(d->irq, IRQ_DISABLE_UNLAZY); + /* + * If the wakeup_enable bit is present and marked as available for the + * requested GPIO, it should be enabled when the GPIO is marked as + * wake irq in order to allow the interrupt event to be transfered to + * the PDC HW. + * While the name implies only the wakeup event, it's also required for + * the interrupt event. + */ + if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) { + u32 intr_cfg; + + raw_spin_lock_irqsave(&pctrl->lock, flags); + + intr_cfg = msm_readl_intr_cfg(pctrl, g); + if (intr_cfg & BIT(g->intr_wakeup_present_bit)) { + intr_cfg |= BIT(g->intr_wakeup_enable_bit); + msm_writel_intr_cfg(intr_cfg, pctrl, g); + } + + raw_spin_unlock_irqrestore(&pctrl->lock, flags); + } + return 0; out: module_put(gc->owner); @@ -1230,6 +1258,24 @@ out: static void msm_gpio_irq_relres(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + const struct msm_pingroup *g = &pctrl->soc->groups[d->hwirq]; + unsigned long flags; + + /* Disable the wakeup_enable bit if it has been set in msm_gpio_irq_reqres() */ + if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) { + u32 intr_cfg; + + raw_spin_lock_irqsave(&pctrl->lock, flags); + + intr_cfg = msm_readl_intr_cfg(pctrl, g); + if (intr_cfg & BIT(g->intr_wakeup_present_bit)) { + intr_cfg &= ~BIT(g->intr_wakeup_enable_bit); + msm_writel_intr_cfg(intr_cfg, pctrl, g); + } + + raw_spin_unlock_irqrestore(&pctrl->lock, flags); + } gpiochip_unlock_as_irq(gc, d->hwirq); module_put(gc->owner); diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h index 4968d08a384d..63852ed70295 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.h +++ b/drivers/pinctrl/qcom/pinctrl-msm.h @@ -58,6 +58,9 @@ struct pinctrl_pin_desc; * @intr_enable_bit: Offset in @intr_cfg_reg for enabling the interrupt for this group. * @intr_status_bit: Offset in @intr_status_reg for reading and acking the interrupt * status. + * @intr_wakeup_present_bit: Offset in @intr_target_reg specifying the GPIO can generate + * wakeup events. + * @intr_wakeup_enable_bit: Offset in @intr_target_reg to enable wakeup events for the GPIO. * @intr_target_bit: Offset in @intr_target_reg for configuring the interrupt routing. * @intr_target_width: Number of bits used for specifying interrupt routing target. * @intr_target_kpss_val: Value in @intr_target_bit for specifying that the interrupt from @@ -100,6 +103,8 @@ struct msm_pingroup { unsigned intr_status_bit:5; unsigned intr_ack_high:1; + unsigned intr_wakeup_present_bit:5; + unsigned intr_wakeup_enable_bit:5; unsigned intr_target_bit:5; unsigned intr_target_width:5; unsigned intr_target_kpss_val:5; diff --git a/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c index 99156217c6a5..6bb39812e1d8 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sc7280-lpass-lpi.c @@ -36,22 +36,6 @@ enum lpass_lpi_functions { LPI_MUX__, }; -static int gpio0_pins[] = { 0 }; -static int gpio1_pins[] = { 1 }; -static int gpio2_pins[] = { 2 }; -static int gpio3_pins[] = { 3 }; -static int gpio4_pins[] = { 4 }; -static int gpio5_pins[] = { 5 }; -static int gpio6_pins[] = { 6 }; -static int gpio7_pins[] = { 7 }; -static int gpio8_pins[] = { 8 }; -static int gpio9_pins[] = { 9 }; -static int gpio10_pins[] = { 10 }; -static int gpio11_pins[] = { 11 }; -static int gpio12_pins[] = { 12 }; -static int gpio13_pins[] = { 13 }; -static int gpio14_pins[] = { 14 }; - static const struct pinctrl_pin_desc sc7280_lpi_pins[] = { PINCTRL_PIN(0, "gpio0"), PINCTRL_PIN(1, "gpio1"), diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c index b33483056f42..c0369baf3398 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp-lpass-lpi.c @@ -45,26 +45,6 @@ enum lpass_lpi_functions { LPI_MUX__, }; -static int gpio0_pins[] = { 0 }; -static int gpio1_pins[] = { 1 }; -static int gpio2_pins[] = { 2 }; -static int gpio3_pins[] = { 3 }; -static int gpio4_pins[] = { 4 }; -static int gpio5_pins[] = { 5 }; -static int gpio6_pins[] = { 6 }; -static int gpio7_pins[] = { 7 }; -static int gpio8_pins[] = { 8 }; -static int gpio9_pins[] = { 9 }; -static int gpio10_pins[] = { 10 }; -static int gpio11_pins[] = { 11 }; -static int gpio12_pins[] = { 12 }; -static int gpio13_pins[] = { 13 }; -static int gpio14_pins[] = { 14 }; -static int gpio15_pins[] = { 15 }; -static int gpio16_pins[] = { 16 }; -static int gpio17_pins[] = { 17 }; -static int gpio18_pins[] = { 18 }; - static const struct pinctrl_pin_desc sc8280xp_lpi_pins[] = { PINCTRL_PIN(0, "gpio0"), PINCTRL_PIN(1, "gpio1"), diff --git a/drivers/pinctrl/qcom/pinctrl-sm4450.c b/drivers/pinctrl/qcom/pinctrl-sm4450.c new file mode 100644 index 000000000000..27317b86d835 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sm4450.c @@ -0,0 +1,1014 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 + +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9 \ + }, \ + .nfuncs = 10, \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .intr_target_reg = 0x8 + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .egpio_enable = 12, \ + .egpio_present = 11, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_target_bit = 5, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +#define UFS_RESET(pg_name, offset) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = offset, \ + .io_reg = offset + 0x4, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +#define QUP_I3C(qup_mode, qup_offset) \ + { \ + .mode = qup_mode, \ + .offset = qup_offset, \ + } + + +static const struct pinctrl_pin_desc sm4450_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), + PINCTRL_PIN(128, "GPIO_128"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "GPIO_133"), + PINCTRL_PIN(134, "GPIO_134"), + PINCTRL_PIN(135, "GPIO_135"), + PINCTRL_PIN(136, "UFS_RESET"), + PINCTRL_PIN(137, "SDC1_RCLK"), + PINCTRL_PIN(138, "SDC1_CLK"), + PINCTRL_PIN(139, "SDC1_CMD"), + PINCTRL_PIN(140, "SDC1_DATA"), + PINCTRL_PIN(141, "SDC2_CLK"), + PINCTRL_PIN(142, "SDC2_CMD"), + PINCTRL_PIN(143, "SDC2_DATA"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); +DECLARE_MSM_GPIO_PINS(123); +DECLARE_MSM_GPIO_PINS(124); +DECLARE_MSM_GPIO_PINS(125); +DECLARE_MSM_GPIO_PINS(126); +DECLARE_MSM_GPIO_PINS(127); +DECLARE_MSM_GPIO_PINS(128); +DECLARE_MSM_GPIO_PINS(129); +DECLARE_MSM_GPIO_PINS(130); +DECLARE_MSM_GPIO_PINS(131); +DECLARE_MSM_GPIO_PINS(132); +DECLARE_MSM_GPIO_PINS(133); +DECLARE_MSM_GPIO_PINS(134); +DECLARE_MSM_GPIO_PINS(135); + +static const unsigned int ufs_reset_pins[] = { 136 }; +static const unsigned int sdc1_rclk_pins[] = { 137 }; +static const unsigned int sdc1_clk_pins[] = { 138 }; +static const unsigned int sdc1_cmd_pins[] = { 139 }; +static const unsigned int sdc1_data_pins[] = { 140 }; +static const unsigned int sdc2_clk_pins[] = { 141 }; +static const unsigned int sdc2_cmd_pins[] = { 142 }; +static const unsigned int sdc2_data_pins[] = { 143 }; + +enum sm4450_functions { + msm_mux_gpio, + msm_mux_atest_char, + msm_mux_atest_usb0, + msm_mux_audio_ref_clk, + msm_mux_cam_mclk, + msm_mux_cci_async_in0, + msm_mux_cci_i2c, + msm_mux_cci, + msm_mux_cmu_rng, + msm_mux_coex_uart1_rx, + msm_mux_coex_uart1_tx, + msm_mux_cri_trng, + msm_mux_dbg_out_clk, + msm_mux_ddr_bist, + msm_mux_ddr_pxi0_test, + msm_mux_ddr_pxi1_test, + msm_mux_gcc_gp1_clk, + msm_mux_gcc_gp2_clk, + msm_mux_gcc_gp3_clk, + msm_mux_host2wlan_sol, + msm_mux_ibi_i3c_qup0, + msm_mux_ibi_i3c_qup1, + msm_mux_jitter_bist_ref, + msm_mux_mdp_vsync0_out, + msm_mux_mdp_vsync1_out, + msm_mux_mdp_vsync2_out, + msm_mux_mdp_vsync3_out, + msm_mux_mdp_vsync, + msm_mux_nav, + msm_mux_pcie0_clk_req, + msm_mux_phase_flag, + msm_mux_pll_bist_sync, + msm_mux_pll_clk_aux, + msm_mux_prng_rosc, + msm_mux_qdss_cti_trig0, + msm_mux_qdss_cti_trig1, + msm_mux_qdss_gpio, + msm_mux_qlink0_enable, + msm_mux_qlink0_request, + msm_mux_qlink0_wmss_reset, + msm_mux_qup0_se0, + msm_mux_qup0_se1, + msm_mux_qup0_se2, + msm_mux_qup0_se3, + msm_mux_qup0_se4, + msm_mux_qup1_se0, + msm_mux_qup1_se1, + msm_mux_qup1_se2, + msm_mux_qup1_se3, + msm_mux_qup1_se4, + msm_mux_sd_write_protect, + msm_mux_tb_trig_sdc1, + msm_mux_tb_trig_sdc2, + msm_mux_tgu_ch0_trigout, + msm_mux_tgu_ch1_trigout, + msm_mux_tgu_ch2_trigout, + msm_mux_tgu_ch3_trigout, + msm_mux_tmess_prng, + msm_mux_tsense_pwm1_out, + msm_mux_tsense_pwm2_out, + msm_mux_uim0, + msm_mux_uim1, + msm_mux_usb0_hs_ac, + msm_mux_usb0_phy_ps, + msm_mux_vfr_0_mira, + msm_mux_vfr_0_mirb, + msm_mux_vfr_1, + msm_mux_vsense_trigger_mirnat, + msm_mux_wlan1_adc_dtest0, + msm_mux_wlan1_adc_dtest1, + msm_mux__, +}; + +static const char * const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", + "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49", + "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56", + "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63", + "gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70", + "gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77", + "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84", + "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91", + "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98", + "gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104", + "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110", + "gpio111", "gpio112", "gpio113", "gpio114", "gpio115", "gpio116", + "gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122", + "gpio123", "gpio124", "gpio125", "gpio126", "gpio127", "gpio128", + "gpio129", "gpio130", "gpio131", "gpio132", "gpio133", "gpio134", + "gpio135", +}; +static const char * const atest_char_groups[] = { + "gpio95", "gpio97", "gpio98", "gpio99", "gpio100", +}; +static const char * const atest_usb0_groups[] = { + "gpio75", "gpio10", "gpio78", "gpio79", "gpio80", +}; +static const char * const audio_ref_clk_groups[] = { + "gpio71", +}; +static const char * const cam_mclk_groups[] = { + "gpio36", "gpio37", "gpio38", "gpio39", +}; +static const char * const cci_async_in0_groups[] = { + "gpio40", +}; +static const char * const cci_i2c_groups[] = { + "gpio45", "gpio47", "gpio49", "gpio44", + "gpio46", "gpio48", +}; +static const char * const cci_groups[] = { + "gpio40", "gpio41", "gpio42", "gpio43", +}; +static const char * const cmu_rng_groups[] = { + "gpio28", "gpio3", "gpio1", "gpio0", +}; +static const char * const coex_uart1_rx_groups[] = { + "gpio54", +}; +static const char * const coex_uart1_tx_groups[] = { + "gpio55", +}; +static const char * const cri_trng_groups[] = { + "gpio42", "gpio40", "gpio41", +}; +static const char * const dbg_out_clk_groups[] = { + "gpio80", +}; +static const char * const ddr_bist_groups[] = { + "gpio32", "gpio29", "gpio30", "gpio31", +}; +static const char * const ddr_pxi0_test_groups[] = { + "gpio90", "gpio127", +}; +static const char * const ddr_pxi1_test_groups[] = { + "gpio118", "gpio122", +}; +static const char * const gcc_gp1_clk_groups[] = { + "gpio37", "gpio48", +}; +static const char * const gcc_gp2_clk_groups[] = { + "gpio30", "gpio49", +}; +static const char * const gcc_gp3_clk_groups[] = { + "gpio3", "gpio50", +}; +static const char * const host2wlan_sol_groups[] = { + "gpio106", +}; +static const char * const ibi_i3c_qup0_groups[] = { + "gpio4", "gpio5", +}; +static const char * const ibi_i3c_qup1_groups[] = { + "gpio0", "gpio1", +}; +static const char * const jitter_bist_ref_groups[] = { + "gpio90", +}; +static const char * const mdp_vsync0_out_groups[] = { + "gpio93", +}; +static const char * const mdp_vsync1_out_groups[] = { + "gpio93", +}; +static const char * const mdp_vsync2_out_groups[] = { + "gpio22", +}; +static const char * const mdp_vsync3_out_groups[] = { + "gpio22", +}; +static const char * const mdp_vsync_groups[] = { + "gpio26", "gpio22", "gpio30", "gpio34", "gpio93", "gpio97", +}; +static const char * const nav_groups[] = { + "gpio81", "gpio83", "gpio84", +}; +static const char * const pcie0_clk_req_groups[] = { + "gpio107", +}; +static const char * const phase_flag_groups[] = { + "gpio7", "gpio8", "gpio9", "gpio11", "gpio13", "gpio14", "gpio15", + "gpio17", "gpio18", "gpio19", "gpio21", "gpio24", "gpio25", "gpio31", + "gpio32", "gpio33", "gpio35", "gpio61", "gpio72", "gpio82", "gpio91", + "gpio95", "gpio97", "gpio98", "gpio99", "gpio100", "gpio105", "gpio115", + "gpio116", "gpio117", "gpio133", "gpio135", +}; +static const char * const pll_bist_sync_groups[] = { + "gpio73", +}; +static const char * const pll_clk_aux_groups[] = { + "gpio108", +}; +static const char * const prng_rosc_groups[] = { + "gpio36", "gpio37", "gpio38", "gpio39", +}; +static const char * const qdss_cti_trig0_groups[] = { + "gpio26", "gpio60", "gpio113", "gpio114", +}; +static const char * const qdss_cti_trig1_groups[] = { + "gpio6", "gpio27", "gpio57", "gpio58", +}; +static const char * const qdss_gpio_groups[] = { + "gpio0", "gpio1", "gpio3", "gpio4", "gpio5", "gpio7", "gpio8", + "gpio9", "gpio14", "gpio15", "gpio17", "gpio23", "gpio31", "gpio32", + "gpio33", "gpio35", "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", + "gpio41", "gpio42", "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", + "gpio49", "gpio59", "gpio62", "gpio118", "gpio121", "gpio122", "gpio126", + "gpio127", +}; +static const char * const qlink0_enable_groups[] = { + "gpio88", +}; +static const char * const qlink0_request_groups[] = { + "gpio87", +}; +static const char * const qlink0_wmss_reset_groups[] = { + "gpio89", +}; +static const char * const qup0_se0_groups[] = { + "gpio4", "gpio5", "gpio34", "gpio35", +}; +static const char * const qup0_se1_groups[] = { + "gpio10", "gpio11", "gpio12", "gpio13", +}; +static const char * const qup0_se2_groups[] = { + "gpio14", "gpio15", "gpio16", "gpio17", +}; +static const char * const qup0_se3_groups[] = { + "gpio18", "gpio19", "gpio20", "gpio21", +}; +static const char * const qup0_se4_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio9", + "gpio26", "gpio27", "gpio34", +}; +static const char * const qup1_se0_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; +static const char * const qup1_se1_groups[] = { + "gpio26", "gpio27", "gpio50", "gpio51", +}; +static const char * const qup1_se2_groups[] = { + "gpio22", "gpio23", "gpio31", "gpio32", +}; +static const char * const qup1_se3_groups[] = { + "gpio24", "gpio25", "gpio51", "gpio50", +}; +static const char * const qup1_se4_groups[] = { + "gpio43", "gpio48", "gpio49", "gpio90", + "gpio91", +}; +static const char * const sd_write_protect_groups[] = { + "gpio102", +}; +static const char * const tb_trig_sdc1_groups[] = { + "gpio128", +}; +static const char * const tb_trig_sdc2_groups[] = { + "gpio51", +}; +static const char * const tgu_ch0_trigout_groups[] = { + "gpio20", +}; +static const char * const tgu_ch1_trigout_groups[] = { + "gpio21", +}; +static const char * const tgu_ch2_trigout_groups[] = { + "gpio22", +}; +static const char * const tgu_ch3_trigout_groups[] = { + "gpio23", +}; +static const char * const tmess_prng_groups[] = { + "gpio57", "gpio58", "gpio59", "gpio60", +}; +static const char * const tsense_pwm1_out_groups[] = { + "gpio134", +}; +static const char * const tsense_pwm2_out_groups[] = { + "gpio134", +}; +static const char * const uim0_groups[] = { + "gpio64", "gpio63", "gpio66", "gpio65", +}; +static const char * const uim1_groups[] = { + "gpio68", "gpio67", "gpio69", "gpio70", +}; +static const char * const usb0_hs_ac_groups[] = { + "gpio99", +}; +static const char * const usb0_phy_ps_groups[] = { + "gpio94", +}; +static const char * const vfr_0_mira_groups[] = { + "gpio19", +}; +static const char * const vfr_0_mirb_groups[] = { + "gpio100", +}; +static const char * const vfr_1_groups[] = { + "gpio84", +}; +static const char * const vsense_trigger_mirnat_groups[] = { + "gpio75", +}; +static const char * const wlan1_adc_dtest0_groups[] = { + "gpio79", +}; +static const char * const wlan1_adc_dtest1_groups[] = { + "gpio80", +}; + +static const struct pinfunction sm4450_functions[] = { + MSM_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_usb0), + MSM_PIN_FUNCTION(audio_ref_clk), + MSM_PIN_FUNCTION(cam_mclk), + MSM_PIN_FUNCTION(cci_async_in0), + MSM_PIN_FUNCTION(cci_i2c), + MSM_PIN_FUNCTION(cci), + MSM_PIN_FUNCTION(cmu_rng), + MSM_PIN_FUNCTION(coex_uart1_rx), + MSM_PIN_FUNCTION(coex_uart1_tx), + MSM_PIN_FUNCTION(cri_trng), + MSM_PIN_FUNCTION(dbg_out_clk), + MSM_PIN_FUNCTION(ddr_bist), + MSM_PIN_FUNCTION(ddr_pxi0_test), + MSM_PIN_FUNCTION(ddr_pxi1_test), + MSM_PIN_FUNCTION(gcc_gp1_clk), + MSM_PIN_FUNCTION(gcc_gp2_clk), + MSM_PIN_FUNCTION(gcc_gp3_clk), + MSM_PIN_FUNCTION(host2wlan_sol), + MSM_PIN_FUNCTION(ibi_i3c_qup0), + MSM_PIN_FUNCTION(ibi_i3c_qup1), + MSM_PIN_FUNCTION(jitter_bist_ref), + MSM_PIN_FUNCTION(mdp_vsync0_out), + MSM_PIN_FUNCTION(mdp_vsync1_out), + MSM_PIN_FUNCTION(mdp_vsync2_out), + MSM_PIN_FUNCTION(mdp_vsync3_out), + MSM_PIN_FUNCTION(mdp_vsync), + MSM_PIN_FUNCTION(nav), + MSM_PIN_FUNCTION(pcie0_clk_req), + MSM_PIN_FUNCTION(phase_flag), + MSM_PIN_FUNCTION(pll_bist_sync), + MSM_PIN_FUNCTION(pll_clk_aux), + MSM_PIN_FUNCTION(prng_rosc), + MSM_PIN_FUNCTION(qdss_cti_trig0), + MSM_PIN_FUNCTION(qdss_cti_trig1), + MSM_PIN_FUNCTION(qdss_gpio), + MSM_PIN_FUNCTION(qlink0_enable), + MSM_PIN_FUNCTION(qlink0_request), + MSM_PIN_FUNCTION(qlink0_wmss_reset), + MSM_PIN_FUNCTION(qup0_se0), + MSM_PIN_FUNCTION(qup0_se1), + MSM_PIN_FUNCTION(qup0_se2), + MSM_PIN_FUNCTION(qup0_se3), + MSM_PIN_FUNCTION(qup0_se4), + MSM_PIN_FUNCTION(qup1_se0), + MSM_PIN_FUNCTION(qup1_se1), + MSM_PIN_FUNCTION(qup1_se2), + MSM_PIN_FUNCTION(qup1_se3), + MSM_PIN_FUNCTION(qup1_se4), + MSM_PIN_FUNCTION(sd_write_protect), + MSM_PIN_FUNCTION(tb_trig_sdc1), + MSM_PIN_FUNCTION(tb_trig_sdc2), + MSM_PIN_FUNCTION(tgu_ch0_trigout), + MSM_PIN_FUNCTION(tgu_ch1_trigout), + MSM_PIN_FUNCTION(tgu_ch2_trigout), + MSM_PIN_FUNCTION(tgu_ch3_trigout), + MSM_PIN_FUNCTION(tmess_prng), + MSM_PIN_FUNCTION(tsense_pwm1_out), + MSM_PIN_FUNCTION(tsense_pwm2_out), + MSM_PIN_FUNCTION(uim0), + MSM_PIN_FUNCTION(uim1), + MSM_PIN_FUNCTION(usb0_hs_ac), + MSM_PIN_FUNCTION(usb0_phy_ps), + MSM_PIN_FUNCTION(vfr_0_mira), + MSM_PIN_FUNCTION(vfr_0_mirb), + MSM_PIN_FUNCTION(vfr_1), + MSM_PIN_FUNCTION(vsense_trigger_mirnat), + MSM_PIN_FUNCTION(wlan1_adc_dtest0), + MSM_PIN_FUNCTION(wlan1_adc_dtest1), +}; + +/* + * Every pin is maintained as a single group, and missing or non-existing pin + * would be maintained as dummy group to synchronize pin group index with + * pin descriptor registered with pinctrl core. + * Clients would not be able to request these dummy pin groups. + */ +static const struct msm_pingroup sm4450_groups[] = { + [0] = PINGROUP(0, qup1_se0, ibi_i3c_qup1, cmu_rng, qdss_gpio, _, _, _, _, _), + [1] = PINGROUP(1, qup1_se0, ibi_i3c_qup1, cmu_rng, qdss_gpio, _, _, _, _, _), + [2] = PINGROUP(2, qup1_se0, _, _, _, _, _, _, _, _), + [3] = PINGROUP(3, qup1_se0, gcc_gp3_clk, cmu_rng, qdss_gpio, _, _, _, _, _), + [4] = PINGROUP(4, qup0_se0, ibi_i3c_qup0, qdss_gpio, _, _, _, _, _, _), + [5] = PINGROUP(5, qup0_se0, ibi_i3c_qup0, qdss_gpio, _, _, _, _, _, _), + [6] = PINGROUP(6, qup0_se4, qdss_cti_trig1, _, _, _, _, _, _, _), + [7] = PINGROUP(7, qup0_se4, _, phase_flag, qdss_gpio, _, _, _, _, _), + [8] = PINGROUP(8, qup0_se4, _, phase_flag, qdss_gpio, _, _, _, _, _), + [9] = PINGROUP(9, qup0_se4, _, phase_flag, qdss_gpio, _, _, _, _, _), + [10] = PINGROUP(10, qup0_se1, _, atest_usb0, _, _, _, _, _, _), + [11] = PINGROUP(11, qup0_se1, _, phase_flag, _, _, _, _, _, _), + [12] = PINGROUP(12, qup0_se1, _, _, _, _, _, _, _, _), + [13] = PINGROUP(13, qup0_se1, _, phase_flag, _, _, _, _, _, _), + [14] = PINGROUP(14, qup0_se2, _, phase_flag, _, qdss_gpio, _, _, _, _), + [15] = PINGROUP(15, qup0_se2, _, phase_flag, _, qdss_gpio, _, _, _, _), + [16] = PINGROUP(16, qup0_se2, _, _, _, _, _, _, _, _), + [17] = PINGROUP(17, qup0_se2, _, phase_flag, _, qdss_gpio, _, _, _, _), + [18] = PINGROUP(18, qup0_se3, _, phase_flag, _, _, _, _, _, _), + [19] = PINGROUP(19, qup0_se3, vfr_0_mira, _, phase_flag, _, _, _, _, _), + [20] = PINGROUP(20, qup0_se3, tgu_ch0_trigout, _, _, _, _, _, _, _), + [21] = PINGROUP(21, qup0_se3, _, phase_flag, tgu_ch1_trigout, _, _, _, _, _), + [22] = PINGROUP(22, qup1_se2, mdp_vsync, mdp_vsync2_out, mdp_vsync3_out, tgu_ch2_trigout, _, _, _, _), + [23] = PINGROUP(23, qup1_se2, tgu_ch3_trigout, qdss_gpio, _, _, _, _, _, _), + [24] = PINGROUP(24, qup1_se3, _, phase_flag, _, _, _, _, _, _), + [25] = PINGROUP(25, qup1_se3, _, phase_flag, _, _, _, _, _, _), + [26] = PINGROUP(26, qup1_se1, mdp_vsync, qup0_se4, qdss_cti_trig0, _, _, _, _, _), + [27] = PINGROUP(27, qup1_se1, qup0_se4, qdss_cti_trig1, _, _, _, _, _, _), + [28] = PINGROUP(28, cmu_rng, _, _, _, _, _, _, _, _), + [29] = PINGROUP(29, ddr_bist, _, _, _, _, _, _, _, _), + [30] = PINGROUP(30, mdp_vsync, gcc_gp2_clk, ddr_bist, _, _, _, _, _, _), + [31] = PINGROUP(31, qup1_se2, _, phase_flag, ddr_bist, qdss_gpio, _, _, _, _), + [32] = PINGROUP(32, qup1_se2, _, phase_flag, ddr_bist, qdss_gpio, _, _, _, _), + [33] = PINGROUP(33, _, phase_flag, qdss_gpio, _, _, _, _, _, _), + [34] = PINGROUP(34, qup0_se0, qup0_se4, mdp_vsync, _, _, _, _, _, _), + [35] = PINGROUP(35, qup0_se0, _, phase_flag, qdss_gpio, _, _, _, _, _), + [36] = PINGROUP(36, cam_mclk, prng_rosc, qdss_gpio, _, _, _, _, _, _), + [37] = PINGROUP(37, cam_mclk, gcc_gp1_clk, prng_rosc, qdss_gpio, _, _, _, _, _), + [38] = PINGROUP(38, cam_mclk, prng_rosc, qdss_gpio, _, _, _, _, _, _), + [39] = PINGROUP(39, cam_mclk, prng_rosc, qdss_gpio, _, _, _, _, _, _), + [40] = PINGROUP(40, cci, cci_async_in0, cri_trng, qdss_gpio, _, _, _, _, _), + [41] = PINGROUP(41, cci, cri_trng, qdss_gpio, _, _, _, _, _, _), + [42] = PINGROUP(42, cci, cri_trng, qdss_gpio, _, _, _, _, _, _), + [43] = PINGROUP(43, cci, qup1_se4, qdss_gpio, _, _, _, _, _, _), + [44] = PINGROUP(44, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [45] = PINGROUP(45, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [46] = PINGROUP(46, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [47] = PINGROUP(47, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [48] = PINGROUP(48, cci_i2c, qup1_se4, gcc_gp1_clk, _, _, _, _, _, _), + [49] = PINGROUP(49, cci_i2c, qup1_se4, gcc_gp2_clk, qdss_gpio, _, _, _, _, _), + [50] = PINGROUP(50, qup1_se1, qup1_se3, _, gcc_gp3_clk, _, _, _, _, _), + [51] = PINGROUP(51, qup1_se1, qup1_se3, _, tb_trig_sdc2, _, _, _, _, _), + [52] = PINGROUP(52, _, _, _, _, _, _, _, _, _), + [53] = PINGROUP(53, _, _, _, _, _, _, _, _, _), + [54] = PINGROUP(54, coex_uart1_rx, _, _, _, _, _, _, _, _), + [55] = PINGROUP(55, coex_uart1_tx, _, _, _, _, _, _, _, _), + [56] = PINGROUP(56, _, _, _, _, _, _, _, _, _), + [57] = PINGROUP(57, tmess_prng, qdss_cti_trig1, _, _, _, _, _, _, _), + [58] = PINGROUP(58, tmess_prng, qdss_cti_trig1, _, _, _, _, _, _, _), + [59] = PINGROUP(59, tmess_prng, qdss_gpio, _, _, _, _, _, _, _), + [60] = PINGROUP(60, tmess_prng, qdss_cti_trig0, _, _, _, _, _, _, _), + [61] = PINGROUP(61, _, phase_flag, _, _, _, _, _, _, _), + [62] = PINGROUP(62, qdss_gpio, _, _, _, _, _, _, _, _), + [63] = PINGROUP(63, uim0, _, _, _, _, _, _, _, _), + [64] = PINGROUP(64, uim0, _, _, _, _, _, _, _, _), + [65] = PINGROUP(65, uim0, _, _, _, _, _, _, _, _), + [66] = PINGROUP(66, uim0, _, _, _, _, _, _, _, _), + [67] = PINGROUP(67, uim1, _, _, _, _, _, _, _, _), + [68] = PINGROUP(68, uim1, _, _, _, _, _, _, _, _), + [69] = PINGROUP(69, uim1, _, _, _, _, _, _, _, _), + [70] = PINGROUP(70, uim1, _, _, _, _, _, _, _, _), + [71] = PINGROUP(71, _, _, _, audio_ref_clk, _, _, _, _, _), + [72] = PINGROUP(72, _, _, _, phase_flag, _, _, _, _, _), + [73] = PINGROUP(73, _, _, _, pll_bist_sync, _, _, _, _, _), + [74] = PINGROUP(74, _, _, _, _, _, _, _, _, _), + [75] = PINGROUP(75, _, _, _, vsense_trigger_mirnat, atest_usb0, _, _, _, _), + [76] = PINGROUP(76, _, _, _, _, _, _, _, _, _), + [77] = PINGROUP(77, _, _, _, _, _, _, _, _, _), + [78] = PINGROUP(78, _, _, _, atest_usb0, _, _, _, _, _), + [79] = PINGROUP(79, _, _, _, wlan1_adc_dtest0, atest_usb0, _, _, _, _), + [80] = PINGROUP(80, _, _, dbg_out_clk, wlan1_adc_dtest1, atest_usb0, _, _, _, _), + [81] = PINGROUP(81, _, nav, _, _, _, _, _, _, _), + [82] = PINGROUP(82, _, _, phase_flag, _, _, _, _, _, _), + [83] = PINGROUP(83, nav, _, _, _, _, _, _, _, _), + [84] = PINGROUP(84, nav, vfr_1, _, _, _, _, _, _, _), + [85] = PINGROUP(85, _, _, _, _, _, _, _, _, _), + [86] = PINGROUP(86, _, _, _, _, _, _, _, _, _), + [87] = PINGROUP(87, qlink0_request, _, _, _, _, _, _, _, _), + [88] = PINGROUP(88, qlink0_enable, _, _, _, _, _, _, _, _), + [89] = PINGROUP(89, qlink0_wmss_reset, _, _, _, _, _, _, _, _), + [90] = PINGROUP(90, qup1_se4, jitter_bist_ref, ddr_pxi0_test, _, _, _, _, _, _), + [91] = PINGROUP(91, qup1_se4, _, phase_flag, _, _, _, _, _, _), + [92] = PINGROUP(92, _, _, _, _, _, _, _, _, _), + [93] = PINGROUP(93, mdp_vsync, mdp_vsync0_out, mdp_vsync1_out, _, _, _, _, _, _), + [94] = PINGROUP(94, usb0_phy_ps, _, _, _, _, _, _, _, _), + [95] = PINGROUP(95, _, phase_flag, atest_char, _, _, _, _, _, _), + [96] = PINGROUP(96, _, _, _, _, _, _, _, _, _), + [97] = PINGROUP(97, mdp_vsync, _, phase_flag, atest_char, _, _, _, _, _), + [98] = PINGROUP(98, _, phase_flag, atest_char, _, _, _, _, _, _), + [99] = PINGROUP(99, usb0_hs_ac, _, phase_flag, atest_char, _, _, _, _, _), + [100] = PINGROUP(100, vfr_0_mirb, _, phase_flag, atest_char, _, _, _, _, _), + [101] = PINGROUP(101, _, _, _, _, _, _, _, _, _), + [102] = PINGROUP(102, sd_write_protect, _, _, _, _, _, _, _, _), + [103] = PINGROUP(103, _, _, _, _, _, _, _, _, _), + [104] = PINGROUP(104, _, _, _, _, _, _, _, _, _), + [105] = PINGROUP(105, _, phase_flag, _, _, _, _, _, _, _), + [106] = PINGROUP(106, host2wlan_sol, _, _, _, _, _, _, _, _), + [107] = PINGROUP(107, pcie0_clk_req, _, _, _, _, _, _, _, _), + [108] = PINGROUP(108, pll_clk_aux, _, _, _, _, _, _, _, _), + [109] = PINGROUP(109, _, _, _, _, _, _, _, _, _), + [110] = PINGROUP(110, _, _, _, _, _, _, _, _, _), + [111] = PINGROUP(111, _, _, _, _, _, _, _, _, _), + [112] = PINGROUP(112, _, _, _, _, _, _, _, _, _), + [113] = PINGROUP(113, qdss_cti_trig0, _, _, _, _, _, _, _, _), + [114] = PINGROUP(114, qdss_cti_trig0, _, _, _, _, _, _, _, _), + [115] = PINGROUP(115, _, phase_flag, _, _, _, _, _, _, _), + [116] = PINGROUP(116, _, phase_flag, _, _, _, _, _, _, _), + [117] = PINGROUP(117, _, phase_flag, _, _, _, _, _, _, _), + [118] = PINGROUP(118, qdss_gpio, _, ddr_pxi1_test, _, _, _, _, _, _), + [119] = PINGROUP(119, _, _, _, _, _, _, _, _, _), + [120] = PINGROUP(120, _, _, _, _, _, _, _, _, _), + [121] = PINGROUP(121, qdss_gpio, _, _, _, _, _, _, _, _), + [122] = PINGROUP(122, qdss_gpio, _, ddr_pxi1_test, _, _, _, _, _, _), + [123] = PINGROUP(123, _, _, _, _, _, _, _, _, _), + [124] = PINGROUP(124, _, _, _, _, _, _, _, _, _), + [125] = PINGROUP(125, _, _, _, _, _, _, _, _, _), + [126] = PINGROUP(126, qdss_gpio, _, _, _, _, _, _, _, _), + [127] = PINGROUP(127, qdss_gpio, ddr_pxi0_test, _, _, _, _, _, _, _), + [128] = PINGROUP(128, tb_trig_sdc1, _, _, _, _, _, _, _, _), + [129] = PINGROUP(129, _, _, _, _, _, _, _, _, _), + [130] = PINGROUP(130, _, _, _, _, _, _, _, _, _), + [131] = PINGROUP(131, _, _, _, _, _, _, _, _, _), + [132] = PINGROUP(132, _, _, _, _, _, _, _, _, _), + [133] = PINGROUP(133, _, phase_flag, _, _, _, _, _, _, _), + [134] = PINGROUP(134, tsense_pwm1_out, tsense_pwm2_out, _, _, _, _, _, _, _), + [135] = PINGROUP(135, _, phase_flag, _, _, _, _, _, _, _), + [136] = UFS_RESET(ufs_reset, 0x97000), + [137] = SDC_QDSD_PINGROUP(sdc1_rclk, 0x8c004, 0, 0), + [138] = SDC_QDSD_PINGROUP(sdc1_clk, 0x8c000, 13, 6), + [139] = SDC_QDSD_PINGROUP(sdc1_cmd, 0x8c000, 11, 3), + [140] = SDC_QDSD_PINGROUP(sdc1_data, 0x8c000, 9, 0), + [141] = SDC_QDSD_PINGROUP(sdc2_clk, 0x8f000, 14, 6), + [142] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x8f000, 11, 3), + [143] = SDC_QDSD_PINGROUP(sdc2_data, 0x8f000, 9, 0), +}; + +static const struct msm_gpio_wakeirq_map sm4450_pdc_map[] = { + { 0, 67 }, { 3, 82 }, { 4, 69 }, { 5, 70 }, { 6, 44 }, { 7, 43 }, + { 8, 71 }, { 9, 86 }, { 10, 48 }, { 11, 77 }, { 12, 90 }, + { 13, 54 }, { 14, 91 }, { 17, 97 }, { 18, 102 }, { 21, 103 }, + { 22, 104 }, { 23, 105 }, { 24, 53 }, { 25, 106 }, { 26, 65 }, + { 27, 55 }, { 28, 89 }, { 30, 80 }, { 31, 109 }, { 33, 87 }, + { 34, 81 }, { 35, 75 }, { 40, 88 }, { 41, 98 }, { 42, 110 }, + { 43, 95 }, { 47, 118 }, { 50, 111 }, { 52, 52 }, { 53, 114 }, + { 54, 115 }, { 55, 99 }, { 56, 45 }, { 57, 85 }, { 58, 56 }, + { 59, 84 }, { 60, 83 }, { 61, 96 }, { 62, 93 }, { 66, 116 }, + { 67, 113 }, { 70, 42 }, { 71, 122 }, { 73, 119 }, { 75, 121 }, + { 77, 120 }, { 79, 123 }, { 81, 124 }, { 83, 64 }, { 84, 128 }, + { 86, 129 }, { 87, 63 }, { 91, 92 }, { 92, 66 }, { 93, 125 }, + { 94, 76 }, { 95, 62 }, { 96, 132 }, { 97, 135 }, { 98, 73 }, + { 99, 133 }, { 101, 46 }, { 102, 134 }, { 103, 49 }, { 105, 58 }, + { 107, 94 }, { 110, 59 }, { 113, 57 }, { 114, 60 }, { 118, 107 }, + { 120, 61 }, { 121, 108 }, { 123, 68 }, { 125, 72 }, { 128, 112 }, +}; + +static const struct msm_pinctrl_soc_data sm4450_tlmm = { + .pins = sm4450_pins, + .npins = ARRAY_SIZE(sm4450_pins), + .functions = sm4450_functions, + .nfunctions = ARRAY_SIZE(sm4450_functions), + .groups = sm4450_groups, + .ngroups = ARRAY_SIZE(sm4450_groups), + .ngpios = 137, + .wakeirq_map = sm4450_pdc_map, + .nwakeirq_map = ARRAY_SIZE(sm4450_pdc_map), +}; + +static int sm4450_tlmm_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &sm4450_tlmm); +} + +static const struct of_device_id sm4450_tlmm_of_match[] = { + { .compatible = "qcom,sm4450-tlmm", }, + { } +}; + +static struct platform_driver sm4450_tlmm_driver = { + .driver = { + .name = "sm4450-tlmm", + .of_match_table = sm4450_tlmm_of_match, + }, + .probe = sm4450_tlmm_probe, + .remove_new = msm_pinctrl_remove, +}; +MODULE_DEVICE_TABLE(of, sm4450_tlmm_of_match); + +static int __init sm4450_tlmm_init(void) +{ + return platform_driver_register(&sm4450_tlmm_driver); +} +arch_initcall(sm4450_tlmm_init); + +static void __exit sm4450_tlmm_exit(void) +{ + platform_driver_unregister(&sm4450_tlmm_driver); +} +module_exit(sm4450_tlmm_exit); + +MODULE_DESCRIPTION("QTI SM4450 TLMM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c index e8a6f6f6af54..316d6fc69131 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6115-lpass-lpi.c @@ -36,26 +36,6 @@ enum lpass_lpi_functions { LPI_MUX__, }; -static int gpio0_pins[] = { 0 }; -static int gpio1_pins[] = { 1 }; -static int gpio2_pins[] = { 2 }; -static int gpio3_pins[] = { 3 }; -static int gpio4_pins[] = { 4 }; -static int gpio5_pins[] = { 5 }; -static int gpio6_pins[] = { 6 }; -static int gpio7_pins[] = { 7 }; -static int gpio8_pins[] = { 8 }; -static int gpio9_pins[] = { 9 }; -static int gpio10_pins[] = { 10 }; -static int gpio11_pins[] = { 11 }; -static int gpio12_pins[] = { 12 }; -static int gpio13_pins[] = { 13 }; -static int gpio14_pins[] = { 14 }; -static int gpio15_pins[] = { 15 }; -static int gpio16_pins[] = { 16 }; -static int gpio17_pins[] = { 17 }; -static int gpio18_pins[] = { 18 }; - static const struct pinctrl_pin_desc sm6115_lpi_pins[] = { PINCTRL_PIN(0, "gpio0"), PINCTRL_PIN(1, "gpio1"), diff --git a/drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c index cb10ce8d5d28..9791d9ba5087 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8250-lpass-lpi.c @@ -36,21 +36,6 @@ enum lpass_lpi_functions { LPI_MUX__, }; -static int gpio0_pins[] = { 0 }; -static int gpio1_pins[] = { 1 }; -static int gpio2_pins[] = { 2 }; -static int gpio3_pins[] = { 3 }; -static int gpio4_pins[] = { 4 }; -static int gpio5_pins[] = { 5 }; -static int gpio6_pins[] = { 6 }; -static int gpio7_pins[] = { 7 }; -static int gpio8_pins[] = { 8 }; -static int gpio9_pins[] = { 9 }; -static int gpio10_pins[] = { 10 }; -static int gpio11_pins[] = { 11 }; -static int gpio12_pins[] = { 12 }; -static int gpio13_pins[] = { 13 }; - static const struct pinctrl_pin_desc sm8250_lpi_pins[] = { PINCTRL_PIN(0, "gpio0"), PINCTRL_PIN(1, "gpio1"), diff --git a/drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c index 297cc95ac3c0..5b9a2cb216bd 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8350-lpass-lpi.c @@ -36,22 +36,6 @@ enum lpass_lpi_functions { LPI_MUX__, }; -static int gpio0_pins[] = { 0 }; -static int gpio1_pins[] = { 1 }; -static int gpio2_pins[] = { 2 }; -static int gpio3_pins[] = { 3 }; -static int gpio4_pins[] = { 4 }; -static int gpio5_pins[] = { 5 }; -static int gpio6_pins[] = { 6 }; -static int gpio7_pins[] = { 7 }; -static int gpio8_pins[] = { 8 }; -static int gpio9_pins[] = { 9 }; -static int gpio10_pins[] = { 10 }; -static int gpio11_pins[] = { 11 }; -static int gpio12_pins[] = { 12 }; -static int gpio13_pins[] = { 13 }; -static int gpio14_pins[] = { 14 }; - static const struct pinctrl_pin_desc sm8350_lpi_pins[] = { PINCTRL_PIN(0, "gpio0"), PINCTRL_PIN(1, "gpio1"), diff --git a/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c index 2e7896791fc0..a028cbb49947 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c @@ -52,30 +52,6 @@ enum lpass_lpi_functions { LPI_MUX__, }; -static int gpio0_pins[] = { 0 }; -static int gpio1_pins[] = { 1 }; -static int gpio2_pins[] = { 2 }; -static int gpio3_pins[] = { 3 }; -static int gpio4_pins[] = { 4 }; -static int gpio5_pins[] = { 5 }; -static int gpio6_pins[] = { 6 }; -static int gpio7_pins[] = { 7 }; -static int gpio8_pins[] = { 8 }; -static int gpio9_pins[] = { 9 }; -static int gpio10_pins[] = { 10 }; -static int gpio11_pins[] = { 11 }; -static int gpio12_pins[] = { 12 }; -static int gpio13_pins[] = { 13 }; -static int gpio14_pins[] = { 14 }; -static int gpio15_pins[] = { 15 }; -static int gpio16_pins[] = { 16 }; -static int gpio17_pins[] = { 17 }; -static int gpio18_pins[] = { 18 }; -static int gpio19_pins[] = { 19 }; -static int gpio20_pins[] = { 20 }; -static int gpio21_pins[] = { 21 }; -static int gpio22_pins[] = { 22 }; - static const struct pinctrl_pin_desc sm8450_lpi_pins[] = { PINCTRL_PIN(0, "gpio0"), PINCTRL_PIN(1, "gpio1"), diff --git a/drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c index 64458c3fbe5e..852192b044e1 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8550-lpass-lpi.c @@ -52,30 +52,6 @@ enum lpass_lpi_functions { LPI_MUX__, }; -static int gpio0_pins[] = { 0 }; -static int gpio1_pins[] = { 1 }; -static int gpio2_pins[] = { 2 }; -static int gpio3_pins[] = { 3 }; -static int gpio4_pins[] = { 4 }; -static int gpio5_pins[] = { 5 }; -static int gpio6_pins[] = { 6 }; -static int gpio7_pins[] = { 7 }; -static int gpio8_pins[] = { 8 }; -static int gpio9_pins[] = { 9 }; -static int gpio10_pins[] = { 10 }; -static int gpio11_pins[] = { 11 }; -static int gpio12_pins[] = { 12 }; -static int gpio13_pins[] = { 13 }; -static int gpio14_pins[] = { 14 }; -static int gpio15_pins[] = { 15 }; -static int gpio16_pins[] = { 16 }; -static int gpio17_pins[] = { 17 }; -static int gpio18_pins[] = { 18 }; -static int gpio19_pins[] = { 19 }; -static int gpio20_pins[] = { 20 }; -static int gpio21_pins[] = { 21 }; -static int gpio22_pins[] = { 22 }; - static const struct pinctrl_pin_desc sm8550_lpi_pins[] = { PINCTRL_PIN(0, "gpio0"), PINCTRL_PIN(1, "gpio1"), diff --git a/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c new file mode 100644 index 000000000000..04400c832327 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022-2023 Linaro Ltd. + */ + +#include <linux/gpio/driver.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "pinctrl-lpass-lpi.h" + +enum lpass_lpi_functions { + LPI_MUX_dmic1_clk, + LPI_MUX_dmic1_data, + LPI_MUX_dmic2_clk, + LPI_MUX_dmic2_data, + LPI_MUX_dmic3_clk, + LPI_MUX_dmic3_data, + LPI_MUX_dmic4_clk, + LPI_MUX_dmic4_data, + LPI_MUX_i2s0_clk, + LPI_MUX_i2s0_data, + LPI_MUX_i2s0_ws, + LPI_MUX_i2s1_clk, + LPI_MUX_i2s1_data, + LPI_MUX_i2s1_ws, + LPI_MUX_i2s2_clk, + LPI_MUX_i2s2_data, + LPI_MUX_i2s2_ws, + LPI_MUX_i2s3_clk, + LPI_MUX_i2s3_data, + LPI_MUX_i2s3_ws, + LPI_MUX_i2s4_clk, + LPI_MUX_i2s4_data, + LPI_MUX_i2s4_ws, + LPI_MUX_qca_swr_clk, + LPI_MUX_qca_swr_data, + LPI_MUX_slimbus_clk, + LPI_MUX_slimbus_data, + LPI_MUX_swr_rx_clk, + LPI_MUX_swr_rx_data, + LPI_MUX_swr_tx_clk, + LPI_MUX_swr_tx_data, + LPI_MUX_wsa_swr_clk, + LPI_MUX_wsa_swr_data, + LPI_MUX_wsa2_swr_clk, + LPI_MUX_wsa2_swr_data, + LPI_MUX_ext_mclk1_a, + LPI_MUX_ext_mclk1_b, + LPI_MUX_ext_mclk1_c, + LPI_MUX_ext_mclk1_d, + LPI_MUX_ext_mclk1_e, + LPI_MUX_gpio, + LPI_MUX__, +}; + +static const struct pinctrl_pin_desc sm8650_lpi_pins[] = { + PINCTRL_PIN(0, "gpio0"), + PINCTRL_PIN(1, "gpio1"), + PINCTRL_PIN(2, "gpio2"), + PINCTRL_PIN(3, "gpio3"), + PINCTRL_PIN(4, "gpio4"), + PINCTRL_PIN(5, "gpio5"), + PINCTRL_PIN(6, "gpio6"), + PINCTRL_PIN(7, "gpio7"), + PINCTRL_PIN(8, "gpio8"), + PINCTRL_PIN(9, "gpio9"), + PINCTRL_PIN(10, "gpio10"), + PINCTRL_PIN(11, "gpio11"), + PINCTRL_PIN(12, "gpio12"), + PINCTRL_PIN(13, "gpio13"), + PINCTRL_PIN(14, "gpio14"), + PINCTRL_PIN(15, "gpio15"), + PINCTRL_PIN(16, "gpio16"), + PINCTRL_PIN(17, "gpio17"), + PINCTRL_PIN(18, "gpio18"), + PINCTRL_PIN(19, "gpio19"), + PINCTRL_PIN(20, "gpio20"), + PINCTRL_PIN(21, "gpio21"), + PINCTRL_PIN(22, "gpio22"), +}; + +static const char * const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", +}; + +static const char * const dmic1_clk_groups[] = { "gpio6" }; +static const char * const dmic1_data_groups[] = { "gpio7" }; +static const char * const dmic2_clk_groups[] = { "gpio8" }; +static const char * const dmic2_data_groups[] = { "gpio9" }; +static const char * const dmic3_clk_groups[] = { "gpio12" }; +static const char * const dmic3_data_groups[] = { "gpio13" }; +static const char * const dmic4_clk_groups[] = { "gpio17" }; +static const char * const dmic4_data_groups[] = { "gpio18" }; +static const char * const i2s0_clk_groups[] = { "gpio0" }; +static const char * const i2s0_ws_groups[] = { "gpio1" }; +static const char * const i2s0_data_groups[] = { "gpio2", "gpio3", "gpio4", "gpio5" }; +static const char * const i2s1_clk_groups[] = { "gpio6" }; +static const char * const i2s1_ws_groups[] = { "gpio7" }; +static const char * const i2s1_data_groups[] = { "gpio8", "gpio9" }; +static const char * const i2s2_clk_groups[] = { "gpio10" }; +static const char * const i2s2_ws_groups[] = { "gpio11" }; +static const char * const i2s2_data_groups[] = { "gpio15", "gpio16" }; +static const char * const i2s3_clk_groups[] = { "gpio12" }; +static const char * const i2s3_ws_groups[] = { "gpio13" }; +static const char * const i2s3_data_groups[] = { "gpio17", "gpio18" }; +static const char * const i2s4_clk_groups[] = { "gpio19"}; +static const char * const i2s4_ws_groups[] = { "gpio20"}; +static const char * const i2s4_data_groups[] = { "gpio21", "gpio22"}; +static const char * const qca_swr_clk_groups[] = { "gpio19" }; +static const char * const qca_swr_data_groups[] = { "gpio20" }; +static const char * const slimbus_clk_groups[] = { "gpio19"}; +static const char * const slimbus_data_groups[] = { "gpio20"}; +static const char * const swr_tx_clk_groups[] = { "gpio0" }; +static const char * const swr_tx_data_groups[] = { "gpio1", "gpio2", "gpio14" }; +static const char * const swr_rx_clk_groups[] = { "gpio3" }; +static const char * const swr_rx_data_groups[] = { "gpio4", "gpio5", "gpio15" }; +static const char * const wsa_swr_clk_groups[] = { "gpio10" }; +static const char * const wsa_swr_data_groups[] = { "gpio11" }; +static const char * const wsa2_swr_clk_groups[] = { "gpio15" }; +static const char * const wsa2_swr_data_groups[] = { "gpio16" }; +static const char * const ext_mclk1_c_groups[] = { "gpio5" }; +static const char * const ext_mclk1_b_groups[] = { "gpio9" }; +static const char * const ext_mclk1_a_groups[] = { "gpio13" }; +static const char * const ext_mclk1_d_groups[] = { "gpio14" }; +static const char * const ext_mclk1_e_groups[] = { "gpio22" }; + +static const struct lpi_pingroup sm8650_groups[] = { + LPI_PINGROUP(0, 11, swr_tx_clk, i2s0_clk, _, _), + LPI_PINGROUP(1, 11, swr_tx_data, i2s0_ws, _, _), + LPI_PINGROUP(2, 11, swr_tx_data, i2s0_data, _, _), + LPI_PINGROUP(3, 11, swr_rx_clk, i2s0_data, _, _), + LPI_PINGROUP(4, 11, swr_rx_data, i2s0_data, _, _), + LPI_PINGROUP(5, 11, swr_rx_data, ext_mclk1_c, i2s0_data, _), + LPI_PINGROUP(6, LPI_NO_SLEW, dmic1_clk, i2s1_clk, _, _), + LPI_PINGROUP(7, LPI_NO_SLEW, dmic1_data, i2s1_ws, _, _), + LPI_PINGROUP(8, LPI_NO_SLEW, dmic2_clk, i2s1_data, _, _), + LPI_PINGROUP(9, LPI_NO_SLEW, dmic2_data, i2s1_data, ext_mclk1_b, _), + LPI_PINGROUP(10, 11, i2s2_clk, wsa_swr_clk, _, _), + LPI_PINGROUP(11, 11, i2s2_ws, wsa_swr_data, _, _), + LPI_PINGROUP(12, LPI_NO_SLEW, dmic3_clk, i2s3_clk, _, _), + LPI_PINGROUP(13, LPI_NO_SLEW, dmic3_data, i2s3_ws, ext_mclk1_a, _), + LPI_PINGROUP(14, 11, swr_tx_data, ext_mclk1_d, _, _), + LPI_PINGROUP(15, 11, i2s2_data, wsa2_swr_clk, _, _), + LPI_PINGROUP(16, 11, i2s2_data, wsa2_swr_data, _, _), + LPI_PINGROUP(17, LPI_NO_SLEW, dmic4_clk, i2s3_data, _, _), + LPI_PINGROUP(18, LPI_NO_SLEW, dmic4_data, i2s3_data, _, _), + LPI_PINGROUP(19, 11, i2s4_clk, slimbus_clk, qca_swr_clk, _), + LPI_PINGROUP(20, 11, i2s4_ws, slimbus_data, qca_swr_data, _), + LPI_PINGROUP(21, LPI_NO_SLEW, i2s4_data, _, _, _), + LPI_PINGROUP(22, LPI_NO_SLEW, i2s4_data, ext_mclk1_e, _, _), +}; + +static const struct lpi_function sm8650_functions[] = { + LPI_FUNCTION(gpio), + LPI_FUNCTION(dmic1_clk), + LPI_FUNCTION(dmic1_data), + LPI_FUNCTION(dmic2_clk), + LPI_FUNCTION(dmic2_data), + LPI_FUNCTION(dmic3_clk), + LPI_FUNCTION(dmic3_data), + LPI_FUNCTION(dmic4_clk), + LPI_FUNCTION(dmic4_data), + LPI_FUNCTION(i2s0_clk), + LPI_FUNCTION(i2s0_data), + LPI_FUNCTION(i2s0_ws), + LPI_FUNCTION(i2s1_clk), + LPI_FUNCTION(i2s1_data), + LPI_FUNCTION(i2s1_ws), + LPI_FUNCTION(i2s2_clk), + LPI_FUNCTION(i2s2_data), + LPI_FUNCTION(i2s2_ws), + LPI_FUNCTION(i2s3_clk), + LPI_FUNCTION(i2s3_data), + LPI_FUNCTION(i2s3_ws), + LPI_FUNCTION(i2s4_clk), + LPI_FUNCTION(i2s4_data), + LPI_FUNCTION(i2s4_ws), + LPI_FUNCTION(qca_swr_clk), + LPI_FUNCTION(qca_swr_data), + LPI_FUNCTION(slimbus_clk), + LPI_FUNCTION(slimbus_data), + LPI_FUNCTION(swr_rx_clk), + LPI_FUNCTION(swr_rx_data), + LPI_FUNCTION(swr_tx_clk), + LPI_FUNCTION(swr_tx_data), + LPI_FUNCTION(wsa_swr_clk), + LPI_FUNCTION(wsa_swr_data), + LPI_FUNCTION(wsa2_swr_clk), + LPI_FUNCTION(wsa2_swr_data), + LPI_FUNCTION(ext_mclk1_a), + LPI_FUNCTION(ext_mclk1_b), + LPI_FUNCTION(ext_mclk1_c), + LPI_FUNCTION(ext_mclk1_d), + LPI_FUNCTION(ext_mclk1_e), +}; + +static const struct lpi_pinctrl_variant_data sm8650_lpi_data = { + .pins = sm8650_lpi_pins, + .npins = ARRAY_SIZE(sm8650_lpi_pins), + .groups = sm8650_groups, + .ngroups = ARRAY_SIZE(sm8650_groups), + .functions = sm8650_functions, + .nfunctions = ARRAY_SIZE(sm8650_functions), + .flags = LPI_FLAG_SLEW_RATE_SAME_REG, +}; + +static const struct of_device_id lpi_pinctrl_of_match[] = { + { + .compatible = "qcom,sm8650-lpass-lpi-pinctrl", + .data = &sm8650_lpi_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); + +static struct platform_driver lpi_pinctrl_driver = { + .driver = { + .name = "qcom-sm8650-lpass-lpi-pinctrl", + .of_match_table = lpi_pinctrl_of_match, + }, + .probe = lpi_pinctrl_probe, + .remove_new = lpi_pinctrl_remove, +}; + +module_platform_driver(lpi_pinctrl_driver); +MODULE_DESCRIPTION("Qualcomm SM8650 LPI GPIO pin control driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/qcom/pinctrl-sm8650.c b/drivers/pinctrl/qcom/pinctrl-sm8650.c new file mode 100644 index 000000000000..adaddd728662 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sm8650.c @@ -0,0 +1,1762 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, Linaro Limited + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 + +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + msm_mux_##f10 \ + }, \ + .nfuncs = 11, \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .intr_target_reg = 0x8 + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .i2c_pull_bit = 13, \ + .egpio_enable = 12, \ + .egpio_present = 11, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_wakeup_present_bit = 6, \ + .intr_wakeup_enable_bit = 7, \ + .intr_target_bit = 8, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +#define UFS_RESET(pg_name, ctl, io) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = io, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +static const struct pinctrl_pin_desc sm8650_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), + PINCTRL_PIN(128, "GPIO_128"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "GPIO_133"), + PINCTRL_PIN(134, "GPIO_134"), + PINCTRL_PIN(135, "GPIO_135"), + PINCTRL_PIN(136, "GPIO_136"), + PINCTRL_PIN(137, "GPIO_137"), + PINCTRL_PIN(138, "GPIO_138"), + PINCTRL_PIN(139, "GPIO_139"), + PINCTRL_PIN(140, "GPIO_140"), + PINCTRL_PIN(141, "GPIO_141"), + PINCTRL_PIN(142, "GPIO_142"), + PINCTRL_PIN(143, "GPIO_143"), + PINCTRL_PIN(144, "GPIO_144"), + PINCTRL_PIN(145, "GPIO_145"), + PINCTRL_PIN(146, "GPIO_146"), + PINCTRL_PIN(147, "GPIO_147"), + PINCTRL_PIN(148, "GPIO_148"), + PINCTRL_PIN(149, "GPIO_149"), + PINCTRL_PIN(150, "GPIO_150"), + PINCTRL_PIN(151, "GPIO_151"), + PINCTRL_PIN(152, "GPIO_152"), + PINCTRL_PIN(153, "GPIO_153"), + PINCTRL_PIN(154, "GPIO_154"), + PINCTRL_PIN(155, "GPIO_155"), + PINCTRL_PIN(156, "GPIO_156"), + PINCTRL_PIN(157, "GPIO_157"), + PINCTRL_PIN(158, "GPIO_158"), + PINCTRL_PIN(159, "GPIO_159"), + PINCTRL_PIN(160, "GPIO_160"), + PINCTRL_PIN(161, "GPIO_161"), + PINCTRL_PIN(162, "GPIO_162"), + PINCTRL_PIN(163, "GPIO_163"), + PINCTRL_PIN(164, "GPIO_164"), + PINCTRL_PIN(165, "GPIO_165"), + PINCTRL_PIN(166, "GPIO_166"), + PINCTRL_PIN(167, "GPIO_167"), + PINCTRL_PIN(168, "GPIO_168"), + PINCTRL_PIN(169, "GPIO_169"), + PINCTRL_PIN(170, "GPIO_170"), + PINCTRL_PIN(171, "GPIO_171"), + PINCTRL_PIN(172, "GPIO_172"), + PINCTRL_PIN(173, "GPIO_173"), + PINCTRL_PIN(174, "GPIO_174"), + PINCTRL_PIN(175, "GPIO_175"), + PINCTRL_PIN(176, "GPIO_176"), + PINCTRL_PIN(177, "GPIO_177"), + PINCTRL_PIN(178, "GPIO_178"), + PINCTRL_PIN(179, "GPIO_179"), + PINCTRL_PIN(180, "GPIO_180"), + PINCTRL_PIN(181, "GPIO_181"), + PINCTRL_PIN(182, "GPIO_182"), + PINCTRL_PIN(183, "GPIO_183"), + PINCTRL_PIN(184, "GPIO_184"), + PINCTRL_PIN(185, "GPIO_185"), + PINCTRL_PIN(186, "GPIO_186"), + PINCTRL_PIN(187, "GPIO_187"), + PINCTRL_PIN(188, "GPIO_188"), + PINCTRL_PIN(189, "GPIO_189"), + PINCTRL_PIN(190, "GPIO_190"), + PINCTRL_PIN(191, "GPIO_191"), + PINCTRL_PIN(192, "GPIO_192"), + PINCTRL_PIN(193, "GPIO_193"), + PINCTRL_PIN(194, "GPIO_194"), + PINCTRL_PIN(195, "GPIO_195"), + PINCTRL_PIN(196, "GPIO_196"), + PINCTRL_PIN(197, "GPIO_197"), + PINCTRL_PIN(198, "GPIO_198"), + PINCTRL_PIN(199, "GPIO_199"), + PINCTRL_PIN(200, "GPIO_200"), + PINCTRL_PIN(201, "GPIO_201"), + PINCTRL_PIN(202, "GPIO_202"), + PINCTRL_PIN(203, "GPIO_203"), + PINCTRL_PIN(204, "GPIO_204"), + PINCTRL_PIN(205, "GPIO_205"), + PINCTRL_PIN(206, "GPIO_206"), + PINCTRL_PIN(207, "GPIO_207"), + PINCTRL_PIN(208, "GPIO_208"), + PINCTRL_PIN(209, "GPIO_209"), + PINCTRL_PIN(210, "UFS_RESET"), + PINCTRL_PIN(211, "SDC2_CLK"), + PINCTRL_PIN(212, "SDC2_CMD"), + PINCTRL_PIN(213, "SDC2_DATA"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); +DECLARE_MSM_GPIO_PINS(123); +DECLARE_MSM_GPIO_PINS(124); +DECLARE_MSM_GPIO_PINS(125); +DECLARE_MSM_GPIO_PINS(126); +DECLARE_MSM_GPIO_PINS(127); +DECLARE_MSM_GPIO_PINS(128); +DECLARE_MSM_GPIO_PINS(129); +DECLARE_MSM_GPIO_PINS(130); +DECLARE_MSM_GPIO_PINS(131); +DECLARE_MSM_GPIO_PINS(132); +DECLARE_MSM_GPIO_PINS(133); +DECLARE_MSM_GPIO_PINS(134); +DECLARE_MSM_GPIO_PINS(135); +DECLARE_MSM_GPIO_PINS(136); +DECLARE_MSM_GPIO_PINS(137); +DECLARE_MSM_GPIO_PINS(138); +DECLARE_MSM_GPIO_PINS(139); +DECLARE_MSM_GPIO_PINS(140); +DECLARE_MSM_GPIO_PINS(141); +DECLARE_MSM_GPIO_PINS(142); +DECLARE_MSM_GPIO_PINS(143); +DECLARE_MSM_GPIO_PINS(144); +DECLARE_MSM_GPIO_PINS(145); +DECLARE_MSM_GPIO_PINS(146); +DECLARE_MSM_GPIO_PINS(147); +DECLARE_MSM_GPIO_PINS(148); +DECLARE_MSM_GPIO_PINS(149); +DECLARE_MSM_GPIO_PINS(150); +DECLARE_MSM_GPIO_PINS(151); +DECLARE_MSM_GPIO_PINS(152); +DECLARE_MSM_GPIO_PINS(153); +DECLARE_MSM_GPIO_PINS(154); +DECLARE_MSM_GPIO_PINS(155); +DECLARE_MSM_GPIO_PINS(156); +DECLARE_MSM_GPIO_PINS(157); +DECLARE_MSM_GPIO_PINS(158); +DECLARE_MSM_GPIO_PINS(159); +DECLARE_MSM_GPIO_PINS(160); +DECLARE_MSM_GPIO_PINS(161); +DECLARE_MSM_GPIO_PINS(162); +DECLARE_MSM_GPIO_PINS(163); +DECLARE_MSM_GPIO_PINS(164); +DECLARE_MSM_GPIO_PINS(165); +DECLARE_MSM_GPIO_PINS(166); +DECLARE_MSM_GPIO_PINS(167); +DECLARE_MSM_GPIO_PINS(168); +DECLARE_MSM_GPIO_PINS(169); +DECLARE_MSM_GPIO_PINS(170); +DECLARE_MSM_GPIO_PINS(171); +DECLARE_MSM_GPIO_PINS(172); +DECLARE_MSM_GPIO_PINS(173); +DECLARE_MSM_GPIO_PINS(174); +DECLARE_MSM_GPIO_PINS(175); +DECLARE_MSM_GPIO_PINS(176); +DECLARE_MSM_GPIO_PINS(177); +DECLARE_MSM_GPIO_PINS(178); +DECLARE_MSM_GPIO_PINS(179); +DECLARE_MSM_GPIO_PINS(180); +DECLARE_MSM_GPIO_PINS(181); +DECLARE_MSM_GPIO_PINS(182); +DECLARE_MSM_GPIO_PINS(183); +DECLARE_MSM_GPIO_PINS(184); +DECLARE_MSM_GPIO_PINS(185); +DECLARE_MSM_GPIO_PINS(186); +DECLARE_MSM_GPIO_PINS(187); +DECLARE_MSM_GPIO_PINS(188); +DECLARE_MSM_GPIO_PINS(189); +DECLARE_MSM_GPIO_PINS(190); +DECLARE_MSM_GPIO_PINS(191); +DECLARE_MSM_GPIO_PINS(192); +DECLARE_MSM_GPIO_PINS(193); +DECLARE_MSM_GPIO_PINS(194); +DECLARE_MSM_GPIO_PINS(195); +DECLARE_MSM_GPIO_PINS(196); +DECLARE_MSM_GPIO_PINS(197); +DECLARE_MSM_GPIO_PINS(198); +DECLARE_MSM_GPIO_PINS(199); +DECLARE_MSM_GPIO_PINS(200); +DECLARE_MSM_GPIO_PINS(201); +DECLARE_MSM_GPIO_PINS(202); +DECLARE_MSM_GPIO_PINS(203); +DECLARE_MSM_GPIO_PINS(204); +DECLARE_MSM_GPIO_PINS(205); +DECLARE_MSM_GPIO_PINS(206); +DECLARE_MSM_GPIO_PINS(207); +DECLARE_MSM_GPIO_PINS(208); +DECLARE_MSM_GPIO_PINS(209); + +static const unsigned int ufs_reset_pins[] = { 210 }; +static const unsigned int sdc2_clk_pins[] = { 211 }; +static const unsigned int sdc2_cmd_pins[] = { 212 }; +static const unsigned int sdc2_data_pins[] = { 213 }; + +enum sm8650_functions { + msm_mux_gpio, + msm_mux_aoss_cti, + msm_mux_atest_char, + msm_mux_atest_usb, + msm_mux_audio_ext_mclk0, + msm_mux_audio_ext_mclk1, + msm_mux_audio_ref_clk, + msm_mux_cam_aon_mclk2, + msm_mux_cam_aon_mclk4, + msm_mux_cam_mclk, + msm_mux_cci_async_in, + msm_mux_cci_i2c_scl, + msm_mux_cci_i2c_sda, + msm_mux_cci_timer, + msm_mux_cmu_rng, + msm_mux_coex_uart1_rx, + msm_mux_coex_uart1_tx, + msm_mux_coex_uart2_rx, + msm_mux_coex_uart2_tx, + msm_mux_cri_trng, + msm_mux_dbg_out_clk, + msm_mux_ddr_bist_complete, + msm_mux_ddr_bist_fail, + msm_mux_ddr_bist_start, + msm_mux_ddr_bist_stop, + msm_mux_ddr_pxi0, + msm_mux_ddr_pxi1, + msm_mux_ddr_pxi2, + msm_mux_ddr_pxi3, + msm_mux_do_not, + msm_mux_dp_hot, + msm_mux_egpio, + msm_mux_gcc_gp1, + msm_mux_gcc_gp2, + msm_mux_gcc_gp3, + msm_mux_gnss_adc0, + msm_mux_gnss_adc1, + msm_mux_i2chub0_se0, + msm_mux_i2chub0_se1, + msm_mux_i2chub0_se2, + msm_mux_i2chub0_se3, + msm_mux_i2chub0_se4, + msm_mux_i2chub0_se5, + msm_mux_i2chub0_se6, + msm_mux_i2chub0_se7, + msm_mux_i2chub0_se8, + msm_mux_i2chub0_se9, + msm_mux_i2s0_data0, + msm_mux_i2s0_data1, + msm_mux_i2s0_sck, + msm_mux_i2s0_ws, + msm_mux_i2s1_data0, + msm_mux_i2s1_data1, + msm_mux_i2s1_sck, + msm_mux_i2s1_ws, + msm_mux_ibi_i3c, + msm_mux_jitter_bist, + msm_mux_mdp_vsync, + msm_mux_mdp_vsync0_out, + msm_mux_mdp_vsync1_out, + msm_mux_mdp_vsync2_out, + msm_mux_mdp_vsync3_out, + msm_mux_mdp_vsync_e, + msm_mux_nav_gpio0, + msm_mux_nav_gpio1, + msm_mux_nav_gpio2, + msm_mux_nav_gpio3, + msm_mux_pcie0_clk_req_n, + msm_mux_pcie1_clk_req_n, + msm_mux_phase_flag, + msm_mux_pll_bist_sync, + msm_mux_pll_clk_aux, + msm_mux_prng_rosc0, + msm_mux_prng_rosc1, + msm_mux_prng_rosc2, + msm_mux_prng_rosc3, + msm_mux_qdss_cti, + msm_mux_qdss_gpio, + msm_mux_qlink_big_enable, + msm_mux_qlink_big_request, + msm_mux_qlink_little_enable, + msm_mux_qlink_little_request, + msm_mux_qlink_wmss, + msm_mux_qspi0, + msm_mux_qspi1, + msm_mux_qspi2, + msm_mux_qspi3, + msm_mux_qspi_clk, + msm_mux_qspi_cs, + msm_mux_qup1_se0, + msm_mux_qup1_se1, + msm_mux_qup1_se2, + msm_mux_qup1_se3, + msm_mux_qup1_se4, + msm_mux_qup1_se5, + msm_mux_qup1_se6, + msm_mux_qup1_se7, + msm_mux_qup2_se0, + msm_mux_qup2_se1, + msm_mux_qup2_se2, + msm_mux_qup2_se3, + msm_mux_qup2_se4, + msm_mux_qup2_se5, + msm_mux_qup2_se6, + msm_mux_qup2_se7, + msm_mux_sd_write_protect, + msm_mux_sdc40, + msm_mux_sdc41, + msm_mux_sdc42, + msm_mux_sdc43, + msm_mux_sdc4_clk, + msm_mux_sdc4_cmd, + msm_mux_tb_trig_sdc2, + msm_mux_tb_trig_sdc4, + msm_mux_tgu_ch0_trigout, + msm_mux_tgu_ch1_trigout, + msm_mux_tgu_ch2_trigout, + msm_mux_tgu_ch3_trigout, + msm_mux_tmess_prng0, + msm_mux_tmess_prng1, + msm_mux_tmess_prng2, + msm_mux_tmess_prng3, + msm_mux_tsense_pwm1, + msm_mux_tsense_pwm2, + msm_mux_tsense_pwm3, + msm_mux_uim0_clk, + msm_mux_uim0_data, + msm_mux_uim0_present, + msm_mux_uim0_reset, + msm_mux_uim1_clk, + msm_mux_uim1_data, + msm_mux_uim1_present, + msm_mux_uim1_reset, + msm_mux_usb1_hs, + msm_mux_usb_phy, + msm_mux_vfr_0, + msm_mux_vfr_1, + msm_mux_vsense_trigger_mirnat, + msm_mux__, +}; + +static const char *const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15", + "gpio16", "gpio17", "gpio18", "gpio19", + "gpio20", "gpio21", "gpio22", "gpio23", + "gpio24", "gpio25", "gpio26", "gpio27", + "gpio28", "gpio29", "gpio30", "gpio31", + "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", + "gpio40", "gpio41", "gpio42", "gpio43", + "gpio44", "gpio45", "gpio46", "gpio47", + "gpio48", "gpio49", "gpio50", "gpio51", + "gpio52", "gpio53", "gpio54", "gpio55", + "gpio56", "gpio57", "gpio58", "gpio59", + "gpio60", "gpio61", "gpio62", "gpio63", + "gpio64", "gpio65", "gpio66", "gpio67", + "gpio68", "gpio69", "gpio70", "gpio71", + "gpio72", "gpio73", "gpio74", "gpio75", + "gpio76", "gpio77", "gpio78", "gpio79", + "gpio80", "gpio81", "gpio82", "gpio83", + "gpio84", "gpio85", "gpio86", "gpio87", + "gpio88", "gpio89", "gpio90", "gpio91", + "gpio92", "gpio93", "gpio94", "gpio95", + "gpio96", "gpio97", "gpio98", "gpio99", + "gpio100", "gpio101", "gpio102", "gpio103", + "gpio104", "gpio105", "gpio106", "gpio107", + "gpio108", "gpio109", "gpio110", "gpio111", + "gpio112", "gpio113", "gpio114", "gpio115", + "gpio116", "gpio117", "gpio118", "gpio119", + "gpio120", "gpio121", "gpio122", "gpio123", + "gpio124", "gpio125", "gpio126", "gpio127", + "gpio128", "gpio129", "gpio130", "gpio131", + "gpio132", "gpio133", "gpio134", "gpio135", + "gpio136", "gpio137", "gpio138", "gpio139", + "gpio140", "gpio141", "gpio142", "gpio143", + "gpio144", "gpio145", "gpio146", "gpio147", + "gpio148", "gpio149", "gpio150", "gpio151", + "gpio152", "gpio153", "gpio154", "gpio155", + "gpio156", "gpio157", "gpio158", "gpio159", + "gpio160", "gpio161", "gpio162", "gpio163", + "gpio164", "gpio165", "gpio166", "gpio167", + "gpio168", "gpio169", "gpio170", "gpio171", + "gpio172", "gpio173", "gpio174", "gpio175", + "gpio176", "gpio177", "gpio178", "gpio179", + "gpio180", "gpio181", "gpio182", "gpio183", + "gpio184", "gpio185", "gpio186", "gpio187", + "gpio188", "gpio189", "gpio190", "gpio191", + "gpio192", "gpio193", "gpio194", "gpio195", + "gpio196", "gpio197", "gpio198", "gpio199", + "gpio200", "gpio201", "gpio202", "gpio203", + "gpio204", "gpio205", "gpio206", "gpio207", + "gpio208", "gpio209", +}; + +static const char * const egpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio165", "gpio166", "gpio167", "gpio168", + "gpio169", "gpio170", "gpio171", "gpio172", "gpio173", "gpio174", + "gpio175", "gpio176", "gpio177", "gpio178", "gpio179", "gpio180", + "gpio181", "gpio182", "gpio183", "gpio184", "gpio185", "gpio186", + "gpio187", "gpio188", "gpio189", "gpio190", "gpio191", "gpio192", + "gpio193", "gpio194", "gpio195", "gpio196", "gpio197", "gpio198", + "gpio199", "gpio200", "gpio201", "gpio202", "gpio203", "gpio204", + "gpio205", "gpio206", "gpio207", "gpio208", "gpio209", +}; + +static const char * const aoss_cti_groups[] = { + "gpio50", "gpio51", "gpio60", "gpio61", +}; + +static const char *const atest_char_groups[] = { + "gpio130", "gpio131", "gpio132", "gpio133", + "gpio137", +}; + +static const char *const atest_usb_groups[] = { + "gpio71", "gpio72", "gpio74", "gpio130", + "gpio131", +}; + +static const char *const audio_ext_mclk0_groups[] = { + "gpio125", +}; + +static const char *const audio_ext_mclk1_groups[] = { + "gpio124", +}; + +static const char *const audio_ref_clk_groups[] = { + "gpio124", +}; + +static const char *const cam_aon_mclk2_groups[] = { + "gpio102", +}; + +static const char *const cam_aon_mclk4_groups[] = { + "gpio104", +}; + +static const char *const cam_mclk_groups[] = { + "gpio100", "gpio101", "gpio103", "gpio105", + "gpio106", "gpio108", +}; + +static const char *const cci_async_in_groups[] = { + "gpio15", "gpio163", "gpio164", +}; + +static const char *const cci_i2c_scl_groups[] = { + "gpio13", "gpio114", "gpio116", "gpio118", + "gpio120", "gpio153", +}; + +static const char *const cci_i2c_sda_groups[] = { + "gpio12", "gpio112", "gpio113", "gpio115", + "gpio117", "gpio119", +}; + +static const char *const cci_timer_groups[] = { + "gpio10", "gpio11", "gpio109", "gpio110", + "gpio111", +}; + +static const char *const cmu_rng_groups[] = { + "gpio95", "gpio96", "gpio112", "gpio127", + "gpio122", "gpio128", +}; + +static const char *const coex_uart1_rx_groups[] = { + "gpio148", +}; + +static const char *const coex_uart1_tx_groups[] = { + "gpio149", +}; + +static const char *const coex_uart2_rx_groups[] = { + "gpio150", +}; + +static const char *const coex_uart2_tx_groups[] = { + "gpio151", +}; + +static const char *const cri_trng_groups[] = { + "gpio187", +}; + +static const char *const dbg_out_clk_groups[] = { + "gpio92", +}; + +static const char *const ddr_bist_complete_groups[] = { + "gpio44", +}; + +static const char *const ddr_bist_fail_groups[] = { + "gpio40", +}; + +static const char *const ddr_bist_start_groups[] = { + "gpio41", +}; + +static const char *const ddr_bist_stop_groups[] = { + "gpio45", +}; + +static const char *const ddr_pxi0_groups[] = { + "gpio75", "gpio76", +}; + +static const char *const ddr_pxi1_groups[] = { + "gpio44", "gpio45", +}; + +static const char *const ddr_pxi2_groups[] = { + "gpio51", "gpio62", +}; + +static const char *const ddr_pxi3_groups[] = { + "gpio46", "gpio47", +}; + +static const char *const do_not_groups[] = { + "gpio36", "gpio37", "gpio38", "gpio39", + "gpio134", "gpio135", "gpio136", +}; + +static const char *const dp_hot_groups[] = { + "gpio47", +}; + +static const char *const gcc_gp1_groups[] = { + "gpio86", "gpio134", +}; + +static const char *const gcc_gp2_groups[] = { + "gpio87", "gpio135", +}; + +static const char *const gcc_gp3_groups[] = { + "gpio88", "gpio136", +}; + +static const char *const gnss_adc0_groups[] = { + "gpio89", "gpio91", +}; + +static const char *const gnss_adc1_groups[] = { + "gpio90", "gpio92", +}; + +static const char *const i2chub0_se0_groups[] = { + "gpio64", "gpio65", +}; + +static const char *const i2chub0_se1_groups[] = { + "gpio66", "gpio67", +}; + +static const char *const i2chub0_se2_groups[] = { + "gpio68", "gpio69", +}; + +static const char *const i2chub0_se3_groups[] = { + "gpio70", "gpio71", +}; + +static const char *const i2chub0_se4_groups[] = { + "gpio72", "gpio73", +}; + +static const char *const i2chub0_se5_groups[] = { + "gpio74", "gpio75", +}; + +static const char *const i2chub0_se6_groups[] = { + "gpio76", "gpio77", +}; + +static const char *const i2chub0_se7_groups[] = { + "gpio78", "gpio79", +}; + +static const char *const i2chub0_se8_groups[] = { + "gpio206", "gpio207", +}; + +static const char *const i2chub0_se9_groups[] = { + "gpio80", "gpio81", +}; + +static const char *const i2s0_data0_groups[] = { + "gpio127", +}; + +static const char *const i2s0_data1_groups[] = { + "gpio128", +}; + +static const char *const i2s0_sck_groups[] = { + "gpio126", +}; + +static const char *const i2s0_ws_groups[] = { + "gpio129", +}; + +static const char *const i2s1_data0_groups[] = { + "gpio122", +}; + +static const char *const i2s1_data1_groups[] = { + "gpio124", +}; + +static const char *const i2s1_sck_groups[] = { + "gpio121", +}; + +static const char *const i2s1_ws_groups[] = { + "gpio123", +}; + +static const char *const ibi_i3c_groups[] = { + "gpio0", "gpio1", "gpio4", "gpio5", + "gpio8", "gpio9", "gpio12", "gpio13", + "gpio32", "gpio33", "gpio36", "gpio37", + "gpio48", "gpio49", "gpio56", "gpio57", +}; + +static const char *const jitter_bist_groups[] = { + "gpio73", +}; + +static const char *const mdp_vsync_groups[] = { + "gpio86", "gpio87", "gpio133", "gpio137", +}; + +static const char *const mdp_vsync0_out_groups[] = { + "gpio86", +}; + +static const char *const mdp_vsync1_out_groups[] = { + "gpio86", +}; + +static const char *const mdp_vsync2_out_groups[] = { + "gpio87", +}; + +static const char *const mdp_vsync3_out_groups[] = { + "gpio87", +}; + +static const char *const mdp_vsync_e_groups[] = { + "gpio88", +}; + +static const char *const nav_gpio0_groups[] = { + "gpio154", +}; + +static const char *const nav_gpio1_groups[] = { + "gpio155", +}; + +static const char *const nav_gpio2_groups[] = { + "gpio152", +}; + +static const char *const nav_gpio3_groups[] = { + "gpio154", +}; + +static const char *const pcie0_clk_req_n_groups[] = { + "gpio95", +}; + +static const char *const pcie1_clk_req_n_groups[] = { + "gpio98", +}; + +static const char *const phase_flag_groups[] = { + "gpio0", "gpio1", "gpio3", "gpio4", + "gpio5", "gpio7", "gpio8", "gpio9", + "gpio11", "gpio12", "gpio13", "gpio15", + "gpio16", "gpio17", "gpio19", "gpio94", + "gpio95", "gpio96", "gpio109", "gpio111", + "gpio112", "gpio113", "gpio114", "gpio115", + "gpio116", "gpio117", "gpio118", "gpio119", + "gpio120", "gpio153", "gpio163", "gpio164", +}; + +static const char *const pll_bist_sync_groups[] = { + "gpio68", +}; + +static const char *const pll_clk_aux_groups[] = { + "gpio106", +}; + +static const char *const prng_rosc0_groups[] = { + "gpio186", +}; + +static const char *const prng_rosc1_groups[] = { + "gpio183", +}; + +static const char *const prng_rosc2_groups[] = { + "gpio182", +}; + +static const char *const prng_rosc3_groups[] = { + "gpio181", +}; + +static const char *const qdss_cti_groups[] = { + "gpio27", "gpio31", "gpio78", "gpio79", + "gpio82", "gpio83", "gpio159", "gpio162", +}; + +static const char *const qdss_gpio_groups[] = { + "gpio3", "gpio7", "gpio8", "gpio13", + "gpio15", "gpio100", "gpio101", "gpio102", + "gpio103", "gpio104", "gpio105", "gpio113", + "gpio114", "gpio115", "gpio116", "gpio117", + "gpio118", "gpio140", "gpio141", "gpio142", + "gpio143", "gpio144", "gpio145", "gpio146", + "gpio147", "gpio148", "gpio149", "gpio150", + "gpio151", "gpio152", "gpio153", "gpio154", + "gpio155", "gpio156", "gpio157", "gpio158", +}; + +static const char *const qlink_big_enable_groups[] = { + "gpio160", +}; + +static const char *const qlink_big_request_groups[] = { + "gpio159", +}; + +static const char *const qlink_little_enable_groups[] = { + "gpio157", +}; + +static const char *const qlink_little_request_groups[] = { + "gpio156", +}; + +static const char *const qlink_wmss_groups[] = { + "gpio158", +}; + +static const char *const qspi0_groups[] = { + "gpio134", +}; + +static const char *const qspi1_groups[] = { + "gpio136", +}; + +static const char *const qspi2_groups[] = { + "gpio56", +}; + +static const char *const qspi3_groups[] = { + "gpio57", +}; + +static const char *const qspi_clk_groups[] = { + "gpio135", +}; + +static const char *const qspi_cs_groups[] = { + "gpio58", "gpio59", +}; + +static const char *const qup1_se0_groups[] = { + "gpio32", "gpio33", "gpio34", "gpio35", +}; + +static const char *const qup1_se1_groups[] = { + "gpio36", "gpio37", "gpio38", "gpio39", +}; + +static const char *const qup1_se2_groups[] = { + "gpio40", "gpio41", "gpio42", "gpio43", + "gpio44", "gpio45", "gpio46", +}; + +static const char *const qup1_se3_groups[] = { + "gpio44", "gpio45", "gpio46", "gpio47", +}; + +static const char *const qup1_se4_groups[] = { + "gpio48", "gpio49", "gpio50", "gpio51", +}; + +static const char *const qup1_se5_groups[] = { + "gpio52", "gpio53", "gpio54", "gpio55", +}; + +static const char *const qup1_se6_groups[] = { + "gpio56", "gpio57", "gpio58", "gpio59", +}; + +static const char *const qup1_se7_groups[] = { + "gpio60", "gpio61", "gpio62", "gpio63", +}; + +static const char *const qup2_se0_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char *const qup2_se1_groups[] = { + "gpio4", "gpio5", "gpio6", "gpio7", +}; + +static const char *const qup2_se2_groups[] = { + "gpio8", "gpio9", "gpio10", "gpio11", + "gpio13", "gpio15", "gpio12", +}; + +static const char *const qup2_se3_groups[] = { + "gpio12", "gpio13", "gpio14", "gpio15", +}; + +static const char *const qup2_se4_groups[] = { + "gpio16", "gpio17", "gpio18", "gpio19", +}; + +static const char *const qup2_se5_groups[] = { + "gpio20", "gpio21", "gpio22", "gpio23", + "gpio23", +}; + +static const char *const qup2_se6_groups[] = { + "gpio24", "gpio25", "gpio26", "gpio27", +}; + +static const char *const qup2_se7_groups[] = { + "gpio28", "gpio29", "gpio30", "gpio31", +}; + +static const char *const sd_write_protect_groups[] = { + "gpio93", +}; + +static const char *const sdc40_groups[] = { + "gpio134", +}; + +static const char *const sdc41_groups[] = { + "gpio136", +}; + +static const char *const sdc42_groups[] = { + "gpio56", +}; + +static const char *const sdc43_groups[] = { + "gpio57", +}; + +static const char *const sdc4_clk_groups[] = { + "gpio135", +}; + +static const char *const sdc4_cmd_groups[] = { + "gpio59", +}; + +static const char *const tb_trig_sdc2_groups[] = { + "gpio8", +}; + +static const char *const tb_trig_sdc4_groups[] = { + "gpio58", +}; + +static const char *const tgu_ch0_trigout_groups[] = { + "gpio8", +}; + +static const char *const tgu_ch1_trigout_groups[] = { + "gpio9", +}; + +static const char *const tgu_ch2_trigout_groups[] = { + "gpio10", +}; + +static const char *const tgu_ch3_trigout_groups[] = { + "gpio11", +}; + +static const char *const tmess_prng0_groups[] = { + "gpio94", +}; + +static const char *const tmess_prng1_groups[] = { + "gpio95", +}; + +static const char *const tmess_prng2_groups[] = { + "gpio96", +}; + +static const char *const tmess_prng3_groups[] = { + "gpio109", +}; + +static const char *const tsense_pwm1_groups[] = { + "gpio58", +}; + +static const char *const tsense_pwm2_groups[] = { + "gpio58", +}; + +static const char *const tsense_pwm3_groups[] = { + "gpio58", +}; + +static const char *const uim0_clk_groups[] = { + "gpio131", +}; + +static const char *const uim0_data_groups[] = { + "gpio130", +}; + +static const char *const uim0_present_groups[] = { + "gpio47", +}; + +static const char *const uim0_reset_groups[] = { + "gpio132", +}; + +static const char *const uim1_clk_groups[] = { + "gpio135", +}; + +static const char *const uim1_data_groups[] = { + "gpio134", +}; + +static const char *const uim1_present_groups[] = { + "gpio76", +}; + +static const char *const uim1_reset_groups[] = { + "gpio136", +}; + +static const char *const usb1_hs_groups[] = { + "gpio89", +}; + +static const char *const usb_phy_groups[] = { + "gpio29", "gpio54", +}; + +static const char *const vfr_0_groups[] = { + "gpio150", +}; + +static const char *const vfr_1_groups[] = { + "gpio155", +}; + +static const char *const vsense_trigger_mirnat_groups[] = { + "gpio60", +}; + +static const struct pinfunction sm8650_functions[] = { + MSM_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(aoss_cti), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_usb), + MSM_PIN_FUNCTION(audio_ext_mclk0), + MSM_PIN_FUNCTION(audio_ext_mclk1), + MSM_PIN_FUNCTION(audio_ref_clk), + MSM_PIN_FUNCTION(cam_aon_mclk2), + MSM_PIN_FUNCTION(cam_aon_mclk4), + MSM_PIN_FUNCTION(cam_mclk), + MSM_PIN_FUNCTION(cci_async_in), + MSM_PIN_FUNCTION(cci_i2c_scl), + MSM_PIN_FUNCTION(cci_i2c_sda), + MSM_PIN_FUNCTION(cci_timer), + MSM_PIN_FUNCTION(cmu_rng), + MSM_PIN_FUNCTION(coex_uart1_rx), + MSM_PIN_FUNCTION(coex_uart1_tx), + MSM_PIN_FUNCTION(coex_uart2_rx), + MSM_PIN_FUNCTION(coex_uart2_tx), + MSM_PIN_FUNCTION(cri_trng), + MSM_PIN_FUNCTION(dbg_out_clk), + MSM_PIN_FUNCTION(ddr_bist_complete), + MSM_PIN_FUNCTION(ddr_bist_fail), + MSM_PIN_FUNCTION(ddr_bist_start), + MSM_PIN_FUNCTION(ddr_bist_stop), + MSM_PIN_FUNCTION(ddr_pxi0), + MSM_PIN_FUNCTION(ddr_pxi1), + MSM_PIN_FUNCTION(ddr_pxi2), + MSM_PIN_FUNCTION(ddr_pxi3), + MSM_PIN_FUNCTION(do_not), + MSM_PIN_FUNCTION(dp_hot), + MSM_PIN_FUNCTION(egpio), + MSM_PIN_FUNCTION(gcc_gp1), + MSM_PIN_FUNCTION(gcc_gp2), + MSM_PIN_FUNCTION(gcc_gp3), + MSM_PIN_FUNCTION(gnss_adc0), + MSM_PIN_FUNCTION(gnss_adc1), + MSM_PIN_FUNCTION(i2chub0_se0), + MSM_PIN_FUNCTION(i2chub0_se1), + MSM_PIN_FUNCTION(i2chub0_se2), + MSM_PIN_FUNCTION(i2chub0_se3), + MSM_PIN_FUNCTION(i2chub0_se4), + MSM_PIN_FUNCTION(i2chub0_se5), + MSM_PIN_FUNCTION(i2chub0_se6), + MSM_PIN_FUNCTION(i2chub0_se7), + MSM_PIN_FUNCTION(i2chub0_se8), + MSM_PIN_FUNCTION(i2chub0_se9), + MSM_PIN_FUNCTION(i2s0_data0), + MSM_PIN_FUNCTION(i2s0_data1), + MSM_PIN_FUNCTION(i2s0_sck), + MSM_PIN_FUNCTION(i2s0_ws), + MSM_PIN_FUNCTION(i2s1_data0), + MSM_PIN_FUNCTION(i2s1_data1), + MSM_PIN_FUNCTION(i2s1_sck), + MSM_PIN_FUNCTION(i2s1_ws), + MSM_PIN_FUNCTION(ibi_i3c), + MSM_PIN_FUNCTION(jitter_bist), + MSM_PIN_FUNCTION(mdp_vsync), + MSM_PIN_FUNCTION(mdp_vsync0_out), + MSM_PIN_FUNCTION(mdp_vsync1_out), + MSM_PIN_FUNCTION(mdp_vsync2_out), + MSM_PIN_FUNCTION(mdp_vsync3_out), + MSM_PIN_FUNCTION(mdp_vsync_e), + MSM_PIN_FUNCTION(nav_gpio0), + MSM_PIN_FUNCTION(nav_gpio1), + MSM_PIN_FUNCTION(nav_gpio2), + MSM_PIN_FUNCTION(nav_gpio3), + MSM_PIN_FUNCTION(pcie0_clk_req_n), + MSM_PIN_FUNCTION(pcie1_clk_req_n), + MSM_PIN_FUNCTION(phase_flag), + MSM_PIN_FUNCTION(pll_bist_sync), + MSM_PIN_FUNCTION(pll_clk_aux), + MSM_PIN_FUNCTION(prng_rosc0), + MSM_PIN_FUNCTION(prng_rosc1), + MSM_PIN_FUNCTION(prng_rosc2), + MSM_PIN_FUNCTION(prng_rosc3), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qdss_gpio), + MSM_PIN_FUNCTION(qlink_big_enable), + MSM_PIN_FUNCTION(qlink_big_request), + MSM_PIN_FUNCTION(qlink_little_enable), + MSM_PIN_FUNCTION(qlink_little_request), + MSM_PIN_FUNCTION(qlink_wmss), + MSM_PIN_FUNCTION(qspi0), + MSM_PIN_FUNCTION(qspi1), + MSM_PIN_FUNCTION(qspi2), + MSM_PIN_FUNCTION(qspi3), + MSM_PIN_FUNCTION(qspi_clk), + MSM_PIN_FUNCTION(qspi_cs), + MSM_PIN_FUNCTION(qup1_se0), + MSM_PIN_FUNCTION(qup1_se1), + MSM_PIN_FUNCTION(qup1_se2), + MSM_PIN_FUNCTION(qup1_se3), + MSM_PIN_FUNCTION(qup1_se4), + MSM_PIN_FUNCTION(qup1_se5), + MSM_PIN_FUNCTION(qup1_se6), + MSM_PIN_FUNCTION(qup1_se7), + MSM_PIN_FUNCTION(qup2_se0), + MSM_PIN_FUNCTION(qup2_se1), + MSM_PIN_FUNCTION(qup2_se2), + MSM_PIN_FUNCTION(qup2_se3), + MSM_PIN_FUNCTION(qup2_se4), + MSM_PIN_FUNCTION(qup2_se5), + MSM_PIN_FUNCTION(qup2_se6), + MSM_PIN_FUNCTION(qup2_se7), + MSM_PIN_FUNCTION(sd_write_protect), + MSM_PIN_FUNCTION(sdc40), + MSM_PIN_FUNCTION(sdc41), + MSM_PIN_FUNCTION(sdc42), + MSM_PIN_FUNCTION(sdc43), + MSM_PIN_FUNCTION(sdc4_clk), + MSM_PIN_FUNCTION(sdc4_cmd), + MSM_PIN_FUNCTION(tb_trig_sdc2), + MSM_PIN_FUNCTION(tb_trig_sdc4), + MSM_PIN_FUNCTION(tgu_ch0_trigout), + MSM_PIN_FUNCTION(tgu_ch1_trigout), + MSM_PIN_FUNCTION(tgu_ch2_trigout), + MSM_PIN_FUNCTION(tgu_ch3_trigout), + MSM_PIN_FUNCTION(tmess_prng0), + MSM_PIN_FUNCTION(tmess_prng1), + MSM_PIN_FUNCTION(tmess_prng2), + MSM_PIN_FUNCTION(tmess_prng3), + MSM_PIN_FUNCTION(tsense_pwm1), + MSM_PIN_FUNCTION(tsense_pwm2), + MSM_PIN_FUNCTION(tsense_pwm3), + MSM_PIN_FUNCTION(uim0_clk), + MSM_PIN_FUNCTION(uim0_data), + MSM_PIN_FUNCTION(uim0_present), + MSM_PIN_FUNCTION(uim0_reset), + MSM_PIN_FUNCTION(uim1_clk), + MSM_PIN_FUNCTION(uim1_data), + MSM_PIN_FUNCTION(uim1_present), + MSM_PIN_FUNCTION(uim1_reset), + MSM_PIN_FUNCTION(usb1_hs), + MSM_PIN_FUNCTION(usb_phy), + MSM_PIN_FUNCTION(vfr_0), + MSM_PIN_FUNCTION(vfr_1), + MSM_PIN_FUNCTION(vsense_trigger_mirnat), +}; + +/* + * Every pin is maintained as a single group, and missing or non-existing pin + * would be maintained as dummy group to synchronize pin group index with + * pin descriptor registered with pinctrl core. + * Clients would not be able to request these dummy pin groups. + */ +static const struct msm_pingroup sm8650_groups[] = { + [0] = PINGROUP(0, qup2_se0, ibi_i3c, phase_flag, _, _, _, _, _, _, egpio), + [1] = PINGROUP(1, qup2_se0, ibi_i3c, phase_flag, _, _, _, _, _, _, egpio), + [2] = PINGROUP(2, qup2_se0, _, _, _, _, _, _, _, _, egpio), + [3] = PINGROUP(3, qup2_se0, phase_flag, _, qdss_gpio, _, _, _, _, _, egpio), + [4] = PINGROUP(4, qup2_se1, ibi_i3c, phase_flag, _, _, _, _, _, _, egpio), + [5] = PINGROUP(5, qup2_se1, ibi_i3c, phase_flag, _, _, _, _, _, _, egpio), + [6] = PINGROUP(6, qup2_se1, _, _, _, _, _, _, _, _, egpio), + [7] = PINGROUP(7, qup2_se1, phase_flag, _, qdss_gpio, _, _, _, _, _, egpio), + [8] = PINGROUP(8, qup2_se2, ibi_i3c, tb_trig_sdc2, phase_flag, tgu_ch0_trigout, _, qdss_gpio, _, _, _), + [9] = PINGROUP(9, qup2_se2, ibi_i3c, phase_flag, tgu_ch1_trigout, _, _, _, _, _, _), + [10] = PINGROUP(10, qup2_se2, cci_timer, tgu_ch2_trigout, _, _, _, _, _, _, _), + [11] = PINGROUP(11, qup2_se2, cci_timer, phase_flag, tgu_ch3_trigout, _, _, _, _, _, _), + [12] = PINGROUP(12, qup2_se3, cci_i2c_sda, ibi_i3c, qup2_se2, phase_flag, _, _, _, _, _), + [13] = PINGROUP(13, qup2_se3, cci_i2c_scl, ibi_i3c, qup2_se2, phase_flag, _, qdss_gpio, _, _, _), + [14] = PINGROUP(14, qup2_se3, _, _, _, _, _, _, _, _, _), + [15] = PINGROUP(15, qup2_se3, cci_async_in, qup2_se2, phase_flag, _, qdss_gpio, _, _, _, _), + [16] = PINGROUP(16, qup2_se4, phase_flag, _, _, _, _, _, _, _, _), + [17] = PINGROUP(17, qup2_se4, phase_flag, _, _, _, _, _, _, _, _), + [18] = PINGROUP(18, qup2_se4, _, _, _, _, _, _, _, _, _), + [19] = PINGROUP(19, qup2_se4, phase_flag, _, _, _, _, _, _, _, _), + [20] = PINGROUP(20, qup2_se5, _, _, _, _, _, _, _, _, _), + [21] = PINGROUP(21, qup2_se5, _, _, _, _, _, _, _, _, _), + [22] = PINGROUP(22, qup2_se5, _, _, _, _, _, _, _, _, _), + [23] = PINGROUP(23, qup2_se5, qup2_se5, _, _, _, _, _, _, _, _), + [24] = PINGROUP(24, qup2_se6, _, _, _, _, _, _, _, _, _), + [25] = PINGROUP(25, qup2_se6, _, _, _, _, _, _, _, _, _), + [26] = PINGROUP(26, qup2_se6, _, _, _, _, _, _, _, _, _), + [27] = PINGROUP(27, qup2_se6, qdss_cti, _, _, _, _, _, _, _, _), + [28] = PINGROUP(28, qup2_se7, _, _, _, _, _, _, _, _, _), + [29] = PINGROUP(29, qup2_se7, usb_phy, _, _, _, _, _, _, _, _), + [30] = PINGROUP(30, qup2_se7, _, _, _, _, _, _, _, _, _), + [31] = PINGROUP(31, qup2_se7, qdss_cti, _, _, _, _, _, _, _, _), + [32] = PINGROUP(32, qup1_se0, ibi_i3c, _, _, _, _, _, _, _, _), + [33] = PINGROUP(33, qup1_se0, ibi_i3c, _, _, _, _, _, _, _, _), + [34] = PINGROUP(34, qup1_se0, _, _, _, _, _, _, _, _, _), + [35] = PINGROUP(35, qup1_se0, _, _, _, _, _, _, _, _, _), + [36] = PINGROUP(36, qup1_se1, do_not, ibi_i3c, _, _, _, _, _, _, _), + [37] = PINGROUP(37, qup1_se1, do_not, ibi_i3c, _, _, _, _, _, _, _), + [38] = PINGROUP(38, qup1_se1, do_not, _, _, _, _, _, _, _, _), + [39] = PINGROUP(39, qup1_se1, do_not, _, _, _, _, _, _, _, _), + [40] = PINGROUP(40, qup1_se2, ddr_bist_fail, _, _, _, _, _, _, _, _), + [41] = PINGROUP(41, qup1_se2, ddr_bist_start, _, _, _, _, _, _, _, _), + [42] = PINGROUP(42, qup1_se2, _, _, _, _, _, _, _, _, _), + [43] = PINGROUP(43, qup1_se2, _, _, _, _, _, _, _, _, _), + [44] = PINGROUP(44, qup1_se3, qup1_se2, ddr_bist_complete, ddr_pxi1, _, _, _, _, _, _), + [45] = PINGROUP(45, qup1_se3, qup1_se2, ddr_bist_stop, ddr_pxi1, _, _, _, _, _, _), + [46] = PINGROUP(46, qup1_se3, qup1_se2, ddr_pxi3, _, _, _, _, _, _, _), + [47] = PINGROUP(47, qup1_se3, uim0_present, dp_hot, ddr_pxi3, _, _, _, _, _, _), + [48] = PINGROUP(48, qup1_se4, ibi_i3c, _, _, _, _, _, _, _, _), + [49] = PINGROUP(49, qup1_se4, ibi_i3c, _, _, _, _, _, _, _, _), + [50] = PINGROUP(50, qup1_se4, aoss_cti, _, _, _, _, _, _, _, _), + [51] = PINGROUP(51, qup1_se4, aoss_cti, ddr_pxi2, _, _, _, _, _, _, _), + [52] = PINGROUP(52, qup1_se5, _, _, _, _, _, _, _, _, _), + [53] = PINGROUP(53, qup1_se5, _, _, _, _, _, _, _, _, _), + [54] = PINGROUP(54, qup1_se5, usb_phy, _, _, _, _, _, _, _, _), + [55] = PINGROUP(55, qup1_se5, _, _, _, _, _, _, _, _, _), + [56] = PINGROUP(56, qup1_se6, ibi_i3c, qspi2, sdc42, _, _, _, _, _, _), + [57] = PINGROUP(57, qup1_se6, ibi_i3c, qspi3, sdc43, _, _, _, _, _, _), + [58] = PINGROUP(58, qup1_se6, qspi_cs, tb_trig_sdc4, tsense_pwm1, tsense_pwm2, tsense_pwm3, _, _, _, _), + [59] = PINGROUP(59, qup1_se6, _, qspi_cs, sdc4_cmd, _, _, _, _, _, _), + [60] = PINGROUP(60, qup1_se7, aoss_cti, vsense_trigger_mirnat, _, _, _, _, _, _, _), + [61] = PINGROUP(61, qup1_se7, aoss_cti, _, _, _, _, _, _, _, _), + [62] = PINGROUP(62, qup1_se7, ddr_pxi2, _, _, _, _, _, _, _, _), + [63] = PINGROUP(63, qup1_se7, _, _, _, _, _, _, _, _, _), + [64] = PINGROUP(64, i2chub0_se0, _, _, _, _, _, _, _, _, _), + [65] = PINGROUP(65, i2chub0_se0, _, _, _, _, _, _, _, _, _), + [66] = PINGROUP(66, i2chub0_se1, _, _, _, _, _, _, _, _, _), + [67] = PINGROUP(67, i2chub0_se1, _, _, _, _, _, _, _, _, _), + [68] = PINGROUP(68, i2chub0_se2, pll_bist_sync, _, _, _, _, _, _, _, _), + [69] = PINGROUP(69, i2chub0_se2, _, _, _, _, _, _, _, _, _), + [70] = PINGROUP(70, i2chub0_se3, _, _, _, _, _, _, _, _, _), + [71] = PINGROUP(71, i2chub0_se3, _, atest_usb, _, _, _, _, _, _, _), + [72] = PINGROUP(72, i2chub0_se4, _, atest_usb, _, _, _, _, _, _, _), + [73] = PINGROUP(73, i2chub0_se4, jitter_bist, _, _, _, _, _, _, _, _), + [74] = PINGROUP(74, i2chub0_se5, atest_usb, _, _, _, _, _, _, _, _), + [75] = PINGROUP(75, i2chub0_se5, ddr_pxi0, _, _, _, _, _, _, _, _), + [76] = PINGROUP(76, i2chub0_se6, ddr_pxi0, uim1_present, _, _, _, _, _, _, _), + [77] = PINGROUP(77, i2chub0_se6, _, _, _, _, _, _, _, _, _), + [78] = PINGROUP(78, i2chub0_se7, qdss_cti, _, _, _, _, _, _, _, _), + [79] = PINGROUP(79, i2chub0_se7, qdss_cti, _, _, _, _, _, _, _, _), + [80] = PINGROUP(80, i2chub0_se9, _, _, _, _, _, _, _, _, _), + [81] = PINGROUP(81, i2chub0_se9, _, _, _, _, _, _, _, _, _), + [82] = PINGROUP(82, qdss_cti, _, _, _, _, _, _, _, _, _), + [83] = PINGROUP(83, qdss_cti, _, _, _, _, _, _, _, _, _), + [84] = PINGROUP(84, _, _, _, _, _, _, _, _, _, _), + [85] = PINGROUP(85, _, _, _, _, _, _, _, _, _, _), + [86] = PINGROUP(86, mdp_vsync, mdp_vsync0_out, mdp_vsync1_out, gcc_gp1, _, _, _, _, _, _), + [87] = PINGROUP(87, mdp_vsync, mdp_vsync2_out, mdp_vsync3_out, gcc_gp2, _, _, _, _, _, _), + [88] = PINGROUP(88, mdp_vsync_e, gcc_gp3, _, _, _, _, _, _, _, _), + [89] = PINGROUP(89, usb1_hs, gnss_adc0, _, _, _, _, _, _, _, _), + [90] = PINGROUP(90, gnss_adc1, _, _, _, _, _, _, _, _, _), + [91] = PINGROUP(91, _, gnss_adc0, _, _, _, _, _, _, _, _), + [92] = PINGROUP(92, dbg_out_clk, gnss_adc1, _, _, _, _, _, _, _, _), + [93] = PINGROUP(93, sd_write_protect, _, _, _, _, _, _, _, _, _), + [94] = PINGROUP(94, cmu_rng, phase_flag, tmess_prng0, _, _, _, _, _, _, _), + [95] = PINGROUP(95, pcie0_clk_req_n, cmu_rng, phase_flag, tmess_prng1, _, _, _, _, _, _), + [96] = PINGROUP(96, cmu_rng, phase_flag, tmess_prng2, _, _, _, _, _, _, _), + [97] = PINGROUP(97, _, _, _, _, _, _, _, _, _, _), + [98] = PINGROUP(98, pcie1_clk_req_n, _, _, _, _, _, _, _, _, _), + [99] = PINGROUP(99, _, _, _, _, _, _, _, _, _, _), + [100] = PINGROUP(100, cam_mclk, qdss_gpio, _, _, _, _, _, _, _, _), + [101] = PINGROUP(101, cam_mclk, qdss_gpio, _, _, _, _, _, _, _, _), + [102] = PINGROUP(102, cam_aon_mclk2, qdss_gpio, _, _, _, _, _, _, _, _), + [103] = PINGROUP(103, cam_mclk, qdss_gpio, _, _, _, _, _, _, _, _), + [104] = PINGROUP(104, cam_aon_mclk4, qdss_gpio, _, _, _, _, _, _, _, _), + [105] = PINGROUP(105, cam_mclk, qdss_gpio, _, _, _, _, _, _, _, _), + [106] = PINGROUP(106, cam_mclk, pll_clk_aux, _, _, _, _, _, _, _, _), + [107] = PINGROUP(107, _, _, _, _, _, _, _, _, _, _), + [108] = PINGROUP(108, cam_mclk, _, _, _, _, _, _, _, _, _), + [109] = PINGROUP(109, cci_timer, phase_flag, tmess_prng3, _, _, _, _, _, _, _), + [110] = PINGROUP(110, cci_timer, _, _, _, _, _, _, _, _, _), + [111] = PINGROUP(111, cci_timer, phase_flag, _, _, _, _, _, _, _, _), + [112] = PINGROUP(112, cci_i2c_sda, cmu_rng, phase_flag, _, _, _, _, _, _, _), + [113] = PINGROUP(113, cci_i2c_sda, phase_flag, _, qdss_gpio, _, _, _, _, _, _), + [114] = PINGROUP(114, cci_i2c_scl, phase_flag, _, qdss_gpio, _, _, _, _, _, _), + [115] = PINGROUP(115, cci_i2c_sda, phase_flag, _, qdss_gpio, _, _, _, _, _, _), + [116] = PINGROUP(116, cci_i2c_scl, phase_flag, _, qdss_gpio, _, _, _, _, _, _), + [117] = PINGROUP(117, cci_i2c_sda, phase_flag, _, qdss_gpio, _, _, _, _, _, _), + [118] = PINGROUP(118, cci_i2c_scl, phase_flag, _, qdss_gpio, _, _, _, _, _, _), + [119] = PINGROUP(119, cci_i2c_sda, phase_flag, _, _, _, _, _, _, _, _), + [120] = PINGROUP(120, cci_i2c_scl, phase_flag, _, _, _, _, _, _, _, _), + [121] = PINGROUP(121, i2s1_sck, _, _, _, _, _, _, _, _, _), + [122] = PINGROUP(122, i2s1_data0, cmu_rng, _, _, _, _, _, _, _, _), + [123] = PINGROUP(123, i2s1_ws, _, _, _, _, _, _, _, _, _), + [124] = PINGROUP(124, i2s1_data1, audio_ext_mclk1, audio_ref_clk, _, _, _, _, _, _, _), + [125] = PINGROUP(125, audio_ext_mclk0, _, _, _, _, _, _, _, _, _), + [126] = PINGROUP(126, i2s0_sck, _, _, _, _, _, _, _, _, _), + [127] = PINGROUP(127, i2s0_data0, cmu_rng, _, _, _, _, _, _, _, _), + [128] = PINGROUP(128, i2s0_data1, cmu_rng, _, _, _, _, _, _, _, _), + [129] = PINGROUP(129, i2s0_ws, cmu_rng, _, _, _, _, _, _, _, _), + [130] = PINGROUP(130, uim0_data, atest_usb, atest_char, _, _, _, _, _, _, _), + [131] = PINGROUP(131, uim0_clk, atest_usb, atest_char, _, _, _, _, _, _, _), + [132] = PINGROUP(132, uim0_reset, atest_char, _, _, _, _, _, _, _, _), + [133] = PINGROUP(133, mdp_vsync, atest_char, _, _, _, _, _, _, _, _), + [134] = PINGROUP(134, uim1_data, do_not, qspi0, sdc40, gcc_gp1, _, _, _, _, _), + [135] = PINGROUP(135, uim1_clk, do_not, qspi_clk, sdc4_clk, gcc_gp2, _, _, _, _, _), + [136] = PINGROUP(136, uim1_reset, do_not, qspi1, sdc41, gcc_gp3, _, _, _, _, _), + [137] = PINGROUP(137, mdp_vsync, atest_char, _, _, _, _, _, _, _, _), + [138] = PINGROUP(138, _, _, _, _, _, _, _, _, _, _), + [139] = PINGROUP(139, _, _, _, _, _, _, _, _, _, _), + [140] = PINGROUP(140, _, _, qdss_gpio, _, _, _, _, _, _, _), + [141] = PINGROUP(141, _, _, qdss_gpio, _, _, _, _, _, _, _), + [142] = PINGROUP(142, _, _, qdss_gpio, _, _, _, _, _, _, _), + [143] = PINGROUP(143, _, _, qdss_gpio, _, _, _, _, _, _, _), + [144] = PINGROUP(144, _, qdss_gpio, _, _, _, _, _, _, _, _), + [145] = PINGROUP(145, _, qdss_gpio, _, _, _, _, _, _, _, _), + [146] = PINGROUP(146, _, qdss_gpio, _, _, _, _, _, _, _, _), + [147] = PINGROUP(147, _, qdss_gpio, _, _, _, _, _, _, _, _), + [148] = PINGROUP(148, coex_uart1_rx, qdss_gpio, _, _, _, _, _, _, _, _), + [149] = PINGROUP(149, coex_uart1_tx, qdss_gpio, _, _, _, _, _, _, _, _), + [150] = PINGROUP(150, _, vfr_0, coex_uart2_rx, qdss_gpio, _, _, _, _, _, _), + [151] = PINGROUP(151, _, coex_uart2_tx, qdss_gpio, _, _, _, _, _, _, _), + [152] = PINGROUP(152, nav_gpio2, _, qdss_gpio, _, _, _, _, _, _, _), + [153] = PINGROUP(153, cci_i2c_scl, phase_flag, _, qdss_gpio, _, _, _, _, _, _), + [154] = PINGROUP(154, nav_gpio0, nav_gpio3, qdss_gpio, _, _, _, _, _, _, _), + [155] = PINGROUP(155, nav_gpio1, vfr_1, qdss_gpio, _, _, _, _, _, _, _), + [156] = PINGROUP(156, qlink_little_request, qdss_gpio, _, _, _, _, _, _, _, _), + [157] = PINGROUP(157, qlink_little_enable, qdss_gpio, _, _, _, _, _, _, _, _), + [158] = PINGROUP(158, qlink_wmss, qdss_gpio, _, _, _, _, _, _, _, _), + [159] = PINGROUP(159, qlink_big_request, qdss_cti, _, _, _, _, _, _, _, _), + [160] = PINGROUP(160, qlink_big_enable, _, _, _, _, _, _, _, _, _), + [161] = PINGROUP(161, _, _, _, _, _, _, _, _, _, _), + [162] = PINGROUP(162, qdss_cti, _, _, _, _, _, _, _, _, _), + [163] = PINGROUP(163, cci_async_in, phase_flag, _, _, _, _, _, _, _, _), + [164] = PINGROUP(164, cci_async_in, phase_flag, _, _, _, _, _, _, _, _), + [165] = PINGROUP(165, _, _, _, _, _, _, _, _, _, egpio), + [166] = PINGROUP(166, _, _, _, _, _, _, _, _, _, egpio), + [167] = PINGROUP(167, _, _, _, _, _, _, _, _, _, egpio), + [168] = PINGROUP(168, _, _, _, _, _, _, _, _, _, egpio), + [169] = PINGROUP(169, _, _, _, _, _, _, _, _, _, egpio), + [170] = PINGROUP(170, _, _, _, _, _, _, _, _, _, egpio), + [171] = PINGROUP(171, _, _, _, _, _, _, _, _, _, egpio), + [172] = PINGROUP(172, _, _, _, _, _, _, _, _, _, egpio), + [173] = PINGROUP(173, _, _, _, _, _, _, _, _, _, egpio), + [174] = PINGROUP(174, _, _, _, _, _, _, _, _, _, egpio), + [175] = PINGROUP(175, _, _, _, _, _, _, _, _, _, egpio), + [176] = PINGROUP(176, _, _, _, _, _, _, _, _, _, egpio), + [177] = PINGROUP(177, _, _, _, _, _, _, _, _, _, egpio), + [178] = PINGROUP(178, _, _, _, _, _, _, _, _, _, egpio), + [179] = PINGROUP(179, _, _, _, _, _, _, _, _, _, egpio), + [180] = PINGROUP(180, _, _, _, _, _, _, _, _, _, egpio), + [181] = PINGROUP(181, prng_rosc3, _, _, _, _, _, _, _, _, egpio), + [182] = PINGROUP(182, prng_rosc2, _, _, _, _, _, _, _, _, egpio), + [183] = PINGROUP(183, prng_rosc1, _, _, _, _, _, _, _, _, egpio), + [184] = PINGROUP(184, _, _, _, _, _, _, _, _, _, egpio), + [185] = PINGROUP(185, _, _, _, _, _, _, _, _, _, egpio), + [186] = PINGROUP(186, prng_rosc0, _, _, _, _, _, _, _, _, egpio), + [187] = PINGROUP(187, cri_trng, _, _, _, _, _, _, _, _, egpio), + [188] = PINGROUP(188, _, _, _, _, _, _, _, _, _, egpio), + [189] = PINGROUP(189, _, _, _, _, _, _, _, _, _, egpio), + [190] = PINGROUP(190, _, _, _, _, _, _, _, _, _, egpio), + [191] = PINGROUP(191, _, _, _, _, _, _, _, _, _, egpio), + [192] = PINGROUP(192, _, _, _, _, _, _, _, _, _, egpio), + [193] = PINGROUP(193, _, _, _, _, _, _, _, _, _, egpio), + [194] = PINGROUP(194, _, _, _, _, _, _, _, _, _, egpio), + [195] = PINGROUP(195, _, _, _, _, _, _, _, _, _, egpio), + [196] = PINGROUP(196, _, _, _, _, _, _, _, _, _, egpio), + [197] = PINGROUP(197, _, _, _, _, _, _, _, _, _, egpio), + [198] = PINGROUP(198, _, _, _, _, _, _, _, _, _, egpio), + [199] = PINGROUP(199, _, _, _, _, _, _, _, _, _, egpio), + [200] = PINGROUP(200, _, _, _, _, _, _, _, _, _, egpio), + [201] = PINGROUP(201, _, _, _, _, _, _, _, _, _, egpio), + [202] = PINGROUP(202, _, _, _, _, _, _, _, _, _, egpio), + [203] = PINGROUP(203, _, _, _, _, _, _, _, _, _, egpio), + [204] = PINGROUP(204, _, _, _, _, _, _, _, _, _, egpio), + [205] = PINGROUP(205, _, _, _, _, _, _, _, _, _, egpio), + [206] = PINGROUP(206, i2chub0_se8, _, _, _, _, _, _, _, _, egpio), + [207] = PINGROUP(207, i2chub0_se8, _, _, _, _, _, _, _, _, egpio), + [208] = PINGROUP(208, _, _, _, _, _, _, _, _, _, egpio), + [209] = PINGROUP(209, _, _, _, _, _, _, _, _, _, egpio), + [210] = UFS_RESET(ufs_reset, 0xde004, 0xdf000), + [211] = SDC_QDSD_PINGROUP(sdc2_clk, 0xd6000, 14, 6), + [212] = SDC_QDSD_PINGROUP(sdc2_cmd, 0xd6000, 11, 3), + [213] = SDC_QDSD_PINGROUP(sdc2_data, 0xd6000, 9, 0), +}; + +static const struct msm_gpio_wakeirq_map sm8650_pdc_map[] = { + { 0, 94 }, { 3, 105 }, { 4, 78 }, { 7, 67 }, { 8, 64 }, + { 11, 121 }, { 12, 71 }, { 15, 82 }, { 18, 75 }, { 19, 63 }, + { 20, 114 }, { 23, 84 }, { 27, 61 }, { 29, 112 }, { 31, 113 }, + { 32, 66 }, { 35, 52 }, { 36, 123 }, { 39, 56 }, { 43, 59 }, + { 46, 79 }, { 47, 124 }, { 48, 125 }, { 51, 93 }, { 54, 60 }, + { 55, 104 }, { 56, 72 }, { 57, 77 }, { 59, 51 }, { 63, 85 }, + { 64, 107 }, { 65, 108 }, { 66, 109 }, { 67, 83 }, { 68, 110 }, + { 69, 111 }, { 75, 96 }, { 76, 97 }, { 77, 98 }, { 80, 89 }, + { 81, 90 }, { 84, 106 }, { 85, 100 }, { 86, 87 }, { 87, 88 }, + { 88, 65 }, { 90, 92 }, { 92, 99 }, { 95, 118 }, { 96, 119 }, + { 98, 101 }, { 99, 62 }, { 112, 120 }, { 133, 80 }, { 136, 69 }, + { 137, 81 }, { 148, 57 }, { 150, 58 }, { 152, 127 }, { 153, 74 }, + { 154, 126 }, { 155, 73 }, { 156, 128 }, { 159, 129 }, { 162, 86 }, + { 163, 122 }, { 166, 139 }, { 169, 140 }, { 171, 141 }, { 172, 142 }, + { 174, 102 }, { 176, 143 }, { 177, 55 }, { 181, 144 }, { 182, 145 }, + { 185, 146 }, { 187, 95 }, { 188, 130 }, { 190, 131 }, { 191, 132 }, + { 192, 133 }, { 193, 134 }, { 195, 68 }, { 196, 135 }, { 197, 136 }, + { 198, 54 }, { 199, 103 }, { 200, 53 }, { 201, 137 }, { 202, 70 }, + { 203, 138 }, { 204, 76 }, { 205, 91 }, +}; + +static const struct msm_pinctrl_soc_data sm8650_tlmm = { + .pins = sm8650_pins, + .npins = ARRAY_SIZE(sm8650_pins), + .functions = sm8650_functions, + .nfunctions = ARRAY_SIZE(sm8650_functions), + .groups = sm8650_groups, + .ngroups = ARRAY_SIZE(sm8650_groups), + .ngpios = 211, + .wakeirq_map = sm8650_pdc_map, + .nwakeirq_map = ARRAY_SIZE(sm8650_pdc_map), + .egpio_func = 10, +}; + +static int sm8650_tlmm_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &sm8650_tlmm); +} + +static const struct of_device_id sm8650_tlmm_of_match[] = { + { .compatible = "qcom,sm8650-tlmm", }, + {}, +}; + +static struct platform_driver sm8650_tlmm_driver = { + .driver = { + .name = "sm8650-tlmm", + .of_match_table = sm8650_tlmm_of_match, + }, + .probe = sm8650_tlmm_probe, + .remove_new = msm_pinctrl_remove, +}; + +static int __init sm8650_tlmm_init(void) +{ + return platform_driver_register(&sm8650_tlmm_driver); +} +arch_initcall(sm8650_tlmm_init); + +static void __exit sm8650_tlmm_exit(void) +{ + platform_driver_unregister(&sm8650_tlmm_driver); +} +module_exit(sm8650_tlmm_exit); + +MODULE_DESCRIPTION("QTI SM8650 TLMM driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, sm8650_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-x1e80100.c b/drivers/pinctrl/qcom/pinctrl-x1e80100.c new file mode 100644 index 000000000000..e30e93840357 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-x1e80100.c @@ -0,0 +1,1876 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 + +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9 \ + }, \ + .nfuncs = 10, \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .intr_target_reg = 0x8 + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .i2c_pull_bit = 13, \ + .egpio_enable = 12, \ + .egpio_present = 11, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_target_bit = 5, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +#define UFS_RESET(pg_name, offset) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = offset, \ + .io_reg = offset + 0x4, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +static const struct pinctrl_pin_desc x1e80100_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), + PINCTRL_PIN(128, "GPIO_128"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "GPIO_133"), + PINCTRL_PIN(134, "GPIO_134"), + PINCTRL_PIN(135, "GPIO_135"), + PINCTRL_PIN(136, "GPIO_136"), + PINCTRL_PIN(137, "GPIO_137"), + PINCTRL_PIN(138, "GPIO_138"), + PINCTRL_PIN(139, "GPIO_139"), + PINCTRL_PIN(140, "GPIO_140"), + PINCTRL_PIN(141, "GPIO_141"), + PINCTRL_PIN(142, "GPIO_142"), + PINCTRL_PIN(143, "GPIO_143"), + PINCTRL_PIN(144, "GPIO_144"), + PINCTRL_PIN(145, "GPIO_145"), + PINCTRL_PIN(146, "GPIO_146"), + PINCTRL_PIN(147, "GPIO_147"), + PINCTRL_PIN(148, "GPIO_148"), + PINCTRL_PIN(149, "GPIO_149"), + PINCTRL_PIN(150, "GPIO_150"), + PINCTRL_PIN(151, "GPIO_151"), + PINCTRL_PIN(152, "GPIO_152"), + PINCTRL_PIN(153, "GPIO_153"), + PINCTRL_PIN(154, "GPIO_154"), + PINCTRL_PIN(155, "GPIO_155"), + PINCTRL_PIN(156, "GPIO_156"), + PINCTRL_PIN(157, "GPIO_157"), + PINCTRL_PIN(158, "GPIO_158"), + PINCTRL_PIN(159, "GPIO_159"), + PINCTRL_PIN(160, "GPIO_160"), + PINCTRL_PIN(161, "GPIO_161"), + PINCTRL_PIN(162, "GPIO_162"), + PINCTRL_PIN(163, "GPIO_163"), + PINCTRL_PIN(164, "GPIO_164"), + PINCTRL_PIN(165, "GPIO_165"), + PINCTRL_PIN(166, "GPIO_166"), + PINCTRL_PIN(167, "GPIO_167"), + PINCTRL_PIN(168, "GPIO_168"), + PINCTRL_PIN(169, "GPIO_169"), + PINCTRL_PIN(170, "GPIO_170"), + PINCTRL_PIN(171, "GPIO_171"), + PINCTRL_PIN(172, "GPIO_172"), + PINCTRL_PIN(173, "GPIO_173"), + PINCTRL_PIN(174, "GPIO_174"), + PINCTRL_PIN(175, "GPIO_175"), + PINCTRL_PIN(176, "GPIO_176"), + PINCTRL_PIN(177, "GPIO_177"), + PINCTRL_PIN(178, "GPIO_178"), + PINCTRL_PIN(179, "GPIO_179"), + PINCTRL_PIN(180, "GPIO_180"), + PINCTRL_PIN(181, "GPIO_181"), + PINCTRL_PIN(182, "GPIO_182"), + PINCTRL_PIN(183, "GPIO_183"), + PINCTRL_PIN(184, "GPIO_184"), + PINCTRL_PIN(185, "GPIO_185"), + PINCTRL_PIN(186, "GPIO_186"), + PINCTRL_PIN(187, "GPIO_187"), + PINCTRL_PIN(188, "GPIO_188"), + PINCTRL_PIN(189, "GPIO_189"), + PINCTRL_PIN(190, "GPIO_190"), + PINCTRL_PIN(191, "GPIO_191"), + PINCTRL_PIN(192, "GPIO_192"), + PINCTRL_PIN(193, "GPIO_193"), + PINCTRL_PIN(194, "GPIO_194"), + PINCTRL_PIN(195, "GPIO_195"), + PINCTRL_PIN(196, "GPIO_196"), + PINCTRL_PIN(197, "GPIO_197"), + PINCTRL_PIN(198, "GPIO_198"), + PINCTRL_PIN(199, "GPIO_199"), + PINCTRL_PIN(200, "GPIO_200"), + PINCTRL_PIN(201, "GPIO_201"), + PINCTRL_PIN(202, "GPIO_202"), + PINCTRL_PIN(203, "GPIO_203"), + PINCTRL_PIN(204, "GPIO_204"), + PINCTRL_PIN(205, "GPIO_205"), + PINCTRL_PIN(206, "GPIO_206"), + PINCTRL_PIN(207, "GPIO_207"), + PINCTRL_PIN(208, "GPIO_208"), + PINCTRL_PIN(209, "GPIO_209"), + PINCTRL_PIN(210, "GPIO_210"), + PINCTRL_PIN(211, "GPIO_211"), + PINCTRL_PIN(212, "GPIO_212"), + PINCTRL_PIN(213, "GPIO_213"), + PINCTRL_PIN(214, "GPIO_214"), + PINCTRL_PIN(215, "GPIO_215"), + PINCTRL_PIN(216, "GPIO_216"), + PINCTRL_PIN(217, "GPIO_217"), + PINCTRL_PIN(218, "GPIO_218"), + PINCTRL_PIN(219, "GPIO_219"), + PINCTRL_PIN(220, "GPIO_220"), + PINCTRL_PIN(221, "GPIO_221"), + PINCTRL_PIN(222, "GPIO_222"), + PINCTRL_PIN(223, "GPIO_223"), + PINCTRL_PIN(224, "GPIO_224"), + PINCTRL_PIN(225, "GPIO_225"), + PINCTRL_PIN(226, "GPIO_226"), + PINCTRL_PIN(227, "GPIO_227"), + PINCTRL_PIN(228, "GPIO_228"), + PINCTRL_PIN(229, "GPIO_229"), + PINCTRL_PIN(230, "GPIO_230"), + PINCTRL_PIN(231, "GPIO_231"), + PINCTRL_PIN(232, "GPIO_232"), + PINCTRL_PIN(233, "GPIO_233"), + PINCTRL_PIN(234, "GPIO_234"), + PINCTRL_PIN(235, "GPIO_235"), + PINCTRL_PIN(236, "GPIO_236"), + PINCTRL_PIN(237, "GPIO_237"), + PINCTRL_PIN(238, "UFS_RESET"), + PINCTRL_PIN(239, "SDC2_CLK"), + PINCTRL_PIN(240, "SDC2_CMD"), + PINCTRL_PIN(241, "SDC2_DATA"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); +DECLARE_MSM_GPIO_PINS(123); +DECLARE_MSM_GPIO_PINS(124); +DECLARE_MSM_GPIO_PINS(125); +DECLARE_MSM_GPIO_PINS(126); +DECLARE_MSM_GPIO_PINS(127); +DECLARE_MSM_GPIO_PINS(128); +DECLARE_MSM_GPIO_PINS(129); +DECLARE_MSM_GPIO_PINS(130); +DECLARE_MSM_GPIO_PINS(131); +DECLARE_MSM_GPIO_PINS(132); +DECLARE_MSM_GPIO_PINS(133); +DECLARE_MSM_GPIO_PINS(134); +DECLARE_MSM_GPIO_PINS(135); +DECLARE_MSM_GPIO_PINS(136); +DECLARE_MSM_GPIO_PINS(137); +DECLARE_MSM_GPIO_PINS(138); +DECLARE_MSM_GPIO_PINS(139); +DECLARE_MSM_GPIO_PINS(140); +DECLARE_MSM_GPIO_PINS(141); +DECLARE_MSM_GPIO_PINS(142); +DECLARE_MSM_GPIO_PINS(143); +DECLARE_MSM_GPIO_PINS(144); +DECLARE_MSM_GPIO_PINS(145); +DECLARE_MSM_GPIO_PINS(146); +DECLARE_MSM_GPIO_PINS(147); +DECLARE_MSM_GPIO_PINS(148); +DECLARE_MSM_GPIO_PINS(149); +DECLARE_MSM_GPIO_PINS(150); +DECLARE_MSM_GPIO_PINS(151); +DECLARE_MSM_GPIO_PINS(152); +DECLARE_MSM_GPIO_PINS(153); +DECLARE_MSM_GPIO_PINS(154); +DECLARE_MSM_GPIO_PINS(155); +DECLARE_MSM_GPIO_PINS(156); +DECLARE_MSM_GPIO_PINS(157); +DECLARE_MSM_GPIO_PINS(158); +DECLARE_MSM_GPIO_PINS(159); +DECLARE_MSM_GPIO_PINS(160); +DECLARE_MSM_GPIO_PINS(161); +DECLARE_MSM_GPIO_PINS(162); +DECLARE_MSM_GPIO_PINS(163); +DECLARE_MSM_GPIO_PINS(164); +DECLARE_MSM_GPIO_PINS(165); +DECLARE_MSM_GPIO_PINS(166); +DECLARE_MSM_GPIO_PINS(167); +DECLARE_MSM_GPIO_PINS(168); +DECLARE_MSM_GPIO_PINS(169); +DECLARE_MSM_GPIO_PINS(170); +DECLARE_MSM_GPIO_PINS(171); +DECLARE_MSM_GPIO_PINS(172); +DECLARE_MSM_GPIO_PINS(173); +DECLARE_MSM_GPIO_PINS(174); +DECLARE_MSM_GPIO_PINS(175); +DECLARE_MSM_GPIO_PINS(176); +DECLARE_MSM_GPIO_PINS(177); +DECLARE_MSM_GPIO_PINS(178); +DECLARE_MSM_GPIO_PINS(179); +DECLARE_MSM_GPIO_PINS(180); +DECLARE_MSM_GPIO_PINS(181); +DECLARE_MSM_GPIO_PINS(182); +DECLARE_MSM_GPIO_PINS(183); +DECLARE_MSM_GPIO_PINS(184); +DECLARE_MSM_GPIO_PINS(185); +DECLARE_MSM_GPIO_PINS(186); +DECLARE_MSM_GPIO_PINS(187); +DECLARE_MSM_GPIO_PINS(188); +DECLARE_MSM_GPIO_PINS(189); +DECLARE_MSM_GPIO_PINS(190); +DECLARE_MSM_GPIO_PINS(191); +DECLARE_MSM_GPIO_PINS(192); +DECLARE_MSM_GPIO_PINS(193); +DECLARE_MSM_GPIO_PINS(194); +DECLARE_MSM_GPIO_PINS(195); +DECLARE_MSM_GPIO_PINS(196); +DECLARE_MSM_GPIO_PINS(197); +DECLARE_MSM_GPIO_PINS(198); +DECLARE_MSM_GPIO_PINS(199); +DECLARE_MSM_GPIO_PINS(200); +DECLARE_MSM_GPIO_PINS(201); +DECLARE_MSM_GPIO_PINS(202); +DECLARE_MSM_GPIO_PINS(203); +DECLARE_MSM_GPIO_PINS(204); +DECLARE_MSM_GPIO_PINS(205); +DECLARE_MSM_GPIO_PINS(206); +DECLARE_MSM_GPIO_PINS(207); +DECLARE_MSM_GPIO_PINS(208); +DECLARE_MSM_GPIO_PINS(209); +DECLARE_MSM_GPIO_PINS(210); +DECLARE_MSM_GPIO_PINS(211); +DECLARE_MSM_GPIO_PINS(212); +DECLARE_MSM_GPIO_PINS(213); +DECLARE_MSM_GPIO_PINS(214); +DECLARE_MSM_GPIO_PINS(215); +DECLARE_MSM_GPIO_PINS(216); +DECLARE_MSM_GPIO_PINS(217); +DECLARE_MSM_GPIO_PINS(218); +DECLARE_MSM_GPIO_PINS(219); +DECLARE_MSM_GPIO_PINS(220); +DECLARE_MSM_GPIO_PINS(221); +DECLARE_MSM_GPIO_PINS(222); +DECLARE_MSM_GPIO_PINS(223); +DECLARE_MSM_GPIO_PINS(224); +DECLARE_MSM_GPIO_PINS(225); +DECLARE_MSM_GPIO_PINS(226); +DECLARE_MSM_GPIO_PINS(227); +DECLARE_MSM_GPIO_PINS(228); +DECLARE_MSM_GPIO_PINS(229); +DECLARE_MSM_GPIO_PINS(230); +DECLARE_MSM_GPIO_PINS(231); +DECLARE_MSM_GPIO_PINS(232); +DECLARE_MSM_GPIO_PINS(233); +DECLARE_MSM_GPIO_PINS(234); +DECLARE_MSM_GPIO_PINS(235); +DECLARE_MSM_GPIO_PINS(236); +DECLARE_MSM_GPIO_PINS(237); + +static const unsigned int ufs_reset_pins[] = { 238 }; +static const unsigned int sdc2_clk_pins[] = { 239 }; +static const unsigned int sdc2_cmd_pins[] = { 240 }; +static const unsigned int sdc2_data_pins[] = { 241 }; + +enum x1e80100_functions { + msm_mux_gpio, + msm_mux_RESOUT_GPIO, + msm_mux_aon_cci, + msm_mux_aoss_cti, + msm_mux_atest_char, + msm_mux_atest_char0, + msm_mux_atest_char1, + msm_mux_atest_char2, + msm_mux_atest_char3, + msm_mux_atest_usb, + msm_mux_audio_ext, + msm_mux_audio_ref, + msm_mux_cam_aon, + msm_mux_cam_mclk, + msm_mux_cci_async, + msm_mux_cci_i2c, + msm_mux_cci_timer0, + msm_mux_cci_timer1, + msm_mux_cci_timer2, + msm_mux_cci_timer3, + msm_mux_cci_timer4, + msm_mux_cmu_rng0, + msm_mux_cmu_rng1, + msm_mux_cmu_rng2, + msm_mux_cmu_rng3, + msm_mux_cri_trng, + msm_mux_dbg_out, + msm_mux_ddr_bist, + msm_mux_ddr_pxi0, + msm_mux_ddr_pxi1, + msm_mux_ddr_pxi2, + msm_mux_ddr_pxi3, + msm_mux_ddr_pxi4, + msm_mux_ddr_pxi5, + msm_mux_ddr_pxi6, + msm_mux_ddr_pxi7, + msm_mux_edp0_hot, + msm_mux_edp0_lcd, + msm_mux_edp1_hot, + msm_mux_edp1_lcd, + msm_mux_eusb0_ac, + msm_mux_eusb1_ac, + msm_mux_eusb2_ac, + msm_mux_eusb3_ac, + msm_mux_eusb5_ac, + msm_mux_eusb6_ac, + msm_mux_gcc_gp1, + msm_mux_gcc_gp2, + msm_mux_gcc_gp3, + msm_mux_i2s0_data0, + msm_mux_i2s0_data1, + msm_mux_i2s0_sck, + msm_mux_i2s0_ws, + msm_mux_i2s1_data0, + msm_mux_i2s1_data1, + msm_mux_i2s1_sck, + msm_mux_i2s1_ws, + msm_mux_ibi_i3c, + msm_mux_jitter_bist, + msm_mux_mdp_vsync0, + msm_mux_mdp_vsync1, + msm_mux_mdp_vsync2, + msm_mux_mdp_vsync3, + msm_mux_mdp_vsync4, + msm_mux_mdp_vsync5, + msm_mux_mdp_vsync6, + msm_mux_mdp_vsync7, + msm_mux_mdp_vsync8, + msm_mux_pcie3_clk, + msm_mux_pcie4_clk, + msm_mux_pcie5_clk, + msm_mux_pcie6a_clk, + msm_mux_pcie6b_clk, + msm_mux_phase_flag, + msm_mux_pll_bist, + msm_mux_pll_clk, + msm_mux_prng_rosc0, + msm_mux_prng_rosc1, + msm_mux_prng_rosc2, + msm_mux_prng_rosc3, + msm_mux_qdss_cti, + msm_mux_qdss_gpio, + msm_mux_qspi00, + msm_mux_qspi01, + msm_mux_qspi02, + msm_mux_qspi03, + msm_mux_qspi0_clk, + msm_mux_qspi0_cs0, + msm_mux_qspi0_cs1, + msm_mux_qup0_se0, + msm_mux_qup0_se1, + msm_mux_qup0_se2, + msm_mux_qup0_se3, + msm_mux_qup0_se4, + msm_mux_qup0_se5, + msm_mux_qup0_se6, + msm_mux_qup0_se7, + msm_mux_qup1_se0, + msm_mux_qup1_se1, + msm_mux_qup1_se2, + msm_mux_qup1_se3, + msm_mux_qup1_se4, + msm_mux_qup1_se5, + msm_mux_qup1_se6, + msm_mux_qup1_se7, + msm_mux_qup2_se0, + msm_mux_qup2_se1, + msm_mux_qup2_se2, + msm_mux_qup2_se3, + msm_mux_qup2_se4, + msm_mux_qup2_se5, + msm_mux_qup2_se6, + msm_mux_qup2_se7, + msm_mux_sd_write, + msm_mux_sdc4_clk, + msm_mux_sdc4_cmd, + msm_mux_sdc4_data0, + msm_mux_sdc4_data1, + msm_mux_sdc4_data2, + msm_mux_sdc4_data3, + msm_mux_sys_throttle, + msm_mux_tb_trig, + msm_mux_tgu_ch0, + msm_mux_tgu_ch1, + msm_mux_tgu_ch2, + msm_mux_tgu_ch3, + msm_mux_tgu_ch4, + msm_mux_tgu_ch5, + msm_mux_tgu_ch6, + msm_mux_tgu_ch7, + msm_mux_tmess_prng0, + msm_mux_tmess_prng1, + msm_mux_tmess_prng2, + msm_mux_tmess_prng3, + msm_mux_tsense_pwm1, + msm_mux_tsense_pwm2, + msm_mux_tsense_pwm3, + msm_mux_tsense_pwm4, + msm_mux_usb0_dp, + msm_mux_usb0_phy, + msm_mux_usb0_sbrx, + msm_mux_usb0_sbtx, + msm_mux_usb1_dp, + msm_mux_usb1_phy, + msm_mux_usb1_sbrx, + msm_mux_usb1_sbtx, + msm_mux_usb2_dp, + msm_mux_usb2_phy, + msm_mux_usb2_sbrx, + msm_mux_usb2_sbtx, + msm_mux_vsense_trigger, + msm_mux__, +}; + +static const char * const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", + "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49", + "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56", + "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63", + "gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70", + "gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77", + "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84", + "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91", + "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98", + "gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104", + "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110", + "gpio111", "gpio112", "gpio113", "gpio114", "gpio115", "gpio116", + "gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122", + "gpio123", "gpio124", "gpio125", "gpio126", "gpio127", "gpio128", + "gpio129", "gpio130", "gpio131", "gpio132", "gpio133", "gpio134", + "gpio135", "gpio136", "gpio137", "gpio138", "gpio139", "gpio140", + "gpio141", "gpio142", "gpio143", "gpio144", "gpio145", "gpio146", + "gpio147", "gpio148", "gpio149", "gpio150", "gpio151", "gpio152", + "gpio153", "gpio154", "gpio155", "gpio156", "gpio157", "gpio158", + "gpio159", "gpio160", "gpio161", "gpio162", "gpio163", "gpio164", + "gpio165", "gpio166", "gpio167", "gpio168", "gpio169", "gpio170", + "gpio171", "gpio172", "gpio173", "gpio174", "gpio175", "gpio176", + "gpio177", "gpio178", "gpio179", "gpio180", "gpio181", "gpio182", + "gpio183", "gpio184", "gpio185", "gpio186", "gpio187", "gpio188", + "gpio189", "gpio190", "gpio191", "gpio192", "gpio193", "gpio194", + "gpio195", "gpio196", "gpio197", "gpio198", "gpio199", "gpio200", + "gpio201", "gpio202", "gpio203", "gpio204", "gpio205", "gpio206", + "gpio207", "gpio208", "gpio209", "gpio210", "gpio211", "gpio212", + "gpio213", "gpio214", "gpio215", "gpio216", "gpio217", "gpio218", + "gpio219", "gpio220", "gpio221", "gpio222", "gpio223", "gpio224", + "gpio225", "gpio226", "gpio227", "gpio228", "gpio229", "gpio230", + "gpio231", "gpio232", "gpio233", "gpio234", "gpio235", "gpio236", + "gpio237", +}; + +static const char * const RESOUT_GPIO_groups[] = { + "gpio160", +}; + +static const char * const aon_cci_groups[] = { + "gpio235", "gpio236", +}; + +static const char * const aoss_cti_groups[] = { + "gpio60", "gpio61", "gpio62", "gpio63", +}; + +static const char * const atest_char_groups[] = { + "gpio181", +}; + +static const char * const atest_char0_groups[] = { + "gpio185", +}; + +static const char * const atest_char1_groups[] = { + "gpio184", +}; + +static const char * const atest_char2_groups[] = { + "gpio188", +}; + +static const char * const atest_char3_groups[] = { + "gpio182", +}; + +static const char * const atest_usb_groups[] = { + "gpio9", "gpio10", "gpio35", "gpio38", "gpio41", "gpio42", + "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", + "gpio49", "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", + "gpio58", "gpio59", "gpio65", "gpio66", "gpio67", "gpio72", + "gpio73", "gpio74", "gpio75", "gpio80", "gpio81", "gpio83", +}; + +static const char * const audio_ext_groups[] = { + "gpio134", "gpio142", +}; + +static const char * const audio_ref_groups[] = { + "gpio142", +}; + +static const char * const cam_aon_groups[] = { + "gpio100", +}; + +static const char * const cam_mclk_groups[] = { + "gpio96", "gpio97", "gpio98", "gpio99", +}; + +static const char * const cci_async_groups[] = { + "gpio111", "gpio112", "gpio113", +}; + +static const char * const cci_i2c_groups[] = { + "gpio101", "gpio102", "gpio103", "gpio104", "gpio105", "gpio106", +}; + +static const char * const cci_timer0_groups[] = { + "gpio109", +}; + +static const char * const cci_timer1_groups[] = { + "gpio110", +}; + +static const char * const cci_timer2_groups[] = { + "gpio111", +}; + +static const char * const cci_timer3_groups[] = { + "gpio112", +}; + +static const char * const cci_timer4_groups[] = { + "gpio113", +}; + +static const char * const cmu_rng0_groups[] = { + "gpio48", +}; + +static const char * const cmu_rng1_groups[] = { + "gpio47", +}; + +static const char * const cmu_rng2_groups[] = { + "gpio46", +}; + +static const char * const cmu_rng3_groups[] = { + "gpio45", +}; + +static const char * const cri_trng_groups[] = { + "gpio187", +}; + +static const char * const dbg_out_groups[] = { + "gpio51", +}; + +static const char * const ddr_bist_groups[] = { + "gpio54", "gpio55", "gpio56", "gpio57", +}; + +static const char * const ddr_pxi0_groups[] = { + "gpio9", "gpio38", +}; + +static const char * const ddr_pxi1_groups[] = { + "gpio10", "gpio41", +}; + +static const char * const ddr_pxi2_groups[] = { + "gpio42", "gpio43", +}; + +static const char * const ddr_pxi3_groups[] = { + "gpio44", "gpio45", +}; + +static const char * const ddr_pxi4_groups[] = { + "gpio46", "gpio47", +}; + +static const char * const ddr_pxi5_groups[] = { + "gpio48", "gpio49", +}; + +static const char * const ddr_pxi6_groups[] = { + "gpio50", "gpio51", +}; + +static const char * const ddr_pxi7_groups[] = { + "gpio52", "gpio53", +}; + +static const char * const edp0_hot_groups[] = { + "gpio119", +}; + +static const char * const edp0_lcd_groups[] = { + "gpio120", +}; + +static const char * const edp1_hot_groups[] = { + "gpio120", +}; + +static const char * const edp1_lcd_groups[] = { + "gpio115", "gpio119", +}; + +static const char * const eusb0_ac_groups[] = { + "gpio168", +}; + +static const char * const eusb1_ac_groups[] = { + "gpio177", +}; + +static const char * const eusb2_ac_groups[] = { + "gpio186", +}; + +static const char * const eusb3_ac_groups[] = { + "gpio169", +}; + +static const char * const eusb5_ac_groups[] = { + "gpio187", +}; + +static const char * const eusb6_ac_groups[] = { + "gpio178", +}; + +static const char * const gcc_gp1_groups[] = { + "gpio71", "gpio72", +}; + +static const char * const gcc_gp2_groups[] = { + "gpio64", "gpio73", +}; + +static const char * const gcc_gp3_groups[] = { + "gpio74", "gpio82", +}; + +static const char * const i2s0_data0_groups[] = { + "gpio136", +}; + +static const char * const i2s0_data1_groups[] = { + "gpio137", +}; + +static const char * const i2s0_sck_groups[] = { + "gpio135", +}; + +static const char * const i2s0_ws_groups[] = { + "gpio138", +}; + +static const char * const i2s1_data0_groups[] = { + "gpio140", +}; + +static const char * const i2s1_data1_groups[] = { + "gpio142", +}; + +static const char * const i2s1_sck_groups[] = { + "gpio139", +}; + +static const char * const i2s1_ws_groups[] = { + "gpio141", +}; + +static const char * const ibi_i3c_groups[] = { + "gpio0", "gpio1", "gpio32", "gpio33", "gpio36", "gpio37", "gpio68", + "gpio69", +}; + +static const char * const jitter_bist_groups[] = { + "gpio42", +}; + +static const char * const mdp_vsync0_groups[] = { + "gpio114", +}; + +static const char * const mdp_vsync1_groups[] = { + "gpio114", +}; + +static const char * const mdp_vsync2_groups[] = { + "gpio115", +}; + +static const char * const mdp_vsync3_groups[] = { + "gpio115", +}; + +static const char * const mdp_vsync4_groups[] = { + "gpio109", +}; + +static const char * const mdp_vsync5_groups[] = { + "gpio110", +}; + +static const char * const mdp_vsync6_groups[] = { + "gpio111", +}; + +static const char * const mdp_vsync7_groups[] = { + "gpio112", +}; + +static const char * const mdp_vsync8_groups[] = { + "gpio113", +}; + +static const char * const pcie3_clk_groups[] = { + "gpio144", +}; + +static const char * const pcie4_clk_groups[] = { + "gpio147", +}; + +static const char * const pcie5_clk_groups[] = { + "gpio150", +}; + +static const char * const pcie6a_clk_groups[] = { + "gpio153", +}; + +static const char * const pcie6b_clk_groups[] = { + "gpio156", +}; + +static const char * const phase_flag_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio11", "gpio12", "gpio13", + "gpio14", "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", + "gpio20", "gpio21", "gpio22", "gpio23", "gpio24", "gpio25", + "gpio26", "gpio27", "gpio39", "gpio40", "gpio76", "gpio77", + "gpio78", "gpio181", "gpio182", "gpio184", "gpio185", + "gpio186", "gpio187", "gpio188", +}; + +static const char * const pll_bist_groups[] = { + "gpio28", +}; + +static const char * const pll_clk_groups[] = { + "gpio35", +}; + +static const char * const prng_rosc0_groups[] = { + "gpio186", +}; + +static const char * const prng_rosc1_groups[] = { + "gpio188", +}; + +static const char * const prng_rosc2_groups[] = { + "gpio182", +}; + +static const char * const prng_rosc3_groups[] = { + "gpio181", +}; + +static const char * const qdss_cti_groups[] = { + "gpio18", "gpio19", "gpio23", "gpio27", "gpio161", "gpio162", + "gpio215", "gpio217", +}; + +static const char * const qdss_gpio_groups[] = { + "gpio96", "gpio97", "gpio98", "gpio99", "gpio100", "gpio101", + "gpio102", "gpio103", "gpio104", "gpio105", "gpio106", "gpio107", + "gpio108", "gpio109", "gpio110", "gpio111", "gpio112", "gpio113", + "gpio219", "gpio220", "gpio221", "gpio222", "gpio223", "gpio224", + "gpio225", "gpio226", "gpio227", "gpio228", "gpio229", "gpio230", + "gpio231", "gpio232", "gpio233", "gpio234", "gpio235", "gpio236", +}; + +static const char * const qspi00_groups[] = { + "gpio128", +}; + +static const char * const qspi01_groups[] = { + "gpio129", +}; + +static const char * const qspi02_groups[] = { + "gpio130", +}; + +static const char * const qspi03_groups[] = { + "gpio131", +}; + +static const char * const qspi0_clk_groups[] = { + "gpio127", +}; + +static const char * const qspi0_cs0_groups[] = { + "gpio132", +}; + +static const char * const qspi0_cs1_groups[] = { + "gpio133", +}; + +static const char * const qup0_se0_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char * const qup0_se1_groups[] = { + "gpio4", "gpio5", "gpio6", "gpio7", +}; + +static const char * const qup0_se2_groups[] = { + "gpio8", "gpio9", "gpio10", "gpio11", "gpio17", "gpio18", "gpio19", +}; + +static const char * const qup0_se3_groups[] = { + "gpio12", "gpio13", "gpio14", "gpio15", "gpio21", "gpio22", "gpio23", +}; + +static const char * const qup0_se4_groups[] = { + "gpio16", "gpio17", "gpio18", "gpio19", +}; + +static const char * const qup0_se5_groups[] = { + "gpio20", "gpio21", "gpio22", "gpio23", +}; + +static const char * const qup0_se6_groups[] = { + "gpio24", "gpio25", "gpio26", "gpio27", +}; + +static const char * const qup0_se7_groups[] = { + "gpio12", "gpio13", "gpio14", "gpio15", +}; + +static const char * const qup1_se0_groups[] = { + "gpio32", "gpio33", "gpio34", "gpio35", +}; + +static const char * const qup1_se1_groups[] = { + "gpio36", "gpio37", "gpio38", "gpio39", +}; + +static const char * const qup1_se2_groups[] = { + "gpio40", "gpio41", "gpio42", "gpio43", "gpio49", "gpio50", "gpio51", +}; + +static const char * const qup1_se3_groups[] = { + "gpio33", "gpio34", "gpio35", "gpio44", "gpio45", "gpio46", "gpio47", +}; + +static const char * const qup1_se4_groups[] = { + "gpio48", "gpio49", "gpio50", "gpio51", +}; + +static const char * const qup1_se5_groups[] = { + "gpio52", "gpio53", "gpio54", "gpio55", +}; + +static const char * const qup1_se6_groups[] = { + "gpio56", "gpio57", "gpio58", "gpio59", +}; + +static const char * const qup1_se7_groups[] = { + "gpio52", "gpio53", "gpio54", "gpio55", +}; + +static const char * const qup2_se0_groups[] = { + "gpio64", "gpio65", "gpio66", "gpio67", +}; + +static const char * const qup2_se1_groups[] = { + "gpio68", "gpio69", "gpio70", "gpio71", +}; + +static const char * const qup2_se2_groups[] = { + "gpio72", "gpio73", "gpio74", "gpio75", "gpio81", "gpio82", "gpio83", +}; + +static const char * const qup2_se3_groups[] = { + "gpio65", "gpio66", "gpio67", "gpio76", "gpio77", "gpio78", "gpio79", +}; + +static const char * const qup2_se4_groups[] = { + "gpio80", "gpio81", "gpio82", "gpio83", +}; + +static const char * const qup2_se5_groups[] = { + "gpio84", "gpio85", "gpio86", "gpio87", +}; + +static const char * const qup2_se6_groups[] = { + "gpio88", "gpio89", "gpio90", "gpio91", +}; + +static const char * const qup2_se7_groups[] = { + "gpio84", "gpio85", "gpio86", "gpio87", +}; + +static const char * const sd_write_groups[] = { + "gpio162", +}; + +static const char * const sdc4_clk_groups[] = { + "gpio127", +}; + +static const char * const sdc4_cmd_groups[] = { + "gpio132", +}; + +static const char * const sdc4_data0_groups[] = { + "gpio128", +}; + +static const char * const sdc4_data1_groups[] = { + "gpio129", +}; + +static const char * const sdc4_data2_groups[] = { + "gpio130", +}; + +static const char * const sdc4_data3_groups[] = { + "gpio131", +}; + +static const char * const sys_throttle_groups[] = { + "gpio39", "gpio94", +}; + +static const char * const tb_trig_groups[] = { + "gpio133", "gpio137", +}; + +static const char * const tgu_ch0_groups[] = { + "gpio81", +}; + +static const char * const tgu_ch1_groups[] = { + "gpio65", +}; + +static const char * const tgu_ch2_groups[] = { + "gpio66", +}; + +static const char * const tgu_ch3_groups[] = { + "gpio67", +}; + +static const char * const tgu_ch4_groups[] = { + "gpio68", +}; + +static const char * const tgu_ch5_groups[] = { + "gpio69", +}; + +static const char * const tgu_ch6_groups[] = { + "gpio83", +}; + +static const char * const tgu_ch7_groups[] = { + "gpio80", +}; + +static const char * const tmess_prng0_groups[] = { + "gpio92", +}; + +static const char * const tmess_prng1_groups[] = { + "gpio93", +}; + +static const char * const tmess_prng2_groups[] = { + "gpio94", +}; + +static const char * const tmess_prng3_groups[] = { + "gpio95", +}; + +static const char * const tsense_pwm1_groups[] = { + "gpio34", +}; + +static const char * const tsense_pwm2_groups[] = { + "gpio34", +}; + +static const char * const tsense_pwm3_groups[] = { + "gpio34", +}; + +static const char * const tsense_pwm4_groups[] = { + "gpio34", +}; + +static const char * const usb0_dp_groups[] = { + "gpio122", +}; + +static const char * const usb0_phy_groups[] = { + "gpio121", +}; + +static const char * const usb0_sbrx_groups[] = { + "gpio163", +}; + +static const char * const usb0_sbtx_groups[] = { + "gpio164", "gpio165", +}; + +static const char * const usb1_dp_groups[] = { + "gpio124", +}; + +static const char * const usb1_phy_groups[] = { + "gpio123", +}; + +static const char * const usb1_sbrx_groups[] = { + "gpio172", +}; + +static const char * const usb1_sbtx_groups[] = { + "gpio173", "gpio174", +}; + +static const char * const usb2_dp_groups[] = { + "gpio126", +}; + +static const char * const usb2_phy_groups[] = { + "gpio125", +}; + +static const char * const usb2_sbrx_groups[] = { + "gpio181", +}; + +static const char * const usb2_sbtx_groups[] = { + "gpio182", "gpio183", +}; + +static const char * const vsense_trigger_groups[] = { + "gpio38", +}; + +static const struct pinfunction x1e80100_functions[] = { + MSM_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(RESOUT_GPIO), + MSM_PIN_FUNCTION(aon_cci), + MSM_PIN_FUNCTION(aoss_cti), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_char0), + MSM_PIN_FUNCTION(atest_char1), + MSM_PIN_FUNCTION(atest_char2), + MSM_PIN_FUNCTION(atest_char3), + MSM_PIN_FUNCTION(atest_usb), + MSM_PIN_FUNCTION(audio_ext), + MSM_PIN_FUNCTION(audio_ref), + MSM_PIN_FUNCTION(cam_aon), + MSM_PIN_FUNCTION(cam_mclk), + MSM_PIN_FUNCTION(cci_async), + MSM_PIN_FUNCTION(cci_i2c), + MSM_PIN_FUNCTION(cci_timer0), + MSM_PIN_FUNCTION(cci_timer1), + MSM_PIN_FUNCTION(cci_timer2), + MSM_PIN_FUNCTION(cci_timer3), + MSM_PIN_FUNCTION(cci_timer4), + MSM_PIN_FUNCTION(cmu_rng0), + MSM_PIN_FUNCTION(cmu_rng1), + MSM_PIN_FUNCTION(cmu_rng2), + MSM_PIN_FUNCTION(cmu_rng3), + MSM_PIN_FUNCTION(cri_trng), + MSM_PIN_FUNCTION(dbg_out), + MSM_PIN_FUNCTION(ddr_bist), + MSM_PIN_FUNCTION(ddr_pxi0), + MSM_PIN_FUNCTION(ddr_pxi1), + MSM_PIN_FUNCTION(ddr_pxi2), + MSM_PIN_FUNCTION(ddr_pxi3), + MSM_PIN_FUNCTION(ddr_pxi4), + MSM_PIN_FUNCTION(ddr_pxi5), + MSM_PIN_FUNCTION(ddr_pxi6), + MSM_PIN_FUNCTION(ddr_pxi7), + MSM_PIN_FUNCTION(edp0_hot), + MSM_PIN_FUNCTION(edp0_lcd), + MSM_PIN_FUNCTION(edp1_hot), + MSM_PIN_FUNCTION(edp1_lcd), + MSM_PIN_FUNCTION(eusb0_ac), + MSM_PIN_FUNCTION(eusb1_ac), + MSM_PIN_FUNCTION(eusb2_ac), + MSM_PIN_FUNCTION(eusb3_ac), + MSM_PIN_FUNCTION(eusb5_ac), + MSM_PIN_FUNCTION(eusb6_ac), + MSM_PIN_FUNCTION(gcc_gp1), + MSM_PIN_FUNCTION(gcc_gp2), + MSM_PIN_FUNCTION(gcc_gp3), + MSM_PIN_FUNCTION(i2s0_data0), + MSM_PIN_FUNCTION(i2s0_data1), + MSM_PIN_FUNCTION(i2s0_sck), + MSM_PIN_FUNCTION(i2s0_ws), + MSM_PIN_FUNCTION(i2s1_data0), + MSM_PIN_FUNCTION(i2s1_data1), + MSM_PIN_FUNCTION(i2s1_sck), + MSM_PIN_FUNCTION(i2s1_ws), + MSM_PIN_FUNCTION(ibi_i3c), + MSM_PIN_FUNCTION(jitter_bist), + MSM_PIN_FUNCTION(mdp_vsync0), + MSM_PIN_FUNCTION(mdp_vsync1), + MSM_PIN_FUNCTION(mdp_vsync2), + MSM_PIN_FUNCTION(mdp_vsync3), + MSM_PIN_FUNCTION(mdp_vsync4), + MSM_PIN_FUNCTION(mdp_vsync5), + MSM_PIN_FUNCTION(mdp_vsync6), + MSM_PIN_FUNCTION(mdp_vsync7), + MSM_PIN_FUNCTION(mdp_vsync8), + MSM_PIN_FUNCTION(pcie3_clk), + MSM_PIN_FUNCTION(pcie4_clk), + MSM_PIN_FUNCTION(pcie5_clk), + MSM_PIN_FUNCTION(pcie6a_clk), + MSM_PIN_FUNCTION(pcie6b_clk), + MSM_PIN_FUNCTION(phase_flag), + MSM_PIN_FUNCTION(pll_bist), + MSM_PIN_FUNCTION(pll_clk), + MSM_PIN_FUNCTION(prng_rosc0), + MSM_PIN_FUNCTION(prng_rosc1), + MSM_PIN_FUNCTION(prng_rosc2), + MSM_PIN_FUNCTION(prng_rosc3), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qdss_gpio), + MSM_PIN_FUNCTION(qspi00), + MSM_PIN_FUNCTION(qspi01), + MSM_PIN_FUNCTION(qspi02), + MSM_PIN_FUNCTION(qspi03), + MSM_PIN_FUNCTION(qspi0_clk), + MSM_PIN_FUNCTION(qspi0_cs0), + MSM_PIN_FUNCTION(qspi0_cs1), + MSM_PIN_FUNCTION(qup0_se0), + MSM_PIN_FUNCTION(qup0_se1), + MSM_PIN_FUNCTION(qup0_se2), + MSM_PIN_FUNCTION(qup0_se3), + MSM_PIN_FUNCTION(qup0_se4), + MSM_PIN_FUNCTION(qup0_se5), + MSM_PIN_FUNCTION(qup0_se6), + MSM_PIN_FUNCTION(qup0_se7), + MSM_PIN_FUNCTION(qup1_se0), + MSM_PIN_FUNCTION(qup1_se1), + MSM_PIN_FUNCTION(qup1_se2), + MSM_PIN_FUNCTION(qup1_se3), + MSM_PIN_FUNCTION(qup1_se4), + MSM_PIN_FUNCTION(qup1_se5), + MSM_PIN_FUNCTION(qup1_se6), + MSM_PIN_FUNCTION(qup1_se7), + MSM_PIN_FUNCTION(qup2_se0), + MSM_PIN_FUNCTION(qup2_se1), + MSM_PIN_FUNCTION(qup2_se2), + MSM_PIN_FUNCTION(qup2_se3), + MSM_PIN_FUNCTION(qup2_se4), + MSM_PIN_FUNCTION(qup2_se5), + MSM_PIN_FUNCTION(qup2_se6), + MSM_PIN_FUNCTION(qup2_se7), + MSM_PIN_FUNCTION(sd_write), + MSM_PIN_FUNCTION(sdc4_clk), + MSM_PIN_FUNCTION(sdc4_cmd), + MSM_PIN_FUNCTION(sdc4_data0), + MSM_PIN_FUNCTION(sdc4_data1), + MSM_PIN_FUNCTION(sdc4_data2), + MSM_PIN_FUNCTION(sdc4_data3), + MSM_PIN_FUNCTION(sys_throttle), + MSM_PIN_FUNCTION(tb_trig), + MSM_PIN_FUNCTION(tgu_ch0), + MSM_PIN_FUNCTION(tgu_ch1), + MSM_PIN_FUNCTION(tgu_ch2), + MSM_PIN_FUNCTION(tgu_ch3), + MSM_PIN_FUNCTION(tgu_ch4), + MSM_PIN_FUNCTION(tgu_ch5), + MSM_PIN_FUNCTION(tgu_ch6), + MSM_PIN_FUNCTION(tgu_ch7), + MSM_PIN_FUNCTION(tmess_prng0), + MSM_PIN_FUNCTION(tmess_prng1), + MSM_PIN_FUNCTION(tmess_prng2), + MSM_PIN_FUNCTION(tmess_prng3), + MSM_PIN_FUNCTION(tsense_pwm1), + MSM_PIN_FUNCTION(tsense_pwm2), + MSM_PIN_FUNCTION(tsense_pwm3), + MSM_PIN_FUNCTION(tsense_pwm4), + MSM_PIN_FUNCTION(usb0_dp), + MSM_PIN_FUNCTION(usb0_phy), + MSM_PIN_FUNCTION(usb0_sbrx), + MSM_PIN_FUNCTION(usb0_sbtx), + MSM_PIN_FUNCTION(usb1_dp), + MSM_PIN_FUNCTION(usb1_phy), + MSM_PIN_FUNCTION(usb1_sbrx), + MSM_PIN_FUNCTION(usb1_sbtx), + MSM_PIN_FUNCTION(usb2_dp), + MSM_PIN_FUNCTION(usb2_phy), + MSM_PIN_FUNCTION(usb2_sbrx), + MSM_PIN_FUNCTION(usb2_sbtx), + MSM_PIN_FUNCTION(vsense_trigger), +}; + +/* + * Every pin is maintained as a single group, and missing or non-existing pin + * would be maintained as dummy group to synchronize pin group index with + * pin descriptor registered with pinctrl core. + * Clients would not be able to request these dummy pin groups. + */ +static const struct msm_pingroup x1e80100_groups[] = { + [0] = PINGROUP(0, qup0_se0, ibi_i3c, _, _, _, _, _, _, _), + [1] = PINGROUP(1, qup0_se0, ibi_i3c, _, _, _, _, _, _, _), + [2] = PINGROUP(2, qup0_se0, _, _, _, _, _, _, _, _), + [3] = PINGROUP(3, qup0_se0, _, _, _, _, _, _, _, _), + [4] = PINGROUP(4, qup0_se1, _, _, _, _, _, _, _, _), + [5] = PINGROUP(5, qup0_se1, _, _, _, _, _, _, _, _), + [6] = PINGROUP(6, qup0_se1, phase_flag, _, _, _, _, _, _, _), + [7] = PINGROUP(7, qup0_se1, phase_flag, _, _, _, _, _, _, _), + [8] = PINGROUP(8, qup0_se2, phase_flag, _, _, _, _, _, _, _), + [9] = PINGROUP(9, qup0_se2, _, atest_usb, ddr_pxi0, _, _, _, _, _), + [10] = PINGROUP(10, qup0_se2, _, atest_usb, ddr_pxi1, _, _, _, _, _), + [11] = PINGROUP(11, qup0_se2, phase_flag, _, _, _, _, _, _, _), + [12] = PINGROUP(12, qup0_se3, qup0_se7, phase_flag, _, _, _, _, _, _), + [13] = PINGROUP(13, qup0_se3, qup0_se7, phase_flag, _, _, _, _, _, _), + [14] = PINGROUP(14, qup0_se3, qup0_se7, phase_flag, _, _, _, _, _, _), + [15] = PINGROUP(15, qup0_se3, qup0_se7, phase_flag, _, _, _, _, _, _), + [16] = PINGROUP(16, qup0_se4, phase_flag, _, _, _, _, _, _, _), + [17] = PINGROUP(17, qup0_se4, qup0_se2, phase_flag, _, _, _, _, _, _), + [18] = PINGROUP(18, qup0_se4, qup0_se2, phase_flag, _, qdss_cti, _, _, _, _), + [19] = PINGROUP(19, qup0_se4, qup0_se2, phase_flag, _, qdss_cti, _, _, _, _), + [20] = PINGROUP(20, qup0_se5, _, phase_flag, _, _, _, _, _, _), + [21] = PINGROUP(21, qup0_se5, qup0_se3, _, phase_flag, _, _, _, _, _), + [22] = PINGROUP(22, qup0_se5, qup0_se3, _, phase_flag, _, _, _, _, _), + [23] = PINGROUP(23, qup0_se5, qup0_se3, phase_flag, _, qdss_cti, _, _, _, _), + [24] = PINGROUP(24, qup0_se6, phase_flag, _, _, _, _, _, _, _), + [25] = PINGROUP(25, qup0_se6, phase_flag, _, _, _, _, _, _, _), + [26] = PINGROUP(26, qup0_se6, phase_flag, _, _, _, _, _, _, _), + [27] = PINGROUP(27, qup0_se6, phase_flag, _, qdss_cti, _, _, _, _, _), + [28] = PINGROUP(28, pll_bist, _, _, _, _, _, _, _, _), + [29] = PINGROUP(29, _, _, _, _, _, _, _, _, _), + [30] = PINGROUP(30, _, _, _, _, _, _, _, _, _), + [31] = PINGROUP(31, _, _, _, _, _, _, _, _, _), + [32] = PINGROUP(32, qup1_se0, ibi_i3c, _, _, _, _, _, _, _), + [33] = PINGROUP(33, qup1_se0, ibi_i3c, qup1_se3, _, _, _, _, _, _), + [34] = PINGROUP(34, qup1_se0, qup1_se3, tsense_pwm1, tsense_pwm2, tsense_pwm3, tsense_pwm4, _, _, _), + [35] = PINGROUP(35, qup1_se0, qup1_se3, pll_clk, atest_usb, _, _, _, _, _), + [36] = PINGROUP(36, qup1_se1, ibi_i3c, _, _, _, _, _, _, _), + [37] = PINGROUP(37, qup1_se1, ibi_i3c, _, _, _, _, _, _, _), + [38] = PINGROUP(38, qup1_se1, vsense_trigger, atest_usb, ddr_pxi0, _, _, _, _, _), + [39] = PINGROUP(39, qup1_se1, sys_throttle, phase_flag, _, _, _, _, _, _), + [40] = PINGROUP(40, qup1_se2, phase_flag, _, _, _, _, _, _, _), + [41] = PINGROUP(41, qup1_se2, atest_usb, ddr_pxi1, _, _, _, _, _, _), + [42] = PINGROUP(42, qup1_se2, jitter_bist, atest_usb, ddr_pxi2, _, _, _, _, _), + [43] = PINGROUP(43, qup1_se2, _, atest_usb, ddr_pxi2, _, _, _, _, _), + [44] = PINGROUP(44, qup1_se3, _, atest_usb, ddr_pxi3, _, _, _, _, _), + [45] = PINGROUP(45, qup1_se3, cmu_rng3, _, atest_usb, ddr_pxi3, _, _, _, _), + [46] = PINGROUP(46, qup1_se3, cmu_rng2, _, atest_usb, ddr_pxi4, _, _, _, _), + [47] = PINGROUP(47, qup1_se3, cmu_rng1, _, atest_usb, ddr_pxi4, _, _, _, _), + [48] = PINGROUP(48, qup1_se4, cmu_rng0, _, atest_usb, ddr_pxi5, _, _, _, _), + [49] = PINGROUP(49, qup1_se4, qup1_se2, _, atest_usb, ddr_pxi5, _, _, _, _), + [50] = PINGROUP(50, qup1_se4, qup1_se2, _, atest_usb, ddr_pxi6, _, _, _, _), + [51] = PINGROUP(51, qup1_se4, qup1_se2, dbg_out, atest_usb, ddr_pxi6, _, _, _, _), + [52] = PINGROUP(52, qup1_se5, qup1_se7, atest_usb, ddr_pxi7, _, _, _, _, _), + [53] = PINGROUP(53, qup1_se5, qup1_se7, _, atest_usb, ddr_pxi7, _, _, _, _), + [54] = PINGROUP(54, qup1_se5, qup1_se7, ddr_bist, atest_usb, _, _, _, _, _), + [55] = PINGROUP(55, qup1_se5, qup1_se7, ddr_bist, _, _, _, _, _, _), + [56] = PINGROUP(56, qup1_se6, ddr_bist, _, _, _, _, _, _, _), + [57] = PINGROUP(57, qup1_se6, ddr_bist, _, _, _, _, _, _, _), + [58] = PINGROUP(58, qup1_se6, atest_usb, _, _, _, _, _, _, _), + [59] = PINGROUP(59, qup1_se6, atest_usb, _, _, _, _, _, _, _), + [60] = PINGROUP(60, aoss_cti, _, _, _, _, _, _, _, _), + [61] = PINGROUP(61, aoss_cti, _, _, _, _, _, _, _, _), + [62] = PINGROUP(62, aoss_cti, _, _, _, _, _, _, _, _), + [63] = PINGROUP(63, aoss_cti, _, _, _, _, _, _, _, _), + [64] = PINGROUP(64, qup2_se0, gcc_gp2, _, _, _, _, _, _, _), + [65] = PINGROUP(65, qup2_se0, qup2_se3, tgu_ch1, atest_usb, _, _, _, _, _), + [66] = PINGROUP(66, qup2_se0, qup2_se3, tgu_ch2, atest_usb, _, _, _, _, _), + [67] = PINGROUP(67, qup2_se0, qup2_se3, tgu_ch3, atest_usb, _, _, _, _, _), + [68] = PINGROUP(68, qup2_se1, ibi_i3c, tgu_ch4, _, _, _, _, _, _), + [69] = PINGROUP(69, qup2_se1, ibi_i3c, tgu_ch5, _, _, _, _, _, _), + [70] = PINGROUP(70, qup2_se1, _, _, _, _, _, _, _, _), + [71] = PINGROUP(71, qup2_se1, gcc_gp1, _, _, _, _, _, _, _), + [72] = PINGROUP(72, qup2_se2, gcc_gp1, atest_usb, _, _, _, _, _, _), + [73] = PINGROUP(73, qup2_se2, gcc_gp2, atest_usb, _, _, _, _, _, _), + [74] = PINGROUP(74, qup2_se2, gcc_gp3, atest_usb, _, _, _, _, _, _), + [75] = PINGROUP(75, qup2_se2, atest_usb, _, _, _, _, _, _, _), + [76] = PINGROUP(76, qup2_se3, phase_flag, _, _, _, _, _, _, _), + [77] = PINGROUP(77, qup2_se3, phase_flag, _, _, _, _, _, _, _), + [78] = PINGROUP(78, qup2_se3, phase_flag, _, _, _, _, _, _, _), + [79] = PINGROUP(79, qup2_se3, _, _, _, _, _, _, _, _), + [80] = PINGROUP(80, qup2_se4, tgu_ch7, atest_usb, _, _, _, _, _, _), + [81] = PINGROUP(81, qup2_se4, qup2_se2, tgu_ch0, atest_usb, _, _, _, _, _), + [82] = PINGROUP(82, qup2_se4, qup2_se2, gcc_gp3, _, _, _, _, _, _), + [83] = PINGROUP(83, qup2_se4, qup2_se2, tgu_ch6, atest_usb, _, _, _, _, _), + [84] = PINGROUP(84, qup2_se5, qup2_se7, _, _, _, _, _, _, _), + [85] = PINGROUP(85, qup2_se5, qup2_se7, _, _, _, _, _, _, _), + [86] = PINGROUP(86, qup2_se5, qup2_se7, _, _, _, _, _, _, _), + [87] = PINGROUP(87, qup2_se5, qup2_se7, _, _, _, _, _, _, _), + [88] = PINGROUP(88, qup2_se6, _, _, _, _, _, _, _, _), + [89] = PINGROUP(89, qup2_se6, _, _, _, _, _, _, _, _), + [90] = PINGROUP(90, qup2_se6, _, _, _, _, _, _, _, _), + [91] = PINGROUP(91, qup2_se6, _, _, _, _, _, _, _, _), + [92] = PINGROUP(92, tmess_prng0, _, _, _, _, _, _, _, _), + [93] = PINGROUP(93, tmess_prng1, _, _, _, _, _, _, _, _), + [94] = PINGROUP(94, sys_throttle, tmess_prng2, _, _, _, _, _, _, _), + [95] = PINGROUP(95, tmess_prng3, _, _, _, _, _, _, _, _), + [96] = PINGROUP(96, cam_mclk, qdss_gpio, _, _, _, _, _, _, _), + [97] = PINGROUP(97, cam_mclk, qdss_gpio, _, _, _, _, _, _, _), + [98] = PINGROUP(98, cam_mclk, qdss_gpio, _, _, _, _, _, _, _), + [99] = PINGROUP(99, cam_mclk, qdss_gpio, _, _, _, _, _, _, _), + [100] = PINGROUP(100, cam_aon, qdss_gpio, _, _, _, _, _, _, _), + [101] = PINGROUP(101, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [102] = PINGROUP(102, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [103] = PINGROUP(103, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [104] = PINGROUP(104, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [105] = PINGROUP(105, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [106] = PINGROUP(106, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [107] = PINGROUP(107, qdss_gpio, _, _, _, _, _, _, _, _), + [108] = PINGROUP(108, qdss_gpio, _, _, _, _, _, _, _, _), + [109] = PINGROUP(109, cci_timer0, mdp_vsync4, qdss_gpio, _, _, _, _, _, _), + [110] = PINGROUP(110, cci_timer1, mdp_vsync5, qdss_gpio, _, _, _, _, _, _), + [111] = PINGROUP(111, cci_timer2, cci_async, mdp_vsync6, qdss_gpio, _, _, _, _, _), + [112] = PINGROUP(112, cci_timer3, cci_async, mdp_vsync7, qdss_gpio, _, _, _, _, _), + [113] = PINGROUP(113, cci_timer4, cci_async, mdp_vsync8, qdss_gpio, _, _, _, _, _), + [114] = PINGROUP(114, mdp_vsync0, mdp_vsync1, _, _, _, _, _, _, _), + [115] = PINGROUP(115, mdp_vsync3, mdp_vsync2, edp1_lcd, _, _, _, _, _, _), + [116] = PINGROUP(116, _, _, _, _, _, _, _, _, _), + [117] = PINGROUP(117, _, _, _, _, _, _, _, _, _), + [118] = PINGROUP(118, _, _, _, _, _, _, _, _, _), + [119] = PINGROUP(119, edp0_hot, edp1_lcd, _, _, _, _, _, _, _), + [120] = PINGROUP(120, edp1_hot, edp0_lcd, _, _, _, _, _, _, _), + [121] = PINGROUP(121, usb0_phy, _, _, _, _, _, _, _, _), + [122] = PINGROUP(122, usb0_dp, _, _, _, _, _, _, _, _), + [123] = PINGROUP(123, usb1_phy, _, _, _, _, _, _, _, _), + [124] = PINGROUP(124, usb1_dp, _, _, _, _, _, _, _, _), + [125] = PINGROUP(125, usb2_phy, _, _, _, _, _, _, _, _), + [126] = PINGROUP(126, usb2_dp, _, _, _, _, _, _, _, _), + [127] = PINGROUP(127, qspi0_clk, sdc4_clk, _, _, _, _, _, _, _), + [128] = PINGROUP(128, qspi00, sdc4_data0, _, _, _, _, _, _, _), + [129] = PINGROUP(129, qspi01, sdc4_data1, _, _, _, _, _, _, _), + [130] = PINGROUP(130, qspi02, sdc4_data2, _, _, _, _, _, _, _), + [131] = PINGROUP(131, qspi03, sdc4_data3, _, _, _, _, _, _, _), + [132] = PINGROUP(132, qspi0_cs0, sdc4_cmd, _, _, _, _, _, _, _), + [133] = PINGROUP(133, qspi0_cs1, tb_trig, _, _, _, _, _, _, _), + [134] = PINGROUP(134, audio_ext, _, _, _, _, _, _, _, _), + [135] = PINGROUP(135, i2s0_sck, _, _, _, _, _, _, _, _), + [136] = PINGROUP(136, i2s0_data0, _, _, _, _, _, _, _, _), + [137] = PINGROUP(137, i2s0_data1, tb_trig, _, _, _, _, _, _, _), + [138] = PINGROUP(138, i2s0_ws, _, _, _, _, _, _, _, _), + [139] = PINGROUP(139, i2s1_sck, _, _, _, _, _, _, _, _), + [140] = PINGROUP(140, i2s1_data0, _, _, _, _, _, _, _, _), + [141] = PINGROUP(141, i2s1_ws, _, _, _, _, _, _, _, _), + [142] = PINGROUP(142, i2s1_data1, audio_ext, audio_ref, _, _, _, _, _, _), + [143] = PINGROUP(143, _, _, _, _, _, _, _, _, _), + [144] = PINGROUP(144, pcie3_clk, _, _, _, _, _, _, _, _), + [145] = PINGROUP(145, _, _, _, _, _, _, _, _, _), + [146] = PINGROUP(146, _, _, _, _, _, _, _, _, _), + [147] = PINGROUP(147, pcie4_clk, _, _, _, _, _, _, _, _), + [148] = PINGROUP(148, _, _, _, _, _, _, _, _, _), + [149] = PINGROUP(149, _, _, _, _, _, _, _, _, _), + [150] = PINGROUP(150, pcie5_clk, _, _, _, _, _, _, _, _), + [151] = PINGROUP(151, _, _, _, _, _, _, _, _, _), + [152] = PINGROUP(152, _, _, _, _, _, _, _, _, _), + [153] = PINGROUP(153, pcie6a_clk, _, _, _, _, _, _, _, _), + [154] = PINGROUP(154, _, _, _, _, _, _, _, _, _), + [155] = PINGROUP(155, _, _, _, _, _, _, _, _, _), + [156] = PINGROUP(156, pcie6b_clk, _, _, _, _, _, _, _, _), + [157] = PINGROUP(157, _, _, _, _, _, _, _, _, _), + [158] = PINGROUP(158, _, _, _, _, _, _, _, _, _), + [159] = PINGROUP(159, _, _, _, _, _, _, _, _, _), + [160] = PINGROUP(160, RESOUT_GPIO, _, _, _, _, _, _, _, _), + [161] = PINGROUP(161, qdss_cti, _, _, _, _, _, _, _, _), + [162] = PINGROUP(162, sd_write, qdss_cti, _, _, _, _, _, _, _), + [163] = PINGROUP(163, usb0_sbrx, _, _, _, _, _, _, _, _), + [164] = PINGROUP(164, usb0_sbtx, _, _, _, _, _, _, _, _), + [165] = PINGROUP(165, usb0_sbtx, _, _, _, _, _, _, _, _), + [166] = PINGROUP(166, _, _, _, _, _, _, _, _, _), + [167] = PINGROUP(167, _, _, _, _, _, _, _, _, _), + [168] = PINGROUP(168, eusb0_ac, _, _, _, _, _, _, _, _), + [169] = PINGROUP(169, eusb3_ac, _, _, _, _, _, _, _, _), + [170] = PINGROUP(170, _, _, _, _, _, _, _, _, _), + [171] = PINGROUP(171, _, _, _, _, _, _, _, _, _), + [172] = PINGROUP(172, usb1_sbrx, _, _, _, _, _, _, _, _), + [173] = PINGROUP(173, usb1_sbtx, _, _, _, _, _, _, _, _), + [174] = PINGROUP(174, usb1_sbtx, _, _, _, _, _, _, _, _), + [175] = PINGROUP(175, _, _, _, _, _, _, _, _, _), + [176] = PINGROUP(176, _, _, _, _, _, _, _, _, _), + [177] = PINGROUP(177, eusb1_ac, _, _, _, _, _, _, _, _), + [178] = PINGROUP(178, eusb6_ac, _, _, _, _, _, _, _, _), + [179] = PINGROUP(179, _, _, _, _, _, _, _, _, _), + [180] = PINGROUP(180, _, _, _, _, _, _, _, _, _), + [181] = PINGROUP(181, usb2_sbrx, prng_rosc3, phase_flag, _, atest_char, _, _, _, _), + [182] = PINGROUP(182, usb2_sbtx, prng_rosc2, phase_flag, _, atest_char3, _, _, _, _), + [183] = PINGROUP(183, usb2_sbtx, _, _, _, _, _, _, _, _), + [184] = PINGROUP(184, phase_flag, _, atest_char1, _, _, _, _, _, _), + [185] = PINGROUP(185, phase_flag, _, atest_char0, _, _, _, _, _, _), + [186] = PINGROUP(186, eusb2_ac, prng_rosc0, phase_flag, _, _, _, _, _, _), + [187] = PINGROUP(187, eusb5_ac, cri_trng, phase_flag, _, _, _, _, _, _), + [188] = PINGROUP(188, prng_rosc1, phase_flag, _, atest_char2, _, _, _, _, _), + [189] = PINGROUP(189, _, _, _, _, _, _, _, _, _), + [190] = PINGROUP(190, _, _, _, _, _, _, _, _, _), + [191] = PINGROUP(191, _, _, _, _, _, _, _, _, _), + [192] = PINGROUP(192, _, _, _, _, _, _, _, _, _), + [193] = PINGROUP(193, _, _, _, _, _, _, _, _, _), + [194] = PINGROUP(194, _, _, _, _, _, _, _, _, _), + [195] = PINGROUP(195, _, _, _, _, _, _, _, _, _), + [196] = PINGROUP(196, _, _, _, _, _, _, _, _, _), + [197] = PINGROUP(197, _, _, _, _, _, _, _, _, _), + [198] = PINGROUP(198, _, _, _, _, _, _, _, _, _), + [199] = PINGROUP(199, _, _, _, _, _, _, _, _, _), + [200] = PINGROUP(200, _, _, _, _, _, _, _, _, _), + [201] = PINGROUP(201, _, _, _, _, _, _, _, _, _), + [202] = PINGROUP(202, _, _, _, _, _, _, _, _, _), + [203] = PINGROUP(203, _, _, _, _, _, _, _, _, _), + [204] = PINGROUP(204, _, _, _, _, _, _, _, _, _), + [205] = PINGROUP(205, _, _, _, _, _, _, _, _, _), + [206] = PINGROUP(206, _, _, _, _, _, _, _, _, _), + [207] = PINGROUP(207, _, _, _, _, _, _, _, _, _), + [208] = PINGROUP(208, _, _, _, _, _, _, _, _, _), + [209] = PINGROUP(209, _, _, _, _, _, _, _, _, _), + [210] = PINGROUP(210, _, _, _, _, _, _, _, _, _), + [211] = PINGROUP(211, _, _, _, _, _, _, _, _, _), + [212] = PINGROUP(212, _, _, _, _, _, _, _, _, _), + [213] = PINGROUP(213, _, _, _, _, _, _, _, _, _), + [214] = PINGROUP(214, _, _, _, _, _, _, _, _, _), + [215] = PINGROUP(215, _, qdss_cti, _, _, _, _, _, _, _), + [216] = PINGROUP(216, _, _, _, _, _, _, _, _, _), + [217] = PINGROUP(217, _, qdss_cti, _, _, _, _, _, _, _), + [218] = PINGROUP(218, _, _, _, _, _, _, _, _, _), + [219] = PINGROUP(219, _, qdss_gpio, _, _, _, _, _, _, _), + [220] = PINGROUP(220, _, qdss_gpio, _, _, _, _, _, _, _), + [221] = PINGROUP(221, _, qdss_gpio, _, _, _, _, _, _, _), + [222] = PINGROUP(222, _, qdss_gpio, _, _, _, _, _, _, _), + [223] = PINGROUP(223, _, qdss_gpio, _, _, _, _, _, _, _), + [224] = PINGROUP(224, _, qdss_gpio, _, _, _, _, _, _, _), + [225] = PINGROUP(225, _, qdss_gpio, _, _, _, _, _, _, _), + [226] = PINGROUP(226, _, qdss_gpio, _, _, _, _, _, _, _), + [227] = PINGROUP(227, _, qdss_gpio, _, _, _, _, _, _, _), + [228] = PINGROUP(228, _, qdss_gpio, _, _, _, _, _, _, _), + [229] = PINGROUP(229, qdss_gpio, _, _, _, _, _, _, _, _), + [230] = PINGROUP(230, qdss_gpio, _, _, _, _, _, _, _, _), + [231] = PINGROUP(231, qdss_gpio, _, _, _, _, _, _, _, _), + [232] = PINGROUP(232, qdss_gpio, _, _, _, _, _, _, _, _), + [233] = PINGROUP(233, qdss_gpio, _, _, _, _, _, _, _, _), + [234] = PINGROUP(234, qdss_gpio, _, _, _, _, _, _, _, _), + [235] = PINGROUP(235, aon_cci, qdss_gpio, _, _, _, _, _, _, _), + [236] = PINGROUP(236, aon_cci, qdss_gpio, _, _, _, _, _, _, _), + [237] = PINGROUP(237, _, _, _, _, _, _, _, _, _), + [238] = UFS_RESET(ufs_reset, 0x1f9000), + [239] = SDC_QDSD_PINGROUP(sdc2_clk, 0x1f2000, 14, 6), + [240] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x1f2000, 11, 3), + [241] = SDC_QDSD_PINGROUP(sdc2_data, 0x1f2000, 9, 0), +}; + +static const struct msm_gpio_wakeirq_map x1e80100_pdc_map[] = { + { 0, 72 }, { 2, 70 }, { 3, 71 }, { 6, 123 }, { 7, 67 }, { 11, 85 }, + { 15, 68 }, { 18, 122 }, { 19, 69 }, { 21, 158 }, { 23, 143 }, { 26, 129 }, + { 27, 144 }, { 28, 77 }, { 29, 78 }, { 30, 92 }, { 32, 145 }, { 33, 115 }, + { 34, 130 }, { 35, 146 }, { 36, 147 }, { 39, 80 }, { 43, 148 }, { 47, 149 }, + { 51, 79 }, { 53, 89 }, { 59, 87 }, { 64, 90 }, { 65, 106 }, { 66, 142 }, + { 67, 88 }, { 71, 91 }, { 75, 152 }, { 79, 153 }, { 80, 125 }, { 81, 128 }, + { 84, 137 }, { 85, 155 }, { 87, 156 }, { 91, 157 }, { 92, 138 }, { 94, 140 }, + { 95, 141 }, { 113, 84 }, { 121, 73 }, { 123, 74 }, { 129, 76 }, { 131, 82 }, + { 134, 83 }, { 141, 93 }, { 144, 94 }, { 147, 96 }, { 148, 97 }, { 150, 102 }, + { 151, 103 }, { 153, 104 }, { 156, 105 }, { 157, 107 }, { 163, 98 }, { 166, 112 }, + { 172, 99 }, { 181, 101 }, { 184, 116 }, { 193, 40 }, { 193, 117 }, { 196, 108 }, + { 203, 133 }, { 212, 120 }, { 213, 150 }, { 214, 121 }, { 215, 118 }, { 217, 109 }, + { 220, 110 }, { 221, 111 }, { 222, 124 }, { 224, 131 }, { 225, 132 }, +}; + +static const struct msm_pinctrl_soc_data x1e80100_pinctrl = { + .pins = x1e80100_pins, + .npins = ARRAY_SIZE(x1e80100_pins), + .functions = x1e80100_functions, + .nfunctions = ARRAY_SIZE(x1e80100_functions), + .groups = x1e80100_groups, + .ngroups = ARRAY_SIZE(x1e80100_groups), + .ngpios = 239, + .wakeirq_map = x1e80100_pdc_map, + .nwakeirq_map = ARRAY_SIZE(x1e80100_pdc_map), + .egpio_func = 9, +}; + +static int x1e80100_pinctrl_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &x1e80100_pinctrl); +} + +static const struct of_device_id x1e80100_pinctrl_of_match[] = { + { .compatible = "qcom,x1e80100-tlmm", }, + { }, +}; + +static struct platform_driver x1e80100_pinctrl_driver = { + .driver = { + .name = "x1e80100-tlmm", + .of_match_table = x1e80100_pinctrl_of_match, + }, + .probe = x1e80100_pinctrl_probe, + .remove_new = msm_pinctrl_remove, +}; + +static int __init x1e80100_pinctrl_init(void) +{ + return platform_driver_register(&x1e80100_pinctrl_driver); +} +arch_initcall(x1e80100_pinctrl_init); + +static void __exit x1e80100_pinctrl_exit(void) +{ + platform_driver_unregister(&x1e80100_pinctrl_driver); +} +module_exit(x1e80100_pinctrl_exit); + +MODULE_DESCRIPTION("QTI X1E80100 TLMM pinctrl driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, x1e80100_pinctrl_of_match); diff --git a/drivers/pinctrl/renesas/pinctrl-rza1.c b/drivers/pinctrl/renesas/pinctrl-rza1.c index f43f1196fea8..edcbe7c9ad56 100644 --- a/drivers/pinctrl/renesas/pinctrl-rza1.c +++ b/drivers/pinctrl/renesas/pinctrl-rza1.c @@ -1131,7 +1131,7 @@ static int rza1_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, return -EINVAL; mux_confs = (struct rza1_mux_conf *)func->data; - for (i = 0; i < grp->num_pins; ++i) { + for (i = 0; i < grp->grp.npins; ++i) { int ret; ret = rza1_pin_mux_single(rza1_pctl, &mux_confs[i]); diff --git a/drivers/pinctrl/renesas/pinctrl-rza2.c b/drivers/pinctrl/renesas/pinctrl-rza2.c index 990b96d45967..af689d7c117f 100644 --- a/drivers/pinctrl/renesas/pinctrl-rza2.c +++ b/drivers/pinctrl/renesas/pinctrl-rza2.c @@ -447,15 +447,15 @@ static int rza2_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, psel_val = func->data; - for (i = 0; i < grp->num_pins; ++i) { + for (i = 0; i < grp->grp.npins; ++i) { dev_dbg(priv->dev, "Setting P%c_%d to PSEL=%d\n", - port_names[RZA2_PIN_ID_TO_PORT(grp->pins[i])], - RZA2_PIN_ID_TO_PIN(grp->pins[i]), + port_names[RZA2_PIN_ID_TO_PORT(grp->grp.pins[i])], + RZA2_PIN_ID_TO_PIN(grp->grp.pins[i]), psel_val[i]); rza2_set_pin_function( priv->base, - RZA2_PIN_ID_TO_PORT(grp->pins[i]), - RZA2_PIN_ID_TO_PIN(grp->pins[i]), + RZA2_PIN_ID_TO_PORT(grp->grp.pins[i]), + RZA2_PIN_ID_TO_PIN(grp->grp.pins[i]), psel_val[i]); } diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 9de350ad7e7d..80fb5011c7bb 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -57,6 +57,7 @@ #define PIN_CFG_FILCLKSEL BIT(12) #define PIN_CFG_IOLH_C BIT(13) #define PIN_CFG_SOFT_PS BIT(14) +#define PIN_CFG_OEN BIT(15) #define RZG2L_MPXED_COMMON_PIN_FUNCS(group) \ (PIN_CFG_IOLH_##group | \ @@ -107,8 +108,11 @@ #define IEN(off) (0x1800 + (off) * 8) #define ISEL(off) (0x2C00 + (off) * 8) #define SD_CH(off, ch) ((off) + (ch) * 4) +#define ETH_POC(off, ch) ((off) + (ch) * 4) #define QSPI (0x3008) +#define ETH_MODE (0x3018) +#define PVDD_2500 2 /* I/O domain voltage 2.5V */ #define PVDD_1800 1 /* I/O domain voltage <= 1.8V */ #define PVDD_3300 0 /* I/O domain voltage >= 3.3V */ @@ -116,7 +120,6 @@ #define PWPR_PFCWE BIT(6) /* PFC Register Write Enable */ #define PM_MASK 0x03 -#define PVDD_MASK 0x01 #define PFC_MASK 0x07 #define IEN_MASK 0x01 #define IOLH_MASK 0x03 @@ -135,10 +138,12 @@ * struct rzg2l_register_offsets - specific register offsets * @pwpr: PWPR register offset * @sd_ch: SD_CH register offset + * @eth_poc: ETH_POC register offset */ struct rzg2l_register_offsets { u16 pwpr; u16 sd_ch; + u16 eth_poc; }; /** @@ -167,6 +172,8 @@ enum rzg2l_iolh_index { * @iolh_groupb_oi: IOLH group B output impedance specific values * @drive_strength_ua: drive strength in uA is supported (otherwise mA is supported) * @func_base: base number for port function (see register PFC) + * @oen_max_pin: the maximum pin number supporting output enable + * @oen_max_port: the maximum port number supporting output enable */ struct rzg2l_hwcfg { const struct rzg2l_register_offsets regs; @@ -176,6 +183,8 @@ struct rzg2l_hwcfg { u16 iolh_groupb_oi[4]; bool drive_strength_ua; u8 func_base; + u8 oen_max_pin; + u8 oen_max_port; }; struct rzg2l_dedicated_configs { @@ -273,7 +282,7 @@ static int rzg2l_pinctrl_set_mux(struct pinctrl_dev *pctldev, struct function_desc *func; unsigned int i, *psel_val; struct group_desc *group; - int *pins; + const unsigned int *pins; func = pinmux_generic_get_function(pctldev, func_selector); if (!func) @@ -283,9 +292,9 @@ static int rzg2l_pinctrl_set_mux(struct pinctrl_dev *pctldev, return -EINVAL; psel_val = func->data; - pins = group->pins; + pins = group->grp.pins; - for (i = 0; i < group->num_pins; i++) { + for (i = 0; i < group->grp.npins; i++) { unsigned int *pin_data = pctrl->desc.pins[pins[i]].drv_data; u32 off = RZG2L_PIN_CFG_TO_PORT_OFFSET(*pin_data); u32 pin = RZG2L_PIN_ID_TO_PIN(pins[i]); @@ -376,8 +385,11 @@ static int rzg2l_dt_subnode_to_map(struct pinctrl_dev *pctldev, goto done; } - if (num_pinmux) + if (num_pinmux) { nmaps += 1; + if (num_configs) + nmaps += 1; + } if (num_pins) nmaps += num_pins; @@ -462,6 +474,16 @@ static int rzg2l_dt_subnode_to_map(struct pinctrl_dev *pctldev, maps[idx].data.mux.function = name; idx++; + if (num_configs) { + ret = rzg2l_map_add_config(&maps[idx], name, + PIN_MAP_TYPE_CONFIGS_GROUP, + configs, num_configs); + if (ret < 0) + goto remove_group; + + idx++; + } + dev_dbg(pctrl->dev, "Parsed %pOF with %d pins\n", np, num_pinmux); ret = 0; goto done; @@ -591,6 +613,10 @@ static int rzg2l_caps_to_pwr_reg(const struct rzg2l_register_offsets *regs, u32 return SD_CH(regs->sd_ch, 0); if (caps & PIN_CFG_IO_VMC_SD1) return SD_CH(regs->sd_ch, 1); + if (caps & PIN_CFG_IO_VMC_ETH0) + return ETH_POC(regs->eth_poc, 0); + if (caps & PIN_CFG_IO_VMC_ETH1) + return ETH_POC(regs->eth_poc, 1); if (caps & PIN_CFG_IO_VMC_QSPI) return QSPI; @@ -602,6 +628,7 @@ static int rzg2l_get_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg; const struct rzg2l_register_offsets *regs = &hwcfg->regs; int pwr_reg; + u8 val; if (caps & PIN_CFG_SOFT_PS) return pctrl->settings[pin].power_source; @@ -610,7 +637,18 @@ static int rzg2l_get_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps if (pwr_reg < 0) return pwr_reg; - return (readl(pctrl->base + pwr_reg) & PVDD_MASK) ? 1800 : 3300; + val = readb(pctrl->base + pwr_reg); + switch (val) { + case PVDD_1800: + return 1800; + case PVDD_2500: + return 2500; + case PVDD_3300: + return 3300; + default: + /* Should not happen. */ + return -EINVAL; + } } static int rzg2l_set_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps, u32 ps) @@ -618,17 +656,32 @@ static int rzg2l_set_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg; const struct rzg2l_register_offsets *regs = &hwcfg->regs; int pwr_reg; + u8 val; if (caps & PIN_CFG_SOFT_PS) { pctrl->settings[pin].power_source = ps; return 0; } + switch (ps) { + case 1800: + val = PVDD_1800; + break; + case 2500: + val = PVDD_2500; + break; + case 3300: + val = PVDD_3300; + break; + default: + return -EINVAL; + } + pwr_reg = rzg2l_caps_to_pwr_reg(regs, caps); if (pwr_reg < 0) return pwr_reg; - writel((ps == 1800) ? PVDD_1800 : PVDD_3300, pctrl->base + pwr_reg); + writeb(val, pctrl->base + pwr_reg); pctrl->settings[pin].power_source = ps; return 0; @@ -735,6 +788,66 @@ static bool rzg2l_ds_is_supported(struct rzg2l_pinctrl *pctrl, u32 caps, return false; } +static bool rzg2l_oen_is_supported(u32 caps, u8 pin, u8 max_pin) +{ + if (!(caps & PIN_CFG_OEN)) + return false; + + if (pin > max_pin) + return false; + + return true; +} + +static u8 rzg2l_pin_to_oen_bit(u32 offset, u8 pin, u8 max_port) +{ + if (pin) + pin *= 2; + + if (offset / RZG2L_PINS_PER_PORT == max_port) + pin += 1; + + return pin; +} + +static u32 rzg2l_read_oen(struct rzg2l_pinctrl *pctrl, u32 caps, u32 offset, u8 pin) +{ + u8 max_port = pctrl->data->hwcfg->oen_max_port; + u8 max_pin = pctrl->data->hwcfg->oen_max_pin; + u8 bit; + + if (!rzg2l_oen_is_supported(caps, pin, max_pin)) + return 0; + + bit = rzg2l_pin_to_oen_bit(offset, pin, max_port); + + return !(readb(pctrl->base + ETH_MODE) & BIT(bit)); +} + +static int rzg2l_write_oen(struct rzg2l_pinctrl *pctrl, u32 caps, u32 offset, u8 pin, u8 oen) +{ + u8 max_port = pctrl->data->hwcfg->oen_max_port; + u8 max_pin = pctrl->data->hwcfg->oen_max_pin; + unsigned long flags; + u8 val, bit; + + if (!rzg2l_oen_is_supported(caps, pin, max_pin)) + return -EINVAL; + + bit = rzg2l_pin_to_oen_bit(offset, pin, max_port); + + spin_lock_irqsave(&pctrl->lock, flags); + val = readb(pctrl->base + ETH_MODE); + if (oen) + val &= ~BIT(bit); + else + val |= BIT(bit); + writeb(val, pctrl->base + ETH_MODE); + spin_unlock_irqrestore(&pctrl->lock, flags); + + return 0; +} + static int rzg2l_pinctrl_pinconf_get(struct pinctrl_dev *pctldev, unsigned int _pin, unsigned long *config) @@ -772,6 +885,12 @@ static int rzg2l_pinctrl_pinconf_get(struct pinctrl_dev *pctldev, return -EINVAL; break; + case PIN_CONFIG_OUTPUT_ENABLE: + arg = rzg2l_read_oen(pctrl, cfg, _pin, bit); + if (!arg) + return -EINVAL; + break; + case PIN_CONFIG_POWER_SOURCE: ret = rzg2l_get_power_source(pctrl, _pin, cfg); if (ret < 0) @@ -842,7 +961,7 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, struct rzg2l_pinctrl_pin_settings settings = pctrl->settings[_pin]; unsigned int *pin_data = pin->drv_data; enum pin_config_param param; - unsigned int i; + unsigned int i, arg, index; u32 cfg, off; int ret; u8 bit; @@ -864,24 +983,28 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(_configs[i]); switch (param) { - case PIN_CONFIG_INPUT_ENABLE: { - unsigned int arg = - pinconf_to_config_argument(_configs[i]); + case PIN_CONFIG_INPUT_ENABLE: + arg = pinconf_to_config_argument(_configs[i]); if (!(cfg & PIN_CFG_IEN)) return -EINVAL; rzg2l_rmw_pin_config(pctrl, IEN(off), bit, IEN_MASK, !!arg); break; - } + + case PIN_CONFIG_OUTPUT_ENABLE: + arg = pinconf_to_config_argument(_configs[i]); + ret = rzg2l_write_oen(pctrl, cfg, _pin, bit, !!arg); + if (ret) + return ret; + break; case PIN_CONFIG_POWER_SOURCE: settings.power_source = pinconf_to_config_argument(_configs[i]); break; - case PIN_CONFIG_DRIVE_STRENGTH: { - unsigned int arg = pinconf_to_config_argument(_configs[i]); - unsigned int index; + case PIN_CONFIG_DRIVE_STRENGTH: + arg = pinconf_to_config_argument(_configs[i]); if (!(cfg & PIN_CFG_IOLH_A) || hwcfg->drive_strength_ua) return -EINVAL; @@ -896,7 +1019,6 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, rzg2l_rmw_pin_config(pctrl, IOLH(off), bit, IOLH_MASK, index); break; - } case PIN_CONFIG_DRIVE_STRENGTH_UA: if (!(cfg & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | PIN_CFG_IOLH_C)) || @@ -906,9 +1028,8 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, settings.drive_strength_ua = pinconf_to_config_argument(_configs[i]); break; - case PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS: { - unsigned int arg = pinconf_to_config_argument(_configs[i]); - unsigned int index; + case PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS: + arg = pinconf_to_config_argument(_configs[i]); if (!(cfg & PIN_CFG_IOLH_B) || !hwcfg->iolh_groupb_oi[0]) return -EINVAL; @@ -922,7 +1043,6 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, rzg2l_rmw_pin_config(pctrl, IOLH(off), bit, IOLH_MASK, index); break; - } default: return -EOPNOTSUPP; @@ -1323,7 +1443,8 @@ static const u32 r9a07g043_gpio_configs[] = { static const u32 r9a08g045_gpio_configs[] = { RZG2L_GPIO_PORT_PACK(4, 0x20, RZG3S_MPXED_PIN_FUNCS(A)), /* P0 */ RZG2L_GPIO_PORT_PACK(5, 0x30, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IOLH_C | - PIN_CFG_IO_VMC_ETH0)), /* P1 */ + PIN_CFG_IO_VMC_ETH0)) | + PIN_CFG_OEN | PIN_CFG_IEN, /* P1 */ RZG2L_GPIO_PORT_PACK(4, 0x31, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IOLH_C | PIN_CFG_IO_VMC_ETH0)), /* P2 */ RZG2L_GPIO_PORT_PACK(4, 0x32, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IOLH_C | @@ -1333,7 +1454,8 @@ static const u32 r9a08g045_gpio_configs[] = { RZG2L_GPIO_PORT_PACK(5, 0x21, RZG3S_MPXED_PIN_FUNCS(A)), /* P5 */ RZG2L_GPIO_PORT_PACK(5, 0x22, RZG3S_MPXED_PIN_FUNCS(A)), /* P6 */ RZG2L_GPIO_PORT_PACK(5, 0x34, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IOLH_C | - PIN_CFG_IO_VMC_ETH1)), /* P7 */ + PIN_CFG_IO_VMC_ETH1)) | + PIN_CFG_OEN | PIN_CFG_IEN, /* P7 */ RZG2L_GPIO_PORT_PACK(5, 0x35, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IOLH_C | PIN_CFG_IO_VMC_ETH1)), /* P8 */ RZG2L_GPIO_PORT_PACK(4, 0x36, RZG2L_MPXED_ETH_PIN_FUNCS(PIN_CFG_IOLH_C | @@ -1576,6 +1698,7 @@ static const struct irq_chip rzg2l_gpio_irqchip = { .irq_set_type = rzg2l_gpio_irq_set_type, .irq_eoi = rzg2l_gpio_irqc_eoi, .irq_print_chip = rzg2l_gpio_irq_print_chip, + .irq_set_affinity = irq_chip_set_affinity_parent, .flags = IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; @@ -1877,6 +2000,7 @@ static const struct rzg2l_hwcfg rzg2l_hwcfg = { .regs = { .pwpr = 0x3014, .sd_ch = 0x3000, + .eth_poc = 0x300c, }, .iolh_groupa_ua = { /* 3v3 power source */ @@ -1889,6 +2013,7 @@ static const struct rzg2l_hwcfg rzg3s_hwcfg = { .regs = { .pwpr = 0x3000, .sd_ch = 0x3004, + .eth_poc = 0x3010, }, .iolh_groupa_ua = { /* 1v8 power source */ @@ -1912,6 +2037,8 @@ static const struct rzg2l_hwcfg rzg3s_hwcfg = { }, .drive_strength_ua = true, .func_base = 1, + .oen_max_pin = 1, /* Pin 1 of P0 and P7 is the maximum OEN pin. */ + .oen_max_port = 7, /* P7_1 is the maximum OEN port. */ }; static struct rzg2l_pinctrl_data r9a07g043_data = { diff --git a/drivers/pinctrl/renesas/pinctrl-rzv2m.c b/drivers/pinctrl/renesas/pinctrl-rzv2m.c index 21d7d5ac8c4a..0767a5ac23e0 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzv2m.c +++ b/drivers/pinctrl/renesas/pinctrl-rzv2m.c @@ -165,7 +165,7 @@ static int rzv2m_pinctrl_set_mux(struct pinctrl_dev *pctldev, struct function_desc *func; unsigned int i, *psel_val; struct group_desc *group; - int *pins; + const unsigned int *pins; func = pinmux_generic_get_function(pctldev, func_selector); if (!func) @@ -175,9 +175,9 @@ static int rzv2m_pinctrl_set_mux(struct pinctrl_dev *pctldev, return -EINVAL; psel_val = func->data; - pins = group->pins; + pins = group->grp.pins; - for (i = 0; i < group->num_pins; i++) { + for (i = 0; i < group->grp.npins; i++) { dev_dbg(pctrl->dev, "port:%u pin: %u PSEL:%u\n", RZV2M_PIN_ID_TO_PORT(pins[i]), RZV2M_PIN_ID_TO_PIN(pins[i]), psel_val[i]); diff --git a/drivers/pinctrl/samsung/pinctrl-exynos-arm64.c b/drivers/pinctrl/samsung/pinctrl-exynos-arm64.c index cb965cf93705..5480e0884abe 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos-arm64.c +++ b/drivers/pinctrl/samsung/pinctrl-exynos-arm64.c @@ -726,6 +726,146 @@ const struct samsung_pinctrl_of_match_data exynosautov9_of_data __initconst = { .num_ctrl = ARRAY_SIZE(exynosautov9_pin_ctrl), }; +/* pin banks of exynosautov920 pin-controller 0 (ALIVE) */ +static const struct samsung_pin_bank_data exynosautov920_pin_banks0[] = { + EXYNOSV920_PIN_BANK_EINTW(8, 0x0000, "gpa0", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTW(2, 0x1000, "gpa1", 0x18, 0x20, 0x24), + EXYNOS850_PIN_BANK_EINTN(2, 0x2000, "gpq0"), +}; + +/* pin banks of exynosautov920 pin-controller 1 (AUD) */ +static const struct samsung_pin_bank_data exynosautov920_pin_banks1[] = { + EXYNOSV920_PIN_BANK_EINTG(7, 0x0000, "gpb0", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(6, 0x1000, "gpb1", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(8, 0x2000, "gpb2", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(8, 0x3000, "gpb3", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(8, 0x4000, "gpb4", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(5, 0x5000, "gpb5", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(5, 0x6000, "gpb6", 0x18, 0x24, 0x28), +}; + +/* pin banks of exynosautov920 pin-controller 2 (HSI0) */ +static const struct samsung_pin_bank_data exynosautov920_pin_banks2[] = { + EXYNOSV920_PIN_BANK_EINTG(6, 0x0000, "gph0", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(2, 0x1000, "gph1", 0x18, 0x20, 0x24), +}; + +/* pin banks of exynosautov920 pin-controller 3 (HSI1) */ +static const struct samsung_pin_bank_data exynosautov920_pin_banks3[] = { + EXYNOSV920_PIN_BANK_EINTG(7, 0x000, "gph8", 0x18, 0x24, 0x28), +}; + +/* pin banks of exynosautov920 pin-controller 4 (HSI2) */ +static const struct samsung_pin_bank_data exynosautov920_pin_banks4[] = { + EXYNOSV920_PIN_BANK_EINTG(8, 0x0000, "gph3", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(7, 0x1000, "gph4", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(8, 0x2000, "gph5", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(7, 0x3000, "gph6", 0x18, 0x24, 0x28), +}; + +/* pin banks of exynosautov920 pin-controller 5 (HSI2UFS) */ +static const struct samsung_pin_bank_data exynosautov920_pin_banks5[] = { + EXYNOSV920_PIN_BANK_EINTG(4, 0x000, "gph2", 0x18, 0x20, 0x24), +}; + +/* pin banks of exynosautov920 pin-controller 6 (PERIC0) */ +static const struct samsung_pin_bank_data exynosautov920_pin_banks6[] = { + EXYNOSV920_PIN_BANK_EINTG(8, 0x0000, "gpp0", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(8, 0x1000, "gpp1", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(8, 0x2000, "gpp2", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(5, 0x3000, "gpg0", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(8, 0x4000, "gpp3", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(4, 0x5000, "gpp4", 0x18, 0x20, 0x24), + EXYNOSV920_PIN_BANK_EINTG(4, 0x6000, "gpg2", 0x18, 0x20, 0x24), + EXYNOSV920_PIN_BANK_EINTG(4, 0x7000, "gpg5", 0x18, 0x20, 0x24), + EXYNOSV920_PIN_BANK_EINTG(3, 0x8000, "gpg3", 0x18, 0x20, 0x24), + EXYNOSV920_PIN_BANK_EINTG(5, 0x9000, "gpg4", 0x18, 0x24, 0x28), +}; + +/* pin banks of exynosautov920 pin-controller 7 (PERIC1) */ +static const struct samsung_pin_bank_data exynosautov920_pin_banks7[] = { + EXYNOSV920_PIN_BANK_EINTG(8, 0x0000, "gpp5", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(5, 0x1000, "gpp6", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(4, 0x2000, "gpp10", 0x18, 0x20, 0x24), + EXYNOSV920_PIN_BANK_EINTG(8, 0x3000, "gpp7", 0x18, 0x24, 0x28), + EXYNOSV920_PIN_BANK_EINTG(4, 0x4000, "gpp8", 0x18, 0x20, 0x24), + EXYNOSV920_PIN_BANK_EINTG(4, 0x5000, "gpp11", 0x18, 0x20, 0x24), + EXYNOSV920_PIN_BANK_EINTG(4, 0x6000, "gpp9", 0x18, 0x20, 0x24), + EXYNOSV920_PIN_BANK_EINTG(4, 0x7000, "gpp12", 0x18, 0x20, 0x24), + EXYNOSV920_PIN_BANK_EINTG(8, 0x8000, "gpg1", 0x18, 0x24, 0x28), +}; + +static const struct samsung_retention_data exynosautov920_retention_data __initconst = { + .regs = NULL, + .nr_regs = 0, + .value = 0, + .refcnt = &exynos_shared_retention_refcnt, + .init = exynos_retention_init, +}; + +static const struct samsung_pin_ctrl exynosautov920_pin_ctrl[] = { + { + /* pin-controller instance 0 ALIVE data */ + .pin_banks = exynosautov920_pin_banks0, + .nr_banks = ARRAY_SIZE(exynosautov920_pin_banks0), + .eint_wkup_init = exynos_eint_wkup_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + .retention_data = &exynosautov920_retention_data, + }, { + /* pin-controller instance 1 AUD data */ + .pin_banks = exynosautov920_pin_banks1, + .nr_banks = ARRAY_SIZE(exynosautov920_pin_banks1), + }, { + /* pin-controller instance 2 HSI0 data */ + .pin_banks = exynosautov920_pin_banks2, + .nr_banks = ARRAY_SIZE(exynosautov920_pin_banks2), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 3 HSI1 data */ + .pin_banks = exynosautov920_pin_banks3, + .nr_banks = ARRAY_SIZE(exynosautov920_pin_banks3), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 4 HSI2 data */ + .pin_banks = exynosautov920_pin_banks4, + .nr_banks = ARRAY_SIZE(exynosautov920_pin_banks4), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 5 HSI2UFS data */ + .pin_banks = exynosautov920_pin_banks5, + .nr_banks = ARRAY_SIZE(exynosautov920_pin_banks5), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 6 PERIC0 data */ + .pin_banks = exynosautov920_pin_banks6, + .nr_banks = ARRAY_SIZE(exynosautov920_pin_banks6), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 7 PERIC1 data */ + .pin_banks = exynosautov920_pin_banks7, + .nr_banks = ARRAY_SIZE(exynosautov920_pin_banks7), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, +}; + +const struct samsung_pinctrl_of_match_data exynosautov920_of_data __initconst = { + .ctrl = exynosautov920_pin_ctrl, + .num_ctrl = ARRAY_SIZE(exynosautov920_pin_ctrl), +}; + /* * Pinctrl driver data for Tesla FSD SoC. FSD SoC includes three * gpio/pin-mux/pinconfig controllers. @@ -796,3 +936,143 @@ const struct samsung_pinctrl_of_match_data fsd_of_data __initconst = { .ctrl = fsd_pin_ctrl, .num_ctrl = ARRAY_SIZE(fsd_pin_ctrl), }; + +/* pin banks of gs101 pin-controller (ALIVE) */ +static const struct samsung_pin_bank_data gs101_pin_alive[] = { + EXYNOS850_PIN_BANK_EINTW(8, 0x0, "gpa0", 0x00), + EXYNOS850_PIN_BANK_EINTW(7, 0x20, "gpa1", 0x04), + EXYNOS850_PIN_BANK_EINTW(5, 0x40, "gpa2", 0x08), + EXYNOS850_PIN_BANK_EINTW(4, 0x60, "gpa3", 0x0c), + EXYNOS850_PIN_BANK_EINTW(4, 0x80, "gpa4", 0x10), + EXYNOS850_PIN_BANK_EINTW(7, 0xa0, "gpa5", 0x14), + EXYNOS850_PIN_BANK_EINTW(8, 0xc0, "gpa9", 0x18), + EXYNOS850_PIN_BANK_EINTW(2, 0xe0, "gpa10", 0x1c), +}; + +/* pin banks of gs101 pin-controller (FAR_ALIVE) */ +static const struct samsung_pin_bank_data gs101_pin_far_alive[] = { + EXYNOS850_PIN_BANK_EINTW(8, 0x0, "gpa6", 0x00), + EXYNOS850_PIN_BANK_EINTW(4, 0x20, "gpa7", 0x04), + EXYNOS850_PIN_BANK_EINTW(8, 0x40, "gpa8", 0x08), + EXYNOS850_PIN_BANK_EINTW(2, 0x60, "gpa11", 0x0c), +}; + +/* pin banks of gs101 pin-controller (GSACORE) */ +static const struct samsung_pin_bank_data gs101_pin_gsacore[] = { + EXYNOS850_PIN_BANK_EINTG(2, 0x0, "gps0", 0x00), + EXYNOS850_PIN_BANK_EINTG(8, 0x20, "gps1", 0x04), + EXYNOS850_PIN_BANK_EINTG(3, 0x40, "gps2", 0x08), +}; + +/* pin banks of gs101 pin-controller (GSACTRL) */ +static const struct samsung_pin_bank_data gs101_pin_gsactrl[] = { + EXYNOS850_PIN_BANK_EINTW(6, 0x0, "gps3", 0x00), +}; + +/* pin banks of gs101 pin-controller (PERIC0) */ +static const struct samsung_pin_bank_data gs101_pin_peric0[] = { + EXYNOS850_PIN_BANK_EINTG(5, 0x0, "gpp0", 0x00), + EXYNOS850_PIN_BANK_EINTG(4, 0x20, "gpp1", 0x04), + EXYNOS850_PIN_BANK_EINTG(4, 0x40, "gpp2", 0x08), + EXYNOS850_PIN_BANK_EINTG(2, 0x60, "gpp3", 0x0c), + EXYNOS850_PIN_BANK_EINTG(4, 0x80, "gpp4", 0x10), + EXYNOS850_PIN_BANK_EINTG(2, 0xa0, "gpp5", 0x14), + EXYNOS850_PIN_BANK_EINTG(4, 0xc0, "gpp6", 0x18), + EXYNOS850_PIN_BANK_EINTG(2, 0xe0, "gpp7", 0x1c), + EXYNOS850_PIN_BANK_EINTG(4, 0x100, "gpp8", 0x20), + EXYNOS850_PIN_BANK_EINTG(2, 0x120, "gpp9", 0x24), + EXYNOS850_PIN_BANK_EINTG(4, 0x140, "gpp10", 0x28), + EXYNOS850_PIN_BANK_EINTG(2, 0x160, "gpp11", 0x2c), + EXYNOS850_PIN_BANK_EINTG(4, 0x180, "gpp12", 0x30), + EXYNOS850_PIN_BANK_EINTG(2, 0x1a0, "gpp13", 0x34), + EXYNOS850_PIN_BANK_EINTG(4, 0x1c0, "gpp14", 0x38), + EXYNOS850_PIN_BANK_EINTG(2, 0x1e0, "gpp15", 0x3c), + EXYNOS850_PIN_BANK_EINTG(4, 0x200, "gpp16", 0x40), + EXYNOS850_PIN_BANK_EINTG(2, 0x220, "gpp17", 0x44), + EXYNOS850_PIN_BANK_EINTG(4, 0x240, "gpp18", 0x48), + EXYNOS850_PIN_BANK_EINTG(4, 0x260, "gpp19", 0x4c), +}; + +/* pin banks of gs101 pin-controller (PERIC1) */ +static const struct samsung_pin_bank_data gs101_pin_peric1[] = { + EXYNOS850_PIN_BANK_EINTG(8, 0x0, "gpp20", 0x00), + EXYNOS850_PIN_BANK_EINTG(4, 0x20, "gpp21", 0x04), + EXYNOS850_PIN_BANK_EINTG(2, 0x40, "gpp22", 0x08), + EXYNOS850_PIN_BANK_EINTG(8, 0x60, "gpp23", 0x0c), + EXYNOS850_PIN_BANK_EINTG(4, 0x80, "gpp24", 0x10), + EXYNOS850_PIN_BANK_EINTG(4, 0xa0, "gpp25", 0x14), + EXYNOS850_PIN_BANK_EINTG(5, 0xc0, "gpp26", 0x18), + EXYNOS850_PIN_BANK_EINTG(4, 0xe0, "gpp27", 0x1c), +}; + +/* pin banks of gs101 pin-controller (HSI1) */ +static const struct samsung_pin_bank_data gs101_pin_hsi1[] = { + EXYNOS850_PIN_BANK_EINTG(6, 0x0, "gph0", 0x00), + EXYNOS850_PIN_BANK_EINTG(7, 0x20, "gph1", 0x04), +}; + +/* pin banks of gs101 pin-controller (HSI2) */ +static const struct samsung_pin_bank_data gs101_pin_hsi2[] = { + EXYNOS850_PIN_BANK_EINTG(6, 0x0, "gph2", 0x00), + EXYNOS850_PIN_BANK_EINTG(2, 0x20, "gph3", 0x04), + EXYNOS850_PIN_BANK_EINTG(6, 0x40, "gph4", 0x08), +}; + +static const struct samsung_pin_ctrl gs101_pin_ctrl[] __initconst = { + { + /* pin banks of gs101 pin-controller (ALIVE) */ + .pin_banks = gs101_pin_alive, + .nr_banks = ARRAY_SIZE(gs101_pin_alive), + .eint_wkup_init = exynos_eint_wkup_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin banks of gs101 pin-controller (FAR_ALIVE) */ + .pin_banks = gs101_pin_far_alive, + .nr_banks = ARRAY_SIZE(gs101_pin_far_alive), + .eint_wkup_init = exynos_eint_wkup_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin banks of gs101 pin-controller (GSACORE) */ + .pin_banks = gs101_pin_gsacore, + .nr_banks = ARRAY_SIZE(gs101_pin_gsacore), + }, { + /* pin banks of gs101 pin-controller (GSACTRL) */ + .pin_banks = gs101_pin_gsactrl, + .nr_banks = ARRAY_SIZE(gs101_pin_gsactrl), + }, { + /* pin banks of gs101 pin-controller (PERIC0) */ + .pin_banks = gs101_pin_peric0, + .nr_banks = ARRAY_SIZE(gs101_pin_peric0), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin banks of gs101 pin-controller (PERIC1) */ + .pin_banks = gs101_pin_peric1, + .nr_banks = ARRAY_SIZE(gs101_pin_peric1), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin banks of gs101 pin-controller (HSI1) */ + .pin_banks = gs101_pin_hsi1, + .nr_banks = ARRAY_SIZE(gs101_pin_hsi1), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin banks of gs101 pin-controller (HSI2) */ + .pin_banks = gs101_pin_hsi2, + .nr_banks = ARRAY_SIZE(gs101_pin_hsi2), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, +}; + +const struct samsung_pinctrl_of_match_data gs101_of_data __initconst = { + .ctrl = gs101_pin_ctrl, + .num_ctrl = ARRAY_SIZE(gs101_pin_ctrl), +}; diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c index 6b58ec84e34b..871c1eb46ddf 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos.c +++ b/drivers/pinctrl/samsung/pinctrl-exynos.c @@ -52,10 +52,15 @@ static void exynos_irq_mask(struct irq_data *irqd) struct irq_chip *chip = irq_data_get_irq_chip(irqd); struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); - unsigned long reg_mask = our_chip->eint_mask + bank->eint_offset; + unsigned long reg_mask; unsigned int mask; unsigned long flags; + if (bank->eint_mask_offset) + reg_mask = bank->pctl_offset + bank->eint_mask_offset; + else + reg_mask = our_chip->eint_mask + bank->eint_offset; + raw_spin_lock_irqsave(&bank->slock, flags); mask = readl(bank->eint_base + reg_mask); @@ -70,7 +75,12 @@ static void exynos_irq_ack(struct irq_data *irqd) struct irq_chip *chip = irq_data_get_irq_chip(irqd); struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); - unsigned long reg_pend = our_chip->eint_pend + bank->eint_offset; + unsigned long reg_pend; + + if (bank->eint_pend_offset) + reg_pend = bank->pctl_offset + bank->eint_pend_offset; + else + reg_pend = our_chip->eint_pend + bank->eint_offset; writel(1 << irqd->hwirq, bank->eint_base + reg_pend); } @@ -80,7 +90,7 @@ static void exynos_irq_unmask(struct irq_data *irqd) struct irq_chip *chip = irq_data_get_irq_chip(irqd); struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip); struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); - unsigned long reg_mask = our_chip->eint_mask + bank->eint_offset; + unsigned long reg_mask; unsigned int mask; unsigned long flags; @@ -95,6 +105,11 @@ static void exynos_irq_unmask(struct irq_data *irqd) if (irqd_get_trigger_type(irqd) & IRQ_TYPE_LEVEL_MASK) exynos_irq_ack(irqd); + if (bank->eint_mask_offset) + reg_mask = bank->pctl_offset + bank->eint_mask_offset; + else + reg_mask = our_chip->eint_mask + bank->eint_offset; + raw_spin_lock_irqsave(&bank->slock, flags); mask = readl(bank->eint_base + reg_mask); @@ -111,7 +126,7 @@ static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type) struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq; unsigned int con, trig_type; - unsigned long reg_con = our_chip->eint_con + bank->eint_offset; + unsigned long reg_con; switch (type) { case IRQ_TYPE_EDGE_RISING: @@ -139,6 +154,11 @@ static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type) else irq_set_handler_locked(irqd, handle_level_irq); + if (bank->eint_con_offset) + reg_con = bank->pctl_offset + bank->eint_con_offset; + else + reg_con = our_chip->eint_con + bank->eint_offset; + con = readl(bank->eint_base + reg_con); con &= ~(EXYNOS_EINT_CON_MASK << shift); con |= trig_type << shift; @@ -147,6 +167,19 @@ static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type) return 0; } +static int exynos_irq_set_affinity(struct irq_data *irqd, + const struct cpumask *dest, bool force) +{ + struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); + struct samsung_pinctrl_drv_data *d = bank->drvdata; + struct irq_data *parent = irq_get_irq_data(d->irq); + + if (parent) + return parent->chip->irq_set_affinity(parent, dest, force); + + return -EINVAL; +} + static int exynos_irq_request_resources(struct irq_data *irqd) { struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); @@ -212,6 +245,7 @@ static const struct exynos_irq_chip exynos_gpio_irq_chip __initconst = { .irq_mask = exynos_irq_mask, .irq_ack = exynos_irq_ack, .irq_set_type = exynos_irq_set_type, + .irq_set_affinity = exynos_irq_set_affinity, .irq_request_resources = exynos_irq_request_resources, .irq_release_resources = exynos_irq_release_resources, }, @@ -247,7 +281,10 @@ static irqreturn_t exynos_eint_gpio_irq(int irq, void *data) unsigned int svc, group, pin; int ret; - svc = readl(bank->eint_base + EXYNOS_SVC_OFFSET); + if (bank->eint_con_offset) + svc = readl(bank->eint_base + EXYNOSAUTO_SVC_OFFSET); + else + svc = readl(bank->eint_base + EXYNOS_SVC_OFFSET); group = EXYNOS_SVC_GROUP(svc); pin = svc & EXYNOS_SVC_NUM_MASK; @@ -456,6 +493,22 @@ static const struct exynos_irq_chip exynos7_wkup_irq_chip __initconst = { .set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask, }; +static const struct exynos_irq_chip exynosautov920_wkup_irq_chip __initconst = { + .chip = { + .name = "exynosautov920_wkup_irq_chip", + .irq_unmask = exynos_irq_unmask, + .irq_mask = exynos_irq_mask, + .irq_ack = exynos_irq_ack, + .irq_set_type = exynos_irq_set_type, + .irq_set_wake = exynos_wkup_irq_set_wake, + .irq_request_resources = exynos_irq_request_resources, + .irq_release_resources = exynos_irq_release_resources, + }, + .eint_wake_mask_value = &eint_wake_mask_value, + .eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK, + .set_eint_wakeup_mask = exynos_pinctrl_set_eint_wakeup_mask, +}; + /* list of external wakeup controllers supported */ static const struct of_device_id exynos_wkup_irq_ids[] = { { .compatible = "samsung,s5pv210-wakeup-eint", @@ -468,6 +521,8 @@ static const struct of_device_id exynos_wkup_irq_ids[] = { .data = &exynos7_wkup_irq_chip }, { .compatible = "samsung,exynosautov9-wakeup-eint", .data = &exynos7_wkup_irq_chip }, + { .compatible = "samsung,exynosautov920-wakeup-eint", + .data = &exynosautov920_wkup_irq_chip }, { } }; @@ -638,7 +693,7 @@ static void exynos_pinctrl_suspend_bank( struct samsung_pin_bank *bank) { struct exynos_eint_gpio_save *save = bank->soc_priv; - void __iomem *regs = bank->eint_base; + const void __iomem *regs = bank->eint_base; save->eint_con = readl(regs + EXYNOS_GPIO_ECON_OFFSET + bank->eint_offset); @@ -655,6 +710,19 @@ static void exynos_pinctrl_suspend_bank( pr_debug("%s: save mask %#010x\n", bank->name, save->eint_mask); } +static void exynosauto_pinctrl_suspend_bank(struct samsung_pinctrl_drv_data *drvdata, + struct samsung_pin_bank *bank) +{ + struct exynos_eint_gpio_save *save = bank->soc_priv; + const void __iomem *regs = bank->eint_base; + + save->eint_con = readl(regs + bank->pctl_offset + bank->eint_con_offset); + save->eint_mask = readl(regs + bank->pctl_offset + bank->eint_mask_offset); + + pr_debug("%s: save con %#010x\n", bank->name, save->eint_con); + pr_debug("%s: save mask %#010x\n", bank->name, save->eint_mask); +} + void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata) { struct samsung_pin_bank *bank = drvdata->pin_banks; @@ -662,8 +730,12 @@ void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata) int i; for (i = 0; i < drvdata->nr_banks; ++i, ++bank) { - if (bank->eint_type == EINT_TYPE_GPIO) - exynos_pinctrl_suspend_bank(drvdata, bank); + if (bank->eint_type == EINT_TYPE_GPIO) { + if (bank->eint_con_offset) + exynosauto_pinctrl_suspend_bank(drvdata, bank); + else + exynos_pinctrl_suspend_bank(drvdata, bank); + } else if (bank->eint_type == EINT_TYPE_WKUP) { if (!irq_chip) { irq_chip = bank->irq_chip; @@ -704,14 +776,33 @@ static void exynos_pinctrl_resume_bank( + bank->eint_offset); } +static void exynosauto_pinctrl_resume_bank(struct samsung_pinctrl_drv_data *drvdata, + struct samsung_pin_bank *bank) +{ + struct exynos_eint_gpio_save *save = bank->soc_priv; + void __iomem *regs = bank->eint_base; + + pr_debug("%s: con %#010x => %#010x\n", bank->name, + readl(regs + bank->pctl_offset + bank->eint_con_offset), save->eint_con); + pr_debug("%s: mask %#010x => %#010x\n", bank->name, + readl(regs + bank->pctl_offset + bank->eint_mask_offset), save->eint_mask); + + writel(save->eint_con, regs + bank->pctl_offset + bank->eint_con_offset); + writel(save->eint_mask, regs + bank->pctl_offset + bank->eint_mask_offset); +} + void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata) { struct samsung_pin_bank *bank = drvdata->pin_banks; int i; for (i = 0; i < drvdata->nr_banks; ++i, ++bank) - if (bank->eint_type == EINT_TYPE_GPIO) - exynos_pinctrl_resume_bank(drvdata, bank); + if (bank->eint_type == EINT_TYPE_GPIO) { + if (bank->eint_con_offset) + exynosauto_pinctrl_resume_bank(drvdata, bank); + else + exynos_pinctrl_resume_bank(drvdata, bank); + } } static void exynos_retention_enable(struct samsung_pinctrl_drv_data *drvdata) diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.h b/drivers/pinctrl/samsung/pinctrl-exynos.h index 3ac52c2cf998..305cb1d31de4 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos.h +++ b/drivers/pinctrl/samsung/pinctrl-exynos.h @@ -31,6 +31,7 @@ #define EXYNOS7_WKUP_EMASK_OFFSET 0x900 #define EXYNOS7_WKUP_EPEND_OFFSET 0xA00 #define EXYNOS_SVC_OFFSET 0xB08 +#define EXYNOSAUTO_SVC_OFFSET 0xF008 /* helpers to access interrupt service register */ #define EXYNOS_SVC_GROUP_SHIFT 3 @@ -140,6 +141,30 @@ .name = id \ } +#define EXYNOSV920_PIN_BANK_EINTG(pins, reg, id, con_offs, mask_offs, pend_offs) \ + { \ + .type = &exynos850_bank_type_off, \ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_GPIO, \ + .eint_con_offset = con_offs, \ + .eint_mask_offset = mask_offs, \ + .eint_pend_offset = pend_offs, \ + .name = id \ + } + +#define EXYNOSV920_PIN_BANK_EINTW(pins, reg, id, con_offs, mask_offs, pend_offs) \ + { \ + .type = &exynos850_bank_type_alive, \ + .pctl_offset = reg, \ + .nr_pins = pins, \ + .eint_type = EINT_TYPE_WKUP, \ + .eint_con_offset = con_offs, \ + .eint_mask_offset = mask_offs, \ + .eint_pend_offset = pend_offs, \ + .name = id \ + } + /** * struct exynos_weint_data: irq specific data for all the wakeup interrupts * generated by the external wakeup interrupt controller. diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c index 79babbb39ced..ed07e23e0912 100644 --- a/drivers/pinctrl/samsung/pinctrl-samsung.c +++ b/drivers/pinctrl/samsung/pinctrl-samsung.c @@ -565,7 +565,7 @@ static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value) /* gpiolib gpio_get callback function */ static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset) { - void __iomem *reg; + const void __iomem *reg; u32 data; struct samsung_pin_bank *bank = gpiochip_get_data(gc); const struct samsung_pin_bank_type *type = bank->type; @@ -1106,6 +1106,9 @@ samsung_pinctrl_get_soc_data(struct samsung_pinctrl_drv_data *d, bank->eint_type = bdata->eint_type; bank->eint_mask = bdata->eint_mask; bank->eint_offset = bdata->eint_offset; + bank->eint_con_offset = bdata->eint_con_offset; + bank->eint_mask_offset = bdata->eint_mask_offset; + bank->eint_pend_offset = bdata->eint_pend_offset; bank->name = bdata->name; raw_spin_lock_init(&bank->slock); @@ -1201,7 +1204,7 @@ static int __maybe_unused samsung_pinctrl_suspend(struct device *dev) for (i = 0; i < drvdata->nr_banks; i++) { struct samsung_pin_bank *bank = &drvdata->pin_banks[i]; - void __iomem *reg = bank->pctl_base + bank->pctl_offset; + const void __iomem *reg = bank->pctl_base + bank->pctl_offset; const u8 *offs = bank->type->reg_offset; const u8 *widths = bank->type->fld_width; enum pincfg_type type; @@ -1309,6 +1312,8 @@ static const struct of_device_id samsung_pinctrl_dt_match[] = { .data = &s5pv210_of_data }, #endif #ifdef CONFIG_PINCTRL_EXYNOS_ARM64 + { .compatible = "google,gs101-pinctrl", + .data = &gs101_of_data }, { .compatible = "samsung,exynos5433-pinctrl", .data = &exynos5433_of_data }, { .compatible = "samsung,exynos7-pinctrl", @@ -1319,6 +1324,8 @@ static const struct of_device_id samsung_pinctrl_dt_match[] = { .data = &exynos850_of_data }, { .compatible = "samsung,exynosautov9-pinctrl", .data = &exynosautov9_of_data }, + { .compatible = "samsung,exynosautov920-pinctrl", + .data = &exynosautov920_of_data }, { .compatible = "tesla,fsd-pinctrl", .data = &fsd_of_data }, #endif diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.h b/drivers/pinctrl/samsung/pinctrl-samsung.h index 9b3db50adef3..ab791afaabf5 100644 --- a/drivers/pinctrl/samsung/pinctrl-samsung.h +++ b/drivers/pinctrl/samsung/pinctrl-samsung.h @@ -122,6 +122,9 @@ struct samsung_pin_bank_type { * @eint_type: type of the external interrupt supported by the bank. * @eint_mask: bit mask of pins which support EINT function. * @eint_offset: SoC-specific EINT register or interrupt offset of bank. + * @eint_con_offset: ExynosAuto SoC-specific EINT control register offset of bank. + * @eint_mask_offset: ExynosAuto SoC-specific EINT mask register offset of bank. + * @eint_pend_offset: ExynosAuto SoC-specific EINT pend register offset of bank. * @name: name to be prefixed for each pin in this pin bank. */ struct samsung_pin_bank_data { @@ -133,6 +136,9 @@ struct samsung_pin_bank_data { enum eint_type eint_type; u32 eint_mask; u32 eint_offset; + u32 eint_con_offset; + u32 eint_mask_offset; + u32 eint_pend_offset; const char *name; }; @@ -147,6 +153,9 @@ struct samsung_pin_bank_data { * @eint_type: type of the external interrupt supported by the bank. * @eint_mask: bit mask of pins which support EINT function. * @eint_offset: SoC-specific EINT register or interrupt offset of bank. + * @eint_con_offset: ExynosAuto SoC-specific EINT register or interrupt offset of bank. + * @eint_mask_offset: ExynosAuto SoC-specific EINT mask register offset of bank. + * @eint_pend_offset: ExynosAuto SoC-specific EINT pend register offset of bank. * @name: name to be prefixed for each pin in this pin bank. * @id: id of the bank, propagated to the pin range. * @pin_base: starting pin number of the bank. @@ -170,6 +179,9 @@ struct samsung_pin_bank { enum eint_type eint_type; u32 eint_mask; u32 eint_offset; + u32 eint_con_offset; + u32 eint_mask_offset; + u32 eint_pend_offset; const char *name; u32 id; @@ -350,7 +362,9 @@ extern const struct samsung_pinctrl_of_match_data exynos7_of_data; extern const struct samsung_pinctrl_of_match_data exynos7885_of_data; extern const struct samsung_pinctrl_of_match_data exynos850_of_data; extern const struct samsung_pinctrl_of_match_data exynosautov9_of_data; +extern const struct samsung_pinctrl_of_match_data exynosautov920_of_data; extern const struct samsung_pinctrl_of_match_data fsd_of_data; +extern const struct samsung_pinctrl_of_match_data gs101_of_data; extern const struct samsung_pinctrl_of_match_data s3c64xx_of_data; extern const struct samsung_pinctrl_of_match_data s3c2412_of_data; extern const struct samsung_pinctrl_of_match_data s3c2416_of_data; diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c b/drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c index b29b0ab9892b..6df7a310c7ed 100644 --- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c +++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c @@ -654,7 +654,7 @@ static int starfive_set_mux(struct pinctrl_dev *pctldev, return -EINVAL; pinmux = group->data; - for (i = 0; i < group->num_pins; i++) { + for (i = 0; i < group->grp.npins; i++) { u32 v = pinmux[i]; unsigned int gpio = starfive_pinmux_to_gpio(v); u32 dout = starfive_pinmux_to_dout(v); @@ -797,7 +797,7 @@ static int starfive_pinconf_group_get(struct pinctrl_dev *pctldev, if (!group) return -EINVAL; - return starfive_pinconf_get(pctldev, group->pins[0], config); + return starfive_pinconf_get(pctldev, group->grp.pins[0], config); } static int starfive_pinconf_group_set(struct pinctrl_dev *pctldev, @@ -876,8 +876,8 @@ static int starfive_pinconf_group_set(struct pinctrl_dev *pctldev, } } - for (i = 0; i < group->num_pins; i++) - starfive_padctl_rmw(sfp, group->pins[i], mask, value); + for (i = 0; i < group->grp.npins; i++) + starfive_padctl_rmw(sfp, group->grp.pins[i], mask, value); return 0; } diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c index 6de11a405734..9609eb1ecc3d 100644 --- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c +++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c @@ -307,7 +307,7 @@ static int jh7110_set_mux(struct pinctrl_dev *pctldev, return -EINVAL; pinmux = group->data; - for (i = 0; i < group->num_pins; i++) { + for (i = 0; i < group->grp.npins; i++) { u32 v = pinmux[i]; if (info->jh7110_set_one_pin_mux) @@ -437,7 +437,7 @@ static int jh7110_pinconf_group_get(struct pinctrl_dev *pctldev, if (!group) return -EINVAL; - return jh7110_pinconf_get(pctldev, group->pins[0], config); + return jh7110_pinconf_get(pctldev, group->grp.pins[0], config); } static int jh7110_pinconf_group_set(struct pinctrl_dev *pctldev, @@ -508,8 +508,8 @@ static int jh7110_pinconf_group_set(struct pinctrl_dev *pctldev, } } - for (i = 0; i < group->num_pins; i++) - jh7110_padcfg_rmw(sfp, group->pins[i], mask, value); + for (i = 0; i < group->grp.npins; i++) + jh7110_padcfg_rmw(sfp, group->grp.pins[i], mask, value); return 0; } diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c index 603f900e88c1..978ccdbaf3d3 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32.c @@ -240,9 +240,8 @@ static int stm32_gpio_direction_output(struct gpio_chip *chip, struct stm32_gpio_bank *bank = gpiochip_get_data(chip); __stm32_gpio_set(bank, offset, value); - pinctrl_gpio_direction_output(chip, offset); - return 0; + return pinctrl_gpio_direction_output(chip, offset); } diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c index 6bf8db424bec..ccfa3870a67d 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -636,6 +636,14 @@ static void tegra_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, seq_printf(s, "\n\t%s=%u", strip_prefix(cfg_params[i].property), val); } + + if (g->mux_reg >= 0) { + /* read pinmux function and dump to seq_file */ + val = pmx_readl(pmx, g->mux_bank, g->mux_reg); + val = g->funcs[(val >> g->mux_bit) & 0x3]; + + seq_printf(s, "\n\tfunction=%s", pmx->functions[val].name); + } } static void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, diff --git a/drivers/platform/chrome/cros_ec_uart.c b/drivers/platform/chrome/cros_ec_uart.c index 788246559bbb..68d80559fddc 100644 --- a/drivers/platform/chrome/cros_ec_uart.c +++ b/drivers/platform/chrome/cros_ec_uart.c @@ -81,9 +81,8 @@ struct cros_ec_uart { struct response_info response; }; -static int cros_ec_uart_rx_bytes(struct serdev_device *serdev, - const u8 *data, - size_t count) +static ssize_t cros_ec_uart_rx_bytes(struct serdev_device *serdev, + const u8 *data, size_t count) { struct ec_host_response *host_response; struct cros_ec_device *ec_dev = serdev_device_get_drvdata(serdev); diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 7737d56191d7..061aa9647c19 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -915,12 +915,11 @@ static int goldfish_pipe_probe(struct platform_device *pdev) return goldfish_pipe_device_init(pdev, dev); } -static int goldfish_pipe_remove(struct platform_device *pdev) +static void goldfish_pipe_remove(struct platform_device *pdev) { struct goldfish_pipe_dev *dev = platform_get_drvdata(pdev); goldfish_pipe_device_deinit(pdev, dev); - return 0; } static const struct acpi_device_id goldfish_pipe_acpi_match[] = { @@ -937,7 +936,7 @@ MODULE_DEVICE_TABLE(of, goldfish_pipe_of_match); static struct platform_driver goldfish_pipe_driver = { .probe = goldfish_pipe_probe, - .remove = goldfish_pipe_remove, + .remove_new = goldfish_pipe_remove, .driver = { .name = "goldfish_pipe", .of_match_table = goldfish_pipe_of_match, diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index 1dd84c7a79de..b1995ac268d7 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -1170,7 +1170,7 @@ static int mlxbf_pmc_program_crspace_counter(int blk_num, uint32_t cnt_num, int ret; addr = pmc->block[blk_num].mmio_base + - (rounddown(cnt_num, 2) * MLXBF_PMC_CRSPACE_PERFSEL_SZ); + ((cnt_num / 2) * MLXBF_PMC_CRSPACE_PERFSEL_SZ); ret = mlxbf_pmc_readl(addr, &word); if (ret) return ret; @@ -1413,7 +1413,7 @@ static int mlxbf_pmc_read_crspace_event(int blk_num, uint32_t cnt_num, int ret; addr = pmc->block[blk_num].mmio_base + - (rounddown(cnt_num, 2) * MLXBF_PMC_CRSPACE_PERFSEL_SZ); + ((cnt_num / 2) * MLXBF_PMC_CRSPACE_PERFSEL_SZ); ret = mlxbf_pmc_readl(addr, &word); if (ret) return ret; diff --git a/drivers/platform/mellanox/mlxbf-tmfifo.c b/drivers/platform/mellanox/mlxbf-tmfifo.c index ed16ec422a7b..b8d1e32e97eb 100644 --- a/drivers/platform/mellanox/mlxbf-tmfifo.c +++ b/drivers/platform/mellanox/mlxbf-tmfifo.c @@ -47,6 +47,9 @@ /* Message with data needs at least two words (for header & data). */ #define MLXBF_TMFIFO_DATA_MIN_WORDS 2 +/* Tx timeout in milliseconds. */ +#define TMFIFO_TX_TIMEOUT 2000 + /* ACPI UID for BlueField-3. */ #define TMFIFO_BF3_UID 1 @@ -62,12 +65,14 @@ struct mlxbf_tmfifo; * @drop_desc: dummy desc for packet dropping * @cur_len: processed length of the current descriptor * @rem_len: remaining length of the pending packet + * @rem_padding: remaining bytes to send as paddings * @pkt_len: total length of the pending packet * @next_avail: next avail descriptor id * @num: vring size (number of descriptors) * @align: vring alignment size * @index: vring index * @vdev_id: vring virtio id (VIRTIO_ID_xxx) + * @tx_timeout: expire time of last tx packet * @fifo: pointer to the tmfifo structure */ struct mlxbf_tmfifo_vring { @@ -79,12 +84,14 @@ struct mlxbf_tmfifo_vring { struct vring_desc drop_desc; int cur_len; int rem_len; + int rem_padding; u32 pkt_len; u16 next_avail; int num; int align; int index; int vdev_id; + unsigned long tx_timeout; struct mlxbf_tmfifo *fifo; }; @@ -819,6 +826,50 @@ mlxbf_tmfifo_desc_done: return true; } +static void mlxbf_tmfifo_check_tx_timeout(struct mlxbf_tmfifo_vring *vring) +{ + unsigned long flags; + + /* Only handle Tx timeout for network vdev. */ + if (vring->vdev_id != VIRTIO_ID_NET) + return; + + /* Initialize the timeout or return if not expired. */ + if (!vring->tx_timeout) { + /* Initialize the timeout. */ + vring->tx_timeout = jiffies + + msecs_to_jiffies(TMFIFO_TX_TIMEOUT); + return; + } else if (time_before(jiffies, vring->tx_timeout)) { + /* Return if not timeout yet. */ + return; + } + + /* + * Drop the packet after timeout. The outstanding packet is + * released and the remaining bytes will be sent with padding byte 0x00 + * as a recovery. On the peer(host) side, the padding bytes 0x00 will be + * either dropped directly, or appended into existing outstanding packet + * thus dropped as corrupted network packet. + */ + vring->rem_padding = round_up(vring->rem_len, sizeof(u64)); + mlxbf_tmfifo_release_pkt(vring); + vring->cur_len = 0; + vring->rem_len = 0; + vring->fifo->vring[0] = NULL; + + /* + * Make sure the load/store are in order before + * returning back to virtio. + */ + virtio_mb(false); + + /* Notify upper layer. */ + spin_lock_irqsave(&vring->fifo->spin_lock[0], flags); + vring_interrupt(0, vring->vq); + spin_unlock_irqrestore(&vring->fifo->spin_lock[0], flags); +} + /* Rx & Tx processing of a queue. */ static void mlxbf_tmfifo_rxtx(struct mlxbf_tmfifo_vring *vring, bool is_rx) { @@ -841,6 +892,7 @@ static void mlxbf_tmfifo_rxtx(struct mlxbf_tmfifo_vring *vring, bool is_rx) return; do { +retry: /* Get available FIFO space. */ if (avail == 0) { if (is_rx) @@ -851,6 +903,17 @@ static void mlxbf_tmfifo_rxtx(struct mlxbf_tmfifo_vring *vring, bool is_rx) break; } + /* Insert paddings for discarded Tx packet. */ + if (!is_rx) { + vring->tx_timeout = 0; + while (vring->rem_padding >= sizeof(u64)) { + writeq(0, vring->fifo->tx.data); + vring->rem_padding -= sizeof(u64); + if (--avail == 0) + goto retry; + } + } + /* Console output always comes from the Tx buffer. */ if (!is_rx && devid == VIRTIO_ID_CONSOLE) { mlxbf_tmfifo_console_tx(fifo, avail); @@ -860,6 +923,10 @@ static void mlxbf_tmfifo_rxtx(struct mlxbf_tmfifo_vring *vring, bool is_rx) /* Handle one descriptor. */ more = mlxbf_tmfifo_rxtx_one_desc(vring, is_rx, &avail); } while (more); + + /* Check Tx timeout. */ + if (avail <= 0 && !is_rx) + mlxbf_tmfifo_check_tx_timeout(vring); } /* Handle Rx or Tx queues. */ diff --git a/drivers/platform/surface/aggregator/bus.c b/drivers/platform/surface/aggregator/bus.c index 42ccd7f1c9b9..118caa651bec 100644 --- a/drivers/platform/surface/aggregator/bus.c +++ b/drivers/platform/surface/aggregator/bus.c @@ -35,6 +35,8 @@ static struct attribute *ssam_device_attrs[] = { }; ATTRIBUTE_GROUPS(ssam_device); +static const struct bus_type ssam_bus_type; + static int ssam_device_uevent(const struct device *dev, struct kobj_uevent_env *env) { const struct ssam_device *sdev = to_ssam_device(dev); @@ -329,13 +331,12 @@ static void ssam_bus_remove(struct device *dev) sdrv->remove(to_ssam_device(dev)); } -struct bus_type ssam_bus_type = { +static const struct bus_type ssam_bus_type = { .name = "surface_aggregator", .match = ssam_bus_match, .probe = ssam_bus_probe, .remove = ssam_bus_remove, }; -EXPORT_SYMBOL_GPL(ssam_bus_type); /** * __ssam_device_driver_register() - Register a SSAM client device driver. diff --git a/drivers/platform/surface/aggregator/controller.h b/drivers/platform/surface/aggregator/controller.h index f0d987abc51e..f1638c2081e8 100644 --- a/drivers/platform/surface/aggregator/controller.h +++ b/drivers/platform/surface/aggregator/controller.h @@ -238,8 +238,8 @@ struct ssam_controller { * layer of the controller has been shut down, %-ESHUTDOWN. */ static inline -int ssam_controller_receive_buf(struct ssam_controller *ctrl, - const unsigned char *buf, size_t n) +ssize_t ssam_controller_receive_buf(struct ssam_controller *ctrl, const u8 *buf, + size_t n) { return ssh_ptl_rx_rcvbuf(&ctrl->rtl.ptl, buf, n); } diff --git a/drivers/platform/surface/aggregator/core.c b/drivers/platform/surface/aggregator/core.c index 6152be38398c..9591a28bc38a 100644 --- a/drivers/platform/surface/aggregator/core.c +++ b/drivers/platform/surface/aggregator/core.c @@ -227,8 +227,8 @@ EXPORT_SYMBOL_GPL(ssam_client_bind); /* -- Glue layer (serdev_device -> ssam_controller). ------------------------ */ -static int ssam_receive_buf(struct serdev_device *dev, const unsigned char *buf, - size_t n) +static ssize_t ssam_receive_buf(struct serdev_device *dev, const u8 *buf, + size_t n) { struct ssam_controller *ctrl; int ret; diff --git a/drivers/platform/surface/aggregator/ssh_packet_layer.c b/drivers/platform/surface/aggregator/ssh_packet_layer.c index def8d7ac541f..d726b1a86319 100644 --- a/drivers/platform/surface/aggregator/ssh_packet_layer.c +++ b/drivers/platform/surface/aggregator/ssh_packet_layer.c @@ -1887,9 +1887,9 @@ int ssh_ptl_rx_stop(struct ssh_ptl *ptl) * Return: Returns the number of bytes transferred (positive or zero) on * success. Returns %-ESHUTDOWN if the packet layer has been shut down. */ -int ssh_ptl_rx_rcvbuf(struct ssh_ptl *ptl, const u8 *buf, size_t n) +ssize_t ssh_ptl_rx_rcvbuf(struct ssh_ptl *ptl, const u8 *buf, size_t n) { - int used; + size_t used; if (test_bit(SSH_PTL_SF_SHUTDOWN_BIT, &ptl->state)) return -ESHUTDOWN; diff --git a/drivers/platform/surface/aggregator/ssh_packet_layer.h b/drivers/platform/surface/aggregator/ssh_packet_layer.h index 64633522f971..c80e822070df 100644 --- a/drivers/platform/surface/aggregator/ssh_packet_layer.h +++ b/drivers/platform/surface/aggregator/ssh_packet_layer.h @@ -162,7 +162,7 @@ void ssh_ptl_shutdown(struct ssh_ptl *ptl); int ssh_ptl_submit(struct ssh_ptl *ptl, struct ssh_packet *p); void ssh_ptl_cancel(struct ssh_packet *p); -int ssh_ptl_rx_rcvbuf(struct ssh_ptl *ptl, const u8 *buf, size_t n); +ssize_t ssh_ptl_rx_rcvbuf(struct ssh_ptl *ptl, const u8 *buf, size_t n); /** * ssh_ptl_tx_wakeup_transfer() - Wake up packet transmitter thread for diff --git a/drivers/platform/x86/amd/pmf/Kconfig b/drivers/platform/x86/amd/pmf/Kconfig index f246252bddd8..f4fa8bd8bda8 100644 --- a/drivers/platform/x86/amd/pmf/Kconfig +++ b/drivers/platform/x86/amd/pmf/Kconfig @@ -10,6 +10,7 @@ config AMD_PMF depends on AMD_NB select ACPI_PLATFORM_PROFILE depends on TEE && AMDTEE + depends on AMD_SFH_HID help This driver provides support for the AMD Platform Management Framework. The goal is to enhance end user experience by making AMD PCs smarter, diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c index a0423942f771..a3dec14c3004 100644 --- a/drivers/platform/x86/amd/pmf/spc.c +++ b/drivers/platform/x86/amd/pmf/spc.c @@ -10,6 +10,7 @@ */ #include <acpi/button.h> +#include <linux/amd-pmf-io.h> #include <linux/power_supply.h> #include <linux/units.h> #include "pmf.h" @@ -44,6 +45,8 @@ void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table * dev_dbg(dev->dev, "Max C0 Residency: %u\n", in->ev_info.max_c0residency); dev_dbg(dev->dev, "GFX Busy: %u\n", in->ev_info.gfx_busy); dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open"); + dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away"); + dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light); dev_dbg(dev->dev, "==== TA inputs END ====\n"); } #else @@ -147,6 +150,38 @@ static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_ return 0; } +static int amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) +{ + struct amd_sfh_info sfh_info; + int ret; + + /* Get ALS data */ + ret = amd_get_sfh_info(&sfh_info, MT_ALS); + if (!ret) + in->ev_info.ambient_light = sfh_info.ambient_light; + else + return ret; + + /* get HPD data */ + ret = amd_get_sfh_info(&sfh_info, MT_HPD); + if (ret) + return ret; + + switch (sfh_info.user_present) { + case SFH_NOT_DETECTED: + in->ev_info.user_present = 0xff; /* assume no sensors connected */ + break; + case SFH_USER_PRESENT: + in->ev_info.user_present = 1; + break; + case SFH_USER_AWAY: + in->ev_info.user_present = 0; + break; + } + + return 0; +} + void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) { /* TA side lid open is 1 and close is 0, hence the ! here */ @@ -155,4 +190,5 @@ void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_tab amd_pmf_get_smu_info(dev, in); amd_pmf_get_battery_info(dev, in); amd_pmf_get_slider_info(dev, in); + amd_pmf_get_sensor_info(dev, in); } diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index 502ce93d5cdd..f8c0177afb0d 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -298,8 +298,10 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf, if (!new_policy_buf) return -ENOMEM; - if (copy_from_user(new_policy_buf, buf, length)) + if (copy_from_user(new_policy_buf, buf, length)) { + kfree(new_policy_buf); return -EFAULT; + } kfree(dev->policy_buf); dev->policy_buf = new_policy_buf; diff --git a/drivers/platform/x86/intel/chtwc_int33fe.c b/drivers/platform/x86/intel/chtwc_int33fe.c index 848baecc1bb0..93f75ba1dafd 100644 --- a/drivers/platform/x86/intel/chtwc_int33fe.c +++ b/drivers/platform/x86/intel/chtwc_int33fe.c @@ -136,7 +136,7 @@ static const struct software_node altmodes_node = { }; static const struct property_entry dp_altmode_properties[] = { - PROPERTY_ENTRY_U32("svid", 0xff01), + PROPERTY_ENTRY_U16("svid", 0xff01), PROPERTY_ENTRY_U32("vdo", 0x0c0086), { } }; diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index a1ee1a74fc3c..2cf3b4a8813f 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -399,7 +399,8 @@ int ifs_load_firmware(struct device *dev) if (fw->size != expected_size) { dev_err(dev, "File size mismatch (expected %u, actual %zu). Corrupted IFS image.\n", expected_size, fw->size); - return -EINVAL; + ret = -EINVAL; + goto release; } ret = image_sanity_check(dev, (struct microcode_header_intel *)fw->data); diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c index 33ab207493e3..33bb58dc3f78 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c @@ -23,23 +23,23 @@ static int (*uncore_read)(struct uncore_data *data, unsigned int *min, unsigned static int (*uncore_write)(struct uncore_data *data, unsigned int input, unsigned int min_max); static int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq); -static ssize_t show_domain_id(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_domain_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - struct uncore_data *data = container_of(attr, struct uncore_data, domain_id_dev_attr); + struct uncore_data *data = container_of(attr, struct uncore_data, domain_id_kobj_attr); return sprintf(buf, "%u\n", data->domain_id); } -static ssize_t show_fabric_cluster_id(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_fabric_cluster_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_dev_attr); + struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_kobj_attr); return sprintf(buf, "%u\n", data->cluster_id); } -static ssize_t show_package_id(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_package_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - struct uncore_data *data = container_of(attr, struct uncore_data, package_id_dev_attr); + struct uncore_data *data = container_of(attr, struct uncore_data, package_id_kobj_attr); return sprintf(buf, "%u\n", data->package_id); } @@ -97,30 +97,30 @@ static ssize_t show_perf_status_freq_khz(struct uncore_data *data, char *buf) } #define store_uncore_min_max(name, min_max) \ - static ssize_t store_##name(struct device *dev, \ - struct device_attribute *attr, \ + static ssize_t store_##name(struct kobject *kobj, \ + struct kobj_attribute *attr, \ const char *buf, size_t count) \ { \ - struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\ + struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\ \ return store_min_max_freq_khz(data, buf, count, \ min_max); \ } #define show_uncore_min_max(name, min_max) \ - static ssize_t show_##name(struct device *dev, \ - struct device_attribute *attr, char *buf)\ + static ssize_t show_##name(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf)\ { \ - struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\ + struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\ \ return show_min_max_freq_khz(data, buf, min_max); \ } #define show_uncore_perf_status(name) \ - static ssize_t show_##name(struct device *dev, \ - struct device_attribute *attr, char *buf)\ + static ssize_t show_##name(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf)\ { \ - struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\ + struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\ \ return show_perf_status_freq_khz(data, buf); \ } @@ -134,11 +134,11 @@ show_uncore_min_max(max_freq_khz, 1); show_uncore_perf_status(current_freq_khz); #define show_uncore_data(member_name) \ - static ssize_t show_##member_name(struct device *dev, \ - struct device_attribute *attr, char *buf)\ + static ssize_t show_##member_name(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf)\ { \ struct uncore_data *data = container_of(attr, struct uncore_data,\ - member_name##_dev_attr);\ + member_name##_kobj_attr);\ \ return sysfs_emit(buf, "%u\n", \ data->member_name); \ @@ -149,29 +149,29 @@ show_uncore_data(initial_max_freq_khz); #define init_attribute_rw(_name) \ do { \ - sysfs_attr_init(&data->_name##_dev_attr.attr); \ - data->_name##_dev_attr.show = show_##_name; \ - data->_name##_dev_attr.store = store_##_name; \ - data->_name##_dev_attr.attr.name = #_name; \ - data->_name##_dev_attr.attr.mode = 0644; \ + sysfs_attr_init(&data->_name##_kobj_attr.attr); \ + data->_name##_kobj_attr.show = show_##_name; \ + data->_name##_kobj_attr.store = store_##_name; \ + data->_name##_kobj_attr.attr.name = #_name; \ + data->_name##_kobj_attr.attr.mode = 0644; \ } while (0) #define init_attribute_ro(_name) \ do { \ - sysfs_attr_init(&data->_name##_dev_attr.attr); \ - data->_name##_dev_attr.show = show_##_name; \ - data->_name##_dev_attr.store = NULL; \ - data->_name##_dev_attr.attr.name = #_name; \ - data->_name##_dev_attr.attr.mode = 0444; \ + sysfs_attr_init(&data->_name##_kobj_attr.attr); \ + data->_name##_kobj_attr.show = show_##_name; \ + data->_name##_kobj_attr.store = NULL; \ + data->_name##_kobj_attr.attr.name = #_name; \ + data->_name##_kobj_attr.attr.mode = 0444; \ } while (0) #define init_attribute_root_ro(_name) \ do { \ - sysfs_attr_init(&data->_name##_dev_attr.attr); \ - data->_name##_dev_attr.show = show_##_name; \ - data->_name##_dev_attr.store = NULL; \ - data->_name##_dev_attr.attr.name = #_name; \ - data->_name##_dev_attr.attr.mode = 0400; \ + sysfs_attr_init(&data->_name##_kobj_attr.attr); \ + data->_name##_kobj_attr.show = show_##_name; \ + data->_name##_kobj_attr.store = NULL; \ + data->_name##_kobj_attr.attr.name = #_name; \ + data->_name##_kobj_attr.attr.mode = 0400; \ } while (0) static int create_attr_group(struct uncore_data *data, char *name) @@ -186,21 +186,21 @@ static int create_attr_group(struct uncore_data *data, char *name) if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) { init_attribute_root_ro(domain_id); - data->uncore_attrs[index++] = &data->domain_id_dev_attr.attr; + data->uncore_attrs[index++] = &data->domain_id_kobj_attr.attr; init_attribute_root_ro(fabric_cluster_id); - data->uncore_attrs[index++] = &data->fabric_cluster_id_dev_attr.attr; + data->uncore_attrs[index++] = &data->fabric_cluster_id_kobj_attr.attr; init_attribute_root_ro(package_id); - data->uncore_attrs[index++] = &data->package_id_dev_attr.attr; + data->uncore_attrs[index++] = &data->package_id_kobj_attr.attr; } - data->uncore_attrs[index++] = &data->max_freq_khz_dev_attr.attr; - data->uncore_attrs[index++] = &data->min_freq_khz_dev_attr.attr; - data->uncore_attrs[index++] = &data->initial_min_freq_khz_dev_attr.attr; - data->uncore_attrs[index++] = &data->initial_max_freq_khz_dev_attr.attr; + data->uncore_attrs[index++] = &data->max_freq_khz_kobj_attr.attr; + data->uncore_attrs[index++] = &data->min_freq_khz_kobj_attr.attr; + data->uncore_attrs[index++] = &data->initial_min_freq_khz_kobj_attr.attr; + data->uncore_attrs[index++] = &data->initial_max_freq_khz_kobj_attr.attr; ret = uncore_read_freq(data, &freq); if (!ret) - data->uncore_attrs[index++] = &data->current_freq_khz_dev_attr.attr; + data->uncore_attrs[index++] = &data->current_freq_khz_kobj_attr.attr; data->uncore_attrs[index] = NULL; diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h index 7afb69977c7e..0e5bf507e555 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h @@ -26,14 +26,14 @@ * @instance_id: Unique instance id to append to directory name * @name: Sysfs entry name for this instance * @uncore_attr_group: Attribute group storage - * @max_freq_khz_dev_attr: Storage for device attribute max_freq_khz - * @mix_freq_khz_dev_attr: Storage for device attribute min_freq_khz - * @initial_max_freq_khz_dev_attr: Storage for device attribute initial_max_freq_khz - * @initial_min_freq_khz_dev_attr: Storage for device attribute initial_min_freq_khz - * @current_freq_khz_dev_attr: Storage for device attribute current_freq_khz - * @domain_id_dev_attr: Storage for device attribute domain_id - * @fabric_cluster_id_dev_attr: Storage for device attribute fabric_cluster_id - * @package_id_dev_attr: Storage for device attribute package_id + * @max_freq_khz_kobj_attr: Storage for kobject attribute max_freq_khz + * @mix_freq_khz_kobj_attr: Storage for kobject attribute min_freq_khz + * @initial_max_freq_khz_kobj_attr: Storage for kobject attribute initial_max_freq_khz + * @initial_min_freq_khz_kobj_attr: Storage for kobject attribute initial_min_freq_khz + * @current_freq_khz_kobj_attr: Storage for kobject attribute current_freq_khz + * @domain_id_kobj_attr: Storage for kobject attribute domain_id + * @fabric_cluster_id_kobj_attr: Storage for kobject attribute fabric_cluster_id + * @package_id_kobj_attr: Storage for kobject attribute package_id * @uncore_attrs: Attribute storage for group creation * * This structure is used to encapsulate all data related to uncore sysfs @@ -53,14 +53,14 @@ struct uncore_data { char name[32]; struct attribute_group uncore_attr_group; - struct device_attribute max_freq_khz_dev_attr; - struct device_attribute min_freq_khz_dev_attr; - struct device_attribute initial_max_freq_khz_dev_attr; - struct device_attribute initial_min_freq_khz_dev_attr; - struct device_attribute current_freq_khz_dev_attr; - struct device_attribute domain_id_dev_attr; - struct device_attribute fabric_cluster_id_dev_attr; - struct device_attribute package_id_dev_attr; + struct kobj_attribute max_freq_khz_kobj_attr; + struct kobj_attribute min_freq_khz_kobj_attr; + struct kobj_attribute initial_max_freq_khz_kobj_attr; + struct kobj_attribute initial_min_freq_khz_kobj_attr; + struct kobj_attribute current_freq_khz_kobj_attr; + struct kobj_attribute domain_id_kobj_attr; + struct kobj_attribute fabric_cluster_id_kobj_attr; + struct kobj_attribute package_id_kobj_attr; struct attribute *uncore_attrs[9]; }; diff --git a/drivers/platform/x86/intel/wmi/sbl-fw-update.c b/drivers/platform/x86/intel/wmi/sbl-fw-update.c index 9cf5ed0f8dc2..040153ad67c1 100644 --- a/drivers/platform/x86/intel/wmi/sbl-fw-update.c +++ b/drivers/platform/x86/intel/wmi/sbl-fw-update.c @@ -32,7 +32,7 @@ static int get_fwu_request(struct device *dev, u32 *out) return -ENODEV; if (obj->type != ACPI_TYPE_INTEGER) { - dev_warn(dev, "wmi_query_block returned invalid value\n"); + dev_warn(dev, "wmidev_block_query returned invalid value\n"); kfree(obj); return -EINVAL; } @@ -55,7 +55,7 @@ static int set_fwu_request(struct device *dev, u32 in) status = wmidev_block_set(to_wmi_device(dev), 0, &input); if (ACPI_FAILURE(status)) { - dev_err(dev, "wmi_set_block failed\n"); + dev_err(dev, "wmidev_block_set failed\n"); return -ENODEV; } diff --git a/drivers/platform/x86/lenovo-yogabook.c b/drivers/platform/x86/lenovo-yogabook.c index b8d0239192cb..fd62bf746ebd 100644 --- a/drivers/platform/x86/lenovo-yogabook.c +++ b/drivers/platform/x86/lenovo-yogabook.c @@ -435,7 +435,7 @@ static int yogabook_pdev_set_kbd_backlight(struct yogabook_data *data, u8 level) .enabled = level, }; - pwm_apply_state(data->kbd_bl_pwm, &state); + pwm_apply_might_sleep(data->kbd_bl_pwm, &state); gpiod_set_value(data->kbd_bl_led_enable, level ? 1 : 0); return 0; } diff --git a/drivers/platform/x86/p2sb.c b/drivers/platform/x86/p2sb.c index 1cf2471d54dd..6bd14d0132db 100644 --- a/drivers/platform/x86/p2sb.c +++ b/drivers/platform/x86/p2sb.c @@ -26,6 +26,21 @@ static const struct x86_cpu_id p2sb_cpu_ids[] = { {} }; +/* + * Cache BAR0 of P2SB device functions 0 to 7. + * TODO: The constant 8 is the number of functions that PCI specification + * defines. Same definitions exist tree-wide. Unify this definition and + * the other definitions then move to include/uapi/linux/pci.h. + */ +#define NR_P2SB_RES_CACHE 8 + +struct p2sb_res_cache { + u32 bus_dev_id; + struct resource res; +}; + +static struct p2sb_res_cache p2sb_resources[NR_P2SB_RES_CACHE]; + static int p2sb_get_devfn(unsigned int *devfn) { unsigned int fn = P2SB_DEVFN_DEFAULT; @@ -39,10 +54,18 @@ static int p2sb_get_devfn(unsigned int *devfn) return 0; } +static bool p2sb_valid_resource(struct resource *res) +{ + if (res->flags) + return true; + + return false; +} + /* Copy resource from the first BAR of the device in question */ -static int p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem) +static void p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem) { - struct resource *bar0 = &pdev->resource[0]; + struct resource *bar0 = pci_resource_n(pdev, 0); /* Make sure we have no dangling pointers in the output */ memset(mem, 0, sizeof(*mem)); @@ -56,49 +79,66 @@ static int p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem) mem->end = bar0->end; mem->flags = bar0->flags; mem->desc = bar0->desc; - - return 0; } -static int p2sb_scan_and_read(struct pci_bus *bus, unsigned int devfn, struct resource *mem) +static void p2sb_scan_and_cache_devfn(struct pci_bus *bus, unsigned int devfn) { + struct p2sb_res_cache *cache = &p2sb_resources[PCI_FUNC(devfn)]; struct pci_dev *pdev; - int ret; pdev = pci_scan_single_device(bus, devfn); if (!pdev) - return -ENODEV; + return; - ret = p2sb_read_bar0(pdev, mem); + p2sb_read_bar0(pdev, &cache->res); + cache->bus_dev_id = bus->dev.id; pci_stop_and_remove_bus_device(pdev); - return ret; } -/** - * p2sb_bar - Get Primary to Sideband (P2SB) bridge device BAR - * @bus: PCI bus to communicate with - * @devfn: PCI slot and function to communicate with - * @mem: memory resource to be filled in - * - * The BIOS prevents the P2SB device from being enumerated by the PCI - * subsystem, so we need to unhide and hide it back to lookup the BAR. - * - * if @bus is NULL, the bus 0 in domain 0 will be used. - * If @devfn is 0, it will be replaced by devfn of the P2SB device. - * - * Caller must provide a valid pointer to @mem. - * - * Locking is handled by pci_rescan_remove_lock mutex. - * - * Return: - * 0 on success or appropriate errno value on error. - */ -int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) +static int p2sb_scan_and_cache(struct pci_bus *bus, unsigned int devfn) +{ + unsigned int slot, fn; + + if (PCI_FUNC(devfn) == 0) { + /* + * When function number of the P2SB device is zero, scan it and + * other function numbers, and if devices are available, cache + * their BAR0s. + */ + slot = PCI_SLOT(devfn); + for (fn = 0; fn < NR_P2SB_RES_CACHE; fn++) + p2sb_scan_and_cache_devfn(bus, PCI_DEVFN(slot, fn)); + } else { + /* Scan the P2SB device and cache its BAR0 */ + p2sb_scan_and_cache_devfn(bus, devfn); + } + + if (!p2sb_valid_resource(&p2sb_resources[PCI_FUNC(devfn)].res)) + return -ENOENT; + + return 0; +} + +static struct pci_bus *p2sb_get_bus(struct pci_bus *bus) +{ + static struct pci_bus *p2sb_bus; + + bus = bus ?: p2sb_bus; + if (bus) + return bus; + + /* Assume P2SB is on the bus 0 in domain 0 */ + p2sb_bus = pci_find_bus(0, 0); + return p2sb_bus; +} + +static int p2sb_cache_resources(void) { - struct pci_dev *pdev_p2sb; unsigned int devfn_p2sb; u32 value = P2SBC_HIDE; + struct pci_bus *bus; + u16 class; int ret; /* Get devfn for P2SB device itself */ @@ -106,8 +146,17 @@ int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) if (ret) return ret; - /* if @bus is NULL, use bus 0 in domain 0 */ - bus = bus ?: pci_find_bus(0, 0); + bus = p2sb_get_bus(NULL); + if (!bus) + return -ENODEV; + + /* + * When a device with same devfn exists and its device class is not + * PCI_CLASS_MEMORY_OTHER for P2SB, do not touch it. + */ + pci_bus_read_config_word(bus, devfn_p2sb, PCI_CLASS_DEVICE, &class); + if (!PCI_POSSIBLE_ERROR(class) && class != PCI_CLASS_MEMORY_OTHER) + return -ENODEV; /* * Prevent concurrent PCI bus scan from seeing the P2SB device and @@ -115,17 +164,16 @@ int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) */ pci_lock_rescan_remove(); - /* Unhide the P2SB device, if needed */ + /* + * The BIOS prevents the P2SB device from being enumerated by the PCI + * subsystem, so we need to unhide and hide it back to lookup the BAR. + * Unhide the P2SB device here, if needed. + */ pci_bus_read_config_dword(bus, devfn_p2sb, P2SBC, &value); if (value & P2SBC_HIDE) pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, 0); - pdev_p2sb = pci_scan_single_device(bus, devfn_p2sb); - if (devfn) - ret = p2sb_scan_and_read(bus, devfn, mem); - else - ret = p2sb_read_bar0(pdev_p2sb, mem); - pci_stop_and_remove_bus_device(pdev_p2sb); + ret = p2sb_scan_and_cache(bus, devfn_p2sb); /* Hide the P2SB device, if it was hidden */ if (value & P2SBC_HIDE) @@ -133,12 +181,62 @@ int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) pci_unlock_rescan_remove(); - if (ret) - return ret; + return ret; +} + +/** + * p2sb_bar - Get Primary to Sideband (P2SB) bridge device BAR + * @bus: PCI bus to communicate with + * @devfn: PCI slot and function to communicate with + * @mem: memory resource to be filled in + * + * If @bus is NULL, the bus 0 in domain 0 will be used. + * If @devfn is 0, it will be replaced by devfn of the P2SB device. + * + * Caller must provide a valid pointer to @mem. + * + * Return: + * 0 on success or appropriate errno value on error. + */ +int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) +{ + struct p2sb_res_cache *cache; + int ret; + + bus = p2sb_get_bus(bus); + if (!bus) + return -ENODEV; + + if (!devfn) { + ret = p2sb_get_devfn(&devfn); + if (ret) + return ret; + } - if (mem->flags == 0) + cache = &p2sb_resources[PCI_FUNC(devfn)]; + if (cache->bus_dev_id != bus->dev.id) return -ENODEV; + if (!p2sb_valid_resource(&cache->res)) + return -ENOENT; + + memcpy(mem, &cache->res, sizeof(*mem)); return 0; } EXPORT_SYMBOL_GPL(p2sb_bar); + +static int __init p2sb_fs_init(void) +{ + p2sb_cache_resources(); + return 0; +} + +/* + * pci_rescan_remove_lock to avoid access to unhidden P2SB devices can + * not be locked in sysfs pci bus rescan path because of deadlock. To + * avoid the deadlock, access to P2SB devices with the lock at an early + * step in kernel initialization and cache required resources. This + * should happen after subsys_initcall which initializes PCI subsystem + * and before device_initcall which requires P2SB resources. + */ +fs_initcall(p2sb_fs_init); diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 0c6733772698..7aee5e9ff2b8 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -944,6 +944,32 @@ static const struct ts_dmi_data teclast_tbook11_data = { .properties = teclast_tbook11_props, }; +static const struct property_entry teclast_x16_plus_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 8), + PROPERTY_ENTRY_U32("touchscreen-min-y", 14), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1916), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1264), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-teclast-x16-plus.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct ts_dmi_data teclast_x16_plus_data = { + .embedded_fw = { + .name = "silead/gsl3692-teclast-x16-plus.fw", + .prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }, + .length = 43560, + .sha256 = { 0x9d, 0xb0, 0x3d, 0xf1, 0x00, 0x3c, 0xb5, 0x25, + 0x62, 0x8a, 0xa0, 0x93, 0x4b, 0xe0, 0x4e, 0x75, + 0xd1, 0x27, 0xb1, 0x65, 0x3c, 0xba, 0xa5, 0x0f, + 0xcd, 0xb4, 0xbe, 0x00, 0xbb, 0xf6, 0x43, 0x29 }, + }, + .acpi_name = "MSSL1680:00", + .properties = teclast_x16_plus_props, +}; + static const struct property_entry teclast_x3_plus_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1980), PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), @@ -1613,6 +1639,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = { }, }, { + /* Teclast X16 Plus */ + .driver_data = (void *)&teclast_x16_plus_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"), + DMI_MATCH(DMI_PRODUCT_NAME, "Default string"), + DMI_MATCH(DMI_PRODUCT_SKU, "D3A5_A1"), + }, + }, + { /* Teclast X3 Plus */ .driver_data = (void *)&teclast_x3_plus_data, .matches = { diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index bd271a5730aa..3c288e8f404b 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -25,6 +25,7 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/rwsem.h> #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/types.h> @@ -56,7 +57,6 @@ static_assert(__alignof__(struct guid_block) == 1); enum { /* wmi_block flags */ WMI_READ_TAKES_NO_ARGS, - WMI_PROBED, }; struct wmi_block { @@ -64,8 +64,10 @@ struct wmi_block { struct list_head list; struct guid_block gblock; struct acpi_device *acpi_device; + struct rw_semaphore notify_lock; /* Protects notify callback add/remove */ wmi_notify_handler handler; void *handler_data; + bool driver_ready; unsigned long flags; }; @@ -219,6 +221,17 @@ static int wmidev_match_guid(struct device *dev, const void *data) return 0; } +static int wmidev_match_notify_id(struct device *dev, const void *data) +{ + struct wmi_block *wblock = dev_to_wblock(dev); + const u32 *notify_id = data; + + if (wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *notify_id) + return 1; + + return 0; +} + static struct bus_type wmi_bus_type; static struct wmi_device *wmi_find_device_by_guid(const char *guid_string) @@ -238,6 +251,17 @@ static struct wmi_device *wmi_find_device_by_guid(const char *guid_string) return dev_to_wdev(dev); } +static struct wmi_device *wmi_find_event_by_notify_id(const u32 notify_id) +{ + struct device *dev; + + dev = bus_find_device(&wmi_bus_type, NULL, ¬ify_id, wmidev_match_notify_id); + if (!dev) + return ERR_PTR(-ENODEV); + + return to_wmi_device(dev); +} + static void wmi_device_put(struct wmi_device *wdev) { put_device(&wdev->dev); @@ -572,32 +596,31 @@ acpi_status wmi_install_notify_handler(const char *guid, wmi_notify_handler handler, void *data) { - struct wmi_block *block; - acpi_status status = AE_NOT_EXIST; - guid_t guid_input; - - if (!guid || !handler) - return AE_BAD_PARAMETER; + struct wmi_block *wblock; + struct wmi_device *wdev; + acpi_status status; - if (guid_parse(guid, &guid_input)) - return AE_BAD_PARAMETER; + wdev = wmi_find_device_by_guid(guid); + if (IS_ERR(wdev)) + return AE_ERROR; - list_for_each_entry(block, &wmi_block_list, list) { - acpi_status wmi_status; + wblock = container_of(wdev, struct wmi_block, dev); - if (guid_equal(&block->gblock.guid, &guid_input)) { - if (block->handler) - return AE_ALREADY_ACQUIRED; + down_write(&wblock->notify_lock); + if (wblock->handler) { + status = AE_ALREADY_ACQUIRED; + } else { + wblock->handler = handler; + wblock->handler_data = data; - block->handler = handler; - block->handler_data = data; + if (ACPI_FAILURE(wmi_method_enable(wblock, true))) + dev_warn(&wblock->dev.dev, "Failed to enable device\n"); - wmi_status = wmi_method_enable(block, true); - if ((wmi_status != AE_OK) || - ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) - status = wmi_status; - } + status = AE_OK; } + up_write(&wblock->notify_lock); + + wmi_device_put(wdev); return status; } @@ -613,30 +636,31 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler); */ acpi_status wmi_remove_notify_handler(const char *guid) { - struct wmi_block *block; - acpi_status status = AE_NOT_EXIST; - guid_t guid_input; + struct wmi_block *wblock; + struct wmi_device *wdev; + acpi_status status; - if (!guid) - return AE_BAD_PARAMETER; + wdev = wmi_find_device_by_guid(guid); + if (IS_ERR(wdev)) + return AE_ERROR; - if (guid_parse(guid, &guid_input)) - return AE_BAD_PARAMETER; + wblock = container_of(wdev, struct wmi_block, dev); - list_for_each_entry(block, &wmi_block_list, list) { - acpi_status wmi_status; + down_write(&wblock->notify_lock); + if (!wblock->handler) { + status = AE_NULL_ENTRY; + } else { + if (ACPI_FAILURE(wmi_method_enable(wblock, false))) + dev_warn(&wblock->dev.dev, "Failed to disable device\n"); - if (guid_equal(&block->gblock.guid, &guid_input)) { - if (!block->handler) - return AE_NULL_ENTRY; + wblock->handler = NULL; + wblock->handler_data = NULL; - wmi_status = wmi_method_enable(block, false); - block->handler = NULL; - block->handler_data = NULL; - if (wmi_status != AE_OK || (wmi_status == AE_OK && status == AE_NOT_EXIST)) - status = wmi_status; - } + status = AE_OK; } + up_write(&wblock->notify_lock); + + wmi_device_put(wdev); return status; } @@ -655,15 +679,19 @@ EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) { struct wmi_block *wblock; + struct wmi_device *wdev; + acpi_status status; - list_for_each_entry(wblock, &wmi_block_list, list) { - struct guid_block *gblock = &wblock->gblock; + wdev = wmi_find_event_by_notify_id(event); + if (IS_ERR(wdev)) + return AE_NOT_FOUND; - if ((gblock->flags & ACPI_WMI_EVENT) && gblock->notify_id == event) - return get_event_data(wblock, out); - } + wblock = container_of(wdev, struct wmi_block, dev); + status = get_event_data(wblock, out); - return AE_NOT_FOUND; + wmi_device_put(wdev); + + return status; } EXPORT_SYMBOL_GPL(wmi_get_event_data); @@ -868,7 +896,7 @@ static int wmi_dev_probe(struct device *dev) if (wdriver->probe) { ret = wdriver->probe(dev_to_wdev(dev), find_guid_context(wblock, wdriver)); - if (!ret) { + if (ret) { if (ACPI_FAILURE(wmi_method_enable(wblock, false))) dev_warn(dev, "Failed to disable device\n"); @@ -876,7 +904,9 @@ static int wmi_dev_probe(struct device *dev) } } - set_bit(WMI_PROBED, &wblock->flags); + down_write(&wblock->notify_lock); + wblock->driver_ready = true; + up_write(&wblock->notify_lock); return 0; } @@ -886,7 +916,9 @@ static void wmi_dev_remove(struct device *dev) struct wmi_block *wblock = dev_to_wblock(dev); struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); - clear_bit(WMI_PROBED, &wblock->flags); + down_write(&wblock->notify_lock); + wblock->driver_ready = false; + up_write(&wblock->notify_lock); if (wdriver->remove) wdriver->remove(dev_to_wdev(dev)); @@ -999,6 +1031,8 @@ static int wmi_create_device(struct device *wmi_bus_dev, wblock->dev.setable = true; out_init: + init_rwsem(&wblock->notify_lock); + wblock->driver_ready = false; wblock->dev.dev.bus = &wmi_bus_type; wblock->dev.dev.parent = wmi_bus_dev; @@ -1171,6 +1205,26 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, } } +static void wmi_notify_driver(struct wmi_block *wblock) +{ + struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver); + struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + + if (!driver->no_notify_data) { + status = get_event_data(wblock, &data); + if (ACPI_FAILURE(status)) { + dev_warn(&wblock->dev.dev, "Failed to get event data\n"); + return; + } + } + + if (driver->notify) + driver->notify(&wblock->dev, data.pointer); + + kfree(data.pointer); +} + static int wmi_notify_device(struct device *dev, void *data) { struct wmi_block *wblock = dev_to_wblock(dev); @@ -1179,28 +1233,17 @@ static int wmi_notify_device(struct device *dev, void *data) if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event)) return 0; - /* If a driver is bound, then notify the driver. */ - if (test_bit(WMI_PROBED, &wblock->flags) && wblock->dev.dev.driver) { - struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver); - struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL }; - acpi_status status; - - if (!driver->no_notify_data) { - status = get_event_data(wblock, &evdata); - if (ACPI_FAILURE(status)) { - dev_warn(&wblock->dev.dev, "failed to get event data\n"); - return -EIO; - } - } - - if (driver->notify) - driver->notify(&wblock->dev, evdata.pointer); - - kfree(evdata.pointer); - } else if (wblock->handler) { - /* Legacy handler */ - wblock->handler(*event, wblock->handler_data); + down_read(&wblock->notify_lock); + /* The WMI driver notify handler conflicts with the legacy WMI handler. + * Because of this the WMI driver notify handler takes precedence. + */ + if (wblock->dev.dev.driver && wblock->driver_ready) { + wmi_notify_driver(wblock); + } else { + if (wblock->handler) + wblock->handler(*event, wblock->handler_data); } + up_read(&wblock->notify_lock); acpi_bus_generate_netlink_event(wblock->acpi_device->pnp.device_class, dev_name(&wblock->dev.dev), *event, 0); diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 9ee1bf2ece10..18e232b5ed53 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -1109,7 +1109,7 @@ static int __init genpd_power_off_unused(void) return 0; } -late_initcall(genpd_power_off_unused); +late_initcall_sync(genpd_power_off_unused); #ifdef CONFIG_PM_SLEEP @@ -2668,7 +2668,7 @@ static void genpd_release_dev(struct device *dev) kfree(dev); } -static struct bus_type genpd_bus_type = { +static const struct bus_type genpd_bus_type = { .name = "genpd", }; diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index e26dc17d07ad..e274e3315fe7 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -561,6 +561,11 @@ static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *paren goto err_put_node; } + /* recursive call to add all subdomains */ + ret = scpsys_add_subdomain(scpsys, child); + if (ret) + goto err_put_node; + ret = pm_genpd_add_subdomain(parent_pd, child_pd); if (ret) { dev_err(scpsys->dev, "failed to add %s subdomain to parent %s\n", @@ -570,11 +575,6 @@ static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *paren dev_dbg(scpsys->dev, "%s add subdomain: %s\n", parent_pd->name, child_pd->name); } - - /* recursive call to add all subdomains */ - ret = scpsys_add_subdomain(scpsys, child); - if (ret) - goto err_put_node; } return 0; @@ -588,9 +588,6 @@ static void scpsys_remove_one_domain(struct scpsys_domain *pd) { int ret; - if (scpsys_domain_is_on(pd)) - scpsys_power_off(&pd->genpd); - /* * We're in the error cleanup already, so we only complain, * but won't emit another error on top of the original one. @@ -600,6 +597,8 @@ static void scpsys_remove_one_domain(struct scpsys_domain *pd) dev_err(pd->scpsys->dev, "failed to remove domain '%s' : %d - state may be inconsistent\n", pd->genpd.name, ret); + if (scpsys_domain_is_on(pd)) + scpsys_power_off(&pd->genpd); clk_bulk_put(pd->num_clks, pd->clks); clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks); diff --git a/drivers/pmdomain/renesas/r8a77980-sysc.c b/drivers/pmdomain/renesas/r8a77980-sysc.c index 39ca84a67daa..621e411fc999 100644 --- a/drivers/pmdomain/renesas/r8a77980-sysc.c +++ b/drivers/pmdomain/renesas/r8a77980-sysc.c @@ -25,7 +25,8 @@ static const struct rcar_sysc_area r8a77980_areas[] __initconst = { PD_CPU_NOCR }, { "ca53-cpu3", 0x200, 3, R8A77980_PD_CA53_CPU3, R8A77980_PD_CA53_SCU, PD_CPU_NOCR }, - { "cr7", 0x240, 0, R8A77980_PD_CR7, R8A77980_PD_ALWAYS_ON }, + { "cr7", 0x240, 0, R8A77980_PD_CR7, R8A77980_PD_ALWAYS_ON, + PD_CPU_NOCR }, { "a3ir", 0x180, 0, R8A77980_PD_A3IR, R8A77980_PD_ALWAYS_ON }, { "a2ir0", 0x400, 0, R8A77980_PD_A2IR0, R8A77980_PD_A3IR }, { "a2ir1", 0x400, 1, R8A77980_PD_A2IR1, R8A77980_PD_A3IR }, diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index 46c534f6b1c9..0a5d0d8befa8 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c @@ -256,7 +256,7 @@ static const struct dev_pm_ops pnp_bus_dev_pm_ops = { .restore = pnp_bus_resume, }; -struct bus_type pnp_bus_type = { +const struct bus_type pnp_bus_type = { .name = "pnp", .match = pnp_bus_match, .probe = pnp_device_probe, diff --git a/drivers/power/reset/as3722-poweroff.c b/drivers/power/reset/as3722-poweroff.c index 829e0dba2fda..ab3350ce2d62 100644 --- a/drivers/power/reset/as3722-poweroff.c +++ b/drivers/power/reset/as3722-poweroff.c @@ -61,13 +61,11 @@ static int as3722_poweroff_probe(struct platform_device *pdev) return 0; } -static int as3722_poweroff_remove(struct platform_device *pdev) +static void as3722_poweroff_remove(struct platform_device *pdev) { if (pm_power_off == as3722_pm_power_off) pm_power_off = NULL; as3722_pm_poweroff = NULL; - - return 0; } static struct platform_driver as3722_poweroff_driver = { @@ -75,7 +73,7 @@ static struct platform_driver as3722_poweroff_driver = { .name = "as3722-power-off", }, .probe = as3722_poweroff_probe, - .remove = as3722_poweroff_remove, + .remove_new = as3722_poweroff_remove, }; module_platform_driver(as3722_poweroff_driver); diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c index dd5399785b69..93eece027865 100644 --- a/drivers/power/reset/at91-poweroff.c +++ b/drivers/power/reset/at91-poweroff.c @@ -57,7 +57,7 @@ static struct shdwc { void __iomem *mpddrc_base; } at91_shdwc; -static void __init at91_wakeup_status(struct platform_device *pdev) +static void at91_wakeup_status(struct platform_device *pdev) { const char *reason; u32 reg = readl(at91_shdwc.shdwc_base + AT91_SHDW_SR); @@ -149,7 +149,7 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev) writel(wakeup_mode | mode, at91_shdwc.shdwc_base + AT91_SHDW_MR); } -static int __init at91_poweroff_probe(struct platform_device *pdev) +static int at91_poweroff_probe(struct platform_device *pdev) { struct device_node *np; u32 ddr_type; @@ -202,7 +202,7 @@ clk_disable: return ret; } -static int __exit at91_poweroff_remove(struct platform_device *pdev) +static void at91_poweroff_remove(struct platform_device *pdev) { if (pm_power_off == at91_poweroff) pm_power_off = NULL; @@ -211,8 +211,6 @@ static int __exit at91_poweroff_remove(struct platform_device *pdev) iounmap(at91_shdwc.mpddrc_base); clk_disable_unprepare(at91_shdwc.sclk); - - return 0; } static const struct of_device_id at91_poweroff_of_match[] = { @@ -224,13 +222,14 @@ static const struct of_device_id at91_poweroff_of_match[] = { MODULE_DEVICE_TABLE(of, at91_poweroff_of_match); static struct platform_driver at91_poweroff_driver = { - .remove = __exit_p(at91_poweroff_remove), + .probe = at91_poweroff_probe, + .remove_new = at91_poweroff_remove, .driver = { .name = "at91-poweroff", .of_match_table = at91_poweroff_of_match, }, }; -module_platform_driver_probe(at91_poweroff_driver, at91_poweroff_probe); +module_platform_driver(at91_poweroff_driver); MODULE_AUTHOR("Atmel Corporation"); MODULE_DESCRIPTION("Shutdown driver for Atmel SoCs"); diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index aa9b012d3d00..16512654295f 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -337,7 +337,7 @@ static int at91_rcdev_init(struct at91_reset *reset, return devm_reset_controller_register(&pdev->dev, &reset->rcdev); } -static int __init at91_reset_probe(struct platform_device *pdev) +static int at91_reset_probe(struct platform_device *pdev) { const struct of_device_id *match; struct at91_reset *reset; @@ -417,24 +417,23 @@ disable_clk: return ret; } -static int __exit at91_reset_remove(struct platform_device *pdev) +static void at91_reset_remove(struct platform_device *pdev) { struct at91_reset *reset = platform_get_drvdata(pdev); unregister_restart_handler(&reset->nb); clk_disable_unprepare(reset->sclk); - - return 0; } static struct platform_driver at91_reset_driver = { - .remove = __exit_p(at91_reset_remove), + .probe = at91_reset_probe, + .remove_new = at91_reset_remove, .driver = { .name = "at91-reset", .of_match_table = at91_reset_of_match, }, }; -module_platform_driver_probe(at91_reset_driver, at91_reset_probe); +module_platform_driver(at91_reset_driver); MODULE_AUTHOR("Atmel Corporation"); MODULE_DESCRIPTION("Reset driver for Atmel SoCs"); diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index e76b102b57b1..959ce0dbe91d 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -107,7 +107,7 @@ static const unsigned long long sdwc_dbc_period[] = { 0, 3, 32, 512, 4096, 32768, }; -static void __init at91_wakeup_status(struct platform_device *pdev) +static void at91_wakeup_status(struct platform_device *pdev) { struct shdwc *shdw = platform_get_drvdata(pdev); const struct reg_config *rcfg = shdw->rcfg; @@ -329,7 +329,7 @@ static const struct of_device_id at91_pmc_ids[] = { { /* Sentinel. */ } }; -static int __init at91_shdwc_probe(struct platform_device *pdev) +static int at91_shdwc_probe(struct platform_device *pdev) { const struct of_device_id *match; struct device_node *np; @@ -421,7 +421,7 @@ clk_disable: return ret; } -static int __exit at91_shdwc_remove(struct platform_device *pdev) +static void at91_shdwc_remove(struct platform_device *pdev) { struct shdwc *shdw = platform_get_drvdata(pdev); @@ -437,18 +437,17 @@ static int __exit at91_shdwc_remove(struct platform_device *pdev) iounmap(shdw->pmc_base); clk_disable_unprepare(shdw->sclk); - - return 0; } static struct platform_driver at91_shdwc_driver = { - .remove = __exit_p(at91_shdwc_remove), + .probe = at91_shdwc_probe, + .remove_new = at91_shdwc_remove, .driver = { .name = "at91-shdwc", .of_match_table = at91_shdwc_of_match, }, }; -module_platform_driver_probe(at91_shdwc_driver, at91_shdwc_probe); +module_platform_driver(at91_shdwc_driver); MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>"); MODULE_DESCRIPTION("Atmel shutdown controller driver"); diff --git a/drivers/power/reset/atc260x-poweroff.c b/drivers/power/reset/atc260x-poweroff.c index 98f20251a6d1..b4aa50e9685e 100644 --- a/drivers/power/reset/atc260x-poweroff.c +++ b/drivers/power/reset/atc260x-poweroff.c @@ -233,7 +233,7 @@ static int atc260x_pwrc_probe(struct platform_device *pdev) return ret; } -static int atc260x_pwrc_remove(struct platform_device *pdev) +static void atc260x_pwrc_remove(struct platform_device *pdev) { struct atc260x_pwrc *priv = platform_get_drvdata(pdev); @@ -243,13 +243,11 @@ static int atc260x_pwrc_remove(struct platform_device *pdev) } unregister_restart_handler(&priv->restart_nb); - - return 0; } static struct platform_driver atc260x_pwrc_driver = { .probe = atc260x_pwrc_probe, - .remove = atc260x_pwrc_remove, + .remove_new = atc260x_pwrc_remove, .driver = { .name = "atc260x-pwrc", }, diff --git a/drivers/power/reset/gpio-restart.c b/drivers/power/reset/gpio-restart.c index 3aa19765772d..d1e177176fa1 100644 --- a/drivers/power/reset/gpio-restart.c +++ b/drivers/power/reset/gpio-restart.c @@ -17,17 +17,14 @@ struct gpio_restart { struct gpio_desc *reset_gpio; - struct notifier_block restart_handler; u32 active_delay_ms; u32 inactive_delay_ms; u32 wait_delay_ms; }; -static int gpio_restart_notify(struct notifier_block *this, - unsigned long mode, void *cmd) +static int gpio_restart_notify(struct sys_off_data *data) { - struct gpio_restart *gpio_restart = - container_of(this, struct gpio_restart, restart_handler); + struct gpio_restart *gpio_restart = data->cb_data; /* drive it active, also inactive->active edge */ gpiod_direction_output(gpio_restart->reset_gpio, 1); @@ -52,6 +49,7 @@ static int gpio_restart_probe(struct platform_device *pdev) { struct gpio_restart *gpio_restart; bool open_source = false; + int priority = 129; u32 property; int ret; @@ -71,8 +69,6 @@ static int gpio_restart_probe(struct platform_device *pdev) return ret; } - gpio_restart->restart_handler.notifier_call = gpio_restart_notify; - gpio_restart->restart_handler.priority = 129; gpio_restart->active_delay_ms = 100; gpio_restart->inactive_delay_ms = 100; gpio_restart->wait_delay_ms = 3000; @@ -83,7 +79,7 @@ static int gpio_restart_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Invalid priority property: %u\n", property); else - gpio_restart->restart_handler.priority = property; + priority = property; } of_property_read_u32(pdev->dev.of_node, "active-delay", @@ -93,9 +89,11 @@ static int gpio_restart_probe(struct platform_device *pdev) of_property_read_u32(pdev->dev.of_node, "wait-delay", &gpio_restart->wait_delay_ms); - platform_set_drvdata(pdev, gpio_restart); - - ret = register_restart_handler(&gpio_restart->restart_handler); + ret = devm_register_sys_off_handler(&pdev->dev, + SYS_OFF_MODE_RESTART, + priority, + gpio_restart_notify, + gpio_restart); if (ret) { dev_err(&pdev->dev, "%s: cannot register restart handler, %d\n", __func__, ret); @@ -105,19 +103,6 @@ static int gpio_restart_probe(struct platform_device *pdev) return 0; } -static void gpio_restart_remove(struct platform_device *pdev) -{ - struct gpio_restart *gpio_restart = platform_get_drvdata(pdev); - int ret; - - ret = unregister_restart_handler(&gpio_restart->restart_handler); - if (ret) { - dev_err(&pdev->dev, - "%s: cannot unregister restart handler, %d\n", - __func__, ret); - } -} - static const struct of_device_id of_gpio_restart_match[] = { { .compatible = "gpio-restart", }, {}, @@ -125,7 +110,6 @@ static const struct of_device_id of_gpio_restart_match[] = { static struct platform_driver gpio_restart_driver = { .probe = gpio_restart_probe, - .remove_new = gpio_restart_remove, .driver = { .name = "restart-gpio", .of_match_table = of_gpio_restart_match, diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index eea05921a054..fa25fbd53934 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -286,7 +286,7 @@ static int ltc2952_poweroff_probe(struct platform_device *pdev) return 0; } -static int ltc2952_poweroff_remove(struct platform_device *pdev) +static void ltc2952_poweroff_remove(struct platform_device *pdev) { struct ltc2952_poweroff *data = platform_get_drvdata(pdev); @@ -295,7 +295,6 @@ static int ltc2952_poweroff_remove(struct platform_device *pdev) hrtimer_cancel(&data->timer_wde); atomic_notifier_chain_unregister(&panic_notifier_list, &data->panic_notifier); - return 0; } static const struct of_device_id of_ltc2952_poweroff_match[] = { @@ -306,7 +305,7 @@ MODULE_DEVICE_TABLE(of, of_ltc2952_poweroff_match); static struct platform_driver ltc2952_poweroff_driver = { .probe = ltc2952_poweroff_probe, - .remove = ltc2952_poweroff_remove, + .remove_new = ltc2952_poweroff_remove, .driver = { .name = "ltc2952-poweroff", .of_match_table = of_ltc2952_poweroff_match, diff --git a/drivers/power/reset/mt6323-poweroff.c b/drivers/power/reset/mt6323-poweroff.c index 108167f7738b..57a63c0ab7fb 100644 --- a/drivers/power/reset/mt6323-poweroff.c +++ b/drivers/power/reset/mt6323-poweroff.c @@ -70,12 +70,10 @@ static int mt6323_pwrc_probe(struct platform_device *pdev) return 0; } -static int mt6323_pwrc_remove(struct platform_device *pdev) +static void mt6323_pwrc_remove(struct platform_device *pdev) { if (pm_power_off == &mt6323_do_pwroff) pm_power_off = NULL; - - return 0; } static const struct of_device_id mt6323_pwrc_dt_match[] = { @@ -86,7 +84,7 @@ MODULE_DEVICE_TABLE(of, mt6323_pwrc_dt_match); static struct platform_driver mt6323_pwrc_driver = { .probe = mt6323_pwrc_probe, - .remove = mt6323_pwrc_remove, + .remove_new = mt6323_pwrc_remove, .driver = { .name = "mt6323-pwrc", .of_match_table = mt6323_pwrc_dt_match, diff --git a/drivers/power/reset/pwr-mlxbf.c b/drivers/power/reset/pwr-mlxbf.c index de35d24bb7ef..1775b318d0ef 100644 --- a/drivers/power/reset/pwr-mlxbf.c +++ b/drivers/power/reset/pwr-mlxbf.c @@ -17,11 +17,17 @@ #include <linux/types.h> struct pwr_mlxbf { - struct work_struct send_work; + struct work_struct reboot_work; + struct work_struct shutdown_work; const char *hid; }; -static void pwr_mlxbf_send_work(struct work_struct *work) +static void pwr_mlxbf_reboot_work(struct work_struct *work) +{ + acpi_bus_generate_netlink_event("button/reboot.*", "Reboot Button", 0x80, 1); +} + +static void pwr_mlxbf_shutdown_work(struct work_struct *work) { acpi_bus_generate_netlink_event("button/power.*", "Power Button", 0x80, 1); } @@ -33,10 +39,10 @@ static irqreturn_t pwr_mlxbf_irq(int irq, void *ptr) struct pwr_mlxbf *priv = ptr; if (!strncmp(priv->hid, rst_pwr_hid, 8)) - emergency_restart(); + schedule_work(&priv->reboot_work); if (!strncmp(priv->hid, low_pwr_hid, 8)) - schedule_work(&priv->send_work); + schedule_work(&priv->shutdown_work); return IRQ_HANDLED; } @@ -64,7 +70,11 @@ static int pwr_mlxbf_probe(struct platform_device *pdev) if (irq < 0) return dev_err_probe(dev, irq, "Error getting %s irq.\n", priv->hid); - err = devm_work_autocancel(dev, &priv->send_work, pwr_mlxbf_send_work); + err = devm_work_autocancel(dev, &priv->shutdown_work, pwr_mlxbf_shutdown_work); + if (err) + return err; + + err = devm_work_autocancel(dev, &priv->reboot_work, pwr_mlxbf_reboot_work); if (err) return err; diff --git a/drivers/power/reset/qnap-poweroff.c b/drivers/power/reset/qnap-poweroff.c index 0ddf7f25f7b8..e0f2ff6b147c 100644 --- a/drivers/power/reset/qnap-poweroff.c +++ b/drivers/power/reset/qnap-poweroff.c @@ -111,15 +111,14 @@ static int qnap_power_off_probe(struct platform_device *pdev) return 0; } -static int qnap_power_off_remove(struct platform_device *pdev) +static void qnap_power_off_remove(struct platform_device *pdev) { pm_power_off = NULL; - return 0; } static struct platform_driver qnap_power_off_driver = { .probe = qnap_power_off_probe, - .remove = qnap_power_off_remove, + .remove_new = qnap_power_off_remove, .driver = { .name = "qnap_power_off", .of_match_table = of_match_ptr(qnap_power_off_of_match_table), diff --git a/drivers/power/reset/regulator-poweroff.c b/drivers/power/reset/regulator-poweroff.c index 7f87fbb8b051..15160809c423 100644 --- a/drivers/power/reset/regulator-poweroff.c +++ b/drivers/power/reset/regulator-poweroff.c @@ -52,12 +52,10 @@ static int regulator_poweroff_probe(struct platform_device *pdev) return 0; } -static int regulator_poweroff_remove(__maybe_unused struct platform_device *pdev) +static void regulator_poweroff_remove(struct platform_device *pdev) { if (pm_power_off == ®ulator_poweroff_do_poweroff) pm_power_off = NULL; - - return 0; } static const struct of_device_id of_regulator_poweroff_match[] = { @@ -68,7 +66,7 @@ MODULE_DEVICE_TABLE(of, of_regulator_poweroff_match); static struct platform_driver regulator_poweroff_driver = { .probe = regulator_poweroff_probe, - .remove = regulator_poweroff_remove, + .remove_new = regulator_poweroff_remove, .driver = { .name = "poweroff-regulator", .of_match_table = of_regulator_poweroff_match, diff --git a/drivers/power/reset/restart-poweroff.c b/drivers/power/reset/restart-poweroff.c index 28f1822db162..f4d6004793d3 100644 --- a/drivers/power/reset/restart-poweroff.c +++ b/drivers/power/reset/restart-poweroff.c @@ -33,12 +33,10 @@ static int restart_poweroff_probe(struct platform_device *pdev) return 0; } -static int restart_poweroff_remove(struct platform_device *pdev) +static void restart_poweroff_remove(struct platform_device *pdev) { if (pm_power_off == &restart_poweroff_do_poweroff) pm_power_off = NULL; - - return 0; } static const struct of_device_id of_restart_poweroff_match[] = { @@ -49,7 +47,7 @@ MODULE_DEVICE_TABLE(of, of_restart_poweroff_match); static struct platform_driver restart_poweroff_driver = { .probe = restart_poweroff_probe, - .remove = restart_poweroff_remove, + .remove_new = restart_poweroff_remove, .driver = { .name = "poweroff-restart", .of_match_table = of_restart_poweroff_match, diff --git a/drivers/power/reset/rmobile-reset.c b/drivers/power/reset/rmobile-reset.c index bd3b396558e0..5df9b41c68c7 100644 --- a/drivers/power/reset/rmobile-reset.c +++ b/drivers/power/reset/rmobile-reset.c @@ -59,11 +59,10 @@ fail_unmap: return error; } -static int rmobile_reset_remove(struct platform_device *pdev) +static void rmobile_reset_remove(struct platform_device *pdev) { unregister_restart_handler(&rmobile_reset_nb); iounmap(sysc_base2); - return 0; } static const struct of_device_id rmobile_reset_of_match[] = { @@ -74,7 +73,7 @@ MODULE_DEVICE_TABLE(of, rmobile_reset_of_match); static struct platform_driver rmobile_reset_driver = { .probe = rmobile_reset_probe, - .remove = rmobile_reset_remove, + .remove_new = rmobile_reset_remove, .driver = { .name = "rmobile_reset", .of_match_table = rmobile_reset_of_match, diff --git a/drivers/power/reset/syscon-poweroff.c b/drivers/power/reset/syscon-poweroff.c index c3aab7f59345..1b2ce7734260 100644 --- a/drivers/power/reset/syscon-poweroff.c +++ b/drivers/power/reset/syscon-poweroff.c @@ -76,12 +76,10 @@ static int syscon_poweroff_probe(struct platform_device *pdev) return 0; } -static int syscon_poweroff_remove(struct platform_device *pdev) +static void syscon_poweroff_remove(struct platform_device *pdev) { if (pm_power_off == syscon_poweroff) pm_power_off = NULL; - - return 0; } static const struct of_device_id syscon_poweroff_of_match[] = { @@ -91,7 +89,7 @@ static const struct of_device_id syscon_poweroff_of_match[] = { static struct platform_driver syscon_poweroff_driver = { .probe = syscon_poweroff_probe, - .remove = syscon_poweroff_remove, + .remove_new = syscon_poweroff_remove, .driver = { .name = "syscon-poweroff", .of_match_table = syscon_poweroff_of_match, diff --git a/drivers/power/reset/tps65086-restart.c b/drivers/power/reset/tps65086-restart.c index 5ec819eac7da..ee8e9f4b837e 100644 --- a/drivers/power/reset/tps65086-restart.c +++ b/drivers/power/reset/tps65086-restart.c @@ -62,19 +62,21 @@ static int tps65086_restart_probe(struct platform_device *pdev) return 0; } -static int tps65086_restart_remove(struct platform_device *pdev) +static void tps65086_restart_remove(struct platform_device *pdev) { struct tps65086_restart *tps65086_restart = platform_get_drvdata(pdev); int ret; ret = unregister_restart_handler(&tps65086_restart->handler); if (ret) { + /* + * tps65086_restart_probe() registered the restart handler. So + * unregistering should work fine. Checking the error code + * shouldn't be needed, still doing it for completeness. + */ dev_err(&pdev->dev, "%s: cannot unregister restart handler: %d\n", __func__, ret); - return -ENODEV; } - - return 0; } static const struct platform_device_id tps65086_restart_id_table[] = { @@ -88,7 +90,7 @@ static struct platform_driver tps65086_restart_driver = { .name = "tps65086-restart", }, .probe = tps65086_restart_probe, - .remove = tps65086_restart_remove, + .remove_new = tps65086_restart_remove, .id_table = tps65086_restart_id_table, }; module_platform_driver(tps65086_restart_driver); diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 1db290ee2591..2b393eb5c282 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -36,10 +36,16 @@ #define BQ24190_REG_POC_WDT_RESET_SHIFT 6 #define BQ24190_REG_POC_CHG_CONFIG_MASK (BIT(5) | BIT(4)) #define BQ24190_REG_POC_CHG_CONFIG_SHIFT 4 -#define BQ24190_REG_POC_CHG_CONFIG_DISABLE 0x0 -#define BQ24190_REG_POC_CHG_CONFIG_CHARGE 0x1 -#define BQ24190_REG_POC_CHG_CONFIG_OTG 0x2 -#define BQ24190_REG_POC_CHG_CONFIG_OTG_ALT 0x3 +#define BQ24190_REG_POC_CHG_CONFIG_DISABLE 0x0 +#define BQ24190_REG_POC_CHG_CONFIG_CHARGE 0x1 +#define BQ24190_REG_POC_CHG_CONFIG_OTG 0x2 +#define BQ24190_REG_POC_CHG_CONFIG_OTG_ALT 0x3 +#define BQ24296_REG_POC_OTG_CONFIG_MASK BIT(5) +#define BQ24296_REG_POC_OTG_CONFIG_SHIFT 5 +#define BQ24296_REG_POC_CHG_CONFIG_MASK BIT(4) +#define BQ24296_REG_POC_CHG_CONFIG_SHIFT 4 +#define BQ24296_REG_POC_OTG_CONFIG_DISABLE 0x0 +#define BQ24296_REG_POC_OTG_CONFIG_OTG 0x1 #define BQ24190_REG_POC_SYS_MIN_MASK (BIT(3) | BIT(2) | BIT(1)) #define BQ24190_REG_POC_SYS_MIN_SHIFT 1 #define BQ24190_REG_POC_SYS_MIN_MIN 3000 @@ -134,59 +140,24 @@ #define BQ24190_REG_F_BAT_FAULT_SHIFT 3 #define BQ24190_REG_F_NTC_FAULT_MASK (BIT(2) | BIT(1) | BIT(0)) #define BQ24190_REG_F_NTC_FAULT_SHIFT 0 +#define BQ24296_REG_F_NTC_FAULT_MASK (BIT(1) | BIT(0)) +#define BQ24296_REG_F_NTC_FAULT_SHIFT 0 #define BQ24190_REG_VPRS 0x0A /* Vendor/Part/Revision Status */ #define BQ24190_REG_VPRS_PN_MASK (BIT(5) | BIT(4) | BIT(3)) #define BQ24190_REG_VPRS_PN_SHIFT 3 -#define BQ24190_REG_VPRS_PN_24190 0x4 -#define BQ24190_REG_VPRS_PN_24192 0x5 /* Also 24193, 24196 */ -#define BQ24190_REG_VPRS_PN_24192I 0x3 +#define BQ24190_REG_VPRS_PN_24190 0x4 +#define BQ24190_REG_VPRS_PN_24192 0x5 /* Also 24193, 24196 */ +#define BQ24190_REG_VPRS_PN_24192I 0x3 +#define BQ24296_REG_VPRS_PN_MASK (BIT(7) | BIT(6) | BIT(5)) +#define BQ24296_REG_VPRS_PN_SHIFT 5 +#define BQ24296_REG_VPRS_PN_24296 0x1 #define BQ24190_REG_VPRS_TS_PROFILE_MASK BIT(2) #define BQ24190_REG_VPRS_TS_PROFILE_SHIFT 2 #define BQ24190_REG_VPRS_DEV_REG_MASK (BIT(1) | BIT(0)) #define BQ24190_REG_VPRS_DEV_REG_SHIFT 0 /* - * The FAULT register is latched by the bq24190 (except for NTC_FAULT) - * so the first read after a fault returns the latched value and subsequent - * reads return the current value. In order to return the fault status - * to the user, have the interrupt handler save the reg's value and retrieve - * it in the appropriate health/status routine. - */ -struct bq24190_dev_info { - struct i2c_client *client; - struct device *dev; - struct extcon_dev *edev; - struct power_supply *charger; - struct power_supply *battery; - struct delayed_work input_current_limit_work; - char model_name[I2C_NAME_SIZE]; - bool initialized; - bool irq_event; - bool otg_vbus_enabled; - int charge_type; - u16 sys_min; - u16 iprechg; - u16 iterm; - u32 ichg; - u32 ichg_max; - u32 vreg; - u32 vreg_max; - struct mutex f_reg_lock; - u8 f_reg; - u8 ss_reg; - u8 watchdog; -}; - -static int bq24190_charger_set_charge_type(struct bq24190_dev_info *bdi, - const union power_supply_propval *val); - -static const unsigned int bq24190_usb_extcon_cable[] = { - EXTCON_USB, - EXTCON_NONE, -}; - -/* * The tables below provide a 2-way mapping for the value that goes in * the register field and the real-world value that it represents. * The index of the array is the value that goes in the register; the @@ -211,6 +182,9 @@ static const int bq24190_ccc_ichg_values[] = { 4096000, 4160000, 4224000, 4288000, 4352000, 4416000, 4480000, 4544000 }; +/* ICHG higher than 3008mA is not supported in BQ24296 */ +#define BQ24296_CCC_ICHG_VALUES_LEN 40 + /* REG04[7:2] (VREG) in uV */ static const int bq24190_cvc_vreg_values[] = { 3504000, 3520000, 3536000, 3552000, 3568000, 3584000, 3600000, 3616000, @@ -228,6 +202,68 @@ static const int bq24190_ictrc_treg_values[] = { 600, 800, 1000, 1200 }; +enum bq24190_chip { + BQ24190, + BQ24192, + BQ24192i, + BQ24196, + BQ24296, +}; + +/* + * The FAULT register is latched by the bq24190 (except for NTC_FAULT) + * so the first read after a fault returns the latched value and subsequent + * reads return the current value. In order to return the fault status + * to the user, have the interrupt handler save the reg's value and retrieve + * it in the appropriate health/status routine. + */ +struct bq24190_dev_info { + struct i2c_client *client; + struct device *dev; + struct extcon_dev *edev; + struct power_supply *charger; + struct power_supply *battery; + struct delayed_work input_current_limit_work; + char model_name[I2C_NAME_SIZE]; + bool initialized; + bool irq_event; + bool otg_vbus_enabled; + int charge_type; + u16 sys_min; + u16 iprechg; + u16 iterm; + u32 ichg; + u32 ichg_max; + u32 vreg; + u32 vreg_max; + struct mutex f_reg_lock; + u8 f_reg; + u8 ss_reg; + u8 watchdog; + const struct bq24190_chip_info *info; +}; + +struct bq24190_chip_info { + int ichg_array_size; +#ifdef CONFIG_REGULATOR + const struct regulator_desc *vbus_desc; +#endif + int (*check_chip)(struct bq24190_dev_info *bdi); + int (*set_chg_config)(struct bq24190_dev_info *bdi, const u8 chg_config); + int (*set_otg_vbus)(struct bq24190_dev_info *bdi, bool enable); + u8 ntc_fault_mask; + int (*get_ntc_status)(const u8 value); +}; + +static int bq24190_charger_set_charge_type(struct bq24190_dev_info *bdi, + const union power_supply_propval *val); + +static const unsigned int bq24190_usb_extcon_cable[] = { + EXTCON_USB, + EXTCON_NONE, +}; + + /* * Return the index in 'tbl' of greatest value that is less than or equal to * 'val'. The index range returned is 0 to 'tbl_size' - 1. Assumes that @@ -529,6 +565,43 @@ static int bq24190_set_otg_vbus(struct bq24190_dev_info *bdi, bool enable) return ret; } +static int bq24296_set_otg_vbus(struct bq24190_dev_info *bdi, bool enable) +{ + int ret; + + ret = pm_runtime_resume_and_get(bdi->dev); + if (ret < 0) { + dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", ret); + return ret; + } + + bdi->otg_vbus_enabled = enable; + if (enable) { + ret = bq24190_write_mask(bdi, BQ24190_REG_POC, + BQ24296_REG_POC_CHG_CONFIG_MASK, + BQ24296_REG_POC_CHG_CONFIG_SHIFT, + BQ24190_REG_POC_CHG_CONFIG_DISABLE); + + if (ret < 0) + goto out; + + ret = bq24190_write_mask(bdi, BQ24190_REG_POC, + BQ24296_REG_POC_OTG_CONFIG_MASK, + BQ24296_REG_POC_CHG_CONFIG_SHIFT, + BQ24296_REG_POC_OTG_CONFIG_OTG); + } else + ret = bq24190_write_mask(bdi, BQ24190_REG_POC, + BQ24296_REG_POC_OTG_CONFIG_MASK, + BQ24296_REG_POC_CHG_CONFIG_SHIFT, + BQ24296_REG_POC_OTG_CONFIG_DISABLE); + +out: + pm_runtime_mark_last_busy(bdi->dev); + pm_runtime_put_autosuspend(bdi->dev); + + return ret; +} + #ifdef CONFIG_REGULATOR static int bq24190_vbus_enable(struct regulator_dev *dev) { @@ -567,6 +640,43 @@ static int bq24190_vbus_is_enabled(struct regulator_dev *dev) return bdi->otg_vbus_enabled; } +static int bq24296_vbus_enable(struct regulator_dev *dev) +{ + return bq24296_set_otg_vbus(rdev_get_drvdata(dev), true); +} + +static int bq24296_vbus_disable(struct regulator_dev *dev) +{ + return bq24296_set_otg_vbus(rdev_get_drvdata(dev), false); +} + +static int bq24296_vbus_is_enabled(struct regulator_dev *dev) +{ + struct bq24190_dev_info *bdi = rdev_get_drvdata(dev); + int ret; + u8 val; + + ret = pm_runtime_resume_and_get(bdi->dev); + if (ret < 0) { + dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", ret); + return ret; + } + + ret = bq24190_read_mask(bdi, BQ24190_REG_POC, + BQ24296_REG_POC_OTG_CONFIG_MASK, + BQ24296_REG_POC_OTG_CONFIG_SHIFT, &val); + + pm_runtime_mark_last_busy(bdi->dev); + pm_runtime_put_autosuspend(bdi->dev); + + if (ret) + return ret; + + bdi->otg_vbus_enabled = (val == BQ24296_REG_POC_OTG_CONFIG_OTG); + + return bdi->otg_vbus_enabled; +} + static const struct regulator_ops bq24190_vbus_ops = { .enable = bq24190_vbus_enable, .disable = bq24190_vbus_disable, @@ -583,6 +693,22 @@ static const struct regulator_desc bq24190_vbus_desc = { .n_voltages = 1, }; +static const struct regulator_ops bq24296_vbus_ops = { + .enable = bq24296_vbus_enable, + .disable = bq24296_vbus_disable, + .is_enabled = bq24296_vbus_is_enabled, +}; + +static const struct regulator_desc bq24296_vbus_desc = { + .name = "usb_otg_vbus", + .of_match = "usb-otg-vbus", + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &bq24296_vbus_ops, + .fixed_uV = 5000000, + .n_voltages = 1, +}; + static const struct regulator_init_data bq24190_vbus_init_data = { .constraints = { .valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -602,7 +728,7 @@ static int bq24190_register_vbus_regulator(struct bq24190_dev_info *bdi) else cfg.init_data = &bq24190_vbus_init_data; cfg.driver_data = bdi; - reg = devm_regulator_register(bdi->dev, &bq24190_vbus_desc, &cfg); + reg = devm_regulator_register(bdi->dev, bdi->info->vbus_desc, &cfg); if (IS_ERR(reg)) { ret = PTR_ERR(reg); dev_err(bdi->dev, "Can't register regulator: %d\n", ret); @@ -678,7 +804,7 @@ static int bq24190_set_config(struct bq24190_dev_info *bdi) BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT, bq24190_ccc_ichg_values, - ARRAY_SIZE(bq24190_ccc_ichg_values), + bdi->info->ichg_array_size, bdi->ichg); if (ret < 0) return ret; @@ -777,6 +903,24 @@ static int bq24190_charger_get_charge_type(struct bq24190_dev_info *bdi, return 0; } +static int bq24190_battery_set_chg_config(struct bq24190_dev_info *bdi, + const u8 chg_config) +{ + return bq24190_write_mask(bdi, BQ24190_REG_POC, + BQ24190_REG_POC_CHG_CONFIG_MASK, + BQ24190_REG_POC_CHG_CONFIG_SHIFT, + chg_config); +} + +static int bq24296_battery_set_chg_config(struct bq24190_dev_info *bdi, + const u8 chg_config) +{ + return bq24190_write_mask(bdi, BQ24190_REG_POC, + BQ24296_REG_POC_CHG_CONFIG_MASK, + BQ24296_REG_POC_CHG_CONFIG_SHIFT, + chg_config); +} + static int bq24190_charger_set_charge_type(struct bq24190_dev_info *bdi, const union power_supply_propval *val) { @@ -835,9 +979,50 @@ static int bq24190_charger_set_charge_type(struct bq24190_dev_info *bdi, return ret; } - return bq24190_write_mask(bdi, BQ24190_REG_POC, - BQ24190_REG_POC_CHG_CONFIG_MASK, - BQ24190_REG_POC_CHG_CONFIG_SHIFT, chg_config); + return bdi->info->set_chg_config(bdi, chg_config); +} + +static int bq24190_charger_get_ntc_status(u8 value) +{ + int health; + + switch (value >> BQ24190_REG_F_NTC_FAULT_SHIFT & 0x7) { + case 0x1: /* TS1 Cold */ + case 0x3: /* TS2 Cold */ + case 0x5: /* Both Cold */ + health = POWER_SUPPLY_HEALTH_COLD; + break; + case 0x2: /* TS1 Hot */ + case 0x4: /* TS2 Hot */ + case 0x6: /* Both Hot */ + health = POWER_SUPPLY_HEALTH_OVERHEAT; + break; + default: + health = POWER_SUPPLY_HEALTH_UNKNOWN; + } + + return health; +} + +static int bq24296_charger_get_ntc_status(u8 value) +{ + int health; + + switch (value >> BQ24296_REG_F_NTC_FAULT_SHIFT & 0x3) { + case 0x0: /* Normal */ + health = POWER_SUPPLY_HEALTH_GOOD; + break; + case 0x1: /* Hot */ + health = POWER_SUPPLY_HEALTH_OVERHEAT; + break; + case 0x2: /* Cold */ + health = POWER_SUPPLY_HEALTH_COLD; + break; + default: + health = POWER_SUPPLY_HEALTH_UNKNOWN; + } + + return health; } static int bq24190_charger_get_health(struct bq24190_dev_info *bdi, @@ -850,21 +1035,8 @@ static int bq24190_charger_get_health(struct bq24190_dev_info *bdi, v = bdi->f_reg; mutex_unlock(&bdi->f_reg_lock); - if (v & BQ24190_REG_F_NTC_FAULT_MASK) { - switch (v >> BQ24190_REG_F_NTC_FAULT_SHIFT & 0x7) { - case 0x1: /* TS1 Cold */ - case 0x3: /* TS2 Cold */ - case 0x5: /* Both Cold */ - health = POWER_SUPPLY_HEALTH_COLD; - break; - case 0x2: /* TS1 Hot */ - case 0x4: /* TS2 Hot */ - case 0x6: /* Both Hot */ - health = POWER_SUPPLY_HEALTH_OVERHEAT; - break; - default: - health = POWER_SUPPLY_HEALTH_UNKNOWN; - } + if (v & bdi->info->ntc_fault_mask) { + health = bdi->info->get_ntc_status(v); } else if (v & BQ24190_REG_F_BAT_FAULT_MASK) { health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; } else if (v & BQ24190_REG_F_CHRG_FAULT_MASK) { @@ -1015,7 +1187,7 @@ static int bq24190_charger_get_current(struct bq24190_dev_info *bdi, ret = bq24190_get_field_val(bdi, BQ24190_REG_CCC, BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT, bq24190_ccc_ichg_values, - ARRAY_SIZE(bq24190_ccc_ichg_values), &curr); + bdi->info->ichg_array_size, &curr); if (ret < 0) return ret; @@ -1055,7 +1227,7 @@ static int bq24190_charger_set_current(struct bq24190_dev_info *bdi, ret = bq24190_set_field_val(bdi, BQ24190_REG_CCC, BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT, bq24190_ccc_ichg_values, - ARRAY_SIZE(bq24190_ccc_ichg_values), curr); + bdi->info->ichg_array_size, curr); if (ret < 0) return ret; @@ -1395,26 +1567,9 @@ static int bq24190_battery_get_health(struct bq24190_dev_info *bdi, if (v & BQ24190_REG_F_BAT_FAULT_MASK) { health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; } else { - v &= BQ24190_REG_F_NTC_FAULT_MASK; - v >>= BQ24190_REG_F_NTC_FAULT_SHIFT; + v &= bdi->info->ntc_fault_mask; - switch (v) { - case 0x0: /* Normal */ - health = POWER_SUPPLY_HEALTH_GOOD; - break; - case 0x1: /* TS1 Cold */ - case 0x3: /* TS2 Cold */ - case 0x5: /* Both Cold */ - health = POWER_SUPPLY_HEALTH_COLD; - break; - case 0x2: /* TS1 Hot */ - case 0x4: /* TS2 Hot */ - case 0x6: /* Both Hot */ - health = POWER_SUPPLY_HEALTH_OVERHEAT; - break; - default: - health = POWER_SUPPLY_HEALTH_UNKNOWN; - } + health = v ? bdi->info->get_ntc_status(v) : POWER_SUPPLY_HEALTH_GOOD; } val->intval = health; @@ -1601,12 +1756,13 @@ static int bq24190_configure_usb_otg(struct bq24190_dev_info *bdi, u8 ss_reg) static void bq24190_check_status(struct bq24190_dev_info *bdi) { const u8 battery_mask_ss = BQ24190_REG_SS_CHRG_STAT_MASK; - const u8 battery_mask_f = BQ24190_REG_F_BAT_FAULT_MASK - | BQ24190_REG_F_NTC_FAULT_MASK; + u8 battery_mask_f = BQ24190_REG_F_BAT_FAULT_MASK; bool alert_charger = false, alert_battery = false; u8 ss_reg = 0, f_reg = 0; int i, ret; + battery_mask_f |= bdi->info->ntc_fault_mask; + ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg); if (ret < 0) { dev_err(bdi->dev, "Can't read SS reg: %d\n", ret); @@ -1633,7 +1789,7 @@ static void bq24190_check_status(struct bq24190_dev_info *bdi) !!(f_reg & BQ24190_REG_F_BOOST_FAULT_MASK), !!(f_reg & BQ24190_REG_F_CHRG_FAULT_MASK), !!(f_reg & BQ24190_REG_F_BAT_FAULT_MASK), - !!(f_reg & BQ24190_REG_F_NTC_FAULT_MASK)); + !!(f_reg & bdi->info->ntc_fault_mask)); mutex_lock(&bdi->f_reg_lock); if ((bdi->f_reg & battery_mask_f) != (f_reg & battery_mask_f)) @@ -1696,12 +1852,11 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) return IRQ_HANDLED; } -static int bq24190_hw_init(struct bq24190_dev_info *bdi) +static int bq24190_check_chip(struct bq24190_dev_info *bdi) { u8 v; int ret; - /* First check that the device really is what its supposed to be */ ret = bq24190_read_mask(bdi, BQ24190_REG_VPRS, BQ24190_REG_VPRS_PN_MASK, BQ24190_REG_VPRS_PN_SHIFT, @@ -1719,6 +1874,40 @@ static int bq24190_hw_init(struct bq24190_dev_info *bdi) return -ENODEV; } + return 0; +} + +static int bq24296_check_chip(struct bq24190_dev_info *bdi) +{ + u8 v; + int ret; + + ret = bq24190_read_mask(bdi, BQ24190_REG_VPRS, + BQ24296_REG_VPRS_PN_MASK, + BQ24296_REG_VPRS_PN_SHIFT, + &v); + if (ret < 0) + return ret; + + switch (v) { + case BQ24296_REG_VPRS_PN_24296: + break; + default: + dev_err(bdi->dev, "Error unknown model: 0x%02x\n", v); + return -ENODEV; + } + + return 0; +} + +static int bq24190_hw_init(struct bq24190_dev_info *bdi) +{ + int ret; + + ret = bdi->info->check_chip(bdi); + if (ret < 0) + return ret; + ret = bq24190_register_reset(bdi); if (ret < 0) return ret; @@ -1736,7 +1925,8 @@ static int bq24190_get_config(struct bq24190_dev_info *bdi) struct power_supply_battery_info *info; int v, idx; - idx = ARRAY_SIZE(bq24190_ccc_ichg_values) - 1; + idx = bdi->info->ichg_array_size - 1; + bdi->ichg_max = bq24190_ccc_ichg_values[idx]; idx = ARRAY_SIZE(bq24190_cvc_vreg_values) - 1; @@ -1781,6 +1971,64 @@ static int bq24190_get_config(struct bq24190_dev_info *bdi) return 0; } +static const struct bq24190_chip_info bq24190_chip_info_tbl[] = { + [BQ24190] = { + .ichg_array_size = ARRAY_SIZE(bq24190_ccc_ichg_values), +#ifdef CONFIG_REGULATOR + .vbus_desc = &bq24190_vbus_desc, +#endif + .check_chip = bq24190_check_chip, + .set_chg_config = bq24190_battery_set_chg_config, + .ntc_fault_mask = BQ24190_REG_F_NTC_FAULT_MASK, + .get_ntc_status = bq24190_charger_get_ntc_status, + .set_otg_vbus = bq24190_set_otg_vbus, + }, + [BQ24192] = { + .ichg_array_size = ARRAY_SIZE(bq24190_ccc_ichg_values), +#ifdef CONFIG_REGULATOR + .vbus_desc = &bq24190_vbus_desc, +#endif + .check_chip = bq24190_check_chip, + .set_chg_config = bq24190_battery_set_chg_config, + .ntc_fault_mask = BQ24190_REG_F_NTC_FAULT_MASK, + .get_ntc_status = bq24190_charger_get_ntc_status, + .set_otg_vbus = bq24190_set_otg_vbus, + }, + [BQ24192i] = { + .ichg_array_size = ARRAY_SIZE(bq24190_ccc_ichg_values), +#ifdef CONFIG_REGULATOR + .vbus_desc = &bq24190_vbus_desc, +#endif + .check_chip = bq24190_check_chip, + .set_chg_config = bq24190_battery_set_chg_config, + .ntc_fault_mask = BQ24190_REG_F_NTC_FAULT_MASK, + .get_ntc_status = bq24190_charger_get_ntc_status, + .set_otg_vbus = bq24190_set_otg_vbus, + }, + [BQ24196] = { + .ichg_array_size = ARRAY_SIZE(bq24190_ccc_ichg_values), +#ifdef CONFIG_REGULATOR + .vbus_desc = &bq24190_vbus_desc, +#endif + .check_chip = bq24190_check_chip, + .set_chg_config = bq24190_battery_set_chg_config, + .ntc_fault_mask = BQ24190_REG_F_NTC_FAULT_MASK, + .get_ntc_status = bq24190_charger_get_ntc_status, + .set_otg_vbus = bq24190_set_otg_vbus, + }, + [BQ24296] = { + .ichg_array_size = BQ24296_CCC_ICHG_VALUES_LEN, +#ifdef CONFIG_REGULATOR + .vbus_desc = &bq24296_vbus_desc, +#endif + .check_chip = bq24296_check_chip, + .set_chg_config = bq24296_battery_set_chg_config, + .ntc_fault_mask = BQ24296_REG_F_NTC_FAULT_MASK, + .get_ntc_status = bq24296_charger_get_ntc_status, + .set_otg_vbus = bq24296_set_otg_vbus, + }, +}; + static int bq24190_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); @@ -1804,6 +2052,7 @@ static int bq24190_probe(struct i2c_client *client) bdi->client = client; bdi->dev = dev; strscpy(bdi->model_name, id->name, sizeof(bdi->model_name)); + bdi->info = i2c_get_match_data(client); mutex_init(&bdi->f_reg_lock); bdi->charge_type = POWER_SUPPLY_CHARGE_TYPE_FAST; bdi->f_reg = 0; @@ -1940,7 +2189,7 @@ static void bq24190_shutdown(struct i2c_client *client) struct bq24190_dev_info *bdi = i2c_get_clientdata(client); /* Turn off 5V boost regulator on shutdown */ - bq24190_set_otg_vbus(bdi, false); + bdi->info->set_otg_vbus(bdi, false); } static __maybe_unused int bq24190_runtime_suspend(struct device *dev) @@ -2029,19 +2278,21 @@ static const struct dev_pm_ops bq24190_pm_ops = { }; static const struct i2c_device_id bq24190_i2c_ids[] = { - { "bq24190" }, - { "bq24192" }, - { "bq24192i" }, - { "bq24196" }, + { "bq24190", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24190] }, + { "bq24192", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192] }, + { "bq24192i", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24192i] }, + { "bq24196", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24196] }, + { "bq24296", (kernel_ulong_t)&bq24190_chip_info_tbl[BQ24296] }, { }, }; MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids); static const struct of_device_id bq24190_of_match[] = { - { .compatible = "ti,bq24190", }, - { .compatible = "ti,bq24192", }, - { .compatible = "ti,bq24192i", }, - { .compatible = "ti,bq24196", }, + { .compatible = "ti,bq24190", .data = &bq24190_chip_info_tbl[BQ24190] }, + { .compatible = "ti,bq24192", .data = &bq24190_chip_info_tbl[BQ24192] }, + { .compatible = "ti,bq24192i", .data = &bq24190_chip_info_tbl[BQ24192i] }, + { .compatible = "ti,bq24196", .data = &bq24190_chip_info_tbl[BQ24196] }, + { .compatible = "ti,bq24296", .data = &bq24190_chip_info_tbl[BQ24296] }, { }, }; MODULE_DEVICE_TABLE(of, bq24190_of_match); diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c index 789a31bd70c3..1a935bc88510 100644 --- a/drivers/power/supply/bq256xx_charger.c +++ b/drivers/power/supply/bq256xx_charger.c @@ -1574,13 +1574,16 @@ static int bq256xx_hw_init(struct bq256xx_device *bq) wd_reg_val = i; break; } - if (bq->watchdog_timer > bq256xx_watchdog_time[i] && + if (i + 1 < BQ256XX_NUM_WD_VAL && + bq->watchdog_timer > bq256xx_watchdog_time[i] && bq->watchdog_timer < bq256xx_watchdog_time[i + 1]) wd_reg_val = i; } ret = regmap_update_bits(bq->regmap, BQ256XX_CHARGER_CONTROL_1, BQ256XX_WATCHDOG_MASK, wd_reg_val << BQ256XX_WDT_BIT_SHIFT); + if (ret) + return ret; ret = power_supply_get_battery_info(bq->charger, &bat_info); if (ret == -ENOMEM) diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 4296600e8912..1c4a9d137744 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -2162,6 +2162,28 @@ void bq27xxx_battery_teardown(struct bq27xxx_device_info *di) } EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown); +#ifdef CONFIG_PM_SLEEP +static int bq27xxx_battery_suspend(struct device *dev) +{ + struct bq27xxx_device_info *di = dev_get_drvdata(dev); + + cancel_delayed_work(&di->work); + return 0; +} + +static int bq27xxx_battery_resume(struct device *dev) +{ + struct bq27xxx_device_info *di = dev_get_drvdata(dev); + + schedule_delayed_work(&di->work, 0); + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +SIMPLE_DEV_PM_OPS(bq27xxx_battery_battery_pm_ops, + bq27xxx_battery_suspend, bq27xxx_battery_resume); +EXPORT_SYMBOL_GPL(bq27xxx_battery_battery_pm_ops); + MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); MODULE_DESCRIPTION("BQ27xxx battery monitor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index 9b5475590518..3a1798b0c1a7 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -295,6 +295,7 @@ static struct i2c_driver bq27xxx_battery_i2c_driver = { .driver = { .name = "bq27xxx-battery", .of_match_table = of_match_ptr(bq27xxx_battery_i2c_of_match_table), + .pm = &bq27xxx_battery_battery_pm_ops, }, .probe = bq27xxx_battery_i2c_probe, .remove = bq27xxx_battery_i2c_remove, diff --git a/drivers/power/supply/cw2015_battery.c b/drivers/power/supply/cw2015_battery.c index bb29e9ebd24a..99f3ccdc30a6 100644 --- a/drivers/power/supply/cw2015_battery.c +++ b/drivers/power/supply/cw2015_battery.c @@ -491,7 +491,7 @@ static int cw_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: if (cw_battery_valid_time_to_empty(cw_bat)) - val->intval = cw_bat->time_to_empty; + val->intval = cw_bat->time_to_empty * 60; else val->intval = 0; break; diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 73265001dd4b..ecef35ac3b7e 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -861,44 +861,44 @@ const size_t power_supply_battery_info_properties_size = ARRAY_SIZE(power_supply EXPORT_SYMBOL_GPL(power_supply_battery_info_properties_size); bool power_supply_battery_info_has_prop(struct power_supply_battery_info *info, - enum power_supply_property psp) + enum power_supply_property psp) { if (!info) return false; switch (psp) { - case POWER_SUPPLY_PROP_TECHNOLOGY: - return info->technology != POWER_SUPPLY_TECHNOLOGY_UNKNOWN; - case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: - return info->energy_full_design_uwh >= 0; - case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: - return info->charge_full_design_uah >= 0; - case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - return info->voltage_min_design_uv >= 0; - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - return info->voltage_max_design_uv >= 0; - case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: - return info->precharge_current_ua >= 0; - case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: - return info->charge_term_current_ua >= 0; - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: - return info->constant_charge_current_max_ua >= 0; - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: - return info->constant_charge_voltage_max_uv >= 0; - case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN: - return info->temp_ambient_alert_min > INT_MIN; - case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX: - return info->temp_ambient_alert_max < INT_MAX; - case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: - return info->temp_alert_min > INT_MIN; - case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: - return info->temp_alert_max < INT_MAX; - case POWER_SUPPLY_PROP_TEMP_MIN: - return info->temp_min > INT_MIN; - case POWER_SUPPLY_PROP_TEMP_MAX: - return info->temp_max < INT_MAX; - default: - return false; + case POWER_SUPPLY_PROP_TECHNOLOGY: + return info->technology != POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + return info->energy_full_design_uwh >= 0; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + return info->charge_full_design_uah >= 0; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + return info->voltage_min_design_uv >= 0; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + return info->voltage_max_design_uv >= 0; + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + return info->precharge_current_ua >= 0; + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + return info->charge_term_current_ua >= 0; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + return info->constant_charge_current_max_ua >= 0; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + return info->constant_charge_voltage_max_uv >= 0; + case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN: + return info->temp_ambient_alert_min > INT_MIN; + case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX: + return info->temp_ambient_alert_max < INT_MAX; + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + return info->temp_alert_min > INT_MIN; + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + return info->temp_alert_max < INT_MAX; + case POWER_SUPPLY_PROP_TEMP_MIN: + return info->temp_min > INT_MIN; + case POWER_SUPPLY_PROP_TEMP_MAX: + return info->temp_max < INT_MAX; + default: + return false; } } EXPORT_SYMBOL_GPL(power_supply_battery_info_has_prop); @@ -914,53 +914,53 @@ int power_supply_battery_info_get_prop(struct power_supply_battery_info *info, return -EINVAL; switch (psp) { - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = info->technology; - return 0; - case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: - val->intval = info->energy_full_design_uwh; - return 0; - case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: - val->intval = info->charge_full_design_uah; - return 0; - case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - val->intval = info->voltage_min_design_uv; - return 0; - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = info->voltage_max_design_uv; - return 0; - case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: - val->intval = info->precharge_current_ua; - return 0; - case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: - val->intval = info->charge_term_current_ua; - return 0; - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: - val->intval = info->constant_charge_current_max_ua; - return 0; - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: - val->intval = info->constant_charge_voltage_max_uv; - return 0; - case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN: - val->intval = info->temp_ambient_alert_min; - return 0; - case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX: - val->intval = info->temp_ambient_alert_max; - return 0; - case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: - val->intval = info->temp_alert_min; - return 0; - case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: - val->intval = info->temp_alert_max; - return 0; - case POWER_SUPPLY_PROP_TEMP_MIN: - val->intval = info->temp_min; - return 0; - case POWER_SUPPLY_PROP_TEMP_MAX: - val->intval = info->temp_max; - return 0; - default: - return -EINVAL; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = info->technology; + return 0; + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + val->intval = info->energy_full_design_uwh; + return 0; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = info->charge_full_design_uah; + return 0; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = info->voltage_min_design_uv; + return 0; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = info->voltage_max_design_uv; + return 0; + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + val->intval = info->precharge_current_ua; + return 0; + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + val->intval = info->charge_term_current_ua; + return 0; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + val->intval = info->constant_charge_current_max_ua; + return 0; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + val->intval = info->constant_charge_voltage_max_uv; + return 0; + case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN: + val->intval = info->temp_ambient_alert_min; + return 0; + case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX: + val->intval = info->temp_ambient_alert_max; + return 0; + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + val->intval = info->temp_alert_min; + return 0; + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + val->intval = info->temp_alert_max; + return 0; + case POWER_SUPPLY_PROP_TEMP_MIN: + val->intval = info->temp_min; + return 0; + case POWER_SUPPLY_PROP_TEMP_MAX: + val->intval = info->temp_max; + return 0; + default: + return -EINVAL; } } EXPORT_SYMBOL_GPL(power_supply_battery_info_get_prop); @@ -1255,6 +1255,7 @@ EXPORT_SYMBOL_GPL(power_supply_powers); static void power_supply_dev_release(struct device *dev) { struct power_supply *psy = to_power_supply(dev); + dev_dbg(dev, "%s\n", __func__); kfree(psy); } @@ -1636,6 +1637,6 @@ subsys_initcall(power_supply_class_init); module_exit(power_supply_class_exit); MODULE_DESCRIPTION("Universal power supply monitor class"); -MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, " - "Szabolcs Gyurko, " - "Anton Vorontsov <cbou@mail.ru>"); +MODULE_AUTHOR("Ian Molton <spyro@f2s.com>"); +MODULE_AUTHOR("Szabolcs Gyurko"); +MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>"); diff --git a/drivers/power/supply/qcom_pmi8998_charger.c b/drivers/power/supply/qcom_pmi8998_charger.c index 8acf63ee6897..9bb777406013 100644 --- a/drivers/power/supply/qcom_pmi8998_charger.c +++ b/drivers/power/supply/qcom_pmi8998_charger.c @@ -972,10 +972,14 @@ static int smb2_probe(struct platform_device *pdev) supply_config.of_node = pdev->dev.of_node; desc = devm_kzalloc(chip->dev, sizeof(smb2_psy_desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; memcpy(desc, &smb2_psy_desc, sizeof(smb2_psy_desc)); desc->name = devm_kasprintf(chip->dev, GFP_KERNEL, "%s-charger", (const char *)device_get_match_data(chip->dev)); + if (!desc->name) + return -ENOMEM; chip->chg_psy = devm_power_supply_register(chip->dev, desc, &supply_config); diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 29078486534d..f2728ee787d7 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -8,6 +8,7 @@ #include <linux/acpi.h> #include <linux/module.h> +#include <linux/idr.h> #include <linux/of.h> #include <linux/pwm.h> #include <linux/list.h> @@ -23,52 +24,25 @@ #define CREATE_TRACE_POINTS #include <trace/events/pwm.h> -#define MAX_PWMS 1024 - static DEFINE_MUTEX(pwm_lookup_lock); static LIST_HEAD(pwm_lookup_list); -/* protects access to pwm_chips and allocated_pwms */ +/* protects access to pwm_chips */ static DEFINE_MUTEX(pwm_lock); -static LIST_HEAD(pwm_chips); -static DECLARE_BITMAP(allocated_pwms, MAX_PWMS); - -/* Called with pwm_lock held */ -static int alloc_pwms(unsigned int count) -{ - unsigned int start; - - start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, 0, - count, 0); - - if (start + count > MAX_PWMS) - return -ENOSPC; - - bitmap_set(allocated_pwms, start, count); - - return start; -} - -/* Called with pwm_lock held */ -static void free_pwms(struct pwm_chip *chip) -{ - bitmap_clear(allocated_pwms, chip->base, chip->npwm); - - kfree(chip->pwms); - chip->pwms = NULL; -} +static DEFINE_IDR(pwm_chips); static struct pwm_chip *pwmchip_find_by_name(const char *name) { struct pwm_chip *chip; + unsigned long id, tmp; if (!name) return NULL; mutex_lock(&pwm_lock); - list_for_each_entry(chip, &pwm_chips, list) { + idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) { const char *chip_name = dev_name(chip->dev); if (chip_name && strcmp(chip_name, name) == 0) { @@ -85,22 +59,24 @@ static struct pwm_chip *pwmchip_find_by_name(const char *name) static int pwm_device_request(struct pwm_device *pwm, const char *label) { int err; + struct pwm_chip *chip = pwm->chip; + const struct pwm_ops *ops = chip->ops; if (test_bit(PWMF_REQUESTED, &pwm->flags)) return -EBUSY; - if (!try_module_get(pwm->chip->owner)) + if (!try_module_get(chip->owner)) return -ENODEV; - if (pwm->chip->ops->request) { - err = pwm->chip->ops->request(pwm->chip, pwm); + if (ops->request) { + err = ops->request(chip, pwm); if (err) { - module_put(pwm->chip->owner); + module_put(chip->owner); return err; } } - if (pwm->chip->ops->get_state) { + if (ops->get_state) { /* * Zero-initialize state because most drivers are unaware of * .usage_power. The other members of state are supposed to be @@ -110,7 +86,7 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) */ struct pwm_state state = { 0, }; - err = pwm->chip->ops->get_state(pwm->chip, pwm, &state); + err = ops->get_state(chip, pwm, &state); trace_pwm_get(pwm, &state, err); if (!err) @@ -176,7 +152,7 @@ of_pwm_single_xlate(struct pwm_chip *chip, const struct of_phandle_args *args) pwm->args.period = args->args[0]; pwm->args.polarity = PWM_POLARITY_NORMAL; - if (args->args_count == 2 && args->args[2] & PWM_POLARITY_INVERTED) + if (args->args_count == 2 && args->args[1] & PWM_POLARITY_INVERTED) pwm->args.polarity = PWM_POLARITY_INVERSED; return pwm; @@ -234,7 +210,6 @@ static bool pwm_ops_check(const struct pwm_chip *chip) */ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) { - struct pwm_device *pwm; unsigned int i; int ret; @@ -246,31 +221,28 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) chip->owner = owner; - chip->pwms = kcalloc(chip->npwm, sizeof(*pwm), GFP_KERNEL); + chip->pwms = kcalloc(chip->npwm, sizeof(*chip->pwms), GFP_KERNEL); if (!chip->pwms) return -ENOMEM; mutex_lock(&pwm_lock); - ret = alloc_pwms(chip->npwm); + ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL); if (ret < 0) { mutex_unlock(&pwm_lock); kfree(chip->pwms); return ret; } - chip->base = ret; + chip->id = ret; for (i = 0; i < chip->npwm; i++) { - pwm = &chip->pwms[i]; + struct pwm_device *pwm = &chip->pwms[i]; pwm->chip = chip; - pwm->pwm = chip->base + i; pwm->hwpwm = i; } - list_add(&chip->list, &pwm_chips); - mutex_unlock(&pwm_lock); if (IS_ENABLED(CONFIG_OF)) @@ -297,11 +269,11 @@ void pwmchip_remove(struct pwm_chip *chip) mutex_lock(&pwm_lock); - list_del_init(&chip->list); - - free_pwms(chip); + idr_remove(&pwm_chips, chip->id); mutex_unlock(&pwm_lock); + + kfree(chip->pwms); } EXPORT_SYMBOL_GPL(pwmchip_remove); @@ -356,8 +328,8 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, } EXPORT_SYMBOL_GPL(pwm_request_from_chip); -static void pwm_apply_state_debug(struct pwm_device *pwm, - const struct pwm_state *state) +static void pwm_apply_debug(struct pwm_device *pwm, + const struct pwm_state *state) { struct pwm_state *last = &pwm->last; struct pwm_chip *chip = pwm->chip; @@ -463,24 +435,15 @@ static void pwm_apply_state_debug(struct pwm_device *pwm, } /** - * pwm_apply_state() - atomically apply a new state to a PWM device + * __pwm_apply() - atomically apply a new state to a PWM device * @pwm: PWM device * @state: new state to apply */ -int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) +static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) { struct pwm_chip *chip; int err; - /* - * Some lowlevel driver's implementations of .apply() make use of - * mutexes, also with some drivers only returning when the new - * configuration is active calling pwm_apply_state() from atomic context - * is a bad idea. So make it explicit that calling this function might - * sleep. - */ - might_sleep(); - if (!pwm || !state || !state->period || state->duty_cycle > state->period) return -EINVAL; @@ -505,11 +468,60 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) * only do this after pwm->state was applied as some * implementations of .get_state depend on this */ - pwm_apply_state_debug(pwm, state); + pwm_apply_debug(pwm, state); return 0; } -EXPORT_SYMBOL_GPL(pwm_apply_state); + +/** + * pwm_apply_might_sleep() - atomically apply a new state to a PWM device + * Cannot be used in atomic context. + * @pwm: PWM device + * @state: new state to apply + */ +int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state) +{ + int err; + + /* + * Some lowlevel driver's implementations of .apply() make use of + * mutexes, also with some drivers only returning when the new + * configuration is active calling pwm_apply_might_sleep() from atomic context + * is a bad idea. So make it explicit that calling this function might + * sleep. + */ + might_sleep(); + + if (IS_ENABLED(CONFIG_PWM_DEBUG) && pwm->chip->atomic) { + /* + * Catch any drivers that have been marked as atomic but + * that will sleep anyway. + */ + non_block_start(); + err = __pwm_apply(pwm, state); + non_block_end(); + } else { + err = __pwm_apply(pwm, state); + } + + return err; +} +EXPORT_SYMBOL_GPL(pwm_apply_might_sleep); + +/** + * pwm_apply_atomic() - apply a new state to a PWM device from atomic context + * Not all PWM devices support this function, check with pwm_might_sleep(). + * @pwm: PWM device + * @state: new state to apply + */ +int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state) +{ + WARN_ONCE(!pwm->chip->atomic, + "sleeping PWM driver used in atomic context\n"); + + return __pwm_apply(pwm, state); +} +EXPORT_SYMBOL_GPL(pwm_apply_atomic); /** * pwm_capture() - capture and report a PWM signal @@ -567,7 +579,7 @@ int pwm_adjust_config(struct pwm_device *pwm) state.period = pargs.period; state.polarity = pargs.polarity; - return pwm_apply_state(pwm, &state); + return pwm_apply_might_sleep(pwm, &state); } /* @@ -590,17 +602,18 @@ int pwm_adjust_config(struct pwm_device *pwm) state.duty_cycle = state.period - state.duty_cycle; } - return pwm_apply_state(pwm, &state); + return pwm_apply_might_sleep(pwm, &state); } EXPORT_SYMBOL_GPL(pwm_adjust_config); static struct pwm_chip *fwnode_to_pwmchip(struct fwnode_handle *fwnode) { struct pwm_chip *chip; + unsigned long id, tmp; mutex_lock(&pwm_lock); - list_for_each_entry(chip, &pwm_chips, list) + idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) if (chip->dev && device_match_fwnode(chip->dev, fwnode)) { mutex_unlock(&pwm_lock); return chip; @@ -1058,17 +1071,27 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) static void *pwm_seq_start(struct seq_file *s, loff_t *pos) { + unsigned long id = *pos; + void *ret; + mutex_lock(&pwm_lock); s->private = ""; - return seq_list_start(&pwm_chips, *pos); + ret = idr_get_next_ul(&pwm_chips, &id); + *pos = id; + return ret; } static void *pwm_seq_next(struct seq_file *s, void *v, loff_t *pos) { + unsigned long id = *pos + 1; + void *ret; + s->private = "\n"; - return seq_list_next(v, &pwm_chips, pos); + ret = idr_get_next_ul(&pwm_chips, &id); + *pos = id; + return ret; } static void pwm_seq_stop(struct seq_file *s, void *v) @@ -1078,9 +1101,10 @@ static void pwm_seq_stop(struct seq_file *s, void *v) static int pwm_seq_show(struct seq_file *s, void *v) { - struct pwm_chip *chip = list_entry(v, struct pwm_chip, list); + struct pwm_chip *chip = v; - seq_printf(s, "%s%s/%s, %d PWM device%s\n", (char *)s->private, + seq_printf(s, "%s%d: %s/%s, %d PWM device%s\n", + (char *)s->private, chip->id, chip->dev->bus ? chip->dev->bus->name : "no-bus", dev_name(chip->dev), chip->npwm, (chip->npwm != 1) ? "s" : ""); diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c index 07920e034757..3f2c5031a3ba 100644 --- a/drivers/pwm/pwm-atmel-hlcdc.c +++ b/drivers/pwm/pwm-atmel-hlcdc.c @@ -180,7 +180,6 @@ static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = { .div1_clk_erratum = true, }; -#ifdef CONFIG_PM_SLEEP static int atmel_hlcdc_pwm_suspend(struct device *dev) { struct atmel_hlcdc_pwm *atmel = dev_get_drvdata(dev); @@ -210,10 +209,9 @@ static int atmel_hlcdc_pwm_resume(struct device *dev) return atmel_hlcdc_pwm_apply(&atmel->chip, &atmel->chip.pwms[0], &state); } -#endif -static SIMPLE_DEV_PM_OPS(atmel_hlcdc_pwm_pm_ops, - atmel_hlcdc_pwm_suspend, atmel_hlcdc_pwm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(atmel_hlcdc_pwm_pm_ops, + atmel_hlcdc_pwm_suspend, atmel_hlcdc_pwm_resume); static const struct of_device_id atmel_hlcdc_dt_ids[] = { { @@ -297,7 +295,7 @@ static struct platform_driver atmel_hlcdc_pwm_driver = { .driver = { .name = "atmel-hlcdc-pwm", .of_match_table = atmel_hlcdc_pwm_dt_ids, - .pm = &atmel_hlcdc_pwm_pm_ops, + .pm = pm_ptr(&atmel_hlcdc_pwm_pm_ops), }, .probe = atmel_hlcdc_pwm_probe, .remove_new = atmel_hlcdc_pwm_remove, diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 98b33c016c3c..d42c897cb85e 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -489,7 +489,6 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); -#ifdef CONFIG_PM_SLEEP static int atmel_tcb_pwm_suspend(struct device *dev) { struct atmel_tcb_pwm_chip *tcbpwm = dev_get_drvdata(dev); @@ -522,16 +521,15 @@ static int atmel_tcb_pwm_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend, - atmel_tcb_pwm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend, + atmel_tcb_pwm_resume); static struct platform_driver atmel_tcb_pwm_driver = { .driver = { .name = "atmel-tcb-pwm", .of_match_table = atmel_tcb_pwm_dt_ids, - .pm = &atmel_tcb_pwm_pm_ops, + .pm = pm_ptr(&atmel_tcb_pwm_pm_ops), }, .probe = atmel_tcb_pwm_probe, .remove_new = atmel_tcb_pwm_remove, diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c index 15d6ed03c3ce..45046a5c20a5 100644 --- a/drivers/pwm/pwm-bcm-kona.c +++ b/drivers/pwm/pwm-bcm-kona.c @@ -260,7 +260,7 @@ static int kona_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm, return err; } - err = kona_pwmc_config(pwm->chip, pwm, state->duty_cycle, state->period); + err = kona_pwmc_config(chip, pwm, state->duty_cycle, state->period); if (err && !pwm->state.enabled) clk_disable_unprepare(kp->clk); diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c index ab30667f4f95..283cf27f25ba 100644 --- a/drivers/pwm/pwm-bcm2835.c +++ b/drivers/pwm/pwm-bcm2835.c @@ -28,6 +28,7 @@ struct bcm2835_pwm { struct device *dev; void __iomem *base; struct clk *clk; + unsigned long rate; }; static inline struct bcm2835_pwm *to_bcm2835_pwm(struct pwm_chip *chip) @@ -63,17 +64,11 @@ static int bcm2835_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, { struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); - unsigned long rate = clk_get_rate(pc->clk); unsigned long long period_cycles; u64 max_period; u32 val; - if (!rate) { - dev_err(pc->dev, "failed to get clock rate\n"); - return -EINVAL; - } - /* * period_cycles must be a 32 bit value, so period * rate / NSEC_PER_SEC * must be <= U32_MAX. As U32_MAX * NSEC_PER_SEC < U64_MAX the @@ -88,13 +83,13 @@ static int bcm2835_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, * <=> period < ((U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC/2) / rate * <=> period <= ceil((U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC/2) / rate) - 1 */ - max_period = DIV_ROUND_UP_ULL((u64)U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC / 2, rate) - 1; + max_period = DIV_ROUND_UP_ULL((u64)U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC / 2, pc->rate) - 1; if (state->period > max_period) return -EINVAL; /* set period */ - period_cycles = DIV_ROUND_CLOSEST_ULL(state->period * rate, NSEC_PER_SEC); + period_cycles = DIV_ROUND_CLOSEST_ULL(state->period * pc->rate, NSEC_PER_SEC); /* don't accept a period that is too small */ if (period_cycles < PERIOD_MIN) @@ -103,7 +98,7 @@ static int bcm2835_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, writel(period_cycles, pc->base + PERIOD(pwm->hwpwm)); /* set duty cycle */ - val = DIV_ROUND_CLOSEST_ULL(state->duty_cycle * rate, NSEC_PER_SEC); + val = DIV_ROUND_CLOSEST_ULL(state->duty_cycle * pc->rate, NSEC_PER_SEC); writel(val, pc->base + DUTY(pwm->hwpwm)); /* set polarity */ @@ -131,6 +126,13 @@ static const struct pwm_ops bcm2835_pwm_ops = { .apply = bcm2835_pwm_apply, }; +static void devm_clk_rate_exclusive_put(void *data) +{ + struct clk *clk = data; + + clk_rate_exclusive_put(clk); +} + static int bcm2835_pwm_probe(struct platform_device *pdev) { struct bcm2835_pwm *pc; @@ -151,8 +153,24 @@ static int bcm2835_pwm_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk), "clock not found\n"); + ret = clk_rate_exclusive_get(pc->clk); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "fail to get exclusive rate\n"); + + ret = devm_add_action_or_reset(&pdev->dev, devm_clk_rate_exclusive_put, + pc->clk); + if (ret) + return ret; + + pc->rate = clk_get_rate(pc->clk); + if (!pc->rate) + return dev_err_probe(&pdev->dev, -EINVAL, + "failed to get clock rate\n"); + pc->chip.dev = &pdev->dev; pc->chip.ops = &bcm2835_pwm_ops; + pc->chip.atomic = true; pc->chip.npwm = 2; platform_set_drvdata(pdev, pc); diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index ba2d79991769..442913232dc0 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c @@ -226,7 +226,6 @@ static int berlin_pwm_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int berlin_pwm_suspend(struct device *dev) { struct berlin_pwm_chip *bpc = dev_get_drvdata(dev); @@ -267,17 +266,16 @@ static int berlin_pwm_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(berlin_pwm_pm_ops, berlin_pwm_suspend, - berlin_pwm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(berlin_pwm_pm_ops, berlin_pwm_suspend, + berlin_pwm_resume); static struct platform_driver berlin_pwm_driver = { .probe = berlin_pwm_probe, .driver = { .name = "berlin-pwm", .of_match_table = berlin_pwm_match, - .pm = &berlin_pwm_pm_ops, + .pm = pm_ptr(&berlin_pwm_pm_ops), }, }; module_platform_driver(berlin_pwm_driver); diff --git a/drivers/pwm/pwm-brcmstb.c b/drivers/pwm/pwm-brcmstb.c index b723c2d4f485..0fdeb0b2dbf3 100644 --- a/drivers/pwm/pwm-brcmstb.c +++ b/drivers/pwm/pwm-brcmstb.c @@ -259,7 +259,6 @@ static int brcmstb_pwm_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int brcmstb_pwm_suspend(struct device *dev) { struct brcmstb_pwm *p = dev_get_drvdata(dev); @@ -275,17 +274,16 @@ static int brcmstb_pwm_resume(struct device *dev) return clk_prepare_enable(p->clk); } -#endif -static SIMPLE_DEV_PM_OPS(brcmstb_pwm_pm_ops, brcmstb_pwm_suspend, - brcmstb_pwm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(brcmstb_pwm_pm_ops, brcmstb_pwm_suspend, + brcmstb_pwm_resume); static struct platform_driver brcmstb_pwm_driver = { .probe = brcmstb_pwm_probe, .driver = { .name = "pwm-brcmstb", .of_match_table = brcmstb_pwm_of_match, - .pm = &brcmstb_pwm_pm_ops, + .pm = pm_ptr(&brcmstb_pwm_pm_ops), }, }; module_platform_driver(brcmstb_pwm_driver); diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 2b0b659eee97..e09358901ab5 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -160,22 +160,22 @@ static const struct pwm_ops crc_pwm_ops = { static int crystalcove_pwm_probe(struct platform_device *pdev) { - struct crystalcove_pwm *pwm; + struct crystalcove_pwm *crc_pwm; struct device *dev = pdev->dev.parent; struct intel_soc_pmic *pmic = dev_get_drvdata(dev); - pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); - if (!pwm) + crc_pwm = devm_kzalloc(&pdev->dev, sizeof(*crc_pwm), GFP_KERNEL); + if (!crc_pwm) return -ENOMEM; - pwm->chip.dev = &pdev->dev; - pwm->chip.ops = &crc_pwm_ops; - pwm->chip.npwm = 1; + crc_pwm->chip.dev = &pdev->dev; + crc_pwm->chip.ops = &crc_pwm_ops; + crc_pwm->chip.npwm = 1; /* get the PMIC regmap */ - pwm->regmap = pmic->regmap; + crc_pwm->regmap = pmic->regmap; - return devm_pwmchip_add(&pdev->dev, &pwm->chip); + return devm_pwmchip_add(&pdev->dev, &crc_pwm->chip); } static struct platform_driver crystalcove_pwm_driver = { diff --git a/drivers/pwm/pwm-cros-ec.c b/drivers/pwm/pwm-cros-ec.c index 4fbd23e4ef69..5fe303b8656d 100644 --- a/drivers/pwm/pwm-cros-ec.c +++ b/drivers/pwm/pwm-cros-ec.c @@ -18,14 +18,12 @@ /** * struct cros_ec_pwm_device - Driver data for EC PWM * - * @dev: Device node * @ec: Pointer to EC device * @chip: PWM controller chip * @use_pwm_type: Use PWM types instead of generic channels * @channel: array with per-channel data */ struct cros_ec_pwm_device { - struct device *dev; struct cros_ec_device *ec; struct pwm_chip chip; bool use_pwm_type; diff --git a/drivers/pwm/pwm-dwc.c b/drivers/pwm/pwm-dwc.c index bd9cadb497d7..4929354f8cd9 100644 --- a/drivers/pwm/pwm-dwc.c +++ b/drivers/pwm/pwm-dwc.c @@ -71,7 +71,6 @@ static void dwc_pwm_remove(struct pci_dev *pci) pm_runtime_get_noresume(&pci->dev); } -#ifdef CONFIG_PM_SLEEP static int dwc_pwm_suspend(struct device *dev) { struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); @@ -106,9 +105,8 @@ static int dwc_pwm_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(dwc_pwm_pm_ops, dwc_pwm_suspend, dwc_pwm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(dwc_pwm_pm_ops, dwc_pwm_suspend, dwc_pwm_resume); static const struct pci_device_id dwc_pwm_id_table[] = { { PCI_VDEVICE(INTEL, 0x4bb7) }, /* Elkhart Lake */ @@ -122,7 +120,7 @@ static struct pci_driver dwc_pwm_driver = { .remove = dwc_pwm_remove, .id_table = dwc_pwm_id_table, .driver = { - .pm = &dwc_pwm_pm_ops, + .pm = pm_ptr(&dwc_pwm_pm_ops), }, }; diff --git a/drivers/pwm/pwm-img.c b/drivers/pwm/pwm-img.c index 116fa060e302..5965ac35b32e 100644 --- a/drivers/pwm/pwm-img.c +++ b/drivers/pwm/pwm-img.c @@ -13,9 +13,9 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/pwm.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -196,7 +196,7 @@ static int img_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } - err = img_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); + err = img_pwm_config(chip, pwm, state->duty_cycle, state->period); if (err) return err; @@ -260,7 +260,6 @@ static int img_pwm_probe(struct platform_device *pdev) u64 val; unsigned long clk_rate; struct img_pwm_chip *imgchip; - const struct of_device_id *of_dev_id; imgchip = devm_kzalloc(&pdev->dev, sizeof(*imgchip), GFP_KERNEL); if (!imgchip) @@ -272,10 +271,7 @@ static int img_pwm_probe(struct platform_device *pdev) if (IS_ERR(imgchip->base)) return PTR_ERR(imgchip->base); - of_dev_id = of_match_device(img_pwm_of_match, &pdev->dev); - if (!of_dev_id) - return -ENODEV; - imgchip->data = of_dev_id->data; + imgchip->data = device_get_match_data(&pdev->dev); imgchip->periph_regs = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "img,cr-periph"); diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c index dc6aafeb9f7b..9fc290e647e1 100644 --- a/drivers/pwm/pwm-imx-tpm.c +++ b/drivers/pwm/pwm-imx-tpm.c @@ -371,7 +371,7 @@ static int pwm_imx_tpm_probe(struct platform_device *pdev) return 0; } -static int __maybe_unused pwm_imx_tpm_suspend(struct device *dev) +static int pwm_imx_tpm_suspend(struct device *dev) { struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev); @@ -390,7 +390,7 @@ static int __maybe_unused pwm_imx_tpm_suspend(struct device *dev) return 0; } -static int __maybe_unused pwm_imx_tpm_resume(struct device *dev) +static int pwm_imx_tpm_resume(struct device *dev) { struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev); int ret = 0; @@ -402,8 +402,8 @@ static int __maybe_unused pwm_imx_tpm_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(imx_tpm_pwm_pm, - pwm_imx_tpm_suspend, pwm_imx_tpm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(imx_tpm_pwm_pm, + pwm_imx_tpm_suspend, pwm_imx_tpm_resume); static const struct of_device_id imx_tpm_pwm_dt_ids[] = { { .compatible = "fsl,imx7ulp-pwm", }, @@ -415,7 +415,7 @@ static struct platform_driver imx_tpm_pwm_driver = { .driver = { .name = "imx7ulp-tpm-pwm", .of_match_table = imx_tpm_pwm_dt_ids, - .pm = &imx_tpm_pwm_pm, + .pm = pm_ptr(&imx_tpm_pwm_pm), }, .probe = pwm_imx_tpm_probe, }; diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c index e9375de60ad6..3933418e551b 100644 --- a/drivers/pwm/pwm-jz4740.c +++ b/drivers/pwm/pwm-jz4740.c @@ -61,9 +61,10 @@ static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) snprintf(name, sizeof(name), "timer%u", pwm->hwpwm); clk = clk_get(chip->dev, name); - if (IS_ERR(clk)) - return dev_err_probe(chip->dev, PTR_ERR(clk), - "Failed to get clock\n"); + if (IS_ERR(clk)) { + dev_err(chip->dev, "error %pe: Failed to get clock\n", clk); + return PTR_ERR(clk); + } err = clk_prepare_enable(clk); if (err < 0) { @@ -123,7 +124,7 @@ static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - struct jz4740_pwm_chip *jz = to_jz4740(pwm->chip); + struct jz4740_pwm_chip *jz = to_jz4740(chip); unsigned long long tmp = 0xffffull * NSEC_PER_SEC; struct clk *clk = jz->clk[pwm->hwpwm]; unsigned long period, duty; @@ -149,7 +150,7 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, */ rate = clk_round_rate(clk, tmp); if (rate < 0) { - dev_err(chip->dev, "Unable to round rate: %ld", rate); + dev_err(chip->dev, "Unable to round rate: %ld\n", rate); return rate; } @@ -170,7 +171,7 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, err = clk_set_rate(clk, rate); if (err) { - dev_err(chip->dev, "Unable to set rate: %d", err); + dev_err(chip->dev, "Unable to set rate: %d\n", err); return err; } diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index ef7d0da137ed..fe891fa71a1d 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -194,7 +194,7 @@ static int lpc18xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); - int requested_events, i; + int requested_events; if (period_ns < lpc18xx_pwm->min_period_ns || period_ns > lpc18xx_pwm->max_period_ns) { @@ -223,8 +223,6 @@ static int lpc18xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if ((requested_events <= 2 && lpc18xx_pwm->period_ns != period_ns) || !lpc18xx_pwm->period_ns) { lpc18xx_pwm->period_ns = period_ns; - for (i = 0; i < chip->npwm; i++) - pwm_set_period(&chip->pwms[i], period_ns); lpc18xx_pwm_config_period(chip, period_ns); } @@ -328,7 +326,7 @@ static int lpc18xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } - err = lpc18xx_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); + err = lpc18xx_pwm_config(chip, pwm, state->duty_cycle, state->period); if (err) return err; diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index 78f664e41e6e..1d9f3e7a2434 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c @@ -103,7 +103,7 @@ static int lpc32xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } - err = lpc32xx_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); + err = lpc32xx_pwm_config(chip, pwm, state->duty_cycle, state->period); if (err) return err; diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 373abfd25acb..17d290f847af 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -217,7 +217,7 @@ static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } - err = pwm_mediatek_config(pwm->chip, pwm, state->duty_cycle, state->period); + err = pwm_mediatek_config(chip, pwm, state->duty_cycle, state->period); if (err) return err; diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index 5bea53243ed2..2971bbf3b5e7 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -468,10 +468,9 @@ static int meson_pwm_init_channels(struct meson_pwm *meson) channel->mux.hw.init = &init; err = devm_clk_hw_register(dev, &channel->mux.hw); - if (err) { - dev_err(dev, "failed to register %s: %d\n", name, err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "failed to register %s\n", name); snprintf(name, sizeof(name), "%s#div%u", dev_name(dev), i); @@ -491,10 +490,9 @@ static int meson_pwm_init_channels(struct meson_pwm *meson) channel->div.lock = &meson->lock; err = devm_clk_hw_register(dev, &channel->div.hw); - if (err) { - dev_err(dev, "failed to register %s: %d\n", name, err); - return err; - } + if (err) + return dev_err_probe(dev, err, + "failed to register %s\n", name); snprintf(name, sizeof(name), "%s#gate%u", dev_name(dev), i); @@ -513,17 +511,13 @@ static int meson_pwm_init_channels(struct meson_pwm *meson) channel->gate.lock = &meson->lock; err = devm_clk_hw_register(dev, &channel->gate.hw); - if (err) { - dev_err(dev, "failed to register %s: %d\n", name, err); - return err; - } + if (err) + return dev_err_probe(dev, err, "failed to register %s\n", name); channel->clk = devm_clk_hw_get_clk(dev, &channel->gate.hw, NULL); - if (IS_ERR(channel->clk)) { - err = PTR_ERR(channel->clk); - dev_err(dev, "failed to register %s: %d\n", name, err); - return err; - } + if (IS_ERR(channel->clk)) + return dev_err_probe(dev, PTR_ERR(channel->clk), + "failed to register %s\n", name); } return 0; @@ -554,10 +548,9 @@ static int meson_pwm_probe(struct platform_device *pdev) return err; err = devm_pwmchip_add(&pdev->dev, &meson->chip); - if (err < 0) { - dev_err(&pdev->dev, "failed to register PWM chip: %d\n", err); - return err; - } + if (err < 0) + return dev_err_probe(&pdev->dev, err, + "failed to register PWM chip\n"); return 0; } diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index 13161e08dd6e..496bd73d29fe 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -37,7 +37,6 @@ #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/of.h> #include <linux/of_platform.h> #include <clocksource/timer-ti-dm.h> @@ -55,7 +54,6 @@ * struct pwm_omap_dmtimer_chip - Structure representing a pwm chip * corresponding to omap dmtimer. * @chip: PWM chip structure representing PWM controller - * @mutex: Mutex to protect pwm apply state * @dm_timer: Pointer to omap dm timer. * @pdata: Pointer to omap dm timer ops. * @dm_timer_pdev: Pointer to omap dm timer platform device @@ -63,7 +61,6 @@ struct pwm_omap_dmtimer_chip { struct pwm_chip chip; /* Mutex to protect pwm apply state */ - struct mutex mutex; struct omap_dm_timer *dm_timer; const struct omap_dm_timer_ops *pdata; struct platform_device *dm_timer_pdev; @@ -277,13 +274,11 @@ static int pwm_omap_dmtimer_apply(struct pwm_chip *chip, const struct pwm_state *state) { struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); - int ret = 0; - - mutex_lock(&omap->mutex); + int ret; if (pwm_omap_dmtimer_is_enabled(omap) && !state->enabled) { omap->pdata->stop(omap->dm_timer); - goto unlock_mutex; + return 0; } if (pwm_omap_dmtimer_polarity(omap) != state->polarity) @@ -292,7 +287,7 @@ static int pwm_omap_dmtimer_apply(struct pwm_chip *chip, ret = pwm_omap_dmtimer_config(chip, pwm, state->duty_cycle, state->period); if (ret) - goto unlock_mutex; + return ret; if (!pwm_omap_dmtimer_is_enabled(omap) && state->enabled) { omap->pdata->set_pwm(omap->dm_timer, @@ -303,10 +298,7 @@ static int pwm_omap_dmtimer_apply(struct pwm_chip *chip, pwm_omap_dmtimer_start(omap); } -unlock_mutex: - mutex_unlock(&omap->mutex); - - return ret; + return 0; } static const struct pwm_ops pwm_omap_dmtimer_ops = { @@ -404,8 +396,6 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) omap->chip.ops = &pwm_omap_dmtimer_ops; omap->chip.npwm = 1; - mutex_init(&omap->mutex); - ret = pwmchip_add(&omap->chip); if (ret < 0) { dev_err(&pdev->dev, "failed to register PWM\n"); @@ -452,8 +442,6 @@ static void pwm_omap_dmtimer_remove(struct platform_device *pdev) omap->pdata->free(omap->dm_timer); put_device(&omap->dm_timer_pdev->dev); - - mutex_destroy(&omap->mutex); } static const struct of_device_id pwm_omap_dmtimer_of_match[] = { diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c index 4239f2c3e8b2..28265fdfc92a 100644 --- a/drivers/pwm/pwm-renesas-tpu.c +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/ioport.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> @@ -416,7 +415,7 @@ static int tpu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } - err = tpu_pwm_config(pwm->chip, pwm, + err = tpu_pwm_config(chip, pwm, state->duty_cycle, state->period, enabled); if (err) return err; diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index cce4381e188a..a7c647e37837 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -10,8 +10,8 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/pwm.h> #include <linux/time.h> @@ -296,16 +296,11 @@ MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids); static int rockchip_pwm_probe(struct platform_device *pdev) { - const struct of_device_id *id; struct rockchip_pwm_chip *pc; u32 enable_conf, ctrl; bool enabled; int ret, count; - id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev); - if (!id) - return -EINVAL; - pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) return -ENOMEM; @@ -344,7 +339,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pc); - pc->data = id->data; + pc->data = device_get_match_data(&pdev->dev); pc->chip.dev = &pdev->dev; pc->chip.ops = &rockchip_pwm_ops; pc->chip.npwm = 1; diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index 69d9f4577b34..6e77302f7368 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -620,7 +620,6 @@ static void pwm_samsung_remove(struct platform_device *pdev) clk_disable_unprepare(our_chip->base_clk); } -#ifdef CONFIG_PM_SLEEP static int pwm_samsung_resume(struct device *dev) { struct samsung_pwm_chip *our_chip = dev_get_drvdata(dev); @@ -653,14 +652,13 @@ static int pwm_samsung_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, NULL, pwm_samsung_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, NULL, pwm_samsung_resume); static struct platform_driver pwm_samsung_driver = { .driver = { .name = "samsung-pwm", - .pm = &pwm_samsung_pm_ops, + .pm = pm_ptr(&pwm_samsung_pm_ops), .of_match_table = of_match_ptr(samsung_pwm_matches), }, .probe = pwm_samsung_probe, diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index dc92cea31cd0..6cf55cf34d39 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c @@ -407,7 +407,7 @@ static int sti_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } - err = sti_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); + err = sti_pwm_config(chip, pwm, state->duty_cycle, state->period); if (err) return err; diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c index b67974cc1872..439068f3eca1 100644 --- a/drivers/pwm/pwm-stm32-lp.c +++ b/drivers/pwm/pwm-stm32-lp.c @@ -218,7 +218,7 @@ static int stm32_pwm_lp_probe(struct platform_device *pdev) return 0; } -static int __maybe_unused stm32_pwm_lp_suspend(struct device *dev) +static int stm32_pwm_lp_suspend(struct device *dev) { struct stm32_pwm_lp *priv = dev_get_drvdata(dev); struct pwm_state state; @@ -233,13 +233,13 @@ static int __maybe_unused stm32_pwm_lp_suspend(struct device *dev) return pinctrl_pm_select_sleep_state(dev); } -static int __maybe_unused stm32_pwm_lp_resume(struct device *dev) +static int stm32_pwm_lp_resume(struct device *dev) { return pinctrl_pm_select_default_state(dev); } -static SIMPLE_DEV_PM_OPS(stm32_pwm_lp_pm_ops, stm32_pwm_lp_suspend, - stm32_pwm_lp_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(stm32_pwm_lp_pm_ops, stm32_pwm_lp_suspend, + stm32_pwm_lp_resume); static const struct of_device_id stm32_pwm_lp_of_match[] = { { .compatible = "st,stm32-pwm-lp", }, @@ -252,7 +252,7 @@ static struct platform_driver stm32_pwm_lp_driver = { .driver = { .name = "stm32-pwm-lp", .of_match_table = stm32_pwm_lp_of_match, - .pm = &stm32_pwm_lp_pm_ops, + .pm = pm_ptr(&stm32_pwm_lp_pm_ops), }, }; module_platform_driver(stm32_pwm_lp_driver); diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 3303a754ea02..5f10cba492ec 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -52,21 +52,6 @@ static u32 active_channels(struct stm32_pwm *dev) return ccer & TIM_CCER_CCXE; } -static int write_ccrx(struct stm32_pwm *dev, int ch, u32 value) -{ - switch (ch) { - case 0: - return regmap_write(dev->regmap, TIM_CCR1, value); - case 1: - return regmap_write(dev->regmap, TIM_CCR2, value); - case 2: - return regmap_write(dev->regmap, TIM_CCR3, value); - case 3: - return regmap_write(dev->regmap, TIM_CCR4, value); - } - return -EINVAL; -} - #define TIM_CCER_CC12P (TIM_CCER_CC1P | TIM_CCER_CC2P) #define TIM_CCER_CC12E (TIM_CCER_CC1E | TIM_CCER_CC2E) #define TIM_CCER_CC34P (TIM_CCER_CC3P | TIM_CCER_CC4P) @@ -323,7 +308,7 @@ unlock: return ret; } -static int stm32_pwm_config(struct stm32_pwm *priv, int ch, +static int stm32_pwm_config(struct stm32_pwm *priv, unsigned int ch, int duty_ns, int period_ns) { unsigned long long prd, div, dty; @@ -369,7 +354,7 @@ static int stm32_pwm_config(struct stm32_pwm *priv, int ch, dty = prd * duty_ns; do_div(dty, period_ns); - write_ccrx(priv, ch, dty); + regmap_write(priv->regmap, TIM_CCR1 + 4 * ch, dty); /* Configure output mode */ shift = (ch & 0x1) * CCMR_CHANNEL_SHIFT; @@ -386,7 +371,7 @@ static int stm32_pwm_config(struct stm32_pwm *priv, int ch, return 0; } -static int stm32_pwm_set_polarity(struct stm32_pwm *priv, int ch, +static int stm32_pwm_set_polarity(struct stm32_pwm *priv, unsigned int ch, enum pwm_polarity polarity) { u32 mask; @@ -401,7 +386,7 @@ static int stm32_pwm_set_polarity(struct stm32_pwm *priv, int ch, return 0; } -static int stm32_pwm_enable(struct stm32_pwm *priv, int ch) +static int stm32_pwm_enable(struct stm32_pwm *priv, unsigned int ch) { u32 mask; int ret; @@ -426,7 +411,7 @@ static int stm32_pwm_enable(struct stm32_pwm *priv, int ch) return 0; } -static void stm32_pwm_disable(struct stm32_pwm *priv, int ch) +static void stm32_pwm_disable(struct stm32_pwm *priv, unsigned int ch) { u32 mask; @@ -486,8 +471,50 @@ static int stm32_pwm_apply_locked(struct pwm_chip *chip, struct pwm_device *pwm, return ret; } +static int stm32_pwm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, struct pwm_state *state) +{ + struct stm32_pwm *priv = to_stm32_pwm_dev(chip); + int ch = pwm->hwpwm; + unsigned long rate; + u32 ccer, psc, arr, ccr; + u64 dty, prd; + int ret; + + mutex_lock(&priv->lock); + + ret = regmap_read(priv->regmap, TIM_CCER, &ccer); + if (ret) + goto out; + + state->enabled = ccer & (TIM_CCER_CC1E << (ch * 4)); + state->polarity = (ccer & (TIM_CCER_CC1P << (ch * 4))) ? + PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL; + ret = regmap_read(priv->regmap, TIM_PSC, &psc); + if (ret) + goto out; + ret = regmap_read(priv->regmap, TIM_ARR, &arr); + if (ret) + goto out; + ret = regmap_read(priv->regmap, TIM_CCR1 + 4 * ch, &ccr); + if (ret) + goto out; + + rate = clk_get_rate(priv->clk); + + prd = (u64)NSEC_PER_SEC * (psc + 1) * (arr + 1); + state->period = DIV_ROUND_UP_ULL(prd, rate); + dty = (u64)NSEC_PER_SEC * (psc + 1) * ccr; + state->duty_cycle = DIV_ROUND_UP_ULL(dty, rate); + +out: + mutex_unlock(&priv->lock); + return ret; +} + static const struct pwm_ops stm32pwm_ops = { .apply = stm32_pwm_apply_locked, + .get_state = stm32_pwm_get_state, .capture = IS_ENABLED(CONFIG_DMA_ENGINE) ? stm32_pwm_capture : NULL, }; @@ -578,32 +605,23 @@ static void stm32_pwm_detect_complementary(struct stm32_pwm *priv) priv->have_complementary_output = (ccer != 0); } -static int stm32_pwm_detect_channels(struct stm32_pwm *priv) +static unsigned int stm32_pwm_detect_channels(struct stm32_pwm *priv, + unsigned int *num_enabled) { - u32 ccer; - int npwm = 0; + u32 ccer, ccer_backup; /* * If channels enable bits don't exist writing 1 will have no * effect so we can detect and count them. */ + regmap_read(priv->regmap, TIM_CCER, &ccer_backup); regmap_set_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE); regmap_read(priv->regmap, TIM_CCER, &ccer); - regmap_clear_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE); - - if (ccer & TIM_CCER_CC1E) - npwm++; - - if (ccer & TIM_CCER_CC2E) - npwm++; + regmap_write(priv->regmap, TIM_CCER, ccer_backup); - if (ccer & TIM_CCER_CC3E) - npwm++; + *num_enabled = hweight32(ccer_backup & TIM_CCER_CCXE); - if (ccer & TIM_CCER_CC4E) - npwm++; - - return npwm; + return hweight32(ccer & TIM_CCER_CCXE); } static int stm32_pwm_probe(struct platform_device *pdev) @@ -612,6 +630,8 @@ static int stm32_pwm_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent); struct stm32_pwm *priv; + unsigned int num_enabled; + unsigned int i; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -634,7 +654,11 @@ static int stm32_pwm_probe(struct platform_device *pdev) priv->chip.dev = dev; priv->chip.ops = &stm32pwm_ops; - priv->chip.npwm = stm32_pwm_detect_channels(priv); + priv->chip.npwm = stm32_pwm_detect_channels(priv, &num_enabled); + + /* Initialize clock refcount to number of enabled PWM channels. */ + for (i = 0; i < num_enabled; i++) + clk_enable(priv->clk); ret = devm_pwmchip_add(dev, &priv->chip); if (ret < 0) @@ -645,7 +669,7 @@ static int stm32_pwm_probe(struct platform_device *pdev) return 0; } -static int __maybe_unused stm32_pwm_suspend(struct device *dev) +static int stm32_pwm_suspend(struct device *dev) { struct stm32_pwm *priv = dev_get_drvdata(dev); unsigned int i; @@ -666,7 +690,7 @@ static int __maybe_unused stm32_pwm_suspend(struct device *dev) return pinctrl_pm_select_sleep_state(dev); } -static int __maybe_unused stm32_pwm_resume(struct device *dev) +static int stm32_pwm_resume(struct device *dev) { struct stm32_pwm *priv = dev_get_drvdata(dev); int ret; @@ -679,7 +703,7 @@ static int __maybe_unused stm32_pwm_resume(struct device *dev) return stm32_pwm_apply_breakinputs(priv); } -static SIMPLE_DEV_PM_OPS(stm32_pwm_pm_ops, stm32_pwm_suspend, stm32_pwm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(stm32_pwm_pm_ops, stm32_pwm_suspend, stm32_pwm_resume); static const struct of_device_id stm32_pwm_of_match[] = { { .compatible = "st,stm32-pwm", }, @@ -692,7 +716,7 @@ static struct platform_driver stm32_pwm_driver = { .driver = { .name = "stm32-pwm", .of_match_table = stm32_pwm_of_match, - .pm = &stm32_pwm_pm_ops, + .pm = pm_ptr(&stm32_pwm_pm_ops), }, }; module_platform_driver(stm32_pwm_driver); diff --git a/drivers/pwm/pwm-stmpe.c b/drivers/pwm/pwm-stmpe.c index a46f5b4dd816..19c0c0f39675 100644 --- a/drivers/pwm/pwm-stmpe.c +++ b/drivers/pwm/pwm-stmpe.c @@ -44,7 +44,7 @@ static int stmpe_24xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ret = stmpe_reg_read(stmpe_pwm->stmpe, STMPE24XX_PWMCS); if (ret < 0) { - dev_err(chip->dev, "error reading PWM#%u control\n", + dev_dbg(chip->dev, "error reading PWM#%u control\n", pwm->hwpwm); return ret; } @@ -53,7 +53,7 @@ static int stmpe_24xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ret = stmpe_reg_write(stmpe_pwm->stmpe, STMPE24XX_PWMCS, value); if (ret) { - dev_err(chip->dev, "error writing PWM#%u control\n", + dev_dbg(chip->dev, "error writing PWM#%u control\n", pwm->hwpwm); return ret; } @@ -70,7 +70,7 @@ static int stmpe_24xx_pwm_disable(struct pwm_chip *chip, ret = stmpe_reg_read(stmpe_pwm->stmpe, STMPE24XX_PWMCS); if (ret < 0) { - dev_err(chip->dev, "error reading PWM#%u control\n", + dev_dbg(chip->dev, "error reading PWM#%u control\n", pwm->hwpwm); return ret; } @@ -79,7 +79,7 @@ static int stmpe_24xx_pwm_disable(struct pwm_chip *chip, ret = stmpe_reg_write(stmpe_pwm->stmpe, STMPE24XX_PWMCS, value); if (ret) - dev_err(chip->dev, "error writing PWM#%u control\n", + dev_dbg(chip->dev, "error writing PWM#%u control\n", pwm->hwpwm); return ret; } @@ -233,7 +233,7 @@ static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ret = stmpe_reg_write(stmpe_pwm->stmpe, offset, value); if (ret) { - dev_err(chip->dev, "error writing register %02x: %d\n", + dev_dbg(chip->dev, "error writing register %02x: %d\n", offset, ret); return ret; } @@ -242,7 +242,7 @@ static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ret = stmpe_reg_write(stmpe_pwm->stmpe, offset, value); if (ret) { - dev_err(chip->dev, "error writing register %02x: %d\n", + dev_dbg(chip->dev, "error writing register %02x: %d\n", offset, ret); return ret; } @@ -275,7 +275,7 @@ static int stmpe_24xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } - err = stmpe_24xx_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); + err = stmpe_24xx_pwm_config(chip, pwm, state->duty_cycle, state->period); if (err) return err; diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index 39ea51e08c94..82ee2f0754f9 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -256,7 +256,7 @@ static int tegra_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } - err = tegra_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); + err = tegra_pwm_config(chip, pwm, state->duty_cycle, state->period); if (err) return err; diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 11e3549cf103..d974f4414ac9 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -269,7 +269,6 @@ static void ecap_pwm_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP static void ecap_pwm_save_context(struct ecap_pwm_chip *pc) { pm_runtime_get_sync(pc->chip.dev); @@ -312,15 +311,14 @@ static int ecap_pwm_resume(struct device *dev) ecap_pwm_restore_context(pc); return 0; } -#endif -static SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume); static struct platform_driver ecap_pwm_driver = { .driver = { .name = "ecap", .of_match_table = ecap_of_match, - .pm = &ecap_pwm_pm_ops, + .pm = pm_ptr(&ecap_pwm_pm_ops), }, .probe = ecap_pwm_probe, .remove_new = ecap_pwm_remove, diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 66ac2655845f..af231fa74fa9 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -521,7 +521,6 @@ static void ehrpwm_pwm_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP static void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc) { pm_runtime_get_sync(pc->chip.dev); @@ -589,16 +588,15 @@ static int ehrpwm_pwm_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend, - ehrpwm_pwm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend, + ehrpwm_pwm_resume); static struct platform_driver ehrpwm_pwm_driver = { .driver = { .name = "ehrpwm", .of_match_table = ehrpwm_of_match, - .pm = &ehrpwm_pwm_pm_ops, + .pm = pm_ptr(&ehrpwm_pwm_pm_ops), }, .probe = ehrpwm_pwm_probe, .remove_new = ehrpwm_pwm_remove, diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c index 625233f4703a..c670ccb81653 100644 --- a/drivers/pwm/pwm-twl-led.c +++ b/drivers/pwm/pwm-twl-led.c @@ -172,10 +172,10 @@ static int twl4030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm, * We cannot skip calling ->config even if state->period == * pwm->state.period && state->duty_cycle == pwm->state.duty_cycle * because we might have exited early in the last call to - * pwm_apply_state because of !state->enabled and so the two values in + * pwm_apply_might_sleep because of !state->enabled and so the two values in * pwm->state might not be configured in hardware. */ - ret = twl4030_pwmled_config(pwm->chip, pwm, + ret = twl4030_pwmled_config(chip, pwm, state->duty_cycle, state->period); if (ret) return ret; @@ -275,7 +275,7 @@ static int twl6030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } - err = twl6030_pwmled_config(pwm->chip, pwm, + err = twl6030_pwmled_config(chip, pwm, state->duty_cycle, state->period); if (err) return err; diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c index 603d31f27470..68e02c9a6bf9 100644 --- a/drivers/pwm/pwm-twl.c +++ b/drivers/pwm/pwm-twl.c @@ -294,7 +294,7 @@ static int twl4030_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } - err = twl_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); + err = twl_pwm_config(chip, pwm, state->duty_cycle, state->period); if (err) return err; @@ -319,7 +319,7 @@ static int twl6030_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } - err = twl_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); + err = twl_pwm_config(chip, pwm, state->duty_cycle, state->period); if (err) return err; diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index 5568d5312d3c..7bfeacee05d0 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -206,10 +206,10 @@ static int vt8500_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, * We cannot skip calling ->config even if state->period == * pwm->state.period && state->duty_cycle == pwm->state.duty_cycle * because we might have exited early in the last call to - * pwm_apply_state because of !state->enabled and so the two values in + * pwm_apply_might_sleep because of !state->enabled and so the two values in * pwm->state might not be configured in hardware. */ - err = vt8500_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); + err = vt8500_pwm_config(chip, pwm, state->duty_cycle, state->period); if (err) return err; diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c index 8d1254761e4d..1698609d91c8 100644 --- a/drivers/pwm/sysfs.c +++ b/drivers/pwm/sysfs.c @@ -62,7 +62,7 @@ static ssize_t period_store(struct device *child, mutex_lock(&export->lock); pwm_get_state(pwm, &state); state.period = val; - ret = pwm_apply_state(pwm, &state); + ret = pwm_apply_might_sleep(pwm, &state); mutex_unlock(&export->lock); return ret ? : size; @@ -97,7 +97,7 @@ static ssize_t duty_cycle_store(struct device *child, mutex_lock(&export->lock); pwm_get_state(pwm, &state); state.duty_cycle = val; - ret = pwm_apply_state(pwm, &state); + ret = pwm_apply_might_sleep(pwm, &state); mutex_unlock(&export->lock); return ret ? : size; @@ -144,7 +144,7 @@ static ssize_t enable_store(struct device *child, goto unlock; } - ret = pwm_apply_state(pwm, &state); + ret = pwm_apply_might_sleep(pwm, &state); unlock: mutex_unlock(&export->lock); @@ -194,7 +194,7 @@ static ssize_t polarity_store(struct device *child, mutex_lock(&export->lock); pwm_get_state(pwm, &state); state.polarity = polarity; - ret = pwm_apply_state(pwm, &state); + ret = pwm_apply_might_sleep(pwm, &state); mutex_unlock(&export->lock); return ret ? : size; @@ -401,7 +401,7 @@ static int pwm_class_apply_state(struct pwm_export *export, struct pwm_device *pwm, struct pwm_state *state) { - int ret = pwm_apply_state(pwm, state); + int ret = pwm_apply_might_sleep(pwm, state); /* release lock taken in pwm_class_get_state */ mutex_unlock(&export->lock); @@ -510,7 +510,7 @@ void pwmchip_sysfs_export(struct pwm_chip *chip) * the kernel it's just not exported. */ parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip, - "pwmchip%d", chip->base); + "pwmchip%d", chip->id); if (IS_ERR(parent)) { dev_warn(chip->dev, "device_create failed for pwm_chip sysfs export\n"); diff --git a/drivers/regulator/max5970-regulator.c b/drivers/regulator/max5970-regulator.c index bc88a40a88d4..830a1c4cd705 100644 --- a/drivers/regulator/max5970-regulator.c +++ b/drivers/regulator/max5970-regulator.c @@ -392,7 +392,7 @@ static int max597x_regmap_read_clear(struct regmap *map, unsigned int reg, return ret; if (*val) - return regmap_write(map, reg, *val); + return regmap_write(map, reg, 0); return 0; } diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c index 2aff6db748e2..60cfcd741c2a 100644 --- a/drivers/regulator/pwm-regulator.c +++ b/drivers/regulator/pwm-regulator.c @@ -90,7 +90,7 @@ static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev, pwm_set_relative_duty_cycle(&pstate, drvdata->duty_cycle_table[selector].dutycycle, 100); - ret = pwm_apply_state(drvdata->pwm, &pstate); + ret = pwm_apply_might_sleep(drvdata->pwm, &pstate); if (ret) { dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret); return ret; @@ -157,7 +157,17 @@ static int pwm_regulator_get_voltage(struct regulator_dev *rdev) pwm_get_state(drvdata->pwm, &pstate); + if (!pstate.enabled) { + if (pstate.polarity == PWM_POLARITY_INVERSED) + pstate.duty_cycle = pstate.period; + else + pstate.duty_cycle = 0; + } + voltage = pwm_get_relative_duty_cycle(&pstate, duty_unit); + if (voltage < min(max_uV_duty, min_uV_duty) || + voltage > max(max_uV_duty, min_uV_duty)) + return -ENOTRECOVERABLE; /* * The dutycycle for min_uV might be greater than the one for max_uV. @@ -216,7 +226,7 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev, pwm_set_relative_duty_cycle(&pstate, dutycycle, duty_unit); - ret = pwm_apply_state(drvdata->pwm, &pstate); + ret = pwm_apply_might_sleep(drvdata->pwm, &pstate); if (ret) { dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret); return ret; @@ -313,6 +323,32 @@ static int pwm_regulator_init_continuous(struct platform_device *pdev, return 0; } +static int pwm_regulator_init_boot_on(struct platform_device *pdev, + struct pwm_regulator_data *drvdata, + const struct regulator_init_data *init_data) +{ + struct pwm_state pstate; + + if (!init_data->constraints.boot_on || drvdata->enb_gpio) + return 0; + + pwm_get_state(drvdata->pwm, &pstate); + if (pstate.enabled) + return 0; + + /* + * Update the duty cycle so the output does not change + * when the regulator core enables the regulator (and + * thus the PWM channel). + */ + if (pstate.polarity == PWM_POLARITY_INVERSED) + pstate.duty_cycle = pstate.period; + else + pstate.duty_cycle = 0; + + return pwm_apply_might_sleep(drvdata->pwm, &pstate); +} + static int pwm_regulator_probe(struct platform_device *pdev) { const struct regulator_init_data *init_data; @@ -372,6 +408,13 @@ static int pwm_regulator_probe(struct platform_device *pdev) if (ret) return ret; + ret = pwm_regulator_init_boot_on(pdev, drvdata, init_data); + if (ret) { + dev_err(&pdev->dev, "Failed to apply boot_on settings: %d\n", + ret); + return ret; + } + regulator = devm_regulator_register(&pdev->dev, &drvdata->desc, &config); if (IS_ERR(regulator)) { diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index f48214e2c3b4..04133510e5af 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -726,9 +726,25 @@ static int ti_abb_probe(struct platform_device *pdev) return PTR_ERR(abb->setup_reg); } - abb->int_base = devm_platform_ioremap_resource_byname(pdev, "int-address"); - if (IS_ERR(abb->int_base)) - return PTR_ERR(abb->int_base); + pname = "int-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_err(dev, "Missing '%s' IO resource\n", pname); + return -ENODEV; + } + /* + * The MPU interrupt status register (PRM_IRQSTATUS_MPU) is + * shared between regulator-abb-{ivahd,dspeve,gpu} driver + * instances. Therefore use devm_ioremap() rather than + * devm_platform_ioremap_resource_byname() to avoid busy + * resource region conflicts. + */ + abb->int_base = devm_ioremap(dev, res->start, + resource_size(res)); + if (!abb->int_base) { + dev_err(dev, "Unable to map '%s'\n", pname); + return -ENOMEM; + } /* Map Optional resources */ pname = "efuse-address"; diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c index 8fcda9b74545..a1c62d15f16c 100644 --- a/drivers/remoteproc/imx_dsp_rproc.c +++ b/drivers/remoteproc/imx_dsp_rproc.c @@ -940,6 +940,7 @@ static const struct rproc_ops imx_dsp_rproc_ops = { .kick = imx_dsp_rproc_kick, .load = imx_dsp_rproc_elf_load_segments, .parse_fw = imx_dsp_rproc_parse_fw, + .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, .sanity_check = rproc_elf_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, }; diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 913a5d2068e8..a9dd58608052 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -1165,6 +1165,22 @@ static const struct adsp_data sm8550_mpss_resource = { .region_assign_idx = 2, }; +static const struct adsp_data sc7280_wpss_resource = { + .crash_reason_smem = 626, + .firmware_name = "wpss.mdt", + .pas_id = 6, + .auto_boot = true, + .proxy_pd_names = (char*[]){ + "cx", + "mx", + NULL + }, + .load_state = "wpss", + .ssr_name = "wpss", + .sysmon_name = "wpss", + .ssctl_id = 0x19, +}; + static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,msm8226-adsp-pil", .data = &adsp_resource_init}, { .compatible = "qcom,msm8953-adsp-pil", .data = &msm8996_adsp_resource}, @@ -1178,7 +1194,10 @@ static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,qcs404-wcss-pas", .data = &wcss_resource_init }, { .compatible = "qcom,sc7180-adsp-pas", .data = &sm8250_adsp_resource}, { .compatible = "qcom,sc7180-mpss-pas", .data = &mpss_resource_init}, + { .compatible = "qcom,sc7280-adsp-pas", .data = &sm8350_adsp_resource}, + { .compatible = "qcom,sc7280-cdsp-pas", .data = &sm6350_cdsp_resource}, { .compatible = "qcom,sc7280-mpss-pas", .data = &mpss_resource_init}, + { .compatible = "qcom,sc7280-wpss-pas", .data = &sc7280_wpss_resource}, { .compatible = "qcom,sc8180x-adsp-pas", .data = &sm8150_adsp_resource}, { .compatible = "qcom,sc8180x-cdsp-pas", .data = &sm8150_cdsp_resource}, { .compatible = "qcom,sc8180x-mpss-pas", .data = &sc8180x_mpss_resource}, diff --git a/drivers/remoteproc/ti_k3_dsp_remoteproc.c b/drivers/remoteproc/ti_k3_dsp_remoteproc.c index ef8415a7cd54..ab882e3b7130 100644 --- a/drivers/remoteproc/ti_k3_dsp_remoteproc.c +++ b/drivers/remoteproc/ti_k3_dsp_remoteproc.c @@ -158,8 +158,8 @@ static void k3_dsp_rproc_kick(struct rproc *rproc, int vqid) /* send the index of the triggered virtqueue in the mailbox payload */ ret = mbox_send_message(kproc->mbox, (void *)msg); if (ret < 0) - dev_err(dev, "failed to send mailbox message, status = %d\n", - ret); + dev_err(dev, "failed to send mailbox message (%pe)\n", + ERR_PTR(ret)); } /* Put the DSP processor into reset */ @@ -170,7 +170,7 @@ static int k3_dsp_rproc_reset(struct k3_dsp_rproc *kproc) ret = reset_control_assert(kproc->reset); if (ret) { - dev_err(dev, "local-reset assert failed, ret = %d\n", ret); + dev_err(dev, "local-reset assert failed (%pe)\n", ERR_PTR(ret)); return ret; } @@ -180,7 +180,7 @@ static int k3_dsp_rproc_reset(struct k3_dsp_rproc *kproc) ret = kproc->ti_sci->ops.dev_ops.put_device(kproc->ti_sci, kproc->ti_sci_id); if (ret) { - dev_err(dev, "module-reset assert failed, ret = %d\n", ret); + dev_err(dev, "module-reset assert failed (%pe)\n", ERR_PTR(ret)); if (reset_control_deassert(kproc->reset)) dev_warn(dev, "local-reset deassert back failed\n"); } @@ -200,14 +200,14 @@ static int k3_dsp_rproc_release(struct k3_dsp_rproc *kproc) ret = kproc->ti_sci->ops.dev_ops.get_device(kproc->ti_sci, kproc->ti_sci_id); if (ret) { - dev_err(dev, "module-reset deassert failed, ret = %d\n", ret); + dev_err(dev, "module-reset deassert failed (%pe)\n", ERR_PTR(ret)); return ret; } lreset: ret = reset_control_deassert(kproc->reset); if (ret) { - dev_err(dev, "local-reset deassert failed, ret = %d\n", ret); + dev_err(dev, "local-reset deassert failed, (%pe)\n", ERR_PTR(ret)); if (kproc->ti_sci->ops.dev_ops.put_device(kproc->ti_sci, kproc->ti_sci_id)) dev_warn(dev, "module-reset assert back failed\n"); @@ -246,7 +246,7 @@ static int k3_dsp_rproc_request_mbox(struct rproc *rproc) */ ret = mbox_send_message(kproc->mbox, (void *)RP_MBOX_ECHO_REQUEST); if (ret < 0) { - dev_err(dev, "mbox_send_message failed: %d\n", ret); + dev_err(dev, "mbox_send_message failed (%pe)\n", ERR_PTR(ret)); mbox_free_channel(kproc->mbox); return ret; } @@ -272,8 +272,8 @@ static int k3_dsp_rproc_prepare(struct rproc *rproc) ret = kproc->ti_sci->ops.dev_ops.get_device(kproc->ti_sci, kproc->ti_sci_id); if (ret) - dev_err(dev, "module-reset deassert failed, cannot enable internal RAM loading, ret = %d\n", - ret); + dev_err(dev, "module-reset deassert failed, cannot enable internal RAM loading (%pe)\n", + ERR_PTR(ret)); return ret; } @@ -296,7 +296,7 @@ static int k3_dsp_rproc_unprepare(struct rproc *rproc) ret = kproc->ti_sci->ops.dev_ops.put_device(kproc->ti_sci, kproc->ti_sci_id); if (ret) - dev_err(dev, "module-reset assert failed, ret = %d\n", ret); + dev_err(dev, "module-reset assert failed (%pe)\n", ERR_PTR(ret)); return ret; } @@ -561,9 +561,9 @@ static int k3_dsp_reserved_mem_init(struct k3_dsp_rproc *kproc) num_rmems = of_property_count_elems_of_size(np, "memory-region", sizeof(phandle)); - if (num_rmems <= 0) { - dev_err(dev, "device does not reserved memory regions, ret = %d\n", - num_rmems); + if (num_rmems < 0) { + dev_err(dev, "device does not reserved memory regions (%pe)\n", + ERR_PTR(num_rmems)); return -EINVAL; } if (num_rmems < 2) { @@ -575,8 +575,8 @@ static int k3_dsp_reserved_mem_init(struct k3_dsp_rproc *kproc) /* use reserved memory region 0 for vring DMA allocations */ ret = of_reserved_mem_device_init_by_idx(dev, np, 0); if (ret) { - dev_err(dev, "device cannot initialize DMA pool, ret = %d\n", - ret); + dev_err(dev, "device cannot initialize DMA pool (%pe)\n", + ERR_PTR(ret)); return ret; } @@ -687,11 +687,8 @@ static int k3_dsp_rproc_probe(struct platform_device *pdev) return -ENODEV; ret = rproc_of_parse_firmware(dev, 0, &fw_name); - if (ret) { - dev_err(dev, "failed to parse firmware-name property, ret = %d\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to parse firmware-name property\n"); rproc = rproc_alloc(dev, dev_name(dev), &k3_dsp_rproc_ops, fw_name, sizeof(*kproc)); @@ -711,39 +708,35 @@ static int k3_dsp_rproc_probe(struct platform_device *pdev) kproc->ti_sci = ti_sci_get_by_phandle(np, "ti,sci"); if (IS_ERR(kproc->ti_sci)) { - ret = PTR_ERR(kproc->ti_sci); - if (ret != -EPROBE_DEFER) { - dev_err(dev, "failed to get ti-sci handle, ret = %d\n", - ret); - } + ret = dev_err_probe(dev, PTR_ERR(kproc->ti_sci), + "failed to get ti-sci handle\n"); kproc->ti_sci = NULL; goto free_rproc; } ret = of_property_read_u32(np, "ti,sci-dev-id", &kproc->ti_sci_id); if (ret) { - dev_err(dev, "missing 'ti,sci-dev-id' property\n"); + dev_err_probe(dev, ret, "missing 'ti,sci-dev-id' property\n"); goto put_sci; } kproc->reset = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(kproc->reset)) { - ret = PTR_ERR(kproc->reset); - dev_err(dev, "failed to get reset, status = %d\n", ret); + ret = dev_err_probe(dev, PTR_ERR(kproc->reset), + "failed to get reset\n"); goto put_sci; } kproc->tsp = k3_dsp_rproc_of_get_tsp(dev, kproc->ti_sci); if (IS_ERR(kproc->tsp)) { - dev_err(dev, "failed to construct ti-sci proc control, ret = %d\n", - ret); - ret = PTR_ERR(kproc->tsp); + ret = dev_err_probe(dev, PTR_ERR(kproc->tsp), + "failed to construct ti-sci proc control\n"); goto put_sci; } ret = ti_sci_proc_request(kproc->tsp); if (ret < 0) { - dev_err(dev, "ti_sci_proc_request failed, ret = %d\n", ret); + dev_err_probe(dev, ret, "ti_sci_proc_request failed\n"); goto free_tsp; } @@ -753,15 +746,14 @@ static int k3_dsp_rproc_probe(struct platform_device *pdev) ret = k3_dsp_reserved_mem_init(kproc); if (ret) { - dev_err(dev, "reserved memory init failed, ret = %d\n", ret); + dev_err_probe(dev, ret, "reserved memory init failed\n"); goto release_tsp; } ret = kproc->ti_sci->ops.dev_ops.is_on(kproc->ti_sci, kproc->ti_sci_id, NULL, &p_state); if (ret) { - dev_err(dev, "failed to get initial state, mode cannot be determined, ret = %d\n", - ret); + dev_err_probe(dev, ret, "failed to get initial state, mode cannot be determined\n"); goto release_mem; } @@ -787,8 +779,7 @@ static int k3_dsp_rproc_probe(struct platform_device *pdev) if (data->uses_lreset) { ret = reset_control_status(kproc->reset); if (ret < 0) { - dev_err(dev, "failed to get reset status, status = %d\n", - ret); + dev_err_probe(dev, ret, "failed to get reset status\n"); goto release_mem; } else if (ret == 0) { dev_warn(dev, "local reset is deasserted for device\n"); @@ -799,8 +790,7 @@ static int k3_dsp_rproc_probe(struct platform_device *pdev) ret = rproc_add(rproc); if (ret) { - dev_err(dev, "failed to add register device with remoteproc core, status = %d\n", - ret); + dev_err_probe(dev, ret, "failed to add register device with remoteproc core\n"); goto release_mem; } @@ -813,19 +803,19 @@ release_mem: release_tsp: ret1 = ti_sci_proc_release(kproc->tsp); if (ret1) - dev_err(dev, "failed to release proc, ret = %d\n", ret1); + dev_err(dev, "failed to release proc (%pe)\n", ERR_PTR(ret1)); free_tsp: kfree(kproc->tsp); put_sci: ret1 = ti_sci_put_handle(kproc->ti_sci); if (ret1) - dev_err(dev, "failed to put ti_sci handle, ret = %d\n", ret1); + dev_err(dev, "failed to put ti_sci handle (%pe)\n", ERR_PTR(ret1)); free_rproc: rproc_free(rproc); return ret; } -static int k3_dsp_rproc_remove(struct platform_device *pdev) +static void k3_dsp_rproc_remove(struct platform_device *pdev) { struct k3_dsp_rproc *kproc = platform_get_drvdata(pdev); struct rproc *rproc = kproc->rproc; @@ -835,8 +825,9 @@ static int k3_dsp_rproc_remove(struct platform_device *pdev) if (rproc->state == RPROC_ATTACHED) { ret = rproc_detach(rproc); if (ret) { - dev_err(dev, "failed to detach proc, ret = %d\n", ret); - return ret; + /* Note this error path leaks resources */ + dev_err(dev, "failed to detach proc (%pe)\n", ERR_PTR(ret)); + return; } } @@ -844,18 +835,16 @@ static int k3_dsp_rproc_remove(struct platform_device *pdev) ret = ti_sci_proc_release(kproc->tsp); if (ret) - dev_err(dev, "failed to release proc, ret = %d\n", ret); + dev_err(dev, "failed to release proc (%pe)\n", ERR_PTR(ret)); kfree(kproc->tsp); ret = ti_sci_put_handle(kproc->ti_sci); if (ret) - dev_err(dev, "failed to put ti_sci handle, ret = %d\n", ret); + dev_err(dev, "failed to put ti_sci handle (%pe)\n", ERR_PTR(ret)); k3_dsp_reserved_mem_exit(kproc); rproc_free(kproc->rproc); - - return 0; } static const struct k3_dsp_mem_data c66_mems[] = { @@ -906,7 +895,7 @@ MODULE_DEVICE_TABLE(of, k3_dsp_of_match); static struct platform_driver k3_dsp_rproc_driver = { .probe = k3_dsp_rproc_probe, - .remove = k3_dsp_rproc_remove, + .remove_new = k3_dsp_rproc_remove, .driver = { .name = "k3-dsp-rproc", .of_match_table = k3_dsp_of_match, diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index dc87965f8164..1062939c3264 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -378,6 +378,7 @@ static void virtio_rpmsg_release_device(struct device *dev) struct rpmsg_device *rpdev = to_rpmsg_device(dev); struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); + kfree(rpdev->driver_override); kfree(vch); } diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 3814e0845e77..e37a4341f442 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -373,6 +373,19 @@ config RTC_DRV_MAX8997 This driver can also be built as a module. If so, the module will be called rtc-max8997. +config RTC_DRV_MAX31335 + tristate "Analog Devices MAX31335" + depends on I2C + depends on COMMON_CLK + depends on HWMON || HWMON=n + select REGMAP_I2C + help + If you say yes here you get support for the Analog Devices + MAX31335. + + This driver can also be built as a module. If so, the module + will be called rtc-max31335. + config RTC_DRV_MAX77686 tristate "Maxim MAX77686" depends on MFD_MAX77686 || MFD_MAX77620 || MFD_MAX77714 || COMPILE_TEST @@ -578,6 +591,18 @@ config RTC_DRV_TPS6586X along with alarm. This driver supports the RTC driver for the TPS6586X RTC module. +config RTC_DRV_TPS6594 + tristate "TI TPS6594 RTC driver" + depends on MFD_TPS6594 + default MFD_TPS6594 + help + TI Power Management IC TPS6594 supports RTC functionality + along with alarm. This driver supports the RTC driver for + the TPS6594 RTC module. + + This driver can also be built as a module. If so, the module + will be called rtc-tps6594. + config RTC_DRV_TPS65910 tristate "TI TPS65910 RTC driver" depends on MFD_TPS65910 @@ -1705,6 +1730,7 @@ config RTC_DRV_LPC24XX tristate "NXP RTC for LPC178x/18xx/408x/43xx" depends on ARCH_LPC18XX || COMPILE_TEST depends on OF && HAS_IOMEM + depends on COMMON_CLK help This enables support for the NXP RTC found which can be found on NXP LPC178x/18xx/408x/43xx devices. @@ -1930,6 +1956,17 @@ config RTC_DRV_TI_K3 This driver can also be built as a module, if so, the module will be called "rtc-ti-k3". +config RTC_DRV_MA35D1 + tristate "Nuvoton MA35D1 RTC" + depends on ARCH_MA35 || COMPILE_TEST + select REGMAP_MMIO + help + If you say yes here you get support for the Nuvoton MA35D1 + On-Chip Real Time Clock. + + This driver can also be built as a module, if so, the module + will be called "rtc-ma35d1". + comment "HID Sensor RTC drivers" config RTC_DRV_HID_SENSOR_TIME diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 7b03c3abfd78..6efff381c484 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -88,6 +88,8 @@ obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o +obj-$(CONFIG_RTC_DRV_MA35D1) += rtc-ma35d1.o +obj-$(CONFIG_RTC_DRV_MAX31335) += rtc-max31335.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_MAX6916) += rtc-max6916.o @@ -176,6 +178,7 @@ obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o obj-$(CONFIG_RTC_DRV_TI_K3) += rtc-ti-k3.o obj-$(CONFIG_RTC_DRV_TPS6586X) += rtc-tps6586x.o +obj-$(CONFIG_RTC_DRV_TPS6594) += rtc-tps6594.o obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index edfd942f8c54..921ee1827974 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -256,7 +256,7 @@ static int rtc_device_get_id(struct device *dev) of_id = of_alias_get_id(dev->parent->of_node, "rtc"); if (of_id >= 0) { - id = ida_simple_get(&rtc_ida, of_id, of_id + 1, GFP_KERNEL); + id = ida_alloc_range(&rtc_ida, of_id, of_id, GFP_KERNEL); if (id < 0) dev_warn(dev, "/aliases ID %d not available\n", of_id); } diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c index eaf2c9ab9661..fa642bba3cee 100644 --- a/drivers/rtc/rtc-ac100.c +++ b/drivers/rtc/rtc-ac100.c @@ -99,7 +99,7 @@ struct ac100_rtc_dev { struct clk_hw_onecell_data *clk_data; }; -/** +/* * Clock controls for 3 clock output pins */ @@ -378,7 +378,7 @@ static void ac100_rtc_unregister_clks(struct ac100_rtc_dev *chip) clk_unregister_fixed_rate(chip->rtc_32k_clk->clk); } -/** +/* * RTC related bits */ static int ac100_rtc_get_time(struct device *dev, struct rtc_time *rtc_tm) diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 228fb2d11c70..7d99cd2c37a0 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -231,7 +231,7 @@ static int cmos_read_time(struct device *dev, struct rtc_time *t) if (!pm_trace_rtc_valid()) return -EIO; - ret = mc146818_get_time(t); + ret = mc146818_get_time(t, 1000); if (ret < 0) { dev_err_ratelimited(dev, "unable to read current time\n"); return ret; @@ -292,7 +292,7 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t) /* This not only a rtc_op, but also called directly */ if (!is_valid_irq(cmos->irq)) - return -EIO; + return -ETIMEDOUT; /* Basic alarms only support hour, minute, and seconds fields. * Some also support day and month, for alarms up to a year in @@ -307,7 +307,7 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t) * * Use the mc146818_avoid_UIP() function to avoid this. */ - if (!mc146818_avoid_UIP(cmos_read_alarm_callback, &p)) + if (!mc146818_avoid_UIP(cmos_read_alarm_callback, 10, &p)) return -EIO; if (!(p.rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { @@ -556,8 +556,8 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) * * Use mc146818_avoid_UIP() to avoid this. */ - if (!mc146818_avoid_UIP(cmos_set_alarm_callback, &p)) - return -EIO; + if (!mc146818_avoid_UIP(cmos_set_alarm_callback, 10, &p)) + return -ETIMEDOUT; cmos->alarm_expires = rtc_tm_to_time64(&t->time); @@ -818,18 +818,24 @@ static void rtc_wake_off(struct device *dev) } #ifdef CONFIG_X86 -/* Enable use_acpi_alarm mode for Intel platforms no earlier than 2015 */ static void use_acpi_alarm_quirks(void) { - if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_INTEL: + if (dmi_get_bios_year() < 2015) + return; + break; + case X86_VENDOR_AMD: + case X86_VENDOR_HYGON: + if (dmi_get_bios_year() < 2021) + return; + break; + default: return; - + } if (!is_hpet_enabled()) return; - if (dmi_get_bios_year() < 2015) - return; - use_acpi_alarm = true; } #else diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c index 2f5d60622564..859397541f29 100644 --- a/drivers/rtc/rtc-da9063.c +++ b/drivers/rtc/rtc-da9063.c @@ -377,7 +377,6 @@ static int da9063_rtc_probe(struct platform_device *pdev) { struct da9063_compatible_rtc *rtc; const struct da9063_compatible_rtc_regmap *config; - const struct of_device_id *match; int irq_alarm; u8 data[RTC_DATA_LEN]; int ret; @@ -385,14 +384,11 @@ static int da9063_rtc_probe(struct platform_device *pdev) if (!pdev->dev.of_node) return -ENXIO; - match = of_match_node(da9063_compatible_reg_id_table, - pdev->dev.of_node); - rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); if (!rtc) return -ENOMEM; - rtc->config = match->data; + rtc->config = device_get_match_data(&pdev->dev); if (of_device_is_compatible(pdev->dev.of_node, "dlg,da9063-rtc")) { struct da9063 *chip = dev_get_drvdata(pdev->dev.parent); @@ -411,57 +407,49 @@ static int da9063_rtc_probe(struct platform_device *pdev) config->rtc_enable_reg, config->rtc_enable_mask, config->rtc_enable_mask); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to enable RTC\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Failed to enable RTC\n"); ret = regmap_update_bits(rtc->regmap, config->rtc_enable_32k_crystal_reg, config->rtc_crystal_mask, config->rtc_crystal_mask); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to run 32kHz oscillator\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to run 32kHz oscillator\n"); ret = regmap_update_bits(rtc->regmap, config->rtc_alarm_secs_reg, config->rtc_alarm_status_mask, 0); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to access RTC alarm register\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to access RTC alarm register\n"); ret = regmap_update_bits(rtc->regmap, config->rtc_alarm_secs_reg, DA9063_ALARM_STATUS_ALARM, DA9063_ALARM_STATUS_ALARM); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to access RTC alarm register\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to access RTC alarm register\n"); ret = regmap_update_bits(rtc->regmap, config->rtc_alarm_year_reg, config->rtc_tick_on_mask, 0); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to disable TICKs\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to disable TICKs\n"); data[RTC_SEC] = 0; ret = regmap_bulk_read(rtc->regmap, config->rtc_alarm_secs_reg, &data[config->rtc_data_start], config->rtc_alarm_len); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to read initial alarm data: %d\n", - ret); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to read initial alarm data\n"); platform_set_drvdata(pdev, rtc); @@ -485,25 +473,29 @@ static int da9063_rtc_probe(struct platform_device *pdev) clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtc_dev->features); } - irq_alarm = platform_get_irq_byname(pdev, "ALARM"); - if (irq_alarm < 0) + irq_alarm = platform_get_irq_byname_optional(pdev, "ALARM"); + if (irq_alarm >= 0) { + ret = devm_request_threaded_irq(&pdev->dev, irq_alarm, NULL, + da9063_alarm_event, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "ALARM", rtc); + if (ret) + dev_err(&pdev->dev, + "Failed to request ALARM IRQ %d: %d\n", + irq_alarm, ret); + + ret = dev_pm_set_wake_irq(&pdev->dev, irq_alarm); + if (ret) + dev_warn(&pdev->dev, + "Failed to set IRQ %d as a wake IRQ: %d\n", + irq_alarm, ret); + + device_init_wakeup(&pdev->dev, true); + } else if (irq_alarm != -ENXIO) { return irq_alarm; - - ret = devm_request_threaded_irq(&pdev->dev, irq_alarm, NULL, - da9063_alarm_event, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "ALARM", rtc); - if (ret) - dev_err(&pdev->dev, "Failed to request ALARM IRQ %d: %d\n", - irq_alarm, ret); - - ret = dev_pm_set_wake_irq(&pdev->dev, irq_alarm); - if (ret) - dev_warn(&pdev->dev, - "Failed to set IRQ %d as a wake IRQ: %d\n", - irq_alarm, ret); - - device_init_wakeup(&pdev->dev, true); + } else { + clear_bit(RTC_FEATURE_ALARM, rtc->rtc_dev->features); + } return devm_rtc_register_device(rtc->rtc_dev); } diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index 89d7b085f721..1485a6ae51e6 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -536,6 +536,8 @@ static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq, return 0; } +#if IS_ENABLED(CONFIG_I2C) + #ifdef CONFIG_PM_SLEEP static int ds3232_suspend(struct device *dev) { @@ -564,8 +566,6 @@ static const struct dev_pm_ops ds3232_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ds3232_suspend, ds3232_resume) }; -#if IS_ENABLED(CONFIG_I2C) - static int ds3232_i2c_probe(struct i2c_client *client) { struct regmap *regmap; diff --git a/drivers/rtc/rtc-ma35d1.c b/drivers/rtc/rtc-ma35d1.c new file mode 100644 index 000000000000..cfcfc28060f6 --- /dev/null +++ b/drivers/rtc/rtc-ma35d1.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for Nuvoton MA35D1 + * + * Copyright (C) 2023 Nuvoton Technology Corp. + */ + +#include <linux/bcd.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +/* MA35D1 RTC Control Registers */ +#define MA35_REG_RTC_INIT 0x00 +#define MA35_REG_RTC_SINFASTS 0x04 +#define MA35_REG_RTC_FREQADJ 0x08 +#define MA35_REG_RTC_TIME 0x0c +#define MA35_REG_RTC_CAL 0x10 +#define MA35_REG_RTC_CLKFMT 0x14 +#define MA35_REG_RTC_WEEKDAY 0x18 +#define MA35_REG_RTC_TALM 0x1c +#define MA35_REG_RTC_CALM 0x20 +#define MA35_REG_RTC_LEAPYEAR 0x24 +#define MA35_REG_RTC_INTEN 0x28 +#define MA35_REG_RTC_INTSTS 0x2c + +/* register MA35_REG_RTC_INIT */ +#define RTC_INIT_ACTIVE BIT(0) +#define RTC_INIT_MAGIC_CODE 0xa5eb1357 + +/* register MA35_REG_RTC_CLKFMT */ +#define RTC_CLKFMT_24HEN BIT(0) +#define RTC_CLKFMT_DCOMPEN BIT(16) + +/* register MA35_REG_RTC_INTEN */ +#define RTC_INTEN_ALMIEN BIT(0) +#define RTC_INTEN_UIEN BIT(1) +#define RTC_INTEN_CLKFIEN BIT(24) +#define RTC_INTEN_CLKSTIEN BIT(25) + +/* register MA35_REG_RTC_INTSTS */ +#define RTC_INTSTS_ALMIF BIT(0) +#define RTC_INTSTS_UIF BIT(1) +#define RTC_INTSTS_CLKFIF BIT(24) +#define RTC_INTSTS_CLKSTIF BIT(25) + +#define RTC_INIT_TIMEOUT 250 + +struct ma35_rtc { + int irq_num; + void __iomem *rtc_reg; + struct rtc_device *rtcdev; +}; + +static u32 rtc_reg_read(struct ma35_rtc *p, u32 offset) +{ + return __raw_readl(p->rtc_reg + offset); +} + +static inline void rtc_reg_write(struct ma35_rtc *p, u32 offset, u32 value) +{ + __raw_writel(value, p->rtc_reg + offset); +} + +static irqreturn_t ma35d1_rtc_interrupt(int irq, void *data) +{ + struct ma35_rtc *rtc = (struct ma35_rtc *)data; + unsigned long events = 0, rtc_irq; + + rtc_irq = rtc_reg_read(rtc, MA35_REG_RTC_INTSTS); + + if (rtc_irq & RTC_INTSTS_ALMIF) { + rtc_reg_write(rtc, MA35_REG_RTC_INTSTS, RTC_INTSTS_ALMIF); + events |= RTC_AF | RTC_IRQF; + } + + rtc_update_irq(rtc->rtcdev, 1, events); + + return IRQ_HANDLED; +} + +static int ma35d1_rtc_init(struct ma35_rtc *rtc, u32 ms_timeout) +{ + const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout); + + do { + if (rtc_reg_read(rtc, MA35_REG_RTC_INIT) & RTC_INIT_ACTIVE) + return 0; + + rtc_reg_write(rtc, MA35_REG_RTC_INIT, RTC_INIT_MAGIC_CODE); + + mdelay(1); + + } while (time_before(jiffies, timeout)); + + return -ETIMEDOUT; +} + +static int ma35d1_alarm_irq_enable(struct device *dev, u32 enabled) +{ + struct ma35_rtc *rtc = dev_get_drvdata(dev); + u32 reg_ien; + + reg_ien = rtc_reg_read(rtc, MA35_REG_RTC_INTEN); + + if (enabled) + rtc_reg_write(rtc, MA35_REG_RTC_INTEN, reg_ien | RTC_INTEN_ALMIEN); + else + rtc_reg_write(rtc, MA35_REG_RTC_INTEN, reg_ien & ~RTC_INTEN_ALMIEN); + + return 0; +} + +static int ma35d1_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ma35_rtc *rtc = dev_get_drvdata(dev); + u32 time, cal, wday; + + do { + time = rtc_reg_read(rtc, MA35_REG_RTC_TIME); + cal = rtc_reg_read(rtc, MA35_REG_RTC_CAL); + wday = rtc_reg_read(rtc, MA35_REG_RTC_WEEKDAY); + } while (time != rtc_reg_read(rtc, MA35_REG_RTC_TIME) || + cal != rtc_reg_read(rtc, MA35_REG_RTC_CAL)); + + tm->tm_mday = bcd2bin(cal >> 0); + tm->tm_wday = wday; + tm->tm_mon = bcd2bin(cal >> 8); + tm->tm_mon = tm->tm_mon - 1; + tm->tm_year = bcd2bin(cal >> 16) + 100; + + tm->tm_sec = bcd2bin(time >> 0); + tm->tm_min = bcd2bin(time >> 8); + tm->tm_hour = bcd2bin(time >> 16); + + return rtc_valid_tm(tm); +} + +static int ma35d1_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ma35_rtc *rtc = dev_get_drvdata(dev); + u32 val; + + val = bin2bcd(tm->tm_mday) << 0 | bin2bcd(tm->tm_mon + 1) << 8 | + bin2bcd(tm->tm_year - 100) << 16; + rtc_reg_write(rtc, MA35_REG_RTC_CAL, val); + + val = bin2bcd(tm->tm_sec) << 0 | bin2bcd(tm->tm_min) << 8 | + bin2bcd(tm->tm_hour) << 16; + rtc_reg_write(rtc, MA35_REG_RTC_TIME, val); + + val = tm->tm_wday; + rtc_reg_write(rtc, MA35_REG_RTC_WEEKDAY, val); + + return 0; +} + +static int ma35d1_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ma35_rtc *rtc = dev_get_drvdata(dev); + u32 talm, calm; + + talm = rtc_reg_read(rtc, MA35_REG_RTC_TALM); + calm = rtc_reg_read(rtc, MA35_REG_RTC_CALM); + + alrm->time.tm_mday = bcd2bin(calm >> 0); + alrm->time.tm_mon = bcd2bin(calm >> 8); + alrm->time.tm_mon = alrm->time.tm_mon - 1; + + alrm->time.tm_year = bcd2bin(calm >> 16) + 100; + + alrm->time.tm_sec = bcd2bin(talm >> 0); + alrm->time.tm_min = bcd2bin(talm >> 8); + alrm->time.tm_hour = bcd2bin(talm >> 16); + + return rtc_valid_tm(&alrm->time); +} + +static int ma35d1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ma35_rtc *rtc = dev_get_drvdata(dev); + unsigned long val; + + val = bin2bcd(alrm->time.tm_mday) << 0 | bin2bcd(alrm->time.tm_mon + 1) << 8 | + bin2bcd(alrm->time.tm_year - 100) << 16; + rtc_reg_write(rtc, MA35_REG_RTC_CALM, val); + + val = bin2bcd(alrm->time.tm_sec) << 0 | bin2bcd(alrm->time.tm_min) << 8 | + bin2bcd(alrm->time.tm_hour) << 16; + rtc_reg_write(rtc, MA35_REG_RTC_TALM, val); + + ma35d1_alarm_irq_enable(dev, alrm->enabled); + + return 0; +} + +static const struct rtc_class_ops ma35d1_rtc_ops = { + .read_time = ma35d1_rtc_read_time, + .set_time = ma35d1_rtc_set_time, + .read_alarm = ma35d1_rtc_read_alarm, + .set_alarm = ma35d1_rtc_set_alarm, + .alarm_irq_enable = ma35d1_alarm_irq_enable, +}; + +static int ma35d1_rtc_probe(struct platform_device *pdev) +{ + struct ma35_rtc *rtc; + struct clk *clk; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->rtc_reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->rtc_reg)) + return PTR_ERR(rtc->rtc_reg); + + clk = of_clk_get(pdev->dev.of_node, 0); + if (IS_ERR(clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(clk), "failed to find rtc clock\n"); + + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + if (!(rtc_reg_read(rtc, MA35_REG_RTC_INIT) & RTC_INIT_ACTIVE)) { + ret = ma35d1_rtc_init(rtc, RTC_INIT_TIMEOUT); + if (ret) + return dev_err_probe(&pdev->dev, ret, "rtc init failed\n"); + } + + rtc->irq_num = platform_get_irq(pdev, 0); + + ret = devm_request_irq(&pdev->dev, rtc->irq_num, ma35d1_rtc_interrupt, + IRQF_NO_SUSPEND, "ma35d1rtc", rtc); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to request rtc irq\n"); + + platform_set_drvdata(pdev, rtc); + + device_init_wakeup(&pdev->dev, true); + + rtc->rtcdev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtcdev)) + return PTR_ERR(rtc->rtcdev); + + rtc->rtcdev->ops = &ma35d1_rtc_ops; + rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099; + + ret = devm_rtc_register_device(rtc->rtcdev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to register rtc device\n"); + + return 0; +} + +static int ma35d1_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct ma35_rtc *rtc = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(rtc->irq_num); + + return 0; +} + +static int ma35d1_rtc_resume(struct platform_device *pdev) +{ + struct ma35_rtc *rtc = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(rtc->irq_num); + + return 0; +} + +static const struct of_device_id ma35d1_rtc_of_match[] = { + { .compatible = "nuvoton,ma35d1-rtc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ma35d1_rtc_of_match); + +static struct platform_driver ma35d1_rtc_driver = { + .suspend = ma35d1_rtc_suspend, + .resume = ma35d1_rtc_resume, + .probe = ma35d1_rtc_probe, + .driver = { + .name = "rtc-ma35d1", + .of_match_table = ma35d1_rtc_of_match, + }, +}; + +module_platform_driver(ma35d1_rtc_driver); + +MODULE_AUTHOR("Ming-Jen Chen <mjchen@nuvoton.com>"); +MODULE_DESCRIPTION("MA35D1 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-max31335.c b/drivers/rtc/rtc-max31335.c new file mode 100644 index 000000000000..402fda8fd548 --- /dev/null +++ b/drivers/rtc/rtc-max31335.c @@ -0,0 +1,697 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for the MAX31335 + * + * Copyright (C) 2023 Analog Devices + * + * Antoniu Miclaus <antoniu.miclaus@analog.com> + * + */ + +#include <asm-generic/unaligned.h> +#include <linux/bcd.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/rtc.h> +#include <linux/util_macros.h> + +/* MAX31335 Register Map */ +#define MAX31335_STATUS1 0x00 +#define MAX31335_INT_EN1 0x01 +#define MAX31335_STATUS2 0x02 +#define MAX31335_INT_EN2 0x03 +#define MAX31335_RTC_RESET 0x04 +#define MAX31335_RTC_CONFIG 0x05 +#define MAX31335_RTC_CONFIG2 0x06 +#define MAX31335_TIMESTAMP_CONFIG 0x07 +#define MAX31335_TIMER_CONFIG 0x08 +#define MAX31335_SECONDS_1_128 0x09 +#define MAX31335_SECONDS 0x0A +#define MAX31335_MINUTES 0x0B +#define MAX31335_HOURS 0x0C +#define MAX31335_DAY 0x0D +#define MAX31335_DATE 0x0E +#define MAX31335_MONTH 0x0F +#define MAX31335_YEAR 0x0F +#define MAX31335_ALM1_SEC 0x11 +#define MAX31335_ALM1_MIN 0x12 +#define MAX31335_ALM1_HRS 0x13 +#define MAX31335_ALM1_DAY_DATE 0x14 +#define MAX31335_ALM1_MON 0x15 +#define MAX31335_ALM1_YEAR 0x16 +#define MAX31335_ALM2_MIN 0x17 +#define MAX31335_ALM2_HRS 0x18 +#define MAX31335_ALM2_DAY_DATE 0x19 +#define MAX31335_TIMER_COUNT 0x1A +#define MAX31335_TIMER_INIT 0x1B +#define MAX31335_PWR_MGMT 0x1C +#define MAX31335_TRICKLE_REG 0x1D +#define MAX31335_AGING_OFFSET 0x1E +#define MAX31335_TS_CONFIG 0x30 +#define MAX31335_TEMP_ALARM_HIGH_MSB 0x31 +#define MAX31335_TEMP_ALARM_HIGH_LSB 0x32 +#define MAX31335_TEMP_ALARM_LOW_MSB 0x33 +#define MAX31335_TEMP_ALARM_LOW_LSB 0x34 +#define MAX31335_TEMP_DATA_MSB 0x35 +#define MAX31335_TEMP_DATA_LSB 0x36 +#define MAX31335_TS0_SEC_1_128 0x40 +#define MAX31335_TS0_SEC 0x41 +#define MAX31335_TS0_MIN 0x42 +#define MAX31335_TS0_HOUR 0x43 +#define MAX31335_TS0_DATE 0x44 +#define MAX31335_TS0_MONTH 0x45 +#define MAX31335_TS0_YEAR 0x46 +#define MAX31335_TS0_FLAGS 0x47 +#define MAX31335_TS1_SEC_1_128 0x48 +#define MAX31335_TS1_SEC 0x49 +#define MAX31335_TS1_MIN 0x4A +#define MAX31335_TS1_HOUR 0x4B +#define MAX31335_TS1_DATE 0x4C +#define MAX31335_TS1_MONTH 0x4D +#define MAX31335_TS1_YEAR 0x4E +#define MAX31335_TS1_FLAGS 0x4F +#define MAX31335_TS2_SEC_1_128 0x50 +#define MAX31335_TS2_SEC 0x51 +#define MAX31335_TS2_MIN 0x52 +#define MAX31335_TS2_HOUR 0x53 +#define MAX31335_TS2_DATE 0x54 +#define MAX31335_TS2_MONTH 0x55 +#define MAX31335_TS2_YEAR 0x56 +#define MAX31335_TS2_FLAGS 0x57 +#define MAX31335_TS3_SEC_1_128 0x58 +#define MAX31335_TS3_SEC 0x59 +#define MAX31335_TS3_MIN 0x5A +#define MAX31335_TS3_HOUR 0x5B +#define MAX31335_TS3_DATE 0x5C +#define MAX31335_TS3_MONTH 0x5D +#define MAX31335_TS3_YEAR 0x5E +#define MAX31335_TS3_FLAGS 0x5F + +/* MAX31335_STATUS1 Bit Definitions */ +#define MAX31335_STATUS1_PSDECT BIT(7) +#define MAX31335_STATUS1_OSF BIT(6) +#define MAX31335_STATUS1_PFAIL BIT(5) +#define MAX31335_STATUS1_VBATLOW BIT(4) +#define MAX31335_STATUS1_DIF BIT(3) +#define MAX31335_STATUS1_TIF BIT(2) +#define MAX31335_STATUS1_A2F BIT(1) +#define MAX31335_STATUS1_A1F BIT(0) + +/* MAX31335_INT_EN1 Bit Definitions */ +#define MAX31335_INT_EN1_DOSF BIT(6) +#define MAX31335_INT_EN1_PFAILE BIT(5) +#define MAX31335_INT_EN1_VBATLOWE BIT(4) +#define MAX31335_INT_EN1_DIE BIT(3) +#define MAX31335_INT_EN1_TIE BIT(2) +#define MAX31335_INT_EN1_A2IE BIT(1) +#define MAX31335_INT_EN1_A1IE BIT(0) + +/* MAX31335_STATUS2 Bit Definitions */ +#define MAX31335_STATUS2_TEMP_RDY BIT(2) +#define MAX31335_STATUS2_OTF BIT(1) +#define MAX31335_STATUS2_UTF BIT(0) + +/* MAX31335_INT_EN2 Bit Definitions */ +#define MAX31335_INT_EN2_TEMP_RDY_EN BIT(2) +#define MAX31335_INT_EN2_OTIE BIT(1) +#define MAX31335_INT_EN2_UTIE BIT(0) + +/* MAX31335_RTC_RESET Bit Definitions */ +#define MAX31335_RTC_RESET_SWRST BIT(0) + +/* MAX31335_RTC_CONFIG1 Bit Definitions */ +#define MAX31335_RTC_CONFIG1_EN_IO BIT(6) +#define MAX31335_RTC_CONFIG1_A1AC GENMASK(5, 4) +#define MAX31335_RTC_CONFIG1_DIP BIT(3) +#define MAX31335_RTC_CONFIG1_I2C_TIMEOUT BIT(1) +#define MAX31335_RTC_CONFIG1_EN_OSC BIT(0) + +/* MAX31335_RTC_CONFIG2 Bit Definitions */ +#define MAX31335_RTC_CONFIG2_ENCLKO BIT(2) +#define MAX31335_RTC_CONFIG2_CLKO_HZ GENMASK(1, 0) + +/* MAX31335_TIMESTAMP_CONFIG Bit Definitions */ +#define MAX31335_TIMESTAMP_CONFIG_TSVLOW BIT(5) +#define MAX31335_TIMESTAMP_CONFIG_TSPWM BIT(4) +#define MAX31335_TIMESTAMP_CONFIG_TSDIN BIT(3) +#define MAX31335_TIMESTAMP_CONFIG_TSOW BIT(2) +#define MAX31335_TIMESTAMP_CONFIG_TSR BIT(1) +#define MAX31335_TIMESTAMP_CONFIG_TSE BIT(0) + +/* MAX31335_TIMER_CONFIG Bit Definitions */ +#define MAX31335_TIMER_CONFIG_TE BIT(4) +#define MAX31335_TIMER_CONFIG_TPAUSE BIT(3) +#define MAX31335_TIMER_CONFIG_TRPT BIT(2) +#define MAX31335_TIMER_CONFIG_TFS GENMASK(1, 0) + +/* MAX31335_HOURS Bit Definitions */ +#define MAX31335_HOURS_F_24_12 BIT(6) +#define MAX31335_HOURS_HR_20_AM_PM BIT(5) + +/* MAX31335_MONTH Bit Definitions */ +#define MAX31335_MONTH_CENTURY BIT(7) + +/* MAX31335_PWR_MGMT Bit Definitions */ +#define MAX31335_PWR_MGMT_PFVT BIT(0) + +/* MAX31335_TRICKLE_REG Bit Definitions */ +#define MAX31335_TRICKLE_REG_TRICKLE GENMASK(3, 1) +#define MAX31335_TRICKLE_REG_EN_TRICKLE BIT(0) + +/* MAX31335_TS_CONFIG Bit Definitions */ +#define MAX31335_TS_CONFIG_AUTO BIT(4) +#define MAX31335_TS_CONFIG_CONVERT_T BIT(3) +#define MAX31335_TS_CONFIG_TSINT GENMASK(2, 0) + +/* MAX31335_TS_FLAGS Bit Definitions */ +#define MAX31335_TS_FLAGS_VLOWF BIT(3) +#define MAX31335_TS_FLAGS_VBATF BIT(2) +#define MAX31335_TS_FLAGS_VCCF BIT(1) +#define MAX31335_TS_FLAGS_DINF BIT(0) + +/* MAX31335 Miscellaneous Definitions */ +#define MAX31335_TRICKLE_SCHOTTKY_DIODE 1 +#define MAX31335_TRICKLE_STANDARD_DIODE 4 +#define MAX31335_RAM_SIZE 32 +#define MAX31335_TIME_SIZE 0x07 + +#define clk_hw_to_max31335(_hw) container_of(_hw, struct max31335_data, clkout) + +struct max31335_data { + struct regmap *regmap; + struct rtc_device *rtc; + struct clk_hw clkout; +}; + +static const int max31335_clkout_freq[] = { 1, 64, 1024, 32768 }; + +static const u16 max31335_trickle_resistors[] = {3000, 6000, 11000}; + +static bool max31335_volatile_reg(struct device *dev, unsigned int reg) +{ + /* time keeping registers */ + if (reg >= MAX31335_SECONDS && + reg < MAX31335_SECONDS + MAX31335_TIME_SIZE) + return true; + + /* interrupt status register */ + if (reg == MAX31335_INT_EN1_A1IE) + return true; + + /* temperature registers */ + if (reg == MAX31335_TEMP_DATA_MSB || reg == MAX31335_TEMP_DATA_LSB) + return true; + + return false; +} + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x5F, + .volatile_reg = max31335_volatile_reg, +}; + +static int max31335_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + u8 date[7]; + int ret; + + ret = regmap_bulk_read(max31335->regmap, MAX31335_SECONDS, date, + sizeof(date)); + if (ret) + return ret; + + tm->tm_sec = bcd2bin(date[0] & 0x7f); + tm->tm_min = bcd2bin(date[1] & 0x7f); + tm->tm_hour = bcd2bin(date[2] & 0x3f); + tm->tm_wday = bcd2bin(date[3] & 0x7) - 1; + tm->tm_mday = bcd2bin(date[4] & 0x3f); + tm->tm_mon = bcd2bin(date[5] & 0x1f) - 1; + tm->tm_year = bcd2bin(date[6]) + 100; + + if (FIELD_GET(MAX31335_MONTH_CENTURY, date[5])) + tm->tm_year += 100; + + return 0; +} + +static int max31335_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + u8 date[7]; + + date[0] = bin2bcd(tm->tm_sec); + date[1] = bin2bcd(tm->tm_min); + date[2] = bin2bcd(tm->tm_hour); + date[3] = bin2bcd(tm->tm_wday + 1); + date[4] = bin2bcd(tm->tm_mday); + date[5] = bin2bcd(tm->tm_mon + 1); + date[6] = bin2bcd(tm->tm_year % 100); + + if (tm->tm_year >= 200) + date[5] |= FIELD_PREP(MAX31335_MONTH_CENTURY, 1); + + return regmap_bulk_write(max31335->regmap, MAX31335_SECONDS, date, + sizeof(date)); +} + +static int max31335_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + int ret, ctrl, status; + struct rtc_time time; + u8 regs[6]; + + ret = regmap_bulk_read(max31335->regmap, MAX31335_ALM1_SEC, regs, + sizeof(regs)); + if (ret) + return ret; + + alrm->time.tm_sec = bcd2bin(regs[0] & 0x7f); + alrm->time.tm_min = bcd2bin(regs[1] & 0x7f); + alrm->time.tm_hour = bcd2bin(regs[2] & 0x3f); + alrm->time.tm_mday = bcd2bin(regs[3] & 0x3f); + alrm->time.tm_mon = bcd2bin(regs[4] & 0x1f) - 1; + alrm->time.tm_year = bcd2bin(regs[5]) + 100; + + ret = max31335_read_time(dev, &time); + if (ret) + return ret; + + if (time.tm_year >= 200) + alrm->time.tm_year += 100; + + ret = regmap_read(max31335->regmap, MAX31335_INT_EN1, &ctrl); + if (ret) + return ret; + + ret = regmap_read(max31335->regmap, MAX31335_STATUS1, &status); + if (ret) + return ret; + + alrm->enabled = FIELD_GET(MAX31335_INT_EN1_A1IE, ctrl); + alrm->pending = FIELD_GET(MAX31335_STATUS1_A1F, status); + + return 0; +} + +static int max31335_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + unsigned int reg; + u8 regs[6]; + int ret; + + regs[0] = bin2bcd(alrm->time.tm_sec); + regs[1] = bin2bcd(alrm->time.tm_min); + regs[2] = bin2bcd(alrm->time.tm_hour); + regs[3] = bin2bcd(alrm->time.tm_mday); + regs[4] = bin2bcd(alrm->time.tm_mon + 1); + regs[5] = bin2bcd(alrm->time.tm_year % 100); + + ret = regmap_bulk_write(max31335->regmap, MAX31335_ALM1_SEC, + regs, sizeof(regs)); + if (ret) + return ret; + + reg = FIELD_PREP(MAX31335_INT_EN1_A1IE, alrm->enabled); + ret = regmap_update_bits(max31335->regmap, MAX31335_INT_EN1, + MAX31335_INT_EN1_A1IE, reg); + if (ret) + return ret; + + ret = regmap_update_bits(max31335->regmap, MAX31335_STATUS1, + MAX31335_STATUS1_A1F, 0); + + return 0; +} + +static int max31335_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + + return regmap_update_bits(max31335->regmap, MAX31335_INT_EN1, + MAX31335_INT_EN1_A1IE, enabled); +} + +static irqreturn_t max31335_handle_irq(int irq, void *dev_id) +{ + struct max31335_data *max31335 = dev_id; + bool status; + int ret; + + ret = regmap_update_bits_check(max31335->regmap, MAX31335_STATUS1, + MAX31335_STATUS1_A1F, 0, &status); + if (ret) + return IRQ_HANDLED; + + if (status) + rtc_update_irq(max31335->rtc, 1, RTC_AF | RTC_IRQF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops max31335_rtc_ops = { + .read_time = max31335_read_time, + .set_time = max31335_set_time, + .read_alarm = max31335_read_alarm, + .set_alarm = max31335_set_alarm, + .alarm_irq_enable = max31335_alarm_irq_enable, +}; + +static int max31335_trickle_charger_setup(struct device *dev, + struct max31335_data *max31335) +{ + u32 ohms, chargeable; + int i, trickle_cfg; + const char *diode; + + if (device_property_read_u32(dev, "aux-voltage-chargeable", + &chargeable)) + return 0; + + if (device_property_read_u32(dev, "trickle-resistor-ohms", &ohms)) + return 0; + + if (device_property_read_string(dev, "adi,tc-diode", &diode)) + return 0; + + if (!strcmp(diode, "schottky")) + trickle_cfg = MAX31335_TRICKLE_SCHOTTKY_DIODE; + else if (!strcmp(diode, "standard+schottky")) + trickle_cfg = MAX31335_TRICKLE_STANDARD_DIODE; + else + return dev_err_probe(dev, -EINVAL, + "Invalid tc-diode value: %s\n", diode); + + for (i = 0; i < ARRAY_SIZE(max31335_trickle_resistors); i++) + if (ohms == max31335_trickle_resistors[i]) + break; + + if (i >= ARRAY_SIZE(max31335_trickle_resistors)) + return 0; + + i = i + trickle_cfg; + + return regmap_write(max31335->regmap, MAX31335_TRICKLE_REG, + FIELD_PREP(MAX31335_TRICKLE_REG_TRICKLE, i) | + FIELD_PREP(MAX31335_TRICKLE_REG_EN_TRICKLE, + chargeable)); +} + +static unsigned long max31335_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct max31335_data *max31335 = clk_hw_to_max31335(hw); + unsigned int freq_mask; + unsigned int reg; + int ret; + + ret = regmap_read(max31335->regmap, MAX31335_RTC_CONFIG2, ®); + if (ret) + return 0; + + freq_mask = __roundup_pow_of_two(ARRAY_SIZE(max31335_clkout_freq)) - 1; + + return max31335_clkout_freq[reg & freq_mask]; +} + +static long max31335_clkout_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int index; + + index = find_closest(rate, max31335_clkout_freq, + ARRAY_SIZE(max31335_clkout_freq)); + + return max31335_clkout_freq[index]; +} + +static int max31335_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct max31335_data *max31335 = clk_hw_to_max31335(hw); + unsigned int freq_mask; + int index; + + index = find_closest(rate, max31335_clkout_freq, + ARRAY_SIZE(max31335_clkout_freq)); + freq_mask = __roundup_pow_of_two(ARRAY_SIZE(max31335_clkout_freq)) - 1; + + return regmap_update_bits(max31335->regmap, MAX31335_RTC_CONFIG2, + freq_mask, index); +} + +static int max31335_clkout_enable(struct clk_hw *hw) +{ + struct max31335_data *max31335 = clk_hw_to_max31335(hw); + + return regmap_set_bits(max31335->regmap, MAX31335_RTC_CONFIG2, + MAX31335_RTC_CONFIG2_ENCLKO); +} + +static void max31335_clkout_disable(struct clk_hw *hw) +{ + struct max31335_data *max31335 = clk_hw_to_max31335(hw); + + regmap_clear_bits(max31335->regmap, MAX31335_RTC_CONFIG2, + MAX31335_RTC_CONFIG2_ENCLKO); +} + +static int max31335_clkout_is_enabled(struct clk_hw *hw) +{ + struct max31335_data *max31335 = clk_hw_to_max31335(hw); + unsigned int reg; + int ret; + + ret = regmap_read(max31335->regmap, MAX31335_RTC_CONFIG2, ®); + if (ret) + return ret; + + return !!(reg & MAX31335_RTC_CONFIG2_ENCLKO); +} + +static const struct clk_ops max31335_clkout_ops = { + .recalc_rate = max31335_clkout_recalc_rate, + .round_rate = max31335_clkout_round_rate, + .set_rate = max31335_clkout_set_rate, + .enable = max31335_clkout_enable, + .disable = max31335_clkout_disable, + .is_enabled = max31335_clkout_is_enabled, +}; + +static struct clk_init_data max31335_clk_init = { + .name = "max31335-clkout", + .ops = &max31335_clkout_ops, +}; + +static int max31335_nvmem_reg_read(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + struct max31335_data *max31335 = priv; + unsigned int reg = MAX31335_TS0_SEC_1_128 + offset; + + return regmap_bulk_read(max31335->regmap, reg, val, bytes); +} + +static int max31335_nvmem_reg_write(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + struct max31335_data *max31335 = priv; + unsigned int reg = MAX31335_TS0_SEC_1_128 + offset; + + return regmap_bulk_write(max31335->regmap, reg, val, bytes); +} + +static struct nvmem_config max31335_nvmem_cfg = { + .reg_read = max31335_nvmem_reg_read, + .reg_write = max31335_nvmem_reg_write, + .word_size = 8, + .size = MAX31335_RAM_SIZE, +}; + +#if IS_REACHABLE(HWMON) +static int max31335_read_temp(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + u8 reg[2]; + s16 temp; + int ret; + + if (type != hwmon_temp || attr != hwmon_temp_input) + return -EOPNOTSUPP; + + ret = regmap_bulk_read(max31335->regmap, MAX31335_TEMP_DATA_MSB, + reg, 2); + if (ret) + return ret; + + temp = get_unaligned_be16(reg); + + *val = (temp / 64) * 250; + + return 0; +} + +static umode_t max31335_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type == hwmon_temp && attr == hwmon_temp_input) + return 0444; + + return 0; +} + +static const struct hwmon_channel_info *max31335_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops max31335_hwmon_ops = { + .is_visible = max31335_is_visible, + .read = max31335_read_temp, +}; + +static const struct hwmon_chip_info max31335_chip_info = { + .ops = &max31335_hwmon_ops, + .info = max31335_info, +}; +#endif + +static int max31335_clkout_register(struct device *dev) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + int ret; + + if (!device_property_present(dev, "#clock-cells")) + return regmap_clear_bits(max31335->regmap, MAX31335_RTC_CONFIG2, + MAX31335_RTC_CONFIG2_ENCLKO); + + max31335->clkout.init = &max31335_clk_init; + + ret = devm_clk_hw_register(dev, &max31335->clkout); + if (ret) + return dev_err_probe(dev, ret, "cannot register clock\n"); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &max31335->clkout); + if (ret) + return dev_err_probe(dev, ret, "cannot add hw provider\n"); + + max31335->clkout.clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(max31335->clkout.clk)) + return dev_err_probe(dev, PTR_ERR(max31335->clkout.clk), + "cannot enable clkout\n"); + + return 0; +} + +static int max31335_probe(struct i2c_client *client) +{ + struct max31335_data *max31335; +#if IS_REACHABLE(HWMON) + struct device *hwmon; +#endif + int ret; + + max31335 = devm_kzalloc(&client->dev, sizeof(*max31335), GFP_KERNEL); + if (!max31335) + return -ENOMEM; + + max31335->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(max31335->regmap)) + return PTR_ERR(max31335->regmap); + + i2c_set_clientdata(client, max31335); + + max31335->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(max31335->rtc)) + return PTR_ERR(max31335->rtc); + + max31335->rtc->ops = &max31335_rtc_ops; + max31335->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + max31335->rtc->range_max = RTC_TIMESTAMP_END_2199; + max31335->rtc->alarm_offset_max = 24 * 60 * 60; + + ret = max31335_clkout_register(&client->dev); + if (ret) + return ret; + + if (client->irq > 0) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, max31335_handle_irq, + IRQF_ONESHOT, + "max31335", max31335); + if (ret) { + dev_warn(&client->dev, + "unable to request IRQ, alarm max31335 disabled\n"); + client->irq = 0; + } + } + + if (!client->irq) + clear_bit(RTC_FEATURE_ALARM, max31335->rtc->features); + + max31335_nvmem_cfg.priv = max31335; + ret = devm_rtc_nvmem_register(max31335->rtc, &max31335_nvmem_cfg); + if (ret) + return dev_err_probe(&client->dev, ret, + "cannot register rtc nvmem\n"); + +#if IS_REACHABLE(HWMON) + hwmon = devm_hwmon_device_register_with_info(&client->dev, client->name, + max31335, + &max31335_chip_info, + NULL); + if (IS_ERR(hwmon)) + return dev_err_probe(&client->dev, PTR_ERR(hwmon), + "cannot register hwmon device\n"); +#endif + + ret = max31335_trickle_charger_setup(&client->dev, max31335); + if (ret) + return ret; + + return devm_rtc_register_device(max31335->rtc); +} + +static const struct i2c_device_id max31335_id[] = { + { "max31335", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, max31335_id); + +static const struct of_device_id max31335_of_match[] = { + { .compatible = "adi,max31335" }, + { } +}; + +MODULE_DEVICE_TABLE(of, max31335_of_match); + +static struct i2c_driver max31335_driver = { + .driver = { + .name = "rtc-max31335", + .of_match_table = max31335_of_match, + }, + .probe = max31335_probe, + .id_table = max31335_id, +}; +module_i2c_driver(max31335_driver); + +MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>"); +MODULE_DESCRIPTION("MAX31335 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c index f1c09f1db044..651bf3c279c7 100644 --- a/drivers/rtc/rtc-mc146818-lib.c +++ b/drivers/rtc/rtc-mc146818-lib.c @@ -8,26 +8,31 @@ #include <linux/acpi.h> #endif +#define UIP_RECHECK_DELAY 100 /* usec */ +#define UIP_RECHECK_DELAY_MS (USEC_PER_MSEC / UIP_RECHECK_DELAY) +#define UIP_RECHECK_LOOPS_MS(x) (x / UIP_RECHECK_DELAY_MS) + /* * Execute a function while the UIP (Update-in-progress) bit of the RTC is - * unset. + * unset. The timeout is configurable by the caller in ms. * * Warning: callback may be executed more then once. */ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), + int timeout, void *param) { int i; unsigned long flags; unsigned char seconds; - for (i = 0; i < 100; i++) { + for (i = 0; UIP_RECHECK_LOOPS_MS(i) < timeout; i++) { spin_lock_irqsave(&rtc_lock, flags); /* * Check whether there is an update in progress during which the * readout is unspecified. The maximum update time is ~2ms. Poll - * every 100 usec for completion. + * for completion. * * Store the second value before checking UIP so a long lasting * NMI which happens to hit after the UIP check cannot make @@ -37,7 +42,7 @@ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { spin_unlock_irqrestore(&rtc_lock, flags); - udelay(100); + udelay(UIP_RECHECK_DELAY); continue; } @@ -56,7 +61,7 @@ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), */ if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { spin_unlock_irqrestore(&rtc_lock, flags); - udelay(100); + udelay(UIP_RECHECK_DELAY); continue; } @@ -72,6 +77,10 @@ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), } spin_unlock_irqrestore(&rtc_lock, flags); + if (UIP_RECHECK_LOOPS_MS(i) >= 100) + pr_warn("Reading current time from RTC took around %li ms\n", + UIP_RECHECK_LOOPS_MS(i)); + return true; } return false; @@ -84,7 +93,7 @@ EXPORT_SYMBOL_GPL(mc146818_avoid_UIP); */ bool mc146818_does_rtc_work(void) { - return mc146818_avoid_UIP(NULL, NULL); + return mc146818_avoid_UIP(NULL, 1000, NULL); } EXPORT_SYMBOL_GPL(mc146818_does_rtc_work); @@ -130,15 +139,27 @@ static void mc146818_get_time_callback(unsigned char seconds, void *param_in) p->ctrl = CMOS_READ(RTC_CONTROL); } -int mc146818_get_time(struct rtc_time *time) +/** + * mc146818_get_time - Get the current time from the RTC + * @time: pointer to struct rtc_time to store the current time + * @timeout: timeout value in ms + * + * This function reads the current time from the RTC and stores it in the + * provided struct rtc_time. The timeout parameter specifies the maximum + * time to wait for the RTC to become ready. + * + * Return: 0 on success, -ETIMEDOUT if the RTC did not become ready within + * the specified timeout, or another error code if an error occurred. + */ +int mc146818_get_time(struct rtc_time *time, int timeout) { struct mc146818_get_time_callback_param p = { .time = time }; - if (!mc146818_avoid_UIP(mc146818_get_time_callback, &p)) { + if (!mc146818_avoid_UIP(mc146818_get_time_callback, timeout, &p)) { memset(time, 0, sizeof(*time)); - return -EIO; + return -ETIMEDOUT; } if (!(p.ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) diff --git a/drivers/rtc/rtc-nct3018y.c b/drivers/rtc/rtc-nct3018y.c index ed4e606be8e5..f488a189a465 100644 --- a/drivers/rtc/rtc-nct3018y.c +++ b/drivers/rtc/rtc-nct3018y.c @@ -23,6 +23,7 @@ #define NCT3018Y_REG_CTRL 0x0A /* timer control */ #define NCT3018Y_REG_ST 0x0B /* status */ #define NCT3018Y_REG_CLKO 0x0C /* clock out */ +#define NCT3018Y_REG_PART 0x21 /* part info */ #define NCT3018Y_BIT_AF BIT(7) #define NCT3018Y_BIT_ST BIT(7) @@ -37,10 +38,12 @@ #define NCT3018Y_REG_BAT_MASK 0x07 #define NCT3018Y_REG_CLKO_F_MASK 0x03 /* frequenc mask */ #define NCT3018Y_REG_CLKO_CKE 0x80 /* clock out enabled */ +#define NCT3018Y_REG_PART_NCT3018Y 0x02 struct nct3018y { struct rtc_device *rtc; struct i2c_client *client; + int part_num; #ifdef CONFIG_COMMON_CLK struct clk_hw clkout_hw; #endif @@ -177,8 +180,27 @@ static int nct3018y_rtc_read_time(struct device *dev, struct rtc_time *tm) static int nct3018y_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct i2c_client *client = to_i2c_client(dev); + struct nct3018y *nct3018y = dev_get_drvdata(dev); unsigned char buf[4] = {0}; - int err; + int err, flags; + int restore_flags = 0; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL); + if (flags < 0) { + dev_dbg(&client->dev, "Failed to read NCT3018Y_REG_CTRL.\n"); + return flags; + } + + /* Check and set TWO bit */ + if (nct3018y->part_num == NCT3018Y_REG_PART_NCT3018Y && !(flags & NCT3018Y_BIT_TWO)) { + restore_flags = 1; + flags |= NCT3018Y_BIT_TWO; + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL.\n"); + return err; + } + } buf[0] = bin2bcd(tm->tm_sec); err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_SC, buf[0]); @@ -212,6 +234,18 @@ static int nct3018y_rtc_set_time(struct device *dev, struct rtc_time *tm) return -EIO; } + /* Restore TWO bit */ + if (restore_flags) { + if (nct3018y->part_num == NCT3018Y_REG_PART_NCT3018Y) + flags &= ~NCT3018Y_BIT_TWO; + + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL.\n"); + return err; + } + } + return err; } @@ -479,11 +513,17 @@ static int nct3018y_probe(struct i2c_client *client) dev_dbg(&client->dev, "%s: NCT3018Y_BIT_TWO is set\n", __func__); } - flags = NCT3018Y_BIT_TWO; - err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags); - if (err < 0) { - dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL\n"); - return err; + nct3018y->part_num = i2c_smbus_read_byte_data(client, NCT3018Y_REG_PART); + if (nct3018y->part_num < 0) { + dev_dbg(&client->dev, "Failed to read NCT3018Y_REG_PART.\n"); + return nct3018y->part_num; + } else if (nct3018y->part_num == NCT3018Y_REG_PART_NCT3018Y) { + flags = NCT3018Y_BIT_HF; + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL.\n"); + return err; + } } flags = 0; diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index 1a3ec1bb5b81..1327251e527c 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/rtc.h> +#include <linux/pm_wakeirq.h> #define RV8803_I2C_TRY_COUNT 4 @@ -607,6 +608,28 @@ static int rv8803_regs_configure(struct rv8803_data *rv8803) return 0; } +static int rv8803_resume(struct device *dev) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + + if (rv8803->client->irq > 0 && device_may_wakeup(dev)) + disable_irq_wake(rv8803->client->irq); + + return 0; +} + +static int rv8803_suspend(struct device *dev) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + + if (rv8803->client->irq > 0 && device_may_wakeup(dev)) + enable_irq_wake(rv8803->client->irq); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(rv8803_pm_ops, rv8803_suspend, rv8803_resume); + static const struct i2c_device_id rv8803_id[] = { { "rv8803", rv_8803 }, { "rv8804", rx_8804 }, @@ -683,10 +706,18 @@ static int rv8803_probe(struct i2c_client *client) if (err) { dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); client->irq = 0; + } else { + device_init_wakeup(&client->dev, true); + err = dev_pm_set_wake_irq(&client->dev, client->irq); + if (err) + dev_err(&client->dev, "failed to set wake IRQ\n"); } + } else { + if (device_property_read_bool(&client->dev, "wakeup-source")) + device_init_wakeup(&client->dev, true); + else + clear_bit(RTC_FEATURE_ALARM, rv8803->rtc->features); } - if (!client->irq) - clear_bit(RTC_FEATURE_ALARM, rv8803->rtc->features); if (of_property_read_bool(client->dev.of_node, "epson,vdet-disable")) rv8803->backup |= RX8900_FLAG_VDETOFF; @@ -737,6 +768,7 @@ static struct i2c_driver rv8803_driver = { .driver = { .name = "rtc-rv8803", .of_match_table = of_match_ptr(rv8803_of_match), + .pm = &rv8803_pm_ops, }, .probe = rv8803_probe, .id_table = rv8803_id, diff --git a/drivers/rtc/rtc-tps6594.c b/drivers/rtc/rtc-tps6594.c new file mode 100644 index 000000000000..838ae8562a35 --- /dev/null +++ b/drivers/rtc/rtc-tps6594.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for tps6594 PMIC + * + * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ + */ + +#include <linux/bcd.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/limits.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> +#include <linux/rtc.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <linux/mfd/tps6594.h> + +// Total number of RTC registers needed to set time +#define NUM_TIME_REGS (TPS6594_REG_RTC_WEEKS - TPS6594_REG_RTC_SECONDS + 1) + +// Total number of RTC alarm registers +#define NUM_TIME_ALARM_REGS (NUM_TIME_REGS - 1) + +/* + * Min and max values supported by 'offset' interface (swapped sign). + * After conversion, the values do not exceed the range [-32767, 33767] + * which COMP_REG must conform to. + */ +#define MIN_OFFSET (-277774) +#define MAX_OFFSET (277774) + +// Number of ticks per hour +#define TICKS_PER_HOUR (32768 * 3600) + +// Multiplier for ppb conversions +#define PPB_MULT NANO + +static int tps6594_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct tps6594 *tps = dev_get_drvdata(dev->parent); + u8 val; + + val = enabled ? TPS6594_BIT_IT_ALARM : 0; + + return regmap_update_bits(tps->regmap, TPS6594_REG_RTC_INTERRUPTS, + TPS6594_BIT_IT_ALARM, val); +} + +/* Pulse GET_TIME field of RTC_CTRL_1 to store a timestamp in shadow registers. */ +static int tps6594_rtc_shadow_timestamp(struct device *dev, struct tps6594 *tps) +{ + int ret; + + /* + * Set GET_TIME to 0. Next time we set GET_TIME to 1 we will be sure to store + * an up-to-date timestamp. + */ + ret = regmap_clear_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_GET_TIME); + if (ret < 0) + return ret; + + /* + * Copy content of RTC registers to shadow registers or latches to read + * a coherent timestamp. + */ + return regmap_set_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_GET_TIME); +} + +static int tps6594_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[NUM_TIME_REGS]; + struct tps6594 *tps = dev_get_drvdata(dev->parent); + int ret; + + // Check if RTC is running. + ret = regmap_test_bits(tps->regmap, TPS6594_REG_RTC_STATUS, + TPS6594_BIT_RUN); + if (ret < 0) + return ret; + if (ret == 0) + return -EINVAL; + + ret = tps6594_rtc_shadow_timestamp(dev, tps); + if (ret < 0) + return ret; + + // Read shadowed RTC registers. + ret = regmap_bulk_read(tps->regmap, TPS6594_REG_RTC_SECONDS, rtc_data, + NUM_TIME_REGS); + if (ret < 0) + return ret; + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]); + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + tm->tm_wday = bcd2bin(rtc_data[6]); + + return 0; +} + +static int tps6594_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[NUM_TIME_REGS]; + struct tps6594 *tps = dev_get_drvdata(dev->parent); + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + rtc_data[6] = bin2bcd(tm->tm_wday); + + // Stop RTC while updating the RTC time registers. + ret = regmap_clear_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_STOP_RTC); + if (ret < 0) + return ret; + + // Update all the time registers in one shot. + ret = regmap_bulk_write(tps->regmap, TPS6594_REG_RTC_SECONDS, rtc_data, + NUM_TIME_REGS); + if (ret < 0) + return ret; + + // Start back RTC. + return regmap_set_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_STOP_RTC); +} + +static int tps6594_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[NUM_TIME_ALARM_REGS]; + u32 int_val; + struct tps6594 *tps = dev_get_drvdata(dev->parent); + int ret; + + ret = regmap_bulk_read(tps->regmap, TPS6594_REG_ALARM_SECONDS, + alarm_data, NUM_TIME_ALARM_REGS); + if (ret < 0) + return ret; + + alm->time.tm_sec = bcd2bin(alarm_data[0]); + alm->time.tm_min = bcd2bin(alarm_data[1]); + alm->time.tm_hour = bcd2bin(alarm_data[2]); + alm->time.tm_mday = bcd2bin(alarm_data[3]); + alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1; + alm->time.tm_year = bcd2bin(alarm_data[5]) + 100; + + ret = regmap_read(tps->regmap, TPS6594_REG_RTC_INTERRUPTS, &int_val); + if (ret < 0) + return ret; + + alm->enabled = int_val & TPS6594_BIT_IT_ALARM; + + return 0; +} + +static int tps6594_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[NUM_TIME_ALARM_REGS]; + struct tps6594 *tps = dev_get_drvdata(dev->parent); + int ret; + + // Disable alarm irq before changing the alarm timestamp. + ret = tps6594_rtc_alarm_irq_enable(dev, 0); + if (ret) + return ret; + + alarm_data[0] = bin2bcd(alm->time.tm_sec); + alarm_data[1] = bin2bcd(alm->time.tm_min); + alarm_data[2] = bin2bcd(alm->time.tm_hour); + alarm_data[3] = bin2bcd(alm->time.tm_mday); + alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); + alarm_data[5] = bin2bcd(alm->time.tm_year - 100); + + // Update all the alarm registers in one shot. + ret = regmap_bulk_write(tps->regmap, TPS6594_REG_ALARM_SECONDS, + alarm_data, NUM_TIME_ALARM_REGS); + if (ret < 0) + return ret; + + if (alm->enabled) + ret = tps6594_rtc_alarm_irq_enable(dev, 1); + + return ret; +} + +static int tps6594_rtc_set_calibration(struct device *dev, int calibration) +{ + struct tps6594 *tps = dev_get_drvdata(dev->parent); + __le16 value; + int ret; + + /* + * TPS6594 uses two's complement 16 bit value for compensation of RTC + * crystal inaccuracies. One time every hour when seconds counter + * increments from 0 to 1 compensation value will be added to internal + * RTC counter value. + * + * Valid range for compensation value: [-32767 .. 32767]. + */ + if (calibration < S16_MIN + 1 || calibration > S16_MAX) + return -ERANGE; + + value = cpu_to_le16(calibration); + + // Update all the compensation registers in one shot. + ret = regmap_bulk_write(tps->regmap, TPS6594_REG_RTC_COMP_LSB, &value, + sizeof(value)); + if (ret < 0) + return ret; + + // Enable automatic compensation. + return regmap_set_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_AUTO_COMP); +} + +static int tps6594_rtc_get_calibration(struct device *dev, int *calibration) +{ + struct tps6594 *tps = dev_get_drvdata(dev->parent); + unsigned int ctrl; + __le16 value; + int ret; + + ret = regmap_read(tps->regmap, TPS6594_REG_RTC_CTRL_1, &ctrl); + if (ret < 0) + return ret; + + // If automatic compensation is not enabled report back zero. + if (!(ctrl & TPS6594_BIT_AUTO_COMP)) { + *calibration = 0; + return 0; + } + + ret = regmap_bulk_read(tps->regmap, TPS6594_REG_RTC_COMP_LSB, &value, + sizeof(value)); + if (ret < 0) + return ret; + + *calibration = le16_to_cpu(value); + + return 0; +} + +static int tps6594_rtc_read_offset(struct device *dev, long *offset) +{ + int calibration; + s64 tmp; + int ret; + + ret = tps6594_rtc_get_calibration(dev, &calibration); + if (ret < 0) + return ret; + + // Convert from RTC calibration register format to ppb format. + tmp = calibration * PPB_MULT; + + if (tmp < 0) + tmp -= TICKS_PER_HOUR / 2LL; + else + tmp += TICKS_PER_HOUR / 2LL; + tmp = div_s64(tmp, TICKS_PER_HOUR); + + /* + * SAFETY: + * Computatiion is the reverse operation of the one done in + * `tps6594_rtc_set_offset`. The safety remarks applie here too. + */ + + /* + * Offset value operates in negative way, so swap sign. + * See 8.3.10.5, (32768 - COMP_REG). + */ + *offset = (long)-tmp; + + return 0; +} + +static int tps6594_rtc_set_offset(struct device *dev, long offset) +{ + int calibration; + s64 tmp; + + // Make sure offset value is within supported range. + if (offset < MIN_OFFSET || offset > MAX_OFFSET) + return -ERANGE; + + // Convert from ppb format to RTC calibration register format. + + tmp = offset * TICKS_PER_HOUR; + if (tmp < 0) + tmp -= PPB_MULT / 2LL; + else + tmp += PPB_MULT / 2LL; + tmp = div_s64(tmp, PPB_MULT); + + /* + * SAFETY: + * - tmp = offset * TICK_PER_HOUR : + * `offset` can't be more than 277774, so `tmp` can't exceed 277774000000000 + * which is lower than the maximum value in an `s64` (2^63-1). No overflow here. + * + * - tmp += TICK_PER_HOUR / 2LL : + * tmp will have a maximum value of 277774117964800 which is still inferior to 2^63-1. + */ + + // Offset value operates in negative way, so swap sign. + calibration = (int)-tmp; + + return tps6594_rtc_set_calibration(dev, calibration); +} + +static irqreturn_t tps6594_rtc_interrupt(int irq, void *rtc) +{ + struct device *dev = rtc; + struct tps6594 *tps = dev_get_drvdata(dev->parent); + struct rtc_device *rtc_dev = dev_get_drvdata(dev); + int ret; + u32 rtc_reg; + + ret = regmap_read(tps->regmap, TPS6594_REG_RTC_STATUS, &rtc_reg); + if (ret) + return IRQ_NONE; + + rtc_update_irq(rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops tps6594_rtc_ops = { + .read_time = tps6594_rtc_read_time, + .set_time = tps6594_rtc_set_time, + .read_alarm = tps6594_rtc_read_alarm, + .set_alarm = tps6594_rtc_set_alarm, + .alarm_irq_enable = tps6594_rtc_alarm_irq_enable, + .read_offset = tps6594_rtc_read_offset, + .set_offset = tps6594_rtc_set_offset, +}; + +static int tps6594_rtc_probe(struct platform_device *pdev) +{ + struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct rtc_device *rtc; + int irq; + int ret; + + rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + // Enable crystal oscillator. + ret = regmap_set_bits(tps->regmap, TPS6594_REG_RTC_CTRL_2, + TPS6594_BIT_XTAL_EN); + if (ret < 0) + return ret; + + ret = regmap_test_bits(tps->regmap, TPS6594_REG_RTC_STATUS, + TPS6594_BIT_RUN); + if (ret < 0) + return ret; + // RTC not running. + if (ret == 0) { + ret = regmap_set_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_STOP_RTC); + if (ret < 0) + return ret; + + /* + * On some boards, a 40 ms delay is needed before BIT_RUN is set. + * 80 ms should provide sufficient margin. + */ + mdelay(80); + + /* + * RTC should be running now. Check if this is the case. + * If not it might be a missing oscillator. + */ + ret = regmap_test_bits(tps->regmap, TPS6594_REG_RTC_STATUS, + TPS6594_BIT_RUN); + if (ret < 0) + return ret; + if (ret == 0) + return -ENODEV; + + // Stop RTC until first call to `tps6594_rtc_set_time`. + ret = regmap_clear_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_STOP_RTC); + if (ret < 0) + return ret; + } + + platform_set_drvdata(pdev, rtc); + + irq = platform_get_irq_byname(pdev, TPS6594_IRQ_NAME_ALARM); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get irq\n"); + + ret = devm_request_threaded_irq(dev, irq, NULL, tps6594_rtc_interrupt, + IRQF_ONESHOT, TPS6594_IRQ_NAME_ALARM, + dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to request_threaded_irq\n"); + + ret = device_init_wakeup(dev, true); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to init rtc as wakeup source\n"); + + rtc->ops = &tps6594_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + + return devm_rtc_register_device(rtc); +} + +static const struct platform_device_id tps6594_rtc_id_table[] = { + { "tps6594-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(platform, tps6594_rtc_id_table); + +static struct platform_driver tps6594_rtc_driver = { + .probe = tps6594_rtc_probe, + .driver = { + .name = "tps6594-rtc", + }, + .id_table = tps6594_rtc_id_table, +}; + +module_platform_driver(tps6594_rtc_driver); +MODULE_AUTHOR("Esteban Blanc <eblanc@baylibre.com>"); +MODULE_DESCRIPTION("TPS6594 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 99361618c31f..0b0324fe4aff 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -79,8 +79,8 @@ struct raw3215_info { struct ccw_device *cdev; /* device for tty driver */ spinlock_t *lock; /* pointer to irq lock */ int flags; /* state flags */ - char *buffer; /* pointer to output buffer */ - char *inbuf; /* pointer to input buffer */ + u8 *buffer; /* pointer to output buffer */ + u8 *inbuf; /* pointer to input buffer */ int head; /* first free byte in output buffer */ int count; /* number of bytes in output buffer */ int written; /* number of bytes in write requests */ @@ -89,7 +89,6 @@ struct raw3215_info { wait_queue_head_t empty_wait; /* wait queue for flushing */ struct timer_list timer; /* timer for delayed output */ int line_pos; /* position on the line (for tabs) */ - char ubuffer[80]; /* copy_from_user buffer */ }; /* array of 3215 devices structures */ @@ -523,12 +522,14 @@ static unsigned int raw3215_make_room(struct raw3215_info *raw, * string without blocking. * Return value is the number of bytes copied. */ -static unsigned int raw3215_addtext(const char *str, unsigned int length, +static unsigned int raw3215_addtext(const u8 *str, size_t length, struct raw3215_info *raw, int opmode, unsigned int todrop) { - unsigned int c, ch, i, blanks, expanded_size = 0; + unsigned int i, blanks, expanded_size = 0; unsigned int column = raw->line_pos; + size_t c; + u8 ch; if (opmode == RAW3215_COUNT) todrop = 0; @@ -559,7 +560,7 @@ static unsigned int raw3215_addtext(const char *str, unsigned int length, if (todrop && expanded_size < todrop) /* Drop head data */ continue; for (i = 0; i < blanks; i++) { - raw->buffer[raw->head] = (char)_ascebc[(int)ch]; + raw->buffer[raw->head] = _ascebc[ch]; raw->head = (raw->head + 1) & (RAW3215_BUFFER_SIZE - 1); raw->count++; } @@ -571,8 +572,8 @@ static unsigned int raw3215_addtext(const char *str, unsigned int length, /* * String write routine for 3215 devices */ -static void raw3215_write(struct raw3215_info *raw, const char *str, - unsigned int length) +static void raw3215_write(struct raw3215_info *raw, const u8 *str, + size_t length) { unsigned int count, avail; unsigned long flags; @@ -597,7 +598,7 @@ static void raw3215_write(struct raw3215_info *raw, const char *str, /* * Put character routine for 3215 devices */ -static void raw3215_putchar(struct raw3215_info *raw, unsigned char ch) +static void raw3215_putchar(struct raw3215_info *raw, u8 ch) { raw3215_write(raw, &ch, 1); } @@ -824,12 +825,10 @@ static struct ccw_driver raw3215_ccw_driver = { .int_class = IRQIO_C15, }; -static void handle_write(struct raw3215_info *raw, const char *str, int count) +static void handle_write(struct raw3215_info *raw, const u8 *str, size_t count) { - int i; - while (count > 0) { - i = min_t(int, count, RAW3215_BUFFER_SIZE - 1); + size_t i = min_t(size_t, count, RAW3215_BUFFER_SIZE - 1); raw3215_write(raw, str, i); count -= i; str += i; diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 363315fa1666..251d2a1c3eef 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -54,7 +54,7 @@ struct tty3270_attribute { }; struct tty3270_cell { - unsigned char character; + u8 character; struct tty3270_attribute attributes; }; @@ -123,7 +123,7 @@ struct tty3270 { /* Character array for put_char/flush_chars. */ unsigned int char_count; - char char_buf[TTY3270_CHAR_BUF_SIZE]; + u8 char_buf[TTY3270_CHAR_BUF_SIZE]; }; /* tty3270->update_flags. See tty3270_update for details. */ @@ -1255,7 +1255,7 @@ static unsigned int tty3270_write_room(struct tty_struct *tty) * Insert character into the screen at the current position with the * current color and highlight. This function does NOT do cursor movement. */ -static void tty3270_put_character(struct tty3270 *tp, char ch) +static void tty3270_put_character(struct tty3270 *tp, u8 ch) { struct tty3270_line *line; struct tty3270_cell *cell; @@ -1561,7 +1561,7 @@ static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) * Pn is a numeric parameter, a string of zero or more decimal digits. * Ps is a selective parameter. */ -static void tty3270_escape_sequence(struct tty3270 *tp, char ch) +static void tty3270_escape_sequence(struct tty3270 *tp, u8 ch) { enum { ES_NORMAL, ES_ESC, ES_SQUARE, ES_PAREN, ES_GETPARS }; @@ -1726,7 +1726,7 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) * String write routine for 3270 ttys */ static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, - const unsigned char *buf, int count) + const u8 *buf, size_t count) { int i_msg, i; @@ -2052,7 +2052,7 @@ con3270_write(struct console *co, const char *str, unsigned int count) { struct tty3270 *tp = co->data; unsigned long flags; - char c; + u8 c; spin_lock_irqsave(&tp->view.lock, flags); while (count--) { diff --git a/drivers/s390/char/uvdevice.c b/drivers/s390/char/uvdevice.c index 144cd2e03590..42c9f77f8da0 100644 --- a/drivers/s390/char/uvdevice.c +++ b/drivers/s390/char/uvdevice.c @@ -109,6 +109,7 @@ static int uvio_copy_attest_result_to_user(struct uv_cb_attest *uvcb_attest, struct uvio_attest *uvio_attest) { struct uvio_attest __user *user_uvio_attest = (void __user *)uv_ioctl->argument_addr; + u32 __user *user_buf_add_len = (u32 __user *)&user_uvio_attest->add_data_len; void __user *user_buf_add = (void __user *)uvio_attest->add_data_addr; void __user *user_buf_meas = (void __user *)uvio_attest->meas_addr; void __user *user_buf_uid = &user_uvio_attest->config_uid; @@ -117,6 +118,8 @@ static int uvio_copy_attest_result_to_user(struct uv_cb_attest *uvcb_attest, return -EFAULT; if (add_data && copy_to_user(user_buf_add, add_data, uvio_attest->add_data_len)) return -EFAULT; + if (put_user(uvio_attest->add_data_len, user_buf_add_len)) + return -EFAULT; if (copy_to_user(user_buf_uid, uvcb_attest->config_uid, sizeof(uvcb_attest->config_uid))) return -EFAULT; return 0; diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index acb710d3d7bc..983b3b16196c 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -32,7 +32,8 @@ #define AP_RESET_INTERVAL 20 /* Reset sleep interval (20ms) */ -static int vfio_ap_mdev_reset_queues(struct ap_queue_table *qtable); +static int vfio_ap_mdev_reset_queues(struct ap_matrix_mdev *matrix_mdev); +static int vfio_ap_mdev_reset_qlist(struct list_head *qlist); static struct vfio_ap_queue *vfio_ap_find_queue(int apqn); static const struct vfio_device_ops vfio_ap_matrix_dev_ops; static void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q); @@ -665,17 +666,23 @@ static bool vfio_ap_mdev_filter_cdoms(struct ap_matrix_mdev *matrix_mdev) * device driver. * * @matrix_mdev: the matrix mdev whose matrix is to be filtered. + * @apm_filtered: a 256-bit bitmap for storing the APIDs filtered from the + * guest's AP configuration that are still in the host's AP + * configuration. * * Note: If an APQN referencing a queue device that is not bound to the vfio_ap * driver, its APID will be filtered from the guest's APCB. The matrix * structure precludes filtering an individual APQN, so its APID will be - * filtered. + * filtered. Consequently, all queues associated with the adapter that + * are in the host's AP configuration must be reset. If queues are + * subsequently made available again to the guest, they should re-appear + * in a reset state * * Return: a boolean value indicating whether the KVM guest's APCB was changed * by the filtering or not. */ -static bool vfio_ap_mdev_filter_matrix(unsigned long *apm, unsigned long *aqm, - struct ap_matrix_mdev *matrix_mdev) +static bool vfio_ap_mdev_filter_matrix(struct ap_matrix_mdev *matrix_mdev, + unsigned long *apm_filtered) { unsigned long apid, apqi, apqn; DECLARE_BITMAP(prev_shadow_apm, AP_DEVICES); @@ -685,6 +692,7 @@ static bool vfio_ap_mdev_filter_matrix(unsigned long *apm, unsigned long *aqm, bitmap_copy(prev_shadow_apm, matrix_mdev->shadow_apcb.apm, AP_DEVICES); bitmap_copy(prev_shadow_aqm, matrix_mdev->shadow_apcb.aqm, AP_DOMAINS); vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->shadow_apcb); + bitmap_clear(apm_filtered, 0, AP_DEVICES); /* * Copy the adapters, domains and control domains to the shadow_apcb @@ -696,8 +704,9 @@ static bool vfio_ap_mdev_filter_matrix(unsigned long *apm, unsigned long *aqm, bitmap_and(matrix_mdev->shadow_apcb.aqm, matrix_mdev->matrix.aqm, (unsigned long *)matrix_dev->info.aqm, AP_DOMAINS); - for_each_set_bit_inv(apid, apm, AP_DEVICES) { - for_each_set_bit_inv(apqi, aqm, AP_DOMAINS) { + for_each_set_bit_inv(apid, matrix_mdev->shadow_apcb.apm, AP_DEVICES) { + for_each_set_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm, + AP_DOMAINS) { /* * If the APQN is not bound to the vfio_ap device * driver, then we can't assign it to the guest's @@ -709,8 +718,16 @@ static bool vfio_ap_mdev_filter_matrix(unsigned long *apm, unsigned long *aqm, apqn = AP_MKQID(apid, apqi); q = vfio_ap_mdev_get_queue(matrix_mdev, apqn); if (!q || q->reset_status.response_code) { - clear_bit_inv(apid, - matrix_mdev->shadow_apcb.apm); + clear_bit_inv(apid, matrix_mdev->shadow_apcb.apm); + + /* + * If the adapter was previously plugged into + * the guest, let's let the caller know that + * the APID was filtered. + */ + if (test_bit_inv(apid, prev_shadow_apm)) + set_bit_inv(apid, apm_filtered); + break; } } @@ -812,7 +829,7 @@ static void vfio_ap_mdev_remove(struct mdev_device *mdev) mutex_lock(&matrix_dev->guests_lock); mutex_lock(&matrix_dev->mdevs_lock); - vfio_ap_mdev_reset_queues(&matrix_mdev->qtable); + vfio_ap_mdev_reset_queues(matrix_mdev); vfio_ap_mdev_unlink_fr_queues(matrix_mdev); list_del(&matrix_mdev->node); mutex_unlock(&matrix_dev->mdevs_lock); @@ -922,6 +939,47 @@ static void vfio_ap_mdev_link_adapter(struct ap_matrix_mdev *matrix_mdev, AP_MKQID(apid, apqi)); } +static void collect_queues_to_reset(struct ap_matrix_mdev *matrix_mdev, + unsigned long apid, + struct list_head *qlist) +{ + struct vfio_ap_queue *q; + unsigned long apqi; + + for_each_set_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm, AP_DOMAINS) { + q = vfio_ap_mdev_get_queue(matrix_mdev, AP_MKQID(apid, apqi)); + if (q) + list_add_tail(&q->reset_qnode, qlist); + } +} + +static void reset_queues_for_apid(struct ap_matrix_mdev *matrix_mdev, + unsigned long apid) +{ + struct list_head qlist; + + INIT_LIST_HEAD(&qlist); + collect_queues_to_reset(matrix_mdev, apid, &qlist); + vfio_ap_mdev_reset_qlist(&qlist); +} + +static int reset_queues_for_apids(struct ap_matrix_mdev *matrix_mdev, + unsigned long *apm_reset) +{ + struct list_head qlist; + unsigned long apid; + + if (bitmap_empty(apm_reset, AP_DEVICES)) + return 0; + + INIT_LIST_HEAD(&qlist); + + for_each_set_bit_inv(apid, apm_reset, AP_DEVICES) + collect_queues_to_reset(matrix_mdev, apid, &qlist); + + return vfio_ap_mdev_reset_qlist(&qlist); +} + /** * assign_adapter_store - parses the APID from @buf and sets the * corresponding bit in the mediated matrix device's APM @@ -962,7 +1020,7 @@ static ssize_t assign_adapter_store(struct device *dev, { int ret; unsigned long apid; - DECLARE_BITMAP(apm_delta, AP_DEVICES); + DECLARE_BITMAP(apm_filtered, AP_DEVICES); struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev); mutex_lock(&ap_perms_mutex); @@ -991,12 +1049,11 @@ static ssize_t assign_adapter_store(struct device *dev, } vfio_ap_mdev_link_adapter(matrix_mdev, apid); - memset(apm_delta, 0, sizeof(apm_delta)); - set_bit_inv(apid, apm_delta); - if (vfio_ap_mdev_filter_matrix(apm_delta, - matrix_mdev->matrix.aqm, matrix_mdev)) + if (vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered)) { vfio_ap_mdev_update_guest_apcb(matrix_mdev); + reset_queues_for_apids(matrix_mdev, apm_filtered); + } ret = count; done: @@ -1027,11 +1084,12 @@ static struct vfio_ap_queue * adapter was assigned. * @matrix_mdev: the matrix mediated device to which the adapter was assigned. * @apid: the APID of the unassigned adapter. - * @qtable: table for storing queues associated with unassigned adapter. + * @qlist: list for storing queues associated with unassigned adapter that + * need to be reset. */ static void vfio_ap_mdev_unlink_adapter(struct ap_matrix_mdev *matrix_mdev, unsigned long apid, - struct ap_queue_table *qtable) + struct list_head *qlist) { unsigned long apqi; struct vfio_ap_queue *q; @@ -1039,11 +1097,10 @@ static void vfio_ap_mdev_unlink_adapter(struct ap_matrix_mdev *matrix_mdev, for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm, AP_DOMAINS) { q = vfio_ap_unlink_apqn_fr_mdev(matrix_mdev, apid, apqi); - if (q && qtable) { + if (q && qlist) { if (test_bit_inv(apid, matrix_mdev->shadow_apcb.apm) && test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm)) - hash_add(qtable->queues, &q->mdev_qnode, - q->apqn); + list_add_tail(&q->reset_qnode, qlist); } } } @@ -1051,26 +1108,23 @@ static void vfio_ap_mdev_unlink_adapter(struct ap_matrix_mdev *matrix_mdev, static void vfio_ap_mdev_hot_unplug_adapter(struct ap_matrix_mdev *matrix_mdev, unsigned long apid) { - int loop_cursor; - struct vfio_ap_queue *q; - struct ap_queue_table *qtable = kzalloc(sizeof(*qtable), GFP_KERNEL); + struct vfio_ap_queue *q, *tmpq; + struct list_head qlist; - hash_init(qtable->queues); - vfio_ap_mdev_unlink_adapter(matrix_mdev, apid, qtable); + INIT_LIST_HEAD(&qlist); + vfio_ap_mdev_unlink_adapter(matrix_mdev, apid, &qlist); if (test_bit_inv(apid, matrix_mdev->shadow_apcb.apm)) { clear_bit_inv(apid, matrix_mdev->shadow_apcb.apm); vfio_ap_mdev_update_guest_apcb(matrix_mdev); } - vfio_ap_mdev_reset_queues(qtable); + vfio_ap_mdev_reset_qlist(&qlist); - hash_for_each(qtable->queues, loop_cursor, q, mdev_qnode) { + list_for_each_entry_safe(q, tmpq, &qlist, reset_qnode) { vfio_ap_unlink_mdev_fr_queue(q); - hash_del(&q->mdev_qnode); + list_del(&q->reset_qnode); } - - kfree(qtable); } /** @@ -1171,7 +1225,7 @@ static ssize_t assign_domain_store(struct device *dev, { int ret; unsigned long apqi; - DECLARE_BITMAP(aqm_delta, AP_DOMAINS); + DECLARE_BITMAP(apm_filtered, AP_DEVICES); struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev); mutex_lock(&ap_perms_mutex); @@ -1200,12 +1254,11 @@ static ssize_t assign_domain_store(struct device *dev, } vfio_ap_mdev_link_domain(matrix_mdev, apqi); - memset(aqm_delta, 0, sizeof(aqm_delta)); - set_bit_inv(apqi, aqm_delta); - if (vfio_ap_mdev_filter_matrix(matrix_mdev->matrix.apm, aqm_delta, - matrix_mdev)) + if (vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered)) { vfio_ap_mdev_update_guest_apcb(matrix_mdev); + reset_queues_for_apids(matrix_mdev, apm_filtered); + } ret = count; done: @@ -1218,7 +1271,7 @@ static DEVICE_ATTR_WO(assign_domain); static void vfio_ap_mdev_unlink_domain(struct ap_matrix_mdev *matrix_mdev, unsigned long apqi, - struct ap_queue_table *qtable) + struct list_head *qlist) { unsigned long apid; struct vfio_ap_queue *q; @@ -1226,11 +1279,10 @@ static void vfio_ap_mdev_unlink_domain(struct ap_matrix_mdev *matrix_mdev, for_each_set_bit_inv(apid, matrix_mdev->matrix.apm, AP_DEVICES) { q = vfio_ap_unlink_apqn_fr_mdev(matrix_mdev, apid, apqi); - if (q && qtable) { + if (q && qlist) { if (test_bit_inv(apid, matrix_mdev->shadow_apcb.apm) && test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm)) - hash_add(qtable->queues, &q->mdev_qnode, - q->apqn); + list_add_tail(&q->reset_qnode, qlist); } } } @@ -1238,26 +1290,23 @@ static void vfio_ap_mdev_unlink_domain(struct ap_matrix_mdev *matrix_mdev, static void vfio_ap_mdev_hot_unplug_domain(struct ap_matrix_mdev *matrix_mdev, unsigned long apqi) { - int loop_cursor; - struct vfio_ap_queue *q; - struct ap_queue_table *qtable = kzalloc(sizeof(*qtable), GFP_KERNEL); + struct vfio_ap_queue *q, *tmpq; + struct list_head qlist; - hash_init(qtable->queues); - vfio_ap_mdev_unlink_domain(matrix_mdev, apqi, qtable); + INIT_LIST_HEAD(&qlist); + vfio_ap_mdev_unlink_domain(matrix_mdev, apqi, &qlist); if (test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm)) { clear_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm); vfio_ap_mdev_update_guest_apcb(matrix_mdev); } - vfio_ap_mdev_reset_queues(qtable); + vfio_ap_mdev_reset_qlist(&qlist); - hash_for_each(qtable->queues, loop_cursor, q, mdev_qnode) { + list_for_each_entry_safe(q, tmpq, &qlist, reset_qnode) { vfio_ap_unlink_mdev_fr_queue(q); - hash_del(&q->mdev_qnode); + list_del(&q->reset_qnode); } - - kfree(qtable); } /** @@ -1612,7 +1661,7 @@ static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev) get_update_locks_for_kvm(kvm); kvm_arch_crypto_clear_masks(kvm); - vfio_ap_mdev_reset_queues(&matrix_mdev->qtable); + vfio_ap_mdev_reset_queues(matrix_mdev); kvm_put_kvm(kvm); matrix_mdev->kvm = NULL; @@ -1748,15 +1797,33 @@ static void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q) } } -static int vfio_ap_mdev_reset_queues(struct ap_queue_table *qtable) +static int vfio_ap_mdev_reset_queues(struct ap_matrix_mdev *matrix_mdev) { int ret = 0, loop_cursor; struct vfio_ap_queue *q; - hash_for_each(qtable->queues, loop_cursor, q, mdev_qnode) + hash_for_each(matrix_mdev->qtable.queues, loop_cursor, q, mdev_qnode) vfio_ap_mdev_reset_queue(q); - hash_for_each(qtable->queues, loop_cursor, q, mdev_qnode) { + hash_for_each(matrix_mdev->qtable.queues, loop_cursor, q, mdev_qnode) { + flush_work(&q->reset_work); + + if (q->reset_status.response_code) + ret = -EIO; + } + + return ret; +} + +static int vfio_ap_mdev_reset_qlist(struct list_head *qlist) +{ + int ret = 0; + struct vfio_ap_queue *q; + + list_for_each_entry(q, qlist, reset_qnode) + vfio_ap_mdev_reset_queue(q); + + list_for_each_entry(q, qlist, reset_qnode) { flush_work(&q->reset_work); if (q->reset_status.response_code) @@ -1942,7 +2009,7 @@ static ssize_t vfio_ap_mdev_ioctl(struct vfio_device *vdev, ret = vfio_ap_mdev_get_device_info(arg); break; case VFIO_DEVICE_RESET: - ret = vfio_ap_mdev_reset_queues(&matrix_mdev->qtable); + ret = vfio_ap_mdev_reset_queues(matrix_mdev); break; case VFIO_DEVICE_GET_IRQ_INFO: ret = vfio_ap_get_irq_info(arg); @@ -2088,6 +2155,7 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev) { int ret; struct vfio_ap_queue *q; + DECLARE_BITMAP(apm_filtered, AP_DEVICES); struct ap_matrix_mdev *matrix_mdev; ret = sysfs_create_group(&apdev->device.kobj, &vfio_queue_attr_group); @@ -2109,15 +2177,28 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev) if (matrix_mdev) { vfio_ap_mdev_link_queue(matrix_mdev, q); - if (vfio_ap_mdev_filter_matrix(matrix_mdev->matrix.apm, - matrix_mdev->matrix.aqm, - matrix_mdev)) + /* + * If we're in the process of handling the adding of adapters or + * domains to the host's AP configuration, then let the + * vfio_ap device driver's on_scan_complete callback filter the + * matrix and update the guest's AP configuration after all of + * the new queue devices are probed. + */ + if (!bitmap_empty(matrix_mdev->apm_add, AP_DEVICES) || + !bitmap_empty(matrix_mdev->aqm_add, AP_DOMAINS)) + goto done; + + if (vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered)) { vfio_ap_mdev_update_guest_apcb(matrix_mdev); + reset_queues_for_apids(matrix_mdev, apm_filtered); + } } + +done: dev_set_drvdata(&apdev->device, q); release_update_locks_for_mdev(matrix_mdev); - return 0; + return ret; err_remove_group: sysfs_remove_group(&apdev->device.kobj, &vfio_queue_attr_group); @@ -2134,26 +2215,40 @@ void vfio_ap_mdev_remove_queue(struct ap_device *apdev) q = dev_get_drvdata(&apdev->device); get_update_locks_for_queue(q); matrix_mdev = q->matrix_mdev; + apid = AP_QID_CARD(q->apqn); + apqi = AP_QID_QUEUE(q->apqn); if (matrix_mdev) { - vfio_ap_unlink_queue_fr_mdev(q); - - apid = AP_QID_CARD(q->apqn); - apqi = AP_QID_QUEUE(q->apqn); - - /* - * If the queue is assigned to the guest's APCB, then remove - * the adapter's APID from the APCB and hot it into the guest. - */ + /* If the queue is assigned to the guest's AP configuration */ if (test_bit_inv(apid, matrix_mdev->shadow_apcb.apm) && test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm)) { + /* + * Since the queues are defined via a matrix of adapters + * and domains, it is not possible to hot unplug a + * single queue; so, let's unplug the adapter. + */ clear_bit_inv(apid, matrix_mdev->shadow_apcb.apm); vfio_ap_mdev_update_guest_apcb(matrix_mdev); + reset_queues_for_apid(matrix_mdev, apid); + goto done; } } - vfio_ap_mdev_reset_queue(q); - flush_work(&q->reset_work); + /* + * If the queue is not in the host's AP configuration, then resetting + * it will fail with response code 01, (APQN not valid); so, let's make + * sure it is in the host's config. + */ + if (test_bit_inv(apid, (unsigned long *)matrix_dev->info.apm) && + test_bit_inv(apqi, (unsigned long *)matrix_dev->info.aqm)) { + vfio_ap_mdev_reset_queue(q); + flush_work(&q->reset_work); + } + +done: + if (matrix_mdev) + vfio_ap_unlink_queue_fr_mdev(q); + dev_set_drvdata(&apdev->device, NULL); kfree(q); release_update_locks_for_mdev(matrix_mdev); @@ -2461,39 +2556,30 @@ void vfio_ap_on_cfg_changed(struct ap_config_info *cur_cfg_info, static void vfio_ap_mdev_hot_plug_cfg(struct ap_matrix_mdev *matrix_mdev) { - bool do_hotplug = false; - int filter_domains = 0; - int filter_adapters = 0; - DECLARE_BITMAP(apm, AP_DEVICES); - DECLARE_BITMAP(aqm, AP_DOMAINS); + DECLARE_BITMAP(apm_filtered, AP_DEVICES); + bool filter_domains, filter_adapters, filter_cdoms, do_hotplug = false; mutex_lock(&matrix_mdev->kvm->lock); mutex_lock(&matrix_dev->mdevs_lock); - filter_adapters = bitmap_and(apm, matrix_mdev->matrix.apm, - matrix_mdev->apm_add, AP_DEVICES); - filter_domains = bitmap_and(aqm, matrix_mdev->matrix.aqm, - matrix_mdev->aqm_add, AP_DOMAINS); - - if (filter_adapters && filter_domains) - do_hotplug |= vfio_ap_mdev_filter_matrix(apm, aqm, matrix_mdev); - else if (filter_adapters) - do_hotplug |= - vfio_ap_mdev_filter_matrix(apm, - matrix_mdev->shadow_apcb.aqm, - matrix_mdev); - else - do_hotplug |= - vfio_ap_mdev_filter_matrix(matrix_mdev->shadow_apcb.apm, - aqm, matrix_mdev); + filter_adapters = bitmap_intersects(matrix_mdev->matrix.apm, + matrix_mdev->apm_add, AP_DEVICES); + filter_domains = bitmap_intersects(matrix_mdev->matrix.aqm, + matrix_mdev->aqm_add, AP_DOMAINS); + filter_cdoms = bitmap_intersects(matrix_mdev->matrix.adm, + matrix_mdev->adm_add, AP_DOMAINS); - if (bitmap_intersects(matrix_mdev->matrix.adm, matrix_mdev->adm_add, - AP_DOMAINS)) + if (filter_adapters || filter_domains) + do_hotplug = vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered); + + if (filter_cdoms) do_hotplug |= vfio_ap_mdev_filter_cdoms(matrix_mdev); if (do_hotplug) vfio_ap_mdev_update_guest_apcb(matrix_mdev); + reset_queues_for_apids(matrix_mdev, apm_filtered); + mutex_unlock(&matrix_dev->mdevs_lock); mutex_unlock(&matrix_mdev->kvm->lock); } diff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h index 88aff8b81f2f..98d37aa27044 100644 --- a/drivers/s390/crypto/vfio_ap_private.h +++ b/drivers/s390/crypto/vfio_ap_private.h @@ -133,6 +133,8 @@ struct ap_matrix_mdev { * @apqn: the APQN of the AP queue device * @saved_isc: the guest ISC registered with the GIB interface * @mdev_qnode: allows the vfio_ap_queue struct to be added to a hashtable + * @reset_qnode: allows the vfio_ap_queue struct to be added to a list of queues + * that need to be reset * @reset_status: the status from the last reset of the queue * @reset_work: work to wait for queue reset to complete */ @@ -143,6 +145,7 @@ struct vfio_ap_queue { #define VFIO_AP_ISC_INVALID 0xff unsigned char saved_isc; struct hlist_node mdev_qnode; + struct list_head reset_qnode; struct ap_queue_status reset_status; struct work_struct reset_work; }; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index b92a32b4b114..04c64ce0a1ca 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -255,9 +255,10 @@ static void qeth_l3_clear_ip_htable(struct qeth_card *card, int recover) if (!recover) { hash_del(&addr->hnode); kfree(addr); - continue; + } else { + /* prepare for recovery */ + addr->disp_flag = QETH_DISP_ADDR_ADD; } - addr->disp_flag = QETH_DISP_ADDR_ADD; } mutex_unlock(&card->ip_lock); @@ -278,9 +279,11 @@ static void qeth_l3_recover_ip(struct qeth_card *card) if (addr->disp_flag == QETH_DISP_ADDR_ADD) { rc = qeth_l3_register_addr_entry(card, addr); - if (!rc) { + if (!rc || rc == -EADDRINUSE || rc == -ENETDOWN) { + /* keep it in the records */ addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; } else { + /* bad address */ hash_del(&addr->hnode); kfree(addr); } diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c index 408a806bf4c2..c64a085a7ee2 100644 --- a/drivers/scsi/fcoe/fcoe_sysfs.c +++ b/drivers/scsi/fcoe/fcoe_sysfs.c @@ -263,6 +263,7 @@ static ssize_t store_ctlr_mode(struct device *dev, const char *buf, size_t count) { struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); + int res; if (count > FCOE_MAX_MODENAME_LEN) return -EINVAL; @@ -279,12 +280,13 @@ static ssize_t store_ctlr_mode(struct device *dev, return -ENOTSUPP; } - ctlr->mode = sysfs_match_string(fip_conn_type_names, buf); - if (ctlr->mode < 0 || ctlr->mode == FIP_CONN_TYPE_UNKNOWN) { + res = sysfs_match_string(fip_conn_type_names, buf); + if (res < 0 || res == FIP_CONN_TYPE_UNKNOWN) { LIBFCOE_SYSFS_DBG(ctlr, "Unknown mode %s provided.\n", buf); return -EINVAL; } + ctlr->mode = res; ctlr->f->set_fcoe_ctlr_mode(ctlr); LIBFCOE_SYSFS_DBG(ctlr, "Mode changed to %s.\n", buf); diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index 4d6db4509e75..8d7fc5284293 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -546,6 +546,7 @@ int fnic_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc) if (fnic->sw_copy_wq[hwq].io_req_table[blk_mq_unique_tag_to_tag(mqtag)] != NULL) { WARN(1, "fnic<%d>: %s: hwq: %d tag 0x%x already exists\n", fnic->fnic_num, __func__, hwq, blk_mq_unique_tag_to_tag(mqtag)); + spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); return SCSI_MLQUEUE_HOST_BUSY; } diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c index 2a50fda3a628..625fd547ee60 100644 --- a/drivers/scsi/initio.c +++ b/drivers/scsi/initio.c @@ -371,7 +371,6 @@ static u16 initio_se2_rd(unsigned long base, u8 addr) */ static void initio_se2_wr(unsigned long base, u8 addr, u16 val) { - u8 rb; u8 instr; int i; @@ -400,7 +399,7 @@ static void initio_se2_wr(unsigned long base, u8 addr, u16 val) udelay(30); outb(SE2CS, base + TUL_NVRAM); /* -CLK */ udelay(30); - if ((rb = inb(base + TUL_NVRAM)) & SE2DI) + if (inb(base + TUL_NVRAM) & SE2DI) break; /* write complete */ } outb(0, base + TUL_NVRAM); /* -CS */ diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 71f711cb0628..355a0bc0828e 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -3387,7 +3387,7 @@ static enum sci_status isci_io_request_build(struct isci_host *ihost, return SCI_FAILURE; } - return SCI_SUCCESS; + return status; } static struct isci_request *isci_request_from_tag(struct isci_host *ihost, u16 tag) diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index d26941b131fd..bf879d81846b 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -1918,7 +1918,7 @@ out: * * Returns the number of SGEs added to the SGL. **/ -static int +static uint32_t lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc, struct sli4_sge *sgl, int datasegcnt, struct lpfc_io_buf *lpfc_cmd) @@ -1926,8 +1926,8 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc, struct scatterlist *sgde = NULL; /* s/g data entry */ struct sli4_sge_diseed *diseed = NULL; dma_addr_t physaddr; - int i = 0, num_sge = 0, status; - uint32_t reftag; + int i = 0, status; + uint32_t reftag, num_sge = 0; uint8_t txop, rxop; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS uint32_t rc; @@ -2099,7 +2099,7 @@ out: * * Returns the number of SGEs added to the SGL. **/ -static int +static uint32_t lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, struct sli4_sge *sgl, int datacnt, int protcnt, struct lpfc_io_buf *lpfc_cmd) @@ -2123,8 +2123,8 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, uint32_t rc; #endif uint32_t checking = 1; - uint32_t dma_offset = 0; - int num_sge = 0, j = 2; + uint32_t dma_offset = 0, num_sge = 0; + int j = 2; struct sli4_hybrid_sgl *sgl_xtra = NULL; sgpe = scsi_prot_sglist(sc); diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 46e6f807d1ca..706985358c6a 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -4880,7 +4880,7 @@ void lpfc_reset_barrier(struct lpfc_hba *phba) lockdep_assert_held(&phba->hbalock); pci_read_config_byte(phba->pcidev, PCI_HEADER_TYPE, &hdrtype); - if (hdrtype != 0x80 || + if (hdrtype != PCI_HEADER_TYPE_MFD || (FC_JEDEC_ID(phba->vpd.rev.biuRev) != HELIOS_JEDEC_ID && FC_JEDEC_ID(phba->vpd.rev.biuRev) != THOR_JEDEC_ID)) return; diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c index d8c57a0a518f..528f19f782f2 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c @@ -475,7 +475,7 @@ int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc) * @op_reply_q: op_reply_qinfo object * @reply_ci: operational reply descriptor's queue consumer index * - * Returns reply descriptor frame address + * Returns: reply descriptor frame address */ static inline struct mpi3_default_reply_descriptor * mpi3mr_get_reply_desc(struct op_reply_qinfo *op_reply_q, u32 reply_ci) @@ -1063,7 +1063,6 @@ enum mpi3mr_iocstate mpi3mr_get_iocstate(struct mpi3mr_ioc *mrioc) * @mrioc: Adapter instance reference * * Free the DMA memory allocated for IOCTL handling purpose. - * * Return: None */ @@ -1106,7 +1105,6 @@ static void mpi3mr_free_ioctl_dma_memory(struct mpi3mr_ioc *mrioc) /** * mpi3mr_alloc_ioctl_dma_memory - Alloc memory for ioctl dma * @mrioc: Adapter instance reference - * * This function allocates dmaable memory required to handle the * application issued MPI3 IOCTL requests. @@ -1241,7 +1239,7 @@ static int mpi3mr_issue_and_process_mur(struct mpi3mr_ioc *mrioc, * during reset/resume * @mrioc: Adapter instance reference * - * Return zero if the new IOCFacts parameters value is compatible with + * Return: zero if the new IOCFacts parameters value is compatible with * older values else return -EPERM */ static int diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 03348f605c2e..dd674378f2f3 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -2889,7 +2889,7 @@ static void qla2x00_iocb_work_fn(struct work_struct *work) static void qla_trace_init(void) { - qla_trc_array = trace_array_get_by_name("qla2xxx"); + qla_trc_array = trace_array_get_by_name("qla2xxx", NULL); if (!qla_trc_array) { ql_log(ql_log_fatal, NULL, 0x0001, "Unable to create qla2xxx trace instance, instance logging will be disabled.\n"); diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 885a7d5df3b9..612489afe8d2 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -61,11 +61,11 @@ static int scsi_eh_try_stu(struct scsi_cmnd *scmd); static enum scsi_disposition scsi_try_to_abort_cmd(const struct scsi_host_template *, struct scsi_cmnd *); -void scsi_eh_wakeup(struct Scsi_Host *shost) +void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy) { lockdep_assert_held(shost->host_lock); - if (scsi_host_busy(shost) == shost->host_failed) { + if (busy == shost->host_failed) { trace_scsi_eh_wakeup(shost); wake_up_process(shost->ehandler); SCSI_LOG_ERROR_RECOVERY(5, shost_printk(KERN_INFO, shost, @@ -88,7 +88,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost) if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 || scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) { shost->host_eh_scheduled++; - scsi_eh_wakeup(shost); + scsi_eh_wakeup(shost, scsi_host_busy(shost)); } spin_unlock_irqrestore(shost->host_lock, flags); @@ -282,11 +282,12 @@ static void scsi_eh_inc_host_failed(struct rcu_head *head) { struct scsi_cmnd *scmd = container_of(head, typeof(*scmd), rcu); struct Scsi_Host *shost = scmd->device->host; + unsigned int busy = scsi_host_busy(shost); unsigned long flags; spin_lock_irqsave(shost->host_lock, flags); shost->host_failed++; - scsi_eh_wakeup(shost); + scsi_eh_wakeup(shost, busy); spin_unlock_irqrestore(shost->host_lock, flags); } @@ -2197,15 +2198,18 @@ void scsi_eh_flush_done_q(struct list_head *done_q) struct scsi_cmnd *scmd, *next; list_for_each_entry_safe(scmd, next, done_q, eh_entry) { + struct scsi_device *sdev = scmd->device; + list_del_init(&scmd->eh_entry); - if (scsi_device_online(scmd->device) && - !scsi_noretry_cmd(scmd) && scsi_cmd_retry_allowed(scmd) && - scsi_eh_should_retry_cmd(scmd)) { + if (scsi_device_online(sdev) && !scsi_noretry_cmd(scmd) && + scsi_cmd_retry_allowed(scmd) && + scsi_eh_should_retry_cmd(scmd)) { SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd, "%s: flush retry cmd\n", current->comm)); scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY); + blk_mq_kick_requeue_list(sdev->request_queue); } else { /* * If just we got sense for the device (called diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index cf3864f72093..df5ac03d5d6c 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -278,9 +278,11 @@ static void scsi_dec_host_busy(struct Scsi_Host *shost, struct scsi_cmnd *cmd) rcu_read_lock(); __clear_bit(SCMD_STATE_INFLIGHT, &cmd->state); if (unlikely(scsi_host_in_recovery(shost))) { + unsigned int busy = scsi_host_busy(shost); + spin_lock_irqsave(shost->host_lock, flags); if (shost->host_failed || shost->host_eh_scheduled) - scsi_eh_wakeup(shost); + scsi_eh_wakeup(shost, busy); spin_unlock_irqrestore(shost->host_lock, flags); } rcu_read_unlock(); diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 3f0dfb97db6b..1fbfe1b52c9f 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -92,7 +92,7 @@ extern void scmd_eh_abort_handler(struct work_struct *work); extern enum blk_eh_timer_return scsi_timeout(struct request *req); extern int scsi_error_handler(void *host); extern enum scsi_disposition scsi_decide_disposition(struct scsi_cmnd *cmd); -extern void scsi_eh_wakeup(struct Scsi_Host *shost); +extern void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy); extern void scsi_eh_scmd_add(struct scsi_cmnd *); void scsi_eh_ready_devs(struct Scsi_Host *shost, struct list_head *work_q, diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h index 041940183516..cdedc271857a 100644 --- a/drivers/scsi/smartpqi/smartpqi.h +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -1347,7 +1347,6 @@ struct pqi_ctrl_info { bool controller_online; bool block_requests; bool scan_blocked; - u8 logical_volume_rescan_needed : 1; u8 inbound_spanning_supported : 1; u8 outbound_spanning_supported : 1; u8 pqi_mode_enabled : 1; diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 9a58df9312fa..ceff1ec13f9e 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -33,11 +33,11 @@ #define BUILD_TIMESTAMP #endif -#define DRIVER_VERSION "2.1.24-046" +#define DRIVER_VERSION "2.1.26-030" #define DRIVER_MAJOR 2 #define DRIVER_MINOR 1 -#define DRIVER_RELEASE 24 -#define DRIVER_REVISION 46 +#define DRIVER_RELEASE 26 +#define DRIVER_REVISION 30 #define DRIVER_NAME "Microchip SmartPQI Driver (v" \ DRIVER_VERSION BUILD_TIMESTAMP ")" @@ -2093,8 +2093,6 @@ static void pqi_scsi_update_device(struct pqi_ctrl_info *ctrl_info, if (existing_device->devtype == TYPE_DISK) { existing_device->raid_level = new_device->raid_level; existing_device->volume_status = new_device->volume_status; - if (ctrl_info->logical_volume_rescan_needed) - existing_device->rescan = true; memset(existing_device->next_bypass_group, 0, sizeof(existing_device->next_bypass_group)); if (!pqi_raid_maps_equal(existing_device->raid_map, new_device->raid_map)) { kfree(existing_device->raid_map); @@ -2164,6 +2162,20 @@ static inline void pqi_init_device_tmf_work(struct pqi_scsi_dev *device) INIT_WORK(&tmf_work->work_struct, pqi_tmf_worker); } +static inline bool pqi_volume_rescan_needed(struct pqi_scsi_dev *device) +{ + if (pqi_device_in_remove(device)) + return false; + + if (device->sdev == NULL) + return false; + + if (!scsi_device_online(device->sdev)) + return false; + + return device->rescan; +} + static void pqi_update_device_list(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *new_device_list[], unsigned int num_new_devices) { @@ -2284,9 +2296,13 @@ static void pqi_update_device_list(struct pqi_ctrl_info *ctrl_info, if (device->sdev && device->queue_depth != device->advertised_queue_depth) { device->advertised_queue_depth = device->queue_depth; scsi_change_queue_depth(device->sdev, device->advertised_queue_depth); - if (device->rescan) { - scsi_rescan_device(device->sdev); + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + if (pqi_volume_rescan_needed(device)) { device->rescan = false; + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); + scsi_rescan_device(device->sdev); + } else { + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); } } } @@ -2308,8 +2324,6 @@ static void pqi_update_device_list(struct pqi_ctrl_info *ctrl_info, } } - ctrl_info->logical_volume_rescan_needed = false; - } static inline bool pqi_is_supported_device(struct pqi_scsi_dev *device) @@ -3702,6 +3716,21 @@ static bool pqi_ofa_process_event(struct pqi_ctrl_info *ctrl_info, return ack_event; } +static void pqi_mark_volumes_for_rescan(struct pqi_ctrl_info *ctrl_info) +{ + unsigned long flags; + struct pqi_scsi_dev *device; + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + + list_for_each_entry(device, &ctrl_info->scsi_device_list, scsi_device_list_entry) { + if (pqi_is_logical_device(device) && device->devtype == TYPE_DISK) + device->rescan = true; + } + + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); +} + static void pqi_disable_raid_bypass(struct pqi_ctrl_info *ctrl_info) { unsigned long flags; @@ -3742,7 +3771,7 @@ static void pqi_event_worker(struct work_struct *work) ack_event = true; rescan_needed = true; if (event->event_type == PQI_EVENT_TYPE_LOGICAL_DEVICE) - ctrl_info->logical_volume_rescan_needed = true; + pqi_mark_volumes_for_rescan(ctrl_info); else if (event->event_type == PQI_EVENT_TYPE_AIO_STATE_CHANGE) pqi_disable_raid_bypass(ctrl_info); } @@ -10144,6 +10173,18 @@ static const struct pci_device_id pqi_pci_id_table[] = { }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1137, 0x02f8) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1137, 0x02f9) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1137, 0x02fa) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, 0x1e93, 0x1000) }, { @@ -10200,6 +10241,34 @@ static const struct pci_device_id pqi_pci_id_table[] = { }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1f51, 0x100e) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1f51, 0x100f) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1f51, 0x1010) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1f51, 0x1011) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1f51, 0x1043) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1f51, 0x1044) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1f51, 0x1045) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, PCI_ANY_ID, PCI_ANY_ID) }, { 0 } diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index a95936b18f69..7ceb982040a5 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -330,6 +330,7 @@ enum storvsc_request_type { */ static int storvsc_ringbuffer_size = (128 * 1024); +static int aligned_ringbuffer_size; static u32 max_outstanding_req_per_channel; static int storvsc_change_queue_depth(struct scsi_device *sdev, int queue_depth); @@ -687,8 +688,8 @@ static void handle_sc_creation(struct vmbus_channel *new_sc) new_sc->next_request_id_callback = storvsc_next_request_id; ret = vmbus_open(new_sc, - storvsc_ringbuffer_size, - storvsc_ringbuffer_size, + aligned_ringbuffer_size, + aligned_ringbuffer_size, (void *)&props, sizeof(struct vmstorage_channel_properties), storvsc_on_channel_callback, new_sc); @@ -1973,7 +1974,7 @@ static int storvsc_probe(struct hv_device *device, dma_set_min_align_mask(&device->device, HV_HYP_PAGE_SIZE - 1); stor_device->port_number = host->host_no; - ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size, is_fc); + ret = storvsc_connect_to_vsp(device, aligned_ringbuffer_size, is_fc); if (ret) goto err_out1; @@ -2164,7 +2165,7 @@ static int storvsc_resume(struct hv_device *hv_dev) { int ret; - ret = storvsc_connect_to_vsp(hv_dev, storvsc_ringbuffer_size, + ret = storvsc_connect_to_vsp(hv_dev, aligned_ringbuffer_size, hv_dev_is_fc(hv_dev)); return ret; } @@ -2198,8 +2199,9 @@ static int __init storvsc_drv_init(void) * the ring buffer indices) by the max request size (which is * vmbus_channel_packet_multipage_buffer + struct vstor_packet + u64) */ + aligned_ringbuffer_size = VMBUS_RING_SIZE(storvsc_ringbuffer_size); max_outstanding_req_per_channel = - ((storvsc_ringbuffer_size - PAGE_SIZE) / + ((aligned_ringbuffer_size - PAGE_SIZE) / ALIGN(MAX_MULTIPAGE_BUFFER_PACKET + sizeof(struct vstor_packet) + sizeof(u64), sizeof(u64))); diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 9d1bdcdc1331..617eb892f4ad 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -37,6 +37,11 @@ #define VIRTIO_SCSI_EVENT_LEN 8 #define VIRTIO_SCSI_VQ_BASE 2 +static unsigned int virtscsi_poll_queues; +module_param(virtscsi_poll_queues, uint, 0644); +MODULE_PARM_DESC(virtscsi_poll_queues, + "The number of dedicated virtqueues for polling I/O"); + /* Command queue element */ struct virtio_scsi_cmd { struct scsi_cmnd *sc; @@ -76,6 +81,7 @@ struct virtio_scsi { struct virtio_scsi_event_node event_list[VIRTIO_SCSI_EVENT_LEN]; u32 num_queues; + int io_queues[HCTX_MAX_TYPES]; struct hlist_node node; @@ -182,8 +188,6 @@ static void virtscsi_vq_done(struct virtio_scsi *vscsi, while ((buf = virtqueue_get_buf(vq, &len)) != NULL) fn(vscsi, buf); - if (unlikely(virtqueue_is_broken(vq))) - break; } while (!virtqueue_enable_cb(vq)); spin_unlock_irqrestore(&virtscsi_vq->vq_lock, flags); } @@ -722,9 +726,49 @@ static int virtscsi_abort(struct scsi_cmnd *sc) static void virtscsi_map_queues(struct Scsi_Host *shost) { struct virtio_scsi *vscsi = shost_priv(shost); - struct blk_mq_queue_map *qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT]; + int i, qoff; + + for (i = 0, qoff = 0; i < shost->nr_maps; i++) { + struct blk_mq_queue_map *map = &shost->tag_set.map[i]; + + map->nr_queues = vscsi->io_queues[i]; + map->queue_offset = qoff; + qoff += map->nr_queues; + + if (map->nr_queues == 0) + continue; + + /* + * Regular queues have interrupts and hence CPU affinity is + * defined by the core virtio code, but polling queues have + * no interrupts so we let the block layer assign CPU affinity. + */ + if (i == HCTX_TYPE_POLL) + blk_mq_map_queues(map); + else + blk_mq_virtio_map_queues(map, vscsi->vdev, 2); + } +} + +static int virtscsi_mq_poll(struct Scsi_Host *shost, unsigned int queue_num) +{ + struct virtio_scsi *vscsi = shost_priv(shost); + struct virtio_scsi_vq *virtscsi_vq = &vscsi->req_vqs[queue_num]; + unsigned long flags; + unsigned int len; + int found = 0; + void *buf; + + spin_lock_irqsave(&virtscsi_vq->vq_lock, flags); + + while ((buf = virtqueue_get_buf(virtscsi_vq->vq, &len)) != NULL) { + virtscsi_complete_cmd(vscsi, buf); + found++; + } + + spin_unlock_irqrestore(&virtscsi_vq->vq_lock, flags); - blk_mq_virtio_map_queues(qmap, vscsi->vdev, 2); + return found; } static void virtscsi_commit_rqs(struct Scsi_Host *shost, u16 hwq) @@ -751,6 +795,7 @@ static const struct scsi_host_template virtscsi_host_template = { .this_id = -1, .cmd_size = sizeof(struct virtio_scsi_cmd), .queuecommand = virtscsi_queuecommand, + .mq_poll = virtscsi_mq_poll, .commit_rqs = virtscsi_commit_rqs, .change_queue_depth = virtscsi_change_queue_depth, .eh_abort_handler = virtscsi_abort, @@ -795,13 +840,14 @@ static int virtscsi_init(struct virtio_device *vdev, { int err; u32 i; - u32 num_vqs; + u32 num_vqs, num_poll_vqs, num_req_vqs; vq_callback_t **callbacks; const char **names; struct virtqueue **vqs; struct irq_affinity desc = { .pre_vectors = 2 }; - num_vqs = vscsi->num_queues + VIRTIO_SCSI_VQ_BASE; + num_req_vqs = vscsi->num_queues; + num_vqs = num_req_vqs + VIRTIO_SCSI_VQ_BASE; vqs = kmalloc_array(num_vqs, sizeof(struct virtqueue *), GFP_KERNEL); callbacks = kmalloc_array(num_vqs, sizeof(vq_callback_t *), GFP_KERNEL); @@ -812,15 +858,31 @@ static int virtscsi_init(struct virtio_device *vdev, goto out; } + num_poll_vqs = min_t(unsigned int, virtscsi_poll_queues, + num_req_vqs - 1); + vscsi->io_queues[HCTX_TYPE_DEFAULT] = num_req_vqs - num_poll_vqs; + vscsi->io_queues[HCTX_TYPE_READ] = 0; + vscsi->io_queues[HCTX_TYPE_POLL] = num_poll_vqs; + + dev_info(&vdev->dev, "%d/%d/%d default/read/poll queues\n", + vscsi->io_queues[HCTX_TYPE_DEFAULT], + vscsi->io_queues[HCTX_TYPE_READ], + vscsi->io_queues[HCTX_TYPE_POLL]); + callbacks[0] = virtscsi_ctrl_done; callbacks[1] = virtscsi_event_done; names[0] = "control"; names[1] = "event"; - for (i = VIRTIO_SCSI_VQ_BASE; i < num_vqs; i++) { + for (i = VIRTIO_SCSI_VQ_BASE; i < num_vqs - num_poll_vqs; i++) { callbacks[i] = virtscsi_req_done; names[i] = "request"; } + for (; i < num_vqs; i++) { + callbacks[i] = NULL; + names[i] = "request_poll"; + } + /* Discover virtqueues and write information to configuration. */ err = virtio_find_vqs(vdev, num_vqs, vqs, callbacks, names, &desc); if (err) @@ -874,6 +936,7 @@ static int virtscsi_probe(struct virtio_device *vdev) sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1; shost->sg_tablesize = sg_elems; + shost->nr_maps = 1; vscsi = shost_priv(shost); vscsi->vdev = vdev; vscsi->num_queues = num_queues; @@ -883,6 +946,9 @@ static int virtscsi_probe(struct virtio_device *vdev) if (err) goto virtscsi_init_failed; + if (vscsi->io_queues[HCTX_TYPE_POLL]) + shost->nr_maps = HCTX_TYPE_POLL + 1; + shost->can_queue = virtqueue_get_vring_size(vscsi->req_vqs[0].vq); cmd_per_lun = virtscsi_config_get(vdev, cmd_per_lun) ?: 1; diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c index e05473c5c267..16018009a5a6 100644 --- a/drivers/sh/maple/maple.c +++ b/drivers/sh/maple/maple.c @@ -59,6 +59,7 @@ struct maple_device_specify { static bool checked[MAPLE_PORTS]; static bool empty[MAPLE_PORTS]; static struct maple_device *baseunits[MAPLE_PORTS]; +static const struct bus_type maple_bus_type; /** * maple_driver_register - register a maple driver @@ -773,11 +774,10 @@ static struct maple_driver maple_unsupported_device = { /* * maple_bus_type - core maple bus structure */ -struct bus_type maple_bus_type = { +static const struct bus_type maple_bus_type = { .name = "maple", .match = maple_match_bus_driver, }; -EXPORT_SYMBOL_GPL(maple_bus_type); static struct device maple_bus = { .init_name = "maple", diff --git a/drivers/soc/apple/mailbox.c b/drivers/soc/apple/mailbox.c index 780199bf351e..49a0955e82d6 100644 --- a/drivers/soc/apple/mailbox.c +++ b/drivers/soc/apple/mailbox.c @@ -296,14 +296,14 @@ struct apple_mbox *apple_mbox_get(struct device *dev, int index) of_node_put(args.np); if (!pdev) - return ERR_PTR(EPROBE_DEFER); + return ERR_PTR(-EPROBE_DEFER); mbox = platform_get_drvdata(pdev); if (!mbox) - return ERR_PTR(EPROBE_DEFER); + return ERR_PTR(-EPROBE_DEFER); if (!device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_CONSUMER)) - return ERR_PTR(ENODEV); + return ERR_PTR(-ENODEV); return mbox; } diff --git a/drivers/soc/xilinx/xlnx_event_manager.c b/drivers/soc/xilinx/xlnx_event_manager.c index 042553abe1bf..253299e4214d 100644 --- a/drivers/soc/xilinx/xlnx_event_manager.c +++ b/drivers/soc/xilinx/xlnx_event_manager.c @@ -77,11 +77,26 @@ struct registered_event_data { static bool xlnx_is_error_event(const u32 node_id) { - if (node_id == EVENT_ERROR_PMC_ERR1 || - node_id == EVENT_ERROR_PMC_ERR2 || - node_id == EVENT_ERROR_PSM_ERR1 || - node_id == EVENT_ERROR_PSM_ERR2) - return true; + u32 pm_family_code, pm_sub_family_code; + + zynqmp_pm_get_family_info(&pm_family_code, &pm_sub_family_code); + + if (pm_sub_family_code == VERSAL_SUB_FAMILY_CODE) { + if (node_id == VERSAL_EVENT_ERROR_PMC_ERR1 || + node_id == VERSAL_EVENT_ERROR_PMC_ERR2 || + node_id == VERSAL_EVENT_ERROR_PSM_ERR1 || + node_id == VERSAL_EVENT_ERROR_PSM_ERR2) + return true; + } else { + if (node_id == VERSAL_NET_EVENT_ERROR_PMC_ERR1 || + node_id == VERSAL_NET_EVENT_ERROR_PMC_ERR2 || + node_id == VERSAL_NET_EVENT_ERROR_PMC_ERR3 || + node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR1 || + node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR2 || + node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR3 || + node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR4) + return true; + } return false; } @@ -483,7 +498,7 @@ static void xlnx_call_notify_cb_handler(const u32 *payload) static void xlnx_get_event_callback_data(u32 *buf) { - zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf); + zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, buf, 0); } static irqreturn_t xlnx_event_handler(int irq, void *dev_id) @@ -656,7 +671,11 @@ static int xlnx_event_manager_probe(struct platform_device *pdev) ret = zynqmp_pm_register_sgi(sgi_num, 0); if (ret) { - dev_err(&pdev->dev, "SGI %d Registration over TF-A failed with %d\n", sgi_num, ret); + if (ret == -EOPNOTSUPP) + dev_err(&pdev->dev, "SGI registration not supported by TF-A or Xen\n"); + else + dev_err(&pdev->dev, "SGI %d registration failed, err %d\n", sgi_num, ret); + xlnx_event_cleanup_sgi(pdev); return ret; } diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index 07d735b38b49..965b1143936a 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -51,7 +51,7 @@ static enum pm_suspend_mode suspend_mode = PM_SUSPEND_MODE_STD; static void zynqmp_pm_get_callback_data(u32 *buf) { - zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf); + zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, buf, 0); } static void suspend_event_callback(const u32 *payload, void *data) diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index 3a99f6dcdfaf..f54bb4dd2d10 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -927,6 +927,14 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) amd_manager->bus.clk_stop_timeout = 200; amd_manager->bus.link_id = amd_manager->instance; + /* + * Due to BIOS compatibility, the two links are exposed within + * the scope of a single controller. If this changes, the + * controller_id will have to be updated with drv_data + * information. + */ + amd_manager->bus.controller_id = 0; + switch (amd_manager->instance) { case ACP_SDW0: amd_manager->num_dout_ports = AMD_SDW0_MAX_TX_PORTS; @@ -942,13 +950,13 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) amd_manager->reg_mask = &sdw_manager_reg_mask_array[amd_manager->instance]; params = &amd_manager->bus.params; - params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2; - params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2; + params->col = AMD_SDW_DEFAULT_COLUMNS; params->row = AMD_SDW_DEFAULT_ROWS; prop = &amd_manager->bus.prop; prop->clk_freq = &amd_sdw_freq_tbl[0]; prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ; + prop->max_clk_freq = AMD_SDW_DEFAULT_CLK_FREQ; ret = sdw_bus_master_add(&amd_manager->bus, dev, dev->fwnode); if (ret) { diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 41b0d9adf68e..f3fec15c3112 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -22,6 +22,10 @@ static int sdw_get_id(struct sdw_bus *bus) return rc; bus->id = rc; + + if (bus->controller_id == -1) + bus->controller_id = rc; + return 0; } diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c index d1553cb77187..67abd7e52f09 100644 --- a/drivers/soundwire/debugfs.c +++ b/drivers/soundwire/debugfs.c @@ -20,7 +20,7 @@ void sdw_bus_debugfs_init(struct sdw_bus *bus) return; /* create the debugfs master-N */ - snprintf(name, sizeof(name), "master-%d-%d", bus->id, bus->link_id); + snprintf(name, sizeof(name), "master-%d-%d", bus->controller_id, bus->link_id); bus->debugfs = debugfs_create_dir(name, sdw_debugfs_root); } diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 31162f2b5638..c70a63d009ae 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -333,7 +333,7 @@ static int sdw_select_row_col(struct sdw_bus *bus, int clk_freq) */ static int sdw_compute_bus_params(struct sdw_bus *bus) { - unsigned int max_dr_freq, curr_dr_freq = 0; + unsigned int curr_dr_freq = 0; struct sdw_master_prop *mstr_prop = &bus->prop; int i, clk_values, ret; bool is_gear = false; @@ -351,14 +351,12 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) clk_buf = NULL; } - max_dr_freq = mstr_prop->max_clk_freq * SDW_DOUBLE_RATE_FACTOR; - for (i = 0; i < clk_values; i++) { if (!clk_buf) - curr_dr_freq = max_dr_freq; + curr_dr_freq = bus->params.max_dr_freq; else curr_dr_freq = (is_gear) ? - (max_dr_freq >> clk_buf[i]) : + (bus->params.max_dr_freq >> clk_buf[i]) : clk_buf[i] * SDW_DOUBLE_RATE_FACTOR; if (curr_dr_freq <= bus->params.bandwidth) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index 7f15e3549e53..93698532deac 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -234,6 +234,9 @@ static int intel_link_probe(struct auxiliary_device *auxdev, cdns->instance = sdw->instance; cdns->msg_count = 0; + /* single controller for all SoundWire links */ + bus->controller_id = 0; + bus->link_id = auxdev->id; bus->clk_stop_timeout = 1; diff --git a/drivers/soundwire/master.c b/drivers/soundwire/master.c index 9b05c9e25ebe..51abedbbaa66 100644 --- a/drivers/soundwire/master.c +++ b/drivers/soundwire/master.c @@ -145,7 +145,7 @@ int sdw_master_device_add(struct sdw_bus *bus, struct device *parent, md->dev.fwnode = fwnode; md->dev.dma_mask = parent->dma_mask; - dev_set_name(&md->dev, "sdw-master-%d", bus->id); + dev_set_name(&md->dev, "sdw-master-%d-%d", bus->controller_id, bus->link_id); ret = device_register(&md->dev); if (ret) { diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 8076d40407d4..3c4d6debab1f 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1157,9 +1157,20 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, struct sdw_port_runtime *p_rt; struct sdw_slave *slave; unsigned long *port_mask; - int i, maxport, pn, nports = 0, ret = 0; + int maxport, pn, nports = 0, ret = 0; unsigned int m_port; + if (direction == SNDRV_PCM_STREAM_CAPTURE) + sconfig.direction = SDW_DATA_DIR_TX; + else + sconfig.direction = SDW_DATA_DIR_RX; + + /* hw parameters wil be ignored as we only support PDM */ + sconfig.ch_count = 1; + sconfig.frame_rate = params_rate(params); + sconfig.type = stream->type; + sconfig.bps = 1; + mutex_lock(&ctrl->port_lock); list_for_each_entry(m_rt, &stream->master_list, stream_node) { if (m_rt->direction == SDW_DATA_DIR_RX) { @@ -1183,7 +1194,7 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, if (pn > maxport) { dev_err(ctrl->dev, "All ports busy\n"); ret = -EBUSY; - goto err; + goto out; } set_bit(pn, port_mask); pconfig[nports].num = pn; @@ -1193,24 +1204,9 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, } } - if (direction == SNDRV_PCM_STREAM_CAPTURE) - sconfig.direction = SDW_DATA_DIR_TX; - else - sconfig.direction = SDW_DATA_DIR_RX; - - /* hw parameters wil be ignored as we only support PDM */ - sconfig.ch_count = 1; - sconfig.frame_rate = params_rate(params); - sconfig.type = stream->type; - sconfig.bps = 1; sdw_stream_add_master(&ctrl->bus, &sconfig, pconfig, nports, stream); -err: - if (ret) { - for (i = 0; i < nports; i++) - clear_bit(pconfig[i].num, port_mask); - } - +out: mutex_unlock(&ctrl->port_lock); return ret; @@ -1593,6 +1589,13 @@ static int qcom_swrm_probe(struct platform_device *pdev) } } + ctrl->bus.controller_id = -1; + + if (ctrl->version > SWRM_VERSION_1_3_0) { + ctrl->reg_read(ctrl, SWRM_COMP_MASTER_ID, &val); + ctrl->bus.controller_id = val; + } + ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode); if (ret) { dev_err(dev, "Failed to register Soundwire controller (%d)\n", diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index c1c1a2ac293a..060c2982e26b 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -39,14 +39,14 @@ int sdw_slave_add(struct sdw_bus *bus, slave->dev.fwnode = fwnode; if (id->unique_id == SDW_IGNORED_UNIQUE_ID) { - /* name shall be sdw:link:mfg:part:class */ - dev_set_name(&slave->dev, "sdw:%01x:%04x:%04x:%02x", - bus->link_id, id->mfg_id, id->part_id, + /* name shall be sdw:ctrl:link:mfg:part:class */ + dev_set_name(&slave->dev, "sdw:%01x:%01x:%04x:%04x:%02x", + bus->controller_id, bus->link_id, id->mfg_id, id->part_id, id->class_id); } else { - /* name shall be sdw:link:mfg:part:class:unique */ - dev_set_name(&slave->dev, "sdw:%01x:%04x:%04x:%02x:%01x", - bus->link_id, id->mfg_id, id->part_id, + /* name shall be sdw:ctrl:link:mfg:part:class:unique */ + dev_set_name(&slave->dev, "sdw:%01x:%01x:%04x:%04x:%02x:%01x", + bus->controller_id, bus->link_id, id->mfg_id, id->part_id, id->class_id, id->unique_id); } diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index f048b3d55b2e..f9c0adc0738d 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -898,7 +898,7 @@ static struct sdw_port_runtime *sdw_port_alloc(struct list_head *port_list) } static int sdw_port_config(struct sdw_port_runtime *p_rt, - struct sdw_port_config *port_config, + const struct sdw_port_config *port_config, int port_index) { p_rt->ch_mask = port_config[port_index].ch_mask; @@ -971,7 +971,7 @@ static int sdw_slave_port_is_valid_range(struct device *dev, int num) static int sdw_slave_port_config(struct sdw_slave *slave, struct sdw_slave_runtime *s_rt, - struct sdw_port_config *port_config) + const struct sdw_port_config *port_config) { struct sdw_port_runtime *p_rt; int ret; @@ -1027,7 +1027,7 @@ static int sdw_master_port_alloc(struct sdw_master_runtime *m_rt, } static int sdw_master_port_config(struct sdw_master_runtime *m_rt, - struct sdw_port_config *port_config) + const struct sdw_port_config *port_config) { struct sdw_port_runtime *p_rt; int ret; @@ -1862,7 +1862,7 @@ EXPORT_SYMBOL(sdw_release_stream); */ int sdw_stream_add_master(struct sdw_bus *bus, struct sdw_stream_config *stream_config, - struct sdw_port_config *port_config, + const struct sdw_port_config *port_config, unsigned int num_ports, struct sdw_stream_runtime *stream) { @@ -1982,7 +1982,7 @@ EXPORT_SYMBOL(sdw_stream_remove_master); */ int sdw_stream_add_slave(struct sdw_slave *slave, struct sdw_stream_config *stream_config, - struct sdw_port_config *port_config, + const struct sdw_port_config *port_config, unsigned int num_ports, struct sdw_stream_runtime *stream) { diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index d96222e6d7d2..cfdaa5eaec76 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -19,7 +19,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/spi/spi.h> -#include <linux/spi/spi-mem.h> +#include <linux/mtd/spi-nor.h> #include <linux/sysfs.h> #include <linux/types.h> #include "spi-bcm-qspi.h" @@ -1221,7 +1221,7 @@ static int bcm_qspi_exec_mem_op(struct spi_mem *mem, /* non-aligned and very short transfers are handled by MSPI */ if (!IS_ALIGNED((uintptr_t)addr, 4) || !IS_ALIGNED((uintptr_t)buf, 4) || - len < 4) + len < 4 || op->cmd.opcode == SPINOR_OP_RDSFDP) mspi_read = true; if (!has_bspi(qspi) || mspi_read) diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index a50eb4db79de..e5140532071d 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -317,6 +317,15 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) xspi->rx_bytes -= nrx; while (ntx || nrx) { + if (nrx) { + u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD); + + if (xspi->rxbuf) + *xspi->rxbuf++ = data; + + nrx--; + } + if (ntx) { if (xspi->txbuf) cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++); @@ -326,14 +335,6 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) ntx--; } - if (nrx) { - u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD); - - if (xspi->rxbuf) - *xspi->rxbuf++ = data; - - nrx--; - } } } diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index f0b630fe16c3..b341b6908df0 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -441,7 +441,6 @@ static void mcfqspi_remove(struct platform_device *pdev) mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR); mcfqspi_cs_teardown(mcfqspi); - clk_disable_unprepare(mcfqspi->clk); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c index f13073e12593..b24190526ce9 100644 --- a/drivers/spi/spi-cs42l43.c +++ b/drivers/spi/spi-cs42l43.c @@ -244,7 +244,10 @@ static int cs42l43_spi_probe(struct platform_device *pdev) priv->ctlr->use_gpio_descriptors = true; priv->ctlr->auto_runtime_pm = true; - devm_pm_runtime_enable(priv->dev); + ret = devm_pm_runtime_enable(priv->dev); + if (ret) + return ret; + pm_runtime_idle(priv->dev); regmap_write(priv->regmap, CS42L43_TRAN_CONFIG6, CS42L43_FIFO_SIZE - 1); diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c index 9d22018f7985..1301d14483d4 100644 --- a/drivers/spi/spi-hisi-sfc-v3xx.c +++ b/drivers/spi/spi-hisi-sfc-v3xx.c @@ -377,6 +377,11 @@ static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = { static irqreturn_t hisi_sfc_v3xx_isr(int irq, void *data) { struct hisi_sfc_v3xx_host *host = data; + u32 reg; + + reg = readl(host->regbase + HISI_SFC_V3XX_INT_STAT); + if (!reg) + return IRQ_NONE; hisi_sfc_v3xx_disable_int(host); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 272bc871a848..546cdce525fc 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1344,7 +1344,7 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, controller->dma_tx = dma_request_chan(dev, "tx"); if (IS_ERR(controller->dma_tx)) { ret = PTR_ERR(controller->dma_tx); - dev_dbg(dev, "can't get the TX DMA channel, error %d!\n", ret); + dev_err_probe(dev, ret, "can't get the TX DMA channel!\n"); controller->dma_tx = NULL; goto err; } @@ -1353,7 +1353,7 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, controller->dma_rx = dma_request_chan(dev, "rx"); if (IS_ERR(controller->dma_rx)) { ret = PTR_ERR(controller->dma_rx); - dev_dbg(dev, "can't get the RX DMA channel, error %d\n", ret); + dev_err_probe(dev, ret, "can't get the RX DMA channel!\n"); controller->dma_rx = NULL; goto err; } diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c index 57d767a68e7b..07d20ca1164c 100644 --- a/drivers/spi/spi-intel-pci.c +++ b/drivers/spi/spi-intel-pci.c @@ -76,6 +76,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x7a24), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x7e23), (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x7f24), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x9d24), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x9da4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&cnl_info }, @@ -84,7 +85,6 @@ static const struct pci_device_id intel_spi_pci_ids[] = { { PCI_VDEVICE(INTEL, 0xa2a4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0xa324), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0xa3a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xae23), (unsigned long)&cnl_info }, { }, }; MODULE_DEVICE_TABLE(pci, intel_spi_pci_ids); diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index cfc3b1ddbd22..6f12e4fb2e2e 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -136,14 +136,14 @@ struct sh_msiof_spi_priv { /* SIFCTR */ #define SIFCTR_TFWM_MASK GENMASK(31, 29) /* Transmit FIFO Watermark */ -#define SIFCTR_TFWM_64 (0 << 29) /* Transfer Request when 64 empty stages */ -#define SIFCTR_TFWM_32 (1 << 29) /* Transfer Request when 32 empty stages */ -#define SIFCTR_TFWM_24 (2 << 29) /* Transfer Request when 24 empty stages */ -#define SIFCTR_TFWM_16 (3 << 29) /* Transfer Request when 16 empty stages */ -#define SIFCTR_TFWM_12 (4 << 29) /* Transfer Request when 12 empty stages */ -#define SIFCTR_TFWM_8 (5 << 29) /* Transfer Request when 8 empty stages */ -#define SIFCTR_TFWM_4 (6 << 29) /* Transfer Request when 4 empty stages */ -#define SIFCTR_TFWM_1 (7 << 29) /* Transfer Request when 1 empty stage */ +#define SIFCTR_TFWM_64 (0UL << 29) /* Transfer Request when 64 empty stages */ +#define SIFCTR_TFWM_32 (1UL << 29) /* Transfer Request when 32 empty stages */ +#define SIFCTR_TFWM_24 (2UL << 29) /* Transfer Request when 24 empty stages */ +#define SIFCTR_TFWM_16 (3UL << 29) /* Transfer Request when 16 empty stages */ +#define SIFCTR_TFWM_12 (4UL << 29) /* Transfer Request when 12 empty stages */ +#define SIFCTR_TFWM_8 (5UL << 29) /* Transfer Request when 8 empty stages */ +#define SIFCTR_TFWM_4 (6UL << 29) /* Transfer Request when 4 empty stages */ +#define SIFCTR_TFWM_1 (7UL << 29) /* Transfer Request when 1 empty stage */ #define SIFCTR_TFUA_MASK GENMASK(26, 20) /* Transmit FIFO Usable Area */ #define SIFCTR_TFUA_SHIFT 20 #define SIFCTR_TFUA(i) ((i) << SIFCTR_TFUA_SHIFT) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 7477a11e12be..f2170f4b5077 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1717,6 +1717,10 @@ static int __spi_pump_transfer_message(struct spi_controller *ctlr, pm_runtime_put_noidle(ctlr->dev.parent); dev_err(&ctlr->dev, "Failed to power device: %d\n", ret); + + msg->status = ret; + spi_finalize_current_message(ctlr); + return ret; } } diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile index 9d974424c8c1..7f152167bb05 100644 --- a/drivers/spmi/Makefile +++ b/drivers/spmi/Makefile @@ -2,7 +2,7 @@ # # Makefile for kernel SPMI framework. # -obj-$(CONFIG_SPMI) += spmi.o +obj-$(CONFIG_SPMI) += spmi.o spmi-devres.o obj-$(CONFIG_SPMI_HISI3670) += hisi-spmi-controller.o obj-$(CONFIG_SPMI_MSM_PMIC_ARB) += spmi-pmic-arb.o diff --git a/drivers/spmi/hisi-spmi-controller.c b/drivers/spmi/hisi-spmi-controller.c index 9cbd473487cb..674a350cc676 100644 --- a/drivers/spmi/hisi-spmi-controller.c +++ b/drivers/spmi/hisi-spmi-controller.c @@ -267,10 +267,10 @@ static int spmi_controller_probe(struct platform_device *pdev) struct resource *iores; int ret; - ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller)); - if (!ctrl) { + ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller)); + if (IS_ERR(ctrl)) { dev_err(&pdev->dev, "can not allocate spmi_controller data\n"); - return -ENOMEM; + return PTR_ERR(ctrl); } spmi_controller = spmi_controller_get_drvdata(ctrl); spmi_controller->controller = ctrl; @@ -278,24 +278,21 @@ static int spmi_controller_probe(struct platform_device *pdev) iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!iores) { dev_err(&pdev->dev, "can not get resource!\n"); - ret = -EINVAL; - goto err_put_controller; + return -EINVAL; } spmi_controller->base = devm_ioremap(&pdev->dev, iores->start, resource_size(iores)); if (!spmi_controller->base) { dev_err(&pdev->dev, "can not remap base addr!\n"); - ret = -EADDRNOTAVAIL; - goto err_put_controller; + return -EADDRNOTAVAIL; } ret = of_property_read_u32(pdev->dev.of_node, "hisilicon,spmi-channel", &spmi_controller->channel); if (ret) { dev_err(&pdev->dev, "can not get channel\n"); - ret = -ENODEV; - goto err_put_controller; + return -ENODEV; } platform_set_drvdata(pdev, spmi_controller); @@ -311,25 +308,13 @@ static int spmi_controller_probe(struct platform_device *pdev) ctrl->read_cmd = spmi_read_cmd; ctrl->write_cmd = spmi_write_cmd; - ret = spmi_controller_add(ctrl); + ret = devm_spmi_controller_add(&pdev->dev, ctrl); if (ret) { dev_err(&pdev->dev, "spmi_controller_add failed with error %d!\n", ret); - goto err_put_controller; + return ret; } return 0; - -err_put_controller: - spmi_controller_put(ctrl); - return ret; -} - -static void spmi_del_controller(struct platform_device *pdev) -{ - struct spmi_controller *ctrl = platform_get_drvdata(pdev); - - spmi_controller_remove(ctrl); - spmi_controller_put(ctrl); } static const struct of_device_id spmi_controller_match_table[] = { @@ -342,7 +327,6 @@ MODULE_DEVICE_TABLE(of, spmi_controller_match_table); static struct platform_driver spmi_controller_driver = { .probe = spmi_controller_probe, - .remove_new = spmi_del_controller, .driver = { .name = "hisi_spmi_controller", .of_match_table = spmi_controller_match_table, diff --git a/drivers/spmi/spmi-devres.c b/drivers/spmi/spmi-devres.c new file mode 100644 index 000000000000..62c4b3f24d06 --- /dev/null +++ b/drivers/spmi/spmi-devres.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 Google LLC. + */ + +#include <linux/device.h> +#include <linux/spmi.h> + +static void devm_spmi_controller_release(struct device *parent, void *res) +{ + spmi_controller_put(*(struct spmi_controller **)res); +} + +struct spmi_controller *devm_spmi_controller_alloc(struct device *parent, size_t size) +{ + struct spmi_controller **ptr, *ctrl; + + ptr = devres_alloc(devm_spmi_controller_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + ctrl = spmi_controller_alloc(parent, size); + if (IS_ERR(ctrl)) { + devres_free(ptr); + return ctrl; + } + + *ptr = ctrl; + devres_add(parent, ptr); + + return ctrl; +} +EXPORT_SYMBOL_GPL(devm_spmi_controller_alloc); + +static void devm_spmi_controller_remove(struct device *parent, void *res) +{ + spmi_controller_remove(*(struct spmi_controller **)res); +} + +int devm_spmi_controller_add(struct device *parent, struct spmi_controller *ctrl) +{ + struct spmi_controller **ptr; + int ret; + + ptr = devres_alloc(devm_spmi_controller_remove, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = spmi_controller_add(ctrl); + if (ret) { + devres_free(ptr); + return ret; + } + + *ptr = ctrl; + devres_add(parent, ptr); + + return 0; + +} +EXPORT_SYMBOL_GPL(devm_spmi_controller_add); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SPMI devres helpers"); diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c index b3c991e1ea40..5079442f8ea1 100644 --- a/drivers/spmi/spmi-mtk-pmif.c +++ b/drivers/spmi/spmi-mtk-pmif.c @@ -50,6 +50,7 @@ struct pmif { struct clk_bulk_data clks[PMIF_MAX_CLKS]; size_t nclks; const struct pmif_data *data; + raw_spinlock_t lock; }; static const char * const pmif_clock_names[] = { @@ -314,6 +315,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, struct ch_reg *inf_reg; int ret; u32 data, cmd; + unsigned long flags; /* Check for argument validation. */ if (sid & ~0xf) { @@ -334,6 +336,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, else return -EINVAL; + raw_spin_lock_irqsave(&arb->lock, flags); /* Wait for Software Interface FSM state to be IDLE. */ inf_reg = &arb->chan; ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta], @@ -343,6 +346,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, /* set channel ready if the data has transferred */ if (pmif_is_fsm_vldclr(arb)) pmif_writel(arb, 1, inf_reg->ch_rdy); + raw_spin_unlock_irqrestore(&arb->lock, flags); dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n"); return ret; } @@ -350,6 +354,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, /* Send the command. */ cmd = (opc << 30) | (sid << 24) | ((len - 1) << 16) | addr; pmif_writel(arb, cmd, inf_reg->ch_send); + raw_spin_unlock_irqrestore(&arb->lock, flags); /* * Wait for Software Interface FSM state to be WFVLDCLR, @@ -376,7 +381,14 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, struct pmif *arb = spmi_controller_get_drvdata(ctrl); struct ch_reg *inf_reg; int ret; - u32 data, cmd; + u32 data, wdata, cmd; + unsigned long flags; + + /* Check for argument validation. */ + if (unlikely(sid & ~0xf)) { + dev_err(&ctrl->dev, "exceed the max slv id\n"); + return -EINVAL; + } if (len > 4) { dev_err(&ctrl->dev, "pmif supports 1..4 bytes per trans, but:%zu requested", len); @@ -394,6 +406,10 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, else return -EINVAL; + /* Set the write data. */ + memcpy(&wdata, buf, len); + + raw_spin_lock_irqsave(&arb->lock, flags); /* Wait for Software Interface FSM state to be IDLE. */ inf_reg = &arb->chan; ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta], @@ -403,17 +419,17 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, /* set channel ready if the data has transferred */ if (pmif_is_fsm_vldclr(arb)) pmif_writel(arb, 1, inf_reg->ch_rdy); + raw_spin_unlock_irqrestore(&arb->lock, flags); dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n"); return ret; } - /* Set the write data. */ - memcpy(&data, buf, len); - pmif_writel(arb, data, inf_reg->wdata); + pmif_writel(arb, wdata, inf_reg->wdata); /* Send the command. */ cmd = (opc << 30) | BIT(29) | (sid << 24) | ((len - 1) << 16) | addr; pmif_writel(arb, cmd, inf_reg->ch_send); + raw_spin_unlock_irqrestore(&arb->lock, flags); return 0; } @@ -437,44 +453,39 @@ static int mtk_spmi_probe(struct platform_device *pdev) int err, i; u32 chan_offset; - ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*arb)); - if (!ctrl) - return -ENOMEM; + ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*arb)); + if (IS_ERR(ctrl)) + return PTR_ERR(ctrl); arb = spmi_controller_get_drvdata(ctrl); arb->data = device_get_match_data(&pdev->dev); if (!arb->data) { - err = -EINVAL; dev_err(&pdev->dev, "Cannot get drv_data\n"); - goto err_put_ctrl; + return -EINVAL; } arb->base = devm_platform_ioremap_resource_byname(pdev, "pmif"); - if (IS_ERR(arb->base)) { - err = PTR_ERR(arb->base); - goto err_put_ctrl; - } + if (IS_ERR(arb->base)) + return PTR_ERR(arb->base); arb->spmimst_base = devm_platform_ioremap_resource_byname(pdev, "spmimst"); - if (IS_ERR(arb->spmimst_base)) { - err = PTR_ERR(arb->spmimst_base); - goto err_put_ctrl; - } + if (IS_ERR(arb->spmimst_base)) + return PTR_ERR(arb->spmimst_base); arb->nclks = ARRAY_SIZE(pmif_clock_names); for (i = 0; i < arb->nclks; i++) arb->clks[i].id = pmif_clock_names[i]; - err = devm_clk_bulk_get(&pdev->dev, arb->nclks, arb->clks); + err = clk_bulk_get(&pdev->dev, arb->nclks, arb->clks); if (err) { dev_err(&pdev->dev, "Failed to get clocks: %d\n", err); - goto err_put_ctrl; + return err; } err = clk_bulk_prepare_enable(arb->nclks, arb->clks); if (err) { dev_err(&pdev->dev, "Failed to enable clocks: %d\n", err); - goto err_put_ctrl; + goto err_put_clks; } ctrl->cmd = pmif_arb_cmd; @@ -488,6 +499,8 @@ static int mtk_spmi_probe(struct platform_device *pdev) arb->chan.ch_send = PMIF_SWINF_0_ACC + chan_offset; arb->chan.ch_rdy = PMIF_SWINF_0_VLD_CLR + chan_offset; + raw_spin_lock_init(&arb->lock); + platform_set_drvdata(pdev, ctrl); err = spmi_controller_add(ctrl); @@ -498,8 +511,8 @@ static int mtk_spmi_probe(struct platform_device *pdev) err_domain_remove: clk_bulk_disable_unprepare(arb->nclks, arb->clks); -err_put_ctrl: - spmi_controller_put(ctrl); +err_put_clks: + clk_bulk_put(arb->nclks, arb->clks); return err; } @@ -508,9 +521,9 @@ static void mtk_spmi_remove(struct platform_device *pdev) struct spmi_controller *ctrl = platform_get_drvdata(pdev); struct pmif *arb = spmi_controller_get_drvdata(ctrl); - clk_bulk_disable_unprepare(arb->nclks, arb->clks); spmi_controller_remove(ctrl); - spmi_controller_put(ctrl); + clk_bulk_disable_unprepare(arb->nclks, arb->clks); + clk_bulk_put(arb->nclks, arb->clks); } static const struct of_device_id mtk_spmi_match_table[] = { diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index dcb675d980d4..9ed1180fe31f 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -1443,9 +1443,9 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) u32 channel, ee, hw_ver; int err; - ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb)); - if (!ctrl) - return -ENOMEM; + ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb)); + if (IS_ERR(ctrl)) + return PTR_ERR(ctrl); pmic_arb = spmi_controller_get_drvdata(ctrl); pmic_arb->spmic = ctrl; @@ -1462,20 +1462,16 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); core = devm_ioremap(&ctrl->dev, res->start, resource_size(res)); - if (IS_ERR(core)) { - err = PTR_ERR(core); - goto err_put_ctrl; - } + if (IS_ERR(core)) + return PTR_ERR(core); pmic_arb->core_size = resource_size(res); pmic_arb->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID, sizeof(*pmic_arb->ppid_to_apid), GFP_KERNEL); - if (!pmic_arb->ppid_to_apid) { - err = -ENOMEM; - goto err_put_ctrl; - } + if (!pmic_arb->ppid_to_apid) + return -ENOMEM; hw_ver = readl_relaxed(core + PMIC_ARB_VERSION); @@ -1499,19 +1495,15 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) "obsrvr"); pmic_arb->rd_base = devm_ioremap(&ctrl->dev, res->start, resource_size(res)); - if (IS_ERR(pmic_arb->rd_base)) { - err = PTR_ERR(pmic_arb->rd_base); - goto err_put_ctrl; - } + if (IS_ERR(pmic_arb->rd_base)) + return PTR_ERR(pmic_arb->rd_base); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "chnls"); pmic_arb->wr_base = devm_ioremap(&ctrl->dev, res->start, resource_size(res)); - if (IS_ERR(pmic_arb->wr_base)) { - err = PTR_ERR(pmic_arb->wr_base); - goto err_put_ctrl; - } + if (IS_ERR(pmic_arb->wr_base)) + return PTR_ERR(pmic_arb->wr_base); } pmic_arb->max_periphs = PMIC_ARB_MAX_PERIPHS; @@ -1522,10 +1514,9 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) of_property_read_u32(pdev->dev.of_node, "qcom,bus-id", &pmic_arb->bus_instance); if (pmic_arb->bus_instance > 1) { - err = -EINVAL; dev_err(&pdev->dev, "invalid bus instance (%u) specified\n", pmic_arb->bus_instance); - goto err_put_ctrl; + return -EINVAL; } if (pmic_arb->bus_instance == 0) { @@ -1543,10 +1534,9 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) } if (pmic_arb->base_apid + pmic_arb->apid_count > pmic_arb->max_periphs) { - err = -EINVAL; dev_err(&pdev->dev, "Unsupported APID count %d detected\n", pmic_arb->base_apid + pmic_arb->apid_count); - goto err_put_ctrl; + return -EINVAL; } } else if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) { pmic_arb->base_apid = 0; @@ -1554,55 +1544,45 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) PMIC_ARB_FEATURES_PERIPH_MASK; if (pmic_arb->apid_count > pmic_arb->max_periphs) { - err = -EINVAL; dev_err(&pdev->dev, "Unsupported APID count %d detected\n", pmic_arb->apid_count); - goto err_put_ctrl; + return -EINVAL; } } pmic_arb->apid_data = devm_kcalloc(&ctrl->dev, pmic_arb->max_periphs, sizeof(*pmic_arb->apid_data), GFP_KERNEL); - if (!pmic_arb->apid_data) { - err = -ENOMEM; - goto err_put_ctrl; - } + if (!pmic_arb->apid_data) + return -ENOMEM; dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n", pmic_arb->ver_ops->ver_str, hw_ver); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr"); pmic_arb->intr = devm_ioremap_resource(&ctrl->dev, res); - if (IS_ERR(pmic_arb->intr)) { - err = PTR_ERR(pmic_arb->intr); - goto err_put_ctrl; - } + if (IS_ERR(pmic_arb->intr)) + return PTR_ERR(pmic_arb->intr); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg"); pmic_arb->cnfg = devm_ioremap_resource(&ctrl->dev, res); - if (IS_ERR(pmic_arb->cnfg)) { - err = PTR_ERR(pmic_arb->cnfg); - goto err_put_ctrl; - } + if (IS_ERR(pmic_arb->cnfg)) + return PTR_ERR(pmic_arb->cnfg); pmic_arb->irq = platform_get_irq_byname(pdev, "periph_irq"); - if (pmic_arb->irq < 0) { - err = pmic_arb->irq; - goto err_put_ctrl; - } + if (pmic_arb->irq < 0) + return pmic_arb->irq; err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel); if (err) { dev_err(&pdev->dev, "channel unspecified.\n"); - goto err_put_ctrl; + return err; } if (channel > 5) { dev_err(&pdev->dev, "invalid channel (%u) specified.\n", channel); - err = -EINVAL; - goto err_put_ctrl; + return -EINVAL; } pmic_arb->channel = channel; @@ -1610,22 +1590,19 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee); if (err) { dev_err(&pdev->dev, "EE unspecified.\n"); - goto err_put_ctrl; + return err; } if (ee > 5) { dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee); - err = -EINVAL; - goto err_put_ctrl; + return -EINVAL; } pmic_arb->ee = ee; mapping_table = devm_kcalloc(&ctrl->dev, pmic_arb->max_periphs, sizeof(*mapping_table), GFP_KERNEL); - if (!mapping_table) { - err = -ENOMEM; - goto err_put_ctrl; - } + if (!mapping_table) + return -ENOMEM; pmic_arb->mapping_table = mapping_table; /* Initialize max_apid/min_apid to the opposite bounds, during @@ -1645,7 +1622,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "could not read APID->PPID mapping table, rc= %d\n", err); - goto err_put_ctrl; + return err; } } @@ -1654,8 +1631,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) &pmic_arb_irq_domain_ops, pmic_arb); if (!pmic_arb->domain) { dev_err(&pdev->dev, "unable to create irq_domain\n"); - err = -ENOMEM; - goto err_put_ctrl; + return -ENOMEM; } irq_set_chained_handler_and_data(pmic_arb->irq, pmic_arb_chained_irq, @@ -1669,8 +1645,6 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) err_domain_remove: irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL); irq_domain_remove(pmic_arb->domain); -err_put_ctrl: - spmi_controller_put(ctrl); return err; } @@ -1681,7 +1655,6 @@ static void spmi_pmic_arb_remove(struct platform_device *pdev) spmi_controller_remove(ctrl); irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL); irq_domain_remove(pmic_arb->domain); - spmi_controller_put(ctrl); } static const struct of_device_id spmi_pmic_arb_match_table[] = { diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index 93cd4a34debc..3a60fd2e09e1 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -448,11 +448,11 @@ struct spmi_controller *spmi_controller_alloc(struct device *parent, int id; if (WARN_ON(!parent)) - return NULL; + return ERR_PTR(-EINVAL); ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL); if (!ctrl) - return NULL; + return ERR_PTR(-ENOMEM); device_initialize(&ctrl->dev); ctrl->dev.type = &spmi_ctrl_type; @@ -466,7 +466,7 @@ struct spmi_controller *spmi_controller_alloc(struct device *parent, dev_err(parent, "unable to allocate SPMI controller identifier.\n"); spmi_controller_put(ctrl); - return NULL; + return ERR_PTR(id); } ctrl->nr = id; diff --git a/drivers/staging/greybus/i2c.c b/drivers/staging/greybus/i2c.c index de2f6516da09..22325ab9d652 100644 --- a/drivers/staging/greybus/i2c.c +++ b/drivers/staging/greybus/i2c.c @@ -264,7 +264,7 @@ static int gb_i2c_probe(struct gbphy_device *gbphy_dev, /* Looks good; up our i2c adapter */ adapter = &gb_i2c_dev->adapter; adapter->owner = THIS_MODULE; - adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adapter->class = I2C_CLASS_HWMON; adapter->algo = &gb_i2c_algorithm; adapter->dev.parent = &gbphy_dev->dev; diff --git a/drivers/staging/rtl8192e/Makefile b/drivers/staging/rtl8192e/Makefile index 6af519938868..a1492215dab1 100644 --- a/drivers/staging/rtl8192e/Makefile +++ b/drivers/staging/rtl8192e/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 rtllib-objs := \ - dot11d.o \ rtllib_module.o \ rtllib_rx.o \ rtllib_tx.o \ diff --git a/drivers/staging/rtl8192e/dot11d.c b/drivers/staging/rtl8192e/dot11d.c deleted file mode 100644 index d0b7332645be..000000000000 --- a/drivers/staging/rtl8192e/dot11d.c +++ /dev/null @@ -1,165 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/****************************************************************************** - * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. - * - * Contact Information: - * wlanfae <wlanfae@realtek.com> - ******************************************************************************/ -#include "dot11d.h" - -struct channel_list { - u8 channel[32]; - u8 len; -}; - -static struct channel_list channel_array[] = { - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 52, 56, 60, 64, - 149, 153, 157, 161, 165}, 24}, - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 11}, - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 36, 40, 44, 48, 52, 56, - 60, 64}, 21}, - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52, - 56, 60, 64}, 22}, - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52, - 56, 60, 64}, 22}, - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52, - 56, 60, 64}, 22}, - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52, - 56, 60, 64}, 22}, - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, 14}, - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, - {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 36, 40, 44, 48, 52, - 56, 60, 64}, 21} -}; - -void dot11d_init(struct rtllib_device *ieee) -{ - struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(ieee); - - dot11d_info->enabled = false; - - dot11d_info->state = DOT11D_STATE_NONE; - dot11d_info->country_len = 0; - memset(dot11d_info->channel_map, 0, MAX_CHANNEL_NUMBER + 1); - memset(dot11d_info->max_tx_power_list, 0xFF, MAX_CHANNEL_NUMBER + 1); - RESET_CIE_WATCHDOG(ieee); -} -EXPORT_SYMBOL(dot11d_init); - -void dot11d_channel_map(u8 channel_plan, struct rtllib_device *ieee) -{ - int i, max_chan = 14, min_chan = 1; - - ieee->global_domain = false; - - if (channel_array[channel_plan].len != 0) { - memset(GET_DOT11D_INFO(ieee)->channel_map, 0, - sizeof(GET_DOT11D_INFO(ieee)->channel_map)); - for (i = 0; i < channel_array[channel_plan].len; i++) { - if (channel_array[channel_plan].channel[i] < min_chan || - channel_array[channel_plan].channel[i] > max_chan) - break; - GET_DOT11D_INFO(ieee)->channel_map[channel_array - [channel_plan].channel[i]] = 1; - } - } - - switch (channel_plan) { - case COUNTRY_CODE_GLOBAL_DOMAIN: - ieee->global_domain = true; - for (i = 12; i <= 14; i++) - GET_DOT11D_INFO(ieee)->channel_map[i] = 2; - ieee->bss_start_channel = 10; - break; - - case COUNTRY_CODE_WORLD_WIDE_13: - for (i = 12; i <= 13; i++) - GET_DOT11D_INFO(ieee)->channel_map[i] = 2; - ieee->bss_start_channel = 10; - break; - - default: - ieee->bss_start_channel = 1; - break; - } -} -EXPORT_SYMBOL(dot11d_channel_map); - -void dot11d_reset(struct rtllib_device *ieee) -{ - struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(ieee); - u32 i; - - memset(dot11d_info->channel_map, 0, MAX_CHANNEL_NUMBER + 1); - memset(dot11d_info->max_tx_power_list, 0xFF, MAX_CHANNEL_NUMBER + 1); - for (i = 1; i <= 11; i++) - (dot11d_info->channel_map)[i] = 1; - for (i = 12; i <= 14; i++) - (dot11d_info->channel_map)[i] = 2; - dot11d_info->state = DOT11D_STATE_NONE; - dot11d_info->country_len = 0; - RESET_CIE_WATCHDOG(ieee); -} - -void dot11d_update_country(struct rtllib_device *dev, u8 *address, - u16 country_len, u8 *country) -{ - struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(dev); - u8 i, j, number_of_triples, max_channel_number; - struct chnl_txpow_triple *triple; - - memset(dot11d_info->channel_map, 0, MAX_CHANNEL_NUMBER + 1); - memset(dot11d_info->max_tx_power_list, 0xFF, MAX_CHANNEL_NUMBER + 1); - max_channel_number = 0; - number_of_triples = (country_len - 3) / 3; - triple = (struct chnl_txpow_triple *)(country + 3); - for (i = 0; i < number_of_triples; i++) { - if (max_channel_number >= triple->first_channel) { - netdev_info(dev->dev, - "%s: Invalid country IE, skip it......1\n", - __func__); - return; - } - if (MAX_CHANNEL_NUMBER < (triple->first_channel + - triple->num_channels)) { - netdev_info(dev->dev, - "%s: Invalid country IE, skip it......2\n", - __func__); - return; - } - - for (j = 0; j < triple->num_channels; j++) { - dot11d_info->channel_map[triple->first_channel + j] = 1; - dot11d_info->max_tx_power_list[triple->first_channel + j] = - triple->max_tx_power; - max_channel_number = triple->first_channel + j; - } - - triple = (struct chnl_txpow_triple *)((u8 *)triple + 3); - } - - UPDATE_CIE_SRC(dev, address); - - dot11d_info->country_len = country_len; - memcpy(dot11d_info->country_buffer, country, country_len); - dot11d_info->state = DOT11D_STATE_LEARNED; -} - -void dot11d_scan_complete(struct rtllib_device *dev) -{ - struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(dev); - - switch (dot11d_info->state) { - case DOT11D_STATE_LEARNED: - dot11d_info->state = DOT11D_STATE_DONE; - break; - case DOT11D_STATE_DONE: - dot11d_reset(dev); - break; - case DOT11D_STATE_NONE: - break; - } -} diff --git a/drivers/staging/rtl8192e/dot11d.h b/drivers/staging/rtl8192e/dot11d.h deleted file mode 100644 index 6d2b93acfa43..000000000000 --- a/drivers/staging/rtl8192e/dot11d.h +++ /dev/null @@ -1,84 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/****************************************************************************** - * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. - * - * Contact Information: - * wlanfae <wlanfae@realtek.com> - ******************************************************************************/ -#ifndef __INC_DOT11D_H -#define __INC_DOT11D_H - -#include "rtllib.h" - -struct chnl_txpow_triple { - u8 first_channel; - u8 num_channels; - u8 max_tx_power; -}; - -enum dot11d_state { - DOT11D_STATE_NONE = 0, - DOT11D_STATE_LEARNED, - DOT11D_STATE_DONE, -}; - -/** - * struct rt_dot11d_info * @country_len: value greater than 0 if - * @country_buffer contains valid country information element. - * @channel_map: holds channel values - * 0 - invalid, - * 1 - valid (active scan), - * 2 - valid (passive scan) - * @country_src_addr - Source AP of the country IE - */ - -struct rt_dot11d_info { - bool enabled; - - u16 country_len; - u8 country_buffer[MAX_IE_LEN]; - u8 country_src_addr[6]; - u8 country_watchdog; - - u8 channel_map[MAX_CHANNEL_NUMBER + 1]; - u8 max_tx_power_list[MAX_CHANNEL_NUMBER + 1]; - - enum dot11d_state state; -}; - -static inline void copy_mac_addr(unsigned char *des, unsigned char *src) -{ - memcpy(des, src, 6); -} - -#define GET_DOT11D_INFO(__ieee_dev) \ - ((struct rt_dot11d_info *)((__ieee_dev)->dot11d_info)) - -#define IS_DOT11D_ENABLE(__ieee_dev) \ - (GET_DOT11D_INFO(__ieee_dev)->enabled) -#define IS_COUNTRY_IE_VALID(__ieee_dev) \ - (GET_DOT11D_INFO(__ieee_dev)->country_len > 0) - -#define IS_EQUAL_CIE_SRC(__ieee_dev, __address) \ - ether_addr_equal_unaligned( \ - GET_DOT11D_INFO(__ieee_dev)->country_src_addr, __address) -#define UPDATE_CIE_SRC(__ieee_dev, __address) \ - copy_mac_addr(GET_DOT11D_INFO(__ieee_dev)->country_src_addr, __address) - -#define GET_CIE_WATCHDOG(__ieee_dev) \ - (GET_DOT11D_INFO(__ieee_dev)->country_watchdog) -static inline void RESET_CIE_WATCHDOG(struct rtllib_device *__ieee_dev) -{ - GET_CIE_WATCHDOG(__ieee_dev) = 0; -} - -#define UPDATE_CIE_WATCHDOG(__ieee_dev) (++GET_CIE_WATCHDOG(__ieee_dev)) - -void dot11d_init(struct rtllib_device *dev); -void dot11d_channel_map(u8 channel_plan, struct rtllib_device *ieee); -void dot11d_reset(struct rtllib_device *dev); -void dot11d_update_country(struct rtllib_device *dev, u8 *address, - u16 country_len, u8 *country); -void dot11d_scan_complete(struct rtllib_device *dev); - -#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c index eba8364d0ff2..7f0c160bc741 100644 --- a/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c @@ -63,9 +63,9 @@ bool rtl92e_send_cmd_pkt(struct net_device *dev, u32 type, const void *data, if (type == DESC_PACKET_TYPE_INIT && (!priv->rtllib->check_nic_enough_desc(dev, TXCMD_QUEUE) || - (!skb_queue_empty(&priv->rtllib->skb_waitQ[TXCMD_QUEUE])) || + (!skb_queue_empty(&priv->rtllib->skb_waitq[TXCMD_QUEUE])) || (priv->rtllib->queue_stop))) { - skb_queue_tail(&priv->rtllib->skb_waitQ[TXCMD_QUEUE], + skb_queue_tail(&priv->rtllib->skb_waitq[TXCMD_QUEUE], skb); } else { priv->rtllib->softmac_hard_start_xmit(skb, dev); diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c index e93394c51264..c7a2eae2fdb9 100644 --- a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c @@ -51,12 +51,12 @@ void rtl92e_set_reg(struct net_device *dev, u8 variable, u8 *val) case HW_VAR_MEDIA_STATUS: { - enum rt_op_mode OpMode = *((enum rt_op_mode *)(val)); + enum rt_op_mode op_mode = *((enum rt_op_mode *)(val)); u8 btMsr = rtl92e_readb(dev, MSR); btMsr &= 0xfc; - switch (OpMode) { + switch (op_mode) { case RT_OP_MODE_INFRASTRUCTURE: btMsr |= MSR_INFRA; break; @@ -261,7 +261,6 @@ static void _rtl92e_read_eeprom_info(struct net_device *dev) priv->eeprom_customer_id = usValue & 0xff; usValue = rtl92e_eeprom_read(dev, EEPROM_ICVersion_ChannelPlan >> 1); - priv->eeprom_chnl_plan = usValue & 0xff; IC_Version = (usValue & 0xff00) >> 8; ICVer8192 = IC_Version & 0xf; @@ -283,7 +282,6 @@ static void _rtl92e_read_eeprom_info(struct net_device *dev) priv->eeprom_vid = 0; priv->eeprom_did = 0; priv->eeprom_customer_id = 0; - priv->eeprom_chnl_plan = 0; } if (!priv->autoload_fail_flag) { @@ -387,25 +385,15 @@ static void _rtl92e_read_eeprom_info(struct net_device *dev) rtl92e_init_adaptive_rate(dev); - priv->chnl_plan = priv->eeprom_chnl_plan; - switch (priv->eeprom_customer_id) { case EEPROM_CID_NetCore: priv->customer_id = RT_CID_819X_NETCORE; break; case EEPROM_CID_TOSHIBA: priv->customer_id = RT_CID_TOSHIBA; - if (priv->eeprom_chnl_plan & 0x80) - priv->chnl_plan = priv->eeprom_chnl_plan & 0x7f; - else - priv->chnl_plan = 0x0; break; } - if (priv->chnl_plan > CHANNEL_PLAN_LEN - 1) - priv->chnl_plan = 0; - priv->chnl_plan = COUNTRY_CODE_WORLD_WIDE_13; - if (priv->eeprom_vid == 0x1186 && priv->eeprom_did == 0x3304) priv->rtllib->bSupportRemoteWakeUp = true; else @@ -891,11 +879,11 @@ void rtl92e_fill_tx_desc(struct net_device *dev, struct tx_desc *pdesc, memset(pTxFwInfo, 0, sizeof(struct tx_fwinfo_8190pci)); pTxFwInfo->TxHT = (cb_desc->data_rate & 0x80) ? 1 : 0; pTxFwInfo->TxRate = _rtl92e_rate_mgn_to_hw(cb_desc->data_rate); - pTxFwInfo->EnableCPUDur = cb_desc->bTxEnableFwCalcDur; + pTxFwInfo->EnableCPUDur = cb_desc->tx_enable_fw_calc_dur; pTxFwInfo->Short = _rtl92e_query_is_short(pTxFwInfo->TxHT, pTxFwInfo->TxRate, cb_desc); - if (cb_desc->bAMPDUEnable) { + if (cb_desc->ampdu_enable) { pTxFwInfo->AllowAggregation = 1; pTxFwInfo->RxMF = cb_desc->ampdu_factor; pTxFwInfo->RxAMD = cb_desc->ampdu_density; @@ -1685,8 +1673,6 @@ bool rtl92e_get_rx_stats(struct net_device *dev, struct rtllib_rx_stats *stats, stats->TimeStampLow = pDrvInfo->TSFL; stats->TimeStampHigh = rtl92e_readl(dev, TSFR + 4); - rtl92e_update_rx_pkt_timestamp(dev, stats); - if ((stats->RxBufShift + stats->RxDrvInfoSize) > 0) stats->bShift = 1; @@ -1707,12 +1693,12 @@ void rtl92e_stop_adapter(struct net_device *dev, bool reset) { struct r8192_priv *priv = rtllib_priv(dev); int i; - u8 OpMode; + u8 op_mode; u8 u1bTmp; u32 ulRegRead; - OpMode = RT_OP_MODE_NO_LINK; - priv->rtllib->SetHwRegHandler(dev, HW_VAR_MEDIA_STATUS, &OpMode); + op_mode = RT_OP_MODE_NO_LINK; + priv->rtllib->SetHwRegHandler(dev, HW_VAR_MEDIA_STATUS, &op_mode); if (!priv->rtllib->bSupportRemoteWakeUp) { u1bTmp = 0x0; @@ -1742,7 +1728,7 @@ void rtl92e_stop_adapter(struct net_device *dev, bool reset) } for (i = 0; i < MAX_QUEUE_SIZE; i++) - skb_queue_purge(&priv->rtllib->skb_waitQ[i]); + skb_queue_purge(&priv->rtllib->skb_waitq[i]); skb_queue_purge(&priv->skb_queue); } @@ -1767,20 +1753,17 @@ void rtl92e_update_ratr_table(struct net_device *dev) ratr_value &= 0x00000FF7; break; case WIRELESS_MODE_N_24G: - if (ieee->ht_info->peer_mimo_ps == 0) - ratr_value &= 0x0007F007; - else - ratr_value &= 0x000FF007; + ratr_value &= 0x000FF007; break; default: break; } ratr_value &= 0x0FFFFFFF; if (ieee->ht_info->cur_tx_bw40mhz && - ieee->ht_info->bCurShortGI40MHz) + ieee->ht_info->cur_short_gi_40mhz) ratr_value |= 0x80000000; else if (!ieee->ht_info->cur_tx_bw40mhz && - ieee->ht_info->bCurShortGI20MHz) + ieee->ht_info->cur_short_gi_20mhz) ratr_value |= 0x80000000; rtl92e_writel(dev, RATR0 + rate_index * 4, ratr_value); rtl92e_writeb(dev, UFWP, 1); @@ -1814,7 +1797,7 @@ rtl92e_init_variables(struct net_device *dev) IMR_MGNTDOK | IMR_COMDOK | IMR_HIGHDOK | IMR_BDOK | IMR_RXCMDOK | IMR_TIMEOUT0 | IMR_RDU | IMR_RXFOVW | IMR_TXFOVW | - IMR_BcnInt | IMR_TBDOK | IMR_TBDER); + IMR_TBDOK | IMR_TBDER); priv->bfirst_after_down = false; } diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c index 4d12d7385041..e1bd4d67e862 100644 --- a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c @@ -671,16 +671,16 @@ static void _rtl92e_phy_switch_channel_work_item(struct net_device *dev) _rtl92e_phy_switch_channel(dev, priv->chan); } -u8 rtl92e_set_channel(struct net_device *dev, u8 channel) +void rtl92e_set_channel(struct net_device *dev, u8 channel) { struct r8192_priv *priv = rtllib_priv(dev); if (!priv->up) { netdev_err(dev, "%s(): Driver is not initialized\n", __func__); - return false; + return; } if (priv->sw_chnl_in_progress) - return false; + return; switch (priv->rtllib->mode) { case WIRELESS_MODE_B: @@ -688,7 +688,7 @@ u8 rtl92e_set_channel(struct net_device *dev, u8 channel) netdev_warn(dev, "Channel %d not available in 802.11b.\n", channel); - return false; + return; } break; case WIRELESS_MODE_G: @@ -697,7 +697,7 @@ u8 rtl92e_set_channel(struct net_device *dev, u8 channel) netdev_warn(dev, "Channel %d not available in 802.11g.\n", channel); - return false; + return; } break; } @@ -714,7 +714,7 @@ u8 rtl92e_set_channel(struct net_device *dev, u8 channel) if (priv->up) _rtl92e_phy_switch_channel_work_item(dev); priv->sw_chnl_in_progress = false; - return true; + return; } static void _rtl92e_cck_tx_power_track_bw_switch_tssi(struct net_device *dev) diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.h index 6c4c33ded6a9..ff4b4004b0d0 100644 --- a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.h +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.h @@ -41,7 +41,7 @@ void rtl92e_get_tx_power(struct net_device *dev); void rtl92e_set_tx_power(struct net_device *dev, u8 channel); u8 rtl92e_config_rf_path(struct net_device *dev, enum rf90_radio_path eRFPath); -u8 rtl92e_set_channel(struct net_device *dev, u8 channel); +void rtl92e_set_channel(struct net_device *dev, u8 channel); void rtl92e_set_bw_mode(struct net_device *dev, enum ht_channel_width bandwidth, enum ht_extchnl_offset Offset); diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c index 995daab906c9..6815d18a7919 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c @@ -226,16 +226,6 @@ static void _rtl92e_tx_timeout(struct net_device *dev, unsigned int txqueue) netdev_info(dev, "TXTIMEOUT"); } -static void _rtl92e_set_chan(struct net_device *dev, short ch) -{ - struct r8192_priv *priv = rtllib_priv(dev); - - priv->chan = ch; - - if (priv->rf_set_chan) - priv->rf_set_chan(dev, priv->chan); -} - static void _rtl92e_update_cap(struct net_device *dev, u16 cap) { struct r8192_priv *priv = rtllib_priv(dev); @@ -297,7 +287,6 @@ static void _rtl92e_update_beacon(void *data) if (ieee->ht_info->current_ht_support) HT_update_self_and_peer_setting(ieee, net); ieee->ht_info->current_rt2rt_long_slot_time = net->bssht.bd_rt2rt_long_slot_time; - ieee->ht_info->RT2RT_HT_Mode = net->bssht.rt2rt_ht_mode; _rtl92e_update_cap(dev, net->capability); } @@ -426,38 +415,6 @@ static int _rtl92e_handle_assoc_response(struct net_device *dev, return 0; } -static void _rtl92e_prepare_beacon(struct tasklet_struct *t) -{ - struct r8192_priv *priv = from_tasklet(priv, t, - irq_prepare_beacon_tasklet); - struct net_device *dev = priv->rtllib->dev; - struct sk_buff *pskb = NULL, *pnewskb = NULL; - struct cb_desc *tcb_desc = NULL; - struct rtl8192_tx_ring *ring = NULL; - struct tx_desc *pdesc = NULL; - - ring = &priv->tx_ring[BEACON_QUEUE]; - pskb = __skb_dequeue(&ring->queue); - kfree_skb(pskb); - - pnewskb = rtllib_get_beacon(priv->rtllib); - if (!pnewskb) - return; - - tcb_desc = (struct cb_desc *)(pnewskb->cb + 8); - tcb_desc->queue_index = BEACON_QUEUE; - tcb_desc->data_rate = 2; - tcb_desc->ratr_index = 7; - tcb_desc->tx_dis_rate_fallback = 1; - tcb_desc->tx_use_drv_assinged_rate = 1; - skb_push(pnewskb, priv->rtllib->tx_headroom); - - pdesc = &ring->desc[0]; - rtl92e_fill_tx_desc(dev, pdesc, tcb_desc, pnewskb); - __skb_queue_tail(&ring->queue, pnewskb); - pdesc->OWN = 1; -} - void rtl92e_config_rate(struct net_device *dev, u16 *rate_config) { struct r8192_priv *priv = rtllib_priv(dev); @@ -685,7 +642,7 @@ static void _rtl92e_init_priv_handler(struct net_device *dev) struct r8192_priv *priv = rtllib_priv(dev); priv->rtllib->softmac_hard_start_xmit = _rtl92e_hard_start_xmit; - priv->rtllib->set_chan = _rtl92e_set_chan; + priv->rtllib->set_chan = rtl92e_set_channel; priv->rtllib->link_change = rtl92e_link_change; priv->rtllib->softmac_data_hard_start_xmit = _rtl92e_hard_data_xmit; priv->rtllib->check_nic_enough_desc = _rtl92e_check_nic_enough_desc; @@ -694,7 +651,6 @@ static void _rtl92e_init_priv_handler(struct net_device *dev) priv->rtllib->set_wireless_mode = rtl92e_set_wireless_mode; priv->rtllib->leisure_ps_leave = rtl92e_leisure_ps_leave; priv->rtllib->set_bw_mode_handler = rtl92e_set_bw_mode; - priv->rf_set_chan = rtl92e_set_channel; priv->rtllib->sta_wake_up = rtl92e_hw_wakeup; priv->rtllib->enter_sleep_state = rtl92e_enter_sleep; @@ -767,7 +723,7 @@ static void _rtl92e_init_priv_variable(struct net_device *dev) skb_queue_head_init(&priv->skb_queue); for (i = 0; i < MAX_QUEUE_SIZE; i++) - skb_queue_head_init(&priv->rtllib->skb_waitQ[i]); + skb_queue_head_init(&priv->rtllib->skb_waitq[i]); } static void _rtl92e_init_priv_lock(struct r8192_priv *priv) @@ -796,8 +752,6 @@ static void _rtl92e_init_priv_task(struct net_device *dev) INIT_DELAYED_WORK(&priv->rtllib->hw_sleep_wq, (void *)rtl92e_hw_sleep_wq); tasklet_setup(&priv->irq_rx_tasklet, _rtl92e_irq_rx_tasklet); tasklet_setup(&priv->irq_tx_tasklet, _rtl92e_irq_tx_tasklet); - tasklet_setup(&priv->irq_prepare_beacon_tasklet, - _rtl92e_prepare_beacon); } static short _rtl92e_get_channel_map(struct net_device *dev) @@ -806,13 +760,6 @@ static short _rtl92e_get_channel_map(struct net_device *dev) struct r8192_priv *priv = rtllib_priv(dev); - if (priv->chnl_plan >= COUNTRY_CODE_MAX) { - netdev_info(dev, - "rtl819x_init:Error channel plan! Set to default.\n"); - priv->chnl_plan = COUNTRY_CODE_FCC; - } - dot11d_init(priv->rtllib); - dot11d_channel_map(priv->chnl_plan, priv->rtllib); for (i = 1; i <= 11; i++) (priv->rtllib->active_channel_map)[i] = 1; (priv->rtllib->active_channel_map)[12] = 2; @@ -1024,21 +971,21 @@ static void _rtl92e_watchdog_wq_cb(void *data) } } if ((ieee->link_state == MAC80211_LINKED) && (ieee->iw_mode == IW_MODE_INFRA)) { - if (ieee->link_detect_info.NumRxOkInPeriod > 100 || - ieee->link_detect_info.NumTxOkInPeriod > 100) + if (ieee->link_detect_info.num_rx_ok_in_period > 100 || + ieee->link_detect_info.num_tx_ok_in_period > 100) bBusyTraffic = true; - if (ieee->link_detect_info.NumRxOkInPeriod > 4000 || - ieee->link_detect_info.NumTxOkInPeriod > 4000) { + if (ieee->link_detect_info.num_rx_ok_in_period > 4000 || + ieee->link_detect_info.num_tx_ok_in_period > 4000) { bHigherBusyTraffic = true; - if (ieee->link_detect_info.NumRxOkInPeriod > 5000) + if (ieee->link_detect_info.num_rx_ok_in_period > 5000) bHigherBusyRxTraffic = true; else bHigherBusyRxTraffic = false; } if (((ieee->link_detect_info.NumRxUnicastOkInPeriod + - ieee->link_detect_info.NumTxOkInPeriod) > 8) || + ieee->link_detect_info.num_tx_ok_in_period) > 8) || (ieee->link_detect_info.NumRxUnicastOkInPeriod > 2)) bEnterPS = false; else @@ -1056,8 +1003,8 @@ static void _rtl92e_watchdog_wq_cb(void *data) rtl92e_leisure_ps_leave(dev); } - ieee->link_detect_info.NumRxOkInPeriod = 0; - ieee->link_detect_info.NumTxOkInPeriod = 0; + ieee->link_detect_info.num_rx_ok_in_period = 0; + ieee->link_detect_info.num_tx_ok_in_period = 0; ieee->link_detect_info.NumRxUnicastOkInPeriod = 0; ieee->link_detect_info.bBusyTraffic = bBusyTraffic; @@ -1240,7 +1187,7 @@ static int _rtl92e_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) tcb_desc->ratr_index = 7; tcb_desc->tx_dis_rate_fallback = 1; tcb_desc->tx_use_drv_assinged_rate = 1; - tcb_desc->bTxEnableFwCalcDur = 1; + tcb_desc->tx_enable_fw_calc_dur = 1; skb_push(skb, priv->rtllib->tx_headroom); ret = _rtl92e_tx(dev, skb); if (ret != 0) @@ -1484,17 +1431,6 @@ void rtl92e_reset_desc_ring(struct net_device *dev) spin_unlock_irqrestore(&priv->irq_th_lock, flags); } -void rtl92e_update_rx_pkt_timestamp(struct net_device *dev, - struct rtllib_rx_stats *stats) -{ - struct r8192_priv *priv = rtllib_priv(dev); - - if (stats->bIsAMPDU && !stats->bFirstMPDU) - stats->mac_time = priv->last_rx_desc_tsf; - else - priv->last_rx_desc_tsf = stats->mac_time; -} - long rtl92e_translate_to_dbm(struct r8192_priv *priv, u8 signal_strength_index) { long signal_power; @@ -1638,9 +1574,9 @@ static void _rtl92e_tx_resume(struct net_device *dev) for (queue_index = BK_QUEUE; queue_index < MAX_QUEUE_SIZE; queue_index++) { - while ((!skb_queue_empty(&ieee->skb_waitQ[queue_index])) && + while ((!skb_queue_empty(&ieee->skb_waitq[queue_index])) && (priv->rtllib->check_nic_enough_desc(dev, queue_index) > 0)) { - skb = skb_dequeue(&ieee->skb_waitQ[queue_index]); + skb = skb_dequeue(&ieee->skb_waitq[queue_index]); ieee->softmac_data_hard_start_xmit(skb, dev, 0); } } @@ -1827,9 +1763,6 @@ static irqreturn_t _rtl92e_irq(int irq, void *netdev) if (inta & IMR_ROK) tasklet_schedule(&priv->irq_rx_tasklet); - if (inta & IMR_BcnInt) - tasklet_schedule(&priv->irq_prepare_beacon_tasklet); - if (inta & IMR_RDU) { rtl92e_writel(dev, INTA_MASK, rtl92e_readl(dev, INTA_MASK) & ~IMR_RDU); @@ -1840,22 +1773,22 @@ static irqreturn_t _rtl92e_irq(int irq, void *netdev) tasklet_schedule(&priv->irq_rx_tasklet); if (inta & IMR_BKDOK) { - priv->rtllib->link_detect_info.NumTxOkInPeriod++; + priv->rtllib->link_detect_info.num_tx_ok_in_period++; _rtl92e_tx_isr(dev, BK_QUEUE); } if (inta & IMR_BEDOK) { - priv->rtllib->link_detect_info.NumTxOkInPeriod++; + priv->rtllib->link_detect_info.num_tx_ok_in_period++; _rtl92e_tx_isr(dev, BE_QUEUE); } if (inta & IMR_VIDOK) { - priv->rtllib->link_detect_info.NumTxOkInPeriod++; + priv->rtllib->link_detect_info.num_tx_ok_in_period++; _rtl92e_tx_isr(dev, VI_QUEUE); } if (inta & IMR_VODOK) { - priv->rtllib->link_detect_info.NumTxOkInPeriod++; + priv->rtllib->link_detect_info.num_tx_ok_in_period++; _rtl92e_tx_isr(dev, VO_QUEUE); } diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.h b/drivers/staging/rtl8192e/rtl8192e/rtl_core.h index a4afbf3e934d..1d6d31292f41 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.h +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.h @@ -34,8 +34,6 @@ #include "../rtllib.h" -#include "../dot11d.h" - #include "r8192E_firmware.h" #include "r8192E_hw.h" @@ -219,7 +217,6 @@ struct r8192_priv { struct tasklet_struct irq_rx_tasklet; struct tasklet_struct irq_tx_tasklet; - struct tasklet_struct irq_prepare_beacon_tasklet; struct mutex wx_mutex; struct mutex rf_mutex; @@ -228,8 +225,6 @@ struct r8192_priv { struct rt_stats stats; struct iw_statistics wstats; - u8 (*rf_set_chan)(struct net_device *dev, u8 ch); - struct rx_desc *rx_ring; struct sk_buff *rx_buf[MAX_RX_COUNT]; dma_addr_t rx_ring_dma; @@ -237,8 +232,6 @@ struct r8192_priv { int rxringcount; u16 rxbuffersize; - u64 last_rx_desc_tsf; - u32 receive_config; u8 retry_data; u8 retry_rts; @@ -286,7 +279,6 @@ struct r8192_priv { u16 eeprom_vid; u16 eeprom_did; u8 eeprom_customer_id; - u16 eeprom_chnl_plan; u8 eeprom_tx_pwr_level_cck[14]; u8 eeprom_tx_pwr_level_ofdm24g[14]; @@ -312,7 +304,6 @@ struct r8192_priv { bool tx_pwr_data_read_from_eeprom; - u16 chnl_plan; u8 hw_rf_off_action; bool rf_change_in_progress; @@ -396,8 +387,6 @@ void rtl92e_irq_enable(struct net_device *dev); void rtl92e_config_rate(struct net_device *dev, u16 *rate_config); void rtl92e_irq_disable(struct net_device *dev); -void rtl92e_update_rx_pkt_timestamp(struct net_device *dev, - struct rtllib_rx_stats *stats); long rtl92e_translate_to_dbm(struct r8192_priv *priv, u8 signal_strength_index); void rtl92e_update_rx_statistics(struct r8192_priv *priv, struct rtllib_rx_stats *pprevious_stats); diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c index 330dafd62656..92143c50c149 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c @@ -287,9 +287,9 @@ static void _rtl92e_dm_check_rate_adaptive(struct net_device *dev) if (priv->rtllib->link_state == MAC80211_LINKED) { bshort_gi_enabled = (ht_info->cur_tx_bw40mhz && - ht_info->bCurShortGI40MHz) || + ht_info->cur_short_gi_40mhz) || (!ht_info->cur_tx_bw40mhz && - ht_info->bCurShortGI20MHz); + ht_info->cur_short_gi_20mhz); pra->upper_rssi_threshold_ratr = (pra->upper_rssi_threshold_ratr & (~BIT(31))) | @@ -1142,7 +1142,7 @@ static void _rtl92e_dm_check_edca_turbo(struct net_device *dev) if (priv->bis_cur_rdlstate || !priv->bcurrent_turbo_EDCA) { rtl92e_writel(dev, EDCAPARA_BE, - edca_setting_UL[ht_info->IOTPeer]); + edca_setting_UL[ht_info->iot_peer]); priv->bis_cur_rdlstate = false; } } else { @@ -1150,10 +1150,10 @@ static void _rtl92e_dm_check_edca_turbo(struct net_device *dev) !priv->bcurrent_turbo_EDCA) { if (priv->rtllib->mode == WIRELESS_MODE_G) rtl92e_writel(dev, EDCAPARA_BE, - edca_setting_DL_GMode[ht_info->IOTPeer]); + edca_setting_DL_GMode[ht_info->iot_peer]); else rtl92e_writel(dev, EDCAPARA_BE, - edca_setting_DL[ht_info->IOTPeer]); + edca_setting_DL[ht_info->iot_peer]); priv->bis_cur_rdlstate = true; } } @@ -1164,17 +1164,17 @@ static void _rtl92e_dm_check_edca_turbo(struct net_device *dev) !priv->bcurrent_turbo_EDCA) { if (priv->rtllib->mode == WIRELESS_MODE_G) rtl92e_writel(dev, EDCAPARA_BE, - edca_setting_DL_GMode[ht_info->IOTPeer]); + edca_setting_DL_GMode[ht_info->iot_peer]); else rtl92e_writel(dev, EDCAPARA_BE, - edca_setting_DL[ht_info->IOTPeer]); + edca_setting_DL[ht_info->iot_peer]); priv->bis_cur_rdlstate = true; } } else { if (priv->bis_cur_rdlstate || !priv->bcurrent_turbo_EDCA) { rtl92e_writel(dev, EDCAPARA_BE, - edca_setting_UL[ht_info->IOTPeer]); + edca_setting_UL[ht_info->iot_peer]); priv->bis_cur_rdlstate = false; } } @@ -1217,7 +1217,7 @@ static void _rtl92e_dm_cts_to_self(struct net_device *dev) ht_info->iot_action &= ~HT_IOT_ACT_FORCED_CTS2SELF; return; } - if (ht_info->IOTPeer == HT_IOT_PEER_BROADCOM) { + if (ht_info->iot_peer == HT_IOT_PEER_BROADCOM) { curTxOkCnt = priv->stats.txbytesunicast - lastTxOkCnt; curRxOkCnt = priv->stats.rxbytesunicast - lastRxOkCnt; if (curRxOkCnt > 4 * curTxOkCnt) @@ -1713,7 +1713,7 @@ static void _rtl92e_dm_check_fsync(struct net_device *dev) static u8 reg_c38_State = RegC38_Default; if (priv->rtllib->link_state == MAC80211_LINKED && - priv->rtllib->ht_info->IOTPeer == HT_IOT_PEER_BROADCOM) { + priv->rtllib->ht_info->iot_peer == HT_IOT_PEER_BROADCOM) { if (priv->rtllib->bfsync_enable == 0) { switch (priv->rtllib->fsync_state) { case Default_Fsync: @@ -1819,7 +1819,7 @@ static void _rtl92e_dm_dynamic_tx_power(struct net_device *dev) priv->dynamic_tx_low_pwr = false; return; } - if ((priv->rtllib->ht_info->IOTPeer == HT_IOT_PEER_ATHEROS) && + if ((priv->rtllib->ht_info->iot_peer == HT_IOT_PEER_ATHEROS) && (priv->rtllib->mode == WIRELESS_MODE_G)) { txhipower_threshold = TX_POWER_ATHEROAP_THRESH_HIGH; txlowpower_threshold = TX_POWER_ATHEROAP_THRESH_LOW; diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c b/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c index 4371ab1239ee..4c884c5277f9 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c @@ -158,28 +158,6 @@ static int _rtl92e_wx_set_mode(struct net_device *dev, return ret; } -struct iw_range_with_scan_capa { - /* Informative stuff (to choose between different interface) */ - __u32 throughput; /* To give an idea... */ - /* In theory this value should be the maximum benchmarked - * TCP/IP throughput, because with most of these devices the - * bit rate is meaningless (overhead an co) to estimate how - * fast the connection will go and pick the fastest one. - * I suggest people to play with Netperf or any benchmark... - */ - - /* NWID (or domain id) */ - __u32 min_nwid; /* Minimal NWID we are able to set */ - __u32 max_nwid; /* Maximal NWID we are able to set */ - - /* Old Frequency (backward compat - moved lower ) */ - __u16 old_num_channels; - __u8 old_num_frequency; - - /* Scan capabilities */ - __u8 scan_capa; -}; - static int _rtl92e_wx_get_range(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) diff --git a/drivers/staging/rtl8192e/rtl819x_BAProc.c b/drivers/staging/rtl8192e/rtl819x_BAProc.c index 4af8055d2489..ee9ce392155c 100644 --- a/drivers/staging/rtl8192e/rtl819x_BAProc.c +++ b/drivers/staging/rtl8192e/rtl819x_BAProc.c @@ -23,35 +23,35 @@ static void deactivate_ba_entry(struct rtllib_device *ieee, struct ba_record *ba del_timer_sync(&ba->timer); } -static u8 tx_ts_delete_ba(struct rtllib_device *ieee, struct tx_ts_record *pTxTs) +static u8 tx_ts_delete_ba(struct rtllib_device *ieee, struct tx_ts_record *ts) { - struct ba_record *admitted_ba = &pTxTs->TxAdmittedBARecord; - struct ba_record *pending_ba = &pTxTs->TxPendingBARecord; - u8 bSendDELBA = false; + struct ba_record *admitted_ba = &ts->tx_admitted_ba_record; + struct ba_record *pending_ba = &ts->tx_pending_ba_record; + u8 send_del_ba = false; if (pending_ba->b_valid) { deactivate_ba_entry(ieee, pending_ba); - bSendDELBA = true; + send_del_ba = true; } if (admitted_ba->b_valid) { deactivate_ba_entry(ieee, admitted_ba); - bSendDELBA = true; + send_del_ba = true; } - return bSendDELBA; + return send_del_ba; } static u8 rx_ts_delete_ba(struct rtllib_device *ieee, struct rx_ts_record *ts) { struct ba_record *ba = &ts->rx_admitted_ba_record; - u8 bSendDELBA = false; + u8 send_del_ba = false; if (ba->b_valid) { deactivate_ba_entry(ieee, ba); - bSendDELBA = true; + send_del_ba = true; } - return bSendDELBA; + return send_del_ba; } void rtllib_reset_ba_entry(struct ba_record *ba) @@ -68,7 +68,7 @@ static struct sk_buff *rtllib_ADDBA(struct rtllib_device *ieee, u8 *dst, u16 status_code, u8 type) { struct sk_buff *skb = NULL; - struct ieee80211_hdr_3addr *BAReq = NULL; + struct ieee80211_hdr_3addr *ba_req = NULL; u8 *tag = NULL; u16 len = ieee->tx_headroom + 9; @@ -87,13 +87,13 @@ static struct sk_buff *rtllib_ADDBA(struct rtllib_device *ieee, u8 *dst, skb_reserve(skb, ieee->tx_headroom); - BAReq = skb_put(skb, sizeof(struct ieee80211_hdr_3addr)); + ba_req = skb_put(skb, sizeof(struct ieee80211_hdr_3addr)); - ether_addr_copy(BAReq->addr1, dst); - ether_addr_copy(BAReq->addr2, ieee->dev->dev_addr); + ether_addr_copy(ba_req->addr1, dst); + ether_addr_copy(ba_req->addr2, ieee->dev->dev_addr); - ether_addr_copy(BAReq->addr3, ieee->current_network.bssid); - BAReq->frame_control = cpu_to_le16(IEEE80211_STYPE_ACTION); + ether_addr_copy(ba_req->addr3, ieee->current_network.bssid); + ba_req->frame_control = cpu_to_le16(IEEE80211_STYPE_ACTION); tag = skb_put(skb, 9); *tag++ = ACT_CAT_BA; @@ -127,9 +127,9 @@ static struct sk_buff *rtllib_DELBA(struct rtllib_device *ieee, u8 *dst, struct ba_record *ba, enum tr_select TxRxSelect, u16 reason_code) { - union delba_param_set DelbaParamSet; + union delba_param_set del_ba_param_set; struct sk_buff *skb = NULL; - struct ieee80211_hdr_3addr *Delba = NULL; + struct ieee80211_hdr_3addr *del_ba = NULL; u8 *tag = NULL; u16 len = 6 + ieee->tx_headroom; @@ -137,10 +137,10 @@ static struct sk_buff *rtllib_DELBA(struct rtllib_device *ieee, u8 *dst, netdev_dbg(ieee->dev, "%s(): reason_code(%d) sentd to: %pM\n", __func__, reason_code, dst); - memset(&DelbaParamSet, 0, 2); + memset(&del_ba_param_set, 0, 2); - DelbaParamSet.field.initiator = (TxRxSelect == TX_DIR) ? 1 : 0; - DelbaParamSet.field.tid = ba->ba_param_set.field.tid; + del_ba_param_set.field.initiator = (TxRxSelect == TX_DIR) ? 1 : 0; + del_ba_param_set.field.tid = ba->ba_param_set.field.tid; skb = dev_alloc_skb(len + sizeof(struct ieee80211_hdr_3addr)); if (!skb) @@ -148,19 +148,19 @@ static struct sk_buff *rtllib_DELBA(struct rtllib_device *ieee, u8 *dst, skb_reserve(skb, ieee->tx_headroom); - Delba = skb_put(skb, sizeof(struct ieee80211_hdr_3addr)); + del_ba = skb_put(skb, sizeof(struct ieee80211_hdr_3addr)); - ether_addr_copy(Delba->addr1, dst); - ether_addr_copy(Delba->addr2, ieee->dev->dev_addr); - ether_addr_copy(Delba->addr3, ieee->current_network.bssid); - Delba->frame_control = cpu_to_le16(IEEE80211_STYPE_ACTION); + ether_addr_copy(del_ba->addr1, dst); + ether_addr_copy(del_ba->addr2, ieee->dev->dev_addr); + ether_addr_copy(del_ba->addr3, ieee->current_network.bssid); + del_ba->frame_control = cpu_to_le16(IEEE80211_STYPE_ACTION); tag = skb_put(skb, 6); *tag++ = ACT_CAT_BA; *tag++ = ACT_DELBA; - put_unaligned_le16(DelbaParamSet.short_data, tag); + put_unaligned_le16(del_ba_param_set.short_data, tag); tag += 2; put_unaligned_le16(reason_code, tag); @@ -215,11 +215,11 @@ int rtllib_rx_ADDBAReq(struct rtllib_device *ieee, struct sk_buff *skb) { struct ieee80211_hdr_3addr *req = NULL; u16 rc = 0; - u8 *dst = NULL, *pDialogToken = NULL, *tag = NULL; + u8 *dst = NULL, *dialog_token = NULL, *tag = NULL; struct ba_record *ba = NULL; - union ba_param_set *pBaParamSet = NULL; - u16 *pBaTimeoutVal = NULL; - union sequence_control *pBaStartSeqCtrl = NULL; + union ba_param_set *ba_param_set = NULL; + u16 *ba_timeout_value = NULL; + union sequence_control *ba_start_seq_ctrl = NULL; struct rx_ts_record *ts = NULL; if (skb->len < sizeof(struct ieee80211_hdr_3addr) + 9) { @@ -238,10 +238,10 @@ int rtllib_rx_ADDBAReq(struct rtllib_device *ieee, struct sk_buff *skb) tag = (u8 *)req; dst = (u8 *)(&req->addr2[0]); tag += sizeof(struct ieee80211_hdr_3addr); - pDialogToken = tag + 2; - pBaParamSet = (union ba_param_set *)(tag + 3); - pBaTimeoutVal = (u16 *)(tag + 5); - pBaStartSeqCtrl = (union sequence_control *)(req + 7); + dialog_token = tag + 2; + ba_param_set = (union ba_param_set *)(tag + 3); + ba_timeout_value = (u16 *)(tag + 5); + ba_start_seq_ctrl = (union sequence_control *)(req + 7); if (!ieee->current_network.qos_data.active || !ieee->ht_info->current_ht_support || @@ -254,14 +254,14 @@ int rtllib_rx_ADDBAReq(struct rtllib_device *ieee, struct sk_buff *skb) goto OnADDBAReq_Fail; } if (!rtllib_get_ts(ieee, (struct ts_common_info **)&ts, dst, - (u8)(pBaParamSet->field.tid), RX_DIR, true)) { + (u8)(ba_param_set->field.tid), RX_DIR, true)) { rc = ADDBA_STATUS_REFUSED; netdev_warn(ieee->dev, "%s(): can't get TS\n", __func__); goto OnADDBAReq_Fail; } ba = &ts->rx_admitted_ba_record; - if (pBaParamSet->field.ba_policy == BA_POLICY_DELAYED) { + if (ba_param_set->field.ba_policy == BA_POLICY_DELAYED) { rc = ADDBA_STATUS_INVALID_PARAM; netdev_warn(ieee->dev, "%s(): BA Policy is not correct\n", __func__); @@ -271,10 +271,10 @@ int rtllib_rx_ADDBAReq(struct rtllib_device *ieee, struct sk_buff *skb) rtllib_FlushRxTsPendingPkts(ieee, ts); deactivate_ba_entry(ieee, ba); - ba->dialog_token = *pDialogToken; - ba->ba_param_set = *pBaParamSet; - ba->ba_timeout_value = *pBaTimeoutVal; - ba->ba_start_seq_ctrl = *pBaStartSeqCtrl; + ba->dialog_token = *dialog_token; + ba->ba_param_set = *ba_param_set; + ba->ba_timeout_value = *ba_timeout_value; + ba->ba_start_seq_ctrl = *ba_start_seq_ctrl; if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev) || (ieee->ht_info->iot_action & HT_IOT_ACT_ALLOW_PEER_AGG_ONE_PKT)) @@ -291,9 +291,9 @@ OnADDBAReq_Fail: { struct ba_record BA; - BA.ba_param_set = *pBaParamSet; - BA.ba_timeout_value = *pBaTimeoutVal; - BA.dialog_token = *pDialogToken; + BA.ba_param_set = *ba_param_set; + BA.ba_timeout_value = *ba_timeout_value; + BA.dialog_token = *dialog_token; BA.ba_param_set.field.ba_policy = BA_POLICY_IMMEDIATE; rtllib_send_ADDBARsp(ieee, dst, &BA, rc); return 0; @@ -303,11 +303,11 @@ OnADDBAReq_Fail: int rtllib_rx_ADDBARsp(struct rtllib_device *ieee, struct sk_buff *skb) { struct ieee80211_hdr_3addr *rsp = NULL; - struct ba_record *pending_ba, *pAdmittedBA; + struct ba_record *pending_ba, *admitted_ba; struct tx_ts_record *ts = NULL; - u8 *dst = NULL, *pDialogToken = NULL, *tag = NULL; - u16 *status_code = NULL, *pBaTimeoutVal = NULL; - union ba_param_set *pBaParamSet = NULL; + u8 *dst = NULL, *dialog_token = NULL, *tag = NULL; + u16 *status_code = NULL, *ba_timeout_value = NULL; + union ba_param_set *ba_param_set = NULL; u16 reason_code; if (skb->len < sizeof(struct ieee80211_hdr_3addr) + 9) { @@ -320,40 +320,40 @@ int rtllib_rx_ADDBARsp(struct rtllib_device *ieee, struct sk_buff *skb) tag = (u8 *)rsp; dst = (u8 *)(&rsp->addr2[0]); tag += sizeof(struct ieee80211_hdr_3addr); - pDialogToken = tag + 2; + dialog_token = tag + 2; status_code = (u16 *)(tag + 3); - pBaParamSet = (union ba_param_set *)(tag + 5); - pBaTimeoutVal = (u16 *)(tag + 7); + ba_param_set = (union ba_param_set *)(tag + 5); + ba_timeout_value = (u16 *)(tag + 7); if (!ieee->current_network.qos_data.active || !ieee->ht_info->current_ht_support || - !ieee->ht_info->bCurrentAMPDUEnable) { + !ieee->ht_info->current_ampdu_enable) { netdev_warn(ieee->dev, "reject to ADDBA_RSP as some capability is not ready(%d, %d, %d)\n", ieee->current_network.qos_data.active, ieee->ht_info->current_ht_support, - ieee->ht_info->bCurrentAMPDUEnable); + ieee->ht_info->current_ampdu_enable); reason_code = DELBA_REASON_UNKNOWN_BA; goto OnADDBARsp_Reject; } if (!rtllib_get_ts(ieee, (struct ts_common_info **)&ts, dst, - (u8)(pBaParamSet->field.tid), TX_DIR, false)) { + (u8)(ba_param_set->field.tid), TX_DIR, false)) { netdev_warn(ieee->dev, "%s(): can't get TS\n", __func__); reason_code = DELBA_REASON_UNKNOWN_BA; goto OnADDBARsp_Reject; } - ts->bAddBaReqInProgress = false; - pending_ba = &ts->TxPendingBARecord; - pAdmittedBA = &ts->TxAdmittedBARecord; + ts->add_ba_req_in_progress = false; + pending_ba = &ts->tx_pending_ba_record; + admitted_ba = &ts->tx_admitted_ba_record; - if (pAdmittedBA->b_valid) { + if (admitted_ba->b_valid) { netdev_dbg(ieee->dev, "%s(): ADDBA response already admitted\n", __func__); return -1; } else if (!pending_ba->b_valid || - (*pDialogToken != pending_ba->dialog_token)) { + (*dialog_token != pending_ba->dialog_token)) { netdev_warn(ieee->dev, "%s(): ADDBA Rsp. BA invalid, DELBA!\n", __func__); @@ -367,22 +367,22 @@ int rtllib_rx_ADDBARsp(struct rtllib_device *ieee, struct sk_buff *skb) } if (*status_code == ADDBA_STATUS_SUCCESS) { - if (pBaParamSet->field.ba_policy == BA_POLICY_DELAYED) { - ts->bAddBaReqDelayed = true; - deactivate_ba_entry(ieee, pAdmittedBA); + if (ba_param_set->field.ba_policy == BA_POLICY_DELAYED) { + ts->add_ba_req_delayed = true; + deactivate_ba_entry(ieee, admitted_ba); reason_code = DELBA_REASON_END_BA; goto OnADDBARsp_Reject; } - pAdmittedBA->dialog_token = *pDialogToken; - pAdmittedBA->ba_timeout_value = *pBaTimeoutVal; - pAdmittedBA->ba_start_seq_ctrl = pending_ba->ba_start_seq_ctrl; - pAdmittedBA->ba_param_set = *pBaParamSet; - deactivate_ba_entry(ieee, pAdmittedBA); - activate_ba_entry(pAdmittedBA, *pBaTimeoutVal); + admitted_ba->dialog_token = *dialog_token; + admitted_ba->ba_timeout_value = *ba_timeout_value; + admitted_ba->ba_start_seq_ctrl = pending_ba->ba_start_seq_ctrl; + admitted_ba->ba_param_set = *ba_param_set; + deactivate_ba_entry(ieee, admitted_ba); + activate_ba_entry(admitted_ba, *ba_timeout_value); } else { - ts->bAddBaReqDelayed = true; - ts->bDisable_AddBa = true; + ts->add_ba_req_delayed = true; + ts->disable_add_ba = true; reason_code = DELBA_REASON_END_BA; goto OnADDBARsp_Reject; } @@ -393,7 +393,7 @@ OnADDBARsp_Reject: { struct ba_record BA; - BA.ba_param_set = *pBaParamSet; + BA.ba_param_set = *ba_param_set; rtllib_send_DELBA(ieee, dst, &BA, TX_DIR, reason_code); return 0; } @@ -402,7 +402,7 @@ OnADDBARsp_Reject: int rtllib_rx_DELBA(struct rtllib_device *ieee, struct sk_buff *skb) { struct ieee80211_hdr_3addr *delba = NULL; - union delba_param_set *pDelBaParamSet = NULL; + union delba_param_set *del_ba_param_set = NULL; u8 *dst = NULL; if (skb->len < sizeof(struct ieee80211_hdr_3addr) + 6) { @@ -427,46 +427,46 @@ int rtllib_rx_DELBA(struct rtllib_device *ieee, struct sk_buff *skb) #endif delba = (struct ieee80211_hdr_3addr *)skb->data; dst = (u8 *)(&delba->addr2[0]); - pDelBaParamSet = (union delba_param_set *)&delba->seq_ctrl + 2; + del_ba_param_set = (union delba_param_set *)&delba->seq_ctrl + 2; - if (pDelBaParamSet->field.initiator == 1) { + if (del_ba_param_set->field.initiator == 1) { struct rx_ts_record *ts; if (!rtllib_get_ts(ieee, (struct ts_common_info **)&ts, dst, - (u8)pDelBaParamSet->field.tid, RX_DIR, false)) { + (u8)del_ba_param_set->field.tid, RX_DIR, false)) { netdev_warn(ieee->dev, "%s(): can't get TS for RXTS. dst:%pM TID:%d\n", __func__, dst, - (u8)pDelBaParamSet->field.tid); + (u8)del_ba_param_set->field.tid); return -1; } rx_ts_delete_ba(ieee, ts); } else { - struct tx_ts_record *pTxTs; + struct tx_ts_record *ts; - if (!rtllib_get_ts(ieee, (struct ts_common_info **)&pTxTs, dst, - (u8)pDelBaParamSet->field.tid, TX_DIR, false)) { + if (!rtllib_get_ts(ieee, (struct ts_common_info **)&ts, dst, + (u8)del_ba_param_set->field.tid, TX_DIR, false)) { netdev_warn(ieee->dev, "%s(): can't get TS for TXTS\n", __func__); return -1; } - pTxTs->bUsingBa = false; - pTxTs->bAddBaReqInProgress = false; - pTxTs->bAddBaReqDelayed = false; - del_timer_sync(&pTxTs->TsAddBaTimer); - tx_ts_delete_ba(ieee, pTxTs); + ts->using_ba = false; + ts->add_ba_req_in_progress = false; + ts->add_ba_req_delayed = false; + del_timer_sync(&ts->ts_add_ba_timer); + tx_ts_delete_ba(ieee, ts); } return 0; } void rtllib_ts_init_add_ba(struct rtllib_device *ieee, struct tx_ts_record *ts, - u8 policy, u8 bOverwritePending) + u8 policy, u8 overwrite_pending) { - struct ba_record *ba = &ts->TxPendingBARecord; + struct ba_record *ba = &ts->tx_pending_ba_record; - if (ba->b_valid && !bOverwritePending) + if (ba->b_valid && !overwrite_pending) return; deactivate_ba_entry(ieee, ba); @@ -474,35 +474,35 @@ void rtllib_ts_init_add_ba(struct rtllib_device *ieee, struct tx_ts_record *ts, ba->dialog_token++; ba->ba_param_set.field.amsdu_support = 0; ba->ba_param_set.field.ba_policy = policy; - ba->ba_param_set.field.tid = ts->TsCommonInfo.TSpec.ucTSID; + ba->ba_param_set.field.tid = ts->ts_common_info.tspec.ts_id; ba->ba_param_set.field.buffer_size = 32; ba->ba_timeout_value = 0; - ba->ba_start_seq_ctrl.field.seq_num = (ts->TxCurSeq + 3) % 4096; + ba->ba_start_seq_ctrl.field.seq_num = (ts->tx_cur_seq + 3) % 4096; activate_ba_entry(ba, BA_SETUP_TIMEOUT); - rtllib_send_ADDBAReq(ieee, ts->TsCommonInfo.addr, ba); + rtllib_send_ADDBAReq(ieee, ts->ts_common_info.addr, ba); } void rtllib_ts_init_del_ba(struct rtllib_device *ieee, - struct ts_common_info *pTsCommonInfo, + struct ts_common_info *ts_common_info, enum tr_select TxRxSelect) { if (TxRxSelect == TX_DIR) { - struct tx_ts_record *pTxTs = - (struct tx_ts_record *)pTsCommonInfo; - - if (tx_ts_delete_ba(ieee, pTxTs)) - rtllib_send_DELBA(ieee, pTsCommonInfo->addr, - (pTxTs->TxAdmittedBARecord.b_valid) ? - (&pTxTs->TxAdmittedBARecord) : - (&pTxTs->TxPendingBARecord), + struct tx_ts_record *ts = + (struct tx_ts_record *)ts_common_info; + + if (tx_ts_delete_ba(ieee, ts)) + rtllib_send_DELBA(ieee, ts_common_info->addr, + (ts->tx_admitted_ba_record.b_valid) ? + (&ts->tx_admitted_ba_record) : + (&ts->tx_pending_ba_record), TxRxSelect, DELBA_REASON_END_BA); } else if (TxRxSelect == RX_DIR) { struct rx_ts_record *ts = - (struct rx_ts_record *)pTsCommonInfo; + (struct rx_ts_record *)ts_common_info; if (rx_ts_delete_ba(ieee, ts)) - rtllib_send_DELBA(ieee, pTsCommonInfo->addr, + rtllib_send_DELBA(ieee, ts_common_info->addr, &ts->rx_admitted_ba_record, TxRxSelect, DELBA_REASON_END_BA); } @@ -510,23 +510,23 @@ void rtllib_ts_init_del_ba(struct rtllib_device *ieee, void rtllib_ba_setup_timeout(struct timer_list *t) { - struct tx_ts_record *pTxTs = from_timer(pTxTs, t, - TxPendingBARecord.timer); + struct tx_ts_record *ts = from_timer(ts, t, + tx_pending_ba_record.timer); - pTxTs->bAddBaReqInProgress = false; - pTxTs->bAddBaReqDelayed = true; - pTxTs->TxPendingBARecord.b_valid = false; + ts->add_ba_req_in_progress = false; + ts->add_ba_req_delayed = true; + ts->tx_pending_ba_record.b_valid = false; } void rtllib_tx_ba_inact_timeout(struct timer_list *t) { - struct tx_ts_record *pTxTs = from_timer(pTxTs, t, - TxAdmittedBARecord.timer); - struct rtllib_device *ieee = container_of(pTxTs, struct rtllib_device, - TxTsRecord[pTxTs->num]); - tx_ts_delete_ba(ieee, pTxTs); - rtllib_send_DELBA(ieee, pTxTs->TsCommonInfo.addr, - &pTxTs->TxAdmittedBARecord, TX_DIR, + struct tx_ts_record *ts = from_timer(ts, t, + tx_admitted_ba_record.timer); + struct rtllib_device *ieee = container_of(ts, struct rtllib_device, + tx_ts_records[ts->num]); + tx_ts_delete_ba(ieee, ts); + rtllib_send_DELBA(ieee, ts->ts_common_info.addr, + &ts->tx_admitted_ba_record, TX_DIR, DELBA_REASON_TIMEOUT); } @@ -535,7 +535,7 @@ void rtllib_rx_ba_inact_timeout(struct timer_list *t) struct rx_ts_record *ts = from_timer(ts, t, rx_admitted_ba_record.timer); struct rtllib_device *ieee = container_of(ts, struct rtllib_device, - RxTsRecord[ts->num]); + rx_ts_records[ts->num]); rx_ts_delete_ba(ieee, ts); rtllib_send_DELBA(ieee, ts->ts_common_info.addr, diff --git a/drivers/staging/rtl8192e/rtl819x_HT.h b/drivers/staging/rtl8192e/rtl819x_HT.h index f8eb4d553fe0..68577bffb936 100644 --- a/drivers/staging/rtl8192e/rtl819x_HT.h +++ b/drivers/staging/rtl8192e/rtl819x_HT.h @@ -94,51 +94,32 @@ enum ht_aggre_mode { struct rt_hi_throughput { u8 enable_ht; u8 current_ht_support; - u8 bRegBW40MHz; - u8 bCurBW40MHz; - u8 bRegShortGI40MHz; - u8 bCurShortGI40MHz; - u8 bRegShortGI20MHz; - u8 bCurShortGI20MHz; - u8 bRegSuppCCK; - u8 bCurSuppCCK; - enum ht_spec_ver ePeerHTSpecVer; + u8 cur_bw_40mhz; + u8 cur_short_gi_40mhz; + u8 cur_short_gi_20mhz; + enum ht_spec_ver peer_ht_spec_ver; struct ht_capab_ele SelfHTCap; - struct ht_info_ele SelfHTInfo; u8 PeerHTCapBuf[32]; u8 PeerHTInfoBuf[32]; - u8 bAMSDU_Support; - u16 nAMSDU_MaxSize; - u8 bCurrent_AMSDU_Support; - u16 nCurrent_AMSDU_MaxSize; - u8 bAMPDUEnable; - u8 bCurrentAMPDUEnable; - u8 AMPDU_Factor; + u8 ampdu_enable; + u8 current_ampdu_enable; + u8 ampdu_factor; u8 CurrentAMPDUFactor; - u8 MPDU_Density; u8 current_mpdu_density; - enum ht_aggre_mode ForcedAMPDUMode; u8 forced_ampdu_factor; u8 forced_mpdu_density; - enum ht_aggre_mode ForcedAMSDUMode; - u8 forced_short_gi; u8 current_op_mode; - u8 self_mimo_ps; - u8 peer_mimo_ps; enum ht_extchnl_offset CurSTAExtChnlOffset; u8 cur_tx_bw40mhz; u8 sw_bw_in_progress; - u8 reg_rt2rt_aggregation; - u8 RT2RT_HT_Mode; u8 current_rt2rt_aggregation; u8 current_rt2rt_long_slot_time; u8 sz_rt2rt_agg_buf[10]; - u8 reg_rx_reorder_enable; u8 cur_rx_reorder_enable; u8 rx_reorder_win_size; u8 rx_reorder_pending_time; u16 rx_reorder_drop_counter; - u8 IOTPeer; + u8 iot_peer; u32 iot_action; u8 iot_ra_func; } __packed; diff --git a/drivers/staging/rtl8192e/rtl819x_HTProc.c b/drivers/staging/rtl8192e/rtl819x_HTProc.c index e607bccc079a..6d0912f90198 100644 --- a/drivers/staging/rtl8192e/rtl819x_HTProc.c +++ b/drivers/staging/rtl8192e/rtl819x_HTProc.c @@ -71,75 +71,55 @@ void ht_update_default_setting(struct rtllib_device *ieee) { struct rt_hi_throughput *ht_info = ieee->ht_info; - ht_info->bRegShortGI20MHz = 1; - ht_info->bRegShortGI40MHz = 1; + ht_info->ampdu_enable = 1; + ht_info->ampdu_factor = 2; - ht_info->bRegBW40MHz = 1; - - if (ht_info->bRegBW40MHz) - ht_info->bRegSuppCCK = 1; - else - ht_info->bRegSuppCCK = true; - - ht_info->nAMSDU_MaxSize = 7935UL; - ht_info->bAMSDU_Support = 0; - - ht_info->bAMPDUEnable = 1; - ht_info->AMPDU_Factor = 2; - ht_info->MPDU_Density = 0; - - ht_info->self_mimo_ps = 3; - if (ht_info->self_mimo_ps == 2) - ht_info->self_mimo_ps = 3; ieee->tx_dis_rate_fallback = 0; ieee->tx_use_drv_assinged_rate = 0; - ieee->bTxEnableFwCalcDur = 1; - - ht_info->reg_rt2rt_aggregation = 1; + ieee->tx_enable_fw_calc_dur = 1; - ht_info->reg_rx_reorder_enable = 1; ht_info->rx_reorder_win_size = 64; ht_info->rx_reorder_pending_time = 30; } -static u16 HTMcsToDataRate(struct rtllib_device *ieee, u8 nMcsRate) +static u16 ht_mcs_to_data_rate(struct rtllib_device *ieee, u8 mcs_rate) { struct rt_hi_throughput *ht_info = ieee->ht_info; - u8 is40MHz = (ht_info->bCurBW40MHz) ? 1 : 0; - u8 isShortGI = (ht_info->bCurBW40MHz) ? - ((ht_info->bCurShortGI40MHz) ? 1 : 0) : - ((ht_info->bCurShortGI20MHz) ? 1 : 0); - return MCS_DATA_RATE[is40MHz][isShortGI][(nMcsRate & 0x7f)]; + u8 is40MHz = (ht_info->cur_bw_40mhz) ? 1 : 0; + u8 isShortGI = (ht_info->cur_bw_40mhz) ? + ((ht_info->cur_short_gi_40mhz) ? 1 : 0) : + ((ht_info->cur_short_gi_20mhz) ? 1 : 0); + return MCS_DATA_RATE[is40MHz][isShortGI][(mcs_rate & 0x7f)]; } -u16 TxCountToDataRate(struct rtllib_device *ieee, u8 nDataRate) +u16 tx_count_to_data_rate(struct rtllib_device *ieee, u8 data_rate) { - u16 CCKOFDMRate[12] = {0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, + u16 cck_of_dm_rate[12] = {0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c}; u8 is40MHz = 0; u8 isShortGI = 0; - if (nDataRate < 12) - return CCKOFDMRate[nDataRate]; - if (nDataRate >= 0x10 && nDataRate <= 0x1f) { + if (data_rate < 12) + return cck_of_dm_rate[data_rate]; + if (data_rate >= 0x10 && data_rate <= 0x1f) { is40MHz = 0; isShortGI = 0; - } else if (nDataRate >= 0x20 && nDataRate <= 0x2f) { + } else if (data_rate >= 0x20 && data_rate <= 0x2f) { is40MHz = 1; isShortGI = 0; - } else if (nDataRate >= 0x30 && nDataRate <= 0x3f) { + } else if (data_rate >= 0x30 && data_rate <= 0x3f) { is40MHz = 0; isShortGI = 1; - } else if (nDataRate >= 0x40 && nDataRate <= 0x4f) { + } else if (data_rate >= 0x40 && data_rate <= 0x4f) { is40MHz = 1; isShortGI = 1; } - return MCS_DATA_RATE[is40MHz][isShortGI][nDataRate & 0xf]; + return MCS_DATA_RATE[is40MHz][isShortGI][data_rate & 0xf]; } -bool IsHTHalfNmodeAPs(struct rtllib_device *ieee) +bool is_ht_half_nmode_aps(struct rtllib_device *ieee) { bool retValue = false; struct rtllib_network *net = &ieee->current_network; @@ -164,187 +144,171 @@ bool IsHTHalfNmodeAPs(struct rtllib_device *ieee) return retValue; } -static void HTIOTPeerDetermine(struct rtllib_device *ieee) +static void ht_iot_peer_determine(struct rtllib_device *ieee) { struct rt_hi_throughput *ht_info = ieee->ht_info; struct rtllib_network *net = &ieee->current_network; if (net->bssht.bd_rt2rt_aggregation) { - ht_info->IOTPeer = HT_IOT_PEER_REALTEK; + ht_info->iot_peer = HT_IOT_PEER_REALTEK; if (net->bssht.rt2rt_ht_mode & RT_HT_CAP_USE_92SE) - ht_info->IOTPeer = HT_IOT_PEER_REALTEK_92SE; + ht_info->iot_peer = HT_IOT_PEER_REALTEK_92SE; if (net->bssht.rt2rt_ht_mode & RT_HT_CAP_USE_SOFTAP) - ht_info->IOTPeer = HT_IOT_PEER_92U_SOFTAP; + ht_info->iot_peer = HT_IOT_PEER_92U_SOFTAP; } else if (net->broadcom_cap_exist) { - ht_info->IOTPeer = HT_IOT_PEER_BROADCOM; + ht_info->iot_peer = HT_IOT_PEER_BROADCOM; } else if (!memcmp(net->bssid, UNKNOWN_BORADCOM, 3) || !memcmp(net->bssid, LINKSYSWRT330_LINKSYSWRT300_BROADCOM, 3) || !memcmp(net->bssid, LINKSYSWRT350_LINKSYSWRT150_BROADCOM, 3)) { - ht_info->IOTPeer = HT_IOT_PEER_BROADCOM; + ht_info->iot_peer = HT_IOT_PEER_BROADCOM; } else if ((memcmp(net->bssid, BELKINF5D8233V1_RALINK, 3) == 0) || (memcmp(net->bssid, BELKINF5D82334V3_RALINK, 3) == 0) || (memcmp(net->bssid, PCI_RALINK, 3) == 0) || (memcmp(net->bssid, EDIMAX_RALINK, 3) == 0) || (memcmp(net->bssid, AIRLINK_RALINK, 3) == 0) || net->ralink_cap_exist) { - ht_info->IOTPeer = HT_IOT_PEER_RALINK; + ht_info->iot_peer = HT_IOT_PEER_RALINK; } else if ((net->atheros_cap_exist) || (memcmp(net->bssid, DLINK_ATHEROS_1, 3) == 0) || (memcmp(net->bssid, DLINK_ATHEROS_2, 3) == 0)) { - ht_info->IOTPeer = HT_IOT_PEER_ATHEROS; + ht_info->iot_peer = HT_IOT_PEER_ATHEROS; } else if ((memcmp(net->bssid, CISCO_BROADCOM, 3) == 0) || net->cisco_cap_exist) { - ht_info->IOTPeer = HT_IOT_PEER_CISCO; + ht_info->iot_peer = HT_IOT_PEER_CISCO; } else if ((memcmp(net->bssid, LINKSYS_MARVELL_4400N, 3) == 0) || net->marvell_cap_exist) { - ht_info->IOTPeer = HT_IOT_PEER_MARVELL; + ht_info->iot_peer = HT_IOT_PEER_MARVELL; } else if (net->airgo_cap_exist) { - ht_info->IOTPeer = HT_IOT_PEER_AIRGO; + ht_info->iot_peer = HT_IOT_PEER_AIRGO; } else { - ht_info->IOTPeer = HT_IOT_PEER_UNKNOWN; + ht_info->iot_peer = HT_IOT_PEER_UNKNOWN; } - netdev_dbg(ieee->dev, "IOTPEER: %x\n", ht_info->IOTPeer); + netdev_dbg(ieee->dev, "IOTPEER: %x\n", ht_info->iot_peer); } -static u8 HTIOTActIsMgntUseCCK6M(struct rtllib_device *ieee, +static u8 ht_iot_act_is_mgnt_use_cck_6m(struct rtllib_device *ieee, struct rtllib_network *network) { u8 retValue = 0; - if (ieee->ht_info->IOTPeer == HT_IOT_PEER_BROADCOM) + if (ieee->ht_info->iot_peer == HT_IOT_PEER_BROADCOM) retValue = 1; return retValue; } -static u8 HTIOTActIsCCDFsync(struct rtllib_device *ieee) +static u8 ht_iot_act_is_ccd_fsync(struct rtllib_device *ieee) { u8 retValue = 0; - if (ieee->ht_info->IOTPeer == HT_IOT_PEER_BROADCOM) + if (ieee->ht_info->iot_peer == HT_IOT_PEER_BROADCOM) retValue = 1; return retValue; } -static void HTIOTActDetermineRaFunc(struct rtllib_device *ieee, bool bPeerRx2ss) +static void ht_iot_act_determine_ra_func(struct rtllib_device *ieee, bool bPeerRx2ss) { struct rt_hi_throughput *ht_info = ieee->ht_info; ht_info->iot_ra_func &= HT_IOT_RAFUNC_DISABLE_ALL; - if (ht_info->IOTPeer == HT_IOT_PEER_RALINK && !bPeerRx2ss) + if (ht_info->iot_peer == HT_IOT_PEER_RALINK && !bPeerRx2ss) ht_info->iot_ra_func |= HT_IOT_RAFUNC_PEER_1R; if (ht_info->iot_action & HT_IOT_ACT_AMSDU_ENABLE) ht_info->iot_ra_func |= HT_IOT_RAFUNC_TX_AMSDU; } -void HTResetIOTSetting(struct rt_hi_throughput *ht_info) +void ht_reset_iot_setting(struct rt_hi_throughput *ht_info) { ht_info->iot_action = 0; - ht_info->IOTPeer = HT_IOT_PEER_UNKNOWN; + ht_info->iot_peer = HT_IOT_PEER_UNKNOWN; ht_info->iot_ra_func = 0; } -void HTConstructCapabilityElement(struct rtllib_device *ieee, u8 *posHTCap, - u8 *len, u8 IsEncrypt, bool bAssoc) +void ht_construct_capability_element(struct rtllib_device *ieee, u8 *pos_ht_cap, + u8 *len, u8 is_encrypt, bool assoc) { - struct rt_hi_throughput *pHT = ieee->ht_info; - struct ht_capab_ele *pCapELE = NULL; + struct rt_hi_throughput *ht = ieee->ht_info; + struct ht_capab_ele *cap_ele = NULL; - if (!posHTCap || !pHT) { + if (!pos_ht_cap || !ht) { netdev_warn(ieee->dev, "%s(): posHTCap and ht_info are null\n", __func__); return; } - memset(posHTCap, 0, *len); + memset(pos_ht_cap, 0, *len); - if ((bAssoc) && (pHT->ePeerHTSpecVer == HT_SPEC_VER_EWC)) { + if ((assoc) && (ht->peer_ht_spec_ver == HT_SPEC_VER_EWC)) { static const u8 EWC11NHTCap[] = { 0x00, 0x90, 0x4c, 0x33 }; - memcpy(posHTCap, EWC11NHTCap, sizeof(EWC11NHTCap)); - pCapELE = (struct ht_capab_ele *)&posHTCap[4]; + memcpy(pos_ht_cap, EWC11NHTCap, sizeof(EWC11NHTCap)); + cap_ele = (struct ht_capab_ele *)&pos_ht_cap[4]; *len = 30 + 2; } else { - pCapELE = (struct ht_capab_ele *)posHTCap; + cap_ele = (struct ht_capab_ele *)pos_ht_cap; *len = 26 + 2; } - pCapELE->AdvCoding = 0; + cap_ele->AdvCoding = 0; if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev)) - pCapELE->ChlWidth = 0; + cap_ele->ChlWidth = 0; else - pCapELE->ChlWidth = (pHT->bRegBW40MHz ? 1 : 0); + cap_ele->ChlWidth = 1; - pCapELE->MimoPwrSave = pHT->self_mimo_ps; - pCapELE->GreenField = 0; - pCapELE->ShortGI20Mhz = 1; - pCapELE->ShortGI40Mhz = 1; + cap_ele->MimoPwrSave = 3; + cap_ele->GreenField = 0; + cap_ele->ShortGI20Mhz = 1; + cap_ele->ShortGI40Mhz = 1; - pCapELE->TxSTBC = 1; - pCapELE->RxSTBC = 0; - pCapELE->DelayBA = 0; - pCapELE->MaxAMSDUSize = (MAX_RECEIVE_BUFFER_SIZE >= 7935) ? 1 : 0; - pCapELE->DssCCk = ((pHT->bRegBW40MHz) ? (pHT->bRegSuppCCK ? 1 : 0) : 0); - pCapELE->PSMP = 0; - pCapELE->LSigTxopProtect = 0; + cap_ele->TxSTBC = 1; + cap_ele->RxSTBC = 0; + cap_ele->DelayBA = 0; + cap_ele->MaxAMSDUSize = (MAX_RECEIVE_BUFFER_SIZE >= 7935) ? 1 : 0; + cap_ele->DssCCk = 1; + cap_ele->PSMP = 0; + cap_ele->LSigTxopProtect = 0; netdev_dbg(ieee->dev, "TX HT cap/info ele BW=%d MaxAMSDUSize:%d DssCCk:%d\n", - pCapELE->ChlWidth, pCapELE->MaxAMSDUSize, pCapELE->DssCCk); + cap_ele->ChlWidth, cap_ele->MaxAMSDUSize, cap_ele->DssCCk); - if (IsEncrypt) { - pCapELE->MPDUDensity = 7; - pCapELE->MaxRxAMPDUFactor = 2; + if (is_encrypt) { + cap_ele->MPDUDensity = 7; + cap_ele->MaxRxAMPDUFactor = 2; } else { - pCapELE->MaxRxAMPDUFactor = 3; - pCapELE->MPDUDensity = 0; + cap_ele->MaxRxAMPDUFactor = 3; + cap_ele->MPDUDensity = 0; } - memcpy(pCapELE->MCS, ieee->reg_dot11ht_oper_rate_set, 16); - memset(&pCapELE->ExtHTCapInfo, 0, 2); - memset(pCapELE->TxBFCap, 0, 4); + memcpy(cap_ele->MCS, ieee->reg_dot11ht_oper_rate_set, 16); + memset(&cap_ele->ExtHTCapInfo, 0, 2); + memset(cap_ele->TxBFCap, 0, 4); - pCapELE->ASCap = 0; + cap_ele->ASCap = 0; - if (bAssoc) { - if (pHT->iot_action & HT_IOT_ACT_DISABLE_MCS15) - pCapELE->MCS[1] &= 0x7f; + if (assoc) { + if (ht->iot_action & HT_IOT_ACT_DISABLE_MCS15) + cap_ele->MCS[1] &= 0x7f; - if (pHT->iot_action & HT_IOT_ACT_DISABLE_MCS14) - pCapELE->MCS[1] &= 0xbf; + if (ht->iot_action & HT_IOT_ACT_DISABLE_MCS14) + cap_ele->MCS[1] &= 0xbf; - if (pHT->iot_action & HT_IOT_ACT_DISABLE_ALL_2SS) - pCapELE->MCS[1] &= 0x00; + if (ht->iot_action & HT_IOT_ACT_DISABLE_ALL_2SS) + cap_ele->MCS[1] &= 0x00; - if (pHT->iot_action & HT_IOT_ACT_DISABLE_RX_40MHZ_SHORT_GI) - pCapELE->ShortGI40Mhz = 0; + if (ht->iot_action & HT_IOT_ACT_DISABLE_RX_40MHZ_SHORT_GI) + cap_ele->ShortGI40Mhz = 0; if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev)) { - pCapELE->ChlWidth = 0; - pCapELE->MCS[1] = 0; + cap_ele->ChlWidth = 0; + cap_ele->MCS[1] = 0; } } } -void HTConstructInfoElement(struct rtllib_device *ieee, u8 *posHTInfo, - u8 *len, u8 IsEncrypt) -{ - struct ht_info_ele *pHTInfoEle = (struct ht_info_ele *)posHTInfo; - - if (!posHTInfo || !pHTInfoEle) { - netdev_warn(ieee->dev, - "%s(): posHTInfo and pHTInfoEle are null\n", - __func__); - return; - } - - memset(posHTInfo, 0, *len); - *len = 0; -} - -void HTConstructRT2RTAggElement(struct rtllib_device *ieee, u8 *posRT2RTAgg, +void ht_construct_rt2rt_agg_element(struct rtllib_device *ieee, u8 *posRT2RTAgg, u8 *len) { if (!posRT2RTAgg) { @@ -366,7 +330,7 @@ void HTConstructRT2RTAggElement(struct rtllib_device *ieee, u8 *posRT2RTAgg, *len = 6 + 2; } -static u8 HT_PickMCSRate(struct rtllib_device *ieee, u8 *pOperateMCS) +static u8 ht_pick_mcs_rate(struct rtllib_device *ieee, u8 *pOperateMCS) { u8 i; @@ -393,7 +357,7 @@ static u8 HT_PickMCSRate(struct rtllib_device *ieee, u8 *pOperateMCS) return true; } -u8 HTGetHighestMCSRate(struct rtllib_device *ieee, u8 *pMCSRateSet, +u8 ht_get_highest_mcs_rate(struct rtllib_device *ieee, u8 *pMCSRateSet, u8 *pMCSFilter) { u8 i, j; @@ -422,8 +386,8 @@ u8 HTGetHighestMCSRate(struct rtllib_device *ieee, u8 *pMCSRateSet, bitMap = availableMcsRate[i]; for (j = 0; j < 8; j++) { if ((bitMap % 2) != 0) { - if (HTMcsToDataRate(ieee, (8 * i + j)) > - HTMcsToDataRate(ieee, mcsRate)) + if (ht_mcs_to_data_rate(ieee, (8 * i + j)) > + ht_mcs_to_data_rate(ieee, mcsRate)) mcsRate = 8 * i + j; } bitMap >>= 1; @@ -433,7 +397,7 @@ u8 HTGetHighestMCSRate(struct rtllib_device *ieee, u8 *pMCSRateSet, return mcsRate | 0x80; } -static u8 HTFilterMCSRate(struct rtllib_device *ieee, u8 *pSupportMCS, +static u8 ht_filter_mcs_rate(struct rtllib_device *ieee, u8 *pSupportMCS, u8 *pOperateMCS) { u8 i; @@ -442,7 +406,7 @@ static u8 HTFilterMCSRate(struct rtllib_device *ieee, u8 *pSupportMCS, pOperateMCS[i] = ieee->reg_dot11tx_ht_oper_rate_set[i] & pSupportMCS[i]; - HT_PickMCSRate(ieee, pOperateMCS); + ht_pick_mcs_rate(ieee, pOperateMCS); if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev)) pOperateMCS[1] = 0; @@ -453,16 +417,15 @@ static u8 HTFilterMCSRate(struct rtllib_device *ieee, u8 *pSupportMCS, return true; } -void HTSetConnectBwMode(struct rtllib_device *ieee, +void ht_set_connect_bw_mode(struct rtllib_device *ieee, enum ht_channel_width bandwidth, enum ht_extchnl_offset Offset); -void HTOnAssocRsp(struct rtllib_device *ieee) +void ht_on_assoc_rsp(struct rtllib_device *ieee) { struct rt_hi_throughput *ht_info = ieee->ht_info; struct ht_capab_ele *pPeerHTCap = NULL; struct ht_info_ele *pPeerHTInfo = NULL; - u16 nMaxAMSDUSize = 0; u8 *pMcsFilter = NULL; static const u8 EWC11NHTCap[] = { 0x00, 0x90, 0x4c, 0x33 }; @@ -489,79 +452,48 @@ void HTOnAssocRsp(struct rtllib_device *ieee) print_hex_dump_bytes("%s: ", __func__, DUMP_PREFIX_NONE, pPeerHTCap, sizeof(struct ht_capab_ele)); #endif - HTSetConnectBwMode(ieee, (enum ht_channel_width)(pPeerHTCap->ChlWidth), + ht_set_connect_bw_mode(ieee, (enum ht_channel_width)(pPeerHTCap->ChlWidth), (enum ht_extchnl_offset)(pPeerHTInfo->ExtChlOffset)); ht_info->cur_tx_bw40mhz = ((pPeerHTInfo->RecommemdedTxWidth == 1) ? true : false); - ht_info->bCurShortGI20MHz = ((ht_info->bRegShortGI20MHz) ? - ((pPeerHTCap->ShortGI20Mhz == 1) ? - true : false) : false); - ht_info->bCurShortGI40MHz = ((ht_info->bRegShortGI40MHz) ? - ((pPeerHTCap->ShortGI40Mhz == 1) ? - true : false) : false); + ht_info->cur_short_gi_20mhz = ((pPeerHTCap->ShortGI20Mhz == 1) ? true : false); + ht_info->cur_short_gi_40mhz = ((pPeerHTCap->ShortGI40Mhz == 1) ? true : false); - ht_info->bCurSuppCCK = ((ht_info->bRegSuppCCK) ? - ((pPeerHTCap->DssCCk == 1) ? true : - false) : false); - - ht_info->bCurrent_AMSDU_Support = ht_info->bAMSDU_Support; - - nMaxAMSDUSize = (pPeerHTCap->MaxAMSDUSize == 0) ? 3839 : 7935; - - if (ht_info->nAMSDU_MaxSize > nMaxAMSDUSize) - ht_info->nCurrent_AMSDU_MaxSize = nMaxAMSDUSize; - else - ht_info->nCurrent_AMSDU_MaxSize = ht_info->nAMSDU_MaxSize; - - ht_info->bCurrentAMPDUEnable = ht_info->bAMPDUEnable; + ht_info->current_ampdu_enable = ht_info->ampdu_enable; if (ieee->rtllib_ap_sec_type && (ieee->rtllib_ap_sec_type(ieee) & (SEC_ALG_WEP | SEC_ALG_TKIP))) { - if ((ht_info->IOTPeer == HT_IOT_PEER_ATHEROS) || - (ht_info->IOTPeer == HT_IOT_PEER_UNKNOWN)) - ht_info->bCurrentAMPDUEnable = false; + if ((ht_info->iot_peer == HT_IOT_PEER_ATHEROS) || + (ht_info->iot_peer == HT_IOT_PEER_UNKNOWN)) + ht_info->current_ampdu_enable = false; } - if (!ht_info->reg_rt2rt_aggregation) { - if (ht_info->AMPDU_Factor > pPeerHTCap->MaxRxAMPDUFactor) + if (ieee->current_network.bssht.bd_rt2rt_aggregation) { + if (ieee->pairwise_key_type != KEY_TYPE_NA) ht_info->CurrentAMPDUFactor = - pPeerHTCap->MaxRxAMPDUFactor; + pPeerHTCap->MaxRxAMPDUFactor; else - ht_info->CurrentAMPDUFactor = ht_info->AMPDU_Factor; - + ht_info->CurrentAMPDUFactor = HT_AGG_SIZE_64K; } else { - if (ieee->current_network.bssht.bd_rt2rt_aggregation) { - if (ieee->pairwise_key_type != KEY_TYPE_NA) - ht_info->CurrentAMPDUFactor = - pPeerHTCap->MaxRxAMPDUFactor; - else - ht_info->CurrentAMPDUFactor = HT_AGG_SIZE_64K; - } else { - ht_info->CurrentAMPDUFactor = min_t(u32, pPeerHTCap->MaxRxAMPDUFactor, - HT_AGG_SIZE_32K); - } + ht_info->CurrentAMPDUFactor = min_t(u32, pPeerHTCap->MaxRxAMPDUFactor, + HT_AGG_SIZE_32K); } - ht_info->current_mpdu_density = max_t(u8, ht_info->MPDU_Density, - pPeerHTCap->MPDUDensity); + + ht_info->current_mpdu_density = pPeerHTCap->MPDUDensity; if (ht_info->iot_action & HT_IOT_ACT_TX_USE_AMSDU_8K) { - ht_info->bCurrentAMPDUEnable = false; - ht_info->ForcedAMSDUMode = HT_AGG_FORCE_ENABLE; + ht_info->current_ampdu_enable = false; } - ht_info->cur_rx_reorder_enable = ht_info->reg_rx_reorder_enable; + ht_info->cur_rx_reorder_enable = 1; if (pPeerHTCap->MCS[0] == 0) pPeerHTCap->MCS[0] = 0xff; - HTIOTActDetermineRaFunc(ieee, ((pPeerHTCap->MCS[1]) != 0)); + ht_iot_act_determine_ra_func(ieee, ((pPeerHTCap->MCS[1]) != 0)); - HTFilterMCSRate(ieee, pPeerHTCap->MCS, ieee->dot11ht_oper_rate_set); + ht_filter_mcs_rate(ieee, pPeerHTCap->MCS, ieee->dot11ht_oper_rate_set); - ht_info->peer_mimo_ps = pPeerHTCap->MimoPwrSave; - if (ht_info->peer_mimo_ps == MIMO_PS_STATIC) - pMcsFilter = MCS_FILTER_1SS; - else - pMcsFilter = MCS_FILTER_ALL; - ieee->HTHighestOperaRate = HTGetHighestMCSRate(ieee, + pMcsFilter = MCS_FILTER_ALL; + ieee->HTHighestOperaRate = ht_get_highest_mcs_rate(ieee, ieee->dot11ht_oper_rate_set, pMcsFilter); ieee->HTCurrentOperaRate = ieee->HTHighestOperaRate; @@ -569,30 +501,23 @@ void HTOnAssocRsp(struct rtllib_device *ieee) ht_info->current_op_mode = pPeerHTInfo->OptMode; } -void HTInitializeHTInfo(struct rtllib_device *ieee) +void ht_initialize_ht_info(struct rtllib_device *ieee) { struct rt_hi_throughput *ht_info = ieee->ht_info; ht_info->current_ht_support = false; - ht_info->bCurBW40MHz = false; + ht_info->cur_bw_40mhz = false; ht_info->cur_tx_bw40mhz = false; - ht_info->bCurShortGI20MHz = false; - ht_info->bCurShortGI40MHz = false; - ht_info->forced_short_gi = false; + ht_info->cur_short_gi_20mhz = false; + ht_info->cur_short_gi_40mhz = false; - ht_info->bCurSuppCCK = true; - - ht_info->bCurrent_AMSDU_Support = false; - ht_info->nCurrent_AMSDU_MaxSize = ht_info->nAMSDU_MaxSize; - ht_info->current_mpdu_density = ht_info->MPDU_Density; - ht_info->CurrentAMPDUFactor = ht_info->AMPDU_Factor; + ht_info->current_mpdu_density = 0; + ht_info->CurrentAMPDUFactor = ht_info->ampdu_factor; memset((void *)(&ht_info->SelfHTCap), 0, sizeof(ht_info->SelfHTCap)); - memset((void *)(&ht_info->SelfHTInfo), 0, - sizeof(ht_info->SelfHTInfo)); memset((void *)(&ht_info->PeerHTCapBuf), 0, sizeof(ht_info->PeerHTCapBuf)); memset((void *)(&ht_info->PeerHTInfoBuf), 0, @@ -600,13 +525,12 @@ void HTInitializeHTInfo(struct rtllib_device *ieee) ht_info->sw_bw_in_progress = false; - ht_info->ePeerHTSpecVer = HT_SPEC_VER_IEEE; + ht_info->peer_ht_spec_ver = HT_SPEC_VER_IEEE; ht_info->current_rt2rt_aggregation = false; ht_info->current_rt2rt_long_slot_time = false; - ht_info->RT2RT_HT_Mode = (enum rt_ht_capability)0; - ht_info->IOTPeer = 0; + ht_info->iot_peer = 0; ht_info->iot_action = 0; ht_info->iot_ra_func = 0; @@ -619,7 +543,7 @@ void HTInitializeHTInfo(struct rtllib_device *ieee) } } -void HTInitializeBssDesc(struct bss_ht *pBssHT) +void ht_initialize_bss_desc(struct bss_ht *pBssHT) { pBssHT->bd_support_ht = false; memset(pBssHT->bd_ht_cap_buf, 0, sizeof(pBssHT->bd_ht_cap_buf)); @@ -634,7 +558,7 @@ void HTInitializeBssDesc(struct bss_ht *pBssHT) pBssHT->rt2rt_ht_mode = (enum rt_ht_capability)0; } -void HTResetSelfAndSavePeerSetting(struct rtllib_device *ieee, +void ht_reset_self_and_save_peer_setting(struct rtllib_device *ieee, struct rtllib_network *pNetwork) { struct rt_hi_throughput *ht_info = ieee->ht_info; @@ -645,7 +569,7 @@ void HTResetSelfAndSavePeerSetting(struct rtllib_device *ieee, */ if (pNetwork->bssht.bd_support_ht) { ht_info->current_ht_support = true; - ht_info->ePeerHTSpecVer = pNetwork->bssht.bd_ht_spec_ver; + ht_info->peer_ht_spec_ver = pNetwork->bssht.bd_ht_spec_ver; if (pNetwork->bssht.bd_ht_cap_len > 0 && pNetwork->bssht.bd_ht_cap_len <= sizeof(ht_info->PeerHTCapBuf)) @@ -660,32 +584,24 @@ void HTResetSelfAndSavePeerSetting(struct rtllib_device *ieee, pNetwork->bssht.bd_ht_info_buf, pNetwork->bssht.bd_ht_info_len); - if (ht_info->reg_rt2rt_aggregation) { - ht_info->current_rt2rt_aggregation = - pNetwork->bssht.bd_rt2rt_aggregation; - ht_info->current_rt2rt_long_slot_time = - pNetwork->bssht.bd_rt2rt_long_slot_time; - ht_info->RT2RT_HT_Mode = pNetwork->bssht.rt2rt_ht_mode; - } else { - ht_info->current_rt2rt_aggregation = false; - ht_info->current_rt2rt_long_slot_time = false; - ht_info->RT2RT_HT_Mode = (enum rt_ht_capability)0; - } + ht_info->current_rt2rt_aggregation = + pNetwork->bssht.bd_rt2rt_aggregation; + ht_info->current_rt2rt_long_slot_time = + pNetwork->bssht.bd_rt2rt_long_slot_time; - HTIOTPeerDetermine(ieee); + ht_iot_peer_determine(ieee); ht_info->iot_action = 0; - bIOTAction = HTIOTActIsMgntUseCCK6M(ieee, pNetwork); + bIOTAction = ht_iot_act_is_mgnt_use_cck_6m(ieee, pNetwork); if (bIOTAction) ht_info->iot_action |= HT_IOT_ACT_MGNT_USE_CCK_6M; - bIOTAction = HTIOTActIsCCDFsync(ieee); + bIOTAction = ht_iot_act_is_ccd_fsync(ieee); if (bIOTAction) ht_info->iot_action |= HT_IOT_ACT_CDD_FSYNC; } else { ht_info->current_ht_support = false; ht_info->current_rt2rt_aggregation = false; ht_info->current_rt2rt_long_slot_time = false; - ht_info->RT2RT_HT_Mode = (enum rt_ht_capability)0; ht_info->iot_action = 0; ht_info->iot_ra_func = 0; @@ -706,7 +622,7 @@ void HT_update_self_and_peer_setting(struct rtllib_device *ieee, } EXPORT_SYMBOL(HT_update_self_and_peer_setting); -u8 HTCCheck(struct rtllib_device *ieee, u8 *pFrame) +u8 ht_c_check(struct rtllib_device *ieee, u8 *pFrame) { if (ieee->ht_info->current_ht_support) { if ((IsQoSDataFrame(pFrame) && Frame_Order(pFrame)) == 1) { @@ -717,11 +633,11 @@ u8 HTCCheck(struct rtllib_device *ieee, u8 *pFrame) return false; } -static void HTSetConnectBwModeCallback(struct rtllib_device *ieee) +static void ht_set_connect_bw_mode_callback(struct rtllib_device *ieee) { struct rt_hi_throughput *ht_info = ieee->ht_info; - if (ht_info->bCurBW40MHz) { + if (ht_info->cur_bw_40mhz) { if (ht_info->CurSTAExtChnlOffset == HT_EXTCHNL_OFFSET_UPPER) ieee->set_chan(ieee->dev, ieee->current_network.channel + 2); @@ -744,15 +660,12 @@ static void HTSetConnectBwModeCallback(struct rtllib_device *ieee) ht_info->sw_bw_in_progress = false; } -void HTSetConnectBwMode(struct rtllib_device *ieee, +void ht_set_connect_bw_mode(struct rtllib_device *ieee, enum ht_channel_width bandwidth, enum ht_extchnl_offset Offset) { struct rt_hi_throughput *ht_info = ieee->ht_info; - if (!ht_info->bRegBW40MHz) - return; - if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev)) bandwidth = HT_CHANNEL_WIDTH_20; @@ -766,21 +679,21 @@ void HTSetConnectBwMode(struct rtllib_device *ieee, Offset = HT_EXTCHNL_OFFSET_NO_EXT; if (Offset == HT_EXTCHNL_OFFSET_UPPER || Offset == HT_EXTCHNL_OFFSET_LOWER) { - ht_info->bCurBW40MHz = true; + ht_info->cur_bw_40mhz = true; ht_info->CurSTAExtChnlOffset = Offset; } else { - ht_info->bCurBW40MHz = false; + ht_info->cur_bw_40mhz = false; ht_info->CurSTAExtChnlOffset = HT_EXTCHNL_OFFSET_NO_EXT; } } else { - ht_info->bCurBW40MHz = false; + ht_info->cur_bw_40mhz = false; ht_info->CurSTAExtChnlOffset = HT_EXTCHNL_OFFSET_NO_EXT; } netdev_dbg(ieee->dev, "%s():ht_info->bCurBW40MHz:%x\n", __func__, - ht_info->bCurBW40MHz); + ht_info->cur_bw_40mhz); ht_info->sw_bw_in_progress = true; - HTSetConnectBwModeCallback(ieee); + ht_set_connect_bw_mode_callback(ieee); } diff --git a/drivers/staging/rtl8192e/rtl819x_Qos.h b/drivers/staging/rtl8192e/rtl819x_Qos.h index 1c00092ea3a5..50e01ca49a4c 100644 --- a/drivers/staging/rtl8192e/rtl819x_Qos.h +++ b/drivers/staging/rtl8192e/rtl819x_Qos.h @@ -8,7 +8,7 @@ #define __INC_QOS_TYPE_H struct qos_tsinfo { - u8 ucTSID:4; + u8 ts_id:4; u8 ucDirection:2; }; diff --git a/drivers/staging/rtl8192e/rtl819x_TS.h b/drivers/staging/rtl8192e/rtl819x_TS.h index fff36315f174..5b0e4cb572d2 100644 --- a/drivers/staging/rtl8192e/rtl819x_TS.h +++ b/drivers/staging/rtl8192e/rtl819x_TS.h @@ -17,21 +17,21 @@ enum tr_select { }; struct ts_common_info { - struct list_head List; + struct list_head list; u8 addr[ETH_ALEN]; - struct qos_tsinfo TSpec; + struct qos_tsinfo tspec; }; struct tx_ts_record { - struct ts_common_info TsCommonInfo; - u16 TxCurSeq; - struct ba_record TxPendingBARecord; - struct ba_record TxAdmittedBARecord; - u8 bAddBaReqInProgress; - u8 bAddBaReqDelayed; - u8 bUsingBa; - u8 bDisable_AddBa; - struct timer_list TsAddBaTimer; + struct ts_common_info ts_common_info; + u16 tx_cur_seq; + struct ba_record tx_pending_ba_record; + struct ba_record tx_admitted_ba_record; + u8 add_ba_req_in_progress; + u8 add_ba_req_delayed; + u8 using_ba; + u8 disable_add_ba; + struct timer_list ts_add_ba_timer; u8 num; }; diff --git a/drivers/staging/rtl8192e/rtl819x_TSProc.c b/drivers/staging/rtl8192e/rtl819x_TSProc.c index 3206fdb3e65a..7e73d31dcccf 100644 --- a/drivers/staging/rtl8192e/rtl819x_TSProc.c +++ b/drivers/staging/rtl8192e/rtl819x_TSProc.c @@ -12,7 +12,7 @@ static void RxPktPendingTimeout(struct timer_list *t) { struct rx_ts_record *ts = from_timer(ts, t, rx_pkt_pending_timer); struct rtllib_device *ieee = container_of(ts, struct rtllib_device, - RxTsRecord[ts->num]); + rx_ts_records[ts->num]); struct rx_reorder_entry *pReorderEntry = NULL; @@ -25,7 +25,7 @@ static void RxPktPendingTimeout(struct timer_list *t) while (!list_empty(&ts->rx_pending_pkt_list)) { pReorderEntry = (struct rx_reorder_entry *) list_entry(ts->rx_pending_pkt_list.prev, - struct rx_reorder_entry, List); + struct rx_reorder_entry, list); if (index == 0) ts->rx_indicate_seq = pReorderEntry->SeqNum; @@ -33,7 +33,7 @@ static void RxPktPendingTimeout(struct timer_list *t) ts->rx_indicate_seq) || SN_EQUAL(pReorderEntry->SeqNum, ts->rx_indicate_seq)) { - list_del_init(&pReorderEntry->List); + list_del_init(&pReorderEntry->list); if (SN_EQUAL(pReorderEntry->SeqNum, ts->rx_indicate_seq)) @@ -47,7 +47,7 @@ static void RxPktPendingTimeout(struct timer_list *t) pReorderEntry->prxb; index++; - list_add_tail(&pReorderEntry->List, + list_add_tail(&pReorderEntry->list, &ieee->RxReorder_Unused_List); } else { bPktInBuf = true; @@ -82,31 +82,31 @@ static void RxPktPendingTimeout(struct timer_list *t) static void TsAddBaProcess(struct timer_list *t) { - struct tx_ts_record *pTxTs = from_timer(pTxTs, t, TsAddBaTimer); - u8 num = pTxTs->num; - struct rtllib_device *ieee = container_of(pTxTs, struct rtllib_device, - TxTsRecord[num]); + struct tx_ts_record *ts = from_timer(ts, t, ts_add_ba_timer); + u8 num = ts->num; + struct rtllib_device *ieee = container_of(ts, struct rtllib_device, + tx_ts_records[num]); - rtllib_ts_init_add_ba(ieee, pTxTs, BA_POLICY_IMMEDIATE, false); + rtllib_ts_init_add_ba(ieee, ts, BA_POLICY_IMMEDIATE, false); netdev_dbg(ieee->dev, "%s(): ADDBA Req is started\n", __func__); } -static void ResetTsCommonInfo(struct ts_common_info *pTsCommonInfo) +static void ResetTsCommonInfo(struct ts_common_info *ts_common_info) { - eth_zero_addr(pTsCommonInfo->addr); - memset(&pTsCommonInfo->TSpec, 0, sizeof(struct qos_tsinfo)); + eth_zero_addr(ts_common_info->addr); + memset(&ts_common_info->tspec, 0, sizeof(struct qos_tsinfo)); } static void ResetTxTsEntry(struct tx_ts_record *ts) { - ResetTsCommonInfo(&ts->TsCommonInfo); - ts->TxCurSeq = 0; - ts->bAddBaReqInProgress = false; - ts->bAddBaReqDelayed = false; - ts->bUsingBa = false; - ts->bDisable_AddBa = false; - rtllib_reset_ba_entry(&ts->TxAdmittedBARecord); - rtllib_reset_ba_entry(&ts->TxPendingBARecord); + ResetTsCommonInfo(&ts->ts_common_info); + ts->tx_cur_seq = 0; + ts->add_ba_req_in_progress = false; + ts->add_ba_req_delayed = false; + ts->using_ba = false; + ts->disable_add_ba = false; + rtllib_reset_ba_entry(&ts->tx_admitted_ba_record); + rtllib_reset_ba_entry(&ts->tx_pending_ba_record); } static void ResetRxTsEntry(struct rx_ts_record *ts) @@ -119,8 +119,8 @@ static void ResetRxTsEntry(struct rx_ts_record *ts) void rtllib_ts_init(struct rtllib_device *ieee) { - struct tx_ts_record *pTxTS = ieee->TxTsRecord; - struct rx_ts_record *rxts = ieee->RxTsRecord; + struct tx_ts_record *pTxTS = ieee->tx_ts_records; + struct rx_ts_record *rxts = ieee->rx_ts_records; struct rx_reorder_entry *pRxReorderEntry = ieee->RxReorderEntry; u8 count = 0; @@ -130,15 +130,15 @@ void rtllib_ts_init(struct rtllib_device *ieee) for (count = 0; count < TOTAL_TS_NUM; count++) { pTxTS->num = count; - timer_setup(&pTxTS->TsAddBaTimer, TsAddBaProcess, 0); + timer_setup(&pTxTS->ts_add_ba_timer, TsAddBaProcess, 0); - timer_setup(&pTxTS->TxPendingBARecord.timer, rtllib_ba_setup_timeout, + timer_setup(&pTxTS->tx_pending_ba_record.timer, rtllib_ba_setup_timeout, 0); - timer_setup(&pTxTS->TxAdmittedBARecord.timer, + timer_setup(&pTxTS->tx_admitted_ba_record.timer, rtllib_tx_ba_inact_timeout, 0); ResetTxTsEntry(pTxTS); - list_add_tail(&pTxTS->TsCommonInfo.List, + list_add_tail(&pTxTS->ts_common_info.list, &ieee->Tx_TS_Unused_List); pTxTS++; } @@ -155,13 +155,13 @@ void rtllib_ts_init(struct rtllib_device *ieee) timer_setup(&rxts->rx_pkt_pending_timer, RxPktPendingTimeout, 0); ResetRxTsEntry(rxts); - list_add_tail(&rxts->ts_common_info.List, + list_add_tail(&rxts->ts_common_info.list, &ieee->Rx_TS_Unused_List); rxts++; } INIT_LIST_HEAD(&ieee->RxReorder_Unused_List); for (count = 0; count < REORDER_ENTRY_NUM; count++) { - list_add_tail(&pRxReorderEntry->List, + list_add_tail(&pRxReorderEntry->list, &ieee->RxReorder_Unused_List); if (count == (REORDER_ENTRY_NUM - 1)) break; @@ -196,31 +196,31 @@ static struct ts_common_info *SearchAdmitTRStream(struct rtllib_device *ieee, for (dir = 0; dir <= DIR_BI_DIR; dir++) { if (!search_dir[dir]) continue; - list_for_each_entry(pRet, psearch_list, List) { + list_for_each_entry(pRet, psearch_list, list) { if (memcmp(pRet->addr, addr, 6) == 0 && - pRet->TSpec.ucTSID == TID && - pRet->TSpec.ucDirection == dir) + pRet->tspec.ts_id == TID && + pRet->tspec.ucDirection == dir) break; } - if (&pRet->List != psearch_list) + if (&pRet->list != psearch_list) break; } - if (pRet && &pRet->List != psearch_list) + if (pRet && &pRet->list != psearch_list) return pRet; return NULL; } -static void MakeTSEntry(struct ts_common_info *pTsCommonInfo, u8 *addr, +static void MakeTSEntry(struct ts_common_info *ts_common_info, u8 *addr, struct qos_tsinfo *pTSPEC) { - if (!pTsCommonInfo) + if (!ts_common_info) return; - memcpy(pTsCommonInfo->addr, addr, 6); + memcpy(ts_common_info->addr, addr, 6); if (pTSPEC) - memcpy((u8 *)(&(pTsCommonInfo->TSpec)), (u8 *)pTSPEC, + memcpy((u8 *)(&(ts_common_info->tspec)), (u8 *)pTSPEC, sizeof(struct qos_tsinfo)); } @@ -228,8 +228,8 @@ bool rtllib_get_ts(struct rtllib_device *ieee, struct ts_common_info **ppTS, u8 *addr, u8 TID, enum tr_select TxRxSelect, bool bAddNewTs) { u8 UP = 0; - struct qos_tsinfo TSpec; - struct qos_tsinfo *ts_info = &TSpec; + struct qos_tsinfo tspec; + struct qos_tsinfo *ts_info = &tspec; struct list_head *pUnusedList; struct list_head *pAddmitList; enum direction_value Dir; @@ -286,13 +286,13 @@ bool rtllib_get_ts(struct rtllib_device *ieee, struct ts_common_info **ppTS, if (!list_empty(pUnusedList)) { (*ppTS) = list_entry(pUnusedList->next, - struct ts_common_info, List); - list_del_init(&(*ppTS)->List); + struct ts_common_info, list); + list_del_init(&(*ppTS)->list); if (TxRxSelect == TX_DIR) { struct tx_ts_record *tmp = container_of(*ppTS, struct tx_ts_record, - TsCommonInfo); + ts_common_info); ResetTxTsEntry(tmp); } else { struct rx_ts_record *ts = @@ -305,11 +305,11 @@ bool rtllib_get_ts(struct rtllib_device *ieee, struct ts_common_info **ppTS, netdev_dbg(ieee->dev, "to init current TS, UP:%d, Dir:%d, addr: %pM ppTs=%p\n", UP, Dir, addr, *ppTS); - ts_info->ucTSID = UP; + ts_info->ts_id = UP; ts_info->ucDirection = Dir; - MakeTSEntry(*ppTS, addr, &TSpec); - list_add_tail(&((*ppTS)->List), pAddmitList); + MakeTSEntry(*ppTS, addr, &tspec); + list_add_tail(&((*ppTS)->list), pAddmitList); return true; } @@ -335,10 +335,10 @@ static void RemoveTsEntry(struct rtllib_device *ieee, while (!list_empty(&ts->rx_pending_pkt_list)) { pRxReorderEntry = (struct rx_reorder_entry *) list_entry(ts->rx_pending_pkt_list.prev, - struct rx_reorder_entry, List); + struct rx_reorder_entry, list); netdev_dbg(ieee->dev, "%s(): Delete SeqNum %d!\n", __func__, pRxReorderEntry->SeqNum); - list_del_init(&pRxReorderEntry->List); + list_del_init(&pRxReorderEntry->list); { int i = 0; struct rtllib_rxb *prxb = pRxReorderEntry->prxb; @@ -350,13 +350,13 @@ static void RemoveTsEntry(struct rtllib_device *ieee, kfree(prxb); prxb = NULL; } - list_add_tail(&pRxReorderEntry->List, + list_add_tail(&pRxReorderEntry->list, &ieee->RxReorder_Unused_List); } } else { struct tx_ts_record *pTxTS = (struct tx_ts_record *)pTs; - del_timer_sync(&pTxTS->TsAddBaTimer); + del_timer_sync(&pTxTS->ts_add_ba_timer); } } @@ -366,37 +366,37 @@ void RemovePeerTS(struct rtllib_device *ieee, u8 *addr) netdev_info(ieee->dev, "===========>%s, %pM\n", __func__, addr); - list_for_each_entry_safe(ts, pTmpTS, &ieee->Tx_TS_Pending_List, List) { + list_for_each_entry_safe(ts, pTmpTS, &ieee->Tx_TS_Pending_List, list) { if (memcmp(ts->addr, addr, 6) == 0) { RemoveTsEntry(ieee, ts, TX_DIR); - list_del_init(&ts->List); - list_add_tail(&ts->List, &ieee->Tx_TS_Unused_List); + list_del_init(&ts->list); + list_add_tail(&ts->list, &ieee->Tx_TS_Unused_List); } } - list_for_each_entry_safe(ts, pTmpTS, &ieee->Tx_TS_Admit_List, List) { + list_for_each_entry_safe(ts, pTmpTS, &ieee->Tx_TS_Admit_List, list) { if (memcmp(ts->addr, addr, 6) == 0) { netdev_info(ieee->dev, "====>remove Tx_TS_admin_list\n"); RemoveTsEntry(ieee, ts, TX_DIR); - list_del_init(&ts->List); - list_add_tail(&ts->List, &ieee->Tx_TS_Unused_List); + list_del_init(&ts->list); + list_add_tail(&ts->list, &ieee->Tx_TS_Unused_List); } } - list_for_each_entry_safe(ts, pTmpTS, &ieee->Rx_TS_Pending_List, List) { + list_for_each_entry_safe(ts, pTmpTS, &ieee->Rx_TS_Pending_List, list) { if (memcmp(ts->addr, addr, 6) == 0) { RemoveTsEntry(ieee, ts, RX_DIR); - list_del_init(&ts->List); - list_add_tail(&ts->List, &ieee->Rx_TS_Unused_List); + list_del_init(&ts->list); + list_add_tail(&ts->list, &ieee->Rx_TS_Unused_List); } } - list_for_each_entry_safe(ts, pTmpTS, &ieee->Rx_TS_Admit_List, List) { + list_for_each_entry_safe(ts, pTmpTS, &ieee->Rx_TS_Admit_List, list) { if (memcmp(ts->addr, addr, 6) == 0) { RemoveTsEntry(ieee, ts, RX_DIR); - list_del_init(&ts->List); - list_add_tail(&ts->List, &ieee->Rx_TS_Unused_List); + list_del_init(&ts->list); + list_add_tail(&ts->list, &ieee->Rx_TS_Unused_List); } } } @@ -406,43 +406,43 @@ void RemoveAllTS(struct rtllib_device *ieee) { struct ts_common_info *ts, *pTmpTS; - list_for_each_entry_safe(ts, pTmpTS, &ieee->Tx_TS_Pending_List, List) { + list_for_each_entry_safe(ts, pTmpTS, &ieee->Tx_TS_Pending_List, list) { RemoveTsEntry(ieee, ts, TX_DIR); - list_del_init(&ts->List); - list_add_tail(&ts->List, &ieee->Tx_TS_Unused_List); + list_del_init(&ts->list); + list_add_tail(&ts->list, &ieee->Tx_TS_Unused_List); } - list_for_each_entry_safe(ts, pTmpTS, &ieee->Tx_TS_Admit_List, List) { + list_for_each_entry_safe(ts, pTmpTS, &ieee->Tx_TS_Admit_List, list) { RemoveTsEntry(ieee, ts, TX_DIR); - list_del_init(&ts->List); - list_add_tail(&ts->List, &ieee->Tx_TS_Unused_List); + list_del_init(&ts->list); + list_add_tail(&ts->list, &ieee->Tx_TS_Unused_List); } - list_for_each_entry_safe(ts, pTmpTS, &ieee->Rx_TS_Pending_List, List) { + list_for_each_entry_safe(ts, pTmpTS, &ieee->Rx_TS_Pending_List, list) { RemoveTsEntry(ieee, ts, RX_DIR); - list_del_init(&ts->List); - list_add_tail(&ts->List, &ieee->Rx_TS_Unused_List); + list_del_init(&ts->list); + list_add_tail(&ts->list, &ieee->Rx_TS_Unused_List); } - list_for_each_entry_safe(ts, pTmpTS, &ieee->Rx_TS_Admit_List, List) { + list_for_each_entry_safe(ts, pTmpTS, &ieee->Rx_TS_Admit_List, list) { RemoveTsEntry(ieee, ts, RX_DIR); - list_del_init(&ts->List); - list_add_tail(&ts->List, &ieee->Rx_TS_Unused_List); + list_del_init(&ts->list); + list_add_tail(&ts->list, &ieee->Rx_TS_Unused_List); } } void TsStartAddBaProcess(struct rtllib_device *ieee, struct tx_ts_record *pTxTS) { - if (pTxTS->bAddBaReqInProgress == false) { - pTxTS->bAddBaReqInProgress = true; + if (pTxTS->add_ba_req_in_progress == false) { + pTxTS->add_ba_req_in_progress = true; - if (pTxTS->bAddBaReqDelayed) { + if (pTxTS->add_ba_req_delayed) { netdev_dbg(ieee->dev, "Start ADDBA after 60 sec!!\n"); - mod_timer(&pTxTS->TsAddBaTimer, jiffies + + mod_timer(&pTxTS->ts_add_ba_timer, jiffies + msecs_to_jiffies(TS_ADDBA_DELAY)); } else { netdev_dbg(ieee->dev, "Immediately Start ADDBA\n"); - mod_timer(&pTxTS->TsAddBaTimer, jiffies + 10); + mod_timer(&pTxTS->ts_add_ba_timer, jiffies + 10); } } else { netdev_dbg(ieee->dev, "BA timer is already added\n"); diff --git a/drivers/staging/rtl8192e/rtllib.h b/drivers/staging/rtl8192e/rtllib.h index d2cf3cfaaaba..7b39a1987fdd 100644 --- a/drivers/staging/rtl8192e/rtllib.h +++ b/drivers/staging/rtl8192e/rtllib.h @@ -103,9 +103,6 @@ struct cb_desc { /* Tx Desc Related flags (8-9) */ u8 bLastIniPkt:1; u8 bCmdOrInit:1; - u8 bFirstSeg:1; - u8 bLastSeg:1; - u8 bEncrypt:1; u8 tx_dis_rate_fallback:1; u8 tx_use_drv_assinged_rate:1; u8 bHwSec:1; @@ -117,8 +114,8 @@ struct cb_desc { u8 bRTSEnable:1; u8 bUseShortGI:1; u8 bUseShortPreamble:1; - u8 bTxEnableFwCalcDur:1; - u8 bAMPDUEnable:1; + u8 tx_enable_fw_calc_dur:1; + u8 ampdu_enable:1; u8 bRTSSTBC:1; u8 RTSSC:1; @@ -139,7 +136,6 @@ struct cb_desc { u8 bAMSDU:1; u8 bFromAggrQ:1; u8 reserved6:6; - u8 macId; u8 priority; /* Tx firmware related element(20-27) */ @@ -471,7 +467,6 @@ enum _REG_PREAMBLE_MODE { * any adverse affects. */ struct rtllib_rx_stats { - u64 mac_time; s8 rssi; u8 signal; u8 noise; @@ -1047,7 +1042,7 @@ struct bandwidth_autoswitch { #define REORDER_WIN_SIZE 128 #define REORDER_ENTRY_NUM 128 struct rx_reorder_entry { - struct list_head List; + struct list_head list; u16 SeqNum; struct rtllib_rxb *prxb; }; @@ -1123,8 +1118,8 @@ struct rt_link_detect { u16 SlotNum; u16 SlotIndex; - u32 NumTxOkInPeriod; - u32 NumRxOkInPeriod; + u32 num_tx_ok_in_period; + u32 num_rx_ok_in_period; u32 NumRxUnicastOkInPeriod; bool bBusyTraffic; bool bHigherBusyTraffic; @@ -1169,7 +1164,7 @@ struct rt_pmkid_list { u8 Bssid[ETH_ALEN]; u8 PMKID[16]; u8 SsidBuf[33]; - u8 bUsed; + u8 used; }; /*************** DRIVER STATUS *****/ @@ -1192,7 +1187,7 @@ struct rtllib_device { unsigned long status; u8 CntAfterLink; - enum rt_op_mode OpMode; + enum rt_op_mode op_mode; /* The last AssocReq/Resp IEs */ u8 *assocreq_ies, *assocresp_ies; @@ -1224,17 +1219,17 @@ struct rtllib_device { u8 HTHighestOperaRate; u8 tx_dis_rate_fallback; u8 tx_use_drv_assinged_rate; - u8 bTxEnableFwCalcDur; + u8 tx_enable_fw_calc_dur; atomic_t atm_swbw; struct list_head Tx_TS_Admit_List; struct list_head Tx_TS_Pending_List; struct list_head Tx_TS_Unused_List; - struct tx_ts_record TxTsRecord[TOTAL_TS_NUM]; + struct tx_ts_record tx_ts_records[TOTAL_TS_NUM]; struct list_head Rx_TS_Admit_List; struct list_head Rx_TS_Pending_List; struct list_head Rx_TS_Unused_List; - struct rx_ts_record RxTsRecord[TOTAL_TS_NUM]; + struct rx_ts_record rx_ts_records[TOTAL_TS_NUM]; struct rx_reorder_entry RxReorderEntry[128]; struct list_head RxReorder_Unused_List; @@ -1321,12 +1316,8 @@ struct rtllib_device { u16 scan_watch_dog; /* map of allowed channels. 0 is dummy */ - void *dot11d_info; - bool global_domain; u8 active_channel_map[MAX_CHANNEL_NUMBER+1]; - u8 bss_start_channel; - int rate; /* current rate */ int basic_rate; @@ -1391,7 +1382,7 @@ struct rtllib_device { int mgmt_queue_head; int mgmt_queue_tail; u8 AsocRetryCount; - struct sk_buff_head skb_waitQ[MAX_QUEUE_SIZE]; + struct sk_buff_head skb_waitq[MAX_QUEUE_SIZE]; bool bdynamic_txpower_enable; @@ -1411,7 +1402,7 @@ struct rtllib_device { bool FwRWRF; struct rt_link_detect link_detect_info; - bool bIsAggregateFrame; + bool is_aggregate_frame; struct rt_pwr_save_ctrl pwr_save_ctrl; /* used if IEEE_SOFTMAC_TX_QUEUE is set */ @@ -1421,7 +1412,6 @@ struct rtllib_device { struct timer_list associate_timer; /* used if IEEE_SOFTMAC_BEACONS is set */ - struct timer_list beacon_timer; u8 need_sw_enc; struct work_struct associate_complete_wq; struct work_struct ips_leave_wq; @@ -1469,7 +1459,7 @@ struct rtllib_device { * This function can sleep. the driver should ensure * the radio has been switched before return. */ - void (*set_chan)(struct net_device *dev, short ch); + void (*set_chan)(struct net_device *dev, u8 ch); /* indicate the driver that the link state is changed * for example it may indicate the card is associated now. @@ -1687,8 +1677,8 @@ void rtllib_sta_ps_send_pspoll_frame(struct rtllib_device *ieee); void rtllib_start_protocol(struct rtllib_device *ieee); void rtllib_stop_protocol(struct rtllib_device *ieee); -void rtllib_EnableNetMonitorMode(struct net_device *dev, bool bInitState); -void rtllib_DisableNetMonitorMode(struct net_device *dev, bool bInitState); +void rtllib_enable_net_monitor_mode(struct net_device *dev, bool init_state); +void rtllib_disable_net_monitor_mode(struct net_device *dev, bool init_state); void rtllib_softmac_stop_protocol(struct rtllib_device *ieee); void rtllib_softmac_start_protocol(struct rtllib_device *ieee); @@ -1696,7 +1686,6 @@ void rtllib_softmac_start_protocol(struct rtllib_device *ieee); void rtllib_reset_queue(struct rtllib_device *ieee); void rtllib_wake_all_queues(struct rtllib_device *ieee); void rtllib_stop_all_queues(struct rtllib_device *ieee); -struct sk_buff *rtllib_get_beacon(struct rtllib_device *ieee); void notify_wx_assoc_event(struct rtllib_device *ieee); void rtllib_ps_tx_ack(struct rtllib_device *ieee, short success); @@ -1758,39 +1747,37 @@ int rtllib_wx_get_rts(struct rtllib_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); #define MAX_RECEIVE_BUFFER_SIZE 9100 -void HTSetConnectBwMode(struct rtllib_device *ieee, +void ht_set_connect_bw_mode(struct rtllib_device *ieee, enum ht_channel_width bandwidth, enum ht_extchnl_offset Offset); void ht_update_default_setting(struct rtllib_device *ieee); -void HTConstructCapabilityElement(struct rtllib_device *ieee, +void ht_construct_capability_element(struct rtllib_device *ieee, u8 *posHTCap, u8 *len, u8 isEncrypt, bool bAssoc); -void HTConstructInfoElement(struct rtllib_device *ieee, - u8 *posHTInfo, u8 *len, u8 isEncrypt); -void HTConstructRT2RTAggElement(struct rtllib_device *ieee, +void ht_construct_rt2rt_agg_element(struct rtllib_device *ieee, u8 *posRT2RTAgg, u8 *len); -void HTOnAssocRsp(struct rtllib_device *ieee); -void HTInitializeHTInfo(struct rtllib_device *ieee); -void HTInitializeBssDesc(struct bss_ht *pBssHT); -void HTResetSelfAndSavePeerSetting(struct rtllib_device *ieee, +void ht_on_assoc_rsp(struct rtllib_device *ieee); +void ht_initialize_ht_info(struct rtllib_device *ieee); +void ht_initialize_bss_desc(struct bss_ht *pBssHT); +void ht_reset_self_and_save_peer_setting(struct rtllib_device *ieee, struct rtllib_network *pNetwork); void HT_update_self_and_peer_setting(struct rtllib_device *ieee, struct rtllib_network *pNetwork); -u8 HTGetHighestMCSRate(struct rtllib_device *ieee, u8 *pMCSRateSet, +u8 ht_get_highest_mcs_rate(struct rtllib_device *ieee, u8 *pMCSRateSet, u8 *pMCSFilter); extern u8 MCS_FILTER_ALL[]; extern u16 MCS_DATA_RATE[2][2][77]; -u8 HTCCheck(struct rtllib_device *ieee, u8 *pFrame); -void HTResetIOTSetting(struct rt_hi_throughput *ht_info); -bool IsHTHalfNmodeAPs(struct rtllib_device *ieee); -u16 TxCountToDataRate(struct rtllib_device *ieee, u8 nDataRate); +u8 ht_c_check(struct rtllib_device *ieee, u8 *pFrame); +void ht_reset_iot_setting(struct rt_hi_throughput *ht_info); +bool is_ht_half_nmode_aps(struct rtllib_device *ieee); +u16 tx_count_to_data_rate(struct rtllib_device *ieee, u8 nDataRate); int rtllib_rx_ADDBAReq(struct rtllib_device *ieee, struct sk_buff *skb); int rtllib_rx_ADDBARsp(struct rtllib_device *ieee, struct sk_buff *skb); int rtllib_rx_DELBA(struct rtllib_device *ieee, struct sk_buff *skb); void rtllib_ts_init_add_ba(struct rtllib_device *ieee, struct tx_ts_record *ts, - u8 policy, u8 bOverwritePending); + u8 policy, u8 overwrite_pending); void rtllib_ts_init_del_ba(struct rtllib_device *ieee, - struct ts_common_info *pTsCommonInfo, + struct ts_common_info *ts_common_info, enum tr_select TxRxSelect); void rtllib_ba_setup_timeout(struct timer_list *t); void rtllib_tx_ba_inact_timeout(struct timer_list *t); diff --git a/drivers/staging/rtl8192e/rtllib_module.c b/drivers/staging/rtl8192e/rtllib_module.c index 95b6d6b9429c..e7af4a25b0be 100644 --- a/drivers/staging/rtl8192e/rtllib_module.c +++ b/drivers/staging/rtl8192e/rtllib_module.c @@ -123,7 +123,7 @@ struct net_device *alloc_rtllib(int sizeof_priv) goto free_softmac; ht_update_default_setting(ieee); - HTInitializeHTInfo(ieee); + ht_initialize_ht_info(ieee); rtllib_ts_init(ieee); for (i = 0; i < IEEE_IBSS_MAC_HASH_SIZE; i++) INIT_LIST_HEAD(&ieee->ibss_mac_hash[i]); diff --git a/drivers/staging/rtl8192e/rtllib_rx.c b/drivers/staging/rtl8192e/rtllib_rx.c index ecaa4dec3f94..4df20f4d6bf9 100644 --- a/drivers/staging/rtl8192e/rtllib_rx.c +++ b/drivers/staging/rtl8192e/rtllib_rx.c @@ -34,7 +34,6 @@ #include <linux/ctype.h> #include "rtllib.h" -#include "dot11d.h" static void rtllib_rx_mgt(struct rtllib_device *ieee, struct sk_buff *skb, struct rtllib_rx_stats *stats); @@ -412,19 +411,19 @@ static bool AddReorderEntry(struct rx_ts_record *ts, while (pList->next != &ts->rx_pending_pkt_list) { if (SN_LESS(pReorderEntry->SeqNum, ((struct rx_reorder_entry *) list_entry(pList->next, struct rx_reorder_entry, - List))->SeqNum)) + list))->SeqNum)) pList = pList->next; else if (SN_EQUAL(pReorderEntry->SeqNum, ((struct rx_reorder_entry *)list_entry(pList->next, - struct rx_reorder_entry, List))->SeqNum)) + struct rx_reorder_entry, list))->SeqNum)) return false; else break; } - pReorderEntry->List.next = pList->next; - pReorderEntry->List.next->prev = &pReorderEntry->List; - pReorderEntry->List.prev = pList; - pList->next = &pReorderEntry->List; + pReorderEntry->list.next = pList->next; + pReorderEntry->list.next->prev = &pReorderEntry->list; + pReorderEntry->list.prev = pList; + pList->next = &pReorderEntry->list; return true; } @@ -505,15 +504,15 @@ void rtllib_FlushRxTsPendingPkts(struct rtllib_device *ieee, pRxReorderEntry = (struct rx_reorder_entry *) list_entry(ts->rx_pending_pkt_list.prev, - struct rx_reorder_entry, List); + struct rx_reorder_entry, list); netdev_dbg(ieee->dev, "%s(): Indicate SeqNum %d!\n", __func__, pRxReorderEntry->SeqNum); - list_del_init(&pRxReorderEntry->List); + list_del_init(&pRxReorderEntry->list); ieee->RfdArray[RfdCnt] = pRxReorderEntry->prxb; RfdCnt = RfdCnt + 1; - list_add_tail(&pRxReorderEntry->List, + list_add_tail(&pRxReorderEntry->list, &ieee->RxReorder_Unused_List); } rtllib_indicate_packets(ieee, ieee->RfdArray, RfdCnt); @@ -602,8 +601,8 @@ static void RxReorderIndicatePacket(struct rtllib_device *ieee, if (!list_empty(&ieee->RxReorder_Unused_List)) { pReorderEntry = (struct rx_reorder_entry *) list_entry(ieee->RxReorder_Unused_List.next, - struct rx_reorder_entry, List); - list_del_init(&pReorderEntry->List); + struct rx_reorder_entry, list); + list_del_init(&pReorderEntry->list); /* Make a reorder entry and insert * into a the packet list. @@ -618,7 +617,7 @@ static void RxReorderIndicatePacket(struct rtllib_device *ieee, "%s(): Duplicate packet is dropped. IndicateSeq: %d, NewSeq: %d\n", __func__, ts->rx_indicate_seq, SeqNum); - list_add_tail(&pReorderEntry->List, + list_add_tail(&pReorderEntry->list, &ieee->RxReorder_Unused_List); for (i = 0; i < prxb->nr_subframes; i++) @@ -658,7 +657,7 @@ static void RxReorderIndicatePacket(struct rtllib_device *ieee, pReorderEntry = (struct rx_reorder_entry *) list_entry(ts->rx_pending_pkt_list.prev, struct rx_reorder_entry, - List); + list); if (SN_LESS(pReorderEntry->SeqNum, ts->rx_indicate_seq) || SN_EQUAL(pReorderEntry->SeqNum, ts->rx_indicate_seq)) { /* This protect struct buffer from overflow. */ @@ -670,7 +669,7 @@ static void RxReorderIndicatePacket(struct rtllib_device *ieee, break; } - list_del_init(&pReorderEntry->List); + list_del_init(&pReorderEntry->list); if (SN_EQUAL(pReorderEntry->SeqNum, ts->rx_indicate_seq)) ts->rx_indicate_seq = (ts->rx_indicate_seq + 1) % @@ -681,7 +680,7 @@ static void RxReorderIndicatePacket(struct rtllib_device *ieee, __func__, pReorderEntry->SeqNum); index++; - list_add_tail(&pReorderEntry->List, + list_add_tail(&pReorderEntry->list, &ieee->RxReorder_Unused_List); } else { bPktInBuf = true; @@ -731,7 +730,7 @@ static u8 parse_subframe(struct rtllib_device *ieee, struct sk_buff *skb, u16 LLCOffset = sizeof(struct ieee80211_hdr_3addr); u16 ChkLength; - bool bIsAggregateFrame = false; + bool is_aggregate_frame = false; u16 nSubframe_Length; u8 nPadding_Length = 0; u16 SeqNum = 0; @@ -740,7 +739,7 @@ static u8 parse_subframe(struct rtllib_device *ieee, struct sk_buff *skb, SeqNum = WLAN_GET_SEQ_SEQ(le16_to_cpu(hdr->seq_ctrl)); if ((RTLLIB_QOS_HAS_SEQ(fc)) && (((union frameqos *)(skb->data + RTLLIB_3ADDR_LEN))->field.reserved)) - bIsAggregateFrame = true; + is_aggregate_frame = true; if (RTLLIB_QOS_HAS_SEQ(fc)) LLCOffset += 2; @@ -753,8 +752,8 @@ static u8 parse_subframe(struct rtllib_device *ieee, struct sk_buff *skb, return 0; skb_pull(skb, LLCOffset); - ieee->bIsAggregateFrame = bIsAggregateFrame; - if (!bIsAggregateFrame) { + ieee->is_aggregate_frame = is_aggregate_frame; + if (!is_aggregate_frame) { rxb->nr_subframes = 1; /* altered by clark 3/30/2010 @@ -858,7 +857,7 @@ static size_t rtllib_rx_get_hdrlen(struct rtllib_device *ieee, size_t hdrlen; hdrlen = rtllib_get_hdrlen(fc); - if (HTCCheck(ieee, skb->data)) { + if (ht_c_check(ieee, skb->data)) { if (net_ratelimit()) netdev_info(ieee->dev, "%s: find HTCControl!\n", __func__); @@ -1151,7 +1150,7 @@ static void rtllib_rx_check_leave_lps(struct rtllib_device *ieee, u8 unicast, if (unicast) { if (ieee->link_state == MAC80211_LINKED) { if (((ieee->link_detect_info.NumRxUnicastOkInPeriod + - ieee->link_detect_info.NumTxOkInPeriod) > 8) || + ieee->link_detect_info.num_tx_ok_in_period) > 8) || (ieee->link_detect_info.NumRxUnicastOkInPeriod > 2)) { ieee->leisure_ps_leave(ieee->dev); } @@ -1286,7 +1285,7 @@ static int rtllib_rx_InfraAdhoc(struct rtllib_device *ieee, struct sk_buff *skb, /* Update statstics for AP roaming */ ieee->link_detect_info.NumRecvDataInPeriod++; - ieee->link_detect_info.NumRxOkInPeriod++; + ieee->link_detect_info.num_rx_ok_in_period++; /* Data frame - extract src/dst addresses */ rtllib_rx_extract_addr(ieee, hdr, dst, src, bssid); @@ -1359,7 +1358,7 @@ static int rtllib_rx_InfraAdhoc(struct rtllib_device *ieee, struct sk_buff *skb, /* Update WAPI PN */ /* Check if leave LPS */ - if (ieee->bIsAggregateFrame) + if (ieee->is_aggregate_frame) nr_subframes = rxb->nr_subframes; else nr_subframes = 1; @@ -1402,7 +1401,7 @@ static int rtllib_rx_Monitor(struct rtllib_device *ieee, struct sk_buff *skb, return 0; } - if (HTCCheck(ieee, skb->data)) { + if (ht_c_check(ieee, skb->data)) { if (net_ratelimit()) netdev_info(ieee->dev, "%s: Find HTCControl!\n", __func__); @@ -1663,35 +1662,6 @@ static const char *get_info_element_string(u16 id) } } -static inline void rtllib_extract_country_ie( - struct rtllib_device *ieee, - struct rtllib_info_element *info_element, - struct rtllib_network *network, - u8 *addr2) -{ - if (IS_DOT11D_ENABLE(ieee)) { - if (info_element->len != 0) { - memcpy(network->CountryIeBuf, info_element->data, - info_element->len); - network->CountryIeLen = info_element->len; - - if (!IS_COUNTRY_IE_VALID(ieee)) { - if (rtllib_act_scanning(ieee, false) && - ieee->FirstIe_InScan) - netdev_info(ieee->dev, - "Received beacon CountryIE, SSID: <%s>\n", - network->ssid); - dot11d_update_country(ieee, addr2, - info_element->len, - info_element->data); - } - } - - if (IS_EQUAL_CIE_SRC(ieee, addr2)) - UPDATE_CIE_WATCHDOG(ieee); - } -} - static void rtllib_parse_mife_generic(struct rtllib_device *ieee, struct rtllib_info_element *info_element, struct rtllib_network *network, @@ -2146,8 +2116,6 @@ int rtllib_parse_info_param(struct rtllib_device *ieee, case MFIE_TYPE_COUNTRY: netdev_dbg(ieee->dev, "MFIE_TYPE_COUNTRY: %d bytes\n", info_element->len); - rtllib_extract_country_ie(ieee, info_element, network, - network->bssid); break; /* TODO */ default: @@ -2221,7 +2189,7 @@ static inline int rtllib_network_init( network->RSSI = stats->SignalStrength; network->CountryIeLen = 0; memset(network->CountryIeBuf, 0, MAX_IE_LEN); - HTInitializeBssDesc(&network->bssht); + ht_initialize_bss_desc(&network->bssht); network->flags |= NETWORK_HAS_CCK; network->wpa_ie_len = 0; diff --git a/drivers/staging/rtl8192e/rtllib_softmac.c b/drivers/staging/rtl8192e/rtllib_softmac.c index 42d652fe8601..b9278b26accd 100644 --- a/drivers/staging/rtl8192e/rtllib_softmac.c +++ b/drivers/staging/rtl8192e/rtllib_softmac.c @@ -18,7 +18,6 @@ #include <linux/uaccess.h> #include <linux/etherdevice.h> #include <linux/ieee80211.h> -#include "dot11d.h" static void rtllib_sta_wakeup(struct rtllib_device *ieee, short nl); @@ -45,7 +44,7 @@ static unsigned int rtllib_MFIE_rate_len(struct rtllib_device *ieee) * Then it updates the pointer so that * it points after the new MFIE tag added. */ -static void rtllib_MFIE_Brate(struct rtllib_device *ieee, u8 **tag_p) +static void rtllib_mfie_brate(struct rtllib_device *ieee, u8 **tag_p) { u8 *tag = *tag_p; @@ -62,7 +61,7 @@ static void rtllib_MFIE_Brate(struct rtllib_device *ieee, u8 **tag_p) *tag_p = tag; } -static void rtllib_MFIE_Grate(struct rtllib_device *ieee, u8 **tag_p) +static void rtllib_mfie_grate(struct rtllib_device *ieee, u8 **tag_p) { u8 *tag = *tag_p; @@ -83,7 +82,7 @@ static void rtllib_MFIE_Grate(struct rtllib_device *ieee, u8 **tag_p) *tag_p = tag; } -static void rtllib_WMM_Info(struct rtllib_device *ieee, u8 **tag_p) +static void rtllib_wmm_info(struct rtllib_device *ieee, u8 **tag_p) { u8 *tag = *tag_p; @@ -99,7 +98,7 @@ static void rtllib_WMM_Info(struct rtllib_device *ieee, u8 **tag_p) *tag_p = tag; } -static void rtllib_TURBO_Info(struct rtllib_device *ieee, u8 **tag_p) +static void rtllib_turbo_info(struct rtllib_device *ieee, u8 **tag_p) { u8 *tag = *tag_p; @@ -135,32 +134,33 @@ static void enqueue_mgmt(struct rtllib_device *ieee, struct sk_buff *skb) static void init_mgmt_queue(struct rtllib_device *ieee) { - ieee->mgmt_queue_tail = ieee->mgmt_queue_head = 0; + ieee->mgmt_queue_tail = 0; + ieee->mgmt_queue_head = 0; } u8 MgntQuery_TxRateExcludeCCKRates(struct rtllib_device *ieee) { u16 i; - u8 QueryRate = 0; - u8 BasicRate; + u8 query_rate = 0; + u8 basic_rate; for (i = 0; i < ieee->current_network.rates_len; i++) { - BasicRate = ieee->current_network.rates[i] & 0x7F; - if (!rtllib_is_cck_rate(BasicRate)) { - if (QueryRate == 0) { - QueryRate = BasicRate; + basic_rate = ieee->current_network.rates[i] & 0x7F; + if (!rtllib_is_cck_rate(basic_rate)) { + if (query_rate == 0) { + query_rate = basic_rate; } else { - if (BasicRate < QueryRate) - QueryRate = BasicRate; + if (basic_rate < query_rate) + query_rate = basic_rate; } } } - if (QueryRate == 0) { - QueryRate = 12; - netdev_info(ieee->dev, "No BasicRate found!!\n"); + if (query_rate == 0) { + query_rate = 12; + netdev_info(ieee->dev, "No basic_rate found!!\n"); } - return QueryRate; + return query_rate; } static u8 MgntQuery_MgntFrameTxRate(struct rtllib_device *ieee) @@ -173,12 +173,8 @@ static u8 MgntQuery_MgntFrameTxRate(struct rtllib_device *ieee) else rate = ieee->basic_rate & 0x7f; - if (rate == 0) { - if (ieee->mode == WIRELESS_MODE_N_24G && !ht_info->bCurSuppCCK) - rate = 0x0c; - else - rate = 0x02; - } + if (rate == 0) + rate = 0x02; return rate; } @@ -240,7 +236,7 @@ inline void softmac_mgmt_xmit(struct sk_buff *skb, struct rtllib_device *ieee) /* check whether the managed packet queued greater than 5 */ if (!ieee->check_nic_enough_desc(ieee->dev, tcb_desc->queue_index) || - skb_queue_len(&ieee->skb_waitQ[tcb_desc->queue_index]) || + skb_queue_len(&ieee->skb_waitq[tcb_desc->queue_index]) || ieee->queue_stop) { /* insert the skb packet to the management queue * @@ -250,7 +246,7 @@ inline void softmac_mgmt_xmit(struct sk_buff *skb, struct rtllib_device *ieee) netdev_info(ieee->dev, "%s():insert to waitqueue, queue_index:%d!\n", __func__, tcb_desc->queue_index); - skb_queue_tail(&ieee->skb_waitQ[tcb_desc->queue_index], + skb_queue_tail(&ieee->skb_waitq[tcb_desc->queue_index], skb); } else { ieee->softmac_hard_start_xmit(skb, ieee->dev); @@ -345,65 +341,34 @@ static inline struct sk_buff *rtllib_probe_req(struct rtllib_device *ieee) memcpy(tag, ieee->current_network.ssid, len); tag += len; - rtllib_MFIE_Brate(ieee, &tag); - rtllib_MFIE_Grate(ieee, &tag); + rtllib_mfie_brate(ieee, &tag); + rtllib_mfie_grate(ieee, &tag); return skb; } -static struct sk_buff *rtllib_get_beacon_(struct rtllib_device *ieee); - -static void rtllib_send_beacon(struct rtllib_device *ieee) -{ - struct sk_buff *skb; - - if (!ieee->ieee_up) - return; - skb = rtllib_get_beacon_(ieee); - - if (skb) { - softmac_mgmt_xmit(skb, ieee); - ieee->softmac_stats.tx_beacons++; - } - - if (ieee->beacon_txing && ieee->ieee_up) - mod_timer(&ieee->beacon_timer, jiffies + - (msecs_to_jiffies(ieee->current_network.beacon_interval - 5))); -} - -static void rtllib_send_beacon_cb(struct timer_list *t) -{ - struct rtllib_device *ieee = - from_timer(ieee, t, beacon_timer); - unsigned long flags; - - spin_lock_irqsave(&ieee->beacon_lock, flags); - rtllib_send_beacon(ieee); - spin_unlock_irqrestore(&ieee->beacon_lock, flags); -} - /* Enables network monitor mode, all rx packets will be received. */ -void rtllib_EnableNetMonitorMode(struct net_device *dev, - bool bInitState) +void rtllib_enable_net_monitor_mode(struct net_device *dev, + bool init_state) { struct rtllib_device *ieee = netdev_priv_rsl(dev); netdev_info(dev, "========>Enter Monitor Mode\n"); - ieee->AllowAllDestAddrHandler(dev, true, !bInitState); + ieee->AllowAllDestAddrHandler(dev, true, !init_state); } /* Disables network monitor mode. Only packets destinated to * us will be received. */ -void rtllib_DisableNetMonitorMode(struct net_device *dev, - bool bInitState) +void rtllib_disable_net_monitor_mode(struct net_device *dev, + bool init_state) { struct rtllib_device *ieee = netdev_priv_rsl(dev); netdev_info(dev, "========>Exit Monitor Mode\n"); - ieee->AllowAllDestAddrHandler(dev, false, !bInitState); + ieee->AllowAllDestAddrHandler(dev, false, !init_state); } static void rtllib_send_probe(struct rtllib_device *ieee) @@ -425,12 +390,6 @@ static void rtllib_send_probe_requests(struct rtllib_device *ieee) } } -static void rtllib_update_active_chan_map(struct rtllib_device *ieee) -{ - memcpy(ieee->active_channel_map, GET_DOT11D_INFO(ieee)->channel_map, - MAX_CHANNEL_NUMBER + 1); -} - /* this performs syncro scan blocking the caller until all channels * in the allowed channel map has been checked. */ @@ -439,8 +398,6 @@ static void rtllib_softmac_scan_syncro(struct rtllib_device *ieee) union iwreq_data wrqu; short ch = 0; - rtllib_update_active_chan_map(ieee); - ieee->be_scan_inprogress = true; mutex_lock(&ieee->scan_mutex); @@ -492,10 +449,6 @@ out: ieee->actscanning = false; ieee->sync_scan_hurryup = 0; - if (ieee->link_state >= MAC80211_LINKED) { - if (IS_DOT11D_ENABLE(ieee)) - dot11d_scan_complete(ieee); - } mutex_unlock(&ieee->scan_mutex); ieee->be_scan_inprogress = false; @@ -510,8 +463,6 @@ static void rtllib_softmac_scan_wq(void *data) struct rtllib_device, softmac_scan_wq); u8 last_channel = ieee->current_network.channel; - rtllib_update_active_chan_map(ieee); - if (!ieee->ieee_up) return; if (rtllib_act_scanning(ieee, true)) @@ -552,8 +503,6 @@ static void rtllib_softmac_scan_wq(void *data) return; out: - if (IS_DOT11D_ENABLE(ieee)) - dot11d_scan_complete(ieee); ieee->current_network.channel = last_channel; out1: @@ -609,10 +558,6 @@ static void rtllib_start_scan(struct rtllib_device *ieee) { ieee->rtllib_ips_leave_wq(ieee->dev); - if (IS_DOT11D_ENABLE(ieee)) { - if (IS_COUNTRY_IE_VALID(ieee)) - RESET_CIE_WATCHDOG(ieee); - } if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) { if (ieee->scanning_continue == 0) { ieee->actscanning = true; @@ -625,10 +570,6 @@ static void rtllib_start_scan(struct rtllib_device *ieee) /* called with wx_mutex held */ void rtllib_start_scan_syncro(struct rtllib_device *ieee) { - if (IS_DOT11D_ENABLE(ieee)) { - if (IS_COUNTRY_IE_VALID(ieee)) - RESET_CIE_WATCHDOG(ieee); - } ieee->sync_scan_hurryup = 0; if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) rtllib_softmac_scan_syncro(ieee); @@ -677,152 +618,6 @@ rtllib_authentication_req(struct rtllib_network *beacon, return skb; } -static struct sk_buff *rtllib_probe_resp(struct rtllib_device *ieee, - const u8 *dest) -{ - u8 *tag; - int beacon_size; - struct rtllib_probe_response *beacon_buf; - struct sk_buff *skb = NULL; - int encrypt; - int atim_len, erp_len; - struct lib80211_crypt_data *crypt; - - char *ssid = ieee->current_network.ssid; - int ssid_len = ieee->current_network.ssid_len; - int rate_len = ieee->current_network.rates_len + 2; - int rate_ex_len = ieee->current_network.rates_ex_len; - int wpa_ie_len = ieee->wpa_ie_len; - u8 erpinfo_content = 0; - - u8 *tmp_ht_cap_buf = NULL; - u8 tmp_ht_cap_len = 0; - u8 *tmp_ht_info_buf = NULL; - u8 tmp_ht_info_len = 0; - struct rt_hi_throughput *ht_info = ieee->ht_info; - u8 *tmp_generic_ie_buf = NULL; - u8 tmp_generic_ie_len = 0; - - if (rate_ex_len > 0) - rate_ex_len += 2; - - if (ieee->current_network.capability & WLAN_CAPABILITY_IBSS) - atim_len = 4; - else - atim_len = 0; - - if ((ieee->current_network.mode == WIRELESS_MODE_G) || - (ieee->current_network.mode == WIRELESS_MODE_N_24G && - ieee->ht_info->bCurSuppCCK)) { - erp_len = 3; - erpinfo_content = 0; - if (ieee->current_network.buseprotection) - erpinfo_content |= ERP_UseProtection; - } else { - erp_len = 0; - } - - crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; - encrypt = crypt && crypt->ops && - ((strcmp(crypt->ops->name, "R-WEP") == 0 || wpa_ie_len)); - if (ieee->ht_info->current_ht_support) { - tmp_ht_cap_buf = (u8 *)&(ieee->ht_info->SelfHTCap); - tmp_ht_cap_len = sizeof(ieee->ht_info->SelfHTCap); - tmp_ht_info_buf = (u8 *)&(ieee->ht_info->SelfHTInfo); - tmp_ht_info_len = sizeof(ieee->ht_info->SelfHTInfo); - HTConstructCapabilityElement(ieee, tmp_ht_cap_buf, - &tmp_ht_cap_len, encrypt, false); - HTConstructInfoElement(ieee, tmp_ht_info_buf, &tmp_ht_info_len, - encrypt); - - if (ht_info->reg_rt2rt_aggregation) { - tmp_generic_ie_buf = ieee->ht_info->sz_rt2rt_agg_buf; - tmp_generic_ie_len = - sizeof(ieee->ht_info->sz_rt2rt_agg_buf); - HTConstructRT2RTAggElement(ieee, tmp_generic_ie_buf, - &tmp_generic_ie_len); - } - } - - beacon_size = sizeof(struct rtllib_probe_response) + 2 + - ssid_len + 3 + rate_len + rate_ex_len + atim_len + erp_len - + wpa_ie_len + ieee->tx_headroom; - skb = dev_alloc_skb(beacon_size); - if (!skb) - return NULL; - - skb_reserve(skb, ieee->tx_headroom); - - beacon_buf = skb_put(skb, (beacon_size - ieee->tx_headroom)); - ether_addr_copy(beacon_buf->header.addr1, dest); - ether_addr_copy(beacon_buf->header.addr2, ieee->dev->dev_addr); - ether_addr_copy(beacon_buf->header.addr3, ieee->current_network.bssid); - - beacon_buf->header.duration_id = 0; - beacon_buf->beacon_interval = - cpu_to_le16(ieee->current_network.beacon_interval); - beacon_buf->capability = - cpu_to_le16(ieee->current_network.capability & - WLAN_CAPABILITY_IBSS); - beacon_buf->capability |= - cpu_to_le16(ieee->current_network.capability & - WLAN_CAPABILITY_SHORT_PREAMBLE); - - if (ieee->current_network.capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) - beacon_buf->capability |= - cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME); - - crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; - if (encrypt) - beacon_buf->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); - - beacon_buf->header.frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_RESP); - beacon_buf->info_element[0].id = MFIE_TYPE_SSID; - beacon_buf->info_element[0].len = ssid_len; - - tag = (u8 *)beacon_buf->info_element[0].data; - - memcpy(tag, ssid, ssid_len); - - tag += ssid_len; - - *(tag++) = MFIE_TYPE_RATES; - *(tag++) = rate_len - 2; - memcpy(tag, ieee->current_network.rates, rate_len - 2); - tag += rate_len - 2; - - *(tag++) = MFIE_TYPE_DS_SET; - *(tag++) = 1; - *(tag++) = ieee->current_network.channel; - - if (atim_len) { - u16 val16; - *(tag++) = MFIE_TYPE_IBSS_SET; - *(tag++) = 2; - val16 = ieee->current_network.atim_window; - memcpy((u8 *)tag, (u8 *)&val16, 2); - tag += 2; - } - - if (erp_len) { - *(tag++) = MFIE_TYPE_ERP; - *(tag++) = 1; - *(tag++) = erpinfo_content; - } - if (rate_ex_len) { - *(tag++) = MFIE_TYPE_RATES_EX; - *(tag++) = rate_ex_len - 2; - memcpy(tag, ieee->current_network.rates_ex, rate_ex_len - 2); - tag += rate_ex_len - 2; - } - - if (wpa_ie_len) { - memcpy(tag, ieee->wpa_ie, ieee->wpa_ie_len); - tag += ieee->wpa_ie_len; - } - return skb; -} - static struct sk_buff *rtllib_null_func(struct rtllib_device *ieee, short pwr) { struct sk_buff *skb; @@ -875,7 +670,7 @@ static inline int SecIsInPMKIDList(struct rtllib_device *ieee, u8 *bssid) int i = 0; do { - if ((ieee->PMKIDList[i].bUsed) && + if ((ieee->PMKIDList[i].used) && (memcmp(ieee->PMKIDList[i].Bssid, bssid, ETH_ALEN) == 0)) break; i++; @@ -933,15 +728,15 @@ rtllib_association_req(struct rtllib_network *beacon, } if (ieee->ht_info->current_ht_support && ieee->ht_info->enable_ht) { - ht_cap_buf = (u8 *)&(ieee->ht_info->SelfHTCap); + ht_cap_buf = (u8 *)&ieee->ht_info->SelfHTCap; ht_cap_len = sizeof(ieee->ht_info->SelfHTCap); - HTConstructCapabilityElement(ieee, ht_cap_buf, &ht_cap_len, + ht_construct_capability_element(ieee, ht_cap_buf, &ht_cap_len, encrypt, true); if (ieee->ht_info->current_rt2rt_aggregation) { realtek_ie_buf = ieee->ht_info->sz_rt2rt_agg_buf; realtek_ie_len = sizeof(ieee->ht_info->sz_rt2rt_agg_buf); - HTConstructRT2RTAggElement(ieee, realtek_ie_buf, + ht_construct_rt2rt_agg_element(ieee, realtek_ie_buf, &realtek_ie_len); } } @@ -1071,7 +866,7 @@ rtllib_association_req(struct rtllib_network *beacon, tag += osCcxVerNum.Length; } if (ieee->ht_info->current_ht_support && ieee->ht_info->enable_ht) { - if (ieee->ht_info->ePeerHTSpecVer != HT_SPEC_VER_EWC) { + if (ieee->ht_info->peer_ht_spec_ver != HT_SPEC_VER_EWC) { tag = skb_put(skb, ht_cap_len); *tag++ = MFIE_TYPE_HT_CAP; *tag++ = ht_cap_len - 2; @@ -1093,7 +888,7 @@ rtllib_association_req(struct rtllib_network *beacon, } if (wmm_info_len) { tag = skb_put(skb, wmm_info_len); - rtllib_WMM_Info(ieee, &tag); + rtllib_wmm_info(ieee, &tag); } if (wps_ie_len && ieee->wps_ie) @@ -1101,11 +896,11 @@ rtllib_association_req(struct rtllib_network *beacon, if (turbo_info_len) { tag = skb_put(skb, turbo_info_len); - rtllib_TURBO_Info(ieee, &tag); + rtllib_turbo_info(ieee, &tag); } if (ieee->ht_info->current_ht_support && ieee->ht_info->enable_ht) { - if (ieee->ht_info->ePeerHTSpecVer == HT_SPEC_VER_EWC) { + if (ieee->ht_info->peer_ht_spec_ver == HT_SPEC_VER_EWC) { tag = skb_put(skb, ht_cap_len); *tag++ = MFIE_TYPE_GENERIC; *tag++ = ht_cap_len - 2; @@ -1123,7 +918,7 @@ rtllib_association_req(struct rtllib_network *beacon, kfree(ieee->assocreq_ies); ieee->assocreq_ies = NULL; - ies = &(hdr->info_element[0].id); + ies = &hdr->info_element[0].id; ieee->assocreq_ies_len = (skb->data + skb->len) - ies; ieee->assocreq_ies = kmemdup(ies, ieee->assocreq_ies_len, GFP_ATOMIC); if (!ieee->assocreq_ies) @@ -1269,7 +1064,7 @@ static void rtllib_associate_complete_wq(void *data) } if (ieee->ht_info->current_ht_support && ieee->ht_info->enable_ht) { netdev_info(ieee->dev, "Successfully associated, ht enabled\n"); - HTOnAssocRsp(ieee); + ht_on_assoc_rsp(ieee); } else { netdev_info(ieee->dev, "Successfully associated, ht not enabled(%d, %d)\n", @@ -1314,7 +1109,7 @@ static void rtllib_associate_procedure_wq(void *data) mutex_lock(&ieee->wx_mutex); rtllib_stop_scan(ieee); - HTSetConnectBwMode(ieee, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT); + ht_set_connect_bw_mode(ieee, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT); if (ieee->rf_power_state == rf_off) { ieee->rtllib_ips_leave_wq(ieee->dev); mutex_unlock(&ieee->wx_mutex); @@ -1417,21 +1212,20 @@ inline void rtllib_softmac_new_net(struct rtllib_device *ieee, !(ieee->softmac_features & IEEE_SOFTMAC_SCAN)) rtllib_stop_scan_syncro(ieee); - HTResetIOTSetting(ieee->ht_info); + ht_reset_iot_setting(ieee->ht_info); ieee->wmm_acm = 0; if (ieee->iw_mode == IW_MODE_INFRA) { /* Join the network for the first time */ ieee->AsocRetryCount = 0; if ((ieee->current_network.qos_data.supported == 1) && ieee->current_network.bssht.bd_support_ht) - HTResetSelfAndSavePeerSetting(ieee, + ht_reset_self_and_save_peer_setting(ieee, &(ieee->current_network)); else ieee->ht_info->current_ht_support = false; ieee->link_state = RTLLIB_ASSOCIATING; - schedule_delayed_work( - &ieee->associate_procedure_wq, 0); + schedule_delayed_work(&ieee->associate_procedure_wq, 0); } else { if (rtllib_is_54g(&ieee->current_network)) { ieee->rate = 108; @@ -1825,7 +1619,7 @@ rtllib_rx_assoc_resp(struct rtllib_device *ieee, struct sk_buff *skb, kfree(ieee->assocresp_ies); ieee->assocresp_ies = NULL; - ies = &(assoc_resp->info_element[0].id); + ies = &assoc_resp->info_element[0].id; ieee->assocresp_ies_len = (skb->data + skb->len) - ies; ieee->assocresp_ies = kmemdup(ies, ieee->assocresp_ies_len, @@ -1841,8 +1635,7 @@ rtllib_rx_assoc_resp(struct rtllib_device *ieee, struct sk_buff *skb, "Association response status code 0x%x\n", errcode); if (ieee->AsocRetryCount < RT_ASOC_RETRY_LIMIT) - schedule_delayed_work( - &ieee->associate_procedure_wq, 0); + schedule_delayed_work(&ieee->associate_procedure_wq, 0); else rtllib_associate_abort(ieee); } @@ -1872,7 +1665,7 @@ static void rtllib_rx_auth_resp(struct rtllib_device *ieee, struct sk_buff *skb) ieee->softmac_stats.rx_auth_rs_ok++; if (!(ieee->ht_info->iot_action & HT_IOT_ACT_PURE_N_MODE)) { if (!ieee->GetNmodeSupportBySecCfg(ieee->dev)) { - if (IsHTHalfNmodeAPs(ieee)) { + if (is_ht_half_nmode_aps(ieee)) { bSupportNmode = true; bHalfSupportNmode = true; } else { @@ -2030,7 +1823,7 @@ void rtllib_softmac_xmit(struct rtllib_txb *txb, struct rtllib_device *ieee) * the wait queue */ for (i = 0; i < txb->nr_frags; i++) { - queue_len = skb_queue_len(&ieee->skb_waitQ[queue_index]); + queue_len = skb_queue_len(&ieee->skb_waitq[queue_index]); if ((queue_len != 0) || (!ieee->check_nic_enough_desc(ieee->dev, queue_index)) || (ieee->queue_stop)) { @@ -2039,13 +1832,12 @@ void rtllib_softmac_xmit(struct rtllib_txb *txb, struct rtllib_device *ieee) * to check it any more. */ if (queue_len < 200) - skb_queue_tail(&ieee->skb_waitQ[queue_index], + skb_queue_tail(&ieee->skb_waitq[queue_index], txb->fragments[i]); else kfree_skb(txb->fragments[i]); } else { - ieee->softmac_data_hard_start_xmit( - txb->fragments[i], + ieee->softmac_data_hard_start_xmit(txb->fragments[i], ieee->dev, ieee->rate); } } @@ -2090,10 +1882,6 @@ static void rtllib_start_bss(struct rtllib_device *ieee) { unsigned long flags; - if (IS_DOT11D_ENABLE(ieee) && !IS_COUNTRY_IE_VALID(ieee)) { - if (!ieee->global_domain) - return; - } /* check if we have already found the net we * are interested in (if any). * if not (we are disassociated and we are not @@ -2121,6 +1909,7 @@ static void rtllib_link_change_wq(void *data) struct rtllib_device, link_change_wq); ieee->link_change(ieee->dev); } + /* called only in userspace context */ void rtllib_disassociate(struct rtllib_device *ieee) { @@ -2128,8 +1917,6 @@ void rtllib_disassociate(struct rtllib_device *ieee) if (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE) rtllib_reset_queue(ieee); - if (IS_DOT11D_ENABLE(ieee)) - dot11d_reset(ieee); ieee->link_state = MAC80211_NOLINK; ieee->is_set_key = false; ieee->wap_set = 0; @@ -2181,46 +1968,6 @@ exit: mutex_unlock(&ieee->wx_mutex); } -static struct sk_buff *rtllib_get_beacon_(struct rtllib_device *ieee) -{ - static const u8 broadcast_addr[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff - }; - struct sk_buff *skb; - struct rtllib_probe_response *b; - - skb = rtllib_probe_resp(ieee, broadcast_addr); - - if (!skb) - return NULL; - - b = (struct rtllib_probe_response *)skb->data; - b->header.frame_control = cpu_to_le16(IEEE80211_STYPE_BEACON); - - return skb; -} - -struct sk_buff *rtllib_get_beacon(struct rtllib_device *ieee) -{ - struct sk_buff *skb; - struct rtllib_probe_response *b; - - skb = rtllib_get_beacon_(ieee); - if (!skb) - return NULL; - - b = (struct rtllib_probe_response *)skb->data; - b->header.seq_ctrl = cpu_to_le16(ieee->seq_ctrl[0] << 4); - - if (ieee->seq_ctrl[0] == 0xFFF) - ieee->seq_ctrl[0] = 0; - else - ieee->seq_ctrl[0]++; - - return skb; -} -EXPORT_SYMBOL(rtllib_get_beacon); - void rtllib_softmac_stop_protocol(struct rtllib_device *ieee) { rtllib_stop_scan_syncro(ieee); @@ -2279,8 +2026,6 @@ void rtllib_start_protocol(struct rtllib_device *ieee) short ch = 0; int i = 0; - rtllib_update_active_chan_map(ieee); - if (ieee->proto_started) return; @@ -2326,18 +2071,15 @@ int rtllib_softmac_init(struct rtllib_device *ieee) ieee->link_state = MAC80211_NOLINK; for (i = 0; i < 5; i++) ieee->seq_ctrl[i] = 0; - ieee->dot11d_info = kzalloc(sizeof(struct rt_dot11d_info), GFP_ATOMIC); - if (!ieee->dot11d_info) - return -ENOMEM; ieee->link_detect_info.SlotIndex = 0; ieee->link_detect_info.SlotNum = 2; ieee->link_detect_info.NumRecvBcnInPeriod = 0; ieee->link_detect_info.NumRecvDataInPeriod = 0; - ieee->link_detect_info.NumTxOkInPeriod = 0; - ieee->link_detect_info.NumRxOkInPeriod = 0; + ieee->link_detect_info.num_tx_ok_in_period = 0; + ieee->link_detect_info.num_rx_ok_in_period = 0; ieee->link_detect_info.NumRxUnicastOkInPeriod = 0; - ieee->bIsAggregateFrame = false; + ieee->is_aggregate_frame = false; ieee->assoc_id = 0; ieee->queue_stop = 0; ieee->scanning_continue = 0; @@ -2369,8 +2111,6 @@ int rtllib_softmac_init(struct rtllib_device *ieee) timer_setup(&ieee->associate_timer, rtllib_associate_abort_cb, 0); - timer_setup(&ieee->beacon_timer, rtllib_send_beacon_cb, 0); - INIT_DELAYED_WORK(&ieee->link_change_wq, (void *)rtllib_link_change_wq); INIT_WORK(&ieee->associate_complete_wq, (void *)rtllib_associate_complete_wq); INIT_DELAYED_WORK(&ieee->associate_procedure_wq, (void *)rtllib_associate_procedure_wq); @@ -2404,9 +2144,6 @@ void rtllib_softmac_free(struct rtllib_device *ieee) cancel_work_sync(&ieee->ips_leave_wq); cancel_work_sync(&ieee->wx_sync_scan_wq); cancel_work_sync(&ieee->ps_task); - - kfree(ieee->dot11d_info); - ieee->dot11d_info = NULL; } static inline struct sk_buff * @@ -2493,7 +2230,7 @@ u8 rtllib_ap_sec_type(struct rtllib_device *ieee) return SEC_ALG_WEP; } else if ((wpa_ie_len != 0)) { if (((ieee->wpa_ie[0] == 0xdd) && - (!memcmp(&(ieee->wpa_ie[14]), ccmp_ie, 4))) || + (!memcmp(&ieee->wpa_ie[14], ccmp_ie, 4))) || ((ieee->wpa_ie[0] == 0x30) && (!memcmp(&ieee->wpa_ie[10], ccmp_rsn_ie, 4)))) return SEC_ALG_CCMP; @@ -2508,7 +2245,7 @@ static void rtllib_MlmeDisassociateRequest(struct rtllib_device *rtllib, u8 *asSta, u8 asRsn) { u8 i; - u8 OpMode; + u8 op_mode; RemovePeerTS(rtllib, asSta); @@ -2517,10 +2254,10 @@ static void rtllib_MlmeDisassociateRequest(struct rtllib_device *rtllib, for (i = 0; i < 6; i++) rtllib->current_network.bssid[i] = 0x22; - OpMode = RT_OP_MODE_NO_LINK; - rtllib->OpMode = RT_OP_MODE_NO_LINK; + op_mode = RT_OP_MODE_NO_LINK; + rtllib->op_mode = RT_OP_MODE_NO_LINK; rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_MEDIA_STATUS, - (u8 *)(&OpMode)); + (u8 *)(&op_mode)); rtllib_disassociate(rtllib); rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_BSSID, @@ -2528,11 +2265,7 @@ static void rtllib_MlmeDisassociateRequest(struct rtllib_device *rtllib, } } -static void -rtllib_MgntDisconnectAP( - struct rtllib_device *rtllib, - u8 asRsn -) +static void rtllib_MgntDisconnectAP(struct rtllib_device *rtllib, u8 asRsn) { bool bFilterOutNonAssociatedBSSID = false; diff --git a/drivers/staging/rtl8192e/rtllib_softmac_wx.c b/drivers/staging/rtl8192e/rtllib_softmac_wx.c index f32584291704..2afa701e5445 100644 --- a/drivers/staging/rtl8192e/rtllib_softmac_wx.c +++ b/drivers/staging/rtl8192e/rtllib_softmac_wx.c @@ -14,7 +14,6 @@ #include <linux/etherdevice.h> #include "rtllib.h" -#include "dot11d.h" int rtllib_wx_set_freq(struct rtllib_device *ieee, struct iw_request_info *a, union iwreq_data *wrqu, char *b) @@ -208,7 +207,7 @@ int rtllib_wx_get_rate(struct rtllib_device *ieee, { u32 tmp_rate; - tmp_rate = TxCountToDataRate(ieee, + tmp_rate = tx_count_to_data_rate(ieee, ieee->softmac_stats.CurrentShowTxate); wrqu->bitrate.value = tmp_rate * 500000; @@ -267,11 +266,11 @@ int rtllib_wx_set_mode(struct rtllib_device *ieee, struct iw_request_info *a, if (wrqu->mode == IW_MODE_MONITOR) { ieee->dev->type = ARPHRD_IEEE80211; - rtllib_EnableNetMonitorMode(ieee->dev, false); + rtllib_enable_net_monitor_mode(ieee->dev, false); } else { ieee->dev->type = ARPHRD_ETHER; if (ieee->iw_mode == IW_MODE_MONITOR) - rtllib_DisableNetMonitorMode(ieee->dev, false); + rtllib_disable_net_monitor_mode(ieee->dev, false); } if (!ieee->proto_started) { @@ -318,10 +317,10 @@ void rtllib_wx_sync_scan_wq(void *data) ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_BACKUP); if (ieee->ht_info->current_ht_support && ieee->ht_info->enable_ht && - ieee->ht_info->bCurBW40MHz) { + ieee->ht_info->cur_bw_40mhz) { b40M = 1; chan_offset = ieee->ht_info->CurSTAExtChnlOffset; - bandwidth = (enum ht_channel_width)ieee->ht_info->bCurBW40MHz; + bandwidth = (enum ht_channel_width)ieee->ht_info->cur_bw_40mhz; ieee->set_bw_mode_handler(ieee->dev, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT); } diff --git a/drivers/staging/rtl8192e/rtllib_tx.c b/drivers/staging/rtl8192e/rtllib_tx.c index 9bf679438ad1..f7098a2ba8b0 100644 --- a/drivers/staging/rtl8192e/rtllib_tx.c +++ b/drivers/staging/rtl8192e/rtllib_tx.c @@ -267,7 +267,7 @@ static void rtllib_tx_query_agg_cap(struct rtllib_device *ieee, struct cb_desc *tcb_desc) { struct rt_hi_throughput *ht_info = ieee->ht_info; - struct tx_ts_record *pTxTs = NULL; + struct tx_ts_record *ts = NULL; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; if (rtllib_act_scanning(ieee, false)) @@ -288,52 +288,35 @@ static void rtllib_tx_query_agg_cap(struct rtllib_device *ieee, if (!ieee->GetNmodeSupportBySecCfg(ieee->dev)) return; - if (ht_info->bCurrentAMPDUEnable) { - if (!rtllib_get_ts(ieee, (struct ts_common_info **)(&pTxTs), hdr->addr1, + if (ht_info->current_ampdu_enable) { + if (!rtllib_get_ts(ieee, (struct ts_common_info **)(&ts), hdr->addr1, skb->priority, TX_DIR, true)) { netdev_info(ieee->dev, "%s: can't get TS\n", __func__); return; } - if (!pTxTs->TxAdmittedBARecord.b_valid) { + if (!ts->tx_admitted_ba_record.b_valid) { if (ieee->wpa_ie_len && (ieee->pairwise_key_type == KEY_TYPE_NA)) { ; } else if (tcb_desc->bdhcp == 1) { ; - } else if (!pTxTs->bDisable_AddBa) { - TsStartAddBaProcess(ieee, pTxTs); + } else if (!ts->disable_add_ba) { + TsStartAddBaProcess(ieee, ts); } - goto FORCED_AGG_SETTING; - } else if (!pTxTs->bUsingBa) { - if (SN_LESS(pTxTs->TxAdmittedBARecord.ba_start_seq_ctrl.field.seq_num, - (pTxTs->TxCurSeq + 1) % 4096)) - pTxTs->bUsingBa = true; + return; + } else if (!ts->using_ba) { + if (SN_LESS(ts->tx_admitted_ba_record.ba_start_seq_ctrl.field.seq_num, + (ts->tx_cur_seq + 1) % 4096)) + ts->using_ba = true; else - goto FORCED_AGG_SETTING; + return; } if (ieee->iw_mode == IW_MODE_INFRA) { - tcb_desc->bAMPDUEnable = true; + tcb_desc->ampdu_enable = true; tcb_desc->ampdu_factor = ht_info->CurrentAMPDUFactor; tcb_desc->ampdu_density = ht_info->current_mpdu_density; } } -FORCED_AGG_SETTING: - switch (ht_info->ForcedAMPDUMode) { - case HT_AGG_AUTO: - break; - - case HT_AGG_FORCE_ENABLE: - tcb_desc->bAMPDUEnable = true; - tcb_desc->ampdu_density = ht_info->forced_mpdu_density; - tcb_desc->ampdu_factor = ht_info->forced_ampdu_factor; - break; - - case HT_AGG_FORCE_DISABLE: - tcb_desc->bAMPDUEnable = false; - tcb_desc->ampdu_density = 0; - tcb_desc->ampdu_factor = 0; - break; - } } static void rtllib_query_ShortPreambleMode(struct rtllib_device *ieee, @@ -357,14 +340,9 @@ static void rtllib_query_HTCapShortGI(struct rtllib_device *ieee, if (!ht_info->current_ht_support || !ht_info->enable_ht) return; - if (ht_info->forced_short_gi) { - tcb_desc->bUseShortGI = true; - return; - } - - if (ht_info->bCurBW40MHz && ht_info->bCurShortGI40MHz) + if (ht_info->cur_bw_40mhz && ht_info->cur_short_gi_40mhz) tcb_desc->bUseShortGI = true; - else if (!ht_info->bCurBW40MHz && ht_info->bCurShortGI20MHz) + else if (!ht_info->cur_bw_40mhz && ht_info->cur_short_gi_20mhz) tcb_desc->bUseShortGI = true; } @@ -383,7 +361,7 @@ static void rtllib_query_BandwidthMode(struct rtllib_device *ieee, if ((tcb_desc->data_rate & 0x80) == 0) return; - if (ht_info->bCurBW40MHz && ht_info->cur_tx_bw40mhz && + if (ht_info->cur_bw_40mhz && ht_info->cur_tx_bw40mhz && !ieee->bandwidth_auto_switch.bforced_tx20Mhz) tcb_desc->bPacketBW = true; } @@ -441,9 +419,9 @@ static void rtllib_query_protectionmode(struct rtllib_device *ieee, if (ht_info->current_ht_support && ht_info->enable_ht) { u8 HTOpMode = ht_info->current_op_mode; - if ((ht_info->bCurBW40MHz && (HTOpMode == 2 || + if ((ht_info->cur_bw_40mhz && (HTOpMode == 2 || HTOpMode == 3)) || - (!ht_info->bCurBW40MHz && HTOpMode == 3)) { + (!ht_info->cur_bw_40mhz && HTOpMode == 3)) { tcb_desc->rts_rate = MGN_24M; tcb_desc->bRTSEnable = true; break; @@ -454,7 +432,7 @@ static void rtllib_query_protectionmode(struct rtllib_device *ieee, tcb_desc->bRTSEnable = true; break; } - if (tcb_desc->bAMPDUEnable) { + if (tcb_desc->ampdu_enable) { tcb_desc->rts_rate = MGN_24M; tcb_desc->bRTSEnable = false; break; @@ -500,8 +478,8 @@ static u16 rtllib_query_seqnum(struct rtllib_device *ieee, struct sk_buff *skb, if (!rtllib_get_ts(ieee, (struct ts_common_info **)(&ts), dst, skb->priority, TX_DIR, true)) return 0; - seqnum = ts->TxCurSeq; - ts->TxCurSeq = (ts->TxCurSeq + 1) % 4096; + seqnum = ts->tx_cur_seq; + ts->tx_cur_seq = (ts->tx_cur_seq + 1) % 4096; return seqnum; } return 0; @@ -847,7 +825,7 @@ static int rtllib_xmit_inter(struct sk_buff *skb, struct net_device *dev) if (txb) { tcb_desc = (struct cb_desc *) (txb->fragments[0]->cb + MAX_DEV_ADDR_SIZE); - tcb_desc->bTxEnableFwCalcDur = 1; + tcb_desc->tx_enable_fw_calc_dur = 1; tcb_desc->priority = skb->priority; if (ether_type == ETH_P_PAE) { diff --git a/drivers/staging/rtl8192e/rtllib_wx.c b/drivers/staging/rtl8192e/rtllib_wx.c index a37250de7ba3..f92ec0faf4d5 100644 --- a/drivers/staging/rtl8192e/rtllib_wx.c +++ b/drivers/staging/rtl8192e/rtllib_wx.c @@ -134,7 +134,7 @@ static inline char *rtl819x_translate_scan(struct rtllib_device *ieee, ((ht_cap->ShortGI40Mhz) ? 1 : 0) : ((ht_cap->ShortGI20Mhz) ? 1 : 0); - max_mcs = HTGetHighestMCSRate(ieee, ht_cap->MCS, + max_mcs = ht_get_highest_mcs_rate(ieee, ht_cap->MCS, MCS_FILTER_ALL); rate = MCS_DATA_RATE[is40M][isShortGI][max_mcs & 0x7f]; if (rate > max_rate) diff --git a/drivers/staging/rtl8712/os_intfs.c b/drivers/staging/rtl8712/os_intfs.c index b18e6d9c832b..7554613fe7e1 100644 --- a/drivers/staging/rtl8712/os_intfs.c +++ b/drivers/staging/rtl8712/os_intfs.c @@ -221,8 +221,7 @@ struct net_device *r8712_init_netdev(void) static u32 start_drv_threads(struct _adapter *padapter) { - padapter->cmd_thread = kthread_run(r8712_cmd_thread, padapter, "%s", - padapter->pnetdev->name); + padapter->cmd_thread = kthread_run(r8712_cmd_thread, padapter, "%s", padapter->pnetdev->name); if (IS_ERR(padapter->cmd_thread)) return _FAIL; return _SUCCESS; diff --git a/drivers/staging/rtl8712/rtl8712_efuse.c b/drivers/staging/rtl8712/rtl8712_efuse.c index c9400e40a1d6..a39d6c06648f 100644 --- a/drivers/staging/rtl8712/rtl8712_efuse.c +++ b/drivers/staging/rtl8712/rtl8712_efuse.c @@ -213,8 +213,8 @@ u16 r8712_efuse_get_current_size(struct _adapter *adapter) u8 hworden = 0; u8 efuse_data, word_cnts = 0; - while (bContinual && efuse_one_byte_read(adapter, efuse_addr, - &efuse_data) && (efuse_addr < efuse_available_max_size)) { + while (bContinual && efuse_one_byte_read(adapter, efuse_addr, &efuse_data) && + (efuse_addr < efuse_available_max_size)) { if (efuse_data != 0xFF) { hworden = efuse_data & 0x0F; word_cnts = calculate_word_cnts(hworden); @@ -252,9 +252,8 @@ u8 r8712_efuse_pg_packet_read(struct _adapter *adapter, u8 offset, u8 *data) memset(tmpdata, 0xFF, PGPKT_DATA_SIZE); for (tmpidx = 0; tmpidx < word_cnts * 2; tmpidx++) { - if (efuse_one_byte_read(adapter, - efuse_addr + 1 + tmpidx, - &efuse_data)) { + if (efuse_one_byte_read(adapter, efuse_addr + 1 + tmpidx, + &efuse_data)) { tmpdata[tmpidx] = efuse_data; } else { ret = false; diff --git a/drivers/staging/rtl8712/rtl8712_recv.c b/drivers/staging/rtl8712/rtl8712_recv.c index 7da014ab0723..a3c4713c59b3 100644 --- a/drivers/staging/rtl8712/rtl8712_recv.c +++ b/drivers/staging/rtl8712/rtl8712_recv.c @@ -267,8 +267,7 @@ union recv_frame *r8712_recvframe_chk_defrag(struct _adapter *padapter, /*the first fragment*/ if (!list_empty(&pdefrag_q->queue)) { /*free current defrag_q */ - r8712_free_recvframe_queue(pdefrag_q, - pfree_recv_queue); + r8712_free_recvframe_queue(pdefrag_q, pfree_recv_queue); } } /* Then enqueue the 0~(n-1) fragment to the defrag_q */ diff --git a/drivers/staging/rtl8712/rtl8712_xmit.c b/drivers/staging/rtl8712/rtl8712_xmit.c index 4cb01f590673..d7d678b04ca8 100644 --- a/drivers/staging/rtl8712/rtl8712_xmit.c +++ b/drivers/staging/rtl8712/rtl8712_xmit.c @@ -147,9 +147,8 @@ static u32 get_ff_hwaddr(struct xmit_frame *pxmitframe) } static struct xmit_frame *dequeue_one_xmitframe(struct xmit_priv *pxmitpriv, - struct hw_xmit *phwxmit, - struct tx_servq *ptxservq, - struct __queue *pframe_queue) + struct hw_xmit *phwxmit, struct tx_servq *ptxservq, + struct __queue *pframe_queue) { struct list_head *xmitframe_plist, *xmitframe_phead; struct xmit_frame *pxmitframe = NULL; @@ -167,7 +166,7 @@ static struct xmit_frame *dequeue_one_xmitframe(struct xmit_priv *pxmitpriv, } static struct xmit_frame *dequeue_xframe_ex(struct xmit_priv *pxmitpriv, - struct hw_xmit *phwxmit_i, sint entry) + struct hw_xmit *phwxmit_i, sint entry) { unsigned long irqL0; struct list_head *sta_plist, *sta_phead; @@ -197,11 +196,10 @@ static struct xmit_frame *dequeue_xframe_ex(struct xmit_priv *pxmitpriv, sta_phead = &phwxmit->sta_queue->queue; sta_plist = sta_phead->next; while (!end_of_queue_search(sta_phead, sta_plist)) { - ptxservq = container_of(sta_plist, struct tx_servq, - tx_pending); + ptxservq = container_of(sta_plist, struct tx_servq, tx_pending); pframe_queue = &ptxservq->sta_pending; - pxmitframe = dequeue_one_xmitframe(pxmitpriv, phwxmit, - ptxservq, pframe_queue); + pxmitframe = dequeue_one_xmitframe(pxmitpriv, phwxmit, ptxservq, + pframe_queue); if (pxmitframe) { phwxmit->accnt--; goto exit_dequeue_xframe_ex; @@ -221,8 +219,7 @@ exit_dequeue_xframe_ex: return pxmitframe; } -void r8712_do_queue_select(struct _adapter *padapter, - struct pkt_attrib *pattrib) +void r8712_do_queue_select(struct _adapter *padapter, struct pkt_attrib *pattrib) { unsigned int qsel = 0; struct dvobj_priv *pdvobj = &padapter->dvobjpriv; @@ -292,14 +289,12 @@ void r8712_append_mpdu_unit(struct xmit_buf *pxmitbuf, r8712_xmit_complete(padapter, pxmitframe); if (pxmitframe->attrib.ether_type != 0x0806) { if ((pxmitframe->attrib.ether_type != 0x888e) && - (pxmitframe->attrib.dhcp_pkt != 1)) { - r8712_issue_addbareq_cmd(padapter, - pxmitframe->attrib.priority); + (pxmitframe->attrib.dhcp_pkt != 1)) { + r8712_issue_addbareq_cmd(padapter, pxmitframe->attrib.priority); } } pxmitframe->last[0] = 1; - update_txdesc(pxmitframe, (uint *)(pxmitframe->buf_addr), - pxmitframe->attrib.last_txcmdsz); + update_txdesc(pxmitframe, (uint *)(pxmitframe->buf_addr), pxmitframe->attrib.last_txcmdsz); /*padding zero */ last_txcmdsz = pxmitframe->attrib.last_txcmdsz; padding_sz = (8 - (last_txcmdsz % 8)); @@ -333,8 +328,7 @@ void r8712_xmitframe_aggr_1st(struct xmit_buf *pxmitbuf, pxmitbuf->aggr_nr = 1; } -u16 r8712_xmitframe_aggr_next(struct xmit_buf *pxmitbuf, - struct xmit_frame *pxmitframe) +u16 r8712_xmitframe_aggr_next(struct xmit_buf *pxmitbuf, struct xmit_frame *pxmitframe) { pxmitframe->pxmitbuf = pxmitbuf; pxmitbuf->priv_data = pxmitframe; @@ -374,9 +368,9 @@ void r8712_dump_aggr_xframe(struct xmit_buf *pxmitbuf, pxmitframe->bpending[0] = false; pxmitframe->mem_addr = pxmitbuf->pbuf; - if ((pdvobj->ishighspeed && ((total_length + TXDESC_SIZE) % 0x200) == - 0) || ((!pdvobj->ishighspeed && ((total_length + TXDESC_SIZE) % - 0x40) == 0))) { + if ((pdvobj->ishighspeed && ((total_length + TXDESC_SIZE) % 0x200) == 0) || + ((!pdvobj->ishighspeed && ((total_length + TXDESC_SIZE) % + 0x40) == 0))) { ptxdesc->txdw0 |= cpu_to_le32 (((TXDESC_SIZE + OFFSET_SZ + 8) << OFFSET_SHT) & 0x00ff0000); @@ -387,8 +381,8 @@ void r8712_dump_aggr_xframe(struct xmit_buf *pxmitbuf, 0x00ff0000); /*default = 32 bytes for TX Desc*/ } - r8712_write_port(pxmitframe->padapter, RTL8712_DMA_H2CCMD, - total_length + TXDESC_SIZE, (u8 *)pxmitframe); + r8712_write_port(pxmitframe->padapter, RTL8712_DMA_H2CCMD, total_length + TXDESC_SIZE, + (u8 *)pxmitframe); } #endif @@ -618,14 +612,12 @@ int r8712_xmitframe_complete(struct _adapter *padapter, pxmitframe = dequeue_xframe_ex(pxmitpriv, phwxmits, hwentry); /* need to remember the 1st frame */ if (pxmitframe) { - #ifdef CONFIG_R8712_TX_AGGR /* 1. dequeue 2nd frame * 2. aggr if 2nd xframe is dequeued, else dump directly */ if (AGGR_NR_HIGH_BOUND > 1) - p2ndxmitframe = dequeue_xframe_ex(pxmitpriv, phwxmits, - hwentry); + p2ndxmitframe = dequeue_xframe_ex(pxmitpriv, phwxmits, hwentry); if (pxmitframe->frame_tag != DATA_FRAMETAG) { r8712_free_xmitbuf(pxmitpriv, pxmitbuf); return false; @@ -639,16 +631,12 @@ int r8712_xmitframe_complete(struct _adapter *padapter, if (p2ndxmitframe) { u16 total_length; - total_length = r8712_xmitframe_aggr_next( - pxmitbuf, p2ndxmitframe); + total_length = r8712_xmitframe_aggr_next(pxmitbuf, p2ndxmitframe); do { - p2ndxmitframe = dequeue_xframe_ex( - pxmitpriv, phwxmits, hwentry); + p2ndxmitframe = dequeue_xframe_ex(pxmitpriv, phwxmits, hwentry); if (p2ndxmitframe) total_length = - r8712_xmitframe_aggr_next( - pxmitbuf, - p2ndxmitframe); + r8712_xmitframe_aggr_next(pxmitbuf, p2ndxmitframe); else break; } while (total_length <= 0x1800 && @@ -662,8 +650,8 @@ int r8712_xmitframe_complete(struct _adapter *padapter, xmitframe_xmitbuf_attach(pxmitframe, pxmitbuf); if (pxmitframe->frame_tag == DATA_FRAMETAG) { if (pxmitframe->attrib.priority <= 15) - res = r8712_xmitframe_coalesce(padapter, - pxmitframe->pkt, pxmitframe); + res = r8712_xmitframe_coalesce(padapter, pxmitframe->pkt, + pxmitframe); /* always return ndis_packet after * r8712_xmitframe_coalesce */ @@ -714,10 +702,10 @@ static void dump_xframe(struct _adapter *padapter, ff_hwaddr = get_ff_hwaddr(pxmitframe); #ifdef CONFIG_R8712_TX_AGGR r8712_write_port(padapter, RTL8712_DMA_H2CCMD, w_sz, - (unsigned char *)pxmitframe); + (unsigned char *)pxmitframe); #else r8712_write_port(padapter, ff_hwaddr, w_sz, - (unsigned char *)pxmitframe); + (unsigned char *)pxmitframe); #endif mem_addr += w_sz; mem_addr = (u8 *)RND4(((addr_t)(mem_addr))); diff --git a/drivers/staging/rtl8712/rtl871x_cmd.c b/drivers/staging/rtl8712/rtl871x_cmd.c index 4be96df5a329..bbd4a13c7bb9 100644 --- a/drivers/staging/rtl8712/rtl871x_cmd.c +++ b/drivers/staging/rtl8712/rtl871x_cmd.c @@ -242,8 +242,7 @@ void r8712_set_chplan_cmd(struct _adapter *padapter, int chplan) kfree(ph2c); return; } - init_h2fwcmd_w_parm_no_rsp(ph2c, psetchplanpara, - GEN_CMD_CODE(_SetChannelPlan)); + init_h2fwcmd_w_parm_no_rsp(ph2c, psetchplanpara, GEN_CMD_CODE(_SetChannelPlan)); psetchplanpara->ChannelPlan = chplan; r8712_enqueue_cmd(pcmdpriv, ph2c); } @@ -302,8 +301,7 @@ void r8712_getbbrfreg_cmdrsp_callback(struct _adapter *padapter, padapter->mppriv.workparam.bcompleted = true; } -void r8712_readtssi_cmdrsp_callback(struct _adapter *padapter, - struct cmd_obj *pcmd) +void r8712_readtssi_cmdrsp_callback(struct _adapter *padapter, struct cmd_obj *pcmd) { kfree(pcmd->parmbuf); kfree(pcmd); @@ -374,11 +372,10 @@ int r8712_joinbss_cmd(struct _adapter *padapter, struct wlan_network *pnetwork) psecuritypriv->authenticator_ie[0] = (unsigned char) psecnetwork->IELength; if ((psecnetwork->IELength - 12) < (256 - 1)) - memcpy(&psecuritypriv->authenticator_ie[1], - &psecnetwork->IEs[12], psecnetwork->IELength - 12); + memcpy(&psecuritypriv->authenticator_ie[1], &psecnetwork->IEs[12], + psecnetwork->IELength - 12); else - memcpy(&psecuritypriv->authenticator_ie[1], - &psecnetwork->IEs[12], (256 - 1)); + memcpy(&psecuritypriv->authenticator_ie[1], &psecnetwork->IEs[12], (256 - 1)); psecnetwork->IELength = 0; /* * If the driver wants to use the bssid to create the connection. @@ -388,19 +385,15 @@ int r8712_joinbss_cmd(struct _adapter *padapter, struct wlan_network *pnetwork) if (!pmlmepriv->assoc_by_bssid) ether_addr_copy(&pmlmepriv->assoc_bssid[0], &pnetwork->network.MacAddress[0]); - psecnetwork->IELength = r8712_restruct_sec_ie(padapter, - &pnetwork->network.IEs[0], - &psecnetwork->IEs[0], - pnetwork->network.IELength); + psecnetwork->IELength = r8712_restruct_sec_ie(padapter, &pnetwork->network.IEs[0], + &psecnetwork->IEs[0], pnetwork->network.IELength); pqospriv->qos_option = 0; if (pregistrypriv->wmm_enable) { u32 tmp_len; - tmp_len = r8712_restruct_wmm_ie(padapter, - &pnetwork->network.IEs[0], - &psecnetwork->IEs[0], - pnetwork->network.IELength, - psecnetwork->IELength); + tmp_len = r8712_restruct_wmm_ie(padapter, &pnetwork->network.IEs[0], + &psecnetwork->IEs[0], pnetwork->network.IELength, + psecnetwork->IELength); if (psecnetwork->IELength != tmp_len) { psecnetwork->IELength = tmp_len; pqospriv->qos_option = 1; /* WMM IE in beacon */ @@ -427,39 +420,28 @@ int r8712_joinbss_cmd(struct _adapter *padapter, struct wlan_network *pnetwork) psecuritypriv->supplicant_ie[0] = (u8)psecnetwork->IELength; if (psecnetwork->IELength < 255) memcpy(&psecuritypriv->supplicant_ie[1], &psecnetwork->IEs[0], - psecnetwork->IELength); + psecnetwork->IELength); else memcpy(&psecuritypriv->supplicant_ie[1], &psecnetwork->IEs[0], - 255); + 255); /* get cmdsz before endian conversion */ pcmd->cmdsz = r8712_get_wlan_bssid_ex_sz(psecnetwork); #ifdef __BIG_ENDIAN /* wlan_network endian conversion */ psecnetwork->Length = cpu_to_le32(psecnetwork->Length); - psecnetwork->Ssid.SsidLength = cpu_to_le32( - psecnetwork->Ssid.SsidLength); + psecnetwork->Ssid.SsidLength = cpu_to_le32(psecnetwork->Ssid.SsidLength); psecnetwork->Privacy = cpu_to_le32(psecnetwork->Privacy); psecnetwork->Rssi = cpu_to_le32(psecnetwork->Rssi); - psecnetwork->NetworkTypeInUse = cpu_to_le32( - psecnetwork->NetworkTypeInUse); - psecnetwork->Configuration.ATIMWindow = cpu_to_le32( - psecnetwork->Configuration.ATIMWindow); - psecnetwork->Configuration.BeaconPeriod = cpu_to_le32( - psecnetwork->Configuration.BeaconPeriod); - psecnetwork->Configuration.DSConfig = cpu_to_le32( - psecnetwork->Configuration.DSConfig); - psecnetwork->Configuration.FHConfig.DwellTime = cpu_to_le32( - psecnetwork->Configuration.FHConfig.DwellTime); - psecnetwork->Configuration.FHConfig.HopPattern = cpu_to_le32( - psecnetwork->Configuration.FHConfig.HopPattern); - psecnetwork->Configuration.FHConfig.HopSet = cpu_to_le32( - psecnetwork->Configuration.FHConfig.HopSet); - psecnetwork->Configuration.FHConfig.Length = cpu_to_le32( - psecnetwork->Configuration.FHConfig.Length); - psecnetwork->Configuration.Length = cpu_to_le32( - psecnetwork->Configuration.Length); - psecnetwork->InfrastructureMode = cpu_to_le32( - psecnetwork->InfrastructureMode); + psecnetwork->NetworkTypeInUse = cpu_to_le32(psecnetwork->NetworkTypeInUse); + psecnetwork->Configuration.ATIMWindow = cpu_to_le32(psecnetwork->Configuration.ATIMWindow); + psecnetwork->Configuration.BeaconPeriod = cpu_to_le32(psecnetwork->Configuration.BeaconPeriod); + psecnetwork->Configuration.DSConfig = cpu_to_le32(psecnetwork->Configuration.DSConfig); + psecnetwork->Configuration.FHConfig.DwellTime = cpu_to_le32(psecnetwork->Configuration.FHConfig.DwellTime); + psecnetwork->Configuration.FHConfig.HopPattern = cpu_to_le32(psecnetwork->Configuration.FHConfig.HopPattern); + psecnetwork->Configuration.FHConfig.HopSet = cpu_to_le32(psecnetwork->Configuration.FHConfig.HopSet); + psecnetwork->Configuration.FHConfig.Length = cpu_to_le32(psecnetwork->Configuration.FHConfig.Length); + psecnetwork->Configuration.Length = cpu_to_le32(psecnetwork->Configuration.Length); + psecnetwork->InfrastructureMode = cpu_to_le32(psecnetwork->InfrastructureMode); psecnetwork->IELength = cpu_to_le32(psecnetwork->IELength); #endif INIT_LIST_HEAD(&pcmd->list); @@ -485,13 +467,12 @@ void r8712_disassoc_cmd(struct _adapter *padapter) /* for sta_mode */ kfree(pdisconnect_cmd); return; } - init_h2fwcmd_w_parm_no_rsp(pdisconnect_cmd, pdisconnect, - _DisConnect_CMD_); + init_h2fwcmd_w_parm_no_rsp(pdisconnect_cmd, pdisconnect, _DisConnect_CMD_); r8712_enqueue_cmd(pcmdpriv, pdisconnect_cmd); } void r8712_setopmode_cmd(struct _adapter *padapter, - enum NDIS_802_11_NETWORK_INFRASTRUCTURE networktype) + enum NDIS_802_11_NETWORK_INFRASTRUCTURE networktype) { struct cmd_obj *ph2c; struct setopmode_parm *psetop; @@ -543,14 +524,12 @@ void r8712_setstakey_cmd(struct _adapter *padapter, u8 *psta, u8 unicast_key) psetstakey_para->algorithm = (unsigned char) psecuritypriv->PrivacyAlgrthm; else - GET_ENCRY_ALGO(psecuritypriv, sta, - psetstakey_para->algorithm, false); + GET_ENCRY_ALGO(psecuritypriv, sta, psetstakey_para->algorithm, false); if (unicast_key) memcpy(&psetstakey_para->key, &sta->x_UncstKey, 16); else - memcpy(&psetstakey_para->key, - &psecuritypriv->XGrpKey[ - psecuritypriv->XGrpKeyid - 1]. skey, 16); + memcpy(&psetstakey_para->key, &psecuritypriv->XGrpKey[psecuritypriv->XGrpKeyid - 1]. + skey, 16); r8712_enqueue_cmd(pcmdpriv, ph2c); } @@ -568,8 +547,7 @@ void r8712_setMacAddr_cmd(struct _adapter *padapter, const u8 *mac_addr) kfree(ph2c); return; } - init_h2fwcmd_w_parm_no_rsp(ph2c, psetMacAddr_para, - _SetMacAddress_CMD_); + init_h2fwcmd_w_parm_no_rsp(ph2c, psetMacAddr_para, _SetMacAddress_CMD_); ether_addr_copy(psetMacAddr_para->MacAddr, mac_addr); r8712_enqueue_cmd(pcmdpriv, ph2c); } @@ -589,8 +567,7 @@ void r8712_addbareq_cmd(struct _adapter *padapter, u8 tid) return; } paddbareq_parm->tid = tid; - init_h2fwcmd_w_parm_no_rsp(ph2c, paddbareq_parm, - GEN_CMD_CODE(_AddBAReq)); + init_h2fwcmd_w_parm_no_rsp(ph2c, paddbareq_parm, GEN_CMD_CODE(_AddBAReq)); r8712_enqueue_cmd_ex(pcmdpriv, ph2c); } @@ -644,13 +621,11 @@ void r8712_joinbss_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd) struct mlme_priv *pmlmepriv = &padapter->mlmepriv; if (pcmd->res != H2C_SUCCESS) - mod_timer(&pmlmepriv->assoc_timer, - jiffies + msecs_to_jiffies(1)); + mod_timer(&pmlmepriv->assoc_timer, jiffies + msecs_to_jiffies(1)); r8712_free_cmd_obj(pcmd); } -void r8712_createbss_cmd_callback(struct _adapter *padapter, - struct cmd_obj *pcmd) +void r8712_createbss_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd) { unsigned long irqL; struct sta_info *psta = NULL; @@ -660,8 +635,7 @@ void r8712_createbss_cmd_callback(struct _adapter *padapter, struct wlan_network *tgt_network = &(pmlmepriv->cur_network); if (pcmd->res != H2C_SUCCESS) - mod_timer(&pmlmepriv->assoc_timer, - jiffies + msecs_to_jiffies(1)); + mod_timer(&pmlmepriv->assoc_timer, jiffies + msecs_to_jiffies(1)); del_timer(&pmlmepriv->assoc_timer); #ifdef __BIG_ENDIAN /* endian_convert */ @@ -670,31 +644,21 @@ void r8712_createbss_cmd_callback(struct _adapter *padapter, pnetwork->Privacy = le32_to_cpu(pnetwork->Privacy); pnetwork->Rssi = le32_to_cpu(pnetwork->Rssi); pnetwork->NetworkTypeInUse = le32_to_cpu(pnetwork->NetworkTypeInUse); - pnetwork->Configuration.ATIMWindow = - le32_to_cpu(pnetwork->Configuration.ATIMWindow); - pnetwork->Configuration.DSConfig = - le32_to_cpu(pnetwork->Configuration.DSConfig); - pnetwork->Configuration.FHConfig.DwellTime = - le32_to_cpu(pnetwork->Configuration.FHConfig.DwellTime); - pnetwork->Configuration.FHConfig.HopPattern = - le32_to_cpu(pnetwork->Configuration.FHConfig.HopPattern); - pnetwork->Configuration.FHConfig.HopSet = - le32_to_cpu(pnetwork->Configuration.FHConfig.HopSet); - pnetwork->Configuration.FHConfig.Length = - le32_to_cpu(pnetwork->Configuration.FHConfig.Length); - pnetwork->Configuration.Length = - le32_to_cpu(pnetwork->Configuration.Length); - pnetwork->InfrastructureMode = - le32_to_cpu(pnetwork->InfrastructureMode); + pnetwork->Configuration.ATIMWindow = le32_to_cpu(pnetwork->Configuration.ATIMWindow); + pnetwork->Configuration.DSConfig = le32_to_cpu(pnetwork->Configuration.DSConfig); + pnetwork->Configuration.FHConfig.DwellTime = le32_to_cpu(pnetwork->Configuration.FHConfig.DwellTime); + pnetwork->Configuration.FHConfig.HopPattern = le32_to_cpu(pnetwork->Configuration.FHConfig.HopPattern); + pnetwork->Configuration.FHConfig.HopSet = le32_to_cpu(pnetwork->Configuration.FHConfig.HopSet); + pnetwork->Configuration.FHConfig.Length = le32_to_cpu(pnetwork->Configuration.FHConfig.Length); + pnetwork->Configuration.Length = le32_to_cpu(pnetwork->Configuration.Length); + pnetwork->InfrastructureMode = le32_to_cpu(pnetwork->InfrastructureMode); pnetwork->IELength = le32_to_cpu(pnetwork->IELength); #endif spin_lock_irqsave(&pmlmepriv->lock, irqL); if ((pmlmepriv->fw_state) & WIFI_AP_STATE) { - psta = r8712_get_stainfo(&padapter->stapriv, - pnetwork->MacAddress); + psta = r8712_get_stainfo(&padapter->stapriv, pnetwork->MacAddress); if (!psta) { - psta = r8712_alloc_stainfo(&padapter->stapriv, - pnetwork->MacAddress); + psta = r8712_alloc_stainfo(&padapter->stapriv, pnetwork->MacAddress); if (!psta) goto createbss_cmd_fail; } @@ -702,20 +666,17 @@ void r8712_createbss_cmd_callback(struct _adapter *padapter, } else { pwlan = _r8712_alloc_network(pmlmepriv); if (!pwlan) { - pwlan = r8712_get_oldest_wlan_network( - &pmlmepriv->scanned_queue); + pwlan = r8712_get_oldest_wlan_network(&pmlmepriv->scanned_queue); if (!pwlan) goto createbss_cmd_fail; pwlan->last_scanned = jiffies; } else { - list_add_tail(&(pwlan->list), - &pmlmepriv->scanned_queue.queue); + list_add_tail(&(pwlan->list), &pmlmepriv->scanned_queue.queue); } pnetwork->Length = r8712_get_wlan_bssid_ex_sz(pnetwork); memcpy(&(pwlan->network), pnetwork, pnetwork->Length); pwlan->fixed = true; - memcpy(&tgt_network->network, pnetwork, - (r8712_get_wlan_bssid_ex_sz(pnetwork))); + memcpy(&tgt_network->network, pnetwork, (r8712_get_wlan_bssid_ex_sz(pnetwork))); if (pmlmepriv->fw_state & _FW_UNDER_LINKING) pmlmepriv->fw_state ^= _FW_UNDER_LINKING; /* @@ -728,14 +689,11 @@ createbss_cmd_fail: r8712_free_cmd_obj(pcmd); } -void r8712_setstaKey_cmdrsp_callback(struct _adapter *padapter, - struct cmd_obj *pcmd) +void r8712_setstaKey_cmdrsp_callback(struct _adapter *padapter, struct cmd_obj *pcmd) { struct sta_priv *pstapriv = &padapter->stapriv; - struct set_stakey_rsp *psetstakey_rsp = (struct set_stakey_rsp *) - (pcmd->rsp); - struct sta_info *psta = r8712_get_stainfo(pstapriv, - psetstakey_rsp->addr); + struct set_stakey_rsp *psetstakey_rsp = (struct set_stakey_rsp *) (pcmd->rsp); + struct sta_info *psta = r8712_get_stainfo(pstapriv, psetstakey_rsp->addr); if (!psta) goto exit; @@ -750,27 +708,23 @@ void r8712_setassocsta_cmdrsp_callback(struct _adapter *padapter, unsigned long irqL; struct sta_priv *pstapriv = &padapter->stapriv; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct set_assocsta_parm *passocsta_parm = - (struct set_assocsta_parm *)(pcmd->parmbuf); - struct set_assocsta_rsp *passocsta_rsp = - (struct set_assocsta_rsp *) (pcmd->rsp); - struct sta_info *psta = r8712_get_stainfo(pstapriv, - passocsta_parm->addr); + struct set_assocsta_parm *passocsta_parm = (struct set_assocsta_parm *)(pcmd->parmbuf); + struct set_assocsta_rsp *passocsta_rsp = (struct set_assocsta_rsp *) (pcmd->rsp); + struct sta_info *psta = r8712_get_stainfo(pstapriv, passocsta_parm->addr); if (!psta) return; psta->aid = psta->mac_id = passocsta_rsp->cam_id; spin_lock_irqsave(&pmlmepriv->lock, irqL); - if ((check_fwstate(pmlmepriv, WIFI_MP_STATE)) && - (check_fwstate(pmlmepriv, _FW_UNDER_LINKING))) + if ((check_fwstate(pmlmepriv, WIFI_MP_STATE)) && (check_fwstate(pmlmepriv, _FW_UNDER_LINKING))) pmlmepriv->fw_state ^= _FW_UNDER_LINKING; set_fwstate(pmlmepriv, _FW_LINKED); spin_unlock_irqrestore(&pmlmepriv->lock, irqL); r8712_free_cmd_obj(pcmd); } -void r8712_disconnectCtrlEx_cmd(struct _adapter *adapter, u32 enableDrvCtrl, - u32 tryPktCnt, u32 tryPktInterval, u32 firstStageTO) +void r8712_disconnectCtrlEx_cmd(struct _adapter *adapter, u32 enableDrvCtrl, u32 tryPktCnt, + u32 tryPktInterval, u32 firstStageTO) { struct cmd_obj *ph2c; struct DisconnectCtrlEx_param *param; @@ -790,7 +744,6 @@ void r8712_disconnectCtrlEx_cmd(struct _adapter *adapter, u32 enableDrvCtrl, param->TryPktInterval = (unsigned char)tryPktInterval; param->FirstStageTO = (unsigned int)firstStageTO; - init_h2fwcmd_w_parm_no_rsp(ph2c, param, - GEN_CMD_CODE(_DisconnectCtrlEx)); + init_h2fwcmd_w_parm_no_rsp(ph2c, param, GEN_CMD_CODE(_DisconnectCtrlEx)); r8712_enqueue_cmd(pcmdpriv, ph2c); } diff --git a/drivers/staging/rtl8712/rtl871x_cmd.h b/drivers/staging/rtl8712/rtl871x_cmd.h index 8453d8de8248..2613b3c2acfc 100644 --- a/drivers/staging/rtl8712/rtl871x_cmd.h +++ b/drivers/staging/rtl8712/rtl871x_cmd.h @@ -716,39 +716,28 @@ struct DisconnectCtrlEx_param { #define H2C_RESERVED 0x07 void r8712_setMacAddr_cmd(struct _adapter *padapter, const u8 *mac_addr); -u8 r8712_sitesurvey_cmd(struct _adapter *padapter, - struct ndis_802_11_ssid *pssid); +u8 r8712_sitesurvey_cmd(struct _adapter *padapter, struct ndis_802_11_ssid *pssid); int r8712_createbss_cmd(struct _adapter *padapter); void r8712_setstakey_cmd(struct _adapter *padapter, u8 *psta, u8 unicast_key); -int r8712_joinbss_cmd(struct _adapter *padapter, - struct wlan_network *pnetwork); +int r8712_joinbss_cmd(struct _adapter *padapter, struct wlan_network *pnetwork); void r8712_disassoc_cmd(struct _adapter *padapter); -void r8712_setopmode_cmd(struct _adapter *padapter, - enum NDIS_802_11_NETWORK_INFRASTRUCTURE networktype); +void r8712_setopmode_cmd(struct _adapter *padapter, enum NDIS_802_11_NETWORK_INFRASTRUCTURE networktype); int r8712_setdatarate_cmd(struct _adapter *padapter, u8 *rateset); void r8712_set_chplan_cmd(struct _adapter *padapter, int chplan); int r8712_getrfreg_cmd(struct _adapter *padapter, u8 offset, u8 *pval); int r8712_setrfreg_cmd(struct _adapter *padapter, u8 offset, u32 val); void r8712_addbareq_cmd(struct _adapter *padapter, u8 tid); void r8712_wdg_wk_cmd(struct _adapter *padapter); -void r8712_survey_cmd_callback(struct _adapter *padapter, - struct cmd_obj *pcmd); -void r8712_disassoc_cmd_callback(struct _adapter *padapter, - struct cmd_obj *pcmd); -void r8712_joinbss_cmd_callback(struct _adapter *padapter, - struct cmd_obj *pcmd); -void r8712_createbss_cmd_callback(struct _adapter *padapter, - struct cmd_obj *pcmd); -void r8712_getbbrfreg_cmdrsp_callback(struct _adapter *padapter, - struct cmd_obj *pcmd); -void r8712_readtssi_cmdrsp_callback(struct _adapter *padapter, - struct cmd_obj *pcmd); -void r8712_setstaKey_cmdrsp_callback(struct _adapter *padapter, - struct cmd_obj *pcmd); -void r8712_setassocsta_cmdrsp_callback(struct _adapter *padapter, - struct cmd_obj *pcmd); -void r8712_disconnectCtrlEx_cmd(struct _adapter *adapter, u32 enableDrvCtrl, - u32 tryPktCnt, u32 tryPktInterval, u32 firstStageTO); +void r8712_survey_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd); +void r8712_disassoc_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd); +void r8712_joinbss_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd); +void r8712_createbss_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd); +void r8712_getbbrfreg_cmdrsp_callback(struct _adapter *padapter, struct cmd_obj *pcmd); +void r8712_readtssi_cmdrsp_callback(struct _adapter *padapter, struct cmd_obj *pcmd); +void r8712_setstaKey_cmdrsp_callback(struct _adapter *padapter, struct cmd_obj *pcmd); +void r8712_setassocsta_cmdrsp_callback(struct _adapter *padapter, struct cmd_obj *pcmd); +void r8712_disconnectCtrlEx_cmd(struct _adapter *adapter, u32 enableDrvCtrl, u32 tryPktCnt, + u32 tryPktInterval, u32 firstStageTO); struct _cmd_callback { u32 cmd_code; diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_linux.c b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c index 36f6904d25ab..0653aa27b1fa 100644 --- a/drivers/staging/rtl8712/rtl871x_ioctl_linux.c +++ b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c @@ -59,8 +59,7 @@ void r8712_indicate_wx_assoc_event(struct _adapter *padapter) struct mlme_priv *pmlmepriv = &padapter->mlmepriv; wrqu.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(wrqu.ap_addr.sa_data, pmlmepriv->cur_network.network.MacAddress, - ETH_ALEN); + memcpy(wrqu.ap_addr.sa_data, pmlmepriv->cur_network.network.MacAddress, ETH_ALEN); wireless_send_event(padapter->pnetdev, SIOCGIWAP, &wrqu, NULL); } @@ -111,11 +110,9 @@ static inline void handle_group_key(struct ieee_param *param, memcpy(grk[param->u.crypt.idx - 1].skey, ¶m->u.crypt.key[24], 8); padapter->securitypriv.binstallGrpkey = true; - r8712_set_key(padapter, &padapter->securitypriv, - param->u.crypt.idx); + r8712_set_key(padapter, &padapter->securitypriv, param->u.crypt.idx); if (padapter->registrypriv.power_mgnt > PS_MODE_ACTIVE) { - if (padapter->registrypriv.power_mgnt != - padapter->pwrctrlpriv.pwr_mode) + if (padapter->registrypriv.power_mgnt != padapter->pwrctrlpriv.pwr_mode) mod_timer(&padapter->mlmepriv.dhcp_timer, jiffies + msecs_to_jiffies(60000)); } @@ -148,13 +145,11 @@ static noinline_for_stack char *translate_scan_wpa(struct iw_request_info *info, memset(iwe, 0, sizeof(*iwe)); iwe->cmd = IWEVCUSTOM; iwe->u.data.length = (u16)strlen(buf); - start = iwe_stream_add_point(info, start, stop, - iwe, buf); + start = iwe_stream_add_point(info, start, stop, iwe, buf); memset(iwe, 0, sizeof(*iwe)); iwe->cmd = IWEVGENIE; iwe->u.data.length = (u16)wpa_len; - start = iwe_stream_add_point(info, start, stop, - iwe, wpa_ie); + start = iwe_stream_add_point(info, start, stop, iwe, wpa_ie); } if (rsn_len > 0) { memset(buf, 0, MAX_WPA_IE_LEN); @@ -168,13 +163,11 @@ static noinline_for_stack char *translate_scan_wpa(struct iw_request_info *info, memset(iwe, 0, sizeof(*iwe)); iwe->cmd = IWEVCUSTOM; iwe->u.data.length = strlen(buf); - start = iwe_stream_add_point(info, start, stop, - iwe, buf); + start = iwe_stream_add_point(info, start, stop, iwe, buf); memset(iwe, 0, sizeof(*iwe)); iwe->cmd = IWEVGENIE; iwe->u.data.length = rsn_len; - start = iwe_stream_add_point(info, start, stop, iwe, - rsn_ie); + start = iwe_stream_add_point(info, start, stop, iwe, rsn_ie); } return start; @@ -189,14 +182,11 @@ static noinline_for_stack char *translate_scan_wps(struct iw_request_info *info, u8 wps_ie[512]; uint wps_ielen; - if (r8712_get_wps_ie(pnetwork->network.IEs, - pnetwork->network.IELength, - wps_ie, &wps_ielen)) { + if (r8712_get_wps_ie(pnetwork->network.IEs, pnetwork->network.IELength, wps_ie, &wps_ielen)) { if (wps_ielen > 2) { iwe->cmd = IWEVGENIE; iwe->u.data.length = (u16)wps_ielen; - start = iwe_stream_add_point(info, start, stop, - iwe, wps_ie); + start = iwe_stream_add_point(info, start, stop, iwe, wps_ie); } } @@ -259,16 +249,14 @@ static char *translate_scan(struct _adapter *padapter, start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); /* Add mode */ iwe.cmd = SIOCGIWMODE; - memcpy((u8 *)&cap, r8712_get_capability_from_ie(pnetwork->network.IEs), - 2); + memcpy((u8 *)&cap, r8712_get_capability_from_ie(pnetwork->network.IEs), 2); le16_to_cpus(&cap); if (cap & (WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_ESS)) { if (cap & WLAN_CAPABILITY_ESS) iwe.u.mode = (u32)IW_MODE_MASTER; else iwe.u.mode = (u32)IW_MODE_ADHOC; - start = iwe_stream_add_event(info, start, stop, &iwe, - IW_EV_UINT_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_UINT_LEN); } /* Add frequency/channel */ iwe.cmd = SIOCGIWFREQ; @@ -276,28 +264,23 @@ static char *translate_scan(struct _adapter *padapter, /* check legal index */ u8 dsconfig = pnetwork->network.Configuration.DSConfig; - if (dsconfig >= 1 && dsconfig <= sizeof( - ieee80211_wlan_frequencies) / sizeof(long)) - iwe.u.freq.m = - (s32)(ieee80211_wlan_frequencies - [dsconfig - 1] * 100000); + if (dsconfig >= 1 && dsconfig <= sizeof(ieee80211_wlan_frequencies) / sizeof(long)) + iwe.u.freq.m = (s32)(ieee80211_wlan_frequencies[dsconfig - 1] * 100000); else iwe.u.freq.m = 0; } iwe.u.freq.e = (s16)1; iwe.u.freq.i = (u8)pnetwork->network.Configuration.DSConfig; start = iwe_stream_add_event(info, start, stop, &iwe, - IW_EV_FREQ_LEN); + IW_EV_FREQ_LEN); /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; if (cap & WLAN_CAPABILITY_PRIVACY) - iwe.u.data.flags = (u16)(IW_ENCODE_ENABLED | - IW_ENCODE_NOKEY); + iwe.u.data.flags = (u16)(IW_ENCODE_ENABLED | IW_ENCODE_NOKEY); else iwe.u.data.flags = (u16)(IW_ENCODE_DISABLED); iwe.u.data.length = (u16)0; - start = iwe_stream_add_point(info, start, stop, &iwe, - pnetwork->network.Ssid.Ssid); + start = iwe_stream_add_point(info, start, stop, &iwe, pnetwork->network.Ssid.Ssid); /*Add basic and extended rates */ current_val = start + iwe_stream_lcp_len(info); iwe.cmd = SIOCGIWRATE; @@ -307,10 +290,9 @@ static char *translate_scan(struct _adapter *padapter, i = 0; while (pnetwork->network.rates[i] != 0) { /* Bit rate given in 500 kb/s units */ - iwe.u.bitrate.value = (pnetwork->network.rates[i++] & - 0x7F) * 500000; - current_val = iwe_stream_add_value(info, start, current_val, - stop, &iwe, IW_EV_PARAM_LEN); + iwe.u.bitrate.value = (pnetwork->network.rates[i++] & 0x7F) * 500000; + current_val = iwe_stream_add_value(info, start, current_val, stop, &iwe, + IW_EV_PARAM_LEN); } /* Check if we added any event */ if ((current_val - start) > iwe_stream_lcp_len(info)) @@ -324,8 +306,7 @@ static char *translate_scan(struct _adapter *padapter, iwe.cmd = IWEVQUAL; rssi = r8712_signal_scale_mapping(pnetwork->network.Rssi); /* we only update signal_level (signal strength) that is rssi. */ - iwe.u.qual.updated = (u8)(IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_UPDATED | - IW_QUAL_NOISE_INVALID); + iwe.u.qual.updated = (u8)(IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID); iwe.u.qual.level = rssi; /* signal strength */ iwe.u.qual.qual = 0; /* signal quality */ iwe.u.qual.noise = 0; /* noise level */ @@ -490,71 +471,59 @@ static int r871x_set_wpa_ie(struct _adapter *padapter, char *pie, goto exit; } if (r8712_parse_wpa_ie(buf, ielen, &group_cipher, - &pairwise_cipher) == 0) { + &pairwise_cipher) == 0) { padapter->securitypriv.AuthAlgrthm = 2; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; } if (r8712_parse_wpa2_ie(buf, ielen, &group_cipher, - &pairwise_cipher) == 0) { + &pairwise_cipher) == 0) { padapter->securitypriv.AuthAlgrthm = 2; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK; } switch (group_cipher) { case WPA_CIPHER_NONE: - padapter->securitypriv.XGrpPrivacy = - _NO_PRIVACY_; - padapter->securitypriv.ndisencryptstatus = - Ndis802_11EncryptionDisabled; + padapter->securitypriv.XGrpPrivacy = _NO_PRIVACY_; + padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; break; case WPA_CIPHER_WEP40: padapter->securitypriv.XGrpPrivacy = _WEP40_; - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption1Enabled; + padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; break; case WPA_CIPHER_TKIP: padapter->securitypriv.XGrpPrivacy = _TKIP_; - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption2Enabled; + padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled; break; case WPA_CIPHER_CCMP: padapter->securitypriv.XGrpPrivacy = _AES_; - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption3Enabled; + padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption3Enabled; break; case WPA_CIPHER_WEP104: padapter->securitypriv.XGrpPrivacy = _WEP104_; - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption1Enabled; + padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; break; } switch (pairwise_cipher) { case WPA_CIPHER_NONE: - padapter->securitypriv.PrivacyAlgrthm = - _NO_PRIVACY_; - padapter->securitypriv.ndisencryptstatus = - Ndis802_11EncryptionDisabled; + padapter->securitypriv.PrivacyAlgrthm = _NO_PRIVACY_; + padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; break; case WPA_CIPHER_WEP40: padapter->securitypriv.PrivacyAlgrthm = _WEP40_; - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption1Enabled; + padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; break; case WPA_CIPHER_TKIP: padapter->securitypriv.PrivacyAlgrthm = _TKIP_; - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption2Enabled; + padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled; break; case WPA_CIPHER_CCMP: padapter->securitypriv.PrivacyAlgrthm = _AES_; - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption3Enabled; + padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption3Enabled; break; case WPA_CIPHER_WEP104: padapter->securitypriv.PrivacyAlgrthm = _WEP104_; - padapter->securitypriv.ndisencryptstatus = - Ndis802_11Encryption1Enabled; + padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; break; } padapter->securitypriv.wps_phase = false; @@ -574,8 +543,8 @@ static int r871x_set_wpa_ie(struct _adapter *padapter, char *pie, (buf[cnt + 1] + 2) : (MAX_WPA_IE_LEN << 2); memcpy(padapter->securitypriv.wps_ie, - &buf[cnt], - padapter->securitypriv.wps_ie_len); + &buf[cnt], + padapter->securitypriv.wps_ie_len); padapter->securitypriv.wps_phase = true; netdev_info(padapter->pnetdev, "r8712u: SET WPS_IE, wps_phase==true\n"); @@ -592,8 +561,7 @@ exit: return ret; } -static int r8711_wx_get_name(struct net_device *dev, - struct iw_request_info *info, +static int r8711_wx_get_name(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct _adapter *padapter = netdev_priv(dev); @@ -604,8 +572,7 @@ static int r8711_wx_get_name(struct net_device *dev, struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; u8 *prates; - if (check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE) == - true) { + if (check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE) == true) { /* parsing HT_CAP_IE */ p = r8712_get_ie(&pcur_bss->IEs[12], WLAN_EID_HT_CAPABILITY, &ht_ielen, pcur_bss->IELength - 12); @@ -658,9 +625,7 @@ static int r8711_wx_set_freq(struct net_device *dev, int rc = 0; /* If setting by frequency, convert to a channel */ - if ((fwrq->e == 1) && - (fwrq->m >= 241200000) && - (fwrq->m <= 248700000)) { + if ((fwrq->e == 1) && (fwrq->m >= 241200000) && (fwrq->m <= 248700000)) { int f = fwrq->m / 100000; int c = 0; @@ -685,8 +650,7 @@ static int r8711_wx_set_freq(struct net_device *dev, return rc; } -static int r8711_wx_get_freq(struct net_device *dev, - struct iw_request_info *info, +static int r8711_wx_get_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct _adapter *padapter = netdev_priv(dev); @@ -745,7 +709,7 @@ static int r8711_wx_get_mode(struct net_device *dev, struct iw_request_info *a, if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) wrqu->mode = IW_MODE_INFRA; else if (check_fwstate(pmlmepriv, - WIFI_ADHOC_MASTER_STATE | WIFI_ADHOC_STATE)) + WIFI_ADHOC_MASTER_STATE | WIFI_ADHOC_STATE)) wrqu->mode = IW_MODE_ADHOC; else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) wrqu->mode = IW_MODE_MASTER; @@ -754,9 +718,8 @@ static int r8711_wx_get_mode(struct net_device *dev, struct iw_request_info *a, return 0; } -static int r871x_wx_set_pmkid(struct net_device *dev, - struct iw_request_info *a, - union iwreq_data *wrqu, char *extra) +static int r871x_wx_set_pmkid(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) { struct _adapter *padapter = netdev_priv(dev); struct security_priv *psecuritypriv = &padapter->securitypriv; @@ -828,7 +791,7 @@ static int r871x_wx_set_pmkid(struct net_device *dev, break; case IW_PMKSA_FLUSH: memset(psecuritypriv->PMKIDList, 0, - sizeof(struct RT_PMKID_LIST) * NUM_PMKID_CACHE); + sizeof(struct RT_PMKID_LIST) * NUM_PMKID_CACHE); psecuritypriv->PMKIDIndex = 0; intReturn = true; break; @@ -850,9 +813,8 @@ static int r8711_wx_get_sens(struct net_device *dev, return 0; } -static int r8711_wx_get_range(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) +static int r8711_wx_get_range(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { struct iw_range *range = (struct iw_range *)extra; u16 val; @@ -912,9 +874,9 @@ static int r8711_wx_get_rate(struct net_device *dev, union iwreq_data *wrqu, char *extra); static int r871x_wx_set_priv(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *awrq, - char *extra) + struct iw_request_info *info, + union iwreq_data *awrq, + char *extra) { int ret = 0, len = 0; char *ext; @@ -995,12 +957,10 @@ static int r871x_wx_set_priv(struct net_device *dev, ); sprintf(ext, "OK"); } else { - netdev_info(dev, "r8712u: %s: unknown Command %s.\n", - __func__, ext); + netdev_info(dev, "r8712u: %s: unknown Command %s.\n", __func__, ext); goto FREE_EXT; } - if (copy_to_user(dwrq->pointer, ext, - min(dwrq->length, (__u16)(strlen(ext) + 1)))) + if (copy_to_user(dwrq->pointer, ext, min(dwrq->length, (__u16)(strlen(ext) + 1)))) ret = -EFAULT; FREE_EXT: @@ -1021,10 +981,8 @@ FREE_EXT: * For this operation to succeed, there is no need for the interface to be up. * */ -static int r8711_wx_set_wap(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *awrq, - char *extra) +static int r8711_wx_set_wap(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *awrq, char *extra) { int ret = -EINPROGRESS; struct _adapter *padapter = netdev_priv(dev); @@ -1072,17 +1030,15 @@ static int r8711_wx_set_wap(struct net_device *dev, return ret; } -static int r8711_wx_get_wap(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) +static int r8711_wx_get_wap(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { struct _adapter *padapter = netdev_priv(dev); struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; wrqu->ap_addr.sa_family = ARPHRD_ETHER; - if (check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE | - WIFI_AP_STATE)) + if (check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE | WIFI_AP_STATE)) ether_addr_copy(wrqu->ap_addr.sa_data, pcur_bss->MacAddress); else eth_zero_addr(wrqu->ap_addr.sa_data); @@ -1122,9 +1078,8 @@ static int r871x_wx_set_mlme(struct net_device *dev, * For this operation to succeed, the interface is brought Up beforehand. * */ -static int r8711_wx_set_scan(struct net_device *dev, - struct iw_request_info *a, - union iwreq_data *wrqu, char *extra) +static int r8711_wx_set_scan(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) { struct _adapter *padapter = netdev_priv(dev); struct mlme_priv *pmlmepriv = &padapter->mlmepriv; @@ -1150,8 +1105,7 @@ static int r8711_wx_set_scan(struct net_device *dev, unsigned long irqL; u32 len = min_t(u8, req->essid_len, IW_ESSID_MAX_SIZE); - memset((unsigned char *)&ssid, 0, - sizeof(struct ndis_802_11_ssid)); + memset((unsigned char *)&ssid, 0, sizeof(struct ndis_802_11_ssid)); memcpy(ssid.Ssid, req->essid, len); ssid.SsidLength = len; spin_lock_irqsave(&pmlmepriv->lock, irqL); @@ -1173,9 +1127,8 @@ static int r8711_wx_set_scan(struct net_device *dev, return 0; } -static int r8711_wx_get_scan(struct net_device *dev, - struct iw_request_info *a, - union iwreq_data *wrqu, char *extra) +static int r8711_wx_get_scan(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) { struct _adapter *padapter = netdev_priv(dev); struct mlme_priv *pmlmepriv = &padapter->mlmepriv; @@ -1189,8 +1142,7 @@ static int r8711_wx_get_scan(struct net_device *dev, if (padapter->driver_stopped) return -EINVAL; - while (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | - _FW_UNDER_LINKING)) { + while (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | _FW_UNDER_LINKING)) { msleep(30); cnt++; if (cnt > 100) @@ -1228,9 +1180,8 @@ static int r8711_wx_get_scan(struct net_device *dev, * For this operation to succeed, there is no need for the interface to be Up. * */ -static int r8711_wx_set_essid(struct net_device *dev, - struct iw_request_info *a, - union iwreq_data *wrqu, char *extra) +static int r8711_wx_set_essid(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) { struct _adapter *padapter = netdev_priv(dev); struct mlme_priv *pmlmepriv = &padapter->mlmepriv; @@ -1268,8 +1219,7 @@ static int r8711_wx_set_essid(struct net_device *dev, if ((!memcmp(dst_ssid, src_ssid, ndis_ssid.SsidLength)) && (pnetwork->network.Ssid.SsidLength == ndis_ssid.SsidLength)) { - if (check_fwstate(pmlmepriv, - WIFI_ADHOC_STATE)) { + if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) { if (pnetwork->network. InfrastructureMode != @@ -1291,9 +1241,8 @@ static int r8711_wx_set_essid(struct net_device *dev, return -EINPROGRESS; } -static int r8711_wx_get_essid(struct net_device *dev, - struct iw_request_info *a, - union iwreq_data *wrqu, char *extra) +static int r8711_wx_get_essid(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) { struct _adapter *padapter = netdev_priv(dev); struct mlme_priv *pmlmepriv = &padapter->mlmepriv; @@ -1311,9 +1260,8 @@ static int r8711_wx_get_essid(struct net_device *dev, return ret; } -static int r8711_wx_set_rate(struct net_device *dev, - struct iw_request_info *a, - union iwreq_data *wrqu, char *extra) +static int r8711_wx_set_rate(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) { struct _adapter *padapter = netdev_priv(dev); u32 target_rate = wrqu->bitrate.value; @@ -1382,8 +1330,7 @@ set_rate: return r8712_setdatarate_cmd(padapter, datarates); } -static int r8711_wx_get_rate(struct net_device *dev, - struct iw_request_info *info, +static int r8711_wx_get_rate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct _adapter *padapter = netdev_priv(dev); @@ -1437,9 +1384,8 @@ static int r8711_wx_get_rate(struct net_device *dev, return 0; } -static int r8711_wx_get_rts(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) +static int r8711_wx_get_rts(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { struct _adapter *padapter = netdev_priv(dev); @@ -1448,9 +1394,8 @@ static int r8711_wx_get_rts(struct net_device *dev, return 0; } -static int r8711_wx_set_frag(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) +static int r8711_wx_set_frag(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { struct _adapter *padapter = netdev_priv(dev); diff --git a/drivers/staging/rtl8712/rtl871x_mp_phy_regdef.h b/drivers/staging/rtl8712/rtl871x_mp_phy_regdef.h index a08c5d2f59e3..bb9f83d58225 100644 --- a/drivers/staging/rtl8712/rtl871x_mp_phy_regdef.h +++ b/drivers/staging/rtl8712/rtl871x_mp_phy_regdef.h @@ -126,7 +126,7 @@ #define rFPGA0_AnalogParameter3 0x888 /* Useless now */ #define rFPGA0_AnalogParameter4 0x88c -#define rFPGA0_XA_LSSIReadBack 0x8a0 /* Tranceiver LSSI Readback */ +#define rFPGA0_XA_LSSIReadBack 0x8a0 /* Transceiver LSSI Readback */ #define rFPGA0_XB_LSSIReadBack 0x8a4 #define rFPGA0_XC_LSSIReadBack 0x8a8 #define rFPGA0_XD_LSSIReadBack 0x8ac diff --git a/drivers/staging/vc04_services/interface/TODO b/drivers/staging/vc04_services/interface/TODO index 6d9d4a800aa7..05eb5140d096 100644 --- a/drivers/staging/vc04_services/interface/TODO +++ b/drivers/staging/vc04_services/interface/TODO @@ -23,11 +23,6 @@ should properly handle a module unload. This also includes that all resources must be freed (kthreads, debugfs entries, ...) and global variables avoided. -* Cleanup logging mechanism - -The driver should probably be using the standard kernel logging mechanisms -such as dev_info, dev_dbg, and friends. - * Documentation A short top-down description of this driver's architecture (function of diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 9fb8f657cc78..1579bd4e5263 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -255,8 +255,7 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, pagelist = dma_alloc_coherent(instance->state->dev, pagelist_size, &dma_addr, GFP_KERNEL); - vchiq_log_trace(instance->state->dev, VCHIQ_ARM, - "%s - %pK", __func__, pagelist); + dev_dbg(instance->state->dev, "arm: %pK\n", pagelist); if (!pagelist) return NULL; @@ -311,9 +310,8 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, type == PAGELIST_READ, pages); if (actual_pages != num_pages) { - vchiq_log_debug(instance->state->dev, VCHIQ_ARM, - "%s - only %d/%d pages locked", - __func__, actual_pages, num_pages); + dev_dbg(instance->state->dev, "arm: Only %d/%d pages locked\n", + actual_pages, num_pages); /* This is probably due to the process being killed */ if (actual_pages > 0) @@ -407,8 +405,7 @@ free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagel struct page **pages = pagelistinfo->pages; unsigned int num_pages = pagelistinfo->num_pages; - vchiq_log_trace(instance->state->dev, VCHIQ_ARM, - "%s - %pK, %d", __func__, pagelistinfo->pagelist, actual); + dev_dbg(instance->state->dev, "arm: %pK, %d\n", pagelistinfo->pagelist, actual); /* * NOTE: dma_unmap_sg must be called before the @@ -556,8 +553,8 @@ static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state return -ENXIO; } - vchiq_log_debug(&pdev->dev, VCHIQ_ARM, "vchiq_init - done (slots %pK, phys %pad)", - vchiq_slot_zero, &slot_phys); + dev_dbg(&pdev->dev, "arm: vchiq_init - done (slots %pK, phys %pad)\n", + vchiq_slot_zero, &slot_phys); vchiq_call_connected_callbacks(); @@ -659,13 +656,9 @@ vchiq_complete_bulk(struct vchiq_instance *instance, struct vchiq_bulk *bulk) bulk->actual); } -int vchiq_dump_platform_state(void *dump_context) +void vchiq_dump_platform_state(struct seq_file *f) { - char buf[80]; - int len; - - len = snprintf(buf, sizeof(buf), " Platform: 2835 (VC master)"); - return vchiq_dump(dump_context, buf, len + 1); + seq_puts(f, " Platform: 2835 (VC master)\n"); } #define VCHIQ_INIT_RETRIES 10 @@ -687,19 +680,17 @@ int vchiq_initialise(struct vchiq_instance **instance_out) usleep_range(500, 600); } if (i == VCHIQ_INIT_RETRIES) { - vchiq_log_error(state->dev, VCHIQ_CORE, "%s: videocore not initialized\n", - __func__); + dev_err(state->dev, "core: %s: Videocore not initialized\n", __func__); ret = -ENOTCONN; goto failed; } else if (i > 0) { - vchiq_log_warning(state->dev, VCHIQ_CORE, - "%s: videocore initialized after %d retries\n", __func__, i); + dev_warn(state->dev, "core: %s: videocore initialized after %d retries\n", + __func__, i); } instance = kzalloc(sizeof(*instance), GFP_KERNEL); if (!instance) { - vchiq_log_error(state->dev, VCHIQ_CORE, - "%s: error allocating vchiq instance\n", __func__); + dev_err(state->dev, "core: %s: Cannot allocate vchiq instance\n", __func__); ret = -ENOMEM; goto failed; } @@ -714,8 +705,7 @@ int vchiq_initialise(struct vchiq_instance **instance_out) ret = 0; failed: - vchiq_log_trace(state->dev, VCHIQ_CORE, - "%s(%p): returning %d", __func__, instance, ret); + dev_dbg(state->dev, "core: (%p): returning %d\n", instance, ret); return ret; } @@ -728,9 +718,9 @@ void free_bulk_waiter(struct vchiq_instance *instance) list_for_each_entry_safe(waiter, next, &instance->bulk_waiter_list, list) { list_del(&waiter->list); - vchiq_log_debug(instance->state->dev, VCHIQ_ARM, - "bulk_waiter - cleaned up %pK for pid %d", - waiter, waiter->pid); + dev_dbg(instance->state->dev, + "arm: bulk_waiter - cleaned up %pK for pid %d\n", + waiter, waiter->pid); kfree(waiter); } } @@ -748,8 +738,7 @@ int vchiq_shutdown(struct vchiq_instance *instance) mutex_unlock(&state->mutex); - vchiq_log_trace(state->dev, VCHIQ_CORE, - "%s(%p): returning %d", __func__, instance, status); + dev_dbg(state->dev, "core: (%p): returning %d\n", instance, status); free_bulk_waiter(instance); kfree(instance); @@ -769,8 +758,8 @@ int vchiq_connect(struct vchiq_instance *instance) struct vchiq_state *state = instance->state; if (mutex_lock_killable(&state->mutex)) { - vchiq_log_trace(state->dev, VCHIQ_CORE, - "%s: call to mutex_lock failed", __func__); + dev_dbg(state->dev, + "core: call to mutex_lock failed\n"); status = -EAGAIN; goto failed; } @@ -782,8 +771,7 @@ int vchiq_connect(struct vchiq_instance *instance) mutex_unlock(&state->mutex); failed: - vchiq_log_trace(state->dev, VCHIQ_CORE, - "%s(%p): returning %d", __func__, instance, status); + dev_dbg(state->dev, "core: (%p): returning %d\n", instance, status); return status; } @@ -814,8 +802,7 @@ vchiq_add_service(struct vchiq_instance *instance, status = -EINVAL; } - vchiq_log_trace(state->dev, VCHIQ_CORE, - "%s(%p): returning %d", __func__, instance, status); + dev_dbg(state->dev, "core: (%p): returning %d\n", instance, status); return status; } @@ -846,8 +833,7 @@ vchiq_open_service(struct vchiq_instance *instance, } failed: - vchiq_log_trace(state->dev, VCHIQ_CORE, - "%s(%p): returning %d", __func__, instance, status); + dev_dbg(state->dev, "core: (%p): returning %d\n", instance, status); return status; } @@ -971,8 +957,7 @@ vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handl } else { waiter = kzalloc(sizeof(*waiter), GFP_KERNEL); if (!waiter) { - vchiq_log_error(service->state->dev, VCHIQ_CORE, - "%s - out of memory", __func__); + dev_err(service->state->dev, "core: %s: - Out of memory\n", __func__); return -ENOMEM; } } @@ -995,9 +980,8 @@ vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handl mutex_lock(&instance->bulk_waiter_list_mutex); list_add(&waiter->list, &instance->bulk_waiter_list); mutex_unlock(&instance->bulk_waiter_list_mutex); - vchiq_log_debug(instance->state->dev, VCHIQ_ARM, - "saved bulk_waiter %pK for pid %d", waiter, - current->pid); + dev_dbg(instance->state->dev, "arm: saved bulk_waiter %pK for pid %d\n", + waiter, current->pid); } return status; @@ -1017,16 +1001,13 @@ add_completion(struct vchiq_instance *instance, enum vchiq_reason reason, while ((insert - instance->completion_remove) >= MAX_COMPLETIONS) { /* Out of space - wait for the client */ DEBUG_TRACE(SERVICE_CALLBACK_LINE); - vchiq_log_trace(instance->state->dev, VCHIQ_CORE, - "%s - completion queue full", __func__); + dev_dbg(instance->state->dev, "core: completion queue full\n"); DEBUG_COUNT(COMPLETION_QUEUE_FULL_COUNT); if (wait_for_completion_interruptible(&instance->remove_event)) { - vchiq_log_debug(instance->state->dev, VCHIQ_ARM, - "service_callback interrupted"); + dev_dbg(instance->state->dev, "arm: service_callback interrupted\n"); return -EAGAIN; } else if (instance->closing) { - vchiq_log_debug(instance->state->dev, VCHIQ_ARM, - "service_callback closing"); + dev_dbg(instance->state->dev, "arm: service_callback closing\n"); return 0; } DEBUG_TRACE(SERVICE_CALLBACK_LINE); @@ -1106,11 +1087,10 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, vchiq_service_get(service); rcu_read_unlock(); - vchiq_log_trace(service->state->dev, VCHIQ_ARM, - "%s - service %lx(%d,%p), reason %d, header %lx, instance %lx, bulk_userdata %lx", - __func__, (unsigned long)user_service, service->localport, - user_service->userdata, reason, (unsigned long)header, - (unsigned long)instance, (unsigned long)bulk_userdata); + dev_dbg(service->state->dev, + "arm: service %p(%d,%p), reason %d, header %p, instance %p, bulk_userdata %p\n", + user_service, service->localport, user_service->userdata, + reason, header, instance, bulk_userdata); if (header && user_service->is_vchi) { spin_lock(&msg_queue_spinlock); @@ -1119,8 +1099,7 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, spin_unlock(&msg_queue_spinlock); DEBUG_TRACE(SERVICE_CALLBACK_LINE); DEBUG_COUNT(MSG_QUEUE_FULL_COUNT); - vchiq_log_trace(service->state->dev, VCHIQ_ARM, - "%s - msg queue full", __func__); + dev_dbg(service->state->dev, "arm: msg queue full\n"); /* * If there is no MESSAGE_AVAILABLE in the completion * queue, add one @@ -1129,8 +1108,8 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, instance->completion_remove) < 0) { int status; - vchiq_log_debug(instance->state->dev, VCHIQ_ARM, - "Inserting extra MESSAGE_AVAILABLE"); + dev_dbg(instance->state->dev, + "arm: Inserting extra MESSAGE_AVAILABLE\n"); DEBUG_TRACE(SERVICE_CALLBACK_LINE); status = add_completion(instance, reason, NULL, user_service, bulk_userdata); @@ -1143,14 +1122,12 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, DEBUG_TRACE(SERVICE_CALLBACK_LINE); if (wait_for_completion_interruptible(&user_service->remove_event)) { - vchiq_log_debug(instance->state->dev, VCHIQ_ARM, - "%s interrupted", __func__); + dev_dbg(instance->state->dev, "arm: interrupted\n"); DEBUG_TRACE(SERVICE_CALLBACK_LINE); vchiq_service_put(service); return -EAGAIN; } else if (instance->closing) { - vchiq_log_debug(instance->state->dev, VCHIQ_ARM, - "%s closing", __func__); + dev_dbg(instance->state->dev, "arm: closing\n"); DEBUG_TRACE(SERVICE_CALLBACK_LINE); vchiq_service_put(service); return -EINVAL; @@ -1190,56 +1167,13 @@ service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, bulk_userdata); } -int vchiq_dump(void *dump_context, const char *str, int len) -{ - struct dump_context *context = (struct dump_context *)dump_context; - int copy_bytes; - - if (context->actual >= context->space) - return 0; - - if (context->offset > 0) { - int skip_bytes = min_t(int, len, context->offset); - - str += skip_bytes; - len -= skip_bytes; - context->offset -= skip_bytes; - if (context->offset > 0) - return 0; - } - copy_bytes = min_t(int, len, context->space - context->actual); - if (copy_bytes == 0) - return 0; - if (copy_to_user(context->buf + context->actual, str, - copy_bytes)) - return -EFAULT; - context->actual += copy_bytes; - len -= copy_bytes; - - /* - * If the terminating NUL is included in the length, then it - * marks the end of a line and should be replaced with a - * carriage return. - */ - if ((len == 0) && (str[copy_bytes - 1] == '\0')) { - char cr = '\n'; - - if (copy_to_user(context->buf + context->actual - 1, - &cr, 1)) - return -EFAULT; - } - return 0; -} - -int vchiq_dump_platform_instances(void *dump_context) +void vchiq_dump_platform_instances(struct seq_file *f) { struct vchiq_state *state = vchiq_get_state(); - char buf[80]; - int len; int i; if (!state) - return -ENOTCONN; + return; /* * There is no list of instances, so instead scan all services, @@ -1264,7 +1198,6 @@ int vchiq_dump_platform_instances(void *dump_context) for (i = 0; i < state->unused_service; i++) { struct vchiq_service *service; struct vchiq_instance *instance; - int err; rcu_read_lock(); service = rcu_dereference(state->services[i]); @@ -1280,43 +1213,35 @@ int vchiq_dump_platform_instances(void *dump_context) } rcu_read_unlock(); - len = snprintf(buf, sizeof(buf), - "Instance %pK: pid %d,%s completions %d/%d", - instance, instance->pid, - instance->connected ? " connected, " : - "", - instance->completion_insert - - instance->completion_remove, - MAX_COMPLETIONS); - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; + seq_printf(f, "Instance %pK: pid %d,%s completions %d/%d\n", + instance, instance->pid, + instance->connected ? " connected, " : + "", + instance->completion_insert - + instance->completion_remove, + MAX_COMPLETIONS); instance->mark = 1; } - return 0; } -int vchiq_dump_platform_service_state(void *dump_context, - struct vchiq_service *service) +void vchiq_dump_platform_service_state(struct seq_file *f, + struct vchiq_service *service) { struct user_service *user_service = (struct user_service *)service->base.userdata; - char buf[80]; - int len; - len = scnprintf(buf, sizeof(buf), " instance %pK", service->instance); + seq_printf(f, " instance %pK", service->instance); if ((service->base.callback == service_callback) && user_service->is_vchi) { - len += scnprintf(buf + len, sizeof(buf) - len, ", %d/%d messages", - user_service->msg_insert - user_service->msg_remove, - MSG_QUEUE_SIZE); + seq_printf(f, ", %d/%d messages", + user_service->msg_insert - user_service->msg_remove, + MSG_QUEUE_SIZE); if (user_service->dequeue_pending) - len += scnprintf(buf + len, sizeof(buf) - len, - " (dequeue pending)"); + seq_puts(f, " (dequeue pending)"); } - return vchiq_dump(dump_context, buf, len + 1); + seq_puts(f, "\n"); } struct vchiq_state * @@ -1346,8 +1271,8 @@ vchiq_keepalive_vchiq_callback(struct vchiq_instance *instance, struct vchiq_header *header, unsigned int service_user, void *bulk_user) { - vchiq_log_error(instance->state->dev, VCHIQ_SUSPEND, - "%s callback reason %d", __func__, reason); + dev_err(instance->state->dev, "suspend: %s: callback reason %d\n", + __func__, reason); return 0; } @@ -1371,22 +1296,20 @@ vchiq_keepalive_thread_func(void *v) ret = vchiq_initialise(&instance); if (ret) { - vchiq_log_error(state->dev, VCHIQ_SUSPEND, - "%s vchiq_initialise failed %d", __func__, ret); + dev_err(state->dev, "suspend: %s: vchiq_initialise failed %d\n", __func__, ret); goto exit; } status = vchiq_connect(instance); if (status) { - vchiq_log_error(state->dev, VCHIQ_SUSPEND, - "%s vchiq_connect failed %d", __func__, status); + dev_err(state->dev, "suspend: %s: vchiq_connect failed %d\n", __func__, status); goto shutdown; } status = vchiq_add_service(instance, ¶ms, &ka_handle); if (status) { - vchiq_log_error(state->dev, VCHIQ_SUSPEND, - "%s vchiq_open_service failed %d", __func__, status); + dev_err(state->dev, "suspend: %s: vchiq_open_service failed %d\n", + __func__, status); goto shutdown; } @@ -1394,8 +1317,7 @@ vchiq_keepalive_thread_func(void *v) long rc = 0, uc = 0; if (wait_for_completion_interruptible(&arm_state->ka_evt)) { - vchiq_log_error(state->dev, VCHIQ_SUSPEND, - "%s interrupted", __func__); + dev_err(state->dev, "suspend: %s: interrupted\n", __func__); flush_signals(current); continue; } @@ -1415,16 +1337,15 @@ vchiq_keepalive_thread_func(void *v) atomic_inc(&arm_state->ka_use_ack_count); status = vchiq_use_service(instance, ka_handle); if (status) { - vchiq_log_error(state->dev, VCHIQ_SUSPEND, - "%s vchiq_use_service error %d", __func__, status); + dev_err(state->dev, "suspend: %s: vchiq_use_service error %d\n", + __func__, status); } } while (rc--) { status = vchiq_release_service(instance, ka_handle); if (status) { - vchiq_log_error(state->dev, VCHIQ_SUSPEND, - "%s vchiq_release_service error %d", __func__, - status); + dev_err(state->dev, "suspend: %s: vchiq_release_service error %d\n", + __func__, status); } } } @@ -1459,7 +1380,7 @@ vchiq_use_internal(struct vchiq_state *state, struct vchiq_service *service, service->client_id); entity_uc = &service->service_use_count; } else { - vchiq_log_error(state->dev, VCHIQ_SUSPEND, "%s null service ptr", __func__); + dev_err(state->dev, "suspend: %s: null service ptr\n", __func__); ret = -EINVAL; goto out; } @@ -1468,8 +1389,8 @@ vchiq_use_internal(struct vchiq_state *state, struct vchiq_service *service, local_uc = ++arm_state->videocore_use_count; ++(*entity_uc); - vchiq_log_trace(state->dev, VCHIQ_SUSPEND, "%s %s count %d, state count %d", - __func__, entity, *entity_uc, local_uc); + dev_dbg(state->dev, "suspend: %s count %d, state count %d\n", + entity, *entity_uc, local_uc); write_unlock_bh(&arm_state->susp_res_lock); @@ -1488,7 +1409,7 @@ vchiq_use_internal(struct vchiq_state *state, struct vchiq_service *service, } out: - vchiq_log_trace(state->dev, VCHIQ_SUSPEND, "%s exit %d", __func__, ret); + dev_dbg(state->dev, "suspend: exit %d\n", ret); return ret; } @@ -1526,14 +1447,14 @@ vchiq_release_internal(struct vchiq_state *state, struct vchiq_service *service) --arm_state->videocore_use_count; --(*entity_uc); - vchiq_log_trace(state->dev, VCHIQ_SUSPEND, "%s %s count %d, state count %d", - __func__, entity, *entity_uc, arm_state->videocore_use_count); + dev_dbg(state->dev, "suspend: %s count %d, state count %d\n", + entity, *entity_uc, arm_state->videocore_use_count); unlock: write_unlock_bh(&arm_state->susp_res_lock); out: - vchiq_log_trace(state->dev, VCHIQ_SUSPEND, "%s exit %d", __func__, ret); + dev_dbg(state->dev, "suspend: exit %d\n", ret); return ret; } @@ -1707,20 +1628,19 @@ vchiq_dump_service_use_state(struct vchiq_state *state) read_unlock_bh(&arm_state->susp_res_lock); if (only_nonzero) - vchiq_log_warning(state->dev, VCHIQ_SUSPEND, - "Too many active services (%d). Only dumping up to first %d services with non-zero use-count", - active_services, found); + dev_warn(state->dev, + "suspend: Too many active services (%d). Only dumping up to first %d services with non-zero use-count\n", + active_services, found); for (i = 0; i < found; i++) { - vchiq_log_warning(state->dev, VCHIQ_SUSPEND, - "%p4cc:%d service count %d %s", - &service_data[i].fourcc, - service_data[i].clientid, service_data[i].use_count, - service_data[i].use_count ? nz : ""); + dev_warn(state->dev, + "suspend: %p4cc:%d service count %d %s\n", + &service_data[i].fourcc, + service_data[i].clientid, service_data[i].use_count, + service_data[i].use_count ? nz : ""); } - vchiq_log_warning(state->dev, VCHIQ_SUSPEND, "VCHIQ use count %d", peer_count); - vchiq_log_warning(state->dev, VCHIQ_SUSPEND, "Overall vchiq instance use count %d", - vc_use_count); + dev_warn(state->dev, "suspend: VCHIQ use count %d\n", peer_count); + dev_warn(state->dev, "suspend: Overall vchiq instance use count %d\n", vc_use_count); kfree(service_data); } @@ -1742,10 +1662,10 @@ vchiq_check_service(struct vchiq_service *service) read_unlock_bh(&arm_state->susp_res_lock); if (ret) { - vchiq_log_error(service->state->dev, VCHIQ_SUSPEND, - "%s ERROR - %p4cc:%d service count %d, state count %d", __func__, - &service->base.fourcc, service->client_id, - service->service_use_count, arm_state->videocore_use_count); + dev_err(service->state->dev, + "suspend: %s: %p4cc:%d service count %d, state count %d\n", + __func__, &service->base.fourcc, service->client_id, + service->service_use_count, arm_state->videocore_use_count); vchiq_dump_service_use_state(service->state); } out: @@ -1759,8 +1679,8 @@ void vchiq_platform_conn_state_changed(struct vchiq_state *state, struct vchiq_arm_state *arm_state = vchiq_platform_get_arm_state(state); char threadname[16]; - vchiq_log_debug(state->dev, VCHIQ_SUSPEND, "%d: %s->%s", state->id, - get_conn_state_name(oldstate), get_conn_state_name(newstate)); + dev_dbg(state->dev, "suspend: %d: %s->%s\n", + state->id, get_conn_state_name(oldstate), get_conn_state_name(newstate)); if (state->conn_state != VCHIQ_CONNSTATE_CONNECTED) return; @@ -1778,9 +1698,8 @@ void vchiq_platform_conn_state_changed(struct vchiq_state *state, (void *)state, threadname); if (IS_ERR(arm_state->ka_thread)) { - vchiq_log_error(state->dev, VCHIQ_SUSPEND, - "vchiq: FATAL: couldn't create thread %s", - threadname); + dev_err(state->dev, "suspend: Couldn't create thread %s\n", + threadname); } else { wake_up_process(arm_state->ka_thread); } @@ -1825,9 +1744,8 @@ static int vchiq_probe(struct platform_device *pdev) vchiq_debugfs_init(); - vchiq_log_debug(&pdev->dev, VCHIQ_ARM, - "vchiq: platform initialised - version %d (min %d)", - VCHIQ_VERSION, VCHIQ_VERSION_MIN); + dev_dbg(&pdev->dev, "arm: platform initialised - version %d (min %d)\n", + VCHIQ_VERSION, VCHIQ_VERSION_MIN); /* * Simply exit on error since the function handles cleanup in @@ -1835,8 +1753,7 @@ static int vchiq_probe(struct platform_device *pdev) */ err = vchiq_register_chrdev(&pdev->dev); if (err) { - vchiq_log_warning(&pdev->dev, VCHIQ_ARM, - "Failed to initialize vchiq cdev"); + dev_warn(&pdev->dev, "arm: Failed to initialize vchiq cdev\n"); goto error_exit; } @@ -1846,7 +1763,7 @@ static int vchiq_probe(struct platform_device *pdev) return 0; failed_platform_init: - vchiq_log_warning(&pdev->dev, VCHIQ_ARM, "could not initialize vchiq platform"); + dev_warn(&pdev->dev, "arm: Could not initialize vchiq platform\n"); error_exit: return err; } diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 7cdc3d70bd2c..7844ef765a00 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -69,13 +69,6 @@ struct vchiq_instance { struct vchiq_debugfs_node debugfs_node; }; -struct dump_context { - char __user *buf; - size_t actual; - size_t space; - loff_t offset; -}; - extern spinlock_t msg_queue_spinlock; extern struct vchiq_state g_state; diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c index b3928bd8c9c6..3cad13f09e37 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.c @@ -27,7 +27,7 @@ static void connected_init(void) * be made immediately, otherwise it will be deferred until * vchiq_call_connected_callbacks is called. */ -void vchiq_add_connected_callback(void (*callback)(void)) +void vchiq_add_connected_callback(struct vchiq_device *device, void (*callback)(void)) { connected_init(); @@ -39,9 +39,9 @@ void vchiq_add_connected_callback(void (*callback)(void)) callback(); } else { if (g_num_deferred_callbacks >= MAX_CALLBACKS) { - vchiq_log_error(NULL, VCHIQ_CORE, - "There already %d callback registered - please increase MAX_CALLBACKS", - g_num_deferred_callbacks); + dev_err(&device->dev, + "core: There already %d callback registered - please increase MAX_CALLBACKS\n", + g_num_deferred_callbacks); } else { g_deferred_callback[g_num_deferred_callbacks] = callback; diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.h index 4caf5e30099d..e4ed56446f8a 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_connected.h @@ -1,10 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright (c) 2010-2012 Broadcom. All rights reserved. */ +#include "vchiq_bus.h" + #ifndef VCHIQ_CONNECTED_H #define VCHIQ_CONNECTED_H -void vchiq_add_connected_callback(void (*callback)(void)); +void vchiq_add_connected_callback(struct vchiq_device *device, void (*callback)(void)); void vchiq_call_connected_callbacks(void); #endif /* VCHIQ_CONNECTED_H */ diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index 39b857da2d42..76c27778154a 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -217,10 +217,10 @@ static const char *msg_type_str(unsigned int msg_type) static inline void set_service_state(struct vchiq_service *service, int newstate) { - vchiq_log_debug(service->state->dev, VCHIQ_CORE, "%d: srv:%d %s->%s", - service->state->id, service->localport, - srvstate_names[service->srvstate], - srvstate_names[newstate]); + dev_dbg(service->state->dev, "core: %d: srv:%d %s->%s\n", + service->state->id, service->localport, + srvstate_names[service->srvstate], + srvstate_names[newstate]); service->srvstate = newstate; } @@ -245,8 +245,7 @@ find_service_by_handle(struct vchiq_instance *instance, unsigned int handle) return service; } rcu_read_unlock(); - vchiq_log_debug(service->state->dev, VCHIQ_CORE, - "Invalid service handle 0x%x", handle); + dev_dbg(instance->state->dev, "core: Invalid service handle 0x%x\n", handle); return NULL; } @@ -266,8 +265,7 @@ find_service_by_port(struct vchiq_state *state, unsigned int localport) } rcu_read_unlock(); } - vchiq_log_debug(state->dev, VCHIQ_CORE, - "Invalid port %u", localport); + dev_dbg(state->dev, "core: Invalid port %u\n", localport); return NULL; } @@ -287,8 +285,7 @@ find_service_for_instance(struct vchiq_instance *instance, unsigned int handle) return service; } rcu_read_unlock(); - vchiq_log_debug(service->state->dev, VCHIQ_CORE, - "Invalid service handle 0x%x", handle); + dev_dbg(instance->state->dev, "core: Invalid service handle 0x%x\n", handle); return NULL; } @@ -310,8 +307,7 @@ find_closed_service_for_instance(struct vchiq_instance *instance, unsigned int h return service; } rcu_read_unlock(); - vchiq_log_debug(service->state->dev, VCHIQ_CORE, - "Invalid service handle 0x%x", handle); + dev_dbg(instance->state->dev, "core: Invalid service handle 0x%x\n", handle); return service; } @@ -459,15 +455,15 @@ make_service_callback(struct vchiq_service *service, enum vchiq_reason reason, { int status; - vchiq_log_trace(service->state->dev, VCHIQ_CORE, "%d: callback:%d (%s, %pK, %pK)", - service->state->id, service->localport, reason_names[reason], - header, bulk_userdata); + dev_dbg(service->state->dev, "core: %d: callback:%d (%s, %pK, %pK)\n", + service->state->id, service->localport, reason_names[reason], + header, bulk_userdata); status = service->base.callback(service->instance, reason, header, service->handle, bulk_userdata); if (status && (status != -EAGAIN)) { - vchiq_log_warning(service->state->dev, VCHIQ_CORE, - "%d: ignoring ERROR from callback to service %x", - service->state->id, service->handle); + dev_warn(service->state->dev, + "core: %d: ignoring ERROR from callback to service %x\n", + service->state->id, service->handle); status = 0; } @@ -482,8 +478,8 @@ vchiq_set_conn_state(struct vchiq_state *state, enum vchiq_connstate newstate) { enum vchiq_connstate oldstate = state->conn_state; - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: %s->%s", state->id, conn_state_names[oldstate], - conn_state_names[newstate]); + dev_dbg(state->dev, "core: %d: %s->%s\n", + state->id, conn_state_names[oldstate], conn_state_names[newstate]); state->conn_state = newstate; vchiq_platform_conn_state_changed(state, oldstate, newstate); } @@ -741,10 +737,10 @@ process_free_data_message(struct vchiq_state *state, u32 *service_found, */ complete("a->quota_event); } else if (count == 0) { - vchiq_log_error(state->dev, VCHIQ_CORE, - "service %d message_use_count=%d (header %pK, msgid %x, header->msgid %x, header->size %x)", - port, quota->message_use_count, header, msgid, header->msgid, - header->size); + dev_err(state->dev, + "core: service %d message_use_count=%d (header %pK, msgid %x, header->msgid %x, header->size %x)\n", + port, quota->message_use_count, header, msgid, + header->msgid, header->size); WARN(1, "invalid message use count\n"); } if (!BITSET_IS_SET(service_found, port)) { @@ -763,12 +759,12 @@ process_free_data_message(struct vchiq_state *state, u32 *service_found, * it has dropped below its quota */ complete("a->quota_event); - vchiq_log_trace(state->dev, VCHIQ_CORE, "%d: pfq:%d %x@%pK - slot_use->%d", - state->id, port, header->size, header, count - 1); + dev_dbg(state->dev, "core: %d: pfq:%d %x@%pK - slot_use->%d\n", + state->id, port, header->size, header, count - 1); } else { - vchiq_log_error(state->dev, VCHIQ_CORE, - "service %d slot_use_count=%d (header %pK, msgid %x, header->msgid %x, header->size %x)", - port, count, header, msgid, header->msgid, header->size); + dev_err(state->dev, + "core: service %d slot_use_count=%d (header %pK, msgid %x, header->msgid %x, header->size %x)\n", + port, count, header, msgid, header->msgid, header->size); WARN(1, "bad slot use count\n"); } } @@ -809,9 +805,9 @@ process_free_queue(struct vchiq_state *state, u32 *service_found, */ rmb(); - vchiq_log_trace(state->dev, VCHIQ_CORE, "%d: pfq %d=%pK %x %x", - state->id, slot_index, data, local->slot_queue_recycle, - slot_queue_available); + dev_dbg(state->dev, "core: %d: pfq %d=%pK %x %x\n", + state->id, slot_index, data, local->slot_queue_recycle, + slot_queue_available); /* Initialise the bitmask for services which have used this slot */ memset(service_found, 0, length); @@ -831,9 +827,9 @@ process_free_queue(struct vchiq_state *state, u32 *service_found, pos += calc_stride(header->size); if (pos > VCHIQ_SLOT_SIZE) { - vchiq_log_error(state->dev, VCHIQ_CORE, - "pfq - pos %x: header %pK, msgid %x, header->msgid %x, header->size %x", - pos, header, msgid, header->msgid, header->size); + dev_err(state->dev, + "core: pfq - pos %x: header %pK, msgid %x, header->msgid %x, header->size %x\n", + pos, header, msgid, header->msgid, header->size); WARN(1, "invalid slot position\n"); } } @@ -980,10 +976,10 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, ((tx_end_index != quota->previous_tx_index) && (quota->slot_use_count == quota->slot_quota))) { spin_unlock("a_spinlock); - vchiq_log_trace(state->dev, VCHIQ_CORE, - "%d: qm:%d %s,%zx - quota stall (msg %d, slot %d)", - state->id, service->localport, msg_type_str(type), size, - quota->message_use_count, quota->slot_use_count); + dev_dbg(state->dev, + "core: %d: qm:%d %s,%zx - quota stall (msg %d, slot %d)\n", + state->id, service->localport, msg_type_str(type), size, + quota->message_use_count, quota->slot_use_count); VCHIQ_SERVICE_STATS_INC(service, quota_stalls); mutex_unlock(&state->slot_mutex); if (wait_for_completion_interruptible("a->quota_event)) @@ -1023,9 +1019,9 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, int tx_end_index; int slot_use_count; - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: qm %s@%pK,%zx (%d->%d)", state->id, - msg_type_str(VCHIQ_MSG_TYPE(msgid)), header, size, - VCHIQ_MSG_SRCPORT(msgid), VCHIQ_MSG_DSTPORT(msgid)); + dev_dbg(state->dev, "core: %d: qm %s@%pK,%zx (%d->%d)\n", + state->id, msg_type_str(VCHIQ_MSG_TYPE(msgid)), header, size, + VCHIQ_MSG_SRCPORT(msgid), VCHIQ_MSG_DSTPORT(msgid)); WARN_ON(flags & (QMFLAGS_NO_MUTEX_LOCK | QMFLAGS_NO_MUTEX_UNLOCK)); @@ -1073,17 +1069,16 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, spin_unlock("a_spinlock); if (slot_use_count) - vchiq_log_trace(state->dev, VCHIQ_CORE, - "%d: qm:%d %s,%zx - slot_use->%d (hdr %p)", state->id, - service->localport, msg_type_str(VCHIQ_MSG_TYPE(msgid)), - size, slot_use_count, header); + dev_dbg(state->dev, "core: %d: qm:%d %s,%zx - slot_use->%d (hdr %p)\n", + state->id, service->localport, msg_type_str(VCHIQ_MSG_TYPE(msgid)), + size, slot_use_count, header); VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); } else { - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: qm %s@%pK,%zx (%d->%d)", state->id, - msg_type_str(VCHIQ_MSG_TYPE(msgid)), header, size, - VCHIQ_MSG_SRCPORT(msgid), VCHIQ_MSG_DSTPORT(msgid)); + dev_dbg(state->dev, "core: %d: qm %s@%pK,%zx (%d->%d)\n", + state->id, msg_type_str(VCHIQ_MSG_TYPE(msgid)), header, size, + VCHIQ_MSG_SRCPORT(msgid), VCHIQ_MSG_DSTPORT(msgid)); if (size != 0) { /* * It is assumed for now that this code path @@ -1111,11 +1106,9 @@ queue_message(struct vchiq_state *state, struct vchiq_service *service, ? service->base.fourcc : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); - vchiq_log_debug(state->dev, VCHIQ_CORE_MSG, - "Sent Msg %s(%u) to %p4cc s:%u d:%d len:%zu", - msg_type_str(VCHIQ_MSG_TYPE(msgid)), VCHIQ_MSG_TYPE(msgid), - &svc_fourcc, VCHIQ_MSG_SRCPORT(msgid), - VCHIQ_MSG_DSTPORT(msgid), size); + dev_dbg(state->dev, "core_msg: Sent Msg %s(%u) to %p4cc s:%u d:%d len:%zu\n", + msg_type_str(VCHIQ_MSG_TYPE(msgid)), VCHIQ_MSG_TYPE(msgid), + &svc_fourcc, VCHIQ_MSG_SRCPORT(msgid), VCHIQ_MSG_DSTPORT(msgid), size); } /* Make sure the new header is visible to the peer. */ @@ -1167,15 +1160,13 @@ queue_message_sync(struct vchiq_state *state, struct vchiq_service *service, int oldmsgid = header->msgid; if (oldmsgid != VCHIQ_MSGID_PADDING) - vchiq_log_error(state->dev, VCHIQ_CORE, "%d: qms - msgid %x, not PADDING", - state->id, oldmsgid); + dev_err(state->dev, "core: %d: qms - msgid %x, not PADDING\n", + state->id, oldmsgid); } - vchiq_log_debug(state->dev, VCHIQ_SYNC, - "%d: qms %s@%pK,%x (%d->%d)", state->id, - msg_type_str(VCHIQ_MSG_TYPE(msgid)), - header, size, VCHIQ_MSG_SRCPORT(msgid), - VCHIQ_MSG_DSTPORT(msgid)); + dev_dbg(state->dev, "sync: %d: qms %s@%pK,%x (%d->%d)\n", + state->id, msg_type_str(VCHIQ_MSG_TYPE(msgid)), header, size, + VCHIQ_MSG_SRCPORT(msgid), VCHIQ_MSG_DSTPORT(msgid)); callback_result = copy_message_data(copy_callback, context, @@ -1205,11 +1196,11 @@ queue_message_sync(struct vchiq_state *state, struct vchiq_service *service, svc_fourcc = service ? service->base.fourcc : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); - vchiq_log_trace(state->dev, VCHIQ_SYNC, - "Sent Sync Msg %s(%u) to %p4cc s:%u d:%d len:%d", - msg_type_str(VCHIQ_MSG_TYPE(msgid)), VCHIQ_MSG_TYPE(msgid), - &svc_fourcc, VCHIQ_MSG_SRCPORT(msgid), - VCHIQ_MSG_DSTPORT(msgid), size); + dev_dbg(state->dev, + "sync: Sent Sync Msg %s(%u) to %p4cc s:%u d:%d len:%d\n", + msg_type_str(VCHIQ_MSG_TYPE(msgid)), VCHIQ_MSG_TYPE(msgid), + &svc_fourcc, VCHIQ_MSG_SRCPORT(msgid), + VCHIQ_MSG_DSTPORT(msgid), size); remote_event_signal(&state->remote->sync_trigger); @@ -1261,9 +1252,9 @@ release_slot(struct vchiq_state *state, struct vchiq_slot_info *slot_info, VCHIQ_SLOT_QUEUE_MASK] = SLOT_INDEX_FROM_INFO(state, slot_info); state->remote->slot_queue_recycle = slot_queue_recycle + 1; - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: %s %d - recycle->%x", - state->id, __func__, SLOT_INDEX_FROM_INFO(state, slot_info), - state->remote->slot_queue_recycle); + dev_dbg(state->dev, "core: %d: %d - recycle->%x\n", + state->id, SLOT_INDEX_FROM_INFO(state, slot_info), + state->remote->slot_queue_recycle); /* * A write barrier is necessary, but remote_event_signal @@ -1298,11 +1289,11 @@ notify_bulks(struct vchiq_service *service, struct vchiq_bulk_queue *queue, { int status = 0; - vchiq_log_trace(service->state->dev, VCHIQ_CORE, - "%d: nb:%d %cx - p=%x rn=%x r=%x", - service->state->id, service->localport, - (queue == &service->bulk_tx) ? 't' : 'r', - queue->process, queue->remote_notify, queue->remove); + dev_dbg(service->state->dev, + "core: %d: nb:%d %cx - p=%x rn=%x r=%x\n", + service->state->id, service->localport, + (queue == &service->bulk_tx) ? 't' : 'r', + queue->process, queue->remote_notify, queue->remove); queue->remote_notify = queue->process; @@ -1382,8 +1373,8 @@ poll_services_of_group(struct vchiq_state *state, int group) service_flags = atomic_xchg(&service->poll_flags, 0); if (service_flags & BIT(VCHIQ_POLL_REMOVE)) { - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: ps - remove %d<->%d", - state->id, service->localport, service->remoteport); + dev_dbg(state->dev, "core: %d: ps - remove %d<->%d\n", + state->id, service->localport, service->remoteport); /* * Make it look like a client, because @@ -1395,8 +1386,8 @@ poll_services_of_group(struct vchiq_state *state, int group) if (vchiq_close_service_internal(service, NO_CLOSE_RECVD)) request_poll(state, service, VCHIQ_POLL_REMOVE); } else if (service_flags & BIT(VCHIQ_POLL_TERMINATE)) { - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: ps - terminate %d<->%d", - state->id, service->localport, service->remoteport); + dev_dbg(state->dev, "core: %d: ps - terminate %d<->%d\n", + state->id, service->localport, service->remoteport); if (vchiq_close_service_internal(service, NO_CLOSE_RECVD)) request_poll(state, service, VCHIQ_POLL_TERMINATE); } @@ -1425,11 +1416,11 @@ abort_outstanding_bulks(struct vchiq_service *service, { int is_tx = (queue == &service->bulk_tx); - vchiq_log_trace(service->state->dev, VCHIQ_CORE, - "%d: aob:%d %cx - li=%x ri=%x p=%x", - service->state->id, service->localport, - is_tx ? 't' : 'r', queue->local_insert, - queue->remote_insert, queue->process); + dev_dbg(service->state->dev, + "core: %d: aob:%d %cx - li=%x ri=%x p=%x\n", + service->state->id, service->localport, + is_tx ? 't' : 'r', queue->local_insert, + queue->remote_insert, queue->process); WARN_ON((int)(queue->local_insert - queue->process) < 0); WARN_ON((int)(queue->remote_insert - queue->process) < 0); @@ -1448,11 +1439,11 @@ abort_outstanding_bulks(struct vchiq_service *service, if (queue->process != queue->local_insert) { vchiq_complete_bulk(service->instance, bulk); - vchiq_log_debug(service->state->dev, VCHIQ_CORE_MSG, - "%s %p4cc d:%d ABORTED - tx len:%d, rx len:%d", - is_tx ? "Send Bulk to" : "Recv Bulk from", - &service->base.fourcc, - service->remoteport, bulk->size, bulk->remote_size); + dev_dbg(service->state->dev, + "core_msg: %s %p4cc d:%d ABORTED - tx len:%d, rx len:%d\n", + is_tx ? "Send Bulk to" : "Recv Bulk from", + &service->base.fourcc, + service->remoteport, bulk->size, bulk->remote_size); } else { /* fabricate a matching dummy bulk */ bulk->data = 0; @@ -1485,8 +1476,8 @@ parse_open(struct vchiq_state *state, struct vchiq_header *header) payload = (struct vchiq_open_payload *)header->data; fourcc = payload->fourcc; - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: prs OPEN@%pK (%d->'%p4cc')", - state->id, header, localport, &fourcc); + dev_dbg(state->dev, "core: %d: prs OPEN@%pK (%d->'%p4cc')\n", + state->id, header, localport, &fourcc); service = get_listening_service(state, fourcc); if (!service) @@ -1609,17 +1600,17 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) vchiq_service_put(service); service = get_connected_service(state, remoteport); if (service) - vchiq_log_warning(state->dev, VCHIQ_CORE, - "%d: prs %s@%pK (%d->%d) - found connected service %d", - state->id, msg_type_str(type), header, - remoteport, localport, service->localport); + dev_warn(state->dev, + "core: %d: prs %s@%pK (%d->%d) - found connected service %d\n", + state->id, msg_type_str(type), header, + remoteport, localport, service->localport); } if (!service) { - vchiq_log_error(state->dev, VCHIQ_CORE, - "%d: prs %s@%pK (%d->%d) - invalid/closed service %d", - state->id, msg_type_str(type), header, remoteport, - localport, localport); + dev_err(state->dev, + "core: %d: prs %s@%pK (%d->%d) - invalid/closed service %d\n", + state->id, msg_type_str(type), header, remoteport, + localport, localport); goto skip_message; } break; @@ -1631,18 +1622,15 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) svc_fourcc = service ? service->base.fourcc : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); - vchiq_log_debug(state->dev, VCHIQ_CORE_MSG, - "Rcvd Msg %s(%u) from %p4cc s:%d d:%d len:%d", - msg_type_str(type), type, &svc_fourcc, - remoteport, localport, size); + dev_dbg(state->dev, "core_msg: Rcvd Msg %s(%u) from %p4cc s:%d d:%d len:%d\n", + msg_type_str(type), type, &svc_fourcc, remoteport, localport, size); if (size > 0) vchiq_log_dump_mem(state->dev, "Rcvd", 0, header->data, min(16, size)); if (((unsigned long)header & VCHIQ_SLOT_MASK) + calc_stride(size) > VCHIQ_SLOT_SIZE) { - vchiq_log_error(state->dev, VCHIQ_CORE, - "header %pK (msgid %x) - size %x too big for slot", - header, (unsigned int)msgid, (unsigned int)size); + dev_err(state->dev, "core: header %pK (msgid %x) - size %x too big for slot\n", + header, (unsigned int)msgid, (unsigned int)size); WARN(1, "oversized for slot\n"); } @@ -1659,37 +1647,36 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) header->data; service->peer_version = payload->version; } - vchiq_log_debug(state->dev, VCHIQ_CORE, - "%d: prs OPENACK@%pK,%x (%d->%d) v:%d", - state->id, header, size, remoteport, localport, - service->peer_version); + dev_dbg(state->dev, + "core: %d: prs OPENACK@%pK,%x (%d->%d) v:%d\n", + state->id, header, size, remoteport, localport, + service->peer_version); if (service->srvstate == VCHIQ_SRVSTATE_OPENING) { service->remoteport = remoteport; set_service_state(service, VCHIQ_SRVSTATE_OPEN); complete(&service->remove_event); } else { - vchiq_log_error(state->dev, VCHIQ_CORE, "OPENACK received in state %s", - srvstate_names[service->srvstate]); + dev_err(state->dev, "core: OPENACK received in state %s\n", + srvstate_names[service->srvstate]); } break; case VCHIQ_MSG_CLOSE: WARN_ON(size); /* There should be no data */ - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: prs CLOSE@%pK (%d->%d)", - state->id, header, remoteport, localport); + dev_dbg(state->dev, "core: %d: prs CLOSE@%pK (%d->%d)\n", + state->id, header, remoteport, localport); mark_service_closing_internal(service, 1); if (vchiq_close_service_internal(service, CLOSE_RECVD) == -EAGAIN) goto bail_not_ready; - vchiq_log_debug(state->dev, VCHIQ_CORE, "Close Service %p4cc s:%u d:%d", - &service->base.fourcc, - service->localport, service->remoteport); + dev_dbg(state->dev, "core: Close Service %p4cc s:%u d:%d\n", + &service->base.fourcc, service->localport, service->remoteport); break; case VCHIQ_MSG_DATA: - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: prs DATA@%pK,%x (%d->%d)", - state->id, header, size, remoteport, localport); + dev_dbg(state->dev, "core: %d: prs DATA@%pK,%x (%d->%d)\n", + state->id, header, size, remoteport, localport); if ((service->remoteport == remoteport) && (service->srvstate == VCHIQ_SRVSTATE_OPEN)) { @@ -1708,8 +1695,8 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) } break; case VCHIQ_MSG_CONNECT: - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: prs CONNECT@%pK", - state->id, header); + dev_dbg(state->dev, "core: %d: prs CONNECT@%pK\n", + state->id, header); state->version_common = ((struct vchiq_slot_zero *) state->slot_data)->version; complete(&state->connect); @@ -1740,11 +1727,10 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) } if ((int)(queue->remote_insert - queue->local_insert) >= 0) { - vchiq_log_error(state->dev, VCHIQ_CORE, - "%d: prs %s@%pK (%d->%d) unexpected (ri=%d,li=%d)", - state->id, msg_type_str(type), header, remoteport, - localport, queue->remote_insert, - queue->local_insert); + dev_err(state->dev, + "core: %d: prs %s@%pK (%d->%d) unexpected (ri=%d,li=%d)\n", + state->id, msg_type_str(type), header, remoteport, + localport, queue->remote_insert, queue->local_insert); mutex_unlock(&service->bulk_mutex); break; } @@ -1761,15 +1747,14 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) bulk->actual = *(int *)header->data; queue->remote_insert++; - vchiq_log_debug(state->dev, VCHIQ_CORE, - "%d: prs %s@%pK (%d->%d) %x@%pad", - state->id, msg_type_str(type), header, remoteport, - localport, bulk->actual, &bulk->data); + dev_dbg(state->dev, "core: %d: prs %s@%pK (%d->%d) %x@%pad\n", + state->id, msg_type_str(type), header, remoteport, + localport, bulk->actual, &bulk->data); - vchiq_log_trace(state->dev, VCHIQ_CORE, "%d: prs:%d %cx li=%x ri=%x p=%x", - state->id, localport, - (type == VCHIQ_MSG_BULK_RX_DONE) ? 'r' : 't', - queue->local_insert, queue->remote_insert, queue->process); + dev_dbg(state->dev, "core: %d: prs:%d %cx li=%x ri=%x p=%x\n", + state->id, localport, + (type == VCHIQ_MSG_BULK_RX_DONE) ? 'r' : 't', + queue->local_insert, queue->remote_insert, queue->process); DEBUG_TRACE(PARSE_LINE); WARN_ON(queue->process == queue->local_insert); @@ -1782,16 +1767,16 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) } break; case VCHIQ_MSG_PADDING: - vchiq_log_trace(state->dev, VCHIQ_CORE, "%d: prs PADDING@%pK,%x", - state->id, header, size); + dev_dbg(state->dev, "core: %d: prs PADDING@%pK,%x\n", + state->id, header, size); break; case VCHIQ_MSG_PAUSE: /* If initiated, signal the application thread */ - vchiq_log_trace(state->dev, VCHIQ_CORE, "%d: prs PAUSE@%pK,%x", - state->id, header, size); + dev_dbg(state->dev, "core: %d: prs PAUSE@%pK,%x\n", + state->id, header, size); if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) { - vchiq_log_error(state->dev, VCHIQ_CORE, - "%d: PAUSE received in state PAUSED", state->id); + dev_err(state->dev, "core: %d: PAUSE received in state PAUSED\n", + state->id); break; } if (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT) { @@ -1804,8 +1789,8 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSED); break; case VCHIQ_MSG_RESUME: - vchiq_log_trace(state->dev, VCHIQ_CORE, "%d: prs RESUME@%pK,%x", - state->id, header, size); + dev_dbg(state->dev, "core: %d: prs RESUME@%pK,%x\n", + state->id, header, size); /* Release the slot mutex */ mutex_unlock(&state->slot_mutex); vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); @@ -1821,8 +1806,8 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) break; default: - vchiq_log_error(state->dev, VCHIQ_CORE, "%d: prs invalid msgid %x@%pK,%x", - state->id, msgid, header, size); + dev_err(state->dev, "core: %d: prs invalid msgid %x@%pK,%x\n", + state->id, msgid, header, size); WARN(1, "invalid message\n"); break; } @@ -1932,7 +1917,7 @@ handle_poll(struct vchiq_state *state) * since the PAUSE should have flushed * through outstanding messages. */ - vchiq_log_error(state->dev, VCHIQ_CORE, "Failed to send RESUME message"); + dev_err(state->dev, "core: Failed to send RESUME message\n"); } break; default: @@ -2032,21 +2017,18 @@ sync_func(void *v) service = find_service_by_port(state, localport); if (!service) { - vchiq_log_error(state->dev, VCHIQ_SYNC, - "%d: sf %s@%pK (%d->%d) - invalid/closed service %d", - state->id, msg_type_str(type), header, - remoteport, localport, localport); + dev_err(state->dev, + "sync: %d: sf %s@%pK (%d->%d) - invalid/closed service %d\n", + state->id, msg_type_str(type), header, remoteport, + localport, localport); release_message_sync(state, header); continue; } - svc_fourcc = service ? service->base.fourcc - : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); + svc_fourcc = service->base.fourcc; - vchiq_log_trace(state->dev, VCHIQ_SYNC, - "Rcvd Msg %s from %p4cc s:%d d:%d len:%d", - msg_type_str(type), &svc_fourcc, - remoteport, localport, size); + dev_dbg(state->dev, "sync: Rcvd Msg %s from %p4cc s:%d d:%d len:%d\n", + msg_type_str(type), &svc_fourcc, remoteport, localport, size); if (size > 0) vchiq_log_dump_mem(state->dev, "Rcvd", 0, header->data, min(16, size)); @@ -2058,9 +2040,9 @@ sync_func(void *v) header->data; service->peer_version = payload->version; } - vchiq_log_debug(state->dev, VCHIQ_SYNC, "%d: sf OPENACK@%pK,%x (%d->%d) v:%d", - state->id, header, size, remoteport, localport, - service->peer_version); + dev_err(state->dev, "sync: %d: sf OPENACK@%pK,%x (%d->%d) v:%d\n", + state->id, header, size, remoteport, localport, + service->peer_version); if (service->srvstate == VCHIQ_SRVSTATE_OPENING) { service->remoteport = remoteport; set_service_state(service, VCHIQ_SRVSTATE_OPENSYNC); @@ -2071,22 +2053,22 @@ sync_func(void *v) break; case VCHIQ_MSG_DATA: - vchiq_log_trace(state->dev, VCHIQ_SYNC, "%d: sf DATA@%pK,%x (%d->%d)", - state->id, header, size, remoteport, localport); + dev_dbg(state->dev, "sync: %d: sf DATA@%pK,%x (%d->%d)\n", + state->id, header, size, remoteport, localport); if ((service->remoteport == remoteport) && (service->srvstate == VCHIQ_SRVSTATE_OPENSYNC)) { if (make_service_callback(service, VCHIQ_MESSAGE_AVAILABLE, header, NULL) == -EAGAIN) - vchiq_log_error(state->dev, VCHIQ_SYNC, - "synchronous callback to service %d returns -EAGAIN", - localport); + dev_err(state->dev, + "sync: error: synchronous callback to service %d returns -EAGAIN\n", + localport); } break; default: - vchiq_log_error(state->dev, VCHIQ_SYNC, "%d: sf unexpected msgid %x@%pK,%x", - state->id, msgid, header, size); + dev_err(state->dev, "sync: error: %d: sf unexpected msgid %x@%pK,%x\n", + state->id, msgid, header, size); release_message_sync(state, header); break; } @@ -2119,8 +2101,8 @@ vchiq_init_slots(struct device *dev, void *mem_base, int mem_size) num_slots -= first_data_slot; if (num_slots < 4) { - vchiq_log_error(dev, VCHIQ_CORE, "%s - insufficient memory %x bytes", - __func__, mem_size); + dev_err(dev, "core: %s: Insufficient memory %x bytes\n", + __func__, mem_size); return NULL; } @@ -2462,9 +2444,9 @@ vchiq_add_service_internal(struct vchiq_state *state, /* Bring this service online */ set_service_state(service, srvstate); - vchiq_log_debug(state->dev, VCHIQ_CORE_MSG, "%s Service %p4cc SrcPort:%d", - (srvstate == VCHIQ_SRVSTATE_OPENING) ? "Open" : "Add", - ¶ms->fourcc, service->localport); + dev_dbg(state->dev, "core_msg: %s Service %p4cc SrcPort:%d\n", + (srvstate == VCHIQ_SRVSTATE_OPENING) ? "Open" : "Add", + ¶ms->fourcc, service->localport); /* Don't unlock the service - leave it with a ref_count of 1. */ @@ -2501,11 +2483,10 @@ vchiq_open_service_internal(struct vchiq_service *service, int client_id) } else if ((service->srvstate != VCHIQ_SRVSTATE_OPEN) && (service->srvstate != VCHIQ_SRVSTATE_OPENSYNC)) { if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) - vchiq_log_error(service->state->dev, VCHIQ_CORE, - "%d: osi - srvstate = %s (ref %u)", - service->state->id, - srvstate_names[service->srvstate], - kref_read(&service->ref_count)); + dev_err(service->state->dev, + "core: %d: osi - srvstate = %s (ref %u)\n", + service->state->id, srvstate_names[service->srvstate], + kref_read(&service->ref_count)); status = -EINVAL; VCHIQ_SERVICE_STATS_INC(service, error_count); vchiq_release_service_internal(service); @@ -2560,15 +2541,14 @@ release_service_messages(struct vchiq_service *service) int port = VCHIQ_MSG_DSTPORT(msgid); if ((port == service->localport) && (msgid & VCHIQ_MSGID_CLAIMED)) { - vchiq_log_debug(state->dev, VCHIQ_CORE, - " fsi - hdr %pK", header); + dev_dbg(state->dev, "core: fsi - hdr %pK\n", header); release_slot(state, slot_info, header, NULL); } pos += calc_stride(header->size); if (pos > VCHIQ_SLOT_SIZE) { - vchiq_log_error(state->dev, VCHIQ_CORE, - "fsi - pos %x: header %pK, msgid %x, header->msgid %x, header->size %x", - pos, header, msgid, header->msgid, header->size); + dev_err(state->dev, + "core: fsi - pos %x: header %pK, msgid %x, header->msgid %x, header->size %x\n", + pos, header, msgid, header->msgid, header->size); WARN(1, "invalid slot position\n"); } } @@ -2622,8 +2602,8 @@ close_service_complete(struct vchiq_service *service, int failstate) case VCHIQ_SRVSTATE_LISTENING: break; default: - vchiq_log_error(service->state->dev, VCHIQ_CORE, "%s(%x) called in state %s", - __func__, service->handle, srvstate_names[service->srvstate]); + dev_err(service->state->dev, "core: (%x) called in state %s\n", + service->handle, srvstate_names[service->srvstate]); WARN(1, "%s in unexpected state\n", __func__); return -EINVAL; } @@ -2669,8 +2649,9 @@ vchiq_close_service_internal(struct vchiq_service *service, int close_recvd) int close_id = MAKE_CLOSE(service->localport, VCHIQ_MSG_DSTPORT(service->remoteport)); - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: csi:%d,%d (%s)", service->state->id, - service->localport, close_recvd, srvstate_names[service->srvstate]); + dev_dbg(state->dev, "core: %d: csi:%d,%d (%s)\n", + service->state->id, service->localport, close_recvd, + srvstate_names[service->srvstate]); switch (service->srvstate) { case VCHIQ_SRVSTATE_CLOSED: @@ -2678,8 +2659,8 @@ vchiq_close_service_internal(struct vchiq_service *service, int close_recvd) case VCHIQ_SRVSTATE_LISTENING: case VCHIQ_SRVSTATE_CLOSEWAIT: if (close_recvd) { - vchiq_log_error(state->dev, VCHIQ_CORE, "%s(1) called in state %s", - __func__, srvstate_names[service->srvstate]); + dev_err(state->dev, "core: (1) called in state %s\n", + srvstate_names[service->srvstate]); } else if (is_server) { if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { status = -EINVAL; @@ -2766,8 +2747,8 @@ vchiq_close_service_internal(struct vchiq_service *service, int close_recvd) break; default: - vchiq_log_error(state->dev, VCHIQ_CORE, "%s(%d) called in state %s", __func__, - close_recvd, srvstate_names[service->srvstate]); + dev_err(state->dev, "core: (%d) called in state %s\n", + close_recvd, srvstate_names[service->srvstate]); break; } @@ -2780,8 +2761,8 @@ vchiq_terminate_service_internal(struct vchiq_service *service) { struct vchiq_state *state = service->state; - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: tsi - (%d<->%d)", state->id, - service->localport, service->remoteport); + dev_dbg(state->dev, "core: %d: tsi - (%d<->%d)\n", + state->id, service->localport, service->remoteport); mark_service_closing(service); @@ -2795,8 +2776,7 @@ vchiq_free_service_internal(struct vchiq_service *service) { struct vchiq_state *state = service->state; - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: fsi - (%d)", - state->id, service->localport); + dev_dbg(state->dev, "core: %d: fsi - (%d)\n", state->id, service->localport); switch (service->srvstate) { case VCHIQ_SRVSTATE_OPENING: @@ -2806,8 +2786,8 @@ vchiq_free_service_internal(struct vchiq_service *service) case VCHIQ_SRVSTATE_CLOSEWAIT: break; default: - vchiq_log_error(state->dev, VCHIQ_CORE, "%d: fsi - (%d) in state %s", state->id, - service->localport, srvstate_names[service->srvstate]); + dev_err(state->dev, "core: %d: fsi - (%d) in state %s\n", + state->id, service->localport, srvstate_names[service->srvstate]); return; } @@ -2876,8 +2856,8 @@ vchiq_close_service(struct vchiq_instance *instance, unsigned int handle) if (!service) return -EINVAL; - vchiq_log_debug(service->state->dev, VCHIQ_CORE, "%d: close_service:%d", - service->state->id, service->localport); + dev_dbg(service->state->dev, "core: %d: close_service:%d\n", + service->state->id, service->localport); if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || (service->srvstate == VCHIQ_SRVSTATE_LISTENING) || @@ -2907,10 +2887,10 @@ vchiq_close_service(struct vchiq_instance *instance, unsigned int handle) (service->srvstate == VCHIQ_SRVSTATE_OPEN)) break; - vchiq_log_warning(service->state->dev, VCHIQ_CORE, - "%d: close_service:%d - waiting in state %s", - service->state->id, service->localport, - srvstate_names[service->srvstate]); + dev_warn(service->state->dev, + "core: %d: close_service:%d - waiting in state %s\n", + service->state->id, service->localport, + srvstate_names[service->srvstate]); } if (!status && @@ -2934,8 +2914,8 @@ vchiq_remove_service(struct vchiq_instance *instance, unsigned int handle) if (!service) return -EINVAL; - vchiq_log_debug(service->state->dev, VCHIQ_CORE, "%d: remove_service:%d", - service->state->id, service->localport); + dev_dbg(service->state->dev, "core: %d: remove_service:%d\n", + service->state->id, service->localport); if (service->srvstate == VCHIQ_SRVSTATE_FREE) { vchiq_service_put(service); @@ -2968,10 +2948,10 @@ vchiq_remove_service(struct vchiq_instance *instance, unsigned int handle) (service->srvstate == VCHIQ_SRVSTATE_OPEN)) break; - vchiq_log_warning(service->state->dev, VCHIQ_CORE, - "%d: remove_service:%d - waiting in state %s", - service->state->id, service->localport, - srvstate_names[service->srvstate]); + dev_warn(service->state->dev, + "core: %d: remove_service:%d - waiting in state %s\n", + service->state->id, service->localport, + srvstate_names[service->srvstate]); } if (!status && (service->srvstate != VCHIQ_SRVSTATE_FREE)) @@ -3078,9 +3058,9 @@ int vchiq_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, */ wmb(); - vchiq_log_debug(state->dev, VCHIQ_CORE, "%d: bt (%d->%d) %cx %x@%pad %pK", - state->id, service->localport, service->remoteport, - dir_char, size, &bulk->data, userdata); + dev_dbg(state->dev, "core: %d: bt (%d->%d) %cx %x@%pad %pK\n", + state->id, service->localport, service->remoteport, + dir_char, size, &bulk->data, userdata); /* * The slot mutex must be held when the service is being closed, so @@ -3115,9 +3095,9 @@ int vchiq_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, mutex_unlock(&state->slot_mutex); mutex_unlock(&service->bulk_mutex); - vchiq_log_trace(state->dev, VCHIQ_CORE, "%d: bt:%d %cx li=%x ri=%x p=%x", - state->id, service->localport, dir_char, queue->local_insert, - queue->remote_insert, queue->process); + dev_dbg(state->dev, "core: %d: bt:%d %cx li=%x ri=%x p=%x\n", + state->id, service->localport, dir_char, queue->local_insert, + queue->remote_insert, queue->process); waiting: vchiq_service_put(service); @@ -3372,8 +3352,8 @@ vchiq_set_service_option(struct vchiq_instance *instance, unsigned int handle, return ret; } -static int -vchiq_dump_shared_state(void *dump_context, struct vchiq_state *state, +static void +vchiq_dump_shared_state(struct seq_file *f, struct vchiq_state *state, struct vchiq_shared_state *shared, const char *label) { static const char *const debug_names[] = { @@ -3390,146 +3370,44 @@ vchiq_dump_shared_state(void *dump_context, struct vchiq_state *state, "COMPLETION_QUEUE_FULL_COUNT" }; int i; - char buf[80]; - int len; - int err; - - len = scnprintf(buf, sizeof(buf), " %s: slots %d-%d tx_pos=%x recycle=%x", - label, shared->slot_first, shared->slot_last, - shared->tx_pos, shared->slot_queue_recycle); - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; - - len = scnprintf(buf, sizeof(buf), " Slots claimed:"); - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; + + seq_printf(f, " %s: slots %d-%d tx_pos=%x recycle=%x\n", + label, shared->slot_first, shared->slot_last, + shared->tx_pos, shared->slot_queue_recycle); + + seq_puts(f, " Slots claimed:\n"); for (i = shared->slot_first; i <= shared->slot_last; i++) { struct vchiq_slot_info slot_info = *SLOT_INFO_FROM_INDEX(state, i); if (slot_info.use_count != slot_info.release_count) { - len = scnprintf(buf, sizeof(buf), " %d: %d/%d", i, slot_info.use_count, - slot_info.release_count); - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; + seq_printf(f, " %d: %d/%d\n", i, slot_info.use_count, + slot_info.release_count); } } for (i = 1; i < shared->debug[DEBUG_ENTRIES]; i++) { - len = scnprintf(buf, sizeof(buf), " DEBUG: %s = %d(%x)", - debug_names[i], shared->debug[i], shared->debug[i]); - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; - } - return 0; -} - -int vchiq_dump_state(void *dump_context, struct vchiq_state *state) -{ - char buf[80]; - int len; - int i; - int err; - - len = scnprintf(buf, sizeof(buf), "State %d: %s", state->id, - conn_state_names[state->conn_state]); - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; - - len = scnprintf(buf, sizeof(buf), " tx_pos=%x(@%pK), rx_pos=%x(@%pK)", - state->local->tx_pos, - state->tx_data + (state->local_tx_pos & VCHIQ_SLOT_MASK), - state->rx_pos, - state->rx_data + (state->rx_pos & VCHIQ_SLOT_MASK)); - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; - - len = scnprintf(buf, sizeof(buf), " Version: %d (min %d)", - VCHIQ_VERSION, VCHIQ_VERSION_MIN); - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; - - if (VCHIQ_ENABLE_STATS) { - len = scnprintf(buf, sizeof(buf), - " Stats: ctrl_tx_count=%d, ctrl_rx_count=%d, error_count=%d", - state->stats.ctrl_tx_count, state->stats.ctrl_rx_count, - state->stats.error_count); - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; - } - - len = scnprintf(buf, sizeof(buf), - " Slots: %d available (%d data), %d recyclable, %d stalls (%d data)", - ((state->slot_queue_available * VCHIQ_SLOT_SIZE) - - state->local_tx_pos) / VCHIQ_SLOT_SIZE, - state->data_quota - state->data_use_count, - state->local->slot_queue_recycle - state->slot_queue_available, - state->stats.slot_stalls, state->stats.data_stalls); - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; - - err = vchiq_dump_platform_state(dump_context); - if (err) - return err; - - err = vchiq_dump_shared_state(dump_context, - state, - state->local, - "Local"); - if (err) - return err; - err = vchiq_dump_shared_state(dump_context, - state, - state->remote, - "Remote"); - if (err) - return err; - - err = vchiq_dump_platform_instances(dump_context); - if (err) - return err; - - for (i = 0; i < state->unused_service; i++) { - struct vchiq_service *service = find_service_by_port(state, i); - - if (service) { - err = vchiq_dump_service_state(dump_context, service); - vchiq_service_put(service); - if (err) - return err; - } + seq_printf(f, " DEBUG: %s = %d(%x)\n", + debug_names[i], shared->debug[i], shared->debug[i]); } - return 0; } -int vchiq_dump_service_state(void *dump_context, struct vchiq_service *service) +static void +vchiq_dump_service_state(struct seq_file *f, struct vchiq_service *service) { - char buf[80]; - int len; - int err; unsigned int ref_count; /*Don't include the lock just taken*/ ref_count = kref_read(&service->ref_count) - 1; - len = scnprintf(buf, sizeof(buf), "Service %u: %s (ref %u)", - service->localport, srvstate_names[service->srvstate], - ref_count); + seq_printf(f, "Service %u: %s (ref %u)", service->localport, + srvstate_names[service->srvstate], ref_count); if (service->srvstate != VCHIQ_SRVSTATE_FREE) { char remoteport[30]; struct vchiq_service_quota *quota = &service->state->service_quotas[service->localport]; int fourcc = service->base.fourcc; - int tx_pending, rx_pending; + int tx_pending, rx_pending, tx_size = 0, rx_size = 0; if (service->remoteport != VCHIQ_PORT_FREE) { int len2 = scnprintf(remoteport, sizeof(remoteport), @@ -3542,68 +3420,100 @@ int vchiq_dump_service_state(void *dump_context, struct vchiq_service *service) strscpy(remoteport, "n/a", sizeof(remoteport)); } - len += scnprintf(buf + len, sizeof(buf) - len, - " '%p4cc' remote %s (msg use %d/%d, slot use %d/%d)", - &fourcc, remoteport, - quota->message_use_count, quota->message_quota, - quota->slot_use_count, quota->slot_quota); - - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; + seq_printf(f, " '%p4cc' remote %s (msg use %d/%d, slot use %d/%d)\n", + &fourcc, remoteport, + quota->message_use_count, quota->message_quota, + quota->slot_use_count, quota->slot_quota); tx_pending = service->bulk_tx.local_insert - service->bulk_tx.remote_insert; + if (tx_pending) { + unsigned int i = BULK_INDEX(service->bulk_tx.remove); + + tx_size = service->bulk_tx.bulks[i].size; + } rx_pending = service->bulk_rx.local_insert - service->bulk_rx.remote_insert; + if (rx_pending) { + unsigned int i = BULK_INDEX(service->bulk_rx.remove); - len = scnprintf(buf, sizeof(buf), - " Bulk: tx_pending=%d (size %d), rx_pending=%d (size %d)", - tx_pending, - tx_pending ? - service->bulk_tx.bulks[BULK_INDEX(service->bulk_tx.remove)].size : - 0, rx_pending, rx_pending ? - service->bulk_rx.bulks[BULK_INDEX(service->bulk_rx.remove)].size : - 0); + rx_size = service->bulk_rx.bulks[i].size; + } + + seq_printf(f, " Bulk: tx_pending=%d (size %d), rx_pending=%d (size %d)\n", + tx_pending, tx_size, rx_pending, rx_size); if (VCHIQ_ENABLE_STATS) { - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; + seq_printf(f, " Ctrl: tx_count=%d, tx_bytes=%llu, rx_count=%d, rx_bytes=%llu\n", + service->stats.ctrl_tx_count, + service->stats.ctrl_tx_bytes, + service->stats.ctrl_rx_count, + service->stats.ctrl_rx_bytes); + + seq_printf(f, " Bulk: tx_count=%d, tx_bytes=%llu, rx_count=%d, rx_bytes=%llu\n", + service->stats.bulk_tx_count, + service->stats.bulk_tx_bytes, + service->stats.bulk_rx_count, + service->stats.bulk_rx_bytes); + + seq_printf(f, " %d quota stalls, %d slot stalls, %d bulk stalls, %d aborted, %d errors\n", + service->stats.quota_stalls, + service->stats.slot_stalls, + service->stats.bulk_stalls, + service->stats.bulk_aborted_count, + service->stats.error_count); + } + } - len = scnprintf(buf, sizeof(buf), - " Ctrl: tx_count=%d, tx_bytes=%llu, rx_count=%d, rx_bytes=%llu", - service->stats.ctrl_tx_count, service->stats.ctrl_tx_bytes, - service->stats.ctrl_rx_count, service->stats.ctrl_rx_bytes); - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; + vchiq_dump_platform_service_state(f, service); +} - len = scnprintf(buf, sizeof(buf), - " Bulk: tx_count=%d, tx_bytes=%llu, rx_count=%d, rx_bytes=%llu", - service->stats.bulk_tx_count, service->stats.bulk_tx_bytes, - service->stats.bulk_rx_count, service->stats.bulk_rx_bytes); - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; +void vchiq_dump_state(struct seq_file *f, struct vchiq_state *state) +{ + int i; - len = scnprintf(buf, sizeof(buf), - " %d quota stalls, %d slot stalls, %d bulk stalls, %d aborted, %d errors", - service->stats.quota_stalls, service->stats.slot_stalls, - service->stats.bulk_stalls, - service->stats.bulk_aborted_count, - service->stats.error_count); - } + seq_printf(f, "State %d: %s\n", state->id, + conn_state_names[state->conn_state]); + + seq_printf(f, " tx_pos=%x(@%pK), rx_pos=%x(@%pK)\n", + state->local->tx_pos, + state->tx_data + (state->local_tx_pos & VCHIQ_SLOT_MASK), + state->rx_pos, + state->rx_data + (state->rx_pos & VCHIQ_SLOT_MASK)); + + seq_printf(f, " Version: %d (min %d)\n", VCHIQ_VERSION, + VCHIQ_VERSION_MIN); + + if (VCHIQ_ENABLE_STATS) { + seq_printf(f, " Stats: ctrl_tx_count=%d, ctrl_rx_count=%d, error_count=%d\n", + state->stats.ctrl_tx_count, state->stats.ctrl_rx_count, + state->stats.error_count); } - err = vchiq_dump(dump_context, buf, len + 1); - if (err) - return err; + seq_printf(f, " Slots: %d available (%d data), %d recyclable, %d stalls (%d data)\n", + ((state->slot_queue_available * VCHIQ_SLOT_SIZE) - + state->local_tx_pos) / VCHIQ_SLOT_SIZE, + state->data_quota - state->data_use_count, + state->local->slot_queue_recycle - state->slot_queue_available, + state->stats.slot_stalls, state->stats.data_stalls); + + vchiq_dump_platform_state(f); + + vchiq_dump_shared_state(f, state, state->local, "Local"); + + vchiq_dump_shared_state(f, state, state->remote, "Remote"); + + vchiq_dump_platform_instances(f); - if (service->srvstate != VCHIQ_SRVSTATE_FREE) - err = vchiq_dump_platform_service_state(dump_context, service); - return err; + for (i = 0; i < state->unused_service; i++) { + struct vchiq_service *service = find_service_by_port(state, i); + + if (service) { + vchiq_dump_service_state(f, service); + vchiq_service_put(service); + } + } } int vchiq_send_remote_use(struct vchiq_state *state) @@ -3653,9 +3563,9 @@ void vchiq_log_dump_mem(struct device *dev, const char *label, u32 addr, *s++ = '\0'; if (label && (*label != '\0')) - vchiq_log_trace(dev, VCHIQ_CORE, "%s: %08x: %s", label, addr, line_buf); + dev_dbg(dev, "core: %s: %08x: %s\n", label, addr, line_buf); else - vchiq_log_trace(dev, VCHIQ_CORE, "%s: %08x: %s", label, addr, line_buf); + dev_dbg(dev, "core: %s: %08x: %s\n", label, addr, line_buf); addr += 16; mem += 16; diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h index 161358db457c..c8527551b58c 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h @@ -6,6 +6,7 @@ #include <linux/mutex.h> #include <linux/completion.h> +#include <linux/debugfs.h> #include <linux/dev_printk.h> #include <linux/kthread.h> #include <linux/kref.h> @@ -31,44 +32,6 @@ #define VCHIQ_SLOT_SIZE 4096 #define VCHIQ_MAX_MSG_SIZE (VCHIQ_SLOT_SIZE - sizeof(struct vchiq_header)) -enum vchiq_log_category { - VCHIQ_ARM, - VCHIQ_CORE, - VCHIQ_CORE_MSG, - VCHIQ_SYNC, - VCHIQ_SUSPEND, -}; - -static inline const char *log_category_str(enum vchiq_log_category c) -{ - static const char * const strings[] = { - "vchiq_arm", - "vchiq_core", - "vchiq_core_msg", - "vchiq_sync", - "vchiq_suspend", - }; - - return strings[c]; -}; - -#ifndef vchiq_log_error -#define vchiq_log_error(dev, cat, fmt, ...) \ - do { dev_dbg(dev, "%s error: " fmt, log_category_str(cat), ##__VA_ARGS__); } while (0) -#endif -#ifndef vchiq_log_warning -#define vchiq_log_warning(dev, cat, fmt, ...) \ - do { dev_dbg(dev, "%s warning: " fmt, log_category_str(cat), ##__VA_ARGS__); } while (0) -#endif -#ifndef vchiq_log_debug -#define vchiq_log_debug(dev, cat, fmt, ...) \ - do { dev_dbg(dev, "%s debug: " fmt, log_category_str(cat), ##__VA_ARGS__); } while (0) -#endif -#ifndef vchiq_log_trace -#define vchiq_log_trace(dev, cat, fmt, ...) \ - do { dev_dbg(dev, "%s trace: " fmt, log_category_str(cat), ##__VA_ARGS__); } while (0) -#endif - #define VCHIQ_SLOT_MASK (VCHIQ_SLOT_SIZE - 1) #define VCHIQ_SLOT_QUEUE_MASK (VCHIQ_MAX_SLOTS_PER_SIDE - 1) #define VCHIQ_SLOT_ZERO_SLOTS DIV_ROUND_UP(sizeof(struct vchiq_slot_zero), \ @@ -504,11 +467,8 @@ vchiq_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, void * void __user *uoffset, int size, void *userdata, enum vchiq_bulk_mode mode, enum vchiq_bulk_dir dir); -extern int -vchiq_dump_state(void *dump_context, struct vchiq_state *state); - -extern int -vchiq_dump_service_state(void *dump_context, struct vchiq_service *service); +extern void +vchiq_dump_state(struct seq_file *f, struct vchiq_state *state); extern void vchiq_loud_error_header(void); @@ -564,13 +524,11 @@ void vchiq_complete_bulk(struct vchiq_instance *instance, struct vchiq_bulk *bul void remote_event_signal(struct remote_event *event); -int vchiq_dump(void *dump_context, const char *str, int len); - -int vchiq_dump_platform_state(void *dump_context); +void vchiq_dump_platform_state(struct seq_file *f); -int vchiq_dump_platform_instances(void *dump_context); +void vchiq_dump_platform_instances(struct seq_file *f); -int vchiq_dump_platform_service_state(void *dump_context, struct vchiq_service *service); +void vchiq_dump_platform_service_state(struct seq_file *f, struct vchiq_service *service); int vchiq_use_service_internal(struct vchiq_service *service); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c index 58db78a9c8d4..d833e4e2973a 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c @@ -40,6 +40,13 @@ static int debugfs_trace_show(struct seq_file *f, void *offset) return 0; } +static int vchiq_dump_show(struct seq_file *f, void *offset) +{ + vchiq_dump_state(f, &g_state); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(vchiq_dump); + static int debugfs_trace_open(struct inode *inode, struct file *file) { return single_open(file, debugfs_trace_show, inode->i_private); @@ -115,6 +122,9 @@ void vchiq_debugfs_init(void) { vchiq_dbg_dir = debugfs_create_dir("vchiq", NULL); vchiq_dbg_clients = debugfs_create_dir("clients", vchiq_dbg_dir); + + debugfs_create_file("state", S_IFREG | 0444, vchiq_dbg_dir, NULL, + &vchiq_dump_fops); } /* remove all the debugfs entries */ diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c index 0bc93f48c14c..4d9deeeb637a 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c @@ -47,9 +47,8 @@ user_service_free(void *userdata) static void close_delivered(struct user_service *user_service) { - vchiq_log_debug(user_service->service->state->dev, VCHIQ_ARM, - "%s(handle=%x)", - __func__, user_service->service->handle); + dev_dbg(user_service->service->state->dev, + "arm: (handle=%x)\n", user_service->service->handle); if (user_service->close_pending) { /* Allow the underlying service to be culled */ @@ -235,8 +234,7 @@ static int vchiq_ioc_dequeue_message(struct vchiq_instance *instance, spin_unlock(&msg_queue_spinlock); DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); if (wait_for_completion_interruptible(&user_service->insert_event)) { - vchiq_log_debug(service->state->dev, VCHIQ_ARM, - "DEQUEUE_MESSAGE interrupted"); + dev_dbg(service->state->dev, "arm: DEQUEUE_MESSAGE interrupted\n"); ret = -EINTR; break; } @@ -271,9 +269,9 @@ static int vchiq_ioc_dequeue_message(struct vchiq_instance *instance, ret = -EFAULT; } } else { - vchiq_log_error(service->state->dev, VCHIQ_ARM, - "header %pK: bufsize %x < size %x", - header, args->bufsize, header->size); + dev_err(service->state->dev, + "arm: header %pK: bufsize %x < size %x\n", + header, args->bufsize, header->size); WARN(1, "invalid size\n"); ret = -EMSGSIZE; } @@ -318,13 +316,13 @@ static int vchiq_irq_queue_bulk_tx_rx(struct vchiq_instance *instance, } mutex_unlock(&instance->bulk_waiter_list_mutex); if (!waiter) { - vchiq_log_error(service->state->dev, VCHIQ_ARM, - "no bulk_waiter found for pid %d", current->pid); + dev_err(service->state->dev, + "arm: no bulk_waiter found for pid %d\n", current->pid); ret = -ESRCH; goto out; } - vchiq_log_debug(service->state->dev, VCHIQ_ARM, - "found bulk_waiter %pK for pid %d", waiter, current->pid); + dev_dbg(service->state->dev, "arm: found bulk_waiter %pK for pid %d\n", + waiter, current->pid); userdata = &waiter->bulk_waiter; } else { userdata = args->userdata; @@ -355,8 +353,8 @@ static int vchiq_irq_queue_bulk_tx_rx(struct vchiq_instance *instance, mutex_lock(&instance->bulk_waiter_list_mutex); list_add(&waiter->list, &instance->bulk_waiter_list); mutex_unlock(&instance->bulk_waiter_list_mutex); - vchiq_log_debug(service->state->dev, VCHIQ_ARM, - "saved bulk_waiter %pK for pid %d", waiter, current->pid); + dev_dbg(service->state->dev, "arm: saved bulk_waiter %pK for pid %d\n", + waiter, current->pid); ret = put_user(mode_waiting, mode); } @@ -455,8 +453,7 @@ static int vchiq_ioc_await_completion(struct vchiq_instance *instance, mutex_lock(&instance->completion_mutex); if (rc) { DEBUG_TRACE(AWAIT_COMPLETION_LINE); - vchiq_log_debug(instance->state->dev, VCHIQ_ARM, - "AWAIT_COMPLETION interrupted"); + dev_dbg(instance->state->dev, "arm: AWAIT_COMPLETION interrupted\n"); ret = -EINTR; goto out; } @@ -501,10 +498,10 @@ static int vchiq_ioc_await_completion(struct vchiq_instance *instance, msglen = header->size + sizeof(struct vchiq_header); /* This must be a VCHIQ-style service */ if (args->msgbufsize < msglen) { - vchiq_log_error(service->state->dev, VCHIQ_ARM, - "header %pK: msgbufsize %x < msglen %x", - header, args->msgbufsize, msglen); - WARN(1, "invalid message size\n"); + dev_err(service->state->dev, + "arm: header %pK: msgbufsize %x < msglen %x\n", + header, args->msgbufsize, msglen); + WARN(1, "invalid message size\n"); if (ret == 0) ret = -EMSGSIZE; break; @@ -582,10 +579,9 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) long ret = 0; int i, rc; - vchiq_log_trace(instance->state->dev, VCHIQ_ARM, - "%s - instance %pK, cmd %s, arg %lx", __func__, instance, - ((_IOC_TYPE(cmd) == VCHIQ_IOC_MAGIC) && (_IOC_NR(cmd) <= VCHIQ_IOC_MAX)) ? - ioctl_names[_IOC_NR(cmd)] : "<invalid>", arg); + dev_dbg(instance->state->dev, "arm: instance %pK, cmd %s, arg %lx\n", instance, + ((_IOC_TYPE(cmd) == VCHIQ_IOC_MAGIC) && (_IOC_NR(cmd) <= VCHIQ_IOC_MAX)) ? + ioctl_names[_IOC_NR(cmd)] : "<invalid>", arg); switch (cmd) { case VCHIQ_IOC_SHUTDOWN: @@ -618,9 +614,9 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } rc = mutex_lock_killable(&instance->state->mutex); if (rc) { - vchiq_log_error(instance->state->dev, VCHIQ_ARM, - "vchiq: connect: could not lock mutex for state %d: %d", - instance->state->id, rc); + dev_err(instance->state->dev, + "arm: vchiq: connect: could not lock mutex for state %d: %d\n", + instance->state->id, rc); ret = -EINTR; break; } @@ -630,8 +626,8 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (!status) instance->connected = 1; else - vchiq_log_error(instance->state->dev, VCHIQ_ARM, - "vchiq: could not connect: %d", status); + dev_err(instance->state->dev, + "arm: vchiq: could not connect: %d\n", status); break; case VCHIQ_IOC_CREATE_SERVICE: { @@ -700,13 +696,13 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) vchiq_use_service_internal(service) : vchiq_release_service_internal(service); if (ret) { - vchiq_log_error(instance->state->dev, VCHIQ_SUSPEND, - "%s: cmd %s returned error %ld for service %p4cc:%03d", - __func__, (cmd == VCHIQ_IOC_USE_SERVICE) ? - "VCHIQ_IOC_USE_SERVICE" : - "VCHIQ_IOC_RELEASE_SERVICE", - ret, &service->base.fourcc, - service->client_id); + dev_err(instance->state->dev, + "suspend: cmd %s returned error %ld for service %p4cc:%03d\n", + (cmd == VCHIQ_IOC_USE_SERVICE) ? + "VCHIQ_IOC_USE_SERVICE" : + "VCHIQ_IOC_RELEASE_SERVICE", + ret, &service->base.fourcc, + service->client_id); } } else { ret = -EINVAL; @@ -868,15 +864,15 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } if (!status && (ret < 0) && (ret != -EINTR) && (ret != -EWOULDBLOCK)) { - vchiq_log_debug(instance->state->dev, VCHIQ_ARM, - " ioctl instance %pK, cmd %s -> status %d, %ld", - instance, (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? - ioctl_names[_IOC_NR(cmd)] : "<invalid>", status, ret); + dev_dbg(instance->state->dev, + "arm: ioctl instance %pK, cmd %s -> status %d, %ld\n", + instance, (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? + ioctl_names[_IOC_NR(cmd)] : "<invalid>", status, ret); } else { - vchiq_log_trace(instance->state->dev, VCHIQ_ARM, - " ioctl instance %pK, cmd %s -> status %d, %ld", - instance, (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? - ioctl_names[_IOC_NR(cmd)] : "<invalid>", status, ret); + dev_dbg(instance->state->dev, + "arm: ioctl instance %pK, cmd %s -> status %d\n, %ld\n", + instance, (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? + ioctl_names[_IOC_NR(cmd)] : "<invalid>", status, ret); } return ret; @@ -1170,11 +1166,10 @@ static int vchiq_open(struct inode *inode, struct file *file) struct vchiq_state *state = vchiq_get_state(); struct vchiq_instance *instance; - vchiq_log_debug(state->dev, VCHIQ_ARM, "vchiq_open"); + dev_dbg(state->dev, "arm: vchiq open\n"); if (!state) { - vchiq_log_error(state->dev, VCHIQ_ARM, - "vchiq has no connection to VideoCore"); + dev_err(state->dev, "arm: vchiq has no connection to VideoCore\n"); return -ENOTCONN; } @@ -1206,8 +1201,7 @@ static int vchiq_release(struct inode *inode, struct file *file) int ret = 0; int i; - vchiq_log_debug(state->dev, VCHIQ_ARM, "%s: instance=%lx", __func__, - (unsigned long)instance); + dev_dbg(state->dev, "arm: instance=%p\n", instance); if (!state) { ret = -EPERM; @@ -1306,26 +1300,6 @@ out: return ret; } -static ssize_t -vchiq_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - struct dump_context context; - int err; - - context.buf = buf; - context.actual = 0; - context.space = count; - context.offset = *ppos; - - err = vchiq_dump_state(&context, &g_state); - if (err) - return err; - - *ppos += context.actual; - - return context.actual; -} - static const struct file_operations vchiq_fops = { .owner = THIS_MODULE, @@ -1335,7 +1309,6 @@ vchiq_fops = { #endif .open = vchiq_open, .release = vchiq_release, - .read = vchiq_read }; static struct miscdevice vchiq_miscdev = { diff --git a/drivers/staging/vme_user/Kconfig b/drivers/staging/vme_user/Kconfig index d65cc5510649..8e5df6ce36e8 100644 --- a/drivers/staging/vme_user/Kconfig +++ b/drivers/staging/vme_user/Kconfig @@ -3,18 +3,32 @@ menuconfig VME_BUS bool "VME bridge support" depends on PCI help - If you say Y here you get support for the VME bridge Framework. + Enable support for VME (VersaModular Eurocard bus) bridge modules. + The bridge allows connecting VME devices to systems with existing + interfaces (like USB or PCI) by means of translating VME protocol + operations. + + Note that this only enables the bridge framework. You'll also + likely want to enable driver for specific bridge device you have + to actually use it. If unsure, say N. if VME_BUS comment "VME Bridge Drivers" config VME_TSI148 - tristate "Tempe" + tristate "Tundra TSI148 VME bridge support" depends on HAS_DMA help - If you say Y here you get support for the Tundra TSI148 VME bridge - chip. + If you say Y here you get support for the Tundra TSI148 VME-to-PCI/X + bridge chip (and pin-compatible clones). + + TSI148 is a high-performant, 2eSST and VME64-compliant VME-to-PCI/X + interconnect bridge with support for PCI and PCI-X bus interface. + It is primarily used in industrial and embedded systems. + + To compile this driver as a module, say M - the module will be + called vme_tsi148. If unsure, say N. config VME_FAKE tristate "Fake" diff --git a/drivers/staging/vme_user/vme.c b/drivers/staging/vme_user/vme.c index 5c416c31ec57..e9461a7a7ab8 100644 --- a/drivers/staging/vme_user/vme.c +++ b/drivers/staging/vme_user/vme.c @@ -340,8 +340,8 @@ int vme_slave_set(struct vme_resource *resource, int enabled, image = list_entry(resource->entry, struct vme_slave_resource, list); if (!bridge->slave_set) { - dev_err(bridge->parent, "Function not supported\n"); - return -ENOSYS; + dev_err(bridge->parent, "%s not supported\n", __func__); + return -EINVAL; } if (!(((image->address_attr & aspace) == aspace) && diff --git a/drivers/staging/vt6655/card.c b/drivers/staging/vt6655/card.c index 350ab8f3778a..36183f2a64c1 100644 --- a/drivers/staging/vt6655/card.c +++ b/drivers/staging/vt6655/card.c @@ -10,12 +10,12 @@ * CARDvUpdateBasicTopRate - Update BasicTopRate * CARDbAddBasicRate - Add to BasicRateSet * CARDbIsOFDMinBasicRate - Check if any OFDM rate is in BasicRateSet - * CARDqGetTSFOffset - Calculate TSFOffset + * card_get_tsf_offset - Calculate TSFOffset * vt6655_get_current_tsf - Read Current NIC TSF counter - * CARDqGetNextTBTT - Calculate Next Beacon TSF counter + * card_get_next_tbtt - Calculate Next Beacon TSF counter * CARDvSetFirstNextTBTT - Set NIC Beacon time * CARDvUpdateNextTBTT - Sync. NIC Beacon time - * CARDbRadioPowerOff - Turn Off NIC Radio Power + * card_radio_power_off - Turn Off NIC Radio Power * * Revision History: * 06-10-2003 Bryan YC Fan: Re-write codes to support VT3253 spec. @@ -280,7 +280,7 @@ bool card_set_phy_parameter(struct vnt_private *priv, u8 bb_type) * In: * priv - The adapter to be sync. * rx_rate - data rate of receive beacon - * qwBSSTimestamp - Rx BCN's TSF + * bss_timestamp - Rx BCN's TSF * qwLocalTSF - Local TSF * Out: * none @@ -288,20 +288,20 @@ bool card_set_phy_parameter(struct vnt_private *priv, u8 bb_type) * Return Value: none */ bool card_update_tsf(struct vnt_private *priv, unsigned char rx_rate, - u64 qwBSSTimestamp) + u64 bss_timestamp) { u64 local_tsf; - u64 qwTSFOffset = 0; + u64 tsf_offset = 0; local_tsf = vt6655_get_current_tsf(priv); - if (qwBSSTimestamp != local_tsf) { - qwTSFOffset = CARDqGetTSFOffset(rx_rate, qwBSSTimestamp, + if (bss_timestamp != local_tsf) { + tsf_offset = card_get_tsf_offset(rx_rate, bss_timestamp, local_tsf); /* adjust TSF, HW's TSF add TSF Offset reg */ - qwTSFOffset = le64_to_cpu(qwTSFOffset); - iowrite32((u32)qwTSFOffset, priv->port_offset + MAC_REG_TSFOFST); - iowrite32((u32)(qwTSFOffset >> 32), priv->port_offset + MAC_REG_TSFOFST + 4); + tsf_offset = le64_to_cpu(tsf_offset); + iowrite32((u32)tsf_offset, priv->port_offset + MAC_REG_TSFOFST); + iowrite32((u32)(tsf_offset >> 32), priv->port_offset + MAC_REG_TSFOFST + 4); vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_TSFSYNCEN); } return true; @@ -314,28 +314,28 @@ bool card_update_tsf(struct vnt_private *priv, unsigned char rx_rate, * Parameters: * In: * priv - The adapter to be set. - * wBeaconInterval - Beacon Interval + * beacon_interval - Beacon Interval * Out: * none * * Return Value: true if succeed; otherwise false */ -bool CARDbSetBeaconPeriod(struct vnt_private *priv, - unsigned short wBeaconInterval) +bool card_set_beacon_period(struct vnt_private *priv, + unsigned short beacon_interval) { - u64 qwNextTBTT; + u64 next_tbtt; - qwNextTBTT = vt6655_get_current_tsf(priv); /* Get Local TSF counter */ + next_tbtt = vt6655_get_current_tsf(priv); /* Get Local TSF counter */ - qwNextTBTT = CARDqGetNextTBTT(qwNextTBTT, wBeaconInterval); + next_tbtt = card_get_next_tbtt(next_tbtt, beacon_interval); /* set HW beacon interval */ - iowrite16(wBeaconInterval, priv->port_offset + MAC_REG_BI); - priv->wBeaconInterval = wBeaconInterval; + iowrite16(beacon_interval, priv->port_offset + MAC_REG_BI); + priv->beacon_interval = beacon_interval; /* Set NextTBTT */ - qwNextTBTT = le64_to_cpu(qwNextTBTT); - iowrite32((u32)qwNextTBTT, priv->port_offset + MAC_REG_NEXTTBTT); - iowrite32((u32)(qwNextTBTT >> 32), priv->port_offset + MAC_REG_NEXTTBTT + 4); + next_tbtt = le64_to_cpu(next_tbtt); + iowrite32((u32)next_tbtt, priv->port_offset + MAC_REG_NEXTTBTT); + iowrite32((u32)(next_tbtt >> 32), priv->port_offset + MAC_REG_NEXTTBTT + 4); vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN); return true; @@ -351,7 +351,7 @@ bool CARDbSetBeaconPeriod(struct vnt_private *priv, * none * */ -void CARDbRadioPowerOff(struct vnt_private *priv) +void card_radio_power_off(struct vnt_private *priv) { if (priv->radio_off) return; @@ -382,29 +382,29 @@ void CARDbRadioPowerOff(struct vnt_private *priv) vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_GPIOCTL0, LED_ACTSET); /* LED issue */ } -void CARDvSafeResetTx(struct vnt_private *priv) +void card_safe_reset_tx(struct vnt_private *priv) { unsigned int uu; - struct vnt_tx_desc *pCurrTD; + struct vnt_tx_desc *curr_td; /* initialize TD index */ - priv->apTailTD[0] = &priv->apTD0Rings[0]; + priv->tail_td[0] = &priv->apTD0Rings[0]; priv->apCurrTD[0] = &priv->apTD0Rings[0]; - priv->apTailTD[1] = &priv->apTD1Rings[0]; + priv->tail_td[1] = &priv->apTD1Rings[0]; priv->apCurrTD[1] = &priv->apTD1Rings[0]; for (uu = 0; uu < TYPE_MAXTD; uu++) priv->iTDUsed[uu] = 0; for (uu = 0; uu < priv->opts.tx_descs[0]; uu++) { - pCurrTD = &priv->apTD0Rings[uu]; - pCurrTD->td0.owner = OWNED_BY_HOST; + curr_td = &priv->apTD0Rings[uu]; + curr_td->td0.owner = OWNED_BY_HOST; /* init all Tx Packet pointer to NULL */ } for (uu = 0; uu < priv->opts.tx_descs[1]; uu++) { - pCurrTD = &priv->apTD1Rings[uu]; - pCurrTD->td0.owner = OWNED_BY_HOST; + curr_td = &priv->apTD1Rings[uu]; + curr_td->td0.owner = OWNED_BY_HOST; /* init all Tx Packet pointer to NULL */ } @@ -708,7 +708,7 @@ unsigned char card_get_pkt_type(struct vnt_private *priv) * * Return Value: TSF Offset value */ -u64 CARDqGetTSFOffset(unsigned char rx_rate, u64 qwTSF1, u64 qwTSF2) +u64 card_get_tsf_offset(unsigned char rx_rate, u64 qwTSF1, u64 qwTSF2) { unsigned short wRxBcnTSFOffst; @@ -764,11 +764,11 @@ u64 vt6655_get_current_tsf(struct vnt_private *priv) * * Return Value: TSF value of next Beacon */ -u64 CARDqGetNextTBTT(u64 qwTSF, unsigned short wBeaconInterval) +u64 card_get_next_tbtt(u64 qwTSF, unsigned short beacon_interval) { u32 beacon_int; - beacon_int = wBeaconInterval * 1024; + beacon_int = beacon_interval * 1024; if (beacon_int) { do_div(qwTSF, beacon_int); qwTSF += 1; @@ -785,25 +785,25 @@ u64 CARDqGetNextTBTT(u64 qwTSF, unsigned short wBeaconInterval) * Parameters: * In: * iobase - IO Base - * wBeaconInterval - Beacon Interval + * beacon_interval - Beacon Interval * Out: * none * * Return Value: none */ void CARDvSetFirstNextTBTT(struct vnt_private *priv, - unsigned short wBeaconInterval) + unsigned short beacon_interval) { void __iomem *iobase = priv->port_offset; - u64 qwNextTBTT; + u64 next_tbtt; - qwNextTBTT = vt6655_get_current_tsf(priv); /* Get Local TSF counter */ + next_tbtt = vt6655_get_current_tsf(priv); /* Get Local TSF counter */ - qwNextTBTT = CARDqGetNextTBTT(qwNextTBTT, wBeaconInterval); + next_tbtt = card_get_next_tbtt(next_tbtt, beacon_interval); /* Set NextTBTT */ - qwNextTBTT = le64_to_cpu(qwNextTBTT); - iowrite32((u32)qwNextTBTT, iobase + MAC_REG_NEXTTBTT); - iowrite32((u32)(qwNextTBTT >> 32), iobase + MAC_REG_NEXTTBTT + 4); + next_tbtt = le64_to_cpu(next_tbtt); + iowrite32((u32)next_tbtt, iobase + MAC_REG_NEXTTBTT); + iowrite32((u32)(next_tbtt >> 32), iobase + MAC_REG_NEXTTBTT + 4); vt6655_mac_reg_bits_on(iobase, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN); } @@ -815,18 +815,18 @@ void CARDvSetFirstNextTBTT(struct vnt_private *priv, * In: * priv - The adapter to be set * qwTSF - Current TSF counter - * wBeaconInterval - Beacon Interval + * beacon_interval - Beacon Interval * Out: * none * * Return Value: none */ void CARDvUpdateNextTBTT(struct vnt_private *priv, u64 qwTSF, - unsigned short wBeaconInterval) + unsigned short beacon_interval) { void __iomem *iobase = priv->port_offset; - qwTSF = CARDqGetNextTBTT(qwTSF, wBeaconInterval); + qwTSF = card_get_next_tbtt(qwTSF, beacon_interval); /* Set NextTBTT */ qwTSF = le64_to_cpu(qwTSF); iowrite32((u32)qwTSF, iobase + MAC_REG_NEXTTBTT); diff --git a/drivers/staging/vt6655/card.h b/drivers/staging/vt6655/card.h index 19689a291f5b..f52e42564e81 100644 --- a/drivers/staging/vt6655/card.h +++ b/drivers/staging/vt6655/card.h @@ -43,20 +43,20 @@ void card_set_rspinf(struct vnt_private *priv, u8 bb_type); void CARDvUpdateBasicTopRate(struct vnt_private *priv); bool CARDbIsOFDMinBasicRate(struct vnt_private *priv); void CARDvSetFirstNextTBTT(struct vnt_private *priv, - unsigned short wBeaconInterval); + unsigned short beacon_interval); void CARDvUpdateNextTBTT(struct vnt_private *priv, u64 qwTSF, - unsigned short wBeaconInterval); + unsigned short beacon_interval); u64 vt6655_get_current_tsf(struct vnt_private *priv); -u64 CARDqGetNextTBTT(u64 qwTSF, unsigned short wBeaconInterval); -u64 CARDqGetTSFOffset(unsigned char rx_rate, u64 qwTSF1, u64 qwTSF2); +u64 card_get_next_tbtt(u64 qwTSF, unsigned short beacon_interval); +u64 card_get_tsf_offset(unsigned char rx_rate, u64 qwTSF1, u64 qwTSF2); unsigned char card_get_pkt_type(struct vnt_private *priv); -void CARDvSafeResetTx(struct vnt_private *priv); +void card_safe_reset_tx(struct vnt_private *priv); void CARDvSafeResetRx(struct vnt_private *priv); -void CARDbRadioPowerOff(struct vnt_private *priv); +void card_radio_power_off(struct vnt_private *priv); bool card_set_phy_parameter(struct vnt_private *priv, u8 bb_type); bool card_update_tsf(struct vnt_private *priv, unsigned char rx_rate, - u64 qwBSSTimestamp); -bool CARDbSetBeaconPeriod(struct vnt_private *priv, - unsigned short wBeaconInterval); + u64 bss_timestamp); +bool card_set_beacon_period(struct vnt_private *priv, + unsigned short beacon_interval); #endif /* __CARD_H__ */ diff --git a/drivers/staging/vt6655/device.h b/drivers/staging/vt6655/device.h index d9ee0b7401fe..0212240ba23f 100644 --- a/drivers/staging/vt6655/device.h +++ b/drivers/staging/vt6655/device.h @@ -133,7 +133,7 @@ struct vnt_private { volatile int iTDUsed[TYPE_MAXTD]; struct vnt_tx_desc *apCurrTD[TYPE_MAXTD]; - struct vnt_tx_desc *apTailTD[TYPE_MAXTD]; + struct vnt_tx_desc *tail_td[TYPE_MAXTD]; struct vnt_tx_desc *apTD0Rings; struct vnt_tx_desc *apTD1Rings; @@ -281,7 +281,7 @@ struct vnt_private { unsigned char abyEEPROM[EEP_MAX_CONTEXT_SIZE]; /* unsigned long alignment */ - unsigned short wBeaconInterval; + unsigned short beacon_interval; u16 wake_up_count; struct work_struct interrupt_work; diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index 7d297526e653..b0b262de6480 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -454,7 +454,7 @@ static void device_init_registers(struct vnt_private *priv) } if (priv->hw_radio_off || priv->bRadioControlOff) - CARDbRadioPowerOff(priv); + card_radio_power_off(priv); /* get Permanent network address */ SROMvReadEtherAddress(priv->port_offset, priv->abyCurrentNetAddr); @@ -463,7 +463,7 @@ static void device_init_registers(struct vnt_private *priv) /* reset Tx pointer */ CARDvSafeResetRx(priv); /* reset Rx pointer */ - CARDvSafeResetTx(priv); + card_safe_reset_tx(priv); if (priv->local_id <= REV_ID_VT3253_A1) vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_RCR, RCR_WPAERR); @@ -737,7 +737,7 @@ static int device_init_td0_ring(struct vnt_private *priv) if (i > 0) priv->apTD0Rings[i - 1].next_desc = cpu_to_le32(priv->td0_pool_dma); - priv->apTailTD[0] = priv->apCurrTD[0] = &priv->apTD0Rings[0]; + priv->tail_td[0] = priv->apCurrTD[0] = &priv->apTD0Rings[0]; return 0; @@ -777,7 +777,7 @@ static int device_init_td1_ring(struct vnt_private *priv) if (i > 0) priv->apTD1Rings[i - 1].next_desc = cpu_to_le32(priv->td1_pool_dma); - priv->apTailTD[1] = priv->apCurrTD[1] = &priv->apTD1Rings[0]; + priv->tail_td[1] = priv->apCurrTD[1] = &priv->apTD1Rings[0]; return 0; @@ -969,7 +969,7 @@ static int device_tx_srv(struct vnt_private *priv, unsigned int idx) unsigned char byTsr0; unsigned char byTsr1; - for (desc = priv->apTailTD[idx]; priv->iTDUsed[idx] > 0; desc = desc->next) { + for (desc = priv->tail_td[idx]; priv->iTDUsed[idx] > 0; desc = desc->next) { if (desc->td0.owner == OWNED_BY_NIC) break; if (works++ > 15) @@ -1007,7 +1007,7 @@ static int device_tx_srv(struct vnt_private *priv, unsigned int idx) } } - priv->apTailTD[idx] = desc; + priv->tail_td[idx] = desc; return works; } @@ -1349,7 +1349,7 @@ static void vnt_stop(struct ieee80211_hw *hw) MACbShutdown(priv); MACbSoftwareReset(priv); - CARDbRadioPowerOff(priv); + card_radio_power_off(priv); device_free_td0_ring(priv); device_free_td1_ring(priv); @@ -1537,7 +1537,7 @@ static void vnt_bss_info_changed(struct ieee80211_hw *hw, card_update_tsf(priv, conf->beacon_rate->hw_value, conf->sync_tsf); - CARDbSetBeaconPeriod(priv, conf->beacon_int); + card_set_beacon_period(priv, conf->beacon_int); CARDvSetFirstNextTBTT(priv, conf->beacon_int); } else { @@ -1712,7 +1712,7 @@ static int vnt_init(struct vnt_private *priv) priv->mac_hw = true; - CARDbRadioPowerOff(priv); + card_radio_power_off(priv); return 0; } diff --git a/drivers/staging/vt6655/rxtx.c b/drivers/staging/vt6655/rxtx.c index 522d34ca9b0f..5e5ed582c35e 100644 --- a/drivers/staging/vt6655/rxtx.c +++ b/drivers/staging/vt6655/rxtx.c @@ -1456,7 +1456,7 @@ int vnt_beacon_enable(struct vnt_private *priv, struct ieee80211_vif *vif, CARDvSetFirstNextTBTT(priv, conf->beacon_int); - CARDbSetBeaconPeriod(priv, conf->beacon_int); + card_set_beacon_period(priv, conf->beacon_int); return vnt_beacon_make(priv, vif); } diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 506193e870c4..7a85e6477e46 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -147,7 +147,6 @@ int transport_lookup_tmr_lun(struct se_cmd *se_cmd) struct se_session *se_sess = se_cmd->se_sess; struct se_node_acl *nacl = se_sess->se_node_acl; struct se_tmr_req *se_tmr = se_cmd->se_tmr_req; - unsigned long flags; rcu_read_lock(); deve = target_nacl_find_deve(nacl, se_cmd->orig_fe_lun); @@ -178,10 +177,6 @@ out_unlock: se_cmd->se_dev = rcu_dereference_raw(se_lun->lun_se_dev); se_tmr->tmr_dev = rcu_dereference_raw(se_lun->lun_se_dev); - spin_lock_irqsave(&se_tmr->tmr_dev->se_tmr_lock, flags); - list_add_tail(&se_tmr->tmr_list, &se_tmr->tmr_dev->dev_tmr_list); - spin_unlock_irqrestore(&se_tmr->tmr_dev->se_tmr_lock, flags); - return 0; } EXPORT_SYMBOL(transport_lookup_tmr_lun); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 670cfb7bd426..73d0d6133ac8 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -3629,6 +3629,10 @@ int transport_generic_handle_tmr( unsigned long flags; bool aborted = false; + spin_lock_irqsave(&cmd->se_dev->se_tmr_lock, flags); + list_add_tail(&cmd->se_tmr_req->tmr_list, &cmd->se_dev->dev_tmr_list); + spin_unlock_irqrestore(&cmd->se_dev->se_tmr_lock, flags); + spin_lock_irqsave(&cmd->t_state_lock, flags); if (cmd->transport_state & CMD_T_ABORTED) { aborted = true; diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 59883502eff4..17a8ae5e991d 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -33,6 +33,13 @@ config THERMAL_STATISTICS If in doubt, say N. +config THERMAL_DEBUGFS + bool "Thermal subsystem debug support" + depends on DEBUG_FS + help + Say Y to allow the thermal subsystem to collect diagnostic + information that can be accessed via debugfs. + config THERMAL_EMERGENCY_POWEROFF_DELAY_MS int "Emergency poweroff delay in milli-seconds" default 0 diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index a8318d671036..d77d7fe99a84 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -10,6 +10,8 @@ thermal_sys-y += thermal_trip.o thermal_helpers.o # netlink interface to manage the thermal framework thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o +thermal_sys-$(CONFIG_THERMAL_DEBUGFS) += thermal_debugfs.o + # interface to/from other layers providing sensors thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o thermal_sys-$(CONFIG_THERMAL_OF) += thermal_of.o diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index 7b6aa265ff6a..81e061f183ad 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -762,7 +762,7 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, trip = params->trip_switch_on; if (trip && tz->temperature < trip->temperature) { - update = tz->last_temperature >= trip->temperature; + update = tz->passive; tz->passive = 0; reset_pid_controller(params); allow_maximum_power(tz, update); diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index 22445403b520..3b04c6ec4fca 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -35,7 +35,9 @@ #include <linux/processor.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/suspend.h> #include <linux/string.h> +#include <linux/syscore_ops.h> #include <linux/topology.h> #include <linux/workqueue.h> @@ -571,6 +573,30 @@ static __init int hfi_parse_features(void) return 0; } +static void hfi_do_enable(void) +{ + /* This code runs only on the boot CPU. */ + struct hfi_cpu_info *info = &per_cpu(hfi_cpu_info, 0); + struct hfi_instance *hfi_instance = info->hfi_instance; + + /* No locking needed. There is no concurrency with CPU online. */ + hfi_set_hw_table(hfi_instance); + hfi_enable(); +} + +static int hfi_do_disable(void) +{ + /* No locking needed. There is no concurrency with CPU offline. */ + hfi_disable(); + + return 0; +} + +static struct syscore_ops hfi_pm_ops = { + .resume = hfi_do_enable, + .suspend = hfi_do_disable, +}; + void __init intel_hfi_init(void) { struct hfi_instance *hfi_instance; @@ -602,6 +628,8 @@ void __init intel_hfi_init(void) if (!hfi_updates_wq) goto err_nomem; + register_syscore_ops(&hfi_pm_ops); + return; err_nomem: diff --git a/drivers/thermal/intel/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c index 5ac5cb60bae6..bc6eb0dd66a4 100644 --- a/drivers/thermal/intel/intel_powerclamp.c +++ b/drivers/thermal/intel/intel_powerclamp.c @@ -49,7 +49,6 @@ */ #define DEFAULT_DURATION_JIFFIES (6) -static unsigned int target_mwait; static struct dentry *debug_dir; static bool poll_pkg_cstate_enable; @@ -312,34 +311,6 @@ MODULE_PARM_DESC(window_size, "sliding window in number of clamping cycles\n" "\twindow size results in slower response time but more smooth\n" "\tclamping results. default to 2."); -static void find_target_mwait(void) -{ - unsigned int eax, ebx, ecx, edx; - unsigned int highest_cstate = 0; - unsigned int highest_subcstate = 0; - int i; - - if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF) - return; - - cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx); - - if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || - !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) - return; - - edx >>= MWAIT_SUBSTATE_SIZE; - for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) { - if (edx & MWAIT_SUBSTATE_MASK) { - highest_cstate = i; - highest_subcstate = edx & MWAIT_SUBSTATE_MASK; - } - } - target_mwait = (highest_cstate << MWAIT_SUBSTATE_SIZE) | - (highest_subcstate - 1); - -} - struct pkg_cstate_info { bool skip; int msr_index; @@ -759,9 +730,6 @@ static int __init powerclamp_probe(void) return -ENODEV; } - /* find the deepest mwait value */ - find_target_mwait(); - return 0; } diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c index 99ca0c7bc41c..0f475fe46bc9 100644 --- a/drivers/thermal/loongson2_thermal.c +++ b/drivers/thermal/loongson2_thermal.c @@ -8,9 +8,10 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/minmax.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/thermal.h> #include <linux/units.h> #include "thermal_hwmon.h" diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index fa88d8707241..dfaa6341694a 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -211,7 +211,7 @@ exit: mutex_unlock(&tz->lock); mutex_unlock(&thermal_governor_lock); - thermal_notify_tz_gov_change(tz->id, policy); + thermal_notify_tz_gov_change(tz, policy); return ret; } @@ -381,9 +381,8 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, * the threshold and the trip temperature will be equal. */ if (tz->temperature >= trip->temperature) { - thermal_notify_tz_trip_up(tz->id, - thermal_zone_trip_id(tz, trip), - tz->temperature); + thermal_notify_tz_trip_up(tz, trip); + thermal_debug_tz_trip_up(tz, trip); trip->threshold = trip->temperature - trip->hysteresis; } else { trip->threshold = trip->temperature; @@ -400,9 +399,8 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, * the trip. */ if (tz->temperature < trip->temperature - trip->hysteresis) { - thermal_notify_tz_trip_down(tz->id, - thermal_zone_trip_id(tz, trip), - tz->temperature); + thermal_notify_tz_trip_down(tz, trip); + thermal_debug_tz_trip_down(tz, trip); trip->threshold = trip->temperature; } else { trip->threshold = trip->temperature - trip->hysteresis; @@ -434,6 +432,7 @@ static void update_temperature(struct thermal_zone_device *tz) trace_thermal_temperature(tz); thermal_genl_sampling_temp(tz->id, temp); + thermal_debug_update_temp(tz); } static void thermal_zone_device_check(struct work_struct *work) @@ -505,9 +504,9 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz, mutex_unlock(&tz->lock); if (mode == THERMAL_DEVICE_ENABLED) - thermal_notify_tz_enable(tz->id); + thermal_notify_tz_enable(tz); else - thermal_notify_tz_disable(tz->id); + thermal_notify_tz_disable(tz); return ret; } @@ -846,7 +845,7 @@ static void thermal_release(struct device *dev) sizeof("cooling_device") - 1)) { cdev = to_cooling_device(dev); thermal_cooling_device_destroy_sysfs(cdev); - kfree(cdev->type); + kfree_const(cdev->type); ida_free(&thermal_cdev_ida, cdev->id); kfree(cdev); } @@ -918,7 +917,7 @@ __thermal_cooling_device_register(struct device_node *np, cdev->id = ret; id = ret; - cdev->type = kstrdup(type ? type : "", GFP_KERNEL); + cdev->type = kstrdup_const(type ? type : "", GFP_KERNEL); if (!cdev->type) { ret = -ENOMEM; goto out_ida_remove; @@ -964,12 +963,14 @@ __thermal_cooling_device_register(struct device_node *np, mutex_unlock(&thermal_list_lock); + thermal_debug_cdev_add(cdev); + return cdev; out_cooling_dev: thermal_cooling_device_destroy_sysfs(cdev); out_cdev_type: - kfree(cdev->type); + kfree_const(cdev->type); out_ida_remove: ida_free(&thermal_cdev_ida, id); out_kfree_cdev: @@ -1170,6 +1171,8 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) if (!cdev) return; + thermal_debug_cdev_remove(cdev); + mutex_lock(&thermal_list_lock); if (!thermal_cooling_device_present(cdev)) { @@ -1411,7 +1414,9 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t if (atomic_cmpxchg(&tz->need_update, 1, 0)) thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); - thermal_notify_tz_create(tz->id, tz->type); + thermal_notify_tz_create(tz); + + thermal_debug_tz_add(tz); return tz; @@ -1470,14 +1475,13 @@ EXPORT_SYMBOL_GPL(thermal_zone_device); */ void thermal_zone_device_unregister(struct thermal_zone_device *tz) { - int tz_id; struct thermal_cooling_device *cdev; struct thermal_zone_device *pos = NULL; if (!tz) return; - tz_id = tz->id; + thermal_debug_tz_remove(tz); mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) @@ -1514,7 +1518,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) put_device(&tz->device); - thermal_notify_tz_delete(tz_id); + thermal_notify_tz_delete(tz); wait_for_completion(&tz->removal); kfree(tz); @@ -1636,6 +1640,8 @@ static int __init thermal_init(void) { int result; + thermal_debug_init(); + result = thermal_netlink_init(); if (result) goto error; diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 4e023d54fd27..e9c099ecdd0f 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -13,6 +13,7 @@ #include <linux/thermal.h> #include "thermal_netlink.h" +#include "thermal_debugfs.h" /* Default Thermal Governor */ #if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE) diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c new file mode 100644 index 000000000000..c617e8b9f0dd --- /dev/null +++ b/drivers/thermal/thermal_debugfs.c @@ -0,0 +1,839 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 Linaro Limited + * + * Author: Daniel Lezcano <daniel.lezcano@linaro.org> + * + * Thermal subsystem debug support + */ +#include <linux/debugfs.h> +#include <linux/ktime.h> +#include <linux/list.h> +#include <linux/minmax.h> +#include <linux/mutex.h> +#include <linux/thermal.h> + +#include "thermal_core.h" + +static struct dentry *d_root; +static struct dentry *d_cdev; +static struct dentry *d_tz; + +/* + * Length of the string containing the thermal zone id or the cooling + * device id, including the ending nul character. We can reasonably + * assume there won't be more than 256 thermal zones as the maximum + * observed today is around 32. + */ +#define IDSLENGTH 4 + +/* + * The cooling device transition list is stored in a hash table where + * the size is CDEVSTATS_HASH_SIZE. The majority of cooling devices + * have dozen of states but some can have much more, so a hash table + * is more adequate in this case, because the cost of browsing the entire + * list when storing the transitions may not be negligible. + */ +#define CDEVSTATS_HASH_SIZE 16 + +/** + * struct cdev_debugfs - per cooling device statistics structure + * A cooling device can have a high number of states. Showing the + * transitions on a matrix based representation can be overkill given + * most of the transitions won't happen and we end up with a matrix + * filled with zero. Instead, we show the transitions which actually + * happened. + * + * Every transition updates the current_state and the timestamp. The + * transitions and the durations are stored in lists. + * + * @total: the number of transitions for this cooling device + * @current_state: the current cooling device state + * @timestamp: the state change timestamp + * @transitions: an array of lists containing the state transitions + * @durations: an array of lists containing the residencies of each state + */ +struct cdev_debugfs { + u32 total; + int current_state; + ktime_t timestamp; + struct list_head transitions[CDEVSTATS_HASH_SIZE]; + struct list_head durations[CDEVSTATS_HASH_SIZE]; +}; + +/** + * struct cdev_record - Common structure for cooling device entry + * + * The following common structure allows to store the information + * related to the transitions and to the state residencies. They are + * identified with a id which is associated to a value. It is used as + * nodes for the "transitions" and "durations" above. + * + * @node: node to insert the structure in a list + * @id: identifier of the value which can be a state or a transition + * @residency: a ktime_t representing a state residency duration + * @count: a number of occurrences + */ +struct cdev_record { + struct list_head node; + int id; + union { + ktime_t residency; + u64 count; + }; +}; + +/** + * struct trip_stats - Thermal trip statistics + * + * The trip_stats structure has the relevant information to show the + * statistics related to temperature going above a trip point. + * + * @timestamp: the trip crossing timestamp + * @duration: total time when the zone temperature was above the trip point + * @count: the number of times the zone temperature was above the trip point + * @max: maximum recorded temperature above the trip point + * @min: minimum recorded temperature above the trip point + * @avg: average temperature above the trip point + */ +struct trip_stats { + ktime_t timestamp; + ktime_t duration; + int count; + int max; + int min; + int avg; +}; + +/** + * struct tz_episode - A mitigation episode information + * + * The tz_episode structure describes a mitigation episode. A + * mitigation episode begins the trip point with the lower temperature + * is crossed the way up and ends when it is crossed the way + * down. During this episode we can have multiple trip points crossed + * the way up and down if there are multiple trip described in the + * firmware after the lowest temperature trip point. + * + * @timestamp: first trip point crossed the way up + * @duration: total duration of the mitigation episode + * @node: a list element to be added to the list of tz events + * @trip_stats: per trip point statistics, flexible array + */ +struct tz_episode { + ktime_t timestamp; + ktime_t duration; + struct list_head node; + struct trip_stats trip_stats[]; +}; + +/** + * struct tz_debugfs - Store all mitigation episodes for a thermal zone + * + * The tz_debugfs structure contains the list of the mitigation + * episodes and has to track which trip point has been crossed in + * order to handle correctly nested trip point mitigation episodes. + * + * We keep the history of the trip point crossed in an array and as we + * can go back and forth inside this history, eg. trip 0,1,2,1,2,1,0, + * we keep track of the current position in the history array. + * + * @tz_episodes: a list of thermal mitigation episodes + * @trips_crossed: an array of trip points crossed by id + * @nr_trips: the number of trip points currently being crossed + */ +struct tz_debugfs { + struct list_head tz_episodes; + int *trips_crossed; + int nr_trips; +}; + +/** + * struct thermal_debugfs - High level structure for a thermal object in debugfs + * + * The thermal_debugfs structure is the common structure used by the + * cooling device or the thermal zone to store the statistics. + * + * @d_top: top directory of the thermal object directory + * @lock: per object lock to protect the internals + * + * @cdev_dbg: a cooling device debug structure + * @tz_dbg: a thermal zone debug structure + */ +struct thermal_debugfs { + struct dentry *d_top; + struct mutex lock; + union { + struct cdev_debugfs cdev_dbg; + struct tz_debugfs tz_dbg; + }; +}; + +void thermal_debug_init(void) +{ + d_root = debugfs_create_dir("thermal", NULL); + if (!d_root) + return; + + d_cdev = debugfs_create_dir("cooling_devices", d_root); + if (!d_cdev) + return; + + d_tz = debugfs_create_dir("thermal_zones", d_root); +} + +static struct thermal_debugfs *thermal_debugfs_add_id(struct dentry *d, int id) +{ + struct thermal_debugfs *thermal_dbg; + char ids[IDSLENGTH]; + + thermal_dbg = kzalloc(sizeof(*thermal_dbg), GFP_KERNEL); + if (!thermal_dbg) + return NULL; + + mutex_init(&thermal_dbg->lock); + + snprintf(ids, IDSLENGTH, "%d", id); + + thermal_dbg->d_top = debugfs_create_dir(ids, d); + if (!thermal_dbg->d_top) { + kfree(thermal_dbg); + return NULL; + } + + return thermal_dbg; +} + +static void thermal_debugfs_remove_id(struct thermal_debugfs *thermal_dbg) +{ + if (!thermal_dbg) + return; + + debugfs_remove(thermal_dbg->d_top); + + kfree(thermal_dbg); +} + +static struct cdev_record * +thermal_debugfs_cdev_record_alloc(struct thermal_debugfs *thermal_dbg, + struct list_head *lists, int id) +{ + struct cdev_record *cdev_record; + + cdev_record = kzalloc(sizeof(*cdev_record), GFP_KERNEL); + if (!cdev_record) + return NULL; + + cdev_record->id = id; + INIT_LIST_HEAD(&cdev_record->node); + list_add_tail(&cdev_record->node, + &lists[cdev_record->id % CDEVSTATS_HASH_SIZE]); + + return cdev_record; +} + +static struct cdev_record * +thermal_debugfs_cdev_record_find(struct thermal_debugfs *thermal_dbg, + struct list_head *lists, int id) +{ + struct cdev_record *entry; + + list_for_each_entry(entry, &lists[id % CDEVSTATS_HASH_SIZE], node) + if (entry->id == id) + return entry; + + return NULL; +} + +static struct cdev_record * +thermal_debugfs_cdev_record_get(struct thermal_debugfs *thermal_dbg, + struct list_head *lists, int id) +{ + struct cdev_record *cdev_record; + + cdev_record = thermal_debugfs_cdev_record_find(thermal_dbg, lists, id); + if (cdev_record) + return cdev_record; + + return thermal_debugfs_cdev_record_alloc(thermal_dbg, lists, id); +} + +static void thermal_debugfs_cdev_clear(struct cdev_debugfs *cdev_dbg) +{ + int i; + struct cdev_record *entry, *tmp; + + for (i = 0; i < CDEVSTATS_HASH_SIZE; i++) { + + list_for_each_entry_safe(entry, tmp, + &cdev_dbg->transitions[i], node) { + list_del(&entry->node); + kfree(entry); + } + + list_for_each_entry_safe(entry, tmp, + &cdev_dbg->durations[i], node) { + list_del(&entry->node); + kfree(entry); + } + } + + cdev_dbg->total = 0; +} + +static void *cdev_seq_start(struct seq_file *s, loff_t *pos) +{ + struct thermal_debugfs *thermal_dbg = s->private; + + mutex_lock(&thermal_dbg->lock); + + return (*pos < CDEVSTATS_HASH_SIZE) ? pos : NULL; +} + +static void *cdev_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + (*pos)++; + + return (*pos < CDEVSTATS_HASH_SIZE) ? pos : NULL; +} + +static void cdev_seq_stop(struct seq_file *s, void *v) +{ + struct thermal_debugfs *thermal_dbg = s->private; + + mutex_unlock(&thermal_dbg->lock); +} + +static int cdev_tt_seq_show(struct seq_file *s, void *v) +{ + struct thermal_debugfs *thermal_dbg = s->private; + struct cdev_debugfs *cdev_dbg = &thermal_dbg->cdev_dbg; + struct list_head *transitions = cdev_dbg->transitions; + struct cdev_record *entry; + int i = *(loff_t *)v; + + if (!i) + seq_puts(s, "Transition\tOccurences\n"); + + list_for_each_entry(entry, &transitions[i], node) { + /* + * Assuming maximum cdev states is 1024, the longer + * string for a transition would be "1024->1024\0" + */ + char buffer[11]; + + snprintf(buffer, ARRAY_SIZE(buffer), "%d->%d", + entry->id >> 16, entry->id & 0xFFFF); + + seq_printf(s, "%-10s\t%-10llu\n", buffer, entry->count); + } + + return 0; +} + +static const struct seq_operations tt_sops = { + .start = cdev_seq_start, + .next = cdev_seq_next, + .stop = cdev_seq_stop, + .show = cdev_tt_seq_show, +}; + +DEFINE_SEQ_ATTRIBUTE(tt); + +static int cdev_dt_seq_show(struct seq_file *s, void *v) +{ + struct thermal_debugfs *thermal_dbg = s->private; + struct cdev_debugfs *cdev_dbg = &thermal_dbg->cdev_dbg; + struct list_head *durations = cdev_dbg->durations; + struct cdev_record *entry; + int i = *(loff_t *)v; + + if (!i) + seq_puts(s, "State\tResidency\n"); + + list_for_each_entry(entry, &durations[i], node) { + s64 duration = ktime_to_ms(entry->residency); + + if (entry->id == cdev_dbg->current_state) + duration += ktime_ms_delta(ktime_get(), + cdev_dbg->timestamp); + + seq_printf(s, "%-5d\t%-10llu\n", entry->id, duration); + } + + return 0; +} + +static const struct seq_operations dt_sops = { + .start = cdev_seq_start, + .next = cdev_seq_next, + .stop = cdev_seq_stop, + .show = cdev_dt_seq_show, +}; + +DEFINE_SEQ_ATTRIBUTE(dt); + +static int cdev_clear_set(void *data, u64 val) +{ + struct thermal_debugfs *thermal_dbg = data; + + if (!val) + return -EINVAL; + + mutex_lock(&thermal_dbg->lock); + + thermal_debugfs_cdev_clear(&thermal_dbg->cdev_dbg); + + mutex_unlock(&thermal_dbg->lock); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(cdev_clear_fops, NULL, cdev_clear_set, "%llu\n"); + +/** + * thermal_debug_cdev_state_update - Update a cooling device state change + * + * Computes a transition and the duration of the previous state residency. + * + * @cdev : a pointer to a cooling device + * @new_state: an integer corresponding to the new cooling device state + */ +void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, + int new_state) +{ + struct thermal_debugfs *thermal_dbg = cdev->debugfs; + struct cdev_debugfs *cdev_dbg; + struct cdev_record *cdev_record; + int transition, old_state; + + if (!thermal_dbg || (thermal_dbg->cdev_dbg.current_state == new_state)) + return; + + mutex_lock(&thermal_dbg->lock); + + cdev_dbg = &thermal_dbg->cdev_dbg; + + old_state = cdev_dbg->current_state; + + /* + * Get the old state information in the durations list. If + * this one does not exist, a new allocated one will be + * returned. Recompute the total duration in the old state and + * get a new timestamp for the new state. + */ + cdev_record = thermal_debugfs_cdev_record_get(thermal_dbg, + cdev_dbg->durations, + old_state); + if (cdev_record) { + ktime_t now = ktime_get(); + ktime_t delta = ktime_sub(now, cdev_dbg->timestamp); + cdev_record->residency = ktime_add(cdev_record->residency, delta); + cdev_dbg->timestamp = now; + } + + cdev_dbg->current_state = new_state; + transition = (old_state << 16) | new_state; + + /* + * Get the transition in the transitions list. If this one + * does not exist, a new allocated one will be returned. + * Increment the occurrence of this transition which is stored + * in the value field. + */ + cdev_record = thermal_debugfs_cdev_record_get(thermal_dbg, + cdev_dbg->transitions, + transition); + if (cdev_record) + cdev_record->count++; + + cdev_dbg->total++; + + mutex_unlock(&thermal_dbg->lock); +} + +/** + * thermal_debug_cdev_add - Add a cooling device debugfs entry + * + * Allocates a cooling device object for debug, initializes the + * statistics and create the entries in sysfs. + * @cdev: a pointer to a cooling device + */ +void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) +{ + struct thermal_debugfs *thermal_dbg; + struct cdev_debugfs *cdev_dbg; + int i; + + thermal_dbg = thermal_debugfs_add_id(d_cdev, cdev->id); + if (!thermal_dbg) + return; + + cdev_dbg = &thermal_dbg->cdev_dbg; + + for (i = 0; i < CDEVSTATS_HASH_SIZE; i++) { + INIT_LIST_HEAD(&cdev_dbg->transitions[i]); + INIT_LIST_HEAD(&cdev_dbg->durations[i]); + } + + cdev_dbg->current_state = 0; + cdev_dbg->timestamp = ktime_get(); + + debugfs_create_file("trans_table", 0400, thermal_dbg->d_top, + thermal_dbg, &tt_fops); + + debugfs_create_file("time_in_state_ms", 0400, thermal_dbg->d_top, + thermal_dbg, &dt_fops); + + debugfs_create_file("clear", 0200, thermal_dbg->d_top, + thermal_dbg, &cdev_clear_fops); + + debugfs_create_u32("total_trans", 0400, thermal_dbg->d_top, + &cdev_dbg->total); + + cdev->debugfs = thermal_dbg; +} + +/** + * thermal_debug_cdev_remove - Remove a cooling device debugfs entry + * + * Frees the statistics memory data and remove the debugfs entry + * + * @cdev: a pointer to a cooling device + */ +void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev) +{ + struct thermal_debugfs *thermal_dbg = cdev->debugfs; + + if (!thermal_dbg) + return; + + mutex_lock(&thermal_dbg->lock); + + thermal_debugfs_cdev_clear(&thermal_dbg->cdev_dbg); + cdev->debugfs = NULL; + + mutex_unlock(&thermal_dbg->lock); + + thermal_debugfs_remove_id(thermal_dbg); +} + +static struct tz_episode *thermal_debugfs_tz_event_alloc(struct thermal_zone_device *tz, + ktime_t now) +{ + struct tz_episode *tze; + int i; + + tze = kzalloc(struct_size(tze, trip_stats, tz->num_trips), GFP_KERNEL); + if (!tze) + return NULL; + + INIT_LIST_HEAD(&tze->node); + tze->timestamp = now; + + for (i = 0; i < tz->num_trips; i++) { + tze->trip_stats[i].min = INT_MAX; + tze->trip_stats[i].max = INT_MIN; + } + + return tze; +} + +void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, + const struct thermal_trip *trip) +{ + struct tz_episode *tze; + struct tz_debugfs *tz_dbg; + struct thermal_debugfs *thermal_dbg = tz->debugfs; + int temperature = tz->temperature; + int trip_id = thermal_zone_trip_id(tz, trip); + ktime_t now = ktime_get(); + + if (!thermal_dbg) + return; + + mutex_lock(&thermal_dbg->lock); + + tz_dbg = &thermal_dbg->tz_dbg; + + /* + * The mitigation is starting. A mitigation can contain + * several episodes where each of them is related to a + * temperature crossing a trip point. The episodes are + * nested. That means when the temperature is crossing the + * first trip point, the duration begins to be measured. If + * the temperature continues to increase and reaches the + * second trip point, the duration of the first trip must be + * also accumulated. + * + * eg. + * + * temp + * ^ + * | -------- + * trip 2 / \ ------ + * | /| |\ /| |\ + * trip 1 / | | `---- | | \ + * | /| | | | | |\ + * trip 0 / | | | | | | \ + * | /| | | | | | | |\ + * | / | | | | | | | | `-- + * | / | | | | | | | | + * |----- | | | | | | | | + * | | | | | | | | | + * --------|-|-|--------|--------|------|-|-|------------------> time + * | | |<--t2-->| |<-t2'>| | | + * | | | | + * | |<------------t1------------>| | + * | | + * |<-------------t0--------------->| + * + */ + if (!tz_dbg->nr_trips) { + tze = thermal_debugfs_tz_event_alloc(tz, now); + if (!tze) + goto unlock; + + list_add(&tze->node, &tz_dbg->tz_episodes); + } + + /* + * Each time a trip point is crossed the way up, the trip_id + * is stored in the trip_crossed array and the nr_trips is + * incremented. A nr_trips equal to zero means we are entering + * a mitigation episode. + * + * The trip ids may not be in the ascending order but the + * result in the array trips_crossed will be in the ascending + * temperature order. The function detecting when a trip point + * is crossed the way down will handle the very rare case when + * the trip points may have been reordered during this + * mitigation episode. + */ + tz_dbg->trips_crossed[tz_dbg->nr_trips++] = trip_id; + + tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); + tze->trip_stats[trip_id].timestamp = now; + tze->trip_stats[trip_id].max = max(tze->trip_stats[trip_id].max, temperature); + tze->trip_stats[trip_id].min = min(tze->trip_stats[trip_id].min, temperature); + tze->trip_stats[trip_id].avg = tze->trip_stats[trip_id].avg + + (temperature - tze->trip_stats[trip_id].avg) / + tze->trip_stats[trip_id].count; + +unlock: + mutex_unlock(&thermal_dbg->lock); +} + +void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, + const struct thermal_trip *trip) +{ + struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct tz_episode *tze; + struct tz_debugfs *tz_dbg; + ktime_t delta, now = ktime_get(); + int trip_id = thermal_zone_trip_id(tz, trip); + int i; + + if (!thermal_dbg) + return; + + mutex_lock(&thermal_dbg->lock); + + tz_dbg = &thermal_dbg->tz_dbg; + + /* + * The temperature crosses the way down but there was not + * mitigation detected before. That may happen when the + * temperature is greater than a trip point when registering a + * thermal zone, which is a common use case as the kernel has + * no mitigation mechanism yet at boot time. + */ + if (!tz_dbg->nr_trips) + goto out; + + for (i = tz_dbg->nr_trips - 1; i >= 0; i--) { + if (tz_dbg->trips_crossed[i] == trip_id) + break; + } + + if (i < 0) + goto out; + + tz_dbg->nr_trips--; + + if (i < tz_dbg->nr_trips) + tz_dbg->trips_crossed[i] = tz_dbg->trips_crossed[tz_dbg->nr_trips]; + + tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); + + delta = ktime_sub(now, tze->trip_stats[trip_id].timestamp); + + tze->trip_stats[trip_id].duration = + ktime_add(delta, tze->trip_stats[trip_id].duration); + + /* + * This event closes the mitigation as we are crossing the + * last trip point the way down. + */ + if (!tz_dbg->nr_trips) + tze->duration = ktime_sub(now, tze->timestamp); + +out: + mutex_unlock(&thermal_dbg->lock); +} + +void thermal_debug_update_temp(struct thermal_zone_device *tz) +{ + struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct tz_episode *tze; + struct tz_debugfs *tz_dbg; + int trip_id, i; + + if (!thermal_dbg) + return; + + mutex_lock(&thermal_dbg->lock); + + tz_dbg = &thermal_dbg->tz_dbg; + + if (!tz_dbg->nr_trips) + goto out; + + for (i = 0; i < tz_dbg->nr_trips; i++) { + trip_id = tz_dbg->trips_crossed[i]; + tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); + tze->trip_stats[trip_id].count++; + tze->trip_stats[trip_id].max = max(tze->trip_stats[trip_id].max, tz->temperature); + tze->trip_stats[trip_id].min = min(tze->trip_stats[trip_id].min, tz->temperature); + tze->trip_stats[trip_id].avg = tze->trip_stats[trip_id].avg + + (tz->temperature - tze->trip_stats[trip_id].avg) / + tze->trip_stats[trip_id].count; + } +out: + mutex_unlock(&thermal_dbg->lock); +} + +static void *tze_seq_start(struct seq_file *s, loff_t *pos) +{ + struct thermal_zone_device *tz = s->private; + struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg; + + mutex_lock(&thermal_dbg->lock); + + return seq_list_start(&tz_dbg->tz_episodes, *pos); +} + +static void *tze_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct thermal_zone_device *tz = s->private; + struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg; + + return seq_list_next(v, &tz_dbg->tz_episodes, pos); +} + +static void tze_seq_stop(struct seq_file *s, void *v) +{ + struct thermal_zone_device *tz = s->private; + struct thermal_debugfs *thermal_dbg = tz->debugfs; + + mutex_unlock(&thermal_dbg->lock); +} + +static int tze_seq_show(struct seq_file *s, void *v) +{ + struct thermal_zone_device *tz = s->private; + struct thermal_trip *trip; + struct tz_episode *tze; + const char *type; + int trip_id; + + tze = list_entry((struct list_head *)v, struct tz_episode, node); + + seq_printf(s, ",-Mitigation at %lluus, duration=%llums\n", + ktime_to_us(tze->timestamp), + ktime_to_ms(tze->duration)); + + seq_printf(s, "| trip | type | temp(°mC) | hyst(°mC) | duration | avg(°mC) | min(°mC) | max(°mC) |\n"); + + for_each_trip(tz, trip) { + /* + * There is no possible mitigation happening at the + * critical trip point, so the stats will be always + * zero, skip this trip point + */ + if (trip->type == THERMAL_TRIP_CRITICAL) + continue; + + if (trip->type == THERMAL_TRIP_PASSIVE) + type = "passive"; + else if (trip->type == THERMAL_TRIP_ACTIVE) + type = "active"; + else + type = "hot"; + + trip_id = thermal_zone_trip_id(tz, trip); + + seq_printf(s, "| %*d | %*s | %*d | %*d | %*lld | %*d | %*d | %*d |\n", + 4 , trip_id, + 8, type, + 9, trip->temperature, + 9, trip->hysteresis, + 10, ktime_to_ms(tze->trip_stats[trip_id].duration), + 9, tze->trip_stats[trip_id].avg, + 9, tze->trip_stats[trip_id].min, + 9, tze->trip_stats[trip_id].max); + } + + return 0; +} + +static const struct seq_operations tze_sops = { + .start = tze_seq_start, + .next = tze_seq_next, + .stop = tze_seq_stop, + .show = tze_seq_show, +}; + +DEFINE_SEQ_ATTRIBUTE(tze); + +void thermal_debug_tz_add(struct thermal_zone_device *tz) +{ + struct thermal_debugfs *thermal_dbg; + struct tz_debugfs *tz_dbg; + + thermal_dbg = thermal_debugfs_add_id(d_tz, tz->id); + if (!thermal_dbg) + return; + + tz_dbg = &thermal_dbg->tz_dbg; + + tz_dbg->trips_crossed = kzalloc(sizeof(int) * tz->num_trips, GFP_KERNEL); + if (!tz_dbg->trips_crossed) { + thermal_debugfs_remove_id(thermal_dbg); + return; + } + + INIT_LIST_HEAD(&tz_dbg->tz_episodes); + + debugfs_create_file("mitigations", 0400, thermal_dbg->d_top, tz, &tze_fops); + + tz->debugfs = thermal_dbg; +} + +void thermal_debug_tz_remove(struct thermal_zone_device *tz) +{ + struct thermal_debugfs *thermal_dbg = tz->debugfs; + + if (!thermal_dbg) + return; + + mutex_lock(&thermal_dbg->lock); + + tz->debugfs = NULL; + + mutex_unlock(&thermal_dbg->lock); + + thermal_debugfs_remove_id(thermal_dbg); +} diff --git a/drivers/thermal/thermal_debugfs.h b/drivers/thermal/thermal_debugfs.h new file mode 100644 index 000000000000..155b9af5fe87 --- /dev/null +++ b/drivers/thermal/thermal_debugfs.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifdef CONFIG_THERMAL_DEBUGFS +void thermal_debug_init(void); +void thermal_debug_cdev_add(struct thermal_cooling_device *cdev); +void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev); +void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, int state); +void thermal_debug_tz_add(struct thermal_zone_device *tz); +void thermal_debug_tz_remove(struct thermal_zone_device *tz); +void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, + const struct thermal_trip *trip); +void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, + const struct thermal_trip *trip); +void thermal_debug_update_temp(struct thermal_zone_device *tz); +#else +static inline void thermal_debug_init(void) {} +static inline void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) {} +static inline void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev) {} +static inline void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, + int state) {} +static inline void thermal_debug_tz_add(struct thermal_zone_device *tz) {} +static inline void thermal_debug_tz_remove(struct thermal_zone_device *tz) {} +static inline void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, + const struct thermal_trip *trip) {}; +static inline void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, + const struct thermal_trip *trip) {} +static inline void thermal_debug_update_temp(struct thermal_zone_device *tz) {} +#endif /* CONFIG_THERMAL_DEBUGFS */ diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index c3982e0f0075..0329f4a71b02 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -146,14 +146,23 @@ unlock: } EXPORT_SYMBOL_GPL(thermal_zone_get_temp); -static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev, - int target) +static int thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev, int state) { - if (cdev->ops->set_cur_state(cdev, target)) - return; + int ret; + + /* + * No check is needed for the ops->set_cur_state as the + * registering function checked the ops are correctly set + */ + ret = cdev->ops->set_cur_state(cdev, state); + if (ret) + return ret; - thermal_notify_cdev_state_update(cdev->id, target); - thermal_cooling_device_stats_update(cdev, target); + thermal_notify_cdev_state_update(cdev, state); + thermal_cooling_device_stats_update(cdev, state); + thermal_debug_cdev_state_update(cdev, state); + + return 0; } void __thermal_cdev_update(struct thermal_cooling_device *cdev) diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c index 332052e24a86..76a231a29654 100644 --- a/drivers/thermal/thermal_netlink.c +++ b/drivers/thermal/thermal_netlink.c @@ -148,7 +148,7 @@ static int thermal_genl_event_tz_trip_up(struct param *p) return 0; } -static int thermal_genl_event_tz_trip_add(struct param *p) +static int thermal_genl_event_tz_trip_change(struct param *p) { if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) || @@ -160,15 +160,6 @@ static int thermal_genl_event_tz_trip_add(struct param *p) return 0; } -static int thermal_genl_event_tz_trip_delete(struct param *p) -{ - if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || - nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id)) - return -EMSGSIZE; - - return 0; -} - static int thermal_genl_event_cdev_add(struct param *p) { if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME, @@ -258,9 +249,6 @@ int thermal_genl_event_tz_disable(struct param *p) int thermal_genl_event_tz_trip_down(struct param *p) __attribute__((alias("thermal_genl_event_tz_trip_up"))); -int thermal_genl_event_tz_trip_change(struct param *p) - __attribute__((alias("thermal_genl_event_tz_trip_add"))); - static cb_t event_cb[] = { [THERMAL_GENL_EVENT_TZ_CREATE] = thermal_genl_event_tz_create, [THERMAL_GENL_EVENT_TZ_DELETE] = thermal_genl_event_tz_delete, @@ -269,8 +257,6 @@ static cb_t event_cb[] = { [THERMAL_GENL_EVENT_TZ_TRIP_UP] = thermal_genl_event_tz_trip_up, [THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = thermal_genl_event_tz_trip_down, [THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = thermal_genl_event_tz_trip_change, - [THERMAL_GENL_EVENT_TZ_TRIP_ADD] = thermal_genl_event_tz_trip_add, - [THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = thermal_genl_event_tz_trip_delete, [THERMAL_GENL_EVENT_CDEV_ADD] = thermal_genl_event_cdev_add, [THERMAL_GENL_EVENT_CDEV_DELETE] = thermal_genl_event_cdev_delete, [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = thermal_genl_event_cdev_state_update, @@ -318,100 +304,93 @@ out_free_msg: return ret; } -int thermal_notify_tz_create(int tz_id, const char *name) +int thermal_notify_tz_create(const struct thermal_zone_device *tz) { - struct param p = { .tz_id = tz_id, .name = name }; + struct param p = { .tz_id = tz->id, .name = tz->type }; return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p); } -int thermal_notify_tz_delete(int tz_id) +int thermal_notify_tz_delete(const struct thermal_zone_device *tz) { - struct param p = { .tz_id = tz_id }; + struct param p = { .tz_id = tz->id }; return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p); } -int thermal_notify_tz_enable(int tz_id) +int thermal_notify_tz_enable(const struct thermal_zone_device *tz) { - struct param p = { .tz_id = tz_id }; + struct param p = { .tz_id = tz->id }; return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p); } -int thermal_notify_tz_disable(int tz_id) +int thermal_notify_tz_disable(const struct thermal_zone_device *tz) { - struct param p = { .tz_id = tz_id }; + struct param p = { .tz_id = tz->id }; return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p); } -int thermal_notify_tz_trip_down(int tz_id, int trip_id, int temp) +int thermal_notify_tz_trip_down(const struct thermal_zone_device *tz, + const struct thermal_trip *trip) { - struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp }; + struct param p = { .tz_id = tz->id, + .trip_id = thermal_zone_trip_id(tz, trip), + .temp = tz->temperature }; return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p); } -int thermal_notify_tz_trip_up(int tz_id, int trip_id, int temp) +int thermal_notify_tz_trip_up(const struct thermal_zone_device *tz, + const struct thermal_trip *trip) { - struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp }; + struct param p = { .tz_id = tz->id, + .trip_id = thermal_zone_trip_id(tz, trip), + .temp = tz->temperature }; return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p); } -int thermal_notify_tz_trip_add(int tz_id, int trip_id, int trip_type, - int trip_temp, int trip_hyst) -{ - struct param p = { .tz_id = tz_id, .trip_id = trip_id, - .trip_type = trip_type, .trip_temp = trip_temp, - .trip_hyst = trip_hyst }; - - return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD, &p); -} - -int thermal_notify_tz_trip_delete(int tz_id, int trip_id) -{ - struct param p = { .tz_id = tz_id, .trip_id = trip_id }; - - return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE, &p); -} - -int thermal_notify_tz_trip_change(int tz_id, int trip_id, int trip_type, - int trip_temp, int trip_hyst) +int thermal_notify_tz_trip_change(const struct thermal_zone_device *tz, + const struct thermal_trip *trip) { - struct param p = { .tz_id = tz_id, .trip_id = trip_id, - .trip_type = trip_type, .trip_temp = trip_temp, - .trip_hyst = trip_hyst }; + struct param p = { .tz_id = tz->id, + .trip_id = thermal_zone_trip_id(tz, trip), + .trip_type = trip->type, + .trip_temp = trip->temperature, + .trip_hyst = trip->hysteresis }; return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p); } -int thermal_notify_cdev_state_update(int cdev_id, int cdev_state) +int thermal_notify_cdev_state_update(const struct thermal_cooling_device *cdev, + int state) { - struct param p = { .cdev_id = cdev_id, .cdev_state = cdev_state }; + struct param p = { .cdev_id = cdev->id, .cdev_state = state }; return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p); } -int thermal_notify_cdev_add(int cdev_id, const char *name, int cdev_max_state) +int thermal_notify_cdev_add(const struct thermal_cooling_device *cdev) { - struct param p = { .cdev_id = cdev_id, .name = name, - .cdev_max_state = cdev_max_state }; + struct param p = { .cdev_id = cdev->id, .name = cdev->type, + .cdev_max_state = cdev->max_state }; return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p); } -int thermal_notify_cdev_delete(int cdev_id) +int thermal_notify_cdev_delete(const struct thermal_cooling_device *cdev) { - struct param p = { .cdev_id = cdev_id }; + struct param p = { .cdev_id = cdev->id }; return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p); } -int thermal_notify_tz_gov_change(int tz_id, const char *name) +int thermal_notify_tz_gov_change(const struct thermal_zone_device *tz, + const char *name) { - struct param p = { .tz_id = tz_id, .name = name }; + struct param p = { .tz_id = tz->id, .name = name }; return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p); } diff --git a/drivers/thermal/thermal_netlink.h b/drivers/thermal/thermal_netlink.h index 0a9987c3bc57..93a927e144d5 100644 --- a/drivers/thermal/thermal_netlink.h +++ b/drivers/thermal/thermal_netlink.h @@ -10,25 +10,30 @@ struct thermal_genl_cpu_caps { int efficiency; }; +struct thermal_zone_device; +struct thermal_trip; +struct thermal_cooling_device; + /* Netlink notification function */ #ifdef CONFIG_THERMAL_NETLINK int __init thermal_netlink_init(void); void __init thermal_netlink_exit(void); -int thermal_notify_tz_create(int tz_id, const char *name); -int thermal_notify_tz_delete(int tz_id); -int thermal_notify_tz_enable(int tz_id); -int thermal_notify_tz_disable(int tz_id); -int thermal_notify_tz_trip_down(int tz_id, int id, int temp); -int thermal_notify_tz_trip_up(int tz_id, int id, int temp); -int thermal_notify_tz_trip_delete(int tz_id, int id); -int thermal_notify_tz_trip_add(int tz_id, int id, int type, - int temp, int hyst); -int thermal_notify_tz_trip_change(int tz_id, int id, int type, - int temp, int hyst); -int thermal_notify_cdev_state_update(int cdev_id, int state); -int thermal_notify_cdev_add(int cdev_id, const char *name, int max_state); -int thermal_notify_cdev_delete(int cdev_id); -int thermal_notify_tz_gov_change(int tz_id, const char *name); +int thermal_notify_tz_create(const struct thermal_zone_device *tz); +int thermal_notify_tz_delete(const struct thermal_zone_device *tz); +int thermal_notify_tz_enable(const struct thermal_zone_device *tz); +int thermal_notify_tz_disable(const struct thermal_zone_device *tz); +int thermal_notify_tz_trip_down(const struct thermal_zone_device *tz, + const struct thermal_trip *trip); +int thermal_notify_tz_trip_up(const struct thermal_zone_device *tz, + const struct thermal_trip *trip); +int thermal_notify_tz_trip_change(const struct thermal_zone_device *tz, + const struct thermal_trip *trip); +int thermal_notify_cdev_state_update(const struct thermal_cooling_device *cdev, + int state); +int thermal_notify_cdev_add(const struct thermal_cooling_device *cdev); +int thermal_notify_cdev_delete(const struct thermal_cooling_device *cdev); +int thermal_notify_tz_gov_change(const struct thermal_zone_device *tz, + const char *name); int thermal_genl_sampling_temp(int id, int temp); int thermal_genl_cpu_capability_event(int count, struct thermal_genl_cpu_caps *caps); @@ -38,70 +43,62 @@ static inline int thermal_netlink_init(void) return 0; } -static inline int thermal_notify_tz_create(int tz_id, const char *name) -{ - return 0; -} - -static inline int thermal_notify_tz_delete(int tz_id) -{ - return 0; -} - -static inline int thermal_notify_tz_enable(int tz_id) +static inline int thermal_notify_tz_create(const struct thermal_zone_device *tz) { return 0; } -static inline int thermal_notify_tz_disable(int tz_id) +static inline int thermal_notify_tz_delete(const struct thermal_zone_device *tz) { return 0; } -static inline int thermal_notify_tz_trip_down(int tz_id, int id, int temp) +static inline int thermal_notify_tz_enable(const struct thermal_zone_device *tz) { return 0; } -static inline int thermal_notify_tz_trip_up(int tz_id, int id, int temp) +static inline int thermal_notify_tz_disable(const struct thermal_zone_device *tz) { return 0; } -static inline int thermal_notify_tz_trip_delete(int tz_id, int id) +static inline int thermal_notify_tz_trip_down(const struct thermal_zone_device *tz, + const struct thermal_trip *trip) { return 0; } -static inline int thermal_notify_tz_trip_add(int tz_id, int id, int type, - int temp, int hyst) +static inline int thermal_notify_tz_trip_up(const struct thermal_zone_device *tz, + const struct thermal_trip *trip) { return 0; } -static inline int thermal_notify_tz_trip_change(int tz_id, int id, int type, - int temp, int hyst) +static inline int thermal_notify_tz_trip_change(const struct thermal_zone_device *tz, + const struct thermal_trip *trip) { return 0; } -static inline int thermal_notify_cdev_state_update(int cdev_id, int state) +static inline int thermal_notify_cdev_state_update(const struct thermal_cooling_device *cdev, + int state) { return 0; } -static inline int thermal_notify_cdev_add(int cdev_id, const char *name, - int max_state) +static inline int thermal_notify_cdev_add(const struct thermal_cooling_device *cdev) { return 0; } -static inline int thermal_notify_cdev_delete(int cdev_id) +static inline int thermal_notify_cdev_delete(const struct thermal_cooling_device *cdev) { return 0; } -static inline int thermal_notify_tz_gov_change(int tz_id, const char *name) +static inline int thermal_notify_tz_gov_change(const struct thermal_zone_device *tz, + const char *name) { return 0; } diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index 8bffa1e5e206..c875a26d5adf 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -155,9 +155,7 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz, void thermal_zone_trip_updated(struct thermal_zone_device *tz, const struct thermal_trip *trip) { - thermal_notify_tz_trip_change(tz->id, thermal_zone_trip_id(tz, trip), - trip->type, trip->temperature, - trip->hysteresis); + thermal_notify_tz_trip_change(tz, trip); __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); } @@ -168,8 +166,6 @@ void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, return; trip->temperature = temp; - thermal_notify_tz_trip_change(tz->id, thermal_zone_trip_id(tz, trip), - trip->type, trip->temperature, - trip->hysteresis); + thermal_notify_tz_trip_change(tz, trip); } EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index ec7b5f65804e..9fb1a64f3300 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -307,7 +307,7 @@ static const struct attribute_group *domain_attr_groups[] = { NULL, }; -struct bus_type tb_bus_type = { +const struct bus_type tb_bus_type = { .name = "thunderbolt", .match = tb_service_match, .probe = tb_service_probe, diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index d8b9c734abd3..56790d50f9e3 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -1020,7 +1020,7 @@ icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level, memset(&reply, 0, sizeof(reply)); ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), - 1, 10, 2000); + 1, 10, 250); if (ret) return ret; diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 4b7bec74e89f..fb4f46e51753 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1517,6 +1517,10 @@ static struct pci_device_id nhi_ids[] = { .driver_data = (kernel_ulong_t)&icl_nhi_ops }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI1), .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_LNL_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_LNL_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_80G_NHI) }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_40G_NHI) }, diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 0f029ce75882..7a07c7c1a9c2 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -90,6 +90,8 @@ extern const struct tb_nhi_ops icl_nhi_ops; #define PCI_DEVICE_ID_INTEL_TGL_H_NHI1 0x9a21 #define PCI_DEVICE_ID_INTEL_RPL_NHI0 0xa73e #define PCI_DEVICE_ID_INTEL_RPL_NHI1 0xa76d +#define PCI_DEVICE_ID_INTEL_LNL_NHI0 0xa833 +#define PCI_DEVICE_ID_INTEL_LNL_NHI1 0xa834 #define PCI_CLASS_SERIAL_USB_USB4 0x0c0340 diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 44e9b09de47a..900114ba4371 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -941,22 +941,6 @@ int tb_port_get_link_generation(struct tb_port *port) } } -static const char *width_name(enum tb_link_width width) -{ - switch (width) { - case TB_LINK_WIDTH_SINGLE: - return "symmetric, single lane"; - case TB_LINK_WIDTH_DUAL: - return "symmetric, dual lanes"; - case TB_LINK_WIDTH_ASYM_TX: - return "asymmetric, 3 transmitters, 1 receiver"; - case TB_LINK_WIDTH_ASYM_RX: - return "asymmetric, 3 receivers, 1 transmitter"; - default: - return "unknown"; - } -} - /** * tb_port_get_link_width() - Get current link width * @port: Port to check (USB4 or CIO) @@ -2769,7 +2753,7 @@ static void tb_switch_link_init(struct tb_switch *sw) return; tb_sw_dbg(sw, "current link speed %u.0 Gb/s\n", sw->link_speed); - tb_sw_dbg(sw, "current link width %s\n", width_name(sw->link_width)); + tb_sw_dbg(sw, "current link width %s\n", tb_width_name(sw->link_width)); bonded = sw->link_width >= TB_LINK_WIDTH_DUAL; @@ -2789,6 +2773,19 @@ static void tb_switch_link_init(struct tb_switch *sw) if (down->dual_link_port) down->dual_link_port->bonded = bonded; tb_port_update_credits(down); + + if (tb_port_get_link_generation(up) < 4) + return; + + /* + * Set the Gen 4 preferred link width. This is what the router + * prefers when the link is brought up. If the router does not + * support asymmetric link configuration, this also will be set + * to TB_LINK_WIDTH_DUAL. + */ + sw->preferred_link_width = sw->link_width; + tb_sw_dbg(sw, "preferred link width %s\n", + tb_width_name(sw->preferred_link_width)); } /** @@ -3029,7 +3026,7 @@ int tb_switch_set_link_width(struct tb_switch *sw, enum tb_link_width width) tb_switch_update_link_attributes(sw); - tb_sw_dbg(sw, "link width set to %s\n", width_name(width)); + tb_sw_dbg(sw, "link width set to %s\n", tb_width_name(width)); return ret; } diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index fd49f86e0353..846d2813bb1a 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -513,8 +513,6 @@ static void tb_port_unconfigure_xdomain(struct tb_port *port) usb4_port_unconfigure_xdomain(port); else tb_lc_unconfigure_xdomain(port); - - tb_port_enable(port->dual_link_port); } static void tb_scan_xdomain(struct tb_port *port) @@ -1087,15 +1085,14 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, struct tb_port *dst_port, int requested_up, int requested_down) { + bool clx = false, clx_disabled = false, downstream; struct tb_switch *sw; - bool clx, downstream; struct tb_port *up; int ret = 0; if (!asym_threshold) return 0; - /* Disable CL states before doing any transitions */ downstream = tb_port_path_direction_downstream(src_port, dst_port); /* Pick up router deepest in the hierarchy */ if (downstream) @@ -1103,11 +1100,10 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, else sw = src_port->sw; - clx = tb_disable_clx(sw); - tb_for_each_upstream_port_on_path(src_port, dst_port, up) { + struct tb_port *down = tb_switch_downstream_port(up->sw); + enum tb_link_width width_up, width_down; int consumed_up, consumed_down; - enum tb_link_width width; ret = tb_consumed_dp_bandwidth(tb, src_port, dst_port, up, &consumed_up, &consumed_down); @@ -1128,7 +1124,8 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, if (consumed_down + requested_down < asym_threshold) continue; - width = TB_LINK_WIDTH_ASYM_RX; + width_up = TB_LINK_WIDTH_ASYM_RX; + width_down = TB_LINK_WIDTH_ASYM_TX; } else { /* Upstream, the opposite of above */ if (consumed_down + requested_down >= TB_ASYM_MIN) { @@ -1138,22 +1135,34 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, if (consumed_up + requested_up < asym_threshold) continue; - width = TB_LINK_WIDTH_ASYM_TX; + width_up = TB_LINK_WIDTH_ASYM_TX; + width_down = TB_LINK_WIDTH_ASYM_RX; } - if (up->sw->link_width == width) + if (up->sw->link_width == width_up) continue; - if (!tb_port_width_supported(up, width)) + if (!tb_port_width_supported(up, width_up) || + !tb_port_width_supported(down, width_down)) continue; + /* + * Disable CL states before doing any transitions. We + * delayed it until now that we know there is a real + * transition taking place. + */ + if (!clx_disabled) { + clx = tb_disable_clx(sw); + clx_disabled = true; + } + tb_sw_dbg(up->sw, "configuring asymmetric link\n"); /* * Here requested + consumed > threshold so we need to * transtion the link into asymmetric now. */ - ret = tb_switch_set_link_width(up->sw, width); + ret = tb_switch_set_link_width(up->sw, width_up); if (ret) { tb_sw_warn(up->sw, "failed to set link width\n"); break; @@ -1174,24 +1183,24 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port, * @dst_port: Destination adapter * @requested_up: New lower bandwidth request upstream (Mb/s) * @requested_down: New lower bandwidth request downstream (Mb/s) + * @keep_asym: Keep asymmetric link if preferred * * Goes over each link from @src_port to @dst_port and tries to * transition the link to symmetric if the currently consumed bandwidth - * allows. + * allows and link asymmetric preference is ignored (if @keep_asym is %false). */ static int tb_configure_sym(struct tb *tb, struct tb_port *src_port, struct tb_port *dst_port, int requested_up, - int requested_down) + int requested_down, bool keep_asym) { + bool clx = false, clx_disabled = false, downstream; struct tb_switch *sw; - bool clx, downstream; struct tb_port *up; int ret = 0; if (!asym_threshold) return 0; - /* Disable CL states before doing any transitions */ downstream = tb_port_path_direction_downstream(src_port, dst_port); /* Pick up router deepest in the hierarchy */ if (downstream) @@ -1199,8 +1208,6 @@ static int tb_configure_sym(struct tb *tb, struct tb_port *src_port, else sw = src_port->sw; - clx = tb_disable_clx(sw); - tb_for_each_upstream_port_on_path(src_port, dst_port, up) { int consumed_up, consumed_down; @@ -1233,6 +1240,25 @@ static int tb_configure_sym(struct tb *tb, struct tb_port *src_port, if (up->sw->link_width == TB_LINK_WIDTH_DUAL) continue; + /* + * Here consumed < threshold so we can transition the + * link to symmetric. + * + * However, if the router prefers asymmetric link we + * honor that (unless @keep_asym is %false). + */ + if (keep_asym && + up->sw->preferred_link_width > TB_LINK_WIDTH_DUAL) { + tb_sw_dbg(up->sw, "keeping preferred asymmetric link\n"); + continue; + } + + /* Disable CL states before doing any transitions */ + if (!clx_disabled) { + clx = tb_disable_clx(sw); + clx_disabled = true; + } + tb_sw_dbg(up->sw, "configuring symmetric link\n"); ret = tb_switch_set_link_width(up->sw, TB_LINK_WIDTH_DUAL); @@ -1280,7 +1306,7 @@ static void tb_configure_link(struct tb_port *down, struct tb_port *up, struct tb_port *host_port; host_port = tb_port_at(tb_route(sw), tb->root_switch); - tb_configure_sym(tb, host_port, up, 0, 0); + tb_configure_sym(tb, host_port, up, 0, 0, false); } /* Set the link configured */ @@ -1465,7 +1491,7 @@ static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel) * If bandwidth on a link is < asym_threshold * transition the link to symmetric. */ - tb_configure_sym(tb, src_port, dst_port, 0, 0); + tb_configure_sym(tb, src_port, dst_port, 0, 0, true); /* Now we can allow the domain to runtime suspend again */ pm_runtime_mark_last_busy(&dst_port->sw->dev); pm_runtime_put_autosuspend(&dst_port->sw->dev); @@ -1901,7 +1927,7 @@ static void tb_dp_resource_available(struct tb *tb, struct tb_port *port) return; } - tb_port_dbg(port, "DP %s resource available\n", + tb_port_dbg(port, "DP %s resource available after hotplug\n", tb_port_is_dpin(port) ? "IN" : "OUT"); list_add_tail(&port->list, &tcm->dp_resources); @@ -2287,7 +2313,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up, * If bandwidth on a link is < asym_threshold transition * the link to symmetric. */ - tb_configure_sym(tb, in, out, *requested_up, *requested_down); + tb_configure_sym(tb, in, out, *requested_up, *requested_down, true); /* * If requested bandwidth is less or equal than what is * currently allocated to that tunnel we simply change @@ -2330,7 +2356,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up, ret = tb_configure_asym(tb, in, out, *requested_up, *requested_down); if (ret) { - tb_configure_sym(tb, in, out, 0, 0); + tb_configure_sym(tb, in, out, 0, 0, true); return ret; } @@ -2338,7 +2364,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up, requested_down); if (ret) { tb_tunnel_warn(tunnel, "failed to allocate bandwidth\n"); - tb_configure_sym(tb, in, out, 0, 0); + tb_configure_sym(tb, in, out, 0, 0, true); } } else { ret = -ENOBUFS; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index e299e53473ae..997c5a536905 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -125,6 +125,7 @@ struct tb_switch_tmu { * @device_name: Name of the device (or %NULL if not known) * @link_speed: Speed of the link in Gb/s * @link_width: Width of the upstream facing link + * @preferred_link_width: Router preferred link width (only set for Gen 4 links) * @link_usb4: Upstream link is USB4 * @generation: Switch Thunderbolt generation * @cap_plug_events: Offset to the plug events capability (%0 if not found) @@ -178,6 +179,7 @@ struct tb_switch { const char *device_name; unsigned int link_speed; enum tb_link_width link_width; + enum tb_link_width preferred_link_width; bool link_usb4; unsigned int generation; int cap_plug_events; @@ -568,6 +570,22 @@ static inline struct tb_port *tb_port_at(u64 route, struct tb_switch *sw) return &sw->ports[port]; } +static inline const char *tb_width_name(enum tb_link_width width) +{ + switch (width) { + case TB_LINK_WIDTH_SINGLE: + return "symmetric, single lane"; + case TB_LINK_WIDTH_DUAL: + return "symmetric, dual lanes"; + case TB_LINK_WIDTH_ASYM_TX: + return "asymmetric, 3 transmitters, 1 receiver"; + case TB_LINK_WIDTH_ASYM_RX: + return "asymmetric, 3 receivers, 1 transmitter"; + default: + return "unknown"; + } +} + /** * tb_port_has_remote() - Does the port have switch connected downstream * @port: Port to check diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index 11f2aec2a5d3..9a259c72e5a7 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -894,7 +894,7 @@ static int tb_switch_tmu_change_mode(struct tb_switch *sw) ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.mode_request); if (ret) - return ret; + goto out; /* Program the new mode and the downstream router lane adapter */ switch (sw->tmu.mode_request) { diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 7534cd3a81f4..6fffb2c82d3d 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -173,16 +173,28 @@ static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable) int ret; /* Only supported of both routers are at least USB4 v2 */ - if (tb_port_get_link_generation(port) < 4) + if ((usb4_switch_version(tunnel->src_port->sw) < 2) || + (usb4_switch_version(tunnel->dst_port->sw) < 2)) + return 0; + + if (enable && tb_port_get_link_generation(port) < 4) return 0; ret = usb4_pci_port_set_ext_encapsulation(tunnel->src_port, enable); if (ret) return ret; + /* + * Downstream router could be unplugged so disable of encapsulation + * in upstream router is still possible. + */ ret = usb4_pci_port_set_ext_encapsulation(tunnel->dst_port, enable); - if (ret) - return ret; + if (ret) { + if (enable) + return ret; + if (ret != -ENODEV) + return ret; + } tb_tunnel_dbg(tunnel, "extended encapsulation %s\n", str_enabled_disabled(enable)); @@ -199,14 +211,21 @@ static int tb_pci_activate(struct tb_tunnel *tunnel, bool activate) return res; } - res = tb_pci_port_enable(tunnel->src_port, activate); + if (activate) + res = tb_pci_port_enable(tunnel->dst_port, activate); + else + res = tb_pci_port_enable(tunnel->src_port, activate); if (res) return res; - if (tb_port_is_pcie_up(tunnel->dst_port)) { - res = tb_pci_port_enable(tunnel->dst_port, activate); + + if (activate) { + res = tb_pci_port_enable(tunnel->src_port, activate); if (res) return res; + } else { + /* Downstream router could be unplugged */ + tb_pci_port_enable(tunnel->dst_port, activate); } return activate ? 0 : tb_pci_set_ext_encapsulation(tunnel, activate); @@ -1067,8 +1086,7 @@ static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up, return 0; } -static int tb_dp_read_dprx(struct tb_tunnel *tunnel, u32 *rate, u32 *lanes, - int timeout_msec) +static int tb_dp_wait_dprx(struct tb_tunnel *tunnel, int timeout_msec) { ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec); struct tb_port *in = tunnel->src_port; @@ -1087,15 +1105,13 @@ static int tb_dp_read_dprx(struct tb_tunnel *tunnel, u32 *rate, u32 *lanes, return ret; if (val & DP_COMMON_CAP_DPRX_DONE) { - *rate = tb_dp_cap_get_rate(val); - *lanes = tb_dp_cap_get_lanes(val); - tb_tunnel_dbg(tunnel, "DPRX read done\n"); return 0; } usleep_range(100, 150); } while (ktime_before(ktime_get(), timeout)); + tb_tunnel_dbg(tunnel, "DPRX read timeout\n"); return -ETIMEDOUT; } @@ -1110,6 +1126,7 @@ static int tb_dp_read_cap(struct tb_tunnel *tunnel, unsigned int cap, u32 *rate, switch (cap) { case DP_LOCAL_CAP: case DP_REMOTE_CAP: + case DP_COMMON_CAP: break; default: @@ -1182,7 +1199,7 @@ static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, * reduced one). Otherwise return the remote (possibly * reduced) caps. */ - ret = tb_dp_read_dprx(tunnel, &rate, &lanes, 150); + ret = tb_dp_wait_dprx(tunnel, 150); if (ret) { if (ret == -ETIMEDOUT) ret = tb_dp_read_cap(tunnel, DP_REMOTE_CAP, @@ -1190,6 +1207,9 @@ static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, if (ret) return ret; } + ret = tb_dp_read_cap(tunnel, DP_COMMON_CAP, &rate, &lanes); + if (ret) + return ret; } else if (sw->generation >= 2) { ret = tb_dp_read_cap(tunnel, DP_REMOTE_CAP, &rate, &lanes); if (ret) @@ -1313,8 +1333,6 @@ static void tb_dp_dump(struct tb_tunnel *tunnel) "DP IN maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n", rate, lanes, tb_dp_bandwidth(rate, lanes)); - out = tunnel->dst_port; - if (tb_port_read(out, &dp_cap, TB_CFG_PORT, out->cap_adap + DP_LOCAL_CAP, 1)) return; diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 9803f0bbf20d..9495742913d5 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1462,6 +1462,11 @@ static int tb_xdomain_get_properties(struct tb_xdomain *xd) tb_port_disable(port->dual_link_port); } + dev_dbg(&xd->dev, "current link speed %u.0 Gb/s\n", + xd->link_speed); + dev_dbg(&xd->dev, "current link width %s\n", + tb_width_name(xd->link_width)); + if (device_add(&xd->dev)) { dev_err(&xd->dev, "failed to add XDomain device\n"); return -ENODEV; @@ -1895,6 +1900,50 @@ struct device_type tb_xdomain_type = { }; EXPORT_SYMBOL_GPL(tb_xdomain_type); +static void tb_xdomain_link_init(struct tb_xdomain *xd, struct tb_port *down) +{ + if (!down->dual_link_port) + return; + + /* + * Gen 4 links come up already as bonded so only update the port + * structures here. + */ + if (tb_port_get_link_generation(down) >= 4) { + down->bonded = true; + down->dual_link_port->bonded = true; + } else { + xd->bonding_possible = true; + } +} + +static void tb_xdomain_link_exit(struct tb_xdomain *xd) +{ + struct tb_port *down = tb_xdomain_downstream_port(xd); + + if (!down->dual_link_port) + return; + + if (tb_port_get_link_generation(down) >= 4) { + down->bonded = false; + down->dual_link_port->bonded = false; + } else if (xd->link_width > TB_LINK_WIDTH_SINGLE) { + /* + * Just return port structures back to way they were and + * update credits. No need to update userspace because + * the XDomain is removed soon anyway. + */ + tb_port_lane_bonding_disable(down); + tb_port_update_credits(down); + } else if (down->dual_link_port) { + /* + * Re-enable the lane 1 adapter we disabled at the end + * of tb_xdomain_get_properties(). + */ + tb_port_enable(down->dual_link_port); + } +} + /** * tb_xdomain_alloc() - Allocate new XDomain object * @tb: Domain where the XDomain belongs @@ -1945,7 +1994,8 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, goto err_free_local_uuid; } else { xd->needs_uuid = true; - xd->bonding_possible = !!down->dual_link_port; + + tb_xdomain_link_init(xd, down); } device_initialize(&xd->dev); @@ -2014,6 +2064,8 @@ void tb_xdomain_remove(struct tb_xdomain *xd) device_for_each_child_reverse(&xd->dev, xd, unregister_service); + tb_xdomain_link_exit(xd); + /* * Undo runtime PM here explicitly because it is possible that * the XDomain was never added to the bus and thus device_del() diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 785558c65ae8..7716ce0d35bc 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -81,7 +81,7 @@ struct serial_state { int quot; int IER; /* Interrupt Enable Register */ int MCR; /* Modem control register */ - int x_char; /* xon/xoff character */ + u8 x_char; /* xon/xoff character */ }; static struct tty_driver *serial_driver; @@ -178,9 +178,9 @@ static void receive_chars(struct serial_state *info) { int status; int serdatr; - unsigned char ch, flag; + u8 ch, flag; struct async_icount *icount; - int oe = 0; + bool overrun = false; icount = &info->icount; @@ -230,7 +230,7 @@ static void receive_chars(struct serial_state *info) * should be ignored. */ if (status & info->ignore_status_mask) - goto out; + return; status &= info->read_status_mask; @@ -251,15 +251,13 @@ static void receive_chars(struct serial_state *info) * reported immediately, and doesn't * affect the current character */ - oe = 1; + overrun = true; } } tty_insert_flip_char(&info->tport, ch, flag); - if (oe == 1) + if (overrun) tty_insert_flip_char(&info->tport, 0, TTY_OVERRUN); tty_flip_buffer_push(&info->tport); -out: - return; } static void transmit_chars(struct serial_state *info) @@ -813,7 +811,7 @@ static void rs_flush_buffer(struct tty_struct *tty) * This function is used to send a high-priority XON/XOFF character to * the device */ -static void rs_send_xchar(struct tty_struct *tty, char ch) +static void rs_send_xchar(struct tty_struct *tty, u8 ch) { struct serial_state *info = tty->driver_data; unsigned long flags; diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index a067628e01c8..69508d7a4135 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -49,7 +49,7 @@ struct ehv_bc_data { unsigned int tx_irq; spinlock_t lock; /* lock for transmit buffer */ - unsigned char buf[BUF_SIZE]; /* transmit circular buffer */ + u8 buf[BUF_SIZE]; /* transmit circular buffer */ unsigned int head; /* circular buffer head */ unsigned int tail; /* circular buffer tail */ @@ -138,14 +138,17 @@ static int find_console_handle(void) static unsigned int local_ev_byte_channel_send(unsigned int handle, unsigned int *count, - const char *p) + const u8 *p) { - char buffer[EV_BYTE_CHANNEL_MAX_BYTES]; + u8 buffer[EV_BYTE_CHANNEL_MAX_BYTES]; unsigned int c = *count; + /* + * ev_byte_channel_send() expects at least EV_BYTE_CHANNEL_MAX_BYTES + * (16 B) in the buffer. Fake it using a local buffer if needed. + */ if (c < sizeof(buffer)) { - memcpy(buffer, p, c); - memset(&buffer[c], 0, sizeof(buffer) - c); + memcpy_and_pad(buffer, sizeof(buffer), p, c, 0); p = buffer; } return ev_byte_channel_send(handle, count, p); @@ -163,7 +166,7 @@ static unsigned int local_ev_byte_channel_send(unsigned int handle, * has been sent, or if some error has occurred. * */ -static void byte_channel_spin_send(const char data) +static void byte_channel_spin_send(const u8 data) { int ret, count; @@ -471,8 +474,7 @@ static ssize_t ehv_bc_tty_write(struct tty_struct *ttys, const u8 *s, { struct ehv_bc_data *bc = ttys->driver_data; unsigned long flags; - unsigned int len; - unsigned int written = 0; + size_t len, written = 0; while (1) { spin_lock_irqsave(&bc->lock, flags); diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 4591f940b7a1..d27979eabfdf 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -50,10 +50,8 @@ static u32 goldfish_tty_line_count = 8; static u32 goldfish_tty_current_line_count; static struct goldfish_tty *goldfish_ttys; -static void do_rw_io(struct goldfish_tty *qtty, - unsigned long address, - unsigned int count, - int is_write) +static void do_rw_io(struct goldfish_tty *qtty, unsigned long address, + size_t count, bool is_write) { unsigned long irq_flags; void __iomem *base = qtty->base; @@ -73,10 +71,8 @@ static void do_rw_io(struct goldfish_tty *qtty, spin_unlock_irqrestore(&qtty->lock, irq_flags); } -static void goldfish_tty_rw(struct goldfish_tty *qtty, - unsigned long addr, - unsigned int count, - int is_write) +static void goldfish_tty_rw(struct goldfish_tty *qtty, unsigned long addr, + size_t count, bool is_write) { dma_addr_t dma_handle; enum dma_data_direction dma_dir; @@ -125,20 +121,18 @@ static void goldfish_tty_rw(struct goldfish_tty *qtty, } } -static void goldfish_tty_do_write(int line, const u8 *buf, unsigned int count) +static void goldfish_tty_do_write(int line, const u8 *buf, size_t count) { struct goldfish_tty *qtty = &goldfish_ttys[line]; - unsigned long address = (unsigned long)(void *)buf; - goldfish_tty_rw(qtty, address, count, 1); + goldfish_tty_rw(qtty, (unsigned long)buf, count, true); } static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) { struct goldfish_tty *qtty = dev_id; void __iomem *base = qtty->base; - unsigned long address; - unsigned char *buf; + u8 *buf; u32 count; count = gf_ioread32(base + GOLDFISH_TTY_REG_BYTES_READY); @@ -147,8 +141,7 @@ static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) count = tty_prepare_flip_string(&qtty->port, &buf, count); - address = (unsigned long)(void *)buf; - goldfish_tty_rw(qtty, address, count, 0); + goldfish_tty_rw(qtty, (unsigned long)buf, count, false); tty_flip_buffer_push(&qtty->port); return IRQ_HANDLED; diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 4f9264d005c0..6e05c5c7bca1 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -108,7 +108,7 @@ config HVC_DCC_SERIALIZE_SMP config HVC_RISCV_SBI bool "RISC-V SBI console support" - depends on RISCV_SBI_V01 + depends on RISCV_SBI select HVC_DRIVER help This enables support for console output via RISC-V SBI calls, which diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index 959fae54ca39..cd1f657f782d 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -922,8 +922,7 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, return ERR_PTR(err); } - hp = kzalloc(ALIGN(sizeof(*hp), sizeof(long)) + outbuf_size, - GFP_KERNEL); + hp = kzalloc(struct_size(hp, outbuf, outbuf_size), GFP_KERNEL); if (!hp) return ERR_PTR(-ENOMEM); @@ -931,7 +930,6 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, hp->data = data; hp->ops = ops; hp->outbuf_size = outbuf_size; - hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))]; tty_port_init(&hp->port); hp->port.ops = &hvc_port_ops; @@ -976,7 +974,7 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, } EXPORT_SYMBOL_GPL(hvc_alloc); -int hvc_remove(struct hvc_struct *hp) +void hvc_remove(struct hvc_struct *hp) { unsigned long flags; struct tty_struct *tty; @@ -1010,7 +1008,6 @@ int hvc_remove(struct hvc_struct *hp) tty_vhangup(tty); tty_kref_put(tty); } - return 0; } EXPORT_SYMBOL_GPL(hvc_remove); diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h index 9668f821db01..cf4c1af08a7c 100644 --- a/drivers/tty/hvc/hvc_console.h +++ b/drivers/tty/hvc/hvc_console.h @@ -37,7 +37,6 @@ struct hvc_struct { spinlock_t lock; int index; int do_wakeup; - char *outbuf; int outbuf_size; int n_outbuf; uint32_t vtermno; @@ -48,12 +47,13 @@ struct hvc_struct { struct work_struct tty_resize; struct list_head next; unsigned long flags; + u8 outbuf[] __aligned(sizeof(long)); }; /* implemented by a low level driver */ struct hv_ops { - int (*get_chars)(uint32_t vtermno, char *buf, int count); - int (*put_chars)(uint32_t vtermno, const char *buf, int count); + ssize_t (*get_chars)(uint32_t vtermno, u8 *buf, size_t count); + ssize_t (*put_chars)(uint32_t vtermno, const u8 *buf, size_t count); int (*flush)(uint32_t vtermno, bool wait); /* Callbacks for notification. Called in open, close and hangup */ @@ -77,7 +77,7 @@ extern int hvc_instantiate(uint32_t vtermno, int index, extern struct hvc_struct * hvc_alloc(uint32_t vtermno, int data, const struct hv_ops *ops, int outbuf_size); /* remove a vterm from hvc tty operation (module_exit or hotplug remove) */ -extern int hvc_remove(struct hvc_struct *hp); +extern void hvc_remove(struct hvc_struct *hp); /* data available */ int hvc_poll(struct hvc_struct *hp); diff --git a/drivers/tty/hvc/hvc_dcc.c b/drivers/tty/hvc/hvc_dcc.c index 1751108cf763..dfc5c9c38f07 100644 --- a/drivers/tty/hvc/hvc_dcc.c +++ b/drivers/tty/hvc/hvc_dcc.c @@ -26,10 +26,10 @@ /* Lock to serialize access to DCC fifo */ static DEFINE_SPINLOCK(dcc_lock); -static DEFINE_KFIFO(inbuf, unsigned char, DCC_INBUF_SIZE); -static DEFINE_KFIFO(outbuf, unsigned char, DCC_OUTBUF_SIZE); +static DEFINE_KFIFO(inbuf, u8, DCC_INBUF_SIZE); +static DEFINE_KFIFO(outbuf, u8, DCC_OUTBUF_SIZE); -static void dcc_uart_console_putchar(struct uart_port *port, unsigned char ch) +static void dcc_uart_console_putchar(struct uart_port *port, u8 ch) { while (__dcc_getstatus() & DCC_STATUS_TX) cpu_relax(); @@ -47,6 +47,14 @@ static void dcc_early_write(struct console *con, const char *s, unsigned n) static int __init dcc_early_console_setup(struct earlycon_device *device, const char *opt) { + unsigned int count = 0x4000000; + + while (--count && (__dcc_getstatus() & DCC_STATUS_TX)) + cpu_relax(); + + if (__dcc_getstatus() & DCC_STATUS_TX) + return -ENODEV; + device->con->write = dcc_early_write; return 0; @@ -54,9 +62,9 @@ static int __init dcc_early_console_setup(struct earlycon_device *device, EARLYCON_DECLARE(dcc, dcc_early_console_setup); -static int hvc_dcc_put_chars(uint32_t vt, const char *buf, int count) +static ssize_t hvc_dcc_put_chars(uint32_t vt, const u8 *buf, size_t count) { - int i; + size_t i; for (i = 0; i < count; i++) { while (__dcc_getstatus() & DCC_STATUS_TX) @@ -68,9 +76,9 @@ static int hvc_dcc_put_chars(uint32_t vt, const char *buf, int count) return count; } -static int hvc_dcc_get_chars(uint32_t vt, char *buf, int count) +static ssize_t hvc_dcc_get_chars(uint32_t vt, u8 *buf, size_t count) { - int i; + size_t i; for (i = 0; i < count; ++i) if (__dcc_getstatus() & DCC_STATUS_RX) @@ -149,8 +157,8 @@ static DECLARE_WORK(dcc_pwork, dcc_put_work); */ static void dcc_get_work(struct work_struct *work) { - unsigned char ch; unsigned long irqflags; + u8 ch; /* * Read characters from DCC and put them into the input FIFO, as @@ -172,10 +180,10 @@ static DECLARE_WORK(dcc_gwork, dcc_get_work); * Write characters directly to the DCC if we're on core 0 and the FIFO * is empty, or write them to the FIFO if we're not. */ -static int hvc_dcc0_put_chars(u32 vt, const char *buf, int count) +static ssize_t hvc_dcc0_put_chars(u32 vt, const u8 *buf, size_t count) { - int len; unsigned long irqflags; + ssize_t len; if (!IS_ENABLED(CONFIG_HVC_DCC_SERIALIZE_SMP)) return hvc_dcc_put_chars(vt, buf, count); @@ -211,10 +219,10 @@ static int hvc_dcc0_put_chars(u32 vt, const char *buf, int count) * Read characters directly from the DCC if we're on core 0 and the FIFO * is empty, or read them from the FIFO if we're not. */ -static int hvc_dcc0_get_chars(u32 vt, char *buf, int count) +static ssize_t hvc_dcc0_get_chars(u32 vt, u8 *buf, size_t count) { - int len; unsigned long irqflags; + ssize_t len; if (!IS_ENABLED(CONFIG_HVC_DCC_SERIALIZE_SMP)) return hvc_dcc_get_chars(vt, buf, count); diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index 543f35ddf523..fdecc0d63731 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -215,11 +215,11 @@ static void destroy_tty_buffer_list(struct list_head *list) * If the IUCV path has been severed, then -EPIPE is returned to cause a * hang up (that is issued by the HVC layer). */ -static int hvc_iucv_write(struct hvc_iucv_private *priv, - char *buf, int count, int *has_more_data) +static ssize_t hvc_iucv_write(struct hvc_iucv_private *priv, + u8 *buf, size_t count, int *has_more_data) { struct iucv_tty_buffer *rb; - int written; + ssize_t written; int rc; /* immediately return if there is no IUCV connection */ @@ -312,10 +312,10 @@ out_written: * the routine locks the struct hvc_iucv_private->lock to call * helper functions. */ -static int hvc_iucv_get_chars(uint32_t vtermno, char *buf, int count) +static ssize_t hvc_iucv_get_chars(uint32_t vtermno, u8 *buf, size_t count) { struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); - int written; + ssize_t written; int has_more_data; if (count <= 0) @@ -352,8 +352,8 @@ static int hvc_iucv_get_chars(uint32_t vtermno, char *buf, int count) * If an existing IUCV communicaton path has been severed, -EPIPE is returned * (that can be passed to HVC layer to cause a tty hangup). */ -static int hvc_iucv_queue(struct hvc_iucv_private *priv, const char *buf, - int count) +static ssize_t hvc_iucv_queue(struct hvc_iucv_private *priv, const u8 *buf, + size_t count) { size_t len; @@ -455,12 +455,12 @@ static void hvc_iucv_sndbuf_work(struct work_struct *work) * Locking: The method gets called under an irqsave() spinlock; and * locks struct hvc_iucv_private->lock. */ -static int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count) +static ssize_t hvc_iucv_put_chars(uint32_t vtermno, const u8 *buf, size_t count) { struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); int queued; - if (count <= 0) + if (!count) return 0; if (!priv) diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index 992e199e0ea8..095c33ad10f8 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -58,7 +58,7 @@ static const struct hv_ops hvc_opal_raw_ops = { .notifier_hangup = notifier_hangup_irq, }; -static int hvc_opal_hvsi_get_chars(uint32_t vtermno, char *buf, int count) +static ssize_t hvc_opal_hvsi_get_chars(uint32_t vtermno, u8 *buf, size_t count) { struct hvc_opal_priv *pv = hvc_opal_privs[vtermno]; @@ -68,7 +68,8 @@ static int hvc_opal_hvsi_get_chars(uint32_t vtermno, char *buf, int count) return hvsilib_get_chars(&pv->hvsi, buf, count); } -static int hvc_opal_hvsi_put_chars(uint32_t vtermno, const char *buf, int count) +static ssize_t hvc_opal_hvsi_put_chars(uint32_t vtermno, const u8 *buf, + size_t count) { struct hvc_opal_priv *pv = hvc_opal_privs[vtermno]; @@ -232,24 +233,21 @@ static int hvc_opal_probe(struct platform_device *dev) return 0; } -static int hvc_opal_remove(struct platform_device *dev) +static void hvc_opal_remove(struct platform_device *dev) { struct hvc_struct *hp = dev_get_drvdata(&dev->dev); - int rc, termno; + int termno; termno = hp->vtermno; - rc = hvc_remove(hp); - if (rc == 0) { - if (hvc_opal_privs[termno] != &hvc_opal_boot_priv) - kfree(hvc_opal_privs[termno]); - hvc_opal_privs[termno] = NULL; - } - return rc; + hvc_remove(hp); + if (hvc_opal_privs[termno] != &hvc_opal_boot_priv) + kfree(hvc_opal_privs[termno]); + hvc_opal_privs[termno] = NULL; } static struct platform_driver hvc_opal_driver = { .probe = hvc_opal_probe, - .remove = hvc_opal_remove, + .remove_new = hvc_opal_remove, .driver = { .name = hvc_opal_name, .of_match_table = hvc_opal_match, diff --git a/drivers/tty/hvc/hvc_riscv_sbi.c b/drivers/tty/hvc/hvc_riscv_sbi.c index 31f53fa77e4a..cede8a572594 100644 --- a/drivers/tty/hvc/hvc_riscv_sbi.c +++ b/drivers/tty/hvc/hvc_riscv_sbi.c @@ -15,9 +15,9 @@ #include "hvc_console.h" -static int hvc_sbi_tty_put(uint32_t vtermno, const char *buf, int count) +static ssize_t hvc_sbi_tty_put(uint32_t vtermno, const u8 *buf, size_t count) { - int i; + size_t i; for (i = 0; i < count; i++) sbi_console_putchar(buf[i]); @@ -25,9 +25,10 @@ static int hvc_sbi_tty_put(uint32_t vtermno, const char *buf, int count) return i; } -static int hvc_sbi_tty_get(uint32_t vtermno, char *buf, int count) +static ssize_t hvc_sbi_tty_get(uint32_t vtermno, u8 *buf, size_t count) { - int i, c; + size_t i; + int c; for (i = 0; i < count; i++) { c = sbi_console_getchar(); @@ -39,21 +40,44 @@ static int hvc_sbi_tty_get(uint32_t vtermno, char *buf, int count) return i; } -static const struct hv_ops hvc_sbi_ops = { +static const struct hv_ops hvc_sbi_v01_ops = { .get_chars = hvc_sbi_tty_get, .put_chars = hvc_sbi_tty_put, }; -static int __init hvc_sbi_init(void) +static ssize_t hvc_sbi_dbcn_tty_put(uint32_t vtermno, const u8 *buf, size_t count) { - return PTR_ERR_OR_ZERO(hvc_alloc(0, 0, &hvc_sbi_ops, 16)); + return sbi_debug_console_write(buf, count); } -device_initcall(hvc_sbi_init); -static int __init hvc_sbi_console_init(void) +static ssize_t hvc_sbi_dbcn_tty_get(uint32_t vtermno, u8 *buf, size_t count) { - hvc_instantiate(0, 0, &hvc_sbi_ops); + return sbi_debug_console_read(buf, count); +} + +static const struct hv_ops hvc_sbi_dbcn_ops = { + .put_chars = hvc_sbi_dbcn_tty_put, + .get_chars = hvc_sbi_dbcn_tty_get, +}; + +static int __init hvc_sbi_init(void) +{ + int err; + + if (sbi_debug_console_available) { + err = PTR_ERR_OR_ZERO(hvc_alloc(0, 0, &hvc_sbi_dbcn_ops, 256)); + if (err) + return err; + hvc_instantiate(0, 0, &hvc_sbi_dbcn_ops); + } else if (IS_ENABLED(CONFIG_RISCV_SBI_V01)) { + err = PTR_ERR_OR_ZERO(hvc_alloc(0, 0, &hvc_sbi_v01_ops, 256)); + if (err) + return err; + hvc_instantiate(0, 0, &hvc_sbi_v01_ops); + } else { + return -ENODEV; + } return 0; } -console_initcall(hvc_sbi_console_init); +device_initcall(hvc_sbi_init); diff --git a/drivers/tty/hvc/hvc_rtas.c b/drivers/tty/hvc/hvc_rtas.c index 184d325abeed..a0b90275b37f 100644 --- a/drivers/tty/hvc/hvc_rtas.c +++ b/drivers/tty/hvc/hvc_rtas.c @@ -31,10 +31,10 @@ static struct hvc_struct *hvc_rtas_dev; static int rtascons_put_char_token = RTAS_UNKNOWN_SERVICE; static int rtascons_get_char_token = RTAS_UNKNOWN_SERVICE; -static inline int hvc_rtas_write_console(uint32_t vtermno, const char *buf, - int count) +static ssize_t hvc_rtas_write_console(uint32_t vtermno, const u8 *buf, + size_t count) { - int i; + size_t i; for (i = 0; i < count; i++) { if (rtas_call(rtascons_put_char_token, 1, 1, NULL, buf[i])) @@ -44,9 +44,10 @@ static inline int hvc_rtas_write_console(uint32_t vtermno, const char *buf, return i; } -static int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count) +static ssize_t hvc_rtas_read_console(uint32_t vtermno, u8 *buf, size_t count) { - int i, c; + size_t i; + int c; for (i = 0; i < count; i++) { if (rtas_call(rtascons_get_char_token, 0, 2, &c)) diff --git a/drivers/tty/hvc/hvc_udbg.c b/drivers/tty/hvc/hvc_udbg.c index ff0dcc56413c..fdc2699b78dc 100644 --- a/drivers/tty/hvc/hvc_udbg.c +++ b/drivers/tty/hvc/hvc_udbg.c @@ -19,9 +19,9 @@ static struct hvc_struct *hvc_udbg_dev; -static int hvc_udbg_put(uint32_t vtermno, const char *buf, int count) +static ssize_t hvc_udbg_put(uint32_t vtermno, const u8 *buf, size_t count) { - int i; + size_t i; for (i = 0; i < count && udbg_putc; i++) udbg_putc(buf[i]); @@ -29,9 +29,10 @@ static int hvc_udbg_put(uint32_t vtermno, const char *buf, int count) return i; } -static int hvc_udbg_get(uint32_t vtermno, char *buf, int count) +static ssize_t hvc_udbg_get(uint32_t vtermno, u8 *buf, size_t count) { - int i, c; + size_t i; + int c; if (!udbg_getc_poll) return 0; diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index 736b230f5ec0..47930601a26a 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -58,20 +58,20 @@ struct hvterm_priv { hv_protocol_t proto; /* Raw data or HVSI packets */ struct hvsi_priv hvsi; /* HVSI specific data */ spinlock_t buf_lock; - char buf[SIZE_VIO_GET_CHARS]; - int left; - int offset; + u8 buf[SIZE_VIO_GET_CHARS]; + size_t left; + size_t offset; }; static struct hvterm_priv *hvterm_privs[MAX_NR_HVC_CONSOLES]; /* For early boot console */ static struct hvterm_priv hvterm_priv0; -static int hvterm_raw_get_chars(uint32_t vtermno, char *buf, int count) +static ssize_t hvterm_raw_get_chars(uint32_t vtermno, u8 *buf, size_t count) { struct hvterm_priv *pv = hvterm_privs[vtermno]; unsigned long i; unsigned long flags; - int got; + size_t got; if (WARN_ON(!pv)) return 0; @@ -115,7 +115,8 @@ static int hvterm_raw_get_chars(uint32_t vtermno, char *buf, int count) * you are sending fewer chars. * @count: number of chars to send. */ -static int hvterm_raw_put_chars(uint32_t vtermno, const char *buf, int count) +static ssize_t hvterm_raw_put_chars(uint32_t vtermno, const u8 *buf, + size_t count) { struct hvterm_priv *pv = hvterm_privs[vtermno]; @@ -133,7 +134,7 @@ static const struct hv_ops hvterm_raw_ops = { .notifier_hangup = notifier_hangup_irq, }; -static int hvterm_hvsi_get_chars(uint32_t vtermno, char *buf, int count) +static ssize_t hvterm_hvsi_get_chars(uint32_t vtermno, u8 *buf, size_t count) { struct hvterm_priv *pv = hvterm_privs[vtermno]; @@ -143,7 +144,8 @@ static int hvterm_hvsi_get_chars(uint32_t vtermno, char *buf, int count) return hvsilib_get_chars(&pv->hvsi, buf, count); } -static int hvterm_hvsi_put_chars(uint32_t vtermno, const char *buf, int count) +static ssize_t hvterm_hvsi_put_chars(uint32_t vtermno, const u8 *buf, + size_t count) { struct hvterm_priv *pv = hvterm_privs[vtermno]; diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index 34c01874f45b..0e497501f8e3 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -84,13 +84,13 @@ static inline void notify_daemon(struct xencons_info *cons) notify_remote_via_evtchn(cons->evtchn); } -static int __write_console(struct xencons_info *xencons, - const char *data, int len) +static ssize_t __write_console(struct xencons_info *xencons, + const u8 *data, size_t len) { XENCONS_RING_IDX cons, prod; struct xencons_interface *intf = xencons->intf; - int sent = 0; unsigned long flags; + size_t sent = 0; spin_lock_irqsave(&xencons->ring_lock, flags); cons = intf->out_cons; @@ -115,10 +115,11 @@ static int __write_console(struct xencons_info *xencons, return sent; } -static int domU_write_console(uint32_t vtermno, const char *data, int len) +static ssize_t domU_write_console(uint32_t vtermno, const u8 *data, size_t len) { - int ret = len; struct xencons_info *cons = vtermno_to_xencons(vtermno); + size_t ret = len; + if (cons == NULL) return -EINVAL; @@ -129,7 +130,7 @@ static int domU_write_console(uint32_t vtermno, const char *data, int len) * kernel is crippled. */ while (len) { - int sent = __write_console(cons, data, len); + ssize_t sent = __write_console(cons, data, len); if (sent < 0) return sent; @@ -144,14 +145,14 @@ static int domU_write_console(uint32_t vtermno, const char *data, int len) return ret; } -static int domU_read_console(uint32_t vtermno, char *buf, int len) +static ssize_t domU_read_console(uint32_t vtermno, u8 *buf, size_t len) { struct xencons_interface *intf; XENCONS_RING_IDX cons, prod; - int recv = 0; struct xencons_info *xencons = vtermno_to_xencons(vtermno); unsigned int eoiflag = 0; unsigned long flags; + size_t recv = 0; if (xencons == NULL) return -EINVAL; @@ -209,7 +210,7 @@ static const struct hv_ops domU_hvc_ops = { .notifier_hangup = notifier_hangup_irq, }; -static int dom0_read_console(uint32_t vtermno, char *buf, int len) +static ssize_t dom0_read_console(uint32_t vtermno, u8 *buf, size_t len) { return HYPERVISOR_console_io(CONSOLEIO_read, len, buf); } @@ -218,9 +219,9 @@ static int dom0_read_console(uint32_t vtermno, char *buf, int len) * Either for a dom0 to write to the system console, or a domU with a * debug version of Xen */ -static int dom0_write_console(uint32_t vtermno, const char *str, int len) +static ssize_t dom0_write_console(uint32_t vtermno, const u8 *str, size_t len) { - int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str); + int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (u8 *)str); if (rc < 0) return rc; diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index 09289c8154ae..22e1bc4d8a66 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -12,7 +12,7 @@ static int hvsi_send_packet(struct hvsi_priv *pv, struct hvsi_header *packet) packet->seqno = cpu_to_be16(atomic_inc_return(&pv->seqno)); /* Assumes that always succeeds, works in practice */ - return pv->put_chars(pv->termno, (char *)packet, packet->len); + return pv->put_chars(pv->termno, (u8 *)packet, packet->len); } static void hvsi_start_handshake(struct hvsi_priv *pv) @@ -178,9 +178,10 @@ static int hvsi_get_packet(struct hvsi_priv *pv) return 0; } -int hvsilib_get_chars(struct hvsi_priv *pv, char *buf, int count) +ssize_t hvsilib_get_chars(struct hvsi_priv *pv, u8 *buf, size_t count) { - unsigned int tries, read = 0; + unsigned int tries; + size_t read = 0; if (WARN_ON(!pv)) return -ENXIO; @@ -199,7 +200,7 @@ int hvsilib_get_chars(struct hvsi_priv *pv, char *buf, int count) for (tries = 1; count && tries < 2; tries++) { /* Consume existing data packet */ if (pv->inbuf_pktlen) { - unsigned int l = min(count, (int)pv->inbuf_pktlen); + size_t l = min(count, pv->inbuf_pktlen); memcpy(&buf[read], &pv->inbuf[pv->inbuf_cur], l); pv->inbuf_cur += l; pv->inbuf_pktlen -= l; @@ -228,10 +229,11 @@ int hvsilib_get_chars(struct hvsi_priv *pv, char *buf, int count) return read; } -int hvsilib_put_chars(struct hvsi_priv *pv, const char *buf, int count) +ssize_t hvsilib_put_chars(struct hvsi_priv *pv, const u8 *buf, size_t count) { struct hvsi_data dp; - int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA); + size_t adjcount = min_t(size_t, count, HVSI_MAX_OUTGOING_DATA); + int rc; if (WARN_ON(!pv)) return -ENODEV; @@ -411,9 +413,9 @@ void hvsilib_close(struct hvsi_priv *pv, struct hvc_struct *hp) } void hvsilib_init(struct hvsi_priv *pv, - int (*get_chars)(uint32_t termno, char *buf, int count), - int (*put_chars)(uint32_t termno, const char *buf, - int count), + ssize_t (*get_chars)(uint32_t termno, u8 *buf, size_t count), + ssize_t (*put_chars)(uint32_t termno, const u8 *buf, + size_t count), int termno, int is_console) { memset(pv, 0, sizeof(*pv)); diff --git a/drivers/tty/ipwireless/main.h b/drivers/tty/ipwireless/main.h index 73818bb64416..a5728a5b3f83 100644 --- a/drivers/tty/ipwireless/main.h +++ b/drivers/tty/ipwireless/main.h @@ -49,9 +49,6 @@ struct ipw_dev { void __iomem *common_memory; - /* Reference to attribute memory, containing CIS data */ - void *attribute_memory; - /* Hardware context */ struct ipw_hardware *hardware; /* Network layer context */ diff --git a/drivers/tty/mips_ejtag_fdc.c b/drivers/tty/mips_ejtag_fdc.c index 369ec71c24ef..aac80b69a069 100644 --- a/drivers/tty/mips_ejtag_fdc.c +++ b/drivers/tty/mips_ejtag_fdc.c @@ -213,16 +213,16 @@ struct fdc_word { */ /* ranges >= 1 && sizes[0] >= 1 */ -static struct fdc_word mips_ejtag_fdc_encode(const char **ptrs, +static struct fdc_word mips_ejtag_fdc_encode(const u8 **ptrs, unsigned int *sizes, unsigned int ranges) { struct fdc_word word = { 0, 0 }; - const char **ptrs_end = ptrs + ranges; + const u8 **ptrs_end = ptrs + ranges; for (; ptrs < ptrs_end; ++ptrs) { - const char *ptr = *(ptrs++); - const char *end = ptr + *(sizes++); + const u8 *ptr = *(ptrs++); + const u8 *end = ptr + *(sizes++); for (; ptr < end; ++ptr) { word.word |= (u8)*ptr << (8*word.bytes); @@ -417,7 +417,7 @@ static unsigned int mips_ejtag_fdc_put_chan(struct mips_ejtag_fdc_tty *priv, { struct mips_ejtag_fdc_tty_port *dport; struct tty_struct *tty; - const char *ptrs[2]; + const u8 *ptrs[2]; unsigned int sizes[2] = { 0 }; struct fdc_word word = { .bytes = 0 }; unsigned long flags; diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index bf3f87ba3a92..ebaada8db929 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -514,7 +514,7 @@ static void MoxaPortLineCtrl(struct moxa_port *, bool, bool); static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int); static int MoxaPortLineStatus(struct moxa_port *); static void MoxaPortFlushData(struct moxa_port *, int); -static int MoxaPortWriteData(struct tty_struct *, const unsigned char *, int); +static ssize_t MoxaPortWriteData(struct tty_struct *, const u8 *, size_t); static int MoxaPortReadData(struct moxa_port *); static unsigned int MoxaPortTxQueue(struct moxa_port *); static int MoxaPortRxQueue(struct moxa_port *); @@ -1933,10 +1933,10 @@ static void MoxaPortFlushData(struct moxa_port *port, int mode) * * Function 20: Write data. * Syntax: - * int MoxaPortWriteData(int port, unsigned char * buffer, int length); + * ssize_t MoxaPortWriteData(int port, u8 *buffer, size_t length); * int port : port number (0 - 127) - * unsigned char * buffer : pointer to write data buffer. - * int length : write data length + * u8 *buffer : pointer to write data buffer. + * size_t length : write data length * * return: 0 - length : real write data length * @@ -2163,11 +2163,12 @@ static int MoxaPortLineStatus(struct moxa_port *port) return val; } -static int MoxaPortWriteData(struct tty_struct *tty, const u8 *buffer, int len) +static ssize_t MoxaPortWriteData(struct tty_struct *tty, const u8 *buffer, + size_t len) { struct moxa_port *port = tty->driver_data; void __iomem *baseAddr, *ofsAddr, *ofs; - unsigned int c, total; + size_t c, total; u16 head, tail, tx_mask, spage, epage; u16 pageno, pageofs, bufhead; @@ -2224,8 +2225,8 @@ static int MoxaPortWriteData(struct tty_struct *tty, const u8 *buffer, int len) static int MoxaPortReadData(struct moxa_port *port) { struct tty_struct *tty = port->port.tty; - unsigned char *dst; void __iomem *baseAddr, *ofsAddr, *ofs; + u8 *dst; unsigned int count, len, total; u16 tail, rx_mask, spage, epage; u16 pageno, pageofs, bufhead, head; diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 6ce7f259968f..458bb1280ebf 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -264,7 +264,7 @@ struct mxser_port { u8 rx_low_water; int type; /* UART type */ - unsigned char x_char; /* xon/xoff character */ + u8 x_char; /* xon/xoff character */ u8 IER; /* Interrupt Enable Register */ u8 MCR; /* Modem control register */ u8 FCR; /* FIFO control register */ @@ -905,7 +905,7 @@ static ssize_t mxser_write(struct tty_struct *tty, const u8 *buf, size_t count) { struct mxser_port *info = tty->driver_data; unsigned long flags; - int written; + size_t written; bool is_empty; spin_lock_irqsave(&info->slock, flags); @@ -1521,7 +1521,7 @@ static u8 mxser_receive_chars_old(struct tty_struct *tty, if (++ignored > 100) break; } else { - char flag = 0; + u8 flag = 0; if (status & UART_LSR_BRK_ERROR_BITS) { if (status & UART_LSR_BI) { flag = TTY_BREAK; @@ -1585,7 +1585,7 @@ static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port count = port->xmit_fifo_size; do { - unsigned char c; + u8 c; if (!kfifo_get(&port->port.xmit_fifo, &c)) break; diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index a3ab3946e4ad..4036566febcb 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -124,8 +124,8 @@ struct gsm_msg { u8 addr; /* DLCI address + flags */ u8 ctrl; /* Control byte + flags */ unsigned int len; /* Length of data block (can be zero) */ - unsigned char *data; /* Points into buffer but not at the start */ - unsigned char buffer[]; + u8 *data; /* Points into buffer but not at the start */ + u8 buffer[]; }; enum gsm_dlci_state { @@ -283,7 +283,7 @@ struct gsm_mux { /* Bits for GSM mode decoding */ /* Framing Layer */ - unsigned char *buf; + u8 *buf; enum gsm_mux_state state; unsigned int len; unsigned int address; @@ -2856,7 +2856,7 @@ invalid: * Receive bytes in gsm mode 0 */ -static void gsm0_receive(struct gsm_mux *gsm, unsigned char c) +static void gsm0_receive(struct gsm_mux *gsm, u8 c) { unsigned int len; @@ -2947,7 +2947,7 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c) * Receive bytes in mode 1 (Advanced option) */ -static void gsm1_receive(struct gsm_mux *gsm, unsigned char c) +static void gsm1_receive(struct gsm_mux *gsm, u8 c) { /* handle XON/XOFF */ if ((c & ISO_IEC_646_MASK) == XON) { @@ -3541,7 +3541,7 @@ static void gsmld_receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp, size_t count) { struct gsm_mux *gsm = tty->disc_data; - char flags = TTY_NORMAL; + u8 flags = TTY_NORMAL; if (debug & DBG_DATA) gsm_hex_dump_bytes(__func__, cp, count); @@ -3711,7 +3711,7 @@ static ssize_t gsmld_write(struct tty_struct *tty, struct file *file, { struct gsm_mux *gsm = tty->disc_data; unsigned long flags; - int space; + size_t space; int ret; if (!gsm) @@ -3909,8 +3909,7 @@ static void gsm_mux_net_tx_timeout(struct net_device *net, unsigned int txqueue) net->stats.tx_errors++; } -static void gsm_mux_rx_netchar(struct gsm_dlci *dlci, - const unsigned char *in_buf, int size) +static void gsm_mux_rx_netchar(struct gsm_dlci *dlci, const u8 *in_buf, int size) { struct net_device *net = dlci->net; struct sk_buff *skb; diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index a670419efe79..1615f074ab86 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -109,8 +109,8 @@ struct n_hdlc_buf { struct list_head list_item; - int count; - char buf[]; + size_t count; + u8 buf[]; }; struct n_hdlc_buf_list { @@ -263,9 +263,9 @@ static int n_hdlc_tty_open(struct tty_struct *tty) */ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) { - register int actual; unsigned long flags; struct n_hdlc_buf *tbuf; + ssize_t actual; check_again: @@ -281,7 +281,7 @@ check_again: tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); while (tbuf) { - pr_debug("sending frame %p, count=%d\n", tbuf, tbuf->count); + pr_debug("sending frame %p, count=%zu\n", tbuf, tbuf->count); /* Send the next block of data to device */ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); @@ -521,9 +521,9 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, const u8 *data, size_t count) { struct n_hdlc *n_hdlc = tty->disc_data; - int error = 0; DECLARE_WAITQUEUE(wait, current); struct n_hdlc_buf *tbuf; + ssize_t error = 0; pr_debug("%s() called count=%zd\n", __func__, count); diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 02cd40147b3a..e28a921c1637 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -65,24 +65,8 @@ do { \ #define DBG3(args...) DBG_(0x04, ##args) #define DBG4(args...) DBG_(0x08, ##args) -/* TODO: rewrite to optimize macros... */ - #define TMP_BUF_MAX 256 -#define DUMP(buf__, len__) \ - do { \ - char tbuf[TMP_BUF_MAX] = {0}; \ - if (len__ > 1) { \ - u32 data_len = min_t(u32, len__, TMP_BUF_MAX); \ - strscpy(tbuf, buf__, data_len); \ - if (tbuf[data_len - 2] == '\r') \ - tbuf[data_len - 2] = 'r'; \ - DBG1("SENDING: '%s' (%d+n)", tbuf, len__); \ - } else { \ - DBG1("SENDING: '%s' (%d)", tbuf, len__); \ - } \ - } while (0) - /* Defines */ #define NOZOMI_NAME "nozomi" #define NOZOMI_NAME_TTY "nozomi_tty" @@ -754,8 +738,6 @@ static int send_data(enum port_type index, struct nozomi *dc) return 0; } - /* DUMP(buf, size); */ - /* Write length + data */ write_mem32(addr, (u32 *) &size, 4); write_mem32(addr + 4, (u32 *) dc->send_buf, size); @@ -801,11 +783,10 @@ static int receive_data(enum port_type index, struct nozomi *dc) tty_insert_flip_char(&port->port, buf[0], TTY_NORMAL); size = 0; } else if (size < RECEIVE_BUF_MAX) { - size -= tty_insert_flip_string(&port->port, - (char *)buf, size); + size -= tty_insert_flip_string(&port->port, buf, size); } else { - i = tty_insert_flip_string(&port->port, - (char *)buf, RECEIVE_BUF_MAX); + i = tty_insert_flip_string(&port->port, buf, + RECEIVE_BUF_MAX); size -= i; offset += i; } @@ -1602,10 +1583,10 @@ static void ntty_hangup(struct tty_struct *tty) static ssize_t ntty_write(struct tty_struct *tty, const u8 *buffer, size_t count) { - int rval = -EINVAL; struct nozomi *dc = get_dc_by_tty(tty); struct port *port = tty->driver_data; unsigned long flags; + size_t rval; if (!dc || !port) return -ENODEV; diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index a5fdaf5e148e..822a5cd05566 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -77,7 +77,7 @@ static bool is_serdev_device(const struct device *dev) static void serdev_ctrl_release(struct device *dev) { struct serdev_controller *ctrl = to_serdev_controller(dev); - ida_simple_remove(&ctrl_ida, ctrl->nr); + ida_free(&ctrl_ida, ctrl->nr); kfree(ctrl); } @@ -225,8 +225,7 @@ EXPORT_SYMBOL_GPL(serdev_device_write_wakeup); * Return: The number of bytes written (less than count if not enough room in * the write buffer), or a negative errno on errors. */ -int serdev_device_write_buf(struct serdev_device *serdev, - const unsigned char *buf, size_t count) +int serdev_device_write_buf(struct serdev_device *serdev, const u8 *buf, size_t count) { struct serdev_controller *ctrl = serdev->ctrl; @@ -259,13 +258,12 @@ EXPORT_SYMBOL_GPL(serdev_device_write_buf); * -ETIMEDOUT or -ERESTARTSYS if interrupted before any bytes were written, or * a negative errno on errors. */ -int serdev_device_write(struct serdev_device *serdev, - const unsigned char *buf, size_t count, - long timeout) +ssize_t serdev_device_write(struct serdev_device *serdev, const u8 *buf, + size_t count, long timeout) { struct serdev_controller *ctrl = serdev->ctrl; - int written = 0; - int ret; + size_t written = 0; + ssize_t ret; if (!ctrl || !ctrl->ops->write_buf || !serdev->ops->write_wakeup) return -EINVAL; @@ -468,6 +466,7 @@ EXPORT_SYMBOL_GPL(serdev_device_alloc); /** * serdev_controller_alloc() - Allocate a new serdev controller + * @host: serial port hardware controller device * @parent: parent device * @size: size of private data * @@ -476,8 +475,9 @@ EXPORT_SYMBOL_GPL(serdev_device_alloc); * The allocated private data region may be accessed via * serdev_controller_get_drvdata() */ -struct serdev_controller *serdev_controller_alloc(struct device *parent, - size_t size) +struct serdev_controller *serdev_controller_alloc(struct device *host, + struct device *parent, + size_t size) { struct serdev_controller *ctrl; int id; @@ -489,7 +489,7 @@ struct serdev_controller *serdev_controller_alloc(struct device *parent, if (!ctrl) return NULL; - id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&ctrl_ida, GFP_KERNEL); if (id < 0) { dev_err(parent, "unable to allocate serdev controller identifier.\n"); @@ -502,7 +502,8 @@ struct serdev_controller *serdev_controller_alloc(struct device *parent, ctrl->dev.type = &serdev_ctrl_type; ctrl->dev.bus = &serdev_bus_type; ctrl->dev.parent = parent; - device_set_node(&ctrl->dev, dev_fwnode(parent)); + ctrl->host = host; + device_set_node(&ctrl->dev, dev_fwnode(host)); serdev_controller_set_drvdata(ctrl, &ctrl[1]); dev_set_name(&ctrl->dev, "serial%d", id); @@ -665,7 +666,7 @@ static int acpi_serdev_check_resources(struct serdev_controller *ctrl, acpi_get_parent(adev->handle, &lookup.controller_handle); /* Make sure controller and ResourceSource handle match */ - if (!device_match_acpi_handle(ctrl->dev.parent, lookup.controller_handle)) + if (!device_match_acpi_handle(ctrl->host, lookup.controller_handle)) return -ENODEV; return 0; @@ -730,7 +731,7 @@ static int acpi_serdev_register_devices(struct serdev_controller *ctrl) bool skip; int ret; - if (!has_acpi_companion(ctrl->dev.parent)) + if (!has_acpi_companion(ctrl->host)) return -ENODEV; /* @@ -739,7 +740,7 @@ static int acpi_serdev_register_devices(struct serdev_controller *ctrl) * succeed in this case, so that the proper serdev devices can be * added "manually" later. */ - ret = acpi_quirk_skip_serdev_enumeration(ctrl->dev.parent, &skip); + ret = acpi_quirk_skip_serdev_enumeration(ctrl->host, &skip); if (ret) return ret; if (skip) diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index e3856814ce77..e94e090cf0a1 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -74,7 +74,7 @@ static const struct tty_port_client_operations client_ops = { * Callback functions from the serdev core. */ -static int ttyport_write_buf(struct serdev_controller *ctrl, const unsigned char *data, size_t len) +static ssize_t ttyport_write_buf(struct serdev_controller *ctrl, const u8 *data, size_t len) { struct serport *serport = serdev_controller_get_drvdata(ctrl); struct tty_struct *tty = serport->tty; @@ -274,6 +274,7 @@ static const struct serdev_controller_ops ctrl_ops = { }; struct device *serdev_tty_port_register(struct tty_port *port, + struct device *host, struct device *parent, struct tty_driver *drv, int idx) { @@ -284,7 +285,7 @@ struct device *serdev_tty_port_register(struct tty_port *port, if (!port || !drv || !parent) return ERR_PTR(-ENODEV); - ctrl = serdev_controller_alloc(parent, sizeof(struct serport)); + ctrl = serdev_controller_alloc(host, parent, sizeof(struct serport)); if (!ctrl) return ERR_PTR(-ENOMEM); serport = serdev_controller_get_drvdata(ctrl); diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index d7482ae33a1c..8c2aaf7af7b7 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -566,7 +566,7 @@ err_sysfs_remove: return rc; } -static int aspeed_vuart_remove(struct platform_device *pdev) +static void aspeed_vuart_remove(struct platform_device *pdev) { struct aspeed_vuart *vuart = platform_get_drvdata(pdev); @@ -574,8 +574,6 @@ static int aspeed_vuart_remove(struct platform_device *pdev) aspeed_vuart_set_enabled(vuart, false); serial8250_unregister_port(vuart->line); sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); - - return 0; } static const struct of_device_id aspeed_vuart_table[] = { @@ -590,7 +588,7 @@ static struct platform_driver aspeed_vuart_driver = { .of_match_table = aspeed_vuart_table, }, .probe = aspeed_vuart_probe, - .remove = aspeed_vuart_remove, + .remove_new = aspeed_vuart_remove, }; module_platform_driver(aspeed_vuart_driver); diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c index 15a2387a5b25..beac6b340ace 100644 --- a/drivers/tty/serial/8250/8250_bcm2835aux.c +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -119,6 +119,8 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev) /* get the clock - this also enables the HW */ data->clk = devm_clk_get_optional(&pdev->dev, NULL); + if (IS_ERR(data->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->clk), "could not get clk\n"); /* get the interrupt */ ret = platform_get_irq(pdev, 0); @@ -195,14 +197,12 @@ dis_clk: return ret; } -static int bcm2835aux_serial_remove(struct platform_device *pdev) +static void bcm2835aux_serial_remove(struct platform_device *pdev) { struct bcm2835aux_data *data = platform_get_drvdata(pdev); serial8250_unregister_port(data->line); clk_disable_unprepare(data->clk); - - return 0; } static const struct bcm2835_aux_serial_driver_data bcm2835_acpi_data = { @@ -228,7 +228,7 @@ static struct platform_driver bcm2835aux_serial_driver = { .acpi_match_table = bcm2835aux_serial_acpi_match, }, .probe = bcm2835aux_serial_probe, - .remove = bcm2835aux_serial_remove, + .remove_new = bcm2835aux_serial_remove, }; module_platform_driver(bcm2835aux_serial_driver); diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c index 55dea2539c47..504c4c020857 100644 --- a/drivers/tty/serial/8250/8250_bcm7271.c +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -1121,7 +1121,7 @@ release_dma: return ret; } -static int brcmuart_remove(struct platform_device *pdev) +static void brcmuart_remove(struct platform_device *pdev) { struct brcmuart_priv *priv = platform_get_drvdata(pdev); @@ -1131,7 +1131,6 @@ static int brcmuart_remove(struct platform_device *pdev) brcmuart_free_bufs(&pdev->dev, priv); if (priv->dma_enabled) brcmuart_arbitration(priv, 0); - return 0; } static int __maybe_unused brcmuart_suspend(struct device *dev) @@ -1207,7 +1206,7 @@ static struct platform_driver brcmuart_platform_driver = { .of_match_table = brcmuart_dt_ids, }, .probe = brcmuart_probe, - .remove = brcmuart_remove, + .remove_new = brcmuart_remove, }; static int __init brcmuart_init(void) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 912733151858..b62ad9006780 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -883,7 +883,7 @@ static int serial8250_probe(struct platform_device *dev) /* * Remove serial ports registered against a platform device. */ -static int serial8250_remove(struct platform_device *dev) +static void serial8250_remove(struct platform_device *dev) { int i; @@ -893,7 +893,6 @@ static int serial8250_remove(struct platform_device *dev) if (up->port.dev == &dev->dev) serial8250_unregister_port(i); } - return 0; } static int serial8250_suspend(struct platform_device *dev, pm_message_t state) @@ -926,7 +925,7 @@ static int serial8250_resume(struct platform_device *dev) static struct platform_driver serial8250_isa_driver = { .probe = serial8250_probe, - .remove = serial8250_remove, + .remove_new = serial8250_remove, .suspend = serial8250_suspend, .resume = serial8250_resume, .driver = { diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index e6218766d0c8..2d1f350a4bea 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -663,7 +663,7 @@ static int dw8250_probe(struct platform_device *pdev) return 0; } -static int dw8250_remove(struct platform_device *pdev) +static void dw8250_remove(struct platform_device *pdev) { struct dw8250_data *data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; @@ -680,8 +680,6 @@ static int dw8250_remove(struct platform_device *pdev) pm_runtime_disable(dev); pm_runtime_put_noidle(dev); - - return 0; } static int dw8250_suspend(struct device *dev) @@ -790,7 +788,7 @@ static struct platform_driver dw8250_platform_driver = { .acpi_match_table = dw8250_acpi_match, }, .probe = dw8250_probe, - .remove = dw8250_remove, + .remove_new = dw8250_remove, }; module_platform_driver(dw8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c index 84843e204a5e..3e33ddf7bc80 100644 --- a/drivers/tty/serial/8250/8250_dwlib.c +++ b/drivers/tty/serial/8250/8250_dwlib.c @@ -259,17 +259,6 @@ void dw8250_setup_port(struct uart_port *p) } up->capabilities |= UART_CAP_NOTEMT; - /* - * If the Component Version Register returns zero, we know that - * ADDITIONAL_FEATURES are not enabled. No need to go any further. - */ - reg = dw8250_readl_ext(p, DW_UART_UCV); - if (!reg) - return; - - dev_dbg(p->dev, "Designware UART version %c.%c%c\n", - (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); - /* Preserve value written by firmware or bootloader */ old_dlf = dw8250_readl_ext(p, DW_UART_DLF); dw8250_writel_ext(p, DW_UART_DLF, ~0U); @@ -282,6 +271,11 @@ void dw8250_setup_port(struct uart_port *p) p->set_divisor = dw8250_set_divisor; } + reg = dw8250_readl_ext(p, DW_UART_UCV); + if (reg) + dev_dbg(p->dev, "Designware UART version %c.%c%c\n", + (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); + reg = dw8250_readl_ext(p, DW_UART_CPR); if (!reg) { reg = data->pdata->cpr_val; diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c index ef5019e944ea..a754755100ff 100644 --- a/drivers/tty/serial/8250/8250_em.c +++ b/drivers/tty/serial/8250/8250_em.c @@ -200,12 +200,11 @@ static int serial8250_em_probe(struct platform_device *pdev) return 0; } -static int serial8250_em_remove(struct platform_device *pdev) +static void serial8250_em_remove(struct platform_device *pdev) { struct serial8250_em_priv *priv = platform_get_drvdata(pdev); serial8250_unregister_port(priv->line); - return 0; } static const struct of_device_id serial8250_em_dt_ids[] = { @@ -220,7 +219,7 @@ static struct platform_driver serial8250_em_platform_driver = { .of_match_table = serial8250_em_dt_ids, }, .probe = serial8250_em_probe, - .remove = serial8250_em_remove, + .remove_new = serial8250_em_remove, }; module_platform_driver(serial8250_em_platform_driver); diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 6085d356ad86..23366f868ae3 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -480,7 +480,7 @@ static int sealevel_rs485_config(struct uart_port *port, struct ktermios *termio } static const struct serial_rs485 generic_rs485_supported = { - .flags = SER_RS485_ENABLED, + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND, }; static const struct exar8250_platform exar8250_default_platform = { @@ -524,7 +524,8 @@ static int iot2040_rs485_config(struct uart_port *port, struct ktermios *termios } static const struct serial_rs485 iot2040_rs485_supported = { - .flags = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX | SER_RS485_TERMINATE_BUS, + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RX_DURING_TX | SER_RS485_TERMINATE_BUS, }; static const struct property_entry iot2040_gpio_properties[] = { diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index f522eb5026c9..b4ed442082a8 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -51,7 +51,8 @@ int fsl8250_handle_irq(struct uart_port *port) * immediately and interrupt the CPU again. The hardware clears LSR.BI * when the next valid char is read.) */ - if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) { + if (unlikely((iir & UART_IIR_ID) == UART_IIR_RLSI && + (up->lsr_saved_flags & UART_LSR_BI))) { up->lsr_saved_flags &= ~UART_LSR_BI; port->serial_in(port, UART_RX); uart_port_unlock_irqrestore(&up->port, flags); @@ -159,12 +160,11 @@ static int fsl8250_acpi_probe(struct platform_device *pdev) return 0; } -static int fsl8250_acpi_remove(struct platform_device *pdev) +static void fsl8250_acpi_remove(struct platform_device *pdev) { struct fsl8250_data *data = platform_get_drvdata(pdev); serial8250_unregister_port(data->line); - return 0; } static const struct acpi_device_id fsl_8250_acpi_id[] = { @@ -179,7 +179,7 @@ static struct platform_driver fsl8250_platform_driver = { .acpi_match_table = ACPI_PTR(fsl_8250_acpi_id), }, .probe = fsl8250_acpi_probe, - .remove = fsl8250_acpi_remove, + .remove_new = fsl8250_acpi_remove, }; module_platform_driver(fsl8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c index 4c4c4da73ad0..a12f737924c0 100644 --- a/drivers/tty/serial/8250/8250_ingenic.c +++ b/drivers/tty/serial/8250/8250_ingenic.c @@ -320,14 +320,13 @@ out: return err; } -static int ingenic_uart_remove(struct platform_device *pdev) +static void ingenic_uart_remove(struct platform_device *pdev) { struct ingenic_uart_data *data = platform_get_drvdata(pdev); serial8250_unregister_port(data->line); clk_disable_unprepare(data->clk_module); clk_disable_unprepare(data->clk_baud); - return 0; } static const struct ingenic_uart_config jz4740_uart_config = { @@ -368,7 +367,7 @@ static struct platform_driver ingenic_uart_platform_driver = { .of_match_table = of_match, }, .probe = ingenic_uart_probe, - .remove = ingenic_uart_remove, + .remove_new = ingenic_uart_remove, }; module_platform_driver(ingenic_uart_platform_driver); diff --git a/drivers/tty/serial/8250/8250_ioc3.c b/drivers/tty/serial/8250/8250_ioc3.c index d5a39e105a76..50c77c3dacf2 100644 --- a/drivers/tty/serial/8250/8250_ioc3.c +++ b/drivers/tty/serial/8250/8250_ioc3.c @@ -75,17 +75,16 @@ static int serial8250_ioc3_probe(struct platform_device *pdev) return 0; } -static int serial8250_ioc3_remove(struct platform_device *pdev) +static void serial8250_ioc3_remove(struct platform_device *pdev) { struct ioc3_8250_data *data = platform_get_drvdata(pdev); serial8250_unregister_port(data->line); - return 0; } static struct platform_driver serial8250_ioc3_driver = { .probe = serial8250_ioc3_probe, - .remove = serial8250_ioc3_remove, + .remove_new = serial8250_ioc3_remove, .driver = { .name = "ioc3-serial8250", } diff --git a/drivers/tty/serial/8250/8250_lpc18xx.c b/drivers/tty/serial/8250/8250_lpc18xx.c index 6dc85aaba5d0..8d728a6a5991 100644 --- a/drivers/tty/serial/8250/8250_lpc18xx.c +++ b/drivers/tty/serial/8250/8250_lpc18xx.c @@ -182,15 +182,13 @@ dis_clk_reg: return ret; } -static int lpc18xx_serial_remove(struct platform_device *pdev) +static void lpc18xx_serial_remove(struct platform_device *pdev) { struct lpc18xx_uart_data *data = platform_get_drvdata(pdev); serial8250_unregister_port(data->line); clk_disable_unprepare(data->clk_uart); clk_disable_unprepare(data->clk_reg); - - return 0; } static const struct of_device_id lpc18xx_serial_match[] = { @@ -201,7 +199,7 @@ MODULE_DEVICE_TABLE(of, lpc18xx_serial_match); static struct platform_driver lpc18xx_serial_driver = { .probe = lpc18xx_serial_probe, - .remove = lpc18xx_serial_remove, + .remove_new = lpc18xx_serial_remove, .driver = { .name = "lpc18xx-uart", .of_match_table = lpc18xx_serial_match, diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index 0e43bdfb7459..776ec1ef29d6 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -287,17 +287,14 @@ static int lpss8250_dma_setup(struct lpss8250 *lpss, struct uart_8250_port *port return 0; } - rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL); + rx_param = devm_kmemdup(dev, &lpss->dma_param, sizeof(*rx_param), GFP_KERNEL); if (!rx_param) return -ENOMEM; - tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL); + tx_param = devm_kmemdup(dev, &lpss->dma_param, sizeof(*tx_param), GFP_KERNEL); if (!tx_param) return -ENOMEM; - *rx_param = lpss->dma_param; - *tx_param = lpss->dma_param; - dma->fn = lpss8250_dma_filter; dma->rx_param = rx_param; dma->tx_param = tx_param; diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 23457daae8a1..9ff6bbe9c086 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -581,7 +581,7 @@ static int mtk8250_probe(struct platform_device *pdev) return 0; } -static int mtk8250_remove(struct platform_device *pdev) +static void mtk8250_remove(struct platform_device *pdev) { struct mtk8250_data *data = platform_get_drvdata(pdev); @@ -591,8 +591,6 @@ static int mtk8250_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); - - return 0; } static int __maybe_unused mtk8250_suspend(struct device *dev) @@ -652,7 +650,7 @@ static struct platform_driver mtk8250_platform_driver = { .of_match_table = mtk8250_of_match, }, .probe = mtk8250_probe, - .remove = mtk8250_remove, + .remove_new = mtk8250_remove, }; module_platform_driver(mtk8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index ef3e745bd09c..34f17a9785e7 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -251,7 +251,7 @@ err_free: /* * Release a line */ -static int of_platform_serial_remove(struct platform_device *ofdev) +static void of_platform_serial_remove(struct platform_device *ofdev) { struct of_serial_info *info = platform_get_drvdata(ofdev); @@ -261,7 +261,6 @@ static int of_platform_serial_remove(struct platform_device *ofdev) pm_runtime_put_sync(&ofdev->dev); pm_runtime_disable(&ofdev->dev); kfree(info); - return 0; } #ifdef CONFIG_PM_SLEEP @@ -337,7 +336,7 @@ static struct platform_driver of_platform_serial_driver = { .pm = &of_serial_pm_ops, }, .probe = of_platform_serial_probe, - .remove = of_platform_serial_remove, + .remove_new = of_platform_serial_remove, }; module_platform_driver(of_platform_serial_driver); diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 578f35895b27..6942990a333c 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1586,7 +1586,7 @@ err: return ret; } -static int omap8250_remove(struct platform_device *pdev) +static void omap8250_remove(struct platform_device *pdev) { struct omap8250_priv *priv = platform_get_drvdata(pdev); struct uart_8250_port *up; @@ -1594,7 +1594,7 @@ static int omap8250_remove(struct platform_device *pdev) err = pm_runtime_resume_and_get(&pdev->dev); if (err) - return err; + dev_err(&pdev->dev, "Failed to resume hardware\n"); up = serial8250_get_port(priv->line); omap_8250_shutdown(&up->port); @@ -1606,7 +1606,6 @@ static int omap8250_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); cpu_latency_qos_remove_request(&priv->pm_qos_request); device_init_wakeup(&pdev->dev, false); - return 0; } static int omap8250_prepare(struct device *dev) @@ -1865,7 +1864,7 @@ static struct platform_driver omap8250_platform_driver = { .of_match_table = omap8250_dt_ids, }, .probe = omap8250_probe, - .remove = omap8250_remove, + .remove_new = omap8250_remove, }; module_platform_driver(omap8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 614be0f13a31..0d35c77fad9e 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -19,6 +19,7 @@ #include <linux/serial_core.h> #include <linux/8250_pci.h> #include <linux/bitops.h> +#include <linux/bitfield.h> #include <asm/byteorder.h> #include <asm/io.h> @@ -1970,6 +1971,20 @@ pci_sunix_setup(struct serial_private *priv, #define MOXA_GPIO_PIN2 BIT(2) +#define MOXA_RS232 0x00 +#define MOXA_RS422 0x01 +#define MOXA_RS485_4W 0x0B +#define MOXA_RS485_2W 0x0F +#define MOXA_UIR_OFFSET 0x04 +#define MOXA_EVEN_RS_MASK GENMASK(3, 0) +#define MOXA_ODD_RS_MASK GENMASK(7, 4) + +enum { + MOXA_SUPP_RS232 = BIT(0), + MOXA_SUPP_RS422 = BIT(1), + MOXA_SUPP_RS485 = BIT(2), +}; + static bool pci_moxa_is_mini_pcie(unsigned short device) { if (device == PCI_DEVICE_ID_MOXA_CP102N || @@ -1983,12 +1998,54 @@ static bool pci_moxa_is_mini_pcie(unsigned short device) return false; } +static unsigned int pci_moxa_supported_rs(struct pci_dev *dev) +{ + switch (dev->device & 0x0F00) { + case 0x0000: + case 0x0600: + return MOXA_SUPP_RS232; + case 0x0100: + return MOXA_SUPP_RS232 | MOXA_SUPP_RS422 | MOXA_SUPP_RS485; + case 0x0300: + return MOXA_SUPP_RS422 | MOXA_SUPP_RS485; + } + return 0; +} + +static int pci_moxa_set_interface(const struct pci_dev *dev, + unsigned int port_idx, + u8 mode) +{ + resource_size_t iobar_addr = pci_resource_start(dev, 2); + resource_size_t UIR_addr = iobar_addr + MOXA_UIR_OFFSET + port_idx / 2; + u8 val; + + val = inb(UIR_addr); + + if (port_idx % 2) { + val &= ~MOXA_ODD_RS_MASK; + val |= FIELD_PREP(MOXA_ODD_RS_MASK, mode); + } else { + val &= ~MOXA_EVEN_RS_MASK; + val |= FIELD_PREP(MOXA_EVEN_RS_MASK, mode); + } + outb(val, UIR_addr); + + return 0; +} + static int pci_moxa_init(struct pci_dev *dev) { unsigned short device = dev->device; resource_size_t iobar_addr = pci_resource_start(dev, 2); - unsigned int num_ports = (device & 0x00F0) >> 4; - u8 val; + unsigned int num_ports = (device & 0x00F0) >> 4, i; + u8 val, init_mode = MOXA_RS232; + + if (!(pci_moxa_supported_rs(dev) & MOXA_SUPP_RS232)) { + init_mode = MOXA_RS422; + } + for (i = 0; i < num_ports; ++i) + pci_moxa_set_interface(dev, i, init_mode); /* * Enable hardware buffer to prevent break signal output when system boots up. diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c index 9f9e21981929..cd258922bd78 100644 --- a/drivers/tty/serial/8250/8250_pci1xxxx.c +++ b/drivers/tty/serial/8250/8250_pci1xxxx.c @@ -9,15 +9,21 @@ #include <linux/bitfield.h> #include <linux/bitops.h> +#include <linux/delay.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/serial_8250.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/units.h> #include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/8250_pci.h> #include <asm/byteorder.h> @@ -52,6 +58,17 @@ #define PCI_SUBDEVICE_ID_EFAR_PCI11400 PCI_DEVICE_ID_EFAR_PCI11400 #define PCI_SUBDEVICE_ID_EFAR_PCI11414 PCI_DEVICE_ID_EFAR_PCI11414 +#define UART_SYSTEM_ADDR_BASE 0x1000 +#define UART_DEV_REV_REG (UART_SYSTEM_ADDR_BASE + 0x00) +#define UART_DEV_REV_MASK GENMASK(7, 0) +#define UART_SYSLOCK_REG (UART_SYSTEM_ADDR_BASE + 0xA0) +#define UART_SYSLOCK BIT(2) +#define SYSLOCK_SLEEP_TIMEOUT 100 +#define SYSLOCK_RETRY_CNT 1000 + +#define UART_RX_BYTE_FIFO 0x00 +#define UART_FIFO_CTL 0x02 + #define UART_ACTV_REG 0x11 #define UART_BLOCK_SET_ACTIVE BIT(0) @@ -82,8 +99,59 @@ #define UART_RESET_REG 0x94 #define UART_RESET_D3_RESET_DISABLE BIT(16) +#define UART_BURST_STATUS_REG 0x9C +#define UART_RX_BURST_FIFO 0xA4 + #define MAX_PORTS 4 #define PORT_OFFSET 0x100 +#define RX_BUF_SIZE 512 +#define UART_BYTE_SIZE 1 +#define UART_BURST_SIZE 4 + +#define UART_BST_STAT_RX_COUNT_MASK 0x00FF +#define UART_BST_STAT_IIR_INT_PEND 0x100000 +#define UART_LSR_OVERRUN_ERR_CLR 0x43 +#define UART_BST_STAT_LSR_RX_MASK 0x9F000000 +#define UART_BST_STAT_LSR_RX_ERR_MASK 0x9E000000 +#define UART_BST_STAT_LSR_OVERRUN_ERR 0x2000000 +#define UART_BST_STAT_LSR_PARITY_ERR 0x4000000 +#define UART_BST_STAT_LSR_FRAME_ERR 0x8000000 + +struct pci1xxxx_8250 { + unsigned int nr; + u8 dev_rev; + u8 pad[3]; + void __iomem *membase; + int line[] __counted_by(nr); +}; + +static const struct serial_rs485 pci1xxxx_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND, + .delay_rts_after_send = 1, + /* Delay RTS before send is not supported */ +}; + +static int pci1xxxx_set_sys_lock(struct pci1xxxx_8250 *port) +{ + writel(UART_SYSLOCK, port->membase + UART_SYSLOCK_REG); + return readl(port->membase + UART_SYSLOCK_REG); +} + +static int pci1xxxx_acquire_sys_lock(struct pci1xxxx_8250 *port) +{ + u32 regval; + + return readx_poll_timeout(pci1xxxx_set_sys_lock, port, regval, + (regval & UART_SYSLOCK), + SYSLOCK_SLEEP_TIMEOUT, + SYSLOCK_RETRY_CNT * SYSLOCK_SLEEP_TIMEOUT); +} + +static void pci1xxxx_release_sys_lock(struct pci1xxxx_8250 *port) +{ + writel(0x0, port->membase + UART_SYSLOCK_REG); +} static const int logical_to_physical_port_idx[][MAX_PORTS] = { {0, 1, 2, 3}, /* PCI12000, PCI11010, PCI11101, PCI11400, PCI11414 */ @@ -104,12 +172,6 @@ static const int logical_to_physical_port_idx[][MAX_PORTS] = { {3, -1, -1, -1}, /* PCI1p3 */ }; -struct pci1xxxx_8250 { - unsigned int nr; - void __iomem *membase; - int line[] __counted_by(nr); -}; - static int pci1xxxx_get_num_ports(struct pci_dev *dev) { switch (dev->subsystem_device) { @@ -205,12 +267,102 @@ static int pci1xxxx_rs485_config(struct uart_port *port, return 0; } -static const struct serial_rs485 pci1xxxx_rs485_supported = { - .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | - SER_RS485_RTS_AFTER_SEND, - .delay_rts_after_send = 1, - /* Delay RTS before send is not supported */ -}; +static u32 pci1xxxx_read_burst_status(struct uart_port *port) +{ + u32 status; + + status = readl(port->membase + UART_BURST_STATUS_REG); + if (status & UART_BST_STAT_LSR_RX_ERR_MASK) { + if (status & UART_BST_STAT_LSR_OVERRUN_ERR) { + writeb(UART_LSR_OVERRUN_ERR_CLR, + port->membase + UART_FIFO_CTL); + port->icount.overrun++; + } + + if (status & UART_BST_STAT_LSR_FRAME_ERR) + port->icount.frame++; + + if (status & UART_BST_STAT_LSR_PARITY_ERR) + port->icount.parity++; + } + return status; +} + +static void pci1xxxx_process_read_data(struct uart_port *port, + unsigned char *rx_buff, u32 *buff_index, + u32 *valid_byte_count) +{ + u32 valid_burst_count = *valid_byte_count / UART_BURST_SIZE; + u32 *burst_buf; + + /* + * Depending on the RX Trigger Level the number of bytes that can be + * stored in RX FIFO at a time varies. Each transaction reads data + * in DWORDs. If there are less than four remaining valid_byte_count + * to read, the data is received one byte at a time. + */ + while (valid_burst_count--) { + if (*buff_index >= (RX_BUF_SIZE - UART_BURST_SIZE)) + break; + burst_buf = (u32 *)&rx_buff[*buff_index]; + *burst_buf = readl(port->membase + UART_RX_BURST_FIFO); + *buff_index += UART_BURST_SIZE; + *valid_byte_count -= UART_BURST_SIZE; + } + + while (*valid_byte_count) { + if (*buff_index >= RX_BUF_SIZE) + break; + rx_buff[*buff_index] = readb(port->membase + + UART_RX_BYTE_FIFO); + *buff_index += UART_BYTE_SIZE; + *valid_byte_count -= UART_BYTE_SIZE; + } +} + +static void pci1xxxx_rx_burst(struct uart_port *port, u32 uart_status) +{ + u32 valid_byte_count = uart_status & UART_BST_STAT_RX_COUNT_MASK; + struct tty_port *tty_port = &port->state->port; + unsigned char rx_buff[RX_BUF_SIZE]; + u32 buff_index = 0; + u32 copied_len; + + if (valid_byte_count != 0 && + valid_byte_count < RX_BUF_SIZE) { + pci1xxxx_process_read_data(port, rx_buff, &buff_index, + &valid_byte_count); + + copied_len = (u32)tty_insert_flip_string(tty_port, rx_buff, + buff_index); + + if (copied_len != buff_index) + port->icount.overrun += buff_index - copied_len; + + port->icount.rx += buff_index; + tty_flip_buffer_push(tty_port); + } +} + +static int pci1xxxx_handle_irq(struct uart_port *port) +{ + unsigned long flags; + u32 status; + + status = pci1xxxx_read_burst_status(port); + + if (status & UART_BST_STAT_IIR_INT_PEND) + return 0; + + spin_lock_irqsave(&port->lock, flags); + + if (status & UART_BST_STAT_LSR_RX_MASK) + pci1xxxx_rx_burst(port, status); + + spin_unlock_irqrestore(&port->lock, flags); + + return 1; +} static bool pci1xxxx_port_suspend(int line) { @@ -323,7 +475,7 @@ static int pci1xxxx_resume(struct device *dev) } static int pci1xxxx_setup(struct pci_dev *pdev, - struct uart_8250_port *port, int port_idx) + struct uart_8250_port *port, int port_idx, int rev) { int ret; @@ -335,6 +487,10 @@ static int pci1xxxx_setup(struct pci_dev *pdev, port->port.rs485_config = pci1xxxx_rs485_config; port->port.rs485_supported = pci1xxxx_rs485_supported; + /* From C0 rev Burst operation is supported */ + if (rev >= 0xC0) + port->port.handle_irq = pci1xxxx_handle_irq; + ret = serial8250_pci_setup_port(pdev, port, 0, PORT_OFFSET * port_idx, 0); if (ret < 0) return ret; @@ -370,6 +526,27 @@ static int pci1xxxx_logical_to_physical_port_translate(int subsys_dev, int port) return logical_to_physical_port_idx[0][port]; } +static int pci1xxxx_get_device_revision(struct pci1xxxx_8250 *priv) +{ + u32 regval; + int ret; + + /* + * DEV REV is a system register, HW Syslock bit + * should be acquired before accessing the register + */ + ret = pci1xxxx_acquire_sys_lock(priv); + if (ret) + return ret; + + regval = readl(priv->membase + UART_DEV_REV_REG); + priv->dev_rev = regval & UART_DEV_REV_MASK; + + pci1xxxx_release_sys_lock(priv); + + return 0; +} + static int pci1xxxx_serial_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -381,6 +558,7 @@ static int pci1xxxx_serial_probe(struct pci_dev *pdev, int num_vectors; int subsys_dev; int port_idx; + int ret; int rc; rc = pcim_enable_device(pdev); @@ -397,6 +575,10 @@ static int pci1xxxx_serial_probe(struct pci_dev *pdev, if (!priv->membase) return -ENOMEM; + ret = pci1xxxx_get_device_revision(priv); + if (ret) + return ret; + pci_set_master(pdev); priv->nr = nr_ports; @@ -428,7 +610,7 @@ static int pci1xxxx_serial_probe(struct pci_dev *pdev, else uart.port.irq = pci_irq_vector(pdev, 0); - rc = pci1xxxx_setup(pdev, &uart, port_idx); + rc = pci1xxxx_setup(pdev, &uart, port_idx, priv->dev_rev); if (rc) { dev_warn(dev, "Failed to setup port %u\n", i); continue; diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c index a5b3ea27fc90..77686da42ce8 100644 --- a/drivers/tty/serial/8250/8250_pxa.c +++ b/drivers/tty/serial/8250/8250_pxa.c @@ -146,20 +146,18 @@ static int serial_pxa_probe(struct platform_device *pdev) return ret; } -static int serial_pxa_remove(struct platform_device *pdev) +static void serial_pxa_remove(struct platform_device *pdev) { struct pxa8250_data *data = platform_get_drvdata(pdev); serial8250_unregister_port(data->line); clk_unprepare(data->clk); - - return 0; } static struct platform_driver serial_pxa_driver = { .probe = serial_pxa_probe, - .remove = serial_pxa_remove, + .remove_new = serial_pxa_remove, .driver = { .name = "pxa2xx-uart", diff --git a/drivers/tty/serial/8250/8250_tegra.c b/drivers/tty/serial/8250/8250_tegra.c index 89956bbf34d9..ba352262df75 100644 --- a/drivers/tty/serial/8250/8250_tegra.c +++ b/drivers/tty/serial/8250/8250_tegra.c @@ -128,15 +128,13 @@ err_clkdisable: return ret; } -static int tegra_uart_remove(struct platform_device *pdev) +static void tegra_uart_remove(struct platform_device *pdev) { struct tegra_uart *uart = platform_get_drvdata(pdev); serial8250_unregister_port(uart->line); reset_control_assert(uart->rst); clk_disable_unprepare(uart->clk); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -192,7 +190,7 @@ static struct platform_driver tegra_uart_driver = { .acpi_match_table = ACPI_PTR(tegra_uart_acpi_match), }, .probe = tegra_uart_probe, - .remove = tegra_uart_remove, + .remove_new = tegra_uart_remove, }; module_platform_driver(tegra_uart_driver); diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index a405155264b1..6399a38ecce2 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -241,14 +241,12 @@ static int uniphier_uart_probe(struct platform_device *pdev) return 0; } -static int uniphier_uart_remove(struct platform_device *pdev) +static void uniphier_uart_remove(struct platform_device *pdev) { struct uniphier8250_priv *priv = platform_get_drvdata(pdev); serial8250_unregister_port(priv->line); clk_disable_unprepare(priv->clk); - - return 0; } static int __maybe_unused uniphier_uart_suspend(struct device *dev) @@ -293,7 +291,7 @@ MODULE_DEVICE_TABLE(of, uniphier_uart_match); static struct platform_driver uniphier_uart_platform_driver = { .probe = uniphier_uart_probe, - .remove = uniphier_uart_remove, + .remove_new = uniphier_uart_remove, .driver = { .name = "uniphier-uart", .of_match_table = uniphier_uart_match, diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index dc2ef05a10eb..2056aed46688 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -90,12 +90,6 @@ struct serial_info { const struct serial_quirk *quirk; }; -struct serial_cfg_mem { - tuple_t tuple; - cisparse_t parse; - u_char buf[256]; -}; - /* * vers_1 5.0, "Brain Boxes", "2-Port RS232 card", "r6" * manfid 0x0160, 0x0104 diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 732c893c8d16..ffcf4882b25f 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -87,7 +87,7 @@ config SERIAL_EARLYCON_SEMIHOST config SERIAL_EARLYCON_RISCV_SBI bool "Early console using RISC-V SBI" - depends on RISCV_SBI_V01 + depends on RISCV_SBI select SERIAL_CORE select SERIAL_CORE_CONSOLE select SERIAL_EARLYCON @@ -532,6 +532,9 @@ config SERIAL_UARTLITE_NR_UARTS help Set this to the number of uartlites in your system, or the number you think you might implement. + If maximum number of uartlite serial ports is more than 4, then the + driver uses dynamic allocation instead of static allocation for major + number. config SERIAL_SUNCORE bool diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index 7090b251dd4d..effcba71ea77 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -425,7 +425,7 @@ static int altera_jtaguart_probe(struct platform_device *pdev) return 0; } -static int altera_jtaguart_remove(struct platform_device *pdev) +static void altera_jtaguart_remove(struct platform_device *pdev) { struct uart_port *port; int i = pdev->id; @@ -436,8 +436,6 @@ static int altera_jtaguart_remove(struct platform_device *pdev) port = &altera_jtaguart_ports[i]; uart_remove_one_port(&altera_jtaguart_driver, port); iounmap(port->membase); - - return 0; } #ifdef CONFIG_OF @@ -451,7 +449,7 @@ MODULE_DEVICE_TABLE(of, altera_jtaguart_match); static struct platform_driver altera_jtaguart_platform_driver = { .probe = altera_jtaguart_probe, - .remove = altera_jtaguart_remove, + .remove_new = altera_jtaguart_remove, .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(altera_jtaguart_match), diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 77835ac68df2..897f0995b2fe 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -305,7 +305,7 @@ static int altera_uart_startup(struct uart_port *port) int ret; ret = request_irq(port->irq, altera_uart_interrupt, 0, - DRV_NAME, port); + dev_name(port->dev), port); if (ret) { pr_err(DRV_NAME ": unable to attach Altera UART %d " "interrupt vector=%d\n", port->line, port->irq); @@ -595,7 +595,7 @@ static int altera_uart_probe(struct platform_device *pdev) return 0; } -static int altera_uart_remove(struct platform_device *pdev) +static void altera_uart_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); @@ -604,8 +604,6 @@ static int altera_uart_remove(struct platform_device *pdev) port->mapbase = 0; iounmap(port->membase); } - - return 0; } #ifdef CONFIG_OF @@ -619,7 +617,7 @@ MODULE_DEVICE_TABLE(of, altera_uart_match); static struct platform_driver altera_uart_platform_driver = { .probe = altera_uart_probe, - .remove = altera_uart_remove, + .remove_new = altera_uart_remove, .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(altera_uart_match), diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index b7635363373e..fccec1698a54 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -50,8 +50,8 @@ #define AMBA_ISR_PASS_LIMIT 256 -#define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) -#define UART_DUMMY_DR_RX (1 << 16) +#define UART_DR_ERROR (UART011_DR_OE | UART011_DR_BE | UART011_DR_PE | UART011_DR_FE) +#define UART_DUMMY_DR_RX BIT(16) enum { REG_DR, @@ -125,7 +125,7 @@ static unsigned int get_fifosize_arm(struct amba_device *dev) static struct vendor_data vendor_arm = { .reg_offset = pl011_std_offsets, - .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, + .ifls = UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8, .fr_busy = UART01x_FR_BUSY, .fr_dsr = UART01x_FR_DSR, .fr_cts = UART01x_FR_CTS, @@ -203,7 +203,7 @@ static unsigned int get_fifosize_st(struct amba_device *dev) static struct vendor_data vendor_st = { .reg_offset = pl011_st_offsets, - .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF, + .ifls = UART011_IFLS_RX_HALF | UART011_IFLS_TX_HALF, .fr_busy = UART01x_FR_BUSY, .fr_dsr = UART01x_FR_DSR, .fr_cts = UART01x_FR_CTS, @@ -277,13 +277,13 @@ struct uart_amba_port { static unsigned int pl011_tx_empty(struct uart_port *port); static unsigned int pl011_reg_to_offset(const struct uart_amba_port *uap, - unsigned int reg) + unsigned int reg) { return uap->reg_offset[reg]; } static unsigned int pl011_read(const struct uart_amba_port *uap, - unsigned int reg) + unsigned int reg) { void __iomem *addr = uap->port.membase + pl011_reg_to_offset(uap, reg); @@ -292,7 +292,7 @@ static unsigned int pl011_read(const struct uart_amba_port *uap, } static void pl011_write(unsigned int val, const struct uart_amba_port *uap, - unsigned int reg) + unsigned int reg) { void __iomem *addr = uap->port.membase + pl011_reg_to_offset(uap, reg); @@ -330,10 +330,11 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap) uap->port.icount.brk++; if (uart_handle_break(&uap->port)) continue; - } else if (ch & UART011_DR_PE) + } else if (ch & UART011_DR_PE) { uap->port.icount.parity++; - else if (ch & UART011_DR_FE) + } else if (ch & UART011_DR_FE) { uap->port.icount.frame++; + } if (ch & UART011_DR_OE) uap->port.icount.overrun++; @@ -358,7 +359,6 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap) return fifotaken; } - /* * All the DMA operation mode stuff goes inside this ifdef. * This assumes that you have a generic DMA device interface, @@ -369,7 +369,7 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap) #define PL011_DMA_BUFFER_SIZE PAGE_SIZE static int pl011_dmabuf_init(struct dma_chan *chan, struct pl011_dmabuf *db, - enum dma_data_direction dir) + enum dma_data_direction dir) { db->buf = dma_alloc_coherent(chan->device->dev, PL011_DMA_BUFFER_SIZE, &db->dma, GFP_KERNEL); @@ -381,7 +381,7 @@ static int pl011_dmabuf_init(struct dma_chan *chan, struct pl011_dmabuf *db, } static void pl011_dmabuf_free(struct dma_chan *chan, struct pl011_dmabuf *db, - enum dma_data_direction dir) + enum dma_data_direction dir) { if (db->buf) { dma_free_coherent(chan->device->dev, @@ -424,7 +424,7 @@ static void pl011_dma_probe(struct uart_amba_port *uap) dma_cap_set(DMA_SLAVE, mask); chan = dma_request_channel(mask, plat->dma_filter, - plat->dma_tx_param); + plat->dma_tx_param); if (!chan) { dev_err(uap->port.dev, "no TX DMA channel!\n"); return; @@ -438,9 +438,9 @@ static void pl011_dma_probe(struct uart_amba_port *uap) dma_chan_name(uap->dmatx.chan)); /* Optionally make use of an RX channel as well */ - chan = dma_request_slave_channel(dev, "rx"); + chan = dma_request_chan(dev, "rx"); - if (!chan && plat && plat->dma_rx_param) { + if (IS_ERR(chan) && plat && plat->dma_rx_param) { chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param); if (!chan) { @@ -449,7 +449,7 @@ static void pl011_dma_probe(struct uart_amba_port *uap) } } - if (chan) { + if (!IS_ERR(chan)) { struct dma_slave_config rx_conf = { .src_addr = uap->port.mapbase + pl011_reg_to_offset(uap, REG_DR), @@ -465,12 +465,12 @@ static void pl011_dma_probe(struct uart_amba_port *uap) * If the controller does, check for suitable residue processing * otherwise assime all is well. */ - if (0 == dma_get_slave_caps(chan, &caps)) { + if (dma_get_slave_caps(chan, &caps) == 0) { if (caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR) { dma_release_channel(chan); dev_info(uap->port.dev, - "RX DMA disabled - no residue processing\n"); + "RX DMA disabled - no residue processing\n"); return; } } @@ -499,18 +499,16 @@ static void pl011_dma_probe(struct uart_amba_port *uap) else uap->dmarx.poll_timeout = 3000; } else if (!plat && dev->of_node) { - uap->dmarx.auto_poll_rate = of_property_read_bool( - dev->of_node, "auto-poll"); + uap->dmarx.auto_poll_rate = + of_property_read_bool(dev->of_node, "auto-poll"); if (uap->dmarx.auto_poll_rate) { u32 x; - if (0 == of_property_read_u32(dev->of_node, - "poll-rate-ms", &x)) + if (of_property_read_u32(dev->of_node, "poll-rate-ms", &x) == 0) uap->dmarx.poll_rate = x; else uap->dmarx.poll_rate = 100; - if (0 == of_property_read_u32(dev->of_node, - "poll-timeout-ms", &x)) + if (of_property_read_u32(dev->of_node, "poll-timeout-ms", &x) == 0) uap->dmarx.poll_timeout = x; else uap->dmarx.poll_timeout = 3000; @@ -547,7 +545,7 @@ static void pl011_dma_tx_callback(void *data) uart_port_lock_irqsave(&uap->port, &flags); if (uap->dmatx.queued) dma_unmap_single(dmatx->chan->device->dev, dmatx->dma, - dmatx->len, DMA_TO_DEVICE); + dmatx->len, DMA_TO_DEVICE); dmacr = uap->dmacr; uap->dmacr = dmacr & ~UART011_TXDMAE; @@ -618,9 +616,9 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) if (count > PL011_DMA_BUFFER_SIZE) count = PL011_DMA_BUFFER_SIZE; - if (xmit->tail < xmit->head) + if (xmit->tail < xmit->head) { memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], count); - else { + } else { size_t first = UART_XMIT_SIZE - xmit->tail; size_t second; @@ -643,7 +641,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) } desc = dmaengine_prep_slave_single(chan, dmatx->dma, dmatx->len, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dma_unmap_single(dma_dev->dev, dmatx->dma, dmatx->len, DMA_TO_DEVICE); uap->dmatx.queued = false; @@ -754,8 +752,9 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap) if (pl011_dma_tx_refill(uap) > 0) { uap->im &= ~UART011_TXIM; pl011_write(uap->im, uap, REG_IMSC); - } else + } else { ret = false; + } } else if (!(uap->dmacr & UART011_TXDMAE)) { uap->dmacr |= UART011_TXDMAE; pl011_write(uap->dmacr, uap, REG_DMACR); @@ -832,8 +831,8 @@ static int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap) dbuf = uap->dmarx.use_buf_b ? &uap->dmarx.dbuf_b : &uap->dmarx.dbuf_a; desc = dmaengine_prep_slave_single(rxchan, dbuf->dma, dbuf->len, - DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); /* * If the DMA engine is busy and cannot prepare a * channel, no big deal, the driver will fall back @@ -889,14 +888,12 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap, /* Pick the remain data from the DMA */ if (pending) { - /* * First take all chars in the DMA pipe, then look in the FIFO. * Note that tty_insert_flip_buf() tries to take as many chars * as it can. */ - dma_count = tty_insert_flip_string(port, dbuf->buf + dmataken, - pending); + dma_count = tty_insert_flip_string(port, dbuf->buf + dmataken, pending); uap->port.icount.rx += dma_count; if (dma_count < pending) @@ -978,8 +975,8 @@ static void pl011_dma_rx_irq(struct uart_amba_port *uap) /* Switch buffer & re-trigger DMA job */ dmarx->use_buf_b = !dmarx->use_buf_b; if (pl011_dma_rx_trigger_dma(uap)) { - dev_dbg(uap->port.dev, "could not retrigger RX DMA job " - "fall back to interrupt mode\n"); + dev_dbg(uap->port.dev, + "could not retrigger RX DMA job fall back to interrupt mode\n"); uap->im |= UART011_RXIM; pl011_write(uap->im, uap, REG_IMSC); } @@ -1026,8 +1023,8 @@ static void pl011_dma_rx_callback(void *data) * get some IRQ immediately from RX. */ if (ret) { - dev_dbg(uap->port.dev, "could not retrigger RX DMA job " - "fall back to interrupt mode\n"); + dev_dbg(uap->port.dev, + "could not retrigger RX DMA job fall back to interrupt mode\n"); uap->im |= UART011_RXIM; pl011_write(uap->im, uap, REG_IMSC); } @@ -1072,7 +1069,7 @@ static void pl011_dma_rx_poll(struct timer_list *t) dmataken = dbuf->len - dmarx->last_residue; size = dmarx->last_residue - state.residue; dma_count = tty_insert_flip_string(port, dbuf->buf + dmataken, - size); + size); if (dma_count == size) dmarx->last_residue = state.residue; dmarx->last_jiffies = jiffies; @@ -1085,7 +1082,6 @@ static void pl011_dma_rx_poll(struct timer_list *t) */ if (jiffies_to_msecs(jiffies - dmarx->last_jiffies) > uap->dmarx.poll_timeout) { - uart_port_lock_irqsave(&uap->port, &flags); pl011_dma_rx_stop(uap); uap->im |= UART011_RXIM; @@ -1097,7 +1093,7 @@ static void pl011_dma_rx_poll(struct timer_list *t) del_timer(&uap->dmarx.timer); } else { mod_timer(&uap->dmarx.timer, - jiffies + msecs_to_jiffies(uap->dmarx.poll_rate)); + jiffies + msecs_to_jiffies(uap->dmarx.poll_rate)); } } @@ -1113,7 +1109,6 @@ static void pl011_dma_startup(struct uart_amba_port *uap) uap->dmatx.buf = kmalloc(PL011_DMA_BUFFER_SIZE, GFP_KERNEL | __GFP_DMA); if (!uap->dmatx.buf) { - dev_err(uap->port.dev, "no memory for DMA TX buffer\n"); uap->port.fifosize = uap->fifosize; return; } @@ -1129,7 +1124,7 @@ static void pl011_dma_startup(struct uart_amba_port *uap) /* Allocate and map DMA RX buffers */ ret = pl011_dmabuf_init(uap->dmarx.chan, &uap->dmarx.dbuf_a, - DMA_FROM_DEVICE); + DMA_FROM_DEVICE); if (ret) { dev_err(uap->port.dev, "failed to init DMA %s: %d\n", "RX buffer A", ret); @@ -1137,12 +1132,12 @@ static void pl011_dma_startup(struct uart_amba_port *uap) } ret = pl011_dmabuf_init(uap->dmarx.chan, &uap->dmarx.dbuf_b, - DMA_FROM_DEVICE); + DMA_FROM_DEVICE); if (ret) { dev_err(uap->port.dev, "failed to init DMA %s: %d\n", "RX buffer B", ret); pl011_dmabuf_free(uap->dmarx.chan, &uap->dmarx.dbuf_a, - DMA_FROM_DEVICE); + DMA_FROM_DEVICE); goto skip_rx; } @@ -1164,13 +1159,12 @@ skip_rx: if (uap->using_rx_dma) { if (pl011_dma_rx_trigger_dma(uap)) - dev_dbg(uap->port.dev, "could not trigger initial " - "RX DMA job, fall back to interrupt mode\n"); + dev_dbg(uap->port.dev, + "could not trigger initial RX DMA job, fall back to interrupt mode\n"); if (uap->dmarx.poll_rate) { timer_setup(&uap->dmarx.timer, pl011_dma_rx_poll, 0); mod_timer(&uap->dmarx.timer, - jiffies + - msecs_to_jiffies(uap->dmarx.poll_rate)); + jiffies + msecs_to_jiffies(uap->dmarx.poll_rate)); uap->dmarx.last_residue = PL011_DMA_BUFFER_SIZE; uap->dmarx.last_jiffies = jiffies; } @@ -1359,8 +1353,8 @@ static void pl011_stop_rx(struct uart_port *port) struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); - uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM| - UART011_PEIM|UART011_BEIM|UART011_OEIM); + uap->im &= ~(UART011_RXIM | UART011_RTIM | UART011_FEIM | + UART011_PEIM | UART011_BEIM | UART011_OEIM); pl011_write(uap->im, uap, REG_IMSC); pl011_dma_rx_stop(uap); @@ -1380,7 +1374,7 @@ static void pl011_enable_ms(struct uart_port *port) struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); - uap->im |= UART011_RIMIM|UART011_CTSMIM|UART011_DCDMIM|UART011_DSRMIM; + uap->im |= UART011_RIMIM | UART011_CTSMIM | UART011_DCDMIM | UART011_DSRMIM; pl011_write(uap->im, uap, REG_IMSC); } @@ -1398,8 +1392,8 @@ __acquires(&uap->port.lock) */ if (pl011_dma_rx_available(uap)) { if (pl011_dma_rx_trigger_dma(uap)) { - dev_dbg(uap->port.dev, "could not trigger RX DMA job " - "fall back to interrupt mode again\n"); + dev_dbg(uap->port.dev, + "could not trigger RX DMA job fall back to interrupt mode again\n"); uap->im |= UART011_RXIM; pl011_write(uap->im, uap, REG_IMSC); } else { @@ -1409,8 +1403,7 @@ __acquires(&uap->port.lock) uap->dmarx.last_jiffies = jiffies; uap->dmarx.last_residue = PL011_DMA_BUFFER_SIZE; mod_timer(&uap->dmarx.timer, - jiffies + - msecs_to_jiffies(uap->dmarx.poll_rate)); + jiffies + msecs_to_jiffies(uap->dmarx.poll_rate)); } #endif } @@ -1557,18 +1550,17 @@ static irqreturn_t pl011_int(int irq, void *dev_id) do { check_apply_cts_event_workaround(uap); - pl011_write(status & ~(UART011_TXIS|UART011_RTIS| - UART011_RXIS), + pl011_write(status & ~(UART011_TXIS | UART011_RTIS | UART011_RXIS), uap, REG_ICR); - if (status & (UART011_RTIS|UART011_RXIS)) { + if (status & (UART011_RTIS | UART011_RXIS)) { if (pl011_dma_rx_running(uap)) pl011_dma_rx_irq(uap); else pl011_rx_chars(uap); } - if (status & (UART011_DSRMIS|UART011_DCDMIS| - UART011_CTSMIS|UART011_RIMIS)) + if (status & (UART011_DSRMIS | UART011_DCDMIS | + UART011_CTSMIS | UART011_RIMIS)) pl011_modem_status(uap); if (status & UART011_TXIS) pl011_tx_chars(uap, true); @@ -1598,6 +1590,12 @@ static unsigned int pl011_tx_empty(struct uart_port *port) 0 : TIOCSER_TEMT; } +static void pl011_maybe_set_bit(bool cond, unsigned int *ptr, unsigned int mask) +{ + if (cond) + *ptr |= mask; +} + static unsigned int pl011_get_mctrl(struct uart_port *port) { struct uart_amba_port *uap = @@ -1605,18 +1603,22 @@ static unsigned int pl011_get_mctrl(struct uart_port *port) unsigned int result = 0; unsigned int status = pl011_read(uap, REG_FR); -#define TIOCMBIT(uartbit, tiocmbit) \ - if (status & uartbit) \ - result |= tiocmbit + pl011_maybe_set_bit(status & UART01x_FR_DCD, &result, TIOCM_CAR); + pl011_maybe_set_bit(status & uap->vendor->fr_dsr, &result, TIOCM_DSR); + pl011_maybe_set_bit(status & uap->vendor->fr_cts, &result, TIOCM_CTS); + pl011_maybe_set_bit(status & uap->vendor->fr_ri, &result, TIOCM_RNG); - TIOCMBIT(UART01x_FR_DCD, TIOCM_CAR); - TIOCMBIT(uap->vendor->fr_dsr, TIOCM_DSR); - TIOCMBIT(uap->vendor->fr_cts, TIOCM_CTS); - TIOCMBIT(uap->vendor->fr_ri, TIOCM_RNG); -#undef TIOCMBIT return result; } +static void pl011_assign_bit(bool cond, unsigned int *ptr, unsigned int mask) +{ + if (cond) + *ptr |= mask; + else + *ptr &= ~mask; +} + static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct uart_amba_port *uap = @@ -1625,23 +1627,16 @@ static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl) cr = pl011_read(uap, REG_CR); -#define TIOCMBIT(tiocmbit, uartbit) \ - if (mctrl & tiocmbit) \ - cr |= uartbit; \ - else \ - cr &= ~uartbit - - TIOCMBIT(TIOCM_RTS, UART011_CR_RTS); - TIOCMBIT(TIOCM_DTR, UART011_CR_DTR); - TIOCMBIT(TIOCM_OUT1, UART011_CR_OUT1); - TIOCMBIT(TIOCM_OUT2, UART011_CR_OUT2); - TIOCMBIT(TIOCM_LOOP, UART011_CR_LBE); + pl011_assign_bit(mctrl & TIOCM_RTS, &cr, UART011_CR_RTS); + pl011_assign_bit(mctrl & TIOCM_DTR, &cr, UART011_CR_DTR); + pl011_assign_bit(mctrl & TIOCM_OUT1, &cr, UART011_CR_OUT1); + pl011_assign_bit(mctrl & TIOCM_OUT2, &cr, UART011_CR_OUT2); + pl011_assign_bit(mctrl & TIOCM_LOOP, &cr, UART011_CR_LBE); if (port->status & UPSTAT_AUTORTS) { /* We need to disable auto-RTS if we want to turn RTS off */ - TIOCMBIT(TIOCM_RTS, UART011_CR_RTSEN); + pl011_assign_bit(mctrl & TIOCM_RTS, &cr, UART011_CR_RTSEN); } -#undef TIOCMBIT pl011_write(cr, uap, REG_CR); } @@ -1707,8 +1702,7 @@ static int pl011_get_poll_char(struct uart_port *port) return pl011_read(uap, REG_DR); } -static void pl011_put_poll_char(struct uart_port *port, - unsigned char ch) +static void pl011_put_poll_char(struct uart_port *port, unsigned char ch) { struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); @@ -1909,14 +1903,13 @@ static int sbsa_uart_startup(struct uart_port *port) return 0; } -static void pl011_shutdown_channel(struct uart_amba_port *uap, - unsigned int lcrh) +static void pl011_shutdown_channel(struct uart_amba_port *uap, unsigned int lcrh) { - unsigned long val; + unsigned long val; - val = pl011_read(uap, lcrh); - val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN); - pl011_write(val, uap, lcrh); + val = pl011_read(uap, lcrh); + val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN); + pl011_write(val, uap, lcrh); } /* @@ -2065,7 +2058,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, uap->dmarx.poll_rate = DIV_ROUND_UP(10000000, baud); #endif - if (baud > port->uartclk/16) + if (baud > port->uartclk / 16) quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); else quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); @@ -2147,9 +2140,9 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, * else we see data corruption. */ if (uap->vendor->oversampling) { - if ((baud >= 3000000) && (baud < 3250000) && (quot > 1)) + if (baud >= 3000000 && baud < 3250000 && quot > 1) quot -= 1; - else if ((baud > 3250000) && (quot > 2)) + else if (baud > 3250000 && quot > 2) quot -= 2; } /* Set baud rate */ @@ -2218,13 +2211,14 @@ static void pl011_config_port(struct uart_port *port, int flags) static int pl011_verify_port(struct uart_port *port, struct serial_struct *ser) { int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) ret = -EINVAL; if (ser->irq < 0 || ser->irq >= nr_irqs) ret = -EINVAL; if (ser->baud_base < 9600) ret = -EINVAL; - if (port->mapbase != (unsigned long) ser->iomem_base) + if (port->mapbase != (unsigned long)ser->iomem_base) ret = -EINVAL; return ret; } @@ -2369,35 +2363,34 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) static void pl011_console_get_options(struct uart_amba_port *uap, int *baud, int *parity, int *bits) { - if (pl011_read(uap, REG_CR) & UART01x_CR_UARTEN) { - unsigned int lcr_h, ibrd, fbrd; + unsigned int lcr_h, ibrd, fbrd; - lcr_h = pl011_read(uap, REG_LCRH_TX); + if (!(pl011_read(uap, REG_CR) & UART01x_CR_UARTEN)) + return; - *parity = 'n'; - if (lcr_h & UART01x_LCRH_PEN) { - if (lcr_h & UART01x_LCRH_EPS) - *parity = 'e'; - else - *parity = 'o'; - } + lcr_h = pl011_read(uap, REG_LCRH_TX); - if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7) - *bits = 7; + *parity = 'n'; + if (lcr_h & UART01x_LCRH_PEN) { + if (lcr_h & UART01x_LCRH_EPS) + *parity = 'e'; else - *bits = 8; + *parity = 'o'; + } + + if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7) + *bits = 7; + else + *bits = 8; - ibrd = pl011_read(uap, REG_IBRD); - fbrd = pl011_read(uap, REG_FBRD); + ibrd = pl011_read(uap, REG_IBRD); + fbrd = pl011_read(uap, REG_FBRD); - *baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd); + *baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd); - if (uap->vendor->oversampling) { - if (pl011_read(uap, REG_CR) - & ST_UART011_CR_OVSFACT) - *baud *= 2; - } - } + if (uap->vendor->oversampling && + (pl011_read(uap, REG_CR) & ST_UART011_CR_OVSFACT)) + *baud *= 2; } static int pl011_console_setup(struct console *co, char *options) @@ -2533,7 +2526,7 @@ static void qdf2400_e44_putc(struct uart_port *port, unsigned char c) cpu_relax(); } -static void qdf2400_e44_early_write(struct console *con, const char *s, unsigned n) +static void qdf2400_e44_early_write(struct console *con, const char *s, unsigned int n) { struct earlycon_device *dev = con->data; @@ -2552,7 +2545,7 @@ static void pl011_putc(struct uart_port *port, unsigned char c) cpu_relax(); } -static void pl011_early_write(struct console *con, const char *s, unsigned n) +static void pl011_early_write(struct console *con, const char *s, unsigned int n) { struct earlycon_device *dev = con->data; @@ -2613,7 +2606,9 @@ static int __init pl011_early_console_setup(struct earlycon_device *device, return 0; } + OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup); + OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", pl011_early_console_setup); /* @@ -2636,6 +2631,7 @@ qdf2400_e44_early_console_setup(struct earlycon_device *device, device->con->write = qdf2400_e44_early_write; return 0; } + EARLYCON_DECLARE(qdf2400_e44, qdf2400_e44_early_console_setup); #else @@ -2655,8 +2651,8 @@ static struct uart_driver amba_reg = { static int pl011_probe_dt_alias(int index, struct device *dev) { struct device_node *np; - static bool seen_dev_with_alias = false; - static bool seen_dev_without_alias = false; + static bool seen_dev_with_alias; + static bool seen_dev_without_alias; int ret = index; if (!IS_ENABLED(CONFIG_OF)) @@ -2672,7 +2668,7 @@ static int pl011_probe_dt_alias(int index, struct device *dev) ret = index; } else { seen_dev_with_alias = true; - if (ret >= ARRAY_SIZE(amba_ports) || amba_ports[ret] != NULL) { + if (ret >= ARRAY_SIZE(amba_ports) || amba_ports[ret]) { dev_warn(dev, "requested serial port %d not available.\n", ret); ret = index; } @@ -2706,7 +2702,7 @@ static int pl011_find_free_port(void) int i; for (i = 0; i < ARRAY_SIZE(amba_ports); i++) - if (amba_ports[i] == NULL) + if (!amba_ports[i]) return i; return -EBUSY; @@ -2873,6 +2869,22 @@ static int pl011_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pl011_dev_pm_ops, pl011_suspend, pl011_resume); +#ifdef CONFIG_ACPI_SPCR_TABLE +static void qpdf2400_erratum44_workaround(struct device *dev, + struct uart_amba_port *uap) +{ + if (!qdf2400_e44_present) + return; + + dev_info(dev, "working around QDF2400 SoC erratum 44\n"); + uap->vendor = &vendor_qdt_qdf2400_e44; +} +#else +static void qpdf2400_erratum44_workaround(struct device *dev, + struct uart_amba_port *uap) +{ /* empty */ } +#endif + static int sbsa_uart_probe(struct platform_device *pdev) { struct uart_amba_port *uap; @@ -2908,13 +2920,8 @@ static int sbsa_uart_probe(struct platform_device *pdev) return ret; uap->port.irq = ret; -#ifdef CONFIG_ACPI_SPCR_TABLE - if (qdf2400_e44_present) { - dev_info(&pdev->dev, "working around QDF2400 SoC erratum 44\n"); - uap->vendor = &vendor_qdt_qdf2400_e44; - } else -#endif - uap->vendor = &vendor_sbsa; + uap->vendor = &vendor_sbsa; + qpdf2400_erratum44_workaround(&pdev->dev, uap); uap->reg_offset = uap->vendor->reg_offset; uap->fifosize = 32; @@ -2935,13 +2942,12 @@ static int sbsa_uart_probe(struct platform_device *pdev) return pl011_register_port(uap); } -static int sbsa_uart_remove(struct platform_device *pdev) +static void sbsa_uart_remove(struct platform_device *pdev) { struct uart_amba_port *uap = platform_get_drvdata(pdev); uart_remove_one_port(&amba_reg, &uap->port); pl011_unregister_port(uap); - return 0; } static const struct of_device_id sbsa_uart_of_match[] = { @@ -2959,7 +2965,7 @@ MODULE_DEVICE_TABLE(acpi, sbsa_uart_acpi_match); static struct platform_driver arm_sbsa_uart_platform_driver = { .probe = sbsa_uart_probe, - .remove = sbsa_uart_remove, + .remove_new = sbsa_uart_remove, .driver = { .name = "sbsa-uart", .pm = &pl011_dev_pm_ops, @@ -2998,7 +3004,7 @@ static struct amba_driver pl011_driver = { static int __init pl011_init(void) { - printk(KERN_INFO "Serial: AMBA PL011 UART driver\n"); + pr_info("Serial: AMBA PL011 UART driver\n"); if (platform_driver_register(&arm_sbsa_uart_platform_driver)) pr_warn("could not register SBSA UART platform driver\n"); diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c index 716cb014c028..364599f256db 100644 --- a/drivers/tty/serial/apbuart.c +++ b/drivers/tty/serial/apbuart.c @@ -122,7 +122,7 @@ static void apbuart_tx_chars(struct uart_port *port) { u8 ch; - uart_port_tx_limited(port, ch, port->fifosize >> 1, + uart_port_tx_limited(port, ch, port->fifosize, true, UART_PUT_CHAR(port, ch), ({})); diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index ffd234673177..8d09ace062e5 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -818,7 +818,7 @@ err_disable_clk: return ret; } -static int ar933x_uart_remove(struct platform_device *pdev) +static void ar933x_uart_remove(struct platform_device *pdev) { struct ar933x_uart_port *up; @@ -828,8 +828,6 @@ static int ar933x_uart_remove(struct platform_device *pdev) uart_remove_one_port(&ar933x_uart_driver, &up->port); clk_disable_unprepare(up->clk); } - - return 0; } #ifdef CONFIG_OF @@ -842,7 +840,7 @@ MODULE_DEVICE_TABLE(of, ar933x_uart_of_ids); static struct platform_driver ar933x_uart_platform_driver = { .probe = ar933x_uart_probe, - .remove = ar933x_uart_remove, + .remove_new = ar933x_uart_remove, .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(ar933x_uart_of_ids), diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 1946fafc3f3e..85667f709515 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1013,14 +1013,18 @@ static int atmel_prepare_tx_dma(struct uart_port *port) struct device *mfd_dev = port->dev->parent; dma_cap_mask_t mask; struct dma_slave_config config; + struct dma_chan *chan; int ret, nent; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - atmel_port->chan_tx = dma_request_slave_channel(mfd_dev, "tx"); - if (atmel_port->chan_tx == NULL) + chan = dma_request_chan(mfd_dev, "tx"); + if (IS_ERR(chan)) { + atmel_port->chan_tx = NULL; goto chan_err; + } + atmel_port->chan_tx = chan; dev_info(port->dev, "using %s for tx DMA transfers\n", dma_chan_name(atmel_port->chan_tx)); @@ -1188,6 +1192,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port) dma_cap_mask_t mask; struct dma_slave_config config; struct circ_buf *ring; + struct dma_chan *chan; int ret, nent; ring = &atmel_port->rx_ring; @@ -1195,9 +1200,12 @@ static int atmel_prepare_rx_dma(struct uart_port *port) dma_cap_zero(mask); dma_cap_set(DMA_CYCLIC, mask); - atmel_port->chan_rx = dma_request_slave_channel(mfd_dev, "rx"); - if (atmel_port->chan_rx == NULL) + chan = dma_request_chan(mfd_dev, "rx"); + if (IS_ERR(chan)) { + atmel_port->chan_rx = NULL; goto chan_err; + } + atmel_port->chan_rx = chan; dev_info(port->dev, "using %s for rx DMA transfers\n", dma_chan_name(atmel_port->chan_rx)); @@ -3001,7 +3009,7 @@ err: * protocol that needs bitbanging on IO lines, but use the regular serial * port in the normal case. */ -static int atmel_serial_remove(struct platform_device *pdev) +static void atmel_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); @@ -3020,8 +3028,6 @@ static int atmel_serial_remove(struct platform_device *pdev) clear_bit(port->line, atmel_ports_in_use); pdev->dev.of_node = NULL; - - return 0; } static SIMPLE_DEV_PM_OPS(atmel_serial_pm_ops, atmel_serial_suspend, @@ -3029,7 +3035,7 @@ static SIMPLE_DEV_PM_OPS(atmel_serial_pm_ops, atmel_serial_suspend, static struct platform_driver atmel_serial_driver = { .probe = atmel_serial_probe, - .remove = atmel_serial_remove, + .remove_new = atmel_serial_remove, .driver = { .name = "atmel_usart_serial", .of_match_table = of_match_ptr(atmel_serial_dt_ids), diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 4a08fd5ee61b..a3cefa153456 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -868,7 +868,7 @@ static int bcm_uart_probe(struct platform_device *pdev) return 0; } -static int bcm_uart_remove(struct platform_device *pdev) +static void bcm_uart_remove(struct platform_device *pdev) { struct uart_port *port; @@ -876,7 +876,6 @@ static int bcm_uart_remove(struct platform_device *pdev) uart_remove_one_port(&bcm_uart_driver, port); /* mark port as free */ ports[pdev->id].membase = NULL; - return 0; } static const struct of_device_id bcm63xx_of_match[] = { @@ -890,7 +889,7 @@ MODULE_DEVICE_TABLE(of, bcm63xx_of_match); */ static struct platform_driver bcm_uart_platform_driver = { .probe = bcm_uart_probe, - .remove = bcm_uart_remove, + .remove_new = bcm_uart_remove, .driver = { .name = "bcm63xx_uart", .of_match_table = bcm63xx_of_match, diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 55d19937efbd..7927725b8957 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -510,13 +510,11 @@ static int uart_clps711x_probe(struct platform_device *pdev) return ret; } -static int uart_clps711x_remove(struct platform_device *pdev) +static void uart_clps711x_remove(struct platform_device *pdev) { struct clps711x_port *s = platform_get_drvdata(pdev); uart_remove_one_port(&clps711x_uart, &s->port); - - return 0; } static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = { @@ -531,7 +529,7 @@ static struct platform_driver clps711x_uart_platform = { .of_match_table = of_match_ptr(clps711x_uart_dt_ids), }, .probe = uart_clps711x_probe, - .remove = uart_clps711x_remove, + .remove_new = uart_clps711x_remove, }; static int __init uart_clps711x_init(void) diff --git a/drivers/tty/serial/cpm_uart.c b/drivers/tty/serial/cpm_uart.c index be4af6eda4c2..df56c6c5afd0 100644 --- a/drivers/tty/serial/cpm_uart.c +++ b/drivers/tty/serial/cpm_uart.c @@ -1549,13 +1549,11 @@ static int cpm_uart_probe(struct platform_device *ofdev) return ret; } -static int cpm_uart_remove(struct platform_device *ofdev) +static void cpm_uart_remove(struct platform_device *ofdev) { struct uart_cpm_port *pinfo = platform_get_drvdata(ofdev); uart_remove_one_port(&cpm_reg, &pinfo->port); - - return 0; } static const struct of_device_id cpm_uart_match[] = { @@ -1581,7 +1579,7 @@ static struct platform_driver cpm_uart_driver = { .of_match_table = cpm_uart_match, }, .probe = cpm_uart_probe, - .remove = cpm_uart_remove, + .remove_new = cpm_uart_remove, }; static int __init cpm_uart_init(void) diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c index 5004125f3045..e419c4bde8b7 100644 --- a/drivers/tty/serial/digicolor-usart.c +++ b/drivers/tty/serial/digicolor-usart.c @@ -503,13 +503,11 @@ static int digicolor_uart_probe(struct platform_device *pdev) return uart_add_one_port(&digicolor_uart, &dp->port); } -static int digicolor_uart_remove(struct platform_device *pdev) +static void digicolor_uart_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); uart_remove_one_port(&digicolor_uart, port); - - return 0; } static const struct of_device_id digicolor_uart_dt_ids[] = { @@ -524,7 +522,7 @@ static struct platform_driver digicolor_uart_platform = { .of_match_table = of_match_ptr(digicolor_uart_dt_ids), }, .probe = digicolor_uart_probe, - .remove = digicolor_uart_remove, + .remove_new = digicolor_uart_remove, }; static int __init digicolor_uart_init(void) diff --git a/drivers/tty/serial/earlycon-riscv-sbi.c b/drivers/tty/serial/earlycon-riscv-sbi.c index 27afb0b74ea7..0162155f0c83 100644 --- a/drivers/tty/serial/earlycon-riscv-sbi.c +++ b/drivers/tty/serial/earlycon-riscv-sbi.c @@ -15,17 +15,38 @@ static void sbi_putc(struct uart_port *port, unsigned char c) sbi_console_putchar(c); } -static void sbi_console_write(struct console *con, - const char *s, unsigned n) +static void sbi_0_1_console_write(struct console *con, + const char *s, unsigned int n) { struct earlycon_device *dev = con->data; uart_console_write(&dev->port, s, n, sbi_putc); } +static void sbi_dbcn_console_write(struct console *con, + const char *s, unsigned int n) +{ + int ret; + + while (n) { + ret = sbi_debug_console_write(s, n); + if (ret < 0) + break; + + s += ret; + n -= ret; + } +} + static int __init early_sbi_setup(struct earlycon_device *device, const char *opt) { - device->con->write = sbi_console_write; + if (sbi_debug_console_available) + device->con->write = sbi_dbcn_console_write; + else if (IS_ENABLED(CONFIG_RISCV_SBI_V01)) + device->con->write = sbi_0_1_console_write; + else + return -ENODEV; + return 0; } EARLYCON_DECLARE(sbi, early_sbi_setup); diff --git a/drivers/tty/serial/esp32_acm.c b/drivers/tty/serial/esp32_acm.c index cb28a87736aa..d4e8bdb1cdef 100644 --- a/drivers/tty/serial/esp32_acm.c +++ b/drivers/tty/serial/esp32_acm.c @@ -8,7 +8,7 @@ #include <linux/irq.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> +#include <linux/platform_device.h> #include <linux/serial_core.h> #include <linux/slab.h> #include <linux/tty_flip.h> @@ -413,18 +413,17 @@ static int esp32s3_acm_probe(struct platform_device *pdev) return uart_add_one_port(&esp32s3_acm_reg, port); } -static int esp32s3_acm_remove(struct platform_device *pdev) +static void esp32s3_acm_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); uart_remove_one_port(&esp32s3_acm_reg, port); - return 0; } static struct platform_driver esp32s3_acm_driver = { .probe = esp32s3_acm_probe, - .remove = esp32s3_acm_remove, + .remove_new = esp32s3_acm_remove, .driver = { .name = DRIVER_NAME, .of_match_table = esp32s3_acm_dt_ids, diff --git a/drivers/tty/serial/esp32_uart.c b/drivers/tty/serial/esp32_uart.c index 85c9c5ad7cc5..6fc61f323355 100644 --- a/drivers/tty/serial/esp32_uart.c +++ b/drivers/tty/serial/esp32_uart.c @@ -9,7 +9,8 @@ #include <linux/irq.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/property.h> #include <linux/serial_core.h> #include <linux/slab.h> #include <linux/tty_flip.h> @@ -678,16 +679,11 @@ static struct uart_driver esp32_uart_reg = { static int esp32_uart_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - static const struct of_device_id *match; struct uart_port *port; struct esp32_port *sport; struct resource *res; int ret; - match = of_match_device(esp32_uart_dt_ids, &pdev->dev); - if (!match) - return -ENODEV; - sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); if (!sport) return -ENOMEM; @@ -728,7 +724,7 @@ static int esp32_uart_probe(struct platform_device *pdev) port->flags = UPF_BOOT_AUTOCONF; port->has_sysrq = 1; port->fifosize = ESP32_UART_TX_FIFO_SIZE; - port->private_data = (void *)match->data; + port->private_data = (void *)device_get_match_data(&pdev->dev); esp32_uart_ports[port->line] = sport; @@ -737,19 +733,17 @@ static int esp32_uart_probe(struct platform_device *pdev) return uart_add_one_port(&esp32_uart_reg, port); } -static int esp32_uart_remove(struct platform_device *pdev) +static void esp32_uart_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); uart_remove_one_port(&esp32_uart_reg, port); - - return 0; } static struct platform_driver esp32_uart_driver = { .probe = esp32_uart_probe, - .remove = esp32_uart_remove, + .remove_new = esp32_uart_remove, .driver = { .name = DRIVER_NAME, .of_match_table = esp32_uart_dt_ids, diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 3bdaf1ddc309..52c87876a88d 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -851,13 +851,11 @@ static int linflex_probe(struct platform_device *pdev) return uart_add_one_port(&linflex_reg, sport); } -static int linflex_remove(struct platform_device *pdev) +static void linflex_remove(struct platform_device *pdev) { struct uart_port *sport = platform_get_drvdata(pdev); uart_remove_one_port(&linflex_reg, sport); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -884,7 +882,7 @@ static SIMPLE_DEV_PM_OPS(linflex_pm_ops, linflex_suspend, linflex_resume); static struct platform_driver linflex_driver = { .probe = linflex_probe, - .remove = linflex_remove, + .remove_new = linflex_remove, .driver = { .name = DRIVER_NAME, .of_match_table = linflex_dt_ids, diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 6d0cfb2e86b4..5ddf110aedbe 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -2959,7 +2959,7 @@ failed_reset: return ret; } -static int lpuart_remove(struct platform_device *pdev) +static void lpuart_remove(struct platform_device *pdev) { struct lpuart_port *sport = platform_get_drvdata(pdev); @@ -2976,7 +2976,6 @@ static int lpuart_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); - return 0; } static int lpuart_runtime_suspend(struct device *dev) @@ -3210,7 +3209,7 @@ static const struct dev_pm_ops lpuart_pm_ops = { static struct platform_driver lpuart_driver = { .probe = lpuart_probe, - .remove = lpuart_remove, + .remove_new = lpuart_remove, .driver = { .name = "fsl-lpuart", .of_match_table = lpuart_dt_ids, diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 708b9852a575..4aa72d5aeafb 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -415,13 +415,13 @@ static void imx_uart_stop_tx(struct uart_port *port) ucr1 = imx_uart_readl(sport, UCR1); imx_uart_writel(sport, ucr1 & ~UCR1_TRDYEN, UCR1); + ucr4 = imx_uart_readl(sport, UCR4); usr2 = imx_uart_readl(sport, USR2); - if (!(usr2 & USR2_TXDC)) { + if ((!(usr2 & USR2_TXDC)) && (ucr4 & UCR4_TCEN)) { /* The shifter is still busy, so retry once TC triggers */ return; } - ucr4 = imx_uart_readl(sport, UCR4); ucr4 &= ~UCR4_TCEN; imx_uart_writel(sport, ucr4, UCR4); @@ -1336,15 +1336,18 @@ static int imx_uart_dma_init(struct imx_port *sport) { struct dma_slave_config slave_config = {}; struct device *dev = sport->port.dev; + struct dma_chan *chan; int ret; /* Prepare for RX : */ - sport->dma_chan_rx = dma_request_slave_channel(dev, "rx"); - if (!sport->dma_chan_rx) { + chan = dma_request_chan(dev, "rx"); + if (IS_ERR(chan)) { dev_dbg(dev, "cannot get the DMA channel.\n"); - ret = -EINVAL; + sport->dma_chan_rx = NULL; + ret = PTR_ERR(chan); goto err; } + sport->dma_chan_rx = chan; slave_config.direction = DMA_DEV_TO_MEM; slave_config.src_addr = sport->port.mapbase + URXD0; @@ -1366,12 +1369,14 @@ static int imx_uart_dma_init(struct imx_port *sport) sport->rx_ring.buf = sport->rx_buf; /* Prepare for TX : */ - sport->dma_chan_tx = dma_request_slave_channel(dev, "tx"); - if (!sport->dma_chan_tx) { + chan = dma_request_chan(dev, "tx"); + if (IS_ERR(chan)) { dev_err(dev, "cannot get the TX DMA channel!\n"); - ret = -EINVAL; + sport->dma_chan_tx = NULL; + ret = PTR_ERR(chan); goto err; } + sport->dma_chan_tx = chan; slave_config.direction = DMA_MEM_TO_DEV; slave_config.dst_addr = sport->port.mapbase + URTX0; @@ -1943,10 +1948,6 @@ static int imx_uart_rs485_config(struct uart_port *port, struct ktermios *termio rs485conf->flags & SER_RS485_RX_DURING_TX) imx_uart_start_rx(port); - if (port->rs485_rx_during_tx_gpio) - gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio, - !!(rs485conf->flags & SER_RS485_RX_DURING_TX)); - return 0; } @@ -2210,7 +2211,6 @@ static enum hrtimer_restart imx_trigger_stop_tx(struct hrtimer *t) return HRTIMER_NORESTART; } -static const struct serial_rs485 imx_no_rs485 = {}; /* No RS485 if no RTS */ static const struct serial_rs485 imx_rs485_supported = { .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND | SER_RS485_RX_DURING_TX, @@ -2294,8 +2294,6 @@ static int imx_uart_probe(struct platform_device *pdev) /* RTS is required to control the RS485 transmitter */ if (sport->have_rtscts || sport->have_rtsgpio) sport->port.rs485_supported = imx_rs485_supported; - else - sport->port.rs485_supported = imx_no_rs485; sport->port.flags = UPF_BOOT_AUTOCONF; timer_setup(&sport->timer, imx_uart_timeout, 0); @@ -2322,19 +2320,13 @@ static int imx_uart_probe(struct platform_device *pdev) /* For register access, we only need to enable the ipg clock. */ ret = clk_prepare_enable(sport->clk_ipg); if (ret) { - dev_err(&pdev->dev, "failed to enable per clk: %d\n", ret); + dev_err(&pdev->dev, "failed to enable ipg clk: %d\n", ret); return ret; } ret = uart_get_rs485_mode(&sport->port); - if (ret) { - clk_disable_unprepare(sport->clk_ipg); - return ret; - } - - if (sport->port.rs485.flags & SER_RS485_ENABLED && - (!sport->have_rtscts && !sport->have_rtsgpio)) - dev_err(&pdev->dev, "no RTS control, disabling rs485\n"); + if (ret) + goto err_clk; /* * If using the i.MX UART RTS/CTS control then the RTS (CTS_B) @@ -2414,8 +2406,6 @@ static int imx_uart_probe(struct platform_device *pdev) imx_uart_writel(sport, ucr3, UCR3); } - clk_disable_unprepare(sport->clk_ipg); - hrtimer_init(&sport->trigger_start_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer_init(&sport->trigger_stop_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL); sport->trigger_start_tx.function = imx_trigger_start_tx; @@ -2431,7 +2421,7 @@ static int imx_uart_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "failed to request rx irq: %d\n", ret); - return ret; + goto err_clk; } ret = devm_request_irq(&pdev->dev, txirq, imx_uart_txint, 0, @@ -2439,7 +2429,7 @@ static int imx_uart_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "failed to request tx irq: %d\n", ret); - return ret; + goto err_clk; } ret = devm_request_irq(&pdev->dev, rtsirq, imx_uart_rtsint, 0, @@ -2447,14 +2437,14 @@ static int imx_uart_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "failed to request rts irq: %d\n", ret); - return ret; + goto err_clk; } } else { ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0, dev_name(&pdev->dev), sport); if (ret) { dev_err(&pdev->dev, "failed to request irq: %d\n", ret); - return ret; + goto err_clk; } } @@ -2462,16 +2452,19 @@ static int imx_uart_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sport); - return uart_add_one_port(&imx_uart_uart_driver, &sport->port); + ret = uart_add_one_port(&imx_uart_uart_driver, &sport->port); + +err_clk: + clk_disable_unprepare(sport->clk_ipg); + + return ret; } -static int imx_uart_remove(struct platform_device *pdev) +static void imx_uart_remove(struct platform_device *pdev) { struct imx_port *sport = platform_get_drvdata(pdev); uart_remove_one_port(&imx_uart_uart_driver, &sport->port); - - return 0; } static void imx_uart_restore_context(struct imx_port *sport) @@ -2640,7 +2633,7 @@ static const struct dev_pm_ops imx_uart_pm_ops = { static struct platform_driver imx_uart_platform_driver = { .probe = imx_uart_probe, - .remove = imx_uart_remove, + .remove_new = imx_uart_remove, .driver = { .name = "imx-uart", diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index 8489c07f4cd5..df55e5dc5afc 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -115,8 +115,6 @@ struct board_ops { void (*send_start_character)(struct jsm_channel *ch); void (*send_stop_character)(struct jsm_channel *ch); void (*copy_data_from_queue_to_uart)(struct jsm_channel *ch); - u32 (*get_uart_bytes_left)(struct jsm_channel *ch); - void (*send_immediate_char)(struct jsm_channel *ch, unsigned char); }; @@ -127,7 +125,6 @@ struct jsm_board { int boardnum; /* Board number: 0-32 */ - int type; /* Type of board */ u8 rev; /* PCI revision ID */ struct pci_dev *pci_dev; u32 maxports; /* MAX ports this board can handle */ @@ -155,8 +152,6 @@ struct jsm_board u32 bd_dividend; /* Board/UARTs specific dividend */ struct board_ops *bd_ops; - - struct list_head jsm_board_entry; }; /************************************************************************ diff --git a/drivers/tty/serial/jsm/jsm_cls.c b/drivers/tty/serial/jsm/jsm_cls.c index 3fd57ac3ad81..1eda48964c0b 100644 --- a/drivers/tty/serial/jsm/jsm_cls.c +++ b/drivers/tty/serial/jsm/jsm_cls.c @@ -878,28 +878,6 @@ static void cls_uart_off(struct jsm_channel *ch) } /* - * cls_get_uarts_bytes_left. - * Returns 0 is nothing left in the FIFO, returns 1 otherwise. - * - * The channel lock MUST be held by the calling function. - */ -static u32 cls_get_uart_bytes_left(struct jsm_channel *ch) -{ - u8 left = 0; - u8 lsr = readb(&ch->ch_cls_uart->lsr); - - /* Determine whether the Transmitter is empty or not */ - if (!(lsr & UART_LSR_TEMT)) - left = 1; - else { - ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - left = 0; - } - - return left; -} - -/* * cls_send_break. * Starts sending a break thru the UART. * @@ -916,18 +894,6 @@ static void cls_send_break(struct jsm_channel *ch) } } -/* - * cls_send_immediate_char. - * Sends a specific character as soon as possible to the UART, - * jumping over any bytes that might be in the write queue. - * - * The channel lock MUST be held by the calling function. - */ -static void cls_send_immediate_char(struct jsm_channel *ch, unsigned char c) -{ - writeb(c, &ch->ch_cls_uart->txrx); -} - struct board_ops jsm_cls_ops = { .intr = cls_intr, .uart_init = cls_uart_init, @@ -943,7 +909,5 @@ struct board_ops jsm_cls_ops = { .send_start_character = cls_send_start_character, .send_stop_character = cls_send_stop_character, .copy_data_from_queue_to_uart = cls_copy_data_from_queue_to_uart, - .get_uart_bytes_left = cls_get_uart_bytes_left, - .send_immediate_char = cls_send_immediate_char }; diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index 2bd640428970..1fa10f19368f 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -1309,25 +1309,6 @@ static void neo_uart_off(struct jsm_channel *ch) writeb(0, &ch->ch_neo_uart->ier); } -static u32 neo_get_uart_bytes_left(struct jsm_channel *ch) -{ - u8 left = 0; - u8 lsr = readb(&ch->ch_neo_uart->lsr); - - /* We must cache the LSR as some of the bits get reset once read... */ - ch->ch_cached_lsr |= lsr; - - /* Determine whether the Transmitter is empty or not */ - if (!(lsr & UART_LSR_TEMT)) - left = 1; - else { - ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - left = 0; - } - - return left; -} - /* Channel lock MUST be held by the calling function! */ static void neo_send_break(struct jsm_channel *ch) { @@ -1348,25 +1329,6 @@ static void neo_send_break(struct jsm_channel *ch) } } -/* - * neo_send_immediate_char. - * - * Sends a specific character as soon as possible to the UART, - * jumping over any bytes that might be in the write queue. - * - * The channel lock MUST be held by the calling function. - */ -static void neo_send_immediate_char(struct jsm_channel *ch, unsigned char c) -{ - if (!ch) - return; - - writeb(c, &ch->ch_neo_uart->txrx); - - /* flush write operation */ - neo_pci_posting_flush(ch->ch_bd); -} - struct board_ops jsm_neo_ops = { .intr = neo_intr, .uart_init = neo_uart_init, @@ -1382,6 +1344,4 @@ struct board_ops jsm_neo_ops = { .send_start_character = neo_send_start_character, .send_stop_character = neo_send_stop_character, .copy_data_from_queue_to_uart = neo_copy_data_from_queue_to_uart, - .get_uart_bytes_left = neo_get_uart_bytes_left, - .send_immediate_char = neo_send_immediate_char }; diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index 3adb60c683f7..a0731773ce75 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -887,13 +887,11 @@ static int lqasc_probe(struct platform_device *pdev) return ret; } -static int lqasc_remove(struct platform_device *pdev) +static void lqasc_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); uart_remove_one_port(&lqasc_reg, port); - - return 0; } static const struct ltq_soc_data soc_data_lantiq = { @@ -917,7 +915,7 @@ MODULE_DEVICE_TABLE(of, ltq_asc_match); static struct platform_driver lqasc_driver = { .probe = lqasc_probe, - .remove = lqasc_remove, + .remove_new = lqasc_remove, .driver = { .name = DRVNAME, .of_match_table = ltq_asc_match, diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c index a25ab1efe38f..3ce369f76349 100644 --- a/drivers/tty/serial/liteuart.c +++ b/drivers/tty/serial/liteuart.c @@ -336,15 +336,13 @@ err_erase_id: return ret; } -static int liteuart_remove(struct platform_device *pdev) +static void liteuart_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); unsigned int line = port->line; uart_remove_one_port(&liteuart_driver, port); xa_erase(&liteuart_array, line); - - return 0; } static const struct of_device_id liteuart_of_match[] = { @@ -355,7 +353,7 @@ MODULE_DEVICE_TABLE(of, liteuart_of_match); static struct platform_driver liteuart_platform_driver = { .probe = liteuart_probe, - .remove = liteuart_remove, + .remove_new = liteuart_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = liteuart_of_match, diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index 5149a947b7fe..ec20329f0603 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -659,13 +659,11 @@ static int serial_hs_lpc32xx_probe(struct platform_device *pdev) /* * Remove serial ports registered against a platform device. */ -static int serial_hs_lpc32xx_remove(struct platform_device *pdev) +static void serial_hs_lpc32xx_remove(struct platform_device *pdev) { struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev); uart_remove_one_port(&lpc32xx_hs_reg, &p->port); - - return 0; } @@ -702,7 +700,7 @@ MODULE_DEVICE_TABLE(of, serial_hs_lpc32xx_dt_ids); static struct platform_driver serial_hs_lpc32xx_driver = { .probe = serial_hs_lpc32xx_probe, - .remove = serial_hs_lpc32xx_remove, + .remove_new = serial_hs_lpc32xx_remove, .suspend = serial_hs_lpc32xx_suspend, .resume = serial_hs_lpc32xx_resume, .driver = { diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c index 21b574f78b86..19f0a305cc43 100644 --- a/drivers/tty/serial/ma35d1_serial.c +++ b/drivers/tty/serial/ma35d1_serial.c @@ -558,7 +558,7 @@ static void ma35d1serial_console_write(struct console *co, const char *s, u32 co u32 ier; if ((co->index < 0) || (co->index >= MA35_UART_NR)) { - pr_warn("Failed to write on ononsole port %x, out of range\n", + pr_warn("Failed to write on console port %x, out of range\n", co->index); return; } @@ -754,14 +754,13 @@ err_iounmap: /* * Remove serial ports registered against a platform device. */ -static int ma35d1serial_remove(struct platform_device *dev) +static void ma35d1serial_remove(struct platform_device *dev) { struct uart_port *port = platform_get_drvdata(dev); struct uart_ma35d1_port *up = to_ma35d1_uart_port(port); uart_remove_one_port(&ma35d1serial_reg, port); clk_disable_unprepare(up->clk); - return 0; } static int ma35d1serial_suspend(struct platform_device *dev, pm_message_t state) @@ -794,7 +793,7 @@ static int ma35d1serial_resume(struct platform_device *dev) static struct platform_driver ma35d1serial_driver = { .probe = ma35d1serial_probe, - .remove = ma35d1serial_remove, + .remove_new = ma35d1serial_remove, .suspend = ma35d1serial_suspend, .resume = ma35d1serial_resume, .driver = { diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 97e4965b73d4..10bf6d75bf9e 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -237,6 +237,14 @@ #define MAX310x_REV_MASK (0xf8) #define MAX310X_WRITE_BIT 0x80 +/* Port startup definitions */ +#define MAX310X_PORT_STARTUP_WAIT_RETRIES 20 /* Number of retries */ +#define MAX310X_PORT_STARTUP_WAIT_DELAY_MS 10 /* Delay between retries */ + +/* Crystal-related definitions */ +#define MAX310X_XTAL_WAIT_RETRIES 20 /* Number of retries */ +#define MAX310X_XTAL_WAIT_DELAY_MS 10 /* Delay between retries */ + /* MAX3107 specific */ #define MAX3107_REV_ID (0xa0) @@ -583,7 +591,7 @@ static int max310x_update_best_err(unsigned long f, long *besterr) return 1; } -static u32 max310x_set_ref_clk(struct device *dev, struct max310x_port *s, +static s32 max310x_set_ref_clk(struct device *dev, struct max310x_port *s, unsigned long freq, bool xtal) { unsigned int div, clksrc, pllcfg = 0; @@ -641,12 +649,20 @@ static u32 max310x_set_ref_clk(struct device *dev, struct max310x_port *s, /* Wait for crystal */ if (xtal) { - unsigned int val; - msleep(10); - regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &val); - if (!(val & MAX310X_STS_CLKREADY_BIT)) { - dev_warn(dev, "clock is not stable yet\n"); - } + bool stable = false; + unsigned int try = 0, val = 0; + + do { + msleep(MAX310X_XTAL_WAIT_DELAY_MS); + regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &val); + + if (val & MAX310X_STS_CLKREADY_BIT) + stable = true; + } while (!stable && (++try < MAX310X_XTAL_WAIT_RETRIES)); + + if (!stable) + return dev_err_probe(dev, -EAGAIN, + "clock is not stable\n"); } return bestfreq; @@ -780,7 +796,7 @@ static void max310x_handle_tx(struct uart_port *port) to_send = uart_circ_chars_pending(xmit); until_end = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); if (likely(to_send)) { - /* Limit to size of TX FIFO */ + /* Limit to space available in TX FIFO */ txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); txlen = port->fifosize - txlen; to_send = (to_send > txlen) ? txlen : to_send; @@ -1271,7 +1287,7 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty { int i, ret, fmin, fmax, freq; struct max310x_port *s; - u32 uartclk = 0; + s32 uartclk = 0; bool xtal; for (i = 0; i < devtype->nr; i++) @@ -1334,6 +1350,9 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty goto out_clk; for (i = 0; i < devtype->nr; i++) { + bool started = false; + unsigned int try = 0, val = 0; + /* Reset port */ regmap_write(regmaps[i], MAX310X_MODE2_REG, MAX310X_MODE2_RST_BIT); @@ -1342,13 +1361,27 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty /* Wait for port startup */ do { - regmap_read(regmaps[i], MAX310X_BRGDIVLSB_REG, &ret); - } while (ret != 0x01); + msleep(MAX310X_PORT_STARTUP_WAIT_DELAY_MS); + regmap_read(regmaps[i], MAX310X_BRGDIVLSB_REG, &val); + + if (val == 0x01) + started = true; + } while (!started && (++try < MAX310X_PORT_STARTUP_WAIT_RETRIES)); + + if (!started) { + ret = dev_err_probe(dev, -EAGAIN, "port reset failed\n"); + goto out_uart; + } regmap_write(regmaps[i], MAX310X_MODE1_REG, devtype->mode1); } uartclk = max310x_set_ref_clk(dev, s, freq, xtal); + if (uartclk < 0) { + ret = uartclk; + goto out_uart; + } + dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk); for (i = 0; i < devtype->nr; i++) { diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index 91b15243f6c6..8690a45239e0 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -627,7 +627,7 @@ static int mcf_probe(struct platform_device *pdev) /****************************************************************************/ -static int mcf_remove(struct platform_device *pdev) +static void mcf_remove(struct platform_device *pdev) { struct uart_port *port; int i; @@ -637,15 +637,13 @@ static int mcf_remove(struct platform_device *pdev) if (port) uart_remove_one_port(&mcf_driver, port); } - - return 0; } /****************************************************************************/ static struct platform_driver mcf_platform_driver = { .probe = mcf_probe, - .remove = mcf_remove, + .remove_new = mcf_remove, .driver = { .name = "mcfuart", }, diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 8dd84617e715..8395688f5ee9 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -795,7 +795,7 @@ static int meson_uart_probe(struct platform_device *pdev) return ret; } -static int meson_uart_remove(struct platform_device *pdev) +static void meson_uart_remove(struct platform_device *pdev) { struct uart_driver *uart_driver; struct uart_port *port; @@ -807,12 +807,10 @@ static int meson_uart_remove(struct platform_device *pdev) for (int id = 0; id < AML_UART_PORT_NUM; id++) if (meson_ports[id]) - return 0; + return; /* No more available uart ports, unregister uart driver */ uart_unregister_driver(uart_driver); - - return 0; } static struct meson_uart_data meson_g12a_uart_data = { @@ -852,7 +850,7 @@ MODULE_DEVICE_TABLE(of, meson_uart_dt_match); static struct platform_driver meson_uart_platform_driver = { .probe = meson_uart_probe, - .remove = meson_uart_remove, + .remove_new = meson_uart_remove, .driver = { .name = "meson_uart", .of_match_table = meson_uart_dt_match, diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c index db3b81f2aa57..da4c6f7e2a30 100644 --- a/drivers/tty/serial/milbeaut_usio.c +++ b/drivers/tty/serial/milbeaut_usio.c @@ -552,15 +552,13 @@ failed: return ret; } -static int mlb_usio_remove(struct platform_device *pdev) +static void mlb_usio_remove(struct platform_device *pdev) { struct uart_port *port = &mlb_usio_ports[pdev->id]; struct clk *clk = port->private_data; uart_remove_one_port(&mlb_usio_uart_driver, port); clk_disable_unprepare(clk); - - return 0; } static const struct of_device_id mlb_usio_dt_ids[] = { @@ -571,7 +569,7 @@ MODULE_DEVICE_TABLE(of, mlb_usio_dt_ids); static struct platform_driver mlb_usio_driver = { .probe = mlb_usio_probe, - .remove = mlb_usio_remove, + .remove_new = mlb_usio_remove, .driver = { .name = USIO_NAME, .of_match_table = mlb_usio_dt_ids, diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index a252465e745f..95dae5e27b28 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1765,15 +1765,12 @@ static int mpc52xx_uart_of_probe(struct platform_device *op) return 0; } -static int -mpc52xx_uart_of_remove(struct platform_device *op) +static void mpc52xx_uart_of_remove(struct platform_device *op) { struct uart_port *port = platform_get_drvdata(op); if (port) uart_remove_one_port(&mpc52xx_uart_driver, port); - - return 0; } #ifdef CONFIG_PM @@ -1846,7 +1843,7 @@ MODULE_DEVICE_TABLE(of, mpc52xx_uart_of_match); static struct platform_driver mpc52xx_uart_of_driver = { .probe = mpc52xx_uart_of_probe, - .remove = mpc52xx_uart_of_remove, + .remove_new = mpc52xx_uart_of_remove, #ifdef CONFIG_PM .suspend = mpc52xx_uart_of_suspend, .resume = mpc52xx_uart_of_resume, diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 597264b546fd..e24204ad35de 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/clk.h> #include <linux/platform_device.h> +#include <linux/pm_opp.h> #include <linux/delay.h> #include <linux/of.h> #include <linux/of_device.h> @@ -1131,7 +1132,7 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, uart_port_unlock_irqrestore(port, flags); entry = msm_find_best_baud(port, baud, &rate); - clk_set_rate(msm_port->clk, rate); + dev_pm_opp_set_rate(port->dev, rate); baud = rate / 16 / entry->divisor; uart_port_lock_irqsave(port, &flags); @@ -1186,6 +1187,7 @@ static void msm_init_clock(struct uart_port *port) { struct msm_port *msm_port = to_msm_port(port); + dev_pm_opp_set_rate(port->dev, port->uartclk); clk_prepare_enable(msm_port->clk); clk_prepare_enable(msm_port->pclk); msm_serial_set_mnd_regs(port); @@ -1239,6 +1241,7 @@ err_irq: clk_disable_unprepare(msm_port->pclk); clk_disable_unprepare(msm_port->clk); + dev_pm_opp_set_rate(port->dev, 0); return ret; } @@ -1254,6 +1257,7 @@ static void msm_shutdown(struct uart_port *port) msm_release_dma(msm_port); clk_disable_unprepare(msm_port->clk); + dev_pm_opp_set_rate(port->dev, 0); free_irq(port->irq, port); } @@ -1419,11 +1423,13 @@ static void msm_power(struct uart_port *port, unsigned int state, switch (state) { case 0: + dev_pm_opp_set_rate(port->dev, port->uartclk); clk_prepare_enable(msm_port->clk); clk_prepare_enable(msm_port->pclk); break; case 3: clk_disable_unprepare(msm_port->clk); + dev_pm_opp_set_rate(port->dev, 0); clk_disable_unprepare(msm_port->pclk); break; default: @@ -1789,7 +1795,7 @@ static int msm_serial_probe(struct platform_device *pdev) struct resource *resource; struct uart_port *port; const struct of_device_id *id; - int irq, line; + int irq, line, ret; if (pdev->dev.of_node) line = of_alias_get_id(pdev->dev.of_node, "serial"); @@ -1824,6 +1830,15 @@ static int msm_serial_probe(struct platform_device *pdev) return PTR_ERR(msm_port->pclk); } + ret = devm_pm_opp_set_clkname(&pdev->dev, "core"); + if (ret) + return ret; + + /* OPP table is optional */ + ret = devm_pm_opp_of_add_table(&pdev->dev); + if (ret && ret != -ENODEV) + return dev_err_probe(&pdev->dev, ret, "invalid OPP table\n"); + port->uartclk = clk_get_rate(msm_port->clk); dev_info(&pdev->dev, "uartclk = %d\n", port->uartclk); @@ -1843,13 +1858,11 @@ static int msm_serial_probe(struct platform_device *pdev) return uart_add_one_port(&msm_uart_driver, port); } -static int msm_serial_remove(struct platform_device *pdev) +static void msm_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); uart_remove_one_port(&msm_uart_driver, port); - - return 0; } static const struct of_device_id msm_match_table[] = { @@ -1882,7 +1895,7 @@ static const struct dev_pm_ops msm_serial_dev_pm_ops = { }; static struct platform_driver msm_platform_driver = { - .remove = msm_serial_remove, + .remove_new = msm_serial_remove, .probe = msm_serial_probe, .driver = { .name = "msm_serial", diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 8eeecf8ad359..3ec725555bcc 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -904,21 +904,27 @@ static void mxs_auart_dma_exit(struct mxs_auart_port *s) static int mxs_auart_dma_init(struct mxs_auart_port *s) { + struct dma_chan *chan; + if (auart_dma_enabled(s)) return 0; /* init for RX */ - s->rx_dma_chan = dma_request_slave_channel(s->dev, "rx"); - if (!s->rx_dma_chan) + chan = dma_request_chan(s->dev, "rx"); + if (IS_ERR(chan)) goto err_out; + s->rx_dma_chan = chan; + s->rx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); if (!s->rx_dma_buf) goto err_out; /* init for TX */ - s->tx_dma_chan = dma_request_slave_channel(s->dev, "tx"); - if (!s->tx_dma_chan) + chan = dma_request_chan(s->dev, "tx"); + if (IS_ERR(chan)) goto err_out; + s->tx_dma_chan = chan; + s->tx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); if (!s->tx_dma_buf) goto err_out; @@ -1686,7 +1692,7 @@ out_disable_clks: return ret; } -static int mxs_auart_remove(struct platform_device *pdev) +static void mxs_auart_remove(struct platform_device *pdev) { struct mxs_auart_port *s = platform_get_drvdata(pdev); @@ -1698,13 +1704,11 @@ static int mxs_auart_remove(struct platform_device *pdev) clk_disable_unprepare(s->clk); clk_disable_unprepare(s->clk_ahb); } - - return 0; } static struct platform_driver mxs_auart_driver = { .probe = mxs_auart_probe, - .remove = mxs_auart_remove, + .remove_new = mxs_auart_remove, .driver = { .name = "mxs-auart", .of_match_table = mxs_auart_dt_ids, diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index ad4c1c5d0a7f..f5a0b401af63 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1483,6 +1483,13 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) return omap_up_info; } +static const struct serial_rs485 serial_omap_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND | + SER_RS485_RX_DURING_TX, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; + static int serial_omap_probe_rs485(struct uart_omap_port *up, struct device *dev) { @@ -1497,6 +1504,9 @@ static int serial_omap_probe_rs485(struct uart_omap_port *up, if (!np) return 0; + up->port.rs485_config = serial_omap_config_rs485; + up->port.rs485_supported = serial_omap_rs485_supported; + ret = uart_get_rs485_mode(&up->port); if (ret) return ret; @@ -1531,13 +1541,6 @@ static int serial_omap_probe_rs485(struct uart_omap_port *up, return 0; } -static const struct serial_rs485 serial_omap_rs485_supported = { - .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND | - SER_RS485_RX_DURING_TX, - .delay_rts_before_send = 1, - .delay_rts_after_send = 1, -}; - static int serial_omap_probe(struct platform_device *pdev) { struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev); @@ -1604,17 +1607,11 @@ static int serial_omap_probe(struct platform_device *pdev) dev_info(up->port.dev, "no wakeirq for uart%d\n", up->port.line); - ret = serial_omap_probe_rs485(up, &pdev->dev); - if (ret < 0) - goto err_rs485; - sprintf(up->name, "OMAP UART%d", up->port.line); up->port.mapbase = mem->start; up->port.membase = base; up->port.flags = omap_up_info->flags; up->port.uartclk = omap_up_info->uartclk; - up->port.rs485_config = serial_omap_config_rs485; - up->port.rs485_supported = serial_omap_rs485_supported; if (!up->port.uartclk) { up->port.uartclk = DEFAULT_CLK_SPEED; dev_warn(&pdev->dev, @@ -1622,6 +1619,10 @@ static int serial_omap_probe(struct platform_device *pdev) DEFAULT_CLK_SPEED); } + ret = serial_omap_probe_rs485(up, &pdev->dev); + if (ret < 0) + goto err_rs485; + up->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; up->calc_latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; cpu_latency_qos_add_request(&up->pm_qos_request, up->latency); @@ -1658,7 +1659,7 @@ err_port_line: return ret; } -static int serial_omap_remove(struct platform_device *dev) +static void serial_omap_remove(struct platform_device *dev) { struct uart_omap_port *up = platform_get_drvdata(dev); @@ -1670,8 +1671,6 @@ static int serial_omap_remove(struct platform_device *dev) pm_runtime_disable(up->dev); cpu_latency_qos_remove_request(&up->pm_qos_request); device_init_wakeup(&dev->dev, false); - - return 0; } /* @@ -1808,7 +1807,7 @@ MODULE_DEVICE_TABLE(of, omap_serial_of_match); static struct platform_driver serial_omap_driver = { .probe = serial_omap_probe, - .remove = serial_omap_remove, + .remove_new = serial_omap_remove, .driver = { .name = OMAP_SERIAL_DRIVER_NAME, .pm = &serial_omap_dev_pm_ops, diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c index 919f5e5aa0f1..d9fe85397741 100644 --- a/drivers/tty/serial/owl-uart.c +++ b/drivers/tty/serial/owl-uart.c @@ -725,20 +725,18 @@ static int owl_uart_probe(struct platform_device *pdev) return ret; } -static int owl_uart_remove(struct platform_device *pdev) +static void owl_uart_remove(struct platform_device *pdev) { struct owl_uart_port *owl_port = platform_get_drvdata(pdev); uart_remove_one_port(&owl_uart_driver, &owl_port->port); owl_uart_ports[pdev->id] = NULL; clk_disable_unprepare(owl_port->clk); - - return 0; } static struct platform_driver owl_uart_platform_driver = { .probe = owl_uart_probe, - .remove = owl_uart_remove, + .remove_new = owl_uart_remove, .driver = { .name = "owl-uart", .of_match_table = owl_uart_dt_matches, diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index 3a95bf5d55d3..bbb46e6e98a2 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -938,7 +938,7 @@ err: return ret; } -static int pic32_uart_remove(struct platform_device *pdev) +static void pic32_uart_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); struct pic32_sport *sport = to_pic32_sport(port); @@ -947,9 +947,6 @@ static int pic32_uart_remove(struct platform_device *pdev) clk_disable_unprepare(sport->clk); platform_set_drvdata(pdev, NULL); pic32_sports[sport->idx] = NULL; - - /* automatic unroll of sport and gpios */ - return 0; } static const struct of_device_id pic32_serial_dt_ids[] = { @@ -960,7 +957,7 @@ MODULE_DEVICE_TABLE(of, pic32_serial_dt_ids); static struct platform_driver pic32_uart_platform_driver = { .probe = pic32_uart_probe, - .remove = pic32_uart_remove, + .remove_new = pic32_uart_remove, .driver = { .name = PIC32_DEV_NAME, .of_match_table = of_match_ptr(pic32_serial_dt_ids), diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 7e78f97e8f43..e63a8fbe63bd 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1696,7 +1696,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) return 0; } -static int qcom_geni_serial_remove(struct platform_device *pdev) +static void qcom_geni_serial_remove(struct platform_device *pdev) { struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); struct uart_driver *drv = port->private_data.drv; @@ -1704,8 +1704,6 @@ static int qcom_geni_serial_remove(struct platform_device *pdev) dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); uart_remove_one_port(drv, &port->uport); - - return 0; } static int qcom_geni_serial_sys_suspend(struct device *dev) @@ -1805,7 +1803,7 @@ static const struct of_device_id qcom_geni_serial_match_table[] = { MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table); static struct platform_driver qcom_geni_serial_platform_driver = { - .remove = qcom_geni_serial_remove, + .remove_new = qcom_geni_serial_remove, .probe = qcom_geni_serial_probe, .driver = { .name = "qcom_geni_serial", diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c index d824c8318f33..13deb355cf1b 100644 --- a/drivers/tty/serial/rda-uart.c +++ b/drivers/tty/serial/rda-uart.c @@ -780,19 +780,17 @@ static int rda_uart_probe(struct platform_device *pdev) return ret; } -static int rda_uart_remove(struct platform_device *pdev) +static void rda_uart_remove(struct platform_device *pdev) { struct rda_uart_port *rda_port = platform_get_drvdata(pdev); uart_remove_one_port(&rda_uart_driver, &rda_port->port); rda_uart_ports[pdev->id] = NULL; - - return 0; } static struct platform_driver rda_uart_platform_driver = { .probe = rda_uart_probe, - .remove = rda_uart_remove, + .remove_new = rda_uart_remove, .driver = { .name = "rda-uart", .of_match_table = rda_uart_dt_matches, diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index d46a81cddfcd..4132fcff7d4e 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -178,7 +178,6 @@ struct rp2_card; struct rp2_uart_port { struct uart_port port; int idx; - int ignore_rx; struct rp2_card *card; void __iomem *asic_base; void __iomem *base; diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index be7bcd75d9f4..79c794fa6545 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -870,19 +870,17 @@ static int sa1100_serial_probe(struct platform_device *dev) return 0; } -static int sa1100_serial_remove(struct platform_device *pdev) +static void sa1100_serial_remove(struct platform_device *pdev) { struct sa1100_port *sport = platform_get_drvdata(pdev); if (sport) uart_remove_one_port(&sa1100_reg, &sport->port); - - return 0; } static struct platform_driver sa11x0_serial_driver = { .probe = sa1100_serial_probe, - .remove = sa1100_serial_remove, + .remove_new = sa1100_serial_remove, .suspend = sa1100_serial_suspend, .resume = sa1100_serial_resume, .driver = { diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index 66bd6c090ace..71d17d804fda 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -2054,7 +2054,7 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) return 0; } -static int s3c24xx_serial_remove(struct platform_device *dev) +static void s3c24xx_serial_remove(struct platform_device *dev) { struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); @@ -2063,8 +2063,6 @@ static int s3c24xx_serial_remove(struct platform_device *dev) } uart_unregister_driver(&s3c24xx_uart_drv); - - return 0; } /* UART power management code */ @@ -2627,7 +2625,7 @@ MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match); static struct platform_driver samsung_serial_driver = { .probe = s3c24xx_serial_probe, - .remove = s3c24xx_serial_remove, + .remove_new = s3c24xx_serial_remove, .id_table = s3c24xx_serial_driver_ids, .driver = { .name = "samsung-uart", diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index cf0c6120d30e..929206a9a6e1 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -24,10 +24,12 @@ #include <linux/tty_flip.h> #include <linux/spi/spi.h> #include <linux/uaccess.h> +#include <linux/units.h> #include <uapi/linux/sched/types.h> #define SC16IS7XX_NAME "sc16is7xx" #define SC16IS7XX_MAX_DEVS 8 +#define SC16IS7XX_MAX_PORTS 2 /* Maximum number of UART ports per IC. */ /* SC16IS7XX register definitions */ #define SC16IS7XX_RHR_REG (0x00) /* RX FIFO */ @@ -300,8 +302,8 @@ /* Misc definitions */ +#define SC16IS7XX_SPI_READ_BIT BIT(7) #define SC16IS7XX_FIFO_SIZE (64) -#define SC16IS7XX_REG_SHIFT 2 #define SC16IS7XX_GPIOS_PER_BANK 4 struct sc16is7xx_devtype { @@ -322,18 +324,19 @@ struct sc16is7xx_one_config { struct sc16is7xx_one { struct uart_port port; - u8 line; + struct regmap *regmap; + struct mutex efr_lock; /* EFR registers access */ struct kthread_work tx_work; struct kthread_work reg_work; struct kthread_delayed_work ms_work; struct sc16is7xx_one_config config; - bool irda_mode; unsigned int old_mctrl; + u8 old_lcr; /* Value before EFR access. */ + bool irda_mode; }; struct sc16is7xx_port { const struct sc16is7xx_devtype *devtype; - struct regmap *regmap; struct clk *clk; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio; @@ -343,65 +346,47 @@ struct sc16is7xx_port { unsigned char buf[SC16IS7XX_FIFO_SIZE]; struct kthread_worker kworker; struct task_struct *kworker_task; - struct mutex efr_lock; struct sc16is7xx_one p[]; }; -static unsigned long sc16is7xx_lines; +static DECLARE_BITMAP(sc16is7xx_lines, SC16IS7XX_MAX_DEVS); static struct uart_driver sc16is7xx_uart = { .owner = THIS_MODULE, + .driver_name = SC16IS7XX_NAME, .dev_name = "ttySC", .nr = SC16IS7XX_MAX_DEVS, }; -static void sc16is7xx_ier_set(struct uart_port *port, u8 bit); -static void sc16is7xx_stop_tx(struct uart_port *port); - #define to_sc16is7xx_one(p,e) ((container_of((p), struct sc16is7xx_one, e))) -static int sc16is7xx_line(struct uart_port *port) -{ - struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - - return one->line; -} - static u8 sc16is7xx_port_read(struct uart_port *port, u8 reg) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); unsigned int val = 0; - const u8 line = sc16is7xx_line(port); - regmap_read(s->regmap, (reg << SC16IS7XX_REG_SHIFT) | line, &val); + regmap_read(one->regmap, reg, &val); return val; } static void sc16is7xx_port_write(struct uart_port *port, u8 reg, u8 val) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); - const u8 line = sc16is7xx_line(port); + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - regmap_write(s->regmap, (reg << SC16IS7XX_REG_SHIFT) | line, val); + regmap_write(one->regmap, reg, val); } -static void sc16is7xx_fifo_read(struct uart_port *port, unsigned int rxlen) +static void sc16is7xx_fifo_read(struct uart_port *port, u8 *rxbuf, unsigned int rxlen) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); - const u8 line = sc16is7xx_line(port); - u8 addr = (SC16IS7XX_RHR_REG << SC16IS7XX_REG_SHIFT) | line; + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - regcache_cache_bypass(s->regmap, true); - regmap_raw_read(s->regmap, addr, s->buf, rxlen); - regcache_cache_bypass(s->regmap, false); + regmap_noinc_read(one->regmap, SC16IS7XX_RHR_REG, rxbuf, rxlen); } -static void sc16is7xx_fifo_write(struct uart_port *port, u8 to_send) +static void sc16is7xx_fifo_write(struct uart_port *port, u8 *txbuf, u8 to_send) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); - const u8 line = sc16is7xx_line(port); - u8 addr = (SC16IS7XX_THR_REG << SC16IS7XX_REG_SHIFT) | line; + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); /* * Don't send zero-length data, at least on SPI it confuses the chip @@ -410,39 +395,101 @@ static void sc16is7xx_fifo_write(struct uart_port *port, u8 to_send) if (unlikely(!to_send)) return; - regcache_cache_bypass(s->regmap, true); - regmap_raw_write(s->regmap, addr, s->buf, to_send); - regcache_cache_bypass(s->regmap, false); + regmap_noinc_write(one->regmap, SC16IS7XX_THR_REG, txbuf, to_send); } static void sc16is7xx_port_update(struct uart_port *port, u8 reg, u8 mask, u8 val) { + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + regmap_update_bits(one->regmap, reg, mask, val); +} + +static void sc16is7xx_power(struct uart_port *port, int on) +{ + sc16is7xx_port_update(port, SC16IS7XX_IER_REG, + SC16IS7XX_IER_SLEEP_BIT, + on ? 0 : SC16IS7XX_IER_SLEEP_BIT); +} + +/* + * In an amazing feat of design, the Enhanced Features Register (EFR) + * shares the address of the Interrupt Identification Register (IIR). + * Access to EFR is switched on by writing a magic value (0xbf) to the + * Line Control Register (LCR). Any interrupt firing during this time will + * see the EFR where it expects the IIR to be, leading to + * "Unexpected interrupt" messages. + * + * Prevent this possibility by claiming a mutex while accessing the EFR, + * and claiming the same mutex from within the interrupt handler. This is + * similar to disabling the interrupt, but that doesn't work because the + * bulk of the interrupt processing is run as a workqueue job in thread + * context. + */ +static void sc16is7xx_efr_lock(struct uart_port *port) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + mutex_lock(&one->efr_lock); + + /* Backup content of LCR. */ + one->old_lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG); + + /* Enable access to Enhanced register set */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_CONF_MODE_B); + + /* Disable cache updates when writing to EFR registers */ + regcache_cache_bypass(one->regmap, true); +} + +static void sc16is7xx_efr_unlock(struct uart_port *port) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + /* Re-enable cache updates when writing to normal registers */ + regcache_cache_bypass(one->regmap, false); + + /* Restore original content of LCR */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, one->old_lcr); + + mutex_unlock(&one->efr_lock); +} + +static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit) +{ struct sc16is7xx_port *s = dev_get_drvdata(port->dev); - const u8 line = sc16is7xx_line(port); + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + lockdep_assert_held_once(&port->lock); - regmap_update_bits(s->regmap, (reg << SC16IS7XX_REG_SHIFT) | line, - mask, val); + one->config.flags |= SC16IS7XX_RECONF_IER; + one->config.ier_mask |= bit; + one->config.ier_val &= ~bit; + kthread_queue_work(&s->kworker, &one->reg_work); } -static int sc16is7xx_alloc_line(void) +static void sc16is7xx_ier_set(struct uart_port *port, u8 bit) { - int i; + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - BUILD_BUG_ON(SC16IS7XX_MAX_DEVS > BITS_PER_LONG); + lockdep_assert_held_once(&port->lock); - for (i = 0; i < SC16IS7XX_MAX_DEVS; i++) - if (!test_and_set_bit(i, &sc16is7xx_lines)) - break; + one->config.flags |= SC16IS7XX_RECONF_IER; + one->config.ier_mask |= bit; + one->config.ier_val |= bit; + kthread_queue_work(&s->kworker, &one->reg_work); +} - return i; +static void sc16is7xx_stop_tx(struct uart_port *port) +{ + sc16is7xx_ier_clear(port, SC16IS7XX_IER_THRI_BIT); } -static void sc16is7xx_power(struct uart_port *port, int on) +static void sc16is7xx_stop_rx(struct uart_port *port) { - sc16is7xx_port_update(port, SC16IS7XX_IER_REG, - SC16IS7XX_IER_SLEEP_BIT, - on ? 0 : SC16IS7XX_IER_SLEEP_BIT); + sc16is7xx_ier_clear(port, SC16IS7XX_IER_RDI_BIT); } static const struct sc16is7xx_devtype sc16is74x_devtype = { @@ -477,7 +524,7 @@ static const struct sc16is7xx_devtype sc16is762_devtype = { static bool sc16is7xx_regmap_volatile(struct device *dev, unsigned int reg) { - switch (reg >> SC16IS7XX_REG_SHIFT) { + switch (reg) { case SC16IS7XX_RHR_REG: case SC16IS7XX_IIR_REG: case SC16IS7XX_LSR_REG: @@ -488,85 +535,60 @@ static bool sc16is7xx_regmap_volatile(struct device *dev, unsigned int reg) case SC16IS7XX_IOCONTROL_REG: return true; default: - break; + return false; } - - return false; } static bool sc16is7xx_regmap_precious(struct device *dev, unsigned int reg) { - switch (reg >> SC16IS7XX_REG_SHIFT) { + switch (reg) { case SC16IS7XX_RHR_REG: return true; default: - break; + return false; } +} - return false; +static bool sc16is7xx_regmap_noinc(struct device *dev, unsigned int reg) +{ + return reg == SC16IS7XX_RHR_REG; } static int sc16is7xx_set_baud(struct uart_port *port, int baud) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); u8 lcr; u8 prescaler = 0; unsigned long clk = port->uartclk, div = clk / 16 / baud; - if (div > 0xffff) { + if (div >= BIT(16)) { prescaler = SC16IS7XX_MCR_CLKSEL_BIT; div /= 4; } - /* In an amazing feat of design, the Enhanced Features Register shares - * the address of the Interrupt Identification Register, and is - * switched in by writing a magic value (0xbf) to the Line Control - * Register. Any interrupt firing during this time will see the EFR - * where it expects the IIR to be, leading to "Unexpected interrupt" - * messages. - * - * Prevent this possibility by claiming a mutex while accessing the - * EFR, and claiming the same mutex from within the interrupt handler. - * This is similar to disabling the interrupt, but that doesn't work - * because the bulk of the interrupt processing is run as a workqueue - * job in thread context. - */ - mutex_lock(&s->efr_lock); - - lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG); - - /* Open the LCR divisors for configuration */ - sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, - SC16IS7XX_LCR_CONF_MODE_B); - /* Enable enhanced features */ - regcache_cache_bypass(s->regmap, true); + sc16is7xx_efr_lock(port); sc16is7xx_port_update(port, SC16IS7XX_EFR_REG, SC16IS7XX_EFR_ENABLE_BIT, SC16IS7XX_EFR_ENABLE_BIT); - - regcache_cache_bypass(s->regmap, false); - - /* Put LCR back to the normal mode */ - sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); - - mutex_unlock(&s->efr_lock); + sc16is7xx_efr_unlock(port); sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, SC16IS7XX_MCR_CLKSEL_BIT, prescaler); - /* Open the LCR divisors for configuration */ + /* Backup LCR and access special register set (DLL/DLH) */ + lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG); sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_CONF_MODE_A); /* Write the new divisor */ - regcache_cache_bypass(s->regmap, true); + regcache_cache_bypass(one->regmap, true); sc16is7xx_port_write(port, SC16IS7XX_DLH_REG, div / 256); sc16is7xx_port_write(port, SC16IS7XX_DLL_REG, div % 256); - regcache_cache_bypass(s->regmap, false); + regcache_cache_bypass(one->regmap, false); - /* Put LCR back to the normal mode */ + /* Restore LCR and access to general register set */ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); return DIV_ROUND_CLOSEST(clk / 16, div); @@ -602,7 +624,7 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, s->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG); bytes_read = 1; } else { - sc16is7xx_fifo_read(port, rxlen); + sc16is7xx_fifo_read(port, s->buf, rxlen); bytes_read = rxlen; } @@ -675,7 +697,7 @@ static void sc16is7xx_handle_tx(struct uart_port *port) /* Get length of data pending in circular buffer */ to_send = uart_circ_chars_pending(xmit); if (likely(to_send)) { - /* Limit to size of TX FIFO */ + /* Limit to space available in TX FIFO */ txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); if (txlen > SC16IS7XX_FIFO_SIZE) { dev_err_ratelimited(port->dev, @@ -691,7 +713,7 @@ static void sc16is7xx_handle_tx(struct uart_port *port) uart_xmit_advance(port, 1); } - sc16is7xx_fifo_write(port, to_send); + sc16is7xx_fifo_write(port, s->buf, to_send); } uart_port_lock_irqsave(port, &flags); @@ -700,6 +722,8 @@ static void sc16is7xx_handle_tx(struct uart_port *port) if (uart_circ_empty(xmit)) sc16is7xx_stop_tx(port); + else + sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT); uart_port_unlock_irqrestore(port, flags); } @@ -718,11 +742,10 @@ static unsigned int sc16is7xx_get_hwmctrl(struct uart_port *port) static void sc16is7xx_update_mlines(struct sc16is7xx_one *one) { struct uart_port *port = &one->port; - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); unsigned long flags; unsigned int status, changed; - lockdep_assert_held_once(&s->efr_lock); + lockdep_assert_held_once(&one->efr_lock); status = sc16is7xx_get_hwmctrl(port); changed = status ^ one->old_mctrl; @@ -748,74 +771,77 @@ static void sc16is7xx_update_mlines(struct sc16is7xx_one *one) static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) { + bool rc = true; + unsigned int iir, rxlen; struct uart_port *port = &s->p[portno].port; + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - do { - unsigned int iir, rxlen; - struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - - iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG); - if (iir & SC16IS7XX_IIR_NO_INT_BIT) - return false; - - iir &= SC16IS7XX_IIR_ID_MASK; - - switch (iir) { - case SC16IS7XX_IIR_RDI_SRC: - case SC16IS7XX_IIR_RLSE_SRC: - case SC16IS7XX_IIR_RTOI_SRC: - case SC16IS7XX_IIR_XOFFI_SRC: - rxlen = sc16is7xx_port_read(port, SC16IS7XX_RXLVL_REG); - - /* - * There is a silicon bug that makes the chip report a - * time-out interrupt but no data in the FIFO. This is - * described in errata section 18.1.4. - * - * When this happens, read one byte from the FIFO to - * clear the interrupt. - */ - if (iir == SC16IS7XX_IIR_RTOI_SRC && !rxlen) - rxlen = 1; - - if (rxlen) - sc16is7xx_handle_rx(port, rxlen, iir); - break; + mutex_lock(&one->efr_lock); + + iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG); + if (iir & SC16IS7XX_IIR_NO_INT_BIT) { + rc = false; + goto out_port_irq; + } + + iir &= SC16IS7XX_IIR_ID_MASK; + + switch (iir) { + case SC16IS7XX_IIR_RDI_SRC: + case SC16IS7XX_IIR_RLSE_SRC: + case SC16IS7XX_IIR_RTOI_SRC: + case SC16IS7XX_IIR_XOFFI_SRC: + rxlen = sc16is7xx_port_read(port, SC16IS7XX_RXLVL_REG); + + /* + * There is a silicon bug that makes the chip report a + * time-out interrupt but no data in the FIFO. This is + * described in errata section 18.1.4. + * + * When this happens, read one byte from the FIFO to + * clear the interrupt. + */ + if (iir == SC16IS7XX_IIR_RTOI_SRC && !rxlen) + rxlen = 1; + + if (rxlen) + sc16is7xx_handle_rx(port, rxlen, iir); + break; /* CTSRTS interrupt comes only when CTS goes inactive */ - case SC16IS7XX_IIR_CTSRTS_SRC: - case SC16IS7XX_IIR_MSI_SRC: - sc16is7xx_update_mlines(one); - break; - case SC16IS7XX_IIR_THRI_SRC: - sc16is7xx_handle_tx(port); - break; - default: - dev_err_ratelimited(port->dev, - "ttySC%i: Unexpected interrupt: %x", - port->line, iir); - break; - } - } while (0); - return true; + case SC16IS7XX_IIR_CTSRTS_SRC: + case SC16IS7XX_IIR_MSI_SRC: + sc16is7xx_update_mlines(one); + break; + case SC16IS7XX_IIR_THRI_SRC: + sc16is7xx_handle_tx(port); + break; + default: + dev_err_ratelimited(port->dev, + "ttySC%i: Unexpected interrupt: %x", + port->line, iir); + break; + } + +out_port_irq: + mutex_unlock(&one->efr_lock); + + return rc; } static irqreturn_t sc16is7xx_irq(int irq, void *dev_id) { - struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id; + bool keep_polling; - mutex_lock(&s->efr_lock); + struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id; - while (1) { - bool keep_polling = false; + do { int i; + keep_polling = false; + for (i = 0; i < s->devtype->nr_uart; ++i) keep_polling |= sc16is7xx_port_irq(s, i); - if (!keep_polling) - break; - } - - mutex_unlock(&s->efr_lock); + } while (keep_polling); return IRQ_HANDLED; } @@ -823,20 +849,15 @@ static irqreturn_t sc16is7xx_irq(int irq, void *dev_id) static void sc16is7xx_tx_proc(struct kthread_work *ws) { struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port); - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); - unsigned long flags; + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); if ((port->rs485.flags & SER_RS485_ENABLED) && (port->rs485.delay_rts_before_send > 0)) msleep(port->rs485.delay_rts_before_send); - mutex_lock(&s->efr_lock); + mutex_lock(&one->efr_lock); sc16is7xx_handle_tx(port); - mutex_unlock(&s->efr_lock); - - uart_port_lock_irqsave(port, &flags); - sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT); - uart_port_unlock_irqrestore(port, flags); + mutex_unlock(&one->efr_lock); } static void sc16is7xx_reconf_rs485(struct uart_port *port) @@ -897,51 +918,15 @@ static void sc16is7xx_reg_proc(struct kthread_work *ws) sc16is7xx_reconf_rs485(&one->port); } -static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit) -{ - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); - struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - - lockdep_assert_held_once(&port->lock); - - one->config.flags |= SC16IS7XX_RECONF_IER; - one->config.ier_mask |= bit; - one->config.ier_val &= ~bit; - kthread_queue_work(&s->kworker, &one->reg_work); -} - -static void sc16is7xx_ier_set(struct uart_port *port, u8 bit) -{ - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); - struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - - lockdep_assert_held_once(&port->lock); - - one->config.flags |= SC16IS7XX_RECONF_IER; - one->config.ier_mask |= bit; - one->config.ier_val |= bit; - kthread_queue_work(&s->kworker, &one->reg_work); -} - -static void sc16is7xx_stop_tx(struct uart_port *port) -{ - sc16is7xx_ier_clear(port, SC16IS7XX_IER_THRI_BIT); -} - -static void sc16is7xx_stop_rx(struct uart_port *port) -{ - sc16is7xx_ier_clear(port, SC16IS7XX_IER_RDI_BIT); -} - static void sc16is7xx_ms_proc(struct kthread_work *ws) { struct sc16is7xx_one *one = to_sc16is7xx_one(ws, ms_work.work); struct sc16is7xx_port *s = dev_get_drvdata(one->port.dev); if (one->port.state) { - mutex_lock(&s->efr_lock); + mutex_lock(&one->efr_lock); sc16is7xx_update_mlines(one); - mutex_unlock(&s->efr_lock); + mutex_unlock(&one->efr_lock); kthread_queue_delayed_work(&s->kworker, &one->ms_work, HZ); } @@ -1025,7 +1010,6 @@ static void sc16is7xx_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); unsigned int lcr, flow = 0; int baud; @@ -1083,17 +1067,7 @@ static void sc16is7xx_set_termios(struct uart_port *port, if (!(termios->c_cflag & CREAD)) port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK; - /* As above, claim the mutex while accessing the EFR. */ - mutex_lock(&s->efr_lock); - - sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, - SC16IS7XX_LCR_CONF_MODE_B); - /* Configure flow control */ - regcache_cache_bypass(s->regmap, true); - sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]); - sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]); - port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS); if (termios->c_cflag & CRTSCTS) { flow |= SC16IS7XX_EFR_AUTOCTS_BIT | @@ -1105,16 +1079,16 @@ static void sc16is7xx_set_termios(struct uart_port *port, if (termios->c_iflag & IXOFF) flow |= SC16IS7XX_EFR_SWFLOW1_BIT; - sc16is7xx_port_update(port, - SC16IS7XX_EFR_REG, - SC16IS7XX_EFR_FLOWCTRL_BITS, - flow); - regcache_cache_bypass(s->regmap, false); - /* Update LCR register */ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); - mutex_unlock(&s->efr_lock); + /* Update EFR registers */ + sc16is7xx_efr_lock(port); + sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]); + sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]); + sc16is7xx_port_update(port, SC16IS7XX_EFR_REG, + SC16IS7XX_EFR_FLOWCTRL_BITS, flow); + sc16is7xx_efr_unlock(port); /* Get baud rate generator configuration */ baud = uart_get_baud_rate(port, termios, old, @@ -1160,7 +1134,6 @@ static int sc16is7xx_config_rs485(struct uart_port *port, struct ktermios *termi static int sc16is7xx_startup(struct uart_port *port) { struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); unsigned int val; unsigned long flags; @@ -1177,7 +1150,7 @@ static int sc16is7xx_startup(struct uart_port *port) sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_CONF_MODE_B); - regcache_cache_bypass(s->regmap, true); + regcache_cache_bypass(one->regmap, true); /* Enable write access to enhanced features and internal clock div */ sc16is7xx_port_update(port, SC16IS7XX_EFR_REG, @@ -1195,7 +1168,7 @@ static int sc16is7xx_startup(struct uart_port *port) SC16IS7XX_TCR_RX_RESUME(24) | SC16IS7XX_TCR_RX_HALT(48)); - regcache_cache_bypass(s->regmap, false); + regcache_cache_bypass(one->regmap, false); /* Now, initialize the UART */ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_WORD_LEN_8); @@ -1425,7 +1398,7 @@ static void sc16is7xx_setup_irda_ports(struct sc16is7xx_port *s) int i; int ret; int count; - u32 irda_port[2]; + u32 irda_port[SC16IS7XX_MAX_PORTS]; struct device *dev = s->p[0].port.dev; count = device_property_count_u32(dev, "irda-mode-ports"); @@ -1446,12 +1419,13 @@ static void sc16is7xx_setup_irda_ports(struct sc16is7xx_port *s) /* * Configure ports designated to operate as modem control lines. */ -static int sc16is7xx_setup_mctrl_ports(struct sc16is7xx_port *s) +static int sc16is7xx_setup_mctrl_ports(struct sc16is7xx_port *s, + struct regmap *regmap) { int i; int ret; int count; - u32 mctrl_port[2]; + u32 mctrl_port[SC16IS7XX_MAX_PORTS]; struct device *dev = s->p[0].port.dev; count = device_property_count_u32(dev, "nxp,modem-control-line-ports"); @@ -1475,8 +1449,8 @@ static int sc16is7xx_setup_mctrl_ports(struct sc16is7xx_port *s) if (s->mctrl_mask) regmap_update_bits( - s->regmap, - SC16IS7XX_IOCONTROL_REG << SC16IS7XX_REG_SHIFT, + regmap, + SC16IS7XX_IOCONTROL_REG, SC16IS7XX_IOCONTROL_MODEM_A_BIT | SC16IS7XX_IOCONTROL_MODEM_B_BIT, s->mctrl_mask); @@ -1491,7 +1465,7 @@ static const struct serial_rs485 sc16is7xx_rs485_supported = { static int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype, - struct regmap *regmap, int irq) + struct regmap *regmaps[], int irq) { unsigned long freq = 0, *pfreq = dev_get_platdata(dev); unsigned int val; @@ -1499,16 +1473,20 @@ static int sc16is7xx_probe(struct device *dev, int i, ret; struct sc16is7xx_port *s; - if (IS_ERR(regmap)) - return PTR_ERR(regmap); + for (i = 0; i < devtype->nr_uart; i++) + if (IS_ERR(regmaps[i])) + return PTR_ERR(regmaps[i]); /* * This device does not have an identification register that would * tell us if we are really connected to the correct device. * The best we can do is to check if communication is at all possible. + * + * Note: regmap[0] is used in the probe function to access registers + * common to all channels/ports, as it is guaranteed to be present on + * all variants. */ - ret = regmap_read(regmap, - SC16IS7XX_LSR_REG << SC16IS7XX_REG_SHIFT, &val); + ret = regmap_read(regmaps[0], SC16IS7XX_LSR_REG, &val); if (ret < 0) return -EPROBE_DEFER; @@ -1542,10 +1520,8 @@ static int sc16is7xx_probe(struct device *dev, return -EINVAL; } - s->regmap = regmap; s->devtype = devtype; dev_set_drvdata(dev, s); - mutex_init(&s->efr_lock); kthread_init_worker(&s->kworker); s->kworker_task = kthread_run(kthread_worker_fn, &s->kworker, @@ -1557,11 +1533,17 @@ static int sc16is7xx_probe(struct device *dev, sched_set_fifo(s->kworker_task); /* reset device, purging any pending irq / data */ - regmap_write(s->regmap, SC16IS7XX_IOCONTROL_REG << SC16IS7XX_REG_SHIFT, - SC16IS7XX_IOCONTROL_SRESET_BIT); + regmap_write(regmaps[0], SC16IS7XX_IOCONTROL_REG, + SC16IS7XX_IOCONTROL_SRESET_BIT); for (i = 0; i < devtype->nr_uart; ++i) { - s->p[i].line = i; + s->p[i].port.line = find_first_zero_bit(sc16is7xx_lines, + SC16IS7XX_MAX_DEVS); + if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) { + ret = -ERANGE; + goto out_ports; + } + /* Initialize port data */ s->p[i].port.dev = dev; s->p[i].port.irq = irq; @@ -1581,12 +1563,9 @@ static int sc16is7xx_probe(struct device *dev, s->p[i].port.rs485_supported = sc16is7xx_rs485_supported; s->p[i].port.ops = &sc16is7xx_ops; s->p[i].old_mctrl = 0; - s->p[i].port.line = sc16is7xx_alloc_line(); + s->p[i].regmap = regmaps[i]; - if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) { - ret = -ENOMEM; - goto out_ports; - } + mutex_init(&s->p[i].efr_lock); ret = uart_get_rs485_mode(&s->p[i].port); if (ret) @@ -1603,20 +1582,25 @@ static int sc16is7xx_probe(struct device *dev, kthread_init_work(&s->p[i].tx_work, sc16is7xx_tx_proc); kthread_init_work(&s->p[i].reg_work, sc16is7xx_reg_proc); kthread_init_delayed_work(&s->p[i].ms_work, sc16is7xx_ms_proc); + /* Register port */ - uart_add_one_port(&sc16is7xx_uart, &s->p[i].port); + ret = uart_add_one_port(&sc16is7xx_uart, &s->p[i].port); + if (ret) + goto out_ports; + + set_bit(s->p[i].port.line, sc16is7xx_lines); /* Enable EFR */ sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_CONF_MODE_B); - regcache_cache_bypass(s->regmap, true); + regcache_cache_bypass(regmaps[i], true); /* Enable write access to enhanced features */ sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFR_REG, SC16IS7XX_EFR_ENABLE_BIT); - regcache_cache_bypass(s->regmap, false); + regcache_cache_bypass(regmaps[i], false); /* Restore access to general registers */ sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, 0x00); @@ -1627,7 +1611,7 @@ static int sc16is7xx_probe(struct device *dev, sc16is7xx_setup_irda_ports(s); - ret = sc16is7xx_setup_mctrl_ports(s); + ret = sc16is7xx_setup_mctrl_ports(s, regmaps[0]); if (ret) goto out_ports; @@ -1662,10 +1646,9 @@ static int sc16is7xx_probe(struct device *dev, #endif out_ports: - for (i--; i >= 0; i--) { - uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); - clear_bit(s->p[i].port.line, &sc16is7xx_lines); - } + for (i = 0; i < devtype->nr_uart; i++) + if (test_and_clear_bit(s->p[i].port.line, sc16is7xx_lines)) + uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); kthread_stop(s->kworker_task); @@ -1687,8 +1670,8 @@ static void sc16is7xx_remove(struct device *dev) for (i = 0; i < s->devtype->nr_uart; i++) { kthread_cancel_delayed_work_sync(&s->p[i].ms_work); - uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); - clear_bit(s->p[i].port.line, &sc16is7xx_lines); + if (test_and_clear_bit(s->p[i].port.line, sc16is7xx_lines)) + uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); sc16is7xx_power(&s->p[i].port, 0); } @@ -1710,45 +1693,74 @@ static const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = { MODULE_DEVICE_TABLE(of, sc16is7xx_dt_ids); static struct regmap_config regcfg = { - .reg_bits = 7, - .pad_bits = 1, + .reg_bits = 5, + .pad_bits = 3, .val_bits = 8, .cache_type = REGCACHE_RBTREE, .volatile_reg = sc16is7xx_regmap_volatile, .precious_reg = sc16is7xx_regmap_precious, + .writeable_noinc_reg = sc16is7xx_regmap_noinc, + .readable_noinc_reg = sc16is7xx_regmap_noinc, + .max_raw_read = SC16IS7XX_FIFO_SIZE, + .max_raw_write = SC16IS7XX_FIFO_SIZE, + .max_register = SC16IS7XX_EFCR_REG, }; +static const char *sc16is7xx_regmap_name(u8 port_id) +{ + switch (port_id) { + case 0: return "port0"; + case 1: return "port1"; + default: + WARN_ON(true); + return NULL; + } +} + +static unsigned int sc16is7xx_regmap_port_mask(unsigned int port_id) +{ + /* CH1,CH0 are at bits 2:1. */ + return port_id << 1; +} + #ifdef CONFIG_SERIAL_SC16IS7XX_SPI static int sc16is7xx_spi_probe(struct spi_device *spi) { const struct sc16is7xx_devtype *devtype; - struct regmap *regmap; + struct regmap *regmaps[SC16IS7XX_MAX_PORTS]; + unsigned int i; int ret; /* Setup SPI bus */ spi->bits_per_word = 8; - /* only supports mode 0 on SC16IS762 */ + /* For all variants, only mode 0 is supported */ + if ((spi->mode & SPI_MODE_X_MASK) != SPI_MODE_0) + return dev_err_probe(&spi->dev, -EINVAL, "Unsupported SPI mode\n"); + spi->mode = spi->mode ? : SPI_MODE_0; - spi->max_speed_hz = spi->max_speed_hz ? : 15000000; + spi->max_speed_hz = spi->max_speed_hz ? : 4 * HZ_PER_MHZ; ret = spi_setup(spi); if (ret) return ret; - if (spi->dev.of_node) { - devtype = device_get_match_data(&spi->dev); - if (!devtype) - return -ENODEV; - } else { - const struct spi_device_id *id_entry = spi_get_device_id(spi); + devtype = spi_get_device_match_data(spi); + if (!devtype) + return dev_err_probe(&spi->dev, -ENODEV, "Failed to match device\n"); - devtype = (struct sc16is7xx_devtype *)id_entry->driver_data; + for (i = 0; i < devtype->nr_uart; i++) { + regcfg.name = sc16is7xx_regmap_name(i); + /* + * If read_flag_mask is 0, the regmap code sets it to a default + * of 0x80. Since we specify our own mask, we must add the READ + * bit ourselves: + */ + regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i) | + SC16IS7XX_SPI_READ_BIT; + regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); + regmaps[i] = devm_regmap_init_spi(spi, ®cfg); } - regcfg.max_register = (0xf << SC16IS7XX_REG_SHIFT) | - (devtype->nr_uart - 1); - regmap = devm_regmap_init_spi(spi, ®cfg); - - return sc16is7xx_probe(&spi->dev, devtype, regmap, spi->irq); + return sc16is7xx_probe(&spi->dev, devtype, regmaps, spi->irq); } static void sc16is7xx_spi_remove(struct spi_device *spi) @@ -1778,30 +1790,27 @@ static struct spi_driver sc16is7xx_spi_uart_driver = { .remove = sc16is7xx_spi_remove, .id_table = sc16is7xx_spi_id_table, }; - -MODULE_ALIAS("spi:sc16is7xx"); #endif #ifdef CONFIG_SERIAL_SC16IS7XX_I2C static int sc16is7xx_i2c_probe(struct i2c_client *i2c) { - const struct i2c_device_id *id = i2c_client_get_device_id(i2c); const struct sc16is7xx_devtype *devtype; - struct regmap *regmap; - - if (i2c->dev.of_node) { - devtype = device_get_match_data(&i2c->dev); - if (!devtype) - return -ENODEV; - } else { - devtype = (struct sc16is7xx_devtype *)id->driver_data; + struct regmap *regmaps[SC16IS7XX_MAX_PORTS]; + unsigned int i; + + devtype = i2c_get_match_data(i2c); + if (!devtype) + return dev_err_probe(&i2c->dev, -ENODEV, "Failed to match device\n"); + + for (i = 0; i < devtype->nr_uart; i++) { + regcfg.name = sc16is7xx_regmap_name(i); + regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i); + regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); + regmaps[i] = devm_regmap_init_i2c(i2c, ®cfg); } - regcfg.max_register = (0xf << SC16IS7XX_REG_SHIFT) | - (devtype->nr_uart - 1); - regmap = devm_regmap_init_i2c(i2c, ®cfg); - - return sc16is7xx_probe(&i2c->dev, devtype, regmap, i2c->irq); + return sc16is7xx_probe(&i2c->dev, devtype, regmaps, i2c->irq); } static void sc16is7xx_i2c_remove(struct i2c_client *client) diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 2be2c1098025..f24217a560d7 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -1021,7 +1021,7 @@ err_out: return ret; } -static int sccnxp_remove(struct platform_device *pdev) +static void sccnxp_remove(struct platform_device *pdev) { int i; struct sccnxp_port *s = platform_get_drvdata(pdev); @@ -1036,10 +1036,11 @@ static int sccnxp_remove(struct platform_device *pdev) uart_unregister_driver(&s->uart); - if (!IS_ERR(s->regulator)) - return regulator_disable(s->regulator); - - return 0; + if (!IS_ERR(s->regulator)) { + int ret = regulator_disable(s->regulator); + if (ret) + dev_err(&pdev->dev, "Failed to disable regulator\n"); + } } static struct platform_driver sccnxp_uart_driver = { @@ -1047,7 +1048,7 @@ static struct platform_driver sccnxp_uart_driver = { .name = SCCNXP_NAME, }, .probe = sccnxp_probe, - .remove = sccnxp_remove, + .remove_new = sccnxp_remove, .id_table = sccnxp_id_table, }; module_platform_driver(sccnxp_uart_driver); diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 6d4006b41975..525f3a2f7bd4 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -1611,13 +1611,12 @@ static int tegra_uart_probe(struct platform_device *pdev) return ret; } -static int tegra_uart_remove(struct platform_device *pdev) +static void tegra_uart_remove(struct platform_device *pdev) { struct tegra_uart_port *tup = platform_get_drvdata(pdev); struct uart_port *u = &tup->uport; uart_remove_one_port(&tegra_uart_driver, u); - return 0; } #ifdef CONFIG_PM_SLEEP @@ -1644,7 +1643,7 @@ static const struct dev_pm_ops tegra_uart_pm_ops = { static struct platform_driver tegra_uart_platform_driver = { .probe = tegra_uart_probe, - .remove = tegra_uart_remove, + .remove_new = tegra_uart_remove, .driver = { .name = "serial-tegra", .of_match_table = tegra_uart_of_match, diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index f1348a509552..d6a58a9e072a 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -156,7 +156,7 @@ static void __uart_start(struct uart_state *state) * enabled, serial_port_runtime_resume() calls start_tx() again * after enabling the device. */ - if (!pm_runtime_enabled(port->dev) || pm_runtime_active(port->dev)) + if (pm_runtime_active(&port_dev->dev)) port->ops->start_tx(port); pm_runtime_mark_last_busy(&port_dev->dev); pm_runtime_put_autosuspend(&port_dev->dev); @@ -410,11 +410,10 @@ void uart_update_timeout(struct uart_port *port, unsigned int cflag, unsigned int baud) { - unsigned int size = tty_get_frame_size(cflag); - u64 frame_time; + u64 temp = tty_get_frame_size(cflag); - frame_time = (u64)size * NSEC_PER_SEC; - port->frame_time = DIV64_U64_ROUND_UP(frame_time, baud); + temp *= NSEC_PER_SEC; + port->frame_time = (unsigned int)DIV64_U64_ROUND_UP(temp, baud); } EXPORT_SYMBOL(uart_update_timeout); @@ -687,7 +686,7 @@ EXPORT_SYMBOL_GPL(uart_xchar_out); * This function is used to send a high-priority XON/XOFF character to * the device */ -static void uart_send_xchar(struct tty_struct *tty, char ch) +static void uart_send_xchar(struct tty_struct *tty, u8 ch) { struct uart_state *state = tty->driver_data; struct uart_port *port; @@ -1085,8 +1084,8 @@ static int uart_tiocmget(struct tty_struct *tty) goto out; if (!tty_io_error(tty)) { - result = uport->mctrl; uart_port_lock_irq(uport); + result = uport->mctrl; result |= uport->ops->get_mctrl(uport); uart_port_unlock_irq(uport); } @@ -1371,20 +1370,34 @@ static void uart_sanitize_serial_rs485(struct uart_port *port, struct serial_rs4 return; } - /* Pick sane settings if the user hasn't */ - if ((supported_flags & (SER_RS485_RTS_ON_SEND|SER_RS485_RTS_AFTER_SEND)) && - !(rs485->flags & SER_RS485_RTS_ON_SEND) == - !(rs485->flags & SER_RS485_RTS_AFTER_SEND)) { - dev_warn_ratelimited(port->dev, - "%s (%d): invalid RTS setting, using RTS_ON_SEND instead\n", - port->name, port->line); - rs485->flags |= SER_RS485_RTS_ON_SEND; - rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; - supported_flags |= SER_RS485_RTS_ON_SEND|SER_RS485_RTS_AFTER_SEND; + /* Clear other RS485 flags but SER_RS485_TERMINATE_BUS and return if enabling RS422 */ + if (rs485->flags & SER_RS485_MODE_RS422) { + rs485->flags &= (SER_RS485_ENABLED | SER_RS485_MODE_RS422 | SER_RS485_TERMINATE_BUS); + return; } rs485->flags &= supported_flags; + /* Pick sane settings if the user hasn't */ + if (!(rs485->flags & SER_RS485_RTS_ON_SEND) == + !(rs485->flags & SER_RS485_RTS_AFTER_SEND)) { + if (supported_flags & SER_RS485_RTS_ON_SEND) { + rs485->flags |= SER_RS485_RTS_ON_SEND; + rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; + + dev_warn_ratelimited(port->dev, + "%s (%d): invalid RTS setting, using RTS_ON_SEND instead\n", + port->name, port->line); + } else { + rs485->flags |= SER_RS485_RTS_AFTER_SEND; + rs485->flags &= ~SER_RS485_RTS_ON_SEND; + + dev_warn_ratelimited(port->dev, + "%s (%d): invalid RTS setting, using RTS_AFTER_SEND instead\n", + port->name, port->line); + } + } + uart_sanitize_serial_rs485_delays(port, rs485); /* Return clean padding area to userspace */ @@ -1402,6 +1415,16 @@ static void uart_set_rs485_termination(struct uart_port *port, !!(rs485->flags & SER_RS485_TERMINATE_BUS)); } +static void uart_set_rs485_rx_during_tx(struct uart_port *port, + const struct serial_rs485 *rs485) +{ + if (!(rs485->flags & SER_RS485_ENABLED)) + return; + + gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio, + !!(rs485->flags & SER_RS485_RX_DURING_TX)); +} + static int uart_rs485_config(struct uart_port *port) { struct serial_rs485 *rs485 = &port->rs485; @@ -1413,12 +1436,17 @@ static int uart_rs485_config(struct uart_port *port) uart_sanitize_serial_rs485(port, rs485); uart_set_rs485_termination(port, rs485); + uart_set_rs485_rx_during_tx(port, rs485); uart_port_lock_irqsave(port, &flags); ret = port->rs485_config(port, NULL, rs485); uart_port_unlock_irqrestore(port, flags); - if (ret) + if (ret) { memset(rs485, 0, sizeof(*rs485)); + /* unset GPIOs */ + gpiod_set_value_cansleep(port->rs485_term_gpio, 0); + gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio, 0); + } return ret; } @@ -1446,7 +1474,7 @@ static int uart_set_rs485_config(struct tty_struct *tty, struct uart_port *port, int ret; unsigned long flags; - if (!port->rs485_config) + if (!(port->rs485_supported.flags & SER_RS485_ENABLED)) return -ENOTTY; if (copy_from_user(&rs485, rs485_user, sizeof(*rs485_user))) @@ -1457,6 +1485,7 @@ static int uart_set_rs485_config(struct tty_struct *tty, struct uart_port *port, return ret; uart_sanitize_serial_rs485(port, &rs485); uart_set_rs485_termination(port, &rs485); + uart_set_rs485_rx_during_tx(port, &rs485); uart_port_lock_irqsave(port, &flags); ret = port->rs485_config(port, &tty->termios, &rs485); @@ -1468,8 +1497,14 @@ static int uart_set_rs485_config(struct tty_struct *tty, struct uart_port *port, port->ops->set_mctrl(port, port->mctrl); } uart_port_unlock_irqrestore(port, flags); - if (ret) + if (ret) { + /* restore old GPIO settings */ + gpiod_set_value_cansleep(port->rs485_term_gpio, + !!(port->rs485.flags & SER_RS485_TERMINATE_BUS)); + gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio, + !!(port->rs485.flags & SER_RS485_RX_DURING_TX)); return ret; + } if (copy_to_user(rs485_user, &port->rs485, sizeof(port->rs485))) return -EFAULT; @@ -2342,7 +2377,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) mutex_lock(&port->mutex); - tty_dev = device_find_child(uport->dev, &match, serial_match_port); + tty_dev = device_find_child(&uport->port_dev->dev, &match, serial_match_port); if (tty_dev && device_may_wakeup(tty_dev)) { enable_irq_wake(uport->irq); put_device(tty_dev); @@ -2423,7 +2458,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) mutex_lock(&port->mutex); - tty_dev = device_find_child(uport->dev, &match, serial_match_port); + tty_dev = device_find_child(&uport->port_dev->dev, &match, serial_match_port); if (!uport->suspended && device_may_wakeup(tty_dev)) { if (irqd_is_wakeup_set(irq_get_irq_data((uport->irq)))) disable_irq_wake(uport->irq); @@ -2633,7 +2668,8 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) mutex_lock(&tport->mutex); port = uart_port_check(state); - if (!port || !(port->ops->poll_get_char && port->ops->poll_put_char)) { + if (!port || port->type == PORT_UNKNOWN || + !(port->ops->poll_get_char && port->ops->poll_put_char)) { ret = -1; goto out; } @@ -3153,7 +3189,8 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u * setserial to be used to alter this port's parameters. */ tty_dev = tty_port_register_device_attr_serdev(port, drv->tty_driver, - uport->line, uport->dev, port, uport->tty_groups); + uport->line, uport->dev, &uport->port_dev->dev, port, + uport->tty_groups); if (!IS_ERR(tty_dev)) { device_set_wakeup_capable(tty_dev, 1); } else { @@ -3570,6 +3607,9 @@ int uart_get_rs485_mode(struct uart_port *port) u32 rs485_delay[2]; int ret; + if (!(port->rs485_supported.flags & SER_RS485_ENABLED)) + return 0; + ret = device_property_read_u32_array(dev, "rs485-rts-delay", rs485_delay, 2); if (!ret) { @@ -3620,6 +3660,8 @@ int uart_get_rs485_mode(struct uart_port *port) if (IS_ERR(desc)) return dev_err_probe(dev, PTR_ERR(desc), "Cannot get rs485-rx-during-tx-gpios\n"); port->rs485_rx_during_tx_gpio = desc; + if (port->rs485_rx_during_tx_gpio) + port->rs485_supported.flags |= SER_RS485_RX_DURING_TX; return 0; } diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index eaa980722455..e1897894a4ef 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -1052,7 +1052,7 @@ static int serial_txx9_probe(struct platform_device *dev) /* * Remove serial ports registered against a platform device. */ -static int serial_txx9_remove(struct platform_device *dev) +static void serial_txx9_remove(struct platform_device *dev) { int i; @@ -1062,7 +1062,6 @@ static int serial_txx9_remove(struct platform_device *dev) if (up->dev == &dev->dev) serial_txx9_unregister_port(i); } - return 0; } #ifdef CONFIG_PM @@ -1097,7 +1096,7 @@ static int serial_txx9_resume(struct platform_device *dev) static struct platform_driver serial_txx9_plat_driver = { .probe = serial_txx9_probe, - .remove = serial_txx9_remove, + .remove_new = serial_txx9_remove, #ifdef CONFIG_PM .suspend = serial_txx9_suspend, .resume = serial_txx9_resume, diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 84ab434c94ba..a85e7b9a2e49 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1558,10 +1558,9 @@ static struct dma_chan *sci_request_dma_chan(struct uart_port *port, struct dma_slave_config cfg; int ret; - chan = dma_request_slave_channel(port->dev, - dir == DMA_MEM_TO_DEV ? "tx" : "rx"); - if (!chan) { - dev_dbg(port->dev, "dma_request_slave_channel failed\n"); + chan = dma_request_chan(port->dev, dir == DMA_MEM_TO_DEV ? "tx" : "rx"); + if (IS_ERR(chan)) { + dev_dbg(port->dev, "dma_request_chan failed\n"); return NULL; } @@ -3190,7 +3189,7 @@ static struct uart_driver sci_uart_driver = { .cons = SCI_CONSOLE, }; -static int sci_remove(struct platform_device *dev) +static void sci_remove(struct platform_device *dev) { struct sci_port *port = platform_get_drvdata(dev); unsigned int type = port->port.type; /* uart_remove_... clears it */ @@ -3204,8 +3203,6 @@ static int sci_remove(struct platform_device *dev) device_remove_file(&dev->dev, &dev_attr_rx_fifo_trigger); if (type == PORT_SCIFA || type == PORT_SCIFB || type == PORT_HSCIF) device_remove_file(&dev->dev, &dev_attr_rx_fifo_timeout); - - return 0; } @@ -3470,7 +3467,7 @@ static SIMPLE_DEV_PM_OPS(sci_dev_pm_ops, sci_suspend, sci_resume); static struct platform_driver sci_driver = { .probe = sci_probe, - .remove = sci_remove, + .remove_new = sci_remove, .driver = { .name = "sh-sci", .pm = &sci_dev_pm_ops, diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c index b296e57a9dee..a4cc569a78a2 100644 --- a/drivers/tty/serial/sifive.c +++ b/drivers/tty/serial/sifive.c @@ -1007,7 +1007,7 @@ probe_out1: return r; } -static int sifive_serial_remove(struct platform_device *dev) +static void sifive_serial_remove(struct platform_device *dev) { struct sifive_serial_port *ssp = platform_get_drvdata(dev); @@ -1015,8 +1015,6 @@ static int sifive_serial_remove(struct platform_device *dev) uart_remove_one_port(&sifive_serial_uart_driver, &ssp->port); free_irq(ssp->port.irq, ssp); clk_notifier_unregister(ssp->clk, &ssp->clk_notifier); - - return 0; } static int sifive_serial_suspend(struct device *dev) @@ -1033,8 +1031,8 @@ static int sifive_serial_resume(struct device *dev) return uart_resume_port(&sifive_serial_uart_driver, &ssp->port); } -DEFINE_SIMPLE_DEV_PM_OPS(sifive_uart_pm_ops, sifive_serial_suspend, - sifive_serial_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(sifive_uart_pm_ops, sifive_serial_suspend, + sifive_serial_resume); static const struct of_device_id sifive_serial_of_match[] = { { .compatible = "sifive,fu540-c000-uart0" }, @@ -1045,7 +1043,7 @@ MODULE_DEVICE_TABLE(of, sifive_serial_of_match); static struct platform_driver sifive_serial_platform_driver = { .probe = sifive_serial_probe, - .remove = sifive_serial_remove, + .remove_new = sifive_serial_remove, .driver = { .name = SIFIVE_SERIAL_NAME, .pm = pm_sleep_ptr(&sifive_uart_pm_ops), diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index f257525f9299..15f14fa593da 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -1076,7 +1076,7 @@ static struct uart_driver sprd_uart_driver = { .cons = SPRD_CONSOLE, }; -static int sprd_remove(struct platform_device *dev) +static void sprd_remove(struct platform_device *dev) { struct sprd_uart_port *sup = platform_get_drvdata(dev); @@ -1089,8 +1089,6 @@ static int sprd_remove(struct platform_device *dev) if (!sprd_ports_num) uart_unregister_driver(&sprd_uart_driver); - - return 0; } static bool sprd_uart_is_console(struct uart_port *uport) @@ -1257,7 +1255,7 @@ MODULE_DEVICE_TABLE(of, serial_ids); static struct platform_driver sprd_platform_driver = { .probe = sprd_probe, - .remove = sprd_remove, + .remove_new = sprd_remove, .driver = { .name = "sprd_serial", .of_match_table = serial_ids, diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index a821f5d76a26..bbb5595d7e24 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -793,13 +793,11 @@ static int asc_serial_probe(struct platform_device *pdev) return 0; } -static int asc_serial_remove(struct platform_device *pdev) +static void asc_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); uart_remove_one_port(&asc_uart_driver, port); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -932,7 +930,7 @@ static const struct dev_pm_ops asc_serial_pm_ops = { static struct platform_driver asc_serial_driver = { .probe = asc_serial_probe, - .remove = asc_serial_remove, + .remove_new = asc_serial_remove, .driver = { .name = DRIVER_NAME, .pm = &asc_serial_pm_ops, diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 3048620315d6..794b77512740 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -226,12 +226,6 @@ static int stm32_usart_config_rs485(struct uart_port *port, struct ktermios *ter stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); - if (port->rs485_rx_during_tx_gpio) - gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio, - !!(rs485conf->flags & SER_RS485_RX_DURING_TX)); - else - rs485conf->flags |= SER_RS485_RX_DURING_TX; - if (rs485conf->flags & SER_RS485_ENABLED) { cr1 = readl_relaxed(port->membase + ofs->cr1); cr3 = readl_relaxed(port->membase + ofs->cr3); @@ -256,6 +250,8 @@ static int stm32_usart_config_rs485(struct uart_port *port, struct ktermios *ter writel_relaxed(cr3, port->membase + ofs->cr3); writel_relaxed(cr1, port->membase + ofs->cr1); + + rs485conf->flags |= SER_RS485_RX_DURING_TX; } else { stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DEM | USART_CR3_DEP); @@ -1822,7 +1818,7 @@ err_dma_rx: return ret; } -static int stm32_usart_serial_remove(struct platform_device *pdev) +static void stm32_usart_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); struct stm32_port *stm32_port = to_stm32_port(port); @@ -1861,8 +1857,6 @@ static int stm32_usart_serial_remove(struct platform_device *pdev) } stm32_usart_deinit_port(stm32_port); - - return 0; } static void __maybe_unused stm32_usart_console_putchar(struct uart_port *port, unsigned char ch) @@ -2146,7 +2140,7 @@ static const struct dev_pm_ops stm32_serial_pm_ops = { static struct platform_driver stm32_serial_driver = { .probe = stm32_usart_serial_probe, - .remove = stm32_usart_serial_remove, + .remove_new = stm32_usart_serial_remove, .driver = { .name = DRIVER_NAME, .pm = &stm32_serial_pm_ops, diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 5bfc0040f17b..8d612ab80680 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -595,7 +595,7 @@ out_free_port: return err; } -static int hv_remove(struct platform_device *dev) +static void hv_remove(struct platform_device *dev) { struct uart_port *port = platform_get_drvdata(dev); @@ -608,8 +608,6 @@ static int hv_remove(struct platform_device *dev) kfree(con_write_page); kfree(port); sunhv_port = NULL; - - return 0; } static const struct of_device_id hv_match[] = { @@ -630,7 +628,7 @@ static struct platform_driver hv_driver = { .of_match_table = hv_match, }, .probe = hv_probe, - .remove = hv_remove, + .remove_new = hv_remove, }; static int __init sunhv_init(void) diff --git a/drivers/tty/serial/sunplus-uart.c b/drivers/tty/serial/sunplus-uart.c index 4251f4e1ba99..99f5285819d4 100644 --- a/drivers/tty/serial/sunplus-uart.c +++ b/drivers/tty/serial/sunplus-uart.c @@ -662,13 +662,11 @@ static int sunplus_uart_probe(struct platform_device *pdev) return ret; } -static int sunplus_uart_remove(struct platform_device *pdev) +static void sunplus_uart_remove(struct platform_device *pdev) { struct sunplus_uart_port *sup = platform_get_drvdata(pdev); uart_remove_one_port(&sunplus_uart_driver, &sup->port); - - return 0; } static int __maybe_unused sunplus_uart_suspend(struct device *dev) @@ -703,7 +701,7 @@ MODULE_DEVICE_TABLE(of, sp_uart_of_match); static struct platform_driver sunplus_uart_platform_driver = { .probe = sunplus_uart_probe, - .remove = sunplus_uart_remove, + .remove_new = sunplus_uart_remove, .driver = { .name = "sunplus_uart", .of_match_table = sp_uart_of_match, diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index 6aa51a6f8063..1ea2f33a07a7 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -443,7 +443,7 @@ static void sunsab_start_tx(struct uart_port *port) up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); writeb(up->interrupt_mask1, &up->regs->w.imr1); - + if (!test_bit(SAB82532_XPR, &up->irqflags)) return; @@ -549,7 +549,7 @@ static int sunsab_startup(struct uart_port *port) (void) readb(&up->regs->r.isr1); /* - * Now, initialize the UART + * Now, initialize the UART */ writeb(0, &up->regs->w.ccr0); /* power-down */ writeb(SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ | @@ -563,7 +563,7 @@ static int sunsab_startup(struct uart_port *port) SAB82532_MODE_RAC); writeb(up->cached_mode, &up->regs->w.mode); writeb(SAB82532_RFC_DPS|SAB82532_RFC_RFTH_32, &up->regs->w.rfc); - + tmp = readb(&up->regs->rw.ccr0); tmp |= SAB82532_CCR0_PU; /* power-up */ writeb(tmp, &up->regs->rw.ccr0); @@ -607,7 +607,7 @@ static void sunsab_shutdown(struct uart_port *port) up->cached_dafo &= ~SAB82532_DAFO_XBRK; writeb(up->cached_dafo, &up->regs->rw.dafo); - /* Disable Receiver */ + /* Disable Receiver */ up->cached_mode &= ~SAB82532_MODE_RAC; writeb(up->cached_mode, &up->regs->rw.mode); @@ -622,7 +622,7 @@ static void sunsab_shutdown(struct uart_port *port) * speed the chip was configured for when the port was open). */ #if 0 - /* Power Down */ + /* Power Down */ tmp = readb(&up->regs->rw.ccr0); tmp &= ~SAB82532_CCR0_PU; writeb(tmp, &up->regs->rw.ccr0); @@ -649,7 +649,7 @@ static void calc_ebrg(int baud, int *n_ret, int *m_ret) *m_ret = 0; return; } - + /* * We scale numbers by 10 so that we get better accuracy * without having to use floating point. Here we increment m @@ -788,7 +788,7 @@ static const char *sunsab_type(struct uart_port *port) { struct uart_sunsab_port *up = (void *)port; static char buf[36]; - + sprintf(buf, "SAB82532 %s", sab82532_version[up->type]); return buf; } @@ -933,7 +933,7 @@ static int sunsab_console_setup(struct console *con, char *options) sunsab_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); uart_port_unlock_irqrestore(&up->port, flags); - + return 0; } @@ -1066,7 +1066,7 @@ out: return err; } -static int sab_remove(struct platform_device *op) +static void sab_remove(struct platform_device *op) { struct uart_sunsab_port *up = platform_get_drvdata(op); @@ -1078,8 +1078,6 @@ static int sab_remove(struct platform_device *op) of_iounmap(&op->resource[0], up[0].port.membase, sizeof(union sab82532_async_regs)); - - return 0; } static const struct of_device_id sab_match[] = { @@ -1100,7 +1098,7 @@ static struct platform_driver sab_driver = { .of_match_table = sab_match, }, .probe = sab_probe, - .remove = sab_remove, + .remove_new = sab_remove, }; static int __init sunsab_init(void) diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 1e051cc2591c..c8b65f4b2710 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1515,7 +1515,7 @@ out_unmap: return err; } -static int su_remove(struct platform_device *op) +static void su_remove(struct platform_device *op) { struct uart_sunsu_port *up = platform_get_drvdata(op); bool kbdms = false; @@ -1536,8 +1536,6 @@ static int su_remove(struct platform_device *op) if (kbdms) kfree(up); - - return 0; } static const struct of_device_id su_match[] = { @@ -1565,7 +1563,7 @@ static struct platform_driver su_driver = { .of_match_table = su_match, }, .probe = su_probe, - .remove = su_remove, + .remove_new = su_remove, }; static int __init sunsu_init(void) diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index d3b5e864b727..c99289c6c8f8 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -1513,7 +1513,7 @@ static void zs_remove_one(struct uart_sunzilog_port *up) uart_remove_one_port(&sunzilog_reg, &up->port); } -static int zs_remove(struct platform_device *op) +static void zs_remove(struct platform_device *op) { struct uart_sunzilog_port *up = platform_get_drvdata(op); struct zilog_layout __iomem *regs; @@ -1523,8 +1523,6 @@ static int zs_remove(struct platform_device *op) regs = sunzilog_chip_regs[up[0].port.line / 2]; of_iounmap(&op->resource[0], regs, sizeof(struct zilog_layout)); - - return 0; } static const struct of_device_id zs_match[] = { @@ -1541,7 +1539,7 @@ static struct platform_driver zs_driver = { .of_match_table = zs_match, }, .probe = zs_probe, - .remove = zs_remove, + .remove_new = zs_remove, }; static int __init sunzilog_init(void) diff --git a/drivers/tty/serial/tegra-tcu.c b/drivers/tty/serial/tegra-tcu.c index 65069daf36ec..d9c78320eb02 100644 --- a/drivers/tty/serial/tegra-tcu.c +++ b/drivers/tty/serial/tegra-tcu.c @@ -266,7 +266,7 @@ free_tx: return err; } -static int tegra_tcu_remove(struct platform_device *pdev) +static void tegra_tcu_remove(struct platform_device *pdev) { struct tegra_tcu *tcu = platform_get_drvdata(pdev); @@ -277,8 +277,6 @@ static int tegra_tcu_remove(struct platform_device *pdev) uart_remove_one_port(&tcu->driver, &tcu->port); uart_unregister_driver(&tcu->driver); mbox_free_channel(tcu->tx); - - return 0; } static const struct of_device_id tegra_tcu_match[] = { @@ -293,7 +291,7 @@ static struct platform_driver tegra_tcu_driver = { .of_match_table = tegra_tcu_match, }, .probe = tegra_tcu_probe, - .remove = tegra_tcu_remove, + .remove_new = tegra_tcu_remove, }; module_platform_driver(tegra_tcu_driver); diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 0cc6524f5e8b..4bc89a9b380a 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -473,7 +473,7 @@ err_mem: return err; } -static int timbuart_remove(struct platform_device *dev) +static void timbuart_remove(struct platform_device *dev) { struct timbuart_port *uart = platform_get_drvdata(dev); @@ -481,8 +481,6 @@ static int timbuart_remove(struct platform_device *dev) uart_remove_one_port(&timbuart_driver, &uart->port); uart_unregister_driver(&timbuart_driver); kfree(uart); - - return 0; } static struct platform_driver timbuart_platform_driver = { @@ -490,7 +488,7 @@ static struct platform_driver timbuart_platform_driver = { .name = "timb-uart", }, .probe = timbuart_probe, - .remove = timbuart_remove, + .remove_new = timbuart_remove, }; module_platform_driver(timbuart_platform_driver); diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 404c14acafa5..10ba41b7be99 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -24,8 +24,13 @@ #include <linux/pm_runtime.h> #define ULITE_NAME "ttyUL" +#if CONFIG_SERIAL_UARTLITE_NR_UARTS > 4 +#define ULITE_MAJOR 0 /* use dynamic node allocation */ +#define ULITE_MINOR 0 +#else #define ULITE_MAJOR 204 #define ULITE_MINOR 187 +#endif #define ULITE_NR_UARTS CONFIG_SERIAL_UARTLITE_NR_UARTS /* --------------------------------------------------------------------- @@ -62,11 +67,11 @@ static struct uart_port *console_port; #endif /** - * struct uartlite_data: Driver private data - * reg_ops: Functions to read/write registers - * clk: Our parent clock, if present - * baud: The baud rate configured when this device was synthesized - * cflags: The cflags for parity and data bits + * struct uartlite_data - Driver private data + * @reg_ops: Functions to read/write registers + * @clk: Our parent clock, if present + * @baud: The baud rate configured when this device was synthesized + * @cflags: The cflags for parity and data bits */ struct uartlite_data { const struct uartlite_reg_ops *reg_ops; @@ -890,7 +895,7 @@ of_err: return ret; } -static int ulite_remove(struct platform_device *pdev) +static void ulite_remove(struct platform_device *pdev) { struct uart_port *port = dev_get_drvdata(&pdev->dev); struct uartlite_data *pdata = port->private_data; @@ -900,7 +905,6 @@ static int ulite_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); - return 0; } /* work with hotplug and coldplug */ @@ -908,7 +912,7 @@ MODULE_ALIAS("platform:uartlite"); static struct platform_driver ulite_platform_driver = { .probe = ulite_probe, - .remove = ulite_remove, + .remove_new = ulite_remove, .driver = { .name = "uartlite", .of_match_table = of_match_ptr(ulite_of_match), diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index ed7a6bb5596a..397b95dff7ed 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -189,10 +189,10 @@ struct uart_qe_port { u16 tx_fifosize; int wait_closing; u32 flags; - struct qe_bd *rx_bd_base; - struct qe_bd *rx_cur; - struct qe_bd *tx_bd_base; - struct qe_bd *tx_cur; + struct qe_bd __iomem *rx_bd_base; + struct qe_bd __iomem *rx_cur; + struct qe_bd __iomem *tx_bd_base; + struct qe_bd __iomem *tx_cur; unsigned char *tx_buf; unsigned char *rx_buf; void *bd_virt; /* virtual address of the BD buffers */ @@ -258,7 +258,7 @@ static unsigned int qe_uart_tx_empty(struct uart_port *port) { struct uart_qe_port *qe_port = container_of(port, struct uart_qe_port, port); - struct qe_bd *bdp = qe_port->tx_bd_base; + struct qe_bd __iomem *bdp = qe_port->tx_bd_base; while (1) { if (ioread16be(&bdp->status) & BD_SC_READY) @@ -330,7 +330,7 @@ static void qe_uart_stop_tx(struct uart_port *port) */ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) { - struct qe_bd *bdp; + struct qe_bd __iomem *bdp; unsigned char *p; unsigned int count; struct uart_port *port = &qe_port->port; @@ -341,7 +341,7 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) /* Pick next descriptor and fill from buffer */ bdp = qe_port->tx_cur; - p = qe2cpu_addr(be32_to_cpu(bdp->buf), qe_port); + p = qe2cpu_addr(ioread32be(&bdp->buf), qe_port); *p++ = port->x_char; iowrite16be(1, &bdp->length); @@ -368,7 +368,7 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) while (!(ioread16be(&bdp->status) & BD_SC_READY) && !uart_circ_empty(xmit)) { count = 0; - p = qe2cpu_addr(be32_to_cpu(bdp->buf), qe_port); + p = qe2cpu_addr(ioread32be(&bdp->buf), qe_port); while (count < qe_port->tx_fifosize) { *p++ = xmit->buf[xmit->tail]; uart_xmit_advance(port, 1); @@ -460,7 +460,7 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port) unsigned char ch, *cp; struct uart_port *port = &qe_port->port; struct tty_port *tport = &port->state->port; - struct qe_bd *bdp; + struct qe_bd __iomem *bdp; u16 status; unsigned int flg; @@ -487,7 +487,7 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port) } /* get pointer */ - cp = qe2cpu_addr(be32_to_cpu(bdp->buf), qe_port); + cp = qe2cpu_addr(ioread32be(&bdp->buf), qe_port); /* loop through the buffer */ while (i-- > 0) { @@ -590,7 +590,7 @@ static void qe_uart_initbd(struct uart_qe_port *qe_port) { int i; void *bd_virt; - struct qe_bd *bdp; + struct qe_bd __iomem *bdp; /* Set the physical address of the host memory buffers in the buffer * descriptors, and the virtual address for us to work with. @@ -648,7 +648,7 @@ static void qe_uart_init_ucc(struct uart_qe_port *qe_port) { u32 cecr_subblock; struct ucc_slow __iomem *uccp = qe_port->uccp; - struct ucc_uart_pram *uccup = qe_port->uccup; + struct ucc_uart_pram __iomem *uccup = qe_port->uccup; unsigned int i; @@ -983,7 +983,7 @@ static int qe_uart_request_port(struct uart_port *port) qe_port->us_private = uccs; qe_port->uccp = uccs->us_regs; - qe_port->uccup = (struct ucc_uart_pram *) uccs->us_pram; + qe_port->uccup = (struct ucc_uart_pram __iomem *)uccs->us_pram; qe_port->rx_bd_base = uccs->rx_bd; qe_port->tx_bd_base = uccs->tx_bd; @@ -1156,7 +1156,7 @@ static void uart_firmware_cont(const struct firmware *fw, void *context) firmware = (struct qe_firmware *) fw->data; - if (firmware->header.length != fw->size) { + if (be32_to_cpu(firmware->header.length) != fw->size) { dev_err(dev, "invalid firmware\n"); goto out; } @@ -1459,7 +1459,7 @@ out_free: return ret; } -static int ucc_uart_remove(struct platform_device *ofdev) +static void ucc_uart_remove(struct platform_device *ofdev) { struct uart_qe_port *qe_port = platform_get_drvdata(ofdev); @@ -1470,8 +1470,6 @@ static int ucc_uart_remove(struct platform_device *ofdev) of_node_put(qe_port->np); kfree(qe_port); - - return 0; } static const struct of_device_id ucc_uart_match[] = { @@ -1492,7 +1490,7 @@ static struct platform_driver ucc_uart_of_driver = { .of_match_table = ucc_uart_match, }, .probe = ucc_uart_probe, - .remove = ucc_uart_remove, + .remove_new = ucc_uart_remove, }; static int __init ucc_uart_init(void) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 66a45a634158..920762d7b4a4 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1663,10 +1663,8 @@ err_out_unregister_driver: /** * cdns_uart_remove - called when the platform driver is unregistered * @pdev: Pointer to the platform device structure - * - * Return: 0 on success, negative errno otherwise */ -static int cdns_uart_remove(struct platform_device *pdev) +static void cdns_uart_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); struct cdns_uart *cdns_uart_data = port->private_data; @@ -1692,12 +1690,11 @@ static int cdns_uart_remove(struct platform_device *pdev) if (!--instances) uart_unregister_driver(cdns_uart_data->cdns_uart_driver); - return 0; } static struct platform_driver cdns_uart_platform_driver = { .probe = cdns_uart_probe, - .remove = cdns_uart_remove, + .remove_new = cdns_uart_remove, .driver = { .name = CDNS_UART_NAME, .of_match_table = cdns_uart_of_match, diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 6b4a28bcf2f5..02217e3c916b 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -1150,16 +1150,29 @@ EXPORT_SYMBOL(unregister_sysrq_key); #ifdef CONFIG_PROC_FS /* * writing 'C' to /proc/sysrq-trigger is like sysrq-C + * Normally, only the first character written is processed. + * However, if the first character is an underscore, + * all characters are processed. */ static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - if (count) { + bool bulk = false; + size_t i; + + for (i = 0; i < count; i++) { char c; - if (get_user(c, buf)) + if (get_user(c, buf + i)) return -EFAULT; - __handle_sysrq(c, false); + + if (c == '_') + bulk = true; + else + __handle_sysrq(c, false); + + if (!bulk) + break; } return count; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 06414e43e0b5..407b0d87b7c1 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -852,9 +852,9 @@ static ssize_t iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, { void *cookie = NULL; unsigned long offset = 0; - char kernel_buf[64]; ssize_t retval = 0; size_t copied, count = iov_iter_count(to); + u8 kernel_buf[64]; do { ssize_t size = min(count, sizeof(kernel_buf)); @@ -995,7 +995,7 @@ static ssize_t iterate_tty_write(struct tty_ldisc *ld, struct tty_struct *tty, /* write_buf/write_cnt is protected by the atomic_write_lock mutex */ if (tty->write_cnt < chunk) { - unsigned char *buf_chunk; + u8 *buf_chunk; if (chunk < 1024) chunk = 1024; @@ -1047,6 +1047,7 @@ out: return ret; } +#ifdef CONFIG_PRINT_QUOTA_WARNING /** * tty_write_message - write a message to a certain tty, not just the console. * @tty: the destination tty_struct @@ -1057,6 +1058,8 @@ out: * needed. * * We must still hold the BTM and test the CLOSING flag for the moment. + * + * This function is DEPRECATED, do not use in new code. */ void tty_write_message(struct tty_struct *tty, char *msg) { @@ -1069,6 +1072,7 @@ void tty_write_message(struct tty_struct *tty, char *msg) tty_write_unlock(tty); } } +#endif static ssize_t file_tty_write(struct file *file, struct kiocb *iocb, struct iov_iter *from) { @@ -1145,7 +1149,7 @@ ssize_t redirected_tty_write(struct kiocb *iocb, struct iov_iter *iter) * * Locking: none for xchar method, write ordering for write method. */ -int tty_send_xchar(struct tty_struct *tty, char ch) +int tty_send_xchar(struct tty_struct *tty, u8 ch) { bool was_stopped = tty->flow.stopped; @@ -2274,10 +2278,10 @@ static bool tty_legacy_tiocsti __read_mostly = IS_ENABLED(CONFIG_LEGACY_TIOCSTI) * * Called functions take tty_ldiscs_lock * * current->signal->tty check is safe without locks */ -static int tiocsti(struct tty_struct *tty, char __user *p) +static int tiocsti(struct tty_struct *tty, u8 __user *p) { - char ch, mbz = 0; struct tty_ldisc *ld; + u8 ch; if (!tty_legacy_tiocsti && !capable(CAP_SYS_ADMIN)) return -EIO; @@ -2292,7 +2296,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p) return -EIO; tty_buffer_lock_exclusive(tty->port); if (ld->ops->receive_buf) - ld->ops->receive_buf(tty, &ch, &mbz, 1); + ld->ops->receive_buf(tty, &ch, NULL, 1); tty_buffer_unlock_exclusive(tty->port); tty_ldisc_deref(ld); return 0; @@ -2489,6 +2493,9 @@ static int send_break(struct tty_struct *tty, unsigned int duration) if (!retval) { msleep_interruptible(duration); retval = tty->ops->break_ctl(tty, 0); + } else if (retval == -EOPNOTSUPP) { + /* some drivers can tell only dynamically */ + retval = 0; } tty_write_unlock(tty); @@ -2499,6 +2506,24 @@ static int send_break(struct tty_struct *tty, unsigned int duration) } /** + * tty_get_tiocm - get tiocm status register + * @tty: tty device + * + * Obtain the modem status bits from the tty driver if the feature + * is supported. + */ +int tty_get_tiocm(struct tty_struct *tty) +{ + int retval = -ENOTTY; + + if (tty->ops->tiocmget) + retval = tty->ops->tiocmget(tty); + + return retval; +} +EXPORT_SYMBOL_GPL(tty_get_tiocm); + +/** * tty_tiocmget - get modem status * @tty: tty device * @p: pointer to result @@ -2510,14 +2535,12 @@ static int send_break(struct tty_struct *tty, unsigned int duration) */ static int tty_tiocmget(struct tty_struct *tty, int __user *p) { - int retval = -ENOTTY; + int retval; - if (tty->ops->tiocmget) { - retval = tty->ops->tiocmget(tty); + retval = tty_get_tiocm(tty); + if (retval >= 0) + retval = put_user(retval, p); - if (retval >= 0) - retval = put_user(retval, p); - } return retval; } @@ -3138,7 +3161,7 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx) * * Return: the number of characters successfully output. */ -int tty_put_char(struct tty_struct *tty, unsigned char ch) +int tty_put_char(struct tty_struct *tty, u8 ch) { if (tty->ops->put_char) return tty->ops->put_char(tty, ch); diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 4b499301a3db..85de90eebc7b 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -844,7 +844,7 @@ int tty_mode_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) ret = -EFAULT; return ret; case TIOCSLCKTRMIOS: - if (!capable(CAP_SYS_ADMIN)) + if (!checkpoint_restore_ns_capable(&init_user_ns)) return -EPERM; copy_termios_locked(real_tty, &kterm); if (user_termios_to_kernel_termios(&kterm, @@ -861,7 +861,7 @@ int tty_mode_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) ret = -EFAULT; return ret; case TIOCSLCKTRMIOS: - if (!capable(CAP_SYS_ADMIN)) + if (!checkpoint_restore_ns_capable(&init_user_ns)) return -EPERM; copy_termios_locked(real_tty, &kterm); if (user_termios_to_kernel_termios_1(&kterm, diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 63c125250961..14cca33d2269 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -171,7 +171,8 @@ EXPORT_SYMBOL_GPL(tty_port_register_device_attr); * @port: tty_port of the device * @driver: tty_driver for this device * @index: index of the tty - * @device: parent if exists, otherwise NULL + * @host: serial port hardware device + * @parent: parent if exists, otherwise NULL * @drvdata: driver data for the device * @attr_grp: attribute group for the device * @@ -180,20 +181,20 @@ EXPORT_SYMBOL_GPL(tty_port_register_device_attr); */ struct device *tty_port_register_device_attr_serdev(struct tty_port *port, struct tty_driver *driver, unsigned index, - struct device *device, void *drvdata, + struct device *host, struct device *parent, void *drvdata, const struct attribute_group **attr_grp) { struct device *dev; tty_port_link_device(port, driver, index); - dev = serdev_tty_port_register(port, device, driver, index); + dev = serdev_tty_port_register(port, host, parent, driver, index); if (PTR_ERR(dev) != -ENODEV) { /* Skip creating cdev if we registered a serdev device */ return dev; } - return tty_register_device_attr(driver, index, device, drvdata, + return tty_register_device_attr(driver, index, parent, drvdata, attr_grp); } EXPORT_SYMBOL_GPL(tty_port_register_device_attr_serdev); @@ -203,17 +204,18 @@ EXPORT_SYMBOL_GPL(tty_port_register_device_attr_serdev); * @port: tty_port of the device * @driver: tty_driver for this device * @index: index of the tty - * @device: parent if exists, otherwise NULL + * @host: serial port hardware controller device + * @parent: parent if exists, otherwise NULL * * Register a serdev or tty device depending on if the parent device has any * defined serdev clients or not. */ struct device *tty_port_register_device_serdev(struct tty_port *port, struct tty_driver *driver, unsigned index, - struct device *device) + struct device *host, struct device *parent) { return tty_port_register_device_attr_serdev(port, driver, index, - device, NULL, NULL); + host, parent, NULL, NULL); } EXPORT_SYMBOL_GPL(tty_port_register_device_serdev); @@ -245,7 +247,7 @@ int tty_port_alloc_xmit_buf(struct tty_port *port) /* We may sleep in get_zeroed_page() */ mutex_lock(&port->buf_mutex); if (port->xmit_buf == NULL) { - port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); + port->xmit_buf = (u8 *)get_zeroed_page(GFP_KERNEL); if (port->xmit_buf) kfifo_init(&port->xmit_fifo, port->xmit_buf, PAGE_SIZE); } diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 5e39a4f430ee..82d70083fead 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -644,7 +644,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) if (!ct) return 0; - unilist = vmemdup_user(list, array_size(sizeof(*unilist), ct)); + unilist = vmemdup_array_user(list, ct, sizeof(*unilist)); if (IS_ERR(unilist)) return PTR_ERR(unilist); diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 12a192e1196b..a2116e135a82 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -1772,12 +1772,10 @@ int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm) return -EINVAL; if (ct) { - - dia = memdup_user(a->kbdiacr, - sizeof(struct kbdiacr) * ct); + dia = memdup_array_user(a->kbdiacr, + ct, sizeof(struct kbdiacr)); if (IS_ERR(dia)) return PTR_ERR(dia); - } spin_lock_irqsave(&kbd_event_lock, flags); @@ -1811,8 +1809,8 @@ int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm) return -EINVAL; if (ct) { - buf = memdup_user(a->kbdiacruc, - ct * sizeof(struct kbdiacruc)); + buf = memdup_array_user(a->kbdiacruc, + ct, sizeof(struct kbdiacruc)); if (IS_ERR(buf)) return PTR_ERR(buf); } diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index d1e33328ff3f..d77b25b79ae3 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -3057,7 +3057,7 @@ bool ufshcd_cmd_inflight(struct scsi_cmnd *cmd) */ static int ufshcd_clear_cmd(struct ufs_hba *hba, u32 task_tag) { - u32 mask = 1U << task_tag; + u32 mask; unsigned long flags; int err; @@ -3075,6 +3075,8 @@ static int ufshcd_clear_cmd(struct ufs_hba *hba, u32 task_tag) return 0; } + mask = 1U << task_tag; + /* clear outstanding transaction before retry */ spin_lock_irqsave(hba->host->host_lock, flags); ufshcd_utrl_clear(hba, mask); @@ -6352,7 +6354,6 @@ static void ufshcd_err_handling_prepare(struct ufs_hba *hba) ufshcd_hold(hba); if (!ufshcd_is_clkgating_allowed(hba)) ufshcd_setup_clocks(hba, true); - ufshcd_release(hba); pm_op = hba->is_sys_suspended ? UFS_SYSTEM_PM : UFS_RUNTIME_PM; ufshcd_vops_resume(hba, pm_op); } else { @@ -8725,7 +8726,6 @@ static int ufshcd_add_lus(struct ufs_hba *hba) ufs_bsg_probe(hba); scsi_scan_host(hba->host); - pm_runtime_put_sync(hba->dev); out: return ret; @@ -8994,15 +8994,12 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie) /* Probe and add UFS logical units */ ret = ufshcd_add_lus(hba); + out: - /* - * If we failed to initialize the device or the device is not - * present, turn off the power/clocks etc. - */ - if (ret) { - pm_runtime_put_sync(hba->dev); - ufshcd_hba_exit(hba); - } + pm_runtime_put_sync(hba->dev); + + if (ret) + dev_err(hba->dev, "%s failed: %d\n", __func__, ret); } static enum scsi_timeout_action ufshcd_eh_timed_out(struct scsi_cmnd *scmd) diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index 480787048e75..39eef470f8fa 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -1716,7 +1716,7 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) ufs_qcom_write_msi_msg); if (ret) { dev_err(hba->dev, "Failed to request Platform MSI %d\n", ret); - goto out; + return ret; } msi_lock_descs(hba->dev); @@ -1750,11 +1750,8 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) FIELD_PREP(ESI_VEC_MASK, MAX_ESI_VEC - 1), REG_UFS_CFG3); ufshcd_mcq_enable_esi(hba); - } - -out: - if (!ret) host->esi_enabled = true; + } return ret; } diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 62082d64ece0..2d572f6c8ec8 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -466,13 +466,13 @@ static int uio_open(struct inode *inode, struct file *filep) mutex_lock(&minor_lock); idev = idr_find(&uio_idr, iminor(inode)); - mutex_unlock(&minor_lock); if (!idev) { ret = -ENODEV; + mutex_unlock(&minor_lock); goto out; } - get_device(&idev->dev); + mutex_unlock(&minor_lock); if (!try_module_get(idev->owner)) { ret = -ENODEV; @@ -1064,9 +1064,8 @@ void uio_unregister_device(struct uio_info *info) wake_up_interruptible(&idev->wait); kill_fasync(&idev->async_queue, SIGIO, POLL_HUP); - device_unregister(&idev->dev); - uio_free_minor(minor); + device_unregister(&idev->dev); return; } diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 5812f7ea7f90..16703815be0c 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -546,7 +546,7 @@ MODULE_PARM_DESC(annex, #define uea_wait(sc, cond, timeo) \ ({ \ - int _r = wait_event_interruptible_timeout(sc->sync_q, \ + int _r = wait_event_freezable_timeout(sc->sync_q, \ (cond) || kthread_should_stop(), timeo); \ if (kthread_should_stop()) \ _r = -ENODEV; \ @@ -1896,7 +1896,6 @@ static int uea_kthread(void *data) ret = sc->stat(sc); if (ret != -EAGAIN) uea_wait(sc, 0, msecs_to_jiffies(1000)); - try_to_freeze(); } uea_leaves(INS_TO_USBDEV(sc)); return ret; @@ -2252,7 +2251,7 @@ static ssize_t stat_status_show(struct device *dev, struct device_attribute *att sc = dev_to_uea(dev); if (!sc) goto out; - ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.state); + ret = sysfs_emit(buf, "%08x\n", sc->stats.phy.state); out: mutex_unlock(&uea_mutex); return ret; @@ -2318,19 +2317,19 @@ static ssize_t stat_human_status_show(struct device *dev, switch (modem_state) { case 0: - ret = sprintf(buf, "Modem is booting\n"); + ret = sysfs_emit(buf, "Modem is booting\n"); break; case 1: - ret = sprintf(buf, "Modem is initializing\n"); + ret = sysfs_emit(buf, "Modem is initializing\n"); break; case 2: - ret = sprintf(buf, "Modem is operational\n"); + ret = sysfs_emit(buf, "Modem is operational\n"); break; case 3: - ret = sprintf(buf, "Modem synchronization failed\n"); + ret = sysfs_emit(buf, "Modem synchronization failed\n"); break; default: - ret = sprintf(buf, "Modem state is unknown\n"); + ret = sysfs_emit(buf, "Modem state is unknown\n"); break; } out: @@ -2364,7 +2363,7 @@ static ssize_t stat_delin_show(struct device *dev, struct device_attribute *attr delin = "LOSS"; } - ret = sprintf(buf, "%s\n", delin); + ret = sysfs_emit(buf, "%s\n", delin); out: mutex_unlock(&uea_mutex); return ret; @@ -2384,7 +2383,7 @@ static ssize_t stat_##name##_show(struct device *dev, \ sc = dev_to_uea(dev); \ if (!sc) \ goto out; \ - ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.name); \ + ret = sysfs_emit(buf, "%08x\n", sc->stats.phy.name); \ if (reset) \ sc->stats.phy.name = 0; \ out: \ diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 11a5b3437c32..aeca902ab6cc 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -1119,6 +1119,8 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, dma_addr_t trb_dma; u32 togle_pcs = 1; int sg_iter = 0; + int num_trb_req; + int trb_burst; int num_trb; int address; u32 control; @@ -1126,16 +1128,15 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, u16 total_tdl = 0; struct scatterlist *s = NULL; bool sg_supported = !!(request->num_mapped_sgs); + u32 ioc = request->no_interrupt ? 0 : TRB_IOC; + num_trb_req = sg_supported ? request->num_mapped_sgs : 1; + + /* ISO transfer require each SOF have a TD, each TD include some TRBs */ if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) - num_trb = priv_ep->interval; + num_trb = priv_ep->interval * num_trb_req; else - num_trb = sg_supported ? request->num_mapped_sgs : 1; - - if (num_trb > priv_ep->free_trbs) { - priv_ep->flags |= EP_RING_FULL; - return -ENOBUFS; - } + num_trb = num_trb_req; priv_req = to_cdns3_request(request); address = priv_ep->endpoint.desc->bEndpointAddress; @@ -1184,14 +1185,31 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, link_trb->control = cpu_to_le32(((priv_ep->pcs) ? TRB_CYCLE : 0) | TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit); + + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) { + /* + * ISO require LINK TRB must be first one of TD. + * Fill LINK TRBs for left trb space to simply software process logic. + */ + while (priv_ep->enqueue) { + *trb = *link_trb; + trace_cdns3_prepare_trb(priv_ep, trb); + + cdns3_ep_inc_enq(priv_ep); + trb = priv_ep->trb_pool + priv_ep->enqueue; + priv_req->trb = trb; + } + } + } + + if (num_trb > priv_ep->free_trbs) { + priv_ep->flags |= EP_RING_FULL; + return -ENOBUFS; } if (priv_dev->dev_ver <= DEV_VER_V2) togle_pcs = cdns3_wa1_update_guard(priv_ep, trb); - if (sg_supported) - s = request->sg; - /* set incorrect Cycle Bit for first trb*/ control = priv_ep->pcs ? 0 : TRB_CYCLE; trb->length = 0; @@ -1209,6 +1227,9 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, do { u32 length; + if (!(sg_iter % num_trb_req) && sg_supported) + s = request->sg; + /* fill TRB */ control |= TRB_TYPE(TRB_NORMAL); if (sg_supported) { @@ -1223,7 +1244,36 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, total_tdl += DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket); - trb->length |= cpu_to_le32(TRB_BURST_LEN(priv_ep->trb_burst_size) | + trb_burst = priv_ep->trb_burst_size; + + /* + * Supposed DMA cross 4k bounder problem should be fixed at DEV_VER_V2, but still + * met problem when do ISO transfer if sg enabled. + * + * Data pattern likes below when sg enabled, package size is 1k and mult is 2 + * [UVC Header(8B) ] [data(3k - 8)] ... + * + * The received data at offset 0xd000 will get 0xc000 data, len 0x70. Error happen + * as below pattern: + * 0xd000: wrong + * 0xe000: wrong + * 0xf000: correct + * 0x10000: wrong + * 0x11000: wrong + * 0x12000: correct + * ... + * + * But it is still unclear about why error have not happen below 0xd000, it should + * cross 4k bounder. But anyway, the below code can fix this problem. + * + * To avoid DMA cross 4k bounder at ISO transfer, reduce burst len according to 16. + */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && priv_dev->dev_ver <= DEV_VER_V2) + if (ALIGN_DOWN(trb->buffer, SZ_4K) != + ALIGN_DOWN(trb->buffer + length, SZ_4K)) + trb_burst = 16; + + trb->length |= cpu_to_le32(TRB_BURST_LEN(trb_burst) | TRB_LEN(length)); pcs = priv_ep->pcs ? TRB_CYCLE : 0; @@ -1235,11 +1285,11 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, control |= pcs; if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) { - control |= TRB_IOC | TRB_ISP; + control |= ioc | TRB_ISP; } else { /* for last element in TD or in SG list */ if (sg_iter == (num_trb - 1) && sg_iter != 0) - control |= pcs | TRB_IOC | TRB_ISP; + control |= pcs | ioc | TRB_ISP; } if (sg_iter) @@ -1250,7 +1300,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, if (sg_supported) { trb->control |= cpu_to_le32(TRB_ISP); /* Don't set chain bit for last TRB */ - if (sg_iter < num_trb - 1) + if ((sg_iter % num_trb_req) < num_trb_req - 1) trb->control |= cpu_to_le32(TRB_CHAIN); s = sg_next(s); @@ -1270,7 +1320,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, priv_req->num_of_trb = num_trb; if (sg_iter == 1) - trb->control |= cpu_to_le32(TRB_IOC | TRB_ISP); + trb->control |= cpu_to_le32(ioc | TRB_ISP); if (priv_dev->dev_ver < DEV_VER_V2 && (priv_ep->flags & EP_TDLCHK_EN)) { @@ -1508,6 +1558,12 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev, /* The TRB was changed as link TRB, and the request was handled at ep_dequeue */ while (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK) { + + /* ISO ep_traddr may stop at LINK TRB */ + if (priv_ep->dequeue == cdns3_get_dma_pos(priv_dev, priv_ep) && + priv_ep->type == USB_ENDPOINT_XFER_ISOC) + break; + trace_cdns3_complete_trb(priv_ep, trb); cdns3_ep_inc_deq(priv_ep); trb = priv_ep->trb_pool + priv_ep->dequeue; @@ -1540,6 +1596,10 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev, } if (request_handled) { + /* TRBs are duplicated by priv_ep->interval time for ISO IN */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && priv_ep->dir) + request->actual /= priv_ep->interval; + cdns3_gadget_giveback(priv_ep, priv_req, 0); request_handled = false; transfer_end = false; @@ -2035,11 +2095,10 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC); struct cdns3_device *priv_dev = priv_ep->cdns3_dev; u32 bEndpointAddress = priv_ep->num | priv_ep->dir; - u32 max_packet_size = 0; - u8 maxburst = 0; + u32 max_packet_size = priv_ep->wMaxPacketSize; + u8 maxburst = priv_ep->bMaxBurst; u32 ep_cfg = 0; u8 buffering; - u8 mult = 0; int ret; buffering = priv_dev->ep_buf_size - 1; @@ -2061,8 +2120,7 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) break; default: ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC); - mult = priv_dev->ep_iso_burst - 1; - buffering = mult + 1; + buffering = (priv_ep->bMaxBurst + 1) * (priv_ep->mult + 1) - 1; } switch (priv_dev->gadget.speed) { @@ -2073,17 +2131,8 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) max_packet_size = is_iso_ep ? 1024 : 512; break; case USB_SPEED_SUPER: - /* It's limitation that driver assumes in driver. */ - mult = 0; - max_packet_size = 1024; - if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) { - maxburst = priv_dev->ep_iso_burst - 1; - buffering = (mult + 1) * - (maxburst + 1); - - if (priv_ep->interval > 1) - buffering++; - } else { + if (priv_ep->type != USB_ENDPOINT_XFER_ISOC) { + max_packet_size = 1024; maxburst = priv_dev->ep_buf_size - 1; } break; @@ -2112,7 +2161,6 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) if (priv_dev->dev_ver < DEV_VER_V2) priv_ep->trb_burst_size = 16; - mult = min_t(u8, mult, EP_CFG_MULT_MAX); buffering = min_t(u8, buffering, EP_CFG_BUFFERING_MAX); maxburst = min_t(u8, maxburst, EP_CFG_MAXBURST_MAX); @@ -2146,7 +2194,7 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) } ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) | - EP_CFG_MULT(mult) | + EP_CFG_MULT(priv_ep->mult) | /* must match EP setting */ EP_CFG_BUFFERING(buffering) | EP_CFG_MAXBURST(maxburst); @@ -2236,6 +2284,13 @@ usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget, priv_ep->type = usb_endpoint_type(desc); priv_ep->flags |= EP_CLAIMED; priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0; + priv_ep->wMaxPacketSize = usb_endpoint_maxp(desc); + priv_ep->mult = USB_EP_MAXP_MULT(priv_ep->wMaxPacketSize); + priv_ep->wMaxPacketSize &= USB_ENDPOINT_MAXP_MASK; + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && comp_desc) { + priv_ep->mult = USB_SS_MULT(comp_desc->bmAttributes) - 1; + priv_ep->bMaxBurst = comp_desc->bMaxBurst; + } spin_unlock_irqrestore(&priv_dev->lock, flags); return &priv_ep->endpoint; @@ -3019,22 +3074,40 @@ static int cdns3_gadget_check_config(struct usb_gadget *gadget) struct cdns3_endpoint *priv_ep; struct usb_ep *ep; int n_in = 0; + int iso = 0; + int out = 1; int total; + int n; list_for_each_entry(ep, &gadget->ep_list, ep_list) { priv_ep = ep_to_cdns3_ep(ep); - if ((priv_ep->flags & EP_CLAIMED) && (ep->address & USB_DIR_IN)) - n_in++; + if (!(priv_ep->flags & EP_CLAIMED)) + continue; + + n = (priv_ep->mult + 1) * (priv_ep->bMaxBurst + 1); + if (ep->address & USB_DIR_IN) { + /* + * ISO transfer: DMA start move data when get ISO, only transfer + * data as min(TD size, iso). No benefit for allocate bigger + * internal memory than 'iso'. + */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) + iso += n; + else + n_in++; + } else { + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) + out = max_t(int, out, n); + } } /* 2KB are reserved for EP0, 1KB for out*/ - total = 2 + n_in + 1; + total = 2 + n_in + out + iso; if (total > priv_dev->onchip_buffers) return -ENOMEM; - priv_dev->ep_buf_size = priv_dev->ep_iso_burst = - (priv_dev->onchip_buffers - 2) / (n_in + 1); + priv_dev->ep_buf_size = (priv_dev->onchip_buffers - 2 - iso) / (n_in + out); return 0; } diff --git a/drivers/usb/cdns3/cdns3-gadget.h b/drivers/usb/cdns3/cdns3-gadget.h index fbe4a8e3aa89..086a7bb83897 100644 --- a/drivers/usb/cdns3/cdns3-gadget.h +++ b/drivers/usb/cdns3/cdns3-gadget.h @@ -1168,6 +1168,9 @@ struct cdns3_endpoint { u8 dir; u8 num; u8 type; + u8 mult; + u8 bMaxBurst; + u16 wMaxPacketSize; int interval; int free_trbs; diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 2c1aca84f226..3ef8e3c872a3 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -87,16 +87,20 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral"); if (cdns->dev_irq < 0) - return cdns->dev_irq; + return dev_err_probe(dev, cdns->dev_irq, + "Failed to get peripheral IRQ\n"); regs = devm_platform_ioremap_resource_byname(pdev, "dev"); if (IS_ERR(regs)) - return PTR_ERR(regs); + return dev_err_probe(dev, PTR_ERR(regs), + "Failed to get dev base\n"); + cdns->dev_regs = regs; cdns->otg_irq = platform_get_irq_byname(pdev, "otg"); if (cdns->otg_irq < 0) - return cdns->otg_irq; + return dev_err_probe(dev, cdns->otg_irq, + "Failed to get otg IRQ\n"); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg"); if (!res) { @@ -119,7 +123,8 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy"); if (IS_ERR(cdns->usb2_phy)) - return PTR_ERR(cdns->usb2_phy); + return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy), + "Failed to get cdn3,usb2-phy\n"); ret = phy_init(cdns->usb2_phy); if (ret) @@ -127,7 +132,8 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy"); if (IS_ERR(cdns->usb3_phy)) - return PTR_ERR(cdns->usb3_phy); + return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy), + "Failed to get cdn3,usb3-phy\n"); ret = phy_init(cdns->usb3_phy); if (ret) diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c index a7265b86e427..c04d196abd87 100644 --- a/drivers/usb/cdns3/cdns3-starfive.c +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller * * Copyright (C) 2023 StarFive Technology Co., Ltd. diff --git a/drivers/usb/cdns3/cdnsp-debug.h b/drivers/usb/cdns3/cdnsp-debug.h index ad617b7455b9..cd138acdcce1 100644 --- a/drivers/usb/cdns3/cdnsp-debug.h +++ b/drivers/usb/cdns3/cdnsp-debug.h @@ -187,202 +187,202 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0, switch (type) { case TRB_LINK: - ret = snprintf(str, size, - "LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c", - field1, field0, GET_INTR_TARGET(field2), - cdnsp_trb_type_string(type), - field3 & TRB_IOC ? 'I' : 'i', - field3 & TRB_CHAIN ? 'C' : 'c', - field3 & TRB_TC ? 'T' : 't', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c", + field1, field0, GET_INTR_TARGET(field2), + cdnsp_trb_type_string(type), + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CHAIN ? 'C' : 'c', + field3 & TRB_TC ? 'T' : 't', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_TRANSFER: case TRB_COMPLETION: case TRB_PORT_STATUS: case TRB_HC_EVENT: - ret = snprintf(str, size, - "ep%d%s(%d) type '%s' TRB %08x%08x status '%s'" - " len %ld slot %ld flags %c:%c", - ep_num, ep_id % 2 ? "out" : "in", - TRB_TO_EP_INDEX(field3), - cdnsp_trb_type_string(type), field1, field0, - cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)), - EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3), - field3 & EVENT_DATA ? 'E' : 'e', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "ep%d%s(%d) type '%s' TRB %08x%08x status '%s'" + " len %ld slot %ld flags %c:%c", + ep_num, ep_id % 2 ? "out" : "in", + TRB_TO_EP_INDEX(field3), + cdnsp_trb_type_string(type), field1, field0, + cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)), + EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3), + field3 & EVENT_DATA ? 'E' : 'e', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_MFINDEX_WRAP: - ret = snprintf(str, size, "%s: flags %c", - cdnsp_trb_type_string(type), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, "%s: flags %c", + cdnsp_trb_type_string(type), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_SETUP: - ret = snprintf(str, size, - "type '%s' bRequestType %02x bRequest %02x " - "wValue %02x%02x wIndex %02x%02x wLength %d " - "length %ld TD size %ld intr %ld Setup ID %ld " - "flags %c:%c:%c", - cdnsp_trb_type_string(type), - field0 & 0xff, - (field0 & 0xff00) >> 8, - (field0 & 0xff000000) >> 24, - (field0 & 0xff0000) >> 16, - (field1 & 0xff00) >> 8, - field1 & 0xff, - (field1 & 0xff000000) >> 16 | - (field1 & 0xff0000) >> 16, - TRB_LEN(field2), GET_TD_SIZE(field2), - GET_INTR_TARGET(field2), - TRB_SETUPID_TO_TYPE(field3), - field3 & TRB_IDT ? 'D' : 'd', - field3 & TRB_IOC ? 'I' : 'i', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "type '%s' bRequestType %02x bRequest %02x " + "wValue %02x%02x wIndex %02x%02x wLength %d " + "length %ld TD size %ld intr %ld Setup ID %ld " + "flags %c:%c:%c", + cdnsp_trb_type_string(type), + field0 & 0xff, + (field0 & 0xff00) >> 8, + (field0 & 0xff000000) >> 24, + (field0 & 0xff0000) >> 16, + (field1 & 0xff00) >> 8, + field1 & 0xff, + (field1 & 0xff000000) >> 16 | + (field1 & 0xff0000) >> 16, + TRB_LEN(field2), GET_TD_SIZE(field2), + GET_INTR_TARGET(field2), + TRB_SETUPID_TO_TYPE(field3), + field3 & TRB_IDT ? 'D' : 'd', + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_DATA: - ret = snprintf(str, size, - "type '%s' Buffer %08x%08x length %ld TD size %ld " - "intr %ld flags %c:%c:%c:%c:%c:%c:%c", - cdnsp_trb_type_string(type), - field1, field0, TRB_LEN(field2), - GET_TD_SIZE(field2), - GET_INTR_TARGET(field2), - field3 & TRB_IDT ? 'D' : 'i', - field3 & TRB_IOC ? 'I' : 'i', - field3 & TRB_CHAIN ? 'C' : 'c', - field3 & TRB_NO_SNOOP ? 'S' : 's', - field3 & TRB_ISP ? 'I' : 'i', - field3 & TRB_ENT ? 'E' : 'e', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "type '%s' Buffer %08x%08x length %ld TD size %ld " + "intr %ld flags %c:%c:%c:%c:%c:%c:%c", + cdnsp_trb_type_string(type), + field1, field0, TRB_LEN(field2), + GET_TD_SIZE(field2), + GET_INTR_TARGET(field2), + field3 & TRB_IDT ? 'D' : 'i', + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CHAIN ? 'C' : 'c', + field3 & TRB_NO_SNOOP ? 'S' : 's', + field3 & TRB_ISP ? 'I' : 'i', + field3 & TRB_ENT ? 'E' : 'e', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_STATUS: - ret = snprintf(str, size, - "Buffer %08x%08x length %ld TD size %ld intr" - "%ld type '%s' flags %c:%c:%c:%c", - field1, field0, TRB_LEN(field2), - GET_TD_SIZE(field2), - GET_INTR_TARGET(field2), - cdnsp_trb_type_string(type), - field3 & TRB_IOC ? 'I' : 'i', - field3 & TRB_CHAIN ? 'C' : 'c', - field3 & TRB_ENT ? 'E' : 'e', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "Buffer %08x%08x length %ld TD size %ld intr" + "%ld type '%s' flags %c:%c:%c:%c", + field1, field0, TRB_LEN(field2), + GET_TD_SIZE(field2), + GET_INTR_TARGET(field2), + cdnsp_trb_type_string(type), + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CHAIN ? 'C' : 'c', + field3 & TRB_ENT ? 'E' : 'e', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_NORMAL: case TRB_ISOC: case TRB_EVENT_DATA: case TRB_TR_NOOP: - ret = snprintf(str, size, - "type '%s' Buffer %08x%08x length %ld " - "TD size %ld intr %ld " - "flags %c:%c:%c:%c:%c:%c:%c:%c:%c", - cdnsp_trb_type_string(type), - field1, field0, TRB_LEN(field2), - GET_TD_SIZE(field2), - GET_INTR_TARGET(field2), - field3 & TRB_BEI ? 'B' : 'b', - field3 & TRB_IDT ? 'T' : 't', - field3 & TRB_IOC ? 'I' : 'i', - field3 & TRB_CHAIN ? 'C' : 'c', - field3 & TRB_NO_SNOOP ? 'S' : 's', - field3 & TRB_ISP ? 'I' : 'i', - field3 & TRB_ENT ? 'E' : 'e', - field3 & TRB_CYCLE ? 'C' : 'c', - !(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v'); + ret = scnprintf(str, size, + "type '%s' Buffer %08x%08x length %ld " + "TD size %ld intr %ld " + "flags %c:%c:%c:%c:%c:%c:%c:%c:%c", + cdnsp_trb_type_string(type), + field1, field0, TRB_LEN(field2), + GET_TD_SIZE(field2), + GET_INTR_TARGET(field2), + field3 & TRB_BEI ? 'B' : 'b', + field3 & TRB_IDT ? 'T' : 't', + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CHAIN ? 'C' : 'c', + field3 & TRB_NO_SNOOP ? 'S' : 's', + field3 & TRB_ISP ? 'I' : 'i', + field3 & TRB_ENT ? 'E' : 'e', + field3 & TRB_CYCLE ? 'C' : 'c', + !(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v'); break; case TRB_CMD_NOOP: case TRB_ENABLE_SLOT: - ret = snprintf(str, size, "%s: flags %c", - cdnsp_trb_type_string(type), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, "%s: flags %c", + cdnsp_trb_type_string(type), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_DISABLE_SLOT: - ret = snprintf(str, size, "%s: slot %ld flags %c", - cdnsp_trb_type_string(type), - TRB_TO_SLOT_ID(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, "%s: slot %ld flags %c", + cdnsp_trb_type_string(type), + TRB_TO_SLOT_ID(field3), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_ADDR_DEV: - ret = snprintf(str, size, - "%s: ctx %08x%08x slot %ld flags %c:%c", - cdnsp_trb_type_string(type), field1, field0, - TRB_TO_SLOT_ID(field3), - field3 & TRB_BSR ? 'B' : 'b', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ctx %08x%08x slot %ld flags %c:%c", + cdnsp_trb_type_string(type), field1, field0, + TRB_TO_SLOT_ID(field3), + field3 & TRB_BSR ? 'B' : 'b', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_CONFIG_EP: - ret = snprintf(str, size, - "%s: ctx %08x%08x slot %ld flags %c:%c", - cdnsp_trb_type_string(type), field1, field0, - TRB_TO_SLOT_ID(field3), - field3 & TRB_DC ? 'D' : 'd', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ctx %08x%08x slot %ld flags %c:%c", + cdnsp_trb_type_string(type), field1, field0, + TRB_TO_SLOT_ID(field3), + field3 & TRB_DC ? 'D' : 'd', + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_EVAL_CONTEXT: - ret = snprintf(str, size, - "%s: ctx %08x%08x slot %ld flags %c", - cdnsp_trb_type_string(type), field1, field0, - TRB_TO_SLOT_ID(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ctx %08x%08x slot %ld flags %c", + cdnsp_trb_type_string(type), field1, field0, + TRB_TO_SLOT_ID(field3), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_RESET_EP: case TRB_HALT_ENDPOINT: - ret = snprintf(str, size, - "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c", - cdnsp_trb_type_string(type), - ep_num, ep_id % 2 ? "out" : "in", - TRB_TO_EP_INDEX(field3), field1, field0, - TRB_TO_SLOT_ID(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c", + cdnsp_trb_type_string(type), + ep_num, ep_id % 2 ? "out" : "in", + TRB_TO_EP_INDEX(field3), field1, field0, + TRB_TO_SLOT_ID(field3), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_STOP_RING: - ret = snprintf(str, size, - "%s: ep%d%s(%d) slot %ld sp %d flags %c", - cdnsp_trb_type_string(type), - ep_num, ep_id % 2 ? "out" : "in", - TRB_TO_EP_INDEX(field3), - TRB_TO_SLOT_ID(field3), - TRB_TO_SUSPEND_PORT(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ep%d%s(%d) slot %ld sp %d flags %c", + cdnsp_trb_type_string(type), + ep_num, ep_id % 2 ? "out" : "in", + TRB_TO_EP_INDEX(field3), + TRB_TO_SLOT_ID(field3), + TRB_TO_SUSPEND_PORT(field3), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_SET_DEQ: - ret = snprintf(str, size, - "%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c", - cdnsp_trb_type_string(type), - ep_num, ep_id % 2 ? "out" : "in", - TRB_TO_EP_INDEX(field3), field1, field0, - TRB_TO_STREAM_ID(field2), - TRB_TO_SLOT_ID(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c", + cdnsp_trb_type_string(type), + ep_num, ep_id % 2 ? "out" : "in", + TRB_TO_EP_INDEX(field3), field1, field0, + TRB_TO_STREAM_ID(field2), + TRB_TO_SLOT_ID(field3), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_RESET_DEV: - ret = snprintf(str, size, "%s: slot %ld flags %c", - cdnsp_trb_type_string(type), - TRB_TO_SLOT_ID(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, "%s: slot %ld flags %c", + cdnsp_trb_type_string(type), + TRB_TO_SLOT_ID(field3), + field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_ENDPOINT_NRDY: temp = TRB_TO_HOST_STREAM(field2); - ret = snprintf(str, size, - "%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c", - cdnsp_trb_type_string(type), - ep_num, ep_id % 2 ? "out" : "in", - TRB_TO_EP_INDEX(field3), temp, - temp == STREAM_PRIME_ACK ? "(PRIME)" : "", - temp == STREAM_REJECTED ? "(REJECTED)" : "", - TRB_TO_DEV_STREAM(field0), - field3 & TRB_STAT ? 'S' : 's', - field3 & TRB_CYCLE ? 'C' : 'c'); + ret = scnprintf(str, size, + "%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c", + cdnsp_trb_type_string(type), + ep_num, ep_id % 2 ? "out" : "in", + TRB_TO_EP_INDEX(field3), temp, + temp == STREAM_PRIME_ACK ? "(PRIME)" : "", + temp == STREAM_REJECTED ? "(REJECTED)" : "", + TRB_TO_DEV_STREAM(field0), + field3 & TRB_STAT ? 'S' : 's', + field3 & TRB_CYCLE ? 'C' : 'c'); break; default: - ret = snprintf(str, size, - "type '%s' -> raw %08x %08x %08x %08x", - cdnsp_trb_type_string(type), - field0, field1, field2, field3); + ret = scnprintf(str, size, + "type '%s' -> raw %08x %08x %08x %08x", + cdnsp_trb_type_string(type), + field0, field1, field2, field3); } - if (ret >= size) - pr_info("CDNSP: buffer overflowed.\n"); + if (ret == size - 1) + pr_info("CDNSP: buffer may be truncated.\n"); return str; } @@ -465,32 +465,32 @@ static inline const char *cdnsp_decode_portsc(char *str, size_t size, { int ret; - ret = snprintf(str, size, "%s %s %s Link:%s PortSpeed:%d ", - portsc & PORT_POWER ? "Powered" : "Powered-off", - portsc & PORT_CONNECT ? "Connected" : "Not-connected", - portsc & PORT_PED ? "Enabled" : "Disabled", - cdnsp_portsc_link_state_string(portsc), - DEV_PORT_SPEED(portsc)); + ret = scnprintf(str, size, "%s %s %s Link:%s PortSpeed:%d ", + portsc & PORT_POWER ? "Powered" : "Powered-off", + portsc & PORT_CONNECT ? "Connected" : "Not-connected", + portsc & PORT_PED ? "Enabled" : "Disabled", + cdnsp_portsc_link_state_string(portsc), + DEV_PORT_SPEED(portsc)); if (portsc & PORT_RESET) - ret += snprintf(str + ret, size - ret, "In-Reset "); + ret += scnprintf(str + ret, size - ret, "In-Reset "); - ret += snprintf(str + ret, size - ret, "Change: "); + ret += scnprintf(str + ret, size - ret, "Change: "); if (portsc & PORT_CSC) - ret += snprintf(str + ret, size - ret, "CSC "); + ret += scnprintf(str + ret, size - ret, "CSC "); if (portsc & PORT_WRC) - ret += snprintf(str + ret, size - ret, "WRC "); + ret += scnprintf(str + ret, size - ret, "WRC "); if (portsc & PORT_RC) - ret += snprintf(str + ret, size - ret, "PRC "); + ret += scnprintf(str + ret, size - ret, "PRC "); if (portsc & PORT_PLC) - ret += snprintf(str + ret, size - ret, "PLC "); + ret += scnprintf(str + ret, size - ret, "PLC "); if (portsc & PORT_CEC) - ret += snprintf(str + ret, size - ret, "CEC "); - ret += snprintf(str + ret, size - ret, "Wake: "); + ret += scnprintf(str + ret, size - ret, "CEC "); + ret += scnprintf(str + ret, size - ret, "Wake: "); if (portsc & PORT_WKCONN_E) - ret += snprintf(str + ret, size - ret, "WCE "); + ret += scnprintf(str + ret, size - ret, "WCE "); if (portsc & PORT_WKDISC_E) - ret += snprintf(str + ret, size - ret, "WDE "); + ret += scnprintf(str + ret, size - ret, "WDE "); return str; } @@ -562,20 +562,20 @@ static inline const char *cdnsp_decode_ep_context(char *str, size_t size, avg = EP_AVG_TRB_LENGTH(tx_info); - ret = snprintf(str, size, "State %s mult %d max P. Streams %d %s", - cdnsp_ep_state_string(ep_state), mult, - max_pstr, lsa ? "LSA " : ""); + ret = scnprintf(str, size, "State %s mult %d max P. Streams %d %s", + cdnsp_ep_state_string(ep_state), mult, + max_pstr, lsa ? "LSA " : ""); - ret += snprintf(str + ret, size - ret, - "interval %d us max ESIT payload %d CErr %d ", - (1 << interval) * 125, esit, cerr); + ret += scnprintf(str + ret, size - ret, + "interval %d us max ESIT payload %d CErr %d ", + (1 << interval) * 125, esit, cerr); - ret += snprintf(str + ret, size - ret, - "Type %s %sburst %d maxp %d deq %016llx ", - cdnsp_ep_type_string(ep_type), hid ? "HID" : "", - burst, maxp, deq); + ret += scnprintf(str + ret, size - ret, + "Type %s %sburst %d maxp %d deq %016llx ", + cdnsp_ep_type_string(ep_type), hid ? "HID" : "", + burst, maxp, deq); - ret += snprintf(str + ret, size - ret, "avg trb len %d", avg); + ret += scnprintf(str + ret, size - ret, "avg trb len %d", avg); return str; } diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index d9bb3d3f026e..2a38e1eb6546 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -176,6 +176,7 @@ struct hw_bank { * @enabled_otg_timer_bits: bits of enabled otg timers * @next_otg_timer: next nearest enabled timer to be expired * @work: work for role changing + * @power_lost_work: work for power lost handling * @wq: workqueue thread * @qh_pool: allocation pool for queue heads * @td_pool: allocation pool for transfer descriptors @@ -226,6 +227,7 @@ struct ci_hdrc { enum otg_fsm_timer next_otg_timer; struct usb_role_switch *role_switch; struct work_struct work; + struct work_struct power_lost_work; struct workqueue_struct *wq; struct dma_pool *qh_pool; diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index e28bb2f2612d..ae9a6a17ec6e 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -96,6 +96,7 @@ struct ci_hdrc_imx_data { struct usb_phy *phy; struct platform_device *ci_pdev; struct clk *clk; + struct clk *clk_wakeup; struct imx_usbmisc_data *usbmisc_data; bool supports_runtime_pm; bool override_phy_control; @@ -199,7 +200,7 @@ static int imx_get_clks(struct device *dev) data->clk_ipg = devm_clk_get(dev, "ipg"); if (IS_ERR(data->clk_ipg)) { - /* If the platform only needs one clocks */ + /* If the platform only needs one primary clock */ data->clk = devm_clk_get(dev, NULL); if (IS_ERR(data->clk)) { ret = PTR_ERR(data->clk); @@ -208,6 +209,13 @@ static int imx_get_clks(struct device *dev) PTR_ERR(data->clk), PTR_ERR(data->clk_ipg)); return ret; } + /* Get wakeup clock. Not all of the platforms need to + * handle this clock. So make it optional. + */ + data->clk_wakeup = devm_clk_get_optional(dev, "usb_wakeup_clk"); + if (IS_ERR(data->clk_wakeup)) + ret = dev_err_probe(dev, PTR_ERR(data->clk_wakeup), + "Failed to get wakeup clk\n"); return ret; } @@ -423,6 +431,10 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) if (ret) goto disable_hsic_regulator; + ret = clk_prepare_enable(data->clk_wakeup); + if (ret) + goto err_wakeup_clk; + data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0); if (IS_ERR(data->phy)) { ret = PTR_ERR(data->phy); @@ -504,6 +516,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) disable_device: ci_hdrc_remove_device(data->ci_pdev); err_clk: + clk_disable_unprepare(data->clk_wakeup); +err_wakeup_clk: imx_disable_unprepare_clks(dev); disable_hsic_regulator: if (data->hsic_pad_regulator) @@ -530,6 +544,7 @@ static void ci_hdrc_imx_remove(struct platform_device *pdev) usb_phy_shutdown(data->phy); if (data->ci_pdev) { imx_disable_unprepare_clks(&pdev->dev); + clk_disable_unprepare(data->clk_wakeup); if (data->plat_data->flags & CI_HDRC_PMQOS) cpu_latency_qos_remove_request(&data->pm_qos_req); if (data->hsic_pad_regulator) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 7ac39a281b8c..835bf2428dc6 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -523,6 +523,13 @@ static irqreturn_t ci_irq_handler(int irq, void *data) u32 otgsc = 0; if (ci->in_lpm) { + /* + * If we already have a wakeup irq pending there, + * let's just return to wait resume finished firstly. + */ + if (ci->wakeup_int) + return IRQ_HANDLED; + disable_irq_nosync(irq); ci->wakeup_int = true; pm_runtime_get(ci->dev); @@ -849,6 +856,27 @@ static int ci_extcon_register(struct ci_hdrc *ci) return 0; } +static void ci_power_lost_work(struct work_struct *work) +{ + struct ci_hdrc *ci = container_of(work, struct ci_hdrc, power_lost_work); + enum ci_role role; + + disable_irq_nosync(ci->irq); + pm_runtime_get_sync(ci->dev); + if (!ci_otg_is_fsm_mode(ci)) { + role = ci_get_role(ci); + + if (ci->role != role) { + ci_handle_id_switch(ci); + } else if (role == CI_ROLE_GADGET) { + if (ci->is_otg && hw_read_otgsc(ci, OTGSC_BSV)) + usb_gadget_vbus_connect(&ci->gadget); + } + } + pm_runtime_put_sync(ci->dev); + enable_irq(ci->irq); +} + static DEFINE_IDA(ci_ida); struct platform_device *ci_hdrc_add_device(struct device *dev, @@ -862,7 +890,7 @@ struct platform_device *ci_hdrc_add_device(struct device *dev, if (ret) return ERR_PTR(ret); - id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&ci_ida, GFP_KERNEL); if (id < 0) return ERR_PTR(id); @@ -892,7 +920,7 @@ struct platform_device *ci_hdrc_add_device(struct device *dev, err: platform_device_put(pdev); put_id: - ida_simple_remove(&ci_ida, id); + ida_free(&ci_ida, id); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(ci_hdrc_add_device); @@ -901,7 +929,7 @@ void ci_hdrc_remove_device(struct platform_device *pdev) { int id = pdev->id; platform_device_unregister(pdev); - ida_simple_remove(&ci_ida, id); + ida_free(&ci_ida, id); } EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); @@ -1038,6 +1066,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) spin_lock_init(&ci->lock); mutex_init(&ci->mutex); + INIT_WORK(&ci->power_lost_work, ci_power_lost_work); + ci->dev = dev; ci->platdata = dev_get_platdata(dev); ci->imx28_write_fix = !!(ci->platdata->flags & @@ -1389,25 +1419,6 @@ static int ci_suspend(struct device *dev) return 0; } -static void ci_handle_power_lost(struct ci_hdrc *ci) -{ - enum ci_role role; - - disable_irq_nosync(ci->irq); - if (!ci_otg_is_fsm_mode(ci)) { - role = ci_get_role(ci); - - if (ci->role != role) { - ci_handle_id_switch(ci); - } else if (role == CI_ROLE_GADGET) { - if (ci->is_otg && hw_read_otgsc(ci, OTGSC_BSV)) - usb_gadget_vbus_connect(&ci->gadget); - } - } - - enable_irq(ci->irq); -} - static int ci_resume(struct device *dev) { struct ci_hdrc *ci = dev_get_drvdata(dev); @@ -1439,7 +1450,7 @@ static int ci_resume(struct device *dev) ci_role(ci)->resume(ci, power_lost); if (power_lost) - ci_handle_power_lost(ci); + queue_work(system_freezable_wq, &ci->power_lost_work); if (ci->supports_runtime_pm) { pm_runtime_disable(dev); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 0b7bd3c643c3..2d7f616270c1 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -688,7 +688,8 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) if ((TD_STATUS_ACTIVE & tmptoken) != 0) { int n = hw_ep_bit(hwep->num, hwep->dir); - if (ci->rev == CI_REVISION_24) + if (ci->rev == CI_REVISION_24 || + ci->rev == CI_REVISION_22) if (!hw_read(ci, OP_ENDPTSTAT, BIT(n))) reprime_dtd(ci, hwep, node); hwreq->req.status = -EALREADY; diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index a1f4e1ead97f..0e7439dba8fe 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -916,6 +916,9 @@ static int acm_tty_break_ctl(struct tty_struct *tty, int state) struct acm *acm = tty->driver_data; int retval; + if (!(acm->ctrl_caps & USB_CDC_CAP_BRK)) + return -EOPNOTSUPP; + retval = acm_send_break(acm, state ? 0xffff : 0); if (retval < 0) dev_dbg(&acm->control->dev, diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index 84d91b1c1eed..0886b19d2e1c 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -301,7 +301,7 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) return ret; } - root = debugfs_create_dir(dev_name(dev), ulpi_root); + root = debugfs_create_dir(dev_name(&ulpi->dev), ulpi_root); debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops); dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n", diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index f58a0299fb3b..e01b1913d02b 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -189,13 +189,13 @@ static int usb_create_newid_files(struct usb_driver *usb_drv) goto exit; if (usb_drv->probe != NULL) { - error = driver_create_file(&usb_drv->drvwrap.driver, + error = driver_create_file(&usb_drv->driver, &driver_attr_new_id); if (error == 0) { - error = driver_create_file(&usb_drv->drvwrap.driver, + error = driver_create_file(&usb_drv->driver, &driver_attr_remove_id); if (error) - driver_remove_file(&usb_drv->drvwrap.driver, + driver_remove_file(&usb_drv->driver, &driver_attr_new_id); } } @@ -209,9 +209,9 @@ static void usb_remove_newid_files(struct usb_driver *usb_drv) return; if (usb_drv->probe != NULL) { - driver_remove_file(&usb_drv->drvwrap.driver, + driver_remove_file(&usb_drv->driver, &driver_attr_remove_id); - driver_remove_file(&usb_drv->drvwrap.driver, + driver_remove_file(&usb_drv->driver, &driver_attr_new_id); } } @@ -290,7 +290,10 @@ static int usb_probe_device(struct device *dev) * specialised device drivers prior to setting the * use_generic_driver bit. */ - error = udriver->probe(udev); + if (udriver->probe) + error = udriver->probe(udev); + else if (!udriver->generic_subclass) + error = -EINVAL; if (error == -ENODEV && udriver != &usb_generic_driver && (udriver->id_table || udriver->match)) { udev->use_generic_driver = 1; @@ -549,7 +552,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, if (!iface->authorized) return -ENODEV; - dev->driver = &driver->drvwrap.driver; + dev->driver = &driver->driver; usb_set_intfdata(iface, data); iface->needs_binding = 0; @@ -612,7 +615,7 @@ void usb_driver_release_interface(struct usb_driver *driver, struct device *dev = &iface->dev; /* this should never happen, don't release something that's not ours */ - if (!dev->driver || dev->driver != &driver->drvwrap.driver) + if (!dev->driver || dev->driver != &driver->driver) return; /* don't release from within disconnect() */ @@ -947,7 +950,7 @@ static int __usb_bus_reprobe_drivers(struct device *dev, void *data) int ret; /* Don't reprobe if current driver isn't usb_generic_driver */ - if (dev->driver != &usb_generic_driver.drvwrap.driver) + if (dev->driver != &usb_generic_driver.driver) return 0; udev = to_usb_device(dev); @@ -961,6 +964,11 @@ static int __usb_bus_reprobe_drivers(struct device *dev, void *data) return 0; } +bool is_usb_device_driver(const struct device_driver *drv) +{ + return drv->probe == usb_probe_device; +} + /** * usb_register_device_driver - register a USB device (not interface) driver * @new_udriver: USB operations for the device driver @@ -980,15 +988,14 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver, if (usb_disabled()) return -ENODEV; - new_udriver->drvwrap.for_devices = 1; - new_udriver->drvwrap.driver.name = new_udriver->name; - new_udriver->drvwrap.driver.bus = &usb_bus_type; - new_udriver->drvwrap.driver.probe = usb_probe_device; - new_udriver->drvwrap.driver.remove = usb_unbind_device; - new_udriver->drvwrap.driver.owner = owner; - new_udriver->drvwrap.driver.dev_groups = new_udriver->dev_groups; + new_udriver->driver.name = new_udriver->name; + new_udriver->driver.bus = &usb_bus_type; + new_udriver->driver.probe = usb_probe_device; + new_udriver->driver.remove = usb_unbind_device; + new_udriver->driver.owner = owner; + new_udriver->driver.dev_groups = new_udriver->dev_groups; - retval = driver_register(&new_udriver->drvwrap.driver); + retval = driver_register(&new_udriver->driver); if (!retval) { pr_info("%s: registered new device driver %s\n", @@ -1020,7 +1027,7 @@ void usb_deregister_device_driver(struct usb_device_driver *udriver) pr_info("%s: deregistering device driver %s\n", usbcore_name, udriver->name); - driver_unregister(&udriver->drvwrap.driver); + driver_unregister(&udriver->driver); } EXPORT_SYMBOL_GPL(usb_deregister_device_driver); @@ -1048,18 +1055,17 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, if (usb_disabled()) return -ENODEV; - new_driver->drvwrap.for_devices = 0; - new_driver->drvwrap.driver.name = new_driver->name; - new_driver->drvwrap.driver.bus = &usb_bus_type; - new_driver->drvwrap.driver.probe = usb_probe_interface; - new_driver->drvwrap.driver.remove = usb_unbind_interface; - new_driver->drvwrap.driver.owner = owner; - new_driver->drvwrap.driver.mod_name = mod_name; - new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups; + new_driver->driver.name = new_driver->name; + new_driver->driver.bus = &usb_bus_type; + new_driver->driver.probe = usb_probe_interface; + new_driver->driver.remove = usb_unbind_interface; + new_driver->driver.owner = owner; + new_driver->driver.mod_name = mod_name; + new_driver->driver.dev_groups = new_driver->dev_groups; spin_lock_init(&new_driver->dynids.lock); INIT_LIST_HEAD(&new_driver->dynids.list); - retval = driver_register(&new_driver->drvwrap.driver); + retval = driver_register(&new_driver->driver); if (retval) goto out; @@ -1074,7 +1080,7 @@ out: return retval; out_newid: - driver_unregister(&new_driver->drvwrap.driver); + driver_unregister(&new_driver->driver); pr_err("%s: error %d registering interface driver %s\n", usbcore_name, retval, new_driver->name); @@ -1099,7 +1105,7 @@ void usb_deregister(struct usb_driver *driver) usbcore_name, driver->name); usb_remove_newid_files(driver); - driver_unregister(&driver->drvwrap.driver); + driver_unregister(&driver->driver); usb_free_dynids(driver); } EXPORT_SYMBOL_GPL(usb_deregister); diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 740342a2812a..b134bff5c3fe 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -59,10 +59,26 @@ int usb_choose_configuration(struct usb_device *udev) int num_configs; int insufficient_power = 0; struct usb_host_config *c, *best; + struct usb_device_driver *udriver; + + /* + * If a USB device (not an interface) doesn't have a driver then the + * kernel has no business trying to select or install a configuration + * for it. + */ + if (!udev->dev.driver) + return -1; + udriver = to_usb_device_driver(udev->dev.driver); if (usb_device_is_owned(udev)) return 0; + if (udriver->choose_configuration) { + i = udriver->choose_configuration(udev); + if (i >= 0) + return i; + } + best = NULL; c = udev->config; num_configs = udev->descriptor.bNumConfigurations; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 87480a6e6d93..e38a4124f610 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -47,12 +47,24 @@ #define USB_VENDOR_TEXAS_INSTRUMENTS 0x0451 #define USB_PRODUCT_TUSB8041_USB3 0x8140 #define USB_PRODUCT_TUSB8041_USB2 0x8142 -#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 -#define HUB_QUIRK_DISABLE_AUTOSUSPEND 0x02 +#define USB_VENDOR_MICROCHIP 0x0424 +#define USB_PRODUCT_USB4913 0x4913 +#define USB_PRODUCT_USB4914 0x4914 +#define USB_PRODUCT_USB4915 0x4915 +#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND BIT(0) +#define HUB_QUIRK_DISABLE_AUTOSUSPEND BIT(1) +#define HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL BIT(2) #define USB_TP_TRANSMISSION_DELAY 40 /* ns */ #define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */ #define USB_PING_RESPONSE_TIME 400 /* ns */ +#define USB_REDUCE_FRAME_INTR_BINTERVAL 9 + +/* + * The SET_ADDRESS request timeout will be 500 ms when + * USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT quirk flag is set. + */ +#define USB_SHORT_SET_ADDRESS_REQ_TIMEOUT 500 /* ms */ /* Protect struct usb_device->state and ->children members * Note: Both are also protected by ->dev.sem, except that ->state can @@ -1904,6 +1916,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_autopm_get_interface_no_resume(intf); } + if ((id->driver_info & HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL) && + desc->endpoint[0].desc.bInterval > USB_REDUCE_FRAME_INTR_BINTERVAL) { + desc->endpoint[0].desc.bInterval = + USB_REDUCE_FRAME_INTR_BINTERVAL; + /* Tell the HCD about the interrupt ep's new bInterval */ + usb_set_interface(hdev, 0, 0); + } + if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) { onboard_hub_create_pdevs(hdev, &hub->onboard_hub_devs); @@ -2033,9 +2053,19 @@ static void update_port_device_state(struct usb_device *udev) if (udev->parent) { hub = usb_hub_to_struct_hub(udev->parent); - port_dev = hub->ports[udev->portnum - 1]; - WRITE_ONCE(port_dev->state, udev->state); - sysfs_notify_dirent(port_dev->state_kn); + + /* + * The Link Layer Validation System Driver (lvstest) + * has a test step to unbind the hub before running the + * rest of the procedure. This triggers hub_disconnect + * which will set the hub's maxchild to 0, further + * resulting in usb_hub_to_struct_hub returning NULL. + */ + if (hub) { + port_dev = hub->ports[udev->portnum - 1]; + WRITE_ONCE(port_dev->state, udev->state); + sysfs_notify_dirent(port_dev->state_kn); + } } } @@ -2368,17 +2398,25 @@ static int usb_enumerate_device_otg(struct usb_device *udev) } } else if (desc->bLength == sizeof (struct usb_otg_descriptor)) { - /* Set a_alt_hnp_support for legacy otg device */ - err = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - USB_REQ_SET_FEATURE, 0, - USB_DEVICE_A_ALT_HNP_SUPPORT, - 0, NULL, 0, - USB_CTRL_SET_TIMEOUT); - if (err < 0) - dev_err(&udev->dev, - "set a_alt_hnp_support failed: %d\n", - err); + /* + * We are operating on a legacy OTP device + * These should be told that they are operating + * on the wrong port if we have another port that does + * support HNP + */ + if (bus->otg_port != 0) { + /* Set a_alt_hnp_support for legacy otg device */ + err = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, 0, + USB_DEVICE_A_ALT_HNP_SUPPORT, + 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (err < 0) + dev_err(&udev->dev, + "set a_alt_hnp_support failed: %d\n", + err); + } } } #endif @@ -4626,7 +4664,12 @@ EXPORT_SYMBOL_GPL(usb_ep0_reinit); static int hub_set_address(struct usb_device *udev, int devnum) { int retval; + unsigned int timeout_ms = USB_CTRL_SET_TIMEOUT; struct usb_hcd *hcd = bus_to_hcd(udev->bus); + struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); + + if (hub->hdev->quirks & USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT) + timeout_ms = USB_SHORT_SET_ADDRESS_REQ_TIMEOUT; /* * The host controller will choose the device address, @@ -4639,11 +4682,11 @@ static int hub_set_address(struct usb_device *udev, int devnum) if (udev->state != USB_STATE_DEFAULT) return -EINVAL; if (hcd->driver->address_device) - retval = hcd->driver->address_device(hcd, udev); + retval = hcd->driver->address_device(hcd, udev, timeout_ms); else retval = usb_control_msg(udev, usb_sndaddr0pipe(), USB_REQ_SET_ADDRESS, 0, devnum, 0, - NULL, 0, USB_CTRL_SET_TIMEOUT); + NULL, 0, timeout_ms); if (retval == 0) { update_devnum(udev, devnum); /* Device now using proper address. */ @@ -5895,6 +5938,21 @@ static const struct usb_device_id hub_id_table[] = { .idVendor = USB_VENDOR_TEXAS_INSTRUMENTS, .idProduct = USB_PRODUCT_TUSB8041_USB3, .driver_info = HUB_QUIRK_DISABLE_AUTOSUSPEND}, + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_PRODUCT, + .idVendor = USB_VENDOR_MICROCHIP, + .idProduct = USB_PRODUCT_USB4913, + .driver_info = HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL}, + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_PRODUCT, + .idVendor = USB_VENDOR_MICROCHIP, + .idProduct = USB_PRODUCT_USB4914, + .driver_info = HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL}, + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_PRODUCT, + .idVendor = USB_VENDOR_MICROCHIP, + .idProduct = USB_PRODUCT_USB4915, + .driver_info = HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL}, { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS, .bDeviceClass = USB_CLASS_HUB}, { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 15e9bd180a1d..b4783574b8e6 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -138,6 +138,9 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp) case 'o': flags |= USB_QUIRK_HUB_SLOW_RESET; break; + case 'p': + flags |= USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT; + break; /* Ignore unrecognized flag characters */ } } @@ -527,6 +530,10 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x2386, 0x350e), .driver_info = USB_QUIRK_NO_LPM }, + /* APTIV AUTOMOTIVE HUB */ + { USB_DEVICE(0x2c48, 0x0132), .driver_info = + USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT }, + /* DJI CineSSD */ { USB_DEVICE(0x2ca3, 0x0031), .driver_info = USB_QUIRK_NO_LPM }, diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 2a938cf47ccd..dc8d9228a5e7 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -431,7 +431,7 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) struct device *dev; argb.minor = minor; - argb.drv = &drv->drvwrap.driver; + argb.drv = &drv->driver; dev = bus_find_device(&usb_bus_type, NULL, &argb, __find_interface); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 60363153fc3f..bfecb50773b6 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -175,13 +175,7 @@ static inline int is_root_hub(struct usb_device *udev) return (udev->parent == NULL); } -/* Do the same for device drivers and interface drivers. */ - -static inline int is_usb_device_driver(struct device_driver *drv) -{ - return container_of(drv, struct usbdrv_wrap, driver)-> - for_devices; -} +extern bool is_usb_device_driver(const struct device_driver *drv); /* for labeling diagnostics */ extern const char *usbcore_name; diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index fb03162ae9b7..eb677c3cfd0b 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -130,6 +130,7 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg) p->lpm_clock_gating = false; p->besl = false; p->hird_threshold_en = false; + p->no_clock_gating = true; } static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index b101dbf8c5dc..3e55838c0001 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -277,48 +277,11 @@ int dwc3_core_soft_reset(struct dwc3 *dwc) /* * We're resetting only the device side because, if we're in host mode, * XHCI driver will reset the host block. If dwc3 was configured for - * host-only mode or current role is host, then we can return early. + * host-only mode, then we can return early. */ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) return 0; - /* - * If the dr_mode is host and the dwc->current_dr_role is not the - * corresponding DWC3_GCTL_PRTCAP_HOST, then the dwc3_core_init_mode - * isn't executed yet. Ensure the phy is ready before the controller - * updates the GCTL.PRTCAPDIR or other settings by soft-resetting - * the phy. - * - * Note: GUSB3PIPECTL[n] and GUSB2PHYCFG[n] are port settings where n - * is port index. If this is a multiport host, then we need to reset - * all active ports. - */ - if (dwc->dr_mode == USB_DR_MODE_HOST) { - u32 usb3_port; - u32 usb2_port; - - usb3_port = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); - usb3_port |= DWC3_GUSB3PIPECTL_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), usb3_port); - - usb2_port = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - usb2_port |= DWC3_GUSB2PHYCFG_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), usb2_port); - - /* Small delay for phy reset assertion */ - usleep_range(1000, 2000); - - usb3_port &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), usb3_port); - - usb2_port &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), usb2_port); - - /* Wait for clock synchronization */ - msleep(50); - return 0; - } - reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg |= DWC3_DCTL_CSFTRST; reg &= ~DWC3_DCTL_RUN_STOP; @@ -1367,6 +1330,18 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_config_threshold(dwc); + /* + * Modify this for all supported Super Speed ports when + * multiport support is added. + */ + if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET && + (DWC3_IP_IS(DWC31)) && + dwc->maximum_speed == USB_SPEED_SUPER) { + reg = dwc3_readl(dwc->regs, DWC3_LLUCTL); + reg |= DWC3_LLUCTL_FORCE_GEN1; + dwc3_writel(dwc->regs, DWC3_LLUCTL, reg); + } + return 0; err_power_off_phy: @@ -2340,12 +2315,15 @@ static int dwc3_resume(struct device *dev) pinctrl_pm_select_default_state(dev); + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + ret = dwc3_resume_common(dwc, PMSG_RESUME); - if (ret) + if (ret) { + pm_runtime_set_suspended(dev); return ret; + } - pm_runtime_disable(dev); - pm_runtime_set_active(dev); pm_runtime_enable(dev); return 0; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index efe6caf4d0e8..e3eea965e57b 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -172,6 +172,8 @@ #define DWC3_OEVTEN 0xcc0C #define DWC3_OSTS 0xcc10 +#define DWC3_LLUCTL 0xd024 + /* Bit fields */ /* Global SoC Bus Configuration INCRx Register 0 */ @@ -374,6 +376,7 @@ /* Global HWPARAMS4 Register */ #define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) #define DWC3_MAX_HIBER_SCRATCHBUFS 15 +#define DWC3_EXT_BUFF_CONTROL BIT(21) /* Global HWPARAMS6 Register */ #define DWC3_GHWPARAMS6_BCSUPPORT BIT(14) @@ -657,6 +660,9 @@ #define DWC3_OSTS_VBUSVLD BIT(1) #define DWC3_OSTS_CONIDSTS BIT(0) +/* Force Gen1 speed on Gen2 link */ +#define DWC3_LLUCTL_FORCE_GEN1 BIT(10) + /* Structures */ struct dwc3_trb; diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index a1e15f2fffdb..8ee448068503 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -363,8 +363,10 @@ static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev) } ret = clk_prepare_enable(dwc3_imx->hsio_clk); - if (ret) + if (ret) { + clk_disable_unprepare(dwc3_imx->suspend_clk); return ret; + } ret = dwc3_imx8mp_resume(dwc3_imx, PMSG_RESUME); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 6604845c397c..39564e17f3b0 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -51,6 +51,8 @@ #define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1 #define PCI_DEVICE_ID_INTEL_MTLS 0x7f6f #define PCI_DEVICE_ID_INTEL_MTL 0x7e7e +#define PCI_DEVICE_ID_INTEL_ARLH 0x7ec1 +#define PCI_DEVICE_ID_INTEL_ARLH_PCH 0x777e #define PCI_DEVICE_ID_INTEL_TGL 0x9a15 #define PCI_DEVICE_ID_AMD_MR 0x163a @@ -421,6 +423,8 @@ static const struct pci_device_id dwc3_pci_id_table[] = { { PCI_DEVICE_DATA(INTEL, MTLP, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, MTL, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, MTLS, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, ARLH, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, ARLH_PCH, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, TGL, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(AMD, NL_USB, &dwc3_pci_amd_swnode) }, diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index fdf6d5d3c2ad..dbd6a5b2b289 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -57,7 +57,7 @@ struct dwc3_acpi_pdata { u32 qscratch_base_offset; u32 qscratch_base_size; u32 dwc3_core_base_size; - int hs_phy_irq_index; + int qusb2_phy_irq_index; int dp_hs_phy_irq_index; int dm_hs_phy_irq_index; int ss_phy_irq_index; @@ -73,7 +73,7 @@ struct dwc3_qcom { int num_clocks; struct reset_control *resets; - int hs_phy_irq; + int qusb2_phy_irq; int dp_hs_phy_irq; int dm_hs_phy_irq; int ss_phy_irq; @@ -372,7 +372,7 @@ static void dwc3_qcom_disable_wakeup_irq(int irq) static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom) { - dwc3_qcom_disable_wakeup_irq(qcom->hs_phy_irq); + dwc3_qcom_disable_wakeup_irq(qcom->qusb2_phy_irq); if (qcom->usb2_speed == USB_SPEED_LOW) { dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq); @@ -389,7 +389,7 @@ static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom) static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom) { - dwc3_qcom_enable_wakeup_irq(qcom->hs_phy_irq, 0); + dwc3_qcom_enable_wakeup_irq(qcom->qusb2_phy_irq, 0); /* * Configure DP/DM line interrupts based on the USB2 device attached to @@ -542,19 +542,19 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev) int irq; int ret; - irq = dwc3_qcom_get_irq(pdev, "hs_phy_irq", - pdata ? pdata->hs_phy_irq_index : -1); + irq = dwc3_qcom_get_irq(pdev, "qusb2_phy", + pdata ? pdata->qusb2_phy_irq_index : -1); if (irq > 0) { /* Keep wakeup interrupts disabled until suspend */ ret = devm_request_threaded_irq(qcom->dev, irq, NULL, qcom_dwc3_resume_irq, IRQF_ONESHOT | IRQF_NO_AUTOEN, - "qcom_dwc3 HS", qcom); + "qcom_dwc3 QUSB2", qcom); if (ret) { - dev_err(qcom->dev, "hs_phy_irq failed: %d\n", ret); + dev_err(qcom->dev, "qusb2_phy_irq failed: %d\n", ret); return ret; } - qcom->hs_phy_irq = irq; + qcom->qusb2_phy_irq = irq; } irq = dwc3_qcom_get_irq(pdev, "dp_hs_phy_irq", @@ -1058,7 +1058,7 @@ static const struct dwc3_acpi_pdata sdm845_acpi_pdata = { .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, .qscratch_base_size = SDM845_QSCRATCH_SIZE, .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, - .hs_phy_irq_index = 1, + .qusb2_phy_irq_index = 1, .dp_hs_phy_irq_index = 4, .dm_hs_phy_irq_index = 3, .ss_phy_irq_index = 2 @@ -1068,7 +1068,7 @@ static const struct dwc3_acpi_pdata sdm845_acpi_urs_pdata = { .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET, .qscratch_base_size = SDM845_QSCRATCH_SIZE, .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE, - .hs_phy_irq_index = 1, + .qusb2_phy_irq_index = 1, .dp_hs_phy_irq_index = 4, .dm_hs_phy_irq_index = 3, .ss_phy_irq_index = 2, diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index 5b7e92f476de..6095f4dee6ce 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -293,11 +293,15 @@ static int dwc3_xlnx_probe(struct platform_device *pdev) goto err_clk_put; pm_runtime_set_active(dev); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret < 0) + goto err_pm_set_suspended; + pm_suspend_ignore_children(dev, false); - pm_runtime_get_sync(dev); + return pm_runtime_resume_and_get(dev); - return 0; +err_pm_set_suspended: + pm_runtime_set_suspended(dev); err_clk_put: clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks); @@ -315,7 +319,6 @@ static void dwc3_xlnx_remove(struct platform_device *pdev) clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks); priv_data->num_clocks = 0; - pm_runtime_disable(dev); pm_runtime_put_noidle(dev); pm_runtime_set_suspended(dev); } diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index b94243237293..6ae8a36f21cf 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -238,7 +238,10 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) struct dwc3_request *req; req = next_request(&dep->pending_list); - dwc3_gadget_giveback(dep, req, -ECONNRESET); + if (!dwc->connected) + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + else + dwc3_gadget_giveback(dep, req, -ECONNRESET); } dwc->eps[0]->trb_enqueue = 0; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 858fe4c299b7..564976b3e2b9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -673,6 +673,12 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(bInterval_m1); } + if (dep->endpoint.fifo_mode) { + if (!(dwc->hwparams.hwparams4 & DWC3_EXT_BUFF_CONTROL)) + return -EINVAL; + params.param1 |= DWC3_DEPCFG_EBC_HWO_NOWB | DWC3_DEPCFG_USE_EBC; + } + return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms); } @@ -2103,7 +2109,17 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, list_for_each_entry(r, &dep->pending_list, list) { if (r == req) { - dwc3_gadget_giveback(dep, req, -ECONNRESET); + /* + * Explicitly check for EP0/1 as dequeue for those + * EPs need to be handled differently. Control EP + * only deals with one USB req, and giveback will + * occur during dwc3_ep0_stall_and_restart(). EP0 + * requests are never added to started_list. + */ + if (dep->number > 1) + dwc3_gadget_giveback(dep, req, -ECONNRESET); + else + dwc3_ep0_reset_state(dwc); goto out; } } @@ -3973,6 +3989,13 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED); dwc3_ep0_reset_state(dwc); + + /* + * Request PM idle to address condition where usage count is + * already decremented to zero, but waiting for the disconnect + * interrupt to set dwc->connected to FALSE. + */ + pm_request_idle(dwc->dev); } static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) @@ -4686,15 +4709,13 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) unsigned long flags; int ret; - if (!dwc->gadget_driver) - return 0; - ret = dwc3_gadget_soft_disconnect(dwc); if (ret) goto err; spin_lock_irqsave(&dwc->lock, flags); - dwc3_disconnect_gadget(dwc); + if (dwc->gadget_driver) + dwc3_disconnect_gadget(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return 0; diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 55a56cf67d73..fd7a4e94397e 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -26,6 +26,8 @@ struct dwc3; #define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10) #define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11) #define DWC3_DEPCFG_STREAM_EVENT_EN BIT(13) +#define DWC3_DEPCFG_EBC_HWO_NOWB BIT(14) +#define DWC3_DEPCFG_USE_EBC BIT(15) #define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16) #define DWC3_DEPCFG_STREAM_CAPABLE BIT(24) #define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25) diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 61f57fe5bb78..43230915323c 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -61,7 +61,7 @@ out: int dwc3_host_init(struct dwc3 *dwc) { - struct property_entry props[4]; + struct property_entry props[5]; struct platform_device *xhci; int ret, irq; int prop_idx = 0; @@ -89,6 +89,8 @@ int dwc3_host_init(struct dwc3 *dwc) memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props)); + props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-sg-trb-cache-size-quirk"); + if (dwc->usb3_lpm_capable) props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable"); diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c index 7bf810a0c98a..8c5aaf860635 100644 --- a/drivers/usb/fotg210/fotg210-hcd.c +++ b/drivers/usb/fotg210/fotg210-hcd.c @@ -404,9 +404,9 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, else if (td->hw_alt_next != list_end) mark = '/'; } - temp = snprintf(next, size, - "\n\t%p%c%s len=%d %08x urb %p", - td, mark, ({ char *tmp; + temp = scnprintf(next, size, + "\n\t%p%c%s len=%d %08x urb %p", + td, mark, ({ char *tmp; switch ((scratch>>8)&0x03) { case 0: tmp = "out"; @@ -424,15 +424,11 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, (scratch >> 16) & 0x7fff, scratch, td->urb); - if (size < temp) - temp = size; size -= temp; next += temp; } - temp = snprintf(next, size, "\n"); - if (size < temp) - temp = size; + temp = scnprintf(next, size, "\n"); size -= temp; next += temp; diff --git a/drivers/usb/fotg210/fotg210-udc.c b/drivers/usb/fotg210/fotg210-udc.c index f7ea84070554..0bae12e34f9a 100644 --- a/drivers/usb/fotg210/fotg210-udc.c +++ b/drivers/usb/fotg210/fotg210-udc.c @@ -1094,10 +1094,10 @@ static int fotg210_udc_stop(struct usb_gadget *g) /** * fotg210_vbus_session - Called by external transceiver to enable/disable udc - * @_gadget: usb gadget + * @g: usb gadget * @is_active: 0 if should disable UDC VBUS, 1 if should enable * - * Returns 0 + * Returns: %0 */ static int fotg210_vbus_session(struct usb_gadget *g, int is_active) { @@ -1122,7 +1122,7 @@ static const struct usb_gadget_ops fotg210_gadget_ops = { * * Called by the USB Phy when a cable connect or disconnect is sensed. * - * Returns NOTIFY_OK or NOTIFY_DONE + * Returns: NOTIFY_OK or NOTIFY_DONE */ static int fotg210_phy_event(struct notifier_block *nb, unsigned long action, void *data) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 4c639e9ddedc..ce3cfa1f36f5 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -606,10 +606,11 @@ static struct config_group *function_make( char *instance_name; int ret; - ret = snprintf(buf, MAX_NAME_LEN, "%s", name); - if (ret >= MAX_NAME_LEN) + if (strlen(name) >= MAX_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); + scnprintf(buf, MAX_NAME_LEN, "%s", name); + func_name = buf; instance_name = strchr(func_name, '.'); if (!instance_name) { @@ -701,10 +702,12 @@ static struct config_group *config_desc_make( int ret; gi = container_of(group, struct gadget_info, configs_group); - ret = snprintf(buf, MAX_NAME_LEN, "%s", name); - if (ret >= MAX_NAME_LEN) + + if (strlen(name) >= MAX_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); + scnprintf(buf, MAX_NAME_LEN, "%s", name); + num_str = strchr(buf, '.'); if (!num_str) { pr_err("Unable to locate . in name.bConfigurationValue\n"); @@ -812,7 +815,7 @@ static ssize_t gadget_string_s_show(struct config_item *item, char *page) struct gadget_string *string = to_gadget_string(item); int ret; - ret = snprintf(page, sizeof(string->string), "%s\n", string->string); + ret = sysfs_emit(page, "%s\n", string->string); return ret; } diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index fdd0fc7b8f25..6bff6cb93789 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2931,9 +2931,8 @@ static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type, t = &func->function.os_desc_table[desc->bFirstInterfaceNumber]; t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber]; - memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID, - ARRAY_SIZE(desc->CompatibleID) + - ARRAY_SIZE(desc->SubCompatibleID)); + memcpy(t->os_desc->ext_compat_id, &desc->IDs, + sizeof_field(struct usb_ext_compat_desc, IDs)); length = sizeof(*desc); } break; diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 722a3ab2b337..c265a1f62fc1 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -545,21 +545,37 @@ static int start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh) { + int rc; + if (!fsg_is_set(common)) return false; bh->state = BUF_STATE_SENDING; - if (start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq)) + rc = start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq); + if (rc) { bh->state = BUF_STATE_EMPTY; + if (rc == -ESHUTDOWN) { + common->running = 0; + return false; + } + } return true; } static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh) { + int rc; + if (!fsg_is_set(common)) return false; bh->state = BUF_STATE_RECEIVING; - if (start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq)) + rc = start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq); + if (rc) { bh->state = BUF_STATE_FULL; + if (rc == -ESHUTDOWN) { + common->running = 0; + return false; + } + } return true; } diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 5335845d697b..20c6fbd94f32 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1177,11 +1177,11 @@ F_MIDI_OPT(out_ports, true, MAX_PORTS); static ssize_t f_midi_opts_id_show(struct config_item *item, char *page) { struct f_midi_opts *opts = to_f_midi_opts(item); - int result; + ssize_t result; mutex_lock(&opts->lock); if (opts->id) { - result = strlcpy(page, opts->id, PAGE_SIZE); + result = strscpy(page, opts->id, PAGE_SIZE); } else { page[0] = 0; result = 0; diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index cc0ed29a4adc..ca5d5f564998 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -103,6 +103,16 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f) /* Delay for the transmit to wait before sending an unfilled NTB frame. */ #define TX_TIMEOUT_NSECS 300000 +/* + * Although max mtu as dictated by u_ether is 15412 bytes, setting + * max_segment_size to 15426 would not be efficient. If user chooses segment + * size to be (>= 8192), then we can't aggregate more than one buffer in each + * NTB (assuming each packet coming from network layer is >= 8192 bytes) as ep + * maxpacket limit is 16384. So let max_segment_size be limited to 8000 to allow + * at least 2 packets to be aggregated reducing wastage of NTB buffer space + */ +#define MAX_DATAGRAM_SIZE 8000 + #define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \ USB_CDC_NCM_NTB32_SUPPORTED) @@ -179,7 +189,6 @@ static struct usb_cdc_ether_desc ecm_desc = { /* this descriptor actually adds value, surprise! */ /* .iMACAddress = DYNAMIC */ .bmEthernetStatistics = cpu_to_le32(0), /* no statistics */ - .wMaxSegmentSize = cpu_to_le16(ETH_FRAME_LEN), .wNumberMCFilters = cpu_to_le16(0), .bNumberPowerFilters = 0, }; @@ -1166,11 +1175,15 @@ static int ncm_unwrap_ntb(struct gether *port, struct sk_buff *skb2; int ret = -EINVAL; unsigned ntb_max = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize); - unsigned frame_max = le16_to_cpu(ecm_desc.wMaxSegmentSize); + unsigned frame_max; const struct ndp_parser_opts *opts = ncm->parser_opts; unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; int dgram_counter; int to_process = skb->len; + struct f_ncm_opts *ncm_opts; + + ncm_opts = container_of(port->func.fi, struct f_ncm_opts, func_inst); + frame_max = ncm_opts->max_segment_size; parse_ntb: tmp = (__le16 *)ntb_ptr; @@ -1430,8 +1443,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) mutex_lock(&ncm_opts->lock); gether_set_gadget(ncm_opts->net, cdev->gadget); - if (!ncm_opts->bound) + if (!ncm_opts->bound) { + ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN); status = gether_register_netdev(ncm_opts->net); + } mutex_unlock(&ncm_opts->lock); if (status) @@ -1474,6 +1489,8 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ncm_data_intf.bInterfaceNumber = status; ncm_union_desc.bSlaveInterface0 = status; + ecm_desc.wMaxSegmentSize = cpu_to_le16(ncm_opts->max_segment_size); + status = -ENODEV; /* allocate instance-specific endpoints */ @@ -1576,11 +1593,56 @@ USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm); /* f_ncm_opts_ifname */ USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm); +static ssize_t ncm_opts_max_segment_size_show(struct config_item *item, + char *page) +{ + struct f_ncm_opts *opts = to_f_ncm_opts(item); + u16 segment_size; + + mutex_lock(&opts->lock); + segment_size = opts->max_segment_size; + mutex_unlock(&opts->lock); + + return sysfs_emit(page, "%u\n", segment_size); +} + +static ssize_t ncm_opts_max_segment_size_store(struct config_item *item, + const char *page, size_t len) +{ + struct f_ncm_opts *opts = to_f_ncm_opts(item); + u16 segment_size; + int ret; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto out; + } + + ret = kstrtou16(page, 0, &segment_size); + if (ret) + goto out; + + if (segment_size > MAX_DATAGRAM_SIZE) { + ret = -EINVAL; + goto out; + } + + opts->max_segment_size = segment_size; + ret = len; +out: + mutex_unlock(&opts->lock); + return ret; +} + +CONFIGFS_ATTR(ncm_opts_, max_segment_size); + static struct configfs_attribute *ncm_attrs[] = { &ncm_opts_attr_dev_addr, &ncm_opts_attr_host_addr, &ncm_opts_attr_qmult, &ncm_opts_attr_ifname, + &ncm_opts_attr_max_segment_size, NULL, }; @@ -1623,6 +1685,7 @@ static struct usb_function_instance *ncm_alloc_inst(void) kfree(opts); return ERR_CAST(net); } + opts->max_segment_size = ETH_FRAME_LEN; INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop); descs[0] = &opts->ncm_os_desc; diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index ff33f31bcdf6..37befd6db001 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -1504,8 +1504,8 @@ static ssize_t tcm_usbg_tpg_nexus_show(struct config_item *item, char *page) ret = -ENODEV; goto out; } - ret = snprintf(page, PAGE_SIZE, "%s\n", - tv_nexus->tvn_se_sess->se_node_acl->initiatorname); + ret = sysfs_emit(page, "%s\n", + tv_nexus->tvn_se_sess->se_node_acl->initiatorname); out: mutex_unlock(&tpg->tpg_mutex); return ret; diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 6f0e1d803dc2..7de74a3dd392 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -292,6 +292,77 @@ static struct usb_descriptor_header *f_audio_desc[] = { NULL, }; +/* Standard ISO OUT Endpoint Descriptor */ +static struct usb_endpoint_descriptor ss_as_out_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), + .bInterval = 4, +}; + +static struct usb_ss_ep_comp_descriptor ss_as_out_ep_desc_comp = { + .bLength = sizeof(ss_as_out_ep_desc_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + /* wBytesPerInterval = DYNAMIC */ +}; + +/* Standard ISO OUT Endpoint Descriptor */ +static struct usb_endpoint_descriptor ss_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_ASYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), + .bInterval = 4, +}; + +static struct usb_ss_ep_comp_descriptor ss_as_in_ep_desc_comp = { + .bLength = sizeof(ss_as_in_ep_desc_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + /* wBytesPerInterval = DYNAMIC */ +}; + +static struct usb_descriptor_header *f_audio_ss_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&usb_out_it_desc, + (struct usb_descriptor_header *)&io_out_ot_desc, + (struct usb_descriptor_header *)&io_in_it_desc, + (struct usb_descriptor_header *)&usb_in_ot_desc, + + (struct usb_descriptor_header *)&as_out_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_out_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_out_header_desc, + + (struct usb_descriptor_header *)&as_out_type_i_desc, + + //(struct usb_descriptor_header *)&as_out_ep_desc, + (struct usb_descriptor_header *)&ss_as_out_ep_desc, + (struct usb_descriptor_header *)&ss_as_out_ep_desc_comp, + (struct usb_descriptor_header *)&as_iso_out_desc, + + (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_in_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_in_header_desc, + + (struct usb_descriptor_header *)&as_in_type_i_desc, + + //(struct usb_descriptor_header *)&as_in_ep_desc, + (struct usb_descriptor_header *)&ss_as_in_ep_desc, + (struct usb_descriptor_header *)&ss_as_in_ep_desc_comp, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + enum { STR_AC_IF, STR_USB_OUT_IT, @@ -1352,6 +1423,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); if (!ep) goto err_free_fu; + ss_as_out_ep_desc.bEndpointAddress = as_out_ep_desc.bEndpointAddress; audio->out_ep = ep; audio->out_ep->desc = &as_out_ep_desc; } @@ -1360,6 +1432,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc); if (!ep) goto err_free_fu; + ss_as_in_ep_desc.bEndpointAddress = as_in_ep_desc.bEndpointAddress; audio->in_ep = ep; audio->in_ep->desc = &as_in_ep_desc; } @@ -1367,8 +1440,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) setup_descriptor(audio_opts); /* copy descriptors, and track endpoint copies */ - status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL, - NULL); + status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, f_audio_ss_desc, + f_audio_ss_desc); if (status) goto err_free_fu; @@ -1561,7 +1634,7 @@ static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \ int result; \ \ mutex_lock(&opts->lock); \ - result = snprintf(page, sizeof(opts->name), "%s", opts->name); \ + result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \ mutex_unlock(&opts->lock); \ \ return result; \ @@ -1579,7 +1652,7 @@ static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \ goto end; \ } \ \ - ret = snprintf(opts->name, min(sizeof(opts->name), len), \ + ret = scnprintf(opts->name, min(sizeof(opts->name), len), \ "%s", page); \ \ end: \ @@ -1685,7 +1758,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void) opts->req_number = UAC1_DEF_REQ_NUM; - snprintf(opts->function_name, sizeof(opts->function_name), "AC Interface"); + scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface"); return &opts->func_inst; } diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index f9a0f07a7476..383f6854cfec 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -2045,7 +2045,7 @@ static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \ int result; \ \ mutex_lock(&opts->lock); \ - result = snprintf(page, sizeof(opts->name), "%s", opts->name); \ + result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \ mutex_unlock(&opts->lock); \ \ return result; \ @@ -2063,7 +2063,7 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \ goto end; \ } \ \ - ret = snprintf(opts->name, min(sizeof(opts->name), len), \ + ret = scnprintf(opts->name, min(sizeof(opts->name), len), \ "%s", page); \ \ end: \ @@ -2187,7 +2187,7 @@ static struct usb_function_instance *afunc_alloc_inst(void) opts->req_number = UAC2_DEF_REQ_NUM; opts->fb_max = FBACK_FAST_MAX; - snprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink"); + scnprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink"); opts->p_terminal_type = UAC2_DEF_P_TERM_TYPE; opts->c_terminal_type = UAC2_DEF_C_TERM_TYPE; diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 786379f1b7b7..929666805bd2 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -263,10 +263,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) return 0; } -void uvc_function_setup_continue(struct uvc_device *uvc) +void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep) { struct usb_composite_dev *cdev = uvc->func.config->cdev; + if (disable_ep && uvc->video.ep) + usb_ep_disable(uvc->video.ep); + usb_composite_setup_continue(cdev); } @@ -337,15 +340,11 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->state != UVC_STATE_STREAMING) return 0; - if (uvc->video.ep) - usb_ep_disable(uvc->video.ep); - memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMOFF; v4l2_event_queue(&uvc->vdev, &v4l2_event); - uvc->state = UVC_STATE_CONNECTED; - return 0; + return USB_GADGET_DELAYED_STATUS; case 1: if (uvc->state != UVC_STATE_CONNECTED) @@ -722,13 +721,29 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) } uvc->enable_interrupt_ep = opts->enable_interrupt_ep; - ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); + /* + * gadget_is_{super|dual}speed() API check UDC controller capitblity. It should pass down + * highest speed endpoint descriptor to UDC controller. So UDC controller driver can reserve + * enough resource at check_config(), especially mult and maxburst. So UDC driver (such as + * cdns3) can know need at least (mult + 1) * (maxburst + 1) * wMaxPacketSize internal + * memory for this uvc functions. This is the only straightforward method to resolve the UDC + * resource allocation issue in the current gadget framework. + */ + if (gadget_is_superspeed(c->cdev->gadget)) + ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep, + &uvc_ss_streaming_comp); + else if (gadget_is_dualspeed(cdev->gadget)) + ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep); + else + ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); + if (!ep) { uvcg_info(f, "Unable to allocate streaming EP\n"); goto error; } uvc->video.ep = ep; + uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; @@ -960,7 +975,8 @@ static void uvc_free(struct usb_function *f) struct uvc_device *uvc = to_uvc(f); struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts, func_inst); - config_item_put(&uvc->header->item); + if (!opts->header) + config_item_put(&uvc->header->item); --opts->refcnt; kfree(uvc); } @@ -1052,25 +1068,29 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) uvc->desc.hs_streaming = opts->hs_streaming; uvc->desc.ss_streaming = opts->ss_streaming; - streaming = config_group_find_item(&opts->func_inst.group, "streaming"); - if (!streaming) - goto err_config; - - header = config_group_find_item(to_config_group(streaming), "header"); - config_item_put(streaming); - if (!header) - goto err_config; - - h = config_group_find_item(to_config_group(header), "h"); - config_item_put(header); - if (!h) - goto err_config; - - uvc->header = to_uvcg_streaming_header(h); - if (!uvc->header->linked) { - mutex_unlock(&opts->lock); - kfree(uvc); - return ERR_PTR(-EBUSY); + if (opts->header) { + uvc->header = opts->header; + } else { + streaming = config_group_find_item(&opts->func_inst.group, "streaming"); + if (!streaming) + goto err_config; + + header = config_group_find_item(to_config_group(streaming), "header"); + config_item_put(streaming); + if (!header) + goto err_config; + + h = config_group_find_item(to_config_group(header), "h"); + config_item_put(header); + if (!h) + goto err_config; + + uvc->header = to_uvcg_streaming_header(h); + if (!uvc->header->linked) { + mutex_unlock(&opts->lock); + kfree(uvc); + return ERR_PTR(-EBUSY); + } } uvc->desc.extension_units = &opts->extension_units; diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index 1db972d4beeb..083aef0c65c6 100644 --- a/drivers/usb/gadget/function/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h @@ -11,7 +11,7 @@ struct uvc_device; -void uvc_function_setup_continue(struct uvc_device *uvc); +void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep); void uvc_function_connect(struct uvc_device *uvc); diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 9d1c40c152d8..3c5a6f6ac341 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -1163,6 +1163,8 @@ struct net_device *gether_connect(struct gether *link) if (netif_running(dev->net)) eth_start(dev, GFP_ATOMIC); + netif_device_attach(dev->net); + /* on error, disable any endpoints */ } else { (void) usb_ep_disable(link->out_ep); diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h index 5408854d8407..49ec095cdb4b 100644 --- a/drivers/usb/gadget/function/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h @@ -31,6 +31,8 @@ struct f_ncm_opts { */ struct mutex lock; int refcnt; + + u16 max_segment_size; }; #endif /* U_NCM_H */ diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index 1ce58f61253c..3ac392cbb779 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -98,6 +98,12 @@ struct f_uvc_opts { */ struct mutex lock; int refcnt; + + /* + * Only for legacy gadget. Shall be NULL for configfs-composed gadgets, + * which is guaranteed by alloc_inst implementation of f_uvc doing kzalloc. + */ + struct uvcg_streaming_header *header; }; #endif /* U_UVC_H */ diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 6751de8b63ad..cb35687b11e7 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -81,6 +81,7 @@ struct uvc_request { struct sg_table sgt; u8 header[UVCG_REQUEST_HEADER_LEN]; struct uvc_buffer *last_buf; + struct list_head list; }; struct uvc_video { @@ -101,9 +102,18 @@ struct uvc_video { unsigned int uvc_num_requests; /* Requests */ + bool is_enabled; /* tracks whether video stream is enabled */ unsigned int req_size; - struct uvc_request *ureq; + struct list_head ureqs; /* all uvc_requests allocated by uvc_video */ + + /* USB requests that the video pump thread can encode into */ struct list_head req_free; + + /* + * USB requests video pump thread has already encoded into. These are + * ready to be queued to the endpoint. + */ + struct list_head req_ready; spinlock_t req_lock; unsigned int req_int_count; @@ -177,7 +187,7 @@ struct uvc_file_handle { * Functions */ -extern void uvc_function_setup_continue(struct uvc_device *uvc); +extern void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep); extern void uvc_function_connect(struct uvc_device *uvc); extern void uvc_function_disconnect(struct uvc_device *uvc); diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 9bf0e985acfa..7e704b2bcfd1 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -3414,7 +3414,7 @@ static ssize_t f_uvc_opts_string_##cname##_show(struct config_item *item,\ int result; \ \ mutex_lock(&opts->lock); \ - result = snprintf(page, sizeof(opts->aname), "%s", opts->aname);\ + result = scnprintf(page, sizeof(opts->aname), "%s", opts->aname);\ mutex_unlock(&opts->lock); \ \ return result; \ diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 3f0a9795c0d4..c7e5fa4f29e0 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -443,7 +443,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) return -EINVAL; /* Enable UVC video. */ - ret = uvcg_video_enable(video, 1); + ret = uvcg_video_enable(video); if (ret < 0) return ret; @@ -451,7 +451,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) * Complete the alternate setting selection setup phase now that * userspace is ready to provide video frames. */ - uvc_function_setup_continue(uvc); + uvc_function_setup_continue(uvc, 0); uvc->state = UVC_STATE_STREAMING; return 0; @@ -463,11 +463,18 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); struct uvc_video *video = &uvc->video; + int ret = 0; if (type != video->queue.queue.type) return -EINVAL; - return uvcg_video_enable(video, 0); + ret = uvcg_video_disable(video); + if (ret < 0) + return ret; + + uvc->state = UVC_STATE_CONNECTED; + uvc_function_setup_continue(uvc, 1); + return 0; } static int @@ -500,7 +507,7 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh, static void uvc_v4l2_disable(struct uvc_device *uvc) { uvc_function_disconnect(uvc); - uvcg_video_enable(&uvc->video, 0); + uvcg_video_disable(&uvc->video); uvcg_free_buffers(&uvc->video.queue); uvc->func_connected = false; wake_up_interruptible(&uvc->func_connected_queue); @@ -647,4 +654,3 @@ const struct v4l2_file_operations uvc_v4l2_fops = { .get_unmapped_area = uvcg_v4l2_get_unmapped_area, #endif }; - diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 91af3b1ef0d4..dd3241fc6939 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -227,6 +227,28 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, * Request handling */ +/* + * Callers must take care to hold req_lock when this function may be called + * from multiple threads. For example, when frames are streaming to the host. + */ +static void +uvc_video_free_request(struct uvc_request *ureq, struct usb_ep *ep) +{ + sg_free_table(&ureq->sgt); + if (ureq->req && ep) { + usb_ep_free_request(ep, ureq->req); + ureq->req = NULL; + } + + kfree(ureq->req_buffer); + ureq->req_buffer = NULL; + + if (!list_empty(&ureq->list)) + list_del_init(&ureq->list); + + kfree(ureq); +} + static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) { int ret; @@ -247,14 +269,127 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) return ret; } +/* This function must be called with video->req_lock held. */ +static int uvcg_video_usb_req_queue(struct uvc_video *video, + struct usb_request *req, bool queue_to_ep) +{ + bool is_bulk = video->max_payload_size; + struct list_head *list = NULL; + + if (!video->is_enabled) + return -ENODEV; + + if (queue_to_ep) { + struct uvc_request *ureq = req->context; + /* + * With USB3 handling more requests at a higher speed, we can't + * afford to generate an interrupt for every request. Decide to + * interrupt: + * + * - When no more requests are available in the free queue, as + * this may be our last chance to refill the endpoint's + * request queue. + * + * - When this is request is the last request for the video + * buffer, as we want to start sending the next video buffer + * ASAP in case it doesn't get started already in the next + * iteration of this loop. + * + * - Four times over the length of the requests queue (as + * indicated by video->uvc_num_requests), as a trade-off + * between latency and interrupt load. + */ + if (list_empty(&video->req_free) || ureq->last_buf || + !(video->req_int_count % + DIV_ROUND_UP(video->uvc_num_requests, 4))) { + video->req_int_count = 0; + req->no_interrupt = 0; + } else { + req->no_interrupt = 1; + } + video->req_int_count++; + return uvcg_video_ep_queue(video, req); + } + /* + * If we're not queuing to the ep, for isoc we're queuing + * to the req_ready list, otherwise req_free. + */ + list = is_bulk ? &video->req_free : &video->req_ready; + list_add_tail(&req->list, list); + return 0; +} + +/* + * Must only be called from uvcg_video_enable - since after that we only want to + * queue requests to the endpoint from the uvc_video_complete complete handler. + * This function is needed in order to 'kick start' the flow of requests from + * gadget driver to the usb controller. + */ +static void uvc_video_ep_queue_initial_requests(struct uvc_video *video) +{ + struct usb_request *req = NULL; + unsigned long flags = 0; + unsigned int count = 0; + int ret = 0; + + /* + * We only queue half of the free list since we still want to have + * some free usb_requests in the free list for the video_pump async_wq + * thread to encode uvc buffers into. Otherwise we could get into a + * situation where the free list does not have any usb requests to + * encode into - we always end up queueing 0 length requests to the + * end point. + */ + unsigned int half_list_size = video->uvc_num_requests / 2; + + spin_lock_irqsave(&video->req_lock, flags); + /* + * Take these requests off the free list and queue them all to the + * endpoint. Since we queue 0 length requests with the req_lock held, + * there isn't any 'data' race involved here with the complete handler. + */ + while (count < half_list_size) { + req = list_first_entry(&video->req_free, struct usb_request, + list); + list_del(&req->list); + req->length = 0; + ret = uvcg_video_ep_queue(video, req); + if (ret < 0) { + uvcg_queue_cancel(&video->queue, 0); + break; + } + count++; + } + spin_unlock_irqrestore(&video->req_lock, flags); +} + static void uvc_video_complete(struct usb_ep *ep, struct usb_request *req) { struct uvc_request *ureq = req->context; struct uvc_video *video = ureq->video; struct uvc_video_queue *queue = &video->queue; - struct uvc_device *uvc = video->uvc; + struct uvc_buffer *last_buf; unsigned long flags; + bool is_bulk = video->max_payload_size; + int ret = 0; + + spin_lock_irqsave(&video->req_lock, flags); + if (!video->is_enabled) { + /* + * When is_enabled is false, uvcg_video_disable() ensures + * that in-flight uvc_buffers are returned, so we can + * safely call free_request without worrying about + * last_buf. + */ + uvc_video_free_request(ureq, ep); + spin_unlock_irqrestore(&video->req_lock, flags); + return; + } + + last_buf = ureq->last_buf; + ureq->last_buf = NULL; + spin_unlock_irqrestore(&video->req_lock, flags); switch (req->status) { case 0: @@ -277,44 +412,85 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) uvcg_queue_cancel(queue, 0); } - if (ureq->last_buf) { - uvcg_complete_buffer(&video->queue, ureq->last_buf); - ureq->last_buf = NULL; + if (last_buf) { + spin_lock_irqsave(&queue->irqlock, flags); + uvcg_complete_buffer(queue, last_buf); + spin_unlock_irqrestore(&queue->irqlock, flags); } spin_lock_irqsave(&video->req_lock, flags); - list_add_tail(&req->list, &video->req_free); + /* + * Video stream might have been disabled while we were + * processing the current usb_request. So make sure + * we're still streaming before queueing the usb_request + * back to req_free + */ + if (video->is_enabled) { + /* + * Here we check whether any request is available in the ready + * list. If it is, queue it to the ep and add the current + * usb_request to the req_free list - for video_pump to fill in. + * Otherwise, just use the current usb_request to queue a 0 + * length request to the ep. Since we always add to the req_free + * list if we dequeue from the ready list, there will never + * be a situation where the req_free list is completely out of + * requests and cannot recover. + */ + struct usb_request *to_queue = req; + + to_queue->length = 0; + if (!list_empty(&video->req_ready)) { + to_queue = list_first_entry(&video->req_ready, + struct usb_request, list); + list_del(&to_queue->list); + list_add_tail(&req->list, &video->req_free); + /* + * Queue work to the wq as well since it is possible that a + * buffer may not have been completely encoded with the set of + * in-flight usb requests for whih the complete callbacks are + * firing. + * In that case, if we do not queue work to the worker thread, + * the buffer will never be marked as complete - and therefore + * not be returned to userpsace. As a result, + * dequeue -> queue -> dequeue flow of uvc buffers will not + * happen. + */ + queue_work(video->async_wq, &video->pump); + } + /* + * Queue to the endpoint. The actual queueing to ep will + * only happen on one thread - the async_wq for bulk endpoints + * and this thread for isoc endpoints. + */ + ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk); + if (ret < 0) { + /* + * Endpoint error, but the stream is still enabled. + * Put request back in req_free for it to be cleaned + * up later. + */ + list_add_tail(&to_queue->list, &video->req_free); + } + } else { + uvc_video_free_request(ureq, ep); + ret = 0; + } spin_unlock_irqrestore(&video->req_lock, flags); - - if (uvc->state == UVC_STATE_STREAMING) - queue_work(video->async_wq, &video->pump); + if (ret < 0) + uvcg_queue_cancel(queue, 0); } static int uvc_video_free_requests(struct uvc_video *video) { - unsigned int i; - - if (video->ureq) { - for (i = 0; i < video->uvc_num_requests; ++i) { - sg_free_table(&video->ureq[i].sgt); + struct uvc_request *ureq, *temp; - if (video->ureq[i].req) { - usb_ep_free_request(video->ep, video->ureq[i].req); - video->ureq[i].req = NULL; - } - - if (video->ureq[i].req_buffer) { - kfree(video->ureq[i].req_buffer); - video->ureq[i].req_buffer = NULL; - } - } - - kfree(video->ureq); - video->ureq = NULL; - } + list_for_each_entry_safe(ureq, temp, &video->ureqs, list) + uvc_video_free_request(ureq, video->ep); + INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); + INIT_LIST_HEAD(&video->req_ready); video->req_size = 0; return 0; } @@ -322,6 +498,7 @@ uvc_video_free_requests(struct uvc_video *video) static int uvc_video_alloc_requests(struct uvc_video *video) { + struct uvc_request *ureq; unsigned int req_size; unsigned int i; int ret = -ENOMEM; @@ -332,29 +509,33 @@ uvc_video_alloc_requests(struct uvc_video *video) * max_t(unsigned int, video->ep->maxburst, 1) * (video->ep->mult); - video->ureq = kcalloc(video->uvc_num_requests, sizeof(struct uvc_request), GFP_KERNEL); - if (video->ureq == NULL) - return -ENOMEM; + for (i = 0; i < video->uvc_num_requests; i++) { + ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL); + if (ureq == NULL) + goto error; + + INIT_LIST_HEAD(&ureq->list); - for (i = 0; i < video->uvc_num_requests; ++i) { - video->ureq[i].req_buffer = kmalloc(req_size, GFP_KERNEL); - if (video->ureq[i].req_buffer == NULL) + list_add_tail(&ureq->list, &video->ureqs); + + ureq->req_buffer = kmalloc(req_size, GFP_KERNEL); + if (ureq->req_buffer == NULL) goto error; - video->ureq[i].req = usb_ep_alloc_request(video->ep, GFP_KERNEL); - if (video->ureq[i].req == NULL) + ureq->req = usb_ep_alloc_request(video->ep, GFP_KERNEL); + if (ureq->req == NULL) goto error; - video->ureq[i].req->buf = video->ureq[i].req_buffer; - video->ureq[i].req->length = 0; - video->ureq[i].req->complete = uvc_video_complete; - video->ureq[i].req->context = &video->ureq[i]; - video->ureq[i].video = video; - video->ureq[i].last_buf = NULL; + ureq->req->buf = ureq->req_buffer; + ureq->req->length = 0; + ureq->req->complete = uvc_video_complete; + ureq->req->context = ureq; + ureq->video = video; + ureq->last_buf = NULL; - list_add_tail(&video->ureq[i].req->list, &video->req_free); + list_add_tail(&ureq->req->list, &video->req_free); /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */ - sg_alloc_table(&video->ureq[i].sgt, + sg_alloc_table(&ureq->sgt, DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN, PAGE_SIZE) + 2, GFP_KERNEL); } @@ -387,16 +568,18 @@ static void uvcg_video_pump(struct work_struct *work) struct usb_request *req = NULL; struct uvc_buffer *buf; unsigned long flags; - bool buf_done; - int ret; + int ret = 0; + + while (true) { + if (!video->ep->enabled) + return; - while (video->ep->enabled) { /* - * Retrieve the first available USB request, protected by the - * request lock. + * Check is_enabled and retrieve the first available USB + * request, protected by the request lock. */ spin_lock_irqsave(&video->req_lock, flags); - if (list_empty(&video->req_free)) { + if (!video->is_enabled || list_empty(&video->req_free)) { spin_unlock_irqrestore(&video->req_lock, flags); return; } @@ -414,15 +597,6 @@ static void uvcg_video_pump(struct work_struct *work) if (buf != NULL) { video->encode(req, video, buf); - buf_done = buf->state == UVC_BUF_STATE_DONE; - } else if (!(queue->flags & UVC_QUEUE_DISCONNECTED) && !is_bulk) { - /* - * No video buffer available; the queue is still connected and - * we're transferring over ISOC. Queue a 0 length request to - * prevent missed ISOC transfers. - */ - req->length = 0; - buf_done = false; } else { /* * Either the queue has been disconnected or no video buffer @@ -433,83 +607,139 @@ static void uvcg_video_pump(struct work_struct *work) break; } - /* - * With USB3 handling more requests at a higher speed, we can't - * afford to generate an interrupt for every request. Decide to - * interrupt: - * - * - When no more requests are available in the free queue, as - * this may be our last chance to refill the endpoint's - * request queue. - * - * - When this is request is the last request for the video - * buffer, as we want to start sending the next video buffer - * ASAP in case it doesn't get started already in the next - * iteration of this loop. - * - * - Four times over the length of the requests queue (as - * indicated by video->uvc_num_requests), as a trade-off - * between latency and interrupt load. - */ - if (list_empty(&video->req_free) || buf_done || - !(video->req_int_count % - DIV_ROUND_UP(video->uvc_num_requests, 4))) { - video->req_int_count = 0; - req->no_interrupt = 0; - } else { - req->no_interrupt = 1; - } - - /* Queue the USB request */ - ret = uvcg_video_ep_queue(video, req); spin_unlock_irqrestore(&queue->irqlock, flags); + spin_lock_irqsave(&video->req_lock, flags); + /* For bulk end points we queue from the worker thread + * since we would preferably not want to wait on requests + * to be ready, in the uvcg_video_complete() handler. + * For isoc endpoints we add the request to the ready list + * and only queue it to the endpoint from the complete handler. + */ + ret = uvcg_video_usb_req_queue(video, req, is_bulk); + spin_unlock_irqrestore(&video->req_lock, flags); + if (ret < 0) { uvcg_queue_cancel(queue, 0); break; } - /* Endpoint now owns the request */ + /* The request is owned by the endpoint / ready list. */ req = NULL; - video->req_int_count++; } if (!req) return; spin_lock_irqsave(&video->req_lock, flags); - list_add_tail(&req->list, &video->req_free); + if (video->is_enabled) + list_add_tail(&req->list, &video->req_free); + else + uvc_video_free_request(req->context, video->ep); spin_unlock_irqrestore(&video->req_lock, flags); - return; } /* - * Enable or disable the video stream. + * Disable the video stream */ -int uvcg_video_enable(struct uvc_video *video, int enable) +int +uvcg_video_disable(struct uvc_video *video) { - unsigned int i; - int ret; + unsigned long flags; + struct list_head inflight_bufs; + struct usb_request *req, *temp; + struct uvc_buffer *buf, *btemp; + struct uvc_request *ureq, *utemp; if (video->ep == NULL) { uvcg_info(&video->uvc->func, - "Video enable failed, device is uninitialized.\n"); + "Video disable failed, device is uninitialized.\n"); return -ENODEV; } - if (!enable) { - cancel_work_sync(&video->pump); - uvcg_queue_cancel(&video->queue, 0); + INIT_LIST_HEAD(&inflight_bufs); + spin_lock_irqsave(&video->req_lock, flags); + video->is_enabled = false; + + /* + * Remove any in-flight buffers from the uvc_requests + * because we want to return them before cancelling the + * queue. This ensures that we aren't stuck waiting for + * all complete callbacks to come through before disabling + * vb2 queue. + */ + list_for_each_entry(ureq, &video->ureqs, list) { + if (ureq->last_buf) { + list_add_tail(&ureq->last_buf->queue, &inflight_bufs); + ureq->last_buf = NULL; + } + } + spin_unlock_irqrestore(&video->req_lock, flags); - for (i = 0; i < video->uvc_num_requests; ++i) - if (video->ureq && video->ureq[i].req) - usb_ep_dequeue(video->ep, video->ureq[i].req); + cancel_work_sync(&video->pump); + uvcg_queue_cancel(&video->queue, 0); + + spin_lock_irqsave(&video->req_lock, flags); + /* + * Remove all uvc_requests from ureqs with list_del_init + * This lets uvc_video_free_request correctly identify + * if the uvc_request is attached to a list or not when freeing + * memory. + */ + list_for_each_entry_safe(ureq, utemp, &video->ureqs, list) + list_del_init(&ureq->list); + + list_for_each_entry_safe(req, temp, &video->req_free, list) { + list_del(&req->list); + uvc_video_free_request(req->context, video->ep); + } - uvc_video_free_requests(video); - uvcg_queue_enable(&video->queue, 0); - return 0; + list_for_each_entry_safe(req, temp, &video->req_ready, list) { + list_del(&req->list); + uvc_video_free_request(req->context, video->ep); } + INIT_LIST_HEAD(&video->ureqs); + INIT_LIST_HEAD(&video->req_free); + INIT_LIST_HEAD(&video->req_ready); + video->req_size = 0; + spin_unlock_irqrestore(&video->req_lock, flags); + + /* + * Return all the video buffers before disabling the queue. + */ + spin_lock_irqsave(&video->queue.irqlock, flags); + list_for_each_entry_safe(buf, btemp, &inflight_bufs, queue) { + list_del(&buf->queue); + uvcg_complete_buffer(&video->queue, buf); + } + spin_unlock_irqrestore(&video->queue.irqlock, flags); + + uvcg_queue_enable(&video->queue, 0); + return 0; +} + +/* + * Enable the video stream. + */ +int uvcg_video_enable(struct uvc_video *video) +{ + int ret; + + if (video->ep == NULL) { + uvcg_info(&video->uvc->func, + "Video enable failed, device is uninitialized.\n"); + return -ENODEV; + } + + /* + * Safe to access request related fields without req_lock because + * this is the only thread currently active, and no other + * request handling thread will become active until this function + * returns. + */ + video->is_enabled = true; + if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0) return ret; @@ -525,7 +755,7 @@ int uvcg_video_enable(struct uvc_video *video, int enable) video->req_int_count = 0; - queue_work(video->async_wq, &video->pump); + uvc_video_ep_queue_initial_requests(video); return ret; } @@ -535,7 +765,10 @@ int uvcg_video_enable(struct uvc_video *video, int enable) */ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) { + video->is_enabled = false; + INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); + INIT_LIST_HEAD(&video->req_ready); spin_lock_init(&video->req_lock); INIT_WORK(&video->pump, uvcg_video_pump); diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h index 03adeefa343b..8ef6259741f1 100644 --- a/drivers/usb/gadget/function/uvc_video.h +++ b/drivers/usb/gadget/function/uvc_video.h @@ -14,7 +14,8 @@ struct uvc_video; -int uvcg_video_enable(struct uvc_video *video, int enable); +int uvcg_video_enable(struct uvc_video *video); +int uvcg_video_disable(struct uvc_video *video); int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc); diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c index c06dd1af7a0c..c395438d3978 100644 --- a/drivers/usb/gadget/legacy/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -12,6 +12,7 @@ #include <linux/usb/video.h> #include "u_uvc.h" +#include "uvc_configfs.h" USB_GADGET_COMPOSITE_OPTIONS(); @@ -84,8 +85,6 @@ static struct usb_device_descriptor webcam_device_descriptor = { .bNumConfigurations = 0, /* dynamic */ }; -DECLARE_UVC_HEADER_DESCRIPTOR(1); - static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = { .bLength = UVC_DT_HEADER_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, @@ -158,43 +157,112 @@ static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = { .bmaControls[1][0] = 4, }; -static const struct uvc_format_uncompressed uvc_format_yuv = { - .bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED, - .bFormatIndex = 1, - .bNumFrameDescriptors = 2, - .guidFormat = - { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, - 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}, - .bBitsPerPixel = 16, - .bDefaultFrameIndex = 1, - .bAspectRatioX = 0, - .bAspectRatioY = 0, - .bmInterlaceFlags = 0, - .bCopyProtect = 0, +static const struct uvcg_color_matching uvcg_color_matching = { + .desc = { + .bLength = UVC_DT_COLOR_MATCHING_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_COLORFORMAT, + .bColorPrimaries = 1, + .bTransferCharacteristics = 1, + .bMatrixCoefficients = 4, + }, +}; + +static struct uvcg_uncompressed uvcg_format_yuv = { + .fmt = { + .type = UVCG_UNCOMPRESSED, + /* add to .frames and fill .num_frames at runtime */ + .color_matching = (struct uvcg_color_matching *)&uvcg_color_matching, + }, + .desc = { + .bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED, + .bFormatIndex = 1, + .bNumFrameDescriptors = 2, + .guidFormat = { + 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 + }, + .bBitsPerPixel = 16, + .bDefaultFrameIndex = 1, + .bAspectRatioX = 0, + .bAspectRatioY = 0, + .bmInterlaceFlags = 0, + .bCopyProtect = 0, + }, +}; + +static struct uvcg_format_ptr uvcg_format_ptr_yuv = { + .fmt = &uvcg_format_yuv.fmt, }; DECLARE_UVC_FRAME_UNCOMPRESSED(1); DECLARE_UVC_FRAME_UNCOMPRESSED(3); +#define UVCG_WIDTH_360P 640 +#define UVCG_HEIGHT_360P 360 +#define UVCG_MIN_BITRATE_360P 18432000 +#define UVCG_MAX_BITRATE_360P 55296000 +#define UVCG_MAX_VIDEO_FB_SZ_360P 460800 +#define UVCG_FRM_INTERV_0_360P 666666 +#define UVCG_FRM_INTERV_1_360P 1000000 +#define UVCG_FRM_INTERV_2_360P 5000000 +#define UVCG_DEFAULT_FRM_INTERV_360P UVCG_FRM_INTERV_0_360P + static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = { .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, .bFrameIndex = 1, .bmCapabilities = 0, - .wWidth = cpu_to_le16(640), - .wHeight = cpu_to_le16(360), - .dwMinBitRate = cpu_to_le32(18432000), - .dwMaxBitRate = cpu_to_le32(55296000), - .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), - .dwDefaultFrameInterval = cpu_to_le32(666666), + .wWidth = cpu_to_le16(UVCG_WIDTH_360P), + .wHeight = cpu_to_le16(UVCG_HEIGHT_360P), + .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_360P), + .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_360P), + .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P), + .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P), .bFrameIntervalType = 3, - .dwFrameInterval[0] = cpu_to_le32(666666), - .dwFrameInterval[1] = cpu_to_le32(1000000), - .dwFrameInterval[2] = cpu_to_le32(5000000), + .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_360P), + .dwFrameInterval[1] = cpu_to_le32(UVCG_FRM_INTERV_1_360P), + .dwFrameInterval[2] = cpu_to_le32(UVCG_FRM_INTERV_2_360P), +}; + +static u32 uvcg_frame_yuv_360p_dw_frame_interval[] = { + [0] = UVCG_FRM_INTERV_0_360P, + [1] = UVCG_FRM_INTERV_1_360P, + [2] = UVCG_FRM_INTERV_2_360P, +}; + +static const struct uvcg_frame uvcg_frame_yuv_360p = { + .fmt_type = UVCG_UNCOMPRESSED, + .frame = { + .b_length = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3), + .b_descriptor_type = USB_DT_CS_INTERFACE, + .b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED, + .b_frame_index = 1, + .bm_capabilities = 0, + .w_width = UVCG_WIDTH_360P, + .w_height = UVCG_HEIGHT_360P, + .dw_min_bit_rate = UVCG_MIN_BITRATE_360P, + .dw_max_bit_rate = UVCG_MAX_BITRATE_360P, + .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_360P, + .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_360P, + .b_frame_interval_type = 3, + }, + .dw_frame_interval = uvcg_frame_yuv_360p_dw_frame_interval, +}; + +static struct uvcg_frame_ptr uvcg_frame_ptr_yuv_360p = { + .frm = (struct uvcg_frame *)&uvcg_frame_yuv_360p, }; +#define UVCG_WIDTH_720P 1280 +#define UVCG_HEIGHT_720P 720 +#define UVCG_MIN_BITRATE_720P 29491200 +#define UVCG_MAX_BITRATE_720P 29491200 +#define UVCG_MAX_VIDEO_FB_SZ_720P 1843200 +#define UVCG_FRM_INTERV_0_720P 5000000 +#define UVCG_DEFAULT_FRM_INTERV_720P UVCG_FRM_INTERV_0_720P static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = { .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), @@ -202,28 +270,66 @@ static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = { .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, .bFrameIndex = 2, .bmCapabilities = 0, - .wWidth = cpu_to_le16(1280), - .wHeight = cpu_to_le16(720), - .dwMinBitRate = cpu_to_le32(29491200), - .dwMaxBitRate = cpu_to_le32(29491200), - .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), - .dwDefaultFrameInterval = cpu_to_le32(5000000), + .wWidth = cpu_to_le16(UVCG_WIDTH_720P), + .wHeight = cpu_to_le16(UVCG_HEIGHT_720P), + .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_720P), + .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_720P), + .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P), + .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P), .bFrameIntervalType = 1, - .dwFrameInterval[0] = cpu_to_le32(5000000), + .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_720P), }; -static const struct uvc_format_mjpeg uvc_format_mjpg = { - .bLength = UVC_DT_FORMAT_MJPEG_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_VS_FORMAT_MJPEG, - .bFormatIndex = 2, - .bNumFrameDescriptors = 2, - .bmFlags = 0, - .bDefaultFrameIndex = 1, - .bAspectRatioX = 0, - .bAspectRatioY = 0, - .bmInterlaceFlags = 0, - .bCopyProtect = 0, +static u32 uvcg_frame_yuv_720p_dw_frame_interval[] = { + [0] = UVCG_FRM_INTERV_0_720P, +}; + +static const struct uvcg_frame uvcg_frame_yuv_720p = { + .fmt_type = UVCG_UNCOMPRESSED, + .frame = { + .b_length = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), + .b_descriptor_type = USB_DT_CS_INTERFACE, + .b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED, + .b_frame_index = 2, + .bm_capabilities = 0, + .w_width = UVCG_WIDTH_720P, + .w_height = UVCG_HEIGHT_720P, + .dw_min_bit_rate = UVCG_MIN_BITRATE_720P, + .dw_max_bit_rate = UVCG_MAX_BITRATE_720P, + .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_720P, + .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_720P, + .b_frame_interval_type = 1, + }, + .dw_frame_interval = uvcg_frame_yuv_720p_dw_frame_interval, +}; + +static struct uvcg_frame_ptr uvcg_frame_ptr_yuv_720p = { + .frm = (struct uvcg_frame *)&uvcg_frame_yuv_720p, +}; + +static struct uvcg_mjpeg uvcg_format_mjpeg = { + .fmt = { + .type = UVCG_MJPEG, + /* add to .frames and fill .num_frames at runtime */ + .color_matching = (struct uvcg_color_matching *)&uvcg_color_matching, + }, + .desc = { + .bLength = UVC_DT_FORMAT_MJPEG_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FORMAT_MJPEG, + .bFormatIndex = 2, + .bNumFrameDescriptors = 2, + .bmFlags = 0, + .bDefaultFrameIndex = 1, + .bAspectRatioX = 0, + .bAspectRatioY = 0, + .bmInterlaceFlags = 0, + .bCopyProtect = 0, + }, +}; + +static struct uvcg_format_ptr uvcg_format_ptr_mjpeg = { + .fmt = &uvcg_format_mjpeg.fmt, }; DECLARE_UVC_FRAME_MJPEG(1); @@ -235,16 +341,45 @@ static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = { .bDescriptorSubType = UVC_VS_FRAME_MJPEG, .bFrameIndex = 1, .bmCapabilities = 0, - .wWidth = cpu_to_le16(640), - .wHeight = cpu_to_le16(360), - .dwMinBitRate = cpu_to_le32(18432000), - .dwMaxBitRate = cpu_to_le32(55296000), - .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), - .dwDefaultFrameInterval = cpu_to_le32(666666), + .wWidth = cpu_to_le16(UVCG_WIDTH_360P), + .wHeight = cpu_to_le16(UVCG_HEIGHT_360P), + .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_360P), + .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_360P), + .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_360P), + .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_360P), .bFrameIntervalType = 3, - .dwFrameInterval[0] = cpu_to_le32(666666), - .dwFrameInterval[1] = cpu_to_le32(1000000), - .dwFrameInterval[2] = cpu_to_le32(5000000), + .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_360P), + .dwFrameInterval[1] = cpu_to_le32(UVCG_FRM_INTERV_1_360P), + .dwFrameInterval[2] = cpu_to_le32(UVCG_FRM_INTERV_2_360P), +}; + +static u32 uvcg_frame_mjpeg_360p_dw_frame_interval[] = { + [0] = UVCG_FRM_INTERV_0_360P, + [1] = UVCG_FRM_INTERV_1_360P, + [2] = UVCG_FRM_INTERV_2_360P, +}; + +static const struct uvcg_frame uvcg_frame_mjpeg_360p = { + .fmt_type = UVCG_MJPEG, + .frame = { + .b_length = UVC_DT_FRAME_MJPEG_SIZE(3), + .b_descriptor_type = USB_DT_CS_INTERFACE, + .b_descriptor_subtype = UVC_VS_FRAME_MJPEG, + .b_frame_index = 1, + .bm_capabilities = 0, + .w_width = UVCG_WIDTH_360P, + .w_height = UVCG_HEIGHT_360P, + .dw_min_bit_rate = UVCG_MIN_BITRATE_360P, + .dw_max_bit_rate = UVCG_MAX_BITRATE_360P, + .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_360P, + .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_360P, + .b_frame_interval_type = 3, + }, + .dw_frame_interval = uvcg_frame_mjpeg_360p_dw_frame_interval, +}; + +static struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_360p = { + .frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_360p, }; static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = { @@ -253,23 +388,44 @@ static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = { .bDescriptorSubType = UVC_VS_FRAME_MJPEG, .bFrameIndex = 2, .bmCapabilities = 0, - .wWidth = cpu_to_le16(1280), - .wHeight = cpu_to_le16(720), - .dwMinBitRate = cpu_to_le32(29491200), - .dwMaxBitRate = cpu_to_le32(29491200), - .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), - .dwDefaultFrameInterval = cpu_to_le32(5000000), + .wWidth = cpu_to_le16(UVCG_WIDTH_720P), + .wHeight = cpu_to_le16(UVCG_HEIGHT_720P), + .dwMinBitRate = cpu_to_le32(UVCG_MIN_BITRATE_720P), + .dwMaxBitRate = cpu_to_le32(UVCG_MAX_BITRATE_720P), + .dwMaxVideoFrameBufferSize = cpu_to_le32(UVCG_MAX_VIDEO_FB_SZ_720P), + .dwDefaultFrameInterval = cpu_to_le32(UVCG_DEFAULT_FRM_INTERV_720P), .bFrameIntervalType = 1, - .dwFrameInterval[0] = cpu_to_le32(5000000), + .dwFrameInterval[0] = cpu_to_le32(UVCG_FRM_INTERV_0_720P), }; -static const struct uvc_color_matching_descriptor uvc_color_matching = { - .bLength = UVC_DT_COLOR_MATCHING_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_VS_COLORFORMAT, - .bColorPrimaries = 1, - .bTransferCharacteristics = 1, - .bMatrixCoefficients = 4, +static u32 uvcg_frame_mjpeg_720p_dw_frame_interval[] = { + [0] = UVCG_FRM_INTERV_0_720P, +}; + +static const struct uvcg_frame uvcg_frame_mjpeg_720p = { + .fmt_type = UVCG_MJPEG, + .frame = { + .b_length = UVC_DT_FRAME_MJPEG_SIZE(1), + .b_descriptor_type = USB_DT_CS_INTERFACE, + .b_descriptor_subtype = UVC_VS_FRAME_MJPEG, + .b_frame_index = 2, + .bm_capabilities = 0, + .w_width = UVCG_WIDTH_720P, + .w_height = UVCG_HEIGHT_720P, + .dw_min_bit_rate = UVCG_MIN_BITRATE_720P, + .dw_max_bit_rate = UVCG_MAX_BITRATE_720P, + .dw_max_video_frame_buffer_size = UVCG_MAX_VIDEO_FB_SZ_720P, + .dw_default_frame_interval = UVCG_DEFAULT_FRM_INTERV_720P, + .b_frame_interval_type = 1, + }, + .dw_frame_interval = uvcg_frame_mjpeg_720p_dw_frame_interval, +}; + +static struct uvcg_frame_ptr uvcg_frame_ptr_mjpeg_720p = { + .frm = (struct uvcg_frame *)&uvcg_frame_mjpeg_720p, +}; + +static struct uvcg_streaming_header uvcg_streaming_header = { }; static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = { @@ -290,40 +446,40 @@ static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = { static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = { (const struct uvc_descriptor_header *) &uvc_input_header, - (const struct uvc_descriptor_header *) &uvc_format_yuv, + (const struct uvc_descriptor_header *) &uvcg_format_yuv.desc, (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, - (const struct uvc_descriptor_header *) &uvc_color_matching, - (const struct uvc_descriptor_header *) &uvc_format_mjpg, + (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, + (const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc, (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, - (const struct uvc_descriptor_header *) &uvc_color_matching, + (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, NULL, }; static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = { (const struct uvc_descriptor_header *) &uvc_input_header, - (const struct uvc_descriptor_header *) &uvc_format_yuv, + (const struct uvc_descriptor_header *) &uvcg_format_yuv.desc, (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, - (const struct uvc_descriptor_header *) &uvc_color_matching, - (const struct uvc_descriptor_header *) &uvc_format_mjpg, + (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, + (const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc, (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, - (const struct uvc_descriptor_header *) &uvc_color_matching, + (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, NULL, }; static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = { (const struct uvc_descriptor_header *) &uvc_input_header, - (const struct uvc_descriptor_header *) &uvc_format_yuv, + (const struct uvc_descriptor_header *) &uvcg_format_yuv.desc, (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, - (const struct uvc_descriptor_header *) &uvc_color_matching, - (const struct uvc_descriptor_header *) &uvc_format_mjpg, + (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, + (const struct uvc_descriptor_header *) &uvcg_format_mjpeg.desc, (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, - (const struct uvc_descriptor_header *) &uvc_color_matching, + (const struct uvc_descriptor_header *) &uvcg_color_matching.desc, NULL, }; @@ -387,6 +543,23 @@ webcam_bind(struct usb_composite_dev *cdev) uvc_opts->hs_streaming = uvc_hs_streaming_cls; uvc_opts->ss_streaming = uvc_ss_streaming_cls; + INIT_LIST_HEAD(&uvcg_format_yuv.fmt.frames); + list_add_tail(&uvcg_frame_ptr_yuv_360p.entry, &uvcg_format_yuv.fmt.frames); + list_add_tail(&uvcg_frame_ptr_yuv_720p.entry, &uvcg_format_yuv.fmt.frames); + uvcg_format_yuv.fmt.num_frames = 2; + + INIT_LIST_HEAD(&uvcg_format_mjpeg.fmt.frames); + list_add_tail(&uvcg_frame_ptr_mjpeg_360p.entry, &uvcg_format_mjpeg.fmt.frames); + list_add_tail(&uvcg_frame_ptr_mjpeg_720p.entry, &uvcg_format_mjpeg.fmt.frames); + uvcg_format_mjpeg.fmt.num_frames = 2; + + INIT_LIST_HEAD(&uvcg_streaming_header.formats); + list_add_tail(&uvcg_format_ptr_yuv.entry, &uvcg_streaming_header.formats); + list_add_tail(&uvcg_format_ptr_mjpeg.entry, &uvcg_streaming_header.formats); + uvcg_streaming_header.num_fmt = 2; + + uvc_opts->header = &uvcg_streaming_header; + /* Allocate string descriptor numbers ... note that string contents * can be overridden by the composite_dev glue. */ diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 30ea4a9d5301..e3bf17a98b38 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1924,7 +1924,7 @@ err_unprepare_fclk: return retval; } -static int at91udc_remove(struct platform_device *pdev) +static void at91udc_remove(struct platform_device *pdev) { struct at91_udc *udc = platform_get_drvdata(pdev); unsigned long flags; @@ -1932,8 +1932,11 @@ static int at91udc_remove(struct platform_device *pdev) DBG("remove\n"); usb_del_gadget_udc(&udc->gadget); - if (udc->driver) - return -EBUSY; + if (udc->driver) { + dev_err(&pdev->dev, + "Driver still in use but removing anyhow\n"); + return; + } spin_lock_irqsave(&udc->lock, flags); pullup(udc, 0); @@ -1943,8 +1946,6 @@ static int at91udc_remove(struct platform_device *pdev) remove_debug_file(udc); clk_unprepare(udc->fclk); clk_unprepare(udc->iclk); - - return 0; } #ifdef CONFIG_PM @@ -2001,7 +2002,7 @@ static int at91udc_resume(struct platform_device *pdev) static struct platform_driver at91_udc_driver = { .probe = at91udc_probe, - .remove = at91udc_remove, + .remove_new = at91udc_remove, .shutdown = at91udc_shutdown, .suspend = at91udc_suspend, .resume = at91udc_resume, diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 02b1bef5e22e..b76885d78e8a 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -94,7 +94,7 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf, inode_lock(file_inode(file)); list_for_each_entry_safe(req, tmp_req, queue, queue) { - len = snprintf(tmpbuf, sizeof(tmpbuf), + len = scnprintf(tmpbuf, sizeof(tmpbuf), "%8p %08x %c%c%c %5d %c%c%c\n", req->req.buf, req->req.length, req->req.no_interrupt ? 'i' : 'I', @@ -104,7 +104,6 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf, req->submitted ? 'F' : 'f', req->using_dma ? 'D' : 'd', req->last_transaction ? 'L' : 'l'); - len = min(len, sizeof(tmpbuf)); if (len > nbytes) break; diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-debug.h b/drivers/usb/gadget/udc/cdns2/cdns2-debug.h index be9ae0d28a40..f5f330004190 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-debug.h +++ b/drivers/usb/gadget/udc/cdns2/cdns2-debug.h @@ -16,34 +16,34 @@ static inline const char *cdns2_decode_usb_irq(char *str, size_t size, { int ret; - ret = snprintf(str, size, "usbirq: 0x%02x - ", usb_irq); + ret = scnprintf(str, size, "usbirq: 0x%02x - ", usb_irq); if (usb_irq & USBIRQ_SOF) - ret += snprintf(str + ret, size - ret, "SOF "); + ret += scnprintf(str + ret, size - ret, "SOF "); if (usb_irq & USBIRQ_SUTOK) - ret += snprintf(str + ret, size - ret, "SUTOK "); + ret += scnprintf(str + ret, size - ret, "SUTOK "); if (usb_irq & USBIRQ_SUDAV) - ret += snprintf(str + ret, size - ret, "SETUP "); + ret += scnprintf(str + ret, size - ret, "SETUP "); if (usb_irq & USBIRQ_SUSPEND) - ret += snprintf(str + ret, size - ret, "Suspend "); + ret += scnprintf(str + ret, size - ret, "Suspend "); if (usb_irq & USBIRQ_URESET) - ret += snprintf(str + ret, size - ret, "Reset "); + ret += scnprintf(str + ret, size - ret, "Reset "); if (usb_irq & USBIRQ_HSPEED) - ret += snprintf(str + ret, size - ret, "HS "); + ret += scnprintf(str + ret, size - ret, "HS "); if (usb_irq & USBIRQ_LPM) - ret += snprintf(str + ret, size - ret, "LPM "); + ret += scnprintf(str + ret, size - ret, "LPM "); - ret += snprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq); + ret += scnprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq); if (ext_irq & EXTIRQ_WAKEUP) - ret += snprintf(str + ret, size - ret, "Wakeup "); + ret += scnprintf(str + ret, size - ret, "Wakeup "); if (ext_irq & EXTIRQ_VBUSFAULT_FALL) - ret += snprintf(str + ret, size - ret, "VBUS_FALL "); + ret += scnprintf(str + ret, size - ret, "VBUS_FALL "); if (ext_irq & EXTIRQ_VBUSFAULT_RISE) - ret += snprintf(str + ret, size - ret, "VBUS_RISE "); + ret += scnprintf(str + ret, size - ret, "VBUS_RISE "); - if (ret >= size) - pr_info("CDNS2: buffer overflowed.\n"); + if (ret == size - 1) + pr_info("CDNS2: buffer may be truncated.\n"); return str; } @@ -54,28 +54,28 @@ static inline const char *cdns2_decode_dma_irq(char *str, size_t size, { int ret; - ret = snprintf(str, size, "ISTS: %08x, %s: %08x ", - ep_ists, ep_name, ep_sts); + ret = scnprintf(str, size, "ISTS: %08x, %s: %08x ", + ep_ists, ep_name, ep_sts); if (ep_sts & DMA_EP_STS_IOC) - ret += snprintf(str + ret, size - ret, "IOC "); + ret += scnprintf(str + ret, size - ret, "IOC "); if (ep_sts & DMA_EP_STS_ISP) - ret += snprintf(str + ret, size - ret, "ISP "); + ret += scnprintf(str + ret, size - ret, "ISP "); if (ep_sts & DMA_EP_STS_DESCMIS) - ret += snprintf(str + ret, size - ret, "DESCMIS "); + ret += scnprintf(str + ret, size - ret, "DESCMIS "); if (ep_sts & DMA_EP_STS_TRBERR) - ret += snprintf(str + ret, size - ret, "TRBERR "); + ret += scnprintf(str + ret, size - ret, "TRBERR "); if (ep_sts & DMA_EP_STS_OUTSMM) - ret += snprintf(str + ret, size - ret, "OUTSMM "); + ret += scnprintf(str + ret, size - ret, "OUTSMM "); if (ep_sts & DMA_EP_STS_ISOERR) - ret += snprintf(str + ret, size - ret, "ISOERR "); + ret += scnprintf(str + ret, size - ret, "ISOERR "); if (ep_sts & DMA_EP_STS_DBUSY) - ret += snprintf(str + ret, size - ret, "DBUSY "); + ret += scnprintf(str + ret, size - ret, "DBUSY "); if (DMA_EP_STS_CCS(ep_sts)) - ret += snprintf(str + ret, size - ret, "CCS "); + ret += scnprintf(str + ret, size - ret, "CCS "); - if (ret >= size) - pr_info("CDNS2: buffer overflowed.\n"); + if (ret == size - 1) + pr_info("CDNS2: buffer may be truncated.\n"); return str; } @@ -105,43 +105,43 @@ static inline const char *cdns2_raw_ring(struct cdns2_endpoint *pep, int ret; int i; - ret = snprintf(str, size, "\n\t\tTR for %s:", pep->name); + ret = scnprintf(str, size, "\n\t\tTR for %s:", pep->name); trb = &trbs[ring->dequeue]; dma = cdns2_trb_virt_to_dma(pep, trb); - ret += snprintf(str + ret, size - ret, - "\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n", - ring->dequeue, trb, &dma); + ret += scnprintf(str + ret, size - ret, + "\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n", + ring->dequeue, trb, &dma); trb = &trbs[ring->enqueue]; dma = cdns2_trb_virt_to_dma(pep, trb); - ret += snprintf(str + ret, size - ret, - "\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n", - ring->enqueue, trb, &dma); + ret += scnprintf(str + ret, size - ret, + "\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n", + ring->enqueue, trb, &dma); - ret += snprintf(str + ret, size - ret, - "\t\tfree trbs: %d, CCS=%d, PCS=%d\n", - ring->free_trbs, ring->ccs, ring->pcs); + ret += scnprintf(str + ret, size - ret, + "\t\tfree trbs: %d, CCS=%d, PCS=%d\n", + ring->free_trbs, ring->ccs, ring->pcs); if (TRBS_PER_SEGMENT > 40) { - ret += snprintf(str + ret, size - ret, - "\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT); + ret += scnprintf(str + ret, size - ret, + "\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT); return str; } dma = ring->dma; for (i = 0; i < TRBS_PER_SEGMENT; ++i) { trb = &trbs[i]; - ret += snprintf(str + ret, size - ret, - "\t\t@%pad %08x %08x %08x\n", &dma, - le32_to_cpu(trb->buffer), - le32_to_cpu(trb->length), - le32_to_cpu(trb->control)); + ret += scnprintf(str + ret, size - ret, + "\t\t@%pad %08x %08x %08x\n", &dma, + le32_to_cpu(trb->buffer), + le32_to_cpu(trb->length), + le32_to_cpu(trb->control)); dma += sizeof(*trb); } - if (ret >= size) - pr_info("CDNS2: buffer overflowed.\n"); + if (ret == size - 1) + pr_info("CDNS2: buffer may be truncated.\n"); return str; } @@ -166,36 +166,36 @@ static inline const char *cdns2_decode_trb(char *str, size_t size, u32 flags, switch (type) { case TRB_LINK: - ret = snprintf(str, size, - "LINK %08x type '%s' flags %c:%c:%c%c:%c", - buffer, cdns2_trb_type_string(type), - flags & TRB_CYCLE ? 'C' : 'c', - flags & TRB_TOGGLE ? 'T' : 't', - flags & TRB_CHAIN ? 'C' : 'c', - flags & TRB_CHAIN ? 'H' : 'h', - flags & TRB_IOC ? 'I' : 'i'); + ret = scnprintf(str, size, + "LINK %08x type '%s' flags %c:%c:%c%c:%c", + buffer, cdns2_trb_type_string(type), + flags & TRB_CYCLE ? 'C' : 'c', + flags & TRB_TOGGLE ? 'T' : 't', + flags & TRB_CHAIN ? 'C' : 'c', + flags & TRB_CHAIN ? 'H' : 'h', + flags & TRB_IOC ? 'I' : 'i'); break; case TRB_NORMAL: - ret = snprintf(str, size, - "type: '%s', Buffer: %08x, length: %ld, burst len: %ld, " - "flags %c:%c:%c%c:%c", - cdns2_trb_type_string(type), - buffer, TRB_LEN(length), - TRB_FIELD_TO_BURST(length), - flags & TRB_CYCLE ? 'C' : 'c', - flags & TRB_ISP ? 'I' : 'i', - flags & TRB_CHAIN ? 'C' : 'c', - flags & TRB_CHAIN ? 'H' : 'h', - flags & TRB_IOC ? 'I' : 'i'); + ret = scnprintf(str, size, + "type: '%s', Buffer: %08x, length: %ld, burst len: %ld, " + "flags %c:%c:%c%c:%c", + cdns2_trb_type_string(type), + buffer, TRB_LEN(length), + TRB_FIELD_TO_BURST(length), + flags & TRB_CYCLE ? 'C' : 'c', + flags & TRB_ISP ? 'I' : 'i', + flags & TRB_CHAIN ? 'C' : 'c', + flags & TRB_CHAIN ? 'H' : 'h', + flags & TRB_IOC ? 'I' : 'i'); break; default: - ret = snprintf(str, size, "type '%s' -> raw %08x %08x %08x", - cdns2_trb_type_string(type), - buffer, length, flags); + ret = scnprintf(str, size, "type '%s' -> raw %08x %08x %08x", + cdns2_trb_type_string(type), + buffer, length, flags); } - if (ret >= size) - pr_info("CDNS2: buffer overflowed.\n"); + if (ret == size - 1) + pr_info("CDNS2: buffer may be truncated.\n"); return str; } diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 2693a10eb0c7..e8042c158f6d 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1360,7 +1360,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, udc->ep0_dir = USB_DIR_IN; /* Borrow the per device status_req */ req = udc->status_req; - /* Fill in the reqest structure */ + /* Fill in the request structure */ *((u16 *) req->req.buf) = cpu_to_le16(tmp); req->ep = ep; @@ -2532,15 +2532,18 @@ err_kfree: /* Driver removal function * Free resources and finish pending transactions */ -static int fsl_udc_remove(struct platform_device *pdev) +static void fsl_udc_remove(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); DECLARE_COMPLETION_ONSTACK(done); - if (!udc_controller) - return -ENODEV; + if (!udc_controller) { + dev_err(&pdev->dev, + "Driver still in use but removing anyhow\n"); + return; + } udc_controller->done = &done; usb_del_gadget_udc(&udc_controller->gadget); @@ -2568,8 +2571,6 @@ static int fsl_udc_remove(struct platform_device *pdev) */ if (pdata->exit) pdata->exit(pdev); - - return 0; } /*----------------------------------------------------------------- @@ -2667,7 +2668,7 @@ static const struct platform_device_id fsl_udc_devtype[] = { MODULE_DEVICE_TABLE(platform, fsl_udc_devtype); static struct platform_driver udc_driver = { .probe = fsl_udc_probe, - .remove = fsl_udc_remove, + .remove_new = fsl_udc_remove, .id_table = fsl_udc_devtype, /* these suspend and resume are not usb suspend and resume */ .suspend = fsl_udc_suspend, diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index c6dfa7cccc11..fb901be5dac1 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2089,15 +2089,18 @@ static void gr_ep_remove(struct gr_udc *dev, int num, int is_in) ep->tailbuf, ep->tailbuf_paddr); } -static int gr_remove(struct platform_device *pdev) +static void gr_remove(struct platform_device *pdev) { struct gr_udc *dev = platform_get_drvdata(pdev); int i; if (dev->added) usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */ - if (dev->driver) - return -EBUSY; + if (dev->driver) { + dev_err(&pdev->dev, + "Driver still in use but removing anyhow\n"); + return; + } gr_dfs_delete(dev); dma_pool_destroy(dev->desc_pool); @@ -2110,8 +2113,6 @@ static int gr_remove(struct platform_device *pdev) gr_ep_remove(dev, i, 0); for (i = 0; i < dev->nepi; i++) gr_ep_remove(dev, i, 1); - - return 0; } static int gr_request_irq(struct gr_udc *dev, int irq) { @@ -2248,7 +2249,7 @@ static struct platform_driver gr_driver = { .of_match_table = gr_match, }, .probe = gr_probe, - .remove = gr_remove, + .remove_new = gr_remove, }; module_platform_driver(gr_driver); diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index a917cc9a32ab..d5f29f8fe481 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3174,13 +3174,16 @@ i2c_fail: return retval; } -static int lpc32xx_udc_remove(struct platform_device *pdev) +static void lpc32xx_udc_remove(struct platform_device *pdev) { struct lpc32xx_udc *udc = platform_get_drvdata(pdev); usb_del_gadget_udc(&udc->gadget); - if (udc->driver) - return -EBUSY; + if (udc->driver) { + dev_err(&pdev->dev, + "Driver still in use but removing anyhow\n"); + return; + } udc_clk_set(udc, 1); udc_disable(udc); @@ -3194,8 +3197,6 @@ static int lpc32xx_udc_remove(struct platform_device *pdev) udc->udca_v_base, udc->udca_p_base); clk_disable_unprepare(udc->usb_slv_clk); - - return 0; } #ifdef CONFIG_PM @@ -3255,7 +3256,7 @@ MODULE_DEVICE_TABLE(of, lpc32xx_udc_of_match); static struct platform_driver lpc32xx_udc_driver = { .probe = lpc32xx_udc_probe, - .remove = lpc32xx_udc_remove, + .remove_new = lpc32xx_udc_remove, .shutdown = lpc32xx_udc_shutdown, .suspend = lpc32xx_udc_suspend, .resume = lpc32xx_udc_resume, diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index d888dcda2bc8..78308b64955d 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -1451,7 +1451,7 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) req = udc->status_req; - /* fill in the reqest structure */ + /* fill in the request structure */ if (empty == false) { *((u16 *) req->req.buf) = cpu_to_le16(status); req->req.length = 2; diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index 4f8617210d85..169f72665739 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -274,7 +274,6 @@ struct pch_udc_cfg_data { * @td_data: for data request * @dev: reference to device struct * @offset_addr: offset address of ep register - * @desc: for this ep * @queue: queue for requests * @num: endpoint number * @in: endpoint is IN diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index df0551ecc810..1ac26cb49ecf 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -2397,12 +2397,15 @@ static void pxa25x_udc_shutdown(struct platform_device *_dev) pullup_off(); } -static int pxa25x_udc_remove(struct platform_device *pdev) +static void pxa25x_udc_remove(struct platform_device *pdev) { struct pxa25x_udc *dev = platform_get_drvdata(pdev); - if (dev->driver) - return -EBUSY; + if (dev->driver) { + dev_err(&pdev->dev, + "Driver still in use but removing anyhow\n"); + return; + } usb_del_gadget_udc(&dev->gadget); dev->pullup = 0; @@ -2414,7 +2417,6 @@ static int pxa25x_udc_remove(struct platform_device *pdev) dev->transceiver = NULL; the_controller = NULL; - return 0; } /*-------------------------------------------------------------------------*/ @@ -2472,7 +2474,7 @@ static int pxa25x_udc_resume(struct platform_device *dev) static struct platform_driver udc_driver = { .shutdown = pxa25x_udc_shutdown, .probe = pxa25x_udc_probe, - .remove = pxa25x_udc_remove, + .remove_new = pxa25x_udc_remove, .suspend = pxa25x_udc_suspend, .resume = pxa25x_udc_resume, .driver = { diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index d152d72de126..9fe4f48b1898 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1158,12 +1158,12 @@ dump_eps(struct usb_hcd *hcd) end = dp + sizeof(ubuf); *dp = '\0'; list_for_each_entry(urb, &ep->urb_list, urb_list) { - ret = snprintf(dp, end - dp, " %p(%d.%s %d/%d)", urb, - usb_pipetype(urb->pipe), - usb_urb_dir_in(urb) ? "IN" : "OUT", - urb->actual_length, - urb->transfer_buffer_length); - if (ret < 0 || ret >= end - dp) + ret = scnprintf(dp, end - dp, " %p(%d.%s %d/%d)", urb, + usb_pipetype(urb->pipe), + usb_urb_dir_in(urb) ? "IN" : "OUT", + urb->actual_length, + urb->transfer_buffer_length); + if (ret == end - dp - 1) break; /* error or buffer full */ dp += ret; } @@ -1255,9 +1255,9 @@ max3421_handle_irqs(struct usb_hcd *hcd) end = sbuf + sizeof(sbuf); *dp = '\0'; for (i = 0; i < 16; ++i) { - int ret = snprintf(dp, end - dp, " %lu", - max3421_hcd->err_stat[i]); - if (ret < 0 || ret >= end - dp) + int ret = scnprintf(dp, end - dp, " %lu", + max3421_hcd->err_stat[i]); + if (ret == end - dp - 1) break; /* error or buffer full */ dp += ret; } diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index b40d9238d447..d82935d31126 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -6,9 +6,24 @@ * * Author: Lu Baolu <baolu.lu@linux.intel.com> */ +#include <linux/bug.h> +#include <linux/device.h> #include <linux/dma-mapping.h> -#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/kstrtox.h> +#include <linux/list.h> #include <linux/nls.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#include <linux/io-64-nonatomic-lo-hi.h> + +#include <asm/byteorder.h> #include "xhci.h" #include "xhci-trace.h" @@ -28,7 +43,7 @@ static void dbc_ring_free(struct device *dev, struct xhci_ring *ring) if (!ring) return; - if (ring->first_seg && ring->first_seg->trbs) { + if (ring->first_seg) { dma_free_coherent(dev, TRB_SEGMENT_SIZE, ring->first_seg->trbs, ring->first_seg->dma); @@ -374,13 +389,13 @@ static void xhci_dbc_eps_init(struct xhci_dbc *dbc) static void xhci_dbc_eps_exit(struct xhci_dbc *dbc) { - memset(dbc->eps, 0, sizeof(struct dbc_ep) * ARRAY_SIZE(dbc->eps)); + memset(dbc->eps, 0, sizeof_field(struct xhci_dbc, eps)); } static int dbc_erst_alloc(struct device *dev, struct xhci_ring *evt_ring, struct xhci_erst *erst, gfp_t flags) { - erst->entries = dma_alloc_coherent(dev, sizeof(struct xhci_erst_entry), + erst->entries = dma_alloc_coherent(dev, sizeof(*erst->entries), &erst->erst_dma_addr, flags); if (!erst->entries) return -ENOMEM; @@ -394,9 +409,8 @@ static int dbc_erst_alloc(struct device *dev, struct xhci_ring *evt_ring, static void dbc_erst_free(struct device *dev, struct xhci_erst *erst) { - if (erst->entries) - dma_free_coherent(dev, sizeof(struct xhci_erst_entry), - erst->entries, erst->erst_dma_addr); + dma_free_coherent(dev, sizeof(*erst->entries), erst->entries, + erst->erst_dma_addr); erst->entries = NULL; } @@ -495,7 +509,7 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags) goto ctx_fail; /* Allocate the string table: */ - dbc->string_size = sizeof(struct dbc_str_descs); + dbc->string_size = sizeof(*dbc->string); dbc->string = dma_alloc_coherent(dev, dbc->string_size, &dbc->string_dma, flags); if (!dbc->string) @@ -543,11 +557,8 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc) xhci_dbc_eps_exit(dbc); - if (dbc->string) { - dma_free_coherent(dbc->dev, dbc->string_size, - dbc->string, dbc->string_dma); - dbc->string = NULL; - } + dma_free_coherent(dbc->dev, dbc->string_size, dbc->string, dbc->string_dma); + dbc->string = NULL; dbc_free_ctx(dbc->dev, dbc->ctx); dbc->ctx = NULL; @@ -597,7 +608,7 @@ static int xhci_do_dbc_start(struct xhci_dbc *dbc) static int xhci_do_dbc_stop(struct xhci_dbc *dbc) { if (dbc->state == DS_DISABLED) - return -1; + return -EINVAL; writel(0, &dbc->regs->control); dbc->state = DS_DISABLED; @@ -650,11 +661,11 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) spin_lock_irqsave(&dbc->lock, flags); ret = xhci_do_dbc_stop(dbc); spin_unlock_irqrestore(&dbc->lock, flags); + if (ret) + return; - if (!ret) { - xhci_dbc_mem_cleanup(dbc); - pm_runtime_put_sync(dbc->dev); /* note, was self.controller */ - } + xhci_dbc_mem_cleanup(dbc); + pm_runtime_put_sync(dbc->dev); /* note, was self.controller */ } static void @@ -914,41 +925,29 @@ static void xhci_dbc_handle_events(struct work_struct *work) mod_delayed_work(system_wq, &dbc->event_work, 1); } +static const char * const dbc_state_strings[DS_MAX] = { + [DS_DISABLED] = "disabled", + [DS_INITIALIZED] = "initialized", + [DS_ENABLED] = "enabled", + [DS_CONNECTED] = "connected", + [DS_CONFIGURED] = "configured", + [DS_STALLED] = "stalled", +}; + static ssize_t dbc_show(struct device *dev, struct device_attribute *attr, char *buf) { - const char *p; struct xhci_dbc *dbc; struct xhci_hcd *xhci; xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - switch (dbc->state) { - case DS_DISABLED: - p = "disabled"; - break; - case DS_INITIALIZED: - p = "initialized"; - break; - case DS_ENABLED: - p = "enabled"; - break; - case DS_CONNECTED: - p = "connected"; - break; - case DS_CONFIGURED: - p = "configured"; - break; - case DS_STALLED: - p = "stalled"; - break; - default: - p = "unknown"; - } + if (dbc->state >= ARRAY_SIZE(dbc_state_strings)) + return sysfs_emit(buf, "unknown\n"); - return sprintf(buf, "%s\n", p); + return sysfs_emit(buf, "%s\n", dbc_state_strings[dbc->state]); } static ssize_t dbc_store(struct device *dev, @@ -961,9 +960,9 @@ static ssize_t dbc_store(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - if (!strncmp(buf, "enable", 6)) + if (sysfs_streq(buf, "enable")) xhci_dbc_start(dbc); - else if (!strncmp(buf, "disable", 7)) + else if (sysfs_streq(buf, "disable")) xhci_dbc_stop(dbc); else return -EINVAL; @@ -981,7 +980,7 @@ static ssize_t dbc_idVendor_show(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - return sprintf(buf, "%04x\n", dbc->idVendor); + return sysfs_emit(buf, "%04x\n", dbc->idVendor); } static ssize_t dbc_idVendor_store(struct device *dev, @@ -993,9 +992,11 @@ static ssize_t dbc_idVendor_store(struct device *dev, void __iomem *ptr; u16 value; u32 dev_info; + int ret; - if (kstrtou16(buf, 0, &value)) - return -EINVAL; + ret = kstrtou16(buf, 0, &value); + if (ret) + return ret; xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; @@ -1021,7 +1022,7 @@ static ssize_t dbc_idProduct_show(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - return sprintf(buf, "%04x\n", dbc->idProduct); + return sysfs_emit(buf, "%04x\n", dbc->idProduct); } static ssize_t dbc_idProduct_store(struct device *dev, @@ -1033,9 +1034,11 @@ static ssize_t dbc_idProduct_store(struct device *dev, void __iomem *ptr; u32 dev_info; u16 value; + int ret; - if (kstrtou16(buf, 0, &value)) - return -EINVAL; + ret = kstrtou16(buf, 0, &value); + if (ret) + return ret; xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; @@ -1060,7 +1063,7 @@ static ssize_t dbc_bcdDevice_show(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - return sprintf(buf, "%04x\n", dbc->bcdDevice); + return sysfs_emit(buf, "%04x\n", dbc->bcdDevice); } static ssize_t dbc_bcdDevice_store(struct device *dev, @@ -1072,9 +1075,11 @@ static ssize_t dbc_bcdDevice_store(struct device *dev, void __iomem *ptr; u32 dev_info; u16 value; + int ret; - if (kstrtou16(buf, 0, &value)) - return -EINVAL; + ret = kstrtou16(buf, 0, &value); + if (ret) + return ret; xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; @@ -1100,7 +1105,7 @@ static ssize_t dbc_bInterfaceProtocol_show(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - return sprintf(buf, "%02x\n", dbc->bInterfaceProtocol); + return sysfs_emit(buf, "%02x\n", dbc->bInterfaceProtocol); } static ssize_t dbc_bInterfaceProtocol_store(struct device *dev, @@ -1114,9 +1119,13 @@ static ssize_t dbc_bInterfaceProtocol_store(struct device *dev, u8 value; int ret; - /* bInterfaceProtocol is 8 bit, but xhci only supports values 0 and 1 */ + /* bInterfaceProtocol is 8 bit, but... */ ret = kstrtou8(buf, 0, &value); - if (ret || value > 1) + if (ret) + return ret; + + /* ...xhci only supports values 0 and 1 */ + if (value > 1) return -EINVAL; xhci = hcd_to_xhci(dev_get_drvdata(dev)); @@ -1139,7 +1148,7 @@ static DEVICE_ATTR_RW(dbc_idProduct); static DEVICE_ATTR_RW(dbc_bcdDevice); static DEVICE_ATTR_RW(dbc_bInterfaceProtocol); -static struct attribute *dbc_dev_attributes[] = { +static struct attribute *dbc_dev_attrs[] = { &dev_attr_dbc.attr, &dev_attr_dbc_idVendor.attr, &dev_attr_dbc_idProduct.attr, @@ -1147,10 +1156,7 @@ static struct attribute *dbc_dev_attributes[] = { &dev_attr_dbc_bInterfaceProtocol.attr, NULL }; - -static const struct attribute_group dbc_dev_attrib_grp = { - .attrs = dbc_dev_attributes, -}; +ATTRIBUTE_GROUPS(dbc_dev); struct xhci_dbc * xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *driver) @@ -1176,7 +1182,7 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver * INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events); spin_lock_init(&dbc->lock); - ret = sysfs_create_group(&dev->kobj, &dbc_dev_attrib_grp); + ret = sysfs_create_groups(&dev->kobj, dbc_dev_groups); if (ret) goto err; @@ -1195,7 +1201,7 @@ void xhci_dbc_remove(struct xhci_dbc *dbc) xhci_dbc_stop(dbc); /* remove sysfs files */ - sysfs_remove_group(&dbc->dev->kobj, &dbc_dev_attrib_grp); + sysfs_remove_groups(&dbc->dev->kobj, dbc_dev_groups); kfree(dbc); } diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index 51a7ab3ba0ca..e39e3ae1677a 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -82,6 +82,7 @@ enum dbc_state { DS_CONNECTED, DS_CONFIGURED, DS_STALLED, + DS_MAX }; struct dbc_ep { diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index 6d142cd61bd6..f8ba15e7c225 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -693,7 +693,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) "command-ring", xhci->debugfs_root); - xhci_debugfs_create_ring_dir(xhci, &xhci->interrupter->event_ring, + xhci_debugfs_create_ring_dir(xhci, &xhci->interrupters[0]->event_ring, "event-ring", xhci->debugfs_root); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 62116586848b..a7716202a8dd 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -323,6 +323,7 @@ void xhci_initialize_ring_info(struct xhci_ring *ring, */ ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1; } +EXPORT_SYMBOL_GPL(xhci_initialize_ring_info); /* Allocate segments and link them for a ring */ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, @@ -1739,6 +1740,8 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, } command->status = 0; + /* set default timeout to 5000 ms */ + command->timeout_ms = XHCI_CMD_DEFAULT_TIMEOUT; INIT_LIST_HEAD(&command->cmd_list); return command; } @@ -1853,6 +1856,31 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) kfree(ir); } +void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrupter *ir) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + unsigned int intr_num; + + spin_lock_irq(&xhci->lock); + + /* interrupter 0 is primary interrupter, don't touch it */ + if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters) { + xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n"); + spin_unlock_irq(&xhci->lock); + return; + } + + intr_num = ir->intr_num; + + xhci_remove_interrupter(xhci, ir); + xhci->interrupters[intr_num] = NULL; + + spin_unlock_irq(&xhci->lock); + + xhci_free_interrupter(xhci, ir); +} +EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter); + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; @@ -1860,10 +1888,14 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) cancel_delayed_work_sync(&xhci->cmd_timer); - xhci_remove_interrupter(xhci, xhci->interrupter); - xhci_free_interrupter(xhci, xhci->interrupter); - xhci->interrupter = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring"); + for (i = 0; i < xhci->max_interrupters; i++) { + if (xhci->interrupters[i]) { + xhci_remove_interrupter(xhci, xhci->interrupters[i]); + xhci_free_interrupter(xhci, xhci->interrupters[i]); + xhci->interrupters[i] = NULL; + } + } + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed interrupters"); if (xhci->cmd_ring) xhci_ring_free(xhci, xhci->cmd_ring); @@ -1933,6 +1965,7 @@ no_bw: for (i = 0; i < xhci->num_port_caps; i++) kfree(xhci->port_caps[i].psi); kfree(xhci->port_caps); + kfree(xhci->interrupters); xhci->num_port_caps = 0; xhci->usb2_rhub.ports = NULL; @@ -1941,6 +1974,7 @@ no_bw: xhci->rh_bw = NULL; xhci->ext_caps = NULL; xhci->port_caps = NULL; + xhci->interrupters = NULL; xhci->page_size = 0; xhci->page_shift = 0; @@ -2246,18 +2280,20 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) } static struct xhci_interrupter * -xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags) +xhci_alloc_interrupter(struct xhci_hcd *xhci, int segs, gfp_t flags) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; struct xhci_interrupter *ir; - unsigned int num_segs; + unsigned int num_segs = segs; int ret; ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); if (!ir) return NULL; - num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2), + /* number of ring segments should be greater than 0 */ + if (segs <= 0) + num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2), ERST_MAX_SEGS); ir->event_ring = xhci_ring_alloc(xhci, num_segs, 1, TYPE_EVENT, 0, @@ -2286,12 +2322,19 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, u64 erst_base; u32 erst_size; - if (intr_num > xhci->max_interrupters) { + if (intr_num >= xhci->max_interrupters) { xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n", intr_num, xhci->max_interrupters); return -EINVAL; } + if (xhci->interrupters[intr_num]) { + xhci_warn(xhci, "Interrupter %d\n already set up", intr_num); + return -EINVAL; + } + + xhci->interrupters[intr_num] = ir; + ir->intr_num = intr_num; ir->ir_set = &xhci->run_regs->ir_set[intr_num]; /* set ERST count with the number of entries in the segment table */ @@ -2311,10 +2354,52 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, return 0; } +struct xhci_interrupter * +xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_interrupter *ir; + unsigned int i; + int err = -ENOSPC; + + if (!xhci->interrupters || xhci->max_interrupters <= 1) + return NULL; + + ir = xhci_alloc_interrupter(xhci, num_seg, GFP_KERNEL); + if (!ir) + return NULL; + + spin_lock_irq(&xhci->lock); + + /* Find available secondary interrupter, interrupter 0 is reserved for primary */ + for (i = 1; i < xhci->max_interrupters; i++) { + if (xhci->interrupters[i] == NULL) { + err = xhci_add_interrupter(xhci, ir, i); + break; + } + } + + spin_unlock_irq(&xhci->lock); + + if (err) { + xhci_warn(xhci, "Failed to add secondary interrupter, max interrupters %d\n", + xhci->max_interrupters); + xhci_free_interrupter(xhci, ir); + return NULL; + } + + xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n", + i, xhci->max_interrupters); + + return ir; +} +EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter); + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { - dma_addr_t dma; + struct xhci_interrupter *ir; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + dma_addr_t dma; unsigned int val, val2; u64 val_64; u32 page_size, temp; @@ -2438,11 +2523,14 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* Allocate and set up primary interrupter 0 with an event ring. */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring"); - xhci->interrupter = xhci_alloc_interrupter(xhci, flags); - if (!xhci->interrupter) + xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters), + flags, dev_to_node(dev)); + + ir = xhci_alloc_interrupter(xhci, 0, flags); + if (!ir) goto fail; - if (xhci_add_interrupter(xhci, xhci->interrupter, 0)) + if (xhci_add_interrupter(xhci, ir, 0)) goto fail; xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index bbdf1b0b7be1..3252e3d2d79c 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -7,6 +7,7 @@ * Chunfeng Yun <chunfeng.yun@mediatek.com> */ +#include <linux/bitfield.h> #include <linux/dma-mapping.h> #include <linux/iopoll.h> #include <linux/kernel.h> @@ -73,6 +74,9 @@ #define FRMCNT_LEV1_RANG (0x12b << 8) #define FRMCNT_LEV1_RANG_MASK GENMASK(19, 8) +#define HSCH_CFG1 0x960 +#define SCH3_RXFIFO_DEPTH_MASK GENMASK(21, 20) + #define SS_GEN2_EOF_CFG 0x990 #define SSG2EOF_OFFSET 0x3c @@ -114,6 +118,8 @@ #define SSC_IP_SLEEP_EN BIT(4) #define SSC_SPM_INT_EN BIT(1) +#define SCH_FIFO_TO_KB(x) ((x) >> 10) + enum ssusb_uwk_vers { SSUSB_UWK_V1 = 1, SSUSB_UWK_V2, @@ -165,6 +171,35 @@ static void xhci_mtk_set_frame_interval(struct xhci_hcd_mtk *mtk) writel(value, hcd->regs + SS_GEN2_EOF_CFG); } +/* + * workaround: usb3.2 gen1 isoc rx hw issue + * host send out unexpected ACK afer device fininsh a burst transfer with + * a short packet. + */ +static void xhci_mtk_rxfifo_depth_set(struct xhci_hcd_mtk *mtk) +{ + struct usb_hcd *hcd = mtk->hcd; + u32 value; + + if (!mtk->rxfifo_depth) + return; + + value = readl(hcd->regs + HSCH_CFG1); + value &= ~SCH3_RXFIFO_DEPTH_MASK; + value |= FIELD_PREP(SCH3_RXFIFO_DEPTH_MASK, + SCH_FIFO_TO_KB(mtk->rxfifo_depth) - 1); + writel(value, hcd->regs + HSCH_CFG1); +} + +static void xhci_mtk_init_quirk(struct xhci_hcd_mtk *mtk) +{ + /* workaround only for mt8195 */ + xhci_mtk_set_frame_interval(mtk); + + /* workaround for SoCs using SSUSB about before IPM v1.6.0 */ + xhci_mtk_rxfifo_depth_set(mtk); +} + static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk) { struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs; @@ -448,8 +483,7 @@ static int xhci_mtk_setup(struct usb_hcd *hcd) if (ret) return ret; - /* workaround only for mt8195 */ - xhci_mtk_set_frame_interval(mtk); + xhci_mtk_init_quirk(mtk); } ret = xhci_gen_setup(hcd, xhci_mtk_quirks); @@ -527,6 +561,8 @@ static int xhci_mtk_probe(struct platform_device *pdev) of_property_read_u32(node, "mediatek,u2p-dis-msk", &mtk->u2p_dis_msk); + of_property_read_u32(node, "rx-fifo-depth", &mtk->rxfifo_depth); + ret = usb_wakeup_of_property_parse(mtk, node); if (ret) { dev_err(dev, "failed to parse uwk property\n"); diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index 39f7ae7d3087..f5e2bd66bb1b 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -171,6 +171,8 @@ struct xhci_hcd_mtk { struct regmap *uwk; u32 uwk_reg_base; u32 uwk_vers; + /* quirk */ + u32 rxfifo_depth; }; static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index d6fc08e5db8f..b534ca9752be 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -95,10 +95,9 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) if (hcd->msix_enabled) { struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - int i; - for (i = 0; i < xhci->msix_count; i++) - synchronize_irq(pci_irq_vector(pdev, i)); + /* for now, the driver only supports one primary interrupter */ + synchronize_irq(pci_irq_vector(pdev, 0)); } } @@ -112,100 +111,18 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci) if (hcd->irq > 0) return; - if (hcd->msix_enabled) { - int i; - - for (i = 0; i < xhci->msix_count; i++) - free_irq(pci_irq_vector(pdev, i), xhci_to_hcd(xhci)); - } else { - free_irq(pci_irq_vector(pdev, 0), xhci_to_hcd(xhci)); - } - + free_irq(pci_irq_vector(pdev, 0), xhci_to_hcd(xhci)); pci_free_irq_vectors(pdev); hcd->msix_enabled = 0; } -/* - * Set up MSI - */ -static int xhci_setup_msi(struct xhci_hcd *xhci) -{ - int ret; - /* - * TODO:Check with MSI Soc for sysdev - */ - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - - ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); - if (ret < 0) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "failed to allocate MSI entry"); - return ret; - } - - ret = request_irq(pdev->irq, xhci_msi_irq, - 0, "xhci_hcd", xhci_to_hcd(xhci)); - if (ret) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "disable MSI interrupt"); - pci_free_irq_vectors(pdev); - } - - return ret; -} - -/* - * Set up MSI-X - */ -static int xhci_setup_msix(struct xhci_hcd *xhci) -{ - int i, ret; - struct usb_hcd *hcd = xhci_to_hcd(xhci); - struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - - /* - * calculate number of msi-x vectors supported. - * - HCS_MAX_INTRS: the max number of interrupts the host can handle, - * with max number of interrupters based on the xhci HCSPARAMS1. - * - num_online_cpus: maximum msi-x vectors per CPUs core. - * Add additional 1 vector to ensure always available interrupt. - */ - xhci->msix_count = min(num_online_cpus() + 1, - HCS_MAX_INTRS(xhci->hcs_params1)); - - ret = pci_alloc_irq_vectors(pdev, xhci->msix_count, xhci->msix_count, - PCI_IRQ_MSIX); - if (ret < 0) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Failed to enable MSI-X"); - return ret; - } - - for (i = 0; i < xhci->msix_count; i++) { - ret = request_irq(pci_irq_vector(pdev, i), xhci_msi_irq, 0, - "xhci_hcd", xhci_to_hcd(xhci)); - if (ret) - goto disable_msix; - } - - hcd->msix_enabled = 1; - return ret; - -disable_msix: - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI-X interrupt"); - while (--i >= 0) - free_irq(pci_irq_vector(pdev, i), xhci_to_hcd(xhci)); - pci_free_irq_vectors(pdev); - return ret; -} - +/* Try enabling MSI-X with MSI and legacy IRQ as fallback */ static int xhci_try_enable_msi(struct usb_hcd *hcd) { + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev; int ret; - pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); /* * Some Fresco Logic host controllers advertise MSI, but fail to * generate interrupts. Don't even try to enable MSI. @@ -218,32 +135,53 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) free_irq(hcd->irq, hcd); hcd->irq = 0; - ret = xhci_setup_msix(xhci); - if (ret) - /* fall back to msi*/ - ret = xhci_setup_msi(xhci); + /* + * calculate number of MSI-X vectors supported. + * - HCS_MAX_INTRS: the max number of interrupts the host can handle, + * with max number of interrupters based on the xhci HCSPARAMS1. + * - num_online_cpus: maximum MSI-X vectors per CPUs core. + * Add additional 1 vector to ensure always available interrupt. + */ + xhci->nvecs = min(num_online_cpus() + 1, + HCS_MAX_INTRS(xhci->hcs_params1)); - if (!ret) { - hcd->msi_enabled = 1; - return 0; + /* TODO: Check with MSI Soc for sysdev */ + xhci->nvecs = pci_alloc_irq_vectors(pdev, 1, xhci->nvecs, + PCI_IRQ_MSIX | PCI_IRQ_MSI); + if (xhci->nvecs < 0) { + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "failed to allocate IRQ vectors"); + goto legacy_irq; } + ret = request_irq(pci_irq_vector(pdev, 0), xhci_msi_irq, 0, "xhci_hcd", + xhci_to_hcd(xhci)); + if (ret) + goto free_irq_vectors; + + hcd->msi_enabled = 1; + hcd->msix_enabled = pdev->msix_enabled; + return 0; + +free_irq_vectors: + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable %s interrupt", + pdev->msix_enabled ? "MSI-X" : "MSI"); + pci_free_irq_vectors(pdev); + +legacy_irq: if (!pdev->irq) { xhci_err(xhci, "No msi-x/msi found and no IRQ in BIOS\n"); return -EINVAL; } - legacy_irq: if (!strlen(hcd->irq_descr)) snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", hcd->driver->description, hcd->self.busnum); - /* fall back to legacy interrupt*/ - ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, - hcd->irq_descr, hcd); + /* fall back to legacy interrupt */ + ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, hcd->irq_descr, hcd); if (ret) { - xhci_err(xhci, "request interrupt %d failed\n", - pdev->irq); + xhci_err(xhci, "request interrupt %d failed\n", pdev->irq); return ret; } hcd->irq = pdev->irq; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 732cdeb73920..3d071b875308 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -131,6 +131,9 @@ static const struct of_device_id usb_xhci_of_match[] = { .compatible = "brcm,xhci-brcm-v2", .data = &xhci_plat_brcm, }, { + .compatible = "brcm,bcm2711-xhci", + .data = &xhci_plat_brcm, + }, { .compatible = "brcm,bcm7445-xhci", .data = &xhci_plat_brcm, }, @@ -250,6 +253,9 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s if (device_property_read_bool(tmpdev, "quirk-broken-port-ped")) xhci->quirks |= XHCI_BROKEN_PORT_PED; + if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk")) + xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK; + device_property_read_u32(tmpdev, "imod-interval-ns", &xhci->imod_interval); } @@ -433,7 +439,7 @@ void xhci_plat_remove(struct platform_device *dev) } EXPORT_SYMBOL_GPL(xhci_plat_remove); -static int __maybe_unused xhci_plat_suspend(struct device *dev) +static int xhci_plat_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -461,7 +467,7 @@ static int __maybe_unused xhci_plat_suspend(struct device *dev) return 0; } -static int __maybe_unused xhci_plat_resume(struct device *dev) +static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -483,7 +489,7 @@ static int __maybe_unused xhci_plat_resume(struct device *dev) if (ret) goto disable_clks; - ret = xhci_resume(xhci, PMSG_RESUME); + ret = xhci_resume(xhci, pmsg); if (ret) goto disable_clks; @@ -502,6 +508,16 @@ disable_clks: return ret; } +static int xhci_plat_resume(struct device *dev) +{ + return xhci_plat_resume_common(dev, PMSG_RESUME); +} + +static int xhci_plat_restore(struct device *dev) +{ + return xhci_plat_resume_common(dev, PMSG_RESTORE); +} + static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); @@ -524,7 +540,12 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) } const struct dev_pm_ops xhci_plat_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume) + .suspend = pm_sleep_ptr(xhci_plat_suspend), + .resume = pm_sleep_ptr(xhci_plat_resume), + .freeze = pm_sleep_ptr(xhci_plat_suspend), + .thaw = pm_sleep_ptr(xhci_plat_resume), + .poweroff = pm_sleep_ptr(xhci_plat_suspend), + .restore = pm_sleep_ptr(xhci_plat_restore), SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, xhci_plat_runtime_resume, diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f3b5e6345858..f0d8a607ff21 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -366,9 +366,10 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci) readl(&xhci->dba->doorbell[0]); } -static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay) +static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci) { - return mod_delayed_work(system_wq, &xhci->cmd_timer, delay); + return mod_delayed_work(system_wq, &xhci->cmd_timer, + msecs_to_jiffies(xhci->current_cmd->timeout_ms)); } static struct xhci_command *xhci_next_queued_cmd(struct xhci_hcd *xhci) @@ -412,7 +413,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) && !(xhci->xhc_state & XHCI_STATE_DYING)) { xhci->current_cmd = cur_cmd; - xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci); xhci_ring_cmd_db(xhci); } } @@ -1787,7 +1788,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, if (!list_is_singular(&xhci->cmd_list)) { xhci->current_cmd = list_first_entry(&cmd->cmd_list, struct xhci_command, cmd_list); - xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci); } else if (xhci->current_cmd == cmd) { xhci->current_cmd = NULL; } @@ -2375,6 +2376,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, /* handle completion code */ switch (trb_comp_code) { case COMP_SUCCESS: + /* Don't overwrite status if TD had an error, see xHCI 4.9.1 */ + if (td->error_mid_td) + break; if (remaining) { frame->status = short_framestatus; if (xhci->quirks & XHCI_TRUST_TX_LENGTH) @@ -2390,9 +2394,13 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, case COMP_BANDWIDTH_OVERRUN_ERROR: frame->status = -ECOMM; break; - case COMP_ISOCH_BUFFER_OVERRUN: case COMP_BABBLE_DETECTED_ERROR: + sum_trbs_for_length = true; + fallthrough; + case COMP_ISOCH_BUFFER_OVERRUN: frame->status = -EOVERFLOW; + if (ep_trb != td->last_trb) + td->error_mid_td = true; break; case COMP_INCOMPATIBLE_DEVICE_ERROR: case COMP_STALL_ERROR: @@ -2400,8 +2408,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, break; case COMP_USB_TRANSACTION_ERROR: frame->status = -EPROTO; + sum_trbs_for_length = true; if (ep_trb != td->last_trb) - return 0; + td->error_mid_td = true; break; case COMP_STOPPED: sum_trbs_for_length = true; @@ -2421,6 +2430,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, break; } + if (td->urb_length_set) + goto finish_td; + if (sum_trbs_for_length) frame->actual_length = sum_trb_lengths(xhci, ep->ring, ep_trb) + ep_trb_len - remaining; @@ -2429,6 +2441,14 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->urb->actual_length += frame->actual_length; +finish_td: + /* Don't give back TD yet if we encountered an error mid TD */ + if (td->error_mid_td && ep_trb != td->last_trb) { + xhci_dbg(xhci, "Error mid isoc TD, wait for final completion event\n"); + td->urb_length_set = true; + return 0; + } + return finish_td(xhci, ep, ep_ring, td, trb_comp_code); } @@ -2807,17 +2827,51 @@ static int handle_tx_event(struct xhci_hcd *xhci, } if (!ep_seg) { - if (!ep->skip || - !usb_endpoint_xfer_isoc(&td->urb->ep->desc)) { - /* Some host controllers give a spurious - * successful event after a short transfer. - * Ignore it. - */ - if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) && - ep_ring->last_td_was_short) { - ep_ring->last_td_was_short = false; - goto cleanup; + + if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) { + skip_isoc_td(xhci, td, ep, status); + goto cleanup; + } + + /* + * Some hosts give a spurious success event after a short + * transfer. Ignore it. + */ + if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) && + ep_ring->last_td_was_short) { + ep_ring->last_td_was_short = false; + goto cleanup; + } + + /* + * xhci 4.10.2 states isoc endpoints should continue + * processing the next TD if there was an error mid TD. + * So host like NEC don't generate an event for the last + * isoc TRB even if the IOC flag is set. + * xhci 4.9.1 states that if there are errors in mult-TRB + * TDs xHC should generate an error for that TRB, and if xHC + * proceeds to the next TD it should genete an event for + * any TRB with IOC flag on the way. Other host follow this. + * So this event might be for the next TD. + */ + if (td->error_mid_td && + !list_is_last(&td->td_list, &ep_ring->td_list)) { + struct xhci_td *td_next = list_next_entry(td, td_list); + + ep_seg = trb_in_td(xhci, td_next->start_seg, td_next->first_trb, + td_next->last_trb, ep_trb_dma, false); + if (ep_seg) { + /* give back previous TD, start handling new */ + xhci_dbg(xhci, "Missing TD completion event after mid TD error\n"); + ep_ring->dequeue = td->last_trb; + ep_ring->deq_seg = td->last_trb_seg; + inc_deq(xhci, ep_ring); + xhci_td_cleanup(xhci, td, ep_ring, td->status); + td = td_next; } + } + + if (!ep_seg) { /* HC is busted, give up! */ xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not " @@ -2829,9 +2883,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, ep_trb_dma, true); return -ESHUTDOWN; } - - skip_isoc_td(xhci, td, ep, status); - goto cleanup; } if (trb_comp_code == COMP_SHORT_PACKET) ep_ring->last_td_was_short = true; @@ -3060,7 +3111,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) writel(status, &xhci->op_regs->status); /* This is the handler of the primary interrupter */ - ir = xhci->interrupter; + ir = xhci->interrupters[0]; if (!hcd->msi_enabled) { u32 irq_pending; irq_pending = readl(&ir->ir_set->irq_pending); @@ -4287,7 +4338,7 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, /* if there are no other commands queued we start the timeout timer */ if (list_empty(&xhci->cmd_list)) { xhci->current_cmd = cmd; - xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); + xhci_mod_cmd_timer(xhci); } list_add_tail(&cmd->cmd_list, &xhci->cmd_list); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 884b0898d9c9..c057c42c36f4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -480,7 +480,7 @@ static int xhci_init(struct usb_hcd *hcd) static int xhci_run_finished(struct xhci_hcd *xhci) { - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir = xhci->interrupters[0]; unsigned long flags; u32 temp; @@ -532,7 +532,7 @@ int xhci_run(struct usb_hcd *hcd) u64 temp_64; int ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir = xhci->interrupters[0]; /* Start the xHCI host controller running only after the USB 2.0 roothub * is setup. */ @@ -596,7 +596,7 @@ void xhci_stop(struct usb_hcd *hcd) { u32 temp; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir = xhci->interrupters[0]; mutex_lock(&xhci->mutex); @@ -692,36 +692,51 @@ EXPORT_SYMBOL_GPL(xhci_shutdown); #ifdef CONFIG_PM static void xhci_save_registers(struct xhci_hcd *xhci) { - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir; + unsigned int i; xhci->s3.command = readl(&xhci->op_regs->command); xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification); xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci->s3.config_reg = readl(&xhci->op_regs->config_reg); - if (!ir) - return; + /* save both primary and all secondary interrupters */ + /* fixme, shold we lock to prevent race with remove secondary interrupter? */ + for (i = 0; i < xhci->max_interrupters; i++) { + ir = xhci->interrupters[i]; + if (!ir) + continue; - ir->s3_erst_size = readl(&ir->ir_set->erst_size); - ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); - ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); - ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); - ir->s3_irq_control = readl(&ir->ir_set->irq_control); + ir->s3_erst_size = readl(&ir->ir_set->erst_size); + ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); + ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); + ir->s3_irq_control = readl(&ir->ir_set->irq_control); + } } static void xhci_restore_registers(struct xhci_hcd *xhci) { - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir; + unsigned int i; writel(xhci->s3.command, &xhci->op_regs->command); writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification); xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); writel(xhci->s3.config_reg, &xhci->op_regs->config_reg); - writel(ir->s3_erst_size, &ir->ir_set->erst_size); - xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); - xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); - writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); - writel(ir->s3_irq_control, &ir->ir_set->irq_control); + + /* FIXME should we lock to protect against freeing of interrupters */ + for (i = 0; i < xhci->max_interrupters; i++) { + ir = xhci->interrupters[i]; + if (!ir) + continue; + + writel(ir->s3_erst_size, &ir->ir_set->erst_size); + xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); + xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); + writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); + writel(ir->s3_irq_control, &ir->ir_set->irq_control); + } } static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) @@ -1084,7 +1099,7 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - xhci_disable_interrupter(xhci->interrupter); + xhci_disable_interrupter(xhci->interrupters[0]); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); @@ -1438,10 +1453,8 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, * descriptor. If the usb_device's max packet size changes after that point, * we need to issue an evaluate context command and wait on it. */ -static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, - unsigned int ep_index, struct urb *urb, gfp_t mem_flags) +static int xhci_check_ep0_maxpacket(struct xhci_hcd *xhci, struct xhci_virt_device *vdev) { - struct xhci_container_ctx *out_ctx; struct xhci_input_control_ctx *ctrl_ctx; struct xhci_ep_ctx *ep_ctx; struct xhci_command *command; @@ -1449,11 +1462,15 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, int hw_max_packet_size; int ret = 0; - out_ctx = xhci->devs[slot_id]->out_ctx; - ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); + ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, 0); hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); - max_packet_size = usb_endpoint_maxp(&urb->dev->ep0.desc); - if (hw_max_packet_size != max_packet_size) { + max_packet_size = usb_endpoint_maxp(&vdev->udev->ep0.desc); + + if (hw_max_packet_size == max_packet_size) + return 0; + + switch (max_packet_size) { + case 8: case 16: case 32: case 64: case 9: xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, "Max Packet Size for ep 0 changed."); xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, @@ -1465,28 +1482,22 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, xhci_dbg_trace(xhci, trace_xhci_dbg_context_change, "Issuing evaluate context command."); - /* Set up the input context flags for the command */ - /* FIXME: This won't work if a non-default control endpoint - * changes max packet sizes. - */ - - command = xhci_alloc_command(xhci, true, mem_flags); + command = xhci_alloc_command(xhci, true, GFP_KERNEL); if (!command) return -ENOMEM; - command->in_ctx = xhci->devs[slot_id]->in_ctx; + command->in_ctx = vdev->in_ctx; ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx); if (!ctrl_ctx) { xhci_warn(xhci, "%s: Could not get input context, bad type.\n", __func__); ret = -ENOMEM; - goto command_cleanup; + break; } /* Set up the modified control endpoint 0 */ - xhci_endpoint_copy(xhci, xhci->devs[slot_id]->in_ctx, - xhci->devs[slot_id]->out_ctx, ep_index); + xhci_endpoint_copy(xhci, vdev->in_ctx, vdev->out_ctx, 0); - ep_ctx = xhci_get_ep_ctx(xhci, command->in_ctx, ep_index); + ep_ctx = xhci_get_ep_ctx(xhci, command->in_ctx, 0); ep_ctx->ep_info &= cpu_to_le32(~EP_STATE_MASK);/* must clear */ ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET_MASK); ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size)); @@ -1494,17 +1505,20 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, ctrl_ctx->add_flags = cpu_to_le32(EP0_FLAG); ctrl_ctx->drop_flags = 0; - ret = xhci_configure_endpoint(xhci, urb->dev, command, - true, false); - - /* Clean up the input context for later use by bandwidth - * functions. - */ + ret = xhci_configure_endpoint(xhci, vdev->udev, command, + true, false); + /* Clean up the input context for later use by bandwidth functions */ ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG); -command_cleanup: - kfree(command->completion); - kfree(command); + break; + default: + dev_dbg(&vdev->udev->dev, "incorrect max packet size %d for ep0\n", + max_packet_size); + return -EINVAL; } + + kfree(command->completion); + kfree(command); + return ret; } @@ -1522,24 +1536,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag struct urb_priv *urb_priv; int num_tds; - if (!urb) - return -EINVAL; - ret = xhci_check_args(hcd, urb->dev, urb->ep, - true, true, __func__); - if (ret <= 0) - return ret ? ret : -EINVAL; - - slot_id = urb->dev->slot_id; ep_index = xhci_get_endpoint_index(&urb->ep->desc); - ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state; - - if (!HCD_HW_ACCESSIBLE(hcd)) - return -ESHUTDOWN; - - if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) { - xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n"); - return -ENODEV; - } if (usb_endpoint_xfer_isoc(&urb->ep->desc)) num_tds = urb->number_of_packets; @@ -1561,22 +1558,27 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag trace_xhci_urb_enqueue(urb); - if (usb_endpoint_xfer_control(&urb->ep->desc)) { - /* Check to see if the max packet size for the default control - * endpoint changed during FS device enumeration - */ - if (urb->dev->speed == USB_SPEED_FULL) { - ret = xhci_check_maxpacket(xhci, slot_id, - ep_index, urb, mem_flags); - if (ret < 0) { - xhci_urb_free_priv(urb_priv); - urb->hcpriv = NULL; - return ret; - } - } + spin_lock_irqsave(&xhci->lock, flags); + + ret = xhci_check_args(hcd, urb->dev, urb->ep, + true, true, __func__); + if (ret <= 0) { + ret = ret ? ret : -EINVAL; + goto free_priv; } - spin_lock_irqsave(&xhci->lock, flags); + slot_id = urb->dev->slot_id; + + if (!HCD_HW_ACCESSIBLE(hcd)) { + ret = -ESHUTDOWN; + goto free_priv; + } + + if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) { + xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n"); + ret = -ENODEV; + goto free_priv; + } if (xhci->xhc_state & XHCI_STATE_DYING) { xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for non-responsive xHCI host.\n", @@ -1584,6 +1586,9 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag ret = -ESHUTDOWN; goto free_priv; } + + ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state; + if (*ep_state & (EP_GETTING_STREAMS | EP_GETTING_NO_STREAMS)) { xhci_warn(xhci, "WARN: Can't enqueue URB, ep in streams transition state %x\n", *ep_state); @@ -3087,6 +3092,9 @@ done: * of an endpoint that isn't in the halted state this function will issue a * configure endpoint command with the Drop and Add bits set for the target * endpoint. Refer to the additional note in xhci spcification section 4.6.8. + * + * vdev may be lost due to xHC restore error and re-initialization during S3/S4 + * resume. A new vdev will be allocated later by xhci_discover_or_reset_device() */ static void xhci_endpoint_reset(struct usb_hcd *hcd, @@ -3104,19 +3112,37 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd, int err; xhci = hcd_to_xhci(hcd); + ep_index = xhci_get_endpoint_index(&host_ep->desc); + + /* + * Usb core assumes a max packet value for ep0 on FS devices until the + * real value is read from the descriptor. Core resets Ep0 if values + * mismatch. Reconfigure the xhci ep0 endpoint context here in that case + */ + if (usb_endpoint_xfer_control(&host_ep->desc) && ep_index == 0) { + + udev = container_of(host_ep, struct usb_device, ep0); + if (udev->speed != USB_SPEED_FULL || !udev->slot_id) + return; + + vdev = xhci->devs[udev->slot_id]; + if (!vdev || vdev->udev != udev) + return; + + xhci_check_ep0_maxpacket(xhci, vdev); + + /* Nothing else should be done here for ep0 during ep reset */ + return; + } + if (!host_ep->hcpriv) return; udev = (struct usb_device *) host_ep->hcpriv; vdev = xhci->devs[udev->slot_id]; - /* - * vdev may be lost due to xHC restore error and re-initialization - * during S3/S4 resume. A new vdev will be allocated later by - * xhci_discover_or_reset_device() - */ if (!udev->slot_id || !vdev) return; - ep_index = xhci_get_endpoint_index(&host_ep->desc); + ep = &vdev->eps[ep_index]; /* Bail out if toggle is already being cleared by a endpoint reset */ @@ -4029,12 +4055,18 @@ disable_slot: return 0; } -/* - * Issue an Address Device command and optionally send a corresponding - * SetAddress request to the device. +/** + * xhci_setup_device - issues an Address Device command to assign a unique + * USB bus address. + * @hcd: USB host controller data structure. + * @udev: USB dev structure representing the connected device. + * @setup: Enum specifying setup mode: address only or with context. + * @timeout_ms: Max wait time (ms) for the command operation to complete. + * + * Return: 0 if successful; otherwise, negative error code. */ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, - enum xhci_setup_dev setup) + enum xhci_setup_dev setup, unsigned int timeout_ms) { const char *act = setup == SETUP_CONTEXT_ONLY ? "context" : "address"; unsigned long flags; @@ -4091,6 +4123,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, } command->in_ctx = virt_dev->in_ctx; + command->timeout_ms = timeout_ms; slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx); @@ -4217,14 +4250,16 @@ out: return ret; } -static int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) +static int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev, + unsigned int timeout_ms) { - return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS); + return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS, timeout_ms); } static int xhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev) { - return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY); + return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY, + XHCI_CMD_DEFAULT_TIMEOUT); } /* diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3ea5c092bba7..6f82d404883f 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -791,6 +791,8 @@ struct xhci_command { struct completion *completion; union xhci_trb *command_trb; struct list_head cmd_list; + /* xHCI command response timeout in milliseconds */ + unsigned int timeout_ms; }; /* drop context bitmasks */ @@ -1547,11 +1549,15 @@ struct xhci_td { struct xhci_segment *bounce_seg; /* actual_length of the URB has already been set */ bool urb_length_set; + bool error_mid_td; unsigned int num_trbs; }; -/* xHCI command default timeout value */ -#define XHCI_CMD_DEFAULT_TIMEOUT (5 * HZ) +/* + * xHCI command default timeout value in milliseconds. + * USB 3.2 spec, section 9.2.6.1 + */ +#define XHCI_CMD_DEFAULT_TIMEOUT 5000 /* command descriptor */ struct xhci_cd { @@ -1760,8 +1766,8 @@ struct xhci_hcd { int page_size; /* Valid values are 12 to 20, inclusive */ int page_shift; - /* msi-x vectors */ - int msix_count; + /* MSI-X/MSI vectors */ + int nvecs; /* optional clocks */ struct clk *clk; struct clk *reg_clk; @@ -1769,7 +1775,7 @@ struct xhci_hcd { struct reset_control *reset; /* data structures */ struct xhci_device_context_array *dcbaa; - struct xhci_interrupter *interrupter; + struct xhci_interrupter **interrupters; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; #define CMD_RING_STATE_RUNNING (1 << 0) @@ -2080,6 +2086,10 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, int type, gfp_t flags); void xhci_free_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx); +struct xhci_interrupter * +xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg); +void xhci_remove_secondary_interrupter(struct usb_hcd + *hcd, struct xhci_interrupter *ir); /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 1e3df27bab58..6d28467ce352 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -501,7 +501,6 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd, dev->minor, cmd, arg); retval = 0; - io_res = 0; switch (cmd) { case IOW_WRITE: if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24 || diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c index 2b45404e9732..0dd2b032c90b 100644 --- a/drivers/usb/misc/onboard_usb_hub.c +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -5,8 +5,10 @@ * Copyright (c) 2022, Google LLC */ +#include <linux/clk.h> #include <linux/device.h> #include <linux/export.h> +#include <linux/err.h> #include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/kernel.h> @@ -60,15 +62,22 @@ struct onboard_hub { bool going_away; struct list_head udev_list; struct mutex lock; + struct clk *clk; }; static int onboard_hub_power_on(struct onboard_hub *hub) { int err; + err = clk_prepare_enable(hub->clk); + if (err) { + dev_err(hub->dev, "failed to enable clock: %pe\n", ERR_PTR(err)); + return err; + } + err = regulator_bulk_enable(hub->pdata->num_supplies, hub->supplies); if (err) { - dev_err(hub->dev, "failed to enable supplies: %d\n", err); + dev_err(hub->dev, "failed to enable supplies: %pe\n", ERR_PTR(err)); return err; } @@ -88,10 +97,12 @@ static int onboard_hub_power_off(struct onboard_hub *hub) err = regulator_bulk_disable(hub->pdata->num_supplies, hub->supplies); if (err) { - dev_err(hub->dev, "failed to disable supplies: %d\n", err); + dev_err(hub->dev, "failed to disable supplies: %pe\n", ERR_PTR(err)); return err; } + clk_disable_unprepare(hub->clk); + hub->is_powered_on = false; return 0; @@ -233,9 +244,9 @@ static void onboard_hub_attach_usb_driver(struct work_struct *work) { int err; - err = driver_attach(&onboard_hub_usbdev_driver.drvwrap.driver); + err = driver_attach(&onboard_hub_usbdev_driver.driver); if (err) - pr_err("Failed to attach USB driver: %d\n", err); + pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err)); } static int onboard_hub_probe(struct platform_device *pdev) @@ -262,10 +273,14 @@ static int onboard_hub_probe(struct platform_device *pdev) err = devm_regulator_bulk_get(dev, hub->pdata->num_supplies, hub->supplies); if (err) { - dev_err(dev, "Failed to get regulator supplies: %d\n", err); + dev_err(dev, "Failed to get regulator supplies: %pe\n", ERR_PTR(err)); return err; } + hub->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(hub->clk)) + return dev_err_probe(dev, PTR_ERR(hub->clk), "failed to get clock\n"); + hub->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(hub->reset_gpio)) @@ -426,6 +441,7 @@ static void onboard_hub_usbdev_disconnect(struct usb_device *udev) static const struct usb_device_id onboard_hub_id_table[] = { { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 */ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 */ + { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6570) }, /* CY7C6563x 2.0 */ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 */ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 */ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0620) }, /* Genesys Logic GL3523 USB 3.1 */ diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h index 292110e64a1d..f360d5cf8d8a 100644 --- a/drivers/usb/misc/onboard_usb_hub.h +++ b/drivers/usb/misc/onboard_usb_hub.h @@ -36,6 +36,11 @@ static const struct onboard_hub_pdata cypress_hx3_data = { .num_supplies = 2, }; +static const struct onboard_hub_pdata cypress_hx2vl_data = { + .reset_us = 1, + .num_supplies = 1, +}; + static const struct onboard_hub_pdata genesys_gl850g_data = { .reset_us = 3, .num_supplies = 1, @@ -61,6 +66,7 @@ static const struct of_device_id onboard_hub_match[] = { { .compatible = "usb451,8142", .data = &ti_tusb8041_data, }, { .compatible = "usb4b4,6504", .data = &cypress_hx3_data, }, { .compatible = "usb4b4,6506", .data = &cypress_hx3_data, }, + { .compatible = "usb4b4,6570", .data = &cypress_hx2vl_data, }, { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, }, { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, }, { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, }, diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c index 7f371ea1248c..26e9b8749d8a 100644 --- a/drivers/usb/misc/qcom_eud.c +++ b/drivers/usb/misc/qcom_eud.c @@ -205,6 +205,9 @@ static int eud_probe(struct platform_device *pdev) return PTR_ERR(chip->mode_mgr); chip->irq = platform_get_irq(pdev, 0); + if (chip->irq < 0) + return chip->irq; + ret = devm_request_threaded_irq(&pdev->dev, chip->irq, handle_eud_irq, handle_eud_irq_thread, IRQF_ONESHOT, NULL, chip); if (ret) diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index c640f98d20c5..9a0649d23693 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -34,6 +34,8 @@ #define YUREX_BUF_SIZE 8 #define YUREX_WRITE_TIMEOUT (HZ*2) +#define MAX_S64_STRLEN 20 /* {-}922337203685477580{7,8} */ + /* table of devices that work with this driver */ static struct usb_device_id yurex_table[] = { { USB_DEVICE(YUREX_VENDOR_ID, YUREX_PRODUCT_ID) }, @@ -401,7 +403,7 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, { struct usb_yurex *dev; int len = 0; - char in_buffer[20]; + char in_buffer[MAX_S64_STRLEN]; unsigned long flags; dev = file->private_data; @@ -412,14 +414,16 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count, return -ENODEV; } + if (WARN_ON_ONCE(dev->bbu > S64_MAX || dev->bbu < S64_MIN)) { + mutex_unlock(&dev->io_mutex); + return -EIO; + } + spin_lock_irqsave(&dev->lock, flags); - len = snprintf(in_buffer, 20, "%lld\n", dev->bbu); + scnprintf(in_buffer, MAX_S64_STRLEN, "%lld\n", dev->bbu); spin_unlock_irqrestore(&dev->lock, flags); mutex_unlock(&dev->io_mutex); - if (WARN_ON_ONCE(len >= sizeof(in_buffer))) - return -EIO; - return simple_read_from_buffer(buffer, count, ppos, in_buffer, len); } diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 9ca9305243fe..4e30de4db1c0 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1250,14 +1250,19 @@ static vm_fault_t mon_bin_vma_fault(struct vm_fault *vmf) struct mon_reader_bin *rp = vmf->vma->vm_private_data; unsigned long offset, chunk_idx; struct page *pageptr; + unsigned long flags; + spin_lock_irqsave(&rp->b_lock, flags); offset = vmf->pgoff << PAGE_SHIFT; - if (offset >= rp->b_size) + if (offset >= rp->b_size) { + spin_unlock_irqrestore(&rp->b_lock, flags); return VM_FAULT_SIGBUS; + } chunk_idx = offset / CHUNK_SIZE; pageptr = rp->b_vec[chunk_idx].pg; get_page(pageptr); vmf->page = pageptr; + spin_unlock_irqrestore(&rp->b_lock, flags); return 0; } diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c index 98ab0cc473d6..3c23805ab1a4 100644 --- a/drivers/usb/mon/mon_stat.c +++ b/drivers/usb/mon/mon_stat.c @@ -35,9 +35,9 @@ static int mon_stat_open(struct inode *inode, struct file *file) mbus = inode->i_private; - sp->slen = snprintf(sp->str, STAT_BUF_SIZE, - "nreaders %d events %u text_lost %u\n", - mbus->nreaders, mbus->cnt_events, mbus->cnt_text_lost); + sp->slen = scnprintf(sp->str, STAT_BUF_SIZE, + "nreaders %d events %u text_lost %u\n", + mbus->nreaders, mbus->cnt_events, mbus->cnt_text_lost); file->private_data = sp; return 0; diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 39cb14164652..2fe9b95bac1d 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -352,7 +352,7 @@ static int mon_text_open(struct inode *inode, struct file *file) rp->r.rnf_error = mon_text_error; rp->r.rnf_complete = mon_text_complete; - snprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp); + scnprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp); rp->e_slab = kmem_cache_create(rp->slab_name, sizeof(struct mon_event_text), sizeof(long), 0, mon_text_ctor); @@ -700,46 +700,28 @@ static const struct file_operations mon_fops_text_u = { int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus) { - enum { NAMESZ = 10 }; + enum { NAMESZ = 12 }; char name[NAMESZ]; int busnum = ubus? ubus->busnum: 0; - int rc; if (mon_dir == NULL) return 0; if (ubus != NULL) { - rc = snprintf(name, NAMESZ, "%dt", busnum); - if (rc <= 0 || rc >= NAMESZ) - goto err_print_t; + scnprintf(name, NAMESZ, "%dt", busnum); mbus->dent_t = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_t); } - rc = snprintf(name, NAMESZ, "%du", busnum); - if (rc <= 0 || rc >= NAMESZ) - goto err_print_u; + scnprintf(name, NAMESZ, "%du", busnum); mbus->dent_u = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_u); - rc = snprintf(name, NAMESZ, "%ds", busnum); - if (rc <= 0 || rc >= NAMESZ) - goto err_print_s; + scnprintf(name, NAMESZ, "%ds", busnum); mbus->dent_s = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat); return 1; - -err_print_s: - debugfs_remove(mbus->dent_u); - mbus->dent_u = NULL; -err_print_u: - if (ubus != NULL) { - debugfs_remove(mbus->dent_t); - mbus->dent_t = NULL; - } -err_print_t: - return 0; } void mon_text_del(struct mon_bus *mbus) diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 770081b828a4..9ab50f26db60 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -46,15 +46,21 @@ EXPORT_SYMBOL_GPL(usb_phy_generic_unregister); static int nop_set_suspend(struct usb_phy *x, int suspend) { struct usb_phy_generic *nop = dev_get_drvdata(x->dev); + int ret = 0; - if (!IS_ERR(nop->clk)) { - if (suspend) + if (suspend) { + if (!IS_ERR(nop->clk)) clk_disable_unprepare(nop->clk); - else + if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev)) + ret = regulator_disable(nop->vcc); + } else { + if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev)) + ret = regulator_enable(nop->vcc); + if (!IS_ERR(nop->clk)) clk_prepare_enable(nop->clk); } - return 0; + return ret; } static void nop_reset(struct usb_phy_generic *nop) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index acd46b72899e..920a32cd094d 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -388,8 +388,7 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy) { - return IS_ENABLED(CONFIG_USB_OTG) && - mxs_phy->phy.last_event == USB_EVENT_ID; + return mxs_phy->phy.last_event == USB_EVENT_ID; } static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index c3ce6b1054f1..da09cff55abc 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -179,16 +179,16 @@ static ssize_t vbus_show(struct device *dev, switch (twl->linkstat) { case MUSB_VBUS_VALID: - ret = snprintf(buf, PAGE_SIZE, "vbus\n"); + ret = sysfs_emit(buf, "vbus\n"); break; case MUSB_ID_GROUND: - ret = snprintf(buf, PAGE_SIZE, "id\n"); + ret = sysfs_emit(buf, "id\n"); break; case MUSB_VBUS_OFF: - ret = snprintf(buf, PAGE_SIZE, "none\n"); + ret = sysfs_emit(buf, "none\n"); break; default: - ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); + ret = sysfs_emit(buf, "UNKNOWN\n"); } spin_unlock_irqrestore(&twl->lock, flags); diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 3eb8dc3a1a8f..6c812d01b37d 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -113,7 +113,7 @@ static ssize_t new_id_store(struct device_driver *driver, if (retval >= 0 && usb_drv->usb_driver != NULL) retval = usb_store_new_id(&usb_drv->usb_driver->dynids, usb_drv->usb_driver->id_table, - &usb_drv->usb_driver->drvwrap.driver, + &usb_drv->usb_driver->driver, buf, count); return retval; } diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 1e61fe043171..923e0ed85444 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -146,6 +146,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */ { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */ { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ + { USB_DEVICE(0x10C4, 0x87ED) }, /* IMST USB-Stick for Smart Meter */ { USB_DEVICE(0x10C4, 0x8856) }, /* CEL EM357 ZigBee USB Stick - LR */ { USB_DEVICE(0x10C4, 0x8857) }, /* CEL EM357 ZigBee USB Stick */ { USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 72390dbf0769..2ae124c49d44 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2269,6 +2269,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0111, 0xff) }, /* Fibocom FM160 (MBIM mode) */ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a0, 0xff) }, /* Fibocom NL668-AM/NL652-EU (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a2, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a3, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a4, 0xff), /* Fibocom FM101-GL (laptop MBIM) */ .driver_info = RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */ diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index b1e844bf31f8..703a9c563557 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -184,6 +184,8 @@ static const struct usb_device_id id_table[] = { {DEVICE_SWI(0x413c, 0x81d0)}, /* Dell Wireless 5819 */ {DEVICE_SWI(0x413c, 0x81d1)}, /* Dell Wireless 5818 */ {DEVICE_SWI(0x413c, 0x81d2)}, /* Dell Wireless 5818 */ + {DEVICE_SWI(0x413c, 0x8217)}, /* Dell Wireless DW5826e */ + {DEVICE_SWI(0x413c, 0x8218)}, /* Dell Wireless DW5826e QDL */ /* Huawei devices */ {DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */ diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 17b09f03ef84..f1e91eb7f8a4 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1521,7 +1521,7 @@ int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[] /* Now set udriver's id_table and look for matches */ udriver->id_table = id_table; - rc = driver_attach(&udriver->drvwrap.driver); + rc = driver_attach(&udriver->driver); return 0; err_deregister_drivers: diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c index 0774ba22fb66..177fa6cd143a 100644 --- a/drivers/usb/storage/sierra_ms.c +++ b/drivers/usb/storage/sierra_ms.c @@ -98,26 +98,26 @@ static ssize_t truinst_show(struct device *dev, struct device_attribute *attr, struct usb_device *udev = interface_to_usbdev(intf); int result; if (swi_tru_install == TRU_FORCE_MS) { - result = snprintf(buf, PAGE_SIZE, "Forced Mass Storage\n"); + result = sysfs_emit(buf, "Forced Mass Storage\n"); } else { swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL); if (!swocInfo) { - snprintf(buf, PAGE_SIZE, "Error\n"); + sysfs_emit(buf, "Error\n"); return -ENOMEM; } result = sierra_get_swoc_info(udev, swocInfo); if (result < 0) { dev_dbg(dev, "SWIMS: failed SWoC query\n"); kfree(swocInfo); - snprintf(buf, PAGE_SIZE, "Error\n"); + sysfs_emit(buf, "Error\n"); return -EIO; } debug_swoc(dev, swocInfo); - result = snprintf(buf, PAGE_SIZE, - "REV=%02d SKU=%04X VER=%04X\n", - swocInfo->rev, - swocInfo->LinuxSKU, - swocInfo->LinuxVer); + result = sysfs_emit(buf, + "REV=%02d SKU=%04X VER=%04X\n", + swocInfo->rev, + swocInfo->LinuxSKU, + swocInfo->LinuxVer); kfree(swocInfo); } return result; diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 696bb0b23599..9707f53cfda9 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -1246,7 +1246,7 @@ static struct usb_driver uas_driver = { .suspend = uas_suspend, .resume = uas_resume, .reset_resume = uas_reset_resume, - .drvwrap.driver.shutdown = uas_shutdown, + .driver.shutdown = uas_shutdown, .id_table = uas_usb_ids, }; diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 16a670828dde..015aa9253353 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -263,11 +263,13 @@ static void typec_altmode_put_partner(struct altmode *altmode) { struct altmode *partner = altmode->partner; struct typec_altmode *adev; + struct typec_altmode *partner_adev; if (!partner) return; adev = &altmode->adev; + partner_adev = &partner->adev; if (is_typec_plug(adev->dev.parent)) { struct typec_plug *plug = to_typec_plug(adev->dev.parent); @@ -276,7 +278,7 @@ static void typec_altmode_put_partner(struct altmode *altmode) } else { partner->partner = NULL; } - put_device(&adev->dev); + put_device(&partner_adev->dev); } /** @@ -476,7 +478,7 @@ static int altmode_id_get(struct device *dev) else ids = &to_typec_port(dev)->mode_ids; - return ida_simple_get(ids, 0, 0, GFP_KERNEL); + return ida_alloc(ids, GFP_KERNEL); } static void altmode_id_remove(struct device *dev, int id) @@ -490,7 +492,7 @@ static void altmode_id_remove(struct device *dev, int id) else ids = &to_typec_port(dev)->mode_ids; - ida_simple_remove(ids, id); + ida_free(ids, id); } static void typec_altmode_release(struct device *dev) @@ -1798,7 +1800,7 @@ static void typec_release(struct device *dev) { struct typec_port *port = to_typec_port(dev); - ida_simple_remove(&typec_index_ida, port->id); + ida_free(&typec_index_ida, port->id); ida_destroy(&port->mode_ids); typec_switch_put(port->sw); typec_mux_put(port->mux); @@ -2231,7 +2233,8 @@ void typec_port_register_altmodes(struct typec_port *port, struct typec_altmode_desc desc; struct typec_altmode *alt; size_t index = 0; - u32 svid, vdo; + u16 svid; + u32 vdo; int ret; altmodes_node = device_get_named_child_node(&port->dev, "altmodes"); @@ -2239,7 +2242,7 @@ void typec_port_register_altmodes(struct typec_port *port, return; /* No altmodes specified */ fwnode_for_each_child_node(altmodes_node, child) { - ret = fwnode_property_read_u32(child, "svid", &svid); + ret = fwnode_property_read_u16(child, "svid", &svid); if (ret) { dev_err(&port->dev, "Error reading svid for altmode %s\n", fwnode_get_name(child)); @@ -2297,7 +2300,7 @@ struct typec_port *typec_register_port(struct device *parent, if (!port) return ERR_PTR(-ENOMEM); - id = ida_simple_get(&typec_index_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&typec_index_ida, GFP_KERNEL); if (id < 0) { kfree(port); return ERR_PTR(id); diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index 38416fb0cc3c..d2cb5e733e57 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -56,4 +56,14 @@ config TYPEC_MUX_PTN36502 Say Y or M if your system has a NXP PTN36502 Type-C redriver chip found on some devices with a Type-C port. +config TYPEC_MUX_WCD939X_USBSS + tristate "Qualcomm WCD939x USBSS Analog Audio Switch driver" + depends on I2C + select REGMAP_I2C + help + Driver for the Qualcomm WCD939x Audio Codec USBSS domain which + provides support for muxing analog audio and sideband signals on a + common USB Type-C connector. + If compiled as a module, the module will be named wcd939x-usbss. + endmenu diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile index 9d6a5557b0bd..57dc9ac6f8dc 100644 --- a/drivers/usb/typec/mux/Makefile +++ b/drivers/usb/typec/mux/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M) += nb7vpq904m.o obj-$(CONFIG_TYPEC_MUX_PTN36502) += ptn36502.o +obj-$(CONFIG_TYPEC_MUX_WCD939X_USBSS) += wcd939x-usbss.o diff --git a/drivers/usb/typec/mux/wcd939x-usbss.c b/drivers/usb/typec/mux/wcd939x-usbss.c new file mode 100644 index 000000000000..d46c353dfaf2 --- /dev/null +++ b/drivers/usb/typec/mux/wcd939x-usbss.c @@ -0,0 +1,779 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (C) 2023 Linaro Ltd. + */ + +#include <linux/bits.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/bitfield.h> +#include <linux/gpio/consumer.h> +#include <linux/usb/typec_dp.h> +#include <linux/usb/typec_mux.h> + +#define WCD_USBSS_PMP_OUT1 0x2 + +#define WCD_USBSS_DP_DN_MISC1 0x20 + +#define WCD_USBSS_DP_DN_MISC1_DP_PCOMP_2X_DYN_BST_ON_EN BIT(3) +#define WCD_USBSS_DP_DN_MISC1_DN_PCOMP_2X_DYN_BST_ON_EN BIT(0) + +#define WCD_USBSS_MG1_EN 0x24 + +#define WCD_USBSS_MG1_EN_CT_SNS_EN BIT(1) + +#define WCD_USBSS_MG1_BIAS 0x25 + +#define WCD_USBSS_MG1_BIAS_PCOMP_DYN_BST_EN BIT(3) + +#define WCD_USBSS_MG1_MISC 0x27 + +#define WCD_USBSS_MG1_MISC_PCOMP_2X_DYN_BST_ON_EN BIT(5) + +#define WCD_USBSS_MG2_EN 0x28 + +#define WCD_USBSS_MG2_EN_CT_SNS_EN BIT(1) + +#define WCD_USBSS_MG2_BIAS 0x29 + +#define WCD_USBSS_MG2_BIAS_PCOMP_DYN_BST_EN BIT(3) + +#define WCD_USBSS_MG2_MISC 0x30 + +#define WCD_USBSS_MG2_MISC_PCOMP_2X_DYN_BST_ON_EN BIT(5) + +#define WCD_USBSS_DISP_AUXP_THRESH 0x80 + +#define WCD_USBSS_DISP_AUXP_THRESH_DISP_AUXP_OVPON_CM GENMASK(7, 5) + +#define WCD_USBSS_DISP_AUXP_CTL 0x81 + +#define WCD_USBSS_DISP_AUXP_CTL_LK_CANCEL_TRK_COEFF GENMASK(2, 0) + +#define WCD_USBSS_CPLDO_CTL2 0xa1 + +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE 0x403 + +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_DEVICE_ENABLE BIT(7) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_DP_AUXP_TO_MGX_SWITCHES BIT(6) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_DP_AUXM_TO_MGX_SWITCHES BIT(5) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_DNL_SWITCHES BIT(4) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_DPR_SWITCHES BIT(3) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_SENSE_SWITCHES BIT(2) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_MIC_SWITCHES BIT(1) +#define WCD_USBSS_SWITCH_SETTINGS_ENABLE_AGND_SWITCHES BIT(0) + +#define WCD_USBSS_SWITCH_SELECT0 0x404 + +#define WCD_USBSS_SWITCH_SELECT0_DP_AUXP_SWITCHES BIT(7) /* 1-> MG2 */ +#define WCD_USBSS_SWITCH_SELECT0_DP_AUXM_SWITCHES BIT(6) /* 1-> MG2 */ +#define WCD_USBSS_SWITCH_SELECT0_DNL_SWITCHES GENMASK(5, 4) +#define WCD_USBSS_SWITCH_SELECT0_DPR_SWITCHES GENMASK(3, 2) +#define WCD_USBSS_SWITCH_SELECT0_SENSE_SWITCHES BIT(1) /* 1-> SBU2 */ +#define WCD_USBSS_SWITCH_SELECT0_MIC_SWITCHES BIT(0) /* 1-> MG2 */ + +#define WCD_USBSS_SWITCH_SELECT0_DNL_SWITCH_L 0 +#define WCD_USBSS_SWITCH_SELECT0_DNL_SWITCH_DN 1 +#define WCD_USBSS_SWITCH_SELECT0_DNL_SWITCH_DN2 2 + +#define WCD_USBSS_SWITCH_SELECT0_DPR_SWITCH_R 0 +#define WCD_USBSS_SWITCH_SELECT0_DPR_SWITCH_DP 1 +#define WCD_USBSS_SWITCH_SELECT0_DPR_SWITCH_DR2 2 + +#define WCD_USBSS_SWITCH_SELECT1 0x405 + +#define WCD_USBSS_SWITCH_SELECT1_AGND_SWITCHES BIT(0) /* 1-> MG2 */ + +#define WCD_USBSS_DELAY_R_SW 0x40d +#define WCD_USBSS_DELAY_MIC_SW 0x40e +#define WCD_USBSS_DELAY_SENSE_SW 0x40f +#define WCD_USBSS_DELAY_GND_SW 0x410 +#define WCD_USBSS_DELAY_L_SW 0x411 + +#define WCD_USBSS_FUNCTION_ENABLE 0x413 + +#define WCD_USBSS_FUNCTION_ENABLE_SOURCE_SELECT GENMASK(1, 0) + +#define WCD_USBSS_FUNCTION_ENABLE_SOURCE_SELECT_MANUAL 1 +#define WCD_USBSS_FUNCTION_ENABLE_SOURCE_SELECT_AUDIO_FSM 2 + +#define WCD_USBSS_EQUALIZER1 0x415 + +#define WCD_USBSS_EQUALIZER1_EQ_EN BIT(7) +#define WCD_USBSS_EQUALIZER1_BW_SETTINGS GENMASK(6, 3) + +#define WCD_USBSS_USB_SS_CNTL 0x419 + +#define WCD_USBSS_USB_SS_CNTL_STANDBY_STATE BIT(4) +#define WCD_USBSS_USB_SS_CNTL_RCO_EN BIT(3) +#define WCD_USBSS_USB_SS_CNTL_USB_SS_MODE GENMASK(2, 0) + +#define WCD_USBSS_USB_SS_CNTL_USB_SS_MODE_AATC 2 +#define WCD_USBSS_USB_SS_CNTL_USB_SS_MODE_USB 5 + +#define WCD_USBSS_AUDIO_FSM_START 0x433 + +#define WCD_USBSS_AUDIO_FSM_START_AUDIO_FSM_AUDIO_TRIG BIT(0) + +#define WCD_USBSS_RATIO_SPKR_REXT_L_LSB 0x461 +#define WCD_USBSS_RATIO_SPKR_REXT_L_MSB 0x462 +#define WCD_USBSS_RATIO_SPKR_REXT_R_LSB 0x463 +#define WCD_USBSS_RATIO_SPKR_REXT_R_MSB 0x464 +#define WCD_USBSS_AUD_COEF_L_K0_0 0x475 +#define WCD_USBSS_AUD_COEF_L_K0_1 0x476 +#define WCD_USBSS_AUD_COEF_L_K0_2 0x477 +#define WCD_USBSS_AUD_COEF_L_K1_0 0x478 +#define WCD_USBSS_AUD_COEF_L_K1_1 0x479 +#define WCD_USBSS_AUD_COEF_L_K2_0 0x47a +#define WCD_USBSS_AUD_COEF_L_K2_1 0x47b +#define WCD_USBSS_AUD_COEF_L_K3_0 0x47c +#define WCD_USBSS_AUD_COEF_L_K3_1 0x47d +#define WCD_USBSS_AUD_COEF_L_K4_0 0x47e +#define WCD_USBSS_AUD_COEF_L_K4_1 0x47f +#define WCD_USBSS_AUD_COEF_L_K5_0 0x480 +#define WCD_USBSS_AUD_COEF_L_K5_1 0x481 +#define WCD_USBSS_AUD_COEF_R_K0_0 0x482 +#define WCD_USBSS_AUD_COEF_R_K0_1 0x483 +#define WCD_USBSS_AUD_COEF_R_K0_2 0x484 +#define WCD_USBSS_AUD_COEF_R_K1_0 0x485 +#define WCD_USBSS_AUD_COEF_R_K1_1 0x486 +#define WCD_USBSS_AUD_COEF_R_K2_0 0x487 +#define WCD_USBSS_AUD_COEF_R_K2_1 0x488 +#define WCD_USBSS_AUD_COEF_R_K3_0 0x489 +#define WCD_USBSS_AUD_COEF_R_K3_1 0x48a +#define WCD_USBSS_AUD_COEF_R_K4_0 0x48b +#define WCD_USBSS_AUD_COEF_R_K4_1 0x48c +#define WCD_USBSS_AUD_COEF_R_K5_0 0x48d +#define WCD_USBSS_AUD_COEF_R_K5_1 0x48e +#define WCD_USBSS_GND_COEF_L_K0_0 0x48f +#define WCD_USBSS_GND_COEF_L_K0_1 0x490 +#define WCD_USBSS_GND_COEF_L_K0_2 0x491 +#define WCD_USBSS_GND_COEF_L_K1_0 0x492 +#define WCD_USBSS_GND_COEF_L_K1_1 0x493 +#define WCD_USBSS_GND_COEF_L_K2_0 0x494 +#define WCD_USBSS_GND_COEF_L_K2_1 0x495 +#define WCD_USBSS_GND_COEF_L_K3_0 0x496 +#define WCD_USBSS_GND_COEF_L_K3_1 0x497 +#define WCD_USBSS_GND_COEF_L_K4_0 0x498 +#define WCD_USBSS_GND_COEF_L_K4_1 0x499 +#define WCD_USBSS_GND_COEF_L_K5_0 0x49a +#define WCD_USBSS_GND_COEF_L_K5_1 0x49b +#define WCD_USBSS_GND_COEF_R_K0_0 0x49c +#define WCD_USBSS_GND_COEF_R_K0_1 0x49d +#define WCD_USBSS_GND_COEF_R_K0_2 0x49e +#define WCD_USBSS_GND_COEF_R_K1_0 0x49f +#define WCD_USBSS_GND_COEF_R_K1_1 0x4a0 +#define WCD_USBSS_GND_COEF_R_K2_0 0x4a1 +#define WCD_USBSS_GND_COEF_R_K2_1 0x4a2 +#define WCD_USBSS_GND_COEF_R_K3_0 0x4a3 +#define WCD_USBSS_GND_COEF_R_K3_1 0x4a4 +#define WCD_USBSS_GND_COEF_R_K4_0 0x4a5 +#define WCD_USBSS_GND_COEF_R_K4_1 0x4a6 +#define WCD_USBSS_GND_COEF_R_K5_0 0x4a7 +#define WCD_USBSS_GND_COEF_R_K5_1 0x4a8 + +#define WCD_USBSS_MAX_REGISTER 0x4c1 + +struct wcd939x_usbss { + struct i2c_client *client; + struct gpio_desc *reset_gpio; + struct regulator *vdd_supply; + + /* used to serialize concurrent change requests */ + struct mutex lock; + + struct typec_switch_dev *sw; + struct typec_mux_dev *mux; + + struct regmap *regmap; + + struct typec_mux *codec; + struct typec_switch *codec_switch; + + enum typec_orientation orientation; + unsigned long mode; + unsigned int svid; +}; + +static const struct regmap_range_cfg wcd939x_usbss_ranges[] = { + { + .range_min = 0, + .range_max = WCD_USBSS_MAX_REGISTER, + .selector_reg = 0x0, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + }, +}; + +static const struct regmap_config wcd939x_usbss_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = WCD_USBSS_MAX_REGISTER, + .ranges = wcd939x_usbss_ranges, + .num_ranges = ARRAY_SIZE(wcd939x_usbss_ranges), +}; + +/* Linearlizer coefficients for 32ohm load */ +static const struct { + unsigned int offset; + unsigned int mask; + unsigned int value; +} wcd939x_usbss_coeff_init[] = { + { WCD_USBSS_AUD_COEF_L_K5_0, GENMASK(7, 0), 0x39 }, + { WCD_USBSS_AUD_COEF_R_K5_0, GENMASK(7, 0), 0x39 }, + { WCD_USBSS_GND_COEF_L_K2_0, GENMASK(7, 0), 0xe8 }, + { WCD_USBSS_GND_COEF_L_K4_0, GENMASK(7, 0), 0x73 }, + { WCD_USBSS_GND_COEF_R_K2_0, GENMASK(7, 0), 0xe8 }, + { WCD_USBSS_GND_COEF_R_K4_0, GENMASK(7, 0), 0x73 }, + { WCD_USBSS_RATIO_SPKR_REXT_L_LSB, GENMASK(7, 0), 0x00 }, + { WCD_USBSS_RATIO_SPKR_REXT_L_MSB, GENMASK(6, 0), 0x04 }, + { WCD_USBSS_RATIO_SPKR_REXT_R_LSB, GENMASK(7, 0), 0x00 }, + { WCD_USBSS_RATIO_SPKR_REXT_R_MSB, GENMASK(6, 0), 0x04 }, +}; + +static int wcd939x_usbss_set(struct wcd939x_usbss *usbss) +{ + bool reverse = (usbss->orientation == TYPEC_ORIENTATION_REVERSE); + bool enable_audio = false; + bool enable_usb = false; + bool enable_dp = false; + int ret; + + /* USB Mode */ + if (usbss->mode < TYPEC_STATE_MODAL || + (!usbss->svid && (usbss->mode == TYPEC_MODE_USB2 || + usbss->mode == TYPEC_MODE_USB3))) { + enable_usb = true; + } else if (usbss->svid) { + switch (usbss->mode) { + /* DP Only */ + case TYPEC_DP_STATE_C: + case TYPEC_DP_STATE_E: + enable_dp = true; + break; + + /* DP + USB */ + case TYPEC_DP_STATE_D: + case TYPEC_DP_STATE_F: + enable_usb = true; + enable_dp = true; + break; + + default: + return -EOPNOTSUPP; + } + } else if (usbss->mode == TYPEC_MODE_AUDIO) { + enable_audio = true; + } else { + return -EOPNOTSUPP; + } + + /* Disable all switches */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DP_AUXP_TO_MGX_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DP_AUXM_TO_MGX_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DPR_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DNL_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_SENSE_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_MIC_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_AGND_SWITCHES); + if (ret) + return ret; + + /* Clear switches */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_DP_AUXP_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DP_AUXM_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DPR_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DNL_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_SENSE_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_MIC_SWITCHES); + if (ret) + return ret; + + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT1, + WCD_USBSS_SWITCH_SELECT1_AGND_SWITCHES); + if (ret) + return ret; + + /* Enable OVP_MG1_BIAS PCOMP_DYN_BST_EN */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_MG1_BIAS, + WCD_USBSS_MG1_BIAS_PCOMP_DYN_BST_EN); + if (ret) + return ret; + + /* Enable OVP_MG2_BIAS PCOMP_DYN_BST_EN */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_MG2_BIAS, + WCD_USBSS_MG2_BIAS_PCOMP_DYN_BST_EN); + if (ret) + return ret; + + /* Disable Equalizer in safe mode */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_EQUALIZER1, + WCD_USBSS_EQUALIZER1_EQ_EN); + if (ret) + return ret; + + /* Start FSM with all disabled, force write */ + ret = regmap_write_bits(usbss->regmap, WCD_USBSS_AUDIO_FSM_START, + WCD_USBSS_AUDIO_FSM_START_AUDIO_FSM_AUDIO_TRIG, + WCD_USBSS_AUDIO_FSM_START_AUDIO_FSM_AUDIO_TRIG); + + /* 35us to allow the SBU switch to turn off */ + usleep_range(35, 1000); + + /* Setup Audio Accessory mux/switch */ + if (enable_audio) { + int i; + + /* + * AATC switch configuration: + * "Normal": + * - R: DNR + * - L: DNL + * - Sense: GSBU2 + * - Mic: MG1 + * - AGND: MG2 + * "Swapped": + * - R: DNR + * - L: DNL + * - Sense: GSBU1 + * - Mic: MG2 + * - AGND: MG1 + * Swapped information is given by the codec MBHC logic + */ + + /* Set AATC mode */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_USB_SS_CNTL, + WCD_USBSS_USB_SS_CNTL_USB_SS_MODE, + FIELD_PREP(WCD_USBSS_USB_SS_CNTL_USB_SS_MODE, + WCD_USBSS_USB_SS_CNTL_USB_SS_MODE_AATC)); + if (ret) + return ret; + + /* Select L for DNL_SWITCHES and R for DPR_SWITCHES */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_DPR_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DNL_SWITCHES, + FIELD_PREP(WCD_USBSS_SWITCH_SELECT0_DNL_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_DNL_SWITCH_L) | + FIELD_PREP(WCD_USBSS_SWITCH_SELECT0_DPR_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_DPR_SWITCH_R)); + if (ret) + return ret; + + if (reverse) + /* Select MG2 for MIC, SBU1 for Sense */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_MIC_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_MIC_SWITCHES); + else + /* Select MG1 for MIC, SBU2 for Sense */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_SENSE_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_SENSE_SWITCHES); + if (ret) + return ret; + + if (reverse) + /* Disable OVP_MG1_BIAS PCOMP_DYN_BST_EN */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_MG1_BIAS, + WCD_USBSS_MG1_BIAS_PCOMP_DYN_BST_EN); + else + /* Disable OVP_MG2_BIAS PCOMP_DYN_BST_EN */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_MG2_BIAS, + WCD_USBSS_MG2_BIAS_PCOMP_DYN_BST_EN); + if (ret) + return ret; + + /* Enable SENSE, MIC switches */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_SENSE_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_MIC_SWITCHES); + if (ret) + return ret; + + if (reverse) + /* Select MG1 for AGND_SWITCHES */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT1, + WCD_USBSS_SWITCH_SELECT1_AGND_SWITCHES); + else + /* Select MG2 for AGND_SWITCHES */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT1, + WCD_USBSS_SWITCH_SELECT1_AGND_SWITCHES); + if (ret) + return ret; + + /* Enable AGND switches */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_AGND_SWITCHES); + if (ret) + return ret; + + /* Enable DPR, DNL switches */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DNL_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DPR_SWITCHES); + if (ret) + return ret; + + /* Setup FSM delays */ + ret = regmap_write(usbss->regmap, WCD_USBSS_DELAY_L_SW, 0x02); + if (ret) + return ret; + + ret = regmap_write(usbss->regmap, WCD_USBSS_DELAY_R_SW, 0x02); + if (ret) + return ret; + + ret = regmap_write(usbss->regmap, WCD_USBSS_DELAY_MIC_SW, 0x01); + if (ret) + return ret; + + /* Start FSM, force write */ + ret = regmap_write_bits(usbss->regmap, WCD_USBSS_AUDIO_FSM_START, + WCD_USBSS_AUDIO_FSM_START_AUDIO_FSM_AUDIO_TRIG, + WCD_USBSS_AUDIO_FSM_START_AUDIO_FSM_AUDIO_TRIG); + if (ret) + return ret; + + /* Default Linearlizer coefficients */ + for (i = 0; i < ARRAY_SIZE(wcd939x_usbss_coeff_init); ++i) + regmap_update_bits(usbss->regmap, + wcd939x_usbss_coeff_init[i].offset, + wcd939x_usbss_coeff_init[i].mask, + wcd939x_usbss_coeff_init[i].value); + + return 0; + } + + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_USB_SS_CNTL, + WCD_USBSS_USB_SS_CNTL_USB_SS_MODE, + FIELD_PREP(WCD_USBSS_USB_SS_CNTL_USB_SS_MODE, + WCD_USBSS_USB_SS_CNTL_USB_SS_MODE_USB)); + if (ret) + return ret; + + /* Enable USB muxes */ + if (enable_usb) { + /* Do not enable Equalizer in safe mode */ + if (usbss->mode != TYPEC_STATE_SAFE) { + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_EQUALIZER1, + WCD_USBSS_EQUALIZER1_EQ_EN); + if (ret) + return ret; + } + + /* Select DN for DNL_SWITCHES and DP for DPR_SWITCHES */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_DPR_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DNL_SWITCHES, + FIELD_PREP(WCD_USBSS_SWITCH_SELECT0_DNL_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_DNL_SWITCH_DN) | + FIELD_PREP(WCD_USBSS_SWITCH_SELECT0_DPR_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_DPR_SWITCH_DP)); + if (ret) + return ret; + + /* Enable DNL_SWITCHES and DPR_SWITCHES */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DPR_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DNL_SWITCHES); + if (ret) + return ret; + } + + /* Enable DP AUX muxes */ + if (enable_dp) { + /* Update Leakage Canceller Coefficient for AUXP pins */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_DISP_AUXP_CTL, + WCD_USBSS_DISP_AUXP_CTL_LK_CANCEL_TRK_COEFF, + FIELD_PREP(WCD_USBSS_DISP_AUXP_CTL_LK_CANCEL_TRK_COEFF, + 5)); + if (ret) + return ret; + + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_DISP_AUXP_THRESH, + WCD_USBSS_DISP_AUXP_THRESH_DISP_AUXP_OVPON_CM); + if (ret) + return ret; + + if (reverse) + /* Select MG2 for AUXP and MG1 for AUXM */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_DP_AUXP_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DP_AUXM_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_DP_AUXP_SWITCHES); + else + /* Select MG1 for AUXP and MG2 for AUXM */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_SWITCH_SELECT0, + WCD_USBSS_SWITCH_SELECT0_DP_AUXP_SWITCHES | + WCD_USBSS_SWITCH_SELECT0_DP_AUXM_SWITCHES, + WCD_USBSS_SWITCH_SELECT0_DP_AUXM_SWITCHES); + if (ret) + return ret; + + /* Enable DP_AUXP_TO_MGX and DP_AUXM_TO_MGX switches */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DP_AUXP_TO_MGX_SWITCHES | + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DP_AUXM_TO_MGX_SWITCHES); + + /* 15us to allow the SBU switch to turn on again */ + usleep_range(15, 1000); + } + + return 0; +} + +static int wcd939x_usbss_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct wcd939x_usbss *usbss = typec_switch_get_drvdata(sw); + int ret = 0; + + mutex_lock(&usbss->lock); + + if (usbss->orientation != orientation) { + usbss->orientation = orientation; + + ret = wcd939x_usbss_set(usbss); + } + + mutex_unlock(&usbss->lock); + + if (ret) + return ret; + + /* Report orientation to codec after switch has been done */ + return typec_switch_set(usbss->codec_switch, orientation); +} + +static int wcd939x_usbss_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + struct wcd939x_usbss *usbss = typec_mux_get_drvdata(mux); + int ret = 0; + + mutex_lock(&usbss->lock); + + if (usbss->mode != state->mode) { + usbss->mode = state->mode; + + if (state->alt) + usbss->svid = state->alt->svid; + else + usbss->svid = 0; // No SVID + + ret = wcd939x_usbss_set(usbss); + } + + mutex_unlock(&usbss->lock); + + if (ret) + return ret; + + /* Report event to codec after switch has been done */ + return typec_mux_set(usbss->codec, state); +} + +static int wcd939x_usbss_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct typec_switch_desc sw_desc = { }; + struct typec_mux_desc mux_desc = { }; + struct wcd939x_usbss *usbss; + int ret; + + usbss = devm_kzalloc(dev, sizeof(*usbss), GFP_KERNEL); + if (!usbss) + return -ENOMEM; + + usbss->client = client; + mutex_init(&usbss->lock); + + usbss->regmap = devm_regmap_init_i2c(client, &wcd939x_usbss_regmap_config); + if (IS_ERR(usbss->regmap)) + return dev_err_probe(dev, PTR_ERR(usbss->regmap), "failed to initialize regmap\n"); + + usbss->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(usbss->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(usbss->reset_gpio), + "unable to acquire reset gpio\n"); + + usbss->vdd_supply = devm_regulator_get_optional(dev, "vdd"); + if (IS_ERR(usbss->vdd_supply)) + return PTR_ERR(usbss->vdd_supply); + + /* Get Codec's MUX & Switch devices */ + usbss->codec = fwnode_typec_mux_get(dev->fwnode); + if (IS_ERR(usbss->codec)) + return dev_err_probe(dev, PTR_ERR(usbss->codec), + "failed to acquire codec mode-switch\n"); + + usbss->codec_switch = fwnode_typec_switch_get(dev->fwnode); + if (IS_ERR(usbss->codec_switch)) { + ret = dev_err_probe(dev, PTR_ERR(usbss->codec_switch), + "failed to acquire codec orientation-switch\n"); + goto err_mux_put; + } + + usbss->mode = TYPEC_STATE_SAFE; + usbss->orientation = TYPEC_ORIENTATION_NONE; + + gpiod_set_value(usbss->reset_gpio, 1); + + ret = regulator_enable(usbss->vdd_supply); + if (ret) { + dev_err(dev, "Failed to enable vdd: %d\n", ret); + goto err_mux_switch; + } + + msleep(20); + + gpiod_set_value(usbss->reset_gpio, 0); + + msleep(20); + + /* Disable standby */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_USB_SS_CNTL, + WCD_USBSS_USB_SS_CNTL_STANDBY_STATE); + if (ret) + goto err_regulator_disable; + + /* Set manual mode by default */ + ret = regmap_update_bits(usbss->regmap, WCD_USBSS_FUNCTION_ENABLE, + WCD_USBSS_FUNCTION_ENABLE_SOURCE_SELECT, + FIELD_PREP(WCD_USBSS_FUNCTION_ENABLE_SOURCE_SELECT, + WCD_USBSS_FUNCTION_ENABLE_SOURCE_SELECT_MANUAL)); + if (ret) + goto err_regulator_disable; + + /* Enable dynamic boosting for DP and DN */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_DP_DN_MISC1, + WCD_USBSS_DP_DN_MISC1_DP_PCOMP_2X_DYN_BST_ON_EN | + WCD_USBSS_DP_DN_MISC1_DN_PCOMP_2X_DYN_BST_ON_EN); + if (ret) + goto err_regulator_disable; + + /* Enable dynamic boosting for MG1 OVP */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_MG1_MISC, + WCD_USBSS_MG1_MISC_PCOMP_2X_DYN_BST_ON_EN); + if (ret) + goto err_regulator_disable; + + /* Enable dynamic boosting for MG2 OVP */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_MG2_MISC, + WCD_USBSS_MG2_MISC_PCOMP_2X_DYN_BST_ON_EN); + if (ret) + goto err_regulator_disable; + + /* Write 0xFF to WCD_USBSS_CPLDO_CTL2 */ + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_CPLDO_CTL2, 0xff); + if (ret) + goto err_regulator_disable; + + /* Set RCO_EN: WCD_USBSS_USB_SS_CNTL Bit<3> --> 0x0 --> 0x1 */ + ret = regmap_clear_bits(usbss->regmap, WCD_USBSS_USB_SS_CNTL, + WCD_USBSS_USB_SS_CNTL_RCO_EN); + if (ret) + goto err_regulator_disable; + + ret = regmap_set_bits(usbss->regmap, WCD_USBSS_USB_SS_CNTL, + WCD_USBSS_USB_SS_CNTL_RCO_EN); + if (ret) + goto err_regulator_disable; + + /* Disable all switches but enable the mux */ + ret = regmap_write(usbss->regmap, WCD_USBSS_SWITCH_SETTINGS_ENABLE, + WCD_USBSS_SWITCH_SETTINGS_ENABLE_DEVICE_ENABLE); + if (ret) + goto err_regulator_disable; + + /* Setup in SAFE mode */ + ret = wcd939x_usbss_set(usbss); + if (ret) + goto err_regulator_disable; + + sw_desc.drvdata = usbss; + sw_desc.fwnode = dev_fwnode(dev); + sw_desc.set = wcd939x_usbss_switch_set; + + usbss->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(usbss->sw)) { + ret = dev_err_probe(dev, PTR_ERR(usbss->sw), "failed to register typec switch\n"); + goto err_regulator_disable; + } + + mux_desc.drvdata = usbss; + mux_desc.fwnode = dev_fwnode(dev); + mux_desc.set = wcd939x_usbss_mux_set; + + usbss->mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(usbss->mux)) { + ret = dev_err_probe(dev, PTR_ERR(usbss->mux), "failed to register typec mux\n"); + goto err_switch_unregister; + } + + i2c_set_clientdata(client, usbss); + + return 0; + +err_switch_unregister: + typec_switch_unregister(usbss->sw); + +err_regulator_disable: + regulator_disable(usbss->vdd_supply); + +err_mux_switch: + typec_switch_put(usbss->codec_switch); + +err_mux_put: + typec_mux_put(usbss->codec); + + return ret; +} + +static void wcd939x_usbss_remove(struct i2c_client *client) +{ + struct wcd939x_usbss *usbss = i2c_get_clientdata(client); + + typec_mux_unregister(usbss->mux); + typec_switch_unregister(usbss->sw); + + regulator_disable(usbss->vdd_supply); + + typec_switch_put(usbss->codec_switch); + typec_mux_put(usbss->codec); +} + +static const struct i2c_device_id wcd939x_usbss_table[] = { + { "wcd9390-usbss" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wcd939x_usbss_table); + +static const struct of_device_id wcd939x_usbss_of_table[] = { + { .compatible = "qcom,wcd9390-usbss" }, + { } +}; +MODULE_DEVICE_TABLE(of, wcd939x_usbss_of_table); + +static struct i2c_driver wcd939x_usbss_driver = { + .driver = { + .name = "wcd939x-usbss", + .of_match_table = wcd939x_usbss_of_table, + }, + .probe = wcd939x_usbss_probe, + .remove = wcd939x_usbss_remove, + .id_table = wcd939x_usbss_table, +}; +module_i2c_driver(wcd939x_usbss_driver); + +MODULE_DESCRIPTION("Qualcomm WCD939x USBSS driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/typec/pd.c b/drivers/usb/typec/pd.c index 85d015cdbe1f..b9cca2be76fc 100644 --- a/drivers/usb/typec/pd.c +++ b/drivers/usb/typec/pd.c @@ -468,7 +468,7 @@ static struct device_type pd_capabilities_type = { /** * usb_power_delivery_register_capabilities - Register a set of capabilities. * @pd: The USB PD instance that the capabilities belong to. - * @desc: Description of the Capablities Message. + * @desc: Description of the Capabilities Message. * * This function registers a Capabilities Message described in @desc. The * capabilities will have their own sub-directory under @pd in sysfs. @@ -571,7 +571,7 @@ static void pd_release(struct device *dev) { struct usb_power_delivery *pd = to_usb_power_delivery(dev); - ida_simple_remove(&pd_ida, pd->id); + ida_free(&pd_ida, pd->id); kfree(pd); } @@ -616,7 +616,7 @@ usb_power_delivery_register(struct device *parent, struct usb_power_delivery_des if (!pd) return ERR_PTR(-ENOMEM); - ret = ida_simple_get(&pd_ida, 0, 0, GFP_KERNEL); + ret = ida_alloc(&pd_ida, GFP_KERNEL); if (ret < 0) { kfree(pd); return ERR_PTR(ret); diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index 9454b12a073c..7fb966fd639b 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -92,11 +92,16 @@ static void max_tcpci_init_regs(struct max_tcpci_chip *chip) return; } + /* Vconn Over Current Protection */ + ret = max_tcpci_write8(chip, TCPC_FAULT_STATUS_MASK, TCPC_FAULT_STATUS_MASK_VCONN_OC); + if (ret < 0) + return; + alert_mask = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_TX_FAILED | TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_RX_STATUS | TCPC_ALERT_CC_STATUS | TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_POWER_STATUS | /* Enable Extended alert for detecting Fast Role Swap Signal */ - TCPC_ALERT_EXTND | TCPC_ALERT_EXTENDED_STATUS; + TCPC_ALERT_EXTND | TCPC_ALERT_EXTENDED_STATUS | TCPC_ALERT_FAULT; ret = max_tcpci_write16(chip, TCPC_ALERT_MASK, alert_mask); if (ret < 0) { @@ -295,6 +300,19 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status) } } + if (status & TCPC_ALERT_FAULT) { + ret = max_tcpci_read8(chip, TCPC_FAULT_STATUS, ®_status); + if (ret < 0) + return ret; + + ret = max_tcpci_write8(chip, TCPC_FAULT_STATUS, reg_status); + if (ret < 0) + return ret; + + if (reg_status & TCPC_FAULT_STATUS_VCONN_OC) + tcpm_port_error_recovery(chip->port); + } + if (status & TCPC_ALERT_EXTND) { ret = max_tcpci_read8(chip, TCPC_ALERT_EXTENDED, ®_status); if (ret < 0) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index bfb6f9481e87..f7d7daa60c8d 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -251,6 +251,7 @@ enum frs_typec_current { #define TCPM_FRS_EVENT BIT(3) #define TCPM_SOURCING_VBUS BIT(4) #define TCPM_PORT_CLEAN BIT(5) +#define TCPM_PORT_ERROR BIT(6) #define LOG_BUFFER_ENTRIES 1024 #define LOG_BUFFER_ENTRY_SIZE 128 @@ -296,6 +297,15 @@ struct pd_pps_data { bool active; }; +struct pd_data { + struct usb_power_delivery *pd; + struct usb_power_delivery_capabilities *source_cap; + struct usb_power_delivery_capabilities_desc source_desc; + struct usb_power_delivery_capabilities *sink_cap; + struct usb_power_delivery_capabilities_desc sink_desc; + unsigned int operating_snk_mw; +}; + struct tcpm_port { struct device *dev; @@ -397,12 +407,14 @@ struct tcpm_port { unsigned int rx_msgid; /* USB PD objects */ - struct usb_power_delivery *pd; + struct usb_power_delivery **pds; + struct pd_data **pd_list; struct usb_power_delivery_capabilities *port_source_caps; struct usb_power_delivery_capabilities *port_sink_caps; struct usb_power_delivery *partner_pd; struct usb_power_delivery_capabilities *partner_source_caps; struct usb_power_delivery_capabilities *partner_sink_caps; + struct usb_power_delivery *selected_pd; /* Partner capabilities/requests */ u32 sink_request; @@ -412,6 +424,7 @@ struct tcpm_port { unsigned int nr_sink_caps; /* Local capabilities */ + unsigned int pd_count; u32 src_pdo[PDO_MAX_OBJECTS]; unsigned int nr_src_pdo; u32 snk_pdo[PDO_MAX_OBJECTS]; @@ -2847,7 +2860,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); } else { - if (port->send_discover) { + if (port->send_discover && port->negotiated_rev < PD_REV30) { tcpm_queue_message(port, PD_MSG_CTRL_WAIT); break; } @@ -2863,7 +2876,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); } else { - if (port->send_discover) { + if (port->send_discover && port->negotiated_rev < PD_REV30) { tcpm_queue_message(port, PD_MSG_CTRL_WAIT); break; } @@ -2872,7 +2885,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, } break; case PD_CTRL_VCONN_SWAP: - if (port->send_discover) { + if (port->send_discover && port->negotiated_rev < PD_REV30) { tcpm_queue_message(port, PD_MSG_CTRL_WAIT); break; } @@ -4401,7 +4414,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_current_limit(port, tcpm_get_current_limit(port), 5000); tcpm_swap_complete(port, 0); tcpm_typec_connect(port); - mod_enable_frs_delayed_work(port, 0); + if (port->pd_capable && port->source_caps[0] & PDO_FIXED_DUAL_ROLE) + mod_enable_frs_delayed_work(port, 0); tcpm_pps_complete(port, port->pps_status); if (port->ams != NONE_AMS) @@ -4862,8 +4876,7 @@ static void run_state_machine(struct tcpm_port *port) break; case PORT_RESET: tcpm_reset_port(port); - tcpm_set_cc(port, tcpm_default_state(port) == SNK_UNATTACHED ? - TYPEC_CC_RD : tcpm_rp_cc(port)); + tcpm_set_cc(port, TYPEC_CC_OPEN); tcpm_set_state(port, PORT_RESET_WAIT_OFF, PD_T_ERROR_RECOVERY); break; @@ -5487,6 +5500,10 @@ static void tcpm_pd_event_handler(struct kthread_work *work) tcpm_set_state(port, tcpm_default_state(port), 0); } } + if (events & TCPM_PORT_ERROR) { + tcpm_log(port, "port triggering error recovery"); + tcpm_set_state(port, ERROR_RECOVERY, 0); + } spin_lock(&port->pd_event_lock); } @@ -5554,6 +5571,15 @@ bool tcpm_port_is_toggling(struct tcpm_port *port) } EXPORT_SYMBOL_GPL(tcpm_port_is_toggling); +void tcpm_port_error_recovery(struct tcpm_port *port) +{ + spin_lock(&port->pd_event_lock); + port->pd_events |= TCPM_PORT_ERROR; + spin_unlock(&port->pd_event_lock); + kthread_queue_work(port->wq, &port->event_work); +} +EXPORT_SYMBOL_GPL(tcpm_port_error_recovery); + static void tcpm_enable_frs_work(struct kthread_work *work) { struct tcpm_port *port = container_of(work, struct tcpm_port, enable_frs); @@ -6045,12 +6071,114 @@ port_unlock: return 0; } +static struct pd_data *tcpm_find_pd_data(struct tcpm_port *port, struct usb_power_delivery *pd) +{ + int i; + + for (i = 0; port->pd_list[i]; i++) { + if (port->pd_list[i]->pd == pd) + return port->pd_list[i]; + } + + return ERR_PTR(-ENODATA); +} + +static struct usb_power_delivery **tcpm_pd_get(struct typec_port *p) +{ + struct tcpm_port *port = typec_get_drvdata(p); + + return port->pds; +} + +static int tcpm_pd_set(struct typec_port *p, struct usb_power_delivery *pd) +{ + struct tcpm_port *port = typec_get_drvdata(p); + struct pd_data *data; + int i, ret = 0; + + mutex_lock(&port->lock); + + if (port->selected_pd == pd) + goto unlock; + + data = tcpm_find_pd_data(port, pd); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + goto unlock; + } + + if (data->sink_desc.pdo[0]) { + for (i = 0; i < PDO_MAX_OBJECTS && data->sink_desc.pdo[i]; i++) + port->snk_pdo[i] = data->sink_desc.pdo[i]; + port->nr_snk_pdo = i + 1; + port->operating_snk_mw = data->operating_snk_mw; + } + + if (data->source_desc.pdo[0]) { + for (i = 0; i < PDO_MAX_OBJECTS && data->source_desc.pdo[i]; i++) + port->snk_pdo[i] = data->source_desc.pdo[i]; + port->nr_src_pdo = i + 1; + } + + switch (port->state) { + case SRC_UNATTACHED: + case SRC_ATTACH_WAIT: + case SRC_TRYWAIT: + tcpm_set_cc(port, tcpm_rp_cc(port)); + break; + case SRC_SEND_CAPABILITIES: + case SRC_SEND_CAPABILITIES_TIMEOUT: + case SRC_NEGOTIATE_CAPABILITIES: + case SRC_READY: + case SRC_WAIT_NEW_CAPABILITIES: + port->caps_count = 0; + port->upcoming_state = SRC_SEND_CAPABILITIES; + ret = tcpm_ams_start(port, POWER_NEGOTIATION); + if (ret == -EAGAIN) { + port->upcoming_state = INVALID_STATE; + goto unlock; + } + break; + case SNK_NEGOTIATE_CAPABILITIES: + case SNK_NEGOTIATE_PPS_CAPABILITIES: + case SNK_READY: + case SNK_TRANSITION_SINK: + case SNK_TRANSITION_SINK_VBUS: + if (port->pps_data.active) + port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES; + else if (port->pd_capable) + port->upcoming_state = SNK_NEGOTIATE_CAPABILITIES; + else + break; + + port->update_sink_caps = true; + + ret = tcpm_ams_start(port, POWER_NEGOTIATION); + if (ret == -EAGAIN) { + port->upcoming_state = INVALID_STATE; + goto unlock; + } + break; + default: + break; + } + + port->port_source_caps = data->source_cap; + port->port_sink_caps = data->sink_cap; + port->selected_pd = pd; +unlock: + mutex_unlock(&port->lock); + return ret; +} + static const struct typec_operations tcpm_ops = { .try_role = tcpm_try_role, .dr_set = tcpm_dr_set, .pr_set = tcpm_pr_set, .vconn_set = tcpm_vconn_set, - .port_type_set = tcpm_port_type_set + .port_type_set = tcpm_port_type_set, + .pd_get = tcpm_pd_get, + .pd_set = tcpm_pd_set }; void tcpm_tcpc_reset(struct tcpm_port *port) @@ -6064,58 +6192,63 @@ EXPORT_SYMBOL_GPL(tcpm_tcpc_reset); static void tcpm_port_unregister_pd(struct tcpm_port *port) { - usb_power_delivery_unregister_capabilities(port->port_sink_caps); + int i; + port->port_sink_caps = NULL; - usb_power_delivery_unregister_capabilities(port->port_source_caps); port->port_source_caps = NULL; - usb_power_delivery_unregister(port->pd); - port->pd = NULL; + for (i = 0; i < port->pd_count; i++) { + usb_power_delivery_unregister_capabilities(port->pd_list[i]->sink_cap); + kfree(port->pd_list[i]->sink_cap); + usb_power_delivery_unregister_capabilities(port->pd_list[i]->source_cap); + kfree(port->pd_list[i]->source_cap); + devm_kfree(port->dev, port->pd_list[i]); + port->pd_list[i] = NULL; + usb_power_delivery_unregister(port->pds[i]); + port->pds[i] = NULL; + } } static int tcpm_port_register_pd(struct tcpm_port *port) { struct usb_power_delivery_desc desc = { port->typec_caps.pd_revision }; - struct usb_power_delivery_capabilities_desc caps = { }; struct usb_power_delivery_capabilities *cap; - int ret; + int ret, i; if (!port->nr_src_pdo && !port->nr_snk_pdo) return 0; - port->pd = usb_power_delivery_register(port->dev, &desc); - if (IS_ERR(port->pd)) { - ret = PTR_ERR(port->pd); - goto err_unregister; - } - - if (port->nr_src_pdo) { - memcpy_and_pad(caps.pdo, sizeof(caps.pdo), port->src_pdo, - port->nr_src_pdo * sizeof(u32), 0); - caps.role = TYPEC_SOURCE; - - cap = usb_power_delivery_register_capabilities(port->pd, &caps); - if (IS_ERR(cap)) { - ret = PTR_ERR(cap); + for (i = 0; i < port->pd_count; i++) { + port->pds[i] = usb_power_delivery_register(port->dev, &desc); + if (IS_ERR(port->pds[i])) { + ret = PTR_ERR(port->pds[i]); goto err_unregister; } - - port->port_source_caps = cap; - } - - if (port->nr_snk_pdo) { - memcpy_and_pad(caps.pdo, sizeof(caps.pdo), port->snk_pdo, - port->nr_snk_pdo * sizeof(u32), 0); - caps.role = TYPEC_SINK; - - cap = usb_power_delivery_register_capabilities(port->pd, &caps); - if (IS_ERR(cap)) { - ret = PTR_ERR(cap); - goto err_unregister; + port->pd_list[i]->pd = port->pds[i]; + + if (port->pd_list[i]->source_desc.pdo[0]) { + cap = usb_power_delivery_register_capabilities(port->pds[i], + &port->pd_list[i]->source_desc); + if (IS_ERR(cap)) { + ret = PTR_ERR(cap); + goto err_unregister; + } + port->pd_list[i]->source_cap = cap; } - port->port_sink_caps = cap; + if (port->pd_list[i]->sink_desc.pdo[0]) { + cap = usb_power_delivery_register_capabilities(port->pds[i], + &port->pd_list[i]->sink_desc); + if (IS_ERR(cap)) { + ret = PTR_ERR(cap); + goto err_unregister; + } + port->pd_list[i]->sink_cap = cap; + } } + port->port_source_caps = port->pd_list[0]->source_cap; + port->port_sink_caps = port->pd_list[0]->sink_cap; + port->selected_pd = port->pds[0]; return 0; err_unregister: @@ -6124,12 +6257,15 @@ err_unregister: return ret; } -static int tcpm_fw_get_caps(struct tcpm_port *port, - struct fwnode_handle *fwnode) +static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) { + struct fwnode_handle *capabilities, *child, *caps = NULL; + unsigned int nr_src_pdo, nr_snk_pdo; const char *opmode_str; - int ret; - u32 mw, frs_current; + u32 *src_pdo, *snk_pdo; + u32 uw, frs_current; + int ret = 0, i; + int mode; if (!fwnode) return -EINVAL; @@ -6147,30 +6283,20 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, if (ret < 0) return ret; + mode = 0; + + if (fwnode_property_read_bool(fwnode, "accessory-mode-audio")) + port->typec_caps.accessory[mode++] = TYPEC_ACCESSORY_AUDIO; + + if (fwnode_property_read_bool(fwnode, "accessory-mode-debug")) + port->typec_caps.accessory[mode++] = TYPEC_ACCESSORY_DEBUG; + port->port_type = port->typec_caps.type; port->pd_supported = !fwnode_property_read_bool(fwnode, "pd-disable"); - port->slow_charger_loop = fwnode_property_read_bool(fwnode, "slow-charger-loop"); - if (port->port_type == TYPEC_PORT_SNK) - goto sink; - - /* Get Source PDOs for the PD port or Source Rp value for the non-PD port */ - if (port->pd_supported) { - ret = fwnode_property_count_u32(fwnode, "source-pdos"); - if (ret == 0) - return -EINVAL; - else if (ret < 0) - return ret; + port->self_powered = fwnode_property_read_bool(fwnode, "self-powered"); - port->nr_src_pdo = min(ret, PDO_MAX_OBJECTS); - ret = fwnode_property_read_u32_array(fwnode, "source-pdos", - port->src_pdo, port->nr_src_pdo); - if (ret) - return ret; - ret = tcpm_validate_caps(port, port->src_pdo, port->nr_src_pdo); - if (ret) - return ret; - } else { + if (!port->pd_supported) { ret = fwnode_property_read_string(fwnode, "typec-power-opmode", &opmode_str); if (ret) return ret; @@ -6178,45 +6304,150 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, if (ret < 0) return ret; port->src_rp = tcpm_pwr_opmode_to_rp(ret); - } - - if (port->port_type == TYPEC_PORT_SRC) return 0; + } -sink: - port->self_powered = fwnode_property_read_bool(fwnode, "self-powered"); - - if (!port->pd_supported) - return 0; - - /* Get sink pdos */ - ret = fwnode_property_count_u32(fwnode, "sink-pdos"); - if (ret <= 0) - return -EINVAL; - - port->nr_snk_pdo = min(ret, PDO_MAX_OBJECTS); - ret = fwnode_property_read_u32_array(fwnode, "sink-pdos", - port->snk_pdo, port->nr_snk_pdo); - if ((ret < 0) || tcpm_validate_caps(port, port->snk_pdo, - port->nr_snk_pdo)) - return -EINVAL; - - if (fwnode_property_read_u32(fwnode, "op-sink-microwatt", &mw) < 0) - return -EINVAL; - port->operating_snk_mw = mw / 1000; + /* The following code are applicable to pd-capable ports, i.e. pd_supported is true. */ /* FRS can only be supported by DRP ports */ if (port->port_type == TYPEC_PORT_DRP) { ret = fwnode_property_read_u32(fwnode, "new-source-frs-typec-current", &frs_current); - if (ret >= 0 && frs_current <= FRS_5V_3A) + if (!ret && frs_current <= FRS_5V_3A) port->new_source_frs_current = frs_current; + + if (ret) + ret = 0; } + /* For the backward compatibility, "capabilities" node is optional. */ + capabilities = fwnode_get_named_child_node(fwnode, "capabilities"); + if (!capabilities) { + port->pd_count = 1; + } else { + fwnode_for_each_child_node(capabilities, child) + port->pd_count++; + + if (!port->pd_count) { + ret = -ENODATA; + goto put_capabilities; + } + } + + port->pds = devm_kcalloc(port->dev, port->pd_count, sizeof(struct usb_power_delivery *), + GFP_KERNEL); + if (!port->pds) { + ret = -ENOMEM; + goto put_capabilities; + } + + port->pd_list = devm_kcalloc(port->dev, port->pd_count, sizeof(struct pd_data *), + GFP_KERNEL); + if (!port->pd_list) { + ret = -ENOMEM; + goto put_capabilities; + } + + for (i = 0; i < port->pd_count; i++) { + port->pd_list[i] = devm_kzalloc(port->dev, sizeof(struct pd_data), GFP_KERNEL); + if (!port->pd_list[i]) { + ret = -ENOMEM; + goto put_capabilities; + } + + src_pdo = port->pd_list[i]->source_desc.pdo; + port->pd_list[i]->source_desc.role = TYPEC_SOURCE; + snk_pdo = port->pd_list[i]->sink_desc.pdo; + port->pd_list[i]->sink_desc.role = TYPEC_SINK; + + /* If "capabilities" is NULL, fall back to single pd cap population. */ + if (!capabilities) + caps = fwnode; + else + caps = fwnode_get_next_child_node(capabilities, caps); + + if (port->port_type != TYPEC_PORT_SNK) { + ret = fwnode_property_count_u32(caps, "source-pdos"); + if (ret == 0) { + ret = -EINVAL; + goto put_caps; + } + if (ret < 0) + goto put_caps; + + nr_src_pdo = min(ret, PDO_MAX_OBJECTS); + ret = fwnode_property_read_u32_array(caps, "source-pdos", src_pdo, + nr_src_pdo); + if (ret) + goto put_caps; + + ret = tcpm_validate_caps(port, src_pdo, nr_src_pdo); + if (ret) + goto put_caps; + + if (i == 0) { + port->nr_src_pdo = nr_src_pdo; + memcpy_and_pad(port->src_pdo, sizeof(u32) * PDO_MAX_OBJECTS, + port->pd_list[0]->source_desc.pdo, + sizeof(u32) * nr_src_pdo, + 0); + } + } + + if (port->port_type != TYPEC_PORT_SRC) { + ret = fwnode_property_count_u32(caps, "sink-pdos"); + if (ret == 0) { + ret = -EINVAL; + goto put_caps; + } + + if (ret < 0) + goto put_caps; + + nr_snk_pdo = min(ret, PDO_MAX_OBJECTS); + ret = fwnode_property_read_u32_array(caps, "sink-pdos", snk_pdo, + nr_snk_pdo); + if (ret) + goto put_caps; + + ret = tcpm_validate_caps(port, snk_pdo, nr_snk_pdo); + if (ret) + goto put_caps; + + if (fwnode_property_read_u32(caps, "op-sink-microwatt", &uw) < 0) { + ret = -EINVAL; + goto put_caps; + } + + port->pd_list[i]->operating_snk_mw = uw / 1000; + + if (i == 0) { + port->nr_snk_pdo = nr_snk_pdo; + memcpy_and_pad(port->snk_pdo, sizeof(u32) * PDO_MAX_OBJECTS, + port->pd_list[0]->sink_desc.pdo, + sizeof(u32) * nr_snk_pdo, + 0); + port->operating_snk_mw = port->pd_list[0]->operating_snk_mw; + } + } + } + +put_caps: + if (caps != fwnode) + fwnode_handle_put(caps); +put_capabilities: + fwnode_handle_put(capabilities); + return ret; +} + +static int tcpm_fw_get_snk_vdos(struct tcpm_port *port, struct fwnode_handle *fwnode) +{ + int ret; + /* sink-vdos is optional */ ret = fwnode_property_count_u32(fwnode, "sink-vdos"); if (ret < 0) - ret = 0; + return 0; port->nr_snk_vdo = min(ret, VDO_MAX_OBJECTS); if (port->nr_snk_vdo) { @@ -6584,10 +6815,12 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) err = tcpm_fw_get_caps(port, tcpc->fwnode); if (err < 0) goto out_destroy_wq; + err = tcpm_fw_get_snk_vdos(port, tcpc->fwnode); + if (err < 0) + goto out_destroy_wq; port->try_role = port->typec_caps.prefer_role; - port->typec_caps.fwnode = tcpc->fwnode; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */ port->typec_caps.svdm_version = SVDM_VER_2_0; @@ -6596,7 +6829,6 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->typec_caps.orientation_aware = 1; port->partner_desc.identity = &port->partner_ident; - port->port_type = port->typec_caps.type; port->role_sw = usb_role_switch_get(port->dev); if (!port->role_sw) @@ -6615,7 +6847,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) if (err) goto out_role_sw_put; - port->typec_caps.pd = port->pd; + if (port->pds) + port->typec_caps.pd = port->pds[0]; port->typec_port = typec_register_port(port->dev, &port->typec_caps); if (IS_ERR(port->typec_port)) { diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 196535ad996d..0717cfcd9f8c 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -8,6 +8,7 @@ #include <linux/i2c.h> #include <linux/acpi.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> #include <linux/power_supply.h> @@ -64,6 +65,9 @@ #define TPS_PBMC_RC 0 /* Return code */ #define TPS_PBMC_DPCS 2 /* device patch complete status */ +/* reset de-assertion to ready for operation */ +#define TPS_SETUP_MS 1000 + enum { TPS_PORTINFO_SINK, TPS_PORTINFO_SINK_ACCESSORY, @@ -111,6 +115,8 @@ struct tipd_data { void (*trace_power_status)(u16 status); void (*trace_status)(u32 status); int (*apply_patch)(struct tps6598x *tps); + int (*init)(struct tps6598x *tps); + int (*reset)(struct tps6598x *tps); }; struct tps6598x { @@ -119,6 +125,7 @@ struct tps6598x { struct mutex lock; /* device lock */ u8 i2c_protocol:1; + struct gpio_desc *reset; struct typec_port *port; struct typec_partner *partner; struct usb_pd_identity partner_identity; @@ -323,7 +330,7 @@ static void tps6598x_disconnect(struct tps6598x *tps, u32 status) } static int tps6598x_exec_cmd_tmo(struct tps6598x *tps, const char *cmd, - size_t in_len, u8 *in_data, + size_t in_len, const u8 *in_data, size_t out_len, u8 *out_data, u32 cmd_timeout_ms, u32 res_delay_ms) { @@ -389,7 +396,7 @@ static int tps6598x_exec_cmd_tmo(struct tps6598x *tps, const char *cmd, } static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd, - size_t in_len, u8 *in_data, + size_t in_len, const u8 *in_data, size_t out_len, u8 *out_data) { return tps6598x_exec_cmd_tmo(tps, cmd, in_len, in_data, @@ -866,6 +873,30 @@ tps6598x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) return 0; } +static int tps_request_firmware(struct tps6598x *tps, const struct firmware **fw) +{ + const char *firmware_name; + int ret; + + ret = device_property_read_string(tps->dev, "firmware-name", + &firmware_name); + if (ret) + return ret; + + ret = request_firmware(fw, firmware_name, tps->dev); + if (ret) { + dev_err(tps->dev, "failed to retrieve \"%s\"\n", firmware_name); + return ret; + } + + if ((*fw)->size == 0) { + release_firmware(*fw); + ret = -EINVAL; + } + + return ret; +} + static int tps25750_write_firmware(struct tps6598x *tps, u8 bpms_addr, const u8 *data, size_t len) @@ -954,16 +985,9 @@ static int tps25750_start_patch_burst_mode(struct tps6598x *tps) if (ret) return ret; - ret = request_firmware(&fw, firmware_name, tps->dev); - if (ret) { - dev_err(tps->dev, "failed to retrieve \"%s\"\n", firmware_name); + ret = tps_request_firmware(tps, &fw); + if (ret) return ret; - } - - if (fw->size == 0) { - ret = -EINVAL; - goto release_fw; - } ret = of_property_match_string(np, "reg-names", "patch-address"); if (ret < 0) { @@ -1101,6 +1125,76 @@ wait_for_app: return 0; }; +static int tps6598x_apply_patch(struct tps6598x *tps) +{ + u8 in = TPS_PTCS_CONTENT_DEV | TPS_PTCS_CONTENT_APP; + u8 out[TPS_MAX_LEN] = {0}; + size_t in_len = sizeof(in); + size_t copied_bytes = 0; + size_t bytes_left; + const struct firmware *fw; + const char *firmware_name; + int ret; + + ret = device_property_read_string(tps->dev, "firmware-name", + &firmware_name); + if (ret) + return ret; + + ret = tps_request_firmware(tps, &fw); + if (ret) + return ret; + + ret = tps6598x_exec_cmd(tps, "PTCs", in_len, &in, + TPS_PTCS_OUT_BYTES, out); + if (ret || out[TPS_PTCS_STATUS] == TPS_PTCS_STATUS_FAIL) { + if (!ret) + ret = -EBUSY; + dev_err(tps->dev, "Update start failed (%d)\n", ret); + goto release_fw; + } + + bytes_left = fw->size; + while (bytes_left) { + if (bytes_left < TPS_MAX_LEN) + in_len = bytes_left; + else + in_len = TPS_MAX_LEN; + ret = tps6598x_exec_cmd(tps, "PTCd", in_len, + fw->data + copied_bytes, + TPS_PTCD_OUT_BYTES, out); + if (ret || out[TPS_PTCD_TRANSFER_STATUS] || + out[TPS_PTCD_LOADING_STATE] == TPS_PTCD_LOAD_ERR) { + if (!ret) + ret = -EBUSY; + dev_err(tps->dev, "Patch download failed (%d)\n", ret); + goto release_fw; + } + copied_bytes += in_len; + bytes_left -= in_len; + } + + ret = tps6598x_exec_cmd(tps, "PTCc", 0, NULL, TPS_PTCC_OUT_BYTES, out); + if (ret || out[TPS_PTCC_DEV] || out[TPS_PTCC_APP]) { + if (!ret) + ret = -EBUSY; + dev_err(tps->dev, "Update completion failed (%d)\n", ret); + goto release_fw; + } + msleep(TPS_SETUP_MS); + dev_info(tps->dev, "Firmware update succeeded\n"); + +release_fw: + release_firmware(fw); + + return ret; +}; + +static int cd321x_init(struct tps6598x *tps) +{ + return 0; +} + static int tps25750_init(struct tps6598x *tps) { int ret; @@ -1119,6 +1213,26 @@ static int tps25750_init(struct tps6598x *tps) return 0; } +static int tps6598x_init(struct tps6598x *tps) +{ + return tps->data->apply_patch(tps); +} + +static int cd321x_reset(struct tps6598x *tps) +{ + return 0; +} + +static int tps25750_reset(struct tps6598x *tps) +{ + return tps6598x_exec_cmd_tmo(tps, "GAID", 0, NULL, 0, NULL, 2000, 0); +} + +static int tps6598x_reset(struct tps6598x *tps) +{ + return 0; +} + static int tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) { @@ -1182,7 +1296,6 @@ static int tps6598x_probe(struct i2c_client *client) u32 vid; int ret; u64 mask1; - bool is_tps25750; tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); if (!tps) @@ -1191,12 +1304,18 @@ static int tps6598x_probe(struct i2c_client *client) mutex_init(&tps->lock); tps->dev = &client->dev; + tps->reset = devm_gpiod_get_optional(tps->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(tps->reset)) + return dev_err_probe(tps->dev, PTR_ERR(tps->reset), + "failed to get reset GPIO\n"); + if (tps->reset) + msleep(TPS_SETUP_MS); + tps->regmap = devm_regmap_init_i2c(client, &tps6598x_regmap_config); if (IS_ERR(tps->regmap)) return PTR_ERR(tps->regmap); - is_tps25750 = device_is_compatible(tps->dev, "ti,tps25750"); - if (!is_tps25750) { + if (!device_is_compatible(tps->dev, "ti,tps25750")) { ret = tps6598x_read32(tps, TPS_REG_VID, &vid); if (ret < 0 || !vid) return -ENODEV; @@ -1239,8 +1358,8 @@ static int tps6598x_probe(struct i2c_client *client) if (ret < 0) return ret; - if (is_tps25750 && ret == TPS_MODE_PTCH) { - ret = tps25750_init(tps); + if (ret == TPS_MODE_PTCH) { + ret = tps->data->init(tps); if (ret) return ret; } @@ -1328,8 +1447,8 @@ err_clear_mask: tps6598x_write64(tps, TPS_REG_INT_MASK1, 0); err_reset_controller: /* Reset PD controller to remove any applied patch */ - if (is_tps25750) - tps6598x_exec_cmd_tmo(tps, "GAID", 0, NULL, 0, NULL, 2000, 0); + tps->data->reset(tps); + return ret; } @@ -1346,8 +1465,10 @@ static void tps6598x_remove(struct i2c_client *client) usb_role_switch_put(tps->role_sw); /* Reset PD controller to remove any applied patch */ - if (device_is_compatible(tps->dev, "ti,tps25750")) - tps6598x_exec_cmd_tmo(tps, "GAID", 0, NULL, 0, NULL, 2000, 0); + tps->data->reset(tps); + + if (tps->reset) + gpiod_set_value_cansleep(tps->reset, 1); } static int __maybe_unused tps6598x_suspend(struct device *dev) @@ -1358,6 +1479,8 @@ static int __maybe_unused tps6598x_suspend(struct device *dev) if (tps->wakeup) { disable_irq(client->irq); enable_irq_wake(client->irq); + } else if (tps->reset) { + gpiod_set_value_cansleep(tps->reset, 1); } if (!client->irq) @@ -1376,8 +1499,8 @@ static int __maybe_unused tps6598x_resume(struct device *dev) if (ret < 0) return ret; - if (device_is_compatible(tps->dev, "ti,tps25750") && ret == TPS_MODE_PTCH) { - ret = tps25750_init(tps); + if (ret == TPS_MODE_PTCH) { + ret = tps->data->init(tps); if (ret) return ret; } @@ -1385,6 +1508,9 @@ static int __maybe_unused tps6598x_resume(struct device *dev) if (tps->wakeup) { disable_irq_wake(client->irq); enable_irq(client->irq); + } else if (tps->reset) { + gpiod_set_value_cansleep(tps->reset, 0); + msleep(TPS_SETUP_MS); } if (!client->irq) @@ -1403,6 +1529,8 @@ static const struct tipd_data cd321x_data = { .register_port = tps6598x_register_port, .trace_power_status = trace_tps6598x_power_status, .trace_status = trace_tps6598x_status, + .init = cd321x_init, + .reset = cd321x_reset, }; static const struct tipd_data tps6598x_data = { @@ -1410,6 +1538,9 @@ static const struct tipd_data tps6598x_data = { .register_port = tps6598x_register_port, .trace_power_status = trace_tps6598x_power_status, .trace_status = trace_tps6598x_status, + .apply_patch = tps6598x_apply_patch, + .init = tps6598x_init, + .reset = tps6598x_reset, }; static const struct tipd_data tps25750_data = { @@ -1418,6 +1549,8 @@ static const struct tipd_data tps25750_data = { .trace_power_status = trace_tps25750_power_status, .trace_status = trace_tps25750_status, .apply_patch = tps25750_apply_patch, + .init = tps25750_init, + .reset = tps25750_reset, }; static const struct of_device_id tps6598x_of_match[] = { diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h index 01609bf509e4..89b24519463a 100644 --- a/drivers/usb/typec/tipd/tps6598x.h +++ b/drivers/usb/typec/tipd/tps6598x.h @@ -235,4 +235,22 @@ /* SLEEP CONF REG */ #define TPS_SLEEP_CONF_SLEEP_MODE_ALLOWED BIT(0) +/* Start Patch Download Sequence */ +#define TPS_PTCS_CONTENT_APP BIT(0) +#define TPS_PTCS_CONTENT_DEV BIT(1) +#define TPS_PTCS_OUT_BYTES 4 +#define TPS_PTCS_STATUS 1 + +#define TPS_PTCS_STATUS_FAIL 0x80 +/* Patch Download */ +#define TPS_PTCD_OUT_BYTES 10 +#define TPS_PTCD_TRANSFER_STATUS 1 +#define TPS_PTCD_LOADING_STATE 2 + +#define TPS_PTCD_LOAD_ERR 0x09 +/* Patch Download Complete */ +#define TPS_PTCC_OUT_BYTES 4 +#define TPS_PTCC_DEV 2 +#define TPS_PTCC_APP 3 + #endif /* __TPS6598X_H__ */ diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 5392ec698959..14f5a7bfae2e 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -938,7 +938,9 @@ static void ucsi_handle_connector_change(struct work_struct *work) clear_bit(EVENT_PENDING, &con->ucsi->flags); + mutex_lock(&ucsi->ppm_lock); ret = ucsi_acknowledge_connector_change(ucsi); + mutex_unlock(&ucsi->ppm_lock); if (ret) dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 6bbf490ac401..928eacbeb21a 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -25,6 +25,8 @@ struct ucsi_acpi { unsigned long flags; guid_t guid; u64 cmd; + bool dell_quirk_probed; + bool dell_quirk_active; }; static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func) @@ -73,9 +75,13 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset, const void *val, size_t val_len) { struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); + bool ack = UCSI_COMMAND(*(u64 *)val) == UCSI_ACK_CC_CI; int ret; - set_bit(COMMAND_PENDING, &ua->flags); + if (ack) + set_bit(ACK_PENDING, &ua->flags); + else + set_bit(COMMAND_PENDING, &ua->flags); ret = ucsi_acpi_async_write(ucsi, offset, val, val_len); if (ret) @@ -85,7 +91,10 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset, ret = -ETIMEDOUT; out_clear_bit: - clear_bit(COMMAND_PENDING, &ua->flags); + if (ack) + clear_bit(ACK_PENDING, &ua->flags); + else + clear_bit(COMMAND_PENDING, &ua->flags); return ret; } @@ -119,12 +128,73 @@ static const struct ucsi_operations ucsi_zenbook_ops = { .async_write = ucsi_acpi_async_write }; -static const struct dmi_system_id zenbook_dmi_id[] = { +/* + * Some Dell laptops expect that an ACK command with the + * UCSI_ACK_CONNECTOR_CHANGE bit set is followed by a (separate) + * ACK command that only has the UCSI_ACK_COMMAND_COMPLETE bit set. + * If this is not done events are not delivered to OSPM and + * subsequent commands will timeout. + */ +static int +ucsi_dell_sync_write(struct ucsi *ucsi, unsigned int offset, + const void *val, size_t val_len) +{ + struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); + u64 cmd = *(u64 *)val, ack = 0; + int ret; + + if (UCSI_COMMAND(cmd) == UCSI_ACK_CC_CI && + cmd & UCSI_ACK_CONNECTOR_CHANGE) + ack = UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE; + + ret = ucsi_acpi_sync_write(ucsi, offset, val, val_len); + if (ret != 0) + return ret; + if (ack == 0) + return ret; + + if (!ua->dell_quirk_probed) { + ua->dell_quirk_probed = true; + + cmd = UCSI_GET_CAPABILITY; + ret = ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &cmd, + sizeof(cmd)); + if (ret == 0) + return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, + &ack, sizeof(ack)); + if (ret != -ETIMEDOUT) + return ret; + + ua->dell_quirk_active = true; + dev_err(ua->dev, "Firmware bug: Additional ACK required after ACKing a connector change.\n"); + dev_err(ua->dev, "Firmware bug: Enabling workaround\n"); + } + + if (!ua->dell_quirk_active) + return ret; + + return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &ack, sizeof(ack)); +} + +static const struct ucsi_operations ucsi_dell_ops = { + .read = ucsi_acpi_read, + .sync_write = ucsi_dell_sync_write, + .async_write = ucsi_acpi_async_write +}; + +static const struct dmi_system_id ucsi_acpi_quirks[] = { { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"), }, + .driver_data = (void *)&ucsi_zenbook_ops, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + }, + .driver_data = (void *)&ucsi_dell_ops, }, { } }; @@ -142,8 +212,10 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data) if (UCSI_CCI_CONNECTOR(cci)) ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci)); - if (test_bit(COMMAND_PENDING, &ua->flags) && - cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE)) + if (cci & UCSI_CCI_ACK_COMPLETE && test_bit(ACK_PENDING, &ua->flags)) + complete(&ua->complete); + if (cci & UCSI_CCI_COMMAND_COMPLETE && + test_bit(COMMAND_PENDING, &ua->flags)) complete(&ua->complete); } @@ -151,6 +223,7 @@ static int ucsi_acpi_probe(struct platform_device *pdev) { struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); const struct ucsi_operations *ops = &ucsi_acpi_ops; + const struct dmi_system_id *id; struct ucsi_acpi *ua; struct resource *res; acpi_status status; @@ -180,8 +253,9 @@ static int ucsi_acpi_probe(struct platform_device *pdev) init_completion(&ua->complete); ua->dev = &pdev->dev; - if (dmi_check_system(zenbook_dmi_id)) - ops = &ucsi_zenbook_ops; + id = dmi_first_match(ucsi_acpi_quirks); + if (id) + ops = id->driver_data; ua->ucsi = ucsi_create(&pdev->dev, ops); if (IS_ERR(ua->ucsi)) diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c index 0a6624d37929..79110a69d697 100644 --- a/drivers/usb/usbip/stub_main.c +++ b/drivers/usb/usbip/stub_main.c @@ -377,14 +377,14 @@ static int __init usbip_host_init(void) goto err_usb_register; } - ret = driver_create_file(&stub_driver.drvwrap.driver, + ret = driver_create_file(&stub_driver.driver, &driver_attr_match_busid); if (ret) { pr_err("driver_create_file failed\n"); goto err_create_file; } - ret = driver_create_file(&stub_driver.drvwrap.driver, + ret = driver_create_file(&stub_driver.driver, &driver_attr_rebind); if (ret) { pr_err("driver_create_file failed\n"); @@ -402,10 +402,10 @@ err_usb_register: static void __exit usbip_host_exit(void) { - driver_remove_file(&stub_driver.drvwrap.driver, + driver_remove_file(&stub_driver.driver, &driver_attr_match_busid); - driver_remove_file(&stub_driver.drvwrap.driver, + driver_remove_file(&stub_driver.driver, &driver_attr_rebind); /* diff --git a/drivers/usb/usbip/vudc.h b/drivers/usb/usbip/vudc.h index 1bd4bc005829..faf61c9c6a98 100644 --- a/drivers/usb/usbip/vudc.h +++ b/drivers/usb/usbip/vudc.h @@ -173,6 +173,6 @@ struct vudc_device *alloc_vudc_device(int devid); void put_vudc_device(struct vudc_device *udc_dev); int vudc_probe(struct platform_device *pdev); -int vudc_remove(struct platform_device *pdev); +void vudc_remove(struct platform_device *pdev); #endif /* __USBIP_VUDC_H */ diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index 44b04c54c086..f11535020e35 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -628,12 +628,11 @@ out: return ret; } -int vudc_remove(struct platform_device *pdev) +void vudc_remove(struct platform_device *pdev) { struct vudc *udc = platform_get_drvdata(pdev); usb_del_gadget_udc(&udc->gadget); cleanup_vudc_hw(udc); kfree(udc); - return 0; } diff --git a/drivers/usb/usbip/vudc_main.c b/drivers/usb/usbip/vudc_main.c index 993e721cb840..8bee553e4894 100644 --- a/drivers/usb/usbip/vudc_main.c +++ b/drivers/usb/usbip/vudc_main.c @@ -19,7 +19,7 @@ MODULE_PARM_DESC(num, "number of emulated controllers"); static struct platform_driver vudc_driver = { .probe = vudc_probe, - .remove = vudc_remove, + .remove_new = vudc_remove, .driver = { .name = GADGET_NAME, .dev_groups = vudc_groups, diff --git a/drivers/vdpa/alibaba/eni_vdpa.c b/drivers/vdpa/alibaba/eni_vdpa.c index 5a09a09cca70..cce3d1837104 100644 --- a/drivers/vdpa/alibaba/eni_vdpa.c +++ b/drivers/vdpa/alibaba/eni_vdpa.c @@ -497,7 +497,7 @@ static int eni_vdpa_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (!eni_vdpa->vring) { ret = -ENOMEM; ENI_ERR(pdev, "failed to allocate virtqueues\n"); - goto err; + goto err_remove_vp_legacy; } for (i = 0; i < eni_vdpa->queues; i++) { @@ -509,11 +509,13 @@ static int eni_vdpa_probe(struct pci_dev *pdev, const struct pci_device_id *id) ret = vdpa_register_device(&eni_vdpa->vdpa, eni_vdpa->queues); if (ret) { ENI_ERR(pdev, "failed to register to vdpa bus\n"); - goto err; + goto err_remove_vp_legacy; } return 0; +err_remove_vp_legacy: + vp_legacy_remove(&eni_vdpa->ldev); err: put_device(&eni_vdpa->vdpa.dev); return ret; diff --git a/drivers/vdpa/mlx5/core/mlx5_vdpa.h b/drivers/vdpa/mlx5/core/mlx5_vdpa.h index 84547d998bcf..50aac8fe57ef 100644 --- a/drivers/vdpa/mlx5/core/mlx5_vdpa.h +++ b/drivers/vdpa/mlx5/core/mlx5_vdpa.h @@ -35,6 +35,9 @@ struct mlx5_vdpa_mr { struct vhost_iotlb *iotlb; bool user_mr; + + refcount_t refcount; + struct list_head mr_list; }; struct mlx5_vdpa_resources { @@ -93,6 +96,7 @@ struct mlx5_vdpa_dev { u32 generation; struct mlx5_vdpa_mr *mr[MLX5_VDPA_NUM_AS]; + struct list_head mr_list_head; /* serialize mr access */ struct mutex mr_mtx; struct mlx5_control_vq cvq; @@ -118,8 +122,10 @@ int mlx5_vdpa_destroy_mkey(struct mlx5_vdpa_dev *mvdev, u32 mkey); struct mlx5_vdpa_mr *mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb); void mlx5_vdpa_destroy_mr_resources(struct mlx5_vdpa_dev *mvdev); -void mlx5_vdpa_destroy_mr(struct mlx5_vdpa_dev *mvdev, - struct mlx5_vdpa_mr *mr); +void mlx5_vdpa_get_mr(struct mlx5_vdpa_dev *mvdev, + struct mlx5_vdpa_mr *mr); +void mlx5_vdpa_put_mr(struct mlx5_vdpa_dev *mvdev, + struct mlx5_vdpa_mr *mr); void mlx5_vdpa_update_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr, unsigned int asid); diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c index 2197c46e563a..4758914ccf86 100644 --- a/drivers/vdpa/mlx5/core/mr.c +++ b/drivers/vdpa/mlx5/core/mr.c @@ -498,32 +498,54 @@ static void destroy_user_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr static void _mlx5_vdpa_destroy_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr) { + if (WARN_ON(!mr)) + return; + if (mr->user_mr) destroy_user_mr(mvdev, mr); else destroy_dma_mr(mvdev, mr); vhost_iotlb_free(mr->iotlb); + + list_del(&mr->mr_list); + + kfree(mr); } -void mlx5_vdpa_destroy_mr(struct mlx5_vdpa_dev *mvdev, - struct mlx5_vdpa_mr *mr) +static void _mlx5_vdpa_put_mr(struct mlx5_vdpa_dev *mvdev, + struct mlx5_vdpa_mr *mr) { if (!mr) return; + if (refcount_dec_and_test(&mr->refcount)) + _mlx5_vdpa_destroy_mr(mvdev, mr); +} + +void mlx5_vdpa_put_mr(struct mlx5_vdpa_dev *mvdev, + struct mlx5_vdpa_mr *mr) +{ mutex_lock(&mvdev->mr_mtx); + _mlx5_vdpa_put_mr(mvdev, mr); + mutex_unlock(&mvdev->mr_mtx); +} - _mlx5_vdpa_destroy_mr(mvdev, mr); +static void _mlx5_vdpa_get_mr(struct mlx5_vdpa_dev *mvdev, + struct mlx5_vdpa_mr *mr) +{ + if (!mr) + return; - for (int i = 0; i < MLX5_VDPA_NUM_AS; i++) { - if (mvdev->mr[i] == mr) - mvdev->mr[i] = NULL; - } + refcount_inc(&mr->refcount); +} +void mlx5_vdpa_get_mr(struct mlx5_vdpa_dev *mvdev, + struct mlx5_vdpa_mr *mr) +{ + mutex_lock(&mvdev->mr_mtx); + _mlx5_vdpa_get_mr(mvdev, mr); mutex_unlock(&mvdev->mr_mtx); - - kfree(mr); } void mlx5_vdpa_update_mr(struct mlx5_vdpa_dev *mvdev, @@ -534,10 +556,23 @@ void mlx5_vdpa_update_mr(struct mlx5_vdpa_dev *mvdev, mutex_lock(&mvdev->mr_mtx); + _mlx5_vdpa_put_mr(mvdev, old_mr); mvdev->mr[asid] = new_mr; - if (old_mr) { - _mlx5_vdpa_destroy_mr(mvdev, old_mr); - kfree(old_mr); + + mutex_unlock(&mvdev->mr_mtx); +} + +static void mlx5_vdpa_show_mr_leaks(struct mlx5_vdpa_dev *mvdev) +{ + struct mlx5_vdpa_mr *mr; + + mutex_lock(&mvdev->mr_mtx); + + list_for_each_entry(mr, &mvdev->mr_list_head, mr_list) { + + mlx5_vdpa_warn(mvdev, "mkey still alive after resource delete: " + "mr: %p, mkey: 0x%x, refcount: %u\n", + mr, mr->mkey, refcount_read(&mr->refcount)); } mutex_unlock(&mvdev->mr_mtx); @@ -547,9 +582,11 @@ void mlx5_vdpa_update_mr(struct mlx5_vdpa_dev *mvdev, void mlx5_vdpa_destroy_mr_resources(struct mlx5_vdpa_dev *mvdev) { for (int i = 0; i < MLX5_VDPA_NUM_AS; i++) - mlx5_vdpa_destroy_mr(mvdev, mvdev->mr[i]); + mlx5_vdpa_update_mr(mvdev, NULL, i); prune_iotlb(mvdev->cvq.iotlb); + + mlx5_vdpa_show_mr_leaks(mvdev); } static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, @@ -576,6 +613,8 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, if (err) goto err_iotlb; + list_add_tail(&mr->mr_list, &mvdev->mr_list_head); + return 0; err_iotlb: @@ -607,6 +646,8 @@ struct mlx5_vdpa_mr *mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, if (err) goto out_err; + refcount_set(&mr->refcount, 1); + return mr; out_err: @@ -651,7 +692,7 @@ int mlx5_vdpa_reset_mr(struct mlx5_vdpa_dev *mvdev, unsigned int asid) if (asid >= MLX5_VDPA_NUM_AS) return -EINVAL; - mlx5_vdpa_destroy_mr(mvdev, mvdev->mr[asid]); + mlx5_vdpa_update_mr(mvdev, NULL, asid); if (asid == 0 && MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) { if (mlx5_vdpa_create_dma_mr(mvdev)) diff --git a/drivers/vdpa/mlx5/net/mlx5_vnet.c b/drivers/vdpa/mlx5/net/mlx5_vnet.c index 26ba7da6b410..778821bab7d9 100644 --- a/drivers/vdpa/mlx5/net/mlx5_vnet.c +++ b/drivers/vdpa/mlx5/net/mlx5_vnet.c @@ -120,6 +120,12 @@ struct mlx5_vdpa_virtqueue { u16 avail_idx; u16 used_idx; int fw_state; + + u64 modified_fields; + + struct mlx5_vdpa_mr *vq_mr; + struct mlx5_vdpa_mr *desc_mr; + struct msi_map map; /* keep last in the struct */ @@ -943,6 +949,14 @@ static int create_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtque kfree(in); mvq->virtq_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id); + mlx5_vdpa_get_mr(mvdev, vq_mr); + mvq->vq_mr = vq_mr; + + if (vq_desc_mr && MLX5_CAP_DEV_VDPA_EMULATION(mvdev->mdev, desc_group_mkey_supported)) { + mlx5_vdpa_get_mr(mvdev, vq_desc_mr); + mvq->desc_mr = vq_desc_mr; + } + return 0; err_cmd: @@ -969,6 +983,12 @@ static void destroy_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtq } mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_NONE; umems_destroy(ndev, mvq); + + mlx5_vdpa_put_mr(&ndev->mvdev, mvq->vq_mr); + mvq->vq_mr = NULL; + + mlx5_vdpa_put_mr(&ndev->mvdev, mvq->desc_mr); + mvq->desc_mr = NULL; } static u32 get_rqpn(struct mlx5_vdpa_virtqueue *mvq, bool fw) @@ -1167,7 +1187,12 @@ err_cmd: return err; } -static bool is_valid_state_change(int oldstate, int newstate) +static bool is_resumable(struct mlx5_vdpa_net *ndev) +{ + return ndev->mvdev.vdev.config->resume; +} + +static bool is_valid_state_change(int oldstate, int newstate, bool resumable) { switch (oldstate) { case MLX5_VIRTIO_NET_Q_OBJECT_STATE_INIT: @@ -1175,25 +1200,43 @@ static bool is_valid_state_change(int oldstate, int newstate) case MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY: return newstate == MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND; case MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND: + return resumable ? newstate == MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY : false; case MLX5_VIRTIO_NET_Q_OBJECT_STATE_ERR: default: return false; } } -static int modify_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, int state) +static bool modifiable_virtqueue_fields(struct mlx5_vdpa_virtqueue *mvq) +{ + /* Only state is always modifiable */ + if (mvq->modified_fields & ~MLX5_VIRTQ_MODIFY_MASK_STATE) + return mvq->fw_state == MLX5_VIRTIO_NET_Q_OBJECT_STATE_INIT || + mvq->fw_state == MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND; + + return true; +} + +static int modify_virtqueue(struct mlx5_vdpa_net *ndev, + struct mlx5_vdpa_virtqueue *mvq, + int state) { int inlen = MLX5_ST_SZ_BYTES(modify_virtio_net_q_in); u32 out[MLX5_ST_SZ_DW(modify_virtio_net_q_out)] = {}; + struct mlx5_vdpa_dev *mvdev = &ndev->mvdev; + struct mlx5_vdpa_mr *desc_mr = NULL; + struct mlx5_vdpa_mr *vq_mr = NULL; + bool state_change = false; void *obj_context; void *cmd_hdr; + void *vq_ctx; void *in; int err; if (mvq->fw_state == MLX5_VIRTIO_NET_Q_OBJECT_NONE) return 0; - if (!is_valid_state_change(mvq->fw_state, state)) + if (!modifiable_virtqueue_fields(mvq)) return -EINVAL; in = kzalloc(inlen, GFP_KERNEL); @@ -1208,17 +1251,83 @@ static int modify_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtque MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid); obj_context = MLX5_ADDR_OF(modify_virtio_net_q_in, in, obj_context); - MLX5_SET64(virtio_net_q_object, obj_context, modify_field_select, - MLX5_VIRTQ_MODIFY_MASK_STATE); - MLX5_SET(virtio_net_q_object, obj_context, state, state); + vq_ctx = MLX5_ADDR_OF(virtio_net_q_object, obj_context, virtio_q_context); + + if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_STATE) { + if (!is_valid_state_change(mvq->fw_state, state, is_resumable(ndev))) { + err = -EINVAL; + goto done; + } + + MLX5_SET(virtio_net_q_object, obj_context, state, state); + state_change = true; + } + + if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_ADDRS) { + MLX5_SET64(virtio_q, vq_ctx, desc_addr, mvq->desc_addr); + MLX5_SET64(virtio_q, vq_ctx, used_addr, mvq->device_addr); + MLX5_SET64(virtio_q, vq_ctx, available_addr, mvq->driver_addr); + } + + if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_AVAIL_IDX) + MLX5_SET(virtio_net_q_object, obj_context, hw_available_index, mvq->avail_idx); + + if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_USED_IDX) + MLX5_SET(virtio_net_q_object, obj_context, hw_used_index, mvq->used_idx); + + if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_MKEY) { + vq_mr = mvdev->mr[mvdev->group2asid[MLX5_VDPA_DATAVQ_GROUP]]; + + if (vq_mr) + MLX5_SET(virtio_q, vq_ctx, virtio_q_mkey, vq_mr->mkey); + else + mvq->modified_fields &= ~MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_MKEY; + } + + if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_DESC_GROUP_MKEY) { + desc_mr = mvdev->mr[mvdev->group2asid[MLX5_VDPA_DATAVQ_DESC_GROUP]]; + + if (desc_mr && MLX5_CAP_DEV_VDPA_EMULATION(mvdev->mdev, desc_group_mkey_supported)) + MLX5_SET(virtio_q, vq_ctx, desc_group_mkey, desc_mr->mkey); + else + mvq->modified_fields &= ~MLX5_VIRTQ_MODIFY_MASK_DESC_GROUP_MKEY; + } + + MLX5_SET64(virtio_net_q_object, obj_context, modify_field_select, mvq->modified_fields); err = mlx5_cmd_exec(ndev->mvdev.mdev, in, inlen, out, sizeof(out)); - kfree(in); - if (!err) + if (err) + goto done; + + if (state_change) mvq->fw_state = state; + if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_MKEY) { + mlx5_vdpa_put_mr(mvdev, mvq->vq_mr); + mlx5_vdpa_get_mr(mvdev, vq_mr); + mvq->vq_mr = vq_mr; + } + + if (mvq->modified_fields & MLX5_VIRTQ_MODIFY_MASK_DESC_GROUP_MKEY) { + mlx5_vdpa_put_mr(mvdev, mvq->desc_mr); + mlx5_vdpa_get_mr(mvdev, desc_mr); + mvq->desc_mr = desc_mr; + } + + mvq->modified_fields = 0; + +done: + kfree(in); return err; } +static int modify_virtqueue_state(struct mlx5_vdpa_net *ndev, + struct mlx5_vdpa_virtqueue *mvq, + unsigned int state) +{ + mvq->modified_fields |= MLX5_VIRTQ_MODIFY_MASK_STATE; + return modify_virtqueue(ndev, mvq, state); +} + static int counter_set_alloc(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq) { u32 in[MLX5_ST_SZ_DW(create_virtio_q_counters_in)] = {}; @@ -1347,7 +1456,7 @@ static int setup_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq) goto err_vq; if (mvq->ready) { - err = modify_virtqueue(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY); + err = modify_virtqueue_state(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY); if (err) { mlx5_vdpa_warn(&ndev->mvdev, "failed to modify to ready vq idx %d(%d)\n", idx, err); @@ -1382,7 +1491,7 @@ static void suspend_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *m if (mvq->fw_state != MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY) return; - if (modify_virtqueue(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND)) + if (modify_virtqueue_state(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND)) mlx5_vdpa_warn(&ndev->mvdev, "modify to suspend failed\n"); if (query_virtqueue(ndev, mvq, &attr)) { @@ -1401,12 +1510,31 @@ static void suspend_vqs(struct mlx5_vdpa_net *ndev) suspend_vq(ndev, &ndev->vqs[i]); } +static void resume_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq) +{ + if (!mvq->initialized || !is_resumable(ndev)) + return; + + if (mvq->fw_state != MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND) + return; + + if (modify_virtqueue_state(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY)) + mlx5_vdpa_warn(&ndev->mvdev, "modify to resume failed for vq %u\n", mvq->index); +} + +static void resume_vqs(struct mlx5_vdpa_net *ndev) +{ + for (int i = 0; i < ndev->mvdev.max_vqs; i++) + resume_vq(ndev, &ndev->vqs[i]); +} + static void teardown_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq) { if (!mvq->initialized) return; suspend_vq(ndev, mvq); + mvq->modified_fields = 0; destroy_virtqueue(ndev, mvq); dealloc_vector(ndev, mvq); counter_set_dealloc(ndev, mvq); @@ -2138,6 +2266,7 @@ static int mlx5_vdpa_set_vq_address(struct vdpa_device *vdev, u16 idx, u64 desc_ mvq->desc_addr = desc_area; mvq->device_addr = device_area; mvq->driver_addr = driver_area; + mvq->modified_fields |= MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_ADDRS; return 0; } @@ -2207,7 +2336,7 @@ static void mlx5_vdpa_set_vq_ready(struct vdpa_device *vdev, u16 idx, bool ready if (!ready) { suspend_vq(ndev, mvq); } else { - err = modify_virtqueue(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY); + err = modify_virtqueue_state(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY); if (err) { mlx5_vdpa_warn(mvdev, "modify VQ %d to ready failed (%d)\n", idx, err); ready = false; @@ -2255,6 +2384,8 @@ static int mlx5_vdpa_set_vq_state(struct vdpa_device *vdev, u16 idx, mvq->used_idx = state->split.avail_index; mvq->avail_idx = state->split.avail_index; + mvq->modified_fields |= MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_AVAIL_IDX | + MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_USED_IDX; return 0; } @@ -2703,24 +2834,35 @@ static int mlx5_vdpa_change_map(struct mlx5_vdpa_dev *mvdev, unsigned int asid) { struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); + bool teardown = !is_resumable(ndev); int err; suspend_vqs(ndev); - err = save_channels_info(ndev); - if (err) - return err; + if (teardown) { + err = save_channels_info(ndev); + if (err) + return err; - teardown_driver(ndev); + teardown_driver(ndev); + } mlx5_vdpa_update_mr(mvdev, new_mr, asid); + for (int i = 0; i < ndev->cur_num_vqs; i++) + ndev->vqs[i].modified_fields |= MLX5_VIRTQ_MODIFY_MASK_VIRTIO_Q_MKEY | + MLX5_VIRTQ_MODIFY_MASK_DESC_GROUP_MKEY; + if (!(mvdev->status & VIRTIO_CONFIG_S_DRIVER_OK) || mvdev->suspended) return 0; - restore_channels_info(ndev); - err = setup_driver(mvdev); - if (err) - return err; + if (teardown) { + restore_channels_info(ndev); + err = setup_driver(mvdev); + if (err) + return err; + } + + resume_vqs(ndev); return 0; } @@ -2804,8 +2946,10 @@ static void clear_vqs_ready(struct mlx5_vdpa_net *ndev) { int i; - for (i = 0; i < ndev->mvdev.max_vqs; i++) + for (i = 0; i < ndev->mvdev.max_vqs; i++) { ndev->vqs[i].ready = false; + ndev->vqs[i].modified_fields = 0; + } ndev->mvdev.cvq.ready = false; } @@ -2982,7 +3126,7 @@ static int set_map_data(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb, return mlx5_vdpa_update_cvq_iotlb(mvdev, iotlb, asid); out_err: - mlx5_vdpa_destroy_mr(mvdev, new_mr); + mlx5_vdpa_put_mr(mvdev, new_mr); return err; } @@ -3229,6 +3373,23 @@ static int mlx5_vdpa_suspend(struct vdpa_device *vdev) return 0; } +static int mlx5_vdpa_resume(struct vdpa_device *vdev) +{ + struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); + struct mlx5_vdpa_net *ndev; + + ndev = to_mlx5_vdpa_ndev(mvdev); + + mlx5_vdpa_info(mvdev, "resuming device\n"); + + down_write(&ndev->reslock); + mvdev->suspended = false; + resume_vqs(ndev); + register_link_notifier(ndev); + up_write(&ndev->reslock); + return 0; +} + static int mlx5_set_group_asid(struct vdpa_device *vdev, u32 group, unsigned int asid) { @@ -3285,6 +3446,7 @@ static const struct vdpa_config_ops mlx5_vdpa_ops = { .get_vq_dma_dev = mlx5_get_vq_dma_dev, .free = mlx5_vdpa_free, .suspend = mlx5_vdpa_suspend, + .resume = mlx5_vdpa_resume, /* Op disabled if not supported. */ }; static int query_mtu(struct mlx5_core_dev *mdev, u16 *mtu) @@ -3560,6 +3722,8 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name, if (err) goto err_mpfs; + INIT_LIST_HEAD(&mvdev->mr_list_head); + if (MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) { err = mlx5_vdpa_create_dma_mr(mvdev); if (err) @@ -3656,6 +3820,9 @@ static int mlx5v_probe(struct auxiliary_device *adev, if (!MLX5_CAP_DEV_VDPA_EMULATION(mdev, desc_group_mkey_supported)) mgtdev->vdpa_ops.get_vq_desc_group = NULL; + if (!MLX5_CAP_DEV_VDPA_EMULATION(mdev, freeze_to_rdy_supported)) + mgtdev->vdpa_ops.resume = NULL; + err = vdpa_mgmtdev_register(&mgtdev->mgtdev); if (err) goto reg_err; diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index a7612e0783b3..d0695680b282 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -131,7 +131,7 @@ static void vdpa_release_dev(struct device *d) if (ops->free) ops->free(vdev); - ida_simple_remove(&vdpa_index_ida, vdev->index); + ida_free(&vdpa_index_ida, vdev->index); kfree(vdev->driver_override); kfree(vdev); } @@ -205,7 +205,7 @@ struct vdpa_device *__vdpa_alloc_device(struct device *parent, return vdev; err_name: - ida_simple_remove(&vdpa_index_ida, vdev->index); + ida_free(&vdpa_index_ida, vdev->index); err_ida: kfree(vdev); err: diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index 6bda6dbb4878..ceae52fd7586 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -80,6 +80,16 @@ config VFIO_VIRQFD select EVENTFD default n +config VFIO_DEBUGFS + bool "Export VFIO internals in DebugFS" + depends on DEBUG_FS + help + Allows exposure of VFIO device internals. This option enables + the use of debugfs by VFIO drivers as required. The device can + cause the VFIO code create a top-level debug/vfio directory + during initialization, and then populate a subdirectory with + entries as required. + source "drivers/vfio/pci/Kconfig" source "drivers/vfio/platform/Kconfig" source "drivers/vfio/mdev/Kconfig" diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index 68c05705200f..b2fc9fb499d8 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile @@ -7,6 +7,7 @@ vfio-$(CONFIG_VFIO_GROUP) += group.o vfio-$(CONFIG_IOMMUFD) += iommufd.o vfio-$(CONFIG_VFIO_CONTAINER) += container.o vfio-$(CONFIG_VFIO_VIRQFD) += virqfd.o +vfio-$(CONFIG_VFIO_DEBUGFS) += debugfs.o obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o diff --git a/drivers/vfio/debugfs.c b/drivers/vfio/debugfs.c new file mode 100644 index 000000000000..298bd866f157 --- /dev/null +++ b/drivers/vfio/debugfs.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, HiSilicon Ltd. + */ + +#include <linux/device.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/vfio.h> +#include "vfio.h" + +static struct dentry *vfio_debugfs_root; + +static int vfio_device_state_read(struct seq_file *seq, void *data) +{ + struct device *vf_dev = seq->private; + struct vfio_device *vdev = container_of(vf_dev, + struct vfio_device, device); + enum vfio_device_mig_state state; + int ret; + + BUILD_BUG_ON(VFIO_DEVICE_STATE_NR != + VFIO_DEVICE_STATE_PRE_COPY_P2P + 1); + + ret = vdev->mig_ops->migration_get_state(vdev, &state); + if (ret) + return -EINVAL; + + switch (state) { + case VFIO_DEVICE_STATE_ERROR: + seq_puts(seq, "ERROR\n"); + break; + case VFIO_DEVICE_STATE_STOP: + seq_puts(seq, "STOP\n"); + break; + case VFIO_DEVICE_STATE_RUNNING: + seq_puts(seq, "RUNNING\n"); + break; + case VFIO_DEVICE_STATE_STOP_COPY: + seq_puts(seq, "STOP_COPY\n"); + break; + case VFIO_DEVICE_STATE_RESUMING: + seq_puts(seq, "RESUMING\n"); + break; + case VFIO_DEVICE_STATE_RUNNING_P2P: + seq_puts(seq, "RUNNING_P2P\n"); + break; + case VFIO_DEVICE_STATE_PRE_COPY: + seq_puts(seq, "PRE_COPY\n"); + break; + case VFIO_DEVICE_STATE_PRE_COPY_P2P: + seq_puts(seq, "PRE_COPY_P2P\n"); + break; + default: + seq_puts(seq, "Invalid\n"); + } + + return 0; +} + +void vfio_device_debugfs_init(struct vfio_device *vdev) +{ + struct device *dev = &vdev->device; + + vdev->debug_root = debugfs_create_dir(dev_name(vdev->dev), + vfio_debugfs_root); + + if (vdev->mig_ops) { + struct dentry *vfio_dev_migration = NULL; + + vfio_dev_migration = debugfs_create_dir("migration", + vdev->debug_root); + debugfs_create_devm_seqfile(dev, "state", vfio_dev_migration, + vfio_device_state_read); + } +} + +void vfio_device_debugfs_exit(struct vfio_device *vdev) +{ + debugfs_remove_recursive(vdev->debug_root); +} + +void vfio_debugfs_create_root(void) +{ + vfio_debugfs_root = debugfs_create_dir("vfio", NULL); +} + +void vfio_debugfs_remove_root(void) +{ + debugfs_remove_recursive(vfio_debugfs_root); + vfio_debugfs_root = NULL; +} diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig index 8125e5f37832..18c397df566d 100644 --- a/drivers/vfio/pci/Kconfig +++ b/drivers/vfio/pci/Kconfig @@ -65,4 +65,6 @@ source "drivers/vfio/pci/hisilicon/Kconfig" source "drivers/vfio/pci/pds/Kconfig" +source "drivers/vfio/pci/virtio/Kconfig" + endmenu diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile index 45167be462d8..046139a4eca5 100644 --- a/drivers/vfio/pci/Makefile +++ b/drivers/vfio/pci/Makefile @@ -13,3 +13,5 @@ obj-$(CONFIG_MLX5_VFIO_PCI) += mlx5/ obj-$(CONFIG_HISI_ACC_VFIO_PCI) += hisilicon/ obj-$(CONFIG_PDS_VFIO_PCI) += pds/ + +obj-$(CONFIG_VIRTIO_VFIO_PCI) += virtio/ diff --git a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c index b2f9778c8366..4d27465c8f1a 100644 --- a/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c +++ b/drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c @@ -694,6 +694,7 @@ static ssize_t hisi_acc_vf_resume_write(struct file *filp, const char __user *bu size_t len, loff_t *pos) { struct hisi_acc_vf_migration_file *migf = filp->private_data; + u8 *vf_data = (u8 *)&migf->vf_data; loff_t requested_length; ssize_t done = 0; int ret; @@ -715,7 +716,7 @@ static ssize_t hisi_acc_vf_resume_write(struct file *filp, const char __user *bu goto out_unlock; } - ret = copy_from_user(&migf->vf_data, buf, len); + ret = copy_from_user(vf_data + *pos, buf, len); if (ret) { done = -EFAULT; goto out_unlock; @@ -835,7 +836,9 @@ static ssize_t hisi_acc_vf_save_read(struct file *filp, char __user *buf, size_t len = min_t(size_t, migf->total_length - *pos, len); if (len) { - ret = copy_to_user(buf, &migf->vf_data, len); + u8 *vf_data = (u8 *)&migf->vf_data; + + ret = copy_to_user(buf, vf_data + *pos, len); if (ret) { done = -EFAULT; goto out_unlock; diff --git a/drivers/vfio/pci/pds/dirty.c b/drivers/vfio/pci/pds/dirty.c index c937aa6f3954..8ddf4346fcd5 100644 --- a/drivers/vfio/pci/pds/dirty.c +++ b/drivers/vfio/pci/pds/dirty.c @@ -70,7 +70,7 @@ out_free_region_info: kfree(region_info); } -static int pds_vfio_dirty_alloc_bitmaps(struct pds_vfio_dirty *dirty, +static int pds_vfio_dirty_alloc_bitmaps(struct pds_vfio_region *region, unsigned long bytes) { unsigned long *host_seq_bmp, *host_ack_bmp; @@ -85,47 +85,63 @@ static int pds_vfio_dirty_alloc_bitmaps(struct pds_vfio_dirty *dirty, return -ENOMEM; } - dirty->host_seq.bmp = host_seq_bmp; - dirty->host_ack.bmp = host_ack_bmp; + region->host_seq = host_seq_bmp; + region->host_ack = host_ack_bmp; + region->bmp_bytes = bytes; return 0; } static void pds_vfio_dirty_free_bitmaps(struct pds_vfio_dirty *dirty) { - vfree(dirty->host_seq.bmp); - vfree(dirty->host_ack.bmp); - dirty->host_seq.bmp = NULL; - dirty->host_ack.bmp = NULL; + if (!dirty->regions) + return; + + for (int i = 0; i < dirty->num_regions; i++) { + struct pds_vfio_region *region = &dirty->regions[i]; + + vfree(region->host_seq); + vfree(region->host_ack); + region->host_seq = NULL; + region->host_ack = NULL; + region->bmp_bytes = 0; + } } static void __pds_vfio_dirty_free_sgl(struct pds_vfio_pci_device *pds_vfio, - struct pds_vfio_bmp_info *bmp_info) + struct pds_vfio_region *region) { struct pci_dev *pdev = pds_vfio->vfio_coredev.pdev; struct device *pdsc_dev = &pci_physfn(pdev)->dev; - dma_unmap_single(pdsc_dev, bmp_info->sgl_addr, - bmp_info->num_sge * sizeof(struct pds_lm_sg_elem), + dma_unmap_single(pdsc_dev, region->sgl_addr, + region->num_sge * sizeof(struct pds_lm_sg_elem), DMA_BIDIRECTIONAL); - kfree(bmp_info->sgl); + kfree(region->sgl); - bmp_info->num_sge = 0; - bmp_info->sgl = NULL; - bmp_info->sgl_addr = 0; + region->num_sge = 0; + region->sgl = NULL; + region->sgl_addr = 0; } static void pds_vfio_dirty_free_sgl(struct pds_vfio_pci_device *pds_vfio) { - if (pds_vfio->dirty.host_seq.sgl) - __pds_vfio_dirty_free_sgl(pds_vfio, &pds_vfio->dirty.host_seq); - if (pds_vfio->dirty.host_ack.sgl) - __pds_vfio_dirty_free_sgl(pds_vfio, &pds_vfio->dirty.host_ack); + struct pds_vfio_dirty *dirty = &pds_vfio->dirty; + + if (!dirty->regions) + return; + + for (int i = 0; i < dirty->num_regions; i++) { + struct pds_vfio_region *region = &dirty->regions[i]; + + if (region->sgl) + __pds_vfio_dirty_free_sgl(pds_vfio, region); + } } -static int __pds_vfio_dirty_alloc_sgl(struct pds_vfio_pci_device *pds_vfio, - struct pds_vfio_bmp_info *bmp_info, - u32 page_count) +static int pds_vfio_dirty_alloc_sgl(struct pds_vfio_pci_device *pds_vfio, + struct pds_vfio_region *region, + u32 page_count) { struct pci_dev *pdev = pds_vfio->vfio_coredev.pdev; struct device *pdsc_dev = &pci_physfn(pdev)->dev; @@ -147,32 +163,81 @@ static int __pds_vfio_dirty_alloc_sgl(struct pds_vfio_pci_device *pds_vfio, return -EIO; } - bmp_info->sgl = sgl; - bmp_info->num_sge = max_sge; - bmp_info->sgl_addr = sgl_addr; + region->sgl = sgl; + region->num_sge = max_sge; + region->sgl_addr = sgl_addr; return 0; } -static int pds_vfio_dirty_alloc_sgl(struct pds_vfio_pci_device *pds_vfio, - u32 page_count) +static void pds_vfio_dirty_free_regions(struct pds_vfio_dirty *dirty) { + vfree(dirty->regions); + dirty->regions = NULL; + dirty->num_regions = 0; +} + +static int pds_vfio_dirty_alloc_regions(struct pds_vfio_pci_device *pds_vfio, + struct pds_lm_dirty_region_info *region_info, + u64 region_page_size, u8 num_regions) +{ + struct pci_dev *pdev = pds_vfio->vfio_coredev.pdev; struct pds_vfio_dirty *dirty = &pds_vfio->dirty; + u32 dev_bmp_offset_byte = 0; int err; - err = __pds_vfio_dirty_alloc_sgl(pds_vfio, &dirty->host_seq, - page_count); - if (err) - return err; + dirty->regions = vcalloc(num_regions, sizeof(struct pds_vfio_region)); + if (!dirty->regions) + return -ENOMEM; + dirty->num_regions = num_regions; + + for (int i = 0; i < num_regions; i++) { + struct pds_lm_dirty_region_info *ri = ®ion_info[i]; + struct pds_vfio_region *region = &dirty->regions[i]; + u64 region_size, region_start; + u32 page_count; + + /* page_count might be adjusted by the device */ + page_count = le32_to_cpu(ri->page_count); + region_start = le64_to_cpu(ri->dma_base); + region_size = page_count * region_page_size; + + err = pds_vfio_dirty_alloc_bitmaps(region, + page_count / BITS_PER_BYTE); + if (err) { + dev_err(&pdev->dev, "Failed to alloc dirty bitmaps: %pe\n", + ERR_PTR(err)); + goto out_free_regions; + } - err = __pds_vfio_dirty_alloc_sgl(pds_vfio, &dirty->host_ack, - page_count); - if (err) { - __pds_vfio_dirty_free_sgl(pds_vfio, &dirty->host_seq); - return err; + err = pds_vfio_dirty_alloc_sgl(pds_vfio, region, page_count); + if (err) { + dev_err(&pdev->dev, "Failed to alloc dirty sg lists: %pe\n", + ERR_PTR(err)); + goto out_free_regions; + } + + region->size = region_size; + region->start = region_start; + region->page_size = region_page_size; + region->dev_bmp_offset_start_byte = dev_bmp_offset_byte; + + dev_bmp_offset_byte += page_count / BITS_PER_BYTE; + if (dev_bmp_offset_byte % BITS_PER_BYTE) { + dev_err(&pdev->dev, "Device bitmap offset is mis-aligned\n"); + err = -EINVAL; + goto out_free_regions; + } } return 0; + +out_free_regions: + pds_vfio_dirty_free_bitmaps(dirty); + pds_vfio_dirty_free_sgl(pds_vfio); + pds_vfio_dirty_free_regions(dirty); + + return err; } static int pds_vfio_dirty_enable(struct pds_vfio_pci_device *pds_vfio, @@ -181,16 +246,14 @@ static int pds_vfio_dirty_enable(struct pds_vfio_pci_device *pds_vfio, { struct pci_dev *pdev = pds_vfio->vfio_coredev.pdev; struct device *pdsc_dev = &pci_physfn(pdev)->dev; - struct pds_vfio_dirty *dirty = &pds_vfio->dirty; - u64 region_start, region_size, region_page_size; struct pds_lm_dirty_region_info *region_info; struct interval_tree_node *node = NULL; + u64 region_page_size = *page_size; u8 max_regions = 0, num_regions; dma_addr_t regions_dma = 0; u32 num_ranges = nnodes; - u32 page_count; - u16 len; int err; + u16 len; dev_dbg(&pdev->dev, "vf%u: Start dirty page tracking\n", pds_vfio->vf_id); @@ -217,39 +280,38 @@ static int pds_vfio_dirty_enable(struct pds_vfio_pci_device *pds_vfio, return -EOPNOTSUPP; } - /* - * Only support 1 region for now. If there are any large gaps in the - * VM's address regions, then this would be a waste of memory as we are - * generating 2 bitmaps (ack/seq) from the min address to the max - * address of the VM's address regions. In the future, if we support - * more than one region in the device/driver we can split the bitmaps - * on the largest address region gaps. We can do this split up to the - * max_regions times returned from the dirty_status command. - */ - max_regions = 1; if (num_ranges > max_regions) { vfio_combine_iova_ranges(ranges, nnodes, max_regions); num_ranges = max_regions; } + region_info = kcalloc(num_ranges, sizeof(*region_info), GFP_KERNEL); + if (!region_info) + return -ENOMEM; + len = num_ranges * sizeof(*region_info); + node = interval_tree_iter_first(ranges, 0, ULONG_MAX); if (!node) return -EINVAL; + for (int i = 0; i < num_ranges; i++) { + struct pds_lm_dirty_region_info *ri = ®ion_info[i]; + u64 region_size = node->last - node->start + 1; + u64 region_start = node->start; + u32 page_count; - region_size = node->last - node->start + 1; - region_start = node->start; - region_page_size = *page_size; + page_count = DIV_ROUND_UP(region_size, region_page_size); - len = sizeof(*region_info); - region_info = kzalloc(len, GFP_KERNEL); - if (!region_info) - return -ENOMEM; + ri->dma_base = cpu_to_le64(region_start); + ri->page_count = cpu_to_le32(page_count); + ri->page_size_log2 = ilog2(region_page_size); - page_count = DIV_ROUND_UP(region_size, region_page_size); + dev_dbg(&pdev->dev, + "region_info[%d]: region_start 0x%llx region_end 0x%lx region_size 0x%llx page_count %u page_size %llu\n", + i, region_start, node->last, region_size, page_count, + region_page_size); - region_info->dma_base = cpu_to_le64(region_start); - region_info->page_count = cpu_to_le32(page_count); - region_info->page_size_log2 = ilog2(region_page_size); + node = interval_tree_iter_next(node, 0, ULONG_MAX); + } regions_dma = dma_map_single(pdsc_dev, (void *)region_info, len, DMA_BIDIRECTIONAL); @@ -258,39 +320,20 @@ static int pds_vfio_dirty_enable(struct pds_vfio_pci_device *pds_vfio, goto out_free_region_info; } - err = pds_vfio_dirty_enable_cmd(pds_vfio, regions_dma, max_regions); + err = pds_vfio_dirty_enable_cmd(pds_vfio, regions_dma, num_ranges); dma_unmap_single(pdsc_dev, regions_dma, len, DMA_BIDIRECTIONAL); if (err) goto out_free_region_info; - /* - * page_count might be adjusted by the device, - * update it before freeing region_info DMA - */ - page_count = le32_to_cpu(region_info->page_count); - - dev_dbg(&pdev->dev, - "region_info: regions_dma 0x%llx dma_base 0x%llx page_count %u page_size_log2 %u\n", - regions_dma, region_start, page_count, - (u8)ilog2(region_page_size)); - - err = pds_vfio_dirty_alloc_bitmaps(dirty, page_count / BITS_PER_BYTE); - if (err) { - dev_err(&pdev->dev, "Failed to alloc dirty bitmaps: %pe\n", - ERR_PTR(err)); - goto out_free_region_info; - } - - err = pds_vfio_dirty_alloc_sgl(pds_vfio, page_count); + err = pds_vfio_dirty_alloc_regions(pds_vfio, region_info, + region_page_size, num_ranges); if (err) { - dev_err(&pdev->dev, "Failed to alloc dirty sg lists: %pe\n", - ERR_PTR(err)); - goto out_free_bitmaps; + dev_err(&pdev->dev, + "Failed to allocate %d regions for tracking dirty regions: %pe\n", + num_regions, ERR_PTR(err)); + goto out_dirty_disable; } - dirty->region_start = region_start; - dirty->region_size = region_size; - dirty->region_page_size = region_page_size; pds_vfio_dirty_set_enabled(pds_vfio); pds_vfio_print_guest_region_info(pds_vfio, max_regions); @@ -299,8 +342,8 @@ static int pds_vfio_dirty_enable(struct pds_vfio_pci_device *pds_vfio, return 0; -out_free_bitmaps: - pds_vfio_dirty_free_bitmaps(dirty); +out_dirty_disable: + pds_vfio_dirty_disable_cmd(pds_vfio); out_free_region_info: kfree(region_info); return err; @@ -314,6 +357,7 @@ void pds_vfio_dirty_disable(struct pds_vfio_pci_device *pds_vfio, bool send_cmd) pds_vfio_dirty_disable_cmd(pds_vfio); pds_vfio_dirty_free_sgl(pds_vfio); pds_vfio_dirty_free_bitmaps(&pds_vfio->dirty); + pds_vfio_dirty_free_regions(&pds_vfio->dirty); } if (send_cmd) @@ -321,8 +365,9 @@ void pds_vfio_dirty_disable(struct pds_vfio_pci_device *pds_vfio, bool send_cmd) } static int pds_vfio_dirty_seq_ack(struct pds_vfio_pci_device *pds_vfio, - struct pds_vfio_bmp_info *bmp_info, - u32 offset, u32 bmp_bytes, bool read_seq) + struct pds_vfio_region *region, + unsigned long *seq_ack_bmp, u32 offset, + u32 bmp_bytes, bool read_seq) { const char *bmp_type_str = read_seq ? "read_seq" : "write_ack"; u8 dma_dir = read_seq ? DMA_FROM_DEVICE : DMA_TO_DEVICE; @@ -339,7 +384,7 @@ static int pds_vfio_dirty_seq_ack(struct pds_vfio_pci_device *pds_vfio, int err; int i; - bmp = (void *)((u64)bmp_info->bmp + offset); + bmp = (void *)((u64)seq_ack_bmp + offset); page_offset = offset_in_page(bmp); bmp -= page_offset; @@ -375,7 +420,7 @@ static int pds_vfio_dirty_seq_ack(struct pds_vfio_pci_device *pds_vfio, goto out_free_sg_table; for_each_sgtable_dma_sg(&sg_table, sg, i) { - struct pds_lm_sg_elem *sg_elem = &bmp_info->sgl[i]; + struct pds_lm_sg_elem *sg_elem = ®ion->sgl[i]; sg_elem->addr = cpu_to_le64(sg_dma_address(sg)); sg_elem->len = cpu_to_le32(sg_dma_len(sg)); @@ -383,15 +428,16 @@ static int pds_vfio_dirty_seq_ack(struct pds_vfio_pci_device *pds_vfio, num_sge = sg_table.nents; size = num_sge * sizeof(struct pds_lm_sg_elem); - dma_sync_single_for_device(pdsc_dev, bmp_info->sgl_addr, size, dma_dir); - err = pds_vfio_dirty_seq_ack_cmd(pds_vfio, bmp_info->sgl_addr, num_sge, + offset += region->dev_bmp_offset_start_byte; + dma_sync_single_for_device(pdsc_dev, region->sgl_addr, size, dma_dir); + err = pds_vfio_dirty_seq_ack_cmd(pds_vfio, region->sgl_addr, num_sge, offset, bmp_bytes, read_seq); if (err) dev_err(&pdev->dev, "Dirty bitmap %s failed offset %u bmp_bytes %u num_sge %u DMA 0x%llx: %pe\n", bmp_type_str, offset, bmp_bytes, - num_sge, bmp_info->sgl_addr, ERR_PTR(err)); - dma_sync_single_for_cpu(pdsc_dev, bmp_info->sgl_addr, size, dma_dir); + num_sge, region->sgl_addr, ERR_PTR(err)); + dma_sync_single_for_cpu(pdsc_dev, region->sgl_addr, size, dma_dir); dma_unmap_sgtable(pdsc_dev, &sg_table, dma_dir, 0); out_free_sg_table: @@ -403,32 +449,36 @@ out_free_pages: } static int pds_vfio_dirty_write_ack(struct pds_vfio_pci_device *pds_vfio, + struct pds_vfio_region *region, u32 offset, u32 len) { - return pds_vfio_dirty_seq_ack(pds_vfio, &pds_vfio->dirty.host_ack, + + return pds_vfio_dirty_seq_ack(pds_vfio, region, region->host_ack, offset, len, WRITE_ACK); } static int pds_vfio_dirty_read_seq(struct pds_vfio_pci_device *pds_vfio, + struct pds_vfio_region *region, u32 offset, u32 len) { - return pds_vfio_dirty_seq_ack(pds_vfio, &pds_vfio->dirty.host_seq, + return pds_vfio_dirty_seq_ack(pds_vfio, region, region->host_seq, offset, len, READ_SEQ); } static int pds_vfio_dirty_process_bitmaps(struct pds_vfio_pci_device *pds_vfio, + struct pds_vfio_region *region, struct iova_bitmap *dirty_bitmap, u32 bmp_offset, u32 len_bytes) { - u64 page_size = pds_vfio->dirty.region_page_size; - u64 region_start = pds_vfio->dirty.region_start; + u64 page_size = region->page_size; + u64 region_start = region->start; u32 bmp_offset_bit; __le64 *seq, *ack; int dword_count; dword_count = len_bytes / sizeof(u64); - seq = (__le64 *)((u64)pds_vfio->dirty.host_seq.bmp + bmp_offset); - ack = (__le64 *)((u64)pds_vfio->dirty.host_ack.bmp + bmp_offset); + seq = (__le64 *)((u64)region->host_seq + bmp_offset); + ack = (__le64 *)((u64)region->host_ack + bmp_offset); bmp_offset_bit = bmp_offset * 8; for (int i = 0; i < dword_count; i++) { @@ -451,12 +501,28 @@ static int pds_vfio_dirty_process_bitmaps(struct pds_vfio_pci_device *pds_vfio, return 0; } +static struct pds_vfio_region * +pds_vfio_get_region(struct pds_vfio_pci_device *pds_vfio, unsigned long iova) +{ + struct pds_vfio_dirty *dirty = &pds_vfio->dirty; + + for (int i = 0; i < dirty->num_regions; i++) { + struct pds_vfio_region *region = &dirty->regions[i]; + + if (iova >= region->start && + iova < (region->start + region->size)) + return region; + } + + return NULL; +} + static int pds_vfio_dirty_sync(struct pds_vfio_pci_device *pds_vfio, struct iova_bitmap *dirty_bitmap, unsigned long iova, unsigned long length) { struct device *dev = &pds_vfio->vfio_coredev.pdev->dev; - struct pds_vfio_dirty *dirty = &pds_vfio->dirty; + struct pds_vfio_region *region; u64 bmp_offset, bmp_bytes; u64 bitmap_size, pages; int err; @@ -469,26 +535,31 @@ static int pds_vfio_dirty_sync(struct pds_vfio_pci_device *pds_vfio, return -EINVAL; } - pages = DIV_ROUND_UP(length, pds_vfio->dirty.region_page_size); + region = pds_vfio_get_region(pds_vfio, iova); + if (!region) { + dev_err(dev, "vf%u: Failed to find region that contains iova 0x%lx length 0x%lx\n", + pds_vfio->vf_id, iova, length); + return -EINVAL; + } + + pages = DIV_ROUND_UP(length, region->page_size); bitmap_size = round_up(pages, sizeof(u64) * BITS_PER_BYTE) / BITS_PER_BYTE; dev_dbg(dev, "vf%u: iova 0x%lx length %lu page_size %llu pages %llu bitmap_size %llu\n", - pds_vfio->vf_id, iova, length, pds_vfio->dirty.region_page_size, + pds_vfio->vf_id, iova, length, region->page_size, pages, bitmap_size); - if (!length || ((dirty->region_start + iova + length) > - (dirty->region_start + dirty->region_size))) { + if (!length || ((iova - region->start + length) > region->size)) { dev_err(dev, "Invalid iova 0x%lx and/or length 0x%lx to sync\n", iova, length); return -EINVAL; } /* bitmap is modified in 64 bit chunks */ - bmp_bytes = ALIGN(DIV_ROUND_UP(length / dirty->region_page_size, - sizeof(u64)), - sizeof(u64)); + bmp_bytes = ALIGN(DIV_ROUND_UP(length / region->page_size, + sizeof(u64)), sizeof(u64)); if (bmp_bytes != bitmap_size) { dev_err(dev, "Calculated bitmap bytes %llu not equal to bitmap size %llu\n", @@ -496,22 +567,30 @@ static int pds_vfio_dirty_sync(struct pds_vfio_pci_device *pds_vfio, return -EINVAL; } - bmp_offset = DIV_ROUND_UP(iova / dirty->region_page_size, sizeof(u64)); + if (bmp_bytes > region->bmp_bytes) { + dev_err(dev, + "Calculated bitmap bytes %llu larger than region's cached bmp_bytes %llu\n", + bmp_bytes, region->bmp_bytes); + return -EINVAL; + } + + bmp_offset = DIV_ROUND_UP((iova - region->start) / + region->page_size, sizeof(u64)); dev_dbg(dev, "Syncing dirty bitmap, iova 0x%lx length 0x%lx, bmp_offset %llu bmp_bytes %llu\n", iova, length, bmp_offset, bmp_bytes); - err = pds_vfio_dirty_read_seq(pds_vfio, bmp_offset, bmp_bytes); + err = pds_vfio_dirty_read_seq(pds_vfio, region, bmp_offset, bmp_bytes); if (err) return err; - err = pds_vfio_dirty_process_bitmaps(pds_vfio, dirty_bitmap, bmp_offset, - bmp_bytes); + err = pds_vfio_dirty_process_bitmaps(pds_vfio, region, dirty_bitmap, + bmp_offset, bmp_bytes); if (err) return err; - err = pds_vfio_dirty_write_ack(pds_vfio, bmp_offset, bmp_bytes); + err = pds_vfio_dirty_write_ack(pds_vfio, region, bmp_offset, bmp_bytes); if (err) return err; diff --git a/drivers/vfio/pci/pds/dirty.h b/drivers/vfio/pci/pds/dirty.h index f78da25d75ca..c8e23018b801 100644 --- a/drivers/vfio/pci/pds/dirty.h +++ b/drivers/vfio/pci/pds/dirty.h @@ -4,20 +4,22 @@ #ifndef _DIRTY_H_ #define _DIRTY_H_ -struct pds_vfio_bmp_info { - unsigned long *bmp; - u32 bmp_bytes; +struct pds_vfio_region { + unsigned long *host_seq; + unsigned long *host_ack; + u64 bmp_bytes; + u64 size; + u64 start; + u64 page_size; struct pds_lm_sg_elem *sgl; dma_addr_t sgl_addr; + u32 dev_bmp_offset_start_byte; u16 num_sge; }; struct pds_vfio_dirty { - struct pds_vfio_bmp_info host_seq; - struct pds_vfio_bmp_info host_ack; - u64 region_size; - u64 region_start; - u64 region_page_size; + struct pds_vfio_region *regions; + u8 num_regions; bool is_enabled; }; diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c index e27de61ac9fe..07fea08ea8a2 100644 --- a/drivers/vfio/pci/vfio_pci_rdwr.c +++ b/drivers/vfio/pci/vfio_pci_rdwr.c @@ -38,7 +38,7 @@ #define vfio_iowrite8 iowrite8 #define VFIO_IOWRITE(size) \ -static int vfio_pci_iowrite##size(struct vfio_pci_core_device *vdev, \ +int vfio_pci_core_iowrite##size(struct vfio_pci_core_device *vdev, \ bool test_mem, u##size val, void __iomem *io) \ { \ if (test_mem) { \ @@ -55,7 +55,8 @@ static int vfio_pci_iowrite##size(struct vfio_pci_core_device *vdev, \ up_read(&vdev->memory_lock); \ \ return 0; \ -} +} \ +EXPORT_SYMBOL_GPL(vfio_pci_core_iowrite##size); VFIO_IOWRITE(8) VFIO_IOWRITE(16) @@ -65,7 +66,7 @@ VFIO_IOWRITE(64) #endif #define VFIO_IOREAD(size) \ -static int vfio_pci_ioread##size(struct vfio_pci_core_device *vdev, \ +int vfio_pci_core_ioread##size(struct vfio_pci_core_device *vdev, \ bool test_mem, u##size *val, void __iomem *io) \ { \ if (test_mem) { \ @@ -82,7 +83,8 @@ static int vfio_pci_ioread##size(struct vfio_pci_core_device *vdev, \ up_read(&vdev->memory_lock); \ \ return 0; \ -} +} \ +EXPORT_SYMBOL_GPL(vfio_pci_core_ioread##size); VFIO_IOREAD(8) VFIO_IOREAD(16) @@ -119,13 +121,13 @@ static ssize_t do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem, if (copy_from_user(&val, buf, 4)) return -EFAULT; - ret = vfio_pci_iowrite32(vdev, test_mem, - val, io + off); + ret = vfio_pci_core_iowrite32(vdev, test_mem, + val, io + off); if (ret) return ret; } else { - ret = vfio_pci_ioread32(vdev, test_mem, - &val, io + off); + ret = vfio_pci_core_ioread32(vdev, test_mem, + &val, io + off); if (ret) return ret; @@ -141,13 +143,13 @@ static ssize_t do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem, if (copy_from_user(&val, buf, 2)) return -EFAULT; - ret = vfio_pci_iowrite16(vdev, test_mem, - val, io + off); + ret = vfio_pci_core_iowrite16(vdev, test_mem, + val, io + off); if (ret) return ret; } else { - ret = vfio_pci_ioread16(vdev, test_mem, - &val, io + off); + ret = vfio_pci_core_ioread16(vdev, test_mem, + &val, io + off); if (ret) return ret; @@ -163,13 +165,13 @@ static ssize_t do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem, if (copy_from_user(&val, buf, 1)) return -EFAULT; - ret = vfio_pci_iowrite8(vdev, test_mem, - val, io + off); + ret = vfio_pci_core_iowrite8(vdev, test_mem, + val, io + off); if (ret) return ret; } else { - ret = vfio_pci_ioread8(vdev, test_mem, - &val, io + off); + ret = vfio_pci_core_ioread8(vdev, test_mem, + &val, io + off); if (ret) return ret; @@ -200,7 +202,7 @@ static ssize_t do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem, return done; } -static int vfio_pci_setup_barmap(struct vfio_pci_core_device *vdev, int bar) +int vfio_pci_core_setup_barmap(struct vfio_pci_core_device *vdev, int bar) { struct pci_dev *pdev = vdev->pdev; int ret; @@ -223,6 +225,7 @@ static int vfio_pci_setup_barmap(struct vfio_pci_core_device *vdev, int bar) return 0; } +EXPORT_SYMBOL_GPL(vfio_pci_core_setup_barmap); ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf, size_t count, loff_t *ppos, bool iswrite) @@ -262,7 +265,7 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf, } x_end = end; } else { - int ret = vfio_pci_setup_barmap(vdev, bar); + int ret = vfio_pci_core_setup_barmap(vdev, bar); if (ret) { done = ret; goto out; @@ -363,21 +366,21 @@ static void vfio_pci_ioeventfd_do_write(struct vfio_pci_ioeventfd *ioeventfd, { switch (ioeventfd->count) { case 1: - vfio_pci_iowrite8(ioeventfd->vdev, test_mem, - ioeventfd->data, ioeventfd->addr); + vfio_pci_core_iowrite8(ioeventfd->vdev, test_mem, + ioeventfd->data, ioeventfd->addr); break; case 2: - vfio_pci_iowrite16(ioeventfd->vdev, test_mem, - ioeventfd->data, ioeventfd->addr); + vfio_pci_core_iowrite16(ioeventfd->vdev, test_mem, + ioeventfd->data, ioeventfd->addr); break; case 4: - vfio_pci_iowrite32(ioeventfd->vdev, test_mem, - ioeventfd->data, ioeventfd->addr); + vfio_pci_core_iowrite32(ioeventfd->vdev, test_mem, + ioeventfd->data, ioeventfd->addr); break; #ifdef iowrite64 case 8: - vfio_pci_iowrite64(ioeventfd->vdev, test_mem, - ioeventfd->data, ioeventfd->addr); + vfio_pci_core_iowrite64(ioeventfd->vdev, test_mem, + ioeventfd->data, ioeventfd->addr); break; #endif } @@ -438,7 +441,7 @@ int vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset, return -EINVAL; #endif - ret = vfio_pci_setup_barmap(vdev, bar); + ret = vfio_pci_core_setup_barmap(vdev, bar); if (ret) return ret; diff --git a/drivers/vfio/pci/virtio/Kconfig b/drivers/vfio/pci/virtio/Kconfig new file mode 100644 index 000000000000..bd80eca4a196 --- /dev/null +++ b/drivers/vfio/pci/virtio/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIRTIO_VFIO_PCI + tristate "VFIO support for VIRTIO NET PCI devices" + depends on VIRTIO_PCI && VIRTIO_PCI_ADMIN_LEGACY + select VFIO_PCI_CORE + help + This provides support for exposing VIRTIO NET VF devices which support + legacy IO access, using the VFIO framework that can work with a legacy + virtio driver in the guest. + Based on PCIe spec, VFs do not support I/O Space. + As of that this driver emulates I/O BAR in software to let a VF be + seen as a transitional device by its users and let it work with + a legacy driver. + + If you don't know what to do here, say N. diff --git a/drivers/vfio/pci/virtio/Makefile b/drivers/vfio/pci/virtio/Makefile new file mode 100644 index 000000000000..7171105baf33 --- /dev/null +++ b/drivers/vfio/pci/virtio/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VIRTIO_VFIO_PCI) += virtio-vfio-pci.o +virtio-vfio-pci-y := main.o diff --git a/drivers/vfio/pci/virtio/main.c b/drivers/vfio/pci/virtio/main.c new file mode 100644 index 000000000000..d5af683837d3 --- /dev/null +++ b/drivers/vfio/pci/virtio/main.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pci.h> +#include <linux/pm_runtime.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/vfio.h> +#include <linux/vfio_pci_core.h> +#include <linux/virtio_pci.h> +#include <linux/virtio_net.h> +#include <linux/virtio_pci_admin.h> + +struct virtiovf_pci_core_device { + struct vfio_pci_core_device core_device; + u8 *bar0_virtual_buf; + /* synchronize access to the virtual buf */ + struct mutex bar_mutex; + void __iomem *notify_addr; + u64 notify_offset; + __le32 pci_base_addr_0; + __le16 pci_cmd; + u8 bar0_virtual_buf_size; + u8 notify_bar; +}; + +static int +virtiovf_issue_legacy_rw_cmd(struct virtiovf_pci_core_device *virtvdev, + loff_t pos, char __user *buf, + size_t count, bool read) +{ + bool msix_enabled = + (virtvdev->core_device.irq_type == VFIO_PCI_MSIX_IRQ_INDEX); + struct pci_dev *pdev = virtvdev->core_device.pdev; + u8 *bar0_buf = virtvdev->bar0_virtual_buf; + bool common; + u8 offset; + int ret; + + common = pos < VIRTIO_PCI_CONFIG_OFF(msix_enabled); + /* offset within the relevant configuration area */ + offset = common ? pos : pos - VIRTIO_PCI_CONFIG_OFF(msix_enabled); + mutex_lock(&virtvdev->bar_mutex); + if (read) { + if (common) + ret = virtio_pci_admin_legacy_common_io_read(pdev, offset, + count, bar0_buf + pos); + else + ret = virtio_pci_admin_legacy_device_io_read(pdev, offset, + count, bar0_buf + pos); + if (ret) + goto out; + if (copy_to_user(buf, bar0_buf + pos, count)) + ret = -EFAULT; + } else { + if (copy_from_user(bar0_buf + pos, buf, count)) { + ret = -EFAULT; + goto out; + } + + if (common) + ret = virtio_pci_admin_legacy_common_io_write(pdev, offset, + count, bar0_buf + pos); + else + ret = virtio_pci_admin_legacy_device_io_write(pdev, offset, + count, bar0_buf + pos); + } +out: + mutex_unlock(&virtvdev->bar_mutex); + return ret; +} + +static int +virtiovf_pci_bar0_rw(struct virtiovf_pci_core_device *virtvdev, + loff_t pos, char __user *buf, + size_t count, bool read) +{ + struct vfio_pci_core_device *core_device = &virtvdev->core_device; + struct pci_dev *pdev = core_device->pdev; + u16 queue_notify; + int ret; + + if (!(le16_to_cpu(virtvdev->pci_cmd) & PCI_COMMAND_IO)) + return -EIO; + + if (pos + count > virtvdev->bar0_virtual_buf_size) + return -EINVAL; + + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret) { + pci_info_ratelimited(pdev, "runtime resume failed %d\n", ret); + return -EIO; + } + + switch (pos) { + case VIRTIO_PCI_QUEUE_NOTIFY: + if (count != sizeof(queue_notify)) { + ret = -EINVAL; + goto end; + } + if (read) { + ret = vfio_pci_core_ioread16(core_device, true, &queue_notify, + virtvdev->notify_addr); + if (ret) + goto end; + if (copy_to_user(buf, &queue_notify, + sizeof(queue_notify))) { + ret = -EFAULT; + goto end; + } + } else { + if (copy_from_user(&queue_notify, buf, count)) { + ret = -EFAULT; + goto end; + } + ret = vfio_pci_core_iowrite16(core_device, true, queue_notify, + virtvdev->notify_addr); + } + break; + default: + ret = virtiovf_issue_legacy_rw_cmd(virtvdev, pos, buf, count, + read); + } + +end: + pm_runtime_put(&pdev->dev); + return ret ? ret : count; +} + +static bool range_intersect_range(loff_t range1_start, size_t count1, + loff_t range2_start, size_t count2, + loff_t *start_offset, + size_t *intersect_count, + size_t *register_offset) +{ + if (range1_start <= range2_start && + range1_start + count1 > range2_start) { + *start_offset = range2_start - range1_start; + *intersect_count = min_t(size_t, count2, + range1_start + count1 - range2_start); + *register_offset = 0; + return true; + } + + if (range1_start > range2_start && + range1_start < range2_start + count2) { + *start_offset = 0; + *intersect_count = min_t(size_t, count1, + range2_start + count2 - range1_start); + *register_offset = range1_start - range2_start; + return true; + } + + return false; +} + +static ssize_t virtiovf_pci_read_config(struct vfio_device *core_vdev, + char __user *buf, size_t count, + loff_t *ppos) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + size_t register_offset; + loff_t copy_offset; + size_t copy_count; + __le32 val32; + __le16 val16; + u8 val8; + int ret; + + ret = vfio_pci_core_read(core_vdev, buf, count, ppos); + if (ret < 0) + return ret; + + if (range_intersect_range(pos, count, PCI_DEVICE_ID, sizeof(val16), + ©_offset, ©_count, ®ister_offset)) { + val16 = cpu_to_le16(VIRTIO_TRANS_ID_NET); + if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, copy_count)) + return -EFAULT; + } + + if ((le16_to_cpu(virtvdev->pci_cmd) & PCI_COMMAND_IO) && + range_intersect_range(pos, count, PCI_COMMAND, sizeof(val16), + ©_offset, ©_count, ®ister_offset)) { + if (copy_from_user((void *)&val16 + register_offset, buf + copy_offset, + copy_count)) + return -EFAULT; + val16 |= cpu_to_le16(PCI_COMMAND_IO); + if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, + copy_count)) + return -EFAULT; + } + + if (range_intersect_range(pos, count, PCI_REVISION_ID, sizeof(val8), + ©_offset, ©_count, ®ister_offset)) { + /* Transional needs to have revision 0 */ + val8 = 0; + if (copy_to_user(buf + copy_offset, &val8, copy_count)) + return -EFAULT; + } + + if (range_intersect_range(pos, count, PCI_BASE_ADDRESS_0, sizeof(val32), + ©_offset, ©_count, ®ister_offset)) { + u32 bar_mask = ~(virtvdev->bar0_virtual_buf_size - 1); + u32 pci_base_addr_0 = le32_to_cpu(virtvdev->pci_base_addr_0); + + val32 = cpu_to_le32((pci_base_addr_0 & bar_mask) | PCI_BASE_ADDRESS_SPACE_IO); + if (copy_to_user(buf + copy_offset, (void *)&val32 + register_offset, copy_count)) + return -EFAULT; + } + + if (range_intersect_range(pos, count, PCI_SUBSYSTEM_ID, sizeof(val16), + ©_offset, ©_count, ®ister_offset)) { + /* + * Transitional devices use the PCI subsystem device id as + * virtio device id, same as legacy driver always did. + */ + val16 = cpu_to_le16(VIRTIO_ID_NET); + if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, + copy_count)) + return -EFAULT; + } + + if (range_intersect_range(pos, count, PCI_SUBSYSTEM_VENDOR_ID, sizeof(val16), + ©_offset, ©_count, ®ister_offset)) { + val16 = cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET); + if (copy_to_user(buf + copy_offset, (void *)&val16 + register_offset, + copy_count)) + return -EFAULT; + } + + return count; +} + +static ssize_t +virtiovf_pci_core_read(struct vfio_device *core_vdev, char __user *buf, + size_t count, loff_t *ppos) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + + if (!count) + return 0; + + if (index == VFIO_PCI_CONFIG_REGION_INDEX) + return virtiovf_pci_read_config(core_vdev, buf, count, ppos); + + if (index == VFIO_PCI_BAR0_REGION_INDEX) + return virtiovf_pci_bar0_rw(virtvdev, pos, buf, count, true); + + return vfio_pci_core_read(core_vdev, buf, count, ppos); +} + +static ssize_t virtiovf_pci_write_config(struct vfio_device *core_vdev, + const char __user *buf, size_t count, + loff_t *ppos) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + size_t register_offset; + loff_t copy_offset; + size_t copy_count; + + if (range_intersect_range(pos, count, PCI_COMMAND, sizeof(virtvdev->pci_cmd), + ©_offset, ©_count, + ®ister_offset)) { + if (copy_from_user((void *)&virtvdev->pci_cmd + register_offset, + buf + copy_offset, + copy_count)) + return -EFAULT; + } + + if (range_intersect_range(pos, count, PCI_BASE_ADDRESS_0, + sizeof(virtvdev->pci_base_addr_0), + ©_offset, ©_count, + ®ister_offset)) { + if (copy_from_user((void *)&virtvdev->pci_base_addr_0 + register_offset, + buf + copy_offset, + copy_count)) + return -EFAULT; + } + + return vfio_pci_core_write(core_vdev, buf, count, ppos); +} + +static ssize_t +virtiovf_pci_core_write(struct vfio_device *core_vdev, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + + if (!count) + return 0; + + if (index == VFIO_PCI_CONFIG_REGION_INDEX) + return virtiovf_pci_write_config(core_vdev, buf, count, ppos); + + if (index == VFIO_PCI_BAR0_REGION_INDEX) + return virtiovf_pci_bar0_rw(virtvdev, pos, (char __user *)buf, count, false); + + return vfio_pci_core_write(core_vdev, buf, count, ppos); +} + +static int +virtiovf_pci_ioctl_get_region_info(struct vfio_device *core_vdev, + unsigned int cmd, unsigned long arg) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + unsigned long minsz = offsetofend(struct vfio_region_info, offset); + void __user *uarg = (void __user *)arg; + struct vfio_region_info info = {}; + + if (copy_from_user(&info, uarg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + switch (info.index) { + case VFIO_PCI_BAR0_REGION_INDEX: + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = virtvdev->bar0_virtual_buf_size; + info.flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + return copy_to_user(uarg, &info, minsz) ? -EFAULT : 0; + default: + return vfio_pci_core_ioctl(core_vdev, cmd, arg); + } +} + +static long +virtiovf_vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case VFIO_DEVICE_GET_REGION_INFO: + return virtiovf_pci_ioctl_get_region_info(core_vdev, cmd, arg); + default: + return vfio_pci_core_ioctl(core_vdev, cmd, arg); + } +} + +static int +virtiovf_set_notify_addr(struct virtiovf_pci_core_device *virtvdev) +{ + struct vfio_pci_core_device *core_device = &virtvdev->core_device; + int ret; + + /* + * Setup the BAR where the 'notify' exists to be used by vfio as well + * This will let us mmap it only once and use it when needed. + */ + ret = vfio_pci_core_setup_barmap(core_device, + virtvdev->notify_bar); + if (ret) + return ret; + + virtvdev->notify_addr = core_device->barmap[virtvdev->notify_bar] + + virtvdev->notify_offset; + return 0; +} + +static int virtiovf_pci_open_device(struct vfio_device *core_vdev) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + struct vfio_pci_core_device *vdev = &virtvdev->core_device; + int ret; + + ret = vfio_pci_core_enable(vdev); + if (ret) + return ret; + + if (virtvdev->bar0_virtual_buf) { + /* + * Upon close_device() the vfio_pci_core_disable() is called + * and will close all the previous mmaps, so it seems that the + * valid life cycle for the 'notify' addr is per open/close. + */ + ret = virtiovf_set_notify_addr(virtvdev); + if (ret) { + vfio_pci_core_disable(vdev); + return ret; + } + } + + vfio_pci_core_finish_enable(vdev); + return 0; +} + +static int virtiovf_get_device_config_size(unsigned short device) +{ + /* Network card */ + return offsetofend(struct virtio_net_config, status); +} + +static int virtiovf_read_notify_info(struct virtiovf_pci_core_device *virtvdev) +{ + u64 offset; + int ret; + u8 bar; + + ret = virtio_pci_admin_legacy_io_notify_info(virtvdev->core_device.pdev, + VIRTIO_ADMIN_CMD_NOTIFY_INFO_FLAGS_OWNER_MEM, + &bar, &offset); + if (ret) + return ret; + + virtvdev->notify_bar = bar; + virtvdev->notify_offset = offset; + return 0; +} + +static int virtiovf_pci_init_device(struct vfio_device *core_vdev) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + struct pci_dev *pdev; + int ret; + + ret = vfio_pci_core_init_dev(core_vdev); + if (ret) + return ret; + + pdev = virtvdev->core_device.pdev; + ret = virtiovf_read_notify_info(virtvdev); + if (ret) + return ret; + + virtvdev->bar0_virtual_buf_size = VIRTIO_PCI_CONFIG_OFF(true) + + virtiovf_get_device_config_size(pdev->device); + BUILD_BUG_ON(!is_power_of_2(virtvdev->bar0_virtual_buf_size)); + virtvdev->bar0_virtual_buf = kzalloc(virtvdev->bar0_virtual_buf_size, + GFP_KERNEL); + if (!virtvdev->bar0_virtual_buf) + return -ENOMEM; + mutex_init(&virtvdev->bar_mutex); + return 0; +} + +static void virtiovf_pci_core_release_dev(struct vfio_device *core_vdev) +{ + struct virtiovf_pci_core_device *virtvdev = container_of( + core_vdev, struct virtiovf_pci_core_device, core_device.vdev); + + kfree(virtvdev->bar0_virtual_buf); + vfio_pci_core_release_dev(core_vdev); +} + +static const struct vfio_device_ops virtiovf_vfio_pci_tran_ops = { + .name = "virtio-vfio-pci-trans", + .init = virtiovf_pci_init_device, + .release = virtiovf_pci_core_release_dev, + .open_device = virtiovf_pci_open_device, + .close_device = vfio_pci_core_close_device, + .ioctl = virtiovf_vfio_pci_core_ioctl, + .device_feature = vfio_pci_core_ioctl_feature, + .read = virtiovf_pci_core_read, + .write = virtiovf_pci_core_write, + .mmap = vfio_pci_core_mmap, + .request = vfio_pci_core_request, + .match = vfio_pci_core_match, + .bind_iommufd = vfio_iommufd_physical_bind, + .unbind_iommufd = vfio_iommufd_physical_unbind, + .attach_ioas = vfio_iommufd_physical_attach_ioas, + .detach_ioas = vfio_iommufd_physical_detach_ioas, +}; + +static const struct vfio_device_ops virtiovf_vfio_pci_ops = { + .name = "virtio-vfio-pci", + .init = vfio_pci_core_init_dev, + .release = vfio_pci_core_release_dev, + .open_device = virtiovf_pci_open_device, + .close_device = vfio_pci_core_close_device, + .ioctl = vfio_pci_core_ioctl, + .device_feature = vfio_pci_core_ioctl_feature, + .read = vfio_pci_core_read, + .write = vfio_pci_core_write, + .mmap = vfio_pci_core_mmap, + .request = vfio_pci_core_request, + .match = vfio_pci_core_match, + .bind_iommufd = vfio_iommufd_physical_bind, + .unbind_iommufd = vfio_iommufd_physical_unbind, + .attach_ioas = vfio_iommufd_physical_attach_ioas, + .detach_ioas = vfio_iommufd_physical_detach_ioas, +}; + +static bool virtiovf_bar0_exists(struct pci_dev *pdev) +{ + struct resource *res = pdev->resource; + + return res->flags; +} + +static int virtiovf_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + const struct vfio_device_ops *ops = &virtiovf_vfio_pci_ops; + struct virtiovf_pci_core_device *virtvdev; + int ret; + + if (pdev->is_virtfn && virtio_pci_admin_has_legacy_io(pdev) && + !virtiovf_bar0_exists(pdev)) + ops = &virtiovf_vfio_pci_tran_ops; + + virtvdev = vfio_alloc_device(virtiovf_pci_core_device, core_device.vdev, + &pdev->dev, ops); + if (IS_ERR(virtvdev)) + return PTR_ERR(virtvdev); + + dev_set_drvdata(&pdev->dev, &virtvdev->core_device); + ret = vfio_pci_core_register_device(&virtvdev->core_device); + if (ret) + goto out; + return 0; +out: + vfio_put_device(&virtvdev->core_device.vdev); + return ret; +} + +static void virtiovf_pci_remove(struct pci_dev *pdev) +{ + struct virtiovf_pci_core_device *virtvdev = dev_get_drvdata(&pdev->dev); + + vfio_pci_core_unregister_device(&virtvdev->core_device); + vfio_put_device(&virtvdev->core_device.vdev); +} + +static const struct pci_device_id virtiovf_pci_table[] = { + /* Only virtio-net is supported/tested so far */ + { PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_REDHAT_QUMRANET, 0x1041) }, + {} +}; + +MODULE_DEVICE_TABLE(pci, virtiovf_pci_table); + +static void virtiovf_pci_aer_reset_done(struct pci_dev *pdev) +{ + struct virtiovf_pci_core_device *virtvdev = dev_get_drvdata(&pdev->dev); + + virtvdev->pci_cmd = 0; +} + +static const struct pci_error_handlers virtiovf_err_handlers = { + .reset_done = virtiovf_pci_aer_reset_done, + .error_detected = vfio_pci_core_aer_err_detected, +}; + +static struct pci_driver virtiovf_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = virtiovf_pci_table, + .probe = virtiovf_pci_probe, + .remove = virtiovf_pci_remove, + .err_handler = &virtiovf_err_handlers, + .driver_managed_dma = true, +}; + +module_pci_driver(virtiovf_pci_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yishai Hadas <yishaih@nvidia.com>"); +MODULE_DESCRIPTION( + "VIRTIO VFIO PCI - User Level meta-driver for VIRTIO NET devices"); diff --git a/drivers/vfio/vfio.h b/drivers/vfio/vfio.h index 307e3f29b527..bde84ad344e5 100644 --- a/drivers/vfio/vfio.h +++ b/drivers/vfio/vfio.h @@ -448,4 +448,18 @@ static inline void vfio_device_put_kvm(struct vfio_device *device) } #endif +#ifdef CONFIG_VFIO_DEBUGFS +void vfio_debugfs_create_root(void); +void vfio_debugfs_remove_root(void); + +void vfio_device_debugfs_init(struct vfio_device *vdev); +void vfio_device_debugfs_exit(struct vfio_device *vdev); +#else +static inline void vfio_debugfs_create_root(void) { } +static inline void vfio_debugfs_remove_root(void) { } + +static inline void vfio_device_debugfs_init(struct vfio_device *vdev) { } +static inline void vfio_device_debugfs_exit(struct vfio_device *vdev) { } +#endif /* CONFIG_VFIO_DEBUGFS */ + #endif diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index eacd6ec04de5..b2854d7939ce 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -1436,7 +1436,7 @@ static int vfio_iommu_map(struct vfio_iommu *iommu, dma_addr_t iova, list_for_each_entry(d, &iommu->domain_list, next) { ret = iommu_map(d->domain, iova, (phys_addr_t)pfn << PAGE_SHIFT, npage << PAGE_SHIFT, prot | IOMMU_CACHE, - GFP_KERNEL); + GFP_KERNEL_ACCOUNT); if (ret) goto unwind; @@ -1750,7 +1750,8 @@ static int vfio_iommu_replay(struct vfio_iommu *iommu, } ret = iommu_map(domain->domain, iova, phys, size, - dma->prot | IOMMU_CACHE, GFP_KERNEL); + dma->prot | IOMMU_CACHE, + GFP_KERNEL_ACCOUNT); if (ret) { if (!dma->iommu_mapped) { vfio_unpin_pages_remote(dma, iova, @@ -1845,7 +1846,8 @@ static void vfio_test_domain_fgsp(struct vfio_domain *domain, struct list_head * continue; ret = iommu_map(domain->domain, start, page_to_phys(pages), PAGE_SIZE * 2, - IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE, GFP_KERNEL); + IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE, + GFP_KERNEL_ACCOUNT); if (!ret) { size_t unmapped = iommu_unmap(domain->domain, start, PAGE_SIZE); diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c index 8d4995ada74a..1cc93aac99a2 100644 --- a/drivers/vfio/vfio_main.c +++ b/drivers/vfio/vfio_main.c @@ -311,6 +311,7 @@ static int __vfio_register_dev(struct vfio_device *device, refcount_set(&device->refcount, 1); vfio_device_group_register(device); + vfio_device_debugfs_init(device); return 0; err_out: @@ -378,6 +379,7 @@ void vfio_unregister_group_dev(struct vfio_device *device) } } + vfio_device_debugfs_exit(device); /* Balances vfio_device_set_group in register path */ vfio_device_remove_group(device); } @@ -1676,6 +1678,7 @@ static int __init vfio_init(void) if (ret) goto err_alloc_dev_chrdev; + vfio_debugfs_create_root(); pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); return 0; @@ -1691,6 +1694,7 @@ err_virqfd: static void __exit vfio_cleanup(void) { + vfio_debugfs_remove_root(); ida_destroy(&vfio.device_ida); vfio_cdev_cleanup(); class_destroy(vfio.device_class); diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index 173beda74b38..bc4a51e4638b 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -59,6 +59,7 @@ struct vhost_vdpa { int in_batch; struct vdpa_iova_range range; u32 batch_asid; + bool suspended; }; static DEFINE_IDA(vhost_vdpa_ida); @@ -232,6 +233,8 @@ static int _compat_vdpa_reset(struct vhost_vdpa *v) struct vdpa_device *vdpa = v->vdpa; u32 flags = 0; + v->suspended = false; + if (v->vdev.vqs) { flags |= !vhost_backend_has_feature(v->vdev.vqs[0], VHOST_BACKEND_F_IOTLB_PERSIST) ? @@ -590,11 +593,16 @@ static long vhost_vdpa_suspend(struct vhost_vdpa *v) { struct vdpa_device *vdpa = v->vdpa; const struct vdpa_config_ops *ops = vdpa->config; + int ret; if (!ops->suspend) return -EOPNOTSUPP; - return ops->suspend(vdpa); + ret = ops->suspend(vdpa); + if (!ret) + v->suspended = true; + + return ret; } /* After a successful return of this ioctl the device resumes processing @@ -605,11 +613,16 @@ static long vhost_vdpa_resume(struct vhost_vdpa *v) { struct vdpa_device *vdpa = v->vdpa; const struct vdpa_config_ops *ops = vdpa->config; + int ret; if (!ops->resume) return -EOPNOTSUPP; - return ops->resume(vdpa); + ret = ops->resume(vdpa); + if (!ret) + v->suspended = false; + + return ret; } static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, @@ -690,6 +703,9 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, switch (cmd) { case VHOST_SET_VRING_ADDR: + if ((ops->get_status(vdpa) & VIRTIO_CONFIG_S_DRIVER_OK) && !v->suspended) + return -EINVAL; + if (ops->set_vq_address(vdpa, idx, (u64)(uintptr_t)vq->desc, (u64)(uintptr_t)vq->avail, @@ -698,6 +714,9 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, break; case VHOST_SET_VRING_BASE: + if ((ops->get_status(vdpa) & VIRTIO_CONFIG_S_DRIVER_OK) && !v->suspended) + return -EINVAL; + if (vhost_has_feature(vq, VIRTIO_F_RING_PACKED)) { vq_state.packed.last_avail_idx = vq->last_avail_idx & 0x7fff; vq_state.packed.last_avail_counter = !!(vq->last_avail_idx & 0x8000); @@ -968,7 +987,8 @@ static int vhost_vdpa_map(struct vhost_vdpa *v, struct vhost_iotlb *iotlb, r = ops->set_map(vdpa, asid, iotlb); } else { r = iommu_map(v->domain, iova, pa, size, - perm_to_iommu_flags(perm), GFP_KERNEL); + perm_to_iommu_flags(perm), + GFP_KERNEL_ACCOUNT); } if (r) { vhost_iotlb_del_range(iotlb, iova, iova + size - 1); diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 94d092091b5e..ea2d0d69bd8c 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -395,6 +395,17 @@ config BACKLIGHT_LP8788 help This supports TI LP8788 backlight driver. +config BACKLIGHT_MP3309C + tristate "Backlight Driver for MPS MP3309C" + depends on I2C && PWM + select REGMAP_I2C + help + This supports MPS MP3309C backlight WLED driver in both PWM and + analog/I2C dimming modes. + + To compile this driver as a module, choose M here: the module will + be called mp3309c. + config BACKLIGHT_PANDORA tristate "Backlight driver for Pandora console" depends on TWL4030_CORE diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 67d3ff39be3c..06966cb20459 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o +obj-$(CONFIG_BACKLIGHT_MP3309C) += mp3309c.o obj-$(CONFIG_BACKLIGHT_MT6370) += mt6370-backlight.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o diff --git a/drivers/video/backlight/hx8357.c b/drivers/video/backlight/hx8357.c index f76d2469d490..d7298376cf74 100644 --- a/drivers/video/backlight/hx8357.c +++ b/drivers/video/backlight/hx8357.c @@ -6,11 +6,11 @@ */ #include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/lcd.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> #include <linux/spi/spi.h> #define HX8357_NUM_IM_PINS 3 @@ -83,11 +83,10 @@ #define HX8369_SET_GAMMA_CURVE_RELATED 0xe0 struct hx8357_data { - unsigned im_pins[HX8357_NUM_IM_PINS]; - unsigned reset; + struct gpio_descs *im_pins; + struct gpio_desc *reset; struct spi_device *spi; int state; - bool use_im_pins; }; static u8 hx8357_seq_power[] = { @@ -321,11 +320,11 @@ static void hx8357_lcd_reset(struct lcd_device *lcdev) struct hx8357_data *lcd = lcd_get_data(lcdev); /* Reset the screen */ - gpio_set_value(lcd->reset, 1); + gpiod_set_value(lcd->reset, 0); usleep_range(10000, 12000); - gpio_set_value(lcd->reset, 0); + gpiod_set_value(lcd->reset, 1); usleep_range(10000, 12000); - gpio_set_value(lcd->reset, 1); + gpiod_set_value(lcd->reset, 0); /* The controller needs 120ms to recover from reset */ msleep(120); @@ -340,10 +339,10 @@ static int hx8357_lcd_init(struct lcd_device *lcdev) * Set the interface selection pins to SPI mode, with three * wires */ - if (lcd->use_im_pins) { - gpio_set_value_cansleep(lcd->im_pins[0], 1); - gpio_set_value_cansleep(lcd->im_pins[1], 0); - gpio_set_value_cansleep(lcd->im_pins[2], 1); + if (lcd->im_pins) { + gpiod_set_value_cansleep(lcd->im_pins->desc[0], 1); + gpiod_set_value_cansleep(lcd->im_pins->desc[1], 0); + gpiod_set_value_cansleep(lcd->im_pins->desc[2], 1); } ret = hx8357_spi_write_array(lcdev, hx8357_seq_power, @@ -580,6 +579,7 @@ MODULE_DEVICE_TABLE(of, hx8357_dt_ids); static int hx8357_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct lcd_device *lcdev; struct hx8357_data *lcd; const struct of_device_id *match; @@ -601,49 +601,19 @@ static int hx8357_probe(struct spi_device *spi) if (!match || !match->data) return -EINVAL; - lcd->reset = of_get_named_gpio(spi->dev.of_node, "gpios-reset", 0); - if (!gpio_is_valid(lcd->reset)) { - dev_err(&spi->dev, "Missing dt property: gpios-reset\n"); - return -EINVAL; - } + lcd->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(lcd->reset)) + return dev_err_probe(dev, PTR_ERR(lcd->reset), "failed to request reset GPIO\n"); + gpiod_set_consumer_name(lcd->reset, "hx8357-reset"); - ret = devm_gpio_request_one(&spi->dev, lcd->reset, - GPIOF_OUT_INIT_HIGH, - "hx8357-reset"); - if (ret) { - dev_err(&spi->dev, - "failed to request gpio %d: %d\n", - lcd->reset, ret); - return -EINVAL; - } + lcd->im_pins = devm_gpiod_get_array_optional(dev, "im", GPIOD_OUT_LOW); + if (IS_ERR(lcd->im_pins)) + return dev_err_probe(dev, PTR_ERR(lcd->im_pins), "failed to request im GPIOs\n"); + if (lcd->im_pins->ndescs < HX8357_NUM_IM_PINS) + return dev_err_probe(dev, -EINVAL, "not enough im GPIOs\n"); - if (of_property_present(spi->dev.of_node, "im-gpios")) { - lcd->use_im_pins = 1; - - for (i = 0; i < HX8357_NUM_IM_PINS; i++) { - lcd->im_pins[i] = of_get_named_gpio(spi->dev.of_node, - "im-gpios", i); - if (lcd->im_pins[i] == -EPROBE_DEFER) { - dev_info(&spi->dev, "GPIO requested is not here yet, deferring the probe\n"); - return -EPROBE_DEFER; - } - if (!gpio_is_valid(lcd->im_pins[i])) { - dev_err(&spi->dev, "Missing dt property: im-gpios\n"); - return -EINVAL; - } - - ret = devm_gpio_request_one(&spi->dev, lcd->im_pins[i], - GPIOF_OUT_INIT_LOW, - "im_pins"); - if (ret) { - dev_err(&spi->dev, "failed to request gpio %d: %d\n", - lcd->im_pins[i], ret); - return -EINVAL; - } - } - } else { - lcd->use_im_pins = 0; - } + for (i = 0; i < HX8357_NUM_IM_PINS; i++) + gpiod_set_consumer_name(lcd->im_pins->desc[i], "im_pins"); lcdev = devm_lcd_device_register(&spi->dev, "mxsfb", &spi->dev, lcd, &hx8357_ops); diff --git a/drivers/video/backlight/ili922x.c b/drivers/video/backlight/ili922x.c index e7b6bd827986..c8e0e655dc86 100644 --- a/drivers/video/backlight/ili922x.c +++ b/drivers/video/backlight/ili922x.c @@ -81,7 +81,7 @@ #define START_RW_WRITE 0 #define START_RW_READ 1 -/** +/* * START_BYTE(id, rs, rw) * * Set the start byte according to the required operation. @@ -100,7 +100,7 @@ #define START_BYTE(id, rs, rw) \ (0x70 | (((id) & 0x01) << 2) | (((rs) & 0x01) << 1) | ((rw) & 0x01)) -/** +/* * CHECK_FREQ_REG(spi_device s, spi_transfer x) - Check the frequency * for the SPI transfer. According to the datasheet, the controller * accept higher frequency for the GRAM transfer, but it requires @@ -269,6 +269,10 @@ static int ili922x_write(struct spi_device *spi, u8 reg, u16 value) spi_message_add_tail(&xfer_regindex, &msg); ret = spi_sync(spi, &msg); + if (ret < 0) { + dev_err(&spi->dev, "Error sending SPI message 0x%x", ret); + return ret; + } spi_message_init(&msg); tbuf[0] = set_tx_byte(START_BYTE(ili922x_id, START_RS_REG, diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c index 8fcb62be597b..a3412c936ca2 100644 --- a/drivers/video/backlight/lm3630a_bl.c +++ b/drivers/video/backlight/lm3630a_bl.c @@ -180,7 +180,7 @@ static int lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max) pchip->pwmd_state.enabled = pchip->pwmd_state.duty_cycle ? true : false; - return pwm_apply_state(pchip->pwmd, &pchip->pwmd_state); + return pwm_apply_might_sleep(pchip->pwmd, &pchip->pwmd_state); } /* update and get brightness */ diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index da1f124db69c..7075bfab59c4 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -234,7 +234,7 @@ static int lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br) state.duty_cycle = div_u64(br * state.period, max_br); state.enabled = state.duty_cycle; - return pwm_apply_state(lp->pwm, &state); + return pwm_apply_might_sleep(lp->pwm, &state); } static int lp855x_bl_update_status(struct backlight_device *bl) diff --git a/drivers/video/backlight/mp3309c.c b/drivers/video/backlight/mp3309c.c new file mode 100644 index 000000000000..34d71259fac1 --- /dev/null +++ b/drivers/video/backlight/mp3309c.c @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for MPS MP3309C White LED driver with I2C interface + * + * This driver support both analog (by I2C commands) and PWM dimming control + * modes. + * + * Copyright (C) 2023 ASEM Srl + * Author: Flavio Suligoi <f.suligoi@asem.it> + * + * Based on pwm_bl.c + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/pwm.h> +#include <linux/regmap.h> + +#define REG_I2C_0 0x00 +#define REG_I2C_1 0x01 + +#define REG_I2C_0_EN 0x80 +#define REG_I2C_0_D0 0x40 +#define REG_I2C_0_D1 0x20 +#define REG_I2C_0_D2 0x10 +#define REG_I2C_0_D3 0x08 +#define REG_I2C_0_D4 0x04 +#define REG_I2C_0_RSRV1 0x02 +#define REG_I2C_0_RSRV2 0x01 + +#define REG_I2C_1_RSRV1 0x80 +#define REG_I2C_1_DIMS 0x40 +#define REG_I2C_1_SYNC 0x20 +#define REG_I2C_1_OVP0 0x10 +#define REG_I2C_1_OVP1 0x08 +#define REG_I2C_1_VOS 0x04 +#define REG_I2C_1_LEDO 0x02 +#define REG_I2C_1_OTP 0x01 + +#define ANALOG_I2C_NUM_LEVELS 32 /* 0..31 */ +#define ANALOG_I2C_REG_MASK 0x7c + +#define MP3309C_PWM_DEFAULT_NUM_LEVELS 256 /* 0..255 */ + +enum mp3309c_status_value { + FIRST_POWER_ON, + BACKLIGHT_OFF, + BACKLIGHT_ON, +}; + +enum mp3309c_dimming_mode_value { + DIMMING_PWM, + DIMMING_ANALOG_I2C, +}; + +struct mp3309c_platform_data { + unsigned int max_brightness; + unsigned int default_brightness; + unsigned int *levels; + u8 dimming_mode; + u8 over_voltage_protection; + bool sync_mode; + u8 status; +}; + +struct mp3309c_chip { + struct device *dev; + struct mp3309c_platform_data *pdata; + struct backlight_device *bl; + struct gpio_desc *enable_gpio; + struct regmap *regmap; + struct pwm_device *pwmd; +}; + +static const struct regmap_config mp3309c_regmap = { + .name = "mp3309c_regmap", + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, + .max_register = REG_I2C_1, +}; + +static int mp3309c_enable_device(struct mp3309c_chip *chip) +{ + u8 reg_val; + int ret; + + /* I2C register #0 - Device enable */ + ret = regmap_update_bits(chip->regmap, REG_I2C_0, REG_I2C_0_EN, + REG_I2C_0_EN); + if (ret) + return ret; + + /* + * I2C register #1 - Set working mode: + * - set one of the two dimming mode: + * - PWM dimming using an external PWM dimming signal + * - analog dimming using I2C commands + * - enable/disable synchronous mode + * - set overvoltage protection (OVP) + */ + reg_val = 0x00; + if (chip->pdata->dimming_mode == DIMMING_PWM) + reg_val |= REG_I2C_1_DIMS; + if (chip->pdata->sync_mode) + reg_val |= REG_I2C_1_SYNC; + reg_val |= chip->pdata->over_voltage_protection; + ret = regmap_write(chip->regmap, REG_I2C_1, reg_val); + if (ret) + return ret; + + return 0; +} + +static int mp3309c_bl_update_status(struct backlight_device *bl) +{ + struct mp3309c_chip *chip = bl_get_data(bl); + int brightness = backlight_get_brightness(bl); + struct pwm_state pwmstate; + unsigned int analog_val, bits_val; + int i, ret; + + if (chip->pdata->dimming_mode == DIMMING_PWM) { + /* + * PWM control mode + */ + pwm_get_state(chip->pwmd, &pwmstate); + pwm_set_relative_duty_cycle(&pwmstate, + chip->pdata->levels[brightness], + chip->pdata->levels[chip->pdata->max_brightness]); + pwmstate.enabled = true; + ret = pwm_apply_state(chip->pwmd, &pwmstate); + if (ret) + return ret; + + switch (chip->pdata->status) { + case FIRST_POWER_ON: + case BACKLIGHT_OFF: + /* + * After 20ms of low pwm signal level, the chip turns + * off automatically. In this case, before enabling the + * chip again, we must wait about 10ms for pwm signal to + * stabilize. + */ + if (brightness > 0) { + msleep(10); + mp3309c_enable_device(chip); + chip->pdata->status = BACKLIGHT_ON; + } else { + chip->pdata->status = BACKLIGHT_OFF; + } + break; + case BACKLIGHT_ON: + if (brightness == 0) + chip->pdata->status = BACKLIGHT_OFF; + break; + } + } else { + /* + * Analog (by I2C command) control mode + * + * The first time, before setting brightness, we must enable the + * device + */ + if (chip->pdata->status == FIRST_POWER_ON) + mp3309c_enable_device(chip); + + /* + * Dimming mode I2C command (fixed dimming range 0..31) + * + * The 5 bits of the dimming analog value D4..D0 is allocated + * in the I2C register #0, in the following way: + * + * +--+--+--+--+--+--+--+--+ + * |EN|D0|D1|D2|D3|D4|XX|XX| + * +--+--+--+--+--+--+--+--+ + */ + analog_val = brightness; + bits_val = 0; + for (i = 0; i <= 5; i++) + bits_val += ((analog_val >> i) & 0x01) << (6 - i); + ret = regmap_update_bits(chip->regmap, REG_I2C_0, + ANALOG_I2C_REG_MASK, bits_val); + if (ret) + return ret; + + if (brightness > 0) + chip->pdata->status = BACKLIGHT_ON; + else + chip->pdata->status = BACKLIGHT_OFF; + } + + return 0; +} + +static const struct backlight_ops mp3309c_bl_ops = { + .update_status = mp3309c_bl_update_status, +}; + +static int pm3309c_parse_dt_node(struct mp3309c_chip *chip, + struct mp3309c_platform_data *pdata) +{ + struct device_node *node = chip->dev->of_node; + struct property *prop_pwms; + struct property *prop_levels = NULL; + int length = 0; + int ret, i; + unsigned int num_levels, tmp_value; + + if (!node) { + dev_err(chip->dev, "failed to get DT node\n"); + return -ENODEV; + } + + /* + * Dimming mode: the MP3309C provides two dimming control mode: + * + * - PWM mode + * - Analog by I2C control mode (default) + * + * I2C control mode is assumed as default but, if the pwms property is + * found in the backlight node, the mode switches to PWM mode. + */ + pdata->dimming_mode = DIMMING_ANALOG_I2C; + prop_pwms = of_find_property(node, "pwms", &length); + if (prop_pwms) { + chip->pwmd = devm_pwm_get(chip->dev, NULL); + if (IS_ERR(chip->pwmd)) + return dev_err_probe(chip->dev, PTR_ERR(chip->pwmd), + "error getting pwm data\n"); + pdata->dimming_mode = DIMMING_PWM; + pwm_apply_args(chip->pwmd); + } + + /* + * In I2C control mode the dimming levels (0..31) are fixed by the + * hardware, while in PWM control mode they can be chosen by the user, + * to allow nonlinear mappings. + */ + if (pdata->dimming_mode == DIMMING_ANALOG_I2C) { + /* + * Analog (by I2C commands) control mode: fixed 0..31 brightness + * levels + */ + num_levels = ANALOG_I2C_NUM_LEVELS; + + /* Enable GPIO used in I2C dimming mode only */ + chip->enable_gpio = devm_gpiod_get(chip->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(chip->enable_gpio)) + return dev_err_probe(chip->dev, + PTR_ERR(chip->enable_gpio), + "error getting enable gpio\n"); + } else { + /* + * PWM control mode: check for brightness level in DT + */ + prop_levels = of_find_property(node, "brightness-levels", + &length); + if (prop_levels) { + /* Read brightness levels from DT */ + num_levels = length / sizeof(u32); + if (num_levels < 2) + return -EINVAL; + } else { + /* Use default brightness levels */ + num_levels = MP3309C_PWM_DEFAULT_NUM_LEVELS; + } + } + + /* Fill brightness levels array */ + pdata->levels = devm_kcalloc(chip->dev, num_levels, + sizeof(*pdata->levels), GFP_KERNEL); + if (!pdata->levels) + return -ENOMEM; + if (prop_levels) { + ret = of_property_read_u32_array(node, "brightness-levels", + pdata->levels, + num_levels); + if (ret < 0) + return ret; + } else { + for (i = 0; i < num_levels; i++) + pdata->levels[i] = i; + } + + pdata->max_brightness = num_levels - 1; + + ret = of_property_read_u32(node, "default-brightness", + &pdata->default_brightness); + if (ret) + pdata->default_brightness = pdata->max_brightness; + if (pdata->default_brightness > pdata->max_brightness) { + dev_err(chip->dev, + "default brightness exceeds max brightness\n"); + pdata->default_brightness = pdata->max_brightness; + } + + /* + * Over-voltage protection (OVP) + * + * This (optional) property values are: + * + * - 13.5V + * - 24V + * - 35.5V (hardware default setting) + * + * If missing, the default value for OVP is 35.5V + */ + pdata->over_voltage_protection = REG_I2C_1_OVP1; + if (!of_property_read_u32(node, "mps,overvoltage-protection-microvolt", + &tmp_value)) { + switch (tmp_value) { + case 13500000: + pdata->over_voltage_protection = 0x00; + break; + case 24000000: + pdata->over_voltage_protection = REG_I2C_1_OVP0; + break; + case 35500000: + pdata->over_voltage_protection = REG_I2C_1_OVP1; + break; + default: + return -EINVAL; + } + } + + /* Synchronous (default) and non-synchronous mode */ + pdata->sync_mode = true; + if (of_property_read_bool(node, "mps,no-sync-mode")) + pdata->sync_mode = false; + + return 0; +} + +static int mp3309c_probe(struct i2c_client *client) +{ + struct mp3309c_platform_data *pdata = dev_get_platdata(&client->dev); + struct mp3309c_chip *chip; + struct backlight_properties props; + struct pwm_state pwmstate; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "failed to check i2c functionality\n"); + return -EOPNOTSUPP; + } + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &client->dev; + + chip->regmap = devm_regmap_init_i2c(client, &mp3309c_regmap); + if (IS_ERR(chip->regmap)) + return dev_err_probe(&client->dev, PTR_ERR(chip->regmap), + "failed to allocate register map\n"); + + i2c_set_clientdata(client, chip); + + if (!pdata) { + pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = pm3309c_parse_dt_node(chip, pdata); + if (ret) + return ret; + } + chip->pdata = pdata; + + /* Backlight properties */ + props.brightness = pdata->default_brightness; + props.max_brightness = pdata->max_brightness; + props.scale = BACKLIGHT_SCALE_LINEAR; + props.type = BACKLIGHT_RAW; + props.power = FB_BLANK_UNBLANK; + props.fb_blank = FB_BLANK_UNBLANK; + chip->bl = devm_backlight_device_register(chip->dev, "mp3309c", + chip->dev, chip, + &mp3309c_bl_ops, &props); + if (IS_ERR(chip->bl)) + return dev_err_probe(chip->dev, PTR_ERR(chip->bl), + "error registering backlight device\n"); + + /* In PWM dimming mode, enable pwm device */ + if (chip->pdata->dimming_mode == DIMMING_PWM) { + pwm_init_state(chip->pwmd, &pwmstate); + pwm_set_relative_duty_cycle(&pwmstate, + chip->pdata->default_brightness, + chip->pdata->max_brightness); + pwmstate.enabled = true; + ret = pwm_apply_state(chip->pwmd, &pwmstate); + if (ret) + return dev_err_probe(chip->dev, ret, + "error setting pwm device\n"); + } + + chip->pdata->status = FIRST_POWER_ON; + backlight_update_status(chip->bl); + + return 0; +} + +static void mp3309c_remove(struct i2c_client *client) +{ + struct mp3309c_chip *chip = i2c_get_clientdata(client); + struct backlight_device *bl = chip->bl; + + bl->props.power = FB_BLANK_POWERDOWN; + bl->props.brightness = 0; + backlight_update_status(chip->bl); +} + +static const struct of_device_id mp3309c_match_table[] = { + { .compatible = "mps,mp3309c", }, + { }, +}; +MODULE_DEVICE_TABLE(of, mp3309c_match_table); + +static const struct i2c_device_id mp3309c_id[] = { + { "mp3309c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mp3309c_id); + +static struct i2c_driver mp3309c_i2c_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = mp3309c_match_table, + }, + .probe = mp3309c_probe, + .remove = mp3309c_remove, + .id_table = mp3309c_id, +}; + +module_i2c_driver(mp3309c_i2c_driver); + +MODULE_DESCRIPTION("Backlight Driver for MPS MP3309C"); +MODULE_AUTHOR("Flavio Suligoi <f.suligoi@asem.it>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 289bd9ce4d36..ffcebf6aa76a 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -103,7 +103,7 @@ static int pwm_backlight_update_status(struct backlight_device *bl) pwm_get_state(pb->pwm, &state); state.duty_cycle = compute_duty_cycle(pb, brightness, &state); state.enabled = true; - pwm_apply_state(pb->pwm, &state); + pwm_apply_might_sleep(pb->pwm, &state); pwm_backlight_power_on(pb); } else { @@ -120,7 +120,7 @@ static int pwm_backlight_update_status(struct backlight_device *bl) * inactive output. */ state.enabled = !pb->power_supply && !pb->enable_gpio; - pwm_apply_state(pb->pwm, &state); + pwm_apply_might_sleep(pb->pwm, &state); } if (pb->notify_after) @@ -461,10 +461,9 @@ static int pwm_backlight_probe(struct platform_device *pdev) if (!data) { ret = pwm_backlight_parse_dt(&pdev->dev, &defdata); - if (ret < 0) { - dev_err(&pdev->dev, "failed to find platform data\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "failed to find platform data\n"); data = &defdata; } @@ -493,24 +492,27 @@ static int pwm_backlight_probe(struct platform_device *pdev) pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_ASIS); if (IS_ERR(pb->enable_gpio)) { - ret = PTR_ERR(pb->enable_gpio); + ret = dev_err_probe(&pdev->dev, PTR_ERR(pb->enable_gpio), + "failed to acquire enable GPIO\n"); goto err_alloc; } pb->power_supply = devm_regulator_get_optional(&pdev->dev, "power"); if (IS_ERR(pb->power_supply)) { ret = PTR_ERR(pb->power_supply); - if (ret == -ENODEV) + if (ret == -ENODEV) { pb->power_supply = NULL; - else + } else { + dev_err_probe(&pdev->dev, ret, + "failed to acquire power regulator\n"); goto err_alloc; + } } pb->pwm = devm_pwm_get(&pdev->dev, NULL); if (IS_ERR(pb->pwm)) { - ret = PTR_ERR(pb->pwm); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "unable to request PWM\n"); + ret = dev_err_probe(&pdev->dev, PTR_ERR(pb->pwm), + "unable to request PWM\n"); goto err_alloc; } @@ -528,10 +530,10 @@ static int pwm_backlight_probe(struct platform_device *pdev) if (!state.period && (data->pwm_period_ns > 0)) state.period = data->pwm_period_ns; - ret = pwm_apply_state(pb->pwm, &state); + ret = pwm_apply_might_sleep(pb->pwm, &state); if (ret) { - dev_err(&pdev->dev, "failed to apply initial PWM state: %d\n", - ret); + dev_err_probe(&pdev->dev, ret, + "failed to apply initial PWM state"); goto err_alloc; } @@ -568,8 +570,8 @@ static int pwm_backlight_probe(struct platform_device *pdev) ret = pwm_backlight_brightness_default(&pdev->dev, data, state.period); if (ret < 0) { - dev_err(&pdev->dev, - "failed to setup default brightness table\n"); + dev_err_probe(&pdev->dev, ret, + "failed to setup default brightness table\n"); goto err_alloc; } @@ -597,8 +599,8 @@ static int pwm_backlight_probe(struct platform_device *pdev) bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb, &pwm_backlight_ops, &props); if (IS_ERR(bl)) { - dev_err(&pdev->dev, "failed to register backlight\n"); - ret = PTR_ERR(bl); + ret = dev_err_probe(&pdev->dev, PTR_ERR(bl), + "failed to register backlight\n"); goto err_alloc; } @@ -633,7 +635,7 @@ static void pwm_backlight_remove(struct platform_device *pdev) pwm_get_state(pb->pwm, &state); state.duty_cycle = 0; state.enabled = false; - pwm_apply_state(pb->pwm, &state); + pwm_apply_might_sleep(pb->pwm, &state); if (pb->exit) pb->exit(&pdev->dev); @@ -649,7 +651,7 @@ static void pwm_backlight_shutdown(struct platform_device *pdev) pwm_get_state(pb->pwm, &state); state.duty_cycle = 0; state.enabled = false; - pwm_apply_state(pb->pwm, &state); + pwm_apply_might_sleep(pb->pwm, &state); } #ifdef CONFIG_PM_SLEEP @@ -673,7 +675,7 @@ static int pwm_backlight_suspend(struct device *dev) pwm_get_state(pb->pwm, &state); state.duty_cycle = 0; state.enabled = false; - pwm_apply_state(pb->pwm, &state); + pwm_apply_might_sleep(pb->pwm, &state); if (pb->notify_after) pb->notify_after(pb->dev, 0); diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 83c2d7329ca5..bc31db6ef7d2 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -7,7 +7,7 @@ menu "Console display driver support" config VGA_CONSOLE bool "VGA text console" if EXPERT || !X86 - depends on ALPHA || IA64 || X86 || \ + depends on ALPHA || X86 || \ (ARM && ARCH_FOOTBRIDGE) || \ (MIPS && (MIPS_MALTA || SIBYTE_BCM112X || SIBYTE_SB1250 || SIBYTE_BCM1x80 || SNI_RM)) select APERTURE_HELPERS if (DRM || FB || VFIO_PCI_CORE) diff --git a/drivers/video/fbdev/core/fb_ddc.c b/drivers/video/fbdev/core/fb_ddc.c index 8bf5f2f54be7..e25143219862 100644 --- a/drivers/video/fbdev/core/fb_ddc.c +++ b/drivers/video/fbdev/core/fb_ddc.c @@ -116,7 +116,6 @@ unsigned char *fb_ddc_read(struct i2c_adapter *adapter) algo_data->setsda(algo_data->data, 1); algo_data->setscl(algo_data->data, 1); - adapter->class |= I2C_CLASS_DDC; return edid; } diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 63af6ab034b5..1183e7a871f8 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -631,8 +631,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, if (logo_lines > vc->vc_bottom) { logo_shown = FBCON_LOGO_CANSHOW; - printk(KERN_INFO - "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n"); + pr_info("fbcon: disable boot-logo (boot-logo bigger than screen).\n"); } else { logo_shown = FBCON_LOGO_DRAW; vc->vc_top = logo_lines; diff --git a/drivers/video/fbdev/cyber2000fb.c b/drivers/video/fbdev/cyber2000fb.c index abb87d3576db..986760b90465 100644 --- a/drivers/video/fbdev/cyber2000fb.c +++ b/drivers/video/fbdev/cyber2000fb.c @@ -1227,7 +1227,6 @@ static int cyber2000fb_setup_ddc_bus(struct cfb_info *cfb) strscpy(cfb->ddc_adapter.name, cfb->fb.fix.id, sizeof(cfb->ddc_adapter.name)); cfb->ddc_adapter.owner = THIS_MODULE; - cfb->ddc_adapter.class = I2C_CLASS_DDC; cfb->ddc_adapter.algo_data = &cfb->ddc_algo; cfb->ddc_adapter.dev.parent = cfb->fb.device; cfb->ddc_algo.setsda = cyber2000fb_ddc_setsda; diff --git a/drivers/video/fbdev/i740fb.c b/drivers/video/fbdev/i740fb.c index 1897e65ab703..9b74dae71472 100644 --- a/drivers/video/fbdev/i740fb.c +++ b/drivers/video/fbdev/i740fb.c @@ -163,7 +163,6 @@ static int i740fb_setup_ddc_bus(struct fb_info *info) strscpy(par->ddc_adapter.name, info->fix.id, sizeof(par->ddc_adapter.name)); par->ddc_adapter.owner = THIS_MODULE; - par->ddc_adapter.class = I2C_CLASS_DDC; par->ddc_adapter.algo_data = &par->ddc_algo; par->ddc_adapter.dev.parent = info->device; par->ddc_algo.setsda = i740fb_ddc_setsda; diff --git a/drivers/video/fbdev/matrox/i2c-matroxfb.c b/drivers/video/fbdev/matrox/i2c-matroxfb.c index e2e4705e3fe0..bb048e14b2cf 100644 --- a/drivers/video/fbdev/matrox/i2c-matroxfb.c +++ b/drivers/video/fbdev/matrox/i2c-matroxfb.c @@ -100,8 +100,7 @@ static const struct i2c_algo_bit_data matrox_i2c_algo_template = }; static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo, - unsigned int data, unsigned int clock, const char *name, - int class) + unsigned int data, unsigned int clock, const char *name) { int err; @@ -112,7 +111,6 @@ static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo, snprintf(b->adapter.name, sizeof(b->adapter.name), name, minfo->fbcon.node); i2c_set_adapdata(&b->adapter, b); - b->adapter.class = class; b->adapter.algo_data = &b->bac; b->adapter.dev.parent = &minfo->pcidev->dev; b->bac = matrox_i2c_algo_template; @@ -160,27 +158,24 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { case MGA_2164: err = i2c_bus_reg(&m2info->ddc1, minfo, DDC1B_DATA, DDC1B_CLK, - "DDC:fb%u #0", I2C_CLASS_DDC); + "DDC:fb%u #0"); break; default: err = i2c_bus_reg(&m2info->ddc1, minfo, DDC1_DATA, DDC1_CLK, - "DDC:fb%u #0", I2C_CLASS_DDC); + "DDC:fb%u #0"); break; } if (err) goto fail_ddc1; if (minfo->devflags.dualhead) { - err = i2c_bus_reg(&m2info->ddc2, minfo, - DDC2_DATA, DDC2_CLK, - "DDC:fb%u #1", I2C_CLASS_DDC); + err = i2c_bus_reg(&m2info->ddc2, minfo, DDC2_DATA, DDC2_CLK, "DDC:fb%u #1"); if (err == -ENODEV) { printk(KERN_INFO "i2c-matroxfb: VGA->TV plug detected, DDC unavailable.\n"); } else if (err) printk(KERN_INFO "i2c-matroxfb: Could not register secondary output i2c bus. Continuing anyway.\n"); /* Register maven bus even on G450/G550 */ - err = i2c_bus_reg(&m2info->maven, minfo, - MAT_DATA, MAT_CLK, "MAVEN:fb%u", 0); + err = i2c_bus_reg(&m2info->maven, minfo, MAT_DATA, MAT_CLK, "MAVEN:fb%u"); if (err) printk(KERN_INFO "i2c-matroxfb: Could not register Maven i2c bus. Continuing anyway.\n"); else { diff --git a/drivers/video/fbdev/s3fb.c b/drivers/video/fbdev/s3fb.c index 589b349cb63e..07722a5ea8ef 100644 --- a/drivers/video/fbdev/s3fb.c +++ b/drivers/video/fbdev/s3fb.c @@ -252,7 +252,6 @@ static int s3fb_setup_ddc_bus(struct fb_info *info) strscpy(par->ddc_adapter.name, info->fix.id, sizeof(par->ddc_adapter.name)); par->ddc_adapter.owner = THIS_MODULE; - par->ddc_adapter.class = I2C_CLASS_DDC; par->ddc_adapter.algo_data = &par->ddc_algo; par->ddc_adapter.dev.parent = info->device; par->ddc_algo.setsda = s3fb_ddc_setsda; diff --git a/drivers/video/fbdev/savage/savagefb_driver.c b/drivers/video/fbdev/savage/savagefb_driver.c index dddd6afcb972..ebc9aeffdde7 100644 --- a/drivers/video/fbdev/savage/savagefb_driver.c +++ b/drivers/video/fbdev/savage/savagefb_driver.c @@ -869,6 +869,9 @@ static int savagefb_check_var(struct fb_var_screeninfo *var, DBG("savagefb_check_var"); + if (!var->pixclock) + return -EINVAL; + var->transp.offset = 0; var->transp.length = 0; switch (var->bits_per_pixel) { diff --git a/drivers/video/fbdev/sis/sis_main.c b/drivers/video/fbdev/sis/sis_main.c index 803ccb6aa479..009bf1d92644 100644 --- a/drivers/video/fbdev/sis/sis_main.c +++ b/drivers/video/fbdev/sis/sis_main.c @@ -1444,6 +1444,8 @@ sisfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) vtotal = var->upper_margin + var->lower_margin + var->vsync_len; + if (!var->pixclock) + return -EINVAL; pixclock = var->pixclock; if((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) { diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 5ae48e36fccb..1a4f90ea7d5a 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -347,7 +347,7 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) pwm_init_state(par->pwm, &pwmstate); pwm_set_relative_duty_cycle(&pwmstate, 50, 100); - pwm_apply_state(par->pwm, &pwmstate); + pwm_apply_might_sleep(par->pwm, &pwmstate); /* Enable the PWM */ pwm_enable(par->pwm); diff --git a/drivers/video/fbdev/stifb.c b/drivers/video/fbdev/stifb.c index 2de0e675fd15..8e5bac27542d 100644 --- a/drivers/video/fbdev/stifb.c +++ b/drivers/video/fbdev/stifb.c @@ -1158,7 +1158,7 @@ stifb_init_display(struct stifb_info *fb) } break; } - stifb_blank(0, (struct fb_info *)fb); /* 0=enable screen */ + stifb_blank(0, fb->info); /* 0=enable screen */ SETUP_FB(fb); } diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c index 22aa953138b0..51ebe78359ec 100644 --- a/drivers/video/fbdev/tdfxfb.c +++ b/drivers/video/fbdev/tdfxfb.c @@ -1267,7 +1267,6 @@ static int tdfxfb_setup_ddc_bus(struct tdfxfb_i2c_chan *chan, const char *name, strscpy(chan->adapter.name, name, sizeof(chan->adapter.name)); chan->adapter.owner = THIS_MODULE; - chan->adapter.class = I2C_CLASS_DDC; chan->adapter.algo_data = &chan->algo; chan->adapter.dev.parent = dev; chan->algo.setsda = tdfxfb_ddc_setsda; diff --git a/drivers/video/fbdev/tridentfb.c b/drivers/video/fbdev/tridentfb.c index 816d40b6f689..516cf2a18757 100644 --- a/drivers/video/fbdev/tridentfb.c +++ b/drivers/video/fbdev/tridentfb.c @@ -274,7 +274,6 @@ static int tridentfb_setup_ddc_bus(struct fb_info *info) strscpy(par->ddc_adapter.name, info->fix.id, sizeof(par->ddc_adapter.name)); par->ddc_adapter.owner = THIS_MODULE; - par->ddc_adapter.class = I2C_CLASS_DDC; par->ddc_adapter.algo_data = &par->ddc_algo; par->ddc_adapter.dev.parent = info->device; if (is_oldclock(par->chip_id)) { /* not sure if this check is OK */ diff --git a/drivers/video/fbdev/via/via_i2c.c b/drivers/video/fbdev/via/via_i2c.c index c35e530e0ec9..582502810575 100644 --- a/drivers/video/fbdev/via/via_i2c.c +++ b/drivers/video/fbdev/via/via_i2c.c @@ -201,7 +201,6 @@ static int create_i2c_bus(struct i2c_adapter *adapter, sprintf(adapter->name, "viafb i2c io_port idx 0x%02x", adap_cfg->ioport_index); adapter->owner = THIS_MODULE; - adapter->class = I2C_CLASS_DDC; adapter->algo_data = algo; if (pdev) adapter->dev.parent = &pdev->dev; diff --git a/drivers/video/fbdev/vt8500lcdfb.c b/drivers/video/fbdev/vt8500lcdfb.c index 42c25dc85197..ac73937073a7 100644 --- a/drivers/video/fbdev/vt8500lcdfb.c +++ b/drivers/video/fbdev/vt8500lcdfb.c @@ -374,7 +374,6 @@ static int vt8500lcd_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "no IRQ defined\n"); ret = -ENODEV; goto failed_free_palette; } diff --git a/drivers/virt/vboxguest/vboxguest_core.c b/drivers/virt/vboxguest/vboxguest_core.c index dfd69bd77f53..c6e9855998ab 100644 --- a/drivers/virt/vboxguest/vboxguest_core.c +++ b/drivers/virt/vboxguest/vboxguest_core.c @@ -33,16 +33,15 @@ VMMDEV_REQUESTOR_CON_DONT_KNOW | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN) /** - * Reserves memory in which the VMM can relocate any guest mappings - * that are floating around. + * vbg_guest_mappings_init - Reserves memory in which the VMM can + * relocate any guest mappings that are floating around. + * @gdev: The Guest extension device. * * This operation is a little bit tricky since the VMM might not accept * just any address because of address clashes between the three contexts * it operates in, so we try several times. * * Failure to reserve the guest mappings is ignored. - * - * @gdev: The Guest extension device. */ static void vbg_guest_mappings_init(struct vbg_dev *gdev) { @@ -125,7 +124,7 @@ out: } /** - * Undo what vbg_guest_mappings_init did. + * vbg_guest_mappings_exit - Undo what vbg_guest_mappings_init did. * * @gdev: The Guest extension device. */ @@ -166,9 +165,10 @@ static void vbg_guest_mappings_exit(struct vbg_dev *gdev) } /** - * Report the guest information to the host. - * Return: 0 or negative errno value. + * vbg_report_guest_info - Report the guest information to the host. * @gdev: The Guest extension device. + * + * Return: %0 or negative errno value. */ static int vbg_report_guest_info(struct vbg_dev *gdev) { @@ -229,10 +229,11 @@ out_free: } /** - * Report the guest driver status to the host. - * Return: 0 or negative errno value. + * vbg_report_driver_status - Report the guest driver status to the host. * @gdev: The Guest extension device. * @active: Flag whether the driver is now active or not. + * + * Return: 0 or negative errno value. */ static int vbg_report_driver_status(struct vbg_dev *gdev, bool active) { @@ -261,10 +262,12 @@ static int vbg_report_driver_status(struct vbg_dev *gdev, bool active) } /** - * Inflate the balloon by one chunk. The caller owns the balloon mutex. - * Return: 0 or negative errno value. + * vbg_balloon_inflate - Inflate the balloon by one chunk. The caller + * owns the balloon mutex. * @gdev: The Guest extension device. * @chunk_idx: Index of the chunk. + * + * Return: %0 or negative errno value. */ static int vbg_balloon_inflate(struct vbg_dev *gdev, u32 chunk_idx) { @@ -312,10 +315,12 @@ out_error: } /** - * Deflate the balloon by one chunk. The caller owns the balloon mutex. - * Return: 0 or negative errno value. + * vbg_balloon_deflate - Deflate the balloon by one chunk. The caller + * owns the balloon mutex. * @gdev: The Guest extension device. * @chunk_idx: Index of the chunk. + * + * Return: %0 or negative errno value. */ static int vbg_balloon_deflate(struct vbg_dev *gdev, u32 chunk_idx) { @@ -344,7 +349,7 @@ static int vbg_balloon_deflate(struct vbg_dev *gdev, u32 chunk_idx) return 0; } -/** +/* * Respond to VMMDEV_EVENT_BALLOON_CHANGE_REQUEST events, query the size * the host wants the balloon to be and adjust accordingly. */ @@ -409,7 +414,7 @@ static void vbg_balloon_work(struct work_struct *work) } } -/** +/* * Callback for heartbeat timer. */ static void vbg_heartbeat_timer(struct timer_list *t) @@ -422,11 +427,12 @@ static void vbg_heartbeat_timer(struct timer_list *t) } /** - * Configure the host to check guest's heartbeat - * and get heartbeat interval from the host. - * Return: 0 or negative errno value. + * vbg_heartbeat_host_config - Configure the host to check guest's heartbeat + * and get heartbeat interval from the host. * @gdev: The Guest extension device. * @enabled: Set true to enable guest heartbeat checks on host. + * + * Return: %0 or negative errno value. */ static int vbg_heartbeat_host_config(struct vbg_dev *gdev, bool enabled) { @@ -449,9 +455,11 @@ static int vbg_heartbeat_host_config(struct vbg_dev *gdev, bool enabled) } /** - * Initializes the heartbeat timer. This feature may be disabled by the host. - * Return: 0 or negative errno value. + * vbg_heartbeat_init - Initializes the heartbeat timer. This feature + * may be disabled by the host. * @gdev: The Guest extension device. + * + * Return: %0 or negative errno value. */ static int vbg_heartbeat_init(struct vbg_dev *gdev) { @@ -481,7 +489,8 @@ static int vbg_heartbeat_init(struct vbg_dev *gdev) } /** - * Cleanup hearbeat code, stop HB timer and disable host heartbeat checking. + * vbg_heartbeat_exit - Cleanup heartbeat code, stop HB timer and disable + * host heartbeat checking. * @gdev: The Guest extension device. */ static void vbg_heartbeat_exit(struct vbg_dev *gdev) @@ -493,11 +502,12 @@ static void vbg_heartbeat_exit(struct vbg_dev *gdev) } /** - * Applies a change to the bit usage tracker. - * Return: true if the mask changed, false if not. + * vbg_track_bit_usage - Applies a change to the bit usage tracker. * @tracker: The bit usage tracker. * @changed: The bits to change. * @previous: The previous value of the bits. + * + * Return: %true if the mask changed, %false if not. */ static bool vbg_track_bit_usage(struct vbg_bit_usage_tracker *tracker, u32 changed, u32 previous) @@ -529,10 +539,12 @@ static bool vbg_track_bit_usage(struct vbg_bit_usage_tracker *tracker, } /** - * Init and termination worker for resetting the (host) event filter on the host - * Return: 0 or negative errno value. + * vbg_reset_host_event_filter - Init and termination worker for + * resetting the (host) event filter on the host * @gdev: The Guest extension device. * @fixed_events: Fixed events (init time). + * + * Return: %0 or negative errno value. */ static int vbg_reset_host_event_filter(struct vbg_dev *gdev, u32 fixed_events) @@ -556,12 +568,8 @@ static int vbg_reset_host_event_filter(struct vbg_dev *gdev, } /** - * Changes the event filter mask for the given session. - * - * This is called in response to VBG_IOCTL_CHANGE_FILTER_MASK as well as to - * do session cleanup. Takes the session mutex. - * - * Return: 0 or negative errno value. + * vbg_set_session_event_filter - Changes the event filter mask for the + * given session. * @gdev: The Guest extension device. * @session: The session. * @or_mask: The events to add. @@ -570,6 +578,11 @@ static int vbg_reset_host_event_filter(struct vbg_dev *gdev, * This tweaks the error handling so we perform * proper session cleanup even if the host * misbehaves. + * + * This is called in response to VBG_IOCTL_CHANGE_FILTER_MASK as well as to + * do session cleanup. Takes the session mutex. + * + * Return: 0 or negative errno value. */ static int vbg_set_session_event_filter(struct vbg_dev *gdev, struct vbg_session *session, @@ -637,9 +650,11 @@ out: } /** - * Init and termination worker for set guest capabilities to zero on the host. - * Return: 0 or negative errno value. + * vbg_reset_host_capabilities - Init and termination worker for set + * guest capabilities to zero on the host. * @gdev: The Guest extension device. + * + * Return: %0 or negative errno value. */ static int vbg_reset_host_capabilities(struct vbg_dev *gdev) { @@ -662,12 +677,14 @@ static int vbg_reset_host_capabilities(struct vbg_dev *gdev) } /** - * Set guest capabilities on the host. - * Must be called with gdev->session_mutex hold. - * Return: 0 or negative errno value. + * vbg_set_host_capabilities - Set guest capabilities on the host. * @gdev: The Guest extension device. * @session: The session. * @session_termination: Set if we're called by the session cleanup code. + * + * Must be called with gdev->session_mutex hold. + * + * Return: %0 or negative errno value. */ static int vbg_set_host_capabilities(struct vbg_dev *gdev, struct vbg_session *session, @@ -704,9 +721,8 @@ static int vbg_set_host_capabilities(struct vbg_dev *gdev, } /** - * Acquire (get exclusive access) guest capabilities for a session. - * Takes the session mutex. - * Return: 0 or negative errno value. + * vbg_acquire_session_capabilities - Acquire (get exclusive access) + * guest capabilities for a session. * @gdev: The Guest extension device. * @session: The session. * @flags: Flags (VBGL_IOC_AGC_FLAGS_XXX). @@ -716,6 +732,10 @@ static int vbg_set_host_capabilities(struct vbg_dev *gdev, * This tweaks the error handling so we perform * proper session cleanup even if the host * misbehaves. + * + * Takes the session mutex. + * + * Return: %0 or negative errno value. */ static int vbg_acquire_session_capabilities(struct vbg_dev *gdev, struct vbg_session *session, @@ -811,8 +831,8 @@ out: } /** - * Sets the guest capabilities for a session. Takes the session mutex. - * Return: 0 or negative errno value. + * vbg_set_session_capabilities - Sets the guest capabilities for a + * session. Takes the session mutex. * @gdev: The Guest extension device. * @session: The session. * @or_mask: The capabilities to add. @@ -821,6 +841,8 @@ out: * This tweaks the error handling so we perform * proper session cleanup even if the host * misbehaves. + * + * Return: %0 or negative errno value. */ static int vbg_set_session_capabilities(struct vbg_dev *gdev, struct vbg_session *session, @@ -866,9 +888,10 @@ out: } /** - * vbg_query_host_version get the host feature mask and version information. - * Return: 0 or negative errno value. + * vbg_query_host_version - get the host feature mask and version information. * @gdev: The Guest extension device. + * + * Return: %0 or negative errno value. */ static int vbg_query_host_version(struct vbg_dev *gdev) { @@ -905,19 +928,18 @@ out: } /** - * Initializes the VBoxGuest device extension when the - * device driver is loaded. + * vbg_core_init - Initializes the VBoxGuest device extension when the + * device driver is loaded. + * @gdev: The Guest extension device. + * @fixed_events: Events that will be enabled upon init and no client + * will ever be allowed to mask. * * The native code locates the VMMDev on the PCI bus and retrieve * the MMIO and I/O port ranges, this function will take care of * mapping the MMIO memory (if present). Upon successful return * the native code should set up the interrupt handler. * - * Return: 0 or negative errno value. - * - * @gdev: The Guest extension device. - * @fixed_events: Events that will be enabled upon init and no client - * will ever be allowed to mask. + * Return: %0 or negative errno value. */ int vbg_core_init(struct vbg_dev *gdev, u32 fixed_events) { @@ -1017,11 +1039,12 @@ err_free_reqs: } /** - * Call this on exit to clean-up vboxguest-core managed resources. + * vbg_core_exit - Call this on exit to clean-up vboxguest-core managed + * resources. + * @gdev: The Guest extension device. * * The native code should call this before the driver is loaded, * but don't call this on shutdown. - * @gdev: The Guest extension device. */ void vbg_core_exit(struct vbg_dev *gdev) { @@ -1046,12 +1069,13 @@ void vbg_core_exit(struct vbg_dev *gdev) } /** - * Creates a VBoxGuest user session. + * vbg_core_open_session - Creates a VBoxGuest user session. + * @gdev: The Guest extension device. + * @requestor: VMMDEV_REQUESTOR_* flags * * vboxguest_linux.c calls this when userspace opens the char-device. + * * Return: A pointer to the new session or an ERR_PTR on error. - * @gdev: The Guest extension device. - * @requestor: VMMDEV_REQUESTOR_* flags */ struct vbg_session *vbg_core_open_session(struct vbg_dev *gdev, u32 requestor) { @@ -1068,7 +1092,7 @@ struct vbg_session *vbg_core_open_session(struct vbg_dev *gdev, u32 requestor) } /** - * Closes a VBoxGuest session. + * vbg_core_close_session - Closes a VBoxGuest session. * @session: The session to close (and free). */ void vbg_core_close_session(struct vbg_session *session) @@ -1250,11 +1274,13 @@ static int vbg_ioctl_interrupt_all_wait_events(struct vbg_dev *gdev, } /** - * Checks if the VMM request is allowed in the context of the given session. - * Return: 0 or negative errno value. + * vbg_req_allowed - Checks if the VMM request is allowed in the + * context of the given session. * @gdev: The Guest extension device. * @session: The calling session. * @req: The request. + * + * Return: %0 or negative errno value. */ static int vbg_req_allowed(struct vbg_dev *gdev, struct vbg_session *session, const struct vmmdev_request_header *req) @@ -1670,11 +1696,12 @@ static int vbg_ioctl_write_core_dump(struct vbg_dev *gdev, } /** - * Common IOCtl for user to kernel communication. - * Return: 0 or negative errno value. + * vbg_core_ioctl - Common IOCtl for user to kernel communication. * @session: The client session. * @req: The requested function. * @data: The i/o data buffer, minimum size sizeof(struct vbg_ioctl_hdr). + * + * Return: %0 or negative errno value. */ int vbg_core_ioctl(struct vbg_session *session, unsigned int req, void *data) { @@ -1744,11 +1771,12 @@ int vbg_core_ioctl(struct vbg_session *session, unsigned int req, void *data) } /** - * Report guest supported mouse-features to the host. + * vbg_core_set_mouse_status - Report guest supported mouse-features to the host. * - * Return: 0 or negative errno value. * @gdev: The Guest extension device. * @features: The set of features to report to the host. + * + * Return: %0 or negative errno value. */ int vbg_core_set_mouse_status(struct vbg_dev *gdev, u32 features) { @@ -1772,7 +1800,7 @@ int vbg_core_set_mouse_status(struct vbg_dev *gdev, u32 features) return vbg_status_code_to_errno(rc); } -/** Core interrupt service routine. */ +/* Core interrupt service routine. */ irqreturn_t vbg_core_isr(int irq, void *dev_id) { struct vbg_dev *gdev = dev_id; diff --git a/drivers/virt/vboxguest/vboxguest_linux.c b/drivers/virt/vboxguest/vboxguest_linux.c index c47e62dc55da..8c92ea5b7305 100644 --- a/drivers/virt/vboxguest/vboxguest_linux.c +++ b/drivers/virt/vboxguest/vboxguest_linux.c @@ -81,10 +81,11 @@ static int vbg_misc_device_user_open(struct inode *inode, struct file *filp) } /** - * Close device. - * Return: 0 on success, negated errno on failure. + * vbg_misc_device_close - Close device. * @inode: Pointer to inode info structure. * @filp: Associated file pointer. + * + * Return: %0 on success, negated errno on failure. */ static int vbg_misc_device_close(struct inode *inode, struct file *filp) { @@ -94,11 +95,12 @@ static int vbg_misc_device_close(struct inode *inode, struct file *filp) } /** - * Device I/O Control entry point. - * Return: 0 on success, negated errno on failure. + * vbg_misc_device_ioctl - Device I/O Control entry point. * @filp: Associated file pointer. * @req: The request specified to ioctl(). * @arg: The argument specified to ioctl(). + * + * Return: %0 on success, negated errno on failure. */ static long vbg_misc_device_ioctl(struct file *filp, unsigned int req, unsigned long arg) @@ -173,7 +175,7 @@ out: return ret; } -/** The file_operations structures. */ +/* The file_operations structures. */ static const struct file_operations vbg_misc_device_fops = { .owner = THIS_MODULE, .open = vbg_misc_device_open, @@ -193,7 +195,7 @@ static const struct file_operations vbg_misc_device_user_fops = { #endif }; -/** +/* * Called when the input device is first opened. * * Sets up absolute mouse reporting. @@ -206,7 +208,7 @@ static int vbg_input_open(struct input_dev *input) return vbg_core_set_mouse_status(gdev, feat); } -/** +/* * Called if all open handles to the input device are closed. * * Disables absolute reporting. @@ -218,7 +220,7 @@ static void vbg_input_close(struct input_dev *input) vbg_core_set_mouse_status(gdev, 0); } -/** +/* * Creates the kernel input device. * * Return: 0 on success, negated errno on failure. @@ -277,7 +279,7 @@ static struct attribute *vbg_pci_attrs[] = { }; ATTRIBUTE_GROUPS(vbg_pci); -/** +/* * Does the PCI detection and init of the device. * * Return: 0 on success, negated errno on failure. @@ -453,7 +455,7 @@ void vbg_put_gdev(struct vbg_dev *gdev) } EXPORT_SYMBOL(vbg_put_gdev); -/** +/* * Callback for mouse events. * * This is called at the end of the ISR, after leaving the event spinlock, if diff --git a/drivers/virt/vboxguest/vboxguest_utils.c b/drivers/virt/vboxguest/vboxguest_utils.c index 8d195e3f8301..1c02b3c0d934 100644 --- a/drivers/virt/vboxguest/vboxguest_utils.c +++ b/drivers/virt/vboxguest/vboxguest_utils.c @@ -237,14 +237,16 @@ static int hgcm_call_preprocess_linaddr( } /** - * Preprocesses the HGCM call, validate parameters, alloc bounce buffers and - * figure out how much extra storage we need for page lists. - * Return: 0 or negative errno value. + * hgcm_call_preprocess - Preprocesses the HGCM call, validate parameters, + * alloc bounce buffers and figure out how much extra storage we need for + * page lists. * @src_parm: Pointer to source function call parameters * @parm_count: Number of function call parameters. * @bounce_bufs_ret: Where to return the allocated bouncebuffer array * @extra: Where to return the extra request space needed for * physical page lists. + * + * Return: %0 or negative errno value. */ static int hgcm_call_preprocess( const struct vmmdev_hgcm_function_parameter *src_parm, @@ -301,10 +303,11 @@ static int hgcm_call_preprocess( } /** - * Translates linear address types to page list direction flags. + * hgcm_call_linear_addr_type_to_pagelist_flags - Translates linear address + * types to page list direction flags. + * @type: The type. * * Return: page list flags. - * @type: The type. */ static u32 hgcm_call_linear_addr_type_to_pagelist_flags( enum vmmdev_hgcm_function_parameter_type type) @@ -369,7 +372,8 @@ static void hgcm_call_init_linaddr(struct vmmdev_hgcm_call *call, } /** - * Initializes the call request that we're sending to the host. + * hgcm_call_init_call - Initializes the call request that we're sending + * to the host. * @call: The call to initialize. * @client_id: The client ID of the caller. * @function: The function number of the function to call. @@ -425,7 +429,9 @@ static void hgcm_call_init_call( } /** - * Tries to cancel a pending HGCM call. + * hgcm_cancel_call - Tries to cancel a pending HGCM call. + * @gdev: The VBoxGuest device extension. + * @call: The call to cancel. * * Return: VBox status code */ @@ -459,13 +465,15 @@ static int hgcm_cancel_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *call) } /** - * Performs the call and completion wait. - * Return: 0 or negative errno value. + * vbg_hgcm_do_call - Performs the call and completion wait. * @gdev: The VBoxGuest device extension. * @call: The call to execute. * @timeout_ms: Timeout in ms. + * @interruptible: whether this call is interruptible * @leak_it: Where to return the leak it / free it, indicator. * Cancellation fun. + * + * Return: %0 or negative errno value. */ static int vbg_hgcm_do_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *call, u32 timeout_ms, bool interruptible, bool *leak_it) @@ -545,13 +553,14 @@ static int vbg_hgcm_do_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *call, } /** - * Copies the result of the call back to the caller info structure and user - * buffers. - * Return: 0 or negative errno value. + * hgcm_call_copy_back_result - Copies the result of the call back to + * the caller info structure and user buffers. * @call: HGCM call request. * @dst_parm: Pointer to function call parameters destination. * @parm_count: Number of function call parameters. * @bounce_bufs: The bouncebuffer array. + * + * Return: %0 or negative errno value. */ static int hgcm_call_copy_back_result( const struct vmmdev_hgcm_call *call, diff --git a/drivers/virt/vmgenid.c b/drivers/virt/vmgenid.c index a1c467a0e9f7..b67a28da4702 100644 --- a/drivers/virt/vmgenid.c +++ b/drivers/virt/vmgenid.c @@ -68,6 +68,7 @@ out: static void vmgenid_notify(struct acpi_device *device, u32 event) { struct vmgenid_state *state = acpi_driver_data(device); + char *envp[] = { "NEW_VMGENID=1", NULL }; u8 old_id[VMGENID_SIZE]; memcpy(old_id, state->this_id, sizeof(old_id)); @@ -75,6 +76,7 @@ static void vmgenid_notify(struct acpi_device *device, u32 event) if (!memcmp(old_id, state->this_id, sizeof(old_id))) return; add_vmfork_randomness(state->this_id, sizeof(state->this_id)); + kobject_uevent_env(&device->dev.kobj, KOBJ_CHANGE, envp); } static const struct acpi_device_id vmgenid_ids[] = { diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 0a53a61231c2..c17193544268 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -60,6 +60,11 @@ config VIRTIO_PCI If unsure, say M. +config VIRTIO_PCI_ADMIN_LEGACY + bool + depends on VIRTIO_PCI && (X86 || COMPILE_TEST) + default y + config VIRTIO_PCI_LEGACY bool "Support for legacy virtio draft 0.9.X and older devices" default y diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 8e98d24917cc..73ace62af440 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o +virtio_pci-$(CONFIG_VIRTIO_PCI_ADMIN_LEGACY) += virtio_pci_admin_legacy_io.o obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o obj-$(CONFIG_VIRTIO_VDPA) += virtio_vdpa.o diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 3893dc29eb26..f4080692b351 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -302,9 +302,15 @@ static int virtio_dev_probe(struct device *_d) if (err) goto err; + if (dev->config->create_avq) { + err = dev->config->create_avq(dev); + if (err) + goto err; + } + err = drv->probe(dev); if (err) - goto err; + goto err_probe; /* If probe didn't do it, mark device DRIVER_OK ourselves. */ if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK)) @@ -316,6 +322,10 @@ static int virtio_dev_probe(struct device *_d) virtio_config_enable(dev); return 0; + +err_probe: + if (dev->config->destroy_avq) + dev->config->destroy_avq(dev); err: virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED); return err; @@ -331,6 +341,9 @@ static void virtio_dev_remove(struct device *_d) drv->remove(dev); + if (dev->config->destroy_avq) + dev->config->destroy_avq(dev); + /* Driver should have reset device. */ WARN_ON_ONCE(dev->config->get_status(dev)); @@ -489,13 +502,20 @@ EXPORT_SYMBOL_GPL(unregister_virtio_device); int virtio_device_freeze(struct virtio_device *dev) { struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); + int ret; virtio_config_disable(dev); dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED; - if (drv && drv->freeze) - return drv->freeze(dev); + if (drv && drv->freeze) { + ret = drv->freeze(dev); + if (ret) + return ret; + } + + if (dev->config->destroy_avq) + dev->config->destroy_avq(dev); return 0; } @@ -532,10 +552,16 @@ int virtio_device_restore(struct virtio_device *dev) if (ret) goto err; + if (dev->config->create_avq) { + ret = dev->config->create_avq(dev); + if (ret) + goto err; + } + if (drv->restore) { ret = drv->restore(dev); if (ret) - goto err; + goto err_restore; } /* If restore didn't do it, mark device DRIVER_OK ourselves. */ @@ -546,6 +572,9 @@ int virtio_device_restore(struct virtio_device *dev) return 0; +err_restore: + if (dev->config->destroy_avq) + dev->config->destroy_avq(dev); err: virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED); return ret; diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 59cdc0292dce..1f5b3dd31fcf 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -119,6 +119,11 @@ struct virtio_balloon { /* Free page reporting device */ struct virtqueue *reporting_vq; struct page_reporting_dev_info pr_dev_info; + + /* State for keeping the wakeup_source active while adjusting the balloon */ + spinlock_t adjustment_lock; + bool adjustment_signal_pending; + bool adjustment_in_progress; }; static const struct virtio_device_id id_table[] = { @@ -437,6 +442,31 @@ static void virtio_balloon_queue_free_page_work(struct virtio_balloon *vb) queue_work(vb->balloon_wq, &vb->report_free_page_work); } +static void start_update_balloon_size(struct virtio_balloon *vb) +{ + unsigned long flags; + + spin_lock_irqsave(&vb->adjustment_lock, flags); + vb->adjustment_signal_pending = true; + if (!vb->adjustment_in_progress) { + vb->adjustment_in_progress = true; + pm_stay_awake(vb->vdev->dev.parent); + } + spin_unlock_irqrestore(&vb->adjustment_lock, flags); + + queue_work(system_freezable_wq, &vb->update_balloon_size_work); +} + +static void end_update_balloon_size(struct virtio_balloon *vb) +{ + spin_lock_irq(&vb->adjustment_lock); + if (!vb->adjustment_signal_pending && vb->adjustment_in_progress) { + vb->adjustment_in_progress = false; + pm_relax(vb->vdev->dev.parent); + } + spin_unlock_irq(&vb->adjustment_lock); +} + static void virtballoon_changed(struct virtio_device *vdev) { struct virtio_balloon *vb = vdev->priv; @@ -444,8 +474,7 @@ static void virtballoon_changed(struct virtio_device *vdev) spin_lock_irqsave(&vb->stop_update_lock, flags); if (!vb->stop_update) { - queue_work(system_freezable_wq, - &vb->update_balloon_size_work); + start_update_balloon_size(vb); virtio_balloon_queue_free_page_work(vb); } spin_unlock_irqrestore(&vb->stop_update_lock, flags); @@ -476,19 +505,25 @@ static void update_balloon_size_func(struct work_struct *work) vb = container_of(work, struct virtio_balloon, update_balloon_size_work); - diff = towards_target(vb); - if (!diff) - return; + spin_lock_irq(&vb->adjustment_lock); + vb->adjustment_signal_pending = false; + spin_unlock_irq(&vb->adjustment_lock); - if (diff > 0) - diff -= fill_balloon(vb, diff); - else - diff += leak_balloon(vb, -diff); - update_balloon_size(vb); + diff = towards_target(vb); + + if (diff) { + if (diff > 0) + diff -= fill_balloon(vb, diff); + else + diff += leak_balloon(vb, -diff); + update_balloon_size(vb); + } if (diff) queue_work(system_freezable_wq, work); + else + end_update_balloon_size(vb); } static int init_vqs(struct virtio_balloon *vb) @@ -992,6 +1027,8 @@ static int virtballoon_probe(struct virtio_device *vdev) goto out_unregister_oom; } + spin_lock_init(&vb->adjustment_lock); + virtio_device_ready(vdev); if (towards_target(vb)) diff --git a/drivers/virtio/virtio_pci_admin_legacy_io.c b/drivers/virtio/virtio_pci_admin_legacy_io.c new file mode 100644 index 000000000000..819cfbbc67c3 --- /dev/null +++ b/drivers/virtio/virtio_pci_admin_legacy_io.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved + */ + +#include <linux/virtio_pci_admin.h> +#include "virtio_pci_common.h" + +/* + * virtio_pci_admin_has_legacy_io - Checks whether the legacy IO + * commands are supported + * @dev: VF pci_dev + * + * Returns true on success. + */ +bool virtio_pci_admin_has_legacy_io(struct pci_dev *pdev) +{ + struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev); + struct virtio_pci_device *vp_dev; + + if (!virtio_dev) + return false; + + if (!virtio_has_feature(virtio_dev, VIRTIO_F_ADMIN_VQ)) + return false; + + vp_dev = to_vp_device(virtio_dev); + + if ((vp_dev->admin_vq.supported_cmds & VIRTIO_LEGACY_ADMIN_CMD_BITMAP) == + VIRTIO_LEGACY_ADMIN_CMD_BITMAP) + return true; + return false; +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_has_legacy_io); + +static int virtio_pci_admin_legacy_io_write(struct pci_dev *pdev, u16 opcode, + u8 offset, u8 size, u8 *buf) +{ + struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev); + struct virtio_admin_cmd_legacy_wr_data *data; + struct virtio_admin_cmd cmd = {}; + struct scatterlist data_sg; + int vf_id; + int ret; + + if (!virtio_dev) + return -ENODEV; + + vf_id = pci_iov_vf_id(pdev); + if (vf_id < 0) + return vf_id; + + data = kzalloc(sizeof(*data) + size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->offset = offset; + memcpy(data->registers, buf, size); + sg_init_one(&data_sg, data, sizeof(*data) + size); + cmd.opcode = cpu_to_le16(opcode); + cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV); + cmd.group_member_id = cpu_to_le64(vf_id + 1); + cmd.data_sg = &data_sg; + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + + kfree(data); + return ret; +} + +/* + * virtio_pci_admin_legacy_io_write_common - Write legacy common configuration + * of a member device + * @dev: VF pci_dev + * @offset: starting byte offset within the common configuration area to write to + * @size: size of the data to write + * @buf: buffer which holds the data + * + * Note: caller must serialize access for the given device. + * Returns 0 on success, or negative on failure. + */ +int virtio_pci_admin_legacy_common_io_write(struct pci_dev *pdev, u8 offset, + u8 size, u8 *buf) +{ + return virtio_pci_admin_legacy_io_write(pdev, + VIRTIO_ADMIN_CMD_LEGACY_COMMON_CFG_WRITE, + offset, size, buf); +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_common_io_write); + +/* + * virtio_pci_admin_legacy_io_write_device - Write legacy device configuration + * of a member device + * @dev: VF pci_dev + * @offset: starting byte offset within the device configuration area to write to + * @size: size of the data to write + * @buf: buffer which holds the data + * + * Note: caller must serialize access for the given device. + * Returns 0 on success, or negative on failure. + */ +int virtio_pci_admin_legacy_device_io_write(struct pci_dev *pdev, u8 offset, + u8 size, u8 *buf) +{ + return virtio_pci_admin_legacy_io_write(pdev, + VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_WRITE, + offset, size, buf); +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_device_io_write); + +static int virtio_pci_admin_legacy_io_read(struct pci_dev *pdev, u16 opcode, + u8 offset, u8 size, u8 *buf) +{ + struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev); + struct virtio_admin_cmd_legacy_rd_data *data; + struct scatterlist data_sg, result_sg; + struct virtio_admin_cmd cmd = {}; + int vf_id; + int ret; + + if (!virtio_dev) + return -ENODEV; + + vf_id = pci_iov_vf_id(pdev); + if (vf_id < 0) + return vf_id; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->offset = offset; + sg_init_one(&data_sg, data, sizeof(*data)); + sg_init_one(&result_sg, buf, size); + cmd.opcode = cpu_to_le16(opcode); + cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV); + cmd.group_member_id = cpu_to_le64(vf_id + 1); + cmd.data_sg = &data_sg; + cmd.result_sg = &result_sg; + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + + kfree(data); + return ret; +} + +/* + * virtio_pci_admin_legacy_device_io_read - Read legacy device configuration of + * a member device + * @dev: VF pci_dev + * @offset: starting byte offset within the device configuration area to read from + * @size: size of the data to be read + * @buf: buffer to hold the returned data + * + * Note: caller must serialize access for the given device. + * Returns 0 on success, or negative on failure. + */ +int virtio_pci_admin_legacy_device_io_read(struct pci_dev *pdev, u8 offset, + u8 size, u8 *buf) +{ + return virtio_pci_admin_legacy_io_read(pdev, + VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_READ, + offset, size, buf); +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_device_io_read); + +/* + * virtio_pci_admin_legacy_common_io_read - Read legacy common configuration of + * a member device + * @dev: VF pci_dev + * @offset: starting byte offset within the common configuration area to read from + * @size: size of the data to be read + * @buf: buffer to hold the returned data + * + * Note: caller must serialize access for the given device. + * Returns 0 on success, or negative on failure. + */ +int virtio_pci_admin_legacy_common_io_read(struct pci_dev *pdev, u8 offset, + u8 size, u8 *buf) +{ + return virtio_pci_admin_legacy_io_read(pdev, + VIRTIO_ADMIN_CMD_LEGACY_COMMON_CFG_READ, + offset, size, buf); +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_common_io_read); + +/* + * virtio_pci_admin_legacy_io_notify_info - Read the queue notification + * information for legacy interface + * @dev: VF pci_dev + * @req_bar_flags: requested bar flags + * @bar: on output the BAR number of the owner or member device + * @bar_offset: on output the offset within bar + * + * Returns 0 on success, or negative on failure. + */ +int virtio_pci_admin_legacy_io_notify_info(struct pci_dev *pdev, + u8 req_bar_flags, u8 *bar, + u64 *bar_offset) +{ + struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev); + struct virtio_admin_cmd_notify_info_result *result; + struct virtio_admin_cmd cmd = {}; + struct scatterlist result_sg; + int vf_id; + int ret; + + if (!virtio_dev) + return -ENODEV; + + vf_id = pci_iov_vf_id(pdev); + if (vf_id < 0) + return vf_id; + + result = kzalloc(sizeof(*result), GFP_KERNEL); + if (!result) + return -ENOMEM; + + sg_init_one(&result_sg, result, sizeof(*result)); + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_LEGACY_NOTIFY_INFO); + cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV); + cmd.group_member_id = cpu_to_le64(vf_id + 1); + cmd.result_sg = &result_sg; + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + if (!ret) { + struct virtio_admin_cmd_notify_info_data *entry; + int i; + + ret = -ENOENT; + for (i = 0; i < VIRTIO_ADMIN_CMD_MAX_NOTIFY_INFO; i++) { + entry = &result->entries[i]; + if (entry->flags == VIRTIO_ADMIN_CMD_NOTIFY_INFO_FLAGS_END) + break; + if (entry->flags != req_bar_flags) + continue; + *bar = entry->bar; + *bar_offset = le64_to_cpu(entry->offset); + ret = 0; + break; + } + } + + kfree(result); + return ret; +} +EXPORT_SYMBOL_GPL(virtio_pci_admin_legacy_io_notify_info); diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index 7a5593997e0e..b655fccaf773 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -236,6 +236,9 @@ void vp_del_vqs(struct virtio_device *vdev) int i; list_for_each_entry_safe(vq, n, &vdev->vqs, list) { + if (vp_dev->is_avq(vdev, vq->index)) + continue; + if (vp_dev->per_vq_vectors) { int v = vp_dev->vqs[vq->index]->msix_vector; @@ -492,8 +495,40 @@ static int virtio_pci_restore(struct device *dev) return virtio_device_restore(&vp_dev->vdev); } +static bool vp_supports_pm_no_reset(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + u16 pmcsr; + + if (!pci_dev->pm_cap) + return false; + + pci_read_config_word(pci_dev, pci_dev->pm_cap + PCI_PM_CTRL, &pmcsr); + if (PCI_POSSIBLE_ERROR(pmcsr)) { + dev_err(dev, "Unable to query pmcsr"); + return false; + } + + return pmcsr & PCI_PM_CTRL_NO_SOFT_RESET; +} + +static int virtio_pci_suspend(struct device *dev) +{ + return vp_supports_pm_no_reset(dev) ? 0 : virtio_pci_freeze(dev); +} + +static int virtio_pci_resume(struct device *dev) +{ + return vp_supports_pm_no_reset(dev) ? 0 : virtio_pci_restore(dev); +} + static const struct dev_pm_ops virtio_pci_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(virtio_pci_freeze, virtio_pci_restore) + .suspend = virtio_pci_suspend, + .resume = virtio_pci_resume, + .freeze = virtio_pci_freeze, + .thaw = virtio_pci_restore, + .poweroff = virtio_pci_freeze, + .restore = virtio_pci_restore, }; #endif @@ -642,6 +677,17 @@ static struct pci_driver virtio_pci_driver = { .sriov_configure = virtio_pci_sriov_configure, }; +struct virtio_device *virtio_pci_vf_get_pf_dev(struct pci_dev *pdev) +{ + struct virtio_pci_device *pf_vp_dev; + + pf_vp_dev = pci_iov_get_pf_drvdata(pdev, &virtio_pci_driver); + if (IS_ERR(pf_vp_dev)) + return NULL; + + return &pf_vp_dev->vdev; +} + module_pci_driver(virtio_pci_driver); MODULE_AUTHOR("Anthony Liguori <aliguori@us.ibm.com>"); diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h index 4b773bd7c58c..7fef52bee455 100644 --- a/drivers/virtio/virtio_pci_common.h +++ b/drivers/virtio/virtio_pci_common.h @@ -29,6 +29,7 @@ #include <linux/virtio_pci_modern.h> #include <linux/highmem.h> #include <linux/spinlock.h> +#include <linux/mutex.h> struct virtio_pci_vq_info { /* the actual virtqueue */ @@ -41,6 +42,17 @@ struct virtio_pci_vq_info { unsigned int msix_vector; }; +struct virtio_pci_admin_vq { + /* Virtqueue info associated with this admin queue. */ + struct virtio_pci_vq_info info; + /* serializing admin commands execution and virtqueue deletion */ + struct mutex cmd_lock; + u64 supported_cmds; + /* Name of the admin queue: avq.$vq_index. */ + char name[10]; + u16 vq_index; +}; + /* Our device structure */ struct virtio_pci_device { struct virtio_device vdev; @@ -58,9 +70,13 @@ struct virtio_pci_device { spinlock_t lock; struct list_head virtqueues; - /* array of all queues for house-keeping */ + /* Array of all virtqueues reported in the + * PCI common config num_queues field + */ struct virtio_pci_vq_info **vqs; + struct virtio_pci_admin_vq admin_vq; + /* MSI-X support */ int msix_enabled; int intx_enabled; @@ -86,6 +102,7 @@ struct virtio_pci_device { void (*del_vq)(struct virtio_pci_vq_info *info); u16 (*config_vector)(struct virtio_pci_device *vp_dev, u16 vector); + bool (*is_avq)(struct virtio_device *vdev, unsigned int index); }; /* Constants for MSI-X */ @@ -139,4 +156,27 @@ static inline void virtio_pci_legacy_remove(struct virtio_pci_device *vp_dev) int virtio_pci_modern_probe(struct virtio_pci_device *); void virtio_pci_modern_remove(struct virtio_pci_device *); +struct virtio_device *virtio_pci_vf_get_pf_dev(struct pci_dev *pdev); + +#define VIRTIO_LEGACY_ADMIN_CMD_BITMAP \ + (BIT_ULL(VIRTIO_ADMIN_CMD_LEGACY_COMMON_CFG_WRITE) | \ + BIT_ULL(VIRTIO_ADMIN_CMD_LEGACY_COMMON_CFG_READ) | \ + BIT_ULL(VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_WRITE) | \ + BIT_ULL(VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_READ) | \ + BIT_ULL(VIRTIO_ADMIN_CMD_LEGACY_NOTIFY_INFO)) + +/* Unlike modern drivers which support hardware virtio devices, legacy drivers + * assume software-based devices: e.g. they don't use proper memory barriers + * on ARM, use big endian on PPC, etc. X86 drivers are mostly ok though, more + * or less by chance. For now, only support legacy IO on X86. + */ +#ifdef CONFIG_VIRTIO_PCI_ADMIN_LEGACY +#define VIRTIO_ADMIN_CMD_BITMAP VIRTIO_LEGACY_ADMIN_CMD_BITMAP +#else +#define VIRTIO_ADMIN_CMD_BITMAP 0 +#endif + +int vp_modern_admin_cmd_exec(struct virtio_device *vdev, + struct virtio_admin_cmd *cmd); + #endif diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index ee6a386d250b..f62b530aa3b5 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -19,6 +19,8 @@ #define VIRTIO_RING_NO_LEGACY #include "virtio_pci_common.h" +#define VIRTIO_AVQ_SGS_MAX 4 + static u64 vp_get_features(struct virtio_device *vdev) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); @@ -26,6 +28,187 @@ static u64 vp_get_features(struct virtio_device *vdev) return vp_modern_get_features(&vp_dev->mdev); } +static bool vp_is_avq(struct virtio_device *vdev, unsigned int index) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + + if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ)) + return false; + + return index == vp_dev->admin_vq.vq_index; +} + +static int virtqueue_exec_admin_cmd(struct virtio_pci_admin_vq *admin_vq, + u16 opcode, + struct scatterlist **sgs, + unsigned int out_num, + unsigned int in_num, + void *data) +{ + struct virtqueue *vq; + int ret, len; + + vq = admin_vq->info.vq; + if (!vq) + return -EIO; + + if (opcode != VIRTIO_ADMIN_CMD_LIST_QUERY && + opcode != VIRTIO_ADMIN_CMD_LIST_USE && + !((1ULL << opcode) & admin_vq->supported_cmds)) + return -EOPNOTSUPP; + + ret = virtqueue_add_sgs(vq, sgs, out_num, in_num, data, GFP_KERNEL); + if (ret < 0) + return -EIO; + + if (unlikely(!virtqueue_kick(vq))) + return -EIO; + + while (!virtqueue_get_buf(vq, &len) && + !virtqueue_is_broken(vq)) + cpu_relax(); + + if (virtqueue_is_broken(vq)) + return -EIO; + + return 0; +} + +int vp_modern_admin_cmd_exec(struct virtio_device *vdev, + struct virtio_admin_cmd *cmd) +{ + struct scatterlist *sgs[VIRTIO_AVQ_SGS_MAX], hdr, stat; + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + struct virtio_admin_cmd_status *va_status; + unsigned int out_num = 0, in_num = 0; + struct virtio_admin_cmd_hdr *va_hdr; + u16 status; + int ret; + + if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ)) + return -EOPNOTSUPP; + + va_status = kzalloc(sizeof(*va_status), GFP_KERNEL); + if (!va_status) + return -ENOMEM; + + va_hdr = kzalloc(sizeof(*va_hdr), GFP_KERNEL); + if (!va_hdr) { + ret = -ENOMEM; + goto err_alloc; + } + + va_hdr->opcode = cmd->opcode; + va_hdr->group_type = cmd->group_type; + va_hdr->group_member_id = cmd->group_member_id; + + /* Add header */ + sg_init_one(&hdr, va_hdr, sizeof(*va_hdr)); + sgs[out_num] = &hdr; + out_num++; + + if (cmd->data_sg) { + sgs[out_num] = cmd->data_sg; + out_num++; + } + + /* Add return status */ + sg_init_one(&stat, va_status, sizeof(*va_status)); + sgs[out_num + in_num] = &stat; + in_num++; + + if (cmd->result_sg) { + sgs[out_num + in_num] = cmd->result_sg; + in_num++; + } + + mutex_lock(&vp_dev->admin_vq.cmd_lock); + ret = virtqueue_exec_admin_cmd(&vp_dev->admin_vq, + le16_to_cpu(cmd->opcode), + sgs, out_num, in_num, sgs); + mutex_unlock(&vp_dev->admin_vq.cmd_lock); + + if (ret) { + dev_err(&vdev->dev, + "Failed to execute command on admin vq: %d\n.", ret); + goto err_cmd_exec; + } + + status = le16_to_cpu(va_status->status); + if (status != VIRTIO_ADMIN_STATUS_OK) { + dev_err(&vdev->dev, + "admin command error: status(%#x) qualifier(%#x)\n", + status, le16_to_cpu(va_status->status_qualifier)); + ret = -status; + } + +err_cmd_exec: + kfree(va_hdr); +err_alloc: + kfree(va_status); + return ret; +} + +static void virtio_pci_admin_cmd_list_init(struct virtio_device *virtio_dev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(virtio_dev); + struct virtio_admin_cmd cmd = {}; + struct scatterlist result_sg; + struct scatterlist data_sg; + __le64 *data; + int ret; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return; + + sg_init_one(&result_sg, data, sizeof(*data)); + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_LIST_QUERY); + cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV); + cmd.result_sg = &result_sg; + + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + if (ret) + goto end; + + *data &= cpu_to_le64(VIRTIO_ADMIN_CMD_BITMAP); + sg_init_one(&data_sg, data, sizeof(*data)); + cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_LIST_USE); + cmd.data_sg = &data_sg; + cmd.result_sg = NULL; + + ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd); + if (ret) + goto end; + + vp_dev->admin_vq.supported_cmds = le64_to_cpu(*data); +end: + kfree(data); +} + +static void vp_modern_avq_activate(struct virtio_device *vdev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + struct virtio_pci_admin_vq *admin_vq = &vp_dev->admin_vq; + + if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ)) + return; + + __virtqueue_unbreak(admin_vq->info.vq); + virtio_pci_admin_cmd_list_init(vdev); +} + +static void vp_modern_avq_deactivate(struct virtio_device *vdev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + struct virtio_pci_admin_vq *admin_vq = &vp_dev->admin_vq; + + if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ)) + return; + + __virtqueue_break(admin_vq->info.vq); +} + static void vp_transport_features(struct virtio_device *vdev, u64 features) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); @@ -37,6 +220,9 @@ static void vp_transport_features(struct virtio_device *vdev, u64 features) if (features & BIT_ULL(VIRTIO_F_RING_RESET)) __virtio_set_bit(vdev, VIRTIO_F_RING_RESET); + + if (features & BIT_ULL(VIRTIO_F_ADMIN_VQ)) + __virtio_set_bit(vdev, VIRTIO_F_ADMIN_VQ); } static int __vp_check_common_size_one_feature(struct virtio_device *vdev, u32 fbit, @@ -69,6 +255,9 @@ static int vp_check_common_size(struct virtio_device *vdev) if (vp_check_common_size_one_feature(vdev, VIRTIO_F_RING_RESET, queue_reset)) return -EINVAL; + if (vp_check_common_size_one_feature(vdev, VIRTIO_F_ADMIN_VQ, admin_queue_num)) + return -EINVAL; + return 0; } @@ -195,6 +384,8 @@ static void vp_set_status(struct virtio_device *vdev, u8 status) /* We should never be setting status to 0. */ BUG_ON(status == 0); vp_modern_set_status(&vp_dev->mdev, status); + if (status & VIRTIO_CONFIG_S_DRIVER_OK) + vp_modern_avq_activate(vdev); } static void vp_reset(struct virtio_device *vdev) @@ -211,6 +402,9 @@ static void vp_reset(struct virtio_device *vdev) */ while (vp_modern_get_status(mdev)) msleep(1); + + vp_modern_avq_deactivate(vdev); + /* Flush pending VQ/configuration callbacks. */ vp_synchronize_vectors(vdev); } @@ -345,6 +539,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, struct virtio_pci_modern_device *mdev = &vp_dev->mdev; bool (*notify)(struct virtqueue *vq); struct virtqueue *vq; + bool is_avq; u16 num; int err; @@ -353,11 +548,13 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, else notify = vp_notify; - if (index >= vp_modern_get_num_queues(mdev)) + is_avq = vp_is_avq(&vp_dev->vdev, index); + if (index >= vp_modern_get_num_queues(mdev) && !is_avq) return ERR_PTR(-EINVAL); + num = is_avq ? + VIRTIO_AVQ_SGS_MAX : vp_modern_get_queue_size(mdev, index); /* Check if queue is either not available or already active. */ - num = vp_modern_get_queue_size(mdev, index); if (!num || vp_modern_get_queue_enable(mdev, index)) return ERR_PTR(-ENOENT); @@ -383,6 +580,12 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, goto err; } + if (is_avq) { + mutex_lock(&vp_dev->admin_vq.cmd_lock); + vp_dev->admin_vq.info.vq = vq; + mutex_unlock(&vp_dev->admin_vq.cmd_lock); + } + return vq; err: @@ -418,6 +621,12 @@ static void del_vq(struct virtio_pci_vq_info *info) struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); struct virtio_pci_modern_device *mdev = &vp_dev->mdev; + if (vp_is_avq(&vp_dev->vdev, vq->index)) { + mutex_lock(&vp_dev->admin_vq.cmd_lock); + vp_dev->admin_vq.info.vq = NULL; + mutex_unlock(&vp_dev->admin_vq.cmd_lock); + } + if (vp_dev->msix_enabled) vp_modern_queue_vector(mdev, vq->index, VIRTIO_MSI_NO_VECTOR); @@ -527,6 +736,45 @@ static bool vp_get_shm_region(struct virtio_device *vdev, return true; } +static int vp_modern_create_avq(struct virtio_device *vdev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + struct virtio_pci_admin_vq *avq; + struct virtqueue *vq; + u16 admin_q_num; + + if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ)) + return 0; + + admin_q_num = vp_modern_avq_num(&vp_dev->mdev); + if (!admin_q_num) + return -EINVAL; + + avq = &vp_dev->admin_vq; + avq->vq_index = vp_modern_avq_index(&vp_dev->mdev); + sprintf(avq->name, "avq.%u", avq->vq_index); + vq = vp_dev->setup_vq(vp_dev, &vp_dev->admin_vq.info, avq->vq_index, NULL, + avq->name, NULL, VIRTIO_MSI_NO_VECTOR); + if (IS_ERR(vq)) { + dev_err(&vdev->dev, "failed to setup admin virtqueue, err=%ld", + PTR_ERR(vq)); + return PTR_ERR(vq); + } + + vp_modern_set_queue_enable(&vp_dev->mdev, avq->info.vq->index, true); + return 0; +} + +static void vp_modern_destroy_avq(struct virtio_device *vdev) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + + if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ)) + return; + + vp_dev->del_vq(&vp_dev->admin_vq.info); +} + static const struct virtio_config_ops virtio_pci_config_nodev_ops = { .get = NULL, .set = NULL, @@ -545,6 +793,8 @@ static const struct virtio_config_ops virtio_pci_config_nodev_ops = { .get_shm_region = vp_get_shm_region, .disable_vq_and_reset = vp_modern_disable_vq_and_reset, .enable_vq_after_reset = vp_modern_enable_vq_after_reset, + .create_avq = vp_modern_create_avq, + .destroy_avq = vp_modern_destroy_avq, }; static const struct virtio_config_ops virtio_pci_config_ops = { @@ -565,6 +815,8 @@ static const struct virtio_config_ops virtio_pci_config_ops = { .get_shm_region = vp_get_shm_region, .disable_vq_and_reset = vp_modern_disable_vq_and_reset, .enable_vq_after_reset = vp_modern_enable_vq_after_reset, + .create_avq = vp_modern_create_avq, + .destroy_avq = vp_modern_destroy_avq, }; /* the PCI probing function */ @@ -588,9 +840,11 @@ int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev) vp_dev->config_vector = vp_config_vector; vp_dev->setup_vq = setup_vq; vp_dev->del_vq = del_vq; + vp_dev->is_avq = vp_is_avq; vp_dev->isr = mdev->isr; vp_dev->vdev.id = mdev->id; + mutex_init(&vp_dev->admin_vq.cmd_lock); return 0; } @@ -598,5 +852,6 @@ void virtio_pci_modern_remove(struct virtio_pci_device *vp_dev) { struct virtio_pci_modern_device *mdev = &vp_dev->mdev; + mutex_destroy(&vp_dev->admin_vq.cmd_lock); vp_modern_remove(mdev); } diff --git a/drivers/virtio/virtio_pci_modern_dev.c b/drivers/virtio/virtio_pci_modern_dev.c index 7de8b1ebabac..0d3dbfaf4b23 100644 --- a/drivers/virtio/virtio_pci_modern_dev.c +++ b/drivers/virtio/virtio_pci_modern_dev.c @@ -207,6 +207,10 @@ static inline void check_offsets(void) offsetof(struct virtio_pci_modern_common_cfg, queue_notify_data)); BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_RESET != offsetof(struct virtio_pci_modern_common_cfg, queue_reset)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_ADM_Q_IDX != + offsetof(struct virtio_pci_modern_common_cfg, admin_queue_index)); + BUILD_BUG_ON(VIRTIO_PCI_COMMON_ADM_Q_NUM != + offsetof(struct virtio_pci_modern_common_cfg, admin_queue_num)); } /* @@ -296,7 +300,7 @@ int vp_modern_probe(struct virtio_pci_modern_device *mdev) mdev->common = vp_modern_map_capability(mdev, common, sizeof(struct virtio_pci_common_cfg), 4, 0, offsetofend(struct virtio_pci_modern_common_cfg, - queue_reset), + admin_queue_num), &mdev->common_len, NULL); if (!mdev->common) goto err_map_common; @@ -719,6 +723,24 @@ void __iomem *vp_modern_map_vq_notify(struct virtio_pci_modern_device *mdev, } EXPORT_SYMBOL_GPL(vp_modern_map_vq_notify); +u16 vp_modern_avq_num(struct virtio_pci_modern_device *mdev) +{ + struct virtio_pci_modern_common_cfg __iomem *cfg; + + cfg = (struct virtio_pci_modern_common_cfg __iomem *)mdev->common; + return vp_ioread16(&cfg->admin_queue_num); +} +EXPORT_SYMBOL_GPL(vp_modern_avq_num); + +u16 vp_modern_avq_index(struct virtio_pci_modern_device *mdev) +{ + struct virtio_pci_modern_common_cfg __iomem *cfg; + + cfg = (struct virtio_pci_modern_common_cfg __iomem *)mdev->common; + return vp_ioread16(&cfg->admin_queue_index); +} +EXPORT_SYMBOL_GPL(vp_modern_avq_index); + MODULE_VERSION("0.1"); MODULE_DESCRIPTION("Modern Virtio PCI Device"); MODULE_AUTHOR("Jason Wang <jasowang@redhat.com>"); diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index ad316573288a..513c0b114337 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -5,6 +5,17 @@ menu "1-wire Bus Masters" +config W1_MASTER_AMD_AXI + tristate "AMD AXI 1-wire bus host" + help + Say Y here is you want to support the AMD AXI 1-wire IP core. + This driver makes use of the programmable logic IP to perform + correctly timed 1 wire transactions without relying on GPIO timing + through the kernel. + + This driver can also be built as a module. If so, the module will be + called amd_w1_axi. + config W1_MASTER_MATROX tristate "Matrox G400 transport layer for 1-wire" depends on PCI diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile index c5d85a827e52..6c5a21f9b88c 100644 --- a/drivers/w1/masters/Makefile +++ b/drivers/w1/masters/Makefile @@ -3,6 +3,7 @@ # Makefile for 1-wire bus master drivers. # +obj-$(CONFIG_W1_MASTER_AMD_AXI) += amd_axi_w1.o obj-$(CONFIG_W1_MASTER_MATROX) += matrox_w1.o obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o diff --git a/drivers/w1/masters/amd_axi_w1.c b/drivers/w1/masters/amd_axi_w1.c new file mode 100644 index 000000000000..4d3a68ca9263 --- /dev/null +++ b/drivers/w1/masters/amd_axi_w1.c @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * amd_axi_w1 - AMD 1Wire programmable logic bus host driver + * + * Copyright (C) 2022-2023 Advanced Micro Devices, Inc. All Rights Reserved. + */ + +#include <linux/atomic.h> +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <linux/wait.h> + +#include <linux/w1.h> + +/* 1-wire AMD IP definition */ +#define AXIW1_IPID 0x10ee4453 +/* Registers offset */ +#define AXIW1_INST_REG 0x0 +#define AXIW1_CTRL_REG 0x4 +#define AXIW1_IRQE_REG 0x8 +#define AXIW1_STAT_REG 0xC +#define AXIW1_DATA_REG 0x10 +#define AXIW1_IPVER_REG 0x18 +#define AXIW1_IPID_REG 0x1C +/* Instructions */ +#define AXIW1_INITPRES 0x0800 +#define AXIW1_READBIT 0x0C00 +#define AXIW1_WRITEBIT 0x0E00 +#define AXIW1_READBYTE 0x0D00 +#define AXIW1_WRITEBYTE 0x0F00 +/* Status flag masks */ +#define AXIW1_DONE BIT(0) +#define AXIW1_READY BIT(4) +#define AXIW1_PRESENCE BIT(31) +#define AXIW1_MAJORVER_MASK GENMASK(23, 8) +#define AXIW1_MINORVER_MASK GENMASK(7, 0) +/* Control flag */ +#define AXIW1_GO BIT(0) +#define AXI_CLEAR 0 +#define AXI_RESET BIT(31) +#define AXIW1_READDATA BIT(0) +/* Interrupt Enable */ +#define AXIW1_READY_IRQ_EN BIT(4) +#define AXIW1_DONE_IRQ_EN BIT(0) + +#define AXIW1_TIMEOUT msecs_to_jiffies(100) + +#define DRIVER_NAME "amd_axi_w1" + +struct amd_axi_w1_local { + struct device *dev; + void __iomem *base_addr; + int irq; + atomic_t flag; /* Set on IRQ, cleared once serviced */ + wait_queue_head_t wait_queue; + struct w1_bus_master bus_host; +}; + +/** + * amd_axi_w1_wait_irq_interruptible_timeout() - Wait for IRQ with timeout. + * + * @amd_axi_w1_local: Pointer to device structure + * @IRQ: IRQ channel to wait on + * + * Return: %0 - OK, %-EINTR - Interrupted, %-EBUSY - Timed out + */ +static int amd_axi_w1_wait_irq_interruptible_timeout(struct amd_axi_w1_local *amd_axi_w1_local, + u32 IRQ) +{ + int ret; + + /* Enable the IRQ requested and wait for flag to indicate it's been triggered */ + iowrite32(IRQ, amd_axi_w1_local->base_addr + AXIW1_IRQE_REG); + ret = wait_event_interruptible_timeout(amd_axi_w1_local->wait_queue, + atomic_read(&amd_axi_w1_local->flag) != 0, + AXIW1_TIMEOUT); + if (ret < 0) { + dev_err(amd_axi_w1_local->dev, "Wait IRQ Interrupted\n"); + return -EINTR; + } + + if (!ret) { + dev_err(amd_axi_w1_local->dev, "Wait IRQ Timeout\n"); + return -EBUSY; + } + + atomic_set(&amd_axi_w1_local->flag, 0); + return 0; +} + +/** + * amd_axi_w1_touch_bit() - Performs the touch-bit function - write a 0 or 1 and reads the level. + * + * @data: Pointer to device structure + * @bit: The level to write + * + * Return: The level read + */ +static u8 amd_axi_w1_touch_bit(void *data, u8 bit) +{ + struct amd_axi_w1_local *amd_axi_w1_local = data; + u8 val = 0; + int rc; + + /* Wait for READY signal to be 1 to ensure 1-wire IP is ready */ + while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_READY) == 0) { + rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local, + AXIW1_READY_IRQ_EN); + if (rc < 0) + return 1; /* Callee doesn't test for error. Return inactive bus state */ + } + + if (bit) + /* Read. Write read Bit command in register 0 */ + iowrite32(AXIW1_READBIT, amd_axi_w1_local->base_addr + AXIW1_INST_REG); + else + /* Write. Write tx Bit command in instruction register with bit to transmit */ + iowrite32(AXIW1_WRITEBIT + (bit & 0x01), + amd_axi_w1_local->base_addr + AXIW1_INST_REG); + + /* Write Go signal and clear control reset signal in control register */ + iowrite32(AXIW1_GO, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG); + + /* Wait for done signal to be 1 */ + while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_DONE) != 1) { + rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local, AXIW1_DONE_IRQ_EN); + if (rc < 0) + return 1; /* Callee doesn't test for error. Return inactive bus state */ + } + + /* If read, Retrieve data from register */ + if (bit) + val = (u8)(ioread32(amd_axi_w1_local->base_addr + AXIW1_DATA_REG) & AXIW1_READDATA); + + /* Clear Go signal in register 1 */ + iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG); + + return val; +} + +/** + * amd_axi_w1_read_byte - Performs the read byte function. + * + * @data: Pointer to device structure + * Return: The value read + */ +static u8 amd_axi_w1_read_byte(void *data) +{ + struct amd_axi_w1_local *amd_axi_w1_local = data; + u8 val = 0; + int rc; + + /* Wait for READY signal to be 1 to ensure 1-wire IP is ready */ + while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_READY) == 0) { + rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local, + AXIW1_READY_IRQ_EN); + if (rc < 0) + return 0xFF; /* Return inactive bus state */ + } + + /* Write read Byte command in instruction register*/ + iowrite32(AXIW1_READBYTE, amd_axi_w1_local->base_addr + AXIW1_INST_REG); + + /* Write Go signal and clear control reset signal in control register */ + iowrite32(AXIW1_GO, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG); + + /* Wait for done signal to be 1 */ + while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_DONE) != 1) { + rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local, AXIW1_DONE_IRQ_EN); + if (rc < 0) + return 0xFF; /* Return inactive bus state */ + } + + /* Retrieve LSB bit in data register to get RX byte */ + val = (u8)(ioread32(amd_axi_w1_local->base_addr + AXIW1_DATA_REG) & 0x000000FF); + + /* Clear Go signal in control register */ + iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG); + + return val; +} + +/** + * amd_axi_w1_write_byte - Performs the write byte function. + * + * @data: The ds2482 channel pointer + * @val: The value to write + */ +static void amd_axi_w1_write_byte(void *data, u8 val) +{ + struct amd_axi_w1_local *amd_axi_w1_local = data; + int rc; + + /* Wait for READY signal to be 1 to ensure 1-wire IP is ready */ + while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_READY) == 0) { + rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local, + AXIW1_READY_IRQ_EN); + if (rc < 0) + return; + } + + /* Write tx Byte command in instruction register with bit to transmit */ + iowrite32(AXIW1_WRITEBYTE + val, amd_axi_w1_local->base_addr + AXIW1_INST_REG); + + /* Write Go signal and clear control reset signal in register 1 */ + iowrite32(AXIW1_GO, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG); + + /* Wait for done signal to be 1 */ + while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_DONE) != 1) { + rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local, + AXIW1_DONE_IRQ_EN); + if (rc < 0) + return; + } + + /* Clear Go signal in control register */ + iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG); +} + +/** + * amd_axi_w1_reset_bus() - Issues a reset bus sequence. + * + * @data: the bus host data struct + * Return: 0=Device present, 1=No device present or error + */ +static u8 amd_axi_w1_reset_bus(void *data) +{ + struct amd_axi_w1_local *amd_axi_w1_local = data; + u8 val = 0; + int rc; + + /* Reset 1-wire Axi IP */ + iowrite32(AXI_RESET, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG); + + /* Wait for READY signal to be 1 to ensure 1-wire IP is ready */ + while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_READY) == 0) { + rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local, + AXIW1_READY_IRQ_EN); + if (rc < 0) + return 1; /* Something went wrong with the hardware */ + } + /* Write Initialization command in instruction register */ + iowrite32(AXIW1_INITPRES, amd_axi_w1_local->base_addr + AXIW1_INST_REG); + + /* Write Go signal and clear control reset signal in register 1 */ + iowrite32(AXIW1_GO, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG); + + /* Wait for done signal to be 1 */ + while ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_DONE) != 1) { + rc = amd_axi_w1_wait_irq_interruptible_timeout(amd_axi_w1_local, AXIW1_DONE_IRQ_EN); + if (rc < 0) + return 1; /* Something went wrong with the hardware */ + } + /* Retrieve MSB bit in status register to get failure bit */ + if ((ioread32(amd_axi_w1_local->base_addr + AXIW1_STAT_REG) & AXIW1_PRESENCE) != 0) + val = 1; + + /* Clear Go signal in control register */ + iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG); + + return val; +} + +/* Reset the 1-wire AXI IP. Put the IP in reset state and clear registers */ +static void amd_axi_w1_reset(struct amd_axi_w1_local *amd_axi_w1_local) +{ + iowrite32(AXI_RESET, amd_axi_w1_local->base_addr + AXIW1_CTRL_REG); + iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_INST_REG); + iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_IRQE_REG); + iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_STAT_REG); + iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_DATA_REG); +} + +static irqreturn_t amd_axi_w1_irq(int irq, void *lp) +{ + struct amd_axi_w1_local *amd_axi_w1_local = lp; + + /* Reset interrupt trigger */ + iowrite32(AXI_CLEAR, amd_axi_w1_local->base_addr + AXIW1_IRQE_REG); + + atomic_set(&amd_axi_w1_local->flag, 1); + wake_up_interruptible(&amd_axi_w1_local->wait_queue); + + return IRQ_HANDLED; +} + +static int amd_axi_w1_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct amd_axi_w1_local *lp; + struct clk *clk; + u32 ver_major, ver_minor; + int val, rc = 0; + + lp = devm_kzalloc(dev, sizeof(*lp), GFP_KERNEL); + if (!lp) + return -ENOMEM; + + lp->dev = dev; + lp->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(lp->base_addr)) + return PTR_ERR(lp->base_addr); + + lp->irq = platform_get_irq(pdev, 0); + if (lp->irq < 0) + return lp->irq; + + rc = devm_request_irq(dev, lp->irq, &amd_axi_w1_irq, IRQF_TRIGGER_HIGH, DRIVER_NAME, lp); + if (rc) + return rc; + + /* Initialize wait queue and flag */ + init_waitqueue_head(&lp->wait_queue); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + /* Verify IP presence in HW */ + if (ioread32(lp->base_addr + AXIW1_IPID_REG) != AXIW1_IPID) { + dev_err(dev, "AMD 1-wire IP not detected in hardware\n"); + return -ENODEV; + } + + /* + * Allow for future driver expansion supporting new hardware features + * This driver currently only supports hardware 1.x, but include logic + * to detect if a potentially incompatible future version is used + * by reading major version ID. It is highly undesirable for new IP versions + * to break the API, but this code will at least allow for graceful failure + * should that happen. Future new features can be enabled by hardware + * incrementing the minor version and augmenting the driver to detect capability + * using the minor version number + */ + val = ioread32(lp->base_addr + AXIW1_IPVER_REG); + ver_major = FIELD_GET(AXIW1_MAJORVER_MASK, val); + ver_minor = FIELD_GET(AXIW1_MINORVER_MASK, val); + + if (ver_major != 1) { + dev_err(dev, "AMD AXI W1 host version %u.%u is not supported by this driver", + ver_major, ver_minor); + return -ENODEV; + } + + lp->bus_host.data = lp; + lp->bus_host.touch_bit = amd_axi_w1_touch_bit; + lp->bus_host.read_byte = amd_axi_w1_read_byte; + lp->bus_host.write_byte = amd_axi_w1_write_byte; + lp->bus_host.reset_bus = amd_axi_w1_reset_bus; + + amd_axi_w1_reset(lp); + + platform_set_drvdata(pdev, lp); + rc = w1_add_master_device(&lp->bus_host); + if (rc) { + dev_err(dev, "Could not add host device\n"); + return rc; + } + + return 0; +} + +static void amd_axi_w1_remove(struct platform_device *pdev) +{ + struct amd_axi_w1_local *lp = platform_get_drvdata(pdev); + + w1_remove_master_device(&lp->bus_host); +} + +static const struct of_device_id amd_axi_w1_of_match[] = { + { .compatible = "amd,axi-1wire-host" }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, amd_axi_w1_of_match); + +static struct platform_driver amd_axi_w1_driver = { + .probe = amd_axi_w1_probe, + .remove_new = amd_axi_w1_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = amd_axi_w1_of_match, + }, +}; +module_platform_driver(amd_axi_w1_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kris Chaplin <kris.chaplin@amd.com>"); +MODULE_DESCRIPTION("Driver for AMD AXI 1 Wire IP core"); diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index 5f5b97e24700..e1cac0730cbb 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -98,6 +98,8 @@ #define ST_EPOF 0x80 /* Status transfer size, 16 bytes status, 16 byte result flags */ #define ST_SIZE 0x20 +/* 1-wire data i/o fifo size, 128 bytes */ +#define FIFO_SIZE 0x80 /* Result Register flags */ #define RR_DETECT 0xA5 /* New device detected */ @@ -614,14 +616,11 @@ static int ds_read_byte(struct ds_device *dev, u8 *byte) return 0; } -static int ds_read_block(struct ds_device *dev, u8 *buf, int len) +static int read_block_chunk(struct ds_device *dev, u8 *buf, int len) { struct ds_status st; int err; - if (len > 64*1024) - return -E2BIG; - memset(buf, 0xFF, len); err = ds_send_data(dev, buf, len); @@ -640,6 +639,24 @@ static int ds_read_block(struct ds_device *dev, u8 *buf, int len) return err; } +static int ds_read_block(struct ds_device *dev, u8 *buf, int len) +{ + int err, to_read, rem = len; + + if (len > 64 * 1024) + return -E2BIG; + + do { + to_read = rem <= FIFO_SIZE ? rem : FIFO_SIZE; + err = read_block_chunk(dev, &buf[len - rem], to_read); + if (err < 0) + return err; + rem -= to_read; + } while (rem); + + return err; +} + static int ds_write_block(struct ds_device *dev, u8 *buf, int len) { int err; diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index e45acb6d916e..05c67038ed20 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -9,7 +9,6 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> -#include <linux/w1-gpio.h> #include <linux/gpio/consumer.h> #include <linux/of_platform.h> #include <linux/err.h> @@ -18,27 +17,33 @@ #include <linux/w1.h> +struct w1_gpio_ddata { + struct gpio_desc *gpiod; + struct gpio_desc *pullup_gpiod; + unsigned int pullup_duration; +}; + static u8 w1_gpio_set_pullup(void *data, int delay) { - struct w1_gpio_platform_data *pdata = data; + struct w1_gpio_ddata *ddata = data; if (delay) { - pdata->pullup_duration = delay; + ddata->pullup_duration = delay; } else { - if (pdata->pullup_duration) { + if (ddata->pullup_duration) { /* * This will OVERRIDE open drain emulation and force-pull * the line high for some time. */ - gpiod_set_raw_value(pdata->gpiod, 1); - msleep(pdata->pullup_duration); + gpiod_set_raw_value(ddata->gpiod, 1); + msleep(ddata->pullup_duration); /* * This will simply set the line as input since we are doing * open drain emulation in the GPIO library. */ - gpiod_set_value(pdata->gpiod, 1); + gpiod_set_value(ddata->gpiod, 1); } - pdata->pullup_duration = 0; + ddata->pullup_duration = 0; } return 0; @@ -46,16 +51,16 @@ static u8 w1_gpio_set_pullup(void *data, int delay) static void w1_gpio_write_bit(void *data, u8 bit) { - struct w1_gpio_platform_data *pdata = data; + struct w1_gpio_ddata *ddata = data; - gpiod_set_value(pdata->gpiod, bit); + gpiod_set_value(ddata->gpiod, bit); } static u8 w1_gpio_read_bit(void *data) { - struct w1_gpio_platform_data *pdata = data; + struct w1_gpio_ddata *ddata = data; - return gpiod_get_value(pdata->gpiod) ? 1 : 0; + return gpiod_get_value(ddata->gpiod) ? 1 : 0; } #if defined(CONFIG_OF) @@ -69,58 +74,48 @@ MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids); static int w1_gpio_probe(struct platform_device *pdev) { struct w1_bus_master *master; - struct w1_gpio_platform_data *pdata; + struct w1_gpio_ddata *ddata; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; /* Enforce open drain mode by default */ enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN; int err; - if (of_have_populated_dt()) { - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - /* - * This parameter means that something else than the gpiolib has - * already set the line into open drain mode, so we should just - * driver it high/low like we are in full control of the line and - * open drain will happen transparently. - */ - if (of_property_present(np, "linux,open-drain")) - gflags = GPIOD_OUT_LOW; - - pdev->dev.platform_data = pdata; - } - pdata = dev_get_platdata(dev); + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; - if (!pdata) { - dev_err(dev, "No configuration data\n"); - return -ENXIO; - } + /* + * This parameter means that something else than the gpiolib has + * already set the line into open drain mode, so we should just + * driver it high/low like we are in full control of the line and + * open drain will happen transparently. + */ + if (of_property_present(np, "linux,open-drain")) + gflags = GPIOD_OUT_LOW; master = devm_kzalloc(dev, sizeof(struct w1_bus_master), GFP_KERNEL); if (!master) return -ENOMEM; - pdata->gpiod = devm_gpiod_get_index(dev, NULL, 0, gflags); - if (IS_ERR(pdata->gpiod)) { + ddata->gpiod = devm_gpiod_get_index(dev, NULL, 0, gflags); + if (IS_ERR(ddata->gpiod)) { dev_err(dev, "gpio_request (pin) failed\n"); - return PTR_ERR(pdata->gpiod); + return PTR_ERR(ddata->gpiod); } - pdata->pullup_gpiod = + ddata->pullup_gpiod = devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_LOW); - if (IS_ERR(pdata->pullup_gpiod)) { + if (IS_ERR(ddata->pullup_gpiod)) { dev_err(dev, "gpio_request_one " "(ext_pullup_enable_pin) failed\n"); - return PTR_ERR(pdata->pullup_gpiod); + return PTR_ERR(ddata->pullup_gpiod); } - master->data = pdata; + master->data = ddata; master->read_bit = w1_gpio_read_bit; - gpiod_direction_output(pdata->gpiod, 1); + gpiod_direction_output(ddata->gpiod, 1); master->write_bit = w1_gpio_write_bit; /* @@ -138,11 +133,8 @@ static int w1_gpio_probe(struct platform_device *pdev) return err; } - if (pdata->enable_external_pullup) - pdata->enable_external_pullup(1); - - if (pdata->pullup_gpiod) - gpiod_set_value(pdata->pullup_gpiod, 1); + if (ddata->pullup_gpiod) + gpiod_set_value(ddata->pullup_gpiod, 1); platform_set_drvdata(pdev, master); @@ -152,45 +144,19 @@ static int w1_gpio_probe(struct platform_device *pdev) static int w1_gpio_remove(struct platform_device *pdev) { struct w1_bus_master *master = platform_get_drvdata(pdev); - struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); - - if (pdata->enable_external_pullup) - pdata->enable_external_pullup(0); + struct w1_gpio_ddata *ddata = master->data; - if (pdata->pullup_gpiod) - gpiod_set_value(pdata->pullup_gpiod, 0); + if (ddata->pullup_gpiod) + gpiod_set_value(ddata->pullup_gpiod, 0); w1_remove_master_device(master); return 0; } -static int __maybe_unused w1_gpio_suspend(struct device *dev) -{ - struct w1_gpio_platform_data *pdata = dev_get_platdata(dev); - - if (pdata->enable_external_pullup) - pdata->enable_external_pullup(0); - - return 0; -} - -static int __maybe_unused w1_gpio_resume(struct device *dev) -{ - struct w1_gpio_platform_data *pdata = dev_get_platdata(dev); - - if (pdata->enable_external_pullup) - pdata->enable_external_pullup(1); - - return 0; -} - -static SIMPLE_DEV_PM_OPS(w1_gpio_pm_ops, w1_gpio_suspend, w1_gpio_resume); - static struct platform_driver w1_gpio_driver = { .driver = { .name = "w1-gpio", - .pm = &w1_gpio_pm_ops, .of_match_table = of_match_ptr(w1_gpio_dt_ids), }, .probe = w1_gpio_probe, diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 9f21fd98f799..250b7f7ec429 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * w1_ds2433.c - w1 family 23 (DS2433) driver + * w1_ds2433.c - w1 family 23 (DS2433) & 43 (DS28EC20) eeprom driver * * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> + * Copyright (c) 2023 Marc Ferland <marc.ferland@sonatest.com> */ #include <linux/kernel.h> @@ -23,23 +24,45 @@ #include <linux/w1.h> #define W1_EEPROM_DS2433 0x23 +#define W1_EEPROM_DS28EC20 0x43 + +#define W1_EEPROM_DS2433_SIZE 512 +#define W1_EEPROM_DS28EC20_SIZE 2560 -#define W1_EEPROM_SIZE 512 -#define W1_PAGE_COUNT 16 #define W1_PAGE_SIZE 32 #define W1_PAGE_BITS 5 #define W1_PAGE_MASK 0x1F - -#define W1_F23_TIME 300 +#define W1_VALIDCRC_MAX 96 #define W1_F23_READ_EEPROM 0xF0 #define W1_F23_WRITE_SCRATCH 0x0F #define W1_F23_READ_SCRATCH 0xAA #define W1_F23_COPY_SCRATCH 0x55 +struct ds2433_config { + size_t eeprom_size; /* eeprom size in bytes */ + unsigned int page_count; /* number of 256 bits pages */ + unsigned int tprog; /* time in ms for page programming */ +}; + +static const struct ds2433_config config_f23 = { + .eeprom_size = W1_EEPROM_DS2433_SIZE, + .page_count = 16, + .tprog = 5, +}; + +static const struct ds2433_config config_f43 = { + .eeprom_size = W1_EEPROM_DS28EC20_SIZE, + .page_count = 80, + .tprog = 10, +}; + struct w1_f23_data { - u8 memory[W1_EEPROM_SIZE]; - u32 validcrc; +#ifdef CONFIG_W1_SLAVE_DS2433_CRC + u8 *memory; + DECLARE_BITMAP(validcrc, W1_VALIDCRC_MAX); +#endif + const struct ds2433_config *cfg; }; /* @@ -64,11 +87,11 @@ static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data, u8 wrbuf[3]; int off = block * W1_PAGE_SIZE; - if (data->validcrc & (1 << block)) + if (test_bit(block, data->validcrc)) return 0; if (w1_reset_select_slave(sl)) { - data->validcrc = 0; + bitmap_zero(data->validcrc, data->cfg->page_count); return -EIO; } @@ -80,7 +103,7 @@ static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data, /* cache the block if the CRC is valid */ if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID) - data->validcrc |= (1 << block); + set_bit(block, data->validcrc); return 0; } @@ -98,7 +121,7 @@ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, u8 wrbuf[3]; #endif - count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE); + count = w1_f23_fix_count(off, count, bin_attr->size); if (!count) return 0; @@ -153,9 +176,7 @@ out_up: */ static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data) { -#ifdef CONFIG_W1_SLAVE_DS2433_CRC struct w1_f23_data *f23 = sl->family_data; -#endif u8 wrbuf[4]; u8 rdbuf[W1_PAGE_SIZE + 3]; u8 es = (addr + len - 1) & 0x1f; @@ -191,13 +212,13 @@ static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data) wrbuf[3] = es; w1_write_block(sl->master, wrbuf, 4); - /* Sleep for 5 ms to wait for the write to complete */ - msleep(5); + /* Sleep for tprog ms to wait for the write to complete */ + msleep(f23->cfg->tprog); /* Reset the bus to wake up the EEPROM (this may not be needed) */ w1_reset_bus(sl->master); #ifdef CONFIG_W1_SLAVE_DS2433_CRC - f23->validcrc &= ~(1 << (addr >> W1_PAGE_BITS)); + clear_bit(addr >> W1_PAGE_BITS, f23->validcrc); #endif return 0; } @@ -209,7 +230,7 @@ static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, struct w1_slave *sl = kobj_to_w1_slave(kobj); int addr, len, idx; - count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE); + count = w1_f23_fix_count(off, count, bin_attr->size); if (!count) return 0; @@ -253,10 +274,22 @@ out_up: return count; } -static BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE); +static struct bin_attribute bin_attr_f23_eeprom = { + .attr = { .name = "eeprom", .mode = 0644 }, + .read = eeprom_read, + .write = eeprom_write, + .size = W1_EEPROM_DS2433_SIZE, +}; + +static struct bin_attribute bin_attr_f43_eeprom = { + .attr = { .name = "eeprom", .mode = 0644 }, + .read = eeprom_read, + .write = eeprom_write, + .size = W1_EEPROM_DS28EC20_SIZE, +}; static struct bin_attribute *w1_f23_bin_attributes[] = { - &bin_attr_eeprom, + &bin_attr_f23_eeprom, NULL, }; @@ -269,26 +302,63 @@ static const struct attribute_group *w1_f23_groups[] = { NULL, }; +static struct bin_attribute *w1_f43_bin_attributes[] = { + &bin_attr_f43_eeprom, + NULL, +}; + +static const struct attribute_group w1_f43_group = { + .bin_attrs = w1_f43_bin_attributes, +}; + +static const struct attribute_group *w1_f43_groups[] = { + &w1_f43_group, + NULL, +}; + static int w1_f23_add_slave(struct w1_slave *sl) { -#ifdef CONFIG_W1_SLAVE_DS2433_CRC struct w1_f23_data *data; data = kzalloc(sizeof(struct w1_f23_data), GFP_KERNEL); if (!data) return -ENOMEM; + + switch (sl->family->fid) { + case W1_EEPROM_DS2433: + data->cfg = &config_f23; + break; + case W1_EEPROM_DS28EC20: + data->cfg = &config_f43; + break; + } + +#ifdef CONFIG_W1_SLAVE_DS2433_CRC + if (data->cfg->page_count > W1_VALIDCRC_MAX) { + dev_err(&sl->dev, "page count too big for crc bitmap\n"); + kfree(data); + return -EINVAL; + } + data->memory = kzalloc(data->cfg->eeprom_size, GFP_KERNEL); + if (!data->memory) { + kfree(data); + return -ENOMEM; + } + bitmap_zero(data->validcrc, data->cfg->page_count); +#endif /* CONFIG_W1_SLAVE_DS2433_CRC */ sl->family_data = data; -#endif /* CONFIG_W1_SLAVE_DS2433_CRC */ return 0; } static void w1_f23_remove_slave(struct w1_slave *sl) { -#ifdef CONFIG_W1_SLAVE_DS2433_CRC - kfree(sl->family_data); + struct w1_f23_data *data = sl->family_data; sl->family_data = NULL; -#endif /* CONFIG_W1_SLAVE_DS2433_CRC */ +#ifdef CONFIG_W1_SLAVE_DS2433_CRC + kfree(data->memory); +#endif /* CONFIG_W1_SLAVE_DS2433_CRC */ + kfree(data); } static const struct w1_family_ops w1_f23_fops = { @@ -297,13 +367,53 @@ static const struct w1_family_ops w1_f23_fops = { .groups = w1_f23_groups, }; +static const struct w1_family_ops w1_f43_fops = { + .add_slave = w1_f23_add_slave, + .remove_slave = w1_f23_remove_slave, + .groups = w1_f43_groups, +}; + static struct w1_family w1_family_23 = { .fid = W1_EEPROM_DS2433, .fops = &w1_f23_fops, }; -module_w1_family(w1_family_23); + +static struct w1_family w1_family_43 = { + .fid = W1_EEPROM_DS28EC20, + .fops = &w1_f43_fops, +}; + +static int __init w1_ds2433_init(void) +{ + int err; + + err = w1_register_family(&w1_family_23); + if (err) + return err; + + err = w1_register_family(&w1_family_43); + if (err) + goto err_43; + + return 0; + +err_43: + w1_unregister_family(&w1_family_23); + return err; +} + +static void __exit w1_ds2433_exit(void) +{ + w1_unregister_family(&w1_family_23); + w1_unregister_family(&w1_family_43); +} + +module_init(w1_ds2433_init); +module_exit(w1_ds2433_exit); MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>"); -MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM"); +MODULE_AUTHOR("Marc Ferland <marc.ferland@sonatest.com>"); +MODULE_DESCRIPTION("w1 family 23/43 driver for DS2433 (4kb) and DS28EC20 (20kb)"); MODULE_LICENSE("GPL"); MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2433)); +MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS28EC20)); diff --git a/drivers/xen/gntdev-dmabuf.c b/drivers/xen/gntdev-dmabuf.c index 4440e626b797..42adc2c1e06b 100644 --- a/drivers/xen/gntdev-dmabuf.c +++ b/drivers/xen/gntdev-dmabuf.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/dma-buf.h> +#include <linux/dma-direct.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/uaccess.h> @@ -50,7 +51,7 @@ struct gntdev_dmabuf { /* Number of pages this buffer has. */ int nr_pages; - /* Pages of this buffer. */ + /* Pages of this buffer (only for dma-buf export). */ struct page **pages; }; @@ -484,7 +485,7 @@ out: /* DMA buffer import support. */ static int -dmabuf_imp_grant_foreign_access(struct page **pages, u32 *refs, +dmabuf_imp_grant_foreign_access(unsigned long *gfns, u32 *refs, int count, int domid) { grant_ref_t priv_gref_head; @@ -507,7 +508,7 @@ dmabuf_imp_grant_foreign_access(struct page **pages, u32 *refs, } gnttab_grant_foreign_access_ref(cur_ref, domid, - xen_page_to_gfn(pages[i]), 0); + gfns[i], 0); refs[i] = cur_ref; } @@ -529,7 +530,6 @@ static void dmabuf_imp_end_foreign_access(u32 *refs, int count) static void dmabuf_imp_free_storage(struct gntdev_dmabuf *gntdev_dmabuf) { - kfree(gntdev_dmabuf->pages); kfree(gntdev_dmabuf->u.imp.refs); kfree(gntdev_dmabuf); } @@ -549,12 +549,6 @@ static struct gntdev_dmabuf *dmabuf_imp_alloc_storage(int count) if (!gntdev_dmabuf->u.imp.refs) goto fail; - gntdev_dmabuf->pages = kcalloc(count, - sizeof(gntdev_dmabuf->pages[0]), - GFP_KERNEL); - if (!gntdev_dmabuf->pages) - goto fail; - gntdev_dmabuf->nr_pages = count; for (i = 0; i < count; i++) @@ -576,7 +570,8 @@ dmabuf_imp_to_refs(struct gntdev_dmabuf_priv *priv, struct device *dev, struct dma_buf *dma_buf; struct dma_buf_attachment *attach; struct sg_table *sgt; - struct sg_page_iter sg_iter; + struct sg_dma_page_iter sg_iter; + unsigned long *gfns; int i; dma_buf = dma_buf_get(fd); @@ -624,26 +619,31 @@ dmabuf_imp_to_refs(struct gntdev_dmabuf_priv *priv, struct device *dev, gntdev_dmabuf->u.imp.sgt = sgt; - /* Now convert sgt to array of pages and check for page validity. */ + gfns = kcalloc(count, sizeof(*gfns), GFP_KERNEL); + if (!gfns) { + ret = ERR_PTR(-ENOMEM); + goto fail_unmap; + } + + /* + * Now convert sgt to array of gfns without accessing underlying pages. + * It is not allowed to access the underlying struct page of an sg table + * exported by DMA-buf, but since we deal with special Xen dma device here + * (not a normal physical one) look at the dma addresses in the sg table + * and then calculate gfns directly from them. + */ i = 0; - for_each_sgtable_page(sgt, &sg_iter, 0) { - struct page *page = sg_page_iter_page(&sg_iter); - /* - * Check if page is valid: this can happen if we are given - * a page from VRAM or other resources which are not backed - * by a struct page. - */ - if (!pfn_valid(page_to_pfn(page))) { - ret = ERR_PTR(-EINVAL); - goto fail_unmap; - } + for_each_sgtable_dma_page(sgt, &sg_iter, 0) { + dma_addr_t addr = sg_page_iter_dma_address(&sg_iter); + unsigned long pfn = bfn_to_pfn(XEN_PFN_DOWN(dma_to_phys(dev, addr))); - gntdev_dmabuf->pages[i++] = page; + gfns[i++] = pfn_to_gfn(pfn); } - ret = ERR_PTR(dmabuf_imp_grant_foreign_access(gntdev_dmabuf->pages, + ret = ERR_PTR(dmabuf_imp_grant_foreign_access(gfns, gntdev_dmabuf->u.imp.refs, count, domid)); + kfree(gfns); if (IS_ERR(ret)) goto fail_end_access; diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c index d4b251925796..32835b4b9bc5 100644 --- a/drivers/xen/xenbus/xenbus_client.c +++ b/drivers/xen/xenbus/xenbus_client.c @@ -119,11 +119,13 @@ EXPORT_SYMBOL_GPL(xenbus_strstate); * @callback: callback to register * * Register a @watch on the given path, using the given xenbus_watch structure - * for storage, and the given @callback function as the callback. Return 0 on - * success, or -errno on error. On success, the given @path will be saved as - * @watch->node, and remains the caller's to free. On error, @watch->node will + * for storage, and the given @callback function as the callback. On success, + * the given @path will be saved as @watch->node, and remains the + * caller's to free. On error, @watch->node will * be NULL, the device will switch to %XenbusStateClosing, and the error will * be saved in the store. + * + * Returns: %0 on success or -errno on error */ int xenbus_watch_path(struct xenbus_device *dev, const char *path, struct xenbus_watch *watch, @@ -160,12 +162,14 @@ EXPORT_SYMBOL_GPL(xenbus_watch_path); * @pathfmt: format of path to watch * * Register a watch on the given @path, using the given xenbus_watch - * structure for storage, and the given @callback function as the callback. - * Return 0 on success, or -errno on error. On success, the watched path - * (@path/@path2) will be saved as @watch->node, and becomes the caller's to - * kfree(). On error, watch->node will be NULL, so the caller has nothing to + * structure for storage, and the given @callback function as the + * callback. On success, the watched path (@path/@path2) will be saved + * as @watch->node, and becomes the caller's to kfree(). + * On error, watch->node will be NULL, so the caller has nothing to * free, the device will switch to %XenbusStateClosing, and the error will be * saved in the store. + * + * Returns: %0 on success or -errno on error */ int xenbus_watch_pathfmt(struct xenbus_device *dev, struct xenbus_watch *watch, @@ -255,13 +259,15 @@ abort: } /** - * xenbus_switch_state + * xenbus_switch_state - save the new state of a driver * @dev: xenbus device * @state: new state * * Advertise in the store a change of the given driver to the given new_state. - * Return 0 on success, or -errno on error. On error, the device will switch - * to XenbusStateClosing, and the error will be saved in the store. + * On error, the device will switch to XenbusStateClosing, and the error + * will be saved in the store. + * + * Returns: %0 on success or -errno on error */ int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state state) { @@ -305,7 +311,7 @@ static void xenbus_va_dev_error(struct xenbus_device *dev, int err, } /** - * xenbus_dev_error + * xenbus_dev_error - place an error message into the store * @dev: xenbus device * @err: error to report * @fmt: error message format @@ -324,7 +330,7 @@ void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, ...) EXPORT_SYMBOL_GPL(xenbus_dev_error); /** - * xenbus_dev_fatal + * xenbus_dev_fatal - put an error messages into the store and then shutdown * @dev: xenbus device * @err: error to report * @fmt: error message format @@ -346,7 +352,7 @@ void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt, ...) } EXPORT_SYMBOL_GPL(xenbus_dev_fatal); -/** +/* * Equivalent to xenbus_dev_fatal(dev, err, fmt, args), but helps * avoiding recursion within xenbus_switch_state. */ @@ -453,7 +459,7 @@ void xenbus_teardown_ring(void **vaddr, unsigned int nr_pages, } EXPORT_SYMBOL_GPL(xenbus_teardown_ring); -/** +/* * Allocate an event channel for the given xenbus_device, assigning the newly * created local port to *port. Return 0 on success, or -errno on error. On * error, the device will switch to XenbusStateClosing, and the error will be @@ -479,7 +485,7 @@ int xenbus_alloc_evtchn(struct xenbus_device *dev, evtchn_port_t *port) EXPORT_SYMBOL_GPL(xenbus_alloc_evtchn); -/** +/* * Free an existing event channel. Returns 0 on success or -errno on error. */ int xenbus_free_evtchn(struct xenbus_device *dev, evtchn_port_t port) @@ -499,7 +505,7 @@ EXPORT_SYMBOL_GPL(xenbus_free_evtchn); /** - * xenbus_map_ring_valloc + * xenbus_map_ring_valloc - allocate & map pages of VA space * @dev: xenbus device * @gnt_refs: grant reference array * @nr_grefs: number of grant references @@ -507,10 +513,11 @@ EXPORT_SYMBOL_GPL(xenbus_free_evtchn); * * Map @nr_grefs pages of memory into this domain from another * domain's grant table. xenbus_map_ring_valloc allocates @nr_grefs - * pages of virtual address space, maps the pages to that address, and - * sets *vaddr to that address. Returns 0 on success, and -errno on - * error. If an error is returned, device will switch to + * pages of virtual address space, maps the pages to that address, and sets + * *vaddr to that address. If an error is returned, device will switch to * XenbusStateClosing and the error message will be saved in XenStore. + * + * Returns: %0 on success or -errno on error */ int xenbus_map_ring_valloc(struct xenbus_device *dev, grant_ref_t *gnt_refs, unsigned int nr_grefs, void **vaddr) @@ -599,14 +606,15 @@ static int __xenbus_map_ring(struct xenbus_device *dev, } /** - * xenbus_unmap_ring + * xenbus_unmap_ring - unmap memory from another domain * @dev: xenbus device * @handles: grant handle array * @nr_handles: number of handles in the array * @vaddrs: addresses to unmap * * Unmap memory in this domain that was imported from another domain. - * Returns 0 on success and returns GNTST_* on error + * + * Returns: %0 on success or GNTST_* on error * (see xen/include/interface/grant_table.h). */ static int xenbus_unmap_ring(struct xenbus_device *dev, grant_handle_t *handles, @@ -712,7 +720,7 @@ static int xenbus_map_ring_hvm(struct xenbus_device *dev, } /** - * xenbus_unmap_ring_vfree + * xenbus_unmap_ring_vfree - unmap a page of memory from another domain * @dev: xenbus device * @vaddr: addr to unmap * @@ -720,7 +728,8 @@ static int xenbus_map_ring_hvm(struct xenbus_device *dev, * Unmap a page of memory in this domain that was imported from another domain. * Use xenbus_unmap_ring_vfree if you mapped in your memory with * xenbus_map_ring_valloc (it will free the virtual address space). - * Returns 0 on success and returns GNTST_* on error + * + * Returns: %0 on success or GNTST_* on error * (see xen/include/interface/grant_table.h). */ int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr) @@ -916,10 +925,10 @@ static int xenbus_unmap_ring_hvm(struct xenbus_device *dev, void *vaddr) } /** - * xenbus_read_driver_state + * xenbus_read_driver_state - read state from a store path * @path: path for driver * - * Return the state of the driver rooted at the given store path, or + * Returns: the state of the driver rooted at the given store path, or * XenbusStateUnknown if no state can be read. */ enum xenbus_state xenbus_read_driver_state(const char *path) |