diff options
Diffstat (limited to 'drivers')
249 files changed, 5547 insertions, 1409 deletions
diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c index f21c99ec46ee..a2dcd62ea32f 100644 --- a/drivers/acpi/acpi_dbg.c +++ b/drivers/acpi/acpi_dbg.c @@ -614,7 +614,7 @@ static ssize_t acpi_aml_read(struct file *file, char __user *buf, if (!count) return 0; - if (!access_ok(VERIFY_WRITE, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; while (count > 0) { @@ -684,7 +684,7 @@ static ssize_t acpi_aml_write(struct file *file, const char __user *buf, if (!count) return 0; - if (!access_ok(VERIFY_READ, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; while (count > 0) { diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index b93fc862d365..0dbc43068eeb 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -25,6 +25,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/kthread.h> +#include <uapi/linux/mount.h> #include "base.h" static struct task_struct *thread; diff --git a/drivers/base/platform.c b/drivers/base/platform.c index be6c1eb3cbe2..1c958eb33ef4 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -26,6 +26,7 @@ #include <linux/clk/clk-conf.h> #include <linux/limits.h> #include <linux/property.h> +#include <linux/kmemleak.h> #include "base.h" #include "power/power.h" @@ -524,6 +525,8 @@ struct platform_device *platform_device_register_full( if (!pdev->dev.dma_mask) goto err; + kmemleak_ignore(pdev->dev.dma_mask); + *pdev->dev.dma_mask = pdevinfo->dma_mask; pdev->dev.coherent_dma_mask = pdevinfo->dma_mask; } diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c index 14e728fbb8a0..ff5394f47587 100644 --- a/drivers/char/generic_nvram.c +++ b/drivers/char/generic_nvram.c @@ -44,7 +44,7 @@ static ssize_t read_nvram(struct file *file, char __user *buf, unsigned int i; char __user *p = buf; - if (!access_ok(VERIFY_WRITE, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; if (*ppos >= nvram_len) return 0; @@ -62,7 +62,7 @@ static ssize_t write_nvram(struct file *file, const char __user *buf, const char __user *p = buf; char c; - if (!access_ok(VERIFY_READ, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; if (*ppos >= nvram_len) return 0; diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 7b4e4de778e4..b08dc50f9f26 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -609,7 +609,7 @@ static ssize_t read_port(struct file *file, char __user *buf, unsigned long i = *ppos; char __user *tmp = buf; - if (!access_ok(VERIFY_WRITE, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; while (count-- > 0 && i < 65536) { if (__put_user(inb(i), tmp) < 0) @@ -627,7 +627,7 @@ static ssize_t write_port(struct file *file, const char __user *buf, unsigned long i = *ppos; const char __user *tmp = buf; - if (!access_ok(VERIFY_READ, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; while (count-- > 0 && i < 65536) { char c; diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c index a284ae25e69a..76fb434068d4 100644 --- a/drivers/char/nwflash.c +++ b/drivers/char/nwflash.c @@ -167,7 +167,7 @@ static ssize_t flash_write(struct file *file, const char __user *buf, if (count > gbFlashSize - p) count = gbFlashSize - p; - if (!access_ok(VERIFY_READ, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; /* diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 809507bf8f1c..7a4eb86aedac 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -1445,11 +1445,11 @@ static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) _IOC_DIR(cmd), _IOC_READ, _IOC_WRITE, size, cmd); if (_IOC_DIR(cmd) & _IOC_READ) { - if (!access_ok(VERIFY_WRITE, argp, size)) + if (!access_ok(argp, size)) goto out; } if (_IOC_DIR(cmd) & _IOC_WRITE) { - if (!access_ok(VERIFY_READ, argp, size)) + if (!access_ok(argp, size)) goto out; } rc = 0; diff --git a/drivers/clocksource/timer-mp-csky.c b/drivers/clocksource/timer-mp-csky.c index a8acc431a774..183a9955160a 100644 --- a/drivers/clocksource/timer-mp-csky.c +++ b/drivers/clocksource/timer-mp-csky.c @@ -79,11 +79,11 @@ static int csky_mptimer_starting_cpu(unsigned int cpu) to->clkevt.cpumask = cpumask_of(cpu); + enable_percpu_irq(csky_mptimer_irq, 0); + clockevents_config_and_register(&to->clkevt, timer_of_rate(to), 2, ULONG_MAX); - enable_percpu_irq(csky_mptimer_irq, 0); - return 0; } @@ -97,7 +97,7 @@ static int csky_mptimer_dying_cpu(unsigned int cpu) /* * clock source */ -static u64 sched_clock_read(void) +static u64 notrace sched_clock_read(void) { return (u64)mfcr(PTIM_CCVR); } diff --git a/drivers/crypto/ccp/psp-dev.c b/drivers/crypto/ccp/psp-dev.c index d64a78ccc03e..b16be8a11d92 100644 --- a/drivers/crypto/ccp/psp-dev.c +++ b/drivers/crypto/ccp/psp-dev.c @@ -364,7 +364,7 @@ static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp) goto cmd; /* allocate a physically contiguous buffer to store the CSR blob */ - if (!access_ok(VERIFY_WRITE, input.address, input.length) || + if (!access_ok(input.address, input.length) || input.length > SEV_FW_BLOB_MAX_SIZE) { ret = -EFAULT; goto e_free; @@ -644,14 +644,14 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp) /* Allocate a physically contiguous buffer to store the PDH blob. */ if ((input.pdh_cert_len > SEV_FW_BLOB_MAX_SIZE) || - !access_ok(VERIFY_WRITE, input.pdh_cert_address, input.pdh_cert_len)) { + !access_ok(input.pdh_cert_address, input.pdh_cert_len)) { ret = -EFAULT; goto e_free; } /* Allocate a physically contiguous buffer to store the cert chain blob. */ if ((input.cert_chain_len > SEV_FW_BLOB_MAX_SIZE) || - !access_ok(VERIFY_WRITE, input.cert_chain_address, input.cert_chain_len)) { + !access_ok(input.cert_chain_address, input.cert_chain_len)) { ret = -EFAULT; goto e_free; } diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index fc359ca4503d..cd57747286f2 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -20,7 +20,7 @@ struct udmabuf { struct page **pages; }; -static int udmabuf_vm_fault(struct vm_fault *vmf) +static vm_fault_t udmabuf_vm_fault(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; struct udmabuf *ubuf = vma->vm_private_data; diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index d8e185582642..16a7045736a9 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1094,7 +1094,7 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) return -EINVAL; p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets); - if (!access_ok(VERIFY_READ, p, a->size)) + if (!access_ok(p, a->size)) return -EFAULT; end = (void __user *)p + a->size; diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index 1ea71640fdc2..c64c7da73829 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -1009,7 +1009,6 @@ static struct platform_driver sdei_driver = { static bool __init sdei_present_dt(void) { - struct platform_device *pdev; struct device_node *np, *fw_np; fw_np = of_find_node_by_name(NULL, "firmware"); @@ -1017,14 +1016,9 @@ static bool __init sdei_present_dt(void) return false; np = of_find_matching_node(fw_np, sdei_of_match); - of_node_put(fw_np); if (!np) return false; - - pdev = of_platform_device_create(np, sdei_driver.driver.name, NULL); of_node_put(np); - if (!pdev) - return false; return true; } diff --git a/drivers/firmware/efi/test/efi_test.c b/drivers/firmware/efi/test/efi_test.c index 769640940c9f..51ecf7d6da48 100644 --- a/drivers/firmware/efi/test/efi_test.c +++ b/drivers/firmware/efi/test/efi_test.c @@ -68,7 +68,7 @@ copy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src, return 0; } - if (!access_ok(VERIFY_READ, src, 1)) + if (!access_ok(src, 1)) return -EFAULT; buf = memdup_user(src, len); @@ -89,7 +89,7 @@ copy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src, static inline int get_ucs2_strsize_from_user(efi_char16_t __user *src, size_t *len) { - if (!access_ok(VERIFY_READ, src, 1)) + if (!access_ok(src, 1)) return -EFAULT; *len = user_ucs2_strsize(src); @@ -116,7 +116,7 @@ copy_ucs2_from_user(efi_char16_t **dst, efi_char16_t __user *src) { size_t len; - if (!access_ok(VERIFY_READ, src, 1)) + if (!access_ok(src, 1)) return -EFAULT; len = user_ucs2_strsize(src); @@ -140,7 +140,7 @@ copy_ucs2_to_user_len(efi_char16_t __user *dst, efi_char16_t *src, size_t len) if (!src) return 0; - if (!access_ok(VERIFY_WRITE, dst, 1)) + if (!access_ok(dst, 1)) return -EFAULT; return copy_to_user(dst, src, len); diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c index d168c87c7d30..ec4fd253a4e9 100644 --- a/drivers/firmware/memmap.c +++ b/drivers/firmware/memmap.c @@ -333,7 +333,7 @@ int __init firmware_map_add_early(u64 start, u64 end, const char *type) { struct firmware_map_entry *entry; - entry = memblock_alloc(sizeof(struct firmware_map_entry), + entry = memblock_alloc_nopanic(sizeof(struct firmware_map_entry), SMP_CACHE_BYTES); if (WARN_ON(!entry)) return -ENOMEM; diff --git a/drivers/fpga/dfl-afu-dma-region.c b/drivers/fpga/dfl-afu-dma-region.c index 025aba3ea76c..e18a786fc943 100644 --- a/drivers/fpga/dfl-afu-dma-region.c +++ b/drivers/fpga/dfl-afu-dma-region.c @@ -369,7 +369,7 @@ int afu_dma_map_region(struct dfl_feature_platform_data *pdata, if (user_addr + length < user_addr) return -EINVAL; - if (!access_ok(VERIFY_WRITE, (void __user *)(unsigned long)user_addr, + if (!access_ok((void __user *)(unsigned long)user_addr, length)) return -EINVAL; diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c index fe5a5578fbf7..d9ca9554844a 100644 --- a/drivers/fpga/dfl-fme-pr.c +++ b/drivers/fpga/dfl-fme-pr.c @@ -99,8 +99,7 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg) return -EINVAL; } - if (!access_ok(VERIFY_READ, - (void __user *)(unsigned long)port_pr.buffer_address, + if (!access_ok((void __user *)(unsigned long)port_pr.buffer_address, port_pr.buffer_size)) return -EFAULT; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index cf4e190c0a72..1c49b8266d69 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1428,6 +1428,9 @@ int amdgpu_cs_fence_to_handle_ioctl(struct drm_device *dev, void *data, if (IS_ERR(fence)) return PTR_ERR(fence); + if (!fence) + fence = dma_fence_get_stub(); + switch (info->in.what) { case AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ: r = drm_syncobj_create(&syncobj, 0, fence); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index b60afeade50a..8a078f4ae73d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -3476,14 +3476,16 @@ static void amdgpu_device_lock_adev(struct amdgpu_device *adev) mutex_lock(&adev->lock_reset); atomic_inc(&adev->gpu_reset_counter); adev->in_gpu_reset = 1; - /* Block kfd */ - amdgpu_amdkfd_pre_reset(adev); + /* Block kfd: SRIOV would do it separately */ + if (!amdgpu_sriov_vf(adev)) + amdgpu_amdkfd_pre_reset(adev); } static void amdgpu_device_unlock_adev(struct amdgpu_device *adev) { - /*unlock kfd */ - amdgpu_amdkfd_post_reset(adev); + /*unlock kfd: SRIOV would do it separately */ + if (!amdgpu_sriov_vf(adev)) + amdgpu_amdkfd_post_reset(adev); amdgpu_vf_error_trans_all(adev); adev->in_gpu_reset = 0; mutex_unlock(&adev->lock_reset); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 9c77eaa45982..c806f984bcc5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -865,6 +865,7 @@ static const struct pci_device_id pciidlist[] = { /* VEGAM */ {0x1002, 0x694C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGAM}, {0x1002, 0x694E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGAM}, + {0x1002, 0x694F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGAM}, /* Vega 10 */ {0x1002, 0x6860, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, {0x1002, 0x6861, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c index e0af44fd6a0c..0a17fb1af204 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c @@ -32,6 +32,9 @@ static void amdgpu_job_timedout(struct drm_sched_job *s_job) { struct amdgpu_ring *ring = to_amdgpu_ring(s_job->sched); struct amdgpu_job *job = to_amdgpu_job(s_job); + struct amdgpu_task_info ti; + + memset(&ti, 0, sizeof(struct amdgpu_task_info)); if (amdgpu_ring_soft_recovery(ring, job->vmid, s_job->s_fence->parent)) { DRM_ERROR("ring %s timeout, but soft recovered\n", @@ -39,9 +42,12 @@ static void amdgpu_job_timedout(struct drm_sched_job *s_job) return; } + amdgpu_vm_get_task_info(ring->adev, job->pasid, &ti); DRM_ERROR("ring %s timeout, signaled seq=%u, emitted seq=%u\n", job->base.sched->name, atomic_read(&ring->fence_drv.last_seq), ring->fence_drv.sync_seq); + DRM_ERROR("Process information: process %s pid %d thread %s pid %d\n", + ti.process_name, ti.tgid, ti.task_name, ti.pid); if (amdgpu_device_should_recover_gpu(ring->adev)) amdgpu_device_gpu_recover(ring->adev, job); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index fd271f9746a2..728e15e5d68a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -912,7 +912,7 @@ int amdgpu_bo_unpin(struct amdgpu_bo *bo) struct ttm_operation_ctx ctx = { false, false }; int r, i; - if (!bo->pin_count) { + if (WARN_ON_ONCE(!bo->pin_count)) { dev_warn(adev->dev, "%p unpin not necessary\n", bo); return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index 6759d898b3ab..8fab0d637ee5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -155,14 +155,6 @@ psp_cmd_submit_buf(struct psp_context *psp, return ret; } -bool psp_support_vmr_ring(struct psp_context *psp) -{ - if (amdgpu_sriov_vf(psp->adev) && psp->sos_fw_version > 0x80045) - return true; - else - return false; -} - static void psp_prep_tmr_cmd_buf(struct psp_context *psp, struct psp_gfx_cmd_resp *cmd, uint64_t tmr_mc, uint32_t size) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h index 10decf70c9aa..3ee573b4016e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h @@ -83,12 +83,13 @@ struct psp_funcs enum AMDGPU_UCODE_ID ucode_type); bool (*smu_reload_quirk)(struct psp_context *psp); int (*mode1_reset)(struct psp_context *psp); - uint64_t (*xgmi_get_node_id)(struct psp_context *psp); - uint64_t (*xgmi_get_hive_id)(struct psp_context *psp); + int (*xgmi_get_node_id)(struct psp_context *psp, uint64_t *node_id); + int (*xgmi_get_hive_id)(struct psp_context *psp, uint64_t *hive_id); int (*xgmi_get_topology_info)(struct psp_context *psp, int number_devices, struct psp_xgmi_topology_info *topology); int (*xgmi_set_topology_info)(struct psp_context *psp, int number_devices, struct psp_xgmi_topology_info *topology); + bool (*support_vmr_ring)(struct psp_context *psp); }; struct psp_xgmi_context { @@ -192,12 +193,14 @@ struct psp_xgmi_topology_info { ((psp)->funcs->bootloader_load_sos ? (psp)->funcs->bootloader_load_sos((psp)) : 0) #define psp_smu_reload_quirk(psp) \ ((psp)->funcs->smu_reload_quirk ? (psp)->funcs->smu_reload_quirk((psp)) : false) +#define psp_support_vmr_ring(psp) \ + ((psp)->funcs->support_vmr_ring ? (psp)->funcs->support_vmr_ring((psp)) : false) #define psp_mode1_reset(psp) \ ((psp)->funcs->mode1_reset ? (psp)->funcs->mode1_reset((psp)) : false) -#define psp_xgmi_get_node_id(psp) \ - ((psp)->funcs->xgmi_get_node_id ? (psp)->funcs->xgmi_get_node_id((psp)) : 0) -#define psp_xgmi_get_hive_id(psp) \ - ((psp)->funcs->xgmi_get_hive_id ? (psp)->funcs->xgmi_get_hive_id((psp)) : 0) +#define psp_xgmi_get_node_id(psp, node_id) \ + ((psp)->funcs->xgmi_get_node_id ? (psp)->funcs->xgmi_get_node_id((psp), (node_id)) : -EINVAL) +#define psp_xgmi_get_hive_id(psp, hive_id) \ + ((psp)->funcs->xgmi_get_hive_id ? (psp)->funcs->xgmi_get_hive_id((psp), (hive_id)) : -EINVAL) #define psp_xgmi_get_topology_info(psp, num_device, topology) \ ((psp)->funcs->xgmi_get_topology_info ? \ (psp)->funcs->xgmi_get_topology_info((psp), (num_device), (topology)) : -EINVAL) @@ -217,8 +220,6 @@ extern const struct amdgpu_ip_block_version psp_v10_0_ip_block; int psp_gpu_reset(struct amdgpu_device *adev); int psp_xgmi_invoke(struct psp_context *psp, uint32_t ta_cmd_id); -bool psp_support_vmr_ring(struct psp_context *psp); - extern const struct amdgpu_ip_block_version psp_v11_0_ip_block; #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h index 0beb01fef83f..d87e828a084b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h @@ -29,7 +29,7 @@ #include <drm/drm_print.h> /* max number of rings */ -#define AMDGPU_MAX_RINGS 21 +#define AMDGPU_MAX_RINGS 23 #define AMDGPU_MAX_GFX_RINGS 1 #define AMDGPU_MAX_COMPUTE_RINGS 8 #define AMDGPU_MAX_VCE_RINGS 3 diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c index e2e42e3fbcf3..ecf6f96df2ad 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c @@ -262,7 +262,7 @@ static int amdgpu_vcn_pause_dpg_mode(struct amdgpu_device *adev, ring = &adev->vcn.ring_dec; WREG32_SOC15(UVD, 0, mmUVD_RBC_RB_WPTR, - RREG32_SOC15(UVD, 0, mmUVD_SCRATCH2)); + RREG32_SOC15(UVD, 0, mmUVD_SCRATCH2) & 0x7FFFFFFF); SOC15_WAIT_ON_RREG(UVD, 0, mmUVD_POWER_STATUS, UVD_PGFSM_CONFIG__UVDM_UVDU_PWR_ON, UVD_POWER_STATUS__UVD_POWER_STATUS_MASK, ret_code); @@ -322,7 +322,7 @@ static int amdgpu_vcn_pause_dpg_mode(struct amdgpu_device *adev, ring = &adev->vcn.ring_dec; WREG32_SOC15(UVD, 0, mmUVD_RBC_RB_WPTR, - RREG32_SOC15(UVD, 0, mmUVD_SCRATCH2)); + RREG32_SOC15(UVD, 0, mmUVD_SCRATCH2) & 0x7FFFFFFF); SOC15_WAIT_ON_RREG(UVD, 0, mmUVD_POWER_STATUS, UVD_PGFSM_CONFIG__UVDM_UVDU_PWR_ON, UVD_POWER_STATUS__UVD_POWER_STATUS_MASK, ret_code); @@ -396,16 +396,26 @@ void amdgpu_vcn_ring_begin_use(struct amdgpu_ring *ring) if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) { struct dpg_pause_state new_state; + unsigned int fences = 0; + unsigned int i; - if (ring->funcs->type == AMDGPU_RING_TYPE_VCN_ENC) + for (i = 0; i < adev->vcn.num_enc_rings; ++i) { + fences += amdgpu_fence_count_emitted(&adev->vcn.ring_enc[i]); + } + if (fences) new_state.fw_based = VCN_DPG_STATE__PAUSE; else - new_state.fw_based = adev->vcn.pause_state.fw_based; + new_state.fw_based = VCN_DPG_STATE__UNPAUSE; - if (ring->funcs->type == AMDGPU_RING_TYPE_VCN_JPEG) + if (amdgpu_fence_count_emitted(&adev->vcn.ring_jpeg)) new_state.jpeg = VCN_DPG_STATE__PAUSE; else - new_state.jpeg = adev->vcn.pause_state.jpeg; + new_state.jpeg = VCN_DPG_STATE__UNPAUSE; + + if (ring->funcs->type == AMDGPU_RING_TYPE_VCN_ENC) + new_state.fw_based = VCN_DPG_STATE__PAUSE; + else if (ring->funcs->type == AMDGPU_RING_TYPE_VCN_JPEG) + new_state.jpeg = VCN_DPG_STATE__PAUSE; amdgpu_vcn_pause_dpg_mode(adev, &new_state); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c index 0b263a9857c6..8a8bc60cb6b4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c @@ -97,8 +97,19 @@ int amdgpu_xgmi_add_device(struct amdgpu_device *adev) if (!adev->gmc.xgmi.supported) return 0; - adev->gmc.xgmi.node_id = psp_xgmi_get_node_id(&adev->psp); - adev->gmc.xgmi.hive_id = psp_xgmi_get_hive_id(&adev->psp); + ret = psp_xgmi_get_node_id(&adev->psp, &adev->gmc.xgmi.node_id); + if (ret) { + dev_err(adev->dev, + "XGMI: Failed to get node id\n"); + return ret; + } + + ret = psp_xgmi_get_hive_id(&adev->psp, &adev->gmc.xgmi.hive_id); + if (ret) { + dev_err(adev->dev, + "XGMI: Failed to get hive id\n"); + return ret; + } mutex_lock(&xgmi_mutex); hive = amdgpu_get_xgmi_hive(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index ce150de723c9..bacdaef77b6c 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -718,37 +718,46 @@ static bool gmc_v9_0_keep_stolen_memory(struct amdgpu_device *adev) } } -static int gmc_v9_0_late_init(void *handle) +static int gmc_v9_0_allocate_vm_inv_eng(struct amdgpu_device *adev) { - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - /* - * The latest engine allocation on gfx9 is: - * Engine 0, 1: idle - * Engine 2, 3: firmware - * Engine 4~13: amdgpu ring, subject to change when ring number changes - * Engine 14~15: idle - * Engine 16: kfd tlb invalidation - * Engine 17: Gart flushes - */ - unsigned vm_inv_eng[AMDGPU_MAX_VMHUBS] = { 4, 4 }; + struct amdgpu_ring *ring; + unsigned vm_inv_engs[AMDGPU_MAX_VMHUBS] = + {GFXHUB_FREE_VM_INV_ENGS_BITMAP, MMHUB_FREE_VM_INV_ENGS_BITMAP}; unsigned i; - int r; + unsigned vmhub, inv_eng; - if (!gmc_v9_0_keep_stolen_memory(adev)) - amdgpu_bo_late_init(adev); + for (i = 0; i < adev->num_rings; ++i) { + ring = adev->rings[i]; + vmhub = ring->funcs->vmhub; + + inv_eng = ffs(vm_inv_engs[vmhub]); + if (!inv_eng) { + dev_err(adev->dev, "no VM inv eng for ring %s\n", + ring->name); + return -EINVAL; + } - for(i = 0; i < adev->num_rings; ++i) { - struct amdgpu_ring *ring = adev->rings[i]; - unsigned vmhub = ring->funcs->vmhub; + ring->vm_inv_eng = inv_eng - 1; + change_bit(inv_eng - 1, (unsigned long *)(&vm_inv_engs[vmhub])); - ring->vm_inv_eng = vm_inv_eng[vmhub]++; dev_info(adev->dev, "ring %s uses VM inv eng %u on hub %u\n", ring->name, ring->vm_inv_eng, ring->funcs->vmhub); } - /* Engine 16 is used for KFD and 17 for GART flushes */ - for(i = 0; i < AMDGPU_MAX_VMHUBS; ++i) - BUG_ON(vm_inv_eng[i] > 16); + return 0; +} + +static int gmc_v9_0_late_init(void *handle) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + int r; + + if (!gmc_v9_0_keep_stolen_memory(adev)) + amdgpu_bo_late_init(adev); + + r = gmc_v9_0_allocate_vm_inv_eng(adev); + if (r) + return r; if (adev->asic_type == CHIP_VEGA10 && !amdgpu_sriov_vf(adev)) { r = gmc_v9_0_ecc_available(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.h b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.h index b030ca5ea107..5c8deac65580 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.h +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.h @@ -24,6 +24,16 @@ #ifndef __GMC_V9_0_H__ #define __GMC_V9_0_H__ + /* + * The latest engine allocation on gfx9 is: + * Engine 2, 3: firmware + * Engine 0, 1, 4~16: amdgpu ring, + * subject to change when ring number changes + * Engine 17: Gart flushes + */ +#define GFXHUB_FREE_VM_INV_ENGS_BITMAP 0x1FFF3 +#define MMHUB_FREE_VM_INV_ENGS_BITMAP 0x1FFF3 + extern const struct amd_ip_funcs gmc_v9_0_ip_funcs; extern const struct amdgpu_ip_block_version gmc_v9_0_ip_block; diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v6_1.c b/drivers/gpu/drm/amd/amdgpu/nbio_v6_1.c index 6f9c54978cc1..accdedd63c98 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v6_1.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v6_1.c @@ -32,6 +32,7 @@ #define smnCPM_CONTROL 0x11180460 #define smnPCIE_CNTL2 0x11180070 #define smnPCIE_CONFIG_CNTL 0x11180044 +#define smnPCIE_CI_CNTL 0x11180080 static u32 nbio_v6_1_get_rev_id(struct amdgpu_device *adev) { @@ -270,6 +271,12 @@ static void nbio_v6_1_init_registers(struct amdgpu_device *adev) if (def != data) WREG32_PCIE(smnPCIE_CONFIG_CNTL, data); + + def = data = RREG32_PCIE(smnPCIE_CI_CNTL); + data = REG_SET_FIELD(data, PCIE_CI_CNTL, CI_SLV_ORDERING_DIS, 1); + + if (def != data) + WREG32_PCIE(smnPCIE_CI_CNTL, data); } const struct amdgpu_nbio_funcs nbio_v6_1_funcs = { diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c b/drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c index f8cee95d61cc..4cd31a276dcd 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c @@ -31,6 +31,7 @@ #define smnCPM_CONTROL 0x11180460 #define smnPCIE_CNTL2 0x11180070 +#define smnPCIE_CI_CNTL 0x11180080 static u32 nbio_v7_4_get_rev_id(struct amdgpu_device *adev) { @@ -222,7 +223,13 @@ static void nbio_v7_4_detect_hw_virt(struct amdgpu_device *adev) static void nbio_v7_4_init_registers(struct amdgpu_device *adev) { + uint32_t def, data; + + def = data = RREG32_PCIE(smnPCIE_CI_CNTL); + data = REG_SET_FIELD(data, PCIE_CI_CNTL, CI_SLV_ORDERING_DIS, 1); + if (def != data) + WREG32_PCIE(smnPCIE_CI_CNTL, data); } const struct amdgpu_nbio_funcs nbio_v7_4_funcs = { diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c index 6c9a1b748ca7..0c6e7f9b143f 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c @@ -34,6 +34,7 @@ #include "nbio/nbio_7_4_offset.h" MODULE_FIRMWARE("amdgpu/vega20_sos.bin"); +MODULE_FIRMWARE("amdgpu/vega20_asd.bin"); MODULE_FIRMWARE("amdgpu/vega20_ta.bin"); /* address block */ @@ -100,6 +101,7 @@ static int psp_v11_0_init_microcode(struct psp_context *psp) char fw_name[30]; int err = 0; const struct psp_firmware_header_v1_0 *sos_hdr; + const struct psp_firmware_header_v1_0 *asd_hdr; const struct ta_firmware_header_v1_0 *ta_hdr; DRM_DEBUG("\n"); @@ -132,14 +134,30 @@ static int psp_v11_0_init_microcode(struct psp_context *psp) adev->psp.sos_start_addr = (uint8_t *)adev->psp.sys_start_addr + le32_to_cpu(sos_hdr->sos_offset_bytes); + snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_asd.bin", chip_name); + err = request_firmware(&adev->psp.asd_fw, fw_name, adev->dev); + if (err) + goto out1; + + err = amdgpu_ucode_validate(adev->psp.asd_fw); + if (err) + goto out1; + + asd_hdr = (const struct psp_firmware_header_v1_0 *)adev->psp.asd_fw->data; + adev->psp.asd_fw_version = le32_to_cpu(asd_hdr->header.ucode_version); + adev->psp.asd_feature_version = le32_to_cpu(asd_hdr->ucode_feature_version); + adev->psp.asd_ucode_size = le32_to_cpu(asd_hdr->header.ucode_size_bytes); + adev->psp.asd_start_addr = (uint8_t *)asd_hdr + + le32_to_cpu(asd_hdr->header.ucode_array_offset_bytes); + snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_ta.bin", chip_name); err = request_firmware(&adev->psp.ta_fw, fw_name, adev->dev); if (err) - goto out; + goto out2; err = amdgpu_ucode_validate(adev->psp.ta_fw); if (err) - goto out; + goto out2; ta_hdr = (const struct ta_firmware_header_v1_0 *)adev->psp.ta_fw->data; adev->psp.ta_xgmi_ucode_version = le32_to_cpu(ta_hdr->ta_xgmi_ucode_version); @@ -148,14 +166,18 @@ static int psp_v11_0_init_microcode(struct psp_context *psp) le32_to_cpu(ta_hdr->header.ucode_array_offset_bytes); return 0; + +out2: + release_firmware(adev->psp.ta_fw); + adev->psp.ta_fw = NULL; +out1: + release_firmware(adev->psp.asd_fw); + adev->psp.asd_fw = NULL; out: - if (err) { - dev_err(adev->dev, - "psp v11.0: Failed to load firmware \"%s\"\n", - fw_name); - release_firmware(adev->psp.sos_fw); - adev->psp.sos_fw = NULL; - } + dev_err(adev->dev, + "psp v11.0: Failed to load firmware \"%s\"\n", fw_name); + release_firmware(adev->psp.sos_fw); + adev->psp.sos_fw = NULL; return err; } @@ -291,6 +313,13 @@ static int psp_v11_0_ring_init(struct psp_context *psp, return 0; } +static bool psp_v11_0_support_vmr_ring(struct psp_context *psp) +{ + if (amdgpu_sriov_vf(psp->adev) && psp->sos_fw_version > 0x80045) + return true; + return false; +} + static int psp_v11_0_ring_create(struct psp_context *psp, enum psp_ring_type ring_type) { @@ -299,7 +328,7 @@ static int psp_v11_0_ring_create(struct psp_context *psp, struct psp_ring *ring = &psp->km_ring; struct amdgpu_device *adev = psp->adev; - if (psp_support_vmr_ring(psp)) { + if (psp_v11_0_support_vmr_ring(psp)) { /* Write low address of the ring to C2PMSG_102 */ psp_ring_reg = lower_32_bits(ring->ring_mem_mc_addr); WREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_102, psp_ring_reg); @@ -351,7 +380,7 @@ static int psp_v11_0_ring_stop(struct psp_context *psp, struct amdgpu_device *adev = psp->adev; /* Write the ring destroy command*/ - if (psp_support_vmr_ring(psp)) + if (psp_v11_0_support_vmr_ring(psp)) WREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_101, GFX_CTRL_CMD_ID_DESTROY_GPCOM_RING); else @@ -362,7 +391,7 @@ static int psp_v11_0_ring_stop(struct psp_context *psp, mdelay(20); /* Wait for response flag (bit 31) */ - if (psp_support_vmr_ring(psp)) + if (psp_v11_0_support_vmr_ring(psp)) ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, mmMP0_SMN_C2PMSG_101), 0x80000000, 0x80000000, false); else @@ -406,7 +435,7 @@ static int psp_v11_0_cmd_submit(struct psp_context *psp, uint32_t rb_frame_size_dw = sizeof(struct psp_gfx_rb_frame) / 4; /* KM (GPCOM) prepare write pointer */ - if (psp_support_vmr_ring(psp)) + if (psp_v11_0_support_vmr_ring(psp)) psp_write_ptr_reg = RREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_102); else psp_write_ptr_reg = RREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_67); @@ -438,7 +467,7 @@ static int psp_v11_0_cmd_submit(struct psp_context *psp, /* Update the write Pointer in DWORDs */ psp_write_ptr_reg = (psp_write_ptr_reg + rb_frame_size_dw) % ring_size_dw; - if (psp_support_vmr_ring(psp)) { + if (psp_v11_0_support_vmr_ring(psp)) { WREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_102, psp_write_ptr_reg); WREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_101, GFX_CTRL_CMD_ID_CONSUME_CMD); } else @@ -680,7 +709,7 @@ static int psp_v11_0_xgmi_set_topology_info(struct psp_context *psp, return psp_xgmi_invoke(psp, TA_COMMAND_XGMI__SET_TOPOLOGY_INFO); } -static u64 psp_v11_0_xgmi_get_hive_id(struct psp_context *psp) +static int psp_v11_0_xgmi_get_hive_id(struct psp_context *psp, uint64_t *hive_id) { struct ta_xgmi_shared_memory *xgmi_cmd; int ret; @@ -693,12 +722,14 @@ static u64 psp_v11_0_xgmi_get_hive_id(struct psp_context *psp) /* Invoke xgmi ta to get hive id */ ret = psp_xgmi_invoke(psp, xgmi_cmd->cmd_id); if (ret) - return 0; - else - return xgmi_cmd->xgmi_out_message.get_hive_id.hive_id; + return ret; + + *hive_id = xgmi_cmd->xgmi_out_message.get_hive_id.hive_id; + + return 0; } -static u64 psp_v11_0_xgmi_get_node_id(struct psp_context *psp) +static int psp_v11_0_xgmi_get_node_id(struct psp_context *psp, uint64_t *node_id) { struct ta_xgmi_shared_memory *xgmi_cmd; int ret; @@ -711,9 +742,11 @@ static u64 psp_v11_0_xgmi_get_node_id(struct psp_context *psp) /* Invoke xgmi ta to get the node id */ ret = psp_xgmi_invoke(psp, xgmi_cmd->cmd_id); if (ret) - return 0; - else - return xgmi_cmd->xgmi_out_message.get_node_id.node_id; + return ret; + + *node_id = xgmi_cmd->xgmi_out_message.get_node_id.node_id; + + return 0; } static const struct psp_funcs psp_v11_0_funcs = { @@ -732,6 +765,7 @@ static const struct psp_funcs psp_v11_0_funcs = { .xgmi_set_topology_info = psp_v11_0_xgmi_set_topology_info, .xgmi_get_hive_id = psp_v11_0_xgmi_get_hive_id, .xgmi_get_node_id = psp_v11_0_xgmi_get_node_id, + .support_vmr_ring = psp_v11_0_support_vmr_ring, }; void psp_v11_0_set_psp_funcs(struct psp_context *psp) diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v3_1.c b/drivers/gpu/drm/amd/amdgpu/psp_v3_1.c index 7357fd56e614..79694ff16969 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v3_1.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v3_1.c @@ -240,8 +240,11 @@ static int psp_v3_1_bootloader_load_sos(struct psp_context *psp) * are already been loaded. */ sol_reg = RREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_81); - if (sol_reg) + if (sol_reg) { + psp->sos_fw_version = RREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_58); + printk("sos fw version = 0x%x.\n", psp->sos_fw_version); return 0; + } /* Wait for bootloader to signify that is ready having bit 31 of C2PMSG_35 set to 1 */ ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, mmMP0_SMN_C2PMSG_35), diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c index 4b6d3e5c821f..fd0bfe140ee0 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c @@ -1458,8 +1458,7 @@ static bool sdma_v4_0_fw_support_paging_queue(struct amdgpu_device *adev) /*return fw_version >= 31;*/ return false; case CHIP_VEGA20: - /*return fw_version >= 115;*/ - return false; + return fw_version >= 123; default: return false; } @@ -1706,13 +1705,15 @@ static int sdma_v4_0_process_trap_irq(struct amdgpu_device *adev, amdgpu_fence_process(&adev->sdma.instance[instance].ring); break; case 1: - /* XXX compute */ + if (adev->asic_type == CHIP_VEGA20) + amdgpu_fence_process(&adev->sdma.instance[instance].page); break; case 2: /* XXX compute */ break; case 3: - amdgpu_fence_process(&adev->sdma.instance[instance].page); + if (adev->asic_type != CHIP_VEGA20) + amdgpu_fence_process(&adev->sdma.instance[instance].page); break; } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/soc15_common.h b/drivers/gpu/drm/amd/amdgpu/soc15_common.h index 958b10a57073..49c262540940 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15_common.h +++ b/drivers/gpu/drm/amd/amdgpu/soc15_common.h @@ -49,14 +49,19 @@ #define SOC15_WAIT_ON_RREG(ip, inst, reg, expected_value, mask, ret) \ do { \ + uint32_t old_ = 0; \ uint32_t tmp_ = RREG32(adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg); \ uint32_t loop = adev->usec_timeout; \ while ((tmp_ & (mask)) != (expected_value)) { \ - udelay(2); \ + if (old_ != tmp_) { \ + loop = adev->usec_timeout; \ + old_ = tmp_; \ + } else \ + udelay(1); \ tmp_ = RREG32(adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg); \ loop--; \ if (!loop) { \ - DRM_ERROR("Register(%d) [%s] failed to reach value 0x%08x != 0x%08x\n", \ + DRM_WARN("Register(%d) [%s] failed to reach value 0x%08x != 0x%08x\n", \ inst, #reg, (unsigned)expected_value, (unsigned)(tmp_ & (mask))); \ ret = -ETIMEDOUT; \ break; \ diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c index 089645e78f98..aef924026a28 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c @@ -435,7 +435,7 @@ static int uvd_v7_0_sw_init(void *handle) continue; if (!amdgpu_sriov_vf(adev)) { ring = &adev->uvd.inst[j].ring; - sprintf(ring->name, "uvd<%d>", j); + sprintf(ring->name, "uvd_%d", ring->me); r = amdgpu_ring_init(adev, ring, 512, &adev->uvd.inst[j].irq, 0); if (r) return r; @@ -443,7 +443,7 @@ static int uvd_v7_0_sw_init(void *handle) for (i = 0; i < adev->uvd.num_enc_rings; ++i) { ring = &adev->uvd.inst[j].ring_enc[i]; - sprintf(ring->name, "uvd_enc%d<%d>", i, j); + sprintf(ring->name, "uvd_enc_%d.%d", ring->me, i); if (amdgpu_sriov_vf(adev)) { ring->use_doorbell = true; diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c index 4f8352044563..89bb2fef90eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v1_0.c @@ -214,7 +214,8 @@ static int vcn_v1_0_hw_fini(void *handle) struct amdgpu_device *adev = (struct amdgpu_device *)handle; struct amdgpu_ring *ring = &adev->vcn.ring_dec; - if (RREG32_SOC15(VCN, 0, mmUVD_STATUS)) + if ((adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) || + RREG32_SOC15(VCN, 0, mmUVD_STATUS)) vcn_v1_0_set_powergating_state(adev, AMD_PG_STATE_GATE); ring->sched.ready = false; @@ -1087,7 +1088,8 @@ static int vcn_v1_0_start_dpg_mode(struct amdgpu_device *adev) WREG32_P(SOC15_REG_OFFSET(UVD, 0, mmUVD_RBC_RB_CNTL), 0, ~UVD_RBC_RB_CNTL__RB_NO_FETCH_MASK); - /* initialize wptr */ + /* initialize JPEG wptr */ + ring = &adev->vcn.ring_jpeg; ring->wptr = RREG32_SOC15(UVD, 0, mmUVD_JRBC_RB_WPTR); /* copy patch commands to the jpeg ring */ @@ -1159,21 +1161,29 @@ static int vcn_v1_0_stop_spg_mode(struct amdgpu_device *adev) static int vcn_v1_0_stop_dpg_mode(struct amdgpu_device *adev) { int ret_code = 0; + uint32_t tmp; /* Wait for power status to be UVD_POWER_STATUS__UVD_POWER_STATUS_TILES_OFF */ SOC15_WAIT_ON_RREG(UVD, 0, mmUVD_POWER_STATUS, UVD_POWER_STATUS__UVD_POWER_STATUS_TILES_OFF, UVD_POWER_STATUS__UVD_POWER_STATUS_MASK, ret_code); - if (!ret_code) { - int tmp = RREG32_SOC15(UVD, 0, mmUVD_RBC_RB_WPTR) & 0x7FFFFFFF; - /* wait for read ptr to be equal to write ptr */ - SOC15_WAIT_ON_RREG(UVD, 0, mmUVD_RBC_RB_RPTR, tmp, 0xFFFFFFFF, ret_code); + /* wait for read ptr to be equal to write ptr */ + tmp = RREG32_SOC15(UVD, 0, mmUVD_RB_WPTR); + SOC15_WAIT_ON_RREG(UVD, 0, mmUVD_RB_RPTR, tmp, 0xFFFFFFFF, ret_code); - SOC15_WAIT_ON_RREG(UVD, 0, mmUVD_POWER_STATUS, - UVD_POWER_STATUS__UVD_POWER_STATUS_TILES_OFF, - UVD_POWER_STATUS__UVD_POWER_STATUS_MASK, ret_code); - } + tmp = RREG32_SOC15(UVD, 0, mmUVD_RB_WPTR2); + SOC15_WAIT_ON_RREG(UVD, 0, mmUVD_RB_RPTR2, tmp, 0xFFFFFFFF, ret_code); + + tmp = RREG32_SOC15(UVD, 0, mmUVD_JRBC_RB_WPTR); + SOC15_WAIT_ON_RREG(UVD, 0, mmUVD_JRBC_RB_RPTR, tmp, 0xFFFFFFFF, ret_code); + + tmp = RREG32_SOC15(UVD, 0, mmUVD_RBC_RB_WPTR) & 0x7FFFFFFF; + SOC15_WAIT_ON_RREG(UVD, 0, mmUVD_RBC_RB_RPTR, tmp, 0xFFFFFFFF, ret_code); + + SOC15_WAIT_ON_RREG(UVD, 0, mmUVD_POWER_STATUS, + UVD_POWER_STATUS__UVD_POWER_STATUS_TILES_OFF, + UVD_POWER_STATUS__UVD_POWER_STATUS_MASK, ret_code); /* disable dynamic power gating mode */ WREG32_P(SOC15_REG_OFFSET(UVD, 0, mmUVD_POWER_STATUS), 0, diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c index ff2906c215fa..77e367459101 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi.c +++ b/drivers/gpu/drm/amd/amdgpu/vi.c @@ -87,9 +87,9 @@ static u32 vi_pcie_rreg(struct amdgpu_device *adev, u32 reg) u32 r; spin_lock_irqsave(&adev->pcie_idx_lock, flags); - WREG32(mmPCIE_INDEX, reg); - (void)RREG32(mmPCIE_INDEX); - r = RREG32(mmPCIE_DATA); + WREG32_NO_KIQ(mmPCIE_INDEX, reg); + (void)RREG32_NO_KIQ(mmPCIE_INDEX); + r = RREG32_NO_KIQ(mmPCIE_DATA); spin_unlock_irqrestore(&adev->pcie_idx_lock, flags); return r; } @@ -99,10 +99,10 @@ static void vi_pcie_wreg(struct amdgpu_device *adev, u32 reg, u32 v) unsigned long flags; spin_lock_irqsave(&adev->pcie_idx_lock, flags); - WREG32(mmPCIE_INDEX, reg); - (void)RREG32(mmPCIE_INDEX); - WREG32(mmPCIE_DATA, v); - (void)RREG32(mmPCIE_DATA); + WREG32_NO_KIQ(mmPCIE_INDEX, reg); + (void)RREG32_NO_KIQ(mmPCIE_INDEX); + WREG32_NO_KIQ(mmPCIE_DATA, v); + (void)RREG32_NO_KIQ(mmPCIE_DATA); spin_unlock_irqrestore(&adev->pcie_idx_lock, flags); } @@ -123,8 +123,8 @@ static void vi_smc_wreg(struct amdgpu_device *adev, u32 reg, u32 v) unsigned long flags; spin_lock_irqsave(&adev->smc_idx_lock, flags); - WREG32(mmSMC_IND_INDEX_11, (reg)); - WREG32(mmSMC_IND_DATA_11, (v)); + WREG32_NO_KIQ(mmSMC_IND_INDEX_11, (reg)); + WREG32_NO_KIQ(mmSMC_IND_DATA_11, (v)); spin_unlock_irqrestore(&adev->smc_idx_lock, flags); } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 3623538baf6f..083bd8114db1 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -158,8 +158,7 @@ static int set_queue_properties_from_user(struct queue_properties *q_properties, } if ((args->ring_base_address) && - (!access_ok(VERIFY_WRITE, - (const void __user *) args->ring_base_address, + (!access_ok((const void __user *) args->ring_base_address, sizeof(uint64_t)))) { pr_err("Can't access ring base address\n"); return -EFAULT; @@ -170,31 +169,27 @@ static int set_queue_properties_from_user(struct queue_properties *q_properties, return -EINVAL; } - if (!access_ok(VERIFY_WRITE, - (const void __user *) args->read_pointer_address, + if (!access_ok((const void __user *) args->read_pointer_address, sizeof(uint32_t))) { pr_err("Can't access read pointer\n"); return -EFAULT; } - if (!access_ok(VERIFY_WRITE, - (const void __user *) args->write_pointer_address, + if (!access_ok((const void __user *) args->write_pointer_address, sizeof(uint32_t))) { pr_err("Can't access write pointer\n"); return -EFAULT; } if (args->eop_buffer_address && - !access_ok(VERIFY_WRITE, - (const void __user *) args->eop_buffer_address, + !access_ok((const void __user *) args->eop_buffer_address, sizeof(uint32_t))) { pr_debug("Can't access eop buffer"); return -EFAULT; } if (args->ctx_save_restore_address && - !access_ok(VERIFY_WRITE, - (const void __user *) args->ctx_save_restore_address, + !access_ok((const void __user *) args->ctx_save_restore_address, sizeof(uint32_t))) { pr_debug("Can't access ctx save restore buffer"); return -EFAULT; @@ -365,8 +360,7 @@ static int kfd_ioctl_update_queue(struct file *filp, struct kfd_process *p, } if ((args->ring_base_address) && - (!access_ok(VERIFY_WRITE, - (const void __user *) args->ring_base_address, + (!access_ok((const void __user *) args->ring_base_address, sizeof(uint64_t)))) { pr_err("Can't access ring base address\n"); return -EFAULT; @@ -1629,8 +1623,8 @@ static int kfd_ioctl_import_dmabuf(struct file *filep, return -EINVAL; dmabuf = dma_buf_get(args->dmabuf_fd); - if (!dmabuf) - return -EINVAL; + if (IS_ERR(dmabuf)) + return PTR_ERR(dmabuf); mutex_lock(&p->mutex); 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 d01315965af0..a9a28dbc3e24 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -331,12 +331,29 @@ static void dm_crtc_high_irq(void *interrupt_params) struct common_irq_params *irq_params = interrupt_params; struct amdgpu_device *adev = irq_params->adev; struct amdgpu_crtc *acrtc; + struct dm_crtc_state *acrtc_state; acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK); if (acrtc) { drm_crtc_handle_vblank(&acrtc->base); amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); + + acrtc_state = to_dm_crtc_state(acrtc->base.state); + + if (acrtc_state->stream && + acrtc_state->vrr_params.supported && + acrtc_state->freesync_config.state == VRR_STATE_ACTIVE_VARIABLE) { + mod_freesync_handle_v_update( + adev->dm.freesync_module, + acrtc_state->stream, + &acrtc_state->vrr_params); + + dc_stream_adjust_vmin_vmax( + adev->dm.dc, + acrtc_state->stream, + &acrtc_state->vrr_params.adjust); + } } } @@ -3009,7 +3026,7 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc) dc_stream_retain(state->stream); } - state->adjust = cur->adjust; + state->vrr_params = cur->vrr_params; state->vrr_infopacket = cur->vrr_infopacket; state->abm_level = cur->abm_level; state->vrr_supported = cur->vrr_supported; @@ -3628,10 +3645,20 @@ static int dm_plane_atomic_check(struct drm_plane *plane, static int dm_plane_atomic_async_check(struct drm_plane *plane, struct drm_plane_state *new_plane_state) { + struct drm_plane_state *old_plane_state = + drm_atomic_get_old_plane_state(new_plane_state->state, plane); + /* Only support async updates on cursor planes. */ if (plane->type != DRM_PLANE_TYPE_CURSOR) return -EINVAL; + /* + * DRM calls prepare_fb and cleanup_fb on new_plane_state for + * async commits so don't allow fb changes. + */ + if (old_plane_state->fb != new_plane_state->fb) + return -EINVAL; + return 0; } @@ -4445,9 +4472,11 @@ struct dc_stream_status *dc_state_get_stream_status( static void update_freesync_state_on_stream( struct amdgpu_display_manager *dm, struct dm_crtc_state *new_crtc_state, - struct dc_stream_state *new_stream) + struct dc_stream_state *new_stream, + struct dc_plane_state *surface, + u32 flip_timestamp_in_us) { - struct mod_vrr_params vrr = {0}; + struct mod_vrr_params vrr_params = new_crtc_state->vrr_params; struct dc_info_packet vrr_infopacket = {0}; struct mod_freesync_config config = new_crtc_state->freesync_config; @@ -4474,43 +4503,52 @@ static void update_freesync_state_on_stream( mod_freesync_build_vrr_params(dm->freesync_module, new_stream, - &config, &vrr); + &config, &vrr_params); + + if (surface) { + mod_freesync_handle_preflip( + dm->freesync_module, + surface, + new_stream, + flip_timestamp_in_us, + &vrr_params); + } mod_freesync_build_vrr_infopacket( dm->freesync_module, new_stream, - &vrr, + &vrr_params, PACKET_TYPE_VRR, TRANSFER_FUNC_UNKNOWN, &vrr_infopacket); new_crtc_state->freesync_timing_changed = - (memcmp(&new_crtc_state->adjust, - &vrr.adjust, - sizeof(vrr.adjust)) != 0); + (memcmp(&new_crtc_state->vrr_params.adjust, + &vrr_params.adjust, + sizeof(vrr_params.adjust)) != 0); new_crtc_state->freesync_vrr_info_changed = (memcmp(&new_crtc_state->vrr_infopacket, &vrr_infopacket, sizeof(vrr_infopacket)) != 0); - new_crtc_state->adjust = vrr.adjust; + new_crtc_state->vrr_params = vrr_params; new_crtc_state->vrr_infopacket = vrr_infopacket; - new_stream->adjust = new_crtc_state->adjust; + new_stream->adjust = new_crtc_state->vrr_params.adjust; new_stream->vrr_infopacket = vrr_infopacket; if (new_crtc_state->freesync_vrr_info_changed) DRM_DEBUG_KMS("VRR packet update: crtc=%u enabled=%d state=%d", new_crtc_state->base.crtc->base.id, (int)new_crtc_state->base.vrr_enabled, - (int)vrr.state); + (int)vrr_params.state); if (new_crtc_state->freesync_timing_changed) DRM_DEBUG_KMS("VRR timing update: crtc=%u min=%u max=%u\n", new_crtc_state->base.crtc->base.id, - vrr.adjust.v_total_min, - vrr.adjust.v_total_max); + vrr_params.adjust.v_total_min, + vrr_params.adjust.v_total_max); } /* @@ -4524,6 +4562,7 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc, struct dc_state *state) { unsigned long flags; + uint64_t timestamp_ns; uint32_t target_vblank; int r, vpos, hpos; struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); @@ -4537,6 +4576,7 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc, struct dc_stream_update stream_update = {0}; struct dm_crtc_state *acrtc_state = to_dm_crtc_state(crtc->state); struct dc_stream_status *stream_status; + struct dc_plane_state *surface; /* Prepare wait for target vblank early - before the fence-waits */ @@ -4586,6 +4626,9 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc, addr.address.grph.addr.high_part = upper_32_bits(afb->address); addr.flip_immediate = async_flip; + timestamp_ns = ktime_get_ns(); + addr.flip_timestamp_in_us = div_u64(timestamp_ns, 1000); + if (acrtc->base.state->event) prepare_flip_isr(acrtc); @@ -4599,8 +4642,10 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc, return; } - surface_updates->surface = stream_status->plane_states[0]; - if (!surface_updates->surface) { + surface = stream_status->plane_states[0]; + surface_updates->surface = surface; + + if (!surface) { DRM_ERROR("No surface for CRTC: id=%d\n", acrtc->crtc_id); return; @@ -4611,7 +4656,9 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc, update_freesync_state_on_stream( &adev->dm, acrtc_state, - acrtc_state->stream); + acrtc_state->stream, + surface, + addr.flip_timestamp_in_us); if (acrtc_state->freesync_timing_changed) stream_update.adjust = @@ -4622,7 +4669,16 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc, &acrtc_state->stream->vrr_infopacket; } + /* Update surface timing information. */ + surface->time.time_elapsed_in_us[surface->time.index] = + addr.flip_timestamp_in_us - surface->time.prev_update_time_in_us; + surface->time.prev_update_time_in_us = addr.flip_timestamp_in_us; + surface->time.index++; + if (surface->time.index >= DC_PLANE_UPDATE_TIMES_MAX) + surface->time.index = 0; + mutex_lock(&adev->dm.dc_lock); + dc_commit_updates_for_stream(adev->dm.dc, surface_updates, 1, @@ -5314,6 +5370,7 @@ static void get_freesync_config_for_crtc( config.max_refresh_in_uhz = aconnector->max_vfreq * 1000000; config.vsif_supported = true; + config.btr = true; } new_crtc_state->freesync_config = config; @@ -5324,8 +5381,8 @@ static void reset_freesync_config_for_crtc( { new_crtc_state->vrr_supported = false; - memset(&new_crtc_state->adjust, 0, - sizeof(new_crtc_state->adjust)); + memset(&new_crtc_state->vrr_params, 0, + sizeof(new_crtc_state->vrr_params)); memset(&new_crtc_state->vrr_infopacket, 0, sizeof(new_crtc_state->vrr_infopacket)); } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index 25bb91ee80ba..fbd161ddc3f4 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -268,7 +268,7 @@ struct dm_crtc_state { bool vrr_supported; struct mod_freesync_config freesync_config; - struct dc_crtc_timing_adjust adjust; + struct mod_vrr_params vrr_params; struct dc_info_packet vrr_infopacket; int abm_level; diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index 751bb614fc0e..c513ab6f3843 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -638,6 +638,7 @@ static enum bp_result get_ss_info_v4_1( { enum bp_result result = BP_RESULT_OK; struct atom_display_controller_info_v4_1 *disp_cntl_tbl = NULL; + struct atom_smu_info_v3_3 *smu_info = NULL; if (!ss_info) return BP_RESULT_BADINPUT; @@ -650,6 +651,7 @@ static enum bp_result get_ss_info_v4_1( if (!disp_cntl_tbl) return BP_RESULT_BADBIOSTABLE; + ss_info->type.STEP_AND_DELAY_INFO = false; ss_info->spread_percentage_divider = 1000; /* BIOS no longer uses target clock. Always enable for now */ @@ -688,6 +690,19 @@ static enum bp_result get_ss_info_v4_1( */ result = BP_RESULT_UNSUPPORTED; break; + case AS_SIGNAL_TYPE_XGMI: + smu_info = GET_IMAGE(struct atom_smu_info_v3_3, + DATA_TABLES(smu_info)); + if (!smu_info) + return BP_RESULT_BADBIOSTABLE; + + ss_info->spread_spectrum_percentage = + smu_info->waflclk_ss_percentage; + ss_info->spread_spectrum_range = + smu_info->gpuclk_ss_rate_10hz * 10; + if (smu_info->waflclk_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + break; default: result = BP_RESULT_UNSUPPORTED; } diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c index 65b006ad372e..8196f3bb10c7 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c @@ -67,6 +67,7 @@ bool dal_bios_parser_init_cmd_tbl_helper2( return true; #endif case DCE_VERSION_12_0: + case DCE_VERSION_12_1: *h = dal_cmd_tbl_helper_dce112_get_table2(); return true; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index d9c57984394b..5fd52094d459 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -151,10 +151,6 @@ static bool create_links( return false; } - if (connectors_num == 0 && num_virtual_links == 0) { - dm_error("DC: Number of connectors is zero!\n"); - } - dm_output_to_console( "DC: %s: connectors_num: physical:%d, virtual:%d\n", __func__, @@ -1471,7 +1467,8 @@ static void commit_planes_do_stream_update(struct dc *dc, if ((stream_update->hdr_static_metadata && !stream->use_dynamic_meta) || stream_update->vrr_infopacket || - stream_update->vsc_infopacket) { + stream_update->vsc_infopacket || + stream_update->vsp_infopacket) { resource_build_info_frame(pipe_ctx); dc->hwss.update_info_frame(pipe_ctx); } @@ -1573,9 +1570,6 @@ static void commit_planes_for_stream(struct dc *dc, } } - if (update_type == UPDATE_TYPE_FULL) - context_timing_trace(dc, &context->res_ctx); - // Update Type FAST, Surface updates if (update_type == UPDATE_TYPE_FAST) { /* Lock the top pipe while updating plane addrs, since freesync requires diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c index 4dc5846de5c4..52deacf39841 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c @@ -215,6 +215,9 @@ bool dc_link_detect_sink(struct dc_link *link, enum dc_connection_type *type) return true; } + if (link->connector_signal == SIGNAL_TYPE_EDP) + link->dc->hwss.edp_wait_for_hpd_ready(link, true); + /* todo: may need to lock gpio access */ hpd_pin = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); if (hpd_pin == NULL) @@ -339,7 +342,7 @@ bool dc_link_is_dp_sink_present(struct dc_link *link) { enum gpio_result gpio_result; uint32_t clock_pin = 0; - + uint8_t retry = 0; struct ddc *ddc; enum connector_id connector_id = @@ -368,11 +371,22 @@ bool dc_link_is_dp_sink_present(struct dc_link *link) return present; } - /* Read GPIO: DP sink is present if both clock and data pins are zero */ - /* [anaumov] in DAL2, there was no check for GPIO failure */ - - gpio_result = dal_gpio_get_value(ddc->pin_clock, &clock_pin); - ASSERT(gpio_result == GPIO_RESULT_OK); + /* + * Read GPIO: DP sink is present if both clock and data pins are zero + * + * [W/A] plug-unplug DP cable, sometimes customer board has + * one short pulse on clk_pin(1V, < 1ms). DP will be config to HDMI/DVI + * then monitor can't br light up. Add retry 3 times + * But in real passive dongle, it need additional 3ms to detect + */ + do { + gpio_result = dal_gpio_get_value(ddc->pin_clock, &clock_pin); + ASSERT(gpio_result == GPIO_RESULT_OK); + if (clock_pin) + udelay(1000); + else + break; + } while (retry++ < 3); present = (gpio_result == GPIO_RESULT_OK) && !clock_pin; @@ -703,12 +717,26 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) if (memcmp(&link->dpcd_caps, &prev_dpcd_caps, sizeof(struct dpcd_caps))) same_dpcd = false; } - /* Active dongle downstream unplug */ + /* Active dongle plug in without display or downstream unplug*/ if (link->type == dc_connection_active_dongle && link->dpcd_caps.sink_count. bits.SINK_COUNT == 0) { - if (prev_sink != NULL) + if (prev_sink != NULL) { + /* Downstream unplug */ dc_sink_release(prev_sink); + } else { + /* Empty dongle plug in */ + for (i = 0; i < LINK_TRAINING_MAX_VERIFY_RETRY; i++) { + int fail_count = 0; + + dp_verify_link_cap(link, + &link->reported_link_cap, + &fail_count); + + if (fail_count == 0) + break; + } + } return true; } @@ -2622,11 +2650,11 @@ void core_link_disable_stream(struct pipe_ctx *pipe_ctx, int option) { struct dc *core_dc = pipe_ctx->stream->ctx->dc; + core_dc->hwss.blank_stream(pipe_ctx); + if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) deallocate_mst_payload(pipe_ctx); - core_dc->hwss.blank_stream(pipe_ctx); - core_dc->hwss.disable_stream(pipe_ctx, option); disable_link(pipe_ctx->stream->sink->link, pipe_ctx->stream->signal); diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c index 849a3a3032f7..0caacb60b02f 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c @@ -1089,6 +1089,121 @@ static struct dc_link_settings get_max_link_cap(struct dc_link *link) return max_link_cap; } +static enum dc_status read_hpd_rx_irq_data( + struct dc_link *link, + union hpd_irq_data *irq_data) +{ + static enum dc_status retval; + + /* The HW reads 16 bytes from 200h on HPD, + * but if we get an AUX_DEFER, the HW cannot retry + * and this causes the CTS tests 4.3.2.1 - 3.2.4 to + * fail, so we now explicitly read 6 bytes which is + * the req from the above mentioned test cases. + * + * For DP 1.4 we need to read those from 2002h range. + */ + if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14) + retval = core_link_read_dpcd( + link, + DP_SINK_COUNT, + irq_data->raw, + sizeof(union hpd_irq_data)); + else { + /* Read 14 bytes in a single read and then copy only the required fields. + * This is more efficient than doing it in two separate AUX reads. */ + + uint8_t tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI + 1]; + + retval = core_link_read_dpcd( + link, + DP_SINK_COUNT_ESI, + tmp, + sizeof(tmp)); + + if (retval != DC_OK) + return retval; + + irq_data->bytes.sink_cnt.raw = tmp[DP_SINK_COUNT_ESI - DP_SINK_COUNT_ESI]; + irq_data->bytes.device_service_irq.raw = tmp[DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI]; + irq_data->bytes.lane01_status.raw = tmp[DP_LANE0_1_STATUS_ESI - DP_SINK_COUNT_ESI]; + irq_data->bytes.lane23_status.raw = tmp[DP_LANE2_3_STATUS_ESI - DP_SINK_COUNT_ESI]; + irq_data->bytes.lane_status_updated.raw = tmp[DP_LANE_ALIGN_STATUS_UPDATED_ESI - DP_SINK_COUNT_ESI]; + irq_data->bytes.sink_status.raw = tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI]; + } + + return retval; +} + +static bool hpd_rx_irq_check_link_loss_status( + struct dc_link *link, + union hpd_irq_data *hpd_irq_dpcd_data) +{ + uint8_t irq_reg_rx_power_state = 0; + enum dc_status dpcd_result = DC_ERROR_UNEXPECTED; + union lane_status lane_status; + uint32_t lane; + bool sink_status_changed; + bool return_code; + + sink_status_changed = false; + return_code = false; + + if (link->cur_link_settings.lane_count == 0) + return return_code; + + /*1. Check that Link Status changed, before re-training.*/ + + /*parse lane status*/ + for (lane = 0; lane < link->cur_link_settings.lane_count; lane++) { + /* check status of lanes 0,1 + * changed DpcdAddress_Lane01Status (0x202) + */ + lane_status.raw = get_nibble_at_index( + &hpd_irq_dpcd_data->bytes.lane01_status.raw, + lane); + + if (!lane_status.bits.CHANNEL_EQ_DONE_0 || + !lane_status.bits.CR_DONE_0 || + !lane_status.bits.SYMBOL_LOCKED_0) { + /* if one of the channel equalization, clock + * recovery or symbol lock is dropped + * consider it as (link has been + * dropped) dp sink status has changed + */ + sink_status_changed = true; + break; + } + } + + /* Check interlane align.*/ + if (sink_status_changed || + !hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) { + + DC_LOG_HW_HPD_IRQ("%s: Link Status changed.\n", __func__); + + return_code = true; + + /*2. Check that we can handle interrupt: Not in FS DOS, + * Not in "Display Timeout" state, Link is trained. + */ + dpcd_result = core_link_read_dpcd(link, + DP_SET_POWER, + &irq_reg_rx_power_state, + sizeof(irq_reg_rx_power_state)); + + if (dpcd_result != DC_OK) { + DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain power state.\n", + __func__); + } else { + if (irq_reg_rx_power_state != DP_SET_POWER_D0) + return_code = false; + } + } + + return return_code; +} + bool dp_verify_link_cap( struct dc_link *link, struct dc_link_settings *known_limit_link_setting, @@ -1104,12 +1219,14 @@ bool dp_verify_link_cap( struct clock_source *dp_cs; enum clock_source_id dp_cs_id = CLOCK_SOURCE_ID_EXTERNAL; enum link_training_result status; + union hpd_irq_data irq_data; if (link->dc->debug.skip_detection_link_training) { link->verified_link_cap = *known_limit_link_setting; return true; } + memset(&irq_data, 0, sizeof(irq_data)); success = false; skip_link_training = false; @@ -1168,9 +1285,15 @@ bool dp_verify_link_cap( (*fail_count)++; } - if (success) + if (success) { link->verified_link_cap = *cur; - + udelay(1000); + if (read_hpd_rx_irq_data(link, &irq_data) == DC_OK) + if (hpd_rx_irq_check_link_loss_status( + link, + &irq_data)) + (*fail_count)++; + } /* always disable the link before trying another * setting or before returning we'll enable it later * based on the actual mode we're driving @@ -1572,122 +1695,6 @@ void decide_link_settings(struct dc_stream_state *stream, } /*************************Short Pulse IRQ***************************/ - -static bool hpd_rx_irq_check_link_loss_status( - struct dc_link *link, - union hpd_irq_data *hpd_irq_dpcd_data) -{ - uint8_t irq_reg_rx_power_state = 0; - enum dc_status dpcd_result = DC_ERROR_UNEXPECTED; - union lane_status lane_status; - uint32_t lane; - bool sink_status_changed; - bool return_code; - - sink_status_changed = false; - return_code = false; - - if (link->cur_link_settings.lane_count == 0) - return return_code; - - /*1. Check that Link Status changed, before re-training.*/ - - /*parse lane status*/ - for (lane = 0; lane < link->cur_link_settings.lane_count; lane++) { - /* check status of lanes 0,1 - * changed DpcdAddress_Lane01Status (0x202) - */ - lane_status.raw = get_nibble_at_index( - &hpd_irq_dpcd_data->bytes.lane01_status.raw, - lane); - - if (!lane_status.bits.CHANNEL_EQ_DONE_0 || - !lane_status.bits.CR_DONE_0 || - !lane_status.bits.SYMBOL_LOCKED_0) { - /* if one of the channel equalization, clock - * recovery or symbol lock is dropped - * consider it as (link has been - * dropped) dp sink status has changed - */ - sink_status_changed = true; - break; - } - } - - /* Check interlane align.*/ - if (sink_status_changed || - !hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) { - - DC_LOG_HW_HPD_IRQ("%s: Link Status changed.\n", __func__); - - return_code = true; - - /*2. Check that we can handle interrupt: Not in FS DOS, - * Not in "Display Timeout" state, Link is trained. - */ - dpcd_result = core_link_read_dpcd(link, - DP_SET_POWER, - &irq_reg_rx_power_state, - sizeof(irq_reg_rx_power_state)); - - if (dpcd_result != DC_OK) { - DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain power state.\n", - __func__); - } else { - if (irq_reg_rx_power_state != DP_SET_POWER_D0) - return_code = false; - } - } - - return return_code; -} - -static enum dc_status read_hpd_rx_irq_data( - struct dc_link *link, - union hpd_irq_data *irq_data) -{ - static enum dc_status retval; - - /* The HW reads 16 bytes from 200h on HPD, - * but if we get an AUX_DEFER, the HW cannot retry - * and this causes the CTS tests 4.3.2.1 - 3.2.4 to - * fail, so we now explicitly read 6 bytes which is - * the req from the above mentioned test cases. - * - * For DP 1.4 we need to read those from 2002h range. - */ - if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14) - retval = core_link_read_dpcd( - link, - DP_SINK_COUNT, - irq_data->raw, - sizeof(union hpd_irq_data)); - else { - /* Read 14 bytes in a single read and then copy only the required fields. - * This is more efficient than doing it in two separate AUX reads. */ - - uint8_t tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI + 1]; - - retval = core_link_read_dpcd( - link, - DP_SINK_COUNT_ESI, - tmp, - sizeof(tmp)); - - if (retval != DC_OK) - return retval; - - irq_data->bytes.sink_cnt.raw = tmp[DP_SINK_COUNT_ESI - DP_SINK_COUNT_ESI]; - irq_data->bytes.device_service_irq.raw = tmp[DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI]; - irq_data->bytes.lane01_status.raw = tmp[DP_LANE0_1_STATUS_ESI - DP_SINK_COUNT_ESI]; - irq_data->bytes.lane23_status.raw = tmp[DP_LANE2_3_STATUS_ESI - DP_SINK_COUNT_ESI]; - irq_data->bytes.lane_status_updated.raw = tmp[DP_LANE_ALIGN_STATUS_UPDATED_ESI - DP_SINK_COUNT_ESI]; - irq_data->bytes.sink_status.raw = tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI]; - } - - return retval; -} - static bool allow_hpd_rx_irq(const struct dc_link *link) { /* @@ -2240,7 +2247,8 @@ static void get_active_converter_info( translate_dpcd_max_bpc( hdmi_color_caps.bits.MAX_BITS_PER_COLOR_COMPONENT); - link->dpcd_caps.dongle_caps.extendedCapValid = true; + if (link->dpcd_caps.dongle_caps.dp_hdmi_max_pixel_clk != 0) + link->dpcd_caps.dongle_caps.extendedCapValid = true; } break; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c index 82cd1d6e6e59..0065ec7d5330 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c @@ -96,6 +96,7 @@ void dp_enable_link_phy( link_settings, clock_source); } + link->cur_link_settings = *link_settings; dp_receiver_power_ctrl(link, true); } @@ -307,6 +308,7 @@ void dp_retrain_link_dp_test(struct dc_link *link, link->link_enc, link_setting, pipes[i].clock_source->id); + link->cur_link_settings = *link_setting; dp_receiver_power_ctrl(link, true); @@ -316,7 +318,6 @@ void dp_retrain_link_dp_test(struct dc_link *link, skip_video_pattern, LINK_TRAINING_ATTEMPTS); - link->cur_link_settings = *link_setting; link->dc->hwss.enable_stream(&pipes[i]); 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 c347afd1030f..76137df74a53 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -83,7 +83,10 @@ enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id) dc_version = DCE_VERSION_11_22; break; case FAMILY_AI: - dc_version = DCE_VERSION_12_0; + if (ASICREV_IS_VEGA20_P(asic_id.hw_internal_rev)) + dc_version = DCE_VERSION_12_1; + else + dc_version = DCE_VERSION_12_0; break; #if defined(CONFIG_DRM_AMD_DC_DCN1_0) case FAMILY_RV: @@ -136,6 +139,7 @@ struct resource_pool *dc_create_resource_pool( num_virtual_links, dc); break; case DCE_VERSION_12_0: + case DCE_VERSION_12_1: res_pool = dce120_create_resource_pool( num_virtual_links, dc); break; diff --git a/drivers/gpu/drm/amd/display/dc/dc_helper.c b/drivers/gpu/drm/amd/display/dc/dc_helper.c index fcfd50b5dba0..4842d2378bbf 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dc_helper.c @@ -234,14 +234,14 @@ uint32_t generic_reg_wait(const struct dc_context *ctx, if (field_value == condition_value) { if (i * delay_between_poll_us > 1000 && !IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) - dm_output_to_console("REG_WAIT taking a while: %dms in %s line:%d\n", + DC_LOG_DC("REG_WAIT taking a while: %dms in %s line:%d\n", delay_between_poll_us * i / 1000, func_name, line); return reg_val; } } - dm_error("REG_WAIT timeout %dus * %d tries - %s line:%d\n", + DC_LOG_WARNING("REG_WAIT timeout %dus * %d tries - %s line:%d\n", delay_between_poll_us, time_out_num_tries, func_name, line); diff --git a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h index 9ddfe4c6938b..e72fce4eca65 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h @@ -192,7 +192,6 @@ enum surface_pixel_format { /*swaped & float*/ SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F, /*grow graphics here if necessary */ - SURFACE_PIXEL_FORMAT_VIDEO_AYCrCb8888, SURFACE_PIXEL_FORMAT_VIDEO_BEGIN, SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr = SURFACE_PIXEL_FORMAT_VIDEO_BEGIN, @@ -200,6 +199,7 @@ enum surface_pixel_format { SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr, SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb, SURFACE_PIXEL_FORMAT_SUBSAMPLE_END, + SURFACE_PIXEL_FORMAT_VIDEO_AYCrCb8888, SURFACE_PIXEL_FORMAT_INVALID /*grow 444 video here if necessary */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clk_mgr.c index bd22f51813bf..afd287f08bc9 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clk_mgr.c @@ -676,6 +676,11 @@ static void dce112_update_clocks(struct clk_mgr *clk_mgr, { struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); struct dm_pp_power_level_change_request level_change_req; + int unpatched_disp_clk = context->bw.dce.dispclk_khz; + + /*TODO: W/A for dal3 linux, investigate why this works */ + if (!clk_mgr_dce->dfs_bypass_active) + context->bw.dce.dispclk_khz = context->bw.dce.dispclk_khz * 115 / 100; level_change_req.power_level = dce_get_required_clocks_state(clk_mgr, context); /* get max clock state from PPLIB */ @@ -690,6 +695,8 @@ static void dce112_update_clocks(struct clk_mgr *clk_mgr, clk_mgr->clks.dispclk_khz = context->bw.dce.dispclk_khz; } dce11_pplib_apply_display_requirements(clk_mgr->ctx->dc, context); + + context->bw.dce.dispclk_khz = unpatched_disp_clk; } static void dce12_update_clocks(struct clk_mgr *clk_mgr, diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c index 6349ba7bec7c..4bf24758217f 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c @@ -1267,10 +1267,19 @@ static void program_scaler(const struct dc *dc, pipe_ctx->plane_res.scl_data.lb_params.depth, &pipe_ctx->stream->bit_depth_params); - if (pipe_ctx->stream_res.tg->funcs->set_overscan_blank_color) + if (pipe_ctx->stream_res.tg->funcs->set_overscan_blank_color) { + /* + * The way 420 is packed, 2 channels carry Y component, 1 channel + * alternate between Cb and Cr, so both channels need the pixel + * value for Y + */ + if (pipe_ctx->stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) + color.color_r_cr = color.color_g_y; + pipe_ctx->stream_res.tg->funcs->set_overscan_blank_color( pipe_ctx->stream_res.tg, &color); + } pipe_ctx->plane_res.xfm->funcs->transform_set_scaler(pipe_ctx->plane_res.xfm, &pipe_ctx->plane_res.scl_data); diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c index 0bd33a713836..91e015e14355 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c @@ -2159,6 +2159,15 @@ static void dcn10_blank_pixel_data( color_space = stream->output_color_space; color_space_to_black_color(dc, color_space, &black_color); + /* + * The way 420 is packed, 2 channels carry Y component, 1 channel + * alternate between Cb and Cr, so both channels need the pixel + * value for Y + */ + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) + black_color.color_r_cr = black_color.color_g_y; + + if (stream_res->tg->funcs->set_blank_color) stream_res->tg->funcs->set_blank_color( stream_res->tg, @@ -2348,7 +2357,8 @@ static void dcn10_apply_ctx_for_surface( struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; /* Skip inactive pipes and ones already updated */ - if (!pipe_ctx->stream || pipe_ctx->stream == stream) + if (!pipe_ctx->stream || pipe_ctx->stream == stream + || !pipe_ctx->plane_state) continue; pipe_ctx->stream_res.tg->funcs->lock(pipe_ctx->stream_res.tg); @@ -2362,7 +2372,8 @@ static void dcn10_apply_ctx_for_surface( for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; - if (!pipe_ctx->stream || pipe_ctx->stream == stream) + if (!pipe_ctx->stream || pipe_ctx->stream == stream + || !pipe_ctx->plane_state) continue; dcn10_pipe_control_lock(dc, pipe_ctx, false); diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c index a683f4102e65..c2028c4744a6 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_factory.c @@ -79,6 +79,7 @@ bool dal_hw_factory_init( dal_hw_factory_dce110_init(factory); return true; case DCE_VERSION_12_0: + case DCE_VERSION_12_1: dal_hw_factory_dce120_init(factory); return true; #if defined(CONFIG_DRM_AMD_DC_DCN1_0) diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c index 096f45628630..236ca28784a9 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c @@ -76,6 +76,7 @@ bool dal_hw_translate_init( dal_hw_translate_dce110_init(translate); return true; case DCE_VERSION_12_0: + case DCE_VERSION_12_1: dal_hw_translate_dce120_init(translate); return true; #if defined(CONFIG_DRM_AMD_DC_DCN1_0) diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c b/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c index e56093f26eed..1ad6e49102ff 100644 --- a/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c +++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c @@ -90,6 +90,7 @@ struct i2caux *dal_i2caux_create( case DCE_VERSION_10_0: return dal_i2caux_dce100_create(ctx); case DCE_VERSION_12_0: + case DCE_VERSION_12_1: return dal_i2caux_dce120_create(ctx); #if defined(CONFIG_DRM_AMD_DC_DCN1_0) case DCN_VERSION_1_0: diff --git a/drivers/gpu/drm/amd/display/include/bios_parser_types.h b/drivers/gpu/drm/amd/display/include/bios_parser_types.h index f8dbfa5b89f2..7fd78a696800 100644 --- a/drivers/gpu/drm/amd/display/include/bios_parser_types.h +++ b/drivers/gpu/drm/amd/display/include/bios_parser_types.h @@ -41,6 +41,7 @@ enum as_signal_type { AS_SIGNAL_TYPE_LVDS, AS_SIGNAL_TYPE_DISPLAY_PORT, AS_SIGNAL_TYPE_GPU_PLL, + AS_SIGNAL_TYPE_XGMI, AS_SIGNAL_TYPE_UNKNOWN }; diff --git a/drivers/gpu/drm/amd/display/include/dal_types.h b/drivers/gpu/drm/amd/display/include/dal_types.h index 89627133e188..f5bd869d4320 100644 --- a/drivers/gpu/drm/amd/display/include/dal_types.h +++ b/drivers/gpu/drm/amd/display/include/dal_types.h @@ -42,6 +42,7 @@ enum dce_version { DCE_VERSION_11_2, DCE_VERSION_11_22, DCE_VERSION_12_0, + DCE_VERSION_12_1, DCE_VERSION_MAX, DCN_VERSION_1_0, #if defined(CONFIG_DRM_AMD_DC_DCN1_01) diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c index 2e99ecf4ab76..26154f9b2178 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c @@ -49,6 +49,10 @@ #include "soc15_common.h" #include "smuio/smuio_9_0_offset.h" #include "smuio/smuio_9_0_sh_mask.h" +#include "nbio/nbio_7_4_sh_mask.h" + +#define smnPCIE_LC_SPEED_CNTL 0x11140290 +#define smnPCIE_LC_LINK_WIDTH_CNTL 0x11140288 static void vega20_set_default_registry_data(struct pp_hwmgr *hwmgr) { @@ -2282,6 +2286,18 @@ static int vega20_force_clock_level(struct pp_hwmgr *hwmgr, break; case PP_PCIE: + soft_min_level = mask ? (ffs(mask) - 1) : 0; + soft_max_level = mask ? (fls(mask) - 1) : 0; + if (soft_min_level >= NUM_LINK_LEVELS || + soft_max_level >= NUM_LINK_LEVELS) + return -EINVAL; + + ret = smum_send_msg_to_smc_with_parameter(hwmgr, + PPSMC_MSG_SetMinLinkDpmByIndex, soft_min_level); + PP_ASSERT_WITH_CODE(!ret, + "Failed to set min link dpm level!", + return ret); + break; default: @@ -2758,9 +2774,14 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr, data->od8_settings.od8_settings_array; OverDriveTable_t *od_table = &(data->smc_state_table.overdrive_table); + struct phm_ppt_v3_information *pptable_information = + (struct phm_ppt_v3_information *)hwmgr->pptable; + PPTable_t *pptable = (PPTable_t *)pptable_information->smc_pptable; + struct amdgpu_device *adev = hwmgr->adev; struct pp_clock_levels_with_latency clocks; int i, now, size = 0; int ret = 0; + uint32_t gen_speed, lane_width; switch (type) { case PP_SCLK: @@ -2798,6 +2819,28 @@ static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr, break; case PP_PCIE: + gen_speed = (RREG32_PCIE(smnPCIE_LC_SPEED_CNTL) & + PSWUSP0_PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE_MASK) + >> PSWUSP0_PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE__SHIFT; + lane_width = (RREG32_PCIE(smnPCIE_LC_LINK_WIDTH_CNTL) & + PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD_MASK) + >> PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD__SHIFT; + for (i = 0; i < NUM_LINK_LEVELS; i++) + size += sprintf(buf + size, "%d: %s %s %dMhz %s\n", i, + (pptable->PcieGenSpeed[i] == 0) ? "2.5GT/s," : + (pptable->PcieGenSpeed[i] == 1) ? "5.0GT/s," : + (pptable->PcieGenSpeed[i] == 2) ? "8.0GT/s," : + (pptable->PcieGenSpeed[i] == 3) ? "16.0GT/s," : "", + (pptable->PcieLaneCount[i] == 1) ? "x1" : + (pptable->PcieLaneCount[i] == 2) ? "x2" : + (pptable->PcieLaneCount[i] == 3) ? "x4" : + (pptable->PcieLaneCount[i] == 4) ? "x8" : + (pptable->PcieLaneCount[i] == 5) ? "x12" : + (pptable->PcieLaneCount[i] == 6) ? "x16" : "", + pptable->LclkFreq[i], + (gen_speed == pptable->PcieGenSpeed[i]) && + (lane_width == pptable->PcieLaneCount[i]) ? + "*" : ""); break; case OD_SCLK: diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c index 892c1d9304bb..642d0e70d0f8 100644 --- a/drivers/gpu/drm/armada/armada_gem.c +++ b/drivers/gpu/drm/armada/armada_gem.c @@ -334,7 +334,7 @@ int armada_gem_pwrite_ioctl(struct drm_device *dev, void *data, ptr = (char __user *)(uintptr_t)args->ptr; - if (!access_ok(VERIFY_READ, ptr, args->size)) + if (!access_ok(ptr, args->size)) return -EFAULT; ret = fault_in_pages_readable(ptr, args->size); diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 60bd7d708e35..4985384e51f6 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -241,6 +241,7 @@ void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, state->fence = NULL; state->commit = NULL; + state->fb_damage_clips = NULL; } EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state); @@ -285,6 +286,8 @@ void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state) if (state->commit) drm_crtc_commit_put(state->commit); + + drm_property_blob_put(state->fb_damage_clips); } EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state); diff --git a/drivers/gpu/drm/drm_damage_helper.c b/drivers/gpu/drm/drm_damage_helper.c index d2a1c7372f36..31032407254d 100644 --- a/drivers/gpu/drm/drm_damage_helper.c +++ b/drivers/gpu/drm/drm_damage_helper.c @@ -178,7 +178,7 @@ int drm_atomic_helper_dirtyfb(struct drm_framebuffer *fb, state = drm_atomic_state_alloc(fb->dev); if (!state) { ret = -ENOMEM; - goto out; + goto out_drop_locks; } state->acquire_ctx = &ctx; @@ -238,6 +238,7 @@ out: kfree(rects); drm_atomic_state_put(state); +out_drop_locks: drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index ffa8dc35515f..46f48f245eb5 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -525,7 +525,7 @@ ssize_t drm_read(struct file *filp, char __user *buffer, struct drm_device *dev = file_priv->minor->dev; ssize_t ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) + if (!access_ok(buffer, count)) return -EFAULT; ret = mutex_lock_interruptible(&file_priv->event_read_lock); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 96efc84396bf..18c27f795cf6 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -339,7 +339,6 @@ static int etnaviv_ioctl_gem_userptr(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_etnaviv_gem_userptr *args = data; - int access; if (args->flags & ~(ETNA_USERPTR_READ|ETNA_USERPTR_WRITE) || args->flags == 0) @@ -351,12 +350,7 @@ static int etnaviv_ioctl_gem_userptr(struct drm_device *dev, void *data, args->user_ptr & ~PAGE_MASK) return -EINVAL; - if (args->flags & ETNA_USERPTR_WRITE) - access = VERIFY_WRITE; - else - access = VERIFY_READ; - - if (!access_ok(access, (void __user *)(unsigned long)args->user_ptr, + if (!access_ok((void __user *)(unsigned long)args->user_ptr, args->user_size)) return -EFAULT; diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c index 77edbfcb0f75..77ae634eb11c 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.c +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c @@ -1900,11 +1900,11 @@ static struct cmd_info cmd_info[] = { {"MI_URB_CLEAR", OP_MI_URB_CLEAR, F_LEN_VAR, R_RCS, D_ALL, 0, 8, NULL}, - {"ME_SEMAPHORE_SIGNAL", OP_MI_SEMAPHORE_SIGNAL, F_LEN_VAR, R_ALL, + {"MI_SEMAPHORE_SIGNAL", OP_MI_SEMAPHORE_SIGNAL, F_LEN_VAR, R_ALL, D_BDW_PLUS, 0, 8, NULL}, - {"ME_SEMAPHORE_WAIT", OP_MI_SEMAPHORE_WAIT, F_LEN_VAR, R_ALL, D_BDW_PLUS, - ADDR_FIX_1(2), 8, cmd_handler_mi_semaphore_wait}, + {"MI_SEMAPHORE_WAIT", OP_MI_SEMAPHORE_WAIT, F_LEN_VAR, R_ALL, + D_BDW_PLUS, ADDR_FIX_1(2), 8, cmd_handler_mi_semaphore_wait}, {"MI_STORE_DATA_IMM", OP_MI_STORE_DATA_IMM, F_LEN_VAR, R_ALL, D_BDW_PLUS, ADDR_FIX_1(1), 10, cmd_handler_mi_store_data_imm}, diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index 6ef5a7fc70df..733a2a0d0c30 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -437,7 +437,7 @@ int intel_gvt_init_device(struct drm_i915_private *dev_priv) ret = intel_gvt_debugfs_init(gvt); if (ret) - gvt_err("debugfs registeration failed, go on.\n"); + gvt_err("debugfs registration failed, go on.\n"); gvt_dbg_core("gvt device initialization is done\n"); dev_priv->gvt = gvt; diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 31f6cdbe5c42..b4ab1dad0143 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -159,6 +159,10 @@ struct intel_vgpu_submission { struct kmem_cache *workloads; atomic_t running_workload_num; struct i915_gem_context *shadow_ctx; + union { + u64 i915_context_pml4; + u64 i915_context_pdps[GEN8_3LVL_PDPES]; + }; DECLARE_BITMAP(shadow_ctx_desc_updated, I915_NUM_ENGINES); DECLARE_BITMAP(tlb_handle_pending, I915_NUM_ENGINES); void *ring_scan_buffer[I915_NUM_ENGINES]; diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index aa280bb07125..b5475c91e2ef 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -475,6 +475,7 @@ static i915_reg_t force_nonpriv_white_list[] = { _MMIO(0x7704), _MMIO(0x7708), _MMIO(0x770c), + _MMIO(0x83a8), _MMIO(0xb110), GEN8_L3SQCREG4,//_MMIO(0xb118) _MMIO(0xe100), diff --git a/drivers/gpu/drm/i915/gvt/interrupt.c b/drivers/gpu/drm/i915/gvt/interrupt.c index 5daa23ae566b..6b9d1354ff29 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.c +++ b/drivers/gpu/drm/i915/gvt/interrupt.c @@ -126,7 +126,7 @@ static const char * const irq_name[INTEL_GVT_EVENT_MAX] = { [FDI_RX_INTERRUPTS_TRANSCODER_C] = "FDI RX Interrupts Combined C", [AUDIO_CP_CHANGE_TRANSCODER_C] = "Audio CP Change Transcoder C", [AUDIO_CP_REQUEST_TRANSCODER_C] = "Audio CP Request Transcoder C", - [ERR_AND_DBG] = "South Error and Debug Interupts Combined", + [ERR_AND_DBG] = "South Error and Debug Interrupts Combined", [GMBUS] = "Gmbus", [SDVO_B_HOTPLUG] = "SDVO B hotplug", [CRT_HOTPLUG] = "CRT Hotplug", diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index b8fbe3fabea3..1ad8c5e1455d 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -1079,6 +1079,21 @@ err: return ret; } +static void +i915_context_ppgtt_root_restore(struct intel_vgpu_submission *s) +{ + struct i915_hw_ppgtt *i915_ppgtt = s->shadow_ctx->ppgtt; + int i; + + if (i915_vm_is_48bit(&i915_ppgtt->vm)) + px_dma(&i915_ppgtt->pml4) = s->i915_context_pml4; + else { + for (i = 0; i < GEN8_3LVL_PDPES; i++) + px_dma(i915_ppgtt->pdp.page_directory[i]) = + s->i915_context_pdps[i]; + } +} + /** * intel_vgpu_clean_submission - free submission-related resource for vGPU * @vgpu: a vGPU @@ -1091,6 +1106,7 @@ void intel_vgpu_clean_submission(struct intel_vgpu *vgpu) struct intel_vgpu_submission *s = &vgpu->submission; intel_vgpu_select_submission_ops(vgpu, ALL_ENGINES, 0); + i915_context_ppgtt_root_restore(s); i915_gem_context_put(s->shadow_ctx); kmem_cache_destroy(s->workloads); } @@ -1116,6 +1132,21 @@ void intel_vgpu_reset_submission(struct intel_vgpu *vgpu, s->ops->reset(vgpu, engine_mask); } +static void +i915_context_ppgtt_root_save(struct intel_vgpu_submission *s) +{ + struct i915_hw_ppgtt *i915_ppgtt = s->shadow_ctx->ppgtt; + int i; + + if (i915_vm_is_48bit(&i915_ppgtt->vm)) + s->i915_context_pml4 = px_dma(&i915_ppgtt->pml4); + else { + for (i = 0; i < GEN8_3LVL_PDPES; i++) + s->i915_context_pdps[i] = + px_dma(i915_ppgtt->pdp.page_directory[i]); + } +} + /** * intel_vgpu_setup_submission - setup submission-related resource for vGPU * @vgpu: a vGPU @@ -1138,6 +1169,8 @@ int intel_vgpu_setup_submission(struct intel_vgpu *vgpu) if (IS_ERR(s->shadow_ctx)) return PTR_ERR(s->shadow_ctx); + i915_context_ppgtt_root_save(s); + bitmap_zero(s->shadow_ctx_desc_updated, I915_NUM_ENGINES); s->workloads = kmem_cache_create_usercopy("gvt-g_vgpu_workload", diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a9de07bb72c8..216f52b744a6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1282,8 +1282,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, if (args->size == 0) return 0; - if (!access_ok(VERIFY_WRITE, - u64_to_user_ptr(args->data_ptr), + if (!access_ok(u64_to_user_ptr(args->data_ptr), args->size)) return -EFAULT; @@ -1609,9 +1608,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, if (args->size == 0) return 0; - if (!access_ok(VERIFY_READ, - u64_to_user_ptr(args->data_ptr), - args->size)) + if (!access_ok(u64_to_user_ptr(args->data_ptr), args->size)) return -EFAULT; obj = i915_gem_object_lookup(file, args->handle); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 8ff6b581cf1c..485b259127c3 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1447,7 +1447,7 @@ static int eb_relocate_vma(struct i915_execbuffer *eb, struct i915_vma *vma) * to read. However, if the array is not writable the user loses * the updated relocation values. */ - if (unlikely(!access_ok(VERIFY_READ, urelocs, remain*sizeof(*urelocs)))) + if (unlikely(!access_ok(urelocs, remain*sizeof(*urelocs)))) return -EFAULT; do { @@ -1554,7 +1554,7 @@ static int check_relocations(const struct drm_i915_gem_exec_object2 *entry) addr = u64_to_user_ptr(entry->relocs_ptr); size *= sizeof(struct drm_i915_gem_relocation_entry); - if (!access_ok(VERIFY_READ, addr, size)) + if (!access_ok(addr, size)) return -EFAULT; end = addr + size; @@ -1605,6 +1605,7 @@ static int eb_copy_relocations(const struct i915_execbuffer *eb) (char __user *)urelocs + copied, len)) { end_user: + user_access_end(); kvfree(relocs); err = -EFAULT; goto err; @@ -1623,7 +1624,9 @@ end_user: * happened we would make the mistake of assuming that the * relocations were valid. */ - user_access_begin(); + if (!user_access_begin(urelocs, size)) + goto end_user; + for (copied = 0; copied < nreloc; copied++) unsafe_put_user(-1, &urelocs[copied].presumed_offset, @@ -2090,7 +2093,7 @@ get_fence_array(struct drm_i915_gem_execbuffer2 *args, return ERR_PTR(-EINVAL); user = u64_to_user_ptr(args->cliprects_ptr); - if (!access_ok(VERIFY_READ, user, nfences * sizeof(*user))) + if (!access_ok(user, nfences * sizeof(*user))) return ERR_PTR(-EFAULT); fences = kvmalloc_array(nfences, sizeof(*fences), @@ -2605,7 +2608,16 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data, unsigned int i; /* Copy the new buffer offsets back to the user's exec list. */ - user_access_begin(); + /* + * Note: count * sizeof(*user_exec_list) does not overflow, + * because we checked 'count' in check_buffer_count(). + * + * And this range already got effectively checked earlier + * when we did the "copy_from_user()" above. + */ + if (!user_access_begin(user_exec_list, count * sizeof(*user_exec_list))) + goto end_user; + for (i = 0; i < args->buffer_count; i++) { if (!(exec2_list[i].offset & UPDATE)) continue; diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c index 3df77020aada..9558582c105e 100644 --- a/drivers/gpu/drm/i915/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -789,8 +789,7 @@ i915_gem_userptr_ioctl(struct drm_device *dev, if (offset_in_page(args->user_ptr | args->user_size)) return -EINVAL; - if (!access_ok(args->flags & I915_USERPTR_READ_ONLY ? VERIFY_READ : VERIFY_WRITE, - (char __user *)(unsigned long)args->user_ptr, args->user_size)) + if (!access_ok((char __user *)(unsigned long)args->user_ptr, args->user_size)) return -EFAULT; if (args->flags & I915_USERPTR_READ_ONLY) { diff --git a/drivers/gpu/drm/i915/i915_ioc32.c b/drivers/gpu/drm/i915/i915_ioc32.c index 0e5c580d117c..e869daf9c8a9 100644 --- a/drivers/gpu/drm/i915/i915_ioc32.c +++ b/drivers/gpu/drm/i915/i915_ioc32.c @@ -52,7 +52,7 @@ static int compat_i915_getparam(struct file *file, unsigned int cmd, return -EFAULT; request = compat_alloc_user_space(sizeof(*request)); - if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) || + if (!access_ok(request, sizeof(*request)) || __put_user(req32.param, &request->param) || __put_user((void __user *)(unsigned long)req32.value, &request->value)) diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index 4529edfdcfc8..2b2eb57ca71f 100644 --- a/drivers/gpu/drm/i915/i915_perf.c +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -3052,7 +3052,7 @@ static struct i915_oa_reg *alloc_oa_regs(struct drm_i915_private *dev_priv, if (!n_regs) return NULL; - if (!access_ok(VERIFY_READ, regs, n_regs * sizeof(u32) * 2)) + if (!access_ok(regs, n_regs * sizeof(u32) * 2)) return ERR_PTR(-EFAULT); /* No is_valid function means we're not allowing any register to be programmed. */ diff --git a/drivers/gpu/drm/i915/i915_query.c b/drivers/gpu/drm/i915/i915_query.c index 6fc4b8eeab42..fe56465cdfd6 100644 --- a/drivers/gpu/drm/i915/i915_query.c +++ b/drivers/gpu/drm/i915/i915_query.c @@ -46,7 +46,7 @@ static int query_topology_info(struct drm_i915_private *dev_priv, if (topo.flags != 0) return -EINVAL; - if (!access_ok(VERIFY_WRITE, u64_to_user_ptr(query_item->data_ptr), + if (!access_ok(u64_to_user_ptr(query_item->data_ptr), total_length)) return -EFAULT; diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index a28465d90529..12b983fc0b56 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -77,7 +77,7 @@ void msm_gem_submit_free(struct msm_gem_submit *submit) static inline unsigned long __must_check copy_from_user_inatomic(void *to, const void __user *from, unsigned long n) { - if (access_ok(VERIFY_READ, from, n)) + if (access_ok(from, n)) return __copy_from_user_inatomic(to, from, n); return -EFAULT; } diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index 4b75ad40dd80..432c440223bb 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -4,7 +4,8 @@ config DRM_NOUVEAU select FW_LOADER select DRM_KMS_HELPER select DRM_TTM - select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT + select BACKLIGHT_CLASS_DEVICE if DRM_NOUVEAU_BACKLIGHT + select BACKLIGHT_LCD_SUPPORT if DRM_NOUVEAU_BACKLIGHT select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && INPUT select X86_PLATFORM_DEVICES if ACPI && X86 select ACPI_WMI if ACPI && X86 diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c index 6e828158bcb0..d410e2925162 100644 --- a/drivers/gpu/drm/qxl/qxl_ioctl.c +++ b/drivers/gpu/drm/qxl/qxl_ioctl.c @@ -163,8 +163,7 @@ static int qxl_process_single_command(struct qxl_device *qdev, if (cmd->command_size > PAGE_SIZE - sizeof(union qxl_release_info)) return -EINVAL; - if (!access_ok(VERIFY_READ, - u64_to_user_ptr(cmd->command), + if (!access_ok(u64_to_user_ptr(cmd->command), cmd->command_size)) return -EFAULT; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index d87935bf8e30..0ec08394e17a 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -77,38 +77,39 @@ static inline int ttm_mem_type_from_place(const struct ttm_place *place, return 0; } -static void ttm_mem_type_debug(struct ttm_bo_device *bdev, int mem_type) +static void ttm_mem_type_debug(struct ttm_bo_device *bdev, struct drm_printer *p, + int mem_type) { struct ttm_mem_type_manager *man = &bdev->man[mem_type]; - struct drm_printer p = drm_debug_printer(TTM_PFX); - pr_err(" has_type: %d\n", man->has_type); - pr_err(" use_type: %d\n", man->use_type); - pr_err(" flags: 0x%08X\n", man->flags); - pr_err(" gpu_offset: 0x%08llX\n", man->gpu_offset); - pr_err(" size: %llu\n", man->size); - pr_err(" available_caching: 0x%08X\n", man->available_caching); - pr_err(" default_caching: 0x%08X\n", man->default_caching); + drm_printf(p, " has_type: %d\n", man->has_type); + drm_printf(p, " use_type: %d\n", man->use_type); + drm_printf(p, " flags: 0x%08X\n", man->flags); + drm_printf(p, " gpu_offset: 0x%08llX\n", man->gpu_offset); + drm_printf(p, " size: %llu\n", man->size); + drm_printf(p, " available_caching: 0x%08X\n", man->available_caching); + drm_printf(p, " default_caching: 0x%08X\n", man->default_caching); if (mem_type != TTM_PL_SYSTEM) - (*man->func->debug)(man, &p); + (*man->func->debug)(man, p); } static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo, struct ttm_placement *placement) { + struct drm_printer p = drm_debug_printer(TTM_PFX); int i, ret, mem_type; - pr_err("No space for %p (%lu pages, %luK, %luM)\n", - bo, bo->mem.num_pages, bo->mem.size >> 10, - bo->mem.size >> 20); + drm_printf(&p, "No space for %p (%lu pages, %luK, %luM)\n", + bo, bo->mem.num_pages, bo->mem.size >> 10, + bo->mem.size >> 20); for (i = 0; i < placement->num_placement; i++) { ret = ttm_mem_type_from_place(&placement->placement[i], &mem_type); if (ret) return; - pr_err(" placement[%d]=0x%08X (%d)\n", - i, placement->placement[i].flags, mem_type); - ttm_mem_type_debug(bo->bdev, mem_type); + drm_printf(&p, " placement[%d]=0x%08X (%d)\n", + i, placement->placement[i].flags, mem_type); + ttm_mem_type_debug(bo->bdev, &p, mem_type); } } diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index a1fa2fc8c9b5..951bb17ae8b2 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -70,6 +70,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); #define QUIRK_T100_KEYBOARD BIT(6) #define QUIRK_T100CHI BIT(7) #define QUIRK_G752_KEYBOARD BIT(8) +#define QUIRK_T101HA_DOCK BIT(9) #define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \ @@ -241,6 +242,18 @@ static int asus_report_input(struct asus_drvdata *drvdat, u8 *data, int size) return 1; } +static int asus_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 && + (usage->hid & HID_USAGE) != 0x00 && !usage->type) { + hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n", + usage->hid & HID_USAGE); + } + + return 0; +} + static int asus_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { @@ -510,6 +523,7 @@ static int asus_input_mapping(struct hid_device *hdev, case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break; case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF); break; case 0x6c: asus_map_key_clear(KEY_SLEEP); break; + case 0x7c: asus_map_key_clear(KEY_MICMUTE); break; case 0x82: asus_map_key_clear(KEY_CAMERA); break; case 0x88: asus_map_key_clear(KEY_RFKILL); break; case 0xb5: asus_map_key_clear(KEY_CALC); break; @@ -528,6 +542,9 @@ static int asus_input_mapping(struct hid_device *hdev, /* Fn+Space Power4Gear Hybrid */ case 0x5c: asus_map_key_clear(KEY_PROG3); break; + /* Fn+F5 "fan" symbol on FX503VD */ + case 0x99: asus_map_key_clear(KEY_PROG4); break; + default: /* ASUS lazily declares 256 usages, ignore the rest, * as some make the keyboard appear as a pointer device. */ @@ -683,6 +700,11 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } + /* use hid-multitouch for T101HA touchpad */ + if (id->driver_data & QUIRK_T101HA_DOCK && + hdev->collection->usage == HID_GD_MOUSE) + return -ENODEV; + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "Asus hw start failed: %d\n", ret); @@ -806,11 +828,16 @@ static const struct hid_device_id asus_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3), QUIRK_G752_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, + USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD), + QUIRK_USE_KBD_BACKLIGHT }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD), QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T100TAF_KEYBOARD), QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, + USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD), QUIRK_T101HA_DOCK }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_ASUS_AK1D) }, { HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_ASUS_MD_5110) }, { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_ASUS_MD_5112) }, @@ -832,6 +859,7 @@ static struct hid_driver asus_driver = { #ifdef CONFIG_PM .reset_resume = asus_reset_resume, #endif + .event = asus_event, .raw_event = asus_raw_event }; module_hid_driver(asus_driver); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5bec9244c45b..f41d5fe51abe 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -172,6 +172,8 @@ static int open_collection(struct hid_parser *parser, unsigned type) collection->type = type; collection->usage = usage; collection->level = parser->collection_stack_ptr - 1; + collection->parent = parser->active_collection; + parser->active_collection = collection; if (type == HID_COLLECTION_APPLICATION) parser->device->maxapplication++; @@ -190,6 +192,8 @@ static int close_collection(struct hid_parser *parser) return -EINVAL; } parser->collection_stack_ptr--; + if (parser->active_collection) + parser->active_collection = parser->active_collection->parent; return 0; } @@ -290,6 +294,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign field->usage[i].collection_index = parser->local.collection_index[j]; field->usage[i].usage_index = i; + field->usage[i].resolution_multiplier = 1; } field->maxusage = usages; @@ -943,6 +948,167 @@ struct hid_report *hid_validate_values(struct hid_device *hid, } EXPORT_SYMBOL_GPL(hid_validate_values); +static int hid_calculate_multiplier(struct hid_device *hid, + struct hid_field *multiplier) +{ + int m; + __s32 v = *multiplier->value; + __s32 lmin = multiplier->logical_minimum; + __s32 lmax = multiplier->logical_maximum; + __s32 pmin = multiplier->physical_minimum; + __s32 pmax = multiplier->physical_maximum; + + /* + * "Because OS implementations will generally divide the control's + * reported count by the Effective Resolution Multiplier, designers + * should take care not to establish a potential Effective + * Resolution Multiplier of zero." + * HID Usage Table, v1.12, Section 4.3.1, p31 + */ + if (lmax - lmin == 0) + return 1; + /* + * Handling the unit exponent is left as an exercise to whoever + * finds a device where that exponent is not 0. + */ + m = ((v - lmin)/(lmax - lmin) * (pmax - pmin) + pmin); + if (unlikely(multiplier->unit_exponent != 0)) { + hid_warn(hid, + "unsupported Resolution Multiplier unit exponent %d\n", + multiplier->unit_exponent); + } + + /* There are no devices with an effective multiplier > 255 */ + if (unlikely(m == 0 || m > 255 || m < -255)) { + hid_warn(hid, "unsupported Resolution Multiplier %d\n", m); + m = 1; + } + + return m; +} + +static void hid_apply_multiplier_to_field(struct hid_device *hid, + struct hid_field *field, + struct hid_collection *multiplier_collection, + int effective_multiplier) +{ + struct hid_collection *collection; + struct hid_usage *usage; + int i; + + /* + * If multiplier_collection is NULL, the multiplier applies + * to all fields in the report. + * Otherwise, it is the Logical Collection the multiplier applies to + * but our field may be in a subcollection of that collection. + */ + for (i = 0; i < field->maxusage; i++) { + usage = &field->usage[i]; + + collection = &hid->collection[usage->collection_index]; + while (collection && collection != multiplier_collection) + collection = collection->parent; + + if (collection || multiplier_collection == NULL) + usage->resolution_multiplier = effective_multiplier; + + } +} + +static void hid_apply_multiplier(struct hid_device *hid, + struct hid_field *multiplier) +{ + struct hid_report_enum *rep_enum; + struct hid_report *rep; + struct hid_field *field; + struct hid_collection *multiplier_collection; + int effective_multiplier; + int i; + + /* + * "The Resolution Multiplier control must be contained in the same + * Logical Collection as the control(s) to which it is to be applied. + * If no Resolution Multiplier is defined, then the Resolution + * Multiplier defaults to 1. If more than one control exists in a + * Logical Collection, the Resolution Multiplier is associated with + * all controls in the collection. If no Logical Collection is + * defined, the Resolution Multiplier is associated with all + * controls in the report." + * HID Usage Table, v1.12, Section 4.3.1, p30 + * + * Thus, search from the current collection upwards until we find a + * logical collection. Then search all fields for that same parent + * collection. Those are the fields the multiplier applies to. + * + * If we have more than one multiplier, it will overwrite the + * applicable fields later. + */ + multiplier_collection = &hid->collection[multiplier->usage->collection_index]; + while (multiplier_collection && + multiplier_collection->type != HID_COLLECTION_LOGICAL) + multiplier_collection = multiplier_collection->parent; + + effective_multiplier = hid_calculate_multiplier(hid, multiplier); + + rep_enum = &hid->report_enum[HID_INPUT_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) { + for (i = 0; i < rep->maxfield; i++) { + field = rep->field[i]; + hid_apply_multiplier_to_field(hid, field, + multiplier_collection, + effective_multiplier); + } + } +} + +/* + * hid_setup_resolution_multiplier - set up all resolution multipliers + * + * @device: hid device + * + * Search for all Resolution Multiplier Feature Reports and apply their + * value to all matching Input items. This only updates the internal struct + * fields. + * + * The Resolution Multiplier is applied by the hardware. If the multiplier + * is anything other than 1, the hardware will send pre-multiplied events + * so that the same physical interaction generates an accumulated + * accumulated_value = value * * multiplier + * This may be achieved by sending + * - "value * multiplier" for each event, or + * - "value" but "multiplier" times as frequently, or + * - a combination of the above + * The only guarantee is that the same physical interaction always generates + * an accumulated 'value * multiplier'. + * + * This function must be called before any event processing and after + * any SetRequest to the Resolution Multiplier. + */ +void hid_setup_resolution_multiplier(struct hid_device *hid) +{ + struct hid_report_enum *rep_enum; + struct hid_report *rep; + struct hid_usage *usage; + int i, j; + + rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) { + for (i = 0; i < rep->maxfield; i++) { + /* Ignore if report count is out of bounds. */ + if (rep->field[i]->report_count < 1) + continue; + + for (j = 0; j < rep->field[i]->maxusage; j++) { + usage = &rep->field[i]->usage[j]; + if (usage->hid == HID_GD_RESOLUTION_MULTIPLIER) + hid_apply_multiplier(hid, + rep->field[i]); + } + } + } +} +EXPORT_SYMBOL_GPL(hid_setup_resolution_multiplier); + /** * hid_open_report - open a driver-specific device report * @@ -1039,9 +1205,17 @@ int hid_open_report(struct hid_device *device) hid_err(device, "unbalanced delimiter at end of report description\n"); goto err; } + + /* + * fetch initial values in case the device's + * default multiplier isn't the recommended 1 + */ + hid_setup_resolution_multiplier(device); + kfree(parser->collection_stack); vfree(parser); device->status |= HID_STAT_PARSED; + return 0; } } diff --git a/drivers/hid/hid-cougar.c b/drivers/hid/hid-cougar.c index 3f0916b64c60..e0bb7b34f3a4 100644 --- a/drivers/hid/hid-cougar.c +++ b/drivers/hid/hid-cougar.c @@ -326,6 +326,8 @@ module_param_cb(g6_is_space, &cougar_g6_is_space_ops, &g6_is_space, 0644); static struct hid_device_id cougar_id_table[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR, USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR, + USB_DEVICE_ID_COUGAR_700K_GAMING_KEYBOARD) }, {} }; MODULE_DEVICE_TABLE(hid, cougar_id_table); diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index b48100236df8..c530476edba6 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -1072,11 +1072,6 @@ static int hid_debug_rdesc_show(struct seq_file *f, void *p) return 0; } -static int hid_debug_rdesc_open(struct inode *inode, struct file *file) -{ - return single_open(file, hid_debug_rdesc_show, inode->i_private); -} - static int hid_debug_events_open(struct inode *inode, struct file *file) { int err = 0; @@ -1211,12 +1206,7 @@ static int hid_debug_events_release(struct inode *inode, struct file *file) return 0; } -static const struct file_operations hid_debug_rdesc_fops = { - .open = hid_debug_rdesc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(hid_debug_rdesc); static const struct file_operations hid_debug_events_fops = { .owner = THIS_MODULE, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 27519eb8ee63..518fa76414f5 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -187,12 +187,14 @@ #define USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD 0x17e0 #define USB_DEVICE_ID_ASUSTEK_T100TAF_KEYBOARD 0x1807 #define USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD 0x8502 +#define USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD 0x183d #define USB_DEVICE_ID_ASUSTEK_T304_KEYBOARD 0x184a #define USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD 0x8585 #define USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD 0x0101 #define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854 #define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837 #define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822 +#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869 #define USB_VENDOR_ID_ATEN 0x0557 #define USB_DEVICE_ID_ATEN_UC100KM 0x2004 @@ -1025,6 +1027,7 @@ #define USB_VENDOR_ID_SOLID_YEAR 0x060b #define USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD 0x500a +#define USB_DEVICE_ID_COUGAR_700K_GAMING_KEYBOARD 0x700a #define USB_VENDOR_ID_SOUNDGRAPH 0x15c2 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index d6fab5798487..59a5608b8dc0 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -712,7 +712,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel map_abs_clear(usage->hid & 0xf); break; - case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL: + case HID_GD_WHEEL: + if (field->flags & HID_MAIN_ITEM_RELATIVE) { + set_bit(REL_WHEEL, input->relbit); + map_rel(REL_WHEEL_HI_RES); + } else { + map_abs(usage->hid & 0xf); + } + break; + case HID_GD_SLIDER: case HID_GD_DIAL: if (field->flags & HID_MAIN_ITEM_RELATIVE) map_rel(usage->hid & 0xf); else @@ -1012,7 +1020,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x22f: map_key_clear(KEY_ZOOMRESET); break; case 0x233: map_key_clear(KEY_SCROLLUP); break; case 0x234: map_key_clear(KEY_SCROLLDOWN); break; - case 0x238: map_rel(REL_HWHEEL); break; + case 0x238: /* AC Pan */ + set_bit(REL_HWHEEL, input->relbit); + map_rel(REL_HWHEEL_HI_RES); + break; case 0x23d: map_key_clear(KEY_EDIT); break; case 0x25f: map_key_clear(KEY_CANCEL); break; case 0x269: map_key_clear(KEY_INSERT); break; @@ -1200,6 +1211,38 @@ ignore: } +static void hidinput_handle_scroll(struct hid_usage *usage, + struct input_dev *input, + __s32 value) +{ + int code; + int hi_res, lo_res; + + if (value == 0) + return; + + if (usage->code == REL_WHEEL_HI_RES) + code = REL_WHEEL; + else + code = REL_HWHEEL; + + /* + * Windows reports one wheel click as value 120. Where a high-res + * scroll wheel is present, a fraction of 120 is reported instead. + * Our REL_WHEEL_HI_RES axis does the same because all HW must + * adhere to the 120 expectation. + */ + hi_res = value * 120/usage->resolution_multiplier; + + usage->wheel_accumulated += hi_res; + lo_res = usage->wheel_accumulated/120; + if (lo_res) + usage->wheel_accumulated -= lo_res * 120; + + input_event(input, EV_REL, code, lo_res); + input_event(input, EV_REL, usage->code, hi_res); +} + void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { struct input_dev *input; @@ -1262,6 +1305,12 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ return; + if ((usage->type == EV_REL) && (usage->code == REL_WHEEL_HI_RES || + usage->code == REL_HWHEEL_HI_RES)) { + hidinput_handle_scroll(usage, input, value); + return; + } + if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->code == ABS_VOLUME)) { int count = abs(value); @@ -1489,6 +1538,58 @@ static void hidinput_close(struct input_dev *dev) hid_hw_close(hid); } +static void hidinput_change_resolution_multipliers(struct hid_device *hid) +{ + struct hid_report_enum *rep_enum; + struct hid_report *rep; + struct hid_usage *usage; + int i, j; + + rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) { + bool update_needed = false; + + if (rep->maxfield == 0) + continue; + + /* + * If we have more than one feature within this report we + * need to fill in the bits from the others before we can + * overwrite the ones for the Resolution Multiplier. + */ + if (rep->maxfield > 1) { + hid_hw_request(hid, rep, HID_REQ_GET_REPORT); + hid_hw_wait(hid); + } + + for (i = 0; i < rep->maxfield; i++) { + __s32 logical_max = rep->field[i]->logical_maximum; + + /* There is no good reason for a Resolution + * Multiplier to have a count other than 1. + * Ignore that case. + */ + if (rep->field[i]->report_count != 1) + continue; + + for (j = 0; j < rep->field[i]->maxusage; j++) { + usage = &rep->field[i]->usage[j]; + + if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER) + continue; + + *rep->field[i]->value = logical_max; + update_needed = true; + } + } + if (update_needed) + hid_hw_request(hid, rep, HID_REQ_SET_REPORT); + } + + /* refresh our structs */ + hid_setup_resolution_multiplier(hid); +} + static void report_features(struct hid_device *hid) { struct hid_driver *drv = hid->driver; @@ -1782,6 +1883,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) } } + hidinput_change_resolution_multipliers(hid); + list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { if (drv->input_configured && drv->input_configured(hid, hidinput)) @@ -1840,4 +1943,3 @@ void hidinput_disconnect(struct hid_device *hid) cancel_work_sync(&hid->led_work); } EXPORT_SYMBOL_GPL(hidinput_disconnect); - diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index 643b6eb54442..eacc76d2ab96 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -743,7 +743,9 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev) data_pointer->led_mute.brightness_get = lenovo_led_brightness_get_tpkbd; data_pointer->led_mute.brightness_set = lenovo_led_brightness_set_tpkbd; data_pointer->led_mute.dev = dev; - led_classdev_register(dev, &data_pointer->led_mute); + ret = led_classdev_register(dev, &data_pointer->led_mute); + if (ret < 0) + goto err; data_pointer->led_micmute.name = name_micmute; data_pointer->led_micmute.brightness_get = @@ -751,7 +753,11 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev) data_pointer->led_micmute.brightness_set = lenovo_led_brightness_set_tpkbd; data_pointer->led_micmute.dev = dev; - led_classdev_register(dev, &data_pointer->led_micmute); + ret = led_classdev_register(dev, &data_pointer->led_micmute); + if (ret < 0) { + led_classdev_unregister(&data_pointer->led_mute); + goto err; + } lenovo_features_set_tpkbd(hdev); diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 19cc980eebce..15ed6177a7a3 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -21,6 +21,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/sched.h> +#include <linux/sched/clock.h> #include <linux/kfifo.h> #include <linux/input/mt.h> #include <linux/workqueue.h> @@ -64,6 +65,14 @@ MODULE_PARM_DESC(disable_tap_to_click, #define HIDPP_QUIRK_NO_HIDINPUT BIT(23) #define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24) #define HIDPP_QUIRK_UNIFYING BIT(25) +#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(26) +#define HIDPP_QUIRK_HI_RES_SCROLL_X2120 BIT(27) +#define HIDPP_QUIRK_HI_RES_SCROLL_X2121 BIT(28) + +/* Convenience constant to check for any high-res support. */ +#define HIDPP_QUIRK_HI_RES_SCROLL (HIDPP_QUIRK_HI_RES_SCROLL_1P0 | \ + HIDPP_QUIRK_HI_RES_SCROLL_X2120 | \ + HIDPP_QUIRK_HI_RES_SCROLL_X2121) #define HIDPP_QUIRK_DELAYED_INIT HIDPP_QUIRK_NO_HIDINPUT @@ -128,6 +137,25 @@ struct hidpp_battery { bool online; }; +/** + * struct hidpp_scroll_counter - Utility class for processing high-resolution + * scroll events. + * @dev: the input device for which events should be reported. + * @wheel_multiplier: the scalar multiplier to be applied to each wheel event + * @remainder: counts the number of high-resolution units moved since the last + * low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should + * only be used by class methods. + * @direction: direction of last movement (1 or -1) + * @last_time: last event time, used to reset remainder after inactivity + */ +struct hidpp_scroll_counter { + struct input_dev *dev; + int wheel_multiplier; + int remainder; + int direction; + unsigned long long last_time; +}; + struct hidpp_device { struct hid_device *hid_dev; struct mutex send_mutex; @@ -149,6 +177,7 @@ struct hidpp_device { unsigned long capabilities; struct hidpp_battery battery; + struct hidpp_scroll_counter vertical_wheel_counter; }; /* HID++ 1.0 error codes */ @@ -391,6 +420,67 @@ static void hidpp_prefix_name(char **name, int name_length) *name = new_name; } +/** + * hidpp_scroll_counter_handle_scroll() - Send high- and low-resolution scroll + * events given a high-resolution wheel + * movement. + * @counter: a hid_scroll_counter struct describing the wheel. + * @hi_res_value: the movement of the wheel, in the mouse's high-resolution + * units. + * + * Given a high-resolution movement, this function converts the movement into + * fractions of 120 and emits high-resolution scroll events for the input + * device. It also uses the multiplier from &struct hid_scroll_counter to + * emit low-resolution scroll events when appropriate for + * backwards-compatibility with userspace input libraries. + */ +static void hidpp_scroll_counter_handle_scroll(struct hidpp_scroll_counter *counter, + int hi_res_value) +{ + int low_res_value, remainder, direction; + unsigned long long now, previous; + + hi_res_value = hi_res_value * 120/counter->wheel_multiplier; + input_report_rel(counter->dev, REL_WHEEL_HI_RES, hi_res_value); + + remainder = counter->remainder; + direction = hi_res_value > 0 ? 1 : -1; + + now = sched_clock(); + previous = counter->last_time; + counter->last_time = now; + /* + * Reset the remainder after a period of inactivity or when the + * direction changes. This prevents the REL_WHEEL emulation point + * from sliding for devices that don't always provide the same + * number of movements per detent. + */ + if (now - previous > 1000000000 || direction != counter->direction) + remainder = 0; + + counter->direction = direction; + remainder += hi_res_value; + + /* Some wheels will rest 7/8ths of a detent from the previous detent + * after slow movement, so we want the threshold for low-res events to + * be in the middle between two detents (e.g. after 4/8ths) as + * opposed to on the detents themselves (8/8ths). + */ + if (abs(remainder) >= 60) { + /* Add (or subtract) 1 because we want to trigger when the wheel + * is half-way to the next detent (i.e. scroll 1 detent after a + * 1/2 detent movement, 2 detents after a 1 1/2 detent movement, + * etc.). + */ + low_res_value = remainder / 120; + if (low_res_value == 0) + low_res_value = (hi_res_value > 0 ? 1 : -1); + input_report_rel(counter->dev, REL_WHEEL, low_res_value); + remainder -= low_res_value * 120; + } + counter->remainder = remainder; +} + /* -------------------------------------------------------------------------- */ /* HIDP++ 1.0 commands */ /* -------------------------------------------------------------------------- */ @@ -400,32 +490,53 @@ static void hidpp_prefix_name(char **name, int name_length) #define HIDPP_SET_LONG_REGISTER 0x82 #define HIDPP_GET_LONG_REGISTER 0x83 -#define HIDPP_REG_GENERAL 0x00 - -static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev) +/** + * hidpp10_set_register_bit() - Sets a single bit in a HID++ 1.0 register. + * @hidpp_dev: the device to set the register on. + * @register_address: the address of the register to modify. + * @byte: the byte of the register to modify. Should be less than 3. + * Return: 0 if successful, otherwise a negative error code. + */ +static int hidpp10_set_register_bit(struct hidpp_device *hidpp_dev, + u8 register_address, u8 byte, u8 bit) { struct hidpp_report response; int ret; u8 params[3] = { 0 }; ret = hidpp_send_rap_command_sync(hidpp_dev, - REPORT_ID_HIDPP_SHORT, - HIDPP_GET_REGISTER, - HIDPP_REG_GENERAL, - NULL, 0, &response); + REPORT_ID_HIDPP_SHORT, + HIDPP_GET_REGISTER, + register_address, + NULL, 0, &response); if (ret) return ret; memcpy(params, response.rap.params, 3); - /* Set the battery bit */ - params[0] |= BIT(4); + params[byte] |= BIT(bit); return hidpp_send_rap_command_sync(hidpp_dev, - REPORT_ID_HIDPP_SHORT, - HIDPP_SET_REGISTER, - HIDPP_REG_GENERAL, - params, 3, &response); + REPORT_ID_HIDPP_SHORT, + HIDPP_SET_REGISTER, + register_address, + params, 3, &response); +} + + +#define HIDPP_REG_GENERAL 0x00 + +static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev) +{ + return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_GENERAL, 0, 4); +} + +#define HIDPP_REG_FEATURES 0x01 + +/* On HID++ 1.0 devices, high-res scroll was called "scrolling acceleration". */ +static int hidpp10_enable_scrolling_acceleration(struct hidpp_device *hidpp_dev) +{ + return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_FEATURES, 0, 6); } #define HIDPP_REG_BATTERY_STATUS 0x07 @@ -1137,6 +1248,99 @@ static int hidpp_battery_get_property(struct power_supply *psy, } /* -------------------------------------------------------------------------- */ +/* 0x2120: Hi-resolution scrolling */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_HI_RESOLUTION_SCROLLING 0x2120 + +#define CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE 0x10 + +static int hidpp_hrs_set_highres_scrolling_mode(struct hidpp_device *hidpp, + bool enabled, u8 *multiplier) +{ + u8 feature_index; + u8 feature_type; + int ret; + u8 params[1]; + struct hidpp_report response; + + ret = hidpp_root_get_feature(hidpp, + HIDPP_PAGE_HI_RESOLUTION_SCROLLING, + &feature_index, + &feature_type); + if (ret) + return ret; + + params[0] = enabled ? BIT(0) : 0; + ret = hidpp_send_fap_command_sync(hidpp, feature_index, + CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE, + params, sizeof(params), &response); + if (ret) + return ret; + *multiplier = response.fap.params[1]; + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* 0x2121: HiRes Wheel */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_HIRES_WHEEL 0x2121 + +#define CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY 0x00 +#define CMD_HIRES_WHEEL_SET_WHEEL_MODE 0x20 + +static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp, + u8 *multiplier) +{ + u8 feature_index; + u8 feature_type; + int ret; + struct hidpp_report response; + + ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL, + &feature_index, &feature_type); + if (ret) + goto return_default; + + ret = hidpp_send_fap_command_sync(hidpp, feature_index, + CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY, + NULL, 0, &response); + if (ret) + goto return_default; + + *multiplier = response.fap.params[0]; + return 0; +return_default: + hid_warn(hidpp->hid_dev, + "Couldn't get wheel multiplier (error %d)\n", ret); + return ret; +} + +static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert, + bool high_resolution, bool use_hidpp) +{ + u8 feature_index; + u8 feature_type; + int ret; + u8 params[1]; + struct hidpp_report response; + + ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL, + &feature_index, &feature_type); + if (ret) + return ret; + + params[0] = (invert ? BIT(2) : 0) | + (high_resolution ? BIT(1) : 0) | + (use_hidpp ? BIT(0) : 0); + + return hidpp_send_fap_command_sync(hidpp, feature_index, + CMD_HIRES_WHEEL_SET_WHEEL_MODE, + params, sizeof(params), &response); +} + +/* -------------------------------------------------------------------------- */ /* 0x4301: Solar Keyboard */ /* -------------------------------------------------------------------------- */ @@ -1465,7 +1669,7 @@ struct hidpp_ff_work_data { u8 size; }; -static const signed short hiddpp_ff_effects[] = { +static const signed short hidpp_ff_effects[] = { FF_CONSTANT, FF_PERIODIC, FF_SINE, @@ -1480,7 +1684,7 @@ static const signed short hiddpp_ff_effects[] = { -1 }; -static const signed short hiddpp_ff_effects_v2[] = { +static const signed short hidpp_ff_effects_v2[] = { FF_RAMP, FF_FRICTION, FF_INERTIA, @@ -1873,11 +2077,11 @@ static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index) version = bcdDevice & 255; /* Set supported force feedback capabilities */ - for (j = 0; hiddpp_ff_effects[j] >= 0; j++) - set_bit(hiddpp_ff_effects[j], dev->ffbit); + for (j = 0; hidpp_ff_effects[j] >= 0; j++) + set_bit(hidpp_ff_effects[j], dev->ffbit); if (version > 1) - for (j = 0; hiddpp_ff_effects_v2[j] >= 0; j++) - set_bit(hiddpp_ff_effects_v2[j], dev->ffbit); + for (j = 0; hidpp_ff_effects_v2[j] >= 0; j++) + set_bit(hidpp_ff_effects_v2[j], dev->ffbit); /* Read number of slots available in device */ error = hidpp_send_fap_command_sync(hidpp, feature_index, @@ -2387,10 +2591,15 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size) input_report_key(mydata->input, BTN_RIGHT, !!(data[1] & M560_MOUSE_BTN_RIGHT)); - if (data[1] & M560_MOUSE_BTN_WHEEL_LEFT) + if (data[1] & M560_MOUSE_BTN_WHEEL_LEFT) { input_report_rel(mydata->input, REL_HWHEEL, -1); - else if (data[1] & M560_MOUSE_BTN_WHEEL_RIGHT) + input_report_rel(mydata->input, REL_HWHEEL_HI_RES, + -120); + } else if (data[1] & M560_MOUSE_BTN_WHEEL_RIGHT) { input_report_rel(mydata->input, REL_HWHEEL, 1); + input_report_rel(mydata->input, REL_HWHEEL_HI_RES, + 120); + } v = hid_snto32(hid_field_extract(hdev, data+3, 0, 12), 12); input_report_rel(mydata->input, REL_X, v); @@ -2399,7 +2608,8 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size) input_report_rel(mydata->input, REL_Y, v); v = hid_snto32(data[6], 8); - input_report_rel(mydata->input, REL_WHEEL, v); + hidpp_scroll_counter_handle_scroll( + &hidpp->vertical_wheel_counter, v); input_sync(mydata->input); } @@ -2426,6 +2636,8 @@ static void m560_populate_input(struct hidpp_device *hidpp, __set_bit(REL_Y, mydata->input->relbit); __set_bit(REL_WHEEL, mydata->input->relbit); __set_bit(REL_HWHEEL, mydata->input->relbit); + __set_bit(REL_WHEEL_HI_RES, mydata->input->relbit); + __set_bit(REL_HWHEEL_HI_RES, mydata->input->relbit); } static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -2528,6 +2740,37 @@ static int g920_get_config(struct hidpp_device *hidpp) } /* -------------------------------------------------------------------------- */ +/* High-resolution scroll wheels */ +/* -------------------------------------------------------------------------- */ + +static int hi_res_scroll_enable(struct hidpp_device *hidpp) +{ + int ret; + u8 multiplier = 1; + + if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2121) { + ret = hidpp_hrw_set_wheel_mode(hidpp, false, true, false); + if (ret == 0) + ret = hidpp_hrw_get_wheel_capability(hidpp, &multiplier); + } else if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2120) { + ret = hidpp_hrs_set_highres_scrolling_mode(hidpp, true, + &multiplier); + } else /* if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_1P0) */ { + ret = hidpp10_enable_scrolling_acceleration(hidpp); + multiplier = 8; + } + if (ret) + return ret; + + if (multiplier == 0) + multiplier = 1; + + hidpp->vertical_wheel_counter.wheel_multiplier = multiplier; + hid_info(hidpp->hid_dev, "multiplier = %d\n", multiplier); + return 0; +} + +/* -------------------------------------------------------------------------- */ /* Generic HID++ devices */ /* -------------------------------------------------------------------------- */ @@ -2572,6 +2815,9 @@ static void hidpp_populate_input(struct hidpp_device *hidpp, wtp_populate_input(hidpp, input, origin_is_hid_core); else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) m560_populate_input(hidpp, input, origin_is_hid_core); + + if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) + hidpp->vertical_wheel_counter.dev = input; } static int hidpp_input_configured(struct hid_device *hdev, @@ -2690,6 +2936,27 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report, return 0; } +static int hidpp_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + /* This function will only be called for scroll events, due to the + * restriction imposed in hidpp_usages. + */ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + struct hidpp_scroll_counter *counter = &hidpp->vertical_wheel_counter; + /* A scroll event may occur before the multiplier has been retrieved or + * the input device set, or high-res scroll enabling may fail. In such + * cases we must return early (falling back to default behaviour) to + * avoid a crash in hidpp_scroll_counter_handle_scroll. + */ + if (!(hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) || value == 0 + || counter->dev == NULL || counter->wheel_multiplier == 0) + return 0; + + hidpp_scroll_counter_handle_scroll(counter, value); + return 1; +} + static int hidpp_initialize_battery(struct hidpp_device *hidpp) { static atomic_t battery_no = ATOMIC_INIT(0); @@ -2901,6 +3168,9 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) if (hidpp->battery.ps) power_supply_changed(hidpp->battery.ps); + if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) + hi_res_scroll_enable(hidpp); + if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input) /* if the input nodes are already created, we can stop now */ return; @@ -3086,35 +3356,63 @@ static void hidpp_remove(struct hid_device *hdev) mutex_destroy(&hidpp->send_mutex); } +#define LDJ_DEVICE(product) \ + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, \ + USB_VENDOR_ID_LOGITECH, (product)) + static const struct hid_device_id hidpp_devices[] = { { /* wireless touchpad */ - HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, - USB_VENDOR_ID_LOGITECH, 0x4011), + LDJ_DEVICE(0x4011), .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS }, { /* wireless touchpad T650 */ - HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, - USB_VENDOR_ID_LOGITECH, 0x4101), + LDJ_DEVICE(0x4101), .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT }, { /* wireless touchpad T651 */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651), .driver_data = HIDPP_QUIRK_CLASS_WTP }, + { /* Mouse Logitech Anywhere MX */ + LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, + { /* Mouse Logitech Cube */ + LDJ_DEVICE(0x4010), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 }, + { /* Mouse Logitech M335 */ + LDJ_DEVICE(0x4050), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { /* Mouse Logitech M515 */ + LDJ_DEVICE(0x4007), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 }, { /* Mouse logitech M560 */ - HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, - USB_VENDOR_ID_LOGITECH, 0x402d), - .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 }, + LDJ_DEVICE(0x402d), + .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 + | HIDPP_QUIRK_HI_RES_SCROLL_X2120 }, + { /* Mouse Logitech M705 (firmware RQM17) */ + LDJ_DEVICE(0x101b), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, + { /* Mouse Logitech M705 (firmware RQM67) */ + LDJ_DEVICE(0x406d), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { /* Mouse Logitech M720 */ + LDJ_DEVICE(0x405e), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { /* Mouse Logitech MX Anywhere 2 */ + LDJ_DEVICE(0x404a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { LDJ_DEVICE(0xb013), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { LDJ_DEVICE(0xb018), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { LDJ_DEVICE(0xb01f), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { /* Mouse Logitech MX Anywhere 2S */ + LDJ_DEVICE(0x406a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { /* Mouse Logitech MX Master */ + LDJ_DEVICE(0x4041), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { LDJ_DEVICE(0x4060), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { LDJ_DEVICE(0x4071), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { /* Mouse Logitech MX Master 2S */ + LDJ_DEVICE(0x4069), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { /* Mouse Logitech Performance MX */ + LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, { /* Keyboard logitech K400 */ - HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, - USB_VENDOR_ID_LOGITECH, 0x4024), + LDJ_DEVICE(0x4024), .driver_data = HIDPP_QUIRK_CLASS_K400 }, { /* Solar Keyboard Logitech K750 */ - HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, - USB_VENDOR_ID_LOGITECH, 0x4002), + LDJ_DEVICE(0x4002), .driver_data = HIDPP_QUIRK_CLASS_K750 }, - { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, - USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, + { LDJ_DEVICE(HID_ANY_ID) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL), .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS}, @@ -3123,12 +3421,19 @@ static const struct hid_device_id hidpp_devices[] = { MODULE_DEVICE_TABLE(hid, hidpp_devices); +static const struct hid_usage_id hidpp_usages[] = { + { HID_GD_WHEEL, EV_REL, REL_WHEEL_HI_RES }, + { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} +}; + static struct hid_driver hidpp_driver = { .name = "logitech-hidpp-device", .id_table = hidpp_devices, .probe = hidpp_probe, .remove = hidpp_remove, .raw_event = hidpp_raw_event, + .usage_table = hidpp_usages, + .event = hidpp_event, .input_configured = hidpp_input_configured, .input_mapping = hidpp_input_mapping, .input_mapped = hidpp_input_mapped, diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 4a44e48e08b2..9fc51eff1079 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -107,8 +107,6 @@ out: /* * The first byte of the report buffer is expected to be a report number. - * - * This function is to be called with the minors_lock mutex held. */ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type) { @@ -117,6 +115,8 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, __u8 *buf; int ret = 0; + lockdep_assert_held(&minors_lock); + if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { ret = -ENODEV; goto out; @@ -181,8 +181,6 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t * of buffer is the report number to request, or 0x0 if the defice does not * use numbered reports. The report_type parameter can be HID_FEATURE_REPORT * or HID_INPUT_REPORT. - * - * This function is to be called with the minors_lock mutex held. */ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type) { @@ -192,6 +190,8 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t int ret = 0, len; unsigned char report_number; + lockdep_assert_held(&minors_lock); + if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { ret = -ENODEV; goto out; diff --git a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c index 89f2976f9c53..fd1b6eea6d2f 100644 --- a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c +++ b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c @@ -346,6 +346,14 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = { }, .driver_data = (void *)&sipodev_desc }, + { + .ident = "Odys Winbook 13", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AXDIA International GmbH"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "WINBOOK 13"), + }, + .driver_data = (void *)&sipodev_desc + }, { } /* Terminate list */ }; diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 8793cc49f855..a6e1ee744f4d 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -117,6 +117,7 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int ret; struct ish_hw *hw; + unsigned long irq_flag = 0; struct ishtp_device *ishtp; struct device *dev = &pdev->dev; @@ -156,8 +157,12 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3; /* request and enable interrupt */ + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (!pdev->msi_enabled && !pdev->msix_enabled) + irq_flag = IRQF_SHARED; + ret = devm_request_irq(dev, pdev->irq, ish_irq_handler, - IRQF_SHARED, KBUILD_MODNAME, ishtp); + irq_flag, KBUILD_MODNAME, ishtp); if (ret) { dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq); return ret; diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.c b/drivers/hid/intel-ish-hid/ishtp-hid.c index cd23903ddcf1..e918d78e541c 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid.c +++ b/drivers/hid/intel-ish-hid/ishtp-hid.c @@ -222,7 +222,7 @@ int ishtp_hid_probe(unsigned int cur_hid_dev, err_hid_device: kfree(hid_data); err_hid_data: - kfree(hid); + hid_destroy_device(hid); return rv; } diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c index 51d34959709b..bf564391091f 100644 --- a/drivers/i2c/busses/i2c-axxia.c +++ b/drivers/i2c/busses/i2c-axxia.c @@ -12,6 +12,7 @@ */ #include <linux/clk.h> #include <linux/clkdev.h> +#include <linux/delay.h> #include <linux/err.h> #include <linux/i2c.h> #include <linux/init.h> @@ -25,6 +26,7 @@ #define I2C_XFER_TIMEOUT (msecs_to_jiffies(250)) #define I2C_STOP_TIMEOUT (msecs_to_jiffies(100)) #define FIFO_SIZE 8 +#define SEQ_LEN 2 #define GLOBAL_CONTROL 0x00 #define GLOBAL_MST_EN BIT(0) @@ -51,6 +53,7 @@ #define CMD_BUSY (1<<3) #define CMD_MANUAL (0x00 | CMD_BUSY) #define CMD_AUTO (0x01 | CMD_BUSY) +#define CMD_SEQUENCE (0x02 | CMD_BUSY) #define MST_RX_XFER 0x2c #define MST_TX_XFER 0x30 #define MST_ADDR_1 0x34 @@ -87,7 +90,9 @@ * axxia_i2c_dev - I2C device context * @base: pointer to register struct * @msg: pointer to current message - * @msg_xfrd: number of bytes transferred in msg + * @msg_r: pointer to current read message (sequence transfer) + * @msg_xfrd: number of bytes transferred in tx_fifo + * @msg_xfrd_r: number of bytes transferred in rx_fifo * @msg_err: error code for completed message * @msg_complete: xfer completion object * @dev: device reference @@ -98,7 +103,9 @@ struct axxia_i2c_dev { void __iomem *base; struct i2c_msg *msg; + struct i2c_msg *msg_r; size_t msg_xfrd; + size_t msg_xfrd_r; int msg_err; struct completion msg_complete; struct device *dev; @@ -227,14 +234,14 @@ static int i2c_m_recv_len(const struct i2c_msg *msg) */ static int axxia_i2c_empty_rx_fifo(struct axxia_i2c_dev *idev) { - struct i2c_msg *msg = idev->msg; + struct i2c_msg *msg = idev->msg_r; size_t rx_fifo_avail = readl(idev->base + MST_RX_FIFO); - int bytes_to_transfer = min(rx_fifo_avail, msg->len - idev->msg_xfrd); + int bytes_to_transfer = min(rx_fifo_avail, msg->len - idev->msg_xfrd_r); while (bytes_to_transfer-- > 0) { int c = readl(idev->base + MST_DATA); - if (idev->msg_xfrd == 0 && i2c_m_recv_len(msg)) { + if (idev->msg_xfrd_r == 0 && i2c_m_recv_len(msg)) { /* * Check length byte for SMBus block read */ @@ -247,7 +254,7 @@ static int axxia_i2c_empty_rx_fifo(struct axxia_i2c_dev *idev) msg->len = 1 + c; writel(msg->len, idev->base + MST_RX_XFER); } - msg->buf[idev->msg_xfrd++] = c; + msg->buf[idev->msg_xfrd_r++] = c; } return 0; @@ -287,7 +294,7 @@ static irqreturn_t axxia_i2c_isr(int irq, void *_dev) } /* RX FIFO needs service? */ - if (i2c_m_rd(idev->msg) && (status & MST_STATUS_RFL)) + if (i2c_m_rd(idev->msg_r) && (status & MST_STATUS_RFL)) axxia_i2c_empty_rx_fifo(idev); /* TX FIFO needs service? */ @@ -296,22 +303,7 @@ static irqreturn_t axxia_i2c_isr(int irq, void *_dev) i2c_int_disable(idev, MST_STATUS_TFL); } - if (status & MST_STATUS_SCC) { - /* Stop completed */ - i2c_int_disable(idev, ~MST_STATUS_TSS); - complete(&idev->msg_complete); - } else if (status & MST_STATUS_SNS) { - /* Transfer done */ - i2c_int_disable(idev, ~MST_STATUS_TSS); - if (i2c_m_rd(idev->msg) && idev->msg_xfrd < idev->msg->len) - axxia_i2c_empty_rx_fifo(idev); - complete(&idev->msg_complete); - } else if (status & MST_STATUS_TSS) { - /* Transfer timeout */ - idev->msg_err = -ETIMEDOUT; - i2c_int_disable(idev, ~MST_STATUS_TSS); - complete(&idev->msg_complete); - } else if (unlikely(status & MST_STATUS_ERR)) { + if (unlikely(status & MST_STATUS_ERR)) { /* Transfer error */ i2c_int_disable(idev, ~0); if (status & MST_STATUS_AL) @@ -328,6 +320,24 @@ static irqreturn_t axxia_i2c_isr(int irq, void *_dev) readl(idev->base + MST_TX_BYTES_XFRD), readl(idev->base + MST_TX_XFER)); complete(&idev->msg_complete); + } else if (status & MST_STATUS_SCC) { + /* Stop completed */ + i2c_int_disable(idev, ~MST_STATUS_TSS); + complete(&idev->msg_complete); + } else if (status & MST_STATUS_SNS) { + /* Transfer done */ + i2c_int_disable(idev, ~MST_STATUS_TSS); + if (i2c_m_rd(idev->msg_r) && idev->msg_xfrd_r < idev->msg_r->len) + axxia_i2c_empty_rx_fifo(idev); + complete(&idev->msg_complete); + } else if (status & MST_STATUS_SS) { + /* Auto/Sequence transfer done */ + complete(&idev->msg_complete); + } else if (status & MST_STATUS_TSS) { + /* Transfer timeout */ + idev->msg_err = -ETIMEDOUT; + i2c_int_disable(idev, ~MST_STATUS_TSS); + complete(&idev->msg_complete); } out: @@ -337,17 +347,9 @@ out: return IRQ_HANDLED; } -static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) +static void axxia_i2c_set_addr(struct axxia_i2c_dev *idev, struct i2c_msg *msg) { - u32 int_mask = MST_STATUS_ERR | MST_STATUS_SNS; - u32 rx_xfer, tx_xfer; u32 addr_1, addr_2; - unsigned long time_left; - unsigned int wt_value; - - idev->msg = msg; - idev->msg_xfrd = 0; - reinit_completion(&idev->msg_complete); if (i2c_m_ten(msg)) { /* 10-bit address @@ -367,6 +369,90 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) addr_2 = 0; } + writel(addr_1, idev->base + MST_ADDR_1); + writel(addr_2, idev->base + MST_ADDR_2); +} + +/* The NAK interrupt will be sent _before_ issuing STOP command + * so the controller might still be busy processing it. No + * interrupt will be sent at the end so we have to poll for it + */ +static int axxia_i2c_handle_seq_nak(struct axxia_i2c_dev *idev) +{ + unsigned long timeout = jiffies + I2C_XFER_TIMEOUT; + + do { + if ((readl(idev->base + MST_COMMAND) & CMD_BUSY) == 0) + return 0; + usleep_range(1, 100); + } while (time_before(jiffies, timeout)); + + return -ETIMEDOUT; +} + +static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[]) +{ + u32 int_mask = MST_STATUS_ERR | MST_STATUS_SS | MST_STATUS_RFL; + u32 rlen = i2c_m_recv_len(&msgs[1]) ? I2C_SMBUS_BLOCK_MAX : msgs[1].len; + unsigned long time_left; + + axxia_i2c_set_addr(idev, &msgs[0]); + + writel(msgs[0].len, idev->base + MST_TX_XFER); + writel(rlen, idev->base + MST_RX_XFER); + + idev->msg = &msgs[0]; + idev->msg_r = &msgs[1]; + idev->msg_xfrd = 0; + idev->msg_xfrd_r = 0; + axxia_i2c_fill_tx_fifo(idev); + + writel(CMD_SEQUENCE, idev->base + MST_COMMAND); + + reinit_completion(&idev->msg_complete); + i2c_int_enable(idev, int_mask); + + time_left = wait_for_completion_timeout(&idev->msg_complete, + I2C_XFER_TIMEOUT); + + i2c_int_disable(idev, int_mask); + + axxia_i2c_empty_rx_fifo(idev); + + if (idev->msg_err == -ENXIO) { + if (axxia_i2c_handle_seq_nak(idev)) + axxia_i2c_init(idev); + } else if (readl(idev->base + MST_COMMAND) & CMD_BUSY) { + dev_warn(idev->dev, "busy after xfer\n"); + } + + if (time_left == 0) { + idev->msg_err = -ETIMEDOUT; + i2c_recover_bus(&idev->adapter); + axxia_i2c_init(idev); + } + + if (unlikely(idev->msg_err) && idev->msg_err != -ENXIO) + axxia_i2c_init(idev); + + return idev->msg_err; +} + +static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) +{ + u32 int_mask = MST_STATUS_ERR | MST_STATUS_SNS; + u32 rx_xfer, tx_xfer; + unsigned long time_left; + unsigned int wt_value; + + idev->msg = msg; + idev->msg_r = msg; + idev->msg_xfrd = 0; + idev->msg_xfrd_r = 0; + reinit_completion(&idev->msg_complete); + + axxia_i2c_set_addr(idev, msg); + if (i2c_m_rd(msg)) { /* I2C read transfer */ rx_xfer = i2c_m_recv_len(msg) ? I2C_SMBUS_BLOCK_MAX : msg->len; @@ -379,8 +465,6 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) writel(rx_xfer, idev->base + MST_RX_XFER); writel(tx_xfer, idev->base + MST_TX_XFER); - writel(addr_1, idev->base + MST_ADDR_1); - writel(addr_2, idev->base + MST_ADDR_2); if (i2c_m_rd(msg)) int_mask |= MST_STATUS_RFL; @@ -445,6 +529,18 @@ static int axxia_i2c_stop(struct axxia_i2c_dev *idev) return 0; } +/* This function checks if the msgs[] array contains messages compatible with + * Sequence mode of operation. This mode assumes there will be exactly one + * write of non-zero length followed by exactly one read of non-zero length, + * both targeted at the same client device. + */ +static bool axxia_i2c_sequence_ok(struct i2c_msg msgs[], int num) +{ + return num == SEQ_LEN && !i2c_m_rd(&msgs[0]) && i2c_m_rd(&msgs[1]) && + msgs[0].len > 0 && msgs[0].len <= FIFO_SIZE && + msgs[1].len > 0 && msgs[0].addr == msgs[1].addr; +} + static int axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { @@ -453,6 +549,12 @@ axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) int ret = 0; idev->msg_err = 0; + + if (axxia_i2c_sequence_ok(msgs, num)) { + ret = axxia_i2c_xfer_seq(idev, msgs); + return ret ? : SEQ_LEN; + } + i2c_int_enable(idev, MST_STATUS_TSS); for (i = 0; ret == 0 && i < num; ++i) diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index 44deae78913e..ec6e69aa3a8e 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -1,14 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * BCM2835 master mode driver - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/clk.h> diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c index eb76b76f4754..82bcd9a78759 100644 --- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c +++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c @@ -1,13 +1,7 @@ -/* - * Copyright (C) 2013 Google, Inc - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Expose an I2C passthrough to the ChromeOS EC. - */ +// SPDX-License-Identifier: GPL-2.0+ +// Expose an I2C passthrough to the ChromeOS EC. +// +// Copyright (C) 2013 Google, Inc. #include <linux/module.h> #include <linux/i2c.h> diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index 6f6e1dfe7cce..d78023d42a35 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -437,7 +437,7 @@ static int iic_wait_for_tc(struct ibm_iic_private* dev){ break; } - if (unlikely(signal_pending(current))){ + if (signal_pending(current)){ DBG("%d: poll interrupted\n", dev->idx); ret = -ERESTARTSYS; break; diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index c406700789e1..fa9ad53845d9 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1090,7 +1090,8 @@ static int i2c_imx_probe(struct platform_device *pdev) /* Get I2C clock */ i2c_imx->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(i2c_imx->clk)) { - dev_err(&pdev->dev, "can't get I2C clock\n"); + if (PTR_ERR(i2c_imx->clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "can't get I2C clock\n"); return PTR_ERR(i2c_imx->clk); } diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c index 0d1c3ec8cb40..02d23edb2fb1 100644 --- a/drivers/i2c/busses/i2c-ismt.c +++ b/drivers/i2c/busses/i2c-ismt.c @@ -75,6 +75,7 @@ /* PCI DIDs for the Intel SMBus Message Transport (SMT) Devices */ #define PCI_DEVICE_ID_INTEL_S1200_SMT0 0x0c59 #define PCI_DEVICE_ID_INTEL_S1200_SMT1 0x0c5a +#define PCI_DEVICE_ID_INTEL_CDF_SMT 0x18ac #define PCI_DEVICE_ID_INTEL_DNV_SMT 0x19ac #define PCI_DEVICE_ID_INTEL_AVOTON_SMT 0x1f15 @@ -181,6 +182,7 @@ struct ismt_priv { static const struct pci_device_id ismt_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT0) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CDF_SMT) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DNV_SMT) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMT) }, { 0, } diff --git a/drivers/i2c/busses/i2c-owl.c b/drivers/i2c/busses/i2c-owl.c index 96b4572e6d9c..b6b5a495118b 100644 --- a/drivers/i2c/busses/i2c-owl.c +++ b/drivers/i2c/busses/i2c-owl.c @@ -475,6 +475,7 @@ disable_clk: } static const struct of_device_id owl_i2c_of_match[] = { + { .compatible = "actions,s700-i2c" }, { .compatible = "actions,s900-i2c" }, { /* sentinel */ } }; diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index f6f4ed8afc93..281113c28314 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -229,9 +229,9 @@ static u32 i2c_powermac_get_addr(struct i2c_adapter *adap, return (be32_to_cpup(prop) & 0xff) >> 1; /* Now handle some devices with missing "reg" properties */ - if (!strcmp(node->name, "cereal")) + if (of_node_name_eq(node, "cereal")) return 0x60; - else if (!strcmp(node->name, "deq")) + else if (of_node_name_eq(node, "deq")) return 0x34; dev_warn(&adap->dev, "No i2c address for %pOF\n", node); @@ -304,7 +304,7 @@ static bool i2c_powermac_get_type(struct i2c_adapter *adap, } /* Now look for known workarounds */ - if (!strcmp(node->name, "deq")) { + if (of_node_name_eq(node, "deq")) { /* Apple uses address 0x34 for TAS3001 and 0x35 for TAS3004 */ if (addr == 0x34) { snprintf(type, type_size, "MAC,tas3001"); @@ -331,7 +331,7 @@ static void i2c_powermac_register_devices(struct i2c_adapter *adap, * case we skip this function completely as the device-tree will * not contain anything useful. */ - if (!strcmp(adap->dev.of_node->name, "via-pmu")) + if (of_node_name_eq(adap->dev.of_node, "via-pmu")) return; for_each_child_of_node(adap->dev.of_node, node) { diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index a7a7a9c3bc7c..a64f2ff3cb49 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -800,6 +800,7 @@ static const struct sh_mobile_dt_config r8a7740_dt_config = { static const struct of_device_id sh_mobile_i2c_dt_ids[] = { { .compatible = "renesas,iic-r8a73a4", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config }, + { .compatible = "renesas,iic-r8a774c0", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7790", .data = &v2_freq_calc_dt_config }, { .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config }, @@ -808,6 +809,7 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = { { .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7795", .data = &fast_clock_dt_config }, { .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a77990", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config }, { .compatible = "renesas,rmobile-iic", .data = &default_dt_config }, {}, diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 62d023e737d9..13e1213561d4 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -21,12 +21,16 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/iopoll.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/pinctrl/consumer.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> #include <linux/reset.h> #include <linux/slab.h> @@ -163,6 +167,8 @@ #define STM32F7_SCLH_MAX BIT(8) #define STM32F7_SCLL_MAX BIT(8) +#define STM32F7_AUTOSUSPEND_DELAY (HZ / 100) + /** * struct stm32f7_i2c_spec - private i2c specification timing * @rate: I2C bus speed (Hz) @@ -276,6 +282,7 @@ struct stm32f7_i2c_msg { * slave) * @dma: dma data * @use_dma: boolean to know if dma is used in the current transfer + * @regmap: holds SYSCFG phandle for Fast Mode Plus bits */ struct stm32f7_i2c_dev { struct i2c_adapter adap; @@ -296,6 +303,7 @@ struct stm32f7_i2c_dev { bool master_mode; struct stm32_i2c_dma *dma; bool use_dma; + struct regmap *regmap; }; /** @@ -1545,15 +1553,13 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap, i2c_dev->msg_id = 0; f7_msg->smbus = false; - ret = clk_enable(i2c_dev->clk); - if (ret) { - dev_err(i2c_dev->dev, "Failed to enable clock\n"); + ret = pm_runtime_get_sync(i2c_dev->dev); + if (ret < 0) return ret; - } ret = stm32f7_i2c_wait_free_bus(i2c_dev); if (ret) - goto clk_free; + goto pm_free; stm32f7_i2c_xfer_msg(i2c_dev, msgs); @@ -1569,8 +1575,9 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap, ret = -ETIMEDOUT; } -clk_free: - clk_disable(i2c_dev->clk); +pm_free: + pm_runtime_mark_last_busy(i2c_dev->dev); + pm_runtime_put_autosuspend(i2c_dev->dev); return (ret < 0) ? ret : num; } @@ -1592,39 +1599,37 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, f7_msg->read_write = read_write; f7_msg->smbus = true; - ret = clk_enable(i2c_dev->clk); - if (ret) { - dev_err(i2c_dev->dev, "Failed to enable clock\n"); + ret = pm_runtime_get_sync(dev); + if (ret < 0) return ret; - } ret = stm32f7_i2c_wait_free_bus(i2c_dev); if (ret) - goto clk_free; + goto pm_free; ret = stm32f7_i2c_smbus_xfer_msg(i2c_dev, flags, command, data); if (ret) - goto clk_free; + goto pm_free; timeout = wait_for_completion_timeout(&i2c_dev->complete, i2c_dev->adap.timeout); ret = f7_msg->result; if (ret) - goto clk_free; + goto pm_free; if (!timeout) { dev_dbg(dev, "Access to slave 0x%x timed out\n", f7_msg->addr); if (i2c_dev->use_dma) dmaengine_terminate_all(dma->chan_using); ret = -ETIMEDOUT; - goto clk_free; + goto pm_free; } /* Check PEC */ if ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && read_write) { ret = stm32f7_i2c_smbus_check_pec(i2c_dev); if (ret) - goto clk_free; + goto pm_free; } if (read_write && size != I2C_SMBUS_QUICK) { @@ -1649,8 +1654,9 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, } } -clk_free: - clk_disable(i2c_dev->clk); +pm_free: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); return ret; } @@ -1676,13 +1682,9 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) if (ret) return ret; - if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) { - ret = clk_enable(i2c_dev->clk); - if (ret) { - dev_err(dev, "Failed to enable clock\n"); - return ret; - } - } + ret = pm_runtime_get_sync(dev); + if (ret < 0) + return ret; if (id == 0) { /* Configure Own Address 1 */ @@ -1703,7 +1705,7 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) oar2 &= ~STM32F7_I2C_OAR2_MASK; if (slave->flags & I2C_CLIENT_TEN) { ret = -EOPNOTSUPP; - goto exit; + goto pm_free; } oar2 |= STM32F7_I2C_OAR2_OA2_7(slave->addr); @@ -1712,7 +1714,7 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2); } else { ret = -ENODEV; - goto exit; + goto pm_free; } /* Enable ACK */ @@ -1723,11 +1725,10 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) STM32F7_I2C_CR1_PE; stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask); - return 0; - -exit: - if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) - clk_disable(i2c_dev->clk); + ret = 0; +pm_free: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); return ret; } @@ -1745,6 +1746,10 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave) WARN_ON(!i2c_dev->slave[id]); + ret = pm_runtime_get_sync(i2c_dev->dev); + if (ret < 0) + return ret; + if (id == 0) { mask = STM32F7_I2C_OAR1_OA1EN; stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask); @@ -1755,14 +1760,39 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave) i2c_dev->slave[id] = NULL; - if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) { + if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK); - clk_disable(i2c_dev->clk); - } + + pm_runtime_mark_last_busy(i2c_dev->dev); + pm_runtime_put_autosuspend(i2c_dev->dev); return 0; } +static int stm32f7_i2c_setup_fm_plus_bits(struct platform_device *pdev, + struct stm32f7_i2c_dev *i2c_dev) +{ + struct device_node *np = pdev->dev.of_node; + int ret; + u32 reg, mask; + + i2c_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg-fmp"); + if (IS_ERR(i2c_dev->regmap)) { + /* Optional */ + return 0; + } + + ret = of_property_read_u32_index(np, "st,syscfg-fmp", 1, ®); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, "st,syscfg-fmp", 2, &mask); + if (ret) + return ret; + + return regmap_update_bits(i2c_dev->regmap, reg, mask, mask); +} + static u32 stm32f7_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE | @@ -1819,6 +1849,7 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Error: Missing controller clock\n"); return PTR_ERR(i2c_dev->clk); } + ret = clk_prepare_enable(i2c_dev->clk); if (ret) { dev_err(&pdev->dev, "Failed to prepare_enable clock\n"); @@ -1828,12 +1859,16 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) i2c_dev->speed = STM32_I2C_SPEED_STANDARD; ret = device_property_read_u32(&pdev->dev, "clock-frequency", &clk_rate); - if (!ret && clk_rate >= 1000000) + if (!ret && clk_rate >= 1000000) { i2c_dev->speed = STM32_I2C_SPEED_FAST_PLUS; - else if (!ret && clk_rate >= 400000) + ret = stm32f7_i2c_setup_fm_plus_bits(pdev, i2c_dev); + if (ret) + goto clk_free; + } else if (!ret && clk_rate >= 400000) { i2c_dev->speed = STM32_I2C_SPEED_FAST; - else if (!ret && clk_rate >= 100000) + } else if (!ret && clk_rate >= 100000) { i2c_dev->speed = STM32_I2C_SPEED_STANDARD; + } rst = devm_reset_control_get(&pdev->dev, NULL); if (IS_ERR(rst)) { @@ -1888,8 +1923,6 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) if (ret) goto clk_free; - stm32f7_i2c_hw_config(i2c_dev); - adap = &i2c_dev->adap; i2c_set_adapdata(adap, i2c_dev); snprintf(adap->name, sizeof(adap->name), "STM32F7 I2C(%pa)", @@ -1908,18 +1941,35 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) STM32F7_I2C_TXDR, STM32F7_I2C_RXDR); - ret = i2c_add_adapter(adap); - if (ret) - goto clk_free; - platform_set_drvdata(pdev, i2c_dev); - clk_disable(i2c_dev->clk); + pm_runtime_set_autosuspend_delay(i2c_dev->dev, + STM32F7_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(i2c_dev->dev); + pm_runtime_set_active(i2c_dev->dev); + pm_runtime_enable(i2c_dev->dev); + + pm_runtime_get_noresume(&pdev->dev); + + stm32f7_i2c_hw_config(i2c_dev); + + ret = i2c_add_adapter(adap); + if (ret) + goto pm_disable; dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr); + pm_runtime_mark_last_busy(i2c_dev->dev); + pm_runtime_put_autosuspend(i2c_dev->dev); + return 0; +pm_disable: + pm_runtime_put_noidle(i2c_dev->dev); + pm_runtime_disable(i2c_dev->dev); + pm_runtime_set_suspended(i2c_dev->dev); + pm_runtime_dont_use_autosuspend(i2c_dev->dev); + clk_free: clk_disable_unprepare(i2c_dev->clk); @@ -1936,11 +1986,50 @@ static int stm32f7_i2c_remove(struct platform_device *pdev) } i2c_del_adapter(&i2c_dev->adap); + pm_runtime_get_sync(i2c_dev->dev); - clk_unprepare(i2c_dev->clk); + clk_disable_unprepare(i2c_dev->clk); + + pm_runtime_put_noidle(i2c_dev->dev); + pm_runtime_disable(i2c_dev->dev); + pm_runtime_set_suspended(i2c_dev->dev); + pm_runtime_dont_use_autosuspend(i2c_dev->dev); + + return 0; +} + +#ifdef CONFIG_PM +static int stm32f7_i2c_runtime_suspend(struct device *dev) +{ + struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); + + if (!stm32f7_i2c_is_slave_registered(i2c_dev)) + clk_disable_unprepare(i2c_dev->clk); + + return 0; +} + +static int stm32f7_i2c_runtime_resume(struct device *dev) +{ + struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); + int ret; + + if (!stm32f7_i2c_is_slave_registered(i2c_dev)) { + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) { + dev_err(dev, "failed to prepare_enable clock\n"); + return ret; + } + } return 0; } +#endif + +static const struct dev_pm_ops stm32f7_i2c_pm_ops = { + SET_RUNTIME_PM_OPS(stm32f7_i2c_runtime_suspend, + stm32f7_i2c_runtime_resume, NULL) +}; static const struct of_device_id stm32f7_i2c_match[] = { { .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup}, @@ -1952,6 +2041,7 @@ static struct platform_driver stm32f7_i2c_driver = { .driver = { .name = "stm32f7-i2c", .of_match_table = stm32f7_i2c_match, + .pm = &stm32f7_i2c_pm_ops, }, .probe = stm32f7_i2c_probe, .remove = stm32f7_i2c_remove, diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 437294ea2f0a..e417ebf7628c 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -1,18 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * drivers/i2c/busses/i2c-tegra.c * * Copyright (C) 2010 Google, Inc. * Author: Colin Cross <ccross@android.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/kernel.h> @@ -145,8 +136,8 @@ enum msg_end_type { * @has_continue_xfer_support: Continue transfer supports. * @has_per_pkt_xfer_complete_irq: Has enable/disable capability for transfer * complete interrupt per packet basis. - * @has_single_clk_source: The i2c controller has single clock source. Tegra30 - * and earlier Socs has two clock sources i.e. div-clk and + * @has_single_clk_source: The I2C controller has single clock source. Tegra30 + * and earlier SoCs have two clock sources i.e. div-clk and * fast-clk. * @has_config_load_reg: Has the config load register to load the new * configuration. @@ -154,8 +145,17 @@ enum msg_end_type { * @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is * applicable if there is no fast clock source i.e. single clock * source. + * @clk_divisor_fast_plus_mode: Clock divisor in fast mode plus. It is + * applicable if there is no fast clock source (i.e. single + * clock source). + * @has_multi_master_mode: The I2C controller supports running in single-master + * or multi-master mode. + * @has_slcg_override_reg: The I2C controller supports a register that + * overrides the second level clock gating. + * @has_mst_fifo: The I2C controller contains the new MST FIFO interface that + * provides additional features and allows for longer messages to + * be transferred in one go. */ - struct tegra_i2c_hw_feature { bool has_continue_xfer_support; bool has_per_pkt_xfer_complete_irq; @@ -170,22 +170,27 @@ struct tegra_i2c_hw_feature { }; /** - * struct tegra_i2c_dev - per device i2c context + * struct tegra_i2c_dev - per device I2C context * @dev: device reference for power management - * @hw: Tegra i2c hw feature. - * @adapter: core i2c layer adapter information - * @div_clk: clock reference for div clock of i2c controller. - * @fast_clk: clock reference for fast clock of i2c controller. + * @hw: Tegra I2C HW feature + * @adapter: core I2C layer adapter information + * @div_clk: clock reference for div clock of I2C controller + * @fast_clk: clock reference for fast clock of I2C controller + * @rst: reset control for the I2C controller * @base: ioremapped registers cookie - * @cont_id: i2c controller id, used for for packet header - * @irq: irq number of transfer complete interrupt - * @is_dvc: identifies the DVC i2c controller, has a different register layout + * @cont_id: I2C controller ID, used for packet header + * @irq: IRQ number of transfer complete interrupt + * @irq_disabled: used to track whether or not the interrupt is enabled + * @is_dvc: identifies the DVC I2C controller, has a different register layout * @msg_complete: transfer completion notifier * @msg_err: error code for completed message * @msg_buf: pointer to current message data * @msg_buf_remaining: size of unsent data in the message buffer * @msg_read: identifies read transfers - * @bus_clk_rate: current i2c bus clock rate + * @bus_clk_rate: current I2C bus clock rate + * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes + * @is_multimaster_mode: track if I2C controller is in multi-master mode + * @xfer_lock: lock to serialize transfer submission and processing */ struct tegra_i2c_dev { struct device *dev; @@ -608,11 +613,10 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) u32 status; const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; struct tegra_i2c_dev *i2c_dev = dev_id; - unsigned long flags; status = i2c_readl(i2c_dev, I2C_INT_STATUS); - spin_lock_irqsave(&i2c_dev->xfer_lock, flags); + spin_lock(&i2c_dev->xfer_lock); if (status == 0) { dev_warn(i2c_dev->dev, "irq status 0 %08x %08x %08x\n", i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS), @@ -670,7 +674,7 @@ err: complete(&i2c_dev->msg_complete); done: - spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags); + spin_unlock(&i2c_dev->xfer_lock); return IRQ_HANDLED; } diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 47ab34ee1a9d..8872453e26c0 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -1232,6 +1232,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) } while (0) SET_DEVICE_OP(dev_ops, add_gid); + SET_DEVICE_OP(dev_ops, advise_mr); SET_DEVICE_OP(dev_ops, alloc_dm); SET_DEVICE_OP(dev_ops, alloc_fmr); SET_DEVICE_OP(dev_ops, alloc_hw_stats); diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 9f9172eb1512..fb0007aa0c27 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -611,8 +611,7 @@ static ssize_t verify_hdr(struct ib_uverbs_cmd_hdr *hdr, if (hdr->out_words * 8 < method_elm->resp_size) return -ENOSPC; - if (!access_ok(VERIFY_WRITE, - u64_to_user_ptr(ex_hdr->response), + if (!access_ok(u64_to_user_ptr(ex_hdr->response), (hdr->out_words + ex_hdr->provider_out_words) * 8)) return -EFAULT; } else { diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.c b/drivers/infiniband/hw/bnxt_re/qplib_sp.c index be03b5738f71..efa0f2949dc7 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_sp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.c @@ -780,9 +780,8 @@ int bnxt_qplib_map_tc2cos(struct bnxt_qplib_res *res, u16 *cids) req.cos0 = cpu_to_le16(cids[0]); req.cos1 = cpu_to_le16(cids[1]); - bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, (void *)&resp, NULL, - 0); - return 0; + return bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, (void *)&resp, + NULL, 0); } int bnxt_qplib_get_roce_stats(struct bnxt_qplib_rcfw *rcfw, diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.c b/drivers/infiniband/hw/hfi1/user_exp_rcv.c index dbe7d14a5c76..0cd71ce7cc71 100644 --- a/drivers/infiniband/hw/hfi1/user_exp_rcv.c +++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.c @@ -232,7 +232,7 @@ static int pin_rcv_pages(struct hfi1_filedata *fd, struct tid_user_buf *tidbuf) } /* Verify that access is OK for the user buffer */ - if (!access_ok(VERIFY_WRITE, (void __user *)vaddr, + if (!access_ok((void __user *)vaddr, npages * PAGE_SIZE)) { dd_dev_err(dd, "Fail vaddr %p, %u pages, !access_ok\n", (void *)vaddr, npages); diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 1bd8c1b1dba1..fd6ea1f75085 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -73,8 +73,7 @@ static int destroy_mkey(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING /* Wait until all page fault handlers using the mr complete. */ - if (mr->umem && mr->umem->is_odp) - synchronize_srcu(&dev->mr_srcu); + synchronize_srcu(&dev->mr_srcu); #endif return err; @@ -238,9 +237,6 @@ static void remove_keys(struct mlx5_ib_dev *dev, int c, int num) { struct mlx5_mr_cache *cache = &dev->cache; struct mlx5_cache_ent *ent = &cache->ent[c]; -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - bool odp_mkey_exist = false; -#endif struct mlx5_ib_mr *tmp_mr; struct mlx5_ib_mr *mr; LIST_HEAD(del_list); @@ -253,10 +249,6 @@ static void remove_keys(struct mlx5_ib_dev *dev, int c, int num) break; } mr = list_first_entry(&ent->head, struct mlx5_ib_mr, list); -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - if (mr->umem && mr->umem->is_odp) - odp_mkey_exist = true; -#endif list_move(&mr->list, &del_list); ent->cur--; ent->size--; @@ -265,8 +257,7 @@ static void remove_keys(struct mlx5_ib_dev *dev, int c, int num) } #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - if (odp_mkey_exist) - synchronize_srcu(&dev->mr_srcu); + synchronize_srcu(&dev->mr_srcu); #endif list_for_each_entry_safe(mr, tmp_mr, &del_list, list) { @@ -581,7 +572,6 @@ static void clean_keys(struct mlx5_ib_dev *dev, int c) { struct mlx5_mr_cache *cache = &dev->cache; struct mlx5_cache_ent *ent = &cache->ent[c]; - bool odp_mkey_exist = false; struct mlx5_ib_mr *tmp_mr; struct mlx5_ib_mr *mr; LIST_HEAD(del_list); @@ -594,8 +584,6 @@ static void clean_keys(struct mlx5_ib_dev *dev, int c) break; } mr = list_first_entry(&ent->head, struct mlx5_ib_mr, list); - if (mr->umem && mr->umem->is_odp) - odp_mkey_exist = true; list_move(&mr->list, &del_list); ent->cur--; ent->size--; @@ -604,8 +592,7 @@ static void clean_keys(struct mlx5_ib_dev *dev, int c) } #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - if (odp_mkey_exist) - synchronize_srcu(&dev->mr_srcu); + synchronize_srcu(&dev->mr_srcu); #endif list_for_each_entry_safe(mr, tmp_mr, &del_list, list) { diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 9c94c1b9ec35..dd2ae640bc84 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -837,7 +837,8 @@ static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd, goto err_umem; } - uid = (attr->qp_type != IB_QPT_XRC_TGT) ? to_mpd(pd)->uid : 0; + uid = (attr->qp_type != IB_QPT_XRC_TGT && + attr->qp_type != IB_QPT_XRC_INI) ? to_mpd(pd)->uid : 0; MLX5_SET(create_qp_in, *in, uid, uid); pas = (__be64 *)MLX5_ADDR_OF(create_qp_in, *in, pas); if (ubuffer->umem) diff --git a/drivers/infiniband/hw/qedr/qedr_iw_cm.c b/drivers/infiniband/hw/qedr/qedr_iw_cm.c index 505fa3648762..93b16237b767 100644 --- a/drivers/infiniband/hw/qedr/qedr_iw_cm.c +++ b/drivers/infiniband/hw/qedr/qedr_iw_cm.c @@ -492,6 +492,8 @@ int qedr_iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) int i; qp = idr_find(&dev->qpidr.idr, conn_param->qpn); + if (unlikely(!qp)) + return -EINVAL; laddr = (struct sockaddr_in *)&cm_id->m_local_addr; raddr = (struct sockaddr_in *)&cm_id->m_remote_addr; diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c index 98e1ce14fa2a..78fa634de98a 100644 --- a/drivers/infiniband/hw/qib/qib_file_ops.c +++ b/drivers/infiniband/hw/qib/qib_file_ops.c @@ -343,7 +343,7 @@ static int qib_tid_update(struct qib_ctxtdata *rcd, struct file *fp, /* virtual address of first page in transfer */ vaddr = ti->tidvaddr; - if (!access_ok(VERIFY_WRITE, (void __user *) vaddr, + if (!access_ok((void __user *) vaddr, cnt * PAGE_SIZE)) { ret = -EFAULT; goto done; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index 6d35570092d6..78fa777c87b1 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -669,7 +669,6 @@ static void __ipoib_reap_ah(struct net_device *dev) { struct ipoib_dev_priv *priv = ipoib_priv(dev); struct ipoib_ah *ah, *tah; - LIST_HEAD(remove_list); unsigned long flags; netif_tx_lock_bh(dev); diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c index 0ff517d3c98f..a4ceb61c5b60 100644 --- a/drivers/isdn/capi/kcapi.c +++ b/drivers/isdn/capi/kcapi.c @@ -852,7 +852,7 @@ u16 capi20_get_manufacturer(u32 contr, u8 *buf) u16 ret; if (contr == 0) { - strlcpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); + strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); return CAPI_NOERROR; } @@ -860,7 +860,7 @@ u16 capi20_get_manufacturer(u32 contr, u8 *buf) ctr = get_capi_ctr_by_nr(contr); if (ctr && ctr->state == CAPI_CTR_RUNNING) { - strlcpy(buf, ctr->manu, CAPI_MANUFACTURER_LEN); + strncpy(buf, ctr->manu, CAPI_MANUFACTURER_LEN); ret = CAPI_NOERROR; } else ret = CAPI_REGNOTINSTALLED; diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c index 5b719b561860..81dd465afcf4 100644 --- a/drivers/isdn/hisax/hfc_pci.c +++ b/drivers/isdn/hisax/hfc_pci.c @@ -1169,11 +1169,13 @@ HFCPCI_l1hw(struct PStack *st, int pr, void *arg) if (cs->debug & L1_DEB_LAPD) debugl1(cs, "-> PH_REQUEST_PULL"); #endif + spin_lock_irqsave(&cs->lock, flags); if (!cs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + spin_unlock_irqrestore(&cs->lock, flags); break; case (HW_RESET | REQUEST): spin_lock_irqsave(&cs->lock, flags); diff --git a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c index ef0c2366cf59..400960cf04d5 100644 --- a/drivers/macintosh/ans-lcd.c +++ b/drivers/macintosh/ans-lcd.c @@ -64,7 +64,7 @@ anslcd_write( struct file * file, const char __user * buf, printk(KERN_DEBUG "LCD: write\n"); #endif - if (!access_ok(VERIFY_READ, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; mutex_lock(&anslcd_mutex); diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index ac0cf37d6239..21d532a78fa4 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -2188,7 +2188,7 @@ pmu_read(struct file *file, char __user *buf, if (count < 1 || !pp) return -EINVAL; - if (!access_ok(VERIFY_WRITE, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; spin_lock_irqsave(&pp->lock, flags); diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c index 3e02de02ffdd..8ec2525d8ef5 100644 --- a/drivers/media/pci/ivtv/ivtvfb.c +++ b/drivers/media/pci/ivtv/ivtvfb.c @@ -356,7 +356,7 @@ static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source, IVTVFB_WARN("ivtvfb_prep_frame: Count not a multiple of 4 (%d)\n", count); /* Check Source */ - if (!access_ok(VERIFY_READ, source + dest_offset, count)) { + if (!access_ok(source + dest_offset, count)) { IVTVFB_WARN("Invalid userspace pointer %p\n", source); IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source %p count %d\n", diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index fe4577a46869..73dac1d8d4f6 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -158,7 +158,7 @@ static int get_v4l2_window32(struct v4l2_window __user *p64, compat_caddr_t p; u32 clipcount; - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || copy_in_user(&p64->w, &p32->w, sizeof(p32->w)) || assign_in_user(&p64->field, &p32->field) || assign_in_user(&p64->chromakey, &p32->chromakey) || @@ -283,7 +283,7 @@ static int __bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size) static int bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size) { - if (!access_ok(VERIFY_READ, p32, sizeof(*p32))) + if (!access_ok(p32, sizeof(*p32))) return -EFAULT; return __bufsize_v4l2_format(p32, size); } @@ -335,7 +335,7 @@ static int get_v4l2_format32(struct v4l2_format __user *p64, struct v4l2_format32 __user *p32, void __user *aux_buf, u32 aux_space) { - if (!access_ok(VERIFY_READ, p32, sizeof(*p32))) + if (!access_ok(p32, sizeof(*p32))) return -EFAULT; return __get_v4l2_format32(p64, p32, aux_buf, aux_space); } @@ -343,7 +343,7 @@ static int get_v4l2_format32(struct v4l2_format __user *p64, static int bufsize_v4l2_create(struct v4l2_create_buffers32 __user *p32, u32 *size) { - if (!access_ok(VERIFY_READ, p32, sizeof(*p32))) + if (!access_ok(p32, sizeof(*p32))) return -EFAULT; return __bufsize_v4l2_format(&p32->format, size); } @@ -352,7 +352,7 @@ static int get_v4l2_create32(struct v4l2_create_buffers __user *p64, struct v4l2_create_buffers32 __user *p32, void __user *aux_buf, u32 aux_space) { - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || copy_in_user(p64, p32, offsetof(struct v4l2_create_buffers32, format))) return -EFAULT; @@ -404,7 +404,7 @@ static int __put_v4l2_format32(struct v4l2_format __user *p64, static int put_v4l2_format32(struct v4l2_format __user *p64, struct v4l2_format32 __user *p32) { - if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32))) + if (!access_ok(p32, sizeof(*p32))) return -EFAULT; return __put_v4l2_format32(p64, p32); } @@ -412,7 +412,7 @@ static int put_v4l2_format32(struct v4l2_format __user *p64, static int put_v4l2_create32(struct v4l2_create_buffers __user *p64, struct v4l2_create_buffers32 __user *p32) { - if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || copy_in_user(p32, p64, offsetof(struct v4l2_create_buffers32, format)) || assign_in_user(&p32->capabilities, &p64->capabilities) || @@ -434,7 +434,7 @@ static int get_v4l2_standard32(struct v4l2_standard __user *p64, struct v4l2_standard32 __user *p32) { /* other fields are not set by the user, nor used by the driver */ - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || assign_in_user(&p64->index, &p32->index)) return -EFAULT; return 0; @@ -443,7 +443,7 @@ static int get_v4l2_standard32(struct v4l2_standard __user *p64, static int put_v4l2_standard32(struct v4l2_standard __user *p64, struct v4l2_standard32 __user *p32) { - if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || assign_in_user(&p32->index, &p64->index) || assign_in_user(&p32->id, &p64->id) || copy_in_user(p32->name, p64->name, sizeof(p32->name)) || @@ -560,7 +560,7 @@ static int bufsize_v4l2_buffer(struct v4l2_buffer32 __user *p32, u32 *size) u32 type; u32 length; - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || get_user(type, &p32->type) || get_user(length, &p32->length)) return -EFAULT; @@ -593,7 +593,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *p64, compat_caddr_t p; int ret; - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || assign_in_user(&p64->index, &p32->index) || get_user(type, &p32->type) || put_user(type, &p64->type) || @@ -632,7 +632,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *p64, return -EFAULT; uplane32 = compat_ptr(p); - if (!access_ok(VERIFY_READ, uplane32, + if (!access_ok(uplane32, num_planes * sizeof(*uplane32))) return -EFAULT; @@ -691,7 +691,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer __user *p64, compat_caddr_t p; int ret; - if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || assign_in_user(&p32->index, &p64->index) || get_user(type, &p64->type) || put_user(type, &p32->type) || @@ -781,7 +781,7 @@ static int get_v4l2_framebuffer32(struct v4l2_framebuffer __user *p64, { compat_caddr_t tmp; - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || get_user(tmp, &p32->base) || put_user_force(compat_ptr(tmp), &p64->base) || assign_in_user(&p64->capability, &p32->capability) || @@ -796,7 +796,7 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer __user *p64, { void *base; - if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || get_user(base, &p64->base) || put_user(ptr_to_compat((void __user *)base), &p32->base) || assign_in_user(&p32->capability, &p64->capability) || @@ -893,7 +893,7 @@ static int bufsize_v4l2_ext_controls(struct v4l2_ext_controls32 __user *p32, { u32 count; - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || get_user(count, &p32->count)) return -EFAULT; if (count > V4L2_CID_MAX_CTRLS) @@ -913,7 +913,7 @@ static int get_v4l2_ext_controls32(struct file *file, u32 n; compat_caddr_t p; - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || assign_in_user(&p64->which, &p32->which) || get_user(count, &p32->count) || put_user(count, &p64->count) || @@ -929,7 +929,7 @@ static int get_v4l2_ext_controls32(struct file *file, if (get_user(p, &p32->controls)) return -EFAULT; ucontrols = compat_ptr(p); - if (!access_ok(VERIFY_READ, ucontrols, count * sizeof(*ucontrols))) + if (!access_ok(ucontrols, count * sizeof(*ucontrols))) return -EFAULT; if (aux_space < count * sizeof(*kcontrols)) return -EFAULT; @@ -979,7 +979,7 @@ static int put_v4l2_ext_controls32(struct file *file, * with __user causes smatch warnings, so instead declare it * without __user and cast it as a userspace pointer where needed. */ - if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || assign_in_user(&p32->which, &p64->which) || get_user(count, &p64->count) || put_user(count, &p32->count) || @@ -994,7 +994,7 @@ static int put_v4l2_ext_controls32(struct file *file, if (get_user(p, &p32->controls)) return -EFAULT; ucontrols = compat_ptr(p); - if (!access_ok(VERIFY_WRITE, ucontrols, count * sizeof(*ucontrols))) + if (!access_ok(ucontrols, count * sizeof(*ucontrols))) return -EFAULT; for (n = 0; n < count; n++) { @@ -1043,7 +1043,7 @@ struct v4l2_event32 { static int put_v4l2_event32(struct v4l2_event __user *p64, struct v4l2_event32 __user *p32) { - if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || assign_in_user(&p32->type, &p64->type) || copy_in_user(&p32->u, &p64->u, sizeof(p64->u)) || assign_in_user(&p32->pending, &p64->pending) || @@ -1069,7 +1069,7 @@ static int get_v4l2_edid32(struct v4l2_edid __user *p64, { compat_uptr_t tmp; - if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || assign_in_user(&p64->pad, &p32->pad) || assign_in_user(&p64->start_block, &p32->start_block) || assign_in_user_cast(&p64->blocks, &p32->blocks) || @@ -1085,7 +1085,7 @@ static int put_v4l2_edid32(struct v4l2_edid __user *p64, { void *edid; - if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + if (!access_ok(p32, sizeof(*p32)) || assign_in_user(&p32->pad, &p64->pad) || assign_in_user(&p32->start_block, &p64->start_block) || assign_in_user(&p32->blocks, &p64->blocks) || diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 63389f075f1d..2d91b00e3591 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -145,6 +145,15 @@ config DA8XX_DDRCTL Texas Instruments da8xx SoCs. It's used to tweak various memory controller configuration options. +config PL353_SMC + tristate "ARM PL35X Static Memory Controller(SMC) driver" + default y + depends on ARM + depends on ARM_AMBA + help + This driver is for the ARM PL351/PL353 Static Memory + Controller(SMC) module. + source "drivers/memory/samsung/Kconfig" source "drivers/memory/tegra/Kconfig" diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index a01ab3e22f94..90161dec6fa5 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o obj-$(CONFIG_JZ4780_NEMC) += jz4780-nemc.o obj-$(CONFIG_MTK_SMI) += mtk-smi.o obj-$(CONFIG_DA8XX_DDRCTL) += da8xx-ddrctl.o +obj-$(CONFIG_PL353_SMC) += pl353-smc.o obj-$(CONFIG_SAMSUNG_MC) += samsung/ obj-$(CONFIG_TEGRA_MC) += tegra/ diff --git a/drivers/memory/pl353-smc.c b/drivers/memory/pl353-smc.c new file mode 100644 index 000000000000..73bd3023202f --- /dev/null +++ b/drivers/memory/pl353-smc.c @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM PL353 SMC driver + * + * Copyright (C) 2012 - 2018 Xilinx, Inc + * Author: Punnaiah Choudary Kalluri <punnaiah@xilinx.com> + * Author: Naga Sureshkumar Relli <nagasure@xilinx.com> + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/pl353-smc.h> +#include <linux/amba/bus.h> + +/* Register definitions */ +#define PL353_SMC_MEMC_STATUS_OFFS 0 /* Controller status reg, RO */ +#define PL353_SMC_CFG_CLR_OFFS 0xC /* Clear config reg, WO */ +#define PL353_SMC_DIRECT_CMD_OFFS 0x10 /* Direct command reg, WO */ +#define PL353_SMC_SET_CYCLES_OFFS 0x14 /* Set cycles register, WO */ +#define PL353_SMC_SET_OPMODE_OFFS 0x18 /* Set opmode register, WO */ +#define PL353_SMC_ECC_STATUS_OFFS 0x400 /* ECC status register */ +#define PL353_SMC_ECC_MEMCFG_OFFS 0x404 /* ECC mem config reg */ +#define PL353_SMC_ECC_MEMCMD1_OFFS 0x408 /* ECC mem cmd1 reg */ +#define PL353_SMC_ECC_MEMCMD2_OFFS 0x40C /* ECC mem cmd2 reg */ +#define PL353_SMC_ECC_VALUE0_OFFS 0x418 /* ECC value 0 reg */ + +/* Controller status register specific constants */ +#define PL353_SMC_MEMC_STATUS_RAW_INT_1_SHIFT 6 + +/* Clear configuration register specific constants */ +#define PL353_SMC_CFG_CLR_INT_CLR_1 0x10 +#define PL353_SMC_CFG_CLR_ECC_INT_DIS_1 0x40 +#define PL353_SMC_CFG_CLR_INT_DIS_1 0x2 +#define PL353_SMC_CFG_CLR_DEFAULT_MASK (PL353_SMC_CFG_CLR_INT_CLR_1 | \ + PL353_SMC_CFG_CLR_ECC_INT_DIS_1 | \ + PL353_SMC_CFG_CLR_INT_DIS_1) + +/* Set cycles register specific constants */ +#define PL353_SMC_SET_CYCLES_T0_MASK 0xF +#define PL353_SMC_SET_CYCLES_T0_SHIFT 0 +#define PL353_SMC_SET_CYCLES_T1_MASK 0xF +#define PL353_SMC_SET_CYCLES_T1_SHIFT 4 +#define PL353_SMC_SET_CYCLES_T2_MASK 0x7 +#define PL353_SMC_SET_CYCLES_T2_SHIFT 8 +#define PL353_SMC_SET_CYCLES_T3_MASK 0x7 +#define PL353_SMC_SET_CYCLES_T3_SHIFT 11 +#define PL353_SMC_SET_CYCLES_T4_MASK 0x7 +#define PL353_SMC_SET_CYCLES_T4_SHIFT 14 +#define PL353_SMC_SET_CYCLES_T5_MASK 0x7 +#define PL353_SMC_SET_CYCLES_T5_SHIFT 17 +#define PL353_SMC_SET_CYCLES_T6_MASK 0xF +#define PL353_SMC_SET_CYCLES_T6_SHIFT 20 + +/* ECC status register specific constants */ +#define PL353_SMC_ECC_STATUS_BUSY BIT(6) +#define PL353_SMC_ECC_REG_SIZE_OFFS 4 + +/* ECC memory config register specific constants */ +#define PL353_SMC_ECC_MEMCFG_MODE_MASK 0xC +#define PL353_SMC_ECC_MEMCFG_MODE_SHIFT 2 +#define PL353_SMC_ECC_MEMCFG_PGSIZE_MASK 0xC + +#define PL353_SMC_DC_UPT_NAND_REGS ((4 << 23) | /* CS: NAND chip */ \ + (2 << 21)) /* UpdateRegs operation */ + +#define PL353_NAND_ECC_CMD1 ((0x80) | /* Write command */ \ + (0 << 8) | /* Read command */ \ + (0x30 << 16) | /* Read End command */ \ + (1 << 24)) /* Read End command calid */ + +#define PL353_NAND_ECC_CMD2 ((0x85) | /* Write col change cmd */ \ + (5 << 8) | /* Read col change cmd */ \ + (0xE0 << 16) | /* Read col change end cmd */ \ + (1 << 24)) /* Read col change end cmd valid */ +#define PL353_NAND_ECC_BUSY_TIMEOUT (1 * HZ) +/** + * struct pl353_smc_data - Private smc driver structure + * @memclk: Pointer to the peripheral clock + * @aclk: Pointer to the APER clock + */ +struct pl353_smc_data { + struct clk *memclk; + struct clk *aclk; +}; + +/* SMC virtual register base */ +static void __iomem *pl353_smc_base; + +/** + * pl353_smc_set_buswidth - Set memory buswidth + * @bw: Memory buswidth (8 | 16) + * Return: 0 on success or negative errno. + */ +int pl353_smc_set_buswidth(unsigned int bw) +{ + if (bw != PL353_SMC_MEM_WIDTH_8 && bw != PL353_SMC_MEM_WIDTH_16) + return -EINVAL; + + writel(bw, pl353_smc_base + PL353_SMC_SET_OPMODE_OFFS); + writel(PL353_SMC_DC_UPT_NAND_REGS, pl353_smc_base + + PL353_SMC_DIRECT_CMD_OFFS); + + return 0; +} +EXPORT_SYMBOL_GPL(pl353_smc_set_buswidth); + +/** + * pl353_smc_set_cycles - Set memory timing parameters + * @timings: NAND controller timing parameters + * + * Sets NAND chip specific timing parameters. + */ +void pl353_smc_set_cycles(u32 timings[]) +{ + /* + * Set write pulse timing. This one is easy to extract: + * + * NWE_PULSE = tWP + */ + timings[0] &= PL353_SMC_SET_CYCLES_T0_MASK; + timings[1] = (timings[1] & PL353_SMC_SET_CYCLES_T1_MASK) << + PL353_SMC_SET_CYCLES_T1_SHIFT; + timings[2] = (timings[2] & PL353_SMC_SET_CYCLES_T2_MASK) << + PL353_SMC_SET_CYCLES_T2_SHIFT; + timings[3] = (timings[3] & PL353_SMC_SET_CYCLES_T3_MASK) << + PL353_SMC_SET_CYCLES_T3_SHIFT; + timings[4] = (timings[4] & PL353_SMC_SET_CYCLES_T4_MASK) << + PL353_SMC_SET_CYCLES_T4_SHIFT; + timings[5] = (timings[5] & PL353_SMC_SET_CYCLES_T5_MASK) << + PL353_SMC_SET_CYCLES_T5_SHIFT; + timings[6] = (timings[6] & PL353_SMC_SET_CYCLES_T6_MASK) << + PL353_SMC_SET_CYCLES_T6_SHIFT; + timings[0] |= timings[1] | timings[2] | timings[3] | + timings[4] | timings[5] | timings[6]; + + writel(timings[0], pl353_smc_base + PL353_SMC_SET_CYCLES_OFFS); + writel(PL353_SMC_DC_UPT_NAND_REGS, pl353_smc_base + + PL353_SMC_DIRECT_CMD_OFFS); +} +EXPORT_SYMBOL_GPL(pl353_smc_set_cycles); + +/** + * pl353_smc_ecc_is_busy - Read ecc busy flag + * Return: the ecc_status bit from the ecc_status register. 1 = busy, 0 = idle + */ +bool pl353_smc_ecc_is_busy(void) +{ + return ((readl(pl353_smc_base + PL353_SMC_ECC_STATUS_OFFS) & + PL353_SMC_ECC_STATUS_BUSY) == PL353_SMC_ECC_STATUS_BUSY); +} +EXPORT_SYMBOL_GPL(pl353_smc_ecc_is_busy); + +/** + * pl353_smc_get_ecc_val - Read ecc_valueN registers + * @ecc_reg: Index of the ecc_value reg (0..3) + * Return: the content of the requested ecc_value register. + * + * There are four valid ecc_value registers. The argument is truncated to stay + * within this valid boundary. + */ +u32 pl353_smc_get_ecc_val(int ecc_reg) +{ + u32 addr, reg; + + addr = PL353_SMC_ECC_VALUE0_OFFS + + (ecc_reg * PL353_SMC_ECC_REG_SIZE_OFFS); + reg = readl(pl353_smc_base + addr); + + return reg; +} +EXPORT_SYMBOL_GPL(pl353_smc_get_ecc_val); + +/** + * pl353_smc_get_nand_int_status_raw - Get NAND interrupt status bit + * Return: the raw_int_status1 bit from the memc_status register + */ +int pl353_smc_get_nand_int_status_raw(void) +{ + u32 reg; + + reg = readl(pl353_smc_base + PL353_SMC_MEMC_STATUS_OFFS); + reg >>= PL353_SMC_MEMC_STATUS_RAW_INT_1_SHIFT; + reg &= 1; + + return reg; +} +EXPORT_SYMBOL_GPL(pl353_smc_get_nand_int_status_raw); + +/** + * pl353_smc_clr_nand_int - Clear NAND interrupt + */ +void pl353_smc_clr_nand_int(void) +{ + writel(PL353_SMC_CFG_CLR_INT_CLR_1, + pl353_smc_base + PL353_SMC_CFG_CLR_OFFS); +} +EXPORT_SYMBOL_GPL(pl353_smc_clr_nand_int); + +/** + * pl353_smc_set_ecc_mode - Set SMC ECC mode + * @mode: ECC mode (BYPASS, APB, MEM) + * Return: 0 on success or negative errno. + */ +int pl353_smc_set_ecc_mode(enum pl353_smc_ecc_mode mode) +{ + u32 reg; + int ret = 0; + + switch (mode) { + case PL353_SMC_ECCMODE_BYPASS: + case PL353_SMC_ECCMODE_APB: + case PL353_SMC_ECCMODE_MEM: + + reg = readl(pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS); + reg &= ~PL353_SMC_ECC_MEMCFG_MODE_MASK; + reg |= mode << PL353_SMC_ECC_MEMCFG_MODE_SHIFT; + writel(reg, pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS); + + break; + default: + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(pl353_smc_set_ecc_mode); + +/** + * pl353_smc_set_ecc_pg_size - Set SMC ECC page size + * @pg_sz: ECC page size + * Return: 0 on success or negative errno. + */ +int pl353_smc_set_ecc_pg_size(unsigned int pg_sz) +{ + u32 reg, sz; + + switch (pg_sz) { + case 0: + sz = 0; + break; + case SZ_512: + sz = 1; + break; + case SZ_1K: + sz = 2; + break; + case SZ_2K: + sz = 3; + break; + default: + return -EINVAL; + } + + reg = readl(pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS); + reg &= ~PL353_SMC_ECC_MEMCFG_PGSIZE_MASK; + reg |= sz; + writel(reg, pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS); + + return 0; +} +EXPORT_SYMBOL_GPL(pl353_smc_set_ecc_pg_size); + +static int __maybe_unused pl353_smc_suspend(struct device *dev) +{ + struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev); + + clk_disable(pl353_smc->memclk); + clk_disable(pl353_smc->aclk); + + return 0; +} + +static int __maybe_unused pl353_smc_resume(struct device *dev) +{ + int ret; + struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev); + + ret = clk_enable(pl353_smc->aclk); + if (ret) { + dev_err(dev, "Cannot enable axi domain clock.\n"); + return ret; + } + + ret = clk_enable(pl353_smc->memclk); + if (ret) { + dev_err(dev, "Cannot enable memory clock.\n"); + clk_disable(pl353_smc->aclk); + return ret; + } + + return ret; +} + +static struct amba_driver pl353_smc_driver; + +static SIMPLE_DEV_PM_OPS(pl353_smc_dev_pm_ops, pl353_smc_suspend, + pl353_smc_resume); + +/** + * pl353_smc_init_nand_interface - Initialize the NAND interface + * @adev: Pointer to the amba_device struct + * @nand_node: Pointer to the pl353_nand device_node struct + */ +static void pl353_smc_init_nand_interface(struct amba_device *adev, + struct device_node *nand_node) +{ + unsigned long timeout; + + pl353_smc_set_buswidth(PL353_SMC_MEM_WIDTH_8); + writel(PL353_SMC_CFG_CLR_INT_CLR_1, + pl353_smc_base + PL353_SMC_CFG_CLR_OFFS); + writel(PL353_SMC_DC_UPT_NAND_REGS, pl353_smc_base + + PL353_SMC_DIRECT_CMD_OFFS); + + timeout = jiffies + PL353_NAND_ECC_BUSY_TIMEOUT; + /* Wait till the ECC operation is complete */ + do { + if (pl353_smc_ecc_is_busy()) + cpu_relax(); + else + break; + } while (!time_after_eq(jiffies, timeout)); + + if (time_after_eq(jiffies, timeout)) + return; + + writel(PL353_NAND_ECC_CMD1, + pl353_smc_base + PL353_SMC_ECC_MEMCMD1_OFFS); + writel(PL353_NAND_ECC_CMD2, + pl353_smc_base + PL353_SMC_ECC_MEMCMD2_OFFS); +} + +static const struct of_device_id pl353_smc_supported_children[] = { + { + .compatible = "cfi-flash" + }, + { + .compatible = "arm,pl353-nand-r2p1", + .data = pl353_smc_init_nand_interface + }, + {} +}; + +static int pl353_smc_probe(struct amba_device *adev, const struct amba_id *id) +{ + struct pl353_smc_data *pl353_smc; + struct device_node *child; + struct resource *res; + int err; + struct device_node *of_node = adev->dev.of_node; + static void (*init)(struct amba_device *adev, + struct device_node *nand_node); + const struct of_device_id *match = NULL; + + pl353_smc = devm_kzalloc(&adev->dev, sizeof(*pl353_smc), GFP_KERNEL); + if (!pl353_smc) + return -ENOMEM; + + /* Get the NAND controller virtual address */ + res = &adev->res; + pl353_smc_base = devm_ioremap_resource(&adev->dev, res); + if (IS_ERR(pl353_smc_base)) + return PTR_ERR(pl353_smc_base); + + pl353_smc->aclk = devm_clk_get(&adev->dev, "apb_pclk"); + if (IS_ERR(pl353_smc->aclk)) { + dev_err(&adev->dev, "aclk clock not found.\n"); + return PTR_ERR(pl353_smc->aclk); + } + + pl353_smc->memclk = devm_clk_get(&adev->dev, "memclk"); + if (IS_ERR(pl353_smc->memclk)) { + dev_err(&adev->dev, "memclk clock not found.\n"); + return PTR_ERR(pl353_smc->memclk); + } + + err = clk_prepare_enable(pl353_smc->aclk); + if (err) { + dev_err(&adev->dev, "Unable to enable AXI clock.\n"); + return err; + } + + err = clk_prepare_enable(pl353_smc->memclk); + if (err) { + dev_err(&adev->dev, "Unable to enable memory clock.\n"); + goto out_clk_dis_aper; + } + + amba_set_drvdata(adev, pl353_smc); + + /* clear interrupts */ + writel(PL353_SMC_CFG_CLR_DEFAULT_MASK, + pl353_smc_base + PL353_SMC_CFG_CLR_OFFS); + + /* Find compatible children. Only a single child is supported */ + for_each_available_child_of_node(of_node, child) { + match = of_match_node(pl353_smc_supported_children, child); + if (!match) { + dev_warn(&adev->dev, "unsupported child node\n"); + continue; + } + break; + } + if (!match) { + dev_err(&adev->dev, "no matching children\n"); + goto out_clk_disable; + } + + init = match->data; + if (init) + init(adev, child); + of_platform_device_create(child, NULL, &adev->dev); + + return 0; + +out_clk_disable: + clk_disable_unprepare(pl353_smc->memclk); +out_clk_dis_aper: + clk_disable_unprepare(pl353_smc->aclk); + + return err; +} + +static int pl353_smc_remove(struct amba_device *adev) +{ + struct pl353_smc_data *pl353_smc = amba_get_drvdata(adev); + + clk_disable_unprepare(pl353_smc->memclk); + clk_disable_unprepare(pl353_smc->aclk); + + return 0; +} + +static const struct amba_id pl353_ids[] = { + { + .id = 0x00041353, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; +MODULE_DEVICE_TABLE(amba, pl353_ids); + +static struct amba_driver pl353_smc_driver = { + .drv = { + .owner = THIS_MODULE, + .name = "pl353-smc", + .pm = &pl353_smc_dev_pm_ops, + }, + .id_table = pl353_ids, + .probe = pl353_smc_probe, + .remove = pl353_smc_remove, +}; + +module_amba_driver(pl353_smc_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("ARM PL353 SMC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index fe7a1d27a017..a846faefa210 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -13,7 +13,7 @@ config EEPROM_AT24 ones like at24c64, 24lc02 or fm24c04: 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, - 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024 + 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024, 24c2048 Unless you like data loss puzzles, always be sure that any chip you configure as a 24c32 (32 kbit) or larger is NOT really a diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 636ed7149793..ddfcf4ade7bf 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -156,6 +156,7 @@ AT24_CHIP_DATA(at24_data_24c128, 131072 / 8, AT24_FLAG_ADDR16); AT24_CHIP_DATA(at24_data_24c256, 262144 / 8, AT24_FLAG_ADDR16); AT24_CHIP_DATA(at24_data_24c512, 524288 / 8, AT24_FLAG_ADDR16); AT24_CHIP_DATA(at24_data_24c1024, 1048576 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c2048, 2097152 / 8, AT24_FLAG_ADDR16); /* identical to 24c08 ? */ AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0); @@ -182,6 +183,7 @@ static const struct i2c_device_id at24_ids[] = { { "24c256", (kernel_ulong_t)&at24_data_24c256 }, { "24c512", (kernel_ulong_t)&at24_data_24c512 }, { "24c1024", (kernel_ulong_t)&at24_data_24c1024 }, + { "24c2048", (kernel_ulong_t)&at24_data_24c2048 }, { "at24", 0 }, { /* END OF LIST */ } }; @@ -210,6 +212,7 @@ static const struct of_device_id at24_of_match[] = { { .compatible = "atmel,24c256", .data = &at24_data_24c256 }, { .compatible = "atmel,24c512", .data = &at24_data_24c512 }, { .compatible = "atmel,24c1024", .data = &at24_data_24c1024 }, + { .compatible = "atmel,24c2048", .data = &at24_data_24c2048 }, { /* END OF LIST */ }, }; MODULE_DEVICE_TABLE(of, at24_of_match); diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c index 5da1f3e3f997..997f92543dd4 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c @@ -236,7 +236,7 @@ static int vmci_host_setup_notify(struct vmci_ctx *context, * about the size. */ BUILD_BUG_ON(sizeof(bool) != sizeof(u8)); - if (!access_ok(VERIFY_WRITE, (void __user *)uva, sizeof(u8))) + if (!access_ok((void __user *)uva, sizeof(u8))) return VMCI_ERROR_GENERIC; /* diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index aa4a1f5206f1..361fbde76654 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -303,11 +303,10 @@ static int bcm_sf2_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, * send them to our master MDIO bus controller */ if (addr == BRCM_PSEUDO_PHY_ADDR && priv->indir_phy_mask & BIT(addr)) - bcm_sf2_sw_indir_rw(priv, 0, addr, regnum, val); + return bcm_sf2_sw_indir_rw(priv, 0, addr, regnum, val); else - mdiobus_write_nested(priv->master_mii_bus, addr, regnum, val); - - return 0; + return mdiobus_write_nested(priv->master_mii_bus, addr, + regnum, val); } static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c index 9dc6da039a6d..3164aad29bcf 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -473,7 +473,9 @@ static void atl1e_mdio_write(struct net_device *netdev, int phy_id, { struct atl1e_adapter *adapter = netdev_priv(netdev); - atl1e_write_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, val); + if (atl1e_write_phy_reg(&adapter->hw, + reg_num & MDIO_REG_ADDR_MASK, val)) + netdev_err(netdev, "write phy register failed\n"); } static int atl1e_mii_ioctl(struct net_device *netdev, diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c index 7c49681407ad..127b1f624413 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c @@ -1229,6 +1229,10 @@ int cudbg_collect_hw_sched(struct cudbg_init *pdbg_init, rc = cudbg_get_buff(pdbg_init, dbg_buff, sizeof(struct cudbg_hw_sched), &temp_buff); + + if (rc) + return rc; + hw_sched_buff = (struct cudbg_hw_sched *)temp_buff.data; hw_sched_buff->map = t4_read_reg(padap, TP_TX_MOD_QUEUE_REQ_MAP_A); hw_sched_buff->mode = TIMERMODE_G(t4_read_reg(padap, TP_MOD_CONFIG_A)); diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c index bc6eb30aa20f..41c6fa200e74 100644 --- a/drivers/net/ethernet/freescale/fman/fman_memac.c +++ b/drivers/net/ethernet/freescale/fman/fman_memac.c @@ -928,7 +928,7 @@ int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr) hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK; /* Create element to be added to the driver hash table */ - hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL); + hash_entry = kmalloc(sizeof(*hash_entry), GFP_ATOMIC); if (!hash_entry) return -ENOMEM; hash_entry->addr = addr; diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.c b/drivers/net/ethernet/freescale/fman/fman_tgec.c index 40705938eecc..f75b9c11b2d2 100644 --- a/drivers/net/ethernet/freescale/fman/fman_tgec.c +++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c @@ -553,7 +553,7 @@ int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr) hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK; /* Create element to be added to the driver hash table */ - hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL); + hash_entry = kmalloc(sizeof(*hash_entry), GFP_ATOMIC); if (!hash_entry) return -ENOMEM; hash_entry->addr = addr; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index d3b9aaf96c1c..07cd58798083 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -3995,17 +3995,18 @@ static int hns3_reset_notify_up_enet(struct hnae3_handle *handle) struct hns3_nic_priv *priv = netdev_priv(kinfo->netdev); int ret = 0; + clear_bit(HNS3_NIC_STATE_RESETTING, &priv->state); + if (netif_running(kinfo->netdev)) { - ret = hns3_nic_net_up(kinfo->netdev); + ret = hns3_nic_net_open(kinfo->netdev); if (ret) { + set_bit(HNS3_NIC_STATE_RESETTING, &priv->state); netdev_err(kinfo->netdev, "hns net up fail, ret=%d!\n", ret); return ret; } } - clear_bit(HNS3_NIC_STATE_RESETTING, &priv->state); - return ret; } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index 6d48dc62a44b..da323b9e1f62 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -1106,6 +1106,11 @@ static void hinic_remove(struct pci_dev *pdev) dev_info(&pdev->dev, "HiNIC driver - removed\n"); } +static void hinic_shutdown(struct pci_dev *pdev) +{ + pci_disable_device(pdev); +} + static const struct pci_device_id hinic_pci_table[] = { { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_QUAD_PORT_25GE), 0}, { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_DUAL_PORT_25GE), 0}, @@ -1119,6 +1124,7 @@ static struct pci_driver hinic_driver = { .id_table = hinic_pci_table, .probe = hinic_probe, .remove = hinic_remove, + .shutdown = hinic_shutdown, }; module_pci_driver(hinic_driver); diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index a4681780a55d..098d8764c0ea 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1171,11 +1171,15 @@ out: map_failed_frags: last = i+1; - for (i = 0; i < last; i++) + for (i = 1; i < last; i++) dma_unmap_page(&adapter->vdev->dev, descs[i].fields.address, descs[i].fields.flags_len & IBMVETH_BUF_LEN_MASK, DMA_TO_DEVICE); + dma_unmap_single(&adapter->vdev->dev, + descs[0].fields.address, + descs[0].fields.flags_len & IBMVETH_BUF_LEN_MASK, + DMA_TO_DEVICE); map_failed: if (!firmware_has_feature(FW_FEATURE_CMO)) netdev_err(netdev, "tx: unable to map xmit buffer\n"); diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 6a059d6ee03f..e0875476a780 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -5240,6 +5240,8 @@ static int mvpp2_probe(struct platform_device *pdev) if (has_acpi_companion(&pdev->dev)) { acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); + if (!acpi_id) + return -EINVAL; priv->hw_version = (unsigned long)acpi_id->driver_data; } else { priv->hw_version = diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 99bc3de906e2..298930d39b79 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -1477,6 +1477,8 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) } RTL_W8(tp, Cfg9346, Cfg9346_Lock); + + device_set_wakeup_enable(tp_to_dev(tp), wolopts); } static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) @@ -1498,8 +1500,6 @@ static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) rtl_unlock_work(tp); - device_set_wakeup_enable(d, tp->saved_wolopts); - pm_runtime_put_noidle(d); return 0; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c index d07520fb969e..62ccbd47c1db 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c @@ -59,7 +59,9 @@ static int sun7i_gmac_init(struct platform_device *pdev, void *priv) gmac->clk_enabled = 1; } else { clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE); - clk_prepare(gmac->tx_clk); + ret = clk_prepare(gmac->tx_clk); + if (ret) + return ret; } return 0; diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index 9319d84bf49f..d84501441edd 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -8100,6 +8100,8 @@ static int niu_pci_vpd_scan_props(struct niu *np, u32 start, u32 end) start += 3; prop_len = niu_pci_eeprom_read(np, start + 4); + if (prop_len < 0) + return prop_len; err = niu_pci_vpd_get_propname(np, start + 5, namebuf, 64); if (err < 0) return err; @@ -8144,8 +8146,12 @@ static int niu_pci_vpd_scan_props(struct niu *np, u32 start, u32 end) netif_printk(np, probe, KERN_DEBUG, np->dev, "VPD_SCAN: Reading in property [%s] len[%d]\n", namebuf, prop_len); - for (i = 0; i < prop_len; i++) - *prop_buf++ = niu_pci_eeprom_read(np, off + i); + for (i = 0; i < prop_len; i++) { + err = niu_pci_eeprom_read(np, off + i); + if (err >= 0) + *prop_buf = err; + ++prop_buf; + } } start += len; diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 054f78295d1d..2a9ba4acd7fa 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -590,7 +590,9 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs, return ERR_CAST(cpts->refclk); } - clk_prepare(cpts->refclk); + ret = clk_prepare(cpts->refclk); + if (ret) + return ERR_PTR(ret); cpts->cc.read = cpts_systim_read; cpts->cc.mask = CLOCKSOURCE_MASK(32); diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 28c749980359..a19868cba48c 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -523,10 +523,7 @@ static void resync_tnc(struct timer_list *t) /* Start resync timer again -- the TNC might be still absent */ - - del_timer(&sp->resync_t); - sp->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT; - add_timer(&sp->resync_t); + mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT); } static inline int tnc_init(struct sixpack *sp) @@ -537,9 +534,7 @@ static inline int tnc_init(struct sixpack *sp) sp->tty->ops->write(sp->tty, &inbyte, 1); - del_timer(&sp->resync_t); - sp->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT; - add_timer(&sp->resync_t); + mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT); return 0; } @@ -897,11 +892,8 @@ static void decode_prio_command(struct sixpack *sp, unsigned char cmd) /* if the state byte has been received, the TNC is present, so the resync timer can be reset. */ - if (sp->tnc_state == TNC_IN_SYNC) { - del_timer(&sp->resync_t); - sp->resync_t.expires = jiffies + SIXP_INIT_RESYNC_TIMEOUT; - add_timer(&sp->resync_t); - } + if (sp->tnc_state == TNC_IN_SYNC) + mod_timer(&sp->resync_t, jiffies + SIXP_INIT_RESYNC_TIMEOUT); sp->status1 = cmd & SIXP_PRIO_DATA_MASK; } diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 443b2694130c..c0b52e48f0e6 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1177,8 +1177,6 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) goto err_kfree; } - skb_probe_transport_header(skb, ETH_HLEN); - /* Move network header to the right position for VLAN tagged packets */ if ((skb->protocol == htons(ETH_P_8021Q) || skb->protocol == htons(ETH_P_8021AD)) && @@ -1189,6 +1187,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) tap = rcu_dereference(q->tap); if (tap) { skb->dev = tap->dev; + skb_probe_transport_header(skb, ETH_HLEN); dev_queue_xmit(skb); } else { kfree_skb(skb); diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c index 7a42336c8af8..839fa7715709 100644 --- a/drivers/net/wan/fsl_ucc_hdlc.c +++ b/drivers/net/wan/fsl_ucc_hdlc.c @@ -1180,7 +1180,6 @@ static int ucc_hdlc_probe(struct platform_device *pdev) if (register_hdlc_device(dev)) { ret = -ENOBUFS; pr_err("ucc_hdlc: unable to register hdlc device\n"); - free_netdev(dev); goto free_dev; } diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 1098263ab862..46c3d983b7b7 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -485,8 +485,10 @@ static int x25_asy_open(struct net_device *dev) /* Cleanup */ kfree(sl->xbuff); + sl->xbuff = NULL; noxbuff: kfree(sl->rbuff); + sl->rbuff = NULL; norbuff: return -ENOMEM; } diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 31ec770b433d..4310c7a4212e 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -103,9 +103,9 @@ config PCI_PF_STUB depends on PCI_IOV help Say Y or M here if you want to enable support for devices that - require SR-IOV support, while at the same time the PF itself is - not providing any actual services on the host itself such as - storage or networking. + require SR-IOV support, while at the same time the PF (Physical + Function) itself is not providing any actual services on the + host itself such as storage or networking. When in doubt, say N. diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 91b0194240a5..548c58223868 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -89,8 +89,8 @@ config PCI_EXYNOS select PCIE_DW_HOST config PCI_IMX6 - bool "Freescale i.MX6 PCIe controller" - depends on SOC_IMX6Q || (ARM && COMPILE_TEST) + bool "Freescale i.MX6/7 PCIe controller" + depends on SOC_IMX6Q || SOC_IMX7D || (ARM && COMPILE_TEST) depends on PCI_MSI_IRQ_DOMAIN select PCIE_DW_HOST @@ -193,4 +193,24 @@ config PCIE_HISI_STB help Say Y here if you want PCIe controller support on HiSilicon STB SoCs +config PCI_MESON + bool "MESON PCIe controller" + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say Y here if you want to enable PCI controller support on Amlogic + SoCs. The PCI controller on Amlogic is based on DesignWare hardware + and therefore the driver re-uses the DesignWare core functions to + implement the driver. + +config PCIE_UNIPHIER + bool "Socionext UniPhier PCIe controllers" + depends on ARCH_UNIPHIER || COMPILE_TEST + depends on OF && HAS_IOMEM + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Say Y here if you want PCIe controller support on UniPhier SoCs. + This driver supports LD20 and PXs3 SoCs. + endmenu diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index fcf91eacfc63..7bcdcdf5024e 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o +obj-$(CONFIG_PCI_MESON) += pci-meson.o +obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o # The following drivers are for devices that use the generic ACPI # pci_root.c driver but don't support standard ECAM config access. diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 88af6bff945f..52e47dac028f 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -27,6 +27,8 @@ #include <linux/types.h> #include <linux/interrupt.h> #include <linux/reset.h> +#include <linux/pm_domain.h> +#include <linux/pm_runtime.h> #include "pcie-designware.h" @@ -59,6 +61,11 @@ struct imx6_pcie { u32 tx_swing_low; int link_gen; struct regulator *vpcie; + + /* power domain for pcie */ + struct device *pd_pcie; + /* power domain for pcie phy */ + struct device *pd_pcie_phy; }; /* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ @@ -67,6 +74,7 @@ struct imx6_pcie { #define PHY_PLL_LOCK_WAIT_USLEEP_MAX 200 /* PCIe Root Complex registers (memory-mapped) */ +#define PCIE_RC_IMX6_MSI_CAP 0x50 #define PCIE_RC_LCR 0x7c #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1 0x1 #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2 0x2 @@ -290,6 +298,43 @@ static int imx6q_pcie_abort_handler(unsigned long addr, return 1; } +static int imx6_pcie_attach_pd(struct device *dev) +{ + struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); + struct device_link *link; + + /* Do nothing when in a single power domain */ + if (dev->pm_domain) + return 0; + + imx6_pcie->pd_pcie = dev_pm_domain_attach_by_name(dev, "pcie"); + if (IS_ERR(imx6_pcie->pd_pcie)) + return PTR_ERR(imx6_pcie->pd_pcie); + link = device_link_add(dev, imx6_pcie->pd_pcie, + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!link) { + dev_err(dev, "Failed to add device_link to pcie pd.\n"); + return -EINVAL; + } + + imx6_pcie->pd_pcie_phy = dev_pm_domain_attach_by_name(dev, "pcie_phy"); + if (IS_ERR(imx6_pcie->pd_pcie_phy)) + return PTR_ERR(imx6_pcie->pd_pcie_phy); + + device_link_add(dev, imx6_pcie->pd_pcie_phy, + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (IS_ERR(link)) { + dev_err(dev, "Failed to add device_link to pcie_phy pd: %ld\n", PTR_ERR(link)); + return PTR_ERR(link); + } + + return 0; +} + static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) { struct device *dev = imx6_pcie->pci->dev; @@ -765,8 +810,28 @@ static void imx6_pcie_ltssm_disable(struct device *dev) static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie) { - reset_control_assert(imx6_pcie->turnoff_reset); - reset_control_deassert(imx6_pcie->turnoff_reset); + struct device *dev = imx6_pcie->pci->dev; + + /* Some variants have a turnoff reset in DT */ + if (imx6_pcie->turnoff_reset) { + reset_control_assert(imx6_pcie->turnoff_reset); + reset_control_deassert(imx6_pcie->turnoff_reset); + goto pm_turnoff_sleep; + } + + /* Others poke directly at IOMUXC registers */ + switch (imx6_pcie->variant) { + case IMX6SX: + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6SX_GPR12_PCIE_PM_TURN_OFF, + IMX6SX_GPR12_PCIE_PM_TURN_OFF); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6SX_GPR12_PCIE_PM_TURN_OFF, 0); + break; + default: + dev_err(dev, "PME_Turn_Off not implemented\n"); + return; + } /* * Components with an upstream port must respond to @@ -775,6 +840,7 @@ static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie) * The standard recommends a 1-10ms timeout after which to * proceed anyway as if acks were received. */ +pm_turnoff_sleep: usleep_range(1000, 10000); } @@ -784,18 +850,31 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie) clk_disable_unprepare(imx6_pcie->pcie_phy); clk_disable_unprepare(imx6_pcie->pcie_bus); - if (imx6_pcie->variant == IMX7D) { + switch (imx6_pcie->variant) { + case IMX6SX: + clk_disable_unprepare(imx6_pcie->pcie_inbound_axi); + break; + case IMX7D: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, IMX7D_GPR12_PCIE_PHY_REFCLK_SEL); + break; + default: + break; } } +static inline bool imx6_pcie_supports_suspend(struct imx6_pcie *imx6_pcie) +{ + return (imx6_pcie->variant == IMX7D || + imx6_pcie->variant == IMX6SX); +} + static int imx6_pcie_suspend_noirq(struct device *dev) { struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); - if (imx6_pcie->variant != IMX7D) + if (!imx6_pcie_supports_suspend(imx6_pcie)) return 0; imx6_pcie_pm_turnoff(imx6_pcie); @@ -811,7 +890,7 @@ static int imx6_pcie_resume_noirq(struct device *dev) struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); struct pcie_port *pp = &imx6_pcie->pci->pp; - if (imx6_pcie->variant != IMX7D) + if (!imx6_pcie_supports_suspend(imx6_pcie)) return 0; imx6_pcie_assert_core_reset(imx6_pcie); @@ -840,6 +919,7 @@ static int imx6_pcie_probe(struct platform_device *pdev) struct resource *dbi_base; struct device_node *node = dev->of_node; int ret; + u16 val; imx6_pcie = devm_kzalloc(dev, sizeof(*imx6_pcie), GFP_KERNEL); if (!imx6_pcie) @@ -977,10 +1057,22 @@ static int imx6_pcie_probe(struct platform_device *pdev) platform_set_drvdata(pdev, imx6_pcie); + ret = imx6_pcie_attach_pd(dev); + if (ret) + return ret; + ret = imx6_add_pcie_port(imx6_pcie, pdev); if (ret < 0) return ret; + if (pci_msi_enabled()) { + val = dw_pcie_readw_dbi(pci, PCIE_RC_IMX6_MSI_CAP + + PCI_MSI_FLAGS); + val |= PCI_MSI_FLAGS_ENABLE; + dw_pcie_writew_dbi(pci, PCIE_RC_IMX6_MSI_CAP + PCI_MSI_FLAGS, + val); + } + return 0; } diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c index 7aa9a82b7ebd..ce45bde29bf8 100644 --- a/drivers/pci/controller/dwc/pci-layerscape.c +++ b/drivers/pci/controller/dwc/pci-layerscape.c @@ -222,12 +222,12 @@ static const struct dw_pcie_ops dw_ls_pcie_ops = { .link_up = ls_pcie_link_up, }; -static struct ls_pcie_drvdata ls1021_drvdata = { +static const struct ls_pcie_drvdata ls1021_drvdata = { .ops = &ls1021_pcie_host_ops, .dw_pcie_ops = &dw_ls1021_pcie_ops, }; -static struct ls_pcie_drvdata ls1043_drvdata = { +static const struct ls_pcie_drvdata ls1043_drvdata = { .lut_offset = 0x10000, .ltssm_shift = 24, .lut_dbg = 0x7fc, @@ -235,7 +235,7 @@ static struct ls_pcie_drvdata ls1043_drvdata = { .dw_pcie_ops = &dw_ls_pcie_ops, }; -static struct ls_pcie_drvdata ls1046_drvdata = { +static const struct ls_pcie_drvdata ls1046_drvdata = { .lut_offset = 0x80000, .ltssm_shift = 24, .lut_dbg = 0x407fc, @@ -243,7 +243,7 @@ static struct ls_pcie_drvdata ls1046_drvdata = { .dw_pcie_ops = &dw_ls_pcie_ops, }; -static struct ls_pcie_drvdata ls2080_drvdata = { +static const struct ls_pcie_drvdata ls2080_drvdata = { .lut_offset = 0x80000, .ltssm_shift = 0, .lut_dbg = 0x7fc, @@ -251,7 +251,7 @@ static struct ls_pcie_drvdata ls2080_drvdata = { .dw_pcie_ops = &dw_ls_pcie_ops, }; -static struct ls_pcie_drvdata ls2088_drvdata = { +static const struct ls_pcie_drvdata ls2088_drvdata = { .lut_offset = 0x80000, .ltssm_shift = 0, .lut_dbg = 0x407fc, diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c new file mode 100644 index 000000000000..241ebe0c4505 --- /dev/null +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -0,0 +1,592 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for Amlogic MESON SoCs + * + * Copyright (c) 2018 Amlogic, inc. + * Author: Yue Wang <yue.wang@amlogic.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/resource.h> +#include <linux/types.h> + +#include "pcie-designware.h" + +#define to_meson_pcie(x) dev_get_drvdata((x)->dev) + +/* External local bus interface registers */ +#define PLR_OFFSET 0x700 +#define PCIE_PORT_LINK_CTRL_OFF (PLR_OFFSET + 0x10) +#define FAST_LINK_MODE BIT(7) +#define LINK_CAPABLE_MASK GENMASK(21, 16) +#define LINK_CAPABLE_X1 BIT(16) + +#define PCIE_GEN2_CTRL_OFF (PLR_OFFSET + 0x10c) +#define NUM_OF_LANES_MASK GENMASK(12, 8) +#define NUM_OF_LANES_X1 BIT(8) +#define DIRECT_SPEED_CHANGE BIT(17) + +#define TYPE1_HDR_OFFSET 0x0 +#define PCIE_STATUS_COMMAND (TYPE1_HDR_OFFSET + 0x04) +#define PCI_IO_EN BIT(0) +#define PCI_MEM_SPACE_EN BIT(1) +#define PCI_BUS_MASTER_EN BIT(2) + +#define PCIE_BASE_ADDR0 (TYPE1_HDR_OFFSET + 0x10) +#define PCIE_BASE_ADDR1 (TYPE1_HDR_OFFSET + 0x14) + +#define PCIE_CAP_OFFSET 0x70 +#define PCIE_DEV_CTRL_DEV_STUS (PCIE_CAP_OFFSET + 0x08) +#define PCIE_CAP_MAX_PAYLOAD_MASK GENMASK(7, 5) +#define PCIE_CAP_MAX_PAYLOAD_SIZE(x) ((x) << 5) +#define PCIE_CAP_MAX_READ_REQ_MASK GENMASK(14, 12) +#define PCIE_CAP_MAX_READ_REQ_SIZE(x) ((x) << 12) + +/* PCIe specific config registers */ +#define PCIE_CFG0 0x0 +#define APP_LTSSM_ENABLE BIT(7) + +#define PCIE_CFG_STATUS12 0x30 +#define IS_SMLH_LINK_UP(x) ((x) & (1 << 6)) +#define IS_RDLH_LINK_UP(x) ((x) & (1 << 16)) +#define IS_LTSSM_UP(x) ((((x) >> 10) & 0x1f) == 0x11) + +#define PCIE_CFG_STATUS17 0x44 +#define PM_CURRENT_STATE(x) (((x) >> 7) & 0x1) + +#define WAIT_LINKUP_TIMEOUT 4000 +#define PORT_CLK_RATE 100000000UL +#define MAX_PAYLOAD_SIZE 256 +#define MAX_READ_REQ_SIZE 256 +#define MESON_PCIE_PHY_POWERUP 0x1c +#define PCIE_RESET_DELAY 500 +#define PCIE_SHARED_RESET 1 +#define PCIE_NORMAL_RESET 0 + +enum pcie_data_rate { + PCIE_GEN1, + PCIE_GEN2, + PCIE_GEN3, + PCIE_GEN4 +}; + +struct meson_pcie_mem_res { + void __iomem *elbi_base; + void __iomem *cfg_base; + void __iomem *phy_base; +}; + +struct meson_pcie_clk_res { + struct clk *clk; + struct clk *mipi_gate; + struct clk *port_clk; + struct clk *general_clk; +}; + +struct meson_pcie_rc_reset { + struct reset_control *phy; + struct reset_control *port; + struct reset_control *apb; +}; + +struct meson_pcie { + struct dw_pcie pci; + struct meson_pcie_mem_res mem_res; + struct meson_pcie_clk_res clk_res; + struct meson_pcie_rc_reset mrst; + struct gpio_desc *reset_gpio; +}; + +static struct reset_control *meson_pcie_get_reset(struct meson_pcie *mp, + const char *id, + u32 reset_type) +{ + struct device *dev = mp->pci.dev; + struct reset_control *reset; + + if (reset_type == PCIE_SHARED_RESET) + reset = devm_reset_control_get_shared(dev, id); + else + reset = devm_reset_control_get(dev, id); + + return reset; +} + +static int meson_pcie_get_resets(struct meson_pcie *mp) +{ + struct meson_pcie_rc_reset *mrst = &mp->mrst; + + mrst->phy = meson_pcie_get_reset(mp, "phy", PCIE_SHARED_RESET); + if (IS_ERR(mrst->phy)) + return PTR_ERR(mrst->phy); + reset_control_deassert(mrst->phy); + + mrst->port = meson_pcie_get_reset(mp, "port", PCIE_NORMAL_RESET); + if (IS_ERR(mrst->port)) + return PTR_ERR(mrst->port); + reset_control_deassert(mrst->port); + + mrst->apb = meson_pcie_get_reset(mp, "apb", PCIE_SHARED_RESET); + if (IS_ERR(mrst->apb)) + return PTR_ERR(mrst->apb); + reset_control_deassert(mrst->apb); + + return 0; +} + +static void __iomem *meson_pcie_get_mem(struct platform_device *pdev, + struct meson_pcie *mp, + const char *id) +{ + struct device *dev = mp->pci.dev; + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, id); + + return devm_ioremap_resource(dev, res); +} + +static void __iomem *meson_pcie_get_mem_shared(struct platform_device *pdev, + struct meson_pcie *mp, + const char *id) +{ + struct device *dev = mp->pci.dev; + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, id); + if (!res) { + dev_err(dev, "No REG resource %s\n", id); + return ERR_PTR(-ENXIO); + } + + return devm_ioremap(dev, res->start, resource_size(res)); +} + +static int meson_pcie_get_mems(struct platform_device *pdev, + struct meson_pcie *mp) +{ + mp->mem_res.elbi_base = meson_pcie_get_mem(pdev, mp, "elbi"); + if (IS_ERR(mp->mem_res.elbi_base)) + return PTR_ERR(mp->mem_res.elbi_base); + + mp->mem_res.cfg_base = meson_pcie_get_mem(pdev, mp, "cfg"); + if (IS_ERR(mp->mem_res.cfg_base)) + return PTR_ERR(mp->mem_res.cfg_base); + + /* Meson SoC has two PCI controllers use same phy register*/ + mp->mem_res.phy_base = meson_pcie_get_mem_shared(pdev, mp, "phy"); + if (IS_ERR(mp->mem_res.phy_base)) + return PTR_ERR(mp->mem_res.phy_base); + + return 0; +} + +static void meson_pcie_power_on(struct meson_pcie *mp) +{ + writel(MESON_PCIE_PHY_POWERUP, mp->mem_res.phy_base); +} + +static void meson_pcie_reset(struct meson_pcie *mp) +{ + struct meson_pcie_rc_reset *mrst = &mp->mrst; + + reset_control_assert(mrst->phy); + udelay(PCIE_RESET_DELAY); + reset_control_deassert(mrst->phy); + udelay(PCIE_RESET_DELAY); + + reset_control_assert(mrst->port); + reset_control_assert(mrst->apb); + udelay(PCIE_RESET_DELAY); + reset_control_deassert(mrst->port); + reset_control_deassert(mrst->apb); + udelay(PCIE_RESET_DELAY); +} + +static inline struct clk *meson_pcie_probe_clock(struct device *dev, + const char *id, u64 rate) +{ + struct clk *clk; + int ret; + + clk = devm_clk_get(dev, id); + if (IS_ERR(clk)) + return clk; + + if (rate) { + ret = clk_set_rate(clk, rate); + if (ret) { + dev_err(dev, "set clk rate failed, ret = %d\n", ret); + return ERR_PTR(ret); + } + } + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "couldn't enable clk\n"); + return ERR_PTR(ret); + } + + devm_add_action_or_reset(dev, + (void (*) (void *))clk_disable_unprepare, + clk); + + return clk; +} + +static int meson_pcie_probe_clocks(struct meson_pcie *mp) +{ + struct device *dev = mp->pci.dev; + struct meson_pcie_clk_res *res = &mp->clk_res; + + res->port_clk = meson_pcie_probe_clock(dev, "port", PORT_CLK_RATE); + if (IS_ERR(res->port_clk)) + return PTR_ERR(res->port_clk); + + res->mipi_gate = meson_pcie_probe_clock(dev, "pcie_mipi_en", 0); + if (IS_ERR(res->mipi_gate)) + return PTR_ERR(res->mipi_gate); + + res->general_clk = meson_pcie_probe_clock(dev, "pcie_general", 0); + if (IS_ERR(res->general_clk)) + return PTR_ERR(res->general_clk); + + res->clk = meson_pcie_probe_clock(dev, "pcie", 0); + if (IS_ERR(res->clk)) + return PTR_ERR(res->clk); + + return 0; +} + +static inline void meson_elb_writel(struct meson_pcie *mp, u32 val, u32 reg) +{ + writel(val, mp->mem_res.elbi_base + reg); +} + +static inline u32 meson_elb_readl(struct meson_pcie *mp, u32 reg) +{ + return readl(mp->mem_res.elbi_base + reg); +} + +static inline u32 meson_cfg_readl(struct meson_pcie *mp, u32 reg) +{ + return readl(mp->mem_res.cfg_base + reg); +} + +static inline void meson_cfg_writel(struct meson_pcie *mp, u32 val, u32 reg) +{ + writel(val, mp->mem_res.cfg_base + reg); +} + +static void meson_pcie_assert_reset(struct meson_pcie *mp) +{ + gpiod_set_value_cansleep(mp->reset_gpio, 0); + udelay(500); + gpiod_set_value_cansleep(mp->reset_gpio, 1); +} + +static void meson_pcie_init_dw(struct meson_pcie *mp) +{ + u32 val; + + val = meson_cfg_readl(mp, PCIE_CFG0); + val |= APP_LTSSM_ENABLE; + meson_cfg_writel(mp, val, PCIE_CFG0); + + val = meson_elb_readl(mp, PCIE_PORT_LINK_CTRL_OFF); + val &= ~LINK_CAPABLE_MASK; + meson_elb_writel(mp, val, PCIE_PORT_LINK_CTRL_OFF); + + val = meson_elb_readl(mp, PCIE_PORT_LINK_CTRL_OFF); + val |= LINK_CAPABLE_X1 | FAST_LINK_MODE; + meson_elb_writel(mp, val, PCIE_PORT_LINK_CTRL_OFF); + + val = meson_elb_readl(mp, PCIE_GEN2_CTRL_OFF); + val &= ~NUM_OF_LANES_MASK; + meson_elb_writel(mp, val, PCIE_GEN2_CTRL_OFF); + + val = meson_elb_readl(mp, PCIE_GEN2_CTRL_OFF); + val |= NUM_OF_LANES_X1 | DIRECT_SPEED_CHANGE; + meson_elb_writel(mp, val, PCIE_GEN2_CTRL_OFF); + + meson_elb_writel(mp, 0x0, PCIE_BASE_ADDR0); + meson_elb_writel(mp, 0x0, PCIE_BASE_ADDR1); +} + +static int meson_size_to_payload(struct meson_pcie *mp, int size) +{ + struct device *dev = mp->pci.dev; + + /* + * dwc supports 2^(val+7) payload size, which val is 0~5 default to 1. + * So if input size is not 2^order alignment or less than 2^7 or bigger + * than 2^12, just set to default size 2^(1+7). + */ + if (!is_power_of_2(size) || size < 128 || size > 4096) { + dev_warn(dev, "payload size %d, set to default 256\n", size); + return 1; + } + + return fls(size) - 8; +} + +static void meson_set_max_payload(struct meson_pcie *mp, int size) +{ + u32 val; + int max_payload_size = meson_size_to_payload(mp, size); + + val = meson_elb_readl(mp, PCIE_DEV_CTRL_DEV_STUS); + val &= ~PCIE_CAP_MAX_PAYLOAD_MASK; + meson_elb_writel(mp, val, PCIE_DEV_CTRL_DEV_STUS); + + val = meson_elb_readl(mp, PCIE_DEV_CTRL_DEV_STUS); + val |= PCIE_CAP_MAX_PAYLOAD_SIZE(max_payload_size); + meson_elb_writel(mp, val, PCIE_DEV_CTRL_DEV_STUS); +} + +static void meson_set_max_rd_req_size(struct meson_pcie *mp, int size) +{ + u32 val; + int max_rd_req_size = meson_size_to_payload(mp, size); + + val = meson_elb_readl(mp, PCIE_DEV_CTRL_DEV_STUS); + val &= ~PCIE_CAP_MAX_READ_REQ_MASK; + meson_elb_writel(mp, val, PCIE_DEV_CTRL_DEV_STUS); + + val = meson_elb_readl(mp, PCIE_DEV_CTRL_DEV_STUS); + val |= PCIE_CAP_MAX_READ_REQ_SIZE(max_rd_req_size); + meson_elb_writel(mp, val, PCIE_DEV_CTRL_DEV_STUS); +} + +static inline void meson_enable_memory_space(struct meson_pcie *mp) +{ + /* Set the RC Bus Master, Memory Space and I/O Space enables */ + meson_elb_writel(mp, PCI_IO_EN | PCI_MEM_SPACE_EN | PCI_BUS_MASTER_EN, + PCIE_STATUS_COMMAND); +} + +static int meson_pcie_establish_link(struct meson_pcie *mp) +{ + struct dw_pcie *pci = &mp->pci; + struct pcie_port *pp = &pci->pp; + + meson_pcie_init_dw(mp); + meson_set_max_payload(mp, MAX_PAYLOAD_SIZE); + meson_set_max_rd_req_size(mp, MAX_READ_REQ_SIZE); + + dw_pcie_setup_rc(pp); + meson_enable_memory_space(mp); + + meson_pcie_assert_reset(mp); + + return dw_pcie_wait_for_link(pci); +} + +static void meson_pcie_enable_interrupts(struct meson_pcie *mp) +{ + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(&mp->pci.pp); +} + +static int meson_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + int ret; + + ret = dw_pcie_read(pci->dbi_base + where, size, val); + if (ret != PCIBIOS_SUCCESSFUL) + return ret; + + /* + * There is a bug in the MESON AXG PCIe controller whereby software + * cannot program the PCI_CLASS_DEVICE register, so we must fabricate + * the return value in the config accessors. + */ + if (where == PCI_CLASS_REVISION && size == 4) + *val = (PCI_CLASS_BRIDGE_PCI << 16) | (*val & 0xffff); + else if (where == PCI_CLASS_DEVICE && size == 2) + *val = PCI_CLASS_BRIDGE_PCI; + else if (where == PCI_CLASS_DEVICE && size == 1) + *val = PCI_CLASS_BRIDGE_PCI & 0xff; + else if (where == PCI_CLASS_DEVICE + 1 && size == 1) + *val = (PCI_CLASS_BRIDGE_PCI >> 8) & 0xff; + + return PCIBIOS_SUCCESSFUL; +} + +static int meson_pcie_wr_own_conf(struct pcie_port *pp, int where, + int size, u32 val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + return dw_pcie_write(pci->dbi_base + where, size, val); +} + +static int meson_pcie_link_up(struct dw_pcie *pci) +{ + struct meson_pcie *mp = to_meson_pcie(pci); + struct device *dev = pci->dev; + u32 speed_okay = 0; + u32 cnt = 0; + u32 state12, state17, smlh_up, ltssm_up, rdlh_up; + + do { + state12 = meson_cfg_readl(mp, PCIE_CFG_STATUS12); + state17 = meson_cfg_readl(mp, PCIE_CFG_STATUS17); + smlh_up = IS_SMLH_LINK_UP(state12); + rdlh_up = IS_RDLH_LINK_UP(state12); + ltssm_up = IS_LTSSM_UP(state12); + + if (PM_CURRENT_STATE(state17) < PCIE_GEN3) + speed_okay = 1; + + if (smlh_up) + dev_dbg(dev, "smlh_link_up is on\n"); + if (rdlh_up) + dev_dbg(dev, "rdlh_link_up is on\n"); + if (ltssm_up) + dev_dbg(dev, "ltssm_up is on\n"); + if (speed_okay) + dev_dbg(dev, "speed_okay\n"); + + if (smlh_up && rdlh_up && ltssm_up && speed_okay) + return 1; + + cnt++; + + udelay(10); + } while (cnt < WAIT_LINKUP_TIMEOUT); + + dev_err(dev, "error: wait linkup timeout\n"); + return 0; +} + +static int meson_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct meson_pcie *mp = to_meson_pcie(pci); + int ret; + + ret = meson_pcie_establish_link(mp); + if (ret) + return ret; + + meson_pcie_enable_interrupts(mp); + + return 0; +} + +static const struct dw_pcie_host_ops meson_pcie_host_ops = { + .rd_own_conf = meson_pcie_rd_own_conf, + .wr_own_conf = meson_pcie_wr_own_conf, + .host_init = meson_pcie_host_init, +}; + +static int meson_add_pcie_port(struct meson_pcie *mp, + struct platform_device *pdev) +{ + struct dw_pcie *pci = &mp->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; + int ret; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq(pdev, 0); + if (pp->msi_irq < 0) { + dev_err(dev, "failed to get MSI IRQ\n"); + return pp->msi_irq; + } + } + + pp->ops = &meson_pcie_host_ops; + pci->dbi_base = mp->mem_res.elbi_base; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = meson_pcie_link_up, +}; + +static int meson_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie *pci; + struct meson_pcie *mp; + int ret; + + mp = devm_kzalloc(dev, sizeof(*mp), GFP_KERNEL); + if (!mp) + return -ENOMEM; + + pci = &mp->pci; + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + mp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(mp->reset_gpio)) { + dev_err(dev, "get reset gpio failed\n"); + return PTR_ERR(mp->reset_gpio); + } + + ret = meson_pcie_get_resets(mp); + if (ret) { + dev_err(dev, "get reset resource failed, %d\n", ret); + return ret; + } + + ret = meson_pcie_get_mems(pdev, mp); + if (ret) { + dev_err(dev, "get memory resource failed, %d\n", ret); + return ret; + } + + meson_pcie_power_on(mp); + meson_pcie_reset(mp); + + ret = meson_pcie_probe_clocks(mp); + if (ret) { + dev_err(dev, "init clock resources failed, %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, mp); + + ret = meson_add_pcie_port(mp, pdev); + if (ret < 0) { + dev_err(dev, "Add PCIe port failed, %d\n", ret); + return ret; + } + + return 0; +} + +static const struct of_device_id meson_pcie_of_match[] = { + { + .compatible = "amlogic,axg-pcie", + }, + {}, +}; + +static struct platform_driver meson_pcie_driver = { + .probe = meson_pcie_probe, + .driver = { + .name = "meson-pcie", + .of_match_table = meson_pcie_of_match, + }, +}; + +builtin_platform_driver(meson_pcie_driver); diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c index 0c389a30ef5d..b171b6bc15c8 100644 --- a/drivers/pci/controller/dwc/pcie-armada8k.c +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -22,6 +22,7 @@ #include <linux/resource.h> #include <linux/of_pci.h> #include <linux/of_irq.h> +#include <linux/gpio/consumer.h> #include "pcie-designware.h" @@ -29,6 +30,7 @@ struct armada8k_pcie { struct dw_pcie *pci; struct clk *clk; struct clk *clk_reg; + struct gpio_desc *reset_gpio; }; #define PCIE_VENDOR_REGS_OFFSET 0x8000 @@ -137,6 +139,12 @@ static int armada8k_pcie_host_init(struct pcie_port *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct armada8k_pcie *pcie = to_armada8k_pcie(pci); + if (pcie->reset_gpio) { + /* assert and then deassert the reset signal */ + gpiod_set_value_cansleep(pcie->reset_gpio, 1); + msleep(100); + gpiod_set_value_cansleep(pcie->reset_gpio, 0); + } dw_pcie_setup_rc(pp); armada8k_pcie_establish_link(pcie); @@ -249,6 +257,14 @@ static int armada8k_pcie_probe(struct platform_device *pdev) goto fail_clkreg; } + /* Get reset gpio signal and hold asserted (logically high) */ + pcie->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(pcie->reset_gpio)) { + ret = PTR_ERR(pcie->reset_gpio); + goto fail_clkreg; + } + platform_set_drvdata(pdev, pcie); ret = armada8k_add_pcie_port(pcie, pdev); diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index de8635af4cde..a543c45c7224 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -503,6 +503,10 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) dev_err(dev, "dbi_base/dbi_base2 is not populated\n"); return -EINVAL; } + if (pci->iatu_unroll_enabled && !pci->atu_base) { + dev_err(dev, "atu_base is not populated\n"); + return -EINVAL; + } ret = of_property_read_u32(np, "num-ib-windows", &ep->num_ib_windows); if (ret < 0) { diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 29a05759a294..721d60a5d9e4 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -99,9 +99,6 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) (i * MAX_MSI_IRQS_PER_CTRL) + pos); generic_handle_irq(irq); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + - (i * MSI_REG_CTRL_BLOCK_SIZE), - 4, 1 << pos); pos++; } } @@ -168,8 +165,8 @@ static void dw_pci_bottom_mask(struct irq_data *data) bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL; pp->irq_status[ctrl] &= ~(1 << bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, - pp->irq_status[ctrl]); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, + ~pp->irq_status[ctrl]); } raw_spin_unlock_irqrestore(&pp->lock, flags); @@ -191,8 +188,8 @@ static void dw_pci_bottom_unmask(struct irq_data *data) bit = data->hwirq % MAX_MSI_IRQS_PER_CTRL; pp->irq_status[ctrl] |= 1 << bit; - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, - pp->irq_status[ctrl]); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, + ~pp->irq_status[ctrl]); } raw_spin_unlock_irqrestore(&pp->lock, flags); @@ -200,13 +197,22 @@ static void dw_pci_bottom_unmask(struct irq_data *data) static void dw_pci_bottom_ack(struct irq_data *d) { - struct msi_desc *msi = irq_data_get_msi_desc(d); - struct pcie_port *pp; + struct pcie_port *pp = irq_data_get_irq_chip_data(d); + unsigned int res, bit, ctrl; + unsigned long flags; + + ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; + res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; + bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; + + raw_spin_lock_irqsave(&pp->lock, flags); - pp = msi_desc_to_pci_sysdata(msi); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + res, 4, 1 << bit); if (pp->ops->msi_irq_ack) pp->ops->msi_irq_ack(d->hwirq, pp); + + raw_spin_unlock_irqrestore(&pp->lock, flags); } static struct irq_chip dw_pci_msi_bottom_irq_chip = { @@ -658,10 +664,15 @@ void dw_pcie_setup_rc(struct pcie_port *pp) num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; /* Initialize IRQ Status array */ - for (ctrl = 0; ctrl < num_ctrls; ctrl++) - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + + for (ctrl = 0; ctrl < num_ctrls; ctrl++) { + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + + (ctrl * MSI_REG_CTRL_BLOCK_SIZE), + 4, ~0); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + (ctrl * MSI_REG_CTRL_BLOCK_SIZE), - 4, &pp->irq_status[ctrl]); + 4, ~0); + pp->irq_status[ctrl] = 0; + } /* Setup RC BARs */ dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004); @@ -699,6 +710,9 @@ void dw_pcie_setup_rc(struct pcie_port *pp) dev_dbg(pci->dev, "iATU unroll: %s\n", pci->iatu_unroll_enabled ? "enabled" : "disabled"); + if (pci->iatu_unroll_enabled && !pci->atu_base) + pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET; + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0, PCIE_ATU_TYPE_MEM, pp->mem_base, pp->mem_bus_addr, pp->mem_size); diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 2153956a0b20..93ef8c31fb39 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -93,7 +93,7 @@ static u32 dw_pcie_readl_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg) { u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - return dw_pcie_readl_dbi(pci, offset + reg); + return dw_pcie_readl_atu(pci, offset + reg); } static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg, @@ -101,7 +101,7 @@ static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg, { u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - dw_pcie_writel_dbi(pci, offset + reg, val); + dw_pcie_writel_atu(pci, offset + reg, val); } static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, int index, @@ -187,7 +187,7 @@ static u32 dw_pcie_readl_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg) { u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index); - return dw_pcie_readl_dbi(pci, offset + reg); + return dw_pcie_readl_atu(pci, offset + reg); } static void dw_pcie_writel_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg, @@ -195,7 +195,7 @@ static void dw_pcie_writel_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg, { u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index); - dw_pcie_writel_dbi(pci, offset + reg, val); + dw_pcie_writel_atu(pci, offset + reg, val); } static int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, int index, diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 0989d880ac46..9943d8c68335 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -92,12 +92,20 @@ #define PCIE_ATU_UNR_LOWER_TARGET 0x14 #define PCIE_ATU_UNR_UPPER_TARGET 0x18 +/* + * The default address offset between dbi_base and atu_base. Root controller + * drivers are not required to initialize atu_base if the offset matches this + * default; the driver core automatically derives atu_base from dbi_base using + * this offset, if atu_base not set. + */ +#define DEFAULT_DBI_ATU_OFFSET (0x3 << 20) + /* Register address builder */ -#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) \ - ((0x3 << 20) | ((region) << 9)) +#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) \ + ((region) << 9) -#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \ - ((0x3 << 20) | ((region) << 9) | (0x1 << 8)) +#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region) \ + (((region) << 9) | (0x1 << 8)) #define MAX_MSI_IRQS 256 #define MAX_MSI_IRQS_PER_CTRL 32 @@ -219,6 +227,8 @@ struct dw_pcie { struct device *dev; void __iomem *dbi_base; void __iomem *dbi_base2; + /* Used when iatu_unroll_enabled is true */ + void __iomem *atu_base; u32 num_viewport; u8 iatu_unroll_enabled; struct pcie_port pp; @@ -289,6 +299,16 @@ static inline u32 dw_pcie_readl_dbi2(struct dw_pcie *pci, u32 reg) return __dw_pcie_read_dbi(pci, pci->dbi_base2, reg, 0x4); } +static inline void dw_pcie_writel_atu(struct dw_pcie *pci, u32 reg, u32 val) +{ + __dw_pcie_write_dbi(pci, pci->atu_base, reg, 0x4, val); +} + +static inline u32 dw_pcie_readl_atu(struct dw_pcie *pci, u32 reg) +{ + return __dw_pcie_read_dbi(pci, pci->atu_base, reg, 0x4); +} + static inline void dw_pcie_dbi_ro_wr_en(struct dw_pcie *pci) { u32 reg; diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index 7b32e619b959..954bc2b74bbc 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -202,7 +202,7 @@ static int histb_pcie_host_init(struct pcie_port *pp) return 0; } -static struct dw_pcie_host_ops histb_pcie_host_ops = { +static const struct dw_pcie_host_ops histb_pcie_host_ops = { .rd_own_conf = histb_pcie_rd_own_conf, .wr_own_conf = histb_pcie_wr_own_conf, .host_init = histb_pcie_host_init, diff --git a/drivers/pci/controller/dwc/pcie-uniphier.c b/drivers/pci/controller/dwc/pcie-uniphier.c new file mode 100644 index 000000000000..d5dc40289cce --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-uniphier.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe host controller driver for UniPhier SoCs + * Copyright 2018 Socionext Inc. + * Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> + */ + +#include <linux/bitops.h> +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/pci.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +#include "pcie-designware.h" + +#define PCL_PINCTRL0 0x002c +#define PCL_PERST_PLDN_REGEN BIT(12) +#define PCL_PERST_NOE_REGEN BIT(11) +#define PCL_PERST_OUT_REGEN BIT(8) +#define PCL_PERST_PLDN_REGVAL BIT(4) +#define PCL_PERST_NOE_REGVAL BIT(3) +#define PCL_PERST_OUT_REGVAL BIT(0) + +#define PCL_PIPEMON 0x0044 +#define PCL_PCLK_ALIVE BIT(15) + +#define PCL_APP_READY_CTRL 0x8008 +#define PCL_APP_LTSSM_ENABLE BIT(0) + +#define PCL_APP_PM0 0x8078 +#define PCL_SYS_AUX_PWR_DET BIT(8) + +#define PCL_RCV_INT 0x8108 +#define PCL_RCV_INT_ALL_ENABLE GENMASK(20, 17) +#define PCL_CFG_BW_MGT_STATUS BIT(4) +#define PCL_CFG_LINK_AUTO_BW_STATUS BIT(3) +#define PCL_CFG_AER_RC_ERR_MSI_STATUS BIT(2) +#define PCL_CFG_PME_MSI_STATUS BIT(1) + +#define PCL_RCV_INTX 0x810c +#define PCL_RCV_INTX_ALL_ENABLE GENMASK(19, 16) +#define PCL_RCV_INTX_ALL_MASK GENMASK(11, 8) +#define PCL_RCV_INTX_MASK_SHIFT 8 +#define PCL_RCV_INTX_ALL_STATUS GENMASK(3, 0) +#define PCL_RCV_INTX_STATUS_SHIFT 0 + +#define PCL_STATUS_LINK 0x8140 +#define PCL_RDLH_LINK_UP BIT(1) +#define PCL_XMLH_LINK_UP BIT(0) + +struct uniphier_pcie_priv { + void __iomem *base; + struct dw_pcie pci; + struct clk *clk; + struct reset_control *rst; + struct phy *phy; + struct irq_domain *legacy_irq_domain; +}; + +#define to_uniphier_pcie(x) dev_get_drvdata((x)->dev) + +static void uniphier_pcie_ltssm_enable(struct uniphier_pcie_priv *priv, + bool enable) +{ + u32 val; + + val = readl(priv->base + PCL_APP_READY_CTRL); + if (enable) + val |= PCL_APP_LTSSM_ENABLE; + else + val &= ~PCL_APP_LTSSM_ENABLE; + writel(val, priv->base + PCL_APP_READY_CTRL); +} + +static void uniphier_pcie_init_rc(struct uniphier_pcie_priv *priv) +{ + u32 val; + + /* use auxiliary power detection */ + val = readl(priv->base + PCL_APP_PM0); + val |= PCL_SYS_AUX_PWR_DET; + writel(val, priv->base + PCL_APP_PM0); + + /* assert PERST# */ + val = readl(priv->base + PCL_PINCTRL0); + val &= ~(PCL_PERST_NOE_REGVAL | PCL_PERST_OUT_REGVAL + | PCL_PERST_PLDN_REGVAL); + val |= PCL_PERST_NOE_REGEN | PCL_PERST_OUT_REGEN + | PCL_PERST_PLDN_REGEN; + writel(val, priv->base + PCL_PINCTRL0); + + uniphier_pcie_ltssm_enable(priv, false); + + usleep_range(100000, 200000); + + /* deassert PERST# */ + val = readl(priv->base + PCL_PINCTRL0); + val |= PCL_PERST_OUT_REGVAL | PCL_PERST_OUT_REGEN; + writel(val, priv->base + PCL_PINCTRL0); +} + +static int uniphier_pcie_wait_rc(struct uniphier_pcie_priv *priv) +{ + u32 status; + int ret; + + /* wait PIPE clock */ + ret = readl_poll_timeout(priv->base + PCL_PIPEMON, status, + status & PCL_PCLK_ALIVE, 100000, 1000000); + if (ret) { + dev_err(priv->pci.dev, + "Failed to initialize controller in RC mode\n"); + return ret; + } + + return 0; +} + +static int uniphier_pcie_link_up(struct dw_pcie *pci) +{ + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + u32 val, mask; + + val = readl(priv->base + PCL_STATUS_LINK); + mask = PCL_RDLH_LINK_UP | PCL_XMLH_LINK_UP; + + return (val & mask) == mask; +} + +static int uniphier_pcie_establish_link(struct dw_pcie *pci) +{ + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + + if (dw_pcie_link_up(pci)) + return 0; + + uniphier_pcie_ltssm_enable(priv, true); + + return dw_pcie_wait_for_link(pci); +} + +static void uniphier_pcie_stop_link(struct dw_pcie *pci) +{ + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + + uniphier_pcie_ltssm_enable(priv, false); +} + +static void uniphier_pcie_irq_enable(struct uniphier_pcie_priv *priv) +{ + writel(PCL_RCV_INT_ALL_ENABLE, priv->base + PCL_RCV_INT); + writel(PCL_RCV_INTX_ALL_ENABLE, priv->base + PCL_RCV_INTX); +} + +static void uniphier_pcie_irq_disable(struct uniphier_pcie_priv *priv) +{ + writel(0, priv->base + PCL_RCV_INT); + writel(0, priv->base + PCL_RCV_INTX); +} + +static void uniphier_pcie_irq_ack(struct irq_data *d) +{ + struct pcie_port *pp = irq_data_get_irq_chip_data(d); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + u32 val; + + val = readl(priv->base + PCL_RCV_INTX); + val &= ~PCL_RCV_INTX_ALL_STATUS; + val |= BIT(irqd_to_hwirq(d) + PCL_RCV_INTX_STATUS_SHIFT); + writel(val, priv->base + PCL_RCV_INTX); +} + +static void uniphier_pcie_irq_mask(struct irq_data *d) +{ + struct pcie_port *pp = irq_data_get_irq_chip_data(d); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + u32 val; + + val = readl(priv->base + PCL_RCV_INTX); + val &= ~PCL_RCV_INTX_ALL_MASK; + val |= BIT(irqd_to_hwirq(d) + PCL_RCV_INTX_MASK_SHIFT); + writel(val, priv->base + PCL_RCV_INTX); +} + +static void uniphier_pcie_irq_unmask(struct irq_data *d) +{ + struct pcie_port *pp = irq_data_get_irq_chip_data(d); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + u32 val; + + val = readl(priv->base + PCL_RCV_INTX); + val &= ~PCL_RCV_INTX_ALL_MASK; + val &= ~BIT(irqd_to_hwirq(d) + PCL_RCV_INTX_MASK_SHIFT); + writel(val, priv->base + PCL_RCV_INTX); +} + +static struct irq_chip uniphier_pcie_irq_chip = { + .name = "PCI", + .irq_ack = uniphier_pcie_irq_ack, + .irq_mask = uniphier_pcie_irq_mask, + .irq_unmask = uniphier_pcie_irq_unmask, +}; + +static int uniphier_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &uniphier_pcie_irq_chip, + handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +} + +static const struct irq_domain_ops uniphier_intx_domain_ops = { + .map = uniphier_pcie_intx_map, +}; + +static void uniphier_pcie_irq_handler(struct irq_desc *desc) +{ + struct pcie_port *pp = irq_desc_get_handler_data(desc); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long reg; + u32 val, bit, virq; + + /* INT for debug */ + val = readl(priv->base + PCL_RCV_INT); + + if (val & PCL_CFG_BW_MGT_STATUS) + dev_dbg(pci->dev, "Link Bandwidth Management Event\n"); + if (val & PCL_CFG_LINK_AUTO_BW_STATUS) + dev_dbg(pci->dev, "Link Autonomous Bandwidth Event\n"); + if (val & PCL_CFG_AER_RC_ERR_MSI_STATUS) + dev_dbg(pci->dev, "Root Error\n"); + if (val & PCL_CFG_PME_MSI_STATUS) + dev_dbg(pci->dev, "PME Interrupt\n"); + + writel(val, priv->base + PCL_RCV_INT); + + /* INTx */ + chained_irq_enter(chip, desc); + + val = readl(priv->base + PCL_RCV_INTX); + reg = FIELD_GET(PCL_RCV_INTX_ALL_STATUS, val); + + for_each_set_bit(bit, ®, PCI_NUM_INTX) { + virq = irq_linear_revmap(priv->legacy_irq_domain, bit); + generic_handle_irq(virq); + } + + chained_irq_exit(chip, desc); +} + +static int uniphier_pcie_config_legacy_irq(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + struct device_node *np = pci->dev->of_node; + struct device_node *np_intc; + + np_intc = of_get_child_by_name(np, "legacy-interrupt-controller"); + if (!np_intc) { + dev_err(pci->dev, "Failed to get legacy-interrupt-controller node\n"); + return -EINVAL; + } + + pp->irq = irq_of_parse_and_map(np_intc, 0); + if (!pp->irq) { + dev_err(pci->dev, "Failed to get an IRQ entry in legacy-interrupt-controller\n"); + return -EINVAL; + } + + priv->legacy_irq_domain = irq_domain_add_linear(np_intc, PCI_NUM_INTX, + &uniphier_intx_domain_ops, pp); + if (!priv->legacy_irq_domain) { + dev_err(pci->dev, "Failed to get INTx domain\n"); + return -ENODEV; + } + + irq_set_chained_handler_and_data(pp->irq, uniphier_pcie_irq_handler, + pp); + + return 0; +} + +static int uniphier_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); + int ret; + + ret = uniphier_pcie_config_legacy_irq(pp); + if (ret) + return ret; + + uniphier_pcie_irq_enable(priv); + + dw_pcie_setup_rc(pp); + ret = uniphier_pcie_establish_link(pci); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); + + return 0; +} + +static const struct dw_pcie_host_ops uniphier_pcie_host_ops = { + .host_init = uniphier_pcie_host_init, +}; + +static int uniphier_add_pcie_port(struct uniphier_pcie_priv *priv, + struct platform_device *pdev) +{ + struct dw_pcie *pci = &priv->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; + int ret; + + pp->ops = &uniphier_pcie_host_ops; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq_byname(pdev, "msi"); + if (pp->msi_irq < 0) + return pp->msi_irq; + } + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "Failed to initialize host (%d)\n", ret); + return ret; + } + + return 0; +} + +static int uniphier_pcie_host_enable(struct uniphier_pcie_priv *priv) +{ + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + ret = reset_control_deassert(priv->rst); + if (ret) + goto out_clk_disable; + + uniphier_pcie_init_rc(priv); + + ret = phy_init(priv->phy); + if (ret) + goto out_rst_assert; + + ret = uniphier_pcie_wait_rc(priv); + if (ret) + goto out_phy_exit; + + return 0; + +out_phy_exit: + phy_exit(priv->phy); +out_rst_assert: + reset_control_assert(priv->rst); +out_clk_disable: + clk_disable_unprepare(priv->clk); + + return ret; +} + +static void uniphier_pcie_host_disable(struct uniphier_pcie_priv *priv) +{ + uniphier_pcie_irq_disable(priv); + phy_exit(priv->phy); + reset_control_assert(priv->rst); + clk_disable_unprepare(priv->clk); +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .start_link = uniphier_pcie_establish_link, + .stop_link = uniphier_pcie_stop_link, + .link_up = uniphier_pcie_link_up, +}; + +static int uniphier_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uniphier_pcie_priv *priv; + struct resource *res; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pci.dev = dev; + priv->pci.ops = &dw_pcie_ops; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + priv->pci.dbi_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(priv->pci.dbi_base)) + return PTR_ERR(priv->pci.dbi_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "link"); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + priv->rst = devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(priv->rst)) + return PTR_ERR(priv->rst); + + priv->phy = devm_phy_optional_get(dev, "pcie-phy"); + if (IS_ERR(priv->phy)) + return PTR_ERR(priv->phy); + + platform_set_drvdata(pdev, priv); + + ret = uniphier_pcie_host_enable(priv); + if (ret) + return ret; + + return uniphier_add_pcie_port(priv, pdev); +} + +static int uniphier_pcie_remove(struct platform_device *pdev) +{ + struct uniphier_pcie_priv *priv = platform_get_drvdata(pdev); + + uniphier_pcie_host_disable(priv); + + return 0; +} + +static const struct of_device_id uniphier_pcie_match[] = { + { .compatible = "socionext,uniphier-pcie", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, uniphier_pcie_match); + +static struct platform_driver uniphier_pcie_driver = { + .probe = uniphier_pcie_probe, + .remove = uniphier_pcie_remove, + .driver = { + .name = "uniphier-pcie", + .of_match_table = uniphier_pcie_match, + }, +}; +builtin_platform_driver(uniphier_pcie_driver); + +MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); +MODULE_DESCRIPTION("UniPhier PCIe host controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index d069a76cbb95..55e471c18e8d 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -161,7 +161,6 @@ struct mtk_pcie_soc { * @obff_ck: pointer to OBFF functional block operating clock * @pipe_ck: pointer to LTSSM and PHY/MAC layer operating clock * @phy: pointer to PHY control block - * @lane: lane count * @slot: port slot * @irq: GIC irq * @irq_domain: legacy INTx IRQ domain @@ -182,7 +181,6 @@ struct mtk_pcie_port { struct clk *obff_ck; struct clk *pipe_ck; struct phy *phy; - u32 lane; u32 slot; int irq; struct irq_domain *irq_domain; @@ -197,29 +195,20 @@ struct mtk_pcie_port { * @dev: pointer to PCIe device * @base: IO mapped register base * @free_ck: free-run reference clock - * @io: IO resource - * @pio: PIO resource * @mem: non-prefetchable memory resource - * @busn: bus range - * @offset: IO / Memory offset * @ports: pointer to PCIe port information * @soc: pointer to SoC-dependent operations + * @busnr: root bus number */ struct mtk_pcie { struct device *dev; void __iomem *base; struct clk *free_ck; - struct resource io; - struct resource pio; struct resource mem; - struct resource busn; - struct { - resource_size_t mem; - resource_size_t io; - } offset; struct list_head ports; const struct mtk_pcie_soc *soc; + unsigned int busnr; }; static void mtk_pcie_subsys_powerdown(struct mtk_pcie *pcie) @@ -904,12 +893,6 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, if (!port) return -ENOMEM; - err = of_property_read_u32(node, "num-lanes", &port->lane); - if (err) { - dev_err(dev, "missing num-lanes property\n"); - return err; - } - snprintf(name, sizeof(name), "port%d", slot); regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); port->base = devm_ioremap_resource(dev, regs); @@ -1045,55 +1028,43 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) { struct device *dev = pcie->dev; struct device_node *node = dev->of_node, *child; - struct of_pci_range_parser parser; - struct of_pci_range range; - struct resource res; struct mtk_pcie_port *port, *tmp; + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct list_head *windows = &host->windows; + struct resource_entry *win, *tmp_win; + resource_size_t io_base; int err; - if (of_pci_range_parser_init(&parser, node)) { - dev_err(dev, "missing \"ranges\" property\n"); - return -EINVAL; - } + err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, + windows, &io_base); + if (err) + return err; - for_each_of_pci_range(&parser, &range) { - err = of_pci_range_to_resource(&range, node, &res); - if (err < 0) - return err; + err = devm_request_pci_bus_resources(dev, windows); + if (err < 0) + return err; - switch (res.flags & IORESOURCE_TYPE_BITS) { + /* Get the I/O and memory ranges from DT */ + resource_list_for_each_entry_safe(win, tmp_win, windows) { + switch (resource_type(win->res)) { case IORESOURCE_IO: - pcie->offset.io = res.start - range.pci_addr; - - memcpy(&pcie->pio, &res, sizeof(res)); - pcie->pio.name = node->full_name; - - pcie->io.start = range.cpu_addr; - pcie->io.end = range.cpu_addr + range.size - 1; - pcie->io.flags = IORESOURCE_MEM; - pcie->io.name = "I/O"; - - memcpy(&res, &pcie->io, sizeof(res)); + err = devm_pci_remap_iospace(dev, win->res, io_base); + if (err) { + dev_warn(dev, "error %d: failed to map resource %pR\n", + err, win->res); + resource_list_destroy_entry(win); + } break; - case IORESOURCE_MEM: - pcie->offset.mem = res.start - range.pci_addr; - - memcpy(&pcie->mem, &res, sizeof(res)); + memcpy(&pcie->mem, win->res, sizeof(*win->res)); pcie->mem.name = "non-prefetchable"; break; + case IORESOURCE_BUS: + pcie->busnr = win->res->start; + break; } } - err = of_pci_parse_bus_range(node, &pcie->busn); - if (err < 0) { - dev_err(dev, "failed to parse bus ranges property: %d\n", err); - pcie->busn.name = node->name; - pcie->busn.start = 0; - pcie->busn.end = 0xff; - pcie->busn.flags = IORESOURCE_BUS; - } - for_each_available_child_of_node(node, child) { int slot; @@ -1125,28 +1096,6 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) return 0; } -static int mtk_pcie_request_resources(struct mtk_pcie *pcie) -{ - struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); - struct list_head *windows = &host->windows; - struct device *dev = pcie->dev; - int err; - - pci_add_resource_offset(windows, &pcie->pio, pcie->offset.io); - pci_add_resource_offset(windows, &pcie->mem, pcie->offset.mem); - pci_add_resource(windows, &pcie->busn); - - err = devm_request_pci_bus_resources(dev, windows); - if (err < 0) - return err; - - err = devm_pci_remap_iospace(dev, &pcie->pio, pcie->io.start); - if (err) - return err; - - return 0; -} - static int mtk_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1169,11 +1118,7 @@ static int mtk_pcie_probe(struct platform_device *pdev) if (err) return err; - err = mtk_pcie_request_resources(pcie); - if (err) - goto put_resources; - - host->busnr = pcie->busn.start; + host->busnr = pcie->busnr; host->dev.parent = pcie->dev; host->ops = pcie->soc->ops; host->map_irq = of_irq_parse_and_map_pci; diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 9616eca3182f..3aa115ed3a65 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -252,6 +252,27 @@ int __weak pcibios_sriov_disable(struct pci_dev *pdev) return 0; } +static int sriov_add_vfs(struct pci_dev *dev, u16 num_vfs) +{ + unsigned int i; + int rc; + + if (dev->no_vf_scan) + return 0; + + for (i = 0; i < num_vfs; i++) { + rc = pci_iov_add_virtfn(dev, i); + if (rc) + goto failed; + } + return 0; +failed: + while (i--) + pci_iov_remove_virtfn(dev, i); + + return rc; +} + static int sriov_enable(struct pci_dev *dev, int nr_virtfn) { int rc; @@ -337,21 +358,15 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) msleep(100); pci_cfg_access_unlock(dev); - for (i = 0; i < initial; i++) { - rc = pci_iov_add_virtfn(dev, i); - if (rc) - goto failed; - } + rc = sriov_add_vfs(dev, initial); + if (rc) + goto err_pcibios; kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); iov->num_VFs = nr_virtfn; return 0; -failed: - while (i--) - pci_iov_remove_virtfn(dev, i); - err_pcibios: iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); pci_cfg_access_lock(dev); @@ -368,17 +383,26 @@ err_pcibios: return rc; } -static void sriov_disable(struct pci_dev *dev) +static void sriov_del_vfs(struct pci_dev *dev) { - int i; struct pci_sriov *iov = dev->sriov; + int i; - if (!iov->num_VFs) + if (dev->no_vf_scan) return; for (i = 0; i < iov->num_VFs; i++) pci_iov_remove_virtfn(dev, i); +} + +static void sriov_disable(struct pci_dev *dev) +{ + struct pci_sriov *iov = dev->sriov; + + if (!iov->num_VFs) + return; + sriov_del_vfs(dev); iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); pci_cfg_access_lock(dev); pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index a2eb25271c96..c52298d76e64 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -416,7 +416,7 @@ static int upstream_bridge_distance_warn(struct pci_dev *provider, * * Returns -1 if any of the clients are not compatible (behind the same * root port as the provider), otherwise returns a positive number where - * a lower number is the preferrable choice. (If there's one client + * a lower number is the preferable choice. (If there's one client * that's the same as the provider it will return 0, which is best choice). * * For now, "compatible" means the provider and the clients are all behind @@ -487,7 +487,7 @@ EXPORT_SYMBOL_GPL(pci_has_p2pmem); * @num_clients: number of client devices in the list * * If multiple devices are behind the same switch, the one "closest" to the - * client devices in use will be chosen first. (So if one of the providers are + * client devices in use will be chosen first. (So if one of the providers is * the same as one of the clients, that provider will be used ahead of any * other providers that are unrelated). If multiple providers are an equal * distance away, one will be chosen at random. @@ -574,7 +574,7 @@ EXPORT_SYMBOL_GPL(pci_alloc_p2pmem); * pci_free_p2pmem - free peer-to-peer DMA memory * @pdev: the device the memory was allocated from * @addr: address of the memory that was allocated - * @size: number of bytes that was allocated + * @size: number of bytes that were allocated */ void pci_free_p2pmem(struct pci_dev *pdev, void *addr, size_t size) { @@ -611,7 +611,7 @@ EXPORT_SYMBOL_GPL(pci_p2pmem_virt_to_bus); * @nents: the number of SG entries in the list * @length: number of bytes to allocate * - * Returns 0 on success + * Return: %NULL on error or &struct scatterlist pointer and @nents on success */ struct scatterlist *pci_p2pmem_alloc_sgl(struct pci_dev *pdev, unsigned int *nents, u32 length) @@ -667,7 +667,7 @@ EXPORT_SYMBOL_GPL(pci_p2pmem_free_sgl); * * Published memory can be used by other PCI device drivers for * peer-2-peer DMA operations. Non-published memory is reserved for - * exlusive use of the device driver that registers the peer-to-peer + * exclusive use of the device driver that registers the peer-to-peer * memory. */ void pci_p2pmem_publish(struct pci_dev *pdev, bool publish) @@ -727,7 +727,7 @@ EXPORT_SYMBOL_GPL(pci_p2pdma_map_sg); * @use_p2pdma: returns whether to enable p2pdma or not * * Parses an attribute value to decide whether to enable p2pdma. - * The value can select a PCI device (using it's full BDF device + * The value can select a PCI device (using its full BDF device * name) or a boolean (in any format strtobool() accepts). A false * value disables p2pdma, a true value expects the caller * to automatically find a compatible device and specifying a PCI device @@ -778,7 +778,7 @@ EXPORT_SYMBOL_GPL(pci_p2pdma_enable_store); * whether p2pdma is enabled * @page: contents of the stored value * @p2p_dev: the selected p2p device (NULL if no device is selected) - * @use_p2pdma: whether p2pdme has been enabled + * @use_p2pdma: whether p2pdma has been enabled * * Attributes that use pci_p2pdma_enable_store() should use this function * to show the value of the attribute. diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index ea55444e6ead..79b1610a8beb 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1251,30 +1251,29 @@ static int pci_pm_runtime_suspend(struct device *dev) return 0; } - if (!pm || !pm->runtime_suspend) - return -ENOSYS; - pci_dev->state_saved = false; - error = pm->runtime_suspend(dev); - if (error) { + if (pm && pm->runtime_suspend) { + error = pm->runtime_suspend(dev); /* * -EBUSY and -EAGAIN is used to request the runtime PM core * to schedule a new suspend, so log the event only with debug * log level. */ - if (error == -EBUSY || error == -EAGAIN) + if (error == -EBUSY || error == -EAGAIN) { dev_dbg(dev, "can't suspend now (%pf returned %d)\n", pm->runtime_suspend, error); - else + return error; + } else if (error) { dev_err(dev, "can't suspend (%pf returned %d)\n", pm->runtime_suspend, error); - - return error; + return error; + } } pci_fixup_device(pci_fixup_suspend, pci_dev); - if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0 + if (pm && pm->runtime_suspend + && !pci_dev->state_saved && pci_dev->current_state != PCI_D0 && pci_dev->current_state != PCI_UNKNOWN) { WARN_ONCE(pci_dev->current_state != prev, "PCI PM: State of device not saved by %pF\n", @@ -1292,7 +1291,7 @@ static int pci_pm_runtime_suspend(struct device *dev) static int pci_pm_runtime_resume(struct device *dev) { - int rc; + int rc = 0; struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; @@ -1306,14 +1305,12 @@ static int pci_pm_runtime_resume(struct device *dev) if (!pci_dev->driver) return 0; - if (!pm || !pm->runtime_resume) - return -ENOSYS; - pci_fixup_device(pci_fixup_resume_early, pci_dev); pci_enable_wake(pci_dev, PCI_D0, false); pci_fixup_device(pci_fixup_resume, pci_dev); - rc = pm->runtime_resume(dev); + if (pm && pm->runtime_resume) + rc = pm->runtime_resume(dev); pci_dev->runtime_d3cold = false; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 662b7457db23..224d88634115 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -2,6 +2,8 @@ #ifndef DRIVERS_PCI_H #define DRIVERS_PCI_H +#include <linux/pci.h> + #define PCI_FIND_CAP_TTL 48 #define PCI_VSEC_ID_INTEL_TBT 0x1234 /* Thunderbolt */ diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index f78860ce884b..727e3c1ef9a4 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -53,8 +53,6 @@ struct pcie_link_state { struct pcie_link_state *root; /* pointer to the root port link */ struct pcie_link_state *parent; /* pointer to the parent Link state */ struct list_head sibling; /* node in link_list */ - struct list_head children; /* list of child link states */ - struct list_head link; /* node in parent's children list */ /* ASPM state */ u32 aspm_support:7; /* Supported ASPM state */ @@ -850,8 +848,6 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev) return NULL; INIT_LIST_HEAD(&link->sibling); - INIT_LIST_HEAD(&link->children); - INIT_LIST_HEAD(&link->link); link->pdev = pdev; link->downstream = pci_function_0(pdev->subordinate); @@ -877,7 +873,6 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev) link->parent = parent; link->root = link->parent->root; - list_add(&link->link, &parent->children); } list_add(&link->sibling, &link_list); @@ -1001,7 +996,6 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) /* All functions are removed, so just disable ASPM for the link */ pcie_config_aspm_link(link, 0); list_del(&link->sibling); - list_del(&link->link); /* Clock PM is for endpoint device */ free_link_state(link); diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index e495f04394d0..fbbf00b0992e 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -71,19 +71,19 @@ static inline void *get_service_data(struct pcie_device *dev) struct pcie_port_service_driver { const char *name; - int (*probe) (struct pcie_device *dev); - void (*remove) (struct pcie_device *dev); - int (*suspend) (struct pcie_device *dev); - int (*resume_noirq) (struct pcie_device *dev); - int (*resume) (struct pcie_device *dev); - int (*runtime_suspend) (struct pcie_device *dev); - int (*runtime_resume) (struct pcie_device *dev); + int (*probe)(struct pcie_device *dev); + void (*remove)(struct pcie_device *dev); + int (*suspend)(struct pcie_device *dev); + int (*resume_noirq)(struct pcie_device *dev); + int (*resume)(struct pcie_device *dev); + int (*runtime_suspend)(struct pcie_device *dev); + int (*runtime_resume)(struct pcie_device *dev); /* Device driver may resume normal operations */ void (*error_resume)(struct pci_dev *dev); /* Link Reset Capability - AER service driver specific */ - pci_ers_result_t (*reset_link) (struct pci_dev *dev); + pci_ers_result_t (*reset_link)(struct pci_dev *dev); int port_type; /* Type of the port this driver can handle */ u32 service; /* Port service this device represents */ diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 7ac035af39f0..6fa1627ce08d 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -52,7 +52,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf, nbytes = size - pos; cnt = nbytes; - if (!access_ok(VERIFY_WRITE, buf, cnt)) + if (!access_ok(buf, cnt)) return -EINVAL; pci_config_pm_runtime_get(dev); @@ -125,7 +125,7 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, nbytes = size - pos; cnt = nbytes; - if (!access_ok(VERIFY_READ, buf, cnt)) + if (!access_ok(buf, cnt)) return -EINVAL; pci_config_pm_runtime_get(dev); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 4700d24e5d55..b0a413f3f7ca 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -619,6 +619,30 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB, quirk_amd_nl_class); /* + * Synopsys USB 3.x host HAPS platform has a class code of + * PCI_CLASS_SERIAL_USB_XHCI, and xhci driver can claim it. However, these + * devices should use dwc3-haps driver. Change these devices' class code to + * PCI_CLASS_SERIAL_USB_DEVICE to prevent the xhci-pci driver from claiming + * them. + */ +static void quirk_synopsys_haps(struct pci_dev *pdev) +{ + u32 class = pdev->class; + + switch (pdev->device) { + case PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3: + case PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI: + case PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31: + 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); + break; + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SYNOPSYS, PCI_ANY_ID, + quirk_synopsys_haps); + +/* * Let's make the southbridge information explicit instead of having to * worry about people probing the ACPI areas, for example.. (Yes, it * happens, and if you read the wrong ACPI register it will put the machine diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 54a8b30dda38..6c5536d3d42a 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -13,7 +13,7 @@ #include <linux/uaccess.h> #include <linux/poll.h> #include <linux/wait.h> - +#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/nospec.h> MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver"); @@ -25,6 +25,11 @@ static int max_devices = 16; module_param(max_devices, int, 0644); MODULE_PARM_DESC(max_devices, "max number of switchtec device instances"); +static bool use_dma_mrpc = 1; +module_param(use_dma_mrpc, bool, 0644); +MODULE_PARM_DESC(use_dma_mrpc, + "Enable the use of the DMA MRPC feature"); + static dev_t switchtec_devt; static DEFINE_IDA(switchtec_minor_ida); @@ -113,6 +118,19 @@ static void stuser_set_state(struct switchtec_user *stuser, static void mrpc_complete_cmd(struct switchtec_dev *stdev); +static void flush_wc_buf(struct switchtec_dev *stdev) +{ + struct ntb_dbmsg_regs __iomem *mmio_dbmsg; + + /* + * odb (outbound doorbell) register is processed by low latency + * hardware and w/o side effect + */ + mmio_dbmsg = (void __iomem *)stdev->mmio_ntb + + SWITCHTEC_NTB_REG_DBMSG_OFFSET; + ioread32(&mmio_dbmsg->odb); +} + static void mrpc_cmd_submit(struct switchtec_dev *stdev) { /* requires the mrpc_mutex to already be held when called */ @@ -128,16 +146,18 @@ static void mrpc_cmd_submit(struct switchtec_dev *stdev) stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user, list); + if (stdev->dma_mrpc) { + stdev->dma_mrpc->status = SWITCHTEC_MRPC_STATUS_INPROGRESS; + memset(stdev->dma_mrpc->data, 0xFF, SWITCHTEC_MRPC_PAYLOAD_SIZE); + } + stuser_set_state(stuser, MRPC_RUNNING); stdev->mrpc_busy = 1; memcpy_toio(&stdev->mmio_mrpc->input_data, stuser->data, stuser->data_len); + flush_wc_buf(stdev); iowrite32(stuser->cmd, &stdev->mmio_mrpc->cmd); - stuser->status = ioread32(&stdev->mmio_mrpc->status); - if (stuser->status != SWITCHTEC_MRPC_STATUS_INPROGRESS) - mrpc_complete_cmd(stdev); - schedule_delayed_work(&stdev->mrpc_timeout, msecs_to_jiffies(500)); } @@ -170,7 +190,11 @@ static void mrpc_complete_cmd(struct switchtec_dev *stdev) stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user, list); - stuser->status = ioread32(&stdev->mmio_mrpc->status); + if (stdev->dma_mrpc) + stuser->status = stdev->dma_mrpc->status; + else + stuser->status = ioread32(&stdev->mmio_mrpc->status); + if (stuser->status == SWITCHTEC_MRPC_STATUS_INPROGRESS) return; @@ -180,13 +204,19 @@ static void mrpc_complete_cmd(struct switchtec_dev *stdev) if (stuser->status != SWITCHTEC_MRPC_STATUS_DONE) goto out; - stuser->return_code = ioread32(&stdev->mmio_mrpc->ret_value); + if (stdev->dma_mrpc) + stuser->return_code = stdev->dma_mrpc->rtn_code; + else + stuser->return_code = ioread32(&stdev->mmio_mrpc->ret_value); if (stuser->return_code != 0) goto out; - memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data, - stuser->read_len); - + if (stdev->dma_mrpc) + memcpy(stuser->data, &stdev->dma_mrpc->data, + stuser->read_len); + else + memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data, + stuser->read_len); out: complete_all(&stuser->comp); list_del_init(&stuser->list); @@ -221,7 +251,10 @@ static void mrpc_timeout_work(struct work_struct *work) mutex_lock(&stdev->mrpc_mutex); - status = ioread32(&stdev->mmio_mrpc->status); + if (stdev->dma_mrpc) + status = stdev->dma_mrpc->status; + else + status = ioread32(&stdev->mmio_mrpc->status); if (status == SWITCHTEC_MRPC_STATUS_INPROGRESS) { schedule_delayed_work(&stdev->mrpc_timeout, msecs_to_jiffies(500)); @@ -229,7 +262,6 @@ static void mrpc_timeout_work(struct work_struct *work) } mrpc_complete_cmd(stdev); - out: mutex_unlock(&stdev->mrpc_mutex); } @@ -800,6 +832,7 @@ static int ioctl_event_ctl(struct switchtec_dev *stdev, { int ret; int nr_idxs; + unsigned int event_flags; struct switchtec_ioctl_event_ctl ctl; if (copy_from_user(&ctl, uctl, sizeof(ctl))) @@ -821,7 +854,9 @@ static int ioctl_event_ctl(struct switchtec_dev *stdev, else return -EINVAL; + event_flags = ctl.flags; for (ctl.index = 0; ctl.index < nr_idxs; ctl.index++) { + ctl.flags = event_flags; ret = event_ctl(stdev, &ctl); if (ret < 0) return ret; @@ -1017,10 +1052,24 @@ static void enable_link_state_events(struct switchtec_dev *stdev) } } +static void enable_dma_mrpc(struct switchtec_dev *stdev) +{ + writeq(stdev->dma_mrpc_dma_addr, &stdev->mmio_mrpc->dma_addr); + flush_wc_buf(stdev); + iowrite32(SWITCHTEC_DMA_MRPC_EN, &stdev->mmio_mrpc->dma_en); +} + 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); } @@ -1176,10 +1225,27 @@ static irqreturn_t switchtec_event_isr(int irq, void *dev) return ret; } + +static irqreturn_t switchtec_dma_mrpc_isr(int irq, void *dev) +{ + struct switchtec_dev *stdev = dev; + irqreturn_t ret = IRQ_NONE; + + iowrite32(SWITCHTEC_EVENT_CLEAR | + SWITCHTEC_EVENT_EN_IRQ, + &stdev->mmio_part_cfg->mrpc_comp_hdr); + schedule_work(&stdev->mrpc_work); + + ret = IRQ_HANDLED; + return ret; +} + static int switchtec_init_isr(struct switchtec_dev *stdev) { int nvecs; int event_irq; + int dma_mrpc_irq; + int rc; nvecs = pci_alloc_irq_vectors(stdev->pdev, 1, 4, PCI_IRQ_MSIX | PCI_IRQ_MSI); @@ -1194,9 +1260,29 @@ static int switchtec_init_isr(struct switchtec_dev *stdev) if (event_irq < 0) return event_irq; - return devm_request_irq(&stdev->pdev->dev, event_irq, + rc = devm_request_irq(&stdev->pdev->dev, event_irq, switchtec_event_isr, 0, KBUILD_MODNAME, stdev); + + if (rc) + return rc; + + if (!stdev->dma_mrpc) + return rc; + + dma_mrpc_irq = ioread32(&stdev->mmio_mrpc->dma_vector); + if (dma_mrpc_irq < 0 || dma_mrpc_irq >= nvecs) + return -EFAULT; + + dma_mrpc_irq = pci_irq_vector(stdev->pdev, dma_mrpc_irq); + if (dma_mrpc_irq < 0) + return dma_mrpc_irq; + + rc = devm_request_irq(&stdev->pdev->dev, dma_mrpc_irq, + switchtec_dma_mrpc_isr, 0, + KBUILD_MODNAME, stdev); + + return rc; } static void init_pff(struct switchtec_dev *stdev) @@ -1232,19 +1318,38 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, struct pci_dev *pdev) { int rc; + void __iomem *map; + unsigned long res_start, res_len; rc = pcim_enable_device(pdev); if (rc) return rc; - rc = pcim_iomap_regions(pdev, 0x1, KBUILD_MODNAME); + rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)); if (rc) return rc; pci_set_master(pdev); - stdev->mmio = pcim_iomap_table(pdev)[0]; - stdev->mmio_mrpc = stdev->mmio + SWITCHTEC_GAS_MRPC_OFFSET; + res_start = pci_resource_start(pdev, 0); + res_len = pci_resource_len(pdev, 0); + + if (!devm_request_mem_region(&pdev->dev, res_start, + res_len, KBUILD_MODNAME)) + return -EBUSY; + + stdev->mmio_mrpc = devm_ioremap_wc(&pdev->dev, res_start, + SWITCHTEC_GAS_TOP_CFG_OFFSET); + if (!stdev->mmio_mrpc) + return -ENOMEM; + + map = devm_ioremap(&pdev->dev, + res_start + SWITCHTEC_GAS_TOP_CFG_OFFSET, + res_len - SWITCHTEC_GAS_TOP_CFG_OFFSET); + if (!map) + return -ENOMEM; + + stdev->mmio = map - SWITCHTEC_GAS_TOP_CFG_OFFSET; stdev->mmio_sw_event = stdev->mmio + SWITCHTEC_GAS_SW_EVENT_OFFSET; stdev->mmio_sys_info = stdev->mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET; stdev->mmio_flash_info = stdev->mmio + SWITCHTEC_GAS_FLASH_INFO_OFFSET; @@ -1262,6 +1367,19 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, pci_set_drvdata(pdev, stdev); + if (!use_dma_mrpc) + return 0; + + if (ioread32(&stdev->mmio_mrpc->dma_ver) == 0) + return 0; + + stdev->dma_mrpc = dma_zalloc_coherent(&stdev->pdev->dev, + sizeof(*stdev->dma_mrpc), + &stdev->dma_mrpc_dma_addr, + GFP_KERNEL); + if (stdev->dma_mrpc == NULL) + return -ENOMEM; + return 0; } @@ -1293,6 +1411,9 @@ static int switchtec_pci_probe(struct pci_dev *pdev, &stdev->mmio_part_cfg->mrpc_comp_hdr); enable_link_state_events(stdev); + if (stdev->dma_mrpc) + enable_dma_mrpc(stdev); + rc = cdev_device_add(&stdev->cdev, &stdev->dev); if (rc) goto err_devadd; @@ -1318,7 +1439,6 @@ static void switchtec_pci_remove(struct pci_dev *pdev) cdev_device_del(&stdev->cdev, &stdev->dev); ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt)); dev_info(&stdev->dev, "unregistered.\n"); - stdev_kill(stdev); put_device(&stdev->dev); } diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index c9bdbb463a7e..fab92ba8e566 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -64,6 +64,9 @@ config CARDBUS If unsure, say Y. +config PCMCIA_MAX1600 + tristate + comment "PC-card bridges" config YENTA @@ -192,6 +195,8 @@ config PCMCIA_SA1111 select PCMCIA_SOC_COMMON select PCMCIA_SA11XX_BASE if ARCH_SA1100 select PCMCIA_PXA2XX if ARCH_LUBBOCK && SA1111 + select PCMCIA_MAX1600 if ASSABET_NEPONSET + select PCMCIA_MAX1600 if ARCH_LUBBOCK && SA1111 help Say Y here to include support for SA1111-based PCMCIA or CF sockets, found on the Jornada 720, Graphicsmaster and other @@ -208,6 +213,7 @@ config PCMCIA_PXA2XX || MACH_VPAC270 || MACH_BALLOON3 || MACH_COLIBRI \ || MACH_COLIBRI320 || MACH_H4700) select PCMCIA_SOC_COMMON + select PCMCIA_MAX1600 if MACH_MAINSTONE help Say Y here to include support for the PXA2xx PCMCIA controller diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 28502bd159e0..01779c5c45f3 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_OMAP_CF) += omap_cf.o obj-$(CONFIG_AT91_CF) += at91_cf.o obj-$(CONFIG_ELECTRA_CF) += electra_cf.o obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o +obj-$(CONFIG_PCMCIA_MAX1600) += max1600.o sa1111_cs-y += sa1111_generic.o sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1111_neponset.o diff --git a/drivers/pcmcia/max1600.c b/drivers/pcmcia/max1600.c new file mode 100644 index 000000000000..379875a5e7cd --- /dev/null +++ b/drivers/pcmcia/max1600.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MAX1600 PCMCIA power switch library + * + * Copyright (C) 2016 Russell King + */ +#include <linux/device.h> +#include <linux/module.h> +#include <linux/gpio/consumer.h> +#include <linux/slab.h> +#include "max1600.h" + +static const char *max1600_gpio_name[2][MAX1600_GPIO_MAX] = { + { "a0vcc", "a1vcc", "a0vpp", "a1vpp" }, + { "b0vcc", "b1vcc", "b0vpp", "b1vpp" }, +}; + +int max1600_init(struct device *dev, struct max1600 **ptr, + unsigned int channel, unsigned int code) +{ + struct max1600 *m; + int chan; + int i; + + switch (channel) { + case MAX1600_CHAN_A: + chan = 0; + break; + case MAX1600_CHAN_B: + chan = 1; + break; + default: + return -EINVAL; + } + + if (code != MAX1600_CODE_LOW && code != MAX1600_CODE_HIGH) + return -EINVAL; + + m = devm_kzalloc(dev, sizeof(*m), GFP_KERNEL); + if (!m) + return -ENOMEM; + + m->dev = dev; + m->code = code; + + for (i = 0; i < MAX1600_GPIO_MAX; i++) { + const char *name; + + name = max1600_gpio_name[chan][i]; + if (i != MAX1600_GPIO_0VPP) { + m->gpio[i] = devm_gpiod_get(dev, name, GPIOD_OUT_LOW); + } else { + m->gpio[i] = devm_gpiod_get_optional(dev, name, + GPIOD_OUT_LOW); + if (!m->gpio[i]) + break; + } + if (IS_ERR(m->gpio[i])) + return PTR_ERR(m->gpio[i]); + } + + *ptr = m; + + return 0; +} +EXPORT_SYMBOL_GPL(max1600_init); + +int max1600_configure(struct max1600 *m, unsigned int vcc, unsigned int vpp) +{ + DECLARE_BITMAP(values, MAX1600_GPIO_MAX) = { 0, }; + int n = MAX1600_GPIO_0VPP; + + if (m->gpio[MAX1600_GPIO_0VPP]) { + if (vpp == 0) { + __assign_bit(MAX1600_GPIO_0VPP, values, 0); + __assign_bit(MAX1600_GPIO_1VPP, values, 0); + } else if (vpp == 120) { + __assign_bit(MAX1600_GPIO_0VPP, values, 0); + __assign_bit(MAX1600_GPIO_1VPP, values, 1); + } else if (vpp == vcc) { + __assign_bit(MAX1600_GPIO_0VPP, values, 1); + __assign_bit(MAX1600_GPIO_1VPP, values, 0); + } else { + dev_err(m->dev, "unrecognised Vpp %u.%uV\n", + vpp / 10, vpp % 10); + return -EINVAL; + } + n = MAX1600_GPIO_MAX; + } else if (vpp != vcc && vpp != 0) { + dev_err(m->dev, "no VPP control\n"); + return -EINVAL; + } + + if (vcc == 0) { + __assign_bit(MAX1600_GPIO_0VCC, values, 0); + __assign_bit(MAX1600_GPIO_1VCC, values, 0); + } else if (vcc == 33) { /* VY */ + __assign_bit(MAX1600_GPIO_0VCC, values, 1); + __assign_bit(MAX1600_GPIO_1VCC, values, 0); + } else if (vcc == 50) { /* VX */ + __assign_bit(MAX1600_GPIO_0VCC, values, 0); + __assign_bit(MAX1600_GPIO_1VCC, values, 1); + } else { + dev_err(m->dev, "unrecognised Vcc %u.%uV\n", + vcc / 10, vcc % 10); + return -EINVAL; + } + + if (m->code == MAX1600_CODE_HIGH) { + /* + * Cirrus mode appears to be the same as Intel mode, + * except the VCC pins are inverted. + */ + __change_bit(MAX1600_GPIO_0VCC, values); + __change_bit(MAX1600_GPIO_1VCC, values); + } + + return gpiod_set_array_value_cansleep(n, m->gpio, NULL, values); +} +EXPORT_SYMBOL_GPL(max1600_configure); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pcmcia/max1600.h b/drivers/pcmcia/max1600.h new file mode 100644 index 000000000000..00bf1a09464f --- /dev/null +++ b/drivers/pcmcia/max1600.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef MAX1600_H +#define MAX1600_H + +struct gpio_desc; + +enum { + MAX1600_GPIO_0VCC = 0, + MAX1600_GPIO_1VCC, + MAX1600_GPIO_0VPP, + MAX1600_GPIO_1VPP, + MAX1600_GPIO_MAX, + + MAX1600_CHAN_A, + MAX1600_CHAN_B, + + MAX1600_CODE_LOW, + MAX1600_CODE_HIGH, +}; + +struct max1600 { + struct gpio_desc *gpio[MAX1600_GPIO_MAX]; + struct device *dev; + unsigned int code; +}; + +int max1600_init(struct device *dev, struct max1600 **ptr, + unsigned int channel, unsigned int code); + +int max1600_configure(struct max1600 *, unsigned int vcc, unsigned int vpp); + +#endif diff --git a/drivers/pcmcia/pxa2xx_mainstone.c b/drivers/pcmcia/pxa2xx_mainstone.c index 7e32e25cdcb2..770c7bf0171d 100644 --- a/drivers/pcmcia/pxa2xx_mainstone.c +++ b/drivers/pcmcia/pxa2xx_mainstone.c @@ -11,56 +11,55 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - +#include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/interrupt.h> #include <linux/platform_device.h> #include <pcmcia/ss.h> #include <asm/mach-types.h> -#include <asm/irq.h> - -#include <mach/pxa2xx-regs.h> -#include <mach/mainstone.h> #include "soc_common.h" - +#include "max1600.h" static int mst_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { - /* - * Setup default state of GPIO outputs - * before we enable them as outputs. - */ - if (skt->nr == 0) { - skt->socket.pci_irq = MAINSTONE_S0_IRQ; - skt->stat[SOC_STAT_CD].irq = MAINSTONE_S0_CD_IRQ; - skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD"; - skt->stat[SOC_STAT_BVD1].irq = MAINSTONE_S0_STSCHG_IRQ; - skt->stat[SOC_STAT_BVD1].name = "PCMCIA0 STSCHG"; - } else { - skt->socket.pci_irq = MAINSTONE_S1_IRQ; - skt->stat[SOC_STAT_CD].irq = MAINSTONE_S1_CD_IRQ; - skt->stat[SOC_STAT_CD].name = "PCMCIA1 CD"; - skt->stat[SOC_STAT_BVD1].irq = MAINSTONE_S1_STSCHG_IRQ; - skt->stat[SOC_STAT_BVD1].name = "PCMCIA1 STSCHG"; - } - return 0; + struct device *dev = skt->socket.dev.parent; + struct max1600 *m; + int ret; + + skt->stat[SOC_STAT_CD].name = skt->nr ? "bdetect" : "adetect"; + skt->stat[SOC_STAT_BVD1].name = skt->nr ? "bbvd1" : "abvd1"; + skt->stat[SOC_STAT_BVD2].name = skt->nr ? "bbvd2" : "abvd2"; + skt->stat[SOC_STAT_RDY].name = skt->nr ? "bready" : "aready"; + skt->stat[SOC_STAT_VS1].name = skt->nr ? "bvs1" : "avs1"; + skt->stat[SOC_STAT_VS2].name = skt->nr ? "bvs2" : "avs2"; + + skt->gpio_reset = devm_gpiod_get(dev, skt->nr ? "breset" : "areset", + GPIOD_OUT_HIGH); + if (IS_ERR(skt->gpio_reset)) + return PTR_ERR(skt->gpio_reset); + + ret = max1600_init(dev, &m, skt->nr ? MAX1600_CHAN_B : MAX1600_CHAN_A, + MAX1600_CODE_HIGH); + if (ret) + return ret; + + skt->driver_data = m; + + return soc_pcmcia_request_gpiods(skt); } -static unsigned long mst_pcmcia_status[2]; +static unsigned int mst_pcmcia_bvd1_status[2]; static void mst_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - unsigned long status, flip; - - status = (skt->nr == 0) ? MST_PCMCIA0 : MST_PCMCIA1; - flip = (status ^ mst_pcmcia_status[skt->nr]) & MST_PCMCIA_nSTSCHG_BVD1; + unsigned int flip = mst_pcmcia_bvd1_status[skt->nr] ^ state->bvd1; /* * Workaround for STSCHG which can't be deasserted: @@ -68,62 +67,18 @@ static void mst_pcmcia_socket_state(struct soc_pcmcia_socket *skt, * as needed to avoid IRQ locks. */ if (flip) { - mst_pcmcia_status[skt->nr] = status; - if (status & MST_PCMCIA_nSTSCHG_BVD1) - enable_irq( (skt->nr == 0) ? MAINSTONE_S0_STSCHG_IRQ - : MAINSTONE_S1_STSCHG_IRQ ); + mst_pcmcia_bvd1_status[skt->nr] = state->bvd1; + if (state->bvd1) + enable_irq(skt->stat[SOC_STAT_BVD1].irq); else - disable_irq( (skt->nr == 0) ? MAINSTONE_S0_STSCHG_IRQ - : MAINSTONE_S1_STSCHG_IRQ ); + disable_irq(skt->stat[SOC_STAT_BVD2].irq); } - - state->detect = (status & MST_PCMCIA_nCD) ? 0 : 1; - state->ready = (status & MST_PCMCIA_nIRQ) ? 1 : 0; - state->bvd1 = (status & MST_PCMCIA_nSTSCHG_BVD1) ? 1 : 0; - state->bvd2 = (status & MST_PCMCIA_nSPKR_BVD2) ? 1 : 0; - state->vs_3v = (status & MST_PCMCIA_nVS1) ? 0 : 1; - state->vs_Xv = (status & MST_PCMCIA_nVS2) ? 0 : 1; } static int mst_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) { - unsigned long power = 0; - int ret = 0; - - switch (state->Vcc) { - case 0: power |= MST_PCMCIA_PWR_VCC_0; break; - case 33: power |= MST_PCMCIA_PWR_VCC_33; break; - case 50: power |= MST_PCMCIA_PWR_VCC_50; break; - default: - printk(KERN_ERR "%s(): bad Vcc %u\n", - __func__, state->Vcc); - ret = -1; - } - - switch (state->Vpp) { - case 0: power |= MST_PCMCIA_PWR_VPP_0; break; - case 120: power |= MST_PCMCIA_PWR_VPP_120; break; - default: - if(state->Vpp == state->Vcc) { - power |= MST_PCMCIA_PWR_VPP_VCC; - } else { - printk(KERN_ERR "%s(): bad Vpp %u\n", - __func__, state->Vpp); - ret = -1; - } - } - - if (state->flags & SS_RESET) - power |= MST_PCMCIA_RESET; - - switch (skt->nr) { - case 0: MST_PCMCIA0 = power; break; - case 1: MST_PCMCIA1 = power; break; - default: ret = -1; - } - - return ret; + return max1600_configure(skt->driver_data, state->Vcc, state->Vpp); } static struct pcmcia_low_level mst_pcmcia_ops __initdata = { diff --git a/drivers/pcmcia/sa1100_simpad.c b/drivers/pcmcia/sa1100_simpad.c index e235ee14eaa6..e2e8729afd9d 100644 --- a/drivers/pcmcia/sa1100_simpad.c +++ b/drivers/pcmcia/sa1100_simpad.c @@ -39,8 +39,8 @@ simpad_pcmcia_socket_state(struct soc_pcmcia_socket *skt, { long cs3reg = simpad_get_cs3_ro(); - state->bvd1 = 1; /* Might be cs3reg & PCMCIA_BVD1 */ - state->bvd2 = 1; /* Might be cs3reg & PCMCIA_BVD2 */ + /* bvd1 might be cs3reg & PCMCIA_BVD1 */ + /* bvd2 might be cs3reg & PCMCIA_BVD2 */ if ((cs3reg & (PCMCIA_VS1|PCMCIA_VS2)) == (PCMCIA_VS1|PCMCIA_VS2)) { diff --git a/drivers/pcmcia/sa1111_jornada720.c b/drivers/pcmcia/sa1111_jornada720.c index 3d4ca87ca76c..1083e1b4f25d 100644 --- a/drivers/pcmcia/sa1111_jornada720.c +++ b/drivers/pcmcia/sa1111_jornada720.c @@ -6,29 +6,62 @@ * */ #include <linux/module.h> -#include <linux/kernel.h> #include <linux/device.h> #include <linux/errno.h> +#include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/io.h> #include <mach/hardware.h> -#include <asm/hardware/sa1111.h> #include <asm/mach-types.h> #include "sa1111_generic.h" -/* Does SOCKET1_3V actually do anything? */ -#define SOCKET0_POWER GPIO_GPIO0 -#define SOCKET0_3V GPIO_GPIO2 -#define SOCKET1_POWER (GPIO_GPIO1 | GPIO_GPIO3) -#define SOCKET1_3V GPIO_GPIO3 +/* + * Socket 0 power: GPIO A0 + * Socket 0 3V: GPIO A2 + * Socket 1 power: GPIO A1 & GPIO A3 + * Socket 1 3V: GPIO A3 + * Does Socket 1 3V actually do anything? + */ +enum { + J720_GPIO_PWR, + J720_GPIO_3V, + J720_GPIO_MAX, +}; +struct jornada720_data { + struct gpio_desc *gpio[J720_GPIO_MAX]; +}; + +static int jornada720_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + struct device *dev = skt->socket.dev.parent; + struct jornada720_data *j; + + j = devm_kzalloc(dev, sizeof(*j), GFP_KERNEL); + if (!j) + return -ENOMEM; + + j->gpio[J720_GPIO_PWR] = devm_gpiod_get(dev, skt->nr ? "s1-power" : + "s0-power", GPIOD_OUT_LOW); + if (IS_ERR(j->gpio[J720_GPIO_PWR])) + return PTR_ERR(j->gpio[J720_GPIO_PWR]); + + j->gpio[J720_GPIO_3V] = devm_gpiod_get(dev, skt->nr ? "s1-3v" : + "s0-3v", GPIOD_OUT_LOW); + if (IS_ERR(j->gpio[J720_GPIO_3V])) + return PTR_ERR(j->gpio[J720_GPIO_3V]); + + skt->driver_data = j; + + return 0; +} static int jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) { - struct sa1111_pcmcia_socket *s = to_skt(skt); - unsigned int pa_dwr_mask, pa_dwr_set; + struct jornada720_data *j = skt->driver_data; + DECLARE_BITMAP(values, J720_GPIO_MAX) = { 0, }; int ret; printk(KERN_INFO "%s(): config socket %d vcc %d vpp %d\n", __func__, @@ -36,35 +69,34 @@ jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_s switch (skt->nr) { case 0: - pa_dwr_mask = SOCKET0_POWER | SOCKET0_3V; - switch (state->Vcc) { default: case 0: - pa_dwr_set = 0; + __assign_bit(J720_GPIO_PWR, values, 0); + __assign_bit(J720_GPIO_3V, values, 0); break; case 33: - pa_dwr_set = SOCKET0_POWER | SOCKET0_3V; + __assign_bit(J720_GPIO_PWR, values, 1); + __assign_bit(J720_GPIO_3V, values, 1); break; case 50: - pa_dwr_set = SOCKET0_POWER; + __assign_bit(J720_GPIO_PWR, values, 1); + __assign_bit(J720_GPIO_3V, values, 0); break; } break; case 1: - pa_dwr_mask = SOCKET1_POWER; - switch (state->Vcc) { default: case 0: - pa_dwr_set = 0; + __assign_bit(J720_GPIO_PWR, values, 0); + __assign_bit(J720_GPIO_3V, values, 0); break; case 33: - pa_dwr_set = SOCKET1_POWER; - break; case 50: - pa_dwr_set = SOCKET1_POWER; + __assign_bit(J720_GPIO_PWR, values, 1); + __assign_bit(J720_GPIO_3V, values, 1); break; } break; @@ -81,13 +113,15 @@ jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_s ret = sa1111_pcmcia_configure_socket(skt, state); if (ret == 0) - sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set); + ret = gpiod_set_array_value_cansleep(J720_GPIO_MAX, j->gpio, + NULL, values); return ret; } static struct pcmcia_low_level jornada720_pcmcia_ops = { .owner = THIS_MODULE, + .hw_init = jornada720_pcmcia_hw_init, .configure_socket = jornada720_pcmcia_configure_socket, .first = 0, .nr = 2, @@ -95,16 +129,9 @@ static struct pcmcia_low_level jornada720_pcmcia_ops = { int pcmcia_jornada720_init(struct sa1111_dev *sadev) { - unsigned int pin = GPIO_A0 | GPIO_A1 | GPIO_A2 | GPIO_A3; - /* Fixme: why messing around with SA11x0's GPIO1? */ GRER |= 0x00000002; - /* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */ - sa1111_set_io_dir(sadev, pin, 0, 0); - sa1111_set_io(sadev, pin, 0); - sa1111_set_sleep_io(sadev, pin, 0); - sa11xx_drv_pcmcia_ops(&jornada720_pcmcia_ops); return sa1111_pcmcia_add(sadev, &jornada720_pcmcia_ops, sa11xx_drv_pcmcia_add_one); diff --git a/drivers/pcmcia/sa1111_lubbock.c b/drivers/pcmcia/sa1111_lubbock.c index e741f499c875..e3fc14cfb42b 100644 --- a/drivers/pcmcia/sa1111_lubbock.c +++ b/drivers/pcmcia/sa1111_lubbock.c @@ -24,20 +24,31 @@ #include <mach/hardware.h> #include <asm/hardware/sa1111.h> #include <asm/mach-types.h> -#include <mach/lubbock.h> #include "sa1111_generic.h" +#include "max1600.h" + +static int lubbock_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + struct max1600 *m; + int ret; + + ret = max1600_init(skt->socket.dev.parent, &m, + skt->nr ? MAX1600_CHAN_B : MAX1600_CHAN_A, + MAX1600_CODE_HIGH); + if (ret == 0) + skt->driver_data = m; + + return ret; +} static int lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) { - struct sa1111_pcmcia_socket *s = to_skt(skt); - unsigned int pa_dwr_mask, pa_dwr_set, misc_mask, misc_set; + struct max1600 *m = skt->driver_data; int ret = 0; - pa_dwr_mask = pa_dwr_set = misc_mask = misc_set = 0; - /* Lubbock uses the Maxim MAX1602, with the following connections: * * Socket 0 (PCMCIA): @@ -71,74 +82,7 @@ lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, again: switch (skt->nr) { case 0: - pa_dwr_mask = GPIO_A0 | GPIO_A1 | GPIO_A2 | GPIO_A3; - - switch (state->Vcc) { - case 0: /* Hi-Z */ - break; - - case 33: /* VY */ - pa_dwr_set |= GPIO_A3; - break; - - case 50: /* VX */ - pa_dwr_set |= GPIO_A2; - break; - - default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", - __func__, state->Vcc); - ret = -1; - } - - switch (state->Vpp) { - case 0: /* Hi-Z */ - break; - - case 120: /* 12IN */ - pa_dwr_set |= GPIO_A1; - break; - - default: /* VCC */ - if (state->Vpp == state->Vcc) - pa_dwr_set |= GPIO_A0; - else { - printk(KERN_ERR "%s(): unrecognized Vpp %u\n", - __func__, state->Vpp); - ret = -1; - break; - } - } - break; - case 1: - misc_mask = (1 << 15) | (1 << 14); - - switch (state->Vcc) { - case 0: /* Hi-Z */ - break; - - case 33: /* VY */ - misc_set |= 1 << 15; - break; - - case 50: /* VX */ - misc_set |= 1 << 14; - break; - - default: - printk(KERN_ERR "%s(): unrecognized Vcc %u\n", - __func__, state->Vcc); - ret = -1; - break; - } - - if (state->Vpp != state->Vcc && state->Vpp != 0) { - printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", - __func__, state->Vpp); - ret = -1; - break; - } break; default: @@ -147,11 +91,8 @@ lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, if (ret == 0) ret = sa1111_pcmcia_configure_socket(skt, state); - - if (ret == 0) { - lubbock_set_misc_wr(misc_mask, misc_set); - sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set); - } + if (ret == 0) + ret = max1600_configure(m, state->Vcc, state->Vpp); #if 1 if (ret == 0 && state->Vcc == 33) { @@ -175,8 +116,7 @@ lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, /* * Switch to 5V, Configure socket with 5V voltage */ - lubbock_set_misc_wr(misc_mask, 0); - sa1111_set_io(s->dev, pa_dwr_mask, 0); + max1600_configure(m, 0, 0); /* * It takes about 100ms to turn off Vcc. @@ -201,6 +141,7 @@ lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, static struct pcmcia_low_level lubbock_pcmcia_ops = { .owner = THIS_MODULE, + .hw_init = lubbock_pcmcia_hw_init, .configure_socket = lubbock_pcmcia_configure_socket, .first = 0, .nr = 2, @@ -210,17 +151,6 @@ static struct pcmcia_low_level lubbock_pcmcia_ops = { int pcmcia_lubbock_init(struct sa1111_dev *sadev) { - /* - * Set GPIO_A<3:0> to be outputs for the MAX1600, - * and switch to standby mode. - */ - sa1111_set_io_dir(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0, 0); - sa1111_set_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0); - sa1111_set_sleep_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0); - - /* Set CF Socket 1 power to standby mode. */ - lubbock_set_misc_wr((1 << 15) | (1 << 14), 0); - pxa2xx_drv_pcmcia_ops(&lubbock_pcmcia_ops); pxa2xx_configure_sockets(&sadev->dev, &lubbock_pcmcia_ops); return sa1111_pcmcia_add(sadev, &lubbock_pcmcia_ops, diff --git a/drivers/pcmcia/sa1111_neponset.c b/drivers/pcmcia/sa1111_neponset.c index 0ccf05a28a4b..de0ce13355b4 100644 --- a/drivers/pcmcia/sa1111_neponset.c +++ b/drivers/pcmcia/sa1111_neponset.c @@ -10,12 +10,10 @@ #include <linux/errno.h> #include <linux/init.h> -#include <mach/hardware.h> #include <asm/mach-types.h> -#include <mach/neponset.h> -#include <asm/hardware/sa1111.h> #include "sa1111_generic.h" +#include "max1600.h" /* * Neponset uses the Maxim MAX1600, with the following connections: @@ -40,70 +38,36 @@ * "Standard Intel code" mode. Refer to the Maxim data sheet for * the corresponding truth table. */ - -static int -neponset_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) +static int neponset_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { - struct sa1111_pcmcia_socket *s = to_skt(skt); - unsigned int ncr_mask, ncr_set, pa_dwr_mask, pa_dwr_set; + struct max1600 *m; int ret; - switch (skt->nr) { - case 0: - pa_dwr_mask = GPIO_A0 | GPIO_A1; - ncr_mask = NCR_A0VPP | NCR_A1VPP; - - if (state->Vpp == 0) - ncr_set = 0; - else if (state->Vpp == 120) - ncr_set = NCR_A1VPP; - else if (state->Vpp == state->Vcc) - ncr_set = NCR_A0VPP; - else { - printk(KERN_ERR "%s(): unrecognized VPP %u\n", - __func__, state->Vpp); - return -1; - } - break; - - case 1: - pa_dwr_mask = GPIO_A2 | GPIO_A3; - ncr_mask = 0; - ncr_set = 0; - - if (state->Vpp != state->Vcc && state->Vpp != 0) { - printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n", - __func__, state->Vpp); - return -1; - } - break; + ret = max1600_init(skt->socket.dev.parent, &m, + skt->nr ? MAX1600_CHAN_B : MAX1600_CHAN_A, + MAX1600_CODE_LOW); + if (ret == 0) + skt->driver_data = m; - default: - return -1; - } + return ret; +} - /* - * pa_dwr_set is the mask for selecting Vcc on both sockets. - * pa_dwr_mask selects which bits (and therefore socket) we change. - */ - switch (state->Vcc) { - default: - case 0: pa_dwr_set = 0; break; - case 33: pa_dwr_set = GPIO_A1|GPIO_A2; break; - case 50: pa_dwr_set = GPIO_A0|GPIO_A3; break; - } +static int +neponset_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) +{ + struct max1600 *m = skt->driver_data; + int ret; ret = sa1111_pcmcia_configure_socket(skt, state); - if (ret == 0) { - neponset_ncr_frob(ncr_mask, ncr_set); - sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set); - } + if (ret == 0) + ret = max1600_configure(m, state->Vcc, state->Vpp); return ret; } static struct pcmcia_low_level neponset_pcmcia_ops = { .owner = THIS_MODULE, + .hw_init = neponset_pcmcia_hw_init, .configure_socket = neponset_pcmcia_configure_socket, .first = 0, .nr = 2, @@ -111,13 +75,6 @@ static struct pcmcia_low_level neponset_pcmcia_ops = { int pcmcia_neponset_init(struct sa1111_dev *sadev) { - /* - * Set GPIO_A<3:0> to be outputs for the MAX1600, - * and switch to standby mode. - */ - sa1111_set_io_dir(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0, 0); - sa1111_set_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0); - sa1111_set_sleep_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0); sa11xx_drv_pcmcia_ops(&neponset_pcmcia_ops); return sa1111_pcmcia_add(sadev, &neponset_pcmcia_ops, sa11xx_drv_pcmcia_add_one); diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index 1b10ea05a914..69372e2bc93c 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -30,8 +30,8 @@ #define DDRC_FLUX_RCMD 0x38c #define DDRC_PRE_CMD 0x3c0 #define DDRC_ACT_CMD 0x3c4 -#define DDRC_BNK_CHG 0x3c8 #define DDRC_RNK_CHG 0x3cc +#define DDRC_RW_CHG 0x3d0 #define DDRC_EVENT_CTRL 0x6C0 #define DDRC_INT_MASK 0x6c8 #define DDRC_INT_STATUS 0x6cc @@ -51,7 +51,7 @@ static const u32 ddrc_reg_off[] = { DDRC_FLUX_WR, DDRC_FLUX_RD, DDRC_FLUX_WCMD, DDRC_FLUX_RCMD, - DDRC_PRE_CMD, DDRC_ACT_CMD, DDRC_BNK_CHG, DDRC_RNK_CHG + DDRC_PRE_CMD, DDRC_ACT_CMD, DDRC_RNK_CHG, DDRC_RW_CHG }; /* diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 7c639006252e..321bc673c417 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -416,8 +416,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, if (unlikely(bufflen == 0)) return 0; /* Check the buffer range for access */ - if (unlikely(!access_ok(is_write ? VERIFY_WRITE : VERIFY_READ, - buffer, bufflen))) + if (unlikely(!access_ok(buffer, bufflen))) return -EFAULT; address = (unsigned long)buffer; diff --git a/drivers/pnp/isapnp/proc.c b/drivers/pnp/isapnp/proc.c index 262285e48a09..051613140812 100644 --- a/drivers/pnp/isapnp/proc.c +++ b/drivers/pnp/isapnp/proc.c @@ -47,7 +47,7 @@ static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf, nbytes = size - pos; cnt = nbytes; - if (!access_ok(VERIFY_WRITE, buf, cnt)) + if (!access_ok(buf, cnt)) return -EINVAL; isapnp_cfg_begin(dev->card->number, dev->number); diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 7c4673308f5b..e338d7a4f571 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -3600,7 +3600,7 @@ static long pmcraid_ioctl_passthrough( u32 ioasc; int request_size; int buffer_size; - u8 access, direction; + u8 direction; int rc = 0; /* If IOA reset is in progress, wait 10 secs for reset to complete */ @@ -3649,10 +3649,8 @@ static long pmcraid_ioctl_passthrough( request_size = le32_to_cpu(buffer->ioarcb.data_transfer_length); if (buffer->ioarcb.request_flags0 & TRANSFER_DIR_WRITE) { - access = VERIFY_READ; direction = DMA_TO_DEVICE; } else { - access = VERIFY_WRITE; direction = DMA_FROM_DEVICE; } diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index cc30fccc1a2e..840d96fe81bc 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -221,7 +221,7 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) switch (cmd) { case SCSI_IOCTL_GET_IDLUN: - if (!access_ok(VERIFY_WRITE, arg, sizeof(struct scsi_idlun))) + if (!access_ok(arg, sizeof(struct scsi_idlun))) return -EFAULT; __put_user((sdev->id & 0xff) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 4e27460ec926..d3f15319b9b3 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -434,7 +434,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_read: count=%d\n", (int) count)); - if (!access_ok(VERIFY_WRITE, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; if (sfp->force_packid && (count >= SZ_SG_HEADER)) { old_hdr = kmalloc(SZ_SG_HEADER, GFP_KERNEL); @@ -632,7 +632,7 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) scsi_block_when_processing_errors(sdp->device))) return -ENXIO; - if (!access_ok(VERIFY_READ, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; /* protects following copy_from_user()s + get_user()s */ if (count < SZ_SG_HEADER) return -EIO; @@ -729,7 +729,7 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, if (count < SZ_SG_IO_HDR) return -EINVAL; - if (!access_ok(VERIFY_READ, buf, count)) + if (!access_ok(buf, count)) return -EFAULT; /* protects following copy_from_user()s + get_user()s */ sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */ @@ -768,7 +768,7 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, sg_remove_request(sfp, srp); return -EMSGSIZE; } - if (!access_ok(VERIFY_READ, hp->cmdp, hp->cmd_len)) { + if (!access_ok(hp->cmdp, hp->cmd_len)) { sg_remove_request(sfp, srp); return -EFAULT; /* protects following copy_from_user()s + get_user()s */ } @@ -922,7 +922,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return -ENODEV; if (!scsi_block_when_processing_errors(sdp->device)) return -ENXIO; - if (!access_ok(VERIFY_WRITE, p, SZ_SG_IO_HDR)) + if (!access_ok(p, SZ_SG_IO_HDR)) return -EFAULT; result = sg_new_write(sfp, filp, p, SZ_SG_IO_HDR, 1, read_only, 1, &srp); @@ -968,7 +968,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) case SG_GET_LOW_DMA: return put_user((int) sdp->device->host->unchecked_isa_dma, ip); case SG_GET_SCSI_ID: - if (!access_ok(VERIFY_WRITE, p, sizeof (sg_scsi_id_t))) + if (!access_ok(p, sizeof (sg_scsi_id_t))) return -EFAULT; else { sg_scsi_id_t __user *sg_idp = p; @@ -997,7 +997,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) sfp->force_packid = val ? 1 : 0; return 0; case SG_GET_PACK_ID: - if (!access_ok(VERIFY_WRITE, ip, sizeof (int))) + if (!access_ok(ip, sizeof (int))) return -EFAULT; read_lock_irqsave(&sfp->rq_list_lock, iflags); list_for_each_entry(srp, &sfp->rq_list, entry) { @@ -1078,7 +1078,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) val = (sdp->device ? 1 : 0); return put_user(val, ip); case SG_GET_REQUEST_TABLE: - if (!access_ok(VERIFY_WRITE, p, SZ_SG_REQ_INFO * SG_MAX_QUEUE)) + if (!access_ok(p, SZ_SG_REQ_INFO * SG_MAX_QUEUE)) return -EFAULT; else { sg_req_info_t *rinfo; diff --git a/drivers/staging/comedi/comedi_compat32.c b/drivers/staging/comedi/comedi_compat32.c index fa9d239474ee..36a3564ba1fb 100644 --- a/drivers/staging/comedi/comedi_compat32.c +++ b/drivers/staging/comedi/comedi_compat32.c @@ -102,8 +102,8 @@ static int compat_chaninfo(struct file *file, unsigned long arg) chaninfo = compat_alloc_user_space(sizeof(*chaninfo)); /* Copy chaninfo structure. Ignore unused members. */ - if (!access_ok(VERIFY_READ, chaninfo32, sizeof(*chaninfo32)) || - !access_ok(VERIFY_WRITE, chaninfo, sizeof(*chaninfo))) + if (!access_ok(chaninfo32, sizeof(*chaninfo32)) || + !access_ok(chaninfo, sizeof(*chaninfo))) return -EFAULT; err = 0; @@ -136,8 +136,8 @@ static int compat_rangeinfo(struct file *file, unsigned long arg) rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo)); /* Copy rangeinfo structure. */ - if (!access_ok(VERIFY_READ, rangeinfo32, sizeof(*rangeinfo32)) || - !access_ok(VERIFY_WRITE, rangeinfo, sizeof(*rangeinfo))) + if (!access_ok(rangeinfo32, sizeof(*rangeinfo32)) || + !access_ok(rangeinfo, sizeof(*rangeinfo))) return -EFAULT; err = 0; @@ -163,8 +163,8 @@ static int get_compat_cmd(struct comedi_cmd __user *cmd, } temp; /* Copy cmd structure. */ - if (!access_ok(VERIFY_READ, cmd32, sizeof(*cmd32)) || - !access_ok(VERIFY_WRITE, cmd, sizeof(*cmd))) + if (!access_ok(cmd32, sizeof(*cmd32)) || + !access_ok(cmd, sizeof(*cmd))) return -EFAULT; err = 0; @@ -217,8 +217,8 @@ static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32, * Assume the pointer values are already valid. * (Could use ptr_to_compat() to set them.) */ - if (!access_ok(VERIFY_READ, cmd, sizeof(*cmd)) || - !access_ok(VERIFY_WRITE, cmd32, sizeof(*cmd32))) + if (!access_ok(cmd, sizeof(*cmd)) || + !access_ok(cmd32, sizeof(*cmd32))) return -EFAULT; err = 0; @@ -317,8 +317,8 @@ static int get_compat_insn(struct comedi_insn __user *insn, /* Copy insn structure. Ignore the unused members. */ err = 0; - if (!access_ok(VERIFY_READ, insn32, sizeof(*insn32)) || - !access_ok(VERIFY_WRITE, insn, sizeof(*insn))) + if (!access_ok(insn32, sizeof(*insn32)) || + !access_ok(insn, sizeof(*insn))) return -EFAULT; err |= __get_user(temp.uint, &insn32->insn); @@ -350,7 +350,7 @@ static int compat_insnlist(struct file *file, unsigned long arg) insnlist32 = compat_ptr(arg); /* Get 32-bit insnlist structure. */ - if (!access_ok(VERIFY_READ, insnlist32, sizeof(*insnlist32))) + if (!access_ok(insnlist32, sizeof(*insnlist32))) return -EFAULT; err = 0; @@ -365,7 +365,7 @@ static int compat_insnlist(struct file *file, unsigned long arg) insn[n_insns])); /* Set native insnlist structure. */ - if (!access_ok(VERIFY_WRITE, &s->insnlist, sizeof(s->insnlist))) + if (!access_ok(&s->insnlist, sizeof(s->insnlist))) return -EFAULT; err |= __put_user(n_insns, &s->insnlist.n_insns); diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 34dce850067b..e5efce3c08e2 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -631,6 +631,9 @@ static struct optee *optee_probe(struct device_node *np) optee_enable_shm_cache(optee); + if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) + pr_info("dynamic shared memory is enabled\n"); + pr_info("initialized driver\n"); return optee; err: diff --git a/drivers/tee/optee/supp.c b/drivers/tee/optee/supp.c index df35fc01fd3e..43626e15703a 100644 --- a/drivers/tee/optee/supp.c +++ b/drivers/tee/optee/supp.c @@ -19,7 +19,7 @@ struct optee_supp_req { struct list_head link; - bool busy; + bool in_queue; u32 func; u32 ret; size_t num_params; @@ -54,7 +54,6 @@ void optee_supp_release(struct optee_supp *supp) /* Abort all request retrieved by supplicant */ idr_for_each_entry(&supp->idr, req, id) { - req->busy = false; idr_remove(&supp->idr, id); req->ret = TEEC_ERROR_COMMUNICATION; complete(&req->c); @@ -63,6 +62,7 @@ void optee_supp_release(struct optee_supp *supp) /* Abort all queued requests */ list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) { list_del(&req->link); + req->in_queue = false; req->ret = TEEC_ERROR_COMMUNICATION; complete(&req->c); } @@ -103,6 +103,7 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, /* Insert the request in the request list */ mutex_lock(&supp->mutex); list_add_tail(&req->link, &supp->reqs); + req->in_queue = true; mutex_unlock(&supp->mutex); /* Tell an eventual waiter there's a new request */ @@ -130,9 +131,10 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, * will serve all requests in a timely manner and * interrupting then wouldn't make sense. */ - interruptable = !req->busy; - if (!req->busy) + if (req->in_queue) { list_del(&req->link); + req->in_queue = false; + } } mutex_unlock(&supp->mutex); @@ -176,7 +178,7 @@ static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp, return ERR_PTR(-ENOMEM); list_del(&req->link); - req->busy = true; + req->in_queue = false; return req; } @@ -318,7 +320,6 @@ static struct optee_supp_req *supp_pop_req(struct optee_supp *supp, if ((num_params - nm) != req->num_params) return ERR_PTR(-EINVAL); - req->busy = false; idr_remove(&supp->idr, id); supp->req_id = -1; *num_meta = nm; diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 5fbfabbf627b..30323426902e 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -212,7 +212,7 @@ config HISI_THERMAL config IMX_THERMAL tristate "Temperature sensor driver for Freescale i.MX SoCs" - depends on (ARCH_MXC && CPU_THERMAL) || COMPILE_TEST + depends on ARCH_MXC || COMPILE_TEST depends on NVMEM || !NVMEM depends on MFD_SYSCON depends on OF @@ -326,84 +326,6 @@ config DA9062_THERMAL zone. Compatible with the DA9062 and DA9061 PMICs. -config INTEL_POWERCLAMP - tristate "Intel PowerClamp idle injection driver" - depends on THERMAL - depends on X86 - depends on CPU_SUP_INTEL - help - Enable this to enable Intel PowerClamp idle injection driver. This - enforce idle time which results in more package C-state residency. The - user interface is exposed via generic thermal framework. - -config X86_PKG_TEMP_THERMAL - tristate "X86 package temperature thermal driver" - depends on X86_THERMAL_VECTOR - select THERMAL_GOV_USER_SPACE - select THERMAL_WRITABLE_TRIPS - default m - help - Enable this to register CPU digital sensor for package temperature as - thermal zone. Each package will have its own thermal zone. There are - two trip points which can be set by user to get notifications via thermal - notification methods. - -config INTEL_SOC_DTS_IOSF_CORE - tristate - depends on X86 && PCI - select IOSF_MBI - help - This is becoming a common feature for Intel SoCs to expose the additional - digital temperature sensors (DTSs) using side band interface (IOSF). This - implements the common set of helper functions to register, get temperature - and get/set thresholds on DTSs. - -config INTEL_SOC_DTS_THERMAL - tristate "Intel SoCs DTS thermal driver" - depends on X86 && PCI && ACPI - select INTEL_SOC_DTS_IOSF_CORE - select THERMAL_WRITABLE_TRIPS - help - Enable this to register Intel SoCs (e.g. Bay Trail) platform digital - temperature sensor (DTS). These SoCs have two additional DTSs in - addition to DTSs on CPU cores. Each DTS will be registered as a - thermal zone. There are two trip points. One of the trip point can - be set by user mode programs to get notifications via Linux thermal - notification methods.The other trip is a critical trip point, which - was set by the driver based on the TJ MAX temperature. - -config INTEL_QUARK_DTS_THERMAL - tristate "Intel Quark DTS thermal driver" - depends on X86_INTEL_QUARK - help - Enable this to register Intel Quark SoC (e.g. X1000) platform digital - temperature sensor (DTS). For X1000 SoC, it has one on-die DTS. - The DTS will be registered as a thermal zone. There are two trip points: - hot & critical. The critical trip point default value is set by - underlying BIOS/Firmware. - -menu "ACPI INT340X thermal drivers" -source "drivers/thermal/int340x_thermal/Kconfig" -endmenu - -config INTEL_BXT_PMIC_THERMAL - tristate "Intel Broxton PMIC thermal driver" - depends on X86 && INTEL_SOC_PMIC_BXTWC && REGMAP - help - Select this driver for Intel Broxton PMIC with ADC channels monitoring - system temperature measurements and alerts. - This driver is used for monitoring the ADC channels of PMIC and handles - the alert trip point interrupts and notifies the thermal framework with - the trip point and temperature details of the zone. - -config INTEL_PCH_THERMAL - tristate "Intel PCH Thermal Reporting Driver" - depends on X86 && PCI - help - Enable this to support thermal reporting on certain intel PCHs. - Thermal reporting device will provide temperature reading, - programmable trip points and other information. - config MTK_THERMAL tristate "Temperature sensor driver for mediatek SoCs" depends on ARCH_MEDIATEK || COMPILE_TEST @@ -415,6 +337,11 @@ config MTK_THERMAL Enable this option if you want to have support for thermal management controller present in Mediatek SoCs +menu "Intel thermal drivers" +depends on X86 || X86_INTEL_QUARK || COMPILE_TEST +source "drivers/thermal/intel/Kconfig" +endmenu + menu "Broadcom thermal drivers" depends on ARCH_BCM || ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST source "drivers/thermal/broadcom/Kconfig" @@ -447,17 +374,6 @@ config TANGO_THERMAL source "drivers/thermal/tegra/Kconfig" -config QCOM_SPMI_TEMP_ALARM - tristate "Qualcomm SPMI PMIC Temperature Alarm" - depends on OF && SPMI && IIO - select REGMAP_SPMI - help - This enables a thermal sysfs driver for Qualcomm plug-and-play (QPNP) - PMIC devices. It shows up in sysfs as a thermal sensor with multiple - trip points. The temperature reported by the thermal sensor reflects the - real time die temperature if an ADC is present or an estimate of the - temperature based upon the over temperature stage value. - config GENERIC_ADC_THERMAL tristate "Generic ADC based thermal sensor" depends on IIO diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 82bb50dc6423..486d682be047 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -29,7 +29,6 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o # platform thermal drivers obj-y += broadcom/ -obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o @@ -44,15 +43,8 @@ obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o -obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o -obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o -obj-$(CONFIG_INTEL_SOC_DTS_IOSF_CORE) += intel_soc_dts_iosf.o -obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o -obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o +obj-y += intel/ obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ -obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ -obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o -obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o obj-y += st/ obj-$(CONFIG_QCOM_TSENS) += qcom/ obj-y += tegra/ diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index d7105d01859a..53129de59dd9 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -26,6 +26,11 @@ #include <linux/iopoll.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> +#include <linux/interrupt.h> + +#include "thermal_core.h" + +#define TO_MCELSIUS(c) ((c) * 1000) /* Thermal Manager Control and Status Register */ #define PMU_TDC0_SW_RST_MASK (0x1 << 1) @@ -61,9 +66,13 @@ #define CONTROL1_TSEN_AVG_MASK 0x7 #define CONTROL1_EXT_TSEN_SW_RESET BIT(7) #define CONTROL1_EXT_TSEN_HW_RESETn BIT(8) +#define CONTROL1_TSEN_INT_EN BIT(25) +#define CONTROL1_TSEN_SELECT_OFF 21 +#define CONTROL1_TSEN_SELECT_MASK 0x3 #define STATUS_POLL_PERIOD_US 1000 #define STATUS_POLL_TIMEOUT_US 100000 +#define OVERHEAT_INT_POLL_DELAY_MS 1000 struct armada_thermal_data; @@ -75,7 +84,11 @@ struct armada_thermal_priv { /* serialize temperature reads/updates */ struct mutex update_lock; struct armada_thermal_data *data; + struct thermal_zone_device *overheat_sensor; + int interrupt_source; int current_channel; + long current_threshold; + long current_hysteresis; }; struct armada_thermal_data { @@ -93,12 +106,20 @@ struct armada_thermal_data { /* Register shift and mask to access the sensor temperature */ unsigned int temp_shift; unsigned int temp_mask; + unsigned int thresh_shift; + unsigned int hyst_shift; + unsigned int hyst_mask; u32 is_valid_bit; /* Syscon access */ unsigned int syscon_control0_off; unsigned int syscon_control1_off; unsigned int syscon_status_off; + unsigned int dfx_irq_cause_off; + unsigned int dfx_irq_mask_off; + unsigned int dfx_overheat_irq; + unsigned int dfx_server_irq_mask_off; + unsigned int dfx_server_irq_en; /* One sensor is in the thermal IC, the others are in the CPUs if any */ unsigned int cpu_nr; @@ -272,6 +293,41 @@ static bool armada_is_valid(struct armada_thermal_priv *priv) return reg & priv->data->is_valid_bit; } +static void armada_enable_overheat_interrupt(struct armada_thermal_priv *priv) +{ + struct armada_thermal_data *data = priv->data; + u32 reg; + + /* Clear DFX temperature IRQ cause */ + regmap_read(priv->syscon, data->dfx_irq_cause_off, ®); + + /* Enable DFX Temperature IRQ */ + regmap_read(priv->syscon, data->dfx_irq_mask_off, ®); + reg |= data->dfx_overheat_irq; + regmap_write(priv->syscon, data->dfx_irq_mask_off, reg); + + /* Enable DFX server IRQ */ + regmap_read(priv->syscon, data->dfx_server_irq_mask_off, ®); + reg |= data->dfx_server_irq_en; + regmap_write(priv->syscon, data->dfx_server_irq_mask_off, reg); + + /* Enable overheat interrupt */ + regmap_read(priv->syscon, data->syscon_control1_off, ®); + reg |= CONTROL1_TSEN_INT_EN; + regmap_write(priv->syscon, data->syscon_control1_off, reg); +} + +static void __maybe_unused +armada_disable_overheat_interrupt(struct armada_thermal_priv *priv) +{ + struct armada_thermal_data *data = priv->data; + u32 reg; + + regmap_read(priv->syscon, data->syscon_control1_off, ®); + reg &= ~CONTROL1_TSEN_INT_EN; + regmap_write(priv->syscon, data->syscon_control1_off, reg); +} + /* There is currently no board with more than one sensor per channel */ static int armada_select_channel(struct armada_thermal_priv *priv, int channel) { @@ -388,6 +444,14 @@ static int armada_get_temp(void *_sensor, int *temp) /* Do the actual reading */ ret = armada_read_sensor(priv, temp); + if (ret) + goto unlock_mutex; + + /* + * Select back the interrupt source channel from which a potential + * critical trip point has been set. + */ + ret = armada_select_channel(priv, priv->interrupt_source); unlock_mutex: mutex_unlock(&priv->update_lock); @@ -399,6 +463,123 @@ static const struct thermal_zone_of_device_ops of_ops = { .get_temp = armada_get_temp, }; +static unsigned int armada_mc_to_reg_temp(struct armada_thermal_data *data, + unsigned int temp_mc) +{ + s64 b = data->coef_b; + s64 m = data->coef_m; + s64 div = data->coef_div; + unsigned int sample; + + if (data->inverted) + sample = div_s64(((temp_mc * div) + b), m); + else + sample = div_s64((b - (temp_mc * div)), m); + + return sample & data->temp_mask; +} + +/* + * The documentation states: + * high/low watermark = threshold +/- 0.4761 * 2^(hysteresis + 2) + * which is the mathematical derivation for: + * 0x0 <=> 1.9°C, 0x1 <=> 3.8°C, 0x2 <=> 7.6°C, 0x3 <=> 15.2°C + */ +static unsigned int hyst_levels_mc[] = {1900, 3800, 7600, 15200}; + +static unsigned int armada_mc_to_reg_hyst(struct armada_thermal_data *data, + unsigned int hyst_mc) +{ + int i; + + /* + * We will always take the smallest possible hysteresis to avoid risking + * the hardware integrity by enlarging the threshold by +8°C in the + * worst case. + */ + for (i = ARRAY_SIZE(hyst_levels_mc) - 1; i > 0; i--) + if (hyst_mc >= hyst_levels_mc[i]) + break; + + return i & data->hyst_mask; +} + +static void armada_set_overheat_thresholds(struct armada_thermal_priv *priv, + int thresh_mc, int hyst_mc) +{ + struct armada_thermal_data *data = priv->data; + unsigned int threshold = armada_mc_to_reg_temp(data, thresh_mc); + unsigned int hysteresis = armada_mc_to_reg_hyst(data, hyst_mc); + u32 ctrl1; + + regmap_read(priv->syscon, data->syscon_control1_off, &ctrl1); + + /* Set Threshold */ + if (thresh_mc >= 0) { + ctrl1 &= ~(data->temp_mask << data->thresh_shift); + ctrl1 |= threshold << data->thresh_shift; + priv->current_threshold = thresh_mc; + } + + /* Set Hysteresis */ + if (hyst_mc >= 0) { + ctrl1 &= ~(data->hyst_mask << data->hyst_shift); + ctrl1 |= hysteresis << data->hyst_shift; + priv->current_hysteresis = hyst_mc; + } + + regmap_write(priv->syscon, data->syscon_control1_off, ctrl1); +} + +static irqreturn_t armada_overheat_isr(int irq, void *blob) +{ + /* + * Disable the IRQ and continue in thread context (thermal core + * notification and temperature monitoring). + */ + disable_irq_nosync(irq); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t armada_overheat_isr_thread(int irq, void *blob) +{ + struct armada_thermal_priv *priv = blob; + int low_threshold = priv->current_threshold - priv->current_hysteresis; + int temperature; + u32 dummy; + int ret; + + /* Notify the core in thread context */ + thermal_zone_device_update(priv->overheat_sensor, + THERMAL_EVENT_UNSPECIFIED); + + /* + * The overheat interrupt must be cleared by reading the DFX interrupt + * cause _after_ the temperature has fallen down to the low threshold. + * Otherwise future interrupts might not be served. + */ + do { + msleep(OVERHEAT_INT_POLL_DELAY_MS); + mutex_lock(&priv->update_lock); + ret = armada_read_sensor(priv, &temperature); + mutex_unlock(&priv->update_lock); + if (ret) + goto enable_irq; + } while (temperature >= low_threshold); + + regmap_read(priv->syscon, priv->data->dfx_irq_cause_off, &dummy); + + /* Notify the thermal core that the temperature is acceptable again */ + thermal_zone_device_update(priv->overheat_sensor, + THERMAL_EVENT_UNSPECIFIED); + +enable_irq: + enable_irq(irq); + + return IRQ_HANDLED; +} + static const struct armada_thermal_data armadaxp_data = { .init = armadaxp_init, .temp_shift = 10, @@ -454,6 +635,9 @@ static const struct armada_thermal_data armada_ap806_data = { .is_valid_bit = BIT(16), .temp_shift = 0, .temp_mask = 0x3ff, + .thresh_shift = 3, + .hyst_shift = 19, + .hyst_mask = 0x3, .coef_b = -150000LL, .coef_m = 423ULL, .coef_div = 1, @@ -462,6 +646,11 @@ static const struct armada_thermal_data armada_ap806_data = { .syscon_control0_off = 0x84, .syscon_control1_off = 0x88, .syscon_status_off = 0x8C, + .dfx_irq_cause_off = 0x108, + .dfx_irq_mask_off = 0x10C, + .dfx_overheat_irq = BIT(22), + .dfx_server_irq_mask_off = 0x104, + .dfx_server_irq_en = BIT(1), .cpu_nr = 4, }; @@ -470,6 +659,9 @@ static const struct armada_thermal_data armada_cp110_data = { .is_valid_bit = BIT(10), .temp_shift = 0, .temp_mask = 0x3ff, + .thresh_shift = 16, + .hyst_shift = 26, + .hyst_mask = 0x3, .coef_b = 1172499100ULL, .coef_m = 2000096ULL, .coef_div = 4201, @@ -477,6 +669,11 @@ static const struct armada_thermal_data armada_cp110_data = { .syscon_control0_off = 0x70, .syscon_control1_off = 0x74, .syscon_status_off = 0x78, + .dfx_irq_cause_off = 0x108, + .dfx_irq_mask_off = 0x10C, + .dfx_overheat_irq = BIT(20), + .dfx_server_irq_mask_off = 0x104, + .dfx_server_irq_en = BIT(1), }; static const struct of_device_id armada_thermal_id_table[] = { @@ -543,20 +740,14 @@ static int armada_thermal_probe_legacy(struct platform_device *pdev, priv->syscon = devm_regmap_init_mmio(&pdev->dev, base, &armada_thermal_regmap_config); - if (IS_ERR(priv->syscon)) - return PTR_ERR(priv->syscon); - - return 0; + return PTR_ERR_OR_ZERO(priv->syscon); } static int armada_thermal_probe_syscon(struct platform_device *pdev, struct armada_thermal_priv *priv) { priv->syscon = syscon_node_to_regmap(pdev->dev.parent->of_node); - if (IS_ERR(priv->syscon)) - return PTR_ERR(priv->syscon); - - return 0; + return PTR_ERR_OR_ZERO(priv->syscon); } static void armada_set_sane_name(struct platform_device *pdev, @@ -590,6 +781,48 @@ static void armada_set_sane_name(struct platform_device *pdev, } while (insane_char); } +/* + * The IP can manage to trigger interrupts on overheat situation from all the + * sensors. However, the interrupt source changes along with the last selected + * source (ie. the last read sensor), which is an inconsistent behavior. Avoid + * possible glitches by always selecting back only one channel (arbitrarily: the + * first in the DT which has a critical trip point). We also disable sensor + * switch during overheat situations. + */ +static int armada_configure_overheat_int(struct armada_thermal_priv *priv, + struct thermal_zone_device *tz, + int sensor_id) +{ + /* Retrieve the critical trip point to enable the overheat interrupt */ + const struct thermal_trip *trips = of_thermal_get_trip_points(tz); + int ret; + int i; + + if (!trips) + return -EINVAL; + + for (i = 0; i < of_thermal_get_ntrips(tz); i++) + if (trips[i].type == THERMAL_TRIP_CRITICAL) + break; + + if (i == of_thermal_get_ntrips(tz)) + return -EINVAL; + + ret = armada_select_channel(priv, sensor_id); + if (ret) + return ret; + + armada_set_overheat_thresholds(priv, + trips[i].temperature, + trips[i].hysteresis); + priv->overheat_sensor = tz; + priv->interrupt_source = sensor_id; + + armada_enable_overheat_interrupt(priv); + + return 0; +} + static int armada_thermal_probe(struct platform_device *pdev) { struct thermal_zone_device *tz; @@ -597,7 +830,7 @@ static int armada_thermal_probe(struct platform_device *pdev) struct armada_drvdata *drvdata; const struct of_device_id *match; struct armada_thermal_priv *priv; - int sensor_id; + int sensor_id, irq; int ret; match = of_match_device(armada_thermal_id_table, &pdev->dev); @@ -667,6 +900,23 @@ static int armada_thermal_probe(struct platform_device *pdev) drvdata->data.priv = priv; platform_set_drvdata(pdev, drvdata); + irq = platform_get_irq(pdev, 0); + if (irq == -EPROBE_DEFER) + return irq; + + /* The overheat interrupt feature is not mandatory */ + if (irq > 0) { + ret = devm_request_threaded_irq(&pdev->dev, irq, + armada_overheat_isr, + armada_overheat_isr_thread, + 0, NULL, priv); + if (ret) { + dev_err(&pdev->dev, "Cannot request threaded IRQ %d\n", + irq); + return ret; + } + } + /* * There is one channel for the IC and one per CPU (if any), each * channel has one sensor. @@ -690,8 +940,20 @@ static int armada_thermal_probe(struct platform_device *pdev) devm_kfree(&pdev->dev, sensor); continue; } + + /* + * The first channel that has a critical trip point registered + * in the DT will serve as interrupt source. Others possible + * critical trip points will simply be ignored by the driver. + */ + if (irq > 0 && !priv->overheat_sensor) + armada_configure_overheat_int(priv, tz, sensor->id); } + /* Just complain if no overheat interrupt was set up */ + if (!priv->overheat_sensor) + dev_warn(&pdev->dev, "Overheat interrupt not available\n"); + return 0; } diff --git a/drivers/thermal/broadcom/bcm2835_thermal.c b/drivers/thermal/broadcom/bcm2835_thermal.c index b9d90f0ed504..720760cd493f 100644 --- a/drivers/thermal/broadcom/bcm2835_thermal.c +++ b/drivers/thermal/broadcom/bcm2835_thermal.c @@ -18,6 +18,8 @@ #include <linux/platform_device.h> #include <linux/thermal.h> +#include "../thermal_hwmon.h" + #define BCM2835_TS_TSENSCTL 0x00 #define BCM2835_TS_TSENSSTAT 0x04 @@ -266,6 +268,15 @@ static int bcm2835_thermal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, tz); + /* + * Thermal_zone doesn't enable hwmon as default, + * enable it here + */ + tz->tzp->no_hwmon = false; + err = thermal_add_hwmon_sysfs(tz); + if (err) + goto err_tz; + bcm2835_thermal_debugfs(pdev); return 0; diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c index e8b1570cc388..65704bdd18e4 100644 --- a/drivers/thermal/broadcom/brcmstb_thermal.c +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -329,7 +329,8 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) priv->dev = &pdev->dev; platform_set_drvdata(pdev, priv); - thermal = thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &of_ops); + thermal = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv, + &of_ops); if (IS_ERR(thermal)) { ret = PTR_ERR(thermal); dev_err(&pdev->dev, "could not register sensor: %d\n", ret); @@ -341,40 +342,23 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "could not get IRQ\n"); - ret = irq; - goto err; + return irq; } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, brcmstb_tmon_irq_thread, IRQF_ONESHOT, DRV_NAME, priv); if (ret < 0) { dev_err(&pdev->dev, "could not request IRQ: %d\n", ret); - goto err; + return ret; } dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n"); return 0; - -err: - thermal_zone_of_sensor_unregister(&pdev->dev, thermal); - return ret; -} - -static int brcmstb_thermal_exit(struct platform_device *pdev) -{ - struct brcmstb_thermal_priv *priv = platform_get_drvdata(pdev); - struct thermal_zone_device *thermal = priv->thermal; - - if (thermal) - thermal_zone_of_sensor_unregister(&pdev->dev, priv->thermal); - - return 0; } static struct platform_driver brcmstb_thermal_driver = { .probe = brcmstb_thermal_probe, - .remove = brcmstb_thermal_exit, .driver = { .name = DRV_NAME, .of_match_table = brcmstb_thermal_id_table, diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 15661549eb67..bb6754a5342c 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -648,15 +648,24 @@ static const struct of_device_id of_imx_thermal_match[] = { }; MODULE_DEVICE_TABLE(of, of_imx_thermal_match); +#ifdef CONFIG_CPU_FREQ /* * Create cooling device in case no #cooling-cells property is available in * CPU node */ static int imx_thermal_register_legacy_cooling(struct imx_thermal_data *data) { - struct device_node *np = of_get_cpu_node(data->policy->cpu, NULL); + struct device_node *np; int ret; + data->policy = cpufreq_cpu_get(0); + if (!data->policy) { + pr_debug("%s: CPUFreq policy not found\n", __func__); + return -EPROBE_DEFER; + } + + np = of_get_cpu_node(data->policy->cpu, NULL); + if (!np || !of_find_property(np, "#cooling-cells", NULL)) { data->cdev = cpufreq_cooling_register(data->policy); if (IS_ERR(data->cdev)) { @@ -669,6 +678,24 @@ static int imx_thermal_register_legacy_cooling(struct imx_thermal_data *data) return 0; } +static void imx_thermal_unregister_legacy_cooling(struct imx_thermal_data *data) +{ + cpufreq_cooling_unregister(data->cdev); + cpufreq_cpu_put(data->policy); +} + +#else + +static inline int imx_thermal_register_legacy_cooling(struct imx_thermal_data *data) +{ + return 0; +} + +static inline void imx_thermal_unregister_legacy_cooling(struct imx_thermal_data *data) +{ +} +#endif + static int imx_thermal_probe(struct platform_device *pdev) { struct imx_thermal_data *data; @@ -715,9 +742,10 @@ static int imx_thermal_probe(struct platform_device *pdev) if (of_find_property(pdev->dev.of_node, "nvmem-cells", NULL)) { ret = imx_init_from_nvmem_cells(pdev); - if (ret == -EPROBE_DEFER) - return ret; if (ret) { + if (ret == -EPROBE_DEFER) + return ret; + dev_err(&pdev->dev, "failed to init from nvmem: %d\n", ret); return ret; @@ -743,14 +771,11 @@ static int imx_thermal_probe(struct platform_device *pdev) regmap_write(map, data->socdata->sensor_ctrl + REG_SET, data->socdata->power_down_mask); - data->policy = cpufreq_cpu_get(0); - if (!data->policy) { - pr_debug("%s: CPUFreq policy not found\n", __func__); - return -EPROBE_DEFER; - } - ret = imx_thermal_register_legacy_cooling(data); if (ret) { + if (ret == -EPROBE_DEFER) + return ret; + dev_err(&pdev->dev, "failed to register cpufreq cooling device: %d\n", ret); return ret; @@ -762,7 +787,7 @@ static int imx_thermal_probe(struct platform_device *pdev) if (ret != -EPROBE_DEFER) dev_err(&pdev->dev, "failed to get thermal clk: %d\n", ret); - goto cpufreq_put; + goto legacy_cleanup; } /* @@ -775,7 +800,7 @@ static int imx_thermal_probe(struct platform_device *pdev) ret = clk_prepare_enable(data->thermal_clk); if (ret) { dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret); - goto cpufreq_put; + goto legacy_cleanup; } data->tz = thermal_zone_device_register("imx_thermal_zone", @@ -829,9 +854,8 @@ thermal_zone_unregister: thermal_zone_device_unregister(data->tz); clk_disable: clk_disable_unprepare(data->thermal_clk); -cpufreq_put: - cpufreq_cooling_unregister(data->cdev); - cpufreq_cpu_put(data->policy); +legacy_cleanup: + imx_thermal_unregister_legacy_cooling(data); return ret; } diff --git a/drivers/thermal/intel/Kconfig b/drivers/thermal/intel/Kconfig new file mode 100644 index 000000000000..2e013eeb4a1d --- /dev/null +++ b/drivers/thermal/intel/Kconfig @@ -0,0 +1,77 @@ +config INTEL_POWERCLAMP + tristate "Intel PowerClamp idle injection driver" + depends on THERMAL + depends on X86 + depends on CPU_SUP_INTEL + help + Enable this to enable Intel PowerClamp idle injection driver. This + enforce idle time which results in more package C-state residency. The + user interface is exposed via generic thermal framework. + +config X86_PKG_TEMP_THERMAL + tristate "X86 package temperature thermal driver" + depends on X86_THERMAL_VECTOR + select THERMAL_GOV_USER_SPACE + select THERMAL_WRITABLE_TRIPS + default m + help + Enable this to register CPU digital sensor for package temperature as + thermal zone. Each package will have its own thermal zone. There are + two trip points which can be set by user to get notifications via thermal + notification methods. + +config INTEL_SOC_DTS_IOSF_CORE + tristate + depends on X86 && PCI + select IOSF_MBI + help + This is becoming a common feature for Intel SoCs to expose the additional + digital temperature sensors (DTSs) using side band interface (IOSF). This + implements the common set of helper functions to register, get temperature + and get/set thresholds on DTSs. + +config INTEL_SOC_DTS_THERMAL + tristate "Intel SoCs DTS thermal driver" + depends on X86 && PCI && ACPI + select INTEL_SOC_DTS_IOSF_CORE + select THERMAL_WRITABLE_TRIPS + help + Enable this to register Intel SoCs (e.g. Bay Trail) platform digital + temperature sensor (DTS). These SoCs have two additional DTSs in + addition to DTSs on CPU cores. Each DTS will be registered as a + thermal zone. There are two trip points. One of the trip point can + be set by user mode programs to get notifications via Linux thermal + notification methods.The other trip is a critical trip point, which + was set by the driver based on the TJ MAX temperature. + +config INTEL_QUARK_DTS_THERMAL + tristate "Intel Quark DTS thermal driver" + depends on X86_INTEL_QUARK + help + Enable this to register Intel Quark SoC (e.g. X1000) platform digital + temperature sensor (DTS). For X1000 SoC, it has one on-die DTS. + The DTS will be registered as a thermal zone. There are two trip points: + hot & critical. The critical trip point default value is set by + underlying BIOS/Firmware. + +menu "ACPI INT340X thermal drivers" +source "drivers/thermal/intel/int340x_thermal/Kconfig" +endmenu + +config INTEL_BXT_PMIC_THERMAL + tristate "Intel Broxton PMIC thermal driver" + depends on X86 && INTEL_SOC_PMIC_BXTWC && REGMAP + help + Select this driver for Intel Broxton PMIC with ADC channels monitoring + system temperature measurements and alerts. + This driver is used for monitoring the ADC channels of PMIC and handles + the alert trip point interrupts and notifies the thermal framework with + the trip point and temperature details of the zone. + +config INTEL_PCH_THERMAL + tristate "Intel PCH Thermal Reporting Driver" + depends on X86 && PCI + help + Enable this to support thermal reporting on certain intel PCHs. + Thermal reporting device will provide temperature reading, + programmable trip points and other information. diff --git a/drivers/thermal/intel/Makefile b/drivers/thermal/intel/Makefile new file mode 100644 index 000000000000..0d9736ced5d4 --- /dev/null +++ b/drivers/thermal/intel/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for various Intel thermal drivers. + +obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o +obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o +obj-$(CONFIG_INTEL_SOC_DTS_IOSF_CORE) += intel_soc_dts_iosf.o +obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o +obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o +obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ +obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o +obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o diff --git a/drivers/thermal/int340x_thermal/Kconfig b/drivers/thermal/intel/int340x_thermal/Kconfig index 0582bd12a239..0582bd12a239 100644 --- a/drivers/thermal/int340x_thermal/Kconfig +++ b/drivers/thermal/intel/int340x_thermal/Kconfig diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/intel/int340x_thermal/Makefile index 287eb0a1476d..287eb0a1476d 100644 --- a/drivers/thermal/int340x_thermal/Makefile +++ b/drivers/thermal/intel/int340x_thermal/Makefile diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c index 45e7e5cbdffb..45e7e5cbdffb 100644 --- a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c +++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.h b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h index 58822575fd54..58822575fd54 100644 --- a/drivers/thermal/int340x_thermal/acpi_thermal_rel.h +++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c index e26b01c05e82..61ca7ce3624e 100644 --- a/drivers/thermal/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c @@ -48,8 +48,7 @@ static ssize_t available_uuids_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct platform_device *pdev = to_platform_device(dev); - struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); + struct int3400_thermal_priv *priv = dev_get_drvdata(dev); int i; int length = 0; @@ -68,8 +67,7 @@ static ssize_t available_uuids_show(struct device *dev, static ssize_t current_uuid_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct platform_device *pdev = to_platform_device(dev); - struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); + struct int3400_thermal_priv *priv = dev_get_drvdata(dev); if (priv->uuid_bitmap & (1 << priv->current_uuid_index)) return sprintf(buf, "%s\n", @@ -82,8 +80,7 @@ static ssize_t current_uuid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct platform_device *pdev = to_platform_device(dev); - struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); + struct int3400_thermal_priv *priv = dev_get_drvdata(dev); int i; for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) { diff --git a/drivers/thermal/int340x_thermal/int3402_thermal.c b/drivers/thermal/intel/int340x_thermal/int3402_thermal.c index 8e90b3151a42..8e90b3151a42 100644 --- a/drivers/thermal/int340x_thermal/int3402_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3402_thermal.c diff --git a/drivers/thermal/int340x_thermal/int3403_thermal.c b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c index 0c19fcd56a0d..0c19fcd56a0d 100644 --- a/drivers/thermal/int340x_thermal/int3403_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c diff --git a/drivers/thermal/int340x_thermal/int3406_thermal.c b/drivers/thermal/intel/int340x_thermal/int3406_thermal.c index f69ab026ba24..f69ab026ba24 100644 --- a/drivers/thermal/int340x_thermal/int3406_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3406_thermal.c diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c index 9ec27ac1856b..9ec27ac1856b 100644 --- a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c +++ b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h index 5f3ba4775c5c..5f3ba4775c5c 100644 --- a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h +++ b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c index 284cf2c5a8fd..284cf2c5a8fd 100644 --- a/drivers/thermal/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c diff --git a/drivers/thermal/intel_bxt_pmic_thermal.c b/drivers/thermal/intel/intel_bxt_pmic_thermal.c index 94cfd0064c43..94cfd0064c43 100644 --- a/drivers/thermal/intel_bxt_pmic_thermal.c +++ b/drivers/thermal/intel/intel_bxt_pmic_thermal.c diff --git a/drivers/thermal/intel_pch_thermal.c b/drivers/thermal/intel/intel_pch_thermal.c index 8a7f69b4b022..8a7f69b4b022 100644 --- a/drivers/thermal/intel_pch_thermal.c +++ b/drivers/thermal/intel/intel_pch_thermal.c diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c index cde891c54cde..7571f7c2e7c9 100644 --- a/drivers/thermal/intel_powerclamp.c +++ b/drivers/thermal/intel/intel_powerclamp.c @@ -708,19 +708,7 @@ static int powerclamp_debug_show(struct seq_file *m, void *unused) return 0; } -static int powerclamp_debug_open(struct inode *inode, - struct file *file) -{ - return single_open(file, powerclamp_debug_show, inode->i_private); -} - -static const struct file_operations powerclamp_debug_fops = { - .open = powerclamp_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(powerclamp_debug); static inline void powerclamp_create_debug_files(void) { diff --git a/drivers/thermal/intel_quark_dts_thermal.c b/drivers/thermal/intel/intel_quark_dts_thermal.c index 5d33b350da1c..5d33b350da1c 100644 --- a/drivers/thermal/intel_quark_dts_thermal.c +++ b/drivers/thermal/intel/intel_quark_dts_thermal.c diff --git a/drivers/thermal/intel_soc_dts_iosf.c b/drivers/thermal/intel/intel_soc_dts_iosf.c index e0813dfaa278..e0813dfaa278 100644 --- a/drivers/thermal/intel_soc_dts_iosf.c +++ b/drivers/thermal/intel/intel_soc_dts_iosf.c diff --git a/drivers/thermal/intel_soc_dts_iosf.h b/drivers/thermal/intel/intel_soc_dts_iosf.h index 625e37bf93dc..625e37bf93dc 100644 --- a/drivers/thermal/intel_soc_dts_iosf.h +++ b/drivers/thermal/intel/intel_soc_dts_iosf.h diff --git a/drivers/thermal/intel_soc_dts_thermal.c b/drivers/thermal/intel/intel_soc_dts_thermal.c index d748527d7a38..d748527d7a38 100644 --- a/drivers/thermal/intel_soc_dts_thermal.c +++ b/drivers/thermal/intel/intel_soc_dts_thermal.c diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/intel/x86_pkg_temp_thermal.c index 1ef937d799e4..1ef937d799e4 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/intel/x86_pkg_temp_thermal.c diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig index be32e5abce3c..cdb455ffd575 100644 --- a/drivers/thermal/qcom/Kconfig +++ b/drivers/thermal/qcom/Kconfig @@ -9,3 +9,14 @@ config QCOM_TSENS thermal zone device via the mode file results in disabling the sensor. Also able to set threshold temperature for both hot and cold and update when a threshold is reached. + +config QCOM_SPMI_TEMP_ALARM + tristate "Qualcomm SPMI PMIC Temperature Alarm" + depends on OF && SPMI && IIO + select REGMAP_SPMI + help + This enables a thermal sysfs driver for Qualcomm plug-and-play (QPNP) + PMIC devices. It shows up in sysfs as a thermal sensor with multiple + trip points. The temperature reported by the thermal sensor reflects the + real time die temperature if an ADC is present or an estimate of the + temperature based upon the over temperature stage value. diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile index a821929ede0b..717a08600bb5 100644 --- a/drivers/thermal/qcom/Makefile +++ b/drivers/thermal/qcom/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-v2.o +obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index b2d5d5bf4a9b..c1fd71dbab3e 100644 --- a/drivers/thermal/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -23,7 +23,7 @@ #include <linux/regmap.h> #include <linux/thermal.h> -#include "thermal_core.h" +#include "../thermal_core.h" #define QPNP_TM_REG_TYPE 0x04 #define QPNP_TM_REG_SUBTYPE 0x05 diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c index 3be4be2e0465..78652cac7f3d 100644 --- a/drivers/thermal/qcom/tsens-common.c +++ b/drivers/thermal/qcom/tsens-common.c @@ -114,6 +114,14 @@ int get_temp_common(struct tsens_device *tmdev, int id, int *temp) } static const struct regmap_config tsens_config = { + .name = "tm", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static const struct regmap_config tsens_srot_config = { + .name = "srot", .reg_bits = 32, .val_bits = 32, .reg_stride = 4, @@ -139,8 +147,8 @@ int __init init_common(struct tsens_device *tmdev) if (IS_ERR(srot_base)) return PTR_ERR(srot_base); - tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev, - srot_base, &tsens_config); + tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev, srot_base, + &tsens_srot_config); if (IS_ERR(tmdev->srot_map)) return PTR_ERR(tmdev->srot_map); diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 8014a207d8d9..97462e9b40d8 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -113,10 +113,18 @@ static const struct of_device_id rcar_thermal_dt_ids[] = { .data = &rcar_gen2_thermal, }, { + .compatible = "renesas,thermal-r8a774c0", + .data = &rcar_gen3_thermal, + }, + { .compatible = "renesas,thermal-r8a77970", .data = &rcar_gen3_thermal, }, { + .compatible = "renesas,thermal-r8a77990", + .data = &rcar_gen3_thermal, + }, + { .compatible = "renesas,thermal-r8a77995", .data = &rcar_gen3_thermal, }, diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index f36375d5a16c..9c7643d62ed7 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -1327,8 +1327,7 @@ static int rockchip_thermal_remove(struct platform_device *pdev) static int __maybe_unused rockchip_thermal_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct rockchip_thermal_data *thermal = platform_get_drvdata(pdev); + struct rockchip_thermal_data *thermal = dev_get_drvdata(dev); int i; for (i = 0; i < thermal->chip->chn_num; i++) @@ -1346,8 +1345,7 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev) static int __maybe_unused rockchip_thermal_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct rockchip_thermal_data *thermal = platform_get_drvdata(pdev); + struct rockchip_thermal_data *thermal = dev_get_drvdata(dev); int i; int error; @@ -1376,7 +1374,7 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev) id, thermal->regs, thermal->tshut_temp); if (error) - dev_err(&pdev->dev, "%s: invalid tshut=%d, error=%d\n", + dev_err(dev, "%s: invalid tshut=%d, error=%d\n", __func__, thermal->tshut_temp, error); } diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index 81b35aace9de..8b9d567134d0 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -56,8 +56,7 @@ static struct thermal_zone_device_ops ops = { static int __maybe_unused spear_thermal_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev); + struct thermal_zone_device *spear_thermal = dev_get_drvdata(dev); struct spear_thermal_dev *stdev = spear_thermal->devdata; unsigned int actual_mask = 0; @@ -73,15 +72,14 @@ static int __maybe_unused spear_thermal_suspend(struct device *dev) static int __maybe_unused spear_thermal_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev); + struct thermal_zone_device *spear_thermal = dev_get_drvdata(dev); struct spear_thermal_dev *stdev = spear_thermal->devdata; unsigned int actual_mask = 0; int ret = 0; ret = clk_enable(stdev->clk); if (ret) { - dev_err(&pdev->dev, "Can't enable clock\n"); + dev_err(dev, "Can't enable clock\n"); return ret; } diff --git a/drivers/thermal/st/Makefile b/drivers/thermal/st/Makefile index b2b9e9b96296..243ca7881b12 100644 --- a/drivers/thermal/st/Makefile +++ b/drivers/thermal/st/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_ST_THERMAL) := st_thermal.o obj-$(CONFIG_ST_THERMAL_SYSCFG) += st_thermal_syscfg.o obj-$(CONFIG_ST_THERMAL_MEMMAP) += st_thermal_memmap.o -obj-$(CONFIG_STM32_THERMAL) := stm_thermal.o
\ No newline at end of file +obj-$(CONFIG_STM32_THERMAL) += stm_thermal.o diff --git a/drivers/thermal/st/st_thermal.c b/drivers/thermal/st/st_thermal.c index be637e6b01d2..b2bbdf6eb02b 100644 --- a/drivers/thermal/st/st_thermal.c +++ b/drivers/thermal/st/st_thermal.c @@ -277,8 +277,7 @@ EXPORT_SYMBOL_GPL(st_thermal_unregister); #ifdef CONFIG_PM_SLEEP static int st_thermal_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct st_thermal_sensor *sensor = platform_get_drvdata(pdev); + struct st_thermal_sensor *sensor = dev_get_drvdata(dev); return st_thermal_sensor_off(sensor); } @@ -286,8 +285,7 @@ static int st_thermal_suspend(struct device *dev) static int st_thermal_resume(struct device *dev) { int ret; - struct platform_device *pdev = to_platform_device(dev); - struct st_thermal_sensor *sensor = platform_get_drvdata(pdev); + struct st_thermal_sensor *sensor = dev_get_drvdata(dev); ret = st_thermal_sensor_on(sensor); if (ret) diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index ed28110a3535..45b41b885f49 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -803,17 +803,7 @@ static int regs_show(struct seq_file *s, void *data) return 0; } -static int regs_open(struct inode *inode, struct file *file) -{ - return single_open(file, regs_show, inode->i_private); -} - -static const struct file_operations regs_fops = { - .open = regs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(regs); static void soctherm_debug_init(struct platform_device *pdev) { diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index bf1c628d4a7a..e22fc60ad36d 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -26,7 +26,7 @@ struct gadc_thermal_info { static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val) { - int temp, adc_hi, adc_lo; + int temp, temp_hi, temp_lo, adc_hi, adc_lo; int i; for (i = 0; i < gti->nlookup_table; i++) { @@ -36,13 +36,17 @@ static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val) if (i == 0) { temp = gti->lookup_table[0]; - } else if (i >= (gti->nlookup_table - 1)) { + } else if (i >= gti->nlookup_table) { temp = gti->lookup_table[2 * (gti->nlookup_table - 1)]; } else { adc_hi = gti->lookup_table[2 * i - 1]; adc_lo = gti->lookup_table[2 * i + 1]; - temp = gti->lookup_table[2 * i]; - temp -= ((val - adc_lo) * 1000) / (adc_hi - adc_lo); + + temp_hi = gti->lookup_table[2 * i - 2]; + temp_lo = gti->lookup_table[2 * i]; + + temp = temp_hi + mult_frac(temp_lo - temp_hi, val - adc_hi, + adc_lo - adc_hi); } return temp; diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index d6ebc1cf6aa9..6590bb5cb688 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -315,9 +315,7 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz) mutex_unlock(&tz->lock); } -static void handle_non_critical_trips(struct thermal_zone_device *tz, - int trip, - enum thermal_trip_type trip_type) +static void handle_non_critical_trips(struct thermal_zone_device *tz, int trip) { tz->governor ? tz->governor->throttle(tz, trip) : def_governor->throttle(tz, trip); @@ -418,7 +416,7 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT) handle_critical_trips(tz, trip, type); else - handle_non_critical_trips(tz, trip, type); + handle_non_critical_trips(tz, trip); /* * Alright, we handled this trip successfully. * So, start monitoring again. @@ -453,16 +451,20 @@ static void update_temperature(struct thermal_zone_device *tz) tz->last_temperature, tz->temperature); } -static void thermal_zone_device_reset(struct thermal_zone_device *tz) +static void thermal_zone_device_init(struct thermal_zone_device *tz) { struct thermal_instance *pos; - tz->temperature = THERMAL_TEMP_INVALID; - tz->passive = 0; list_for_each_entry(pos, &tz->thermal_instances, tz_node) pos->initialized = false; } +static void thermal_zone_device_reset(struct thermal_zone_device *tz) +{ + tz->passive = 0; + thermal_zone_device_init(tz); +} + void thermal_zone_device_update(struct thermal_zone_device *tz, enum thermal_notify_event event) { @@ -1504,7 +1506,7 @@ static int thermal_pm_notify(struct notifier_block *nb, case PM_POST_SUSPEND: atomic_set(&in_suspend, 0); list_for_each_entry(tz, &thermal_tz_list, node) { - thermal_zone_device_reset(tz); + thermal_zone_device_init(tz); thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); } diff --git a/drivers/thermal/thermal_hwmon.h b/drivers/thermal/thermal_hwmon.h index 019f6f88224e..a160b9d62dd0 100644 --- a/drivers/thermal/thermal_hwmon.h +++ b/drivers/thermal/thermal_hwmon.h @@ -19,13 +19,13 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz); void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz); #else -static int +static inline int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) { return 0; } -static void +static inline void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) { } diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 2241ceae7d7f..aa99edb4dff7 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -712,11 +712,14 @@ cur_state_store(struct device *dev, struct device_attribute *attr, if ((long)state < 0) return -EINVAL; + mutex_lock(&cdev->lock); + result = cdev->ops->set_cur_state(cdev, state); - if (result) - return result; - thermal_cooling_device_stats_update(cdev, state); - return count; + if (!result) + thermal_cooling_device_stats_update(cdev, state); + + mutex_unlock(&cdev->lock); + return result ? result : count; } static struct device_attribute diff --git a/drivers/thermal/uniphier_thermal.c b/drivers/thermal/uniphier_thermal.c index 55477d74d591..bba2284412d3 100644 --- a/drivers/thermal/uniphier_thermal.c +++ b/drivers/thermal/uniphier_thermal.c @@ -1,21 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /** * uniphier_thermal.c - Socionext UniPhier thermal driver - * * Copyright 2014 Panasonic Corporation * Copyright 2016-2017 Socionext Inc. - * All rights reserved. - * * Author: * Kunihiko Hayashi <hayashi.kunihiko@socionext.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 of - * the License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/bitops.h> diff --git a/drivers/thermal/zx2967_thermal.c b/drivers/thermal/zx2967_thermal.c index 6acce0bce7c0..145ebf371598 100644 --- a/drivers/thermal/zx2967_thermal.c +++ b/drivers/thermal/zx2967_thermal.c @@ -207,8 +207,7 @@ MODULE_DEVICE_TABLE(of, zx2967_thermal_id_table); #ifdef CONFIG_PM_SLEEP static int zx2967_thermal_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev); + struct zx2967_thermal_priv *priv = dev_get_drvdata(dev); if (priv && priv->clk_topcrm) clk_disable_unprepare(priv->clk_topcrm); @@ -221,8 +220,7 @@ static int zx2967_thermal_suspend(struct device *dev) static int zx2967_thermal_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev); + struct zx2967_thermal_priv *priv = dev_get_drvdata(dev); int error; error = clk_prepare_enable(priv->clk_topcrm); diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 99460af61b77..4164414d4c64 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -573,7 +573,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, return -EIO; /* verify user access to buffer */ - if (!access_ok(VERIFY_WRITE, buf, nr)) { + if (!access_ok(buf, nr)) { printk(KERN_WARNING "%s(%d) n_hdlc_tty_read() can't verify user " "buffer\n", __FILE__, __LINE__); return -EFAULT; diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 32886c304641..67b9bf3b500e 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1529,6 +1529,25 @@ config SERIAL_OWL_CONSOLE Say 'Y' here if you wish to use Actions Semiconductor S500/S900 UART as the system console. +config SERIAL_RDA + bool "RDA Micro serial port support" + depends on ARCH_RDA || COMPILE_TEST + select SERIAL_CORE + help + This driver is for RDA8810PL SoC's UART. + Say 'Y' here if you wish to use the on-board serial port. + Otherwise, say 'N'. + +config SERIAL_RDA_CONSOLE + bool "Console on RDA Micro serial port" + depends on SERIAL_RDA=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + default y + help + Say 'Y' here if you wish to use the RDA8810PL UART as the system + console. Only earlycon is implemented currently. + endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index daac675612df..8c303736b7e8 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_SERIAL_MVEBU_UART) += mvebu-uart.o obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o obj-$(CONFIG_SERIAL_OWL) += owl-uart.o +obj-$(CONFIG_SERIAL_RDA) += rda-uart.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c new file mode 100644 index 000000000000..284623eefaeb --- /dev/null +++ b/drivers/tty/serial/rda-uart.c @@ -0,0 +1,831 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * RDA8810PL serial device driver + * + * Copyright RDA Microelectronics Company Limited + * Copyright (c) 2017 Andreas Färber + * Copyright (c) 2018 Manivannan Sadhasivam + */ + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +#define RDA_UART_PORT_NUM 3 +#define RDA_UART_DEV_NAME "ttyRDA" + +#define RDA_UART_CTRL 0x00 +#define RDA_UART_STATUS 0x04 +#define RDA_UART_RXTX_BUFFER 0x08 +#define RDA_UART_IRQ_MASK 0x0c +#define RDA_UART_IRQ_CAUSE 0x10 +#define RDA_UART_IRQ_TRIGGERS 0x14 +#define RDA_UART_CMD_SET 0x18 +#define RDA_UART_CMD_CLR 0x1c + +/* UART_CTRL Bits */ +#define RDA_UART_ENABLE BIT(0) +#define RDA_UART_DBITS_8 BIT(1) +#define RDA_UART_TX_SBITS_2 BIT(2) +#define RDA_UART_PARITY_EN BIT(3) +#define RDA_UART_PARITY(x) (((x) & 0x3) << 4) +#define RDA_UART_PARITY_ODD RDA_UART_PARITY(0) +#define RDA_UART_PARITY_EVEN RDA_UART_PARITY(1) +#define RDA_UART_PARITY_SPACE RDA_UART_PARITY(2) +#define RDA_UART_PARITY_MARK RDA_UART_PARITY(3) +#define RDA_UART_DIV_MODE BIT(20) +#define RDA_UART_IRDA_EN BIT(21) +#define RDA_UART_DMA_EN BIT(22) +#define RDA_UART_FLOW_CNT_EN BIT(23) +#define RDA_UART_LOOP_BACK_EN BIT(24) +#define RDA_UART_RX_LOCK_ERR BIT(25) +#define RDA_UART_RX_BREAK_LEN(x) (((x) & 0xf) << 28) + +/* UART_STATUS Bits */ +#define RDA_UART_RX_FIFO(x) (((x) & 0x7f) << 0) +#define RDA_UART_RX_FIFO_MASK (0x7f << 0) +#define RDA_UART_TX_FIFO(x) (((x) & 0x1f) << 8) +#define RDA_UART_TX_FIFO_MASK (0x1f << 8) +#define RDA_UART_TX_ACTIVE BIT(14) +#define RDA_UART_RX_ACTIVE BIT(15) +#define RDA_UART_RX_OVERFLOW_ERR BIT(16) +#define RDA_UART_TX_OVERFLOW_ERR BIT(17) +#define RDA_UART_RX_PARITY_ERR BIT(18) +#define RDA_UART_RX_FRAMING_ERR BIT(19) +#define RDA_UART_RX_BREAK_INT BIT(20) +#define RDA_UART_DCTS BIT(24) +#define RDA_UART_CTS BIT(25) +#define RDA_UART_DTR BIT(28) +#define RDA_UART_CLK_ENABLED BIT(31) + +/* UART_RXTX_BUFFER Bits */ +#define RDA_UART_RX_DATA(x) (((x) & 0xff) << 0) +#define RDA_UART_TX_DATA(x) (((x) & 0xff) << 0) + +/* UART_IRQ_MASK Bits */ +#define RDA_UART_TX_MODEM_STATUS BIT(0) +#define RDA_UART_RX_DATA_AVAILABLE BIT(1) +#define RDA_UART_TX_DATA_NEEDED BIT(2) +#define RDA_UART_RX_TIMEOUT BIT(3) +#define RDA_UART_RX_LINE_ERR BIT(4) +#define RDA_UART_TX_DMA_DONE BIT(5) +#define RDA_UART_RX_DMA_DONE BIT(6) +#define RDA_UART_RX_DMA_TIMEOUT BIT(7) +#define RDA_UART_DTR_RISE BIT(8) +#define RDA_UART_DTR_FALL BIT(9) + +/* UART_IRQ_CAUSE Bits */ +#define RDA_UART_TX_MODEM_STATUS_U BIT(16) +#define RDA_UART_RX_DATA_AVAILABLE_U BIT(17) +#define RDA_UART_TX_DATA_NEEDED_U BIT(18) +#define RDA_UART_RX_TIMEOUT_U BIT(19) +#define RDA_UART_RX_LINE_ERR_U BIT(20) +#define RDA_UART_TX_DMA_DONE_U BIT(21) +#define RDA_UART_RX_DMA_DONE_U BIT(22) +#define RDA_UART_RX_DMA_TIMEOUT_U BIT(23) +#define RDA_UART_DTR_RISE_U BIT(24) +#define RDA_UART_DTR_FALL_U BIT(25) + +/* UART_TRIGGERS Bits */ +#define RDA_UART_RX_TRIGGER(x) (((x) & 0x1f) << 0) +#define RDA_UART_TX_TRIGGER(x) (((x) & 0xf) << 8) +#define RDA_UART_AFC_LEVEL(x) (((x) & 0x1f) << 16) + +/* UART_CMD_SET Bits */ +#define RDA_UART_RI BIT(0) +#define RDA_UART_DCD BIT(1) +#define RDA_UART_DSR BIT(2) +#define RDA_UART_TX_BREAK_CONTROL BIT(3) +#define RDA_UART_TX_FINISH_N_WAIT BIT(4) +#define RDA_UART_RTS BIT(5) +#define RDA_UART_RX_FIFO_RESET BIT(6) +#define RDA_UART_TX_FIFO_RESET BIT(7) + +#define RDA_UART_TX_FIFO_SIZE 16 + +static struct uart_driver rda_uart_driver; + +struct rda_uart_port { + struct uart_port port; + struct clk *clk; +}; + +#define to_rda_uart_port(port) container_of(port, struct rda_uart_port, port) + +static struct rda_uart_port *rda_uart_ports[RDA_UART_PORT_NUM]; + +static inline void rda_uart_write(struct uart_port *port, u32 val, + unsigned int off) +{ + writel(val, port->membase + off); +} + +static inline u32 rda_uart_read(struct uart_port *port, unsigned int off) +{ + return readl(port->membase + off); +} + +static unsigned int rda_uart_tx_empty(struct uart_port *port) +{ + unsigned long flags; + unsigned int ret; + u32 val; + + spin_lock_irqsave(&port->lock, flags); + + val = rda_uart_read(port, RDA_UART_STATUS); + ret = (val & RDA_UART_TX_FIFO_MASK) ? TIOCSER_TEMT : 0; + + spin_unlock_irqrestore(&port->lock, flags); + + return ret; +} + +static unsigned int rda_uart_get_mctrl(struct uart_port *port) +{ + unsigned int mctrl = 0; + u32 cmd_set, status; + + cmd_set = rda_uart_read(port, RDA_UART_CMD_SET); + status = rda_uart_read(port, RDA_UART_STATUS); + if (cmd_set & RDA_UART_RTS) + mctrl |= TIOCM_RTS; + if (!(status & RDA_UART_CTS)) + mctrl |= TIOCM_CTS; + + return mctrl; +} + +static void rda_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + u32 val; + + if (mctrl & TIOCM_RTS) { + val = rda_uart_read(port, RDA_UART_CMD_SET); + rda_uart_write(port, (val | RDA_UART_RTS), RDA_UART_CMD_SET); + } else { + /* Clear RTS to stop to receive. */ + val = rda_uart_read(port, RDA_UART_CMD_CLR); + rda_uart_write(port, (val | RDA_UART_RTS), RDA_UART_CMD_CLR); + } + + val = rda_uart_read(port, RDA_UART_CTRL); + + if (mctrl & TIOCM_LOOP) + val |= RDA_UART_LOOP_BACK_EN; + else + val &= ~RDA_UART_LOOP_BACK_EN; + + rda_uart_write(port, val, RDA_UART_CTRL); +} + +static void rda_uart_stop_tx(struct uart_port *port) +{ + u32 val; + + val = rda_uart_read(port, RDA_UART_IRQ_MASK); + val &= ~RDA_UART_TX_DATA_NEEDED; + rda_uart_write(port, val, RDA_UART_IRQ_MASK); + + val = rda_uart_read(port, RDA_UART_CMD_SET); + val |= RDA_UART_TX_FIFO_RESET; + rda_uart_write(port, val, RDA_UART_CMD_SET); +} + +static void rda_uart_stop_rx(struct uart_port *port) +{ + u32 val; + + val = rda_uart_read(port, RDA_UART_IRQ_MASK); + val &= ~(RDA_UART_RX_DATA_AVAILABLE | RDA_UART_RX_TIMEOUT); + rda_uart_write(port, val, RDA_UART_IRQ_MASK); + + /* Read Rx buffer before reset to avoid Rx timeout interrupt */ + val = rda_uart_read(port, RDA_UART_RXTX_BUFFER); + + val = rda_uart_read(port, RDA_UART_CMD_SET); + val |= RDA_UART_RX_FIFO_RESET; + rda_uart_write(port, val, RDA_UART_CMD_SET); +} + +static void rda_uart_start_tx(struct uart_port *port) +{ + u32 val; + + if (uart_tx_stopped(port)) { + rda_uart_stop_tx(port); + return; + } + + val = rda_uart_read(port, RDA_UART_IRQ_MASK); + val |= RDA_UART_TX_DATA_NEEDED; + rda_uart_write(port, val, RDA_UART_IRQ_MASK); +} + +static void rda_uart_change_baudrate(struct rda_uart_port *rda_port, + unsigned long baud) +{ + clk_set_rate(rda_port->clk, baud * 8); +} + +static void rda_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct rda_uart_port *rda_port = to_rda_uart_port(port); + unsigned long flags; + unsigned int ctrl, cmd_set, cmd_clr, triggers; + unsigned int baud; + u32 irq_mask; + + spin_lock_irqsave(&port->lock, flags); + + baud = uart_get_baud_rate(port, termios, old, 9600, port->uartclk / 4); + rda_uart_change_baudrate(rda_port, baud); + + ctrl = rda_uart_read(port, RDA_UART_CTRL); + cmd_set = rda_uart_read(port, RDA_UART_CMD_SET); + cmd_clr = rda_uart_read(port, RDA_UART_CMD_CLR); + + switch (termios->c_cflag & CSIZE) { + case CS5: + case CS6: + dev_warn(port->dev, "bit size not supported, using 7 bits\n"); + /* Fall through */ + case CS7: + ctrl &= ~RDA_UART_DBITS_8; + break; + default: + ctrl |= RDA_UART_DBITS_8; + break; + } + + /* stop bits */ + if (termios->c_cflag & CSTOPB) + ctrl |= RDA_UART_TX_SBITS_2; + else + ctrl &= ~RDA_UART_TX_SBITS_2; + + /* parity check */ + if (termios->c_cflag & PARENB) { + ctrl |= RDA_UART_PARITY_EN; + + /* Mark or Space parity */ + if (termios->c_cflag & CMSPAR) { + if (termios->c_cflag & PARODD) + ctrl |= RDA_UART_PARITY_MARK; + else + ctrl |= RDA_UART_PARITY_SPACE; + } else if (termios->c_cflag & PARODD) { + ctrl |= RDA_UART_PARITY_ODD; + } else { + ctrl |= RDA_UART_PARITY_EVEN; + } + } else { + ctrl &= ~RDA_UART_PARITY_EN; + } + + /* Hardware handshake (RTS/CTS) */ + if (termios->c_cflag & CRTSCTS) { + ctrl |= RDA_UART_FLOW_CNT_EN; + cmd_set |= RDA_UART_RTS; + } else { + ctrl &= ~RDA_UART_FLOW_CNT_EN; + cmd_clr |= RDA_UART_RTS; + } + + ctrl |= RDA_UART_ENABLE; + ctrl &= ~RDA_UART_DMA_EN; + + triggers = (RDA_UART_AFC_LEVEL(20) | RDA_UART_RX_TRIGGER(16)); + irq_mask = rda_uart_read(port, RDA_UART_IRQ_MASK); + rda_uart_write(port, 0, RDA_UART_IRQ_MASK); + + rda_uart_write(port, triggers, RDA_UART_IRQ_TRIGGERS); + rda_uart_write(port, ctrl, RDA_UART_CTRL); + rda_uart_write(port, cmd_set, RDA_UART_CMD_SET); + rda_uart_write(port, cmd_clr, RDA_UART_CMD_CLR); + + rda_uart_write(port, irq_mask, RDA_UART_IRQ_MASK); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void rda_uart_send_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + unsigned int ch; + u32 val; + + if (uart_tx_stopped(port)) + return; + + if (port->x_char) { + while (!(rda_uart_read(port, RDA_UART_STATUS) & + RDA_UART_TX_FIFO_MASK)) + cpu_relax(); + + rda_uart_write(port, port->x_char, RDA_UART_RXTX_BUFFER); + port->icount.tx++; + port->x_char = 0; + } + + while (rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK) { + if (uart_circ_empty(xmit)) + break; + + ch = xmit->buf[xmit->tail]; + rda_uart_write(port, ch, RDA_UART_RXTX_BUFFER); + xmit->tail = (xmit->tail + 1) & (SERIAL_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (!uart_circ_empty(xmit)) { + /* Re-enable Tx FIFO interrupt */ + val = rda_uart_read(port, RDA_UART_IRQ_MASK); + val |= RDA_UART_TX_DATA_NEEDED; + rda_uart_write(port, val, RDA_UART_IRQ_MASK); + } +} + +static void rda_uart_receive_chars(struct uart_port *port) +{ + u32 status, val; + + status = rda_uart_read(port, RDA_UART_STATUS); + while ((status & RDA_UART_RX_FIFO_MASK)) { + char flag = TTY_NORMAL; + + if (status & RDA_UART_RX_PARITY_ERR) { + port->icount.parity++; + flag = TTY_PARITY; + } + + if (status & RDA_UART_RX_FRAMING_ERR) { + port->icount.frame++; + flag = TTY_FRAME; + } + + if (status & RDA_UART_RX_OVERFLOW_ERR) { + port->icount.overrun++; + flag = TTY_OVERRUN; + } + + val = rda_uart_read(port, RDA_UART_RXTX_BUFFER); + val &= 0xff; + + port->icount.rx++; + tty_insert_flip_char(&port->state->port, val, flag); + + status = rda_uart_read(port, RDA_UART_STATUS); + } + + spin_unlock(&port->lock); + tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); +} + +static irqreturn_t rda_interrupt(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned long flags; + u32 val, irq_mask; + + spin_lock_irqsave(&port->lock, flags); + + /* Clear IRQ cause */ + val = rda_uart_read(port, RDA_UART_IRQ_CAUSE); + rda_uart_write(port, val, RDA_UART_IRQ_CAUSE); + + if (val & (RDA_UART_RX_DATA_AVAILABLE | RDA_UART_RX_TIMEOUT)) + rda_uart_receive_chars(port); + + if (val & (RDA_UART_TX_DATA_NEEDED)) { + irq_mask = rda_uart_read(port, RDA_UART_IRQ_MASK); + irq_mask &= ~RDA_UART_TX_DATA_NEEDED; + rda_uart_write(port, irq_mask, RDA_UART_IRQ_MASK); + + rda_uart_send_chars(port); + } + + spin_unlock_irqrestore(&port->lock, flags); + + return IRQ_HANDLED; +} + +static int rda_uart_startup(struct uart_port *port) +{ + unsigned long flags; + int ret; + u32 val; + + spin_lock_irqsave(&port->lock, flags); + rda_uart_write(port, 0, RDA_UART_IRQ_MASK); + spin_unlock_irqrestore(&port->lock, flags); + + ret = request_irq(port->irq, rda_interrupt, IRQF_NO_SUSPEND, + "rda-uart", port); + if (ret) + return ret; + + spin_lock_irqsave(&port->lock, flags); + + val = rda_uart_read(port, RDA_UART_CTRL); + val |= RDA_UART_ENABLE; + rda_uart_write(port, val, RDA_UART_CTRL); + + /* enable rx interrupt */ + val = rda_uart_read(port, RDA_UART_IRQ_MASK); + val |= (RDA_UART_RX_DATA_AVAILABLE | RDA_UART_RX_TIMEOUT); + rda_uart_write(port, val, RDA_UART_IRQ_MASK); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void rda_uart_shutdown(struct uart_port *port) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&port->lock, flags); + + rda_uart_stop_tx(port); + rda_uart_stop_rx(port); + + val = rda_uart_read(port, RDA_UART_CTRL); + val &= ~RDA_UART_ENABLE; + rda_uart_write(port, val, RDA_UART_CTRL); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *rda_uart_type(struct uart_port *port) +{ + return (port->type == PORT_RDA) ? "rda-uart" : NULL; +} + +static int rda_uart_request_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + if (!devm_request_mem_region(port->dev, port->mapbase, + resource_size(res), dev_name(port->dev))) + return -EBUSY; + + if (port->flags & UPF_IOREMAP) { + port->membase = devm_ioremap_nocache(port->dev, port->mapbase, + resource_size(res)); + if (!port->membase) + return -EBUSY; + } + + return 0; +} + +static void rda_uart_config_port(struct uart_port *port, int flags) +{ + unsigned long irq_flags; + + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_RDA; + rda_uart_request_port(port); + } + + spin_lock_irqsave(&port->lock, irq_flags); + + /* Clear mask, so no surprise interrupts. */ + rda_uart_write(port, 0, RDA_UART_IRQ_MASK); + + /* Clear status register */ + rda_uart_write(port, 0, RDA_UART_STATUS); + + spin_unlock_irqrestore(&port->lock, irq_flags); +} + +static void rda_uart_release_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return; + + if (port->flags & UPF_IOREMAP) { + devm_release_mem_region(port->dev, port->mapbase, + resource_size(res)); + devm_iounmap(port->dev, port->membase); + port->membase = NULL; + } +} + +static int rda_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (port->type != PORT_RDA) + return -EINVAL; + + if (port->irq != ser->irq) + return -EINVAL; + + return 0; +} + +static const struct uart_ops rda_uart_ops = { + .tx_empty = rda_uart_tx_empty, + .get_mctrl = rda_uart_get_mctrl, + .set_mctrl = rda_uart_set_mctrl, + .start_tx = rda_uart_start_tx, + .stop_tx = rda_uart_stop_tx, + .stop_rx = rda_uart_stop_rx, + .startup = rda_uart_startup, + .shutdown = rda_uart_shutdown, + .set_termios = rda_uart_set_termios, + .type = rda_uart_type, + .request_port = rda_uart_request_port, + .release_port = rda_uart_release_port, + .config_port = rda_uart_config_port, + .verify_port = rda_uart_verify_port, +}; + +#ifdef CONFIG_SERIAL_RDA_CONSOLE + +static void rda_console_putchar(struct uart_port *port, int ch) +{ + if (!port->membase) + return; + + while (!(rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK)) + cpu_relax(); + + rda_uart_write(port, ch, RDA_UART_RXTX_BUFFER); +} + +static void rda_uart_port_write(struct uart_port *port, const char *s, + u_int count) +{ + u32 old_irq_mask; + unsigned long flags; + int locked; + + local_irq_save(flags); + + if (port->sysrq) { + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&port->lock); + } else { + spin_lock(&port->lock); + locked = 1; + } + + old_irq_mask = rda_uart_read(port, RDA_UART_IRQ_MASK); + rda_uart_write(port, 0, RDA_UART_IRQ_MASK); + + uart_console_write(port, s, count, rda_console_putchar); + + /* wait until all contents have been sent out */ + while (!(rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK)) + cpu_relax(); + + rda_uart_write(port, old_irq_mask, RDA_UART_IRQ_MASK); + + if (locked) + spin_unlock(&port->lock); + + local_irq_restore(flags); +} + +static void rda_uart_console_write(struct console *co, const char *s, + u_int count) +{ + struct rda_uart_port *rda_port; + + rda_port = rda_uart_ports[co->index]; + if (!rda_port) + return; + + rda_uart_port_write(&rda_port->port, s, count); +} + +static int rda_uart_console_setup(struct console *co, char *options) +{ + struct rda_uart_port *rda_port; + int baud = 921600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= RDA_UART_PORT_NUM) + return -EINVAL; + + rda_port = rda_uart_ports[co->index]; + if (!rda_port || !rda_port->port.membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&rda_port->port, co, baud, parity, bits, flow); +} + +static struct console rda_uart_console = { + .name = RDA_UART_DEV_NAME, + .write = rda_uart_console_write, + .device = uart_console_device, + .setup = rda_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &rda_uart_driver, +}; + +static int __init rda_uart_console_init(void) +{ + register_console(&rda_uart_console); + + return 0; +} +console_initcall(rda_uart_console_init); + +static void rda_uart_early_console_write(struct console *co, + const char *s, + u_int count) +{ + struct earlycon_device *dev = co->data; + + rda_uart_port_write(&dev->port, s, count); +} + +static int __init +rda_uart_early_console_setup(struct earlycon_device *device, const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = rda_uart_early_console_write; + + return 0; +} + +OF_EARLYCON_DECLARE(rda, "rda,8810pl-uart", + rda_uart_early_console_setup); + +#define RDA_UART_CONSOLE (&rda_uart_console) +#else +#define RDA_UART_CONSOLE NULL +#endif /* CONFIG_SERIAL_RDA_CONSOLE */ + +static struct uart_driver rda_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "rda-uart", + .dev_name = RDA_UART_DEV_NAME, + .nr = RDA_UART_PORT_NUM, + .cons = RDA_UART_CONSOLE, +}; + +static const struct of_device_id rda_uart_dt_matches[] = { + { .compatible = "rda,8810pl-uart" }, + { } +}; +MODULE_DEVICE_TABLE(of, rda_uart_dt_matches); + +static int rda_uart_probe(struct platform_device *pdev) +{ + struct resource *res_mem; + struct rda_uart_port *rda_port; + int ret, irq; + + if (pdev->dev.of_node) + pdev->id = of_alias_get_id(pdev->dev.of_node, "serial"); + + if (pdev->id < 0 || pdev->id >= RDA_UART_PORT_NUM) { + dev_err(&pdev->dev, "id %d out of range\n", pdev->id); + return -EINVAL; + } + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) { + dev_err(&pdev->dev, "could not get mem\n"); + return -ENODEV; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "could not get irq\n"); + return irq; + } + + if (rda_uart_ports[pdev->id]) { + dev_err(&pdev->dev, "port %d already allocated\n", pdev->id); + return -EBUSY; + } + + rda_port = devm_kzalloc(&pdev->dev, sizeof(*rda_port), GFP_KERNEL); + if (!rda_port) + return -ENOMEM; + + rda_port->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(rda_port->clk)) { + dev_err(&pdev->dev, "could not get clk\n"); + return PTR_ERR(rda_port->clk); + } + + rda_port->port.dev = &pdev->dev; + rda_port->port.regshift = 0; + rda_port->port.line = pdev->id; + rda_port->port.type = PORT_RDA; + rda_port->port.iotype = UPIO_MEM; + rda_port->port.mapbase = res_mem->start; + rda_port->port.irq = irq; + rda_port->port.uartclk = clk_get_rate(rda_port->clk); + if (rda_port->port.uartclk == 0) { + dev_err(&pdev->dev, "clock rate is zero\n"); + return -EINVAL; + } + rda_port->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | + UPF_LOW_LATENCY; + rda_port->port.x_char = 0; + rda_port->port.fifosize = RDA_UART_TX_FIFO_SIZE; + rda_port->port.ops = &rda_uart_ops; + + rda_uart_ports[pdev->id] = rda_port; + platform_set_drvdata(pdev, rda_port); + + ret = uart_add_one_port(&rda_uart_driver, &rda_port->port); + if (ret) + rda_uart_ports[pdev->id] = NULL; + + return ret; +} + +static int 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, + .driver = { + .name = "rda-uart", + .of_match_table = rda_uart_dt_matches, + }, +}; + +static int __init rda_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&rda_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&rda_uart_platform_driver); + if (ret) + uart_unregister_driver(&rda_uart_driver); + + return ret; +} + +static void __init rda_uart_exit(void) +{ + platform_driver_unregister(&rda_uart_platform_driver); + uart_unregister_driver(&rda_uart_driver); +} + +module_init(rda_uart_init); +module_exit(rda_uart_exit); + +MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); +MODULE_DESCRIPTION("RDA8810PL serial device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 3de3c750b5f6..44f28a114c2b 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -598,7 +598,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, return -EINVAL; if (nbytes <= 0) return 0; - if (!access_ok(VERIFY_WRITE, buf, nbytes)) + if (!access_ok(buf, nbytes)) return -EFAULT; mutex_lock(&usb_bus_idr_lock); diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index a75bc0b8a50f..d65566341dd1 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1094,7 +1094,7 @@ static int proc_control(struct usb_dev_state *ps, void __user *arg) ctrl.bRequestType, ctrl.bRequest, ctrl.wValue, ctrl.wIndex, ctrl.wLength); if (ctrl.bRequestType & 0x80) { - if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data, + if (ctrl.wLength && !access_ok(ctrl.data, ctrl.wLength)) { ret = -EINVAL; goto done; @@ -1183,7 +1183,7 @@ static int proc_bulk(struct usb_dev_state *ps, void __user *arg) } tmo = bulk.timeout; if (bulk.ep & 0x80) { - if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) { + if (len1 && !access_ok(bulk.data, len1)) { ret = -EINVAL; goto done; } @@ -1584,8 +1584,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb } if (uurb->buffer_length > 0 && - !access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, - uurb->buffer, uurb->buffer_length)) { + !access_ok(uurb->buffer, uurb->buffer_length)) { ret = -EFAULT; goto error; } diff --git a/drivers/usb/dwc3/dwc3-haps.c b/drivers/usb/dwc3/dwc3-haps.c index c9cc33881bef..02d57d98ef9b 100644 --- a/drivers/usb/dwc3/dwc3-haps.c +++ b/drivers/usb/dwc3/dwc3-haps.c @@ -15,10 +15,6 @@ #include <linux/platform_device.h> #include <linux/property.h> -#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd -#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI 0xabce -#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31 0xabcf - /** * struct dwc3_haps - Driver private structure * @dwc3: child dwc3 platform_device diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 54e859dcb25c..75b113a5b25c 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -252,7 +252,7 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer, if (!count) return 0; - if (!access_ok(VERIFY_WRITE, buffer, count)) + if (!access_ok(buffer, count)) return -EFAULT; spin_lock_irqsave(&hidg->read_spinlock, flags); @@ -339,7 +339,7 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, unsigned long flags; ssize_t status = -ENOMEM; - if (!access_ok(VERIFY_READ, buffer, count)) + if (!access_ok(buffer, count)) return -EFAULT; spin_lock_irqsave(&hidg->write_spinlock, flags); diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 11247322d587..660712e0bf98 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -88,7 +88,7 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf, size_t len, remaining, actual = 0; char tmpbuf[38]; - if (!access_ok(VERIFY_WRITE, buf, nbytes)) + if (!access_ok(buf, nbytes)) return -EFAULT; inode_lock(file_inode(file)); diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 55e5aa662ad5..9f7942cbcbb2 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -655,7 +655,7 @@ static bool log_access_ok(void __user *log_base, u64 addr, unsigned long sz) a + (unsigned long)log_base > ULONG_MAX) return false; - return access_ok(VERIFY_WRITE, log_base + a, + return access_ok(log_base + a, (sz + VHOST_PAGE_SIZE * 8 - 1) / VHOST_PAGE_SIZE / 8); } @@ -681,7 +681,7 @@ static bool vq_memory_access_ok(void __user *log_base, struct vhost_umem *umem, return false; - if (!access_ok(VERIFY_WRITE, (void __user *)a, + if (!access_ok((void __user *)a, node->size)) return false; else if (log_all && !log_access_ok(log_base, @@ -973,10 +973,10 @@ static bool umem_access_ok(u64 uaddr, u64 size, int access) return false; if ((access & VHOST_ACCESS_RO) && - !access_ok(VERIFY_READ, (void __user *)a, size)) + !access_ok((void __user *)a, size)) return false; if ((access & VHOST_ACCESS_WO) && - !access_ok(VERIFY_WRITE, (void __user *)a, size)) + !access_ok((void __user *)a, size)) return false; return true; } @@ -1185,10 +1185,10 @@ static bool vq_access_ok(struct vhost_virtqueue *vq, unsigned int num, { size_t s = vhost_has_feature(vq, VIRTIO_RING_F_EVENT_IDX) ? 2 : 0; - return access_ok(VERIFY_READ, desc, num * sizeof *desc) && - access_ok(VERIFY_READ, avail, + return access_ok(desc, num * sizeof *desc) && + access_ok(avail, sizeof *avail + num * sizeof *avail->ring + s) && - access_ok(VERIFY_WRITE, used, + access_ok(used, sizeof *used + num * sizeof *used->ring + s); } @@ -1814,7 +1814,7 @@ int vhost_vq_init_access(struct vhost_virtqueue *vq) goto err; vq->signalled_used_valid = false; if (!vq->iotlb && - !access_ok(VERIFY_READ, &vq->used->idx, sizeof vq->used->idx)) { + !access_ok(&vq->used->idx, sizeof vq->used->idx)) { r = -EFAULT; goto err; } diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index e413f54208f4..ae7712c9687a 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -184,7 +184,7 @@ config FB_MACMODES depends on FB config FB_BACKLIGHT - bool + tristate depends on FB select BACKLIGHT_LCD_SUPPORT select BACKLIGHT_CLASS_DEVICE @@ -2037,7 +2037,8 @@ config FB_XILINX config FB_GOLDFISH tristate "Goldfish Framebuffer" - depends on FB && HAS_DMA && (GOLDFISH || COMPILE_TEST) + depends on FB + depends on GOLDFISH || COMPILE_TEST select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/video/fbdev/amifb.c b/drivers/video/fbdev/amifb.c index 0777aff211e5..758457026694 100644 --- a/drivers/video/fbdev/amifb.c +++ b/drivers/video/fbdev/amifb.c @@ -1855,7 +1855,7 @@ static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, var->yspot = par->crsr.spot_y; if (size > var->height * var->width) return -ENAMETOOLONG; - if (!access_ok(VERIFY_WRITE, data, size)) + if (!access_ok(data, size)) return -EFAULT; delta = 1 << par->crsr.fmode; lspr = lofsprite + (delta << 1); @@ -1935,7 +1935,7 @@ static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, return -EINVAL; if (!var->height) return -EINVAL; - if (!access_ok(VERIFY_READ, data, var->width * var->height)) + if (!access_ok(data, var->width * var->height)) return -EFAULT; delta = 1 << fmode; lofsprite = shfsprite = (u_short *)spritememory; diff --git a/drivers/video/fbdev/clps711x-fb.c b/drivers/video/fbdev/clps711x-fb.c index ff561073ee4e..42f909618f04 100644 --- a/drivers/video/fbdev/clps711x-fb.c +++ b/drivers/video/fbdev/clps711x-fb.c @@ -287,14 +287,17 @@ static int clps711x_fb_probe(struct platform_device *pdev) } ret = of_get_fb_videomode(disp, &cfb->mode, OF_USE_NATIVE_MODE); - if (ret) + if (ret) { + of_node_put(disp); goto out_fb_release; + } of_property_read_u32(disp, "ac-prescale", &cfb->ac_prescale); cfb->cmap_invert = of_property_read_bool(disp, "cmap-invert"); ret = of_property_read_u32(disp, "bits-per-pixel", &info->var.bits_per_pixel); + of_node_put(disp); if (ret) goto out_fb_release; diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 8958ccc8b1ac..8976190b6c1f 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -3064,7 +3064,7 @@ static int fbcon_fb_unbind(int idx) for (i = first_fb_vc; i <= last_fb_vc; i++) { if (con2fb_map[i] != idx && con2fb_map[i] != -1) { - new_idx = i; + new_idx = con2fb_map[i]; break; } } diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 861bf8081619..558ed2ed3124 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -436,7 +436,9 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image, image->dx += image->width + 8; } } else if (rotate == FB_ROTATE_UD) { - for (x = 0; x < num; x++) { + u32 dx = image->dx; + + for (x = 0; x < num && image->dx <= dx; x++) { info->fbops->fb_imageblit(info, image); image->dx -= image->width + 8; } @@ -448,7 +450,9 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image, image->dy += image->height + 8; } } else if (rotate == FB_ROTATE_CCW) { - for (x = 0; x < num; x++) { + u32 dy = image->dy; + + for (x = 0; x < num && image->dy <= dy; x++) { info->fbops->fb_imageblit(info, image); image->dy -= image->height + 8; } @@ -502,8 +506,25 @@ static int fb_show_logo_line(struct fb_info *info, int rotate, fb_set_logo(info, logo, logo_new, fb_logo.depth); } +#ifdef CONFIG_FB_LOGO_CENTER + { + int xres = info->var.xres; + int yres = info->var.yres; + + if (rotate == FB_ROTATE_CW || rotate == FB_ROTATE_CCW) { + xres = info->var.yres; + yres = info->var.xres; + } + + while (n && (n * (logo->width + 8) - 8 > xres)) + --n; + image.dx = (xres - n * (logo->width + 8) - 8) / 2; + image.dy = y ?: (yres - logo->height) / 2; + } +#else image.dx = 0; image.dy = y; +#endif image.width = logo->width; image.height = logo->height; @@ -521,7 +542,7 @@ static int fb_show_logo_line(struct fb_info *info, int rotate, info->pseudo_palette = saved_pseudo_palette; kfree(logo_new); kfree(logo_rotate); - return logo->height; + return image.dy + logo->height; } @@ -573,8 +594,8 @@ static int fb_show_extra_logos(struct fb_info *info, int y, int rotate) unsigned int i; for (i = 0; i < fb_logo_ex_num; i++) - y += fb_show_logo_line(info, rotate, - fb_logo_ex[i].logo, y, fb_logo_ex[i].n); + y = fb_show_logo_line(info, rotate, + fb_logo_ex[i].logo, y, fb_logo_ex[i].n); return y; } @@ -600,6 +621,7 @@ int fb_prepare_logo(struct fb_info *info, int rotate) { int depth = fb_get_color_depth(&info->var, &info->fix); unsigned int yres; + int height; memset(&fb_logo, 0, sizeof(struct logo_data)); @@ -661,7 +683,12 @@ int fb_prepare_logo(struct fb_info *info, int rotate) } } - return fb_prepare_extra_logos(info, fb_logo.logo->height, yres); + height = fb_logo.logo->height; +#ifdef CONFIG_FB_LOGO_CENTER + height += (yres - fb_logo.logo->height) / 2; +#endif + + return fb_prepare_extra_logos(info, height, yres); } int fb_show_logo(struct fb_info *info, int rotate) diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index e31a182b42bf..44cca39f2b51 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -60,7 +60,7 @@ struct fb_info *framebuffer_alloc(size_t size, struct device *dev) info->device = dev; info->fbcon_rotate_hint = -1; -#ifdef CONFIG_FB_BACKLIGHT +#if IS_ENABLED(CONFIG_FB_BACKLIGHT) mutex_init(&info->bl_curve_mutex); #endif @@ -429,7 +429,7 @@ static ssize_t show_fbstate(struct device *device, return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state); } -#ifdef CONFIG_FB_BACKLIGHT +#if IS_ENABLED(CONFIG_FB_BACKLIGHT) static ssize_t store_bl_curve(struct device *device, struct device_attribute *attr, const char *buf, size_t count) @@ -510,7 +510,7 @@ static struct device_attribute device_attrs[] = { __ATTR(stride, S_IRUGO, show_stride, NULL), __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate), __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate), -#ifdef CONFIG_FB_BACKLIGHT +#if IS_ENABLED(CONFIG_FB_BACKLIGHT) __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve), #endif }; @@ -551,7 +551,7 @@ void fb_cleanup_device(struct fb_info *fb_info) } } -#ifdef CONFIG_FB_BACKLIGHT +#if IS_ENABLED(CONFIG_FB_BACKLIGHT) /* This function generates a linear backlight curve * * 0: off diff --git a/drivers/video/fbdev/fsl-diu-fb.c b/drivers/video/fbdev/fsl-diu-fb.c index 332a56b6811f..9a5451ba4d44 100644 --- a/drivers/video/fbdev/fsl-diu-fb.c +++ b/drivers/video/fbdev/fsl-diu-fb.c @@ -1575,8 +1575,7 @@ static void uninstall_fb(struct fb_info *info) unregister_framebuffer(info); unmap_video_memory(info); - if (&info->cmap) - fb_dealloc_cmap(&info->cmap); + fb_dealloc_cmap(&info->cmap); mfbi->registered = 0; } diff --git a/drivers/video/fbdev/omap2/omapfb/dss/core.c b/drivers/video/fbdev/omap2/omapfb/dss/core.c index a5e58a829ea0..b4bcf3a4a647 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/core.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/core.c @@ -99,24 +99,14 @@ int dss_set_min_bus_tput(struct device *dev, unsigned long tput) } #if defined(CONFIG_FB_OMAP2_DSS_DEBUGFS) -static int dss_debug_show(struct seq_file *s, void *unused) +static int dss_show(struct seq_file *s, void *unused) { void (*func)(struct seq_file *) = s->private; func(s); return 0; } -static int dss_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, dss_debug_show, inode->i_private); -} - -static const struct file_operations dss_debug_fops = { - .open = dss_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(dss); static struct dentry *dss_debugfs_dir; @@ -130,7 +120,7 @@ static int dss_initialize_debugfs(void) } debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir, - &dss_debug_dump_clocks, &dss_debug_fops); + &dss_debug_dump_clocks, &dss_fops); return 0; } @@ -145,7 +135,7 @@ int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) struct dentry *d; d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir, - write, &dss_debug_fops); + write, &dss_fops); return PTR_ERR_OR_ZERO(d); } diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c b/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c index a3edb20ea4c3..53f93616c671 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c @@ -493,7 +493,7 @@ static int omapfb_memory_read(struct fb_info *fbi, if (!display || !display->driver->memory_read) return -ENOENT; - if (!access_ok(VERIFY_WRITE, mr->buffer, mr->buffer_size)) + if (!access_ok(mr->buffer, mr->buffer_size)) return -EFAULT; if (mr->w > 4096 || mr->h > 4096) diff --git a/drivers/video/fbdev/pxa168fb.c b/drivers/video/fbdev/pxa168fb.c index e31340fad3c7..1410f476e135 100644 --- a/drivers/video/fbdev/pxa168fb.c +++ b/drivers/video/fbdev/pxa168fb.c @@ -279,7 +279,7 @@ static void set_clock_divider(struct pxa168fb_info *fbi, /* check whether divisor is too small. */ if (divider_int < 2) { - dev_warn(fbi->dev, "Warning: clock source is too slow." + dev_warn(fbi->dev, "Warning: clock source is too slow. " "Try smaller resolution\n"); divider_int = 2; } diff --git a/drivers/video/fbdev/pxafb.c b/drivers/video/fbdev/pxafb.c index bbed039617a4..d59c8a59f582 100644 --- a/drivers/video/fbdev/pxafb.c +++ b/drivers/video/fbdev/pxafb.c @@ -2234,10 +2234,8 @@ static struct pxafb_mach_info *of_pxafb_of_mach_info(struct device *dev) if (!info) return ERR_PTR(-ENOMEM); ret = of_get_pxafb_mode_info(dev, info); - if (ret) { - kfree(info->modes); + if (ret) return ERR_PTR(ret); - } /* * On purpose, neither lccrX registers nor video memory size can be diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c index 070026a7e55a..1d034dddc556 100644 --- a/drivers/video/fbdev/udlfb.c +++ b/drivers/video/fbdev/udlfb.c @@ -1598,7 +1598,7 @@ static int dlfb_usb_probe(struct usb_interface *intf, dlfb = kzalloc(sizeof(*dlfb), GFP_KERNEL); if (!dlfb) { dev_err(&intf->dev, "%s: failed to allocate dlfb\n", __func__); - goto error; + return -ENOMEM; } INIT_LIST_HEAD(&dlfb->deferred_free); @@ -1703,7 +1703,7 @@ static int dlfb_usb_probe(struct usb_interface *intf, error: if (dlfb->info) { dlfb_ops_destroy(dlfb->info); - } else if (dlfb) { + } else { usb_put_dev(dlfb->udev); kfree(dlfb); } @@ -1730,12 +1730,10 @@ static void dlfb_usb_disconnect(struct usb_interface *intf) /* this function will wait for all in-flight urbs to complete */ dlfb_free_urb_list(dlfb); - if (info) { - /* remove udlfb's sysfs interfaces */ - for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) - device_remove_file(info->dev, &fb_device_attrs[i]); - device_remove_bin_file(info->dev, &edid_attr); - } + /* remove udlfb's sysfs interfaces */ + for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) + device_remove_file(info->dev, &fb_device_attrs[i]); + device_remove_bin_file(info->dev, &edid_attr); unregister_framebuffer(info); } diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c index 440a6636d8f0..34dc8e53a1e9 100644 --- a/drivers/video/fbdev/uvesafb.c +++ b/drivers/video/fbdev/uvesafb.c @@ -1979,7 +1979,7 @@ MODULE_PARM_DESC(noedid, module_param(vram_remap, uint, 0); MODULE_PARM_DESC(vram_remap, "Set amount of video memory to be used [MiB]"); module_param(vram_total, uint, 0); -MODULE_PARM_DESC(vram_total, "Set total amount of video memoery [MiB]"); +MODULE_PARM_DESC(vram_total, "Set total amount of video memory [MiB]"); module_param(maxclk, ushort, 0); MODULE_PARM_DESC(maxclk, "Maximum pixelclock [MHz], overrides EDID data"); module_param(maxhf, ushort, 0); diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig index d1f6196c8b9a..1e972c4e88b1 100644 --- a/drivers/video/logo/Kconfig +++ b/drivers/video/logo/Kconfig @@ -10,6 +10,15 @@ menuconfig LOGO if LOGO +config FB_LOGO_CENTER + bool "Center the logo" + depends on FB=y + help + When this option is selected, the bootup logo is centered both + horizontally and vertically. If more than one logo is displayed + due to multiple CPUs, the collected line of logos is centered + as a whole. + config FB_LOGO_EXTRA bool depends on FB=y diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 7e6e682104dc..b24ddac1604b 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -459,14 +459,14 @@ static long privcmd_ioctl_mmap_batch( return -EFAULT; /* Returns per-frame error in m.arr. */ m.err = NULL; - if (!access_ok(VERIFY_WRITE, m.arr, m.num * sizeof(*m.arr))) + if (!access_ok(m.arr, m.num * sizeof(*m.arr))) return -EFAULT; break; case 2: if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch_v2))) return -EFAULT; /* Returns per-frame error code in m.err. */ - if (!access_ok(VERIFY_WRITE, m.err, m.num * (sizeof(*m.err)))) + if (!access_ok(m.err, m.num * (sizeof(*m.err)))) return -EFAULT; break; default: @@ -661,7 +661,7 @@ static long privcmd_ioctl_dm_op(struct file *file, void __user *udata) goto out; } - if (!access_ok(VERIFY_WRITE, kbufs[i].uptr, + if (!access_ok(kbufs[i].uptr, kbufs[i].size)) { rc = -EFAULT; goto out; |